From e07b45f44724cb9686021757d16bfc03b98b6793 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 15 Dec 2018 11:13:38 +0100 Subject: [PATCH 001/439] Revert "completions/git: Allow aliases with whitespace in the command" This reverts commit 081e14fd213b99f0a28c71e801bf5f60a843be75, which was bogus. --- 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 ca8f24f75..60822fe49 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -445,7 +445,7 @@ end # So instead, we store the aliases in global variables, named after the alias, containing the command. # This is because alias:command is an n:1 mapping (an alias can only have one corresponding command, # but a command can be aliased multiple times) -git config -z --get-regexp 'alias\..*' | while read -lz alias command +git config -z --get-regexp 'alias\..*' | while read -lz alias command _ # Git aliases can contain chars that variable names can't - escape them. set alias (string replace 'alias.' '' -- $alias | string escape --style=var) set -g __fish_git_alias_$alias $command From 1f871c4d0c5c0e1e155bc5bf535a1bfc7ee866f9 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Fri, 14 Dec 2018 11:42:23 -0800 Subject: [PATCH 002/439] builtin_test.cpp: check for ERANGE and special fish_wcstoll errno We were not parsing an in-range number when we claimed we were, and were thus failing to error with invalid numbers and returned a wrong test result. Fixed #5414 Also, provide the detail we can for the other error cases. --- src/builtin_test.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp index 07ae926b9..07aa37356 100644 --- a/src/builtin_test.cpp +++ b/src/builtin_test.cpp @@ -657,7 +657,7 @@ static bool parse_number(const wcstring &arg, number_t *number, wcstring_list_t // invalid (e.g. not a representable integer). *number = number_t{integral, 0.0}; return true; - } else if (got_float) { + } else if (got_float && errno != ERANGE) { // Here we parsed an (in range) floating point value that could not be parsed as an integer. // Break the floating point value into base and delta. Ensure that base is <= the floating // point value. @@ -667,7 +667,11 @@ static bool parse_number(const wcstring &arg, number_t *number, wcstring_list_t return true; } else { // We could not parse a float or an int. - errors.push_back(format_string(_(L"invalid number '%ls'"), arg.c_str())); + // Check for special fish_wcsto* value or show standard EINVAL/ERANGE error. + if (errno == -1) + errors.push_back(format_string(_(L"Integer %lld in '%ls' followed by non-digit"), integral, argcs)); + else + errors.push_back(format_string(L"%s: '%ls'", strerror(errno), argcs)); return false; } } From b404b9392cac8e14314f757fbc72f7692df275bf Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Fri, 14 Dec 2018 12:43:18 -0800 Subject: [PATCH 003/439] builtin_test.cpp: split a long line, add braces --- src/builtin_test.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp index 07aa37356..cac1167b2 100644 --- a/src/builtin_test.cpp +++ b/src/builtin_test.cpp @@ -668,10 +668,12 @@ static bool parse_number(const wcstring &arg, number_t *number, wcstring_list_t } else { // We could not parse a float or an int. // Check for special fish_wcsto* value or show standard EINVAL/ERANGE error. - if (errno == -1) - errors.push_back(format_string(_(L"Integer %lld in '%ls' followed by non-digit"), integral, argcs)); - else + if (errno == -1) { + errors.push_back(format_string(_(L"Integer %lld in '%ls' followed by non-digit"), + integral, argcs)); + } else { errors.push_back(format_string(L"%s: '%ls'", strerror(errno), argcs)); + } return false; } } From cf2b40040aaf8fa39d7e56657eab4e130e0a1026 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 15 Dec 2018 21:05:27 -0800 Subject: [PATCH 004/439] STATUS_INVALID_ARGS = 2 The rest of the high-numbered exit codes are not values used by scripts or builtins, they are internal to fish and come out of the parser for example. Prior to adding STATUS_INVALID_ARGS, builtins were usually exiting 2 if they had a special exit status for the situation of bad arguments. Set it to 2. --- src/common.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common.h b/src/common.h index 83d6cd0ba..49a381f0f 100644 --- a/src/common.h +++ b/src/common.h @@ -1078,9 +1078,13 @@ enum { STATUS_CMD_OK = 0, /// The status code used for failure exit in a command (but not if the args were invalid). STATUS_CMD_ERROR = 1, + /// The status code used for invalid arguments given to a command. This is distinct from valid + /// arguments that might result in a command failure. An invalid args condition is something + /// like an unrecognized flag, missing or too many arguments, an invalid integer, etc. But + STATUS_INVALID_ARGS = 2, + /// The status code used when a command was not found. STATUS_CMD_UNKNOWN = 127, - /// TODO: Figure out why we have two distinct failure codes for when an external command cannot /// be run. /// @@ -1095,10 +1099,6 @@ enum { STATUS_ILLEGAL_CMD = 123, /// The status code used when `read` is asked to consume too much data. STATUS_READ_TOO_MUCH = 122, - /// The status code used for invalid arguments given to a command. This is distinct from valid - /// arguments that might result in a command failure. An invalid args condition is something - /// like an unrecognized flag, missing or too many arguments, an invalid integer, etc. But - STATUS_INVALID_ARGS = 121, }; /* Normally casting an expression to void discards its value, but GCC From 57d6124e6e925490f601d1b77cf1f72bf663c699 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 15 Dec 2018 22:05:19 -0800 Subject: [PATCH 005/439] builtin_test: don't exit 1 for eval errors, add tests for big args Return STATUS_INVALID_ARGS when failing due to evaluation errors, so we can tell the difference between an error and falseness. Add a test for the ERANGE error --- src/builtin_test.cpp | 15 +++++++++------ src/fish_tests.cpp | 23 ++++++++++++++++------- tests/test4.out | 2 +- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp index cac1167b2..7284034a3 100644 --- a/src/builtin_test.cpp +++ b/src/builtin_test.cpp @@ -648,7 +648,6 @@ static bool parse_number(const wcstring &arg, number_t *number, wcstring_list_t const wchar_t *argcs = arg.c_str(); double floating = 0; bool got_float = parse_double(argcs, &floating); - errno = 0; long long integral = fish_wcstoll(argcs); bool got_int = (errno == 0); @@ -656,6 +655,7 @@ static bool parse_number(const wcstring &arg, number_t *number, wcstring_list_t // Here the value is just an integer; ignore the floating point parse because it may be // invalid (e.g. not a representable integer). *number = number_t{integral, 0.0}; + return true; } else if (got_float && errno != ERANGE) { // Here we parsed an (in range) floating point value that could not be parsed as an integer. @@ -664,6 +664,7 @@ static bool parse_number(const wcstring &arg, number_t *number, wcstring_list_t double intpart = std::floor(floating); double delta = floating - intpart; *number = number_t{static_cast(intpart), delta}; + return true; } else { // We could not parse a float or an int. @@ -835,7 +836,7 @@ int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv) { const wcstring_list_t args(argv + 1, argv + 1 + argc); if (argc == 0) { - return STATUS_CMD_ERROR; // Per 1003.1, exit false. + return STATUS_INVALID_ARGS; // Per 1003.1, exit false. } else if (argc == 1) { // Per 1003.1, exit true if the arg is non-empty. return args.at(0).empty() ? STATUS_CMD_ERROR : STATUS_CMD_OK; @@ -858,11 +859,13 @@ int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wcstring_list_t eval_errors; bool result = expr->evaluate(eval_errors); - if (!eval_errors.empty() && !should_suppress_stderr_for_tests()) { - streams.err.append(L"test returned eval errors:\n"); - for (size_t i = 0; i < eval_errors.size(); i++) { - streams.err.append_format(L"\t%ls\n", eval_errors.at(i).c_str()); + if (!eval_errors.empty()) { + if (!should_suppress_stderr_for_tests()) { + for (size_t i = 0; i < eval_errors.size(); i++) { + streams.err.append_format(L"\t%ls\n", eval_errors.at(i).c_str()); + } } + return STATUS_INVALID_ARGS; } return result ? STATUS_CMD_OK : STATUS_CMD_ERROR; } diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 02136d888..f60942542 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -2155,7 +2155,12 @@ static bool run_one_test_test(int expected, wcstring_list_t &lst, bool bracket) argv[i + 1] = NULL; io_streams_t streams(0); int result = builtin_test(parser, streams, argv); + + if (expected != result) + err(L"expected builtin_test() to return %d, got %d", expected, result); + delete[] argv; + return expected == result; } @@ -2207,13 +2212,13 @@ static void test_test() { do_test(run_test_test(0, L"' 2' -eq 2")); do_test(run_test_test(0, L"'2 ' -eq 2")); do_test(run_test_test(0, L"' 2 ' -eq 2")); - do_test(run_test_test(1, L"' 2x' -eq 2")); - do_test(run_test_test(1, L"'' -eq 0")); - do_test(run_test_test(1, L"'' -ne 0")); - do_test(run_test_test(1, L"' ' -eq 0")); - do_test(run_test_test(1, L"' ' -ne 0")); - do_test(run_test_test(1, L"'x' -eq 0")); - do_test(run_test_test(1, L"'x' -ne 0")); + do_test(run_test_test(2, L"' 2x' -eq 2")); + do_test(run_test_test(2, L"'' -eq 0")); + do_test(run_test_test(2, L"'' -ne 0")); + do_test(run_test_test(2, L"' ' -eq 0")); + do_test(run_test_test(2, L"' ' -ne 0")); + do_test(run_test_test(2, L"'x' -eq 0")); + do_test(run_test_test(2, L"'x' -ne 0")); do_test(run_test_test(1, L"-1 -ne -1")); do_test(run_test_test(0, L"abc != def")); do_test(run_test_test(1, L"abc = def")); @@ -2284,6 +2289,10 @@ static void test_test() { do_test(run_test_test(1, L"-4611686018427387904 -ge 4611686018427387904")); do_test(run_test_test(1, L"4611686018427387904 -gt 4611686018427387904")); do_test(run_test_test(0, L"4611686018427387904 -ge 4611686018427387904")); + + // test out-of-range numbers + do_test(run_test_test(2, L"99999999999999999999999999 -ge 1")); + do_test(run_test_test(2, L"1 -eq -99999999999999999999999999.9")); } static void test_wcstod() { diff --git a/tests/test4.out b/tests/test4.out index e0d388e26..a7af43eef 100644 --- a/tests/test4.out +++ b/tests/test4.out @@ -38,7 +38,7 @@ Test 22 pass 6 0 7 4 8 0 -9 121 +9 2 10 0 A 11 1 B From 14ee19cc1b60e426b6f86c7c8663660c339a805a Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 18 Dec 2018 11:03:33 +0100 Subject: [PATCH 006/439] Use HAVE_WCSTOD_L also in header --- src/fallback.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fallback.h b/src/fallback.h index b0680de05..f8b7d4977 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -199,6 +199,6 @@ int flock(int fd, int op); #endif -#ifndef wcstod_l +#ifndef HAVE_WCSTOD_L double wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc); #endif From 082450b1e711c75f6722bf7d8651cda8f835fd1e Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 18 Dec 2018 15:01:38 +0100 Subject: [PATCH 007/439] Severely extended the sorin theme (#5411) * Severely extended the sorin theme This theme should now mostly match the original. * Removed superfluous whitespace * Inlined external links as ASCII art * Made myself the author of the sorin theme * Removed superfluous read delemiter * Renamed __fish_git_action to fish_print_git_action * Adde a minor comment --- share/functions/fish_print_git_action.fish | 71 ++++++ .../web_config/sample_prompts/sorin.fish | 219 ++++++++++++++---- 2 files changed, 244 insertions(+), 46 deletions(-) create mode 100644 share/functions/fish_print_git_action.fish diff --git a/share/functions/fish_print_git_action.fish b/share/functions/fish_print_git_action.fish new file mode 100644 index 000000000..4810dc59d --- /dev/null +++ b/share/functions/fish_print_git_action.fish @@ -0,0 +1,71 @@ +# Given the path to a .git directory this function prints a human-readable name +# for the git action in progress (e.g. "merge") or returns 1. +function fish_print_git_action --argument-names git_dir + if test -z "$git_dir" + if not command -sq git + return 1 + end + if not set git_dir (command git rev-parse --git-dir 2>/dev/null) + return 1 + end + end + + for action_dir in "$git_dir/rebase-apply" "$git_dir/rebase" + if test -d "$action_dir" + if test -f "$action_dir/rebasing" + echo -n 'rebase' + else if test -f "$action_dir/applying" + echo -n 'apply' + else + echo -n 'rebase/apply' + end + return 0 + end + end + + for action_dir in "$git_dir/rebase-merge/interactive" "$git_dir/.dotest-merge/interactive" + if test -f "$action_dir" + echo -n 'rebase-interactive' + return 0 + end + end + + for action_dir in "$git_dir/rebase-merge" "$git_dir/.dotest-merge" + if test -d "$action_dir" + echo -n 'rebase-merge' + return 0 + end + end + + if test -f "$git_dir/MERGE_HEAD" + echo -n 'merge' + return 0 + end + + if test -f "$git_dir/CHERRY_PICK_HEAD" + if test -d "$git_dir/sequencer" + cherry_pick_sequence_formatted='cherry-pick-sequence' + echo -n 'cherry-pick-sequence' + else + cherry_pick_formatted='cherry-pick' + echo -n 'cherry-pick' + end + return 0 + end + + if test -f "$git_dir/REVERT_HEAD" + if test -d "$git_dir/sequencer" + echo -n 'revert-sequence' + else + echo -n 'revert' + end + return 0 + end + + if test -f "$git_dir/BISECT_LOG" + echo -n 'bisect' + return 0 + end + + return 1 +end diff --git a/share/tools/web_config/sample_prompts/sorin.fish b/share/tools/web_config/sample_prompts/sorin.fish index 69fff2d14..0efd4c310 100644 --- a/share/tools/web_config/sample_prompts/sorin.fish +++ b/share/tools/web_config/sample_prompts/sorin.fish @@ -1,61 +1,188 @@ # name: Sorin -# author: Ivan Tham +# author: Leonard Hecker + +# Sources: +# - General theme setup: https://github.com/sorin-ionescu/prezto/blob/d275f316ffdd0bbd075afbff677c3e00791fba16/modules/prompt/functions/prompt_sorin_setup +# - Extraction of git info: https://github.com/sorin-ionescu/prezto/blob/d275f316ffdd0bbd075afbff677c3e00791fba16/modules/git/functions/git-info#L180-L441 function fish_prompt - test $SSH_TTY - and printf (set_color red)$USER(set_color brwhite)'@'(set_color yellow)(prompt_hostname)' ' - test "$USER" = 'root' - and echo (set_color red)"#" + if test -n "$SSH_TTY" + echo -n (set_color brred)"$USER"(set_color white)'@'(set_color yellow)(prompt_hostname)' ' + end - # Main - echo -n (set_color cyan)(prompt_pwd) (set_color red)'❯'(set_color yellow)'❯'(set_color green)'❯ ' + echo -n (set_color blue)(prompt_pwd)' ' + + set_color -o + if test "$USER" = 'root' + echo -n (set_color red)'# ' + end + echo -n (set_color red)'❯'(set_color yellow)'❯'(set_color green)'❯ ' + set_color normal end function fish_right_prompt - # last status - test $status != 0 - and printf (set_color red)"⏎ " + set -l cmd_status $status + if test $cmd_status -ne 0 + echo -n (set_color red)"✘ $cmd_status" + end - if set -l git_dir (git rev-parse --git-dir 2>/dev/null) - # Magenta if branch detached else green - set -l branch (command git branch -qv | string match "\**") - string match -rq detached -- $branch - and set_color brmagenta - or set_color brgreen + if not command -sq git + set_color normal + return + end - git name-rev --name-only HEAD + # Get the git directory for later use. + # Return if not inside a Git repository work tree. + if not set -l git_dir (command git rev-parse --git-dir 2>/dev/null) + set_color normal + return + end - # Merging state - test -f "$git_dir/MERGE_HEAD" - and printf ':'(set_color red)'merge' - printf ' ' + # Get the current action ("merge", "rebase", etc.) + # and if there's one get the current commit hash too. + set -l commit '' + if set -l action (__fish_git_action "$git_dir") + set commit (command git rev-parse HEAD 2> /dev/null | string sub -l 7) + end - # Symbols - if set -l count (command git rev-list --count --left-right $upstream...HEAD 2>/dev/null) - echo $count | read -l ahead behind - if test "$ahead" -gt 0 - printf (set_color magenta)⬆' ' - end - if test "$behind" -gt 0 - printf (set_color magenta)⬇' ' - end - end + # Get either the branch name or a branch descriptor. + set -l branch_detached 0 + if not set -l branch (command git symbolic-ref --short HEAD 2>/dev/null) + set branch_detached 1 + set branch (command git describe --contains --all HEAD 2>/dev/null) + end - for i in (git status --porcelain | string sub -l 2 | uniq) - switch $i - case "." - printf (set_color green)✚' ' - case " D" - printf (set_color red)✖' ' - case "*M*" - printf (set_color blue)✱' ' - case "*R*" - printf (set_color brmagenta)➜' ' - case "*U*" - printf (set_color bryellow)═' ' - case "??" - printf (set_color brwhite)◼' ' - end + # Get the commit difference counts between local and remote. + command git rev-list --count --left-right 'HEAD...@{upstream}' 2>/dev/null \ + | read -d \t -l status_ahead status_behind + if test $status -ne 0 + set status_ahead 0 + set status_behind 0 + end + + # Get the stash status. + # (git stash list) is very slow. => Avoid using it. + set -l status_stashed 0 + if test -f "$git_dir/refs/stash" + set status_stashed 1 + else if test -r "$git_dir/commondir" + read -l commondir <"$git_dir/commondir" + if test -f "$commondir/refs/stash" + set status_stashed 1 end end + + # git-status' porcelain v1 format starts with 2 letters on each line: + # The first letter (X) denotes the index state. + # The second letter (Y) denotes the working directory state. + # + # The following table presents the possible combinations: + # * The underscore character denotes whitespace. + # * The cell values stand for the following file states: + # a: added + # d: deleted + # m: modified + # r: renamed + # u: unmerged + # t: untracked + # * Cells with more than one letter signify that both states + # are simultaneously the case. This is possible since the git index + # and working directory operate independently of each other. + # * Cells which are empty are unhandled by this code. + # * T (= type change) is undocumented. + # See Git v1.7.8.2 release notes for more information. + # + # \ Y→ + # X \ + # ↓ | A | C | D | M | R | T | U | X | B | ? | _ + # ----+----+----+----+----+----+----+----+----+----+----+---- + # A | u | | ad | am | r | am | u | | | | a + # C | | | ad | am | r | am | u | | | | a + # D | | | u | am | r | am | u | | | | a + # M | | | ad | am | r | am | u | | | | a + # R | r | r | rd | rm | r | rm | ur | r | r | r | r + # T | | | ad | am | r | am | u | | | | a + # U | u | u | u | um | ur | um | u | u | u | u | u + # X | | | | m | r | m | u | | | | + # B | | | | m | r | m | u | | | | + # ? | | | | m | r | m | u | | | t | + # _ | | | d | m | r | m | u | | | | + + set -l status_added 0 + set -l status_deleted 0 + set -l status_modified 0 + set -l status_renamed 0 + set -l status_unmerged 0 + set -l status_untracked 0 + for line in (command git status --porcelain | string sub -l 2) + # Check unambiguous cases first which allows us + # to skip running all the other regexps. + if test "$line" = '??' + set status_untracked 1 + continue + end + if string match -r '^(?:AA|DD|U.|.U)$' "$line" >/dev/null + set status_unmerged 1 + continue + end + if string match -r '^(?:[ACDMT][ MT]|[ACMT]D)$' "$line" >/dev/null + set status_added 1 + end + if string match -r '^[ ACMRT]D$' "$line" >/dev/null + set status_deleted 1 + end + if string match -r '^.[MT]$' "$line" >/dev/null + set status_modified 1 + end + if string match -e 'R' "$line" >/dev/null + set status_renamed 1 + end + end + + set_color -o + + if test -n "$branch" + if test $branch_detached -ne 0 + set_color brmagenta + else + set_color green + end + echo -n " $branch" + end + if test -n "$commit" + echo -n ' '(set_color yellow)"$commit" + end + if test -n "$action" + set_color normal + echo -n (set_color white)':'(set_color -o brred)"$action" + end + if test $status_ahead -ne 0 + echo -n ' '(set_color brmagenta)'⬆' + end + if test $status_behind -ne 0 + echo -n ' '(set_color brmagenta)'⬇' + end + if test $status_stashed -ne 0 + echo -n ' '(set_color cyan)'✭' + end + if test $status_added -ne 0 + echo -n ' '(set_color green)'✚' + end + if test $status_deleted -ne 0 + echo -n ' '(set_color red)'✖' + end + if test $status_modified -ne 0 + echo -n ' '(set_color blue)'✱' + end + if test $status_renamed -ne 0 + echo -n ' '(set_color magenta)'➜' + end + if test $status_unmerged -ne 0 + echo -n ' '(set_color yellow)'═' + end + if test $status_untracked -ne 0 + echo -n ' '(set_color white)'◼' + end + + set_color normal end From 50fbc36b739c1495bdeef497d5dda6791bdf582b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 19 Dec 2018 09:34:57 +0100 Subject: [PATCH 008/439] sample_prompts/sorin: Correct git_action function name We renamed this, and apparently missed it. [ci skip] --- share/tools/web_config/sample_prompts/sorin.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/tools/web_config/sample_prompts/sorin.fish b/share/tools/web_config/sample_prompts/sorin.fish index 0efd4c310..a7d379b32 100644 --- a/share/tools/web_config/sample_prompts/sorin.fish +++ b/share/tools/web_config/sample_prompts/sorin.fish @@ -41,7 +41,7 @@ function fish_right_prompt # Get the current action ("merge", "rebase", etc.) # and if there's one get the current commit hash too. set -l commit '' - if set -l action (__fish_git_action "$git_dir") + if set -l action (fish_print_git_action "$git_dir") set commit (command git rev-parse HEAD 2> /dev/null | string sub -l 7) end From d6e315d25db5c2a10b552cddd3aec93ce9398f26 Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 20 Dec 2018 21:36:01 +0800 Subject: [PATCH 009/439] cmake: define _GNU_SOURCE Fixes the build on Cygwin. Analogous to AC_USE_SYSTEM_EXTENSIONS under Autoconf. Closes #5423. --- config_cmake.h.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config_cmake.h.in b/config_cmake.h.in index 2c23ad452..d234acba9 100644 --- a/config_cmake.h.in +++ b/config_cmake.h.in @@ -142,6 +142,11 @@ /* Define to 1 if tparm accepts a fixed amount of paramters. */ #cmakedefine TPARM_SOLARIS_KLUDGE 1 +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + /* The size of wchar_t in bits. */ #define WCHAR_T_BITS ${WCHAR_T_BITS} From fb679ac9c3eefee8714e175a469fcd0ceaf05ea6 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 23 Dec 2018 20:03:45 -0600 Subject: [PATCH 010/439] Add completions for `pkg [info|show|list]` --- share/completions/pkg.fish | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/share/completions/pkg.fish b/share/completions/pkg.fish index 4acf44b13..8a4b96737 100644 --- a/share/completions/pkg.fish +++ b/share/completions/pkg.fish @@ -48,7 +48,6 @@ complete -c pkg -n __fish_pkg_subcommand -r -s R -l repo-conf-dir -d "Set reposi complete -c pkg -n __fish_pkg_subcommand -s 4 -d "Use IPv4" complete -c pkg -n __fish_pkg_subcommand -s 6 -d "Use IPv6" -complete -c pkg -n __fish_pkg_subcommand -xa help -d "Display help for command" complete -c pkg -n __fish_pkg_subcommand -xa add -d "Install package file" complete -c pkg -n __fish_pkg_subcommand -xa annotate -d "Modify annotations on packages" complete -c pkg -n __fish_pkg_subcommand -xa audit -d "Audit installed packages" @@ -60,8 +59,10 @@ complete -c pkg -n __fish_pkg_subcommand -xa convert -d "Convert package from complete -c pkg -n __fish_pkg_subcommand -xa create -d "Create a package" complete -c pkg -n __fish_pkg_subcommand -xa delete -d "Remove a package" complete -c pkg -n __fish_pkg_subcommand -xa fetch -d "Download a remote package" +complete -c pkg -n __fish_pkg_subcommand -xa help -d "Display help for command" complete -c pkg -n __fish_pkg_subcommand -xa info -d "List installed packages" complete -c pkg -n __fish_pkg_subcommand -xa install -d "Install packages" +complete -c pkg -n __fish_pkg_subcommand -xa list -d "List files belonging to package(s)" complete -c pkg -n __fish_pkg_subcommand -xa lock -d "Prevent package modification" complete -c pkg -n __fish_pkg_subcommand -xa plugins -d "List package manager plugins" complete -c pkg -n __fish_pkg_subcommand -xa query -d "Query installed packages" @@ -73,6 +74,7 @@ complete -c pkg -n __fish_pkg_subcommand -xa search -d "Find packages" complete -c pkg -n __fish_pkg_subcommand -xa set -d "Modify package information in database" complete -c pkg -n __fish_pkg_subcommand -xa shell -d "Open a SQLite shell" complete -c pkg -n __fish_pkg_subcommand -xa shlib -d "Display packages linking to shared library" +complete -c pkg -n __fish_pkg_subcommand -xa show -d "Show information about package(s)" complete -c pkg -n __fish_pkg_subcommand -xa stats -d "Display package statistics" complete -c pkg -n __fish_pkg_subcommand -xa unlock -d "Stop preventing package modification" complete -c pkg -n __fish_pkg_subcommand -xa update -d "Update remote repositories" @@ -115,5 +117,14 @@ complete -c pkg -n '__fish_pkg_is install' -s R -l from-root -d "Reinstall packa complete -c pkg -n '__fish_pkg_is install update' -x -s r -l repository -d "Use only a given repository" complete -c pkg -n '__fish_pkg_is install upgrade' -s U -l no-repo-update -d "Do not automatically update database" +# info +complete -c pkg -n '__fish_pkg_is info' -xa '(pkg query "%n")' + +# show +complete -c pkg -n '__fish_pkg_is show' -xa '(pkg query "%n")' + +# list +complete -c pkg -n '__fish_pkg_is list' -xa '(pkg query "%n")' + # update complete -c pkg -n '__fish_pkg_is add update' -s f -l force -d "Force a full download of a repository" From 69a1c5a3a19b1e7f397c45b2d6c88e4530101f2f Mon Sep 17 00:00:00 2001 From: hrvoj3e Date: Wed, 26 Dec 2018 15:24:23 +0100 Subject: [PATCH 011/439] Fix typos in anchor to fish_opt --- doc_src/argparse.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/argparse.txt b/doc_src/argparse.txt index 70278198a..42da8fdd0 100644 --- a/doc_src/argparse.txt +++ b/doc_src/argparse.txt @@ -9,7 +9,7 @@ argparse [OPTIONS] OPTION_SPEC... -- [ARG...] This command makes it easy for fish scripts and functions to handle arguments in a manner 100% identical to how fish builtin commands handle their arguments. You pass a sequence of arguments that define the options recognized, followed by a literal `--`, then the arguments to be parsed (which might also include a literal `--`). More on this in the usage section below. -Each OPTION_SPEC can be written in the domain specific language described below or created using the companion `fish_opt` command. All OPTION_SPECs must appear after any argparse flags and before the `--` that separates them from the arguments to be parsed. +Each OPTION_SPEC can be written in the domain specific language described below or created using the companion `fish_opt` command. All OPTION_SPECs must appear after any argparse flags and before the `--` that separates them from the arguments to be parsed. Each option that is seen in the ARG list will result in a var name of the form `_flag_X`, where `X` is the short flag letter and the long flag name. The OPTION_SPEC always requires a short flag even if it can't be used. So there will always be `_flag_X` var set using the short flag letter if the corresponding short or long flag is seen. The long flag name var (e.g., `_flag_help`) will only be defined, obviously, if the OPTION_SPEC includes a long flag name. @@ -78,7 +78,7 @@ Each option specification is a string composed of - Optionally a `!` followed by fish script to validate the value. Typically this will be a function to run. If the return status is zero the value for the flag is valid. If non-zero the value is invalid. Any error messages should be written to stdout (not stderr). See the section on Flag Value Validation for more information. -See the `fish_opt` command for a friendlier but more verbose way to create option specifications. +See the `fish_opt` command for a friendlier but more verbose way to create option specifications. In the following examples if a flag is not seen when parsing the arguments then the corresponding _flag_X var(s) will not be set. From 0c25d7a49c2f4fa24ed8ac6decf6210244b2f01f Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 28 Dec 2018 20:56:17 +0800 Subject: [PATCH 012/439] CHANGELOG: 3.0.0 updates --- CHANGELOG.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5deaf80e4..c2efdde7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# fish 3.0.0 (released December 28, 2018) + +fish 3 is a major release, which introduces some breaking changes alongside improved functionality. Although most existing scripts will continue to work, they should be reviewed against the list contained in the 3.0b1 release notes below. + +Compared to the beta release of fish 3.0b1, fish version 3.0.0: + +- builds correctly against musl libc (#5407) +- handles huge numeric arguments to `test` correctly (#5414) +- removes the history colouring introduced in 3.0b1, which did not always work correctly + +There is one significant known issue which was not able to be corrected before the release: + +- fish 3.0.0 builds on Cygwin (#5423), but does not run correctly (#5426) and will result in a hanging terminal when started. Cygwin users are encouraged to continue using 2.7.1 until a release which corrects this is available. + +If you are upgrading from version 2.7.1 or before, please also review the release notes for 3.0b1 (included below). + +--- + # fish 3.0b1 (released December 11, 2018) fish 3 is a major release, which introduces some breaking changes alongside improved functionality. Although most existing scripts will continue to work, they should be reviewed against the list below. @@ -57,7 +75,7 @@ A new feature flags mechanism is added for staging deprecations and breaking cha - `exec` prompts for confirmation if background jobs are running. - `funced` has a new `--save` option to automatically save the edited function after successfully editing (#4668). - `functions` has a new ` --handlers` option to show functions registered as event handlers (#4694). -- `history search` supports globs for wildcard searching (#3136) and has a new `--reverse` option to show entries from oldest to newest (#4375).. +- `history search` supports globs for wildcard searching (#3136) and has a new `--reverse` option to show entries from oldest to newest (#4375). - `jobs` has a new `--quiet` option to silence the output. - `read` has a new `--delimiter` option for splitting input into arrays (#4256). - `read` writes directly to stdout if called without arguments (#4407). From 938ce48d259d1ceee51312b6b3e005fabfc07d1f Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 28 Dec 2018 21:01:03 +0800 Subject: [PATCH 013/439] Bump version for 3.0.0 --- 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 ada78049b..2eb4b5a8e 100644 --- a/osx/Info.plist +++ b/osx/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.7.900 + 3.0.0 CFBundleVersion 0.1 LSApplicationCategoryType diff --git a/osx/config.h b/osx/config.h index d9f913647..d8036fee7 100644 --- a/osx/config.h +++ b/osx/config.h @@ -206,7 +206,7 @@ #define PACKAGE_NAME "fish" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "fish 3.0b1" +#define PACKAGE_STRING "fish 3.0.0" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "fish" @@ -215,7 +215,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "3.0b1" +#define PACKAGE_VERSION "3.0.0" /* The size of `wchar_t', as computed by sizeof. */ #define SIZEOF_WCHAR_T 4 From 9e4ece8d89162919cf300bc88b2f8dd93cbbcbc0 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 28 Dec 2018 22:18:32 +0800 Subject: [PATCH 014/439] CHANGELOG: next-minor updates, up to 05222a055a0 [ci skip] --- CHANGELOG.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2efdde7e..178add350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# fish next-minor + +## Deprecations +- None yet. + +## Notable fixes and improvements +### Syntax changes and new commands +- None yet. + +### Interactive improvements +- Major improvements in performance and functionality to the 'sorin' sample prompt (#5411). +- Added completions for: + - nothing yet... +- Lots of improvements to completions. + +--- + # fish 3.0.0 (released December 28, 2018) fish 3 is a major release, which introduces some breaking changes alongside improved functionality. Although most existing scripts will continue to work, they should be reviewed against the list contained in the 3.0b1 release notes below. @@ -21,7 +38,7 @@ If you are upgrading from version 2.7.1 or before, please also review the releas fish 3 is a major release, which introduces some breaking changes alongside improved functionality. Although most existing scripts will continue to work, they should be reviewed against the list below. ## Notable non-backward compatible changes -- Process and job expansion has largely been removed. `%` will no longer perform these expansions, except for `%self` for the PID of the current shell. Additionally, job management commands (`disown`, `wait`, `bg`, `fg` and `kill`) will expand job specifiers starting with `%` (#4230, #1202). +- Process and job expansion has largely been removed. `%` will no longer perform these expansions, except for `%self` for the PID of the current shell. Additionally, job management commands (`disown`, `wait`, `bg`, `fg` and `kill`) will expand job specifiers starting with `%` (#4230, #1202). - `set x[1] x[2] a b`, to set multiple elements of an array at once, is no longer valid syntax (#4236). - A literal `{}` now expands to itself, rather than nothing. This makes working with `find -exec` easier (#1109, #4632). - Literally accessing a zero-index is now illegal syntax and is caught by the parser (#4862). (fish indices start at 1) From 742fde0dd68858ad603b449501f0c42cd7864fab Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 28 Dec 2018 17:57:53 +0100 Subject: [PATCH 015/439] Don't use less in highlighting test It doesn't have to be installed. `cat` is in our dependencies, so we can assume it's there. Fixes #5436. --- src/fish_tests.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index f60942542..abe21a2e5 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -4243,7 +4243,8 @@ static void test_highlighting() { {L"cat", highlight_spec_command}, {L"/dev/null", highlight_spec_param}, {L"|", highlight_spec_statement_terminator}, - {L"less", highlight_spec_command}, + // This is bogus, but we used to use "less" here and that doesn't have to be installed. + {L"cat", highlight_spec_command}, {L"2>", highlight_spec_redirection}, }); From b60a9d8c4acbcebac3b97fce8d2163432a1cf22f Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 29 Dec 2018 21:33:00 +0800 Subject: [PATCH 016/439] pcre2: move to PCRE2 10.32 Closes #5353. --- .gitattributes | 4 +- Makefile.in | 2 +- build_tools/lint.fish | 6 +- cmake/PCRE2.cmake | 4 +- configure.ac | 2 +- fish.xcodeproj/project.pbxproj | 2 +- pcre2-10.22/.gitignore | 15 - pcre2-10.22/ChangeLog | 934 -- pcre2-10.22/INSTALL | 370 - pcre2-10.22/NEWS | 110 - pcre2-10.22/src/pcre2_compile.c | 9081 --------------- pcre2-10.22/src/pcre2_match.c | 7243 ------------ pcre2-10.22/src/pcre2_ucd.c | 3747 ------- pcre2-10.22/src/sljit/sljitNativeMIPS_32.c | 366 - pcre2-10.22/src/sljit/sljitNativeX86_32.c | 550 - {pcre2-10.22 => pcre2-10.32}/132html | 7 +- {pcre2-10.22 => pcre2-10.32}/AUTHORS | 6 +- {pcre2-10.22 => pcre2-10.32}/CMakeLists.txt | 88 +- {pcre2-10.22 => pcre2-10.32}/COPYING | 0 pcre2-10.32/ChangeLog | 1867 ++++ {pcre2-10.22 => pcre2-10.32}/CheckMan | 0 {pcre2-10.22 => pcre2-10.32}/CleanTxt | 0 {pcre2-10.22 => pcre2-10.32}/Detrail | 0 {pcre2-10.22 => pcre2-10.32}/HACKING | 433 +- pcre2-10.32/INSTALL | 368 + {pcre2-10.22 => pcre2-10.32}/LICENCE | 33 +- {pcre2-10.22 => pcre2-10.32}/Makefile.am | 69 +- {pcre2-10.22 => pcre2-10.32}/Makefile.in | 435 +- pcre2-10.32/NEWS | 248 + .../NON-AUTOTOOLS-BUILD | 80 +- {pcre2-10.22 => pcre2-10.32}/PrepareRelease | 5 +- {pcre2-10.22 => pcre2-10.32}/README | 223 +- {pcre2-10.22 => pcre2-10.32}/RunGrepTest | 136 +- {pcre2-10.22 => pcre2-10.32}/RunTest | 132 +- {pcre2-10.22 => pcre2-10.32}/aclocal.m4 | 156 +- {pcre2-10.22 => pcre2-10.32}/ar-lib | 2 +- .../cmake/COPYING-CMAKE-SCRIPTS | 0 .../cmake/FindEditline.cmake | 0 .../cmake/FindPackageHandleStandardArgs.cmake | 0 .../cmake/FindReadline.cmake | 0 {pcre2-10.22 => pcre2-10.32}/compile | 9 +- .../config-cmake.h.in | 5 +- {pcre2-10.22 => pcre2-10.32}/config.guess | 188 +- {pcre2-10.22 => pcre2-10.32}/config.sub | 73 +- {pcre2-10.22 => pcre2-10.32}/configure | 1969 ++-- {pcre2-10.22 => pcre2-10.32}/configure.ac | 351 +- {pcre2-10.22 => pcre2-10.32}/depcomp | 6 +- {pcre2-10.22 => pcre2-10.32}/install-sh | 4 +- .../libpcre2-16.pc.in | 0 .../libpcre2-32.pc.in | 0 {pcre2-10.22 => pcre2-10.32}/libpcre2-8.pc.in | 0 .../libpcre2-posix.pc.in | 0 {pcre2-10.22 => pcre2-10.32}/ltmain.sh | 768 +- {pcre2-10.22 => pcre2-10.32}/m4/ax_pthread.m4 | 0 {pcre2-10.22 => pcre2-10.32}/m4/libtool.m4 | 69 +- {pcre2-10.22 => pcre2-10.32}/m4/ltoptions.m4 | 2 +- {pcre2-10.22 => pcre2-10.32}/m4/ltsugar.m4 | 2 +- {pcre2-10.22 => pcre2-10.32}/m4/ltversion.m4 | 12 +- .../m4/lt~obsolete.m4 | 2 +- .../m4/pcre2_visibility.m4 | 0 {pcre2-10.22 => pcre2-10.32}/missing | 6 +- {pcre2-10.22 => pcre2-10.32}/pcre2-config.in | 2 +- {pcre2-10.22 => pcre2-10.32}/perltest.sh | 119 +- .../src/config.h.generic | 134 +- {pcre2-10.22 => pcre2-10.32}/src/config.h.in | 123 +- {pcre2-10.22 => pcre2-10.32}/src/dftables.c | 27 +- .../src/pcre2.h.generic | 516 +- {pcre2-10.22 => pcre2-10.32}/src/pcre2.h.in | 516 +- .../src/pcre2_auto_possess.c | 85 +- .../src/pcre2_chartables.c.dist | 46 +- pcre2-10.32/src/pcre2_compile.c | 9921 +++++++++++++++++ .../src/pcre2_config.c | 48 +- .../src/pcre2_context.c | 119 +- pcre2-10.32/src/pcre2_convert.c | 1182 ++ .../src/pcre2_dfa_match.c | 816 +- .../src/pcre2_error.c | 54 +- pcre2-10.32/src/pcre2_extuni.c | 148 + .../src/pcre2_find_bracket.c | 5 +- pcre2-10.32/src/pcre2_fuzzsupport.c | 365 + .../src/pcre2_internal.h | 269 +- .../src/pcre2_intmodedep.h | 210 +- .../src/pcre2_jit_compile.c | 3951 ++++--- .../src/pcre2_jit_match.c | 10 +- .../src/pcre2_jit_misc.c | 0 .../src/pcre2_jit_test.c | 41 +- .../src/pcre2_maketables.c | 9 +- pcre2-10.32/src/pcre2_match.c | 6860 ++++++++++++ .../src/pcre2_match_data.c | 6 +- .../src/pcre2_newline.c | 0 .../src/pcre2_ord2utf.c | 2 +- .../src/pcre2_pattern_info.c | 38 +- .../src/pcre2_printint.c | 10 +- .../src/pcre2_serialize.c | 25 +- .../src/pcre2_string_utils.c | 38 +- .../src/pcre2_study.c | 196 +- .../src/pcre2_substitute.c | 61 +- .../src/pcre2_substring.c | 9 +- .../src/pcre2_tables.c | 476 +- pcre2-10.32/src/pcre2_ucd.c | 4189 +++++++ {pcre2-10.22 => pcre2-10.32}/src/pcre2_ucp.h | 76 +- .../src/pcre2_valid_utf.c | 14 +- .../src/pcre2_xclass.c | 0 {pcre2-10.22 => pcre2-10.32}/src/pcre2demo.c | 53 + {pcre2-10.22 => pcre2-10.32}/src/pcre2grep.c | 1424 ++- {pcre2-10.22 => pcre2-10.32}/src/pcre2posix.c | 25 +- {pcre2-10.22 => pcre2-10.32}/src/pcre2posix.h | 8 +- {pcre2-10.22 => pcre2-10.32}/src/pcre2test.c | 2180 +++- .../src/sljit/sljitConfig.h | 20 +- .../src/sljit/sljitConfigInternal.h | 141 +- .../src/sljit/sljitExecAllocator.c | 21 +- .../src/sljit/sljitLir.c | 1206 +- .../src/sljit/sljitLir.h | 872 +- .../src/sljit/sljitNativeARM_32.c | 1738 +-- .../src/sljit/sljitNativeARM_64.c | 1174 +- .../src/sljit/sljitNativeARM_T2_32.c | 1358 ++- pcre2-10.32/src/sljit/sljitNativeMIPS_32.c | 666 ++ .../src/sljit/sljitNativeMIPS_64.c | 403 +- .../src/sljit/sljitNativeMIPS_common.c | 847 +- .../src/sljit/sljitNativePPC_32.c | 115 +- .../src/sljit/sljitNativePPC_64.c | 217 +- .../src/sljit/sljitNativePPC_common.c | 1424 ++- .../src/sljit/sljitNativeSPARC_32.c | 146 +- .../src/sljit/sljitNativeSPARC_common.c | 371 +- .../src/sljit/sljitNativeTILEGX-encoder.c | 2 +- .../src/sljit/sljitNativeTILEGX_64.c | 38 +- pcre2-10.32/src/sljit/sljitNativeX86_32.c | 894 ++ .../src/sljit/sljitNativeX86_64.c | 451 +- .../src/sljit/sljitNativeX86_common.c | 896 +- .../src/sljit/sljitProtExecAllocator.c | 421 + .../src/sljit/sljitUtils.c | 116 +- {pcre2-10.22 => pcre2-10.32}/test-driver | 6 +- 131 files changed, 44615 insertions(+), 33524 deletions(-) delete mode 100644 pcre2-10.22/.gitignore delete mode 100644 pcre2-10.22/ChangeLog delete mode 100644 pcre2-10.22/INSTALL delete mode 100644 pcre2-10.22/NEWS delete mode 100644 pcre2-10.22/src/pcre2_compile.c delete mode 100644 pcre2-10.22/src/pcre2_match.c delete mode 100644 pcre2-10.22/src/pcre2_ucd.c delete mode 100644 pcre2-10.22/src/sljit/sljitNativeMIPS_32.c delete mode 100644 pcre2-10.22/src/sljit/sljitNativeX86_32.c rename {pcre2-10.22 => pcre2-10.32}/132html (96%) rename {pcre2-10.22 => pcre2-10.32}/AUTHORS (82%) rename {pcre2-10.22 => pcre2-10.32}/CMakeLists.txt (90%) rename {pcre2-10.22 => pcre2-10.32}/COPYING (100%) create mode 100644 pcre2-10.32/ChangeLog rename {pcre2-10.22 => pcre2-10.32}/CheckMan (100%) rename {pcre2-10.22 => pcre2-10.32}/CleanTxt (100%) rename {pcre2-10.22 => pcre2-10.32}/Detrail (100%) rename {pcre2-10.22 => pcre2-10.32}/HACKING (57%) create mode 100644 pcre2-10.32/INSTALL rename {pcre2-10.22 => pcre2-10.32}/LICENCE (69%) rename {pcre2-10.22 => pcre2-10.32}/Makefile.am (91%) rename {pcre2-10.22 => pcre2-10.32}/Makefile.in (88%) create mode 100644 pcre2-10.32/NEWS rename {pcre2-10.22 => pcre2-10.32}/NON-AUTOTOOLS-BUILD (87%) rename {pcre2-10.22 => pcre2-10.32}/PrepareRelease (98%) rename {pcre2-10.22 => pcre2-10.32}/README (81%) rename {pcre2-10.22 => pcre2-10.32}/RunGrepTest (81%) rename {pcre2-10.22 => pcre2-10.32}/RunTest (83%) rename {pcre2-10.22 => pcre2-10.32}/aclocal.m4 (93%) rename {pcre2-10.22 => pcre2-10.32}/ar-lib (99%) rename {pcre2-10.22 => pcre2-10.32}/cmake/COPYING-CMAKE-SCRIPTS (100%) rename {pcre2-10.22 => pcre2-10.32}/cmake/FindEditline.cmake (100%) rename {pcre2-10.22 => pcre2-10.32}/cmake/FindPackageHandleStandardArgs.cmake (100%) rename {pcre2-10.22 => pcre2-10.32}/cmake/FindReadline.cmake (100%) rename {pcre2-10.22 => pcre2-10.32}/compile (97%) rename {pcre2-10.22 => pcre2-10.32}/config-cmake.h.in (85%) rename {pcre2-10.22 => pcre2-10.32}/config.guess (89%) rename {pcre2-10.22 => pcre2-10.32}/config.sub (96%) rename {pcre2-10.22 => pcre2-10.32}/configure (95%) rename {pcre2-10.22 => pcre2-10.32}/configure.ac (73%) rename {pcre2-10.22 => pcre2-10.32}/depcomp (99%) rename {pcre2-10.22 => pcre2-10.32}/install-sh (99%) rename {pcre2-10.22 => pcre2-10.32}/libpcre2-16.pc.in (100%) rename {pcre2-10.22 => pcre2-10.32}/libpcre2-32.pc.in (100%) rename {pcre2-10.22 => pcre2-10.32}/libpcre2-8.pc.in (100%) rename {pcre2-10.22 => pcre2-10.32}/libpcre2-posix.pc.in (100%) rename {pcre2-10.22 => pcre2-10.32}/ltmain.sh (94%) rename {pcre2-10.22 => pcre2-10.32}/m4/ax_pthread.m4 (100%) rename {pcre2-10.22 => pcre2-10.32}/m4/libtool.m4 (99%) rename {pcre2-10.22 => pcre2-10.32}/m4/ltoptions.m4 (99%) rename {pcre2-10.22 => pcre2-10.32}/m4/ltsugar.m4 (98%) rename {pcre2-10.22 => pcre2-10.32}/m4/ltversion.m4 (65%) rename {pcre2-10.22 => pcre2-10.32}/m4/lt~obsolete.m4 (98%) rename {pcre2-10.22 => pcre2-10.32}/m4/pcre2_visibility.m4 (100%) rename {pcre2-10.22 => pcre2-10.32}/missing (98%) rename {pcre2-10.22 => pcre2-10.32}/pcre2-config.in (97%) rename {pcre2-10.22 => pcre2-10.32}/perltest.sh (68%) rename {pcre2-10.22 => pcre2-10.32}/src/config.h.generic (70%) rename {pcre2-10.22 => pcre2-10.32}/src/config.h.in (70%) rename {pcre2-10.22 => pcre2-10.32}/src/dftables.c (87%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2.h.generic (56%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2.h.in (56%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_auto_possess.c (93%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_chartables.c.dist (80%) create mode 100644 pcre2-10.32/src/pcre2_compile.c rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_config.c (89%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_context.c (77%) create mode 100644 pcre2-10.32/src/pcre2_convert.c rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_dfa_match.c (85%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_error.c (87%) create mode 100644 pcre2-10.32/src/pcre2_extuni.c rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_find_bracket.c (98%) create mode 100644 pcre2-10.32/src/pcre2_fuzzsupport.c rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_internal.h (93%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_intmodedep.h (79%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_jit_compile.c (77%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_jit_match.c (96%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_jit_misc.c (100%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_jit_test.c (97%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_maketables.c (93%) create mode 100644 pcre2-10.32/src/pcre2_match.c rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_match_data.c (95%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_newline.c (100%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_ord2utf.c (99%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_pattern_info.c (93%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_printint.c (99%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_serialize.c (88%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_string_utils.c (84%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_study.c (90%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_substitute.c (91%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_substring.c (98%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_tables.c (69%) create mode 100644 pcre2-10.32/src/pcre2_ucd.c rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_ucp.h (79%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_valid_utf.c (98%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2_xclass.c (100%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2demo.c (88%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2grep.c (72%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2posix.c (93%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2posix.h (93%) rename {pcre2-10.22 => pcre2-10.32}/src/pcre2test.c (79%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitConfig.h (86%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitConfigInternal.h (89%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitExecAllocator.c (95%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitLir.c (64%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitLir.h (60%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeARM_32.c (61%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeARM_64.c (65%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeARM_T2_32.c (64%) create mode 100644 pcre2-10.32/src/sljit/sljitNativeMIPS_32.c rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeMIPS_64.c (54%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeMIPS_common.c (75%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativePPC_32.c (73%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativePPC_64.c (68%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativePPC_common.c (63%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeSPARC_32.c (62%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeSPARC_common.c (82%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeTILEGX-encoder.c (99%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeTILEGX_64.c (99%) create mode 100644 pcre2-10.32/src/sljit/sljitNativeX86_32.c rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeX86_64.c (64%) rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitNativeX86_common.c (80%) create mode 100644 pcre2-10.32/src/sljit/sljitProtExecAllocator.c rename {pcre2-10.22 => pcre2-10.32}/src/sljit/sljitUtils.c (68%) rename {pcre2-10.22 => pcre2-10.32}/test-driver (97%) diff --git a/.gitattributes b/.gitattributes index f51088262..d440640bc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -20,8 +20,8 @@ /.github/* export-ignore # for linguist; let github identify our project as C++ instead of C due to pcre2 -/pcre2-10.22/ linguist-vendored -/pcre2-10.22/* linguist-vendored +/pcre2-10.32/ linguist-vendored +/pcre2-10.32/* linguist-vendored /muparser-2.2.5/ linguist-vendored /muparser-2.2.5/* linguist-vendored angular.js linguist-vendored diff --git a/Makefile.in b/Makefile.in index 3ae3660d3..bfdd75e4b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -71,7 +71,7 @@ extra_confdir = @extra_confdir@ # pcre2 # PCRE2_WIDTH = @WCHAR_T_BITS@ -PCRE2_DIR = pcre2-10.22 +PCRE2_DIR = pcre2-10.32 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 266c15027..88bf8445b 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -134,14 +134,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.22/' -- -enable-global-analysis 2>&1 + oclint-json-compilation-database -e '/pcre2-10.32/' -- -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.22/' $i_files - oclint-json-compilation-database -e '/pcre2-10.22/' $i_files 2>&1 + echo oclint-json-compilation-database -e '/pcre2-10.32/' $i_files + oclint-json-compilation-database -e '/pcre2-10.32/' $i_files 2>&1 end else # Presumably we're on Linux or other platform not requiring special diff --git a/cmake/PCRE2.cmake b/cmake/PCRE2.cmake index 2b4afce25..23f483551 100644 --- a/cmake/PCRE2.cmake +++ b/cmake/PCRE2.cmake @@ -13,8 +13,8 @@ IF (PCRE2_LIB AND PCRE2_INCLUDE_DIR) MESSAGE(STATUS "Found system PCRE2 library ${PCRE2_INCLUDE_DIR}") ELSE() MESSAGE(STATUS "Using bundled PCRE2 library") - ADD_SUBDIRECTORY(pcre2-10.22 EXCLUDE_FROM_ALL) - SET(PCRE2_INCLUDE_DIR ${CMAKE_BINARY_DIR}/pcre2-10.22/) + ADD_SUBDIRECTORY(pcre2-10.32 EXCLUDE_FROM_ALL) + SET(PCRE2_INCLUDE_DIR ${CMAKE_BINARY_DIR}/pcre2-10.32/) SET(PCRE2_LIB pcre2-${PCRE2_WIDTH}) endif(PCRE2_LIB AND PCRE2_INCLUDE_DIR) INCLUDE_DIRECTORIES(${PCRE2_INCLUDE_DIR}) diff --git a/configure.ac b/configure.ac index 7070799ff..08c415739 100644 --- a/configure.ac +++ b/configure.ac @@ -686,7 +686,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.22]) + AC_CONFIG_SUBDIRS([pcre2-10.32]) PCRE2_CXXFLAGS='-I$(PCRE2_DIR)/src' PCRE2_LIBS='-L$(PCRE2_LIBDIR) -lpcre2-$(PCRE2_WIDTH)' diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index 172b204d6..c19f683d1 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -986,7 +986,7 @@ D04F7FF71BA4E82C00B0F227 /* pcre2_chartables.c.dist */, ); name = pcre; - path = "pcre2-10.22/src"; + path = "pcre2-10.32/src"; sourceTree = SOURCE_ROOT; }; D08A328E17B4455100F3A533 /* fish_tests */ = { diff --git a/pcre2-10.22/.gitignore b/pcre2-10.22/.gitignore deleted file mode 100644 index fa91befa7..000000000 --- a/pcre2-10.22/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -!config.h.in -!configure -!doc/ -.deps -.dirstamp -.libs/ -*.la -*.lo -*.pc -libtool -pcre2_chartables.c -pcre2-config -pcre2test -pcre2.h -stamp-h1 \ No newline at end of file diff --git a/pcre2-10.22/ChangeLog b/pcre2-10.22/ChangeLog deleted file mode 100644 index 3dcebb954..000000000 --- a/pcre2-10.22/ChangeLog +++ /dev/null @@ -1,934 +0,0 @@ -Change Log for PCRE2 --------------------- - - -Version 10.22 29-July-2016 --------------------------- - -1. Applied Jason Hood's patches to RunTest.bat and testdata/wintestoutput3 -to fix problems with running the tests under Windows. - -2. Implemented a facility for quoting literal characters within hexadecimal -patterns in pcre2test, to make it easier to create patterns with just a few -non-printing characters. - -3. Binary zeros are not supported in pcre2test input files. It now detects them -and gives an error. - -4. Updated the valgrind parameters in RunTest: (a) changed smc-check=all to -smc-check=all-non-file; (b) changed obj:* in the suppression file to obj:??? so -that it matches only unknown objects. - -5. Updated the maintenance script maint/ManyConfigTests to make it easier to -select individual groups of tests. - -6. When the POSIX wrapper function regcomp() is called, the REG_NOSUB option -used to set PCRE2_NO_AUTO_CAPTURE when calling pcre2_compile(). However, this -disables the use of back references (and subroutine calls), which are supported -by other implementations of regcomp() with RE_NOSUB. Therefore, REG_NOSUB no -longer causes PCRE2_NO_AUTO_CAPTURE to be set, though it still ignores nmatch -and pmatch when regexec() is called. - -7. Because of 6 above, pcre2test has been modified with a new modifier called -posix_nosub, to call regcomp() with REG_NOSUB. Previously the no_auto_capture -modifier had this effect. That option is now ignored when the POSIX API is in -use. - -8. Minor tidies to the pcre2demo.c sample program, including more comments -about its 8-bit-ness. - -9. Detect unmatched closing parentheses and give the error in the pre-scan -instead of later. Previously the pre-scan carried on and could give a -misleading incorrect error message. For example, /(?J)(?'a'))(?'a')/ gave a -message about invalid duplicate group names. - -10. It has happened that pcre2test was accidentally linked with another POSIX -regex library instead of libpcre2-posix. In this situation, a call to regcomp() -(in the other library) may succeed, returning zero, but of course putting its -own data into the regex_t block. In one example the re_pcre2_code field was -left as NULL, which made pcre2test think it had not got a compiled POSIX regex, -so it treated the next line as another pattern line, resulting in a confusing -error message. A check has been added to pcre2test to see if the data returned -from a successful call of regcomp() are valid for PCRE2's regcomp(). If they -are not, an error message is output and the pcre2test run is abandoned. The -message points out the possibility of a mis-linking. Hopefully this will avoid -some head-scratching the next time this happens. - -11. A pattern such as /(?<=((?C)0))/, which has a callout inside a lookbehind -assertion, caused pcre2test to output a very large number of spaces when the -callout was taken, making the program appearing to loop. - -12. A pattern that included (*ACCEPT) in the middle of a sufficiently deeply -nested set of parentheses of sufficient size caused an overflow of the -compiling workspace (which was diagnosed, but of course is not desirable). - -13. Detect missing closing parentheses during the pre-pass for group -identification. - -14. Changed some integer variable types and put in a number of casts, following -a report of compiler warnings from Visual Studio 2013 and a few tests with -gcc's -Wconversion (which still throws up a lot). - -15. Implemented pcre2_code_copy(), and added pushcopy and #popcopy to pcre2test -for testing it. - -16. Change 66 for 10.21 introduced the use of snprintf() in PCRE2's version of -regerror(). When the error buffer is too small, my version of snprintf() puts a -binary zero in the final byte. Bug #1801 seems to show that other versions do -not do this, leading to bad output from pcre2test when it was checking for -buffer overflow. It no longer assumes a binary zero at the end of a too-small -regerror() buffer. - -17. Fixed typo ("&&" for "&") in pcre2_study(). Fortunately, this could not -actually affect anything, by sheer luck. - -18. Two minor fixes for MSVC compilation: (a) removal of apparently incorrect -"const" qualifiers in pcre2test and (b) defining snprintf as _snprintf for -older MSVC compilers. This has been done both in src/pcre2_internal.h for most -of the library, and also in src/pcre2posix.c, which no longer includes -pcre2_internal.h (see 24 below). - -19. Applied Chris Wilson's patch (Bugzilla #1681) to CMakeLists.txt for MSVC -static compilation. Subsequently applied Chris Wilson's second patch, putting -the first patch under a new option instead of being unconditional when -PCRE_STATIC is set. - -20. Updated pcre2grep to set stdout as binary when run under Windows, so as not -to convert \r\n at the ends of reflected lines into \r\r\n. This required -ensuring that other output that is written to stdout (e.g. file names) uses the -appropriate line terminator: \r\n for Windows, \n otherwise. - -21. When a line is too long for pcre2grep's internal buffer, show the maximum -length in the error message. - -22. Added support for string callouts to pcre2grep (Zoltan's patch with PH -additions). - -23. RunTest.bat was missing a "set type" line for test 22. - -24. The pcre2posix.c file was including pcre2_internal.h, and using some -"private" knowledge of the data structures. This is unnecessary; the code has -been re-factored and no longer includes pcre2_internal.h. - -25. A racing condition is fixed in JIT reported by Mozilla. - -26. Minor code refactor to avoid "array subscript is below array bounds" -compiler warning. - -27. Minor code refactor to avoid "left shift of negative number" warning. - -28. Add a bit more sanity checking to pcre2_serialize_decode() and document -that it expects trusted data. - -29. Fix typo in pcre2_jit_test.c - -30. Due to an oversight, pcre2grep was not making use of JIT when available. -This is now fixed. - -31. The RunGrepTest script is updated to use the valgrind suppressions file -when testing with JIT under valgrind (compare 10.21/51 below). The suppressions -file is updated so that is now the same as for PCRE1: it suppresses the -Memcheck warnings Addr16 and Cond in unknown objects (that is, JIT-compiled -code). Also changed smc-check=all to smc-check=all-non-file as was done for -RunTest (see 4 above). - -32. Implemented the PCRE2_NO_JIT option for pcre2_match(). - -33. Fix typo that gave a compiler error when JIT not supported. - -34. Fix comment describing the returns from find_fixedlength(). - -35. Fix potential negative index in pcre2test. - -36. Calls to pcre2_get_error_message() with error numbers that are never -returned by PCRE2 functions were returning empty strings. Now the error code -PCRE2_ERROR_BADDATA is returned. A facility has been added to pcre2test to -show the texts for given error numbers (i.e. to call pcre2_get_error_message() -and display what it returns) and a few representative error codes are now -checked in RunTest. - -37. Added "&& !defined(__INTEL_COMPILER)" to the test for __GNUC__ in -pcre2_match.c, in anticipation that this is needed for the same reason it was -recently added to pcrecpp.cc in PCRE1. - -38. Using -o with -M in pcre2grep could cause unnecessary repeated output when -the match extended over a line boundary, as it tried to find more matches "on -the same line" - but it was already over the end. - -39. Allow \C in lookbehinds and DFA matching in UTF-32 mode (by converting it -to the same code as '.' when PCRE2_DOTALL is set). - -40. Fix two clang compiler warnings in pcre2test when only one code unit width -is supported. - -41. Upgrade RunTest to automatically re-run test 2 with a large (64M) stack if -it fails when running the interpreter with a 16M stack (and if changing the -stack size via pcre2test is possible). This avoids having to manually set a -large stack size when testing with clang. - -42. Fix register overwite in JIT when SSE2 acceleration is enabled. - -43. Detect integer overflow in pcre2test pattern and data repetition counts. - -44. In pcre2test, ignore "allcaptures" after DFA matching. - -45. Fix unaligned accesses on x86. Patch by Marc Mutz. - -46. Fix some more clang compiler warnings. - - -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 (?imsxJU) 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 --------------------------- - -1. Callouts with string arguments have been added. - -2. Assertion code generator in JIT has been optimized. - -3. The invalid pattern (?(?C) has a missing assertion condition at the end. The -pcre2_compile() function read past the end of the input before diagnosing an -error. This bug was discovered by the LLVM fuzzer. - -4. Implemented pcre2_callout_enumerate(). - -5. Fix JIT compilation of conditional blocks whose assertion is converted to -(*FAIL). E.g: /(?(?!))/. - -6. The pattern /(?(?!)^)/ caused references to random memory. This bug was -discovered by the LLVM fuzzer. - -7. The assertion (?!) is optimized to (*FAIL). This was not handled correctly -when this assertion was used as a condition, for example (?(?!)a|b). In -pcre2_match() it worked by luck; in pcre2_dfa_match() it gave an incorrect -error about an unsupported item. - -8. For some types of pattern, for example /Z*(|d*){216}/, the auto- -possessification code could take exponential time to complete. A recursion -depth limit of 1000 has been imposed to limit the resources used by this -optimization. This infelicity was discovered by the LLVM fuzzer. - -9. A pattern such as /(*UTF)[\S\V\H]/, which contains a negated special class -such as \S in non-UCP mode, explicit wide characters (> 255) can be ignored -because \S ensures they are all in the class. The code for doing this was -interacting badly with the code for computing the amount of space needed to -compile the pattern, leading to a buffer overflow. This bug was discovered by -the LLVM fuzzer. - -10. A pattern such as /((?2)+)((?1))/ which has mutual recursion nested inside -other kinds of group caused stack overflow at compile time. This bug was -discovered by the LLVM fuzzer. - -11. A pattern such as /(?1)(?#?'){8}(a)/ which had a parenthesized comment -between a subroutine call and its quantifier was incorrectly compiled, leading -to buffer overflow or other errors. This bug was discovered by the LLVM fuzzer. - -12. The illegal pattern /(?(?.*!.*)?)/ was not being diagnosed as missing an -assertion after (?(. The code was failing to check the character after (?(?< -for the ! or = that would indicate a lookbehind assertion. This bug was -discovered by the LLVM fuzzer. - -13. A pattern such as /X((?2)()*+){2}+/ which has a possessive quantifier with -a fixed maximum following a group that contains a subroutine reference was -incorrectly compiled and could trigger buffer overflow. This bug was discovered -by the LLVM fuzzer. - -14. Negative relative recursive references such as (?-7) to non-existent -subpatterns were not being diagnosed and could lead to unpredictable behaviour. -This bug was discovered by the LLVM fuzzer. - -15. The bug fixed in 14 was due to an integer variable that was unsigned when -it should have been signed. Some other "int" variables, having been checked, -have either been changed to uint32_t or commented as "must be signed". - -16. A mutual recursion within a lookbehind assertion such as (?<=((?2))((?1))) -caused a stack overflow instead of the diagnosis of a non-fixed length -lookbehind assertion. This bug was discovered by the LLVM fuzzer. - -17. The use of \K in a positive lookbehind assertion in a non-anchored pattern -(e.g. /(?<=\Ka)/) could make pcre2grep loop. - -18. There was a similar problem to 17 in pcre2test for global matches, though -the code there did catch the loop. - -19. If a greedy quantified \X was preceded by \C in UTF mode (e.g. \C\X*), -and a subsequent item in the pattern caused a non-match, backtracking over the -repeated \X did not stop, but carried on past the start of the subject, causing -reference to random memory and/or a segfault. There were also some other cases -where backtracking after \C could crash. This set of bugs was discovered by the -LLVM fuzzer. - -20. The function for finding the minimum length of a matching string could take -a very long time if mutual recursion was present many times in a pattern, for -example, /((?2){73}(?2))((?1))/. A better mutual recursion detection method has -been implemented. This infelicity was discovered by the LLVM fuzzer. - -21. Implemented PCRE2_NEVER_BACKSLASH_C. - -22. The feature for string replication in pcre2test could read from freed -memory if the replication required a buffer to be extended, and it was not -working properly in 16-bit and 32-bit modes. This issue was discovered by a -fuzzer: see http://lcamtuf.coredump.cx/afl/. - -23. Added the PCRE2_ALT_CIRCUMFLEX option. - -24. Adjust the treatment of \8 and \9 to be the same as the current Perl -behaviour. - -25. Static linking against the PCRE2 library using the pkg-config module was -failing on missing pthread symbols. - -26. If a group that contained a recursive back reference also contained a -forward reference subroutine call followed by a non-forward-reference -subroutine call, for example /.((?2)(?R)\1)()/, pcre2_compile() failed to -compile correct code, leading to undefined behaviour or an internally detected -error. This bug was discovered by the LLVM fuzzer. - -27. Quantification of certain items (e.g. atomic back references) could cause -incorrect code to be compiled when recursive forward references were involved. -For example, in this pattern: /(?1)()((((((\1++))\x85)+)|))/. This bug was -discovered by the LLVM fuzzer. - -28. A repeated conditional group whose condition was a reference by name caused -a buffer overflow if there was more than one group with the given name. This -bug was discovered by the LLVM fuzzer. - -29. A recursive back reference by name within a group that had the same name as -another group caused a buffer overflow. For example: /(?J)(?'d'(?'d'\g{d}))/. -This bug was discovered by the LLVM fuzzer. - -30. A forward reference by name to a group whose number is the same as the -current group, for example in this pattern: /(?|(\k'Pm')|(?'Pm'))/, caused a -buffer overflow at compile time. This bug was discovered by the LLVM fuzzer. - -31. Fix -fsanitize=undefined warnings for left shifts of 1 by 31 (it treats 1 -as an int; fixed by writing it as 1u). - -32. Fix pcre2grep compile when -std=c99 is used with gcc, though it still gives -a warning for "fileno" unless -std=gnu99 us used. - -33. A lookbehind assertion within a set of mutually recursive subpatterns could -provoke a buffer overflow. This bug was discovered by the LLVM fuzzer. - -34. Give an error for an empty subpattern name such as (?''). - -35. Make pcre2test give an error if a pattern that follows #forbud_utf contains -\P, \p, or \X. - -36. The way named subpatterns are handled has been refactored. There is now a -pre-pass over the regex which does nothing other than identify named -subpatterns and count the total captures. This means that information about -named patterns is known before the rest of the compile. In particular, it means -that forward references can be checked as they are encountered. Previously, the -code for handling forward references was contorted and led to several errors in -computing the memory requirements for some patterns, leading to buffer -overflows. - -37. There was no check for integer overflow in subroutine calls such as (?123). - -38. The table entry for \l in EBCDIC environments was incorrect, leading to its -being treated as a literal 'l' instead of causing an error. - -39. If a non-capturing group containing a conditional group that could match -an empty string was repeated, it was not identified as matching an empty string -itself. For example: /^(?:(?(1)x|)+)+$()/. - -40. In an EBCDIC environment, pcretest was mishandling the escape sequences -\a and \e in test subject lines. - -41. In an EBCDIC environment, \a in a pattern was converted to the ASCII -instead of the EBCDIC value. - -42. The handling of \c in an EBCDIC environment has been revised so that it is -now compatible with the specification in Perl's perlebcdic page. - -43. Single character repetition in JIT has been improved. 20-30% speedup -was achieved on certain patterns. - -44. The EBCDIC character 0x41 is a non-breaking space, equivalent to 0xa0 in -ASCII/Unicode. This has now been added to the list of characters that are -recognized as white space in EBCDIC. - -45. When PCRE2 was compiled without Unicode support, the use of \p and \P gave -an error (correctly) when used outside a class, but did not give an error -within a class. - -46. \h within a class was incorrectly compiled in EBCDIC environments. - -47. JIT should return with error when the compiled pattern requires -more stack space than the maximum. - -48. Fixed a memory leak in pcre2grep when a locale is set. - - -Version 10.10 06-March-2015 ---------------------------- - -1. When a pattern is compiled, it remembers the highest back reference so that -when matching, if the ovector is too small, extra memory can be obtained to -use instead. A conditional subpattern whose condition is a check on a capture -having happened, such as, for example in the pattern /^(?:(a)|b)(?(1)A|B)/, is -another kind of back reference, but it was not setting the highest -backreference number. This mattered only if pcre2_match() was called with an -ovector that was too small to hold the capture, and there was no other kind of -back reference (a situation which is probably quite rare). The effect of the -bug was that the condition was always treated as FALSE when the capture could -not be consulted, leading to a incorrect behaviour by pcre2_match(). This bug -has been fixed. - -2. Functions for serialization and deserialization of sets of compiled patterns -have been added. - -3. The value that is returned by PCRE2_INFO_SIZE has been corrected to remove -excess code units at the end of the data block that may occasionally occur if -the code for calculating the size over-estimates. This change stops the -serialization code copying uninitialized data, to which valgrind objects. The -documentation of PCRE2_INFO_SIZE was incorrect in stating that the size did not -include the general overhead. This has been corrected. - -4. All code units in every slot in the table of group names are now set, again -in order to avoid accessing uninitialized data when serializing. - -5. The (*NO_JIT) feature is implemented. - -6. If a bug that caused pcre2_compile() to use more memory than allocated was -triggered when using valgrind, the code in (3) above passed a stupidly large -value to valgrind. This caused a crash instead of an "internal error" return. - -7. A reference to a duplicated named group (either a back reference or a test -for being set in a conditional) that occurred in a part of the pattern where -PCRE2_DUPNAMES was not set caused the amount of memory needed for the pattern -to be incorrectly calculated, leading to overwriting. - -8. A mutually recursive set of back references such as (\2)(\1) caused a -segfault at compile time (while trying to find the minimum matching length). -The infinite loop is now broken (with the minimum length unset, that is, zero). - -9. If an assertion that was used as a condition was quantified with a minimum -of zero, matching went wrong. In particular, if the whole group had unlimited -repetition and could match an empty string, a segfault was likely. The pattern -(?(?=0)?)+ is an example that caused this. Perl allows assertions to be -quantified, but not if they are being used as conditions, so the above pattern -is faulted by Perl. PCRE2 has now been changed so that it also rejects such -patterns. - -10. The error message for an invalid quantifier has been changed from "nothing -to repeat" to "quantifier does not follow a repeatable item". - -11. If a bad UTF string is compiled with NO_UTF_CHECK, it may succeed, but -scanning the compiled pattern in subsequent auto-possessification can get out -of step and lead to an unknown opcode. Previously this could have caused an -infinite loop. Now it generates an "internal error" error. This is a tidyup, -not a bug fix; passing bad UTF with NO_UTF_CHECK is documented as having an -undefined outcome. - -12. A UTF pattern containing a "not" match of a non-ASCII character and a -subroutine reference could loop at compile time. Example: /[^\xff]((?1))/. - -13. The locale test (RunTest 3) has been upgraded. It now checks that a locale -that is found in the output of "locale -a" can actually be set by pcre2test -before it is accepted. Previously, in an environment where a locale was listed -but would not set (an example does exist), the test would "pass" without -actually doing anything. Also the fr_CA locale has been added to the list of -locales that can be used. - -14. Fixed a bug in pcre2_substitute(). If a replacement string ended in a -capturing group number without parentheses, the last character was incorrectly -literally included at the end of the replacement string. - -15. A possessive capturing group such as (a)*+ with a minimum repeat of zero -failed to allow the zero-repeat case if pcre2_match() was called with an -ovector too small to capture the group. - -16. Improved error message in pcre2test when setting the stack size (-S) fails. - -17. Fixed two bugs in CMakeLists.txt: (1) Some lines had got lost in the -transfer from PCRE1, meaning that CMake configuration failed if "build tests" -was selected. (2) The file src/pcre2_serialize.c had not been added to the list -of PCRE2 sources, which caused a failure to build pcre2test. - -18. Fixed typo in pcre2_serialize.c (DECL instead of DEFN) that causes problems -only on Windows. - -19. Use binary input when reading back saved serialized patterns in pcre2test. - -20. Added RunTest.bat for running the tests under Windows. - -21. "make distclean" was not removing config.h, a file that may be created for -use with CMake. - -22. A pattern such as "((?2){0,1999}())?", which has a group containing a -forward reference repeated a large (but limited) number of times within a -repeated outer group that has a zero minimum quantifier, caused incorrect code -to be compiled, leading to the error "internal error: previously-checked -referenced subpattern not found" when an incorrect memory address was read. -This bug was reported as "heap overflow", discovered by Kai Lu of Fortinet's -FortiGuard Labs. (Added 24-March-2015: CVE-2015-2325 was given to this.) - -23. A pattern such as "((?+1)(\1))/" containing a forward reference subroutine -call within a group that also contained a recursive back reference caused -incorrect code to be compiled. This bug was reported as "heap overflow", -discovered by Kai Lu of Fortinet's FortiGuard Labs. (Added 24-March-2015: -CVE-2015-2326 was given to this.) - -24. Computing the size of the JIT read-only data in advance has been a source -of various issues, and new ones are still appear unfortunately. To fix -existing and future issues, size computation is eliminated from the code, -and replaced by on-demand memory allocation. - -25. A pattern such as /(?i)[A-`]/, where characters in the other case are -adjacent to the end of the range, and the range contained characters with more -than one other case, caused incorrect behaviour when compiled in UTF mode. In -that example, the range a-j was left out of the class. - - -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. - -The code of the library was heavily revised as part of the new API -implementation. Details of each and every modification were not individually -logged. In addition to the API changes, the following changes were made. They -are either new functionality, or bug fixes and other noticeable changes of -behaviour that were implemented after the code had been forked. - -1. Including Unicode support at build time is now enabled by default, but it -can optionally be disabled. It is not enabled by default at run time (no -change). - -2. The test program, now called pcre2test, was re-specified and almost -completely re-written. Its input is not compatible with input for pcretest. - -3. Patterns may start with (*NOTEMPTY) or (*NOTEMPTY_ATSTART) to set the -PCRE2_NOTEMPTY or PCRE2_NOTEMPTY_ATSTART options for every subject line that is -matched by that pattern. - -4. For the benefit of those who use PCRE2 via some other application, that is, -not writing the function calls themselves, it is possible to check the PCRE2 -version by matching a pattern such as /(?(VERSION>=10)yes|no)/ against a -string such as "yesno". - -5. There are case-equivalent Unicode characters whose encodings use different -numbers of code units in UTF-8. U+023A and U+2C65 are one example. (It is -theoretically possible for this to happen in UTF-16 too.) If a backreference to -a group containing one of these characters was greedily repeated, and during -the match a backtrack occurred, the subject might be backtracked by the wrong -number of code units. For example, if /^(\x{23a})\1*(.)/ is matched caselessly -(and in UTF-8 mode) against "\x{23a}\x{2c65}\x{2c65}\x{2c65}", group 2 should -capture the final character, which is the three bytes E2, B1, and A5 in UTF-8. -Incorrect backtracking meant that group 2 captured only the last two bytes. -This bug has been fixed; the new code is slower, but it is used only when the -strings matched by the repetition are not all the same length. - -6. A pattern such as /()a/ was not setting the "first character must be 'a'" -information. This applied to any pattern with a group that matched no -characters, for example: /(?:(?=.)|(?' header file. The option `-nodtk' can be used as -a workaround. If GNU CC is not installed, it is therefore recommended -to try - - ./configure CC="cc" - -and if that doesn't work, try - - ./configure CC="cc -nodtk" - - On Solaris, don't put `/usr/ucb' early in your `PATH'. This -directory contains several dysfunctional programs; working variants of -these programs are available in `/usr/bin'. So, if you need `/usr/ucb' -in your `PATH', put it _after_ `/usr/bin'. - - On Haiku, software installed for all users goes in `/boot/common', -not `/usr/local'. It is recommended to use the following options: - - ./configure --prefix=/boot/common - -Specifying the System Type -========================== - - There may be some features `configure' cannot figure out -automatically, but needs to determine by the type of machine the package -will run on. Usually, assuming the package is built to be run on the -_same_ architectures, `configure' can figure that out, but if it prints -a message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS - KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - - Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf limitation. Until the limitation is lifted, you can use -this workaround: - - CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - - `configure' recognizes the following options to control how it -operates. - -`--help' -`-h' - Print a summary of all of the options to `configure', and exit. - -`--help=short' -`--help=recursive' - Print a summary of the options unique to this package's - `configure', and exit. The `short' variant lists options used - only in the top level, while the `recursive' variant lists options - also present in any nested packages. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`--prefix=DIR' - Use DIR as the installation prefix. *note Installation Names:: - for more details, including other options available for fine-tuning - the installation locations. - -`--no-create' -`-n' - Run the configure checks, but stop before creating any output - files. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. diff --git a/pcre2-10.22/NEWS b/pcre2-10.22/NEWS deleted file mode 100644 index 602e32425..000000000 --- a/pcre2-10.22/NEWS +++ /dev/null @@ -1,110 +0,0 @@ -News about PCRE2 releases -------------------------- - -Version 10.22 29-July-2016 --------------------------- - -1. ChangeLog has the details of a number of bug fixes. - -2. The POSIX wrapper function regcomp() did not used to support back references -and subroutine calls if called with the REG_NOSUB option. It now does. - -3. A new function, pcre2_code_copy(), is added, to make a copy of a compiled -pattern. - -4. Support for string callouts is added to pcre2grep. - -5. Added the PCRE2_NO_JIT option to pcre2_match(). - -6. The pcre2_get_error_message() function now returns with a negative error -code if the error number it is given is unknown. - -7. Several updates have been made to pcre2test and test scripts (see -ChangeLog). - - -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.22/src/pcre2_compile.c b/pcre2-10.22/src/pcre2_compile.c deleted file mode 100644 index bb9736cd5..000000000 --- a/pcre2-10.22/src/pcre2_compile.c +++ /dev/null @@ -1,9081 +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) 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 - -#define NLBLOCK cb /* Block containing newline information */ -#define PSSTART start_pattern /* Field containing processed string start */ -#define PSEND end_pattern /* Field containing processed string end */ - -#include "pcre2_internal.h" - -/* In rare error cases debugging might require calling pcre2_printint(). */ - -#if 0 -#ifdef EBCDIC -#define PRINTABLE(c) ((c) >= 64 && (c) < 255) -#else -#define PRINTABLE(c) ((c) >= 32 && (c) < 127) -#endif -#include "pcre2_printint.c" -#define CALL_PRINTINT -#endif - -/* There are a few things that vary with different code unit sizes. Handle them -by defining macros in order to minimize #if usage. */ - -#if PCRE2_CODE_UNIT_WIDTH == 8 -#define STRING_UTFn_RIGHTPAR STRING_UTF8_RIGHTPAR, 5 -#define XDIGIT(c) xdigitab[c] - -#else /* Either 16-bit or 32-bit */ -#define XDIGIT(c) (MAX_255(c)? xdigitab[c] : 0xff) - -#if PCRE2_CODE_UNIT_WIDTH == 16 -#define STRING_UTFn_RIGHTPAR STRING_UTF16_RIGHTPAR, 6 - -#else /* 32-bit */ -#define STRING_UTFn_RIGHTPAR STRING_UTF32_RIGHTPAR, 6 -#endif -#endif - -/* Function definitions to allow mutual recursion */ - -static unsigned int - add_list_to_class(uint8_t *, PCRE2_UCHAR **, uint32_t, compile_block *, - const uint32_t *, unsigned int); - -static BOOL - compile_regex(uint32_t, PCRE2_UCHAR **, PCRE2_SPTR *, int *, BOOL, BOOL, - uint32_t, int, uint32_t *, int32_t *, uint32_t *, int32_t *, - branch_chain *, compile_block *, size_t *); - - - -/************************************************* -* Code parameters and static tables * -*************************************************/ - -/* 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. - -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. - -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. */ - -#define WORK_SIZE_SAFETY_MARGIN (100) - -/* This value determines the size of the initial vector that is used for -remembering named groups during the pre-compile. It is allocated on the stack, -but if it is too small, it is expanded, in a similar way to the workspace. The -value is the number of slots in the list. */ - -#define NAMED_GROUP_LIST_SIZE 20 - -/* The original PCRE required patterns to be zero-terminated, and it simplifies -the compiling code if it is guaranteed that there is a zero code unit at the -end of the pattern, because this means that tests for coding sequences such as -(*SKIP) or even just (?<= can check a sequence of code units without having to -keep checking for the end of the pattern. The new PCRE2 API allows zero code -units within patterns if a positive length is given, but in order to keep most -of the compiling code as it was, we copy such patterns and add a zero on the -end. This value determines the size of space on the stack that is used if the -pattern fits; if not, heap memory is used. */ - -#define COPIED_PATTERN_SIZE 1024 - -/* Maximum length value to check against when making sure that the variable -that holds the compiled pattern length does not overflow. We make it a bit less -than INT_MAX to allow for adding in group terminating bytes, so that we don't -have to check them every time. */ - -#define OFLOW_MAX (INT_MAX - 20) - -/* Macro for setting individual bits in class bitmaps. It took some -experimenting to figure out how to stop gcc 5.3.0 from warning with --Wconversion. This version gets a warning: - - #define SETBIT(a,b) a[(b)/8] |= (uint8_t)(1 << ((b)&7)) - -Let's hope the apparently less efficient version isn't actually so bad if the -compiler is clever with identical subexpressions. */ - -#define SETBIT(a,b) a[(b)/8] = (uint8_t)(a[(b)/8] | (1 << ((b)&7))) - -/* Private flags added to firstcu and reqcu. */ - -#define REQ_CASELESS (1 << 0) /* Indicates caselessness */ -#define REQ_VARY (1 << 1) /* reqcu followed non-literal item */ -/* Negative values for the firstcu and reqcu flags */ -#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. */ - -#define UTF_LENGTH 0x10000000l - -/* This simple test for a decimal digit works for both ASCII/Unicode and EBCDIC -and is fast (a good compiler can turn it into a subtraction and unsigned -comparison). */ - -#define IS_DIGIT(x) ((x) >= CHAR_0 && (x) <= CHAR_9) - -/* Table to identify hex digits. The tables in chartables are dependent on the -locale, and may mark arbitrary characters as digits. We want to recognize only -0-9, a-z, and A-Z as hex digits, which is why we have a private table here. It -costs 256 bytes, but it is a lot faster than doing character value tests (at -least in some simple cases I timed), and in some applications one wants PCRE to -compile efficiently as well as match efficiently. The value in the table is -the binary hex digit value, or 0xff for non-hex digits. */ - -/* This is the "normal" case, for ASCII systems, and EBCDIC systems running in -UTF-8 mode. */ - -#ifndef EBCDIC -static const uint8_t xdigitab[] = - { - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - ' */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ( - / */ - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 */ - 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff, /* 8 - ? */ - 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* @ - G */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H - O */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* P - W */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* X - _ */ - 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* ` - g */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h - o */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* p - w */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* x -127 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 128-135 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 136-143 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144-151 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 152-159 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160-167 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 168-175 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 176-183 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 192-199 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 2ff-207 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 208-215 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 216-223 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 224-231 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 232-239 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 240-247 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};/* 248-255 */ - -#else - -/* This is the "abnormal" case, for EBCDIC systems not running in UTF-8 mode. */ - -static const uint8_t xdigitab[] = - { - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 10 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 32- 39 20 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 40- 47 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 48- 55 30 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 56- 63 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - 71 40 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 72- | */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* & - 87 50 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 88- 95 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - -103 60 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 104- ? */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 112-119 70 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 120- " */ - 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* 128- g 80 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h -143 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144- p 90 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* q -159 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160- x A0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* y -175 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ^ -183 B0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */ - 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* { - G C0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H -207 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* } - P D0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Q -223 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* \ - X E0 */ - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Y -239 */ - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 F0 */ - 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff};/* 8 -255 */ -#endif /* EBCDIC */ - - -/* Table for handling alphanumeric escaped characters. Positive returns are -simple data values; negative values are for special things like \d and so on. -Zero means further processing is needed (for things like \x), or the escape is -invalid. */ - -/* This is the "normal" table for ASCII systems or for EBCDIC systems running -in UTF-8 mode. It runs from '0' to 'z'. */ - -#ifndef EBCDIC -#define ESCAPES_FIRST CHAR_0 -#define ESCAPES_LAST CHAR_z -#define UPPER_CASE(c) (c-32) - -static const short int escapes[] = { - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - CHAR_COLON, CHAR_SEMICOLON, - CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN, - CHAR_GREATER_THAN_SIGN, CHAR_QUESTION_MARK, - CHAR_COMMERCIAL_AT, -ESC_A, - -ESC_B, -ESC_C, - -ESC_D, -ESC_E, - 0, -ESC_G, - -ESC_H, 0, - 0, -ESC_K, - 0, 0, - -ESC_N, 0, - -ESC_P, -ESC_Q, - -ESC_R, -ESC_S, - 0, 0, - -ESC_V, -ESC_W, - -ESC_X, 0, - -ESC_Z, CHAR_LEFT_SQUARE_BRACKET, - CHAR_BACKSLASH, CHAR_RIGHT_SQUARE_BRACKET, - CHAR_CIRCUMFLEX_ACCENT, CHAR_UNDERSCORE, - CHAR_GRAVE_ACCENT, ESC_a, - -ESC_b, 0, - -ESC_d, ESC_e, - ESC_f, 0, - -ESC_h, 0, - 0, -ESC_k, - 0, 0, - ESC_n, 0, - -ESC_p, 0, - ESC_r, -ESC_s, - ESC_tee, 0, - -ESC_v, -ESC_w, - 0, 0, - -ESC_z -}; - -#else - -/* This is the "abnormal" table for EBCDIC systems without UTF-8 support. -It runs from 'a' to '9'. For some minimal testing of EBCDIC features, the code -is sometimes compiled on an ASCII system. In this case, we must not use CHAR_a -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 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 UPPER_CASE(c) (c-32) -#endif - -static const short int escapes[] = { -/* 80 */ ESC_a, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0, -/* 88 */-ESC_h, 0, 0, '{', 0, 0, 0, 0, -/* 90 */ 0, 0, -ESC_k, 0, 0, ESC_n, 0, -ESC_p, -/* 98 */ 0, ESC_r, 0, '}', 0, 0, 0, 0, -/* A0 */ 0, '~', -ESC_s, ESC_tee, 0,-ESC_v, -ESC_w, 0, -/* A8 */ 0,-ESC_z, 0, 0, 0, '[', 0, 0, -/* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, -/* B8 */ 0, 0, 0, 0, 0, ']', '=', '-', -/* C0 */ '{',-ESC_A, -ESC_B, -ESC_C, -ESC_D,-ESC_E, 0, -ESC_G, -/* C8 */-ESC_H, 0, 0, 0, 0, 0, 0, 0, -/* D0 */ '}', 0, -ESC_K, 0, 0,-ESC_N, 0, -ESC_P, -/* D8 */-ESC_Q,-ESC_R, 0, 0, 0, 0, 0, 0, -/* E0 */ '\\', 0, -ESC_S, 0, 0,-ESC_V, -ESC_W, -ESC_X, -/* E8 */ 0,-ESC_Z, 0, 0, 0, 0, 0, 0, -/* F0 */ 0, 0, 0, 0, 0, 0, 0, 0, -/* F8 */ 0, 0 -}; - -/* We also need a table of characters that may follow \c in an EBCDIC -environment for characters 0-31. */ - -static unsigned char ebcdic_escape_c[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; - -#endif /* EBCDIC */ - - -/* Table of special "verbs" like (*PRUNE). This is a short table, so it is -searched linearly. Put all the names into a single string, in order to reduce -the number of relocations when a shared library is dynamically linked. The -string is built from string macros so that it works in UTF-8 mode on EBCDIC -platforms. */ - -typedef struct verbitem { - int len; /* Length of verb name */ - int op; /* Op when no arg, or -1 if arg mandatory */ - int op_arg; /* Op when arg present, or -1 if not allowed */ -} verbitem; - -static const char verbnames[] = - "\0" /* Empty name is a shorthand for MARK */ - STRING_MARK0 - STRING_ACCEPT0 - STRING_COMMIT0 - STRING_F0 - STRING_FAIL0 - STRING_PRUNE0 - STRING_SKIP0 - STRING_THEN; - -static const verbitem verbs[] = { - { 0, -1, OP_MARK }, - { 4, -1, OP_MARK }, - { 6, OP_ACCEPT, -1 }, - { 6, OP_COMMIT, -1 }, - { 1, OP_FAIL, -1 }, - { 4, OP_FAIL, -1 }, - { 5, OP_PRUNE, OP_PRUNE_ARG }, - { 4, OP_SKIP, OP_SKIP_ARG }, - { 4, OP_THEN, OP_THEN_ARG } -}; - -static const int verbcount = sizeof(verbs)/sizeof(verbitem); - - -/* Substitutes for [[:<:]] and [[:>:]], which mean start and end of word in -another regex library. */ - -static const PCRE2_UCHAR sub_start_of_word[] = { - CHAR_BACKSLASH, CHAR_b, CHAR_LEFT_PARENTHESIS, CHAR_QUESTION_MARK, - CHAR_EQUALS_SIGN, CHAR_BACKSLASH, CHAR_w, CHAR_RIGHT_PARENTHESIS, '\0' }; - -static const PCRE2_UCHAR sub_end_of_word[] = { - CHAR_BACKSLASH, CHAR_b, CHAR_LEFT_PARENTHESIS, CHAR_QUESTION_MARK, - CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN, CHAR_BACKSLASH, CHAR_w, - CHAR_RIGHT_PARENTHESIS, '\0' }; - - -/* Tables of names of POSIX character classes and their lengths. The names are -now all in a single string, to reduce the number of relocations when a shared -library is dynamically loaded. The list of lengths is terminated by a zero -length entry. The first three must be alpha, lower, upper, as this is assumed -for handling case independence. The indices for graph, print, and punct are -needed, so identify them. */ - -static const char posix_names[] = - STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0 - STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0 - STRING_graph0 STRING_print0 STRING_punct0 STRING_space0 - STRING_word0 STRING_xdigit; - -static const uint8_t posix_name_lengths[] = { - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; - -#define PC_GRAPH 8 -#define PC_PRINT 9 -#define PC_PUNCT 10 - - -/* Table of class bit maps for each POSIX class. Each class is formed from a -base map, with an optional addition or removal of another map. Then, for some -classes, there is some additional tweaking: for [:blank:] the vertical space -characters are removed, and for [:alpha:] and [:alnum:] the underscore -character is removed. The triples in the table consist of the base map offset, -second map offset or -1 if no second map, and a non-negative value for map -addition or a negative value for map subtraction (if there are two maps). The -absolute value of the third field has these meanings: 0 => no tweaking, 1 => -remove vertical space characters, 2 => remove underscore. */ - -static const int posix_class_maps[] = { - cbit_word, cbit_digit, -2, /* alpha */ - cbit_lower, -1, 0, /* lower */ - cbit_upper, -1, 0, /* upper */ - cbit_word, -1, 2, /* alnum - word without underscore */ - cbit_print, cbit_cntrl, 0, /* ascii */ - cbit_space, -1, 1, /* blank - a GNU extension */ - cbit_cntrl, -1, 0, /* cntrl */ - cbit_digit, -1, 0, /* digit */ - cbit_graph, -1, 0, /* graph */ - cbit_print, -1, 0, /* print */ - cbit_punct, -1, 0, /* punct */ - cbit_space, -1, 0, /* space */ - cbit_word, -1, 0, /* word - a Perl extension */ - cbit_xdigit,-1, 0 /* xdigit */ -}; - -/* Table of substitutes for \d etc when PCRE2_UCP is set. They are replaced by -Unicode property escapes. */ - -#ifdef SUPPORT_UNICODE -static const PCRE2_UCHAR string_PNd[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_N, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pNd[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_N, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PXsp[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_s, CHAR_p, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pXsp[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_s, CHAR_p, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PXwd[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_w, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pXwd[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_w, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; - -static PCRE2_SPTR substitutes[] = { - string_PNd, /* \D */ - string_pNd, /* \d */ - string_PXsp, /* \S */ /* Xsp is Perl space, but from 8.34, Perl */ - string_pXsp, /* \s */ /* space and POSIX space are the same. */ - string_PXwd, /* \W */ - string_pXwd /* \w */ -}; - -/* The POSIX class substitutes must be in the order of the POSIX class names, -defined above, and there are both positive and negative cases. NULL means no -general substitute of a Unicode property escape (\p or \P). However, for some -POSIX classes (e.g. graph, print, punct) a special property code is compiled -directly. */ - -static const PCRE2_UCHAR string_pCc[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_C, CHAR_c, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pL[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pLl[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_l, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pLu[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_u, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_pXan[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_a, CHAR_n, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_h[] = { - CHAR_BACKSLASH, CHAR_h, '\0' }; -static const PCRE2_UCHAR string_pXps[] = { - CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_p, CHAR_s, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PCc[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_C, CHAR_c, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PL[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PLl[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_l, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PLu[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_L, CHAR_u, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_PXan[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_a, CHAR_n, CHAR_RIGHT_CURLY_BRACKET, '\0' }; -static const PCRE2_UCHAR string_H[] = { - CHAR_BACKSLASH, CHAR_H, '\0' }; -static const PCRE2_UCHAR string_PXps[] = { - CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, - CHAR_X, CHAR_p, CHAR_s, CHAR_RIGHT_CURLY_BRACKET, '\0' }; - -static PCRE2_SPTR posix_substitutes[] = { - string_pL, /* alpha */ - string_pLl, /* lower */ - string_pLu, /* upper */ - string_pXan, /* alnum */ - NULL, /* ascii */ - string_h, /* blank */ - string_pCc, /* cntrl */ - string_pNd, /* digit */ - NULL, /* graph */ - NULL, /* print */ - NULL, /* punct */ - string_pXps, /* space */ /* Xps is POSIX space, but from 8.34 */ - string_pXwd, /* word */ /* Perl and POSIX space are the same */ - NULL, /* xdigit */ - /* Negated cases */ - string_PL, /* ^alpha */ - string_PLl, /* ^lower */ - string_PLu, /* ^upper */ - string_PXan, /* ^alnum */ - NULL, /* ^ascii */ - string_H, /* ^blank */ - string_PCc, /* ^cntrl */ - string_PNd, /* ^digit */ - NULL, /* ^graph */ - NULL, /* ^print */ - NULL, /* ^punct */ - string_PXps, /* ^space */ /* Xps is POSIX space, but from 8.34 */ - string_PXwd, /* ^word */ /* Perl and POSIX space are the same */ - NULL /* ^xdigit */ -}; -#define POSIX_SUBSIZE (sizeof(posix_substitutes) / sizeof(PCRE2_UCHAR *)) -#endif /* SUPPORT_UNICODE */ - -/* Masks for checking option settings. */ - -#define PUBLIC_COMPILE_OPTIONS \ - (PCRE2_ANCHORED|PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \ - 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 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, - ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20, - ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29, ERR30, - ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40, - ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, ERR50, - 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, 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 -compatibility, (*UTFn) is supported in the relevant libraries, but (*UTF) is -generic and always supported. */ - -enum { PSO_OPT, /* Value is an option bit */ - PSO_FLG, /* Value is a flag bit */ - PSO_NL, /* Value is a newline type */ - PSO_BSR, /* Value is a \R type */ - PSO_LIMM, /* Read integer value for match limit */ - PSO_LIMR }; /* Read integer value for recursion limit */ - -typedef struct pso { - const uint8_t *name; - uint16_t length; - uint16_t type; - uint32_t value; -} pso; - -/* NB: STRING_UTFn_RIGHTPAR contains the length as well */ - -static pso pso_list[] = { - { (uint8_t *)STRING_UTFn_RIGHTPAR, PSO_OPT, PCRE2_UTF }, - { (uint8_t *)STRING_UTF_RIGHTPAR, 4, PSO_OPT, PCRE2_UTF }, - { (uint8_t *)STRING_UCP_RIGHTPAR, 4, PSO_OPT, PCRE2_UCP }, - { (uint8_t *)STRING_NOTEMPTY_RIGHTPAR, 9, PSO_FLG, PCRE2_NOTEMPTY_SET }, - { (uint8_t *)STRING_NOTEMPTY_ATSTART_RIGHTPAR, 17, PSO_FLG, PCRE2_NE_ATST_SET }, - { (uint8_t *)STRING_NO_AUTO_POSSESS_RIGHTPAR, 16, PSO_OPT, PCRE2_NO_AUTO_POSSESS }, - { (uint8_t *)STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR, 18, PSO_OPT, PCRE2_NO_DOTSTAR_ANCHOR }, - { (uint8_t *)STRING_NO_JIT_RIGHTPAR, 7, PSO_FLG, PCRE2_NOJIT }, - { (uint8_t *)STRING_NO_START_OPT_RIGHTPAR, 13, PSO_OPT, PCRE2_NO_START_OPTIMIZE }, - { (uint8_t *)STRING_LIMIT_MATCH_EQ, 12, PSO_LIMM, 0 }, - { (uint8_t *)STRING_LIMIT_RECURSION_EQ, 16, PSO_LIMR, 0 }, - { (uint8_t *)STRING_CR_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_CR }, - { (uint8_t *)STRING_LF_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_LF }, - { (uint8_t *)STRING_CRLF_RIGHTPAR, 5, PSO_NL, PCRE2_NEWLINE_CRLF }, - { (uint8_t *)STRING_ANY_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_ANY }, - { (uint8_t *)STRING_ANYCRLF_RIGHTPAR, 8, PSO_NL, PCRE2_NEWLINE_ANYCRLF }, - { (uint8_t *)STRING_BSR_ANYCRLF_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_ANYCRLF }, - { (uint8_t *)STRING_BSR_UNICODE_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_UNICODE } -}; - -/* 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 */ -}; - - - -/************************************************* -* Copy compiled code * -*************************************************/ - -/* Compiled JIT code cannot be copied, so the new compiled block has no -associated JIT data. */ - -PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION -pcre2_code_copy(const pcre2_code *code) -{ -PCRE2_SIZE* ref_count; -pcre2_code *newcode; - -if (code == NULL) return NULL; -newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data); -if (newcode == NULL) return NULL; -memcpy(newcode, code, code->blocksize); -newcode->executable_jit = NULL; - -/* If the code is one that has been deserialized, increment the reference count -in the decoded tables. */ - -if ((code->flags & PCRE2_DEREF_TABLES) != 0) - { - ref_count = (PCRE2_SIZE *)(code->tables + tables_length); - (*ref_count)++; - } - -return newcode; -} - - - -/************************************************* -* Free compiled code * -*************************************************/ - -PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION -pcre2_code_free(pcre2_code *code) -{ -PCRE2_SIZE* ref_count; - -if (code != NULL) - { - if (code->executable_jit != NULL) - PRIV(jit_free)(code->executable_jit, &code->memctl); - - if ((code->flags & PCRE2_DEREF_TABLES) != 0) - { - /* Decoded tables belong to the codes after deserialization, and they must - be freed when there are no more reference to them. The *ref_count should - always be > 0. */ - - ref_count = (PCRE2_SIZE *)(code->tables + tables_length); - if (*ref_count > 0) - { - (*ref_count)--; - if (*ref_count == 0) - code->memctl.free((void *)code->tables, code->memctl.memory_data); - } - } - - code->memctl.free(code, code->memctl.memory_data); - } -} - - - -/************************************************* -* Insert an automatic callout point * -*************************************************/ - -/* This function is called when the PCRE2_AUTO_CALLOUT option is set, to insert -callout points before each pattern item. - -Arguments: - code current code pointer - ptr current pattern pointer - cb general compile-time data - -Returns: new code pointer -*/ - -static PCRE2_UCHAR * -auto_callout(PCRE2_UCHAR *code, PCRE2_SPTR ptr, compile_block *cb) -{ -code[0] = OP_CALLOUT; -PUT(code, 1, ptr - cb->start_pattern); /* Pattern offset */ -PUT(code, 1 + LINK_SIZE, 0); /* Default length */ -code[1 + 2*LINK_SIZE] = 255; -return code + PRIV(OP_lengths)[OP_CALLOUT]; -} - - - -/************************************************* -* Complete a callout item * -*************************************************/ - -/* A callout item contains the length of the next item in the pattern, which -we can't fill in till after we have reached the relevant point. This is used -for both automatic and manual callouts. - -Arguments: - previous_callout points to previous callout item - ptr current pattern pointer - cb general compile-time data - -Returns: nothing -*/ - -static void -complete_callout(PCRE2_UCHAR *previous_callout, PCRE2_SPTR ptr, - compile_block *cb) -{ -size_t length = (size_t)(ptr - cb->start_pattern - GET(previous_callout, 1)); -PUT(previous_callout, 1 + LINK_SIZE, length); -} - - - -/************************************************* -* Find the fixed length of a branch * -*************************************************/ - -/* 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 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 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. - -Lookbehind lengths are held in 16-bit fields and the maximum value is defined -as LOOKBEHIND_MAX. - -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 mode only) - or -4 if length is too long - or -5 if regex is too complicated - or -6 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, int *countptr) -{ -uint32_t length = 0xffffffffu; /* Unset */ -uint32_t group = 0; -uint32_t groupinfo = 0; -recurse_check this_recurse; -register uint32_t 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. */ - -for (;;) - { - int d; - 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 - OP_BRA (normal non-capturing bracket) because the other variants of these - opcodes are all concerned with unlimited repeated groups, which of course - are not of fixed length. */ - - case OP_CBRA: - case OP_BRA: - case OP_ONCE: - case OP_ONCE_NC: - case OP_COND: - d = find_fixedlength(cc, utf, atend, cb, recurses, countptr); - if (d < 0) return d; - branchlength += (uint32_t)d; - do cc += GET(cc, 1); while (*cc == OP_ALT); - cc += 1 + LINK_SIZE; - break; - - /* Reached end of a branch; if it's a ket it is the end of a nested call. - If it's ALT it is an alternation in a nested call. An ACCEPT is effectively - an ALT. If it is END it's the end of the outer call. All can be handled by - the same code. Note that we must not include the OP_KETRxxx opcodes here, - because they all imply an unlimited repeat. */ - - case OP_ALT: - case OP_KET: - case OP_END: - case OP_ACCEPT: - case OP_ASSERT_ACCEPT: - if (length == 0xffffffffu) length = branchlength; - else if (length != branchlength) goto ISNOTFIXED; - if (*cc != OP_ALT) - { - if (group > 0) - { - groupinfo |= (uint32_t)(GI_SET_FIXED_LENGTH | length); - cb->groupinfo[group] = groupinfo; - } - return (int)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 FFL_LATER. */ - - case OP_RECURSE: - 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) 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) goto ISNOTFIXED; /* Mutual recursion */ - } - this_recurse.prev = recurses; - this_recurse.group = cs; - d = find_fixedlength(cs, utf, atend, cb, &this_recurse, countptr); - if (d < 0) return d; - branchlength += (uint32_t)d; - cc += 1 + LINK_SIZE; - break; - - /* Skip over assertive subpatterns. Note that we must increment cc by - 1 + LINK_SIZE at the end, not by OP_length[*cc] because in a recursive - situation this assertion may be the one that is ultimately being checked - for having a fixed length, in which case its terminating OP_KET will have - been temporarily replaced by OP_END. */ - - case OP_ASSERT: - case OP_ASSERT_NOT: - case OP_ASSERTBACK: - case OP_ASSERTBACK_NOT: - do cc += GET(cc, 1); while (*cc == OP_ALT); - cc += 1 + LINK_SIZE; - break; - - /* Skip over things that don't match chars */ - - case OP_MARK: - case OP_PRUNE_ARG: - case OP_SKIP_ARG: - case OP_THEN_ARG: - cc += cc[1] + PRIV(OP_lengths)[*cc]; - break; - - case OP_CALLOUT: - case OP_CIRC: - case OP_CIRCM: - case OP_CLOSE: - case OP_COMMIT: - case OP_CREF: - case OP_FALSE: - case OP_TRUE: - case OP_DNCREF: - case OP_DNRREF: - case OP_DOLL: - case OP_DOLLM: - case OP_EOD: - case OP_EODN: - case OP_FAIL: - case OP_NOT_WORD_BOUNDARY: - case OP_PRUNE: - case OP_REVERSE: - case OP_RREF: - case OP_SET_SOM: - case OP_SKIP: - case OP_SOD: - case OP_SOM: - case OP_THEN: - case OP_WORD_BOUNDARY: - cc += PRIV(OP_lengths)[*cc]; - break; - - case OP_CALLOUT_STR: - cc += GET(cc, 1 + 2*LINK_SIZE); - break; - - /* Handle literal characters */ - - case OP_CHAR: - case OP_CHARI: - case OP_NOT: - case OP_NOTI: - branchlength++; - cc += 2; -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); -#endif - break; - - /* Handle exact repetitions. The count is already in characters, but we - need to skip over a multibyte character in UTF8 mode. */ - - case OP_EXACT: - case OP_EXACTI: - case OP_NOTEXACT: - case OP_NOTEXACTI: - branchlength += GET2(cc,1); - cc += 2 + IMM2_SIZE; -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); -#endif - break; - - case OP_TYPEEXACT: - branchlength += GET2(cc,1); - if (cc[1 + IMM2_SIZE] == OP_PROP || cc[1 + IMM2_SIZE] == OP_NOTPROP) - cc += 2; - cc += 1 + IMM2_SIZE + 1; - break; - - /* Handle single-char matchers */ - - case OP_PROP: - case OP_NOTPROP: - cc += 2; - /* Fall through */ - - case OP_HSPACE: - case OP_VSPACE: - case OP_NOT_HSPACE: - case OP_NOT_VSPACE: - case OP_NOT_DIGIT: - case OP_DIGIT: - case OP_NOT_WHITESPACE: - case OP_WHITESPACE: - case OP_NOT_WORDCHAR: - case OP_WORDCHAR: - case OP_ANY: - case OP_ALLANY: - branchlength++; - cc++; - break; - - /* The single-byte matcher isn't allowed. This only happens in UTF-8 or - UTF-16 mode; otherwise \C is coded as OP_ALLANY. */ - - case OP_ANYBYTE: - return FFL_BACKSLASHC; - - /* Check a class for variable quantification */ - - case OP_CLASS: - case OP_NCLASS: -#ifdef SUPPORT_WIDE_CHARS - case OP_XCLASS: - /* The original code caused an unsigned overflow in 64 bit systems, - so now we use a conditional statement. */ - if (op == OP_XCLASS) - cc += GET(cc, 1); - else - cc += PRIV(OP_lengths)[OP_CLASS]; -#else - cc += PRIV(OP_lengths)[OP_CLASS]; -#endif - - switch (*cc) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - case OP_CRPOSSTAR: - case OP_CRPOSPLUS: - case OP_CRPOSQUERY: - goto ISNOTFIXED; - - case OP_CRRANGE: - case OP_CRMINRANGE: - case OP_CRPOSRANGE: - if (GET2(cc,1) != GET2(cc,1+IMM2_SIZE)) goto ISNOTFIXED; - branchlength += GET2(cc,1); - cc += 1 + 2 * IMM2_SIZE; - break; - - default: - branchlength++; - } - break; - - /* Anything else is variable length */ - - case OP_ANYNL: - case OP_BRAMINZERO: - case OP_BRAPOS: - case OP_BRAPOSZERO: - case OP_BRAZERO: - case OP_CBRAPOS: - case OP_EXTUNI: - case OP_KETRMAX: - case OP_KETRMIN: - case OP_KETRPOS: - case OP_MINPLUS: - case OP_MINPLUSI: - case OP_MINQUERY: - case OP_MINQUERYI: - case OP_MINSTAR: - case OP_MINSTARI: - case OP_MINUPTO: - case OP_MINUPTOI: - case OP_NOTMINPLUS: - case OP_NOTMINPLUSI: - case OP_NOTMINQUERY: - case OP_NOTMINQUERYI: - case OP_NOTMINSTAR: - case OP_NOTMINSTARI: - case OP_NOTMINUPTO: - case OP_NOTMINUPTOI: - case OP_NOTPLUS: - case OP_NOTPLUSI: - case OP_NOTPOSPLUS: - case OP_NOTPOSPLUSI: - case OP_NOTPOSQUERY: - case OP_NOTPOSQUERYI: - case OP_NOTPOSSTAR: - case OP_NOTPOSSTARI: - case OP_NOTPOSUPTO: - case OP_NOTPOSUPTOI: - case OP_NOTQUERY: - case OP_NOTQUERYI: - case OP_NOTSTAR: - case OP_NOTSTARI: - case OP_NOTUPTO: - case OP_NOTUPTOI: - case OP_PLUS: - case OP_PLUSI: - case OP_POSPLUS: - case OP_POSPLUSI: - case OP_POSQUERY: - case OP_POSQUERYI: - case OP_POSSTAR: - case OP_POSSTARI: - case OP_POSUPTO: - case OP_POSUPTOI: - case OP_QUERY: - case OP_QUERYI: - case OP_REF: - case OP_REFI: - case OP_DNREF: - case OP_DNREFI: - case OP_SBRA: - case OP_SBRAPOS: - case OP_SCBRA: - case OP_SCBRAPOS: - case OP_SCOND: - case OP_SKIPZERO: - case OP_STAR: - case OP_STARI: - case OP_TYPEMINPLUS: - case OP_TYPEMINQUERY: - case OP_TYPEMINSTAR: - case OP_TYPEMINUPTO: - case OP_TYPEPLUS: - case OP_TYPEPOSPLUS: - case OP_TYPEPOSQUERY: - case OP_TYPEPOSSTAR: - case OP_TYPEPOSUPTO: - case OP_TYPEQUERY: - case OP_TYPESTAR: - case OP_TYPEUPTO: - case OP_UPTO: - case OP_UPTOI: - goto ISNOTFIXED; - - /* Catch unrecognized opcodes so that when new ones are added they - are not forgotten, as has happened in the past. */ - - default: - return FFL_UNKNOWNOP; - } - } -/* Control never gets here except by goto. */ - -ISNOTFIXED: -if (group > 0) - { - groupinfo |= GI_NOT_FIXED_LENGTH; - cb->groupinfo[group] = groupinfo; - } -return FFL_NOTFIXED; -} - - - -/************************************************* -* Find first significant op code * -*************************************************/ - -/* This is called by several functions that scan a compiled expression looking -for a fixed first character, or an anchoring op code etc. It skips over things -that do not influence this. For some calls, it makes sense to skip negative -forward and all backward assertions, and also the \b assertion; for others it -does not. - -Arguments: - code pointer to the start of the group - skipassert TRUE if certain assertions are to be skipped - -Returns: pointer to the first significant opcode -*/ - -static const PCRE2_UCHAR* -first_significant_code(PCRE2_SPTR code, BOOL skipassert) -{ -for (;;) - { - switch ((int)*code) - { - case OP_ASSERT_NOT: - case OP_ASSERTBACK: - case OP_ASSERTBACK_NOT: - if (!skipassert) return code; - do code += GET(code, 1); while (*code == OP_ALT); - code += PRIV(OP_lengths)[*code]; - break; - - case OP_WORD_BOUNDARY: - case OP_NOT_WORD_BOUNDARY: - if (!skipassert) return code; - /* Fall through */ - - case OP_CALLOUT: - case OP_CREF: - case OP_DNCREF: - case OP_RREF: - case OP_DNRREF: - case OP_FALSE: - case OP_TRUE: - code += PRIV(OP_lengths)[*code]; - break; - - case OP_CALLOUT_STR: - code += GET(code, 1 + 2*LINK_SIZE); - break; - - default: - return code; - } - } -/* Control never reaches here */ -} - - - -/************************************************* -* Scan compiled branch for non-emptiness * -*************************************************/ - -/* This function scans through a branch of a compiled pattern to see whether it -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: 0 if what is matched cannot be empty - 1 if what is matched could be empty - -1 if the pattern is too complicated -*/ - -#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, 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)) - { - PCRE2_SPTR ccode; - - c = *code; - - /* Skip over forward assertions; the other assertions are skipped by - first_significant_code() with a TRUE final argument. */ - - if (c == OP_ASSERT) - { - do code += GET(code, 1); while (*code == OP_ALT); - c = *code; - continue; - } - - /* 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, endgroup; - BOOL empty_branch; - - if (!atend) goto ISTRUE; - scode = cb->start_code + GET(code, 1); - endgroup = scode; - - /* 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 */ - else - { - recurse_check *r = recurses; - for (r = recurses; r != NULL; r = r->prev) - if (r->group == scode) break; - if (r != NULL) continue; /* Mutual recursion */ - } - - /* Scan the referenced group, remembering it on the stack chain to detect - mutual recursions. */ - - empty_branch = FALSE; - this_recurse.prev = recurses; - this_recurse.group = scode; - - do - { - 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; - } - scode += GET(scode, 1); - } - while (*scode == OP_ALT); - - if (!empty_branch) goto ISFALSE; /* All branches are non-empty */ - continue; - } - - /* Groups with zero repeats can of course be empty; skip them. */ - - if (c == OP_BRAZERO || c == OP_BRAMINZERO || c == OP_SKIPZERO || - c == OP_BRAPOSZERO) - { - code += PRIV(OP_lengths)[c]; - do code += GET(code, 1); while (*code == OP_ALT); - c = *code; - continue; - } - - /* A nested group that is already marked as "could be empty" can just be - skipped. */ - - if (c == OP_SBRA || c == OP_SBRAPOS || - c == OP_SCBRA || c == OP_SCBRAPOS) - { - do code += GET(code, 1); while (*code == OP_ALT); - c = *code; - continue; - } - - /* For other groups, scan the branches. */ - - if (c == OP_BRA || c == OP_BRAPOS || - c == OP_CBRA || c == OP_CBRAPOS || - c == OP_ONCE || c == OP_ONCE_NC || - c == OP_COND || c == OP_SCOND) - { - BOOL empty_branch; - 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. - Otherwise, scan the individual branches of the group. */ - - if (c == OP_COND && code[GET(code, 1)] != OP_ALT) - code += GET(code, 1); - else - { - empty_branch = FALSE; - do - { - 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) goto ISFALSE; /* All branches are non-empty */ - } - - c = *code; - continue; - } - - /* Handle the other opcodes */ - - switch (c) - { - /* Check for quantifiers after a class. XCLASS is used for classes that - cannot be represented just by a bit map. This includes negated single - high-valued characters. The length in PRIV(OP_lengths)[] is zero; the - actual length is stored in the compiled code, so we must update "code" - here. */ - -#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 - case OP_XCLASS: - ccode = code += GET(code, 1); - goto CHECK_CLASS_REPEAT; -#endif - - case OP_CLASS: - case OP_NCLASS: - ccode = code + PRIV(OP_lengths)[OP_CLASS]; - -#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 - CHECK_CLASS_REPEAT: -#endif - - switch (*ccode) - { - case OP_CRSTAR: /* These could be empty; continue */ - case OP_CRMINSTAR: - case OP_CRQUERY: - case OP_CRMINQUERY: - case OP_CRPOSSTAR: - case OP_CRPOSQUERY: - break; - - default: /* Non-repeat => class must match */ - case OP_CRPLUS: /* These repeats aren't empty */ - case OP_CRMINPLUS: - case OP_CRPOSPLUS: - goto ISFALSE; - - case OP_CRRANGE: - case OP_CRMINRANGE: - case OP_CRPOSRANGE: - if (GET2(ccode, 1) > 0) goto ISFALSE; /* Minimum > 0 */ - break; - } - break; - - /* Opcodes that must match a character */ - - case OP_ANY: - case OP_ALLANY: - case OP_ANYBYTE: - - case OP_PROP: - case OP_NOTPROP: - case OP_ANYNL: - - case OP_NOT_HSPACE: - case OP_HSPACE: - case OP_NOT_VSPACE: - case OP_VSPACE: - case OP_EXTUNI: - - case OP_NOT_DIGIT: - case OP_DIGIT: - case OP_NOT_WHITESPACE: - case OP_WHITESPACE: - case OP_NOT_WORDCHAR: - case OP_WORDCHAR: - - case OP_CHAR: - case OP_CHARI: - case OP_NOT: - case OP_NOTI: - - case OP_PLUS: - case OP_PLUSI: - case OP_MINPLUS: - case OP_MINPLUSI: - - case OP_NOTPLUS: - case OP_NOTPLUSI: - case OP_NOTMINPLUS: - case OP_NOTMINPLUSI: - - case OP_POSPLUS: - case OP_POSPLUSI: - case OP_NOTPOSPLUS: - case OP_NOTPOSPLUSI: - - case OP_EXACT: - case OP_EXACTI: - case OP_NOTEXACT: - case OP_NOTEXACTI: - - case OP_TYPEPLUS: - case OP_TYPEMINPLUS: - case OP_TYPEPOSPLUS: - case OP_TYPEEXACT: - 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. */ - - case OP_TYPESTAR: - case OP_TYPEMINSTAR: - case OP_TYPEPOSSTAR: - case OP_TYPEQUERY: - case OP_TYPEMINQUERY: - case OP_TYPEPOSQUERY: - if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; - break; - - /* Same for these */ - - case OP_TYPEUPTO: - case OP_TYPEMINUPTO: - case OP_TYPEPOSUPTO: - if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) - code += 2; - break; - - /* End of branch */ - - case OP_KET: - case OP_KETRMAX: - case OP_KETRMIN: - case OP_KETRPOS: - case OP_ALT: - goto ISTRUE; - - /* In UTF-8 or UTF-16 mode, STAR, MINSTAR, POSSTAR, QUERY, MINQUERY, - POSQUERY, UPTO, MINUPTO, and POSUPTO and their caseless and negative - versions may be followed by a multibyte character. */ - -#ifdef MAYBE_UTF_MULTI - 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_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 (utf && HAS_EXTRALEN(code[1])) code += GET_EXTRALEN(code[1]); - break; - - 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: - if (utf && HAS_EXTRALEN(code[1 + IMM2_SIZE])) code += GET_EXTRALEN(code[1 + IMM2_SIZE]); - break; -#endif /* MAYBE_UTF_MULTI */ - - /* MARK, and PRUNE/SKIP/THEN with an argument must skip over the argument - string. */ - - case OP_MARK: - case OP_PRUNE_ARG: - case OP_SKIP_ARG: - case OP_THEN_ARG: - code += code[1]; - break; - - /* None of the remaining opcodes are required to match a character. */ - - default: - break; - } - } - -ISTRUE: -groupinfo |= GI_COULD_BE_EMPTY; - -ISFALSE: -if (group > 0) cb->groupinfo[group] = groupinfo | GI_SET_COULD_BE_EMPTY; - -return ((groupinfo & GI_COULD_BE_EMPTY) != 0)? CBE_EMPTY : CBE_NOTEMPTY; -} - - - -/************************************************* -* Check for counted repeat * -*************************************************/ - -/* This function is called when a '{' is encountered in a place where it might -start a quantifier. It looks ahead to see if it really is a quantifier, that -is, one of the forms {ddd} {ddd,} or {ddd,ddd} where the ddds are digits. - -Argument: pointer to the first char after '{' -Returns: TRUE or FALSE -*/ - -static BOOL -is_counted_repeat(PCRE2_SPTR p) -{ -if (!IS_DIGIT(*p)) return FALSE; -p++; -while (IS_DIGIT(*p)) p++; -if (*p == CHAR_RIGHT_CURLY_BRACKET) return TRUE; - -if (*p++ != CHAR_COMMA) return FALSE; -if (*p == CHAR_RIGHT_CURLY_BRACKET) return TRUE; - -if (!IS_DIGIT(*p)) return FALSE; -p++; -while (IS_DIGIT(*p)) p++; - -return (*p == CHAR_RIGHT_CURLY_BRACKET); -} - - - -/************************************************* -* Handle escapes * -*************************************************/ - -/* This function is called when a \ has been encountered. It either returns a -positive value for a simple escape such as \d, or 0 for a data character, which -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 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 - isclass TRUE if inside a character class - cb compile data block - -Returns: zero => a data character - positive => a special escape sequence - negative => a back reference - on error, errorcodeptr is set non-zero -*/ - -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; -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 */ - -/* 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. */ - -if (c < ESCAPES_FIRST || c > ESCAPES_LAST) {} /* Definitely literal */ - -else if ((i = escapes[c - ESCAPES_FIRST]) != 0) - { - if (i > 0) c = (uint32_t)i; else /* Positive is a data character */ - { - escape = -i; /* Else return a special escape */ - if (escape == ESC_P || escape == ESC_p || escape == ESC_X) - cb->external_flags |= PCRE2_HASBKPORX; /* Note \P, \p, or \X */ - } - } - -/* 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 - { - PCRE2_SPTR oldptr; - 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 - error. */ - - case CHAR_l: - case CHAR_L: - *errorcodeptr = ERR37; - break; - - /* \u is unrecognized when PCRE2_ALT_BSUX is not set. When it is treated - specially, \u must be followed by four hex digits. Otherwise it is a - lowercase u letter. */ - - case CHAR_u: - if ((options & PCRE2_ALT_BSUX) == 0) *errorcodeptr = ERR37; else - { - uint32_t xc; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ - if ((xc = XDIGIT(ptr[2])) == 0xff) break; /* Not a hex digit */ - cc = (cc << 4) | xc; - if ((xc = XDIGIT(ptr[3])) == 0xff) break; /* Not a hex digit */ - cc = (cc << 4) | xc; - if ((xc = XDIGIT(ptr[4])) == 0xff) break; /* Not a hex digit */ - c = (cc << 4) | xc; - ptr += 4; - if (utf) - { - if (c > 0x10ffffU) *errorcodeptr = ERR77; - else if (c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; - } - else if (c > MAX_NON_UTF_CHAR) *errorcodeptr = ERR77; - } - break; - - case CHAR_U: - /* \U is unrecognized unless PCRE2_ALT_BSUX is set, in which case it is an - upper case letter. */ - if ((options & PCRE2_ALT_BSUX) == 0) *errorcodeptr = ERR37; - break; - - /* In a character class, \g is just a literal "g". Outside a character - class, \g must be followed by one of a number of specific things: - - (1) A number, either plain or braced. If positive, it is an absolute - backreference. If negative, it is a relative backreference. This is a Perl - 5.10 feature. - - (2) Perl 5.10 also supports \g{name} as a reference to a named group. This - is part of Perl's movement towards a unified syntax for back references. As - this is synonymous with \k{name}, we fudge it up by pretending it really - was \k. - - (3) For Oniguruma compatibility we also support \g followed by a name or a - number either in angle brackets or in single quotes. However, these are - (possibly recursive) subroutine calls, _not_ backreferences. Just return - the ESC_g code (cf \k). */ - - case CHAR_g: - if (isclass) break; - if (ptr[1] == CHAR_LESS_THAN_SIGN || ptr[1] == CHAR_APOSTROPHE) - { - escape = ESC_g; - break; - } - - /* Handle the Perl-compatible cases */ - - if (ptr[1] == CHAR_LEFT_CURLY_BRACKET) - { - PCRE2_SPTR p; - for (p = ptr+2; *p != CHAR_NULL && *p != CHAR_RIGHT_CURLY_BRACKET; p++) - if (*p != CHAR_MINUS && !IS_DIGIT(*p)) break; - if (*p != CHAR_NULL && *p != CHAR_RIGHT_CURLY_BRACKET) - { - escape = ESC_k; - break; - } - braced = TRUE; - ptr++; - } - else braced = FALSE; - - if (ptr[1] == CHAR_MINUS) - { - negated = TRUE; - ptr++; - } - else negated = FALSE; - - /* The integer range is limited by the machine's int representation. */ - s = 0; - overflow = FALSE; - while (IS_DIGIT(ptr[1])) - { - if (s > INT_MAX / 10 - 1) /* Integer overflow */ - { - overflow = TRUE; - break; - } - s = s * 10 + (unsigned int)(*(++ptr) - CHAR_0); - } - if (overflow) /* Integer overflow */ - { - while (IS_DIGIT(ptr[1])) ptr++; - *errorcodeptr = ERR61; - break; - } - - if (braced && *(++ptr) != CHAR_RIGHT_CURLY_BRACKET) - { - *errorcodeptr = ERR57; - break; - } - - if (s == 0) - { - *errorcodeptr = ERR58; - break; - } - - if (negated) - { - if (s > cb->bracount) - { - *errorcodeptr = ERR15; - break; - } - s = cb->bracount - (s - 1); - } - - escape = -(int)s; - break; - - /* The handling of escape sequences consisting of a string of digits - starting with one that is not zero is not straightforward. Perl has changed - over the years. Nowadays \g{} for backreferences and \o{} for octal are - recommended to avoid the ambiguities in the old syntax. - - Outside a character class, the digits are read as a decimal number. If the - 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). - - Inside a character class, \ followed by a digit is always either a literal - 8 or 9 or an octal number. */ - - 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: - - if (!isclass) - { - oldptr = ptr; - /* The integer range is limited by the machine's int representation. */ - s = c - CHAR_0; - overflow = FALSE; - while (IS_DIGIT(ptr[1])) - { - if (s > INT_MAX / 10 - 1) /* Integer overflow */ - { - overflow = TRUE; - break; - } - s = s * 10 + (unsigned int)(*(++ptr) - CHAR_0); - } - if (overflow) /* Integer overflow */ - { - while (IS_DIGIT(ptr[1])) ptr++; - *errorcodeptr = ERR61; - break; - } - - /* \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 = -(int)s; /* Indicates a back reference */ - break; - } - ptr = oldptr; /* Put the pointer back and fall through */ - } - - /* Handle a digit following \ when the number is not a back reference, or - we are within a character class. If the first digit is 8 or 9, Perl used to - generate a binary zero byte and then treat the digit as a following - literal. At least by Perl 5.18 this changed so as not to insert the binary - zero. */ - - if ((c = *ptr) >= CHAR_8) break; - - /* Fall through with a digit less than 8 */ - - /* \0 always starts an octal number, but we may drop through to here with a - larger first octal digit. The original code used just to take the least - significant 8 bits of octal numbers (I think this is what early Perls used - to do). Nowadays we allow for larger numbers in UTF-8 mode and 16-bit mode, - but no more than 3 octal digits. */ - - case CHAR_0: - c -= CHAR_0; - while(i++ < 2 && ptr[1] >= CHAR_0 && ptr[1] <= CHAR_7) - c = c * 8 + *(++ptr) - CHAR_0; -#if PCRE2_CODE_UNIT_WIDTH == 8 - if (!utf && c > 0xff) *errorcodeptr = ERR51; -#endif - break; - - /* \o is a relatively new Perl feature, supporting a more general way of - specifying character codes in octal. The only supported form is \o{ddd}. */ - - case CHAR_o: - if (ptr[1] != CHAR_LEFT_CURLY_BRACKET) *errorcodeptr = ERR55; else - if (ptr[2] == CHAR_RIGHT_CURLY_BRACKET) *errorcodeptr = ERR78; else - { - ptr += 2; - c = 0; - overflow = FALSE; - while (*ptr >= CHAR_0 && *ptr <= CHAR_7) - { - cc = *ptr++; - if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */ -#if PCRE2_CODE_UNIT_WIDTH == 32 - if (c >= 0x20000000l) { overflow = TRUE; break; } -#endif - 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 - if (c > (utf ? 0x10ffffU : 0xffffU)) { overflow = TRUE; break; } -#elif PCRE2_CODE_UNIT_WIDTH == 32 - if (utf && c > 0x10ffffU) { overflow = TRUE; break; } -#endif - } - if (overflow) - { - while (*ptr >= CHAR_0 && *ptr <= CHAR_7) ptr++; - *errorcodeptr = ERR34; - } - else if (*ptr == CHAR_RIGHT_CURLY_BRACKET) - { - if (utf && c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; - } - else *errorcodeptr = ERR64; - } - break; - - /* \x is complicated. When PCRE2_ALT_BSUX is set, \x must be followed by - two hexadecimal digits. Otherwise it is a lowercase x letter. */ - - case CHAR_x: - if ((options & PCRE2_ALT_BSUX) != 0) - { - uint32_t xc; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ - if ((xc = XDIGIT(ptr[2])) == 0xff) break; /* Not a hex digit */ - c = (cc << 4) | xc; - ptr += 2; - } /* End PCRE2_ALT_BSUX handling */ - - /* Handle \x in Perl's style. \x{ddd} is a character number which can be - greater than 0xff in UTF-8 or non-8bit mode, but only if the ddd are hex - digits. If not, { used to be treated as a data character. However, Perl - seems to read hex digits up to the first non-such, and ignore the rest, so - that, for example \x{zz} matches a binary zero. This seems crazy, so PCRE - now gives an error. */ - - else - { - if (ptr[1] == CHAR_LEFT_CURLY_BRACKET) - { - ptr += 2; - if (*ptr == CHAR_RIGHT_CURLY_BRACKET) - { - *errorcodeptr = ERR78; - break; - } - c = 0; - overflow = FALSE; - - while ((cc = XDIGIT(*ptr)) != 0xff) - { - ptr++; - if (c == 0 && cc == 0) continue; /* Leading zeroes */ -#if PCRE2_CODE_UNIT_WIDTH == 32 - if (c >= 0x10000000l) { overflow = TRUE; break; } -#endif - c = (c << 4) | cc; - if ((utf && c > 0x10ffffU) || (!utf && c > MAX_NON_UTF_CHAR)) - { - overflow = TRUE; - break; - } - } - - if (overflow) - { - while (XDIGIT(*ptr) != 0xff) ptr++; - *errorcodeptr = ERR34; - } - else if (*ptr == CHAR_RIGHT_CURLY_BRACKET) - { - if (utf && c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; - } - - /* If the sequence of hex digits does not end with '}', give an error. - We used just to recognize this construct and fall through to the normal - \x handling, but nowadays Perl gives an error, which seems much more - sensible, so we do too. */ - - else *errorcodeptr = ERR67; - } /* End of \x{} processing */ - - /* Read a single-byte hex-defined char (up to two hex digits after \x) */ - - else - { - c = 0; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ - ptr++; - c = cc; - if ((cc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ - ptr++; - c = (c << 4) | cc; - } /* End of \xdd handling */ - } /* End of Perl-style \x handling */ - break; - - /* The handling of \c is different in ASCII and EBCDIC environments. In an - ASCII (or Unicode) environment, an error is given if the character - following \c is not a printable ASCII character. Otherwise, the following - character is upper-cased if it is a letter, and after that the 0x40 bit is - flipped. The result is the value of the escape. - - In an EBCDIC environment the handling of \c is compatible with the - specification in the perlebcdic document. The following character must be - a letter or one of small number of special characters. These provide a - means of defining the character values 0-31. - - For testing the EBCDIC handling of \c in an ASCII environment, recognize - the EBCDIC value of 'c' explicitly. */ - -#if defined EBCDIC && 'a' != 0x81 - case 0x83: -#else - case CHAR_c: -#endif - - c = *(++ptr); - if (c >= CHAR_a && c <= CHAR_z) c = UPPER_CASE(c); - if (c == CHAR_NULL && ptr >= ptrend) - { - *errorcodeptr = ERR2; - break; - } - - /* Handle \c in an ASCII/Unicode environment. */ - -#ifndef EBCDIC /* ASCII/UTF-8 coding */ - if (c < 32 || c > 126) /* Excludes all non-printable ASCII */ - { - *errorcodeptr = ERR68; - break; - } - c ^= 0x40; - - /* Handle \c in an EBCDIC environment. The special case \c? is converted to - 255 (0xff) or 95 (0x5f) if other character suggest we are using th POSIX-BC - encoding. (This is the way Perl indicates that it handles \c?.) The other - valid sequences correspond to a list of specific characters. */ - -#else - if (c == CHAR_QUESTION_MARK) - c = ('\\' == 188 && '`' == 74)? 0x5f : 0xff; - else - { - for (i = 0; i < 32; i++) - { - if (c == ebcdic_escape_c[i]) break; - } - if (i < 32) c = i; else *errorcodeptr = ERR68; - } -#endif /* EBCDIC */ - - break; - - /* Any other alphanumeric following \ is an error. Perl gives an error only - if in warning mode, but PCRE doesn't have a warning mode. */ - - default: - *errorcodeptr = ERR3; - break; - } - } - -/* Perl supports \N{name} for character names, as well as plain \N for "not -newline". PCRE does not support \N{name}. However, it does support -quantification such as \N{2,3}. */ - -if (escape == ESC_N && ptr[1] == CHAR_LEFT_CURLY_BRACKET && - !is_counted_repeat(ptr+2)) - *errorcodeptr = ERR37; - -/* If PCRE2_UCP is set, we change the values for \d etc. */ - -if ((options & PCRE2_UCP) != 0 && escape >= ESC_D && escape <= ESC_w) - escape += (ESC_DU - ESC_D); - -/* Set the pointer to the final character before returning. */ - -*ptrptr = ptr; -*chptr = c; -return escape; -} - - - -#ifdef SUPPORT_UNICODE -/************************************************* -* Handle \P and \p * -*************************************************/ - -/* This function is called after \P or \p has been encountered, provided that -PCRE2 is compiled with support for UTF and Unicode properties. On entry, the -contents of ptrptr are pointing at the P or p. On exit, it is left pointing at -the final code unit of the escape sequence. - -Arguments: - ptrptr the pattern position pointer - negptr a boolean that is set TRUE for negation else FALSE - ptypeptr an unsigned int that is set to the type value - pdataptr an unsigned int that is set to the detailed property value - errorcodeptr the error code variable - cb the compile data - -Returns: TRUE if the type value was found, or FALSE for an invalid type -*/ - -static BOOL -get_ucp(PCRE2_SPTR *ptrptr, BOOL *negptr, unsigned int *ptypeptr, - unsigned int *pdataptr, int *errorcodeptr, compile_block *cb) -{ -register PCRE2_UCHAR c; -size_t i, bot, top; -PCRE2_SPTR ptr = *ptrptr; -PCRE2_UCHAR name[32]; - -*negptr = FALSE; -c = *(++ptr); - -/* \P or \p can be followed by a name in {}, optionally preceded by ^ for -negation. */ - -if (c == CHAR_LEFT_CURLY_BRACKET) - { - if (ptr[1] == CHAR_CIRCUMFLEX_ACCENT) - { - *negptr = TRUE; - ptr++; - } - for (i = 0; i < (int)(sizeof(name) / sizeof(PCRE2_UCHAR)) - 1; i++) - { - c = *(++ptr); - if (c == CHAR_NULL) goto ERROR_RETURN; - if (c == CHAR_RIGHT_CURLY_BRACKET) break; - name[i] = c; - } - if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN; - name[i] = 0; - } - -/* Otherwise there is just one following character, which must be an ASCII -letter. */ - -else if (MAX_255(c) && (cb->ctypes[c] & ctype_letter) != 0) - { - name[0] = c; - name[1] = 0; - } -else goto ERROR_RETURN; - -*ptrptr = ptr; - -/* Search for a recognized property name using binary chop. */ - -bot = 0; -top = PRIV(utt_size); - -while (bot < top) - { - int r; - i = (bot + top) >> 1; - r = PRIV(strcmp_c8)(name, PRIV(utt_names) + PRIV(utt)[i].name_offset); - if (r == 0) - { - *ptypeptr = PRIV(utt)[i].type; - *pdataptr = PRIV(utt)[i].value; - return TRUE; - } - if (r > 0) bot = i + 1; else top = i; - } -*errorcodeptr = ERR47; /* Unrecognized name */ -return FALSE; - -ERROR_RETURN: /* Malformed \P or \p */ -*errorcodeptr = ERR46; -*ptrptr = ptr; -return FALSE; -} -#endif - - - -/************************************************* -* Read repeat counts * -*************************************************/ - -/* Read an item of the form {n,m} and return the values. This is called only -after is_counted_repeat() has confirmed that a repeat-count quantifier exists, -so the syntax is guaranteed to be correct, but we need to check the values. - -Arguments: - p pointer to first char after '{' - minp pointer to int for min - maxp pointer to int for max - returned as -1 if no max - errorcodeptr points to error code variable - -Returns: pointer to '}' on success; - current ptr on error, with errorcodeptr set non-zero -*/ - -static PCRE2_SPTR -read_repeat_counts(PCRE2_SPTR p, int *minp, int *maxp, int *errorcodeptr) -{ -int min = 0; -int max = -1; - -while (IS_DIGIT(*p)) - { - min = min * 10 + (int)(*p++ - CHAR_0); - if (min > 65535) - { - *errorcodeptr = ERR5; - return p; - } - } - -if (*p == CHAR_RIGHT_CURLY_BRACKET) max = min; else - { - if (*(++p) != CHAR_RIGHT_CURLY_BRACKET) - { - max = 0; - while(IS_DIGIT(*p)) - { - max = max * 10 + (int)(*p++ - CHAR_0); - if (max > 65535) - { - *errorcodeptr = ERR5; - return p; - } - } - if (max < min) - { - *errorcodeptr = ERR4; - return p; - } - } - } - -*minp = min; -*maxp = max; -return p; -} - - - -/************************************************* -* Scan compiled regex for recursion reference * -*************************************************/ - -/* This function scans through a compiled pattern until it finds an instance of -OP_RECURSE. - -Arguments: - code points to start of expression - utf TRUE in UTF mode - -Returns: pointer to the opcode for OP_RECURSE, or NULL if not found -*/ - -static PCRE2_SPTR -find_recurse(PCRE2_SPTR code, BOOL utf) -{ -for (;;) - { - register PCRE2_UCHAR c = *code; - if (c == OP_END) return NULL; - if (c == OP_RECURSE) return code; - - /* 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); - - /* 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_TYPEPOSUPTO: - case OP_TYPEUPTO: - case OP_TYPEMINUPTO: - case OP_TYPEEXACT: - 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-unit character. The length in the table is a - minimum, so we have to arrange to skip the extra units. */ - -#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 */ - } - } -} - - - -/************************************************* -* Check for POSIX class syntax * -*************************************************/ - -/* This function is called when the sequence "[:" or "[." or "[=" is -encountered in a character class. It checks whether this is followed by a -sequence of characters terminated by a matching ":]" or ".]" or "=]". If we -reach an unescaped ']' without the special preceding character, return FALSE. - -Originally, this function only recognized a sequence of letters between the -terminators, but it seems that Perl recognizes any sequence of characters, -though of course unknown POSIX names are subsequently rejected. Perl gives an -"Unknown POSIX class" error for [:f\oo:] for example, where previously PCRE -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 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. 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 -[:a[:abc]b][b:] it gives unknown POSIX class "[:abc]b][b:]", which does not -seem right at all. PCRE does not allow closing square brackets in POSIX class -names. - -Arguments: - ptr pointer to the initial [ - endptr where to return a pointer to the terminating ':', '.', or '=' - -Returns: TRUE or FALSE -*/ - -static BOOL -check_posix_syntax(PCRE2_SPTR ptr, PCRE2_SPTR *endptr) -{ -PCRE2_UCHAR terminator; /* Don't combine these lines; the Solaris cc */ -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[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) - { - *endptr = ptr; - return TRUE; - } - } - -return FALSE; -} - - - -/************************************************* -* Check POSIX class name * -*************************************************/ - -/* This function is called to check the name given in a POSIX-style class entry -such as [:alnum:]. - -Arguments: - ptr points to the first letter - len the length of the name - -Returns: a value representing the name, or -1 if unknown -*/ - -static int -check_posix_name(PCRE2_SPTR ptr, int len) -{ -const char *pn = posix_names; -register int yield = 0; -while (posix_name_lengths[yield] != 0) - { - if (len == posix_name_lengths[yield] && - PRIV(strncmp_c8)(ptr, pn, (unsigned int)len) == 0) return yield; - pn += posix_name_lengths[yield] + 1; - yield++; - } -return -1; -} - - - -#ifdef SUPPORT_UNICODE -/************************************************* -* Get othercase range * -*************************************************/ - -/* This function is passed the start and end of a class range in UCT mode. It -searches up the characters, looking for ranges of characters in the "other" -case. Each call returns the next one, updating the start address. A character -with multiple other cases is returned on its own with a special return value. - -Arguments: - cptr points to starting character value; updated - d end value - ocptr where to put start of othercase range - odptr where to put end of othercase range - -Yield: -1 when no more - 0 when a range is returned - >0 the CASESET offset for char with multiple other cases - in this case, ocptr contains the original -*/ - -static int -get_othercase_range(uint32_t *cptr, uint32_t d, uint32_t *ocptr, - uint32_t *odptr) -{ -uint32_t c, othercase, next; -unsigned int co; - -/* Find the first character that has an other case. If it has multiple other -cases, return its case offset value. */ - -for (c = *cptr; c <= d; c++) - { - if ((co = UCD_CASESET(c)) != 0) - { - *ocptr = c++; /* Character that has the set */ - *cptr = c; /* Rest of input range */ - return (int)co; - } - if ((othercase = UCD_OTHERCASE(c)) != c) break; - } - -if (c > d) return -1; /* Reached end of range */ - -/* Found a character that has a single other case. Search for the end of the -range, which is either the end of the input range, or a character that has zero -or more than one other cases. */ - -*ocptr = othercase; -next = othercase + 1; - -for (++c; c <= d; c++) - { - if ((co = UCD_CASESET(c)) != 0 || UCD_OTHERCASE(c) != next) break; - next++; - } - -*odptr = next - 1; /* End of othercase range */ -*cptr = c; /* Rest of input range */ -return 0; -} -#endif /* SUPPORT_UNICODE */ - - - -/************************************************* -* Add a character or range to a class * -*************************************************/ - -/* This function packages up the logic of adding a character or range of -characters to a class. The character values in the arguments will be within the -valid values for the current mode (8-bit, 16-bit, UTF, etc). This function is -mutually recursive with the function immediately below. - -Arguments: - classbits the bit map for characters < 256 - uchardptr points to the pointer for extra data - options the options word - cb compile data - start start of range character - end end of range character - -Returns: the number of < 256 characters added - the pointer to extra data is updated -*/ - -static unsigned int -add_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, - compile_block *cb, uint32_t start, uint32_t end) -{ -uint32_t c; -uint32_t classbits_end = (end <= 0xff ? end : 0xff); -unsigned int n8 = 0; - -/* If caseless matching is required, scan the range and process alternate -cases. In Unicode, there are 8-bit characters that have alternate cases that -are greater than 255 and vice-versa. Sometimes we can just extend the original -range. */ - -if ((options & PCRE2_CASELESS) != 0) - { -#ifdef SUPPORT_UNICODE - if ((options & PCRE2_UTF) != 0) - { - int rc; - uint32_t oc, od; - - options &= ~PCRE2_CASELESS; /* Remove for recursive calls */ - c = start; - - while ((rc = get_othercase_range(&c, end, &oc, &od)) >= 0) - { - /* Handle a single character that has more than one other case. */ - - if (rc > 0) n8 += add_list_to_class(classbits, uchardptr, options, cb, - PRIV(ucd_caseless_sets) + rc, oc); - - /* Do nothing if the other case range is within the original range. */ - - else if (oc >= start && od <= end) continue; - - /* Extend the original range if there is overlap, noting that if oc < c, we - can't have od > end because a subrange is always shorter than the basic - range. Otherwise, use a recursive call to add the additional range. */ - - else if (oc < start && od >= start - 1) start = oc; /* Extend downwards */ - else if (od > end && oc <= end + 1) - { - end = od; /* Extend upwards */ - if (end > classbits_end) classbits_end = (end <= 0xff ? end : 0xff); - } - else n8 += add_to_class(classbits, uchardptr, options, cb, oc, od); - } - } - else -#endif /* SUPPORT_UNICODE */ - - /* Not UTF mode */ - - for (c = start; c <= classbits_end; c++) - { - SETBIT(classbits, cb->fcc[c]); - n8++; - } - } - -/* Now handle the original range. Adjust the final value according to the bit -length - this means that the same lists of (e.g.) horizontal spaces can be used -in all cases. */ - -if ((options & PCRE2_UTF) == 0 && end > MAX_NON_UTF_CHAR) - end = MAX_NON_UTF_CHAR; - -/* Use the bitmap for characters < 256. Otherwise use extra data.*/ - -for (c = start; c <= classbits_end; c++) - { - /* Regardless of start, c will always be <= 255. */ - SETBIT(classbits, c); - n8++; - } - -#ifdef SUPPORT_WIDE_CHARS -if (start <= 0xff) start = 0xff + 1; - -if (end >= start) - { - PCRE2_UCHAR *uchardata = *uchardptr; - -#ifdef SUPPORT_UNICODE - if ((options & PCRE2_UTF) != 0) - { - if (start < end) - { - *uchardata++ = XCL_RANGE; - uchardata += PRIV(ord2utf)(start, uchardata); - uchardata += PRIV(ord2utf)(end, uchardata); - } - else if (start == end) - { - *uchardata++ = XCL_SINGLE; - uchardata += PRIV(ord2utf)(start, uchardata); - } - } - else -#endif /* SUPPORT_UNICODE */ - - /* Without UTF support, character values are constrained by the bit length, - and can only be > 256 for 16-bit and 32-bit libraries. */ - -#if PCRE2_CODE_UNIT_WIDTH == 8 - {} -#else - if (start < end) - { - *uchardata++ = XCL_RANGE; - *uchardata++ = start; - *uchardata++ = end; - } - else if (start == end) - { - *uchardata++ = XCL_SINGLE; - *uchardata++ = start; - } -#endif - *uchardptr = uchardata; /* Updata extra data pointer */ - } -#else - (void)uchardptr; /* Avoid compiler warning */ -#endif /* SUPPORT_WIDE_CHARS */ - -return n8; /* Number of 8-bit characters */ -} - - - -/************************************************* -* Add a list of characters to a class * -*************************************************/ - -/* This function is used for adding a list of case-equivalent characters to a -class, and also for adding a list of horizontal or vertical whitespace. If the -list is in order (which it should be), ranges of characters are detected and -handled appropriately. This function is mutually recursive with the function -above. - -Arguments: - classbits the bit map for characters < 256 - uchardptr points to the pointer for extra data - options the options word - cb contains pointers to tables etc. - p points to row of 32-bit values, terminated by NOTACHAR - except character to omit; this is used when adding lists of - case-equivalent characters to avoid including the one we - already know about - -Returns: the number of < 256 characters added - the pointer to extra data is updated -*/ - -static unsigned int -add_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, - compile_block *cb, const uint32_t *p, unsigned int except) -{ -unsigned int n8 = 0; -while (p[0] < NOTACHAR) - { - unsigned int n = 0; - if (p[0] != except) - { - while(p[n+1] == p[0] + n + 1) n++; - n8 += add_to_class(classbits, uchardptr, options, cb, p[0], p[n]); - } - p += n + 1; - } -return n8; -} - - - -/************************************************* -* Add characters not in a list to a class * -*************************************************/ - -/* This function is used for adding the complement of a list of horizontal or -vertical whitespace to a class. The list must be in order. - -Arguments: - classbits the bit map for characters < 256 - uchardptr points to the pointer for extra data - options the options word - cb contains pointers to tables etc. - p points to row of 32-bit values, terminated by NOTACHAR - -Returns: the number of < 256 characters added - the pointer to extra data is updated -*/ - -static unsigned int -add_not_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, - uint32_t options, compile_block *cb, const uint32_t *p) -{ -BOOL utf = (options & PCRE2_UTF) != 0; -unsigned int n8 = 0; -if (p[0] > 0) - n8 += add_to_class(classbits, uchardptr, options, cb, 0, p[0] - 1); -while (p[0] < NOTACHAR) - { - while (p[1] == p[0] + 1) p++; - n8 += add_to_class(classbits, uchardptr, options, cb, p[0] + 1, - (p[1] == NOTACHAR) ? (utf ? 0x10ffffu : 0xffffffffu) : p[1] - 1); - p++; - } -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++ = (PCRE2_UCHAR)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 * -*************************************************/ - -/* This function is called first of all, to scan for named capturing groups so -that information about them is fully available to both the compiling scans. -It skips over everything except parenthesized items. - -Arguments: - ptrptr points to pointer to the start of the pattern - options compiling dynamic options - cb pointer to the compile data block - -Returns: zero on success or a non-zero error code, with pointer updated -*/ - -typedef struct nest_save { - uint16_t nest_depth; - uint16_t reset_group; - uint16_t max_group; - uint16_t flags; -} nest_save; - -#define NSF_RESET 0x0001u -#define NSF_EXTENDED 0x0002u -#define NSF_DUPNAMES 0x0004u - -static int scan_for_captures(PCRE2_SPTR *ptrptr, uint32_t options, - compile_block *cb) -{ -uint32_t c; -uint32_t delimiter; -uint32_t set, unset, *optset; -uint32_t skiptoket = 0; -uint16_t nest_depth = 0; -int errorcode = 0; -int escape; -int namelen; -int i; -BOOL inescq = FALSE; -BOOL isdupname; -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 != 0) - { - if (c != CHAR_RIGHT_PARENTHESIS) continue; - skiptoket = 0; - } - - /* Skip over literals */ - - if (inescq) - { - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) - { - inescq = FALSE; - ptr++; - } - continue; - } - - /* Skip over # comments and whitespace in extended mode. */ - - if ((options & PCRE2_EXTENDED) != 0) - { - PCRE2_SPTR wscptr = ptr; - while (MAX_255(c) && (cb->ctypes[c] & ctype_space) != 0) c = *(++ptr); - if (c == CHAR_NUMBER_SIGN) - { - ptr++; - while (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 skipped any characters, restart the loop. Otherwise, we didn't see - a comment. */ - - if (ptr > wscptr) - { - ptr--; - continue; - } - } - - /* Process the next pattern item. */ - - switch(c) - { - default: /* Most characters are just skipped */ - break; - - /* Skip escapes except for \Q */ - - case CHAR_BACKSLASH: - errorcode = 0; - 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; - - /* Skip a character class. The syntax is complicated so we have to - replicate some of what happens when a class is processed for real. */ - - case CHAR_LEFT_SQUARE_BRACKET: - if (PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_STARTWORD, 6) == 0 || - PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_ENDWORD, 6) == 0) - { - ptr += 6; - break; - } - - /* If the first character is '^', set the negation flag (not actually used - here, except to recognize only one ^) and skip it. If the first few - characters (either before or after ^) are \Q\E or \E we skip them too. This - makes for compatibility with Perl. */ - - negate_class = FALSE; - for (;;) - { - c = *(++ptr); /* First character in class */ - if (c == CHAR_BACKSLASH) - { - if (ptr[1] == CHAR_E) - ptr++; - else if (PRIV(strncmp_c8)(ptr + 1, STR_Q STR_BACKSLASH STR_E, 3) == 0) - ptr += 3; - else - break; - } - else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) - negate_class = TRUE; - else break; - } - - if (c == CHAR_RIGHT_SQUARE_BRACKET && - (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0) - break; - - /* Loop for the contents of the class */ - - for (;;) - { - PCRE2_SPTR tempptr; - - if (c == CHAR_NULL && ptr >= cb->end_pattern) - { - errorcode = ERR6; /* Missing terminating ']' */ - goto FAILED; - } - -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(c)) - { /* Braces are required because the */ - GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */ - } -#endif - - /* Inside \Q...\E everything is literal except \E */ - - if (inescq) - { - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) /* If we are at \E */ - { - inescq = FALSE; /* Reset literal state */ - ptr++; /* Skip the 'E' */ - } - goto CONTINUE_CLASS; - } - - /* 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, &tempptr)) - { - ptr = tempptr + 1; - } - else if (c == CHAR_BACKSLASH) - { - errorcode = 0; - escape = PRIV(check_escape)(&ptr, cb->end_pattern, &c, &errorcode, - options, TRUE, cb); - if (errorcode != 0) goto FAILED; - if (escape == ESC_Q) inescq = TRUE; - } - - CONTINUE_CLASS: - c = *(++ptr); - if (c == CHAR_RIGHT_SQUARE_BRACKET && !inescq) break; - } /* End of class-processing loop */ - break; - - /* This is the real work of this function - handling parentheses. */ - - case CHAR_LEFT_PARENTHESIS: - nest_depth++; - - if (ptr[1] != CHAR_QUESTION_MARK) - { - if (ptr[1] != CHAR_ASTERISK) - { - if ((options & PCRE2_NO_AUTO_CAPTURE) == 0) cb->bracount++; - } - - /* (*something) - skip over a name, and then just skip to closing ket - unless PCRE2_ALT_VERBNAMES is set, in which case we have to process - escapes in the string after a verb name terminated by a colon. */ - - else - { - ptr += 2; - while (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0) ptr++; - if (*ptr == CHAR_COLON && (options & PCRE2_ALT_VERBNAMES) != 0) - { - ptr++; - 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--; - } - } - - /* Handle (?...) groups */ - - else switch(ptr[2]) - { - default: - ptr += 2; - 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 = ptr[0]; - break; - } - - /* Handle (?| and (?imsxJU: which are the only other valid forms. Both - need a new block on the nest stack. */ - - if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); - else if (++top_nest >= end_nests) - { - errorcode = ERR84; - goto FAILED; - } - top_nest->nest_depth = nest_depth; - top_nest->flags = 0; - if ((options & PCRE2_EXTENDED) != 0) top_nest->flags |= NSF_EXTENDED; - if ((options & PCRE2_DUPNAMES) != 0) top_nest->flags |= NSF_DUPNAMES; - - if (*ptr == CHAR_VERTICAL_LINE) - { - top_nest->reset_group = (uint16_t)cb->bracount; - top_nest->max_group = (uint16_t)cb->bracount; - top_nest->flags |= NSF_RESET; - cb->external_flags |= PCRE2_DUPCAPUSED; - break; - } - - /* Scan options */ - - top_nest->reset_group = 0; - top_nest->max_group = 0; - - set = unset = 0; - optset = &set; - - /* Need only track (?x: and (?J: at this stage */ - - while (*ptr != CHAR_RIGHT_PARENTHESIS && *ptr != CHAR_COLON) - { - switch (*ptr++) - { - case CHAR_MINUS: optset = &unset; break; - - case CHAR_x: *optset |= PCRE2_EXTENDED; break; - - case CHAR_J: - *optset |= PCRE2_DUPNAMES; - cb->external_flags |= PCRE2_JCHANGED; - break; - - case CHAR_i: - case CHAR_m: - case CHAR_s: - case CHAR_U: - break; - - default: - errorcode = ERR11; - ptr--; /* Correct the offset */ - goto FAILED; - } - } - - options = (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 the - previous level set up a nest block, discard the one we have just created. - Otherwise adjust it for the previous level. */ - - if (*ptr == CHAR_RIGHT_PARENTHESIS) - { - nest_depth--; - if (top_nest > (nest_save *)(cb->start_workspace) && - (top_nest-1)->nest_depth == nest_depth) top_nest --; - else top_nest->nest_depth = nest_depth; - } - break; - - /* 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])) - { - 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: - 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: - case CHAR_EQUALS_SIGN: - case CHAR_EXCLAMATION_MARK: - case CHAR_AMPERSAND: - case CHAR_PLUS: - ptr += 2; - break; - - case CHAR_P: - if (ptr[3] != CHAR_LESS_THAN_SIGN) - { - ptr += 3; - break; - } - ptr++; - c = CHAR_GREATER_THAN_SIGN; /* Terminator */ - goto DEFINE_NAME; - - case CHAR_LESS_THAN_SIGN: - if (ptr[3] == CHAR_EQUALS_SIGN || ptr[3] == CHAR_EXCLAMATION_MARK) - { - ptr += 3; - break; - } - c = CHAR_GREATER_THAN_SIGN; /* Terminator */ - goto DEFINE_NAME; - - case CHAR_APOSTROPHE: - c = CHAR_APOSTROPHE; /* Terminator */ - - DEFINE_NAME: - name = ptr = ptr + 3; - - if (*ptr == c) /* Empty name */ - { - errorcode = ERR62; - goto FAILED; - } - - if (IS_DIGIT(*ptr)) - { - errorcode = ERR44; /* Group name must start with non-digit */ - goto FAILED; - } - - if (MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) == 0) - { - errorcode = ERR24; - goto FAILED; - } - - /* Advance ptr, set namelen and check its length. */ - READ_NAME(ctype_word, ERR48, errorcode); - - if (*ptr != c) - { - errorcode = ERR42; - goto FAILED; - } - - if (cb->names_found >= MAX_NAME_COUNT) - { - errorcode = ERR49; - goto FAILED; - } - - if (namelen + IMM2_SIZE + 1 > cb->name_entry_size) - cb->name_entry_size = (uint16_t)(namelen + IMM2_SIZE + 1); - - /* We have a valid name for this capturing group. */ - - cb->bracount++; - - /* Scan the list to check for duplicates. For duplicate names, if the - number is the same, break the loop, which causes the name to be - discarded; otherwise, if DUPNAMES is not set, give an error. - If it is set, allow the name with a different number, but continue - scanning in case this is a duplicate with the same number. For - non-duplicate names, give an error if the number is duplicated. */ - - isdupname = FALSE; - ng = cb->named_groups; - for (i = 0; i < cb->names_found; i++, ng++) - { - if (namelen == ng->length && - PRIV(strncmp)(name, ng->name, (size_t)namelen) == 0) - { - if (ng->number == cb->bracount) break; - if ((options & PCRE2_DUPNAMES) == 0) - { - errorcode = ERR43; - goto FAILED; - } - isdupname = ng->isdup = TRUE; /* Mark as a duplicate */ - cb->dupnames = TRUE; /* Duplicate names exist */ - } - else if (ng->number == cb->bracount) - { - errorcode = ERR65; - goto FAILED; - } - } - - if (i < cb->names_found) break; /* Ignore duplicate with same number */ - - /* Increase the list size if necessary */ - - if (cb->names_found >= cb->named_group_list_size) - { - uint32_t newsize = cb->named_group_list_size * 2; - named_group *newspace = - cb->cx->memctl.malloc(newsize * sizeof(named_group), - cb->cx->memctl.memory_data); - if (newspace == NULL) - { - errorcode = ERR21; - goto FAILED; - } - - memcpy(newspace, cb->named_groups, - cb->named_group_list_size * sizeof(named_group)); - if (cb->named_group_list_size > NAMED_GROUP_LIST_SIZE) - cb->cx->memctl.free((void *)cb->named_groups, - cb->cx->memctl.memory_data); - cb->named_groups = newspace; - cb->named_group_list_size = newsize; - } - - /* Add this name to the list */ - - cb->named_groups[cb->names_found].name = name; - cb->named_groups[cb->names_found].length = (uint16_t)namelen; - cb->named_groups[cb->names_found].number = cb->bracount; - cb->named_groups[cb->names_found].isdup = (uint16_t)isdupname; - cb->names_found++; - break; - } /* End of (? switch */ - break; /* End of ( handling */ - - /* At an alternation, reset the capture count if we are in a (?| group. */ - - case CHAR_VERTICAL_LINE: - if (top_nest != NULL && top_nest->nest_depth == nest_depth && - (top_nest->flags & NSF_RESET) != 0) - { - if (cb->bracount > top_nest->max_group) - top_nest->max_group = (uint16_t)cb->bracount; - cb->bracount = top_nest->reset_group; - } - break; - - /* At a right parenthesis, reset the capture count to the maximum if we - are in a (?| group and/or reset the extended option. */ - - case CHAR_RIGHT_PARENTHESIS: - if (top_nest != NULL && top_nest->nest_depth == nest_depth) - { - if ((top_nest->flags & NSF_RESET) != 0 && - top_nest->max_group > cb->bracount) - cb->bracount = top_nest->max_group; - if ((top_nest->flags & NSF_EXTENDED) != 0) options |= PCRE2_EXTENDED; - else options &= ~PCRE2_EXTENDED; - if ((top_nest->flags & NSF_DUPNAMES) != 0) options |= PCRE2_DUPNAMES; - else options &= ~PCRE2_DUPNAMES; - if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL; - else top_nest--; - } - if (nest_depth == 0) /* Unmatched closing parenthesis */ - { - errorcode = ERR22; - goto FAILED; - } - nest_depth--; - break; - } - } - -if (nest_depth == 0) - { - cb->final_bracount = cb->bracount; - return 0; - } - -/* We give a special error for a missing closing parentheses after (?# because -it might otherwise be hard to see where the missing character is. */ - -errorcode = (skiptoket == CHAR_NUMBER_SIGN)? ERR18 : ERR14; - -FAILED: -*ptrptr = ptr; -return errorcode; -} - - - -/************************************************* -* Compile one branch * -*************************************************/ - -/* Scan the pattern, compiling it into the a vector. If the options are -changed during the branch, the pointer is used to change the external options -bits. This function is used during the pre-compile phase when we are trying -to find out the amount of memory needed, as well as during the real compile -phase. The value of lengthptr distinguishes the two phases. - -Arguments: - optionsptr pointer to the option bits - codeptr points to the pointer to the current code point - ptrptr points to the current pattern pointer - errorcodeptr points to error code variable - firstcuptr place to put the first required code unit - firstcuflagsptr place to put the first code unit flags, or a negative number - reqcuptr place to put the last required code unit - reqcuflagsptr place to put the last required code unit flags, or a negative number - bcptr points to current branch chain - cond_depth conditional nesting depth - cb contains pointers to tables etc. - lengthptr NULL during the real compile phase - points to length accumulator during pre-compile phase - -Returns: TRUE on success - FALSE, with *errorcodeptr set non-zero on error -*/ - -static BOOL -compile_branch(uint32_t *optionsptr, PCRE2_UCHAR **codeptr, - PCRE2_SPTR *ptrptr, int *errorcodeptr, - uint32_t *firstcuptr, int32_t *firstcuflagsptr, - uint32_t *reqcuptr, int32_t *reqcuflagsptr, - branch_chain *bcptr, int cond_depth, - compile_block *cb, size_t *lengthptr) -{ -int repeat_min = 0, repeat_max = 0; /* To please picky compilers */ -int bravalue = 0; -uint32_t greedy_default, greedy_non_default; -uint32_t repeat_type, op_type; -uint32_t options = *optionsptr; /* May change dynamically */ -uint32_t firstcu, reqcu; -int32_t firstcuflags, reqcuflags; -uint32_t zeroreqcu, zerofirstcu; -int32_t zeroreqcuflags, zerofirstcuflags; -int32_t req_caseopt, reqvary, tempreqvary; -int after_manual_callout = 0; -int escape; -size_t length_prevgroup = 0; -register uint32_t c; -register PCRE2_UCHAR *code = *codeptr; -PCRE2_UCHAR *last_code = code; -PCRE2_UCHAR *orig_code = code; -PCRE2_UCHAR *tempcode; -BOOL inescq = FALSE; -BOOL groupsetfirstcu = FALSE; -PCRE2_SPTR ptr = *ptrptr; -PCRE2_SPTR tempptr; -PCRE2_UCHAR *previous = NULL; -PCRE2_UCHAR *previous_callout = NULL; -uint8_t classbits[32]; - -/* We can fish out the UTF setting once and for all into a BOOL, but we must -not do this for other options (e.g. PCRE2_EXTENDED) because they may change -dynamically as we process the pattern. */ - -#ifdef SUPPORT_UNICODE -BOOL utf = (options & PCRE2_UTF) != 0; -#if PCRE2_CODE_UNIT_WIDTH != 32 -PCRE2_UCHAR utf_units[6]; /* For setting up multi-cu chars */ -#endif - -#else /* No UTF support */ -BOOL utf = FALSE; -#endif - -/* Helper variables for OP_XCLASS opcode (for characters > 255). We define -class_uchardata always so that it can be passed to add_to_class() always, -though it will not be used in non-UTF 8-bit cases. This avoids having to supply -alternative calls for the different cases. */ - -PCRE2_UCHAR *class_uchardata; -#ifdef SUPPORT_WIDE_CHARS -BOOL xclass; -PCRE2_UCHAR *class_uchardata_base; -#endif - -/* Set up the default and non-default settings for greediness */ - -greedy_default = ((options & PCRE2_UNGREEDY) != 0); -greedy_non_default = greedy_default ^ 1; - -/* Initialize no first unit, no required unit. REQ_UNSET means "no char -matching encountered yet". It gets changed to REQ_NONE if we hit something that -matches a non-fixed first unit; reqcu just remains unset if we never find one. - -When we hit a repeat whose minimum is zero, we may have to adjust these values -to take the zero repeat into account. This is implemented by setting them to -zerofirstcu and zeroreqcu when such a repeat is encountered. The individual -item types that can be repeated set these backoff variables appropriately. */ - -firstcu = reqcu = zerofirstcu = zeroreqcu = 0; -firstcuflags = reqcuflags = zerofirstcuflags = zeroreqcuflags = REQ_UNSET; - -/* The variable req_caseopt contains either the REQ_CASELESS value or zero, -according to the current setting of the caseless flag. The REQ_CASELESS value -leaves the lower 28 bit empty. It is added into the firstcu or reqcu variables -to record the case status of the value. This is used only for ASCII characters. -*/ - -req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS:0; - -/* Switch on next character until the end of the branch */ - -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; - BOOL is_dupname; - BOOL reset_bracount; - int class_has_8bitchar; - int class_one_char; -#ifdef SUPPORT_WIDE_CHARS - BOOL xclass_has_prop; -#endif - int recno; /* Must be signed */ - int refsign; /* Must be signed */ - int terminator; /* Must be signed */ - unsigned int mclength; - unsigned int tempbracount; - uint32_t ec; - uint32_t newoptions; - uint32_t skipunits; - uint32_t subreqcu, subfirstcu; - int32_t subreqcuflags, subfirstcuflags; /* Must be signed */ - PCRE2_UCHAR mcbuffer[8]; - - /* Come here to restart the loop. */ - - REDO_LOOP: - - /* Get next character in the pattern */ - - c = *ptr; - - /* If we are at the end of a nested substitution, revert to the outer level - string. Nesting only happens one or two levels deep, and the inserted string - is always zero terminated. */ - - if (c == CHAR_NULL && cb->nestptr[0] != NULL) - { - ptr = cb->nestptr[0]; - cb->nestptr[0] = cb->nestptr[1]; - cb->nestptr[1] = NULL; - c = *ptr; - } - - /* If we are in the pre-compile phase, accumulate the length used for the - previous cycle of this loop. */ - - if (lengthptr != NULL) - { - if (code > cb->start_workspace + cb->workspace_size - - WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */ - { - *errorcodeptr = (code >= cb->start_workspace + cb->workspace_size)? - ERR52 : ERR86; - goto FAILED; - } - - /* There is at least one situation where code goes backwards: this is the - case of a zero quantifier after a class (e.g. [ab]{0}). At compile time, - the class is simply eliminated. However, it is created first, so we have to - allow memory for it. Therefore, don't ever reduce the length at this point. - */ - - if (code < last_code) code = last_code; - - /* Paranoid check for integer overflow */ - - if (OFLOW_MAX - *lengthptr < (size_t)(code - last_code)) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += (size_t)(code - last_code); - - /* If "previous" is set and it is not at the start of the work space, move - it back to there, in order to avoid filling up the work space. Otherwise, - if "previous" is NULL, reset the current code pointer to the start. */ - - if (previous != NULL) - { - if (previous > orig_code) - { - memmove(orig_code, previous, (size_t)CU2BYTES(code - previous)); - code -= previous - orig_code; - previous = orig_code; - } - } - else code = orig_code; - - /* Remember where this code item starts so we can pick up the length - next time round. */ - - last_code = code; - } - - /* 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. */ - - /* 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 (c != CHAR_NULL || ptr < cb->end_pattern) - { - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) - { - inescq = FALSE; - ptr++; - continue; - } - else if (inescq) /* Literal character */ - { - if (previous_callout != NULL) - { - if (lengthptr == NULL) /* Don't attempt in pre-compile phase */ - complete_callout(previous_callout, ptr, cb); - previous_callout = NULL; - } - if ((options & PCRE2_AUTO_CALLOUT) != 0) - { - previous_callout = code; - code = auto_callout(code, ptr, cb); - } - goto NORMAL_CHAR; - } - - /* 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 that end at newline. */ - - if ((options & PCRE2_EXTENDED) != 0) - { - PCRE2_SPTR wscptr = ptr; - while (MAX_255(c) && (cb->ctypes[c] & ctype_space) != 0) c = *(++ptr); - if (c == CHAR_NUMBER_SIGN) - { - ptr++; - while (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 skipped any characters, restart the loop. Otherwise, we didn't see - a comment. */ - - if (ptr > wscptr) goto REDO_LOOP; - } - - /* 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 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 && cb->nestptr[0] == 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; - } - - if ((options & PCRE2_AUTO_CALLOUT) != 0) - { - previous_callout = code; - code = auto_callout(code, ptr, cb); - } - } - - /* Process the next pattern item. */ - - switch(c) - { - /* ===================================================================*/ - /* The branch terminates at string end or | or ) */ - - case CHAR_NULL: - if (ptr < cb->end_pattern) goto NORMAL_CHAR; /* Zero data character */ - /* Fall through */ - - case CHAR_VERTICAL_LINE: - case CHAR_RIGHT_PARENTHESIS: - *firstcuptr = firstcu; - *firstcuflagsptr = firstcuflags; - *reqcuptr = reqcu; - *reqcuflagsptr = reqcuflags; - *codeptr = code; - *ptrptr = ptr; - if (lengthptr != NULL) - { - if (OFLOW_MAX - *lengthptr < (size_t)(code - last_code)) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += (size_t)(code - last_code); /* To include callout length */ - } - return TRUE; - - - /* ===================================================================*/ - /* Handle single-character metacharacters. In multiline mode, ^ disables - the setting of any following char as a first character. */ - - case CHAR_CIRCUMFLEX_ACCENT: - previous = NULL; - if ((options & PCRE2_MULTILINE) != 0) - { - if (firstcuflags == REQ_UNSET) - zerofirstcuflags = firstcuflags = REQ_NONE; - *code++ = OP_CIRCM; - } - else *code++ = OP_CIRC; - break; - - case CHAR_DOLLAR_SIGN: - previous = NULL; - *code++ = ((options & PCRE2_MULTILINE) != 0)? OP_DOLLM : OP_DOLL; - break; - - /* There can never be a first char if '.' is first, whatever happens about - repeats. The value of reqcu doesn't change either. */ - - case CHAR_DOT: - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - previous = code; - *code++ = ((options & PCRE2_DOTALL) != 0)? OP_ALLANY: OP_ANY; - break; - - - /* ===================================================================*/ - /* Character classes. If the included characters are all < 256, we build a - 32-byte bitmap of the permitted characters, except in the special case - where there is only one such character. For negated classes, we build the - map as usual, then invert it at the end. However, we use a different opcode - so that data characters > 255 can be handled correctly. - - If the class contains characters outside the 0-255 range, a different - opcode is compiled. It may optionally have a bit map for characters < 256, - but those above are are explicitly listed afterwards. A flag byte tells - whether the bitmap is present, and whether this is a negated class or not. - - An isolated ']' character is not treated specially, so is just another data - character. In earlier versions of PCRE that used the original API there was - a "JavaScript compatibility mode" in which it gave an error. However, - JavaScript itself has changed in this respect so there is no longer any - need for this special handling. - - 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. 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) - { - cb->nestptr[0] = ptr + 7; - ptr = sub_start_of_word; - goto REDO_LOOP; - } - - if (PRIV(strncmp_c8)(ptr+1, STRING_WEIRD_ENDWORD, 6) == 0) - { - cb->nestptr[0] = ptr + 7; - ptr = sub_end_of_word; - goto REDO_LOOP; - } - - /* Handle a real character class. */ - - previous = code; - - /* 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. */ - - if ((ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || - ptr[1] == CHAR_EQUALS_SIGN) && - check_posix_syntax(ptr, &tempptr)) - { - *errorcodeptr = (ptr[1] == CHAR_COLON)? ERR12 : ERR13; - goto FAILED; - } - - /* If the first character is '^', set the negation flag and skip it. Also, - if the first few characters (either before or after ^) are \Q\E or \E we - skip them too. This makes for compatibility with Perl. */ - - negate_class = FALSE; - for (;;) - { - c = *(++ptr); - if (c == CHAR_BACKSLASH) - { - if (ptr[1] == CHAR_E) - ptr++; - else if (PRIV(strncmp_c8)(ptr + 1, STR_Q STR_BACKSLASH STR_E, 3) == 0) - ptr += 3; - else - break; - } - else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) - negate_class = TRUE; - else break; - } - - /* Empty classes are allowed if PCRE2_ALLOW_EMPTY_CLASS is set. Otherwise, - an initial ']' is taken as a data character -- the code below handles - that. When empty classes are allowed, [] must always fail, so generate - OP_FAIL, whereas [^] must match any character, so generate OP_ALLANY. */ - - if (c == CHAR_RIGHT_SQUARE_BRACKET && - (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0) - { - *code++ = negate_class? OP_ALLANY : OP_FAIL; - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - break; - } - - /* 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 = match_all_or_no_wide_chars = FALSE; - - /* Extended class (xclass) will be used when characters > 255 - might match. */ - -#ifdef SUPPORT_WIDE_CHARS - xclass = FALSE; - class_uchardata = code + LINK_SIZE + 2; /* For XCLASS items */ - class_uchardata_base = class_uchardata; /* Save the start */ -#endif - - /* For optimization purposes, we track some properties of the class: - class_has_8bitchar will be non-zero if the class contains at least one 256 - character with a code point less than 256; class_one_char will be 1 if the - class contains just one character; xclass_has_prop will be TRUE if Unicode - property checks are present in the class. */ - - class_has_8bitchar = 0; - class_one_char = 0; -#ifdef SUPPORT_WIDE_CHARS - xclass_has_prop = FALSE; -#endif - - /* Initialize the 256-bit (32-byte) bit map to all zeros. We build the map - in a temporary bit of memory, in case the class contains fewer than two - 8-bit characters because in that case the compiled code doesn't use the bit - map. */ - - memset(classbits, 0, 32 * sizeof(uint8_t)); - - /* Process characters until ] is reached. As the test is at the end of the - loop, an initial ] is taken as a data character. At the start of the loop, - c contains the first code unit of the character. If it is zero, check for - the end of the pattern, to allow binary zero as data. */ - - for(;;) - { - PCRE2_SPTR oldptr; -#ifdef EBCDIC - BOOL range_is_literal = TRUE; -#endif - - if (c == CHAR_NULL && ptr >= cb->end_pattern) - { - *errorcodeptr = ERR6; /* Missing terminating ']' */ - goto FAILED; - } - -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(c)) - { /* Braces are required because the */ - GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */ - } -#endif - - /* Inside \Q...\E everything is literal except \E */ - - if (inescq) - { - if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) /* If we are at \E */ - { - inescq = FALSE; /* Reset literal state */ - ptr++; /* Skip the 'E' */ - goto CONTINUE_CLASS; /* Carry on with next char */ - } - goto CHECK_RANGE; /* Could be range if \E follows */ - } - - /* Handle POSIX class names. Perl allows a negation extension of the - form [:^name:]. A square bracket that doesn't match the syntax is - treated as a literal. We also recognize the POSIX constructions - [.ch.] and [=ch=] ("collating elements") and fault them, as Perl - 5.6 and 5.8 do. */ - - if (c == CHAR_LEFT_SQUARE_BRACKET && - (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || - ptr[1] == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, &tempptr)) - { - BOOL local_negate = FALSE; - int posix_class, taboffset, tabopt; - register const uint8_t *cbits = cb->cbits; - uint8_t pbits[32]; - - if (ptr[1] != CHAR_COLON) - { - *errorcodeptr = ERR13; - goto FAILED; - } - - ptr += 2; - if (*ptr == CHAR_CIRCUMFLEX_ACCENT) - { - local_negate = TRUE; - should_flip_negation = TRUE; /* Note negative special */ - ptr++; - } - - posix_class = check_posix_name(ptr, (int)(tempptr - ptr)); - if (posix_class < 0) - { - *errorcodeptr = ERR30; - goto FAILED; - } - - /* If matching is caseless, upper and lower are converted to - alpha. This relies on the fact that the class table starts with - alpha, lower, upper as the first 3 entries. */ - - if ((options & PCRE2_CASELESS) != 0 && posix_class <= 2) - posix_class = 0; - - /* When PCRE2_UCP is set, some of the POSIX classes are converted to - different escape sequences that use Unicode properties \p or \P. Others - that are not available via \p or \P generate XCL_PROP/XCL_NOTPROP - directly. UCP support is not available unless UTF support is.*/ - -#ifdef SUPPORT_UNICODE - if ((options & PCRE2_UCP) != 0) - { - unsigned int ptype = 0; - 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. 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) - { - cb->nestptr[0] = tempptr + 1; - ptr = posix_substitutes[pc] - 1; - goto CONTINUE_CLASS; - } - - /* There are three other classes that generate special property calls - that are recognized only in an XCLASS. */ - - else switch(posix_class) - { - case PC_GRAPH: - ptype = PT_PXGRAPH; - /* Fall through */ - case PC_PRINT: - if (ptype == 0) ptype = PT_PXPRINT; - /* Fall through */ - case PC_PUNCT: - if (ptype == 0) ptype = PT_PXPUNCT; - *class_uchardata++ = local_negate? XCL_NOTPROP : XCL_PROP; - *class_uchardata++ = (PCRE2_UCHAR)ptype; - *class_uchardata++ = 0; - xclass_has_prop = TRUE; - ptr = tempptr + 1; - goto CONTINUE_CLASS; - - /* 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; - } - } -#endif /* SUPPORT_UNICODE */ - - /* In the non-UCP case, or when UCP makes no difference, we build the - bit map for the POSIX class in a chunk of local store because we may be - adding and subtracting from it, and we don't want to subtract bits that - may be in the main map already. At the end we or the result into the - bit map that is being built. */ - - posix_class *= 3; - - /* Copy in the first table (always present) */ - - memcpy(pbits, cbits + posix_class_maps[posix_class], - 32 * sizeof(uint8_t)); - - /* If there is a second table, add or remove it as required. */ - - taboffset = posix_class_maps[posix_class + 1]; - tabopt = posix_class_maps[posix_class + 2]; - - if (taboffset >= 0) - { - if (tabopt >= 0) - for (c = 0; c < 32; c++) pbits[c] |= cbits[(int)c + taboffset]; - else - for (c = 0; c < 32; c++) pbits[c] &= ~cbits[(int)c + taboffset]; - } - - /* Now see if we need to remove any special characters. An option - value of 1 removes vertical space and 2 removes underscore. */ - - if (tabopt < 0) tabopt = -tabopt; - if (tabopt == 1) pbits[1] &= ~0x3c; - else if (tabopt == 2) pbits[11] &= 0x7f; - - /* Add the POSIX table or its complement into the main table that is - being built and we are done. */ - - if (local_negate) - for (c = 0; c < 32; c++) classbits[c] |= ~pbits[c]; - else - for (c = 0; c < 32; c++) classbits[c] |= pbits[c]; - - ptr = tempptr + 1; - /* Every class contains at least one < 256 character. */ - class_has_8bitchar = 1; - /* Every class contains at least two characters. */ - class_one_char = 2; - goto CONTINUE_CLASS; /* End of POSIX syntax handling */ - } - - /* Backslash may introduce a single character, or it may introduce one - of the specials, which just set a flag. The sequence \b is a special - case. Inside a class (and only there) it is treated as backspace. We - assume that other escapes have more than one character in them, so - speculatively set both class_has_8bitchar and class_one_char bigger - than one. Unrecognized escapes fall through and are faulted. */ - - if (c == CHAR_BACKSLASH) - { - escape = PRIV(check_escape)(&ptr, cb->end_pattern, &ec, errorcodeptr, - options, TRUE, cb); - if (*errorcodeptr != 0) goto FAILED; - 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 */ - { - *errorcodeptr = ERR71; - goto FAILED; - } - 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; - goto CONTINUE_CLASS; - } - else if (escape == ESC_E) goto CONTINUE_CLASS; /* Ignore orphan \E */ - - else /* Handle \d-type escapes */ - { - register const uint8_t *cbits = cb->cbits; - /* Every class contains at least two < 256 characters. */ - class_has_8bitchar++; - /* Every class contains at least two characters. */ - class_one_char += 2; - - switch (escape) - { -#ifdef SUPPORT_UNICODE - case ESC_du: /* These are the values given for \d etc */ - 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. 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; -#endif - case ESC_d: - for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_digit]; - break; - - case ESC_D: - should_flip_negation = TRUE; - for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_digit]; - break; - - case ESC_w: - for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_word]; - break; - - case ESC_W: - should_flip_negation = TRUE; - for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_word]; - break; - - /* Perl 5.004 onwards omitted VT from \s, but restored it at Perl - 5.18. Before PCRE 8.34, we had to preserve the VT bit if it was - previously set by something earlier in the character class. - Luckily, the value of CHAR_VT is 0x0b in both ASCII and EBCDIC, so - we could just adjust the appropriate bit. From PCRE 8.34 we no - longer treat \s and \S specially. */ - - case ESC_s: - for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_space]; - break; - - case ESC_S: - should_flip_negation = TRUE; - for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_space]; - break; - - /* The rest apply in both UCP and non-UCP cases. */ - - case ESC_h: - (void)add_list_to_class(classbits, &class_uchardata, options, cb, - PRIV(hspace_list), NOTACHAR); - break; - - case ESC_H: - (void)add_not_list_to_class(classbits, &class_uchardata, options, - cb, PRIV(hspace_list)); - break; - - case ESC_v: - (void)add_list_to_class(classbits, &class_uchardata, options, cb, - PRIV(vspace_list), NOTACHAR); - break; - - case ESC_V: - (void)add_not_list_to_class(classbits, &class_uchardata, options, - cb, PRIV(vspace_list)); - break; - - case ESC_p: - case ESC_P: -#ifdef SUPPORT_UNICODE - { - BOOL negated; - unsigned int ptype = 0, pdata = 0; - if (!get_ucp(&ptr, &negated, &ptype, &pdata, errorcodeptr, cb)) - goto FAILED; - *class_uchardata++ = ((escape == ESC_p) != negated)? - XCL_PROP : XCL_NOTPROP; - *class_uchardata++ = ptype; - *class_uchardata++ = pdata; - xclass_has_prop = TRUE; - class_has_8bitchar--; /* Undo! */ - } - break; -#else - *errorcodeptr = ERR45; - goto FAILED; -#endif - /* Unrecognized escapes are faulted. */ - - default: - *errorcodeptr = ERR7; - goto FAILED; - } - - /* Handled \d-type escape */ - - goto CONTINUE_CLASS; - } - - /* Control gets here if the escape just defined a single character. - This is in c and may be greater than 256. */ - - escape = 0; - } /* End of backslash handling */ - - /* A character may be followed by '-' to form a range. However, Perl does - not permit ']' to be the end of the range. A '-' character at the end is - treated as a literal. Perl ignores orphaned \E sequences entirely. The - code for handling \Q and \E is messy. */ - - CHECK_RANGE: - while (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E) - { - inescq = FALSE; - ptr += 2; - } - oldptr = ptr; - - /* Remember if \r or \n were explicitly used */ - - if (c == CHAR_CR || c == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; - - /* Check for range */ - - if (!inescq && ptr[1] == CHAR_MINUS) - { - uint32_t d; - ptr += 2; - while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E) ptr += 2; - - /* If we hit \Q (not followed by \E) at this point, go into escaped - mode. */ - - while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_Q) - { - ptr += 2; - if (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E) - { ptr += 2; continue; } - inescq = TRUE; - break; - } - - /* Minus (hyphen) at the end of a class is treated as a literal, so put - back the pointer and jump to handle the character that preceded it. */ - - if (*ptr == CHAR_NULL || (!inescq && *ptr == CHAR_RIGHT_SQUARE_BRACKET)) - { - ptr = oldptr; - goto CLASS_SINGLE_CHARACTER; - } - - /* Otherwise, we have a potential range; pick up the next character */ - -#ifdef SUPPORT_UNICODE - if (utf) - { /* Braces are required because the */ - GETCHARLEN(d, ptr, ptr); /* macro generates multiple statements */ - } - else -#endif - d = *ptr; /* Not UTF mode */ - - /* The second part of a range can be a single-character escape - sequence, but not any of the other escapes. Perl treats a hyphen as a - literal in such circumstances. However, in Perl's warning mode, a - warning is given, so PCRE now faults it as it is almost certainly a - mistake on the user's part. */ - - if (!inescq) - { - if (d == CHAR_BACKSLASH) - { - int descape; - 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. */ - - if (descape != 0) - { - if (descape == ESC_b) d = CHAR_BS; else - { - *errorcodeptr = ERR50; - goto FAILED; - } - } - } - - /* A hyphen followed by a POSIX class is treated in the same way. */ - - else if (d == CHAR_LEFT_SQUARE_BRACKET && - (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || - ptr[1] == CHAR_EQUALS_SIGN) && - check_posix_syntax(ptr, &tempptr)) - { - *errorcodeptr = ERR50; - goto FAILED; - } - } - - /* Check that the two values are in the correct order. Optimize - one-character ranges. */ - - if (d < c) - { - *errorcodeptr = ERR8; - goto FAILED; - } - if (d == c) goto CLASS_SINGLE_CHARACTER; /* A few lines below */ - - /* We have found a character range, so single character optimizations - cannot be done anymore. Any value greater than 1 indicates that there - is more than one character. */ - - class_one_char = 2; - - /* Remember an explicit \r or \n, and add the range to the class. */ - - 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 */ - } - - /* Handle a single character - we can get here for a normal non-escape - char, or after \ that introduces a single character or for an apparent - range that isn't. Only the value 1 matters for class_one_char, so don't - increase it if it is already 2 or more ... just in case there's a class - with a zillion characters in it. */ - - CLASS_SINGLE_CHARACTER: - if (class_one_char < 2) class_one_char++; - - /* 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 && -#ifdef SUPPORT_UNICODE - !xclass_has_prop && -#endif - class_one_char == 1 && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) - { - ptr++; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - - if (negate_class) - { -#ifdef SUPPORT_UNICODE - int d; -#endif - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - - /* For caseless UTF mode, check whether this character has more than - one other case. If so, generate a special OP_NOTPROP item instead of - OP_NOTI. */ - -#ifdef SUPPORT_UNICODE - if (utf && (options & PCRE2_CASELESS) != 0 && - (d = UCD_CASESET(c)) != 0) - { - *code++ = OP_NOTPROP; - *code++ = PT_CLIST; - *code++ = d; - } - else -#endif - /* Char has only one other case, or UCP not available */ - - { - *code++ = ((options & PCRE2_CASELESS) != 0)? OP_NOTI: OP_NOT; - code += PUTCHAR(c, code); - } - - /* We are finished with this character class */ - - goto END_CLASS; - } - - /* 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 */ - - /* There is more than one character in the class, or an XCLASS item - has been generated. Add this character to the class. */ - - class_has_8bitchar += - add_to_class(classbits, &class_uchardata, options, cb, c, c); - - /* Continue to the next character in the class. Closing square bracket - not within \Q..\E ends the class. A NULL character terminates a - nested substitution string, but may be a data character in the main - pattern (tested at the start of this loop). */ - - CONTINUE_CLASS: - c = *(++ptr); - if (c == CHAR_NULL && cb->nestptr[0] != NULL) - { - ptr = cb->nestptr[0]; - cb->nestptr[0] = cb->nestptr[1]; - cb->nestptr[1] = NULL; - c = *(++ptr); - } - -#ifdef SUPPORT_WIDE_CHARS - /* If any wide characters have been encountered, set xclass = TRUE. Then, - in the pre-compile phase, accumulate the length of the wide characters - and reset the pointer. This is so that very large classes that contain a - zillion wide characters do not overwrite the work space (which is on the - stack). */ - - if (class_uchardata > class_uchardata_base) - { - xclass = TRUE; - if (lengthptr != NULL) - { - *lengthptr += class_uchardata - class_uchardata_base; - class_uchardata = class_uchardata_base; - } - } -#endif - /* An unescaped ] ends the class */ - - if (c == CHAR_RIGHT_SQUARE_BRACKET && !inescq) break; - } /* End of main class-processing loop */ - - /* If this is the first thing in the branch, there can be no first char - setting, whatever the repeat count. Any reqcu setting must remain - unchanged after any kind of repeat. */ - - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - - /* 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 && (xclass_has_prop || !should_flip_negation || - (options & PCRE2_UCP) != 0)) -#elif PCRE2_CODE_UNIT_WIDTH != 8 - 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; - *code = negate_class? XCL_NOT:0; - if (xclass_has_prop) *code |= XCL_HASPROP; - - /* If the map is required, move up the extra data to make room for it; - otherwise just move the code pointer to the end of the extra data. */ - - if (class_has_8bitchar > 0) - { - *code++ |= XCL_MAP; - memmove(code + (32 / sizeof(PCRE2_UCHAR)), code, - CU2BYTES(class_uchardata - code)); - if (negate_class && !xclass_has_prop) - for (c = 0; c < 32; c++) classbits[c] = ~classbits[c]; - memcpy(code, classbits, 32); - code = class_uchardata + (32 / sizeof(PCRE2_UCHAR)); - } - else code = class_uchardata; - - /* Now fill in the complete length of the item */ - - PUT(previous, 1, (int)(code - previous)); - break; /* End of class handling */ - } -#endif - - /* If there are no characters > 255, or they are all to be included or - excluded, set the opcode to OP_CLASS or OP_NCLASS, depending on whether the - whole class was negated and whether there were negative specials such as \S - (non-UCP) in the class. Then copy the 32-byte map into the code vector, - negating it if necessary. */ - - *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS; - if (lengthptr == NULL) /* Save time in the pre-compile phase */ - { - if (negate_class) - for (c = 0; c < 32; c++) classbits[c] = ~classbits[c]; - memcpy(code, classbits, 32); - } - code += 32 / sizeof(PCRE2_UCHAR); - - END_CLASS: - break; - - - /* ===================================================================*/ - /* Various kinds of repeat; '{' is not necessarily a quantifier, but this - has been tested above. */ - - case CHAR_LEFT_CURLY_BRACKET: - if (!is_quantifier) goto NORMAL_CHAR; - ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorcodeptr); - if (*errorcodeptr != 0) goto FAILED; - goto REPEAT; - - case CHAR_ASTERISK: - repeat_min = 0; - repeat_max = -1; - goto REPEAT; - - case CHAR_PLUS: - repeat_min = 1; - repeat_max = -1; - goto REPEAT; - - case CHAR_QUESTION_MARK: - repeat_min = 0; - repeat_max = 1; - - REPEAT: - if (previous == NULL) - { - *errorcodeptr = ERR9; - goto FAILED; - } - - if (repeat_min == 0) - { - firstcu = zerofirstcu; /* Adjust for zero repeat */ - firstcuflags = zerofirstcuflags; - reqcu = zeroreqcu; /* Ditto */ - reqcuflags = zeroreqcuflags; - } - - /* Remember whether this is a variable length repeat */ - - reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY; - - op_type = 0; /* Default single-char op codes */ - possessive_quantifier = FALSE; /* Default not possessive quantifier */ - - /* Save start of previous item, in case we have to move it up in order to - insert something before it. */ - - tempcode = previous; - - /* Before checking for a possessive quantifier, we must skip over - whitespace and comments in extended mode because Perl allows white space at - this point. */ - - if ((options & PCRE2_EXTENDED) != 0) - { - ptr++; - for (;;) - { - 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(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 - } /* Loop for comment characters */ - } /* Loop for multiple comments */ - ptr--; /* Last code unit of previous character. */ - } - - /* If the next character is '+', we have a possessive quantifier. This - implies greediness, whatever the setting of the PCRE2_UNGREEDY option. - If the next character is '?' this is a minimizing repeat, by default, - but if PCRE2_UNGREEDY is set, it works the other way round. We change the - repeat type to the non-default. */ - - if (ptr[1] == CHAR_PLUS) - { - repeat_type = 0; /* Force greedy */ - possessive_quantifier = TRUE; - ptr++; - } - else if (ptr[1] == CHAR_QUESTION_MARK) - { - repeat_type = greedy_non_default; - 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 - repeated ones could be re-implemented independently so as not to need this, - but for the moment we rely on the code for repeating groups. */ - - if (*previous == OP_RECURSE) - { - memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE)); - *previous = OP_ONCE; - PUT(previous, 1, 2 + 2*LINK_SIZE); - previous[2 + 2*LINK_SIZE] = OP_KET; - PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE); - code += 2 + 2 * LINK_SIZE; - length_prevgroup = 3 + 3*LINK_SIZE; - } - - /* Now handle repetition for the different types of item. */ - - /* If previous was a character or negated character match, abolish the item - and generate a repeat item instead. If a char item has a minimum of more - than one, ensure that it is set in reqcu - it might not be if a sequence - such as x{3} is the first thing in a branch because the x will have gone - into firstcu instead. */ - - if (*previous == OP_CHAR || *previous == OP_CHARI - || *previous == OP_NOT || *previous == OP_NOTI) - { - switch (*previous) - { - default: /* Make compiler happy. */ - case OP_CHAR: op_type = OP_STAR - OP_STAR; break; - case OP_CHARI: op_type = OP_STARI - OP_STAR; break; - case OP_NOT: op_type = OP_NOTSTAR - OP_STAR; break; - case OP_NOTI: op_type = OP_NOTSTARI - OP_STAR; break; - } - - /* Deal with UTF characters that take up more than one code unit. It's - easier to write this out separately than try to macrify it. Use c to - hold the length of the character in code units, plus UTF_LENGTH to flag - that it's a length rather than a small character. */ - -#ifdef MAYBE_UTF_MULTI - if (utf && NOT_FIRSTCU(code[-1])) - { - PCRE2_UCHAR *lastchar = code - 1; - BACKCHAR(lastchar); - c = (int)(code - lastchar); /* Length of UTF character */ - memcpy(utf_units, lastchar, CU2BYTES(c)); /* Save the char */ - c |= UTF_LENGTH; /* Flag c as a length */ - } - else -#endif /* MAYBE_UTF_MULTI */ - - /* Handle the case of a single charater - either with no UTF support, or - with UTF disabled, or for a single-code-unit UTF character. */ - { - c = code[-1]; - if (*previous <= OP_CHARI && repeat_min > 1) - { - reqcu = c; - reqcuflags = req_caseopt | cb->req_varyopt; - } - } - - goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ - } - - /* If previous was a character type match (\d or similar), abolish it and - create a suitable repeat item. The code is shared with single-character - repeats by setting op_type to add a suitable offset into repeat_type. Note - the the Unicode property types will be present only when SUPPORT_UNICODE is - defined, but we don't wrap the little bits of code here because it just - makes it horribly messy. */ - - else if (*previous < OP_EODN) - { - PCRE2_UCHAR *oldcode; - int prop_type, prop_value; - op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ - c = *previous; /* Save previous opcode */ - if (c == OP_PROP || c == OP_NOTPROP) - { - prop_type = previous[1]; - prop_value = previous[2]; - } - else - { - /* Come here from just above with a character in c */ - OUTPUT_SINGLE_REPEAT: - prop_type = prop_value = -1; - } - - /* At this point we either have prop_type == prop_value == -1 and either - a code point or a character type that is not OP_[NOT]PROP in c, or we - have OP_[NOT]PROP in c and prop_type/prop_value not negative. */ - - oldcode = code; /* Save where we were */ - code = previous; /* Usually overwrite previous item */ - - /* If the maximum is zero then the minimum must also be zero; Perl allows - this case, so we do too - by simply omitting the item altogether. */ - - if (repeat_max == 0) goto END_REPEAT; - - /* Combine the op_type with the repeat_type */ - - repeat_type += op_type; - - /* A minimum of zero is handled either as the special case * or ?, or as - an UPTO, with the maximum given. */ - - if (repeat_min == 0) - { - if (repeat_max == -1) *code++ = OP_STAR + repeat_type; - else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; - else - { - *code++ = OP_UPTO + repeat_type; - PUT2INC(code, 0, repeat_max); - } - } - - /* A repeat minimum of 1 is optimized into some special cases. If the - maximum is unlimited, we use OP_PLUS. Otherwise, the original item is - left in place and, if the maximum is greater than 1, we use OP_UPTO with - one less than the maximum. */ - - else if (repeat_min == 1) - { - if (repeat_max == -1) - *code++ = OP_PLUS + repeat_type; - else - { - code = oldcode; /* Leave previous item in place */ - if (repeat_max == 1) goto END_REPEAT; - *code++ = OP_UPTO + repeat_type; - PUT2INC(code, 0, repeat_max - 1); - } - } - - /* The case {n,n} is just an EXACT, while the general case {n,m} is - handled as an EXACT followed by an UPTO or STAR or QUERY. */ - - else - { - *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ - PUT2INC(code, 0, repeat_min); - - /* Unless repeat_max equals repeat_min, fill in the data for EXACT, and - then generate the second opcode. In UTF mode, multi-code-unit - characters have their length in c, with the UTF_LENGTH bit as a flag, - and the code units in utf_units. For a repeated Unicode property match, - there are two extra values that define the required property, and c - never has the UTF_LENGTH bit set. */ - - if (repeat_max != repeat_min) - { -#ifdef MAYBE_UTF_MULTI - if (utf && (c & UTF_LENGTH) != 0) - { - memcpy(code, utf_units, CU2BYTES(c & 7)); - code += c & 7; - } - else -#endif /* MAYBE_UTF_MULTI */ - { - *code++ = c; - if (prop_type >= 0) - { - *code++ = prop_type; - *code++ = prop_value; - } - } - - /* Now set up the following opcode */ - - if (repeat_max < 0) *code++ = OP_STAR + repeat_type; else - { - repeat_max -= repeat_min; - if (repeat_max == 1) - { - *code++ = OP_QUERY + repeat_type; - } - else - { - *code++ = OP_UPTO + repeat_type; - PUT2INC(code, 0, repeat_max); - } - } - } - } - - /* Fill in the character or character type for the final opcode. */ - -#ifdef MAYBE_UTF_MULTI - if (utf && (c & UTF_LENGTH) != 0) - { - memcpy(code, utf_units, CU2BYTES(c & 7)); - code += c & 7; - } - else -#endif /* MAYBEW_UTF_MULTI */ - { - *code++ = c; - if (prop_type >= 0) - { - *code++ = prop_type; - *code++ = prop_value; - } - } - } - - /* If previous was a character class or a back reference, we put the repeat - stuff after it, but just skip the item if the repeat was {0,0}. */ - - else if (*previous == OP_CLASS || *previous == OP_NCLASS || -#ifdef SUPPORT_WIDE_CHARS - *previous == OP_XCLASS || -#endif - *previous == OP_REF || *previous == OP_REFI || - *previous == OP_DNREF || *previous == OP_DNREFI) - { - if (repeat_max == 0) - { - code = previous; - goto END_REPEAT; - } - - if (repeat_min == 0 && repeat_max == -1) - *code++ = OP_CRSTAR + repeat_type; - else if (repeat_min == 1 && repeat_max == -1) - *code++ = OP_CRPLUS + repeat_type; - else if (repeat_min == 0 && repeat_max == 1) - *code++ = OP_CRQUERY + repeat_type; - else - { - *code++ = OP_CRRANGE + repeat_type; - PUT2INC(code, 0, repeat_min); - if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ - PUT2INC(code, 0, repeat_max); - } - } - - /* If previous was a bracket group, we may have to replicate it in certain - cases. Note that at this point we can encounter only the "basic" bracket - opcodes such as BRA and CBRA, as this is the place where they get converted - into the more special varieties such as BRAPOS and SBRA. A test for >= - OP_ASSERT and <= OP_COND includes ASSERT, ASSERT_NOT, ASSERTBACK, - ASSERTBACK_NOT, ONCE, ONCE_NC, BRA, BRAPOS, CBRA, CBRAPOS, and COND. - Originally, PCRE did not allow repetition of assertions, but now it does, - for Perl compatibility. */ - - else if (*previous >= OP_ASSERT && *previous <= OP_COND) - { - register int i; - int len = (int)(code - previous); - PCRE2_UCHAR *bralink = NULL; - PCRE2_UCHAR *brazeroptr = NULL; - - /* Repeating a DEFINE group (or any group where the condition is always - FALSE and there is only one branch) is pointless, but Perl allows the - syntax, so we just ignore the repeat. */ - - if (*previous == OP_COND && previous[LINK_SIZE+1] == OP_FALSE && - previous[GET(previous, 1)] != OP_ALT) - goto END_REPEAT; - - /* There is no sense in actually repeating assertions. The only potential - use of repetition is in cases when the assertion is optional. Therefore, - if the minimum is greater than zero, just ignore the repeat. If the - maximum is not zero or one, set it to 1. */ - - if (*previous < OP_ONCE) /* Assertion */ - { - if (repeat_min > 0) goto END_REPEAT; - if (repeat_max < 0 || repeat_max > 1) repeat_max = 1; - } - - /* The case of a zero minimum is special because of the need to stick - OP_BRAZERO in front of it, and because the group appears once in the - data, whereas in other cases it appears the minimum number of times. For - this reason, it is simplest to treat this case separately, as otherwise - the code gets far too messy. There are several special subcases when the - minimum is zero. */ - - if (repeat_min == 0) - { - /* If the maximum is also zero, we used to just omit the group from the - output altogether, like this: - - ** if (repeat_max == 0) - ** { - ** code = previous; - ** goto END_REPEAT; - ** } - - However, that fails when a group or a subgroup within it is referenced - as a subroutine from elsewhere in the pattern, so now we stick in - OP_SKIPZERO in front of it so that it is skipped on execution. As we - don't have a list of which groups are referenced, we cannot do this - selectively. - - If the maximum is 1 or unlimited, we just have to stick in the BRAZERO - and do no more at this point. */ - - if (repeat_max <= 1) /* Covers 0, 1, and unlimited */ - { - memmove(previous + 1, previous, CU2BYTES(len)); - code++; - if (repeat_max == 0) - { - *previous++ = OP_SKIPZERO; - goto END_REPEAT; - } - brazeroptr = previous; /* Save for possessive optimizing */ - *previous++ = OP_BRAZERO + repeat_type; - } - - /* If the maximum is greater than 1 and limited, we have to replicate - in a nested fashion, sticking OP_BRAZERO before each set of brackets. - 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. */ - - else - { - int offset; - memmove(previous + 2 + LINK_SIZE, previous, CU2BYTES(len)); - code += 2 + LINK_SIZE; - *previous++ = OP_BRAZERO + repeat_type; - *previous++ = OP_BRA; - - /* We chain together the bracket offset fields that have to be - filled in later when the ends of the brackets are reached. */ - - offset = (bralink == NULL)? 0 : (int)(previous - bralink); - bralink = previous; - PUTINC(previous, 0, offset); - } - - repeat_max--; - } - - /* 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. */ - - else - { - if (repeat_min > 1) - { - /* In the pre-compile phase, we don't actually do the replication. We - just adjust the length as if we had. Do some paranoid checks for - potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit - integer type when available, otherwise double. */ - - if (lengthptr != NULL) - { - size_t delta = (repeat_min - 1)*length_prevgroup; - if ((INT64_OR_DOUBLE)(repeat_min - 1)* - (INT64_OR_DOUBLE)length_prevgroup > - (INT64_OR_DOUBLE)INT_MAX || - OFLOW_MAX - *lengthptr < delta) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += delta; - } - - /* 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. */ - - else - { - if (groupsetfirstcu && reqcuflags < 0) - { - reqcu = firstcu; - reqcuflags = firstcuflags; - } - for (i = 1; i < repeat_min; i++) - { - memcpy(code, previous, CU2BYTES(len)); - code += len; - } - } - } - - if (repeat_max > 0) repeat_max -= repeat_min; - } - - /* This code is common to both the zero and non-zero minimum cases. If - the maximum is limited, it replicates the group in a nested fashion, - remembering the bracket starts on a stack. In the case of a zero minimum, - the first one was set up above. In all cases the repeat_max now specifies - the number of additional copies needed. Again, we must remember to - replicate entries on the forward reference list. */ - - if (repeat_max >= 0) - { - /* In the pre-compile phase, we don't actually do the replication. We - just adjust the length as if we had. For each repetition we must add 1 - to the length for BRAZERO and for all but the last repetition we must - add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some - paranoid checks to avoid integer overflow. The INT64_OR_DOUBLE type is - a 64-bit integer type when available, otherwise double. */ - - if (lengthptr != NULL && repeat_max > 0) - { - size_t delta = repeat_max*(length_prevgroup + 1 + 2 + 2*LINK_SIZE) - - 2 - 2*LINK_SIZE; /* Last one doesn't nest */ - if ((INT64_OR_DOUBLE)repeat_max * - (INT64_OR_DOUBLE)(length_prevgroup + 1 + 2 + 2*LINK_SIZE) - > (INT64_OR_DOUBLE)INT_MAX || - OFLOW_MAX - *lengthptr < delta) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += delta; - } - - /* This is compiling for real */ - - else for (i = repeat_max - 1; i >= 0; i--) - { - *code++ = OP_BRAZERO + repeat_type; - - /* All but the final copy start a new nesting, maintaining the - chain of brackets outstanding. */ - - if (i != 0) - { - int offset; - *code++ = OP_BRA; - offset = (bralink == NULL)? 0 : (int)(code - bralink); - bralink = code; - PUTINC(code, 0, offset); - } - - memcpy(code, previous, CU2BYTES(len)); - code += len; - } - - /* Now chain through the pending brackets, and fill in their length - fields (which are holding the chain links pro tem). */ - - while (bralink != NULL) - { - int oldlinkoffset; - int offset = (int)(code - bralink + 1); - PCRE2_UCHAR *bra = code - offset; - oldlinkoffset = GET(bra, 1); - bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; - *code++ = OP_KET; - PUTINC(code, 0, offset); - PUT(bra, 1, offset); - } - } - - /* If the maximum is unlimited, set a repeater in the final copy. For - ONCE brackets, that's all we need to do. However, possessively repeated - ONCE brackets can be converted into non-capturing brackets, as the - behaviour of (?:xx)++ is the same as (?>xx)++ and this saves having to - deal with possessive ONCEs specially. - - Otherwise, when we are doing the actual compile phase, check to see - whether this group is one that could match an empty string. If so, - convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so - that runtime checking can be done. [This check is also applied to ONCE - groups at runtime, but in a different way.] - - Then, if the quantifier was possessive and the bracket is not a - 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 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 - flag so that the default action below, of wrapping everything inside - atomic brackets, does not happen. When the minimum is greater than 1, - there will be earlier copies of the group, and so we still have to wrap - the whole thing. */ - - else - { - PCRE2_UCHAR *ketcode = code - 1 - LINK_SIZE; - PCRE2_UCHAR *bracode = ketcode - GET(ketcode, 1); - - /* Convert possessive ONCE brackets to non-capturing */ - - if ((*bracode == OP_ONCE || *bracode == OP_ONCE_NC) && - possessive_quantifier) *bracode = OP_BRA; - - /* For non-possessive ONCE brackets, all we need to do is to - set the KET. */ - - if (*bracode == OP_ONCE || *bracode == OP_ONCE_NC) - *ketcode = OP_KETRMAX + repeat_type; - - /* Handle non-ONCE brackets and possessive ONCEs (which have been - converted to non-capturing above). */ - - else - { - /* In the compile phase, check whether the group could match an empty - string. */ - - if (lengthptr == NULL) - { - PCRE2_UCHAR *scode = bracode; - do - { - 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; - } - 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. */ - - if (possessive_quantifier) - { - /* 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. */ - - if (*bracode == OP_COND || *bracode == OP_SCOND) - { - int nlen = (int)(code - bracode); - memmove(bracode + 1 + LINK_SIZE, bracode, CU2BYTES(nlen)); - code += 1 + LINK_SIZE; - nlen += 1 + LINK_SIZE; - *bracode = (*bracode == OP_COND)? OP_BRAPOS : OP_SBRAPOS; - *code++ = OP_KETRPOS; - PUTINC(code, 0, nlen); - PUT(bracode, 1, nlen); - } - - /* For non-COND brackets, we modify the BRA code and use KETRPOS. */ - - else - { - *bracode += 1; /* Switch to xxxPOS opcodes */ - *ketcode = OP_KETRPOS; - } - - /* If the minimum is zero, mark it as possessive, then unset the - possessive flag when the minimum is 0 or 1. */ - - if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO; - if (repeat_min < 2) possessive_quantifier = FALSE; - } - - /* Non-possessive quantifier */ - - else *ketcode = OP_KETRMAX + repeat_type; - } - } - } - - /* If previous is OP_FAIL, it was generated by an empty class [] - (PCRE2_ALLOW_EMPTY_CLASS is set). The other ways in which OP_FAIL can be - generated, that is by (*FAIL) or (?!), set previous to NULL, which gives a - "nothing to repeat" error above. We can just ignore the repeat in empty - class case. */ - - else if (*previous == OP_FAIL) goto END_REPEAT; - - /* Else there's some kind of shambles */ - - else - { - *errorcodeptr = ERR10; - goto FAILED; - } - - /* If the character following a repeat is '+', possessive_quantifier is - TRUE. For some opcodes, there are special alternative opcodes for this - case. For anything else, we wrap the entire repeated item inside OP_ONCE - brackets. Logically, the '+' notation is just syntactic sugar, taken from - Sun's Java package, but the special opcodes can optimize it. - - Some (but not all) possessively repeated subpatterns have already been - completely handled in the code just above. For them, possessive_quantifier - is always FALSE at this stage. Note that the repeated item starts at - tempcode, not at previous, which might be the first part of a string whose - (former) last char we repeated. */ - - if (possessive_quantifier) - { - int len; - - /* Possessifying an EXACT quantifier has no effect, so we can ignore it. - However, QUERY, STAR, or UPTO may follow (for quantifiers such as {5,6}, - {5,}, or {5,10}). We skip over an EXACT item; if the length of what - remains is greater than zero, there's a further opcode that can be - handled. If not, do nothing, leaving the EXACT alone. */ - - switch(*tempcode) - { - case OP_TYPEEXACT: - tempcode += PRIV(OP_lengths)[*tempcode] + - ((tempcode[1 + IMM2_SIZE] == OP_PROP - || tempcode[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); - break; - - /* CHAR opcodes are used for exacts whose count is 1. */ - - case OP_CHAR: - case OP_CHARI: - case OP_NOT: - case OP_NOTI: - case OP_EXACT: - case OP_EXACTI: - case OP_NOTEXACT: - case OP_NOTEXACTI: - tempcode += PRIV(OP_lengths)[*tempcode]; -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(tempcode[-1])) - tempcode += GET_EXTRALEN(tempcode[-1]); -#endif - break; - - /* For the class opcodes, the repeat operator appears at the end; - adjust tempcode to point to it. */ - - case OP_CLASS: - case OP_NCLASS: - tempcode += 1 + 32/sizeof(PCRE2_UCHAR); - break; - -#ifdef SUPPORT_WIDE_CHARS - case OP_XCLASS: - tempcode += GET(tempcode, 1); - break; -#endif - } - - /* If tempcode is equal to code (which points to the end of the repeated - item), it means we have skipped an EXACT item but there is no following - QUERY, STAR, or UPTO; the value of len will be 0, and we do nothing. In - all other cases, tempcode will be pointing to the repeat opcode, and will - be less than code, so the value of len will be greater than 0. */ - - len = (int)(code - tempcode); - if (len > 0) - { - unsigned int repcode = *tempcode; - - /* There is a table for possessifying opcodes, all of which are less - than OP_CALLOUT. A zero entry means there is no possessified version. - */ - - if (repcode < OP_CALLOUT && opcode_possessify[repcode] > 0) - *tempcode = opcode_possessify[repcode]; - - /* For opcode without a special possessified version, wrap the item in - ONCE brackets. */ - - else - { - memmove(tempcode + 1 + LINK_SIZE, tempcode, CU2BYTES(len)); - code += 1 + LINK_SIZE; - len += 1 + LINK_SIZE; - tempcode[0] = OP_ONCE; - *code++ = OP_KET; - PUTINC(code, 0, len); - PUT(tempcode, 1, len); - } - } - } - - /* In all case we no longer have a previous item. We also set the - "follows varying string" flag for subsequently encountered reqcus if - it isn't already set and we have just passed a varying length item. */ - - END_REPEAT: - previous = NULL; - cb->req_varyopt |= reqvary; - break; - - - /* ===================================================================*/ - /* 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++; - - /* 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)))) - { - int i, namelen; - int arglen = 0; - const char *vn = verbnames; - PCRE2_SPTR name = ptr + 1; - PCRE2_SPTR arg = NULL; - previous = NULL; - ptr++; - - /* 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. 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; - - if ((options & PCRE2_ALT_VERBNAMES) == 0) - { - 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; - } - } - - if (*ptr != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR60; - goto FAILED; - } - - /* Scan the table of verb names */ - - for (i = 0; i < verbcount; i++) - { - if (namelen == verbs[i].len && - PRIV(strncmp_c8)(name, vn, namelen) == 0) - { - int setverb; - - /* Check for open captures before ACCEPT and convert it to - ASSERT_ACCEPT if in an assertion. */ - - if (verbs[i].op == OP_ACCEPT) - { - open_capitem *oc; - if (arglen != 0) - { - *errorcodeptr = ERR59; - 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) - { - 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; - - /* Do not set firstcu after *ACCEPT */ - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - } - - /* Handle other cases with/without an argument */ - - else if (arglen == 0) /* There is no argument */ - { - if (verbs[i].op < 0) /* Argument is mandatory */ - { - *errorcodeptr = ERR66; - goto FAILED; - } - setverb = *code++ = verbs[i].op; - } - - else /* An argument is present */ - { - if (verbs[i].op_arg < 0) /* Argument is forbidden */ - { - *errorcodeptr = ERR59; - goto FAILED; - } - setverb = *code++ = verbs[i].op_arg; - - /* 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; - } - - switch (setverb) - { - case OP_THEN: - case OP_THEN_ARG: - cb->external_flags |= PCRE2_HASTHEN; - break; - - case OP_PRUNE: - case OP_PRUNE_ARG: - case OP_SKIP: - case OP_SKIP_ARG: - cb->had_pruneorskip = TRUE; - break; - } - - break; /* Found verb, exit loop */ - } - - vn += verbs[i].len + 1; - } - - if (i < verbcount) continue; /* Successfully handled a verb */ - *errorcodeptr = ERR60; /* Verb not recognized */ - goto FAILED; - } - - /* Initialization for "real" parentheses */ - - newoptions = options; - skipunits = 0; - bravalue = OP_CBRA; - reset_bracount = FALSE; - - /* Deal with the extended parentheses; all are introduced by '?', and the - appearance of any of them means that this is not a capturing group. */ - - if (*ptr == CHAR_QUESTION_MARK) - { - int i, count; - int namelen; /* Must be signed */ - uint32_t index; - uint32_t set, unset, *optset; - named_group *ng; - PCRE2_SPTR name; - PCRE2_UCHAR *slot; - - switch (*(++ptr)) - { - /* ------------------------------------------------------------ */ - case CHAR_VERTICAL_LINE: /* Reset capture count for each branch */ - reset_bracount = TRUE; - /* Fall through */ - - /* ------------------------------------------------------------ */ - case CHAR_COLON: /* Non-capturing bracket */ - bravalue = OP_BRA; - ptr++; - break; - - /* ------------------------------------------------------------ */ - case CHAR_LEFT_PARENTHESIS: - bravalue = OP_COND; /* Conditional group */ - tempptr = ptr; - - /* A condition can be an assertion, a number (referring to a numbered - group's having been set), a name (referring to a named group), or 'R', - referring to recursion. R and R&name are also permitted for - recursion tests. - - There are ways of testing a named group: (?(name)) is used by Python; - Perl 5.10 onwards uses (?() or (?('name')). - - There is one unfortunate ambiguity, caused by history. 'R' can be the - recursive thing or the name 'R' (and similarly for 'R' followed by - digits). We look for a name first; if not found, we try the other case. - - For compatibility with auto-callouts, we allow a callout to be - specified before a condition that is an assertion. First, check for the - syntax of a callout; if found, adjust the temporary pointer that is - used to check for an assertion condition. That's all that is needed! */ - - if (ptr[1] == CHAR_QUESTION_MARK && ptr[2] == CHAR_C) - { - if (IS_DIGIT(ptr[3]) || ptr[3] == CHAR_RIGHT_PARENTHESIS) - { - for (i = 3;; i++) if (!IS_DIGIT(ptr[i])) break; - if (ptr[i] == CHAR_RIGHT_PARENTHESIS) - tempptr += i + 1; - } - else - { - uint32_t delimiter = 0; - for (i = 0; PRIV(callout_start_delims)[i] != 0; i++) - { - if (ptr[3] == PRIV(callout_start_delims)[i]) - { - delimiter = PRIV(callout_end_delims)[i]; - break; - } - } - if (delimiter != 0) - { - for (i = 4; ptr + i < cb->end_pattern; i++) - { - if (ptr[i] == delimiter) - { - if (ptr[i+1] == delimiter) i++; - else - { - if (ptr[i+1] == CHAR_RIGHT_PARENTHESIS) tempptr += i + 2; - break; - } - } - } - } - } - - /* tempptr should now be pointing to the opening parenthesis of the - assertion condition. */ - - if (*tempptr != CHAR_LEFT_PARENTHESIS) - { - *errorcodeptr = ERR28; - goto FAILED; - } - } - - /* For conditions that are assertions, check the syntax, and then exit - the switch. This will take control down to where bracketed groups - are processed. The assertion will be handled as part of the group, - but we need to identify this case because the conditional assertion may - not be quantifier. */ - - if (tempptr[1] == CHAR_QUESTION_MARK && - (tempptr[2] == CHAR_EQUALS_SIGN || - tempptr[2] == CHAR_EXCLAMATION_MARK || - (tempptr[2] == CHAR_LESS_THAN_SIGN && - (tempptr[3] == CHAR_EQUALS_SIGN || - tempptr[3] == CHAR_EXCLAMATION_MARK)))) - { - cb->iscondassert = TRUE; - break; - } - - /* Other conditions use OP_CREF/OP_DNCREF/OP_RREF/OP_DNRREF, and all - need to skip at least 1+IMM2_SIZE bytes at the start of the group. */ - - code[1+LINK_SIZE] = OP_CREF; - skipunits = 1+IMM2_SIZE; - refsign = -1; /* => not a number */ - namelen = -1; /* => not a name; must set to avoid warning */ - name = NULL; /* Always set to avoid warning */ - recno = 0; /* Always set to avoid warning */ - - /* Point at character after (?( */ - - ptr++; - - /* Check for (?(VERSION[>]=n.m), which is a facility whereby indirect - users of PCRE2 via an application can discover which release of PCRE2 - is being used. */ - - if (PRIV(strncmp_c8)(ptr, STRING_VERSION, 7) == 0 && - ptr[7] != CHAR_RIGHT_PARENTHESIS) - { - BOOL ge = FALSE; - int major = 0; - int minor = 0; - - ptr += 7; - if (*ptr == CHAR_GREATER_THAN_SIGN) - { - ge = TRUE; - ptr++; - } - - /* NOTE: cannot write IS_DIGIT(*(++ptr)) here because IS_DIGIT - references its argument twice. */ - - if (*ptr != CHAR_EQUALS_SIGN || (ptr++, !IS_DIGIT(*ptr))) - { - *errorcodeptr = ERR79; - goto FAILED; - } - - while (IS_DIGIT(*ptr)) major = major * 10 + *ptr++ - '0'; - if (*ptr == CHAR_DOT) - { - ptr++; - while (IS_DIGIT(*ptr)) minor = minor * 10 + *ptr++ - '0'; - if (minor < 10) minor *= 10; - } - - if (*ptr != CHAR_RIGHT_PARENTHESIS || minor > 99) - { - *errorcodeptr = ERR79; - goto FAILED; - } - - if (ge) - code[1+LINK_SIZE] = ((PCRE2_MAJOR > major) || - (PCRE2_MAJOR == major && PCRE2_MINOR >= minor))? - OP_TRUE : OP_FALSE; - else - code[1+LINK_SIZE] = (PCRE2_MAJOR == major && PCRE2_MINOR == minor)? - OP_TRUE : OP_FALSE; - - ptr++; - skipunits = 1; - break; /* End of condition processing */ - } - - /* Check for a test for recursion in a named group. */ - - if (*ptr == CHAR_R && ptr[1] == CHAR_AMPERSAND) - { - terminator = -1; - ptr += 2; - code[1+LINK_SIZE] = OP_RREF; /* Change the type of test */ - } - - /* Check for a test for a named group's having been set, using the Perl - syntax (?() or (?('name'), and also allow for the original PCRE - syntax of (?(name) or for (?(+n), (?(-n), and just (?(n). */ - - else if (*ptr == CHAR_LESS_THAN_SIGN) - { - terminator = CHAR_GREATER_THAN_SIGN; - ptr++; - } - else if (*ptr == CHAR_APOSTROPHE) - { - terminator = CHAR_APOSTROPHE; - ptr++; - } - else - { - terminator = CHAR_NULL; - if (*ptr == CHAR_MINUS || *ptr == CHAR_PLUS) refsign = *ptr++; - else if (IS_DIGIT(*ptr)) refsign = 0; - } - - /* Handle a number */ - - if (refsign >= 0) - { - 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++; - } - } - - /* Otherwise we expect to read a name; anything else is an error. When - the referenced name is one of a number of duplicates, a different - opcode is used and it needs more memory. Unfortunately we cannot tell - whether this is the case in the first pass, so we have to allow for - more memory always. In the second pass, the additional to skipunits - happens later. */ - - else - { - if (IS_DIGIT(*ptr)) - { - *errorcodeptr = ERR44; /* Group name must start with non-digit */ - goto FAILED; - } - if (!MAX_255(*ptr) || (cb->ctypes[*ptr] & ctype_word) == 0) - { - *errorcodeptr = ERR28; /* Assertion expected */ - goto FAILED; - } - name = ptr; - /* Increment ptr, set namelen, check length */ - READ_NAME(ctype_word, ERR48, *errorcodeptr); - if (lengthptr != NULL) skipunits += IMM2_SIZE; - } - - /* Check the terminator */ - - if ((terminator > 0 && *ptr++ != (PCRE2_UCHAR)terminator) || - *ptr++ != CHAR_RIGHT_PARENTHESIS) - { - ptr--; /* Error offset */ - *errorcodeptr = ERR26; /* Malformed number or name */ - goto FAILED; - } - - /* Do no further checking in the pre-compile phase. */ - - if (lengthptr != NULL) break; - - /* In the real compile we do the work of looking for the actual - reference. If refsign is not negative, it means we have a number in - recno. */ - - if (refsign >= 0) - { - if (recno <= 0) - { - *errorcodeptr = ERR35; - goto FAILED; - } - if (refsign != 0) recno = (refsign == CHAR_MINUS)? - (cb->bracount + 1) - recno : recno + cb->bracount; - if (recno <= 0 || (uint32_t)recno > cb->final_bracount) - { - *errorcodeptr = ERR15; - goto FAILED; - } - PUT2(code, 2+LINK_SIZE, recno); - if ((uint32_t)recno > cb->top_backref) cb->top_backref = recno; - break; - } - - /* Otherwise look for the name. */ - - slot = cb->name_table; - for (i = 0; i < cb->names_found; i++) - { - if (PRIV(strncmp)(name, slot+IMM2_SIZE, namelen) == 0) break; - slot += cb->name_entry_size; - } - - /* Found the named subpattern. If the name is duplicated, add one to - the opcode to change CREF/RREF into DNCREF/DNRREF and insert - appropriate data values. Otherwise, just insert the unique subpattern - number. */ - - if (i < cb->names_found) - { - int offset = i; /* Offset of first name found */ - - count = 0; - for (;;) - { - recno = GET2(slot, 0); /* Number for last found */ - if ((uint32_t)recno > cb->top_backref) cb->top_backref = recno; - count++; - if (++i >= cb->names_found) break; - slot += cb->name_entry_size; - if (PRIV(strncmp)(name, slot+IMM2_SIZE, namelen) != 0 || - (slot+IMM2_SIZE)[namelen] != 0) break; - } - - if (count > 1) - { - PUT2(code, 2+LINK_SIZE, offset); - PUT2(code, 2+LINK_SIZE+IMM2_SIZE, count); - skipunits += IMM2_SIZE; - code[1+LINK_SIZE]++; - } - else /* Not a duplicated name */ - { - PUT2(code, 2+LINK_SIZE, recno); - } - } - - /* If terminator == CHAR_NULL it means that the name followed directly - after the opening parenthesis [e.g. (?(abc)...] and in this case there - are some further alternatives to try. For the cases where terminator != - CHAR_NULL [things like (?(... or (?('name')... or (?(R&name)... ] - we have now checked all the possibilities, so give an error. */ - - else if (terminator != CHAR_NULL) - { - *errorcodeptr = ERR15; - goto FAILED; - } - - /* Check for (?(R) for recursion. Allow digits after R to specify a - specific group number. */ - - else if (*name == CHAR_R) - { - recno = 0; - for (i = 1; i < namelen; i++) - { - if (!IS_DIGIT(name[i])) - { - *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; - } - if (recno == 0) recno = RREF_ANY; - code[1+LINK_SIZE] = OP_RREF; /* Change test type */ - PUT2(code, 2+LINK_SIZE, recno); - } - - /* Similarly, check for the (?(DEFINE) "condition", which is always - false. During compilation we set OP_DEFINE to distinguish this from - other OP_FALSE conditions so that it can be checked for having only one - branch, but after that the opcode is changed to OP_FALSE. */ - - else if (namelen == 6 && PRIV(strncmp_c8)(name, STRING_DEFINE, 6) == 0) - { - code[1+LINK_SIZE] = OP_DEFINE; - skipunits = 1; - } - - /* Reference to an unidentified subpattern. */ - - else - { - *errorcodeptr = ERR15; - goto FAILED; - } - break; - - - /* ------------------------------------------------------------ */ - case CHAR_EQUALS_SIGN: /* Positive lookahead */ - bravalue = OP_ASSERT; - cb->assert_depth += 1; - ptr++; - break; - - /* Optimize (?!) to (*FAIL) unless it is quantified - which is a weird - thing to do, but Perl allows all assertions to be quantified, and when - they contain capturing parentheses there may be a potential use for - this feature. Not that that applies to a quantified (?!) but we allow - it for uniformity. */ - - /* ------------------------------------------------------------ */ - case CHAR_EXCLAMATION_MARK: /* Negative lookahead */ - ptr++; - if (*ptr == CHAR_RIGHT_PARENTHESIS && ptr[1] != CHAR_ASTERISK && - ptr[1] != CHAR_PLUS && ptr[1] != CHAR_QUESTION_MARK && - (ptr[1] != CHAR_LEFT_CURLY_BRACKET || !is_counted_repeat(ptr+2))) - { - *code++ = OP_FAIL; - previous = NULL; - continue; - } - bravalue = OP_ASSERT_NOT; - cb->assert_depth += 1; - break; - - - /* ------------------------------------------------------------ */ - case CHAR_LESS_THAN_SIGN: /* Lookbehind or named define */ - switch (ptr[1]) - { - case CHAR_EQUALS_SIGN: /* Positive lookbehind */ - bravalue = OP_ASSERTBACK; - cb->assert_depth += 1; - ptr += 2; - break; - - case CHAR_EXCLAMATION_MARK: /* Negative lookbehind */ - bravalue = OP_ASSERTBACK_NOT; - cb->assert_depth += 1; - ptr += 2; - break; - - /* Must be a name definition - as the syntax was checked in the - pre-pass, we can assume here that it is valid. Skip over the name - and go to handle the numbered group. */ - - default: - while (*(++ptr) != CHAR_GREATER_THAN_SIGN); - ptr++; - goto NUMBERED_GROUP; - } - break; - - - /* ------------------------------------------------------------ */ - case CHAR_GREATER_THAN_SIGN: /* One-time brackets */ - bravalue = OP_ONCE; - ptr++; - break; - - - /* ------------------------------------------------------------ */ - case CHAR_C: /* Callout */ - previous_callout = code; /* Save for later completion */ - after_manual_callout = 1; /* Skip one item before completing */ - ptr++; /* Character after (?C */ - - /* A callout may have a string argument, delimited by one of a fixed - number of characters, or an undelimited numerical argument, or no - argument, which is the same as (?C0). Different opcodes are used for - the two cases. */ - - if (*ptr != CHAR_RIGHT_PARENTHESIS && !IS_DIGIT(*ptr)) - { - uint32_t 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) - { - *errorcodeptr = ERR82; - goto FAILED; - } - - /* During the pre-compile phase, we parse the string and update the - 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 */ - { - PCRE2_SPTR start = ptr; - do - { - if (++ptr >= cb->end_pattern) - { - *errorcodeptr = ERR81; - ptr = start; /* To give a more useful message */ - goto FAILED; - } - if (ptr[0] == delimiter && ptr[1] == delimiter) ptr += 2; - } - while (ptr[0] != delimiter); - - /* Start points to the opening delimiter, ptr points to the - closing delimiter. We must allow for including the delimiter and - for the terminating zero. Any doubled delimiters within the string - make this an overestimate, but it is not worth bothering about. */ - - (*lengthptr) += (ptr - start) + 2 + (1 + 4*LINK_SIZE); - } - - /* In the real compile we can copy the string, knowing that it is - syntactically OK. The starting delimiter is included so that the - client can discover it if they want. We also pass the start offset to - help a script language give better error messages. */ - - else - { - PCRE2_UCHAR *callout_string = code + (1 + 4*LINK_SIZE); - *callout_string++ = *ptr++; - PUT(code, 1 + 3*LINK_SIZE, (int)(ptr - cb->start_pattern)); /* Start offset */ - for(;;) - { - if (*ptr == delimiter) - { - if (ptr[1] == delimiter) ptr++; else break; - } - *callout_string++ = *ptr++; - } - *callout_string++ = CHAR_NULL; - code[0] = OP_CALLOUT_STR; - PUT(code, 1, (int)(ptr + 2 - cb->start_pattern)); /* Next offset */ - PUT(code, 1 + LINK_SIZE, 0); /* Default length */ - PUT(code, 1 + 2*LINK_SIZE, /* Compute size */ - (int)(callout_string - code)); - code = callout_string; - } - - /* Advance to what should be the closing parenthesis, which is - checked below. */ - - ptr++; - } - - /* Handle a callout with an optional numerical argument, which must be - less than or equal to 255. A missing argument gives 0. */ - - else - { - int n = 0; - code[0] = OP_CALLOUT; /* Numerical callout */ - while (IS_DIGIT(*ptr)) - { - n = n * 10 + *ptr++ - CHAR_0; - if (n > 255) - { - *errorcodeptr = ERR38; - goto FAILED; - } - } - PUT(code, 1, (int)(ptr - cb->start_pattern + 1)); /* Next offset */ - PUT(code, 1 + LINK_SIZE, 0); /* Default length */ - code[1 + 2*LINK_SIZE] = n; /* Callout number */ - code += PRIV(OP_lengths)[OP_CALLOUT]; - } - - /* Both formats must have a closing parenthesis */ - - if (*ptr != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR39; - goto FAILED; - } - - /* Callouts cannot be quantified. */ - - previous = NULL; - continue; - - - /* ------------------------------------------------------------ */ - case CHAR_P: /* Python-style named subpattern handling */ - if (*(++ptr) == CHAR_EQUALS_SIGN || - *ptr == CHAR_GREATER_THAN_SIGN) /* Reference or recursion */ - { - is_recurse = *ptr == CHAR_GREATER_THAN_SIGN; - terminator = CHAR_RIGHT_PARENTHESIS; - goto NAMED_REF_OR_RECURSE; - } - else if (*ptr != CHAR_LESS_THAN_SIGN) /* Test for Python-style defn */ - { - *errorcodeptr = ERR41; - goto FAILED; - } - /* Fall through to handle (?P< as (?< is handled */ - - - /* ------------------------------------------------------------ */ - case CHAR_APOSTROPHE: /* Define a name - note fall through above */ - - /* The syntax was checked and the list of names was set up in the - pre-pass, so there is nothing to be done now except to skip over the - name. */ - - terminator = (*ptr == CHAR_LESS_THAN_SIGN)? - CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE; - while (*(++ptr) != (unsigned int)terminator); - ptr++; - goto NUMBERED_GROUP; /* Set up numbered group */ - - - /* ------------------------------------------------------------ */ - case CHAR_AMPERSAND: /* Perl recursion/subroutine syntax */ - terminator = CHAR_RIGHT_PARENTHESIS; - is_recurse = TRUE; - /* Fall through */ - - /* We come here from the Python syntax above that handles both - references (?P=name) and recursion (?P>name), as well as falling - through from the Perl recursion syntax (?&name). We also come here from - the Perl \k or \k'name' back reference syntax and the \k{name} - .NET syntax, and the Oniguruma \g<...> and \g'...' subroutine syntax. */ - - NAMED_REF_OR_RECURSE: - name = ++ptr; - if (IS_DIGIT(*ptr)) - { - *errorcodeptr = ERR44; /* Group name must start with non-digit */ - goto FAILED; - } - /* Increment ptr, set namelen, check length */ - READ_NAME(ctype_word, ERR48, *errorcodeptr); - - /* In the pre-compile phase, do a syntax check. */ - - if (lengthptr != NULL) - { - if (namelen == 0) - { - *errorcodeptr = ERR62; - goto FAILED; - } - if (*ptr != (PCRE2_UCHAR)terminator) - { - *errorcodeptr = ERR42; - goto FAILED; - } - } - - /* Scan the list of names generated in the pre-pass in order to get - a number and whether or not this name is duplicated. */ - - recno = 0; - is_dupname = FALSE; - ng = cb->named_groups; - - for (i = 0; i < cb->names_found; i++, ng++) - { - if (namelen == ng->length && - PRIV(strncmp)(name, ng->name, namelen) == 0) - { - open_capitem *oc; - is_dupname = ng->isdup; - recno = ng->number; - - /* For a recursion, that's all that is needed. We can now go to the - code that handles numerical recursion. */ - - if (is_recurse) goto HANDLE_RECURSION; - - /* For a back reference, update the back reference map and the - maximum back reference. Then for each group we must check to see if - it is recursive, that is, it is inside the group that it - references. A flag is set so that the group can be made atomic. */ - - cb->backref_map |= (recno < 32)? (1u << recno) : 1; - if ((uint32_t)recno > cb->top_backref) cb->top_backref = recno; - - for (oc = cb->open_caps; oc != NULL; oc = oc->next) - { - if (oc->number == recno) - { - oc->flag = TRUE; - break; - } - } - } - } - - /* If the name was not found we have a bad reference. */ - - if (recno == 0) - { - *errorcodeptr = ERR15; - goto FAILED; - } - - /* If a back reference name is not duplicated, we can handle it as a - numerical reference. */ - - if (!is_dupname) goto HANDLE_REFERENCE; - - /* If a back reference name is duplicated, we generate a different - opcode to a numerical back reference. In the second pass we must search - for the index and count in the final name table. */ - - count = 0; - index = 0; - - if (lengthptr == NULL) - { - slot = cb->name_table; - for (i = 0; i < cb->names_found; i++) - { - if (PRIV(strncmp)(name, slot+IMM2_SIZE, namelen) == 0 && - slot[IMM2_SIZE+namelen] == 0) - { - if (count == 0) index = i; - count++; - } - slot += cb->name_entry_size; - } - - if (count == 0) - { - *errorcodeptr = ERR15; - goto FAILED; - } - } - - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - previous = code; - *code++ = ((options & PCRE2_CASELESS) != 0)? OP_DNREFI : OP_DNREF; - PUT2INC(code, 0, index); - PUT2INC(code, 0, count); - continue; /* End of back ref handling */ - - - /* ------------------------------------------------------------ */ - case CHAR_R: /* Recursion, same as (?0) */ - recno = 0; - if (*(++ptr) != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR29; - goto FAILED; - } - goto HANDLE_RECURSION; - - - /* ------------------------------------------------------------ */ - case CHAR_MINUS: case CHAR_PLUS: /* Recursion or subroutine */ - 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: - { - terminator = CHAR_RIGHT_PARENTHESIS; - - /* Come here from the \g<...> and \g'...' code (Oniguruma - compatibility). However, the syntax has been checked to ensure that - the ... are a (signed) number, so that neither ERR63 nor ERR29 will - be called on this path, nor with the jump to OTHER_CHAR_AFTER_QUERY - ever be taken. */ - - HANDLE_NUMERICAL_RECURSION: - - if ((refsign = *ptr) == CHAR_PLUS) - { - ptr++; - if (!IS_DIGIT(*ptr)) - { - *errorcodeptr = ERR63; - goto FAILED; - } - } - else if (refsign == CHAR_MINUS) - { - if (!IS_DIGIT(ptr[1])) - goto OTHER_CHAR_AFTER_QUERY; - ptr++; - } - - recno = 0; - while (IS_DIGIT(*ptr)) - { - if (recno > INT_MAX / 10 - 1) /* Integer overflow */ - { - while (IS_DIGIT(*ptr)) ptr++; - *errorcodeptr = ERR61; - goto FAILED; - } - recno = recno * 10 + *ptr++ - CHAR_0; - } - - if (*ptr != (PCRE2_UCHAR)terminator) - { - *errorcodeptr = ERR29; - goto FAILED; - } - - if (refsign == CHAR_MINUS) - { - if (recno == 0) - { - *errorcodeptr = ERR58; - goto FAILED; - } - recno = (int)(cb->bracount + 1) - recno; - if (recno <= 0) - { - *errorcodeptr = ERR15; - goto FAILED; - } - } - else if (refsign == CHAR_PLUS) - { - if (recno == 0) - { - *errorcodeptr = ERR58; - goto FAILED; - } - recno += cb->bracount; - } - - 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; - *code = OP_RECURSE; - PUT(code, 1, recno); - code += 1 + LINK_SIZE; - groupsetfirstcu = FALSE; - cb->had_recurse = TRUE; - } - - /* Can't determine a first byte now */ - - if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; - continue; - - - /* ------------------------------------------------------------ */ - default: /* Other characters: check option setting */ - OTHER_CHAR_AFTER_QUERY: - set = unset = 0; - optset = &set; - - while (*ptr != CHAR_RIGHT_PARENTHESIS && *ptr != CHAR_COLON) - { - switch (*ptr++) - { - case CHAR_MINUS: optset = &unset; break; - - case CHAR_J: /* Record that it changed in the external options */ - *optset |= PCRE2_DUPNAMES; - cb->external_flags |= PCRE2_JCHANGED; - break; - - case CHAR_i: *optset |= PCRE2_CASELESS; break; - case CHAR_m: *optset |= PCRE2_MULTILINE; break; - case CHAR_s: *optset |= PCRE2_DOTALL; break; - case CHAR_x: *optset |= PCRE2_EXTENDED; break; - case CHAR_U: *optset |= PCRE2_UNGREEDY; break; - - default: *errorcodeptr = ERR11; - ptr--; /* Correct the offset */ - goto FAILED; - } - } - - /* Set up the changed option bits, but don't change anything yet. */ - - 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. 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) - { - *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 */ - } - - /* If the options ended with ':' we are heading into a nested group - with possible change of options. Such groups are non-capturing and are - not assertions of any kind. All we need to do is skip over the ':'; - the newoptions value is handled below. */ - - bravalue = OP_BRA; - ptr++; - } /* End of switch for character following (? */ - } /* End of (? handling */ - - /* Opening parenthesis not followed by '*' or '?'. If PCRE2_NO_AUTO_CAPTURE - is set, all unadorned brackets become non-capturing and behave like (?:...) - brackets. */ - - else if ((options & PCRE2_NO_AUTO_CAPTURE) != 0) - { - bravalue = OP_BRA; - } - - /* Else we have a capturing group. */ - - else - { - NUMBERED_GROUP: - cb->bracount += 1; - PUT2(code, 1+LINK_SIZE, cb->bracount); - skipunits = IMM2_SIZE; - } - - /* Process nested bracketed regex. First check for parentheses nested too - deeply. */ - - if ((cb->parens_depth += 1) > (int)(cb->cx->parens_nest_limit)) - { - *errorcodeptr = ERR19; - goto FAILED; - } - - /* All assertions used not to be repeatable, but this was changed for Perl - compatibility. All kinds can now be repeated except for assertions that are - conditions (Perl also forbids these to be repeated). We copy code into a - non-register variable (tempcode) in order to be able to pass its address - because some compilers complain otherwise. At the start of a conditional - group whose condition is an assertion, cb->iscondassert is set. We unset it - here so as to allow assertions later in the group to be quantified. */ - - if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT && - cb->iscondassert) - { - previous = NULL; - cb->iscondassert = FALSE; - } - else - { - previous = code; - } - - *code = bravalue; - tempcode = code; - tempreqvary = cb->req_varyopt; /* Save value before bracket */ - tempbracount = cb->bracount; /* Save value before bracket */ - length_prevgroup = 0; /* Initialize for pre-compile phase */ - - if (!compile_regex( - newoptions, /* The complete new option state */ - &tempcode, /* Where to put code (updated) */ - &ptr, /* Input pointer (updated) */ - errorcodeptr, /* Where to put an error message */ - (bravalue == OP_ASSERTBACK || - bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ - reset_bracount, /* True if (?| group */ - skipunits, /* Skip over bracket number */ - cond_depth + - ((bravalue == OP_COND)?1:0), /* Depth of condition subpatterns */ - &subfirstcu, /* For possible first char */ - &subfirstcuflags, - &subreqcu, /* For possible last char */ - &subreqcuflags, - bcptr, /* Current branch chain */ - cb, /* Compile data block */ - (lengthptr == NULL)? NULL : /* Actual compile phase */ - &length_prevgroup /* Pre-compile phase */ - )) - goto FAILED; - - cb->parens_depth -= 1; - - /* If this was an atomic group and there are no capturing groups within it, - generate OP_ONCE_NC instead of OP_ONCE. */ - - if (bravalue == OP_ONCE && cb->bracount <= tempbracount) - *code = OP_ONCE_NC; - - if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT) - cb->assert_depth -= 1; - - /* At the end of compiling, code is still pointing to the start of the - group, while tempcode has been updated to point past the end of the group. - The pattern pointer (ptr) is on the bracket. - - If this is a conditional bracket, check that there are no more than - two branches in the group, or just one if it's a DEFINE group. We do this - in the real compile phase, not in the pre-pass, where the whole group may - not be available. */ - - if (bravalue == OP_COND && lengthptr == NULL) - { - PCRE2_UCHAR *tc = code; - int condcount = 0; - - do { - condcount++; - tc += GET(tc,1); - } - while (*tc != OP_KET); - - /* A DEFINE group is never obeyed inline (the "condition" is always - false). It must have only one branch. Having checked this, change the - opcode to OP_FALSE. */ - - if (code[LINK_SIZE+1] == OP_DEFINE) - { - if (condcount > 1) - { - *errorcodeptr = ERR54; - goto FAILED; - } - code[LINK_SIZE+1] = OP_FALSE; - bravalue = OP_DEFINE; /* Just a flag to suppress char handling below */ - } - - /* A "normal" conditional group. If there is just one branch, we must not - make use of its firstcu or reqcu, because this is equivalent to an - empty second branch. */ - - else - { - if (condcount > 2) - { - *errorcodeptr = ERR27; - goto FAILED; - } - if (condcount == 1) subfirstcuflags = subreqcuflags = REQ_NONE; - } - } - - /* At the end of a group, it's an error if we hit end of pattern or - any non-closing parenthesis. This check also happens in the pre-scan, - so should not trigger here, but leave this code as an insurance. */ - - if (*ptr != CHAR_RIGHT_PARENTHESIS) - { - *errorcodeptr = ERR14; - goto FAILED; - } - - /* In the pre-compile phase, update the length by the length of the group, - less the brackets at either end. Then reduce the compiled code to just a - set of non-capturing brackets so that it doesn't use much memory if it is - duplicated by a quantifier.*/ - - if (lengthptr != NULL) - { - if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE) - { - *errorcodeptr = ERR20; - goto FAILED; - } - *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE; - code++; /* This already contains bravalue */ - PUTINC(code, 0, 1 + LINK_SIZE); - *code++ = OP_KET; - PUTINC(code, 0, 1 + LINK_SIZE); - break; /* No need to waste time with special character handling */ - } - - /* Otherwise update the main code pointer to the end of the group. */ - - code = tempcode; - - /* For a DEFINE group, required and first character settings are not - relevant. */ - - if (bravalue == OP_DEFINE) break; - - /* Handle updating of the required and first characters for other types of - group. Update for normal brackets of all kinds, and conditions with two - branches (see code above). If the bracket is followed by a quantifier with - zero repeat, we have to back off. Hence the definition of zeroreqcu and - zerofirstcu outside the main loop so that they can be accessed for the - back off. */ - - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - groupsetfirstcu = FALSE; - - if (bravalue >= OP_ONCE) - { - /* If we have not yet set a firstcu in this branch, take it from the - subpattern, remembering that it was set here so that a repeat of more - than one can replicate it as reqcu if necessary. If the subpattern has - no firstcu, set "none" for the whole branch. In both cases, a zero - repeat forces firstcu to "none". */ - - if (firstcuflags == REQ_UNSET && subfirstcuflags != REQ_UNSET) - { - if (subfirstcuflags >= 0) - { - firstcu = subfirstcu; - firstcuflags = subfirstcuflags; - groupsetfirstcu = TRUE; - } - else firstcuflags = REQ_NONE; - zerofirstcuflags = REQ_NONE; - } - - /* If firstcu was previously set, convert the subpattern's firstcu - into reqcu if there wasn't one, using the vary flag that was in - existence beforehand. */ - - else if (subfirstcuflags >= 0 && subreqcuflags < 0) - { - subreqcu = subfirstcu; - subreqcuflags = subfirstcuflags | tempreqvary; - } - - /* If the subpattern set a required byte (or set a first byte that isn't - really the first byte - see above), set it. */ - - if (subreqcuflags >= 0) - { - reqcu = subreqcu; - reqcuflags = subreqcuflags; - } - } - - /* For a forward assertion, we take the reqcu, if set. This can be - helpful if the pattern that follows the assertion doesn't set a different - char. For example, it's useful for /(?=abcde).+/. We can't set firstcu - for an assertion, however because it leads to incorrect effect for patterns - such as /(?=a)a.+/ when the "real" "a" would then become a reqcu instead - of a firstcu. This is overcome by a scan at the end if there's no - firstcu, looking for an asserted first char. */ - - else if (bravalue == OP_ASSERT && subreqcuflags >= 0) - { - reqcu = subreqcu; - reqcuflags = subreqcuflags; - } - break; /* End of processing '(' */ - - - /* ===================================================================*/ - /* Handle metasequences introduced by \. For ones like \d, the ESC_ values - are arranged to be the negation of the corresponding OP_values in the - default case when PCRE2_UCP is not set. For the back references, the values - 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. - - Note: \Q and \E are handled at the start of the character-processing loop, - not here. */ - - case CHAR_BACKSLASH: - tempptr = ptr; - 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 - { - /* For metasequences that actually match a character, we disable the - setting of a first character if it hasn't already been set. */ - - if (firstcuflags == REQ_UNSET && escape > ESC_b && escape < ESC_Z) - firstcuflags = REQ_NONE; - - /* Set values to reset to if this is followed by a zero repeat. */ - - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - - /* \g or \g'name' is a subroutine call by name and \g or \g'n' - is a subroutine call by number (Oniguruma syntax). In fact, the value - ESC_g is returned only for these cases. So we don't need to check for < - or ' if the value is ESC_g. For the Perl syntax \g{n} the value is - -n, and for the Perl syntax \g{name} the result is ESC_k (as - that is a synonym for a named back reference). */ - - if (escape == ESC_g) - { - PCRE2_SPTR p; - uint32_t cf; - - terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)? - CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE; - - /* These two statements stop the compiler for warning about possibly - unset variables caused by the jump to HANDLE_NUMERICAL_RECURSION. In - fact, because we do the check for a number below, the paths that - would actually be in error are never taken. */ - - skipunits = 0; - reset_bracount = FALSE; - - /* If it's not a signed or unsigned number, treat it as a name. */ - - cf = ptr[1]; - if (cf != CHAR_PLUS && cf != CHAR_MINUS && !IS_DIGIT(cf)) - { - is_recurse = TRUE; - goto NAMED_REF_OR_RECURSE; - } - - /* Signed or unsigned number (cf = ptr[1]) is known to be plus or minus - or a digit. */ - - p = ptr + 2; - while (IS_DIGIT(*p)) p++; - if (*p != (PCRE2_UCHAR)terminator) - { - *errorcodeptr = ERR57; - goto FAILED; - } - ptr++; - goto HANDLE_NUMERICAL_RECURSION; - } - - /* \k or \k'name' is a back reference by name (Perl syntax). - We also support \k{name} (.NET syntax). */ - - if (escape == ESC_k) - { - if ((ptr[1] != CHAR_LESS_THAN_SIGN && - ptr[1] != CHAR_APOSTROPHE && ptr[1] != CHAR_LEFT_CURLY_BRACKET)) - { - *errorcodeptr = ERR69; - goto FAILED; - } - is_recurse = FALSE; - terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)? - CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)? - CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET; - goto NAMED_REF_OR_RECURSE; - } - - /* Back references are handled specially; must disable firstcu if - not set to cope with cases like (?=(\w+))\1: which would otherwise set - ':' later. */ - - if (escape < 0) - { - open_capitem *oc; - recno = -escape; - - /* Come here from named backref handling when the reference is to a - 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; - *code++ = ((options & PCRE2_CASELESS) != 0)? OP_REFI : OP_REF; - PUT2INC(code, 0, recno); - cb->backref_map |= (recno < 32)? (1u << recno) : 1; - if ((uint32_t)recno > cb->top_backref) cb->top_backref = recno; - - /* Check to see if this back reference is recursive, that it, it - is inside the group that it references. A flag is set so that the - group can be made atomic. */ - - for (oc = cb->open_caps; oc != NULL; oc = oc->next) - { - if (oc->number == recno) - { - oc->flag = TRUE; - break; - } - } - } - - /* So are Unicode property matches, if supported. */ - -#ifdef SUPPORT_UNICODE - else if (escape == ESC_P || escape == ESC_p) - { - BOOL negated; - unsigned int ptype = 0, pdata = 0; - if (!get_ucp(&ptr, &negated, &ptype, &pdata, errorcodeptr, cb)) - goto FAILED; - previous = code; - *code++ = ((escape == ESC_p) != negated)? OP_PROP : OP_NOTPROP; - *code++ = ptype; - *code++ = pdata; - } -#else - - /* If Unicode properties are not supported, \X, \P, and \p are not - allowed. */ - - else if (escape == ESC_X || escape == ESC_P || escape == ESC_p) - { - *errorcodeptr = ERR45; - goto FAILED; - } -#endif - - /* 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 - situation when PCRE2_UCP is not set. When it *is* set, we substitute - Unicode property tests. Note that \b and \B do a one-character - lookbehind, and \A also behaves as if it does. */ - - 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) - { - 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 -#endif - /* In non-UTF mode, and for both 32-bit modes, we turn \C into - OP_ALLANY instead of OP_ANYBYTE so that it works in DFA mode and in - lookbehinds. */ - - { - previous = (escape > ESC_b && escape < ESC_Z)? code : NULL; -#if PCRE2_CODE_UNIT_WIDTH == 32 - *code++ = (escape == ESC_C)? OP_ALLANY : escape; -#else - *code++ = (!utf && escape == ESC_C)? OP_ALLANY : escape; -#endif - } - } - continue; - } - - /* We have a data character whose value is in c. In UTF-8 mode it may have - a value > 127. We set its representation in the length/buffer, and then - handle it as a data character. */ - - mclength = PUTCHAR(c, mcbuffer); - goto ONE_CHAR; - - - /* ===================================================================*/ - /* Handle a literal character. It is guaranteed not to be whitespace or # - when the extended flag is set. If we are in a UTF mode, it may be a - multi-unit literal character. */ - - default: - NORMAL_CHAR: - mclength = 1; - mcbuffer[0] = c; - -#ifdef SUPPORT_UNICODE - if (utf && HAS_EXTRALEN(c)) - ACROSSCHAR(TRUE, ptr[1], mcbuffer[mclength++] = *(++ptr)); -#endif - - /* At this point we have the character's bytes in mcbuffer, and the length - in mclength. When not in UTF mode, the length is always 1. */ - - ONE_CHAR: - previous = code; - - /* 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. */ - -#ifdef SUPPORT_UNICODE - if (utf && (options & PCRE2_CASELESS) != 0) - { - GETCHAR(c, mcbuffer); - if ((c = UCD_CASESET(c)) != 0) - { - *code++ = OP_PROP; - *code++ = PT_CLIST; - *code++ = c; - if (firstcuflags == REQ_UNSET) - firstcuflags = zerofirstcuflags = REQ_NONE; - break; - } - } -#endif - - /* Caseful matches, or not one of the multicase characters. */ - - *code++ = ((options & PCRE2_CASELESS) != 0)? OP_CHARI : OP_CHAR; - for (c = 0; c < mclength; c++) *code++ = mcbuffer[c]; - - /* Remember if \r or \n were seen */ - - if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL) - cb->external_flags |= PCRE2_HASCRORLF; - - /* Set the first and required bytes appropriately. If no previous first - byte, set it from this character, but revert to none on a zero repeat. - Otherwise, leave the firstcu value alone, and don't change it on a zero - repeat. */ - - if (firstcuflags == REQ_UNSET) - { - zerofirstcuflags = REQ_NONE; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - - /* If the character is more than one byte long, we can set firstcu - only if it is not to be matched caselessly. */ - - if (mclength == 1 || req_caseopt == 0) - { - firstcu = mcbuffer[0] | req_caseopt; - firstcu = mcbuffer[0]; - firstcuflags = req_caseopt; - - if (mclength != 1) - { - reqcu = code[-1]; - reqcuflags = cb->req_varyopt; - } - } - else firstcuflags = reqcuflags = REQ_NONE; - } - - /* firstcu was previously set; we can set reqcu only if the length is - 1 or the matching is caseful. */ - - else - { - zerofirstcu = firstcu; - zerofirstcuflags = firstcuflags; - zeroreqcu = reqcu; - zeroreqcuflags = reqcuflags; - if (mclength == 1 || req_caseopt == 0) - { - reqcu = code[-1]; - reqcuflags = req_caseopt | cb->req_varyopt; - } - } - - break; /* End of literal character handling */ - } - } /* end of big loop */ - -/* Control never reaches here by falling through, only by a goto for all the -error states. Pass back the position in the pattern so that it can be displayed -to the user for diagnosing the error. */ - -FAILED: -*ptrptr = ptr; -return FALSE; -} - - - -/************************************************* -* Compile regex: a sequence of alternatives * -*************************************************/ - -/* On entry, ptr is pointing past the bracket character, but on return it -points to the closing bracket, or vertical bar, or end of string. The code -variable is pointing at the byte into which the BRA operator has been stored. -This function is used during the pre-compile phase when we are trying to find -out the amount of memory needed, as well as during the real compile phase. The -value of lengthptr distinguishes the two phases. - -Arguments: - options option bits, including any changes for this subpattern - codeptr -> the address of the current code pointer - ptrptr -> the address of the current pattern pointer - errorcodeptr -> pointer to error code variable - lookbehind TRUE if this is a lookbehind assertion - reset_bracount TRUE to reset the count for each branch - skipunits skip this many code units at start (for brackets and OP_COND) - cond_depth depth of nesting for conditional subpatterns - firstcuptr place to put the first required code unit - firstcuflagsptr place to put the first code unit flags, or a negative number - reqcuptr place to put the last required code unit - reqcuflagsptr place to put the last required code unit flags, or a negative number - bcptr pointer to the chain of currently open branches - cb points to the data block with tables pointers etc. - lengthptr NULL during the real compile phase - points to length accumulator during pre-compile phase - -Returns: TRUE on success -*/ - -static BOOL -compile_regex(uint32_t options, PCRE2_UCHAR **codeptr, PCRE2_SPTR *ptrptr, - int *errorcodeptr, BOOL lookbehind, BOOL reset_bracount, uint32_t skipunits, - int cond_depth, uint32_t *firstcuptr, int32_t *firstcuflagsptr, - uint32_t *reqcuptr, int32_t *reqcuflagsptr, branch_chain *bcptr, - compile_block *cb, size_t *lengthptr) -{ -PCRE2_SPTR ptr = *ptrptr; -PCRE2_UCHAR *code = *codeptr; -PCRE2_UCHAR *last_branch = code; -PCRE2_UCHAR *start_bracket = code; -PCRE2_UCHAR *reverse_count = NULL; -open_capitem capitem; -int capnumber = 0; -uint32_t firstcu, reqcu; -int32_t firstcuflags, reqcuflags; -uint32_t branchfirstcu, branchreqcu; -int32_t branchfirstcuflags, branchreqcuflags; -size_t length; -unsigned int orig_bracount; -unsigned int max_bracount; -branch_chain bc; - -/* If set, call the external function that checks for stack availability. */ - -if (cb->cx->stack_guard != NULL && - cb->cx->stack_guard(cb->parens_depth, cb->cx->stack_guard_data)) - { - *errorcodeptr= ERR33; - return FALSE; - } - -/* Miscellaneous initialization */ - -bc.outer = bcptr; -bc.current_branch = code; - -firstcu = reqcu = 0; -firstcuflags = reqcuflags = REQ_UNSET; - -/* 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 -lengthptr for NULL. We cannot do this by looking at the value of 'code' at the -start and end of each alternative, because compiled items are discarded during -the pre-compile phase so that the work space is not exceeded. */ - -length = 2 + 2*LINK_SIZE + skipunits; - -/* WARNING: If the above line is changed for any reason, you must also change -the code that abstracts option settings at the start of the pattern and makes -them global. It tests the value of length for (2 + 2*LINK_SIZE) in the -pre-compile phase to find out whether or not anything has yet been compiled. - -If this is a capturing subpattern, add to the chain of open capturing items -so that we can detect them if (*ACCEPT) is encountered. This is also used to -detect groups that contain recursive back references to themselves. Note that -only OP_CBRA need be tested here; changing this opcode to one of its variants, -e.g. OP_SCBRAPOS, happens later, after the group has been compiled. */ - -if (*code == OP_CBRA) - { - capnumber = GET2(code, 1 + LINK_SIZE); - capitem.number = capnumber; - capitem.next = cb->open_caps; - capitem.flag = FALSE; - cb->open_caps = &capitem; - } - -/* Offset is set zero to mark that this bracket is still open */ - -PUT(code, 1, 0); -code += 1 + LINK_SIZE + skipunits; - -/* Loop for each alternative branch */ - -orig_bracount = max_bracount = cb->bracount; - -for (;;) - { - /* For a (?| group, reset the capturing bracket count so that each branch - uses the same numbers. */ - - if (reset_bracount) cb->bracount = orig_bracount; - - /* Set up dummy OP_REVERSE if lookbehind assertion */ - - if (lookbehind) - { - *code++ = OP_REVERSE; - reverse_count = code; - PUTINC(code, 0, 0); - length += 1 + LINK_SIZE; - } - - /* Now compile the branch; in the pre-compile phase its length gets added - into the length. */ - - if (!compile_branch(&options, &code, &ptr, errorcodeptr, &branchfirstcu, - &branchfirstcuflags, &branchreqcu, &branchreqcuflags, &bc, - cond_depth, cb, (lengthptr == NULL)? NULL : &length)) - { - *ptrptr = ptr; - return FALSE; - } - - /* Keep the highest bracket count in case (?| was used and some branch - has fewer than the rest. */ - - if (cb->bracount > max_bracount) max_bracount = cb->bracount; - - /* In the real compile phase, there is some post-processing to be done. */ - - if (lengthptr == NULL) - { - /* If this is the first branch, the firstcu and reqcu values for the - branch become the values for the regex. */ - - if (*last_branch != OP_ALT) - { - firstcu = branchfirstcu; - firstcuflags = branchfirstcuflags; - reqcu = branchreqcu; - reqcuflags = branchreqcuflags; - } - - /* If this is not the first branch, the first char and reqcu have to - match the values from all the previous branches, except that if the - previous value for reqcu didn't have REQ_VARY set, it can still match, - and we set REQ_VARY for the regex. */ - - else - { - /* If we previously had a firstcu, but it doesn't match the new branch, - we have to abandon the firstcu for the regex, but if there was - previously no reqcu, it takes on the value of the old firstcu. */ - - if (firstcuflags != branchfirstcuflags || firstcu != branchfirstcu) - { - if (firstcuflags >= 0) - { - if (reqcuflags < 0) - { - reqcu = firstcu; - reqcuflags = firstcuflags; - } - } - firstcuflags = REQ_NONE; - } - - /* If we (now or from before) have no firstcu, a firstcu from the - branch becomes a reqcu if there isn't a branch reqcu. */ - - if (firstcuflags < 0 && branchfirstcuflags >= 0 && - branchreqcuflags < 0) - { - branchreqcu = branchfirstcu; - branchreqcuflags = branchfirstcuflags; - } - - /* Now ensure that the reqcus match */ - - if (((reqcuflags & ~REQ_VARY) != (branchreqcuflags & ~REQ_VARY)) || - reqcu != branchreqcu) - reqcuflags = REQ_NONE; - else - { - reqcu = branchreqcu; - reqcuflags |= branchreqcuflags; /* To "or" REQ_VARY */ - } - } - - /* 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 - 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, &count); - if (fixed_length == FFL_LATER) - { - cb->check_lookbehind = TRUE; - } - else if (fixed_length < 0) - { - *errorcodeptr = fixed_length_errors[-fixed_length]; - *ptrptr = ptr; - return FALSE; - } - else - { - if (fixed_length > cb->max_lookbehind) - cb->max_lookbehind = fixed_length; - PUT(reverse_count, 0, fixed_length); - } - } - } - - /* Reached end of expression, either ')' or end of pattern. In the real - compile phase, go back through the alternative branches and reverse the chain - of offsets, with the field in the BRA item now becoming an offset to the - first alternative. If there are no alternatives, it points to the end of the - group. The length in the terminating ket is always the length of the whole - bracketed item. Return leaving the pointer at the terminating char. */ - - if (*ptr != CHAR_VERTICAL_LINE) - { - if (lengthptr == NULL) - { - size_t branch_length = code - last_branch; - do - { - size_t prev_length = GET(last_branch, 1); - PUT(last_branch, 1, branch_length); - branch_length = prev_length; - last_branch -= branch_length; - } - while (branch_length > 0); - } - - /* Fill in the ket */ - - *code = OP_KET; - PUT(code, 1, (int)(code - start_bracket)); - 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. In - any event, remove the block from the chain. */ - - if (capnumber > 0) - { - if (cb->open_caps->flag) - { - memmove(start_bracket + 1 + LINK_SIZE, start_bracket, - CU2BYTES(code - start_bracket)); - *start_bracket = OP_ONCE; - code += 1 + LINK_SIZE; - PUT(start_bracket, 1, (int)(code - start_bracket)); - *code = OP_KET; - PUT(code, 1, (int)(code - start_bracket)); - code += 1 + LINK_SIZE; - length += 2 + 2*LINK_SIZE; - } - cb->open_caps = cb->open_caps->next; - } - - /* Retain the highest bracket number, in case resetting was used. */ - - cb->bracount = max_bracount; - - /* Set values to pass back */ - - *codeptr = code; - *ptrptr = ptr; - *firstcuptr = firstcu; - *firstcuflagsptr = firstcuflags; - *reqcuptr = reqcu; - *reqcuflagsptr = reqcuflags; - if (lengthptr != NULL) - { - if (OFLOW_MAX - *lengthptr < length) - { - *errorcodeptr = ERR20; - return FALSE; - } - *lengthptr += length; - } - return TRUE; - } - - /* Another branch follows. In the pre-compile phase, we can move the code - pointer back to where it was for the start of the first branch. (That is, - pretend that each branch is the only one.) - - In the real compile phase, insert an ALT node. Its length field points back - to the previous branch while the bracket remains open. At the end the chain - is reversed. It's done like this so that the start of the bracket has a - zero offset until it is closed, making it possible to detect recursion. */ - - if (lengthptr != NULL) - { - code = *codeptr + 1 + LINK_SIZE + skipunits; - length += 1 + LINK_SIZE; - } - else - { - *code = OP_ALT; - PUT(code, 1, (int)(code - last_branch)); - bc.current_branch = last_branch = code; - code += 1 + LINK_SIZE; - } - - /* Advance past the vertical bar */ - - ptr++; - } -/* Control never reaches here */ -} - - - -/************************************************* -* Check for anchored pattern * -*************************************************/ - -/* Try to find out if this is an anchored regular expression. Consider each -alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket -all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then -it's anchored. However, if this is a multiline pattern, then only OP_SOD will -be found, because ^ generates OP_CIRCM in that mode. - -We can also consider a regex to be anchored if OP_SOM starts all its branches. -This is the code for \G, which means "match at start of match position, taking -into account the match offset". - -A branch is also implicitly anchored if it starts with .* and DOTALL is set, -because that will try the rest of the pattern at all possible matching points, -so there is no point trying again.... er .... - -.... except when the .* appears inside capturing parentheses, and there is a -subsequent back reference to those parentheses. We haven't enough information -to catch that case precisely. - -At first, the best we could do was to detect when .* was in capturing brackets -and the highest back reference was greater than or equal to that level. -However, by keeping a bitmap of the first 31 back references, we can catch some -of the more common cases more precisely. - -... A second exception is when the .* appears inside an atomic group, because -this prevents the number of characters it matches from being adjusted. - -Arguments: - code points to start of the compiled pattern - bracket_map a bitmap of which brackets we are inside while testing; this - handles up to substring 31; after that we just have to take - the less precise approach - cb points to the compile data block - atomcount atomic group level - -Returns: TRUE or FALSE -*/ - -static BOOL -is_anchored(register PCRE2_SPTR code, unsigned int bracket_map, - compile_block *cb, int atomcount) -{ -do { - PCRE2_SPTR scode = first_significant_code( - code + PRIV(OP_lengths)[*code], FALSE); - register int op = *scode; - - /* Non-capturing brackets */ - - if (op == OP_BRA || op == OP_BRAPOS || - op == OP_SBRA || op == OP_SBRAPOS) - { - if (!is_anchored(scode, bracket_map, cb, atomcount)) return FALSE; - } - - /* Capturing brackets */ - - else if (op == OP_CBRA || op == OP_CBRAPOS || - op == OP_SCBRA || op == OP_SCBRAPOS) - { - int n = GET2(scode, 1+LINK_SIZE); - int new_map = bracket_map | ((n < 32)? (1u << n) : 1); - if (!is_anchored(scode, new_map, cb, atomcount)) return FALSE; - } - - /* Positive forward assertions and conditions */ - - else if (op == OP_ASSERT || op == OP_COND) - { - if (!is_anchored(scode, bracket_map, cb, atomcount)) return FALSE; - } - - /* Atomic groups */ - - else if (op == OP_ONCE || op == OP_ONCE_NC) - { - if (!is_anchored(scode, bracket_map, cb, atomcount + 1)) - return FALSE; - } - - /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and - it isn't in brackets that are or may be referenced or inside an atomic - group. There is also an option that disables auto-anchoring. */ - - else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR || - op == OP_TYPEPOSSTAR)) - { - if (scode[1] != OP_ALLANY || (bracket_map & cb->backref_map) != 0 || - atomcount > 0 || cb->had_pruneorskip || - (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0) - return FALSE; - } - - /* Check for explicit anchoring */ - - else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE; - - code += GET(code, 1); - } -while (*code == OP_ALT); /* Loop for each alternative */ -return TRUE; -} - - - -/************************************************* -* Check for starting with ^ or .* * -*************************************************/ - -/* This is called to find out if every branch starts with ^ or .* so that -"first char" processing can be done to speed things up in multiline -matching and for non-DOTALL patterns that start with .* (which must start at -the beginning or after \n). As in the case of is_anchored() (see above), we -have to take account of back references to capturing brackets that contain .* -because in that case we can't make the assumption. Also, the appearance of .* -inside atomic brackets or in a pattern that contains *PRUNE or *SKIP does not -count, because once again the assumption no longer holds. - -Arguments: - code points to start of the compiled pattern or a group - bracket_map a bitmap of which brackets we are inside while testing; this - handles up to substring 31; after that we just have to take - the less precise approach - cb points to the compile data - atomcount atomic group level - -Returns: TRUE or FALSE -*/ - -static BOOL -is_startline(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb, - int atomcount) -{ -do { - PCRE2_SPTR scode = first_significant_code( - code + PRIV(OP_lengths)[*code], FALSE); - register int op = *scode; - - /* If we are at the start of a conditional assertion group, *both* the - conditional assertion *and* what follows the condition must satisfy the test - for start of line. Other kinds of condition fail. Note that there may be an - auto-callout at the start of a condition. */ - - if (op == OP_COND) - { - scode += 1 + LINK_SIZE; - - if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT]; - else if (*scode == OP_CALLOUT_STR) scode += GET(scode, 1 + 2*LINK_SIZE); - - switch (*scode) - { - case OP_CREF: - case OP_DNCREF: - case OP_RREF: - case OP_DNRREF: - case OP_FAIL: - case OP_FALSE: - case OP_TRUE: - return FALSE; - - default: /* Assertion */ - if (!is_startline(scode, bracket_map, cb, atomcount)) return FALSE; - do scode += GET(scode, 1); while (*scode == OP_ALT); - scode += 1 + LINK_SIZE; - break; - } - scode = first_significant_code(scode, FALSE); - op = *scode; - } - - /* Non-capturing brackets */ - - if (op == OP_BRA || op == OP_BRAPOS || - op == OP_SBRA || op == OP_SBRAPOS) - { - if (!is_startline(scode, bracket_map, cb, atomcount)) return FALSE; - } - - /* Capturing brackets */ - - else if (op == OP_CBRA || op == OP_CBRAPOS || - op == OP_SCBRA || op == OP_SCBRAPOS) - { - int n = GET2(scode, 1+LINK_SIZE); - int new_map = bracket_map | ((n < 32)? (1u << n) : 1); - if (!is_startline(scode, new_map, cb, atomcount)) return FALSE; - } - - /* Positive forward assertions */ - - else if (op == OP_ASSERT) - { - if (!is_startline(scode, bracket_map, cb, atomcount)) return FALSE; - } - - /* Atomic brackets */ - - else if (op == OP_ONCE || op == OP_ONCE_NC) - { - if (!is_startline(scode, bracket_map, cb, atomcount + 1)) return FALSE; - } - - /* .* means "start at start or after \n" if it isn't in atomic brackets or - brackets that may be referenced, as long as the pattern does not contain - *PRUNE or *SKIP, because these break the feature. Consider, for example, - /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab", i.e. not at the - start of a line. There is also an option that disables this optimization. */ - - else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR) - { - if (scode[1] != OP_ANY || (bracket_map & cb->backref_map) != 0 || - atomcount > 0 || cb->had_pruneorskip || - (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0) - return FALSE; - } - - /* Check for explicit circumflex; anything else gives a FALSE result. Note - in particular that this includes atomic brackets OP_ONCE and OP_ONCE_NC - because the number of characters matched by .* cannot be adjusted inside - them. */ - - else if (op != OP_CIRC && op != OP_CIRCM) return FALSE; - - /* Move on to the next alternative */ - - code += GET(code, 1); - } -while (*code == OP_ALT); /* Loop for each alternative */ -return TRUE; -} - - - -/************************************************* -* Check for asserted fixed first code unit * -*************************************************/ - -/* During compilation, the "first code unit" settings from forward assertions -are discarded, because they can cause conflicts with actual literals that -follow. However, if we end up without a first code unit setting for an -unanchored pattern, it is worth scanning the regex to see if there is an -initial asserted first code unit. If all branches start with the same asserted -code unit, or with a non-conditional bracket all of whose alternatives start -with the same asserted code unit (recurse ad lib), then we return that code -unit, with the flags set to zero or REQ_CASELESS; otherwise return zero with -REQ_NONE in the flags. - -Arguments: - code points to start of compiled pattern - flags points to the first code unit flags - inassert TRUE if in an assertion - -Returns: the fixed first code unit, or 0 with REQ_NONE in flags -*/ - -static uint32_t -find_firstassertedcu(PCRE2_SPTR code, int32_t *flags, BOOL inassert) -{ -register uint32_t c = 0; -int cflags = REQ_NONE; - -*flags = REQ_NONE; -do { - uint32_t d; - int dflags; - int xl = (*code == OP_CBRA || *code == OP_SCBRA || - *code == OP_CBRAPOS || *code == OP_SCBRAPOS)? IMM2_SIZE:0; - PCRE2_SPTR scode = first_significant_code(code + 1+LINK_SIZE + xl, TRUE); - register PCRE2_UCHAR op = *scode; - - switch(op) - { - default: - return 0; - - case OP_BRA: - case OP_BRAPOS: - case OP_CBRA: - case OP_SCBRA: - case OP_CBRAPOS: - case OP_SCBRAPOS: - case OP_ASSERT: - case OP_ONCE: - case OP_ONCE_NC: - d = find_firstassertedcu(scode, &dflags, op == OP_ASSERT); - if (dflags < 0) - return 0; - if (cflags < 0) { c = d; cflags = dflags; } - else if (c != d || cflags != dflags) return 0; - break; - - case OP_EXACT: - scode += IMM2_SIZE; - /* Fall through */ - - case OP_CHAR: - case OP_PLUS: - case OP_MINPLUS: - case OP_POSPLUS: - if (!inassert) return 0; - if (cflags < 0) { c = scode[1]; cflags = 0; } - else if (c != scode[1]) return 0; - break; - - case OP_EXACTI: - scode += IMM2_SIZE; - /* Fall through */ - - case OP_CHARI: - case OP_PLUSI: - case OP_MINPLUSI: - case OP_POSPLUSI: - if (!inassert) return 0; - if (cflags < 0) { c = scode[1]; cflags = REQ_CASELESS; } - else if (c != scode[1]) return 0; - break; - } - - code += GET(code, 1); - } -while (*code == OP_ALT); - -*flags = cflags; -return c; -} - - - -/************************************************* -* Add an entry to the name/number table * -*************************************************/ - -/* This function is called between compiling passes to add an entry to the -name/number table, maintaining alphabetical order. Checking for permitted -and forbidden duplicates has already been done. - -Arguments: - cb the compile data block - name the name to add - length the length of the name - groupno the group number - -Returns: nothing -*/ - -static void -add_name_to_table(compile_block *cb, PCRE2_SPTR name, int length, - unsigned int groupno) -{ -int i; -PCRE2_UCHAR *slot = cb->name_table; - -for (i = 0; i < cb->names_found; i++) - { - int crc = memcmp(name, slot+IMM2_SIZE, CU2BYTES(length)); - if (crc == 0 && slot[IMM2_SIZE+length] != 0) - crc = -1; /* Current name is a substring */ - - /* Make space in the table and break the loop for an earlier name. For a - duplicate or later name, carry on. We do this for duplicates so that in the - simple case (when ?(| is not used) they are in order of their numbers. In all - cases they are in the order in which they appear in the pattern. */ - - if (crc < 0) - { - memmove(slot + cb->name_entry_size, slot, - CU2BYTES((cb->names_found - i) * cb->name_entry_size)); - break; - } - - /* Continue the loop for a later or duplicate name */ - - slot += cb->name_entry_size; - } - -PUT2(slot, 0, groupno); -memcpy(slot + IMM2_SIZE, name, CU2BYTES(length)); -cb->names_found++; - -/* Add a terminating zero and fill the rest of the slot with zeroes so that -the memory is all initialized. Otherwise valgrind moans about uninitialized -memory when saving serialized compiled patterns. */ - -memset(slot + IMM2_SIZE + length, 0, - CU2BYTES(cb->name_entry_size - length - IMM2_SIZE)); -} - - - -/************************************************* -* External function to compile a pattern * -*************************************************/ - -/* This function reads a regular expression in the form of a string and returns -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 PCRE2_ZERO_TERMINATED - options option bits - errorptr pointer to errorcode - erroroffset pointer to error offset - ccontext points to a compile context or is NULL - -Returns: pointer to compiled data block, or NULL on error, - with errorcode and erroroffset set -*/ - -PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION -pcre2_compile(PCRE2_SPTR pattern, PCRE2_SIZE patlen, uint32_t options, - int *errorptr, PCRE2_SIZE *erroroffset, pcre2_compile_context *ccontext) -{ -BOOL utf; /* Set TRUE for UTF mode */ -pcre2_real_code *re = NULL; /* What we will return */ -compile_block cb; /* "Static" compile-time data */ -const uint8_t *tables; /* Char tables base pointer */ - -PCRE2_UCHAR *code; /* Current pointer in compiled code */ -PCRE2_SPTR codestart; /* Start of compiled code */ -PCRE2_SPTR ptr; /* Current pointer in pattern */ - -size_t length = 1; /* Allow or final END opcode */ -size_t usedlength; /* Actual length used */ -size_t re_blocksize; /* Size of memory block */ - -int32_t firstcuflags, reqcuflags; /* Type of first/req code unit */ -uint32_t firstcu, reqcu; /* Value of first/req code unit */ -uint32_t setflags = 0; /* NL and BSR set flags */ - -uint32_t skipatstart; /* When checking (*UTF) etc */ -uint32_t limit_match = UINT32_MAX; /* Unset match limits */ -uint32_t limit_recursion = UINT32_MAX; - -int newline = 0; /* Unset; can be set by the pattern */ -int bsr = 0; /* Unset; can be set by the pattern */ -int errorcode = 0; /* Initialize to avoid compiler warn */ - -/* Comments at the head of this file explain about these variables. */ - -PCRE2_UCHAR *copied_pattern = NULL; -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. -It needs to be 16-bit aligned for the preliminary group scan, and 32-bit -aligned for the group information cache. */ - -uint32_t c32workspace[C32_WORK_SIZE]; -PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c32workspace; - - -/* -------------- Check arguments and set up the pattern ----------------- */ - -/* There must be error code and offset pointers. */ - -if (errorptr == NULL || erroroffset == NULL) return NULL; -*errorptr = ERR0; -*erroroffset = 0; - -/* There must be a pattern! */ - -if (pattern == NULL) - { - *errorptr = ERR16; - return NULL; - } - -/* Check that all undefined public option bits are zero. */ - -if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0) - { - *errorptr = ERR17; - return NULL; - } - -/* A NULL compile context means "use a default context" */ - -if (ccontext == NULL) - ccontext = (pcre2_compile_context *)(&PRIV(default_compile_context)); - -/* 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. In both cases, check that the pattern is overlong. */ - -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 - { - copied_pattern = ccontext->memctl.malloc(CU2BYTES(patlen + 1), - ccontext->memctl.memory_data); - if (copied_pattern == NULL) - { - *errorptr = ERR21; - return NULL; - } - } - memcpy(copied_pattern, pattern, CU2BYTES(patlen)); - copied_pattern[patlen] = 0; - pattern = copied_pattern; - } - -/* ------------ Initialize the "static" compile data -------------- */ - -tables = (ccontext->tables != NULL)? ccontext->tables : PRIV(default_tables); - -cb.lcc = tables + lcc_offset; /* Individual */ -cb.fcc = tables + fcc_offset; /* character */ -cb.cbits = tables + cbits_offset; /* tables */ -cb.ctypes = tables + ctypes_offset; - -cb.assert_depth = 0; -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.groupinfo = c32workspace; -cb.had_recurse = FALSE; -cb.iscondassert = FALSE; -cb.max_lookbehind = 0; -cb.name_entry_size = 0; -cb.name_table = NULL; -cb.named_groups = named_groups; -cb.named_group_list_size = NAMED_GROUP_LIST_SIZE; -cb.names_found = 0; -cb.open_caps = NULL; -cb.parens_depth = 0; -cb.req_varyopt = 0; -cb.start_code = cworkspace; -cb.start_pattern = pattern; -cb.start_workspace = cworkspace; -cb.workspace_size = COMPILE_WORK_SIZE; - -/* Maximum back reference and backref bitmap. The bitmap records up to 31 back -references to help in deciding whether (.*) can be treated as anchored or not. -*/ - -cb.top_backref = 0; -cb.backref_map = 0; - -/* --------------- Start looking at the pattern --------------- */ - -/* Check for global one-time option settings at the start of the pattern, and -remember the offset to the actual regex. */ - -ptr = pattern; -skipatstart = 0; - -while (ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && - ptr[skipatstart+1] == CHAR_ASTERISK) - { - unsigned int i; - for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++) - { - pso *p = pso_list + i; - - if (PRIV(strncmp_c8)(ptr+skipatstart+2, (char *)(p->name), p->length) == 0) - { - uint32_t c, pp; - - skipatstart += p->length + 2; - switch(p->type) - { - case PSO_OPT: - cb.external_options |= p->value; - break; - - case PSO_FLG: - setflags |= p->value; - break; - - case PSO_NL: - newline = p->value; - setflags |= PCRE2_NL_SET; - break; - - case PSO_BSR: - bsr = p->value; - setflags |= PCRE2_BSR_SET; - break; - - case PSO_LIMM: - 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); - } - if (ptr[pp++] != CHAR_RIGHT_PARENTHESIS) - { - errorcode = ERR60; - ptr += pp; - goto HAD_ERROR; - } - if (p->type == PSO_LIMM) limit_match = c; - else limit_recursion = c; - skipatstart += pp - skipatstart; - break; - } - break; /* Out of the table scan loop */ - } - } - if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */ - } - -/* End of pattern-start options; advance to start of real regex. */ - -ptr += skipatstart; - -/* Can't support UTF or UCP unless PCRE2 has been compiled with UTF support. */ - -#ifndef SUPPORT_UNICODE -if ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0) - { - errorcode = ERR32; - goto HAD_ERROR; - } -#endif - -/* Check UTF. We have the original options in 'options', with that value as -modified by (*UTF) etc in cb->external_options. */ - -utf = (cb.external_options & PCRE2_UTF) != 0; -if (utf) - { - if ((options & PCRE2_NEVER_UTF) != 0) - { - errorcode = ERR74; - goto HAD_ERROR; - } - if ((options & PCRE2_NO_UTF_CHECK) == 0 && - (errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0) - goto HAD_UTF_ERROR; - } - -/* Check UCP lockout. */ - -if ((cb.external_options & (PCRE2_UCP|PCRE2_NEVER_UCP)) == - (PCRE2_UCP|PCRE2_NEVER_UCP)) - { - errorcode = ERR75; - goto HAD_ERROR; - } - -/* Process the BSR setting. */ - -if (bsr == 0) bsr = ccontext->bsr_convention; - -/* Process the newline setting. */ - -if (newline == 0) newline = ccontext->newline_convention; -cb.nltype = NLTYPE_FIXED; -switch(newline) - { - case PCRE2_NEWLINE_CR: - cb.nllen = 1; - cb.nl[0] = CHAR_CR; - break; - - case PCRE2_NEWLINE_LF: - cb.nllen = 1; - cb.nl[0] = CHAR_NL; - break; - - case PCRE2_NEWLINE_CRLF: - cb.nllen = 2; - cb.nl[0] = CHAR_CR; - cb.nl[1] = CHAR_NL; - break; - - case PCRE2_NEWLINE_ANY: - cb.nltype = NLTYPE_ANY; - break; - - case PCRE2_NEWLINE_ANYCRLF: - cb.nltype = NLTYPE_ANYCRLF; - break; - - default: - errorcode = ERR56; - goto HAD_ERROR; - } - -/* Before we do anything else, do a pre-scan of the pattern in order to -discover the named groups and their numerical equivalents, so that this -information is always available for the remaining processing. */ - -errorcode = scan_for_captures(&ptr, cb.external_options, &cb); -if (errorcode != 0) goto HAD_ERROR; - -/* For obscure debugging this code can be enabled. */ - -#if 0 - { - int i; - named_group *ng = cb.named_groups; - fprintf(stderr, "+++Captures: %d\n", cb.final_bracount); - for (i = 0; i < cb.names_found; i++, ng++) - { - fprintf(stderr, "+++%3d %.*s\n", ng->number, ng->length, ng->name); - } - } -#endif - -/* Reset current bracket count to zero and current pointer to the start of the -pattern. */ - -cb.bracount = 0; -ptr = pattern + skipatstart; - -/* Pretend to compile the pattern while actually just accumulating the amount -of memory required in the 'length' variable. This behaviour is triggered by -passing a non-NULL final argument to compile_regex(). We pass a block of -workspace (cworkspace) for it to compile parts of the pattern into; the -compiled code is discarded when it is no longer needed, so hopefully this -workspace will never overflow, though there is a test for its doing so. - -On error, errorcode will be set non-zero, so we don't need to look at the -result of the function. The initial options have been put into the cb block so -that they can be changed if an option setting is found within the regex right -at the beginning. Bringing initial option settings outside can help speed up -starting point checks. We still have to pass a separate options variable (the -first argument) because that may change as the pattern is processed. */ - -code = cworkspace; -*code = OP_BRA; - -(void)compile_regex(cb.external_options, &code, &ptr, &errorcode, FALSE, - FALSE, 0, 0, &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, - &cb, &length); - -if (errorcode != 0) goto HAD_ERROR; -if (length > MAX_PATTERN_SIZE) - { - errorcode = ERR20; - goto HAD_ERROR; - } - -/* Compute the size of, and then get and initialize, the data block for storing -the compiled pattern and names table. Integer overflow should no longer be -possible because nowadays we limit the maximum value of cb.names_found and -cb.name_entry_size. */ - -re_blocksize = sizeof(pcre2_real_code) + - CU2BYTES(length + cb.names_found * cb.name_entry_size); -re = (pcre2_real_code *) - ccontext->memctl.malloc(re_blocksize, ccontext->memctl.memory_data); -if (re == NULL) - { - errorcode = ERR21; - goto HAD_ERROR; - } - -re->memctl = ccontext->memctl; -re->tables = tables; -re->executable_jit = NULL; -memset(re->start_bitmap, 0, 32 * sizeof(uint8_t)); -re->blocksize = re_blocksize; -re->magic_number = MAGIC_NUMBER; -re->compile_options = options; -re->overall_options = cb.external_options; -re->flags = PCRE2_CODE_UNIT_WIDTH/8 | cb.external_flags | setflags; -re->limit_match = limit_match; -re->limit_recursion = limit_recursion; -re->first_codeunit = 0; -re->last_codeunit = 0; -re->bsr_convention = bsr; -re->newline_convention = newline; -re->max_lookbehind = 0; -re->minlength = 0; -re->top_bracket = 0; -re->top_backref = 0; -re->name_entry_size = cb.name_entry_size; -re->name_count = cb.names_found; - -/* The basic block is immediately followed by the name table, and the compiled -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. */ - -cb.parens_depth = 0; -cb.assert_depth = 0; -cb.bracount = 0; -cb.max_lookbehind = 0; -cb.name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)); -cb.start_code = codestart; -cb.iscondassert = FALSE; -cb.req_varyopt = 0; -cb.had_accept = FALSE; -cb.had_pruneorskip = FALSE; -cb.check_lookbehind = FALSE; -cb.open_caps = NULL; - -/* If any named groups were found, create the name/number table from the list -created in the pre-pass. */ - -if (cb.names_found > 0) - { - int i = cb.names_found; - named_group *ng = cb.named_groups; - cb.names_found = 0; - for (; i > 0; i--, ng++) - add_name_to_table(&cb, ng->name, ng->length, ng->number); - } - -/* Set up a starting, non-extracting bracket, then compile the expression. On -error, errorcode will be set non-zero, so we don't need to look at the result -of the function here. */ - -ptr = pattern + skipatstart; -code = (PCRE2_UCHAR *)codestart; -*code = OP_BRA; -(void)compile_regex(re->overall_options, &code, &ptr, &errorcode, FALSE, FALSE, - 0, 0, &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, NULL); - -re->top_bracket = cb.bracount; -re->top_backref = cb.top_backref; -re->max_lookbehind = cb.max_lookbehind; - -if (cb.had_accept) - { - reqcu = 0; /* Must disable after (*ACCEPT) */ - reqcuflags = REQ_NONE; - } - -/* Fill in the final opcode and check for disastrous overflow. If no overflow, -but the estimated length exceeds the really used length, adjust the value of -re->blocksize, and if valgrind support is configured, mark the extra allocated -memory as unaddressable, so that any out-of-bound reads can be detected. */ - -*code++ = OP_END; -usedlength = code - codestart; -if (usedlength > length) errorcode = ERR23; else - { - re->blocksize -= CU2BYTES(length - usedlength); -#ifdef SUPPORT_VALGRIND - VALGRIND_MAKE_MEM_NOACCESS(code, CU2BYTES(length - usedlength)); -#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. */ - -#ifdef CALL_PRINTINT -pcre2_printint(re, stderr, TRUE); -fprintf(stderr, "Length=%lu Used=%lu\n", length, usedlength); -#endif - -/* 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 -the appropriate opcode values, so the type of the pointer must be cast. NOTE: -the intermediate variable "temp" is used in this code because at least one -compiler gives a warning about loss of "const" attribute if the cast -(PCRE2_UCHAR *)codestart is used directly in the function call. */ - -if (errorcode == 0) - { - if (re->top_backref > re->top_bracket) errorcode = ERR15; - 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; - } - } - -/* If there were any lookbehind assertions that contained OP_RECURSE -(recursions or subroutine calls), a flag is set for them to be checked here, -because they may contain forward references. Actual recursions cannot be fixed -length, but subroutine calls can. It is done like this so that those without -OP_RECURSE that are not fixed length get a diagnosic with a useful offset. The -exceptional ones forgo this. We scan the pattern to check that they are fixed -length, and set their lengths. */ - -if (errorcode == 0 && cb.check_lookbehind) - { - PCRE2_UCHAR *cc = (PCRE2_UCHAR *)codestart; - - /* Loop, searching for OP_REVERSE items, and process those that do not have - their length set. (Actually, it will also re-process any that have a length - of zero, but that is a pathological case, and it does no harm.) When we find - one, we temporarily terminate the branch it is in while we scan it. Note that - calling find_bracket() with a negative group number returns a pointer to the - OP_REVERSE item, not the actual lookbehind. */ - - for (cc = (PCRE2_UCHAR *)PRIV(find_bracket)(codestart, utf, -1); - cc != NULL; - cc = (PCRE2_UCHAR *)PRIV(find_bracket)(cc, utf, -1)) - { - 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, &count); - *be = end_op; - if (fixed_length < 0) - { - errorcode = fixed_length_errors[-fixed_length]; - break; - } - if (fixed_length > cb.max_lookbehind) cb.max_lookbehind = fixed_length; - PUT(cc, 1, fixed_length); - } - 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 -via the dreaded goto. */ - -if (errorcode != 0) - { - HAD_ERROR: - *erroroffset = (int)(ptr - pattern); - HAD_UTF_ERROR: - *errorptr = errorcode; - pcre2_code_free(re); - re = NULL; - goto EXIT; - } - -/* Successful compile. If the anchored option was not passed, set it if -we can determine that the pattern is anchored by virtue of ^ characters or \A -or anything else, such as starting with non-atomic .* when DOTALL is set and -there are no occurrences of *PRUNE or *SKIP (though there is an option to -disable this case). */ - -if ((re->overall_options & PCRE2_ANCHORED) == 0 && - is_anchored(codestart, 0, &cb, 0)) - re->overall_options |= PCRE2_ANCHORED; - -/* If the pattern is still not anchored and we do not have a first code unit, -see if there is one that is asserted (these are not saved during the compile -because they can cause conflicts with actual literals that follow). This code -need not be obeyed if PCRE2_NO_START_OPTIMIZE is set, as the data it would -create will not be used. */ - -if ((re->overall_options & (PCRE2_ANCHORED|PCRE2_NO_START_OPTIMIZE)) == 0) - { - if (firstcuflags < 0) - firstcu = find_firstassertedcu(codestart, &firstcuflags, FALSE); - - /* Save the data for a first code unit. */ - - if (firstcuflags >= 0) - { - re->first_codeunit = firstcu; - re->flags |= PCRE2_FIRSTSET; - - /* Handle caseless first code units. */ - - if ((firstcuflags & REQ_CASELESS) != 0) - { - if (firstcu < 128 || (!utf && firstcu < 255)) - { - if (cb.fcc[firstcu] != firstcu) re->flags |= PCRE2_FIRSTCASELESS; - } - - /* The first code unit is > 128 in UTF mode, or > 255 otherwise. In - 8-bit UTF mode, codepoints in the range 128-255 are introductory code - points and cannot have another case. In 16-bit and 32-bit modes, we can - check wide characters when UTF (and therefore UCP) is supported. */ - -#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 - else if (firstcu <= MAX_UTF_CODE_POINT && - UCD_OTHERCASE(firstcu) != firstcu) - re->flags |= PCRE2_FIRSTCASELESS; -#endif - } - } - - /* When there is no first code unit, see if we can set the PCRE2_STARTLINE - flag. This is helpful for multiline matches when all branches start with ^ - and also when all branches start with non-atomic .* for non-DOTALL matches - when *PRUNE and SKIP are not present. (There is an option that disables this - case.) */ - - else if (is_startline(codestart, 0, &cb, 0)) re->flags |= PCRE2_STARTLINE; - } - -/* Handle the "required code unit", if one is set. In the case of an anchored -pattern, do this only if it follows a variable length item in the pattern. -Again, skip this if PCRE2_NO_START_OPTIMIZE is set. */ - -if (reqcuflags >= 0 && - ((re->overall_options & (PCRE2_ANCHORED|PCRE2_NO_START_OPTIMIZE)) == 0 || - (reqcuflags & REQ_VARY) != 0)) - { - re->last_codeunit = reqcu; - re->flags |= PCRE2_LASTSET; - - /* Handle caseless required code units as for first code units (above). */ - - if ((reqcuflags & REQ_CASELESS) != 0) - { - if (reqcu < 128 || (!utf && reqcu < 255)) - { - if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS; - } -#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 - else if (reqcu <= MAX_UTF_CODE_POINT && UCD_OTHERCASE(reqcu) != reqcu) - re->flags |= PCRE2_LASTCASELESS; -#endif - } - } - -/* Check for a pattern than can match an empty string, so that this information -can be provided to applications. */ - -do - { - 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; - } - codestart += GET(codestart, 1); - } -while (*codestart == OP_ALT); - -/* Finally, unless PCRE2_NO_START_OPTIMIZE is set, study the compiled pattern -to set up information such as a bitmap of starting code units and a minimum -matching length. */ - -if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 && - PRIV(study)(re) != 0) - { - errorcode = ERR31; - goto HAD_ERROR; - } - -/* 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, 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 */ -} - -/* End of pcre2_compile.c */ diff --git a/pcre2-10.22/src/pcre2_match.c b/pcre2-10.22/src/pcre2_match.c deleted file mode 100644 index 0763a239e..000000000 --- a/pcre2-10.22/src/pcre2_match.c +++ /dev/null @@ -1,7243 +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) 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 - -#define NLBLOCK mb /* Block containing newline information */ -#define PSSTART start_subject /* Field containing processed string start */ -#define PSEND end_subject /* Field containing processed string end */ - -#include "pcre2_internal.h" - -/* Masks for identifying the public options that are permitted at match time. -*/ - -#define PUBLIC_MATCH_OPTIONS \ - (PCRE2_ANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ - PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \ - PCRE2_PARTIAL_SOFT|PCRE2_NO_JIT) - -#define PUBLIC_JIT_MATCH_OPTIONS \ - (PCRE2_NO_UTF_CHECK|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY|\ - PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_SOFT|PCRE2_PARTIAL_HARD) - -/* The mb->capture_last field uses the lower 16 bits for the last captured -substring (which can never be greater than 65535) and a bit in the top half -to mean "capture vector overflowed". This odd way of doing things was -implemented when it was realized that preserving and restoring the overflow bit -whenever the last capture number was saved/restored made for a neater -interface, and doing it this way saved on (a) another variable, which would -have increased the stack frame size (a big NO-NO in PCRE) and (b) another -separate set of save/restore instructions. The following defines are used in -implementing this. */ - -#define CAPLMASK 0x0000ffff /* The bits used for last_capture */ -#define OVFLMASK 0xffff0000 /* The bits used for the overflow flag */ -#define OVFLBIT 0x00010000 /* The bit that is set for overflow */ - -/* Bits for setting in mb->match_function_type to indicate two special types -of call to match(). We do it this way to save on using another stack variable, -as stack usage is to be discouraged. */ - -#define MATCH_CONDASSERT 1 /* Called to check a condition assertion */ -#define MATCH_CBEGROUP 2 /* Could-be-empty unlimited repeat group */ - -/* Non-error returns from the match() function. Error returns are externally -defined PCRE2_ERROR_xxx codes, which are all negative. */ - -#define MATCH_MATCH 1 -#define MATCH_NOMATCH 0 - -/* Special internal returns from the match() function. Make them sufficiently -negative to avoid the external error codes. */ - -#define MATCH_ACCEPT (-999) -#define MATCH_KETRPOS (-998) -#define MATCH_ONCE (-997) -/* The next 5 must be kept together and in sequence so that a test that checks -for any one of them can use a range. */ -#define MATCH_COMMIT (-996) -#define MATCH_PRUNE (-995) -#define MATCH_SKIP (-994) -#define MATCH_SKIP_ARG (-993) -#define MATCH_THEN (-992) -#define MATCH_BACKTRACK_MAX MATCH_THEN -#define MATCH_BACKTRACK_MIN MATCH_COMMIT - -/* Min and max values for the common repeats; for the maxima, 0 => infinity */ - -static const char rep_min[] = { 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, }; -static const char rep_max[] = { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, }; - -/* Maximum number of ovector elements that can be saved on the system stack -when processing OP_RECURSE in non-HEAP_MATCH_RECURSE mode. If the ovector is -bigger, malloc() is used. This value should be a multiple of 3, because the -ovector length is always a multiple of 3. */ - -#define OP_RECURSE_STACK_SAVE_MAX 45 - - - -/************************************************* -* Match a back-reference * -*************************************************/ - -/* This function is called only when it is known that the offset lies within -the offsets that have so far been used in the match. Note that in caseless -UTF-8 mode, the number of subject bytes matched may be different to the number -of reference bytes. (In theory this could also happen in UTF-16 mode, but it -seems unlikely.) - -Arguments: - offset index into the offset vector - offset_top top of the used offset vector - eptr pointer into the subject - mb points to match block - caseless TRUE if caseless - lengthptr pointer for returning the length matched - -Returns: = 0 sucessful match; number of code units matched is set - < 0 no match - > 0 partial match -*/ - -static int -match_ref(PCRE2_SIZE offset, PCRE2_SIZE offset_top, register PCRE2_SPTR eptr, - match_block *mb, BOOL caseless, PCRE2_SIZE *lengthptr) -{ -#if defined SUPPORT_UNICODE -BOOL utf = (mb->poptions & PCRE2_UTF) != 0; -#endif - -register PCRE2_SPTR p; -PCRE2_SIZE length; -PCRE2_SPTR eptr_start = eptr; - -/* Deal with an unset group. The default is no match, but there is an option to -match an empty string. */ - -if (offset >= offset_top || mb->ovector[offset] == PCRE2_UNSET) - { - if ((mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0) - { - *lengthptr = 0; - return 0; /* Match */ - } - else return -1; /* No match */ - } - -/* Separate the caseless and UTF cases for speed. */ - -p = mb->start_subject + mb->ovector[offset]; -length = mb->ovector[offset+1] - mb->ovector[offset]; - -if (caseless) - { -#if defined SUPPORT_UNICODE - if (utf) - { - /* Match characters up to the end of the reference. NOTE: the number of - code units matched may differ, because in UTF-8 there are some characters - whose upper and lower case versions code have different numbers of bytes. - For example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65 - (3 bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a - sequence of two of the latter. It is important, therefore, to check the - length along the reference, not along the subject (earlier code did this - wrong). */ - - PCRE2_SPTR endptr = p + length; - while (p < endptr) - { - uint32_t c, d; - const ucd_record *ur; - if (eptr >= mb->end_subject) return 1; /* Partial match */ - GETCHARINC(c, eptr); - GETCHARINC(d, p); - ur = GET_UCD(d); - if (c != d && c != (uint32_t)((int)d + ur->other_case)) - { - const uint32_t *pp = PRIV(ucd_caseless_sets) + ur->caseset; - for (;;) - { - if (c < *pp) return -1; /* No match */ - if (c == *pp++) break; - } - } - } - } - else -#endif - - /* Not in UTF mode */ - - { - for (; length > 0; length--) - { - uint32_t cc, cp; - if (eptr >= mb->end_subject) return 1; /* Partial match */ - cc = UCHAR21TEST(eptr); - cp = UCHAR21TEST(p); - if (TABLE_GET(cp, mb->lcc, cp) != TABLE_GET(cc, mb->lcc, cc)) - return -1; /* No match */ - p++; - eptr++; - } - } - } - -/* In the caseful case, we can just compare the code units, whether or not we -are in UTF mode. */ - -else - { - for (; length > 0; length--) - { - if (eptr >= mb->end_subject) return 1; /* Partial match */ - if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1; /*No match */ - } - } - -*lengthptr = eptr - eptr_start; -return 0; /* Match */ -} - - - -/*************************************************************************** -**************************************************************************** - RECURSION IN THE match() FUNCTION - -The match() function is highly recursive, though not every recursive call -increases the recursion depth. Nevertheless, some regular expressions can cause -it to recurse to a great depth. I was writing for Unix, so I just let it call -itself recursively. This uses the stack for saving everything that has to be -saved for a recursive call. On Unix, the stack can be large, and this works -fine. - -It turns out that on some non-Unix-like systems there are problems with -programs that use a lot of stack. (This despite the fact that every last chip -has oodles of memory these days, and techniques for extending the stack have -been known for decades.) So.... - -There is a fudge, triggered by defining HEAP_MATCH_RECURSE, which avoids -recursive calls by keeping local variables that need to be preserved in blocks -of memory on the heap instead instead of on the stack. Macros are used to -achieve this so that the actual code doesn't look very different to what it -always used to. - -The original heap-recursive code used longjmp(). However, it seems that this -can be very slow on some operating systems. Following a suggestion from Stan -Switzer, the use of longjmp() has been abolished, at the cost of having to -provide a unique number for each call to RMATCH. There is no way of generating -a sequence of numbers at compile time in C. I have given them names, to make -them stand out more clearly. - -Crude tests on x86 Linux show a small speedup of around 5-8%. However, on -FreeBSD, avoiding longjmp() more than halves the time taken to run the standard -tests. Furthermore, not using longjmp() means that local dynamic variables -don't have indeterminate values; this has meant that the frame size can be -reduced because the result can be "passed back" by straight setting of the -variable instead of being passed in the frame. -**************************************************************************** -***************************************************************************/ - -/* Numbers for RMATCH calls. When this list is changed, the code at HEAP_RETURN -below must be updated in sync. */ - -enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10, - RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20, - RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30, - RM31, RM32, RM33, RM34, RM35, RM36, RM37, RM38, RM39, RM40, - RM41, RM42, RM43, RM44, RM45, RM46, RM47, RM48, RM49, RM50, - RM51, RM52, RM53, RM54, RM55, RM56, RM57, RM58, RM59, RM60, - RM61, RM62, RM63, RM64, RM65, RM66, RM67, RM68 }; - -/* These versions of the macros use the stack, as normal. Note that the "rw" -argument of RMATCH isn't actually used in this definition. */ - -#ifndef HEAP_MATCH_RECURSE -#define REGISTER register -#define RMATCH(ra,rb,rc,rd,re,rw) \ - rrc = match(ra,rb,mstart,rc,rd,re,rdepth+1) -#define RRETURN(ra) return ra -#else - -/* These versions of the macros manage a private stack on the heap. Note that -the "rd" argument of RMATCH isn't actually used in this definition. It's the mb -argument of match(), which never changes. */ - -#define REGISTER - -#define RMATCH(ra,rb,rc,rd,re,rw)\ - {\ - heapframe *newframe = frame->Xnextframe;\ - if (newframe == NULL)\ - {\ - newframe = (heapframe *)(mb->stack_memctl.malloc)\ - (sizeof(heapframe), mb->stack_memctl.memory_data);\ - if (newframe == NULL) RRETURN(PCRE2_ERROR_NOMEMORY);\ - newframe->Xnextframe = NULL;\ - frame->Xnextframe = newframe;\ - }\ - frame->Xwhere = rw;\ - newframe->Xeptr = ra;\ - newframe->Xecode = rb;\ - newframe->Xmstart = mstart;\ - newframe->Xoffset_top = rc;\ - newframe->Xeptrb = re;\ - newframe->Xrdepth = frame->Xrdepth + 1;\ - newframe->Xprevframe = frame;\ - frame = newframe;\ - goto HEAP_RECURSE;\ - L_##rw:;\ - } - -#define RRETURN(ra)\ - {\ - heapframe *oldframe = frame;\ - frame = oldframe->Xprevframe;\ - if (frame != NULL)\ - {\ - rrc = ra;\ - goto HEAP_RETURN;\ - }\ - return ra;\ - } - - -/* Structure for remembering the local variables in a private frame. Arrange it -so as to minimize the number of holes. */ - -typedef struct heapframe { - struct heapframe *Xprevframe; - struct heapframe *Xnextframe; - -#ifdef SUPPORT_UNICODE - PCRE2_SPTR Xcharptr; -#endif - PCRE2_SPTR Xeptr; - PCRE2_SPTR Xecode; - PCRE2_SPTR Xmstart; - PCRE2_SPTR Xcallpat; - PCRE2_SPTR Xdata; - PCRE2_SPTR Xnext_ecode; - PCRE2_SPTR Xpp; - PCRE2_SPTR Xprev; - PCRE2_SPTR Xsaved_eptr; - - eptrblock *Xeptrb; - - PCRE2_SIZE Xlength; - PCRE2_SIZE Xoffset; - PCRE2_SIZE Xoffset_top; - PCRE2_SIZE Xsave_offset1, Xsave_offset2, Xsave_offset3; - - uint32_t Xfc; - uint32_t Xnumber; - uint32_t Xrdepth; - uint32_t Xop; - uint32_t Xsave_capture_last; - -#ifdef SUPPORT_UNICODE - uint32_t Xprop_value; - int Xprop_type; - int Xprop_fail_result; - int Xoclength; -#endif - - int Xcodelink; - int Xctype; - int Xfi; - int Xmax; - int Xmin; - int Xwhere; /* Where to jump back to */ - - BOOL Xcondition; - BOOL Xcur_is_word; - BOOL Xprev_is_word; - - eptrblock Xnewptrb; - recursion_info Xnew_recursive; - -#ifdef SUPPORT_UNICODE - PCRE2_UCHAR Xocchars[6]; -#endif -} heapframe; - -#endif - - -/*************************************************************************** -***************************************************************************/ - - -/* When HEAP_MATCH_RECURSE is not defined, the match() function implements -backtrack points by calling itself recursively in all but one case. The one -special case is when processing OP_RECURSE, which specifies recursion in the -pattern. The entire ovector must be saved and restored while processing -OP_RECURSE. If the ovector is small enough, instead of calling match() -directly, op_recurse_ovecsave() is called. This function uses the system stack -to save the ovector while calling match() to process the pattern recursion. */ - -#ifndef HEAP_MATCH_RECURSE - -/* We need a prototype for match() because it is mutually recursive with -op_recurse_ovecsave(). */ - -static int -match(REGISTER PCRE2_SPTR eptr, REGISTER PCRE2_SPTR ecode, PCRE2_SPTR mstart, - PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, uint32_t rdepth); - - -/************************************************* -* Process OP_RECURSE, stacking ovector * -*************************************************/ - -/* When this function is called, mb->recursive has already been updated to -point to a new recursion data block, and all its fields other than ovec_save -have been set. - -This function exists so that the local vector variable ovecsave is no longer -defined in the match() function, as it was in PCRE1. It is used only when there -is recursion in the pattern, so it wastes a lot of stack to have it defined for -every call of match(). We now use this function as an indirect way of calling -match() only in the case when ovecsave is needed. (David Wheeler used to say -"All problems in computer science can be solved by another level of -indirection.") - -HOWEVER: when this file is compiled by gcc in an optimizing mode, because this -function is called only once, and only from within match(), gcc will "inline" -it - that is, move it inside match() - and this completely negates its reason -for existence. Therefore, we mark it as non-inline when gcc is in use. - -Arguments: - eptr pointer to current character in subject - callpat the recursion point in the pattern - mstart pointer to the current match start position (can be modified - by encountering \K) - offset_top current top pointer (highest ovector offset used + 1) - mb pointer to "static" info block for the match - eptrb pointer to chain of blocks containing eptr at start of - brackets - for testing for empty matches - rdepth the recursion depth - -Returns: a match() return code -*/ - -static int -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) -__attribute__ ((noinline)) -#endif -op_recurse_ovecsave(REGISTER PCRE2_SPTR eptr, PCRE2_SPTR callpat, - PCRE2_SPTR mstart, PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, - uint32_t rdepth) -{ -register int rrc; -BOOL cbegroup = *callpat >= OP_SBRA; -recursion_info *new_recursive = mb->recursive; -PCRE2_SIZE ovecsave[OP_RECURSE_STACK_SAVE_MAX]; - -/* Save the ovector */ - -new_recursive->ovec_save = ovecsave; -memcpy(ovecsave, mb->ovector, mb->offset_end * sizeof(PCRE2_SIZE)); - -/* Do the recursion. After processing each alternative, restore the ovector -data and the last captured value. */ - -do - { - if (cbegroup) mb->match_function_type |= MATCH_CBEGROUP; - rrc = match(eptr, callpat + PRIV(OP_lengths)[*callpat], mstart, offset_top, - mb, eptrb, rdepth + 1); - memcpy(mb->ovector, new_recursive->ovec_save, - mb->offset_end * sizeof(PCRE2_SIZE)); - mb->capture_last = new_recursive->saved_capture_last; - if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) return rrc; - - /* PCRE does not allow THEN, SKIP, PRUNE or COMMIT to escape beyond a - recursion; they cause a NOMATCH for the entire recursion. These codes - are defined in a range that can be tested for. */ - - if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX) - return MATCH_NOMATCH; - - /* Any return code other than NOMATCH is an error. Otherwise, advance to the - next alternative or to the end of the recursing subpattern. If there were - nested recursions, mb->recursive might be changed, so reset it before - looping. */ - - if (rrc != MATCH_NOMATCH) return rrc; - mb->recursive = new_recursive; - callpat += GET(callpat, 1); - } -while (*callpat == OP_ALT); /* Loop for the alternatives */ - -/* None of the alternatives matched. */ - -return MATCH_NOMATCH; -} -#endif /* HEAP_MATCH_RECURSE */ - - - -/************************************************* -* Match from current position * -*************************************************/ - -/* This function is called recursively in many circumstances. Whenever it -returns a negative (error) response, the outer incarnation must also return the -same response. */ - -/* These macros pack up tests that are used for partial matching, and which -appear several times in the code. We set the "hit end" flag if the pointer is -at the end of the subject and also past the earliest inspected character (i.e. -something has been matched, even if not part of the actual matched string). For -hard partial matching, we then return immediately. The second one is used when -we already know we are past the end of the subject. */ - -#define CHECK_PARTIAL()\ - if (mb->partial != 0 && eptr >= mb->end_subject && \ - eptr > mb->start_used_ptr) \ - { \ - mb->hitend = TRUE; \ - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); \ - } - -#define SCHECK_PARTIAL()\ - if (mb->partial != 0 && eptr > mb->start_used_ptr) \ - { \ - mb->hitend = TRUE; \ - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); \ - } - - -/* Performance note: It might be tempting to extract commonly used fields from -the mb structure (e.g. utf, end_subject) into individual variables to improve -performance. Tests using gcc on a SPARC disproved this; in the first case, it -made performance worse. - -Arguments: - eptr pointer to current character in subject - ecode pointer to current position in compiled code - mstart pointer to the current match start position (can be modified - by encountering \K) - offset_top current top pointer (highest ovector offset used + 1) - mb pointer to "static" info block for the match - eptrb pointer to chain of blocks containing eptr at start of - brackets - for testing for empty matches - rdepth the recursion depth - -Returns: MATCH_MATCH if matched ) these values are >= 0 - MATCH_NOMATCH if failed to match ) - a negative MATCH_xxx value for PRUNE, SKIP, etc - a negative PCRE2_ERROR_xxx value if aborted by an error condition - (e.g. stopped by repeated call or recursion limit) -*/ - -static int -match(REGISTER PCRE2_SPTR eptr, REGISTER PCRE2_SPTR ecode, PCRE2_SPTR mstart, - PCRE2_SIZE offset_top, match_block *mb, eptrblock *eptrb, uint32_t rdepth) -{ -/* These variables do not need to be preserved over recursion in this function, -so they can be ordinary variables in all cases. Mark some of them with -"register" because they are used a lot in loops. */ - -register int rrc; /* Returns from recursive calls */ -register int i; /* Used for loops not involving calls to RMATCH() */ -register uint32_t c; /* Character values not kept over RMATCH() calls */ -register BOOL utf; /* Local copy of UTF flag for speed */ - -BOOL minimize, possessive; /* Quantifier options */ -BOOL caseless; -int condcode; - -/* When recursion is not being used, all "local" variables that have to be -preserved over calls to RMATCH() are part of a "frame". We set up the top-level -frame on the stack here; subsequent instantiations are obtained from the heap -whenever RMATCH() does a "recursion". See the macro definitions above. Putting -the top-level on the stack rather than malloc-ing them all gives a performance -boost in many cases where there is not much "recursion". */ - -#ifdef HEAP_MATCH_RECURSE -heapframe *frame = (heapframe *)mb->match_frames_base; - -/* Copy in the original argument variables */ - -frame->Xeptr = eptr; -frame->Xecode = ecode; -frame->Xmstart = mstart; -frame->Xoffset_top = offset_top; -frame->Xeptrb = eptrb; -frame->Xrdepth = rdepth; - -/* This is where control jumps back to to effect "recursion" */ - -HEAP_RECURSE: - -/* Macros make the argument variables come from the current frame */ - -#define eptr frame->Xeptr -#define ecode frame->Xecode -#define mstart frame->Xmstart -#define offset_top frame->Xoffset_top -#define eptrb frame->Xeptrb -#define rdepth frame->Xrdepth - -/* Ditto for the local variables */ - -#ifdef SUPPORT_UNICODE -#define charptr frame->Xcharptr -#define prop_value frame->Xprop_value -#define prop_type frame->Xprop_type -#define prop_fail_result frame->Xprop_fail_result -#define oclength frame->Xoclength -#define occhars frame->Xocchars -#endif - - -#define callpat frame->Xcallpat -#define codelink frame->Xcodelink -#define data frame->Xdata -#define next_ecode frame->Xnext_ecode -#define pp frame->Xpp -#define prev frame->Xprev -#define saved_eptr frame->Xsaved_eptr - -#define new_recursive frame->Xnew_recursive - -#define ctype frame->Xctype -#define fc frame->Xfc -#define fi frame->Xfi -#define length frame->Xlength -#define max frame->Xmax -#define min frame->Xmin -#define number frame->Xnumber -#define offset frame->Xoffset -#define op frame->Xop -#define save_capture_last frame->Xsave_capture_last -#define save_offset1 frame->Xsave_offset1 -#define save_offset2 frame->Xsave_offset2 -#define save_offset3 frame->Xsave_offset3 - -#define condition frame->Xcondition -#define cur_is_word frame->Xcur_is_word -#define prev_is_word frame->Xprev_is_word - -#define newptrb frame->Xnewptrb - -/* When normal stack-based recursion is being used for match(), local variables -are allocated on the stack and get preserved during recursion in the usual way. -In this environment, fi and i, and fc and c, can be the same variables. */ - -#else /* HEAP_MATCH_RECURSE not defined */ -#define fi i -#define fc c - -/* Many of the following variables are used only in small blocks of the code. -My normal style of coding would have declared them within each of those blocks. -However, in order to accommodate the version of this code that uses an external -"stack" implemented on the heap, it is easier to declare them all here, so the -declarations can be cut out in a block. The only declarations within blocks -below are for variables that do not have to be preserved over a recursive call -to RMATCH(). */ - -#ifdef SUPPORT_UNICODE -PCRE2_SPTR charptr; -#endif -PCRE2_SPTR callpat; -PCRE2_SPTR data; -PCRE2_SPTR next_ecode; -PCRE2_SPTR pp; -PCRE2_SPTR prev; -PCRE2_SPTR saved_eptr; - -PCRE2_SIZE length; -PCRE2_SIZE offset; -PCRE2_SIZE save_offset1, save_offset2, save_offset3; - -uint32_t number; -uint32_t op; -uint32_t save_capture_last; - -#ifdef SUPPORT_UNICODE -uint32_t prop_value; -int prop_type; -int prop_fail_result; -int oclength; -PCRE2_UCHAR occhars[6]; -#endif - -int codelink; -int ctype; -int max; -int min; - -BOOL condition; -BOOL cur_is_word; -BOOL prev_is_word; - -eptrblock newptrb; -recursion_info new_recursive; -#endif /* HEAP_MATCH_RECURSE not defined */ - -/* To save space on the stack and in the heap frame, I have doubled up on some -of the local variables that are used only in localised parts of the code, but -still need to be preserved over recursive calls of match(). These macros define -the alternative names that are used. */ - -#define allow_zero cur_is_word -#define cbegroup condition -#define code_offset codelink -#define condassert condition -#define foc number -#define matched_once prev_is_word -#define save_mark data - -/* These statements are here to stop the compiler complaining about unitialized -variables. */ - -#ifdef SUPPORT_UNICODE -prop_value = 0; -prop_fail_result = 0; -#endif - - -/* This label is used for tail recursion, which is used in a few cases even -when HEAP_MATCH_RECURSE is not defined, in order to reduce the amount of stack -that is used. Thanks to Ian Taylor for noticing this possibility and sending -the original patch. */ - -TAIL_RECURSE: - -/* OK, now we can get on with the real code of the function. Recursive calls -are specified by the macro RMATCH and RRETURN is used to return. When -HEAP_MATCH_RECURSE is *not* defined, these just turn into a recursive call to -match() and a "return", respectively. However, RMATCH isn't like a function -call because it's quite a complicated macro. It has to be used in one -particular way. This shouldn't, however, impact performance when true recursion -is being used. */ - -#ifdef SUPPORT_UNICODE -utf = (mb->poptions & PCRE2_UTF) != 0; -#else -utf = FALSE; -#endif - -/* First check that we haven't called match() too many times, or that we -haven't exceeded the recursive call limit. */ - -if (mb->match_call_count++ >= mb->match_limit) RRETURN(PCRE2_ERROR_MATCHLIMIT); -if (rdepth >= mb->match_limit_recursion) RRETURN(PCRE2_ERROR_RECURSIONLIMIT); - -/* At the start of a group with an unlimited repeat that may match an empty -string, the variable mb->match_function_type contains the MATCH_CBEGROUP bit. -It is done this way to save having to use another function argument, which -would take up space on the stack. See also MATCH_CONDASSERT below. - -When MATCH_CBEGROUP is set, add the current subject pointer to the chain of -such remembered pointers, to be checked when we hit the closing ket, in order -to break infinite loops that match no characters. When match() is called in -other circumstances, don't add to the chain. The MATCH_CBEGROUP feature must -NOT be used with tail recursion, because the memory block that is used is on -the stack, so a new one may be required for each match(). */ - -if ((mb->match_function_type & MATCH_CBEGROUP) != 0) - { - newptrb.epb_saved_eptr = eptr; - newptrb.epb_prev = eptrb; - eptrb = &newptrb; - mb->match_function_type &= ~MATCH_CBEGROUP; - } - -/* Now, at last, we can start processing the opcodes. */ - -for (;;) - { - minimize = possessive = FALSE; - op = *ecode; - - switch(op) - { - case OP_MARK: - mb->nomatch_mark = ecode + 2; - mb->mark = NULL; /* In case previously set by assertion */ - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, mb, - eptrb, RM55); - if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && - mb->mark == NULL) mb->mark = ecode + 2; - - /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an - argument, and we must check whether that argument matches this MARK's - argument. It is passed back in mb->start_match_ptr (an overloading of that - variable). If it does match, we reset that variable to the current subject - position and return MATCH_SKIP. Otherwise, pass back the return code - unaltered. */ - - else if (rrc == MATCH_SKIP_ARG && - PRIV(strcmp)(ecode + 2, mb->start_match_ptr) == 0) - { - mb->start_match_ptr = eptr; - RRETURN(MATCH_SKIP); - } - RRETURN(rrc); - - case OP_FAIL: - RRETURN(MATCH_NOMATCH); - - case OP_COMMIT: - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM52); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - RRETURN(MATCH_COMMIT); - - case OP_PRUNE: - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM51); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - RRETURN(MATCH_PRUNE); - - case OP_PRUNE_ARG: - mb->nomatch_mark = ecode + 2; - mb->mark = NULL; /* In case previously set by assertion */ - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, mb, - eptrb, RM56); - if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && - mb->mark == NULL) mb->mark = ecode + 2; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - RRETURN(MATCH_PRUNE); - - case OP_SKIP: - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM53); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->start_match_ptr = eptr; /* Pass back current position */ - RRETURN(MATCH_SKIP); - - /* Note that, for Perl compatibility, SKIP with an argument does NOT set - nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was - not a matching mark, we have to re-run the match, ignoring the SKIP_ARG - that failed and any that precede it (either they also failed, or were not - triggered). To do this, we maintain a count of executed SKIP_ARGs. If a - SKIP_ARG gets to top level, the match is re-run with mb->ignore_skip_arg - set to the count of the one that failed. */ - - case OP_SKIP_ARG: - mb->skip_arg_count++; - if (mb->skip_arg_count <= mb->ignore_skip_arg) - { - ecode += PRIV(OP_lengths)[*ecode] + ecode[1]; - break; - } - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, mb, - eptrb, RM57); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - - /* Pass back the current skip name by overloading mb->start_match_ptr and - returning the special MATCH_SKIP_ARG return code. This will either be - caught by a matching MARK, or get to the top, where it causes a rematch - with mb->ignore_skip_arg set to the value of mb->skip_arg_count. */ - - mb->start_match_ptr = ecode + 2; - RRETURN(MATCH_SKIP_ARG); - - /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that - the branch in which it occurs can be determined. Overload the start of - match pointer to do this. */ - - case OP_THEN: - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM54); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->start_match_ptr = ecode; - RRETURN(MATCH_THEN); - - case OP_THEN_ARG: - mb->nomatch_mark = ecode + 2; - mb->mark = NULL; /* In case previously set by assertion */ - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, - mb, eptrb, RM58); - if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && - mb->mark == NULL) mb->mark = ecode + 2; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->start_match_ptr = ecode; - RRETURN(MATCH_THEN); - - /* Handle an atomic group that does not contain any capturing parentheses. - This can be handled like an assertion. Prior to 8.13, all atomic groups - were handled this way. In 8.13, the code was changed as below for ONCE, so - that backups pass through the group and thereby reset captured values. - However, this uses a lot more stack, so in 8.20, atomic groups that do not - contain any captures generate OP_ONCE_NC, which can be handled in the old, - less stack intensive way. - - Check the alternative branches in turn - the matching won't pass the KET - for this kind of subpattern. If any one branch matches, we carry on as at - the end of a normal bracket, leaving the subject pointer, but resetting - the start-of-match value in case it was changed by \K. */ - - case OP_ONCE_NC: - prev = ecode; - saved_eptr = eptr; - save_mark = mb->mark; - do - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM64); - if (rrc == MATCH_MATCH) /* Note: _not_ MATCH_ACCEPT */ - { - mstart = mb->start_match_ptr; - break; - } - if (rrc == MATCH_THEN) - { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; - } - - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode += GET(ecode,1); - mb->mark = save_mark; - } - while (*ecode == OP_ALT); - - /* If hit the end of the group (which could be repeated), fail */ - - if (*ecode != OP_ONCE_NC && *ecode != OP_ALT) RRETURN(MATCH_NOMATCH); - - /* Continue as from after the group, updating the offsets high water - mark, since extracts may have been taken. */ - - do ecode += GET(ecode, 1); while (*ecode == OP_ALT); - - offset_top = mb->end_offset_top; - eptr = mb->end_match_ptr; - - /* For a non-repeating ket, just continue at this level. This also - happens for a repeating ket if no characters were matched in the group. - This is the forcible breaking of infinite loops as implemented in Perl - 5.005. */ - - if (*ecode == OP_KET || eptr == saved_eptr) - { - ecode += 1+LINK_SIZE; - break; - } - - /* The repeating kets try the rest of the pattern or restart from the - preceding bracket, in the appropriate order. The second "call" of match() - uses tail recursion, to avoid using another stack frame. */ - - if (*ecode == OP_KETRMIN) - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM65); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode = prev; - goto TAIL_RECURSE; - } - else /* OP_KETRMAX */ - { - RMATCH(eptr, prev, offset_top, mb, eptrb, RM66); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode += 1 + LINK_SIZE; - goto TAIL_RECURSE; - } - /* Control never gets here */ - - /* Handle a capturing bracket, other than those that are possessive with an - unlimited repeat. If there is space in the offset vector, save the current - subject position in the working slot at the top of the vector. We mustn't - change the current values of the data slot, because they may be set from a - previous iteration of this group, and be referred to by a reference inside - the group. A failure to match might occur after the group has succeeded, - if something later on doesn't match. For this reason, we need to restore - the working value and also the values of the final offsets, in case they - were set by a previous iteration of the same bracket. - - If there isn't enough space in the offset vector, treat this as if it were - a non-capturing bracket. Don't worry about setting the flag for the error - case here; that is handled in the code for KET. */ - - case OP_CBRA: - case OP_SCBRA: - number = GET2(ecode, 1+LINK_SIZE); - offset = number << 1; - - if (offset < mb->offset_max) - { - save_offset1 = mb->ovector[offset]; - save_offset2 = mb->ovector[offset+1]; - save_offset3 = mb->ovector[mb->offset_end - number]; - save_capture_last = mb->capture_last; - save_mark = mb->mark; - - mb->ovector[mb->offset_end - number] = eptr - mb->start_subject; - - for (;;) - { - if (op >= OP_SBRA) mb->match_function_type |= MATCH_CBEGROUP; - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM1); - if (rrc == MATCH_ONCE) break; /* Backing up through an atomic group */ - - /* If we backed up to a THEN, check whether it is within the current - branch by comparing the address of the THEN that is passed back with - the end of the branch. If it is within the current branch, and the - branch is one of two or more alternatives (it either starts or ends - with OP_ALT), we have reached the limit of THEN's action, so convert - the return code to NOMATCH, which will cause normal backtracking to - happen from now on. Otherwise, THEN is passed back to an outer - alternative. This implements Perl's treatment of parenthesized groups, - where a group not containing | does not affect the current alternative, - that is, (X) is NOT the same as (X|(*F)). */ - - if (rrc == MATCH_THEN) - { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; - } - - /* Anything other than NOMATCH is passed back. */ - - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->capture_last = save_capture_last; - ecode += GET(ecode, 1); - mb->mark = save_mark; - if (*ecode != OP_ALT) break; - } - - mb->ovector[offset] = save_offset1; - mb->ovector[offset+1] = save_offset2; - mb->ovector[mb->offset_end - number] = save_offset3; - - /* At this point, rrc will be one of MATCH_ONCE or MATCH_NOMATCH. */ - - RRETURN(rrc); - } - - /* FALL THROUGH ... Insufficient room for saving captured contents. Treat - as a non-capturing bracket. */ - - /* VVVVVVVVVVVVVVVVVVVVVVVVV */ - /* VVVVVVVVVVVVVVVVVVVVVVVVV */ - - /* Non-capturing or atomic group, except for possessive with unlimited - repeat and ONCE group with no captures. Loop for all the alternatives. - - When we get to the final alternative within the brackets, we used to return - the result of a recursive call to match() whatever happened so it was - possible to reduce stack usage by turning this into a tail recursion, - except in the case of a possibly empty group. However, now that there is - the possiblity of (*THEN) occurring in the final alternative, this - optimization is no longer always possible. - - We can optimize if we know there are no (*THEN)s in the pattern; at present - this is the best that can be done. - - MATCH_ONCE is returned when the end of an atomic group is successfully - reached, but subsequent matching fails. It passes back up the tree (causing - captured values to be reset) until the original atomic group level is - reached. This is tested by comparing mb->once_target with the start of the - group. At this point, the return is converted into MATCH_NOMATCH so that - previous backup points can be taken. */ - - case OP_ONCE: - case OP_BRA: - case OP_SBRA: - - for (;;) - { - if (op >= OP_SBRA || op == OP_ONCE) - mb->match_function_type |= MATCH_CBEGROUP; - - /* If this is not a possibly empty group, and there are no (*THEN)s in - the pattern, and this is the final alternative, optimize as described - above. */ - - else if (!mb->hasthen && ecode[GET(ecode, 1)] != OP_ALT) - { - ecode += PRIV(OP_lengths)[*ecode]; - goto TAIL_RECURSE; - } - - /* In all other cases, we have to make another call to match(). */ - - save_mark = mb->mark; - save_capture_last = mb->capture_last; - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, eptrb, - RM2); - - /* See comment in the code for capturing groups above about handling - THEN. */ - - if (rrc == MATCH_THEN) - { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; - } - - if (rrc != MATCH_NOMATCH) - { - if (rrc == MATCH_ONCE) - { - PCRE2_SPTR scode = ecode; - if (*scode != OP_ONCE) /* If not at start, find it */ - { - while (*scode == OP_ALT) scode += GET(scode, 1); - scode -= GET(scode, 1); - } - if (mb->once_target == scode) rrc = MATCH_NOMATCH; - } - RRETURN(rrc); - } - ecode += GET(ecode, 1); - mb->mark = save_mark; - if (*ecode != OP_ALT) break; - mb->capture_last = save_capture_last; - } - - RRETURN(MATCH_NOMATCH); - - /* Handle possessive capturing brackets with an unlimited repeat. We come - here from BRAZERO with allow_zero set TRUE. The ovector values are - handled similarly to the normal case above. However, the matching is - different. The end of these brackets will always be OP_KETRPOS, which - returns MATCH_KETRPOS without going further in the pattern. By this means - we can handle the group by iteration rather than recursion, thereby - reducing the amount of stack needed. If the ovector is too small for - capturing, treat as non-capturing. */ - - case OP_CBRAPOS: - case OP_SCBRAPOS: - allow_zero = FALSE; - - POSSESSIVE_CAPTURE: - number = GET2(ecode, 1+LINK_SIZE); - offset = number << 1; - if (offset >= mb->offset_max) goto POSSESSIVE_NON_CAPTURE; - - matched_once = FALSE; - code_offset = (int)(ecode - mb->start_code); - - save_offset1 = mb->ovector[offset]; - save_offset2 = mb->ovector[offset+1]; - save_offset3 = mb->ovector[mb->offset_end - number]; - save_capture_last = mb->capture_last; - - /* Each time round the loop, save the current subject position for use - when the group matches. For MATCH_MATCH, the group has matched, so we - restart it with a new subject starting position, remembering that we had - at least one match. For MATCH_NOMATCH, carry on with the alternatives, as - usual. If we haven't matched any alternatives in any iteration, check to - see if a previous iteration matched. If so, the group has matched; - continue from afterwards. Otherwise it has failed; restore the previous - capture values before returning NOMATCH. */ - - for (;;) - { - mb->ovector[mb->offset_end - number] = eptr - mb->start_subject; - if (op >= OP_SBRA) mb->match_function_type |= MATCH_CBEGROUP; - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM63); - if (rrc == MATCH_KETRPOS) - { - offset_top = mb->end_offset_top; - ecode = mb->start_code + code_offset; - save_capture_last = mb->capture_last; - matched_once = TRUE; - mstart = mb->start_match_ptr; /* In case \K changed it */ - if (eptr == mb->end_match_ptr) /* Matched an empty string */ - { - do ecode += GET(ecode, 1); while (*ecode == OP_ALT); - break; - } - eptr = mb->end_match_ptr; - continue; - } - - /* See comment in the code for capturing groups above about handling - THEN. */ - - if (rrc == MATCH_THEN) - { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; - } - - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->capture_last = save_capture_last; - ecode += GET(ecode, 1); - if (*ecode != OP_ALT) break; - } - - if (!matched_once) - { - mb->ovector[offset] = save_offset1; - mb->ovector[offset+1] = save_offset2; - mb->ovector[mb->offset_end - number] = save_offset3; - } - - if (allow_zero || matched_once) - { - ecode += 1 + LINK_SIZE; - break; - } - RRETURN(MATCH_NOMATCH); - - /* Non-capturing possessive bracket with unlimited repeat. We come here - from BRAZERO with allow_zero = TRUE. The code is similar to the above, - without the capturing complication. It is written out separately for speed - and cleanliness. */ - - case OP_BRAPOS: - case OP_SBRAPOS: - allow_zero = FALSE; - - POSSESSIVE_NON_CAPTURE: - matched_once = FALSE; - code_offset = (int)(ecode - mb->start_code); - save_capture_last = mb->capture_last; - - for (;;) - { - if (op >= OP_SBRA) mb->match_function_type |= MATCH_CBEGROUP; - RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, mb, - eptrb, RM48); - if (rrc == MATCH_KETRPOS) - { - offset_top = mb->end_offset_top; - ecode = mb->start_code + code_offset; - matched_once = TRUE; - mstart = mb->start_match_ptr; /* In case \K reset it */ - if (eptr == mb->end_match_ptr) /* Matched an empty string */ - { - do ecode += GET(ecode, 1); while (*ecode == OP_ALT); - break; - } - eptr = mb->end_match_ptr; - continue; - } - - /* See comment in the code for capturing groups above about handling - THEN. */ - - if (rrc == MATCH_THEN) - { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; - } - - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode += GET(ecode, 1); - if (*ecode != OP_ALT) break; - mb->capture_last = save_capture_last; - } - - if (matched_once || allow_zero) - { - ecode += 1 + LINK_SIZE; - break; - } - RRETURN(MATCH_NOMATCH); - - /* Control never reaches here. */ - - /* Conditional group: compilation checked that there are no more than two - branches. If the condition is false, skipping the first branch takes us - past the end of the item if there is only one branch, but that's exactly - what we want. */ - - case OP_COND: - case OP_SCOND: - - /* The variable codelink will be added to ecode when the condition is - false, to get to the second branch. Setting it to the offset to the ALT - or KET, then incrementing ecode achieves this effect. We now have ecode - pointing to the condition or callout. */ - - codelink = GET(ecode, 1); /* Offset to the second branch */ - ecode += 1 + LINK_SIZE; /* From this opcode */ - - /* Because of the way auto-callout works during compile, a callout item is - inserted between OP_COND and an assertion condition. */ - - if (*ecode == OP_CALLOUT || *ecode == OP_CALLOUT_STR) - { - unsigned int callout_length = (*ecode == OP_CALLOUT) - ? PRIV(OP_lengths)[OP_CALLOUT] : GET(ecode, 1 + 2*LINK_SIZE); - - if (mb->callout != NULL) - { - pcre2_callout_block cb; - cb.version = 1; - cb.capture_top = offset_top/2; - cb.capture_last = mb->capture_last & CAPLMASK; - cb.offset_vector = mb->ovector; - cb.mark = mb->nomatch_mark; - cb.subject = mb->start_subject; - cb.subject_length = (PCRE2_SIZE)(mb->end_subject - mb->start_subject); - cb.start_match = (PCRE2_SIZE)(mstart - mb->start_subject); - cb.current_position = (PCRE2_SIZE)(eptr - mb->start_subject); - cb.pattern_position = GET(ecode, 1); - cb.next_item_length = GET(ecode, 1 + LINK_SIZE); - - if (*ecode == OP_CALLOUT) - { - cb.callout_number = ecode[1 + 2*LINK_SIZE]; - cb.callout_string_offset = 0; - cb.callout_string = NULL; - cb.callout_string_length = 0; - } - else - { - cb.callout_number = 0; - cb.callout_string_offset = GET(ecode, 1 + 3*LINK_SIZE); - cb.callout_string = ecode + (1 + 4*LINK_SIZE) + 1; - cb.callout_string_length = - callout_length - (1 + 4*LINK_SIZE) - 2; - } - - if ((rrc = mb->callout(&cb, mb->callout_data)) > 0) - RRETURN(MATCH_NOMATCH); - if (rrc < 0) RRETURN(rrc); - } - - /* Advance ecode past the callout, so it now points to the condition. We - must adjust codelink so that the value of ecode+codelink is unchanged. */ - - ecode += callout_length; - codelink -= callout_length; - } - - /* Test the various possible conditions */ - - condition = FALSE; - switch(condcode = *ecode) - { - case OP_RREF: /* Numbered group recursion test */ - if (mb->recursive != NULL) /* Not recursing => FALSE */ - { - uint32_t recno = GET2(ecode, 1); /* Recursion group number*/ - condition = (recno == RREF_ANY || recno == mb->recursive->group_num); - } - break; - - case OP_DNRREF: /* Duplicate named group recursion test */ - if (mb->recursive != NULL) - { - int count = GET2(ecode, 1 + IMM2_SIZE); - PCRE2_SPTR slot = mb->name_table + GET2(ecode, 1) * mb->name_entry_size; - while (count-- > 0) - { - uint32_t recno = GET2(slot, 0); - condition = recno == mb->recursive->group_num; - if (condition) break; - slot += mb->name_entry_size; - } - } - break; - - case OP_CREF: /* Numbered group used test */ - offset = GET2(ecode, 1) << 1; /* Doubled ref number */ - condition = offset < offset_top && - mb->ovector[offset] != PCRE2_UNSET; - break; - - case OP_DNCREF: /* Duplicate named group used test */ - { - int count = GET2(ecode, 1 + IMM2_SIZE); - PCRE2_SPTR slot = mb->name_table + GET2(ecode, 1) * mb->name_entry_size; - while (count-- > 0) - { - offset = GET2(slot, 0) << 1; - condition = offset < offset_top && - mb->ovector[offset] != PCRE2_UNSET; - if (condition) break; - slot += mb->name_entry_size; - } - } - break; - - case OP_FALSE: - case OP_FAIL: /* The assertion (?!) becomes OP_FAIL */ - break; - - case OP_TRUE: - condition = TRUE; - break; - - /* The condition is an assertion. Call match() to evaluate it - setting - the MATCH_CONDASSERT bit in mb->match_function_type causes it to stop at - the end of an assertion. */ - - default: - mb->match_function_type |= MATCH_CONDASSERT; - RMATCH(eptr, ecode, offset_top, mb, NULL, RM3); - if (rrc == MATCH_MATCH) - { - if (mb->end_offset_top > offset_top) - offset_top = mb->end_offset_top; /* Captures may have happened */ - condition = TRUE; - - /* Advance ecode past the assertion to the start of the first branch, - but adjust it so that the general choosing code below works. If the - assertion has a quantifier that allows zero repeats we must skip over - the BRAZERO. This is a lunatic thing to do, but somebody did! */ - - if (*ecode == OP_BRAZERO) ecode++; - ecode += GET(ecode, 1); - while (*ecode == OP_ALT) ecode += GET(ecode, 1); - ecode += 1 + LINK_SIZE - PRIV(OP_lengths)[condcode]; - } - - /* PCRE doesn't allow the effect of (*THEN) to escape beyond an - assertion; it is therefore treated as NOMATCH. Any other return is an - error. */ - - else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) - { - RRETURN(rrc); /* Need braces because of following else */ - } - break; - } - - /* Choose branch according to the condition */ - - ecode += condition? PRIV(OP_lengths)[condcode] : codelink; - - /* We are now at the branch that is to be obeyed. As there is only one, we - can use tail recursion to avoid using another stack frame, except when - there is unlimited repeat of a possibly empty group. In the latter case, a - recursive call to match() is always required, unless the second alternative - doesn't exist, in which case we can just plough on. Note that, for - compatibility with Perl, the | in a conditional group is NOT treated as - creating two alternatives. If a THEN is encountered in the branch, it - propagates out to the enclosing alternative (unless nested in a deeper set - of alternatives, of course). */ - - if (condition || ecode[-(1+LINK_SIZE)] == OP_ALT) - { - if (op != OP_SCOND) - { - goto TAIL_RECURSE; - } - - mb->match_function_type |= MATCH_CBEGROUP; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM49); - RRETURN(rrc); - } - - /* Condition false & no alternative; continue after the group. */ - - else - { - } - break; - - - /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, - to close any currently open capturing brackets. */ - - case OP_CLOSE: - number = GET2(ecode, 1); /* Must be less than 65536 */ - offset = number << 1; - mb->capture_last = (mb->capture_last & OVFLMASK) | number; - if (offset >= mb->offset_max) mb->capture_last |= OVFLBIT; else - { - mb->ovector[offset] = - mb->ovector[mb->offset_end - number]; - mb->ovector[offset+1] = eptr - mb->start_subject; - - /* If this group is at or above the current highwater mark, ensure that - any groups between the current high water mark and this group are marked - unset and then update the high water mark. */ - - if (offset >= offset_top) - { - register PCRE2_SIZE *iptr = mb->ovector + offset_top; - register PCRE2_SIZE *iend = mb->ovector + offset; - while (iptr < iend) *iptr++ = PCRE2_UNSET; - offset_top = offset + 2; - } - } - ecode += 1 + IMM2_SIZE; - break; - - - /* End of the pattern, either real or forced. In an assertion ACCEPT, - update the last used pointer. */ - - case OP_ASSERT_ACCEPT: - if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr; - - case OP_ACCEPT: - case OP_END: - - /* If we have matched an empty string, fail if not in an assertion and not - in a recursion if either PCRE2_NOTEMPTY is set, or if PCRE2_NOTEMPTY_ATSTART - is set and we have matched at the start of the subject. In both cases, - backtracking will then try other alternatives, if any. */ - - if (eptr == mstart && op != OP_ASSERT_ACCEPT && - mb->recursive == NULL && - ((mb->moptions & PCRE2_NOTEMPTY) != 0 || - ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) != 0 && - mstart == mb->start_subject + mb->start_offset))) - RRETURN(MATCH_NOMATCH); - - /* Otherwise, we have a match. */ - - mb->end_match_ptr = eptr; /* Record where we ended */ - mb->end_offset_top = offset_top; /* and how many extracts were taken */ - mb->start_match_ptr = mstart; /* and the start (\K can modify) */ - - /* For some reason, the macros don't work properly if an expression is - given as the argument to RRETURN when the heap is in use. */ - - rrc = (op == OP_END)? MATCH_MATCH : MATCH_ACCEPT; - RRETURN(rrc); - - /* Assertion brackets. Check the alternative branches in turn - the - matching won't pass the KET for an assertion. If any one branch matches, - the assertion is true. Lookbehind assertions have an OP_REVERSE item at the - start of each branch to move the current point backwards, so the code at - this level is identical to the lookahead case. When the assertion is part - of a condition, we want to return immediately afterwards. The caller of - this incarnation of the match() function will have set MATCH_CONDASSERT in - mb->match_function type, and one of these opcodes will be the first opcode - that is processed. We use a local variable that is preserved over calls to - match() to remember this case. */ - - case OP_ASSERT: - case OP_ASSERTBACK: - save_mark = mb->mark; - if ((mb->match_function_type & MATCH_CONDASSERT) != 0) - { - condassert = TRUE; - mb->match_function_type &= ~MATCH_CONDASSERT; - } - else condassert = FALSE; - - /* Loop for each branch */ - - do - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, NULL, RM4); - - /* A match means that the assertion is true; break out of the loop - that matches its alternatives. */ - - if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) - { - mstart = mb->start_match_ptr; /* In case \K reset it */ - break; - } - - /* If not matched, restore the previous mark setting. */ - - mb->mark = save_mark; - - /* See comment in the code for capturing groups above about handling - THEN. */ - - if (rrc == MATCH_THEN) - { - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - rrc = MATCH_NOMATCH; - } - - /* Anything other than NOMATCH causes the entire assertion to fail, - passing back the return code. This includes COMMIT, SKIP, PRUNE and an - uncaptured THEN, which means they take their normal effect. This - consistent approach does not always have exactly the same effect as in - Perl. */ - - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode += GET(ecode, 1); - } - while (*ecode == OP_ALT); /* Continue for next alternative */ - - /* If we have tried all the alternative branches, the assertion has - failed. If not, we broke out after a match. */ - - if (*ecode == OP_KET) RRETURN(MATCH_NOMATCH); - - /* If checking an assertion for a condition, return MATCH_MATCH. */ - - if (condassert) RRETURN(MATCH_MATCH); - - /* Continue from after a successful assertion, updating the offsets high - water mark, since extracts may have been taken during the assertion. */ - - do ecode += GET(ecode,1); while (*ecode == OP_ALT); - ecode += 1 + LINK_SIZE; - offset_top = mb->end_offset_top; - continue; - - /* Negative assertion: all branches must fail to match for the assertion to - succeed. */ - - case OP_ASSERT_NOT: - case OP_ASSERTBACK_NOT: - save_mark = mb->mark; - if ((mb->match_function_type & MATCH_CONDASSERT) != 0) - { - condassert = TRUE; - mb->match_function_type &= ~MATCH_CONDASSERT; - } - else condassert = FALSE; - - /* Loop for each alternative branch. */ - - do - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, NULL, RM5); - mb->mark = save_mark; /* Always restore the mark setting */ - - switch(rrc) - { - case MATCH_MATCH: /* A successful match means */ - case MATCH_ACCEPT: /* the assertion has failed. */ - RRETURN(MATCH_NOMATCH); - - case MATCH_NOMATCH: /* Carry on with next branch */ - break; - - /* See comment in the code for capturing groups above about handling - THEN. */ - - case MATCH_THEN: - next_ecode = ecode + GET(ecode,1); - if (mb->start_match_ptr < next_ecode && - (*ecode == OP_ALT || *next_ecode == OP_ALT)) - { - rrc = MATCH_NOMATCH; - break; - } - /* Otherwise fall through. */ - - /* COMMIT, SKIP, PRUNE, and an uncaptured THEN cause the whole - assertion to fail to match, without considering any more alternatives. - Failing to match means the assertion is true. This is a consistent - approach, but does not always have the same effect as in Perl. */ - - case MATCH_COMMIT: - case MATCH_SKIP: - case MATCH_SKIP_ARG: - case MATCH_PRUNE: - do ecode += GET(ecode,1); while (*ecode == OP_ALT); - goto NEG_ASSERT_TRUE; /* Break out of alternation loop */ - - /* Anything else is an error */ - - default: - RRETURN(rrc); - } - - /* Continue with next branch */ - - ecode += GET(ecode,1); - } - while (*ecode == OP_ALT); - - /* All branches in the assertion failed to match. */ - - NEG_ASSERT_TRUE: - if (condassert) RRETURN(MATCH_MATCH); /* Condition assertion */ - ecode += 1 + LINK_SIZE; /* Continue with current branch */ - continue; - - /* Move the subject pointer back. This occurs only at the start of - each branch of a lookbehind assertion. If we are too close to the start to - move back, this match function fails. When working with UTF-8 we move - back a number of characters, not bytes. */ - - case OP_REVERSE: - i = GET(ecode, 1); -#ifdef SUPPORT_UNICODE - if (utf) - { - while (i-- > 0) - { - if (eptr <= mb->start_subject) RRETURN(MATCH_NOMATCH); - eptr--; - BACKCHAR(eptr); - } - } - else -#endif - - /* No UTF-8 support, or not in UTF-8 mode: count is byte count */ - - { - if (i > eptr - mb->start_subject) RRETURN(MATCH_NOMATCH); - eptr -= i; - } - - /* Save the earliest consulted character, then skip to next op code */ - - if (eptr < mb->start_used_ptr) mb->start_used_ptr = eptr; - ecode += 1 + LINK_SIZE; - break; - - /* The callout item calls an external function, if one is provided, passing - details of the match so far. This is mainly for debugging, though the - function is able to force a failure. */ - - case OP_CALLOUT: - case OP_CALLOUT_STR: - { - unsigned int callout_length = (*ecode == OP_CALLOUT) - ? PRIV(OP_lengths)[OP_CALLOUT] : GET(ecode, 1 + 2*LINK_SIZE); - - if (mb->callout != NULL) - { - pcre2_callout_block cb; - cb.version = 1; - cb.callout_number = ecode[LINK_SIZE + 1]; - cb.capture_top = offset_top/2; - cb.capture_last = mb->capture_last & CAPLMASK; - cb.offset_vector = mb->ovector; - cb.mark = mb->nomatch_mark; - cb.subject = mb->start_subject; - cb.subject_length = (PCRE2_SIZE)(mb->end_subject - mb->start_subject); - cb.start_match = (PCRE2_SIZE)(mstart - mb->start_subject); - cb.current_position = (PCRE2_SIZE)(eptr - mb->start_subject); - cb.pattern_position = GET(ecode, 1); - cb.next_item_length = GET(ecode, 1 + LINK_SIZE); - - if (*ecode == OP_CALLOUT) - { - cb.callout_number = ecode[1 + 2*LINK_SIZE]; - cb.callout_string_offset = 0; - cb.callout_string = NULL; - cb.callout_string_length = 0; - } - else - { - cb.callout_number = 0; - cb.callout_string_offset = GET(ecode, 1 + 3*LINK_SIZE); - cb.callout_string = ecode + (1 + 4*LINK_SIZE) + 1; - cb.callout_string_length = - callout_length - (1 + 4*LINK_SIZE) - 2; - } - - if ((rrc = mb->callout(&cb, mb->callout_data)) > 0) - RRETURN(MATCH_NOMATCH); - if (rrc < 0) RRETURN(rrc); - } - ecode += callout_length; - } - break; - - /* Recursion either matches the current regex, or some subexpression. The - offset data is the offset to the starting bracket from the start of the - whole pattern. (This is so that it works from duplicated subpatterns.) - - The state of the capturing groups is preserved over recursion, and - re-instated afterwards. We don't know how many are started and not yet - finished (offset_top records the completed total) so we just have to save - all the potential data. There may be up to 65535 such values, which is too - large to put on the stack, but using malloc for small numbers seems - expensive. As a compromise, the stack is used when there are no more than - OP_RECURSE_STACK_SAVE_MAX values to store; otherwise malloc is used. - - There are also other values that have to be saved. We use a chained - sequence of blocks that actually live on the stack. Thanks to Robin Houston - for the original version of this logic. It has, however, been hacked around - a lot, so he is not to blame for the current way it works. */ - - case OP_RECURSE: - { - ovecsave_frame *fr; - recursion_info *ri; - uint32_t recno; - - callpat = mb->start_code + GET(ecode, 1); - recno = (callpat == mb->start_code)? 0 : GET2(callpat, 1 + LINK_SIZE); - - /* Check for repeating a pattern recursion without advancing the subject - pointer. This should catch convoluted mutual recursions. (Some simple - cases are caught at compile time.) */ - - for (ri = mb->recursive; ri != NULL; ri = ri->prevrec) - if (recno == ri->group_num && eptr == ri->subject_position) - RRETURN(PCRE2_ERROR_RECURSELOOP); - - /* Add to "recursing stack" */ - - new_recursive.group_num = recno; - new_recursive.saved_capture_last = mb->capture_last; - new_recursive.subject_position = eptr; - new_recursive.prevrec = mb->recursive; - mb->recursive = &new_recursive; - - /* Where to continue from afterwards */ - - ecode += 1 + LINK_SIZE; - - /* When we are using the system stack for match() recursion we can call a - function that uses the system stack for preserving the ovector while - processing the pattern recursion, but only if the ovector is small - enough. */ - -#ifndef HEAP_MATCH_RECURSE - if (mb->offset_end <= OP_RECURSE_STACK_SAVE_MAX) - { - rrc = op_recurse_ovecsave(eptr, callpat, mstart, offset_top, mb, - eptrb, rdepth); - mb->recursive = new_recursive.prevrec; - if (rrc != MATCH_MATCH && rrc != MATCH_ACCEPT) RRETURN(rrc); - - /* Set where we got to in the subject, and reset the start, in case - it was changed by \K. This *is* propagated back out of a recursion, - for Perl compatibility. */ - - eptr = mb->end_match_ptr; - mstart = mb->start_match_ptr; - break; /* End of processing OP_RECURSE */ - } -#endif - /* If the ovector is too big, or if we are using the heap for match() - recursion, we have to use the heap for saving the ovector. Used ovecsave - frames are kept on a chain and re-used. This makes a small improvement in - execution time on Linux. */ - - if (mb->ovecsave_chain != NULL) - { - new_recursive.ovec_save = mb->ovecsave_chain->saved_ovec; - mb->ovecsave_chain = mb->ovecsave_chain->next; - } - else - { - fr = (ovecsave_frame *)(mb->memctl.malloc(sizeof(ovecsave_frame *) + - mb->offset_end * sizeof(PCRE2_SIZE), mb->memctl.memory_data)); - if (fr == NULL) RRETURN(PCRE2_ERROR_NOMEMORY); - new_recursive.ovec_save = fr->saved_ovec; - } - - memcpy(new_recursive.ovec_save, mb->ovector, - mb->offset_end * sizeof(PCRE2_SIZE)); - - /* Do the recursion. After processing each alternative, restore the - ovector data and the last captured value. This code has the same overall - logic as the code in the op_recurse_ovecsave() function, but is adapted - to use RMATCH/RRETURN and to release the heap block containing the saved - ovector. */ - - cbegroup = (*callpat >= OP_SBRA); - do - { - if (cbegroup) mb->match_function_type |= MATCH_CBEGROUP; - RMATCH(eptr, callpat + PRIV(OP_lengths)[*callpat], offset_top, - mb, eptrb, RM6); - memcpy(mb->ovector, new_recursive.ovec_save, - mb->offset_end * sizeof(PCRE2_SIZE)); - mb->capture_last = new_recursive.saved_capture_last; - mb->recursive = new_recursive.prevrec; - - if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) - { - fr = (ovecsave_frame *) - ((uint8_t *)new_recursive.ovec_save - sizeof(ovecsave_frame *)); - fr->next = mb->ovecsave_chain; - mb->ovecsave_chain = fr; - - /* Set where we got to in the subject, and reset the start, in case - it was changed by \K. This *is* propagated back out of a recursion, - for Perl compatibility. */ - - eptr = mb->end_match_ptr; - mstart = mb->start_match_ptr; - goto RECURSION_MATCHED; /* Exit loop; end processing */ - } - - /* PCRE does not allow THEN, SKIP, PRUNE or COMMIT to escape beyond a - recursion; they cause a NOMATCH for the entire recursion. These codes - are defined in a range that can be tested for. */ - - if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX) - { - rrc = MATCH_NOMATCH; - goto RECURSION_RETURN; - } - - /* Any return code other than NOMATCH is an error. */ - - if (rrc != MATCH_NOMATCH) goto RECURSION_RETURN; - mb->recursive = &new_recursive; - callpat += GET(callpat, 1); - } - while (*callpat == OP_ALT); - - RECURSION_RETURN: - mb->recursive = new_recursive.prevrec; - fr = (ovecsave_frame *) - ((uint8_t *)new_recursive.ovec_save - sizeof(ovecsave_frame *)); - fr->next = mb->ovecsave_chain; - mb->ovecsave_chain = fr; - RRETURN(rrc); - } - - RECURSION_MATCHED: - break; - - /* An alternation is the end of a branch; scan along to find the end of the - bracketed group and go to there. */ - - case OP_ALT: - do ecode += GET(ecode,1); while (*ecode == OP_ALT); - break; - - /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a bracket group, - indicating that it may occur zero times. It may repeat infinitely, or not - at all - i.e. it could be ()* or ()? or even (){0} in the pattern. Brackets - with fixed upper repeat limits are compiled as a number of copies, with the - optional ones preceded by BRAZERO or BRAMINZERO. */ - - case OP_BRAZERO: - next_ecode = ecode + 1; - RMATCH(eptr, next_ecode, offset_top, mb, eptrb, RM10); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - do next_ecode += GET(next_ecode, 1); while (*next_ecode == OP_ALT); - ecode = next_ecode + 1 + LINK_SIZE; - break; - - case OP_BRAMINZERO: - next_ecode = ecode + 1; - do next_ecode += GET(next_ecode, 1); while (*next_ecode == OP_ALT); - RMATCH(eptr, next_ecode + 1+LINK_SIZE, offset_top, mb, eptrb, RM11); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - ecode++; - break; - - case OP_SKIPZERO: - next_ecode = ecode+1; - do next_ecode += GET(next_ecode,1); while (*next_ecode == OP_ALT); - ecode = next_ecode + 1 + LINK_SIZE; - break; - - /* BRAPOSZERO occurs before a possessive bracket group. Don't do anything - here; just jump to the group, with allow_zero set TRUE. */ - - case OP_BRAPOSZERO: - op = *(++ecode); - allow_zero = TRUE; - if (op == OP_CBRAPOS || op == OP_SCBRAPOS) goto POSSESSIVE_CAPTURE; - goto POSSESSIVE_NON_CAPTURE; - - /* End of a group, repeated or non-repeating. */ - - case OP_KET: - case OP_KETRMIN: - case OP_KETRMAX: - case OP_KETRPOS: - prev = ecode - GET(ecode, 1); - - /* If this was a group that remembered the subject start, in order to break - infinite repeats of empty string matches, retrieve the subject start from - the chain. Otherwise, set it NULL. */ - - if (*prev >= OP_SBRA || *prev == OP_ONCE) - { - saved_eptr = eptrb->epb_saved_eptr; /* Value at start of group */ - eptrb = eptrb->epb_prev; /* Backup to previous group */ - } - else saved_eptr = NULL; - - /* If we are at the end of an assertion group or a non-capturing atomic - group, stop matching and return MATCH_MATCH, but record the current high - water mark for use by positive assertions. We also need to record the match - start in case it was changed by \K. */ - - if ((*prev >= OP_ASSERT && *prev <= OP_ASSERTBACK_NOT) || - *prev == OP_ONCE_NC) - { - mb->end_match_ptr = eptr; /* For ONCE_NC */ - mb->end_offset_top = offset_top; - mb->start_match_ptr = mstart; - if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr; - RRETURN(MATCH_MATCH); /* Sets mb->mark */ - } - - /* For capturing groups we have to check the group number back at the start - and if necessary complete handling an extraction by setting the offsets and - bumping the high water mark. Whole-pattern recursion is coded as a recurse - into group 0, so it won't be picked up here. Instead, we catch it when the - OP_END is reached. Other recursion is handled here. We just have to record - the current subject position and start match pointer and give a MATCH - return. */ - - if (*prev == OP_CBRA || *prev == OP_SCBRA || - *prev == OP_CBRAPOS || *prev == OP_SCBRAPOS) - { - number = GET2(prev, 1+LINK_SIZE); - offset = number << 1; - - /* Handle a recursively called group. */ - - if (mb->recursive != NULL && mb->recursive->group_num == number) - { - mb->end_match_ptr = eptr; - mb->start_match_ptr = mstart; - if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr; - RRETURN(MATCH_MATCH); - } - - /* Deal with capturing */ - - mb->capture_last = (mb->capture_last & OVFLMASK) | number; - if (offset >= mb->offset_max) mb->capture_last |= OVFLBIT; else - { - /* If offset is greater than offset_top, it means that we are - "skipping" a capturing group, and that group's offsets must be marked - unset. In earlier versions of PCRE, all the offsets were unset at the - start of matching, but this doesn't work because atomic groups and - assertions can cause a value to be set that should later be unset. - Example: matching /(?>(a))b|(a)c/ against "ac". This sets group 1 as - part of the atomic group, but this is not on the final matching path, - so must be unset when 2 is set. (If there is no group 2, there is no - problem, because offset_top will then be 2, indicating no capture.) */ - - if (offset > offset_top) - { - register PCRE2_SIZE *iptr = mb->ovector + offset_top; - register PCRE2_SIZE *iend = mb->ovector + offset; - while (iptr < iend) *iptr++ = PCRE2_UNSET; - } - - /* Now make the extraction */ - - mb->ovector[offset] = mb->ovector[mb->offset_end - number]; - mb->ovector[offset+1] = eptr - mb->start_subject; - if (offset_top <= offset) offset_top = offset + 2; - } - } - - /* OP_KETRPOS is a possessive repeating ket. Remember the current position, - and return the MATCH_KETRPOS. This makes it possible to do the repeats one - at a time from the outer level, thus saving stack. This must precede the - empty string test - in this case that test is done at the outer level. */ - - if (*ecode == OP_KETRPOS) - { - mb->start_match_ptr = mstart; /* In case \K reset it */ - mb->end_match_ptr = eptr; - mb->end_offset_top = offset_top; - if (eptr > mb->last_used_ptr) mb->last_used_ptr = eptr; - RRETURN(MATCH_KETRPOS); - } - - /* For an ordinary non-repeating ket, just continue at this level. This - also happens for a repeating ket if no characters were matched in the - group. This is the forcible breaking of infinite loops as implemented in - Perl 5.005. For a non-repeating atomic group that includes captures, - establish a backup point by processing the rest of the pattern at a lower - level. If this results in a NOMATCH return, pass MATCH_ONCE back to the - original OP_ONCE level, thereby bypassing intermediate backup points, but - resetting any captures that happened along the way. */ - - if (*ecode == OP_KET || eptr == saved_eptr) - { - if (*prev == OP_ONCE) - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM12); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->once_target = prev; /* Level at which to change to MATCH_NOMATCH */ - RRETURN(MATCH_ONCE); - } - ecode += 1 + LINK_SIZE; /* Carry on at this level */ - break; - } - - /* The normal repeating kets try the rest of the pattern or restart from - the preceding bracket, in the appropriate order. In the second case, we can - use tail recursion to avoid using another stack frame, unless we have an - an atomic group or an unlimited repeat of a group that can match an empty - string. */ - - if (*ecode == OP_KETRMIN) - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM7); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (*prev == OP_ONCE) - { - RMATCH(eptr, prev, offset_top, mb, eptrb, RM8); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->once_target = prev; /* Level at which to change to MATCH_NOMATCH */ - RRETURN(MATCH_ONCE); - } - if (*prev >= OP_SBRA) /* Could match an empty string */ - { - RMATCH(eptr, prev, offset_top, mb, eptrb, RM50); - RRETURN(rrc); - } - ecode = prev; - goto TAIL_RECURSE; - } - else /* OP_KETRMAX */ - { - RMATCH(eptr, prev, offset_top, mb, eptrb, RM13); - if (rrc == MATCH_ONCE && mb->once_target == prev) rrc = MATCH_NOMATCH; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (*prev == OP_ONCE) - { - RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, mb, eptrb, RM9); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - mb->once_target = prev; - RRETURN(MATCH_ONCE); - } - ecode += 1 + LINK_SIZE; - goto TAIL_RECURSE; - } - /* Control never gets here */ - - /* Not multiline mode: start of subject assertion, unless notbol. */ - - case OP_CIRC: - if ((mb->moptions & PCRE2_NOTBOL) != 0 && eptr == mb->start_subject) - RRETURN(MATCH_NOMATCH); - - /* Start of subject assertion */ - - case OP_SOD: - if (eptr != mb->start_subject) RRETURN(MATCH_NOMATCH); - ecode++; - break; - - /* Multiline mode: start of subject unless notbol, or after any newline - except for one at the very end, unless PCRE2_ALT_CIRCUMFLEX is set. */ - - case OP_CIRCM: - if ((mb->moptions & PCRE2_NOTBOL) != 0 && eptr == mb->start_subject) - RRETURN(MATCH_NOMATCH); - if (eptr != mb->start_subject && - ((eptr == mb->end_subject && - (mb->poptions & PCRE2_ALT_CIRCUMFLEX) == 0) || - !WAS_NEWLINE(eptr))) - RRETURN(MATCH_NOMATCH); - ecode++; - break; - - /* Start of match assertion */ - - case OP_SOM: - if (eptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH); - ecode++; - break; - - /* Reset the start of match point */ - - case OP_SET_SOM: - mstart = eptr; - ecode++; - break; - - /* Multiline mode: assert before any newline, or before end of subject - unless noteol is set. */ - - case OP_DOLLM: - if (eptr < mb->end_subject) - { - if (!IS_NEWLINE(eptr)) - { - if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - UCHAR21TEST(eptr) == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - RRETURN(MATCH_NOMATCH); - } - } - else - { - if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH); - SCHECK_PARTIAL(); - } - ecode++; - break; - - /* Not multiline mode: assert before a terminating newline or before end of - subject unless noteol is set. */ - - case OP_DOLL: - if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH); - if ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0) goto ASSERT_NL_OR_EOS; - - /* ... else fall through for endonly */ - - /* End of subject assertion (\z) */ - - case OP_EOD: - if (eptr < mb->end_subject) RRETURN(MATCH_NOMATCH); - SCHECK_PARTIAL(); - ecode++; - break; - - /* End of subject or ending \n assertion (\Z) */ - - case OP_EODN: - ASSERT_NL_OR_EOS: - if (eptr < mb->end_subject && - (!IS_NEWLINE(eptr) || eptr != mb->end_subject - mb->nllen)) - { - if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - UCHAR21TEST(eptr) == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - RRETURN(MATCH_NOMATCH); - } - - /* Either at end of string or \n before end. */ - - SCHECK_PARTIAL(); - ecode++; - break; - - /* Word boundary assertions */ - - case OP_NOT_WORD_BOUNDARY: - case OP_WORD_BOUNDARY: - { - - /* Find out if the previous and current characters are "word" characters. - It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to - be "non-word" characters. Remember the earliest consulted character for - partial matching. */ - -#ifdef SUPPORT_UNICODE - if (utf) - { - /* Get status of previous character */ - - if (eptr == mb->start_subject) prev_is_word = FALSE; else - { - PCRE2_SPTR lastptr = eptr - 1; - BACKCHAR(lastptr); - if (lastptr < mb->start_used_ptr) mb->start_used_ptr = lastptr; - GETCHAR(c, lastptr); - if ((mb->poptions & PCRE2_UCP) != 0) - { - if (c == '_') prev_is_word = TRUE; else - { - int cat = UCD_CATEGORY(c); - prev_is_word = (cat == ucp_L || cat == ucp_N); - } - } - else - prev_is_word = c < 256 && (mb->ctypes[c] & ctype_word) != 0; - } - - /* Get status of next character */ - - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - cur_is_word = FALSE; - } - else - { - PCRE2_SPTR nextptr = eptr + 1; - FORWARDCHARTEST(nextptr, mb->end_subject); - if (nextptr > mb->last_used_ptr) mb->last_used_ptr = nextptr; - GETCHAR(c, eptr); - if ((mb->poptions & PCRE2_UCP) != 0) - { - if (c == '_') cur_is_word = TRUE; else - { - int cat = UCD_CATEGORY(c); - cur_is_word = (cat == ucp_L || cat == ucp_N); - } - } - else - cur_is_word = c < 256 && (mb->ctypes[c] & ctype_word) != 0; - } - } - else -#endif /* SUPPORT UTF */ - - /* Not in UTF-8 mode, but we may still have PCRE2_UCP set, and for - consistency with the behaviour of \w we do use it in this case. */ - - { - /* Get status of previous character */ - - if (eptr == mb->start_subject) prev_is_word = FALSE; else - { - if (eptr <= mb->start_used_ptr) mb->start_used_ptr = eptr - 1; -#ifdef SUPPORT_UNICODE - if ((mb->poptions & PCRE2_UCP) != 0) - { - c = eptr[-1]; - if (c == '_') prev_is_word = TRUE; else - { - int cat = UCD_CATEGORY(c); - prev_is_word = (cat == ucp_L || cat == ucp_N); - } - } - else -#endif - prev_is_word = MAX_255(eptr[-1]) - && ((mb->ctypes[eptr[-1]] & ctype_word) != 0); - } - - /* Get status of next character */ - - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - cur_is_word = FALSE; - } - else - { - if (eptr >= mb->last_used_ptr) mb->last_used_ptr = eptr + 1; -#ifdef SUPPORT_UNICODE - if ((mb->poptions & PCRE2_UCP) != 0) - { - c = *eptr; - if (c == '_') cur_is_word = TRUE; else - { - int cat = UCD_CATEGORY(c); - cur_is_word = (cat == ucp_L || cat == ucp_N); - } - } - else -#endif - cur_is_word = MAX_255(*eptr) - && ((mb->ctypes[*eptr] & ctype_word) != 0); - } - } - - /* Now see if the situation is what we want */ - - if ((*ecode++ == OP_WORD_BOUNDARY)? - cur_is_word == prev_is_word : cur_is_word != prev_is_word) - RRETURN(MATCH_NOMATCH); - } - break; - - /* Match any single character type except newline; have to take care with - CRLF newlines and partial matching. */ - - case OP_ANY: - if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); - if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - UCHAR21TEST(eptr) == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - - /* Fall through */ - - /* Match any single character whatsoever. */ - - case OP_ALLANY: - if (eptr >= mb->end_subject) /* DO NOT merge the eptr++ here; it must */ - { /* not be updated before SCHECK_PARTIAL. */ - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr++; -#ifdef SUPPORT_UNICODE - if (utf) ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); -#endif - ecode++; - break; - - /* 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 */ - { /* not be updated before SCHECK_PARTIAL. */ - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr++; - ecode++; - break; - - case OP_NOT_DIGIT: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c < 256 && -#endif - (mb->ctypes[c] & ctype_digit) != 0 - ) - RRETURN(MATCH_NOMATCH); - ecode++; - break; - - case OP_DIGIT: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c > 255 || -#endif - (mb->ctypes[c] & ctype_digit) == 0 - ) - RRETURN(MATCH_NOMATCH); - ecode++; - break; - - case OP_NOT_WHITESPACE: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c < 256 && -#endif - (mb->ctypes[c] & ctype_space) != 0 - ) - RRETURN(MATCH_NOMATCH); - ecode++; - break; - - case OP_WHITESPACE: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c > 255 || -#endif - (mb->ctypes[c] & ctype_space) == 0 - ) - RRETURN(MATCH_NOMATCH); - ecode++; - break; - - case OP_NOT_WORDCHAR: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c < 256 && -#endif - (mb->ctypes[c] & ctype_word) != 0 - ) - RRETURN(MATCH_NOMATCH); - ecode++; - break; - - case OP_WORDCHAR: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ( -#ifdef SUPPORT_WIDE_CHARS - c > 255 || -#endif - (mb->ctypes[c] & ctype_word) == 0 - ) - RRETURN(MATCH_NOMATCH); - ecode++; - break; - - case OP_ANYNL: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - switch(c) - { - default: RRETURN(MATCH_NOMATCH); - - case CHAR_CR: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - } - else if (UCHAR21TEST(eptr) == CHAR_LF) eptr++; - break; - - case CHAR_LF: - break; - - case CHAR_VT: - case CHAR_FF: - case CHAR_NEL: -#ifndef EBCDIC - case 0x2028: - case 0x2029: -#endif /* Not EBCDIC */ - if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); - break; - } - ecode++; - break; - - case OP_NOT_HSPACE: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - switch(c) - { - HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */ - default: break; - } - ecode++; - break; - - case OP_HSPACE: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - switch(c) - { - HSPACE_CASES: break; /* Byte and multibyte cases */ - default: RRETURN(MATCH_NOMATCH); - } - ecode++; - break; - - case OP_NOT_VSPACE: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - switch(c) - { - VSPACE_CASES: RRETURN(MATCH_NOMATCH); - default: break; - } - ecode++; - break; - - case OP_VSPACE: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - switch(c) - { - VSPACE_CASES: break; - default: RRETURN(MATCH_NOMATCH); - } - ecode++; - break; - -#ifdef SUPPORT_UNICODE - /* Check the next character by Unicode property. We will get here only - if the support is in the binary; otherwise a compile-time error occurs. */ - - case OP_PROP: - case OP_NOTPROP: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - { - const uint32_t *cp; - const ucd_record *prop = GET_UCD(c); - - switch(ecode[1]) - { - case PT_ANY: - if (op == OP_NOTPROP) RRETURN(MATCH_NOMATCH); - break; - - case PT_LAMP: - if ((prop->chartype == ucp_Lu || - prop->chartype == ucp_Ll || - prop->chartype == ucp_Lt) == (op == OP_NOTPROP)) - RRETURN(MATCH_NOMATCH); - break; - - case PT_GC: - if ((ecode[2] != PRIV(ucp_gentype)[prop->chartype]) == (op == OP_PROP)) - RRETURN(MATCH_NOMATCH); - break; - - case PT_PC: - if ((ecode[2] != prop->chartype) == (op == OP_PROP)) - RRETURN(MATCH_NOMATCH); - break; - - case PT_SC: - if ((ecode[2] != prop->script) == (op == OP_PROP)) - RRETURN(MATCH_NOMATCH); - break; - - /* These are specials */ - - case PT_ALNUM: - if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || - PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (op == OP_NOTPROP)) - RRETURN(MATCH_NOMATCH); - break; - - /* Perl space used to exclude VT, but from Perl 5.18 it is included, - which means that Perl space and POSIX space are now identical. PCRE - was changed at release 8.34. */ - - case PT_SPACE: /* Perl space */ - case PT_PXSPACE: /* POSIX space */ - switch(c) - { - HSPACE_CASES: - VSPACE_CASES: - if (op == OP_NOTPROP) RRETURN(MATCH_NOMATCH); - break; - - default: - if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == - (op == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); - break; - } - break; - - case PT_WORD: - if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || - PRIV(ucp_gentype)[prop->chartype] == ucp_N || - c == CHAR_UNDERSCORE) == (op == OP_NOTPROP)) - RRETURN(MATCH_NOMATCH); - break; - - case PT_CLIST: - cp = PRIV(ucd_caseless_sets) + ecode[2]; - for (;;) - { - if (c < *cp) - { if (op == OP_PROP) { RRETURN(MATCH_NOMATCH); } else break; } - if (c == *cp++) - { if (op == OP_PROP) break; else { RRETURN(MATCH_NOMATCH); } } - } - break; - - case PT_UCNC: - if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || - c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || - c >= 0xe000) == (op == OP_NOTPROP)) - RRETURN(MATCH_NOMATCH); - break; - - /* This should never occur */ - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - - ecode += 3; - } - break; - - /* Match an extended Unicode sequence. We will get here only if the support - is in the binary; otherwise a compile-time error occurs. */ - - case OP_EXTUNI: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - else - { - int lgb, rgb; - GETCHARINCTEST(c, eptr); - lgb = UCD_GRAPHBREAK(c); - while (eptr < mb->end_subject) - { - int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); - if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; - } - } - CHECK_PARTIAL(); - ecode++; - break; -#endif /* SUPPORT_UNICODE */ - - - /* Match a back reference, possibly repeatedly. Look past the end of the - item to see if there is repeat information following. - - The OP_REF and OP_REFI opcodes are used for a reference to a numbered group - or to a non-duplicated named group. For a duplicated named group, OP_DNREF - and OP_DNREFI are used. In this case we must scan the list of groups to - which the name refers, and use the first one that is set. */ - - case OP_DNREF: - case OP_DNREFI: - caseless = op == OP_DNREFI; - { - int count = GET2(ecode, 1+IMM2_SIZE); - PCRE2_SPTR slot = mb->name_table + GET2(ecode, 1) * mb->name_entry_size; - ecode += 1 + 2*IMM2_SIZE; - - /* Initializing 'offset' avoids a compiler warning in the REF_REPEAT - code. */ - - offset = 0; - while (count-- > 0) - { - offset = GET2(slot, 0) << 1; - if (offset < offset_top && mb->ovector[offset] != PCRE2_UNSET) break; - slot += mb->name_entry_size; - } - } - goto REF_REPEAT; - - case OP_REF: - case OP_REFI: - caseless = op == OP_REFI; - offset = GET2(ecode, 1) << 1; /* Doubled ref number */ - ecode += 1 + IMM2_SIZE; - - /* Set up for repetition, or handle the non-repeated case */ - - REF_REPEAT: - switch (*ecode) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - c = *ecode++ - OP_CRSTAR; - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - break; - - case OP_CRRANGE: - case OP_CRMINRANGE: - minimize = (*ecode == OP_CRMINRANGE); - min = GET2(ecode, 1); - max = GET2(ecode, 1 + IMM2_SIZE); - if (max == 0) max = INT_MAX; - ecode += 1 + 2 * IMM2_SIZE; - break; - - default: /* No repeat follows */ - { - int rc = match_ref(offset, offset_top, eptr, mb, caseless, &length); - if (rc != 0) - { - if (rc > 0) eptr = mb->end_subject; /* Partial match */ - CHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - } - eptr += length; - continue; /* With the main loop */ - } - - /* Handle repeated back references. If a set group has length zero, just - continue with the main loop, because it matches however many times. For an - unset reference, if the minimum is zero, we can also just continue. We an - also continue if PCRE2_MATCH_UNSET_BACKREF is set, because this makes unset - group be have as a zero-length group. For any other unset cases, carrying - on will result in NOMATCH. */ - - if (offset < offset_top && mb->ovector[offset] != PCRE2_UNSET) - { - if (mb->ovector[offset] == mb->ovector[offset + 1]) continue; - } - else /* Group is not set */ - { - if (min == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0) - continue; - } - - /* First, ensure the minimum number of matches are present. We get back - the length of the reference string explicitly rather than passing the - address of eptr, so that eptr can be a register variable. */ - - for (i = 1; i <= min; i++) - { - PCRE2_SIZE slength; - int rc = match_ref(offset, offset_top, eptr, mb, caseless, &slength); - if (rc != 0) - { - if (rc > 0) eptr = mb->end_subject; /* Partial match */ - CHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr += slength; - } - - /* If min = max, continue at the same level without recursion. - They are not both allowed to be zero. */ - - if (min == max) continue; - - /* If minimizing, keep trying and advancing the pointer */ - - if (minimize) - { - for (fi = min;; fi++) - { - int rc; - PCRE2_SIZE slength; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM14); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - rc = match_ref(offset, offset_top, eptr, mb, caseless, &slength); - if (rc != 0) - { - if (rc > 0) eptr = mb->end_subject; /* Partial match */ - CHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr += slength; - } - /* Control never gets here */ - } - - /* If maximizing, find the longest string and work backwards, as long as - the matched lengths for each iteration are the same. */ - - else - { - BOOL samelengths = TRUE; - pp = eptr; - length = mb->ovector[offset+1] - mb->ovector[offset]; - - for (i = min; i < max; i++) - { - PCRE2_SIZE slength; - int rc = match_ref(offset, offset_top, eptr, mb, caseless, &slength); - - if (rc != 0) - { - /* Can't use CHECK_PARTIAL because we don't want to update eptr in - the soft partial matching case. */ - - if (rc > 0 && mb->partial != 0 && - mb->end_subject > mb->start_used_ptr) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - break; - } - - if (slength != length) samelengths = FALSE; - eptr += slength; - } - - /* If the length matched for each repetition is the same as the length of - the captured group, we can easily work backwards. This is the normal - case. However, in caseless UTF-8 mode there are pairs of case-equivalent - characters whose lengths (in terms of code units) differ. However, this - is very rare, so we handle it by re-matching fewer and fewer times. */ - - if (samelengths) - { - while (eptr >= pp) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM15); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr -= length; - } - } - - /* The rare case of non-matching lengths. Re-scan the repetition for each - iteration. We know that match_ref() will succeed every time. */ - - else - { - max = i; - for (;;) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM68); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr == pp) break; /* Failed after minimal repetition */ - eptr = pp; - max--; - for (i = min; i < max; i++) - { - PCRE2_SIZE slength; - (void)match_ref(offset, offset_top, eptr, mb, caseless, &slength); - eptr += slength; - } - } - } - - RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - - /* Match a bit-mapped character class, possibly repeatedly. This op code is - used when all the characters in the class have values in the range 0-255, - and either the matching is caseful, or the characters are in the range - 0-127 when UTF-8 processing is enabled. The only difference between - OP_CLASS and OP_NCLASS occurs when a data character outside the range is - encountered. - - First, look past the end of the item to see if there is repeat information - following. Then obey similar code to character type repeats - written out - again for speed. */ - - case OP_NCLASS: - case OP_CLASS: - { - /* The data variable is saved across frames, so the byte map needs to - be stored there. */ -#define BYTE_MAP ((uint8_t *)data) - data = ecode + 1; /* Save for matching */ - ecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */ - - switch (*ecode) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - case OP_CRPOSSTAR: - case OP_CRPOSPLUS: - case OP_CRPOSQUERY: - c = *ecode++ - OP_CRSTAR; - if (c < OP_CRPOSSTAR - OP_CRSTAR) minimize = (c & 1) != 0; - else possessive = TRUE; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - break; - - case OP_CRRANGE: - case OP_CRMINRANGE: - case OP_CRPOSRANGE: - minimize = (*ecode == OP_CRMINRANGE); - possessive = (*ecode == OP_CRPOSRANGE); - min = GET2(ecode, 1); - max = GET2(ecode, 1 + IMM2_SIZE); - if (max == 0) max = INT_MAX; - ecode += 1 + 2 * IMM2_SIZE; - break; - - default: /* No repeat follows */ - min = max = 1; - break; - } - - /* First, ensure the minimum number of matches are present. */ - -#ifdef SUPPORT_UNICODE - if (utf) - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - if (c > 255) - { - if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); - } - else - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); - } - } - else -#endif - /* Not UTF mode */ - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - c = *eptr++; -#if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) - { - if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); - } - else -#endif - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); - } - } - - /* If max == min we can continue with the main loop without the - need to recurse. */ - - if (min == max) continue; - - /* If minimizing, keep testing the rest of the expression and advancing - the pointer while it matches the class. */ - - if (minimize) - { -#ifdef SUPPORT_UNICODE - if (utf) - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM16); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - if (c > 255) - { - if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); - } - else - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); - } - } - else -#endif - /* Not UTF mode */ - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM17); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - c = *eptr++; -#if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) - { - if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); - } - else -#endif - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); - } - } - /* Control never gets here */ - } - - /* If maximizing, find the longest possible run, then work backwards. */ - - else - { - pp = eptr; - -#ifdef SUPPORT_UNICODE - if (utf) - { - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c > 255) - { - if (op == OP_CLASS) break; - } - else - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break; - eptr += len; - } - - if (possessive) continue; /* No backtracking */ - - for (;;) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM18); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr-- == pp) break; /* Stop if tried at original pos */ - BACKCHAR(eptr); - } - } - else -#endif - /* Not UTF mode */ - { - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - c = *eptr; -#if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) - { - if (op == OP_CLASS) break; - } - else -#endif - if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break; - eptr++; - } - - if (possessive) continue; /* No backtracking */ - - while (eptr >= pp) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM19); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - } - } - - RRETURN(MATCH_NOMATCH); - } -#undef BYTE_MAP - } - /* Control never gets here */ - - - /* Match an extended character class. In the 8-bit library, this opcode is - encountered only when UTF-8 mode mode is supported. In the 16-bit and - 32-bit libraries, codepoints greater than 255 may be encountered even when - UTF is not supported. */ - -#ifdef SUPPORT_WIDE_CHARS - case OP_XCLASS: - { - data = ecode + 1 + LINK_SIZE; /* Save for matching */ - ecode += GET(ecode, 1); /* Advance past the item */ - - switch (*ecode) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - case OP_CRPOSSTAR: - case OP_CRPOSPLUS: - case OP_CRPOSQUERY: - c = *ecode++ - OP_CRSTAR; - if (c < OP_CRPOSSTAR - OP_CRSTAR) minimize = (c & 1) != 0; - else possessive = TRUE; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - break; - - case OP_CRRANGE: - case OP_CRMINRANGE: - case OP_CRPOSRANGE: - minimize = (*ecode == OP_CRMINRANGE); - possessive = (*ecode == OP_CRPOSRANGE); - min = GET2(ecode, 1); - max = GET2(ecode, 1 + IMM2_SIZE); - if (max == 0) max = INT_MAX; - ecode += 1 + 2 * IMM2_SIZE; - break; - - default: /* No repeat follows */ - min = max = 1; - break; - } - - /* First, ensure the minimum number of matches are present. */ - - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if (!PRIV(xclass)(c, data, utf)) RRETURN(MATCH_NOMATCH); - } - - /* If max == min we can continue with the main loop without the - need to recurse. */ - - if (min == max) continue; - - /* If minimizing, keep testing the rest of the expression and advancing - the pointer while it matches the class. */ - - if (minimize) - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM20); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if (!PRIV(xclass)(c, data, utf)) RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - } - - /* If maximizing, find the longest possible run, then work backwards. */ - - else - { - pp = eptr; - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } -#ifdef SUPPORT_UNICODE - GETCHARLENTEST(c, eptr, len); -#else - c = *eptr; -#endif - if (!PRIV(xclass)(c, data, utf)) break; - eptr += len; - } - - if (possessive) continue; /* No backtracking */ - - for(;;) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM21); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (eptr-- == pp) break; /* Stop if tried at original pos */ -#ifdef SUPPORT_UNICODE - if (utf) BACKCHAR(eptr); -#endif - } - RRETURN(MATCH_NOMATCH); - } - - /* Control never gets here */ - } -#endif /* End of XCLASS */ - - /* Match a single character, casefully */ - - case OP_CHAR: -#ifdef SUPPORT_UNICODE - if (utf) - { - length = 1; - ecode++; - GETCHARLEN(fc, ecode, length); - if (length > (PCRE2_SIZE)(mb->end_subject - eptr)) - { - CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */ - RRETURN(MATCH_NOMATCH); - } - for (; length > 0; length--) - { - if (*ecode++ != UCHAR21INC(eptr)) RRETURN(MATCH_NOMATCH); - } - } - else -#endif - /* Not UTF mode */ - { - if (mb->end_subject - eptr < 1) - { - SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */ - RRETURN(MATCH_NOMATCH); - } - if (ecode[1] != *eptr++) RRETURN(MATCH_NOMATCH); - ecode += 2; - } - break; - - /* Match a single character, caselessly. If we are at the end of the - subject, give up immediately. */ - - case OP_CHARI: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - -#ifdef SUPPORT_UNICODE - if (utf) - { - length = 1; - ecode++; - GETCHARLEN(fc, ecode, length); - - /* If the pattern character's value is < 128, we have only one byte, and - we know that its other case must also be one byte long, so we can use the - fast lookup table. We know that there is at least one byte left in the - subject. */ - - if (fc < 128) - { - uint32_t cc = UCHAR21(eptr); - if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH); - ecode++; - eptr++; - } - - /* Otherwise we must pick up the subject character. Note that we cannot - use the value of "length" to check for sufficient bytes left, because the - other case of the character may have more or fewer bytes. */ - - else - { - uint32_t dc; - GETCHARINC(dc, eptr); - ecode += length; - - /* If we have Unicode property support, we can use it to test the other - case of the character, if there is one. */ - - if (fc != dc) - { -#ifdef SUPPORT_UNICODE - if (dc != UCD_OTHERCASE(fc)) -#endif - RRETURN(MATCH_NOMATCH); - } - } - } - else -#endif /* SUPPORT_UNICODE */ - - /* Not UTF mode */ - { - if (TABLE_GET(ecode[1], mb->lcc, ecode[1]) - != TABLE_GET(*eptr, mb->lcc, *eptr)) RRETURN(MATCH_NOMATCH); - eptr++; - ecode += 2; - } - break; - - /* Match a single character repeatedly. */ - - case OP_EXACT: - case OP_EXACTI: - min = max = GET2(ecode, 1); - ecode += 1 + IMM2_SIZE; - goto REPEATCHAR; - - case OP_POSUPTO: - case OP_POSUPTOI: - possessive = TRUE; - /* Fall through */ - - case OP_UPTO: - case OP_UPTOI: - case OP_MINUPTO: - case OP_MINUPTOI: - min = 0; - max = GET2(ecode, 1); - minimize = *ecode == OP_MINUPTO || *ecode == OP_MINUPTOI; - ecode += 1 + IMM2_SIZE; - goto REPEATCHAR; - - case OP_POSSTAR: - case OP_POSSTARI: - possessive = TRUE; - min = 0; - max = INT_MAX; - ecode++; - goto REPEATCHAR; - - case OP_POSPLUS: - case OP_POSPLUSI: - possessive = TRUE; - min = 1; - max = INT_MAX; - ecode++; - goto REPEATCHAR; - - case OP_POSQUERY: - case OP_POSQUERYI: - possessive = TRUE; - min = 0; - max = 1; - ecode++; - goto REPEATCHAR; - - case OP_STAR: - case OP_STARI: - case OP_MINSTAR: - case OP_MINSTARI: - case OP_PLUS: - case OP_PLUSI: - case OP_MINPLUS: - case OP_MINPLUSI: - case OP_QUERY: - case OP_QUERYI: - case OP_MINQUERY: - case OP_MINQUERYI: - c = *ecode++ - ((op < OP_STARI)? OP_STAR : OP_STARI); - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - - /* Common code for all repeated single-character matches. We first check - for the minimum number of characters. If the minimum equals the maximum, we - are done. Otherwise, if minimizing, check the rest of the pattern for a - match; if there isn't one, advance up to the maximum, one character at a - time. - - If maximizing, advance up to the maximum number of matching characters, - until eptr is past the end of the maximum run. If possessive, we are - then done (no backing up). Otherwise, match at this position; anything - other than no match is immediately returned. For nomatch, back up one - character, unless we are matching \R and the last thing matched was - \r\n, in which case, back up two bytes. When we reach the first optional - character position, we can save stack by doing a tail recurse. - - The various UTF/non-UTF and caseful/caseless cases are handled separately, - for speed. */ - - REPEATCHAR: -#ifdef SUPPORT_UNICODE - if (utf) - { - length = 1; - charptr = ecode; - GETCHARLEN(fc, ecode, length); - ecode += length; - - /* Handle multibyte character matching specially here. There is - support for caseless matching if UCP support is present. */ - - if (length > 1) - { - uint32_t othercase; - if (op >= OP_STARI && /* Caseless */ - (othercase = UCD_OTHERCASE(fc)) != fc) - oclength = PRIV(ord2utf)(othercase, occhars); - else oclength = 0; - - for (i = 1; i <= min; i++) - { - if (eptr <= mb->end_subject - length && - memcmp(eptr, charptr, CU2BYTES(length)) == 0) eptr += length; - else if (oclength > 0 && - eptr <= mb->end_subject - oclength && - memcmp(eptr, occhars, CU2BYTES(oclength)) == 0) eptr += oclength; - else - { - CHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - } - - if (min == max) continue; - - if (minimize) - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM22); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr <= mb->end_subject - length && - memcmp(eptr, charptr, CU2BYTES(length)) == 0) eptr += length; - else if (oclength > 0 && - eptr <= mb->end_subject - oclength && - memcmp(eptr, occhars, CU2BYTES(oclength)) == 0) eptr += oclength; - else - { - CHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - } - /* Control never gets here */ - } - - else /* Maximize */ - { - pp = eptr; - for (i = min; i < max; i++) - { - if (eptr <= mb->end_subject - length && - memcmp(eptr, charptr, CU2BYTES(length)) == 0) eptr += length; - else if (oclength > 0 && - eptr <= mb->end_subject - oclength && - memcmp(eptr, occhars, CU2BYTES(oclength)) == 0) eptr += oclength; - else - { - CHECK_PARTIAL(); - break; - } - } - - if (possessive) continue; /* No backtracking */ - - /* After \C in UTF mode, pp might be in the middle of a Unicode - character. Use <= pp to ensure backtracking doesn't go too far. */ - - for(;;) - { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM23); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); - } - } - /* Control never gets here */ - } - - /* If the length of a UTF-8 character is 1, we fall through here, and - obey the code as for non-UTF-8 characters below, though in this case the - value of fc will always be < 128. */ - } - else -#endif /* SUPPORT_UNICODE */ - - /* When not in UTF-8 mode, load a single-byte character. */ - fc = *ecode++; - - /* The value of fc at this point is always one character, though we may - or may not be in UTF mode. The code is duplicated for the caseless and - caseful cases, for speed, since matching characters is likely to be quite - common. First, ensure the minimum number of matches are present. If min = - max, continue at the same level without recursing. Otherwise, if - minimizing, keep trying the rest of the expression and advancing one - matching character if failing, up to the maximum. Alternatively, if - maximizing, find the maximum number of characters and work backwards. */ - - if (op >= OP_STARI) /* Caseless */ - { -#if PCRE2_CODE_UNIT_WIDTH == 8 - /* fc must be < 128 if UTF is enabled. */ - foc = mb->fcc[fc]; -#else -#ifdef SUPPORT_UNICODE - if (utf && fc > 127) - foc = UCD_OTHERCASE(fc); - else -#endif /* SUPPORT_UNICODE */ - foc = TABLE_GET(fc, mb->fcc, fc); -#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ - - for (i = 1; i <= min; i++) - { - uint32_t cc; /* Faster than PCRE2_UCHAR */ - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21TEST(eptr); - if (fc != cc && foc != cc) RRETURN(MATCH_NOMATCH); - eptr++; - } - if (min == max) continue; - if (minimize) - { - for (fi = min;; fi++) - { - uint32_t cc; /* Faster than PCRE2_UCHAR */ - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM24); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21TEST(eptr); - if (fc != cc && foc != cc) RRETURN(MATCH_NOMATCH); - eptr++; - } - /* Control never gets here */ - } - else /* Maximize */ - { - pp = eptr; - for (i = min; i < max; i++) - { - uint32_t cc; /* Faster than PCRE2_UCHAR */ - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - cc = UCHAR21TEST(eptr); - if (fc != cc && foc != cc) break; - eptr++; - } - if (possessive) continue; /* No backtracking */ - for (;;) - { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM25); - eptr--; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - } - /* Control never gets here */ - } - } - - /* Caseful comparisons (includes all multi-byte characters) */ - - else - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (fc != UCHAR21INCTEST(eptr)) RRETURN(MATCH_NOMATCH); - } - - if (min == max) continue; - - if (minimize) - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM26); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (fc != UCHAR21INCTEST(eptr)) RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - } - else /* Maximize */ - { - pp = eptr; - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (fc != UCHAR21TEST(eptr)) break; - eptr++; - } - if (possessive) continue; /* No backtracking */ - for (;;) - { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM27); - eptr--; - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - } - /* Control never gets here */ - } - } - /* Control never gets here */ - - /* Match a negated single one-byte character. The character we are - checking can be multibyte. */ - - case OP_NOT: - case OP_NOTI: - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t ch, och; - - ecode++; - GETCHARINC(ch, ecode); - GETCHARINC(c, eptr); - - if (op == OP_NOT) - { - if (ch == c) RRETURN(MATCH_NOMATCH); - } - else - { - if (ch > 127) - och = UCD_OTHERCASE(ch); - else - och = TABLE_GET(ch, mb->fcc, ch); - if (ch == c || och == c) RRETURN(MATCH_NOMATCH); - } - } - else -#endif /* SUPPORT_UNICODE */ - { - register uint32_t ch = ecode[1]; - c = *eptr++; - if (ch == c || (op == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == c)) - RRETURN(MATCH_NOMATCH); - ecode += 2; - } - break; - - /* Match a negated single one-byte character repeatedly. This is almost a - repeat of the code for a repeated single character, but I haven't found a - nice way of commoning these up that doesn't require a test of the - positive/negative option for each character match. Maybe that wouldn't add - very much to the time taken, but character matching *is* what this is all - about... */ - - case OP_NOTEXACT: - case OP_NOTEXACTI: - min = max = GET2(ecode, 1); - ecode += 1 + IMM2_SIZE; - goto REPEATNOTCHAR; - - case OP_NOTUPTO: - case OP_NOTUPTOI: - case OP_NOTMINUPTO: - case OP_NOTMINUPTOI: - min = 0; - max = GET2(ecode, 1); - minimize = *ecode == OP_NOTMINUPTO || *ecode == OP_NOTMINUPTOI; - ecode += 1 + IMM2_SIZE; - goto REPEATNOTCHAR; - - case OP_NOTPOSSTAR: - case OP_NOTPOSSTARI: - possessive = TRUE; - min = 0; - max = INT_MAX; - ecode++; - goto REPEATNOTCHAR; - - case OP_NOTPOSPLUS: - case OP_NOTPOSPLUSI: - possessive = TRUE; - min = 1; - max = INT_MAX; - ecode++; - goto REPEATNOTCHAR; - - case OP_NOTPOSQUERY: - case OP_NOTPOSQUERYI: - possessive = TRUE; - min = 0; - max = 1; - ecode++; - goto REPEATNOTCHAR; - - case OP_NOTPOSUPTO: - case OP_NOTPOSUPTOI: - possessive = TRUE; - min = 0; - max = GET2(ecode, 1); - ecode += 1 + IMM2_SIZE; - goto REPEATNOTCHAR; - - case OP_NOTSTAR: - case OP_NOTSTARI: - case OP_NOTMINSTAR: - case OP_NOTMINSTARI: - case OP_NOTPLUS: - case OP_NOTPLUSI: - case OP_NOTMINPLUS: - case OP_NOTMINPLUSI: - case OP_NOTQUERY: - case OP_NOTQUERYI: - case OP_NOTMINQUERY: - case OP_NOTMINQUERYI: - c = *ecode++ - ((op >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR); - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - - /* Common code for all repeated single-byte matches. */ - - REPEATNOTCHAR: - GETCHARINCTEST(fc, ecode); - - /* The code is duplicated for the caseless and caseful cases, for speed, - since matching characters is likely to be quite common. First, ensure the - minimum number of matches are present. If min = max, continue at the same - level without recursing. Otherwise, if minimizing, keep trying the rest of - the expression and advancing one matching character if failing, up to the - maximum. Alternatively, if maximizing, find the maximum number of - characters and work backwards. */ - - if (op >= OP_NOTSTARI) /* Caseless */ - { -#ifdef SUPPORT_UNICODE - if (utf && fc > 127) - foc = UCD_OTHERCASE(fc); - else -#endif /* SUPPORT_UNICODE */ - foc = TABLE_GET(fc, mb->fcc, fc); - -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(d, eptr); - if (fc == d || (uint32_t)foc == d) RRETURN(MATCH_NOMATCH); - } - } - else -#endif /* SUPPORT_UNICODE */ - /* Not UTF mode */ - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH); - eptr++; - } - } - - if (min == max) continue; - - if (minimize) - { -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM28); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(d, eptr); - if (fc == d || (uint32_t)foc == d) RRETURN(MATCH_NOMATCH); - } - } - else -#endif /*SUPPORT_UNICODE */ - /* Not UTF mode */ - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM29); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH); - eptr++; - } - } - /* Control never gets here */ - } - - /* Maximize case */ - - else - { - pp = eptr; - -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(d, eptr, len); - if (fc == d || (uint32_t)foc == d) break; - eptr += len; - } - if (possessive) continue; /* No backtracking */ - - /* After \C in UTF mode, pp might be in the middle of a Unicode - character. Use <= pp to ensure backtracking doesn't go too far. */ - - for(;;) - { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM30); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); - } - } - else -#endif /* SUPPORT_UNICODE */ - /* Not UTF mode */ - { - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (fc == *eptr || foc == *eptr) break; - eptr++; - } - if (possessive) continue; /* No backtracking */ - for (;;) - { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM31); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - } - } - /* Control never gets here */ - } - } - - /* Caseful comparisons */ - - else - { -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(d, eptr); - if (fc == d) RRETURN(MATCH_NOMATCH); - } - } - else -#endif - /* Not UTF mode */ - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (fc == *eptr++) RRETURN(MATCH_NOMATCH); - } - } - - if (min == max) continue; - - if (minimize) - { -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM32); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(d, eptr); - if (fc == d) RRETURN(MATCH_NOMATCH); - } - } - else -#endif - /* Not UTF mode */ - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM33); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (fc == *eptr++) RRETURN(MATCH_NOMATCH); - } - } - /* Control never gets here */ - } - - /* Maximize case */ - - else - { - pp = eptr; - -#ifdef SUPPORT_UNICODE - if (utf) - { - register uint32_t d; - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(d, eptr, len); - if (fc == d) break; - eptr += len; - } - if (possessive) continue; /* No backtracking */ - - /* After \C in UTF mode, pp might be in the middle of a Unicode - character. Use <= pp to ensure backtracking doesn't go too far. */ - - for(;;) - { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM34); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); - } - } - else -#endif - /* Not UTF mode */ - { - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (fc == *eptr) break; - eptr++; - } - if (possessive) continue; /* No backtracking */ - for (;;) - { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM35); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - } - } - /* Control never gets here */ - } - } - /* Control never gets here */ - - /* Match a single character type repeatedly; several different opcodes - share code. This is very similar to the code for single characters, but we - repeat it in the interests of efficiency. */ - - case OP_TYPEEXACT: - min = max = GET2(ecode, 1); - minimize = TRUE; - ecode += 1 + IMM2_SIZE; - goto REPEATTYPE; - - case OP_TYPEUPTO: - case OP_TYPEMINUPTO: - min = 0; - max = GET2(ecode, 1); - minimize = *ecode == OP_TYPEMINUPTO; - ecode += 1 + IMM2_SIZE; - goto REPEATTYPE; - - case OP_TYPEPOSSTAR: - possessive = TRUE; - min = 0; - max = INT_MAX; - ecode++; - goto REPEATTYPE; - - case OP_TYPEPOSPLUS: - possessive = TRUE; - min = 1; - max = INT_MAX; - ecode++; - goto REPEATTYPE; - - case OP_TYPEPOSQUERY: - possessive = TRUE; - min = 0; - max = 1; - ecode++; - goto REPEATTYPE; - - case OP_TYPEPOSUPTO: - possessive = TRUE; - min = 0; - max = GET2(ecode, 1); - ecode += 1 + IMM2_SIZE; - goto REPEATTYPE; - - case OP_TYPESTAR: - case OP_TYPEMINSTAR: - case OP_TYPEPLUS: - case OP_TYPEMINPLUS: - case OP_TYPEQUERY: - case OP_TYPEMINQUERY: - c = *ecode++ - OP_TYPESTAR; - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - - /* Common code for all repeated single character type matches. Note that - in UTF-8 mode, '.' matches a character of any length, but for the other - character types, the valid characters are all one-byte long. */ - - REPEATTYPE: - ctype = *ecode++; /* Code for the character type */ - -#ifdef SUPPORT_UNICODE - if (ctype == OP_PROP || ctype == OP_NOTPROP) - { - prop_fail_result = ctype == OP_NOTPROP; - prop_type = *ecode++; - prop_value = *ecode++; - } - else prop_type = -1; -#endif - - /* First, ensure the minimum number of matches are present. Use inline - code for maximizing the speed, and do the type test once at the start - (i.e. keep it out of the loop). Separate the UTF-8 code completely as that - is tidier. Also separate the UCP code, which can be the same for both UTF-8 - and single-bytes. */ - - if (min > 0) - { -#ifdef SUPPORT_UNICODE - if (prop_type >= 0) - { - switch(prop_type) - { - case PT_ANY: - if (prop_fail_result) RRETURN(MATCH_NOMATCH); - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - } - break; - - case PT_LAMP: - for (i = 1; i <= min; i++) - { - int chartype; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - chartype = UCD_CHARTYPE(c); - if ((chartype == ucp_Lu || - chartype == ucp_Ll || - chartype == ucp_Lt) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - case PT_GC: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - case PT_PC: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - case PT_SC: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - case PT_ALNUM: - for (i = 1; i <= min; i++) - { - int category; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - category = UCD_CATEGORY(c); - if ((category == ucp_L || category == ucp_N) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - /* Perl space used to exclude VT, but from Perl 5.18 it is included, - which means that Perl space and POSIX space are now identical. PCRE - was changed at release 8.34. */ - - case PT_SPACE: /* Perl space */ - case PT_PXSPACE: /* POSIX space */ - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - switch(c) - { - HSPACE_CASES: - VSPACE_CASES: - if (prop_fail_result) RRETURN(MATCH_NOMATCH); - break; - - default: - if ((UCD_CATEGORY(c) == ucp_Z) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - break; - } - } - break; - - case PT_WORD: - for (i = 1; i <= min; i++) - { - int category; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - category = UCD_CATEGORY(c); - if ((category == ucp_L || category == ucp_N || c == CHAR_UNDERSCORE) - == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - case PT_CLIST: - for (i = 1; i <= min; i++) - { - const uint32_t *cp; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - cp = PRIV(ucd_caseless_sets) + prop_value; - for (;;) - { - if (c < *cp) - { if (prop_fail_result) break; else { RRETURN(MATCH_NOMATCH); } } - if (c == *cp++) - { if (prop_fail_result) { RRETURN(MATCH_NOMATCH); } else break; } - } - } - break; - - case PT_UCNC: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || - c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || - c >= 0xe000) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - break; - - /* This should not occur */ - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - } - - /* Match extended Unicode sequences. We will get here only if the - support is in the binary; otherwise a compile-time error occurs. */ - - else if (ctype == OP_EXTUNI) - { - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - else - { - int lgb, rgb; - GETCHARINCTEST(c, eptr); - lgb = UCD_GRAPHBREAK(c); - while (eptr < mb->end_subject) - { - int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); - if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; - } - } - CHECK_PARTIAL(); - } - } - - else -#endif /* SUPPORT_UNICODE */ - -/* Handle all other cases when the coding is UTF-8 */ - -#ifdef SUPPORT_UNICODE - if (utf) switch(ctype) - { - case OP_ANY: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); - if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - UCHAR21(eptr) == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - break; - - case OP_ALLANY: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - break; - - case OP_ANYBYTE: - if (eptr > mb->end_subject - min) RRETURN(MATCH_NOMATCH); - eptr += min; - break; - - case OP_ANYNL: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - switch(c) - { - default: RRETURN(MATCH_NOMATCH); - - case CHAR_CR: - if (eptr < mb->end_subject && UCHAR21(eptr) == CHAR_LF) eptr++; - break; - - case CHAR_LF: - break; - - case CHAR_VT: - case CHAR_FF: - case CHAR_NEL: -#ifndef EBCDIC - case 0x2028: - case 0x2029: -#endif /* Not EBCDIC */ - if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); - break; - } - } - break; - - case OP_NOT_HSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - switch(c) - { - HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */ - default: break; - } - } - break; - - case OP_HSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - switch(c) - { - HSPACE_CASES: break; /* Byte and multibyte cases */ - default: RRETURN(MATCH_NOMATCH); - } - } - break; - - case OP_NOT_VSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - switch(c) - { - VSPACE_CASES: RRETURN(MATCH_NOMATCH); - default: break; - } - } - break; - - case OP_VSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - switch(c) - { - VSPACE_CASES: break; - default: RRETURN(MATCH_NOMATCH); - } - } - break; - - case OP_NOT_DIGIT: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINC(c, eptr); - if (c < 128 && (mb->ctypes[c] & ctype_digit) != 0) - RRETURN(MATCH_NOMATCH); - } - break; - - case OP_DIGIT: - for (i = 1; i <= min; i++) - { - uint32_t cc; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21(eptr); - if (cc >= 128 || (mb->ctypes[cc] & ctype_digit) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - /* No need to skip more bytes - we know it's a 1-byte character */ - } - break; - - case OP_NOT_WHITESPACE: - for (i = 1; i <= min; i++) - { - uint32_t cc; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21(eptr); - if (cc < 128 && (mb->ctypes[cc] & ctype_space) != 0) - RRETURN(MATCH_NOMATCH); - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - break; - - case OP_WHITESPACE: - for (i = 1; i <= min; i++) - { - uint32_t cc; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21(eptr); - if (cc >= 128 || (mb->ctypes[cc] & ctype_space) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - /* No need to skip more bytes - we know it's a 1-byte character */ - } - break; - - case OP_NOT_WORDCHAR: - for (i = 1; i <= min; i++) - { - uint32_t cc; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21(eptr); - if (cc < 128 && (mb->ctypes[cc] & ctype_word) != 0) - RRETURN(MATCH_NOMATCH); - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - break; - - case OP_WORDCHAR: - for (i = 1; i <= min; i++) - { - uint32_t cc; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - cc = UCHAR21(eptr); - if (cc >= 128 || (mb->ctypes[cc] & ctype_word) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - /* No need to skip more bytes - we know it's a 1-byte character */ - } - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } /* End switch(ctype) */ - - else -#endif /* SUPPORT_UNICODE */ - - /* Code for the non-UTF-8 case for minimum matching of operators other - than OP_PROP and OP_NOTPROP. */ - - switch(ctype) - { - case OP_ANY: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); - if (mb->partial != 0 && - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - *eptr == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - eptr++; - } - break; - - case OP_ALLANY: - if (eptr > mb->end_subject - min) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr += min; - break; - - case OP_ANYBYTE: - if (eptr > mb->end_subject - min) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - eptr += min; - break; - - case OP_ANYNL: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) - { - default: RRETURN(MATCH_NOMATCH); - - case CHAR_CR: - if (eptr < mb->end_subject && *eptr == CHAR_LF) eptr++; - break; - - case CHAR_LF: - break; - - case CHAR_VT: - case CHAR_FF: - case CHAR_NEL: -#if PCRE2_CODE_UNIT_WIDTH != 8 - case 0x2028: - case 0x2029: -#endif - if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); - break; - } - } - break; - - case OP_NOT_HSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) - { - default: break; - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif - RRETURN(MATCH_NOMATCH); - } - } - break; - - case OP_HSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) - { - default: RRETURN(MATCH_NOMATCH); - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif - break; - } - } - break; - - case OP_NOT_VSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) - { - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - RRETURN(MATCH_NOMATCH); - default: break; - } - } - break; - - case OP_VSPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - switch(*eptr++) - { - default: RRETURN(MATCH_NOMATCH); - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - break; - } - } - break; - - case OP_NOT_DIGIT: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_digit) != 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; - - case OP_DIGIT: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_digit) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; - - case OP_NOT_WHITESPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_space) != 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; - - case OP_WHITESPACE: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_space) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; - - case OP_NOT_WORDCHAR: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_word) != 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; - - case OP_WORDCHAR: - for (i = 1; i <= min; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_word) == 0) - RRETURN(MATCH_NOMATCH); - eptr++; - } - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - } - - /* If min = max, continue at the same level without recursing */ - - if (min == max) continue; - - /* If minimizing, we have to test the rest of the pattern before each - subsequent match. Again, separate the UTF-8 case for speed, and also - separate the UCP cases. */ - - if (minimize) - { -#ifdef SUPPORT_UNICODE - if (prop_type >= 0) - { - switch(prop_type) - { - case PT_ANY: - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM36); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if (prop_fail_result) RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - - case PT_LAMP: - for (fi = min;; fi++) - { - int chartype; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM37); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - chartype = UCD_CHARTYPE(c); - if ((chartype == ucp_Lu || - chartype == ucp_Ll || - chartype == ucp_Lt) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - - case PT_GC: - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM38); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - - case PT_PC: - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM39); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - - case PT_SC: - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM40); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - - case PT_ALNUM: - for (fi = min;; fi++) - { - int category; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM59); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - category = UCD_CATEGORY(c); - if ((category == ucp_L || category == ucp_N) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - - /* Perl space used to exclude VT, but from Perl 5.18 it is included, - which means that Perl space and POSIX space are now identical. PCRE - was changed at release 8.34. */ - - case PT_SPACE: /* Perl space */ - case PT_PXSPACE: /* POSIX space */ - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM61); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - switch(c) - { - HSPACE_CASES: - VSPACE_CASES: - if (prop_fail_result) RRETURN(MATCH_NOMATCH); - break; - - default: - if ((UCD_CATEGORY(c) == ucp_Z) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - break; - } - } - /* Control never gets here */ - - case PT_WORD: - for (fi = min;; fi++) - { - int category; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM62); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - category = UCD_CATEGORY(c); - if ((category == ucp_L || - category == ucp_N || - c == CHAR_UNDERSCORE) - == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - - case PT_CLIST: - for (fi = min;; fi++) - { - const uint32_t *cp; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM67); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - cp = PRIV(ucd_caseless_sets) + prop_value; - for (;;) - { - if (c < *cp) - { if (prop_fail_result) break; else { RRETURN(MATCH_NOMATCH); } } - if (c == *cp++) - { if (prop_fail_result) { RRETURN(MATCH_NOMATCH); } else break; } - } - } - /* Control never gets here */ - - case PT_UCNC: - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM60); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - GETCHARINCTEST(c, eptr); - if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || - c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || - c >= 0xe000) == prop_fail_result) - RRETURN(MATCH_NOMATCH); - } - /* Control never gets here */ - - /* This should never occur */ - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - } - - /* Match extended Unicode sequences. We will get here only if the - support is in the binary; otherwise a compile-time error occurs. */ - - else if (ctype == OP_EXTUNI) - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM41); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - else - { - int lgb, rgb; - GETCHARINCTEST(c, eptr); - lgb = UCD_GRAPHBREAK(c); - while (eptr < mb->end_subject) - { - int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); - if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; - } - } - CHECK_PARTIAL(); - } - } - else -#endif /* SUPPORT_UNICODE */ - -#ifdef SUPPORT_UNICODE - if (utf) - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM42); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (ctype == OP_ANY && IS_NEWLINE(eptr)) - RRETURN(MATCH_NOMATCH); - GETCHARINC(c, eptr); - switch(ctype) - { - case OP_ANY: /* This is the non-NL case */ - if (mb->partial != 0 && /* Take care with CRLF partial */ - eptr >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - c == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - break; - - case OP_ALLANY: - case OP_ANYBYTE: - break; - - case OP_ANYNL: - switch(c) - { - default: RRETURN(MATCH_NOMATCH); - case CHAR_CR: - if (eptr < mb->end_subject && UCHAR21(eptr) == CHAR_LF) eptr++; - break; - - case CHAR_LF: - break; - - case CHAR_VT: - case CHAR_FF: - case CHAR_NEL: -#ifndef EBCDIC - case 0x2028: - case 0x2029: -#endif /* Not EBCDIC */ - if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); - break; - } - break; - - case OP_NOT_HSPACE: - switch(c) - { - HSPACE_CASES: RRETURN(MATCH_NOMATCH); - default: break; - } - break; - - case OP_HSPACE: - switch(c) - { - HSPACE_CASES: break; - default: RRETURN(MATCH_NOMATCH); - } - break; - - case OP_NOT_VSPACE: - switch(c) - { - VSPACE_CASES: RRETURN(MATCH_NOMATCH); - default: break; - } - break; - - case OP_VSPACE: - switch(c) - { - VSPACE_CASES: break; - default: RRETURN(MATCH_NOMATCH); - } - break; - - case OP_NOT_DIGIT: - if (c < 256 && (mb->ctypes[c] & ctype_digit) != 0) - RRETURN(MATCH_NOMATCH); - break; - - case OP_DIGIT: - if (c >= 256 || (mb->ctypes[c] & ctype_digit) == 0) - RRETURN(MATCH_NOMATCH); - break; - - case OP_NOT_WHITESPACE: - if (c < 256 && (mb->ctypes[c] & ctype_space) != 0) - RRETURN(MATCH_NOMATCH); - break; - - case OP_WHITESPACE: - if (c >= 256 || (mb->ctypes[c] & ctype_space) == 0) - RRETURN(MATCH_NOMATCH); - break; - - case OP_NOT_WORDCHAR: - if (c < 256 && (mb->ctypes[c] & ctype_word) != 0) - RRETURN(MATCH_NOMATCH); - break; - - case OP_WORDCHAR: - if (c >= 256 || (mb->ctypes[c] & ctype_word) == 0) - RRETURN(MATCH_NOMATCH); - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - } - } - else -#endif - /* Not UTF mode */ - { - for (fi = min;; fi++) - { - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM43); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - if (fi >= max) RRETURN(MATCH_NOMATCH); - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - RRETURN(MATCH_NOMATCH); - } - if (ctype == OP_ANY && IS_NEWLINE(eptr)) - RRETURN(MATCH_NOMATCH); - c = *eptr++; - switch(ctype) - { - case OP_ANY: /* This is the non-NL case */ - if (mb->partial != 0 && /* Take care with CRLF partial */ - eptr >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - c == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - break; - - case OP_ALLANY: - case OP_ANYBYTE: - break; - - case OP_ANYNL: - switch(c) - { - default: RRETURN(MATCH_NOMATCH); - case CHAR_CR: - if (eptr < mb->end_subject && *eptr == CHAR_LF) eptr++; - break; - - case CHAR_LF: - break; - - case CHAR_VT: - case CHAR_FF: - case CHAR_NEL: -#if PCRE2_CODE_UNIT_WIDTH != 8 - case 0x2028: - case 0x2029: -#endif - if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); - break; - } - break; - - case OP_NOT_HSPACE: - switch(c) - { - default: break; - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif - RRETURN(MATCH_NOMATCH); - } - break; - - case OP_HSPACE: - switch(c) - { - default: RRETURN(MATCH_NOMATCH); - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif - break; - } - break; - - case OP_NOT_VSPACE: - switch(c) - { - default: break; - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - RRETURN(MATCH_NOMATCH); - } - break; - - case OP_VSPACE: - switch(c) - { - default: RRETURN(MATCH_NOMATCH); - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - break; - } - break; - - case OP_NOT_DIGIT: - if (MAX_255(c) && (mb->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); - break; - - case OP_DIGIT: - if (!MAX_255(c) || (mb->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); - break; - - case OP_NOT_WHITESPACE: - if (MAX_255(c) && (mb->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); - break; - - case OP_WHITESPACE: - if (!MAX_255(c) || (mb->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); - break; - - case OP_NOT_WORDCHAR: - if (MAX_255(c) && (mb->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); - break; - - case OP_WORDCHAR: - if (!MAX_255(c) || (mb->ctypes[c] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - } - } - /* Control never gets here */ - } - - /* If maximizing, it is worth using inline code for speed, doing the type - test once at the start (i.e. keep it out of the loop). Again, keep the - UTF-8 and UCP stuff separate. */ - - else - { - pp = eptr; /* Remember where we started */ - -#ifdef SUPPORT_UNICODE - if (prop_type >= 0) - { - switch(prop_type) - { - case PT_ANY: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLENTEST(c, eptr, len); - if (prop_fail_result) break; - eptr+= len; - } - break; - - case PT_LAMP: - for (i = min; i < max; i++) - { - int chartype; - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLENTEST(c, eptr, len); - chartype = UCD_CHARTYPE(c); - if ((chartype == ucp_Lu || - chartype == ucp_Ll || - chartype == ucp_Lt) == prop_fail_result) - break; - eptr+= len; - } - break; - - case PT_GC: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLENTEST(c, eptr, len); - if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) break; - eptr+= len; - } - break; - - case PT_PC: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLENTEST(c, eptr, len); - if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) break; - eptr+= len; - } - break; - - case PT_SC: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLENTEST(c, eptr, len); - if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) break; - eptr+= len; - } - break; - - case PT_ALNUM: - for (i = min; i < max; i++) - { - int category; - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLENTEST(c, eptr, len); - category = UCD_CATEGORY(c); - if ((category == ucp_L || category == ucp_N) == prop_fail_result) - break; - eptr+= len; - } - break; - - /* Perl space used to exclude VT, but from Perl 5.18 it is included, - which means that Perl space and POSIX space are now identical. PCRE - was changed at release 8.34. */ - - case PT_SPACE: /* Perl space */ - case PT_PXSPACE: /* POSIX space */ - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLENTEST(c, eptr, len); - switch(c) - { - HSPACE_CASES: - VSPACE_CASES: - if (prop_fail_result) goto ENDLOOP99; /* Break the loop */ - break; - - default: - if ((UCD_CATEGORY(c) == ucp_Z) == prop_fail_result) - goto ENDLOOP99; /* Break the loop */ - break; - } - eptr+= len; - } - ENDLOOP99: - break; - - case PT_WORD: - for (i = min; i < max; i++) - { - int category; - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLENTEST(c, eptr, len); - category = UCD_CATEGORY(c); - if ((category == ucp_L || category == ucp_N || - c == CHAR_UNDERSCORE) == prop_fail_result) - break; - eptr+= len; - } - break; - - case PT_CLIST: - for (i = min; i < max; i++) - { - const uint32_t *cp; - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLENTEST(c, eptr, len); - cp = PRIV(ucd_caseless_sets) + prop_value; - for (;;) - { - if (c < *cp) - { if (prop_fail_result) break; else goto GOT_MAX; } - if (c == *cp++) - { if (prop_fail_result) goto GOT_MAX; else break; } - } - eptr += len; - } - GOT_MAX: - break; - - case PT_UCNC: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLENTEST(c, eptr, len); - if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT || - c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) || - c >= 0xe000) == prop_fail_result) - break; - eptr += len; - } - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - - /* eptr is now past the end of the maximum run */ - - if (possessive) continue; /* No backtracking */ - - /* After \C in UTF mode, pp might be in the middle of a Unicode - character. Use <= pp to ensure backtracking doesn't go too far. */ - - for(;;) - { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM44); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - if (utf) BACKCHAR(eptr); - } - } - - /* Match extended Unicode grapheme clusters. We will get here only if the - support is in the binary; otherwise a compile-time error occurs. */ - - else if (ctype == OP_EXTUNI) - { - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - else - { - int lgb, rgb; - GETCHARINCTEST(c, eptr); - lgb = UCD_GRAPHBREAK(c); - while (eptr < mb->end_subject) - { - int len = 1; - if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } - rgb = UCD_GRAPHBREAK(c); - if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - lgb = rgb; - eptr += len; - } - } - CHECK_PARTIAL(); - } - - /* eptr is now past the end of the maximum run */ - - if (possessive) continue; /* No backtracking */ - - /* We use <= pp rather than == pp to detect the start of the run while - backtracking because the use of \C in UTF mode can cause BACKCHAR to - move back past pp. This is just palliative; the use of \C in UTF mode - is fraught with danger. */ - - for(;;) - { - int lgb, rgb; - PCRE2_SPTR fptr; - - if (eptr <= pp) goto TAIL_RECURSE; /* At start of char run */ - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM45); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - - /* Backtracking over an extended grapheme cluster involves inspecting - the previous two characters (if present) to see if a break is - permitted between them. */ - - eptr--; - if (!utf) c = *eptr; else - { - BACKCHAR(eptr); - GETCHAR(c, eptr); - } - rgb = UCD_GRAPHBREAK(c); - - for (;;) - { - if (eptr <= pp) goto TAIL_RECURSE; /* At start of char run */ - fptr = eptr - 1; - if (!utf) c = *fptr; else - { - BACKCHAR(fptr); - GETCHAR(c, fptr); - } - lgb = UCD_GRAPHBREAK(c); - if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; - eptr = fptr; - rgb = lgb; - } - } - } - - else -#endif /* SUPPORT_UNICODE */ - -#ifdef SUPPORT_UNICODE - if (utf) - { - switch(ctype) - { - case OP_ANY: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (IS_NEWLINE(eptr)) break; - if (mb->partial != 0 && /* Take care with CRLF partial */ - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - UCHAR21(eptr) == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - break; - - case OP_ALLANY: - if (max < INT_MAX) - { - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - eptr++; - ACROSSCHAR(eptr < mb->end_subject, *eptr, eptr++); - } - } - else - { - eptr = mb->end_subject; /* Unlimited UTF-8 repeat */ - SCHECK_PARTIAL(); - } - break; - - /* The byte case is the same as non-UTF8 */ - - case OP_ANYBYTE: - c = max - min; - if (c > (uint32_t)(mb->end_subject - eptr)) - { - eptr = mb->end_subject; - SCHECK_PARTIAL(); - } - else eptr += c; - break; - - case OP_ANYNL: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c == CHAR_CR) - { - if (++eptr >= mb->end_subject) break; - if (UCHAR21(eptr) == CHAR_LF) eptr++; - } - else - { - if (c != CHAR_LF && - (mb->bsr_convention == PCRE2_BSR_ANYCRLF || - (c != CHAR_VT && c != CHAR_FF && c != CHAR_NEL -#ifndef EBCDIC - && c != 0x2028 && c != 0x2029 -#endif /* Not EBCDIC */ - ))) - break; - eptr += len; - } - } - break; - - case OP_NOT_HSPACE: - case OP_HSPACE: - for (i = min; i < max; i++) - { - BOOL gotspace; - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - switch(c) - { - HSPACE_CASES: gotspace = TRUE; break; - default: gotspace = FALSE; break; - } - if (gotspace == (ctype == OP_NOT_HSPACE)) break; - eptr += len; - } - break; - - case OP_NOT_VSPACE: - case OP_VSPACE: - for (i = min; i < max; i++) - { - BOOL gotspace; - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - switch(c) - { - VSPACE_CASES: gotspace = TRUE; break; - default: gotspace = FALSE; break; - } - if (gotspace == (ctype == OP_NOT_VSPACE)) break; - eptr += len; - } - break; - - case OP_NOT_DIGIT: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c < 256 && (mb->ctypes[c] & ctype_digit) != 0) break; - eptr+= len; - } - break; - - case OP_DIGIT: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c >= 256 ||(mb->ctypes[c] & ctype_digit) == 0) break; - eptr+= len; - } - break; - - case OP_NOT_WHITESPACE: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c < 256 && (mb->ctypes[c] & ctype_space) != 0) break; - eptr+= len; - } - break; - - case OP_WHITESPACE: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c >= 256 ||(mb->ctypes[c] & ctype_space) == 0) break; - eptr+= len; - } - break; - - case OP_NOT_WORDCHAR: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c < 256 && (mb->ctypes[c] & ctype_word) != 0) break; - eptr+= len; - } - break; - - case OP_WORDCHAR: - for (i = min; i < max; i++) - { - int len = 1; - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - GETCHARLEN(c, eptr, len); - if (c >= 256 || (mb->ctypes[c] & ctype_word) == 0) break; - eptr+= len; - } - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - - if (possessive) continue; /* No backtracking */ - - /* After \C in UTF mode, pp might be in the middle of a Unicode - character. Use <= pp to ensure backtracking doesn't go too far. */ - - for(;;) - { - if (eptr <= pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM46); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - BACKCHAR(eptr); - if (ctype == OP_ANYNL && eptr > pp && UCHAR21(eptr) == CHAR_NL && - UCHAR21(eptr - 1) == CHAR_CR) eptr--; - } - } - else -#endif /* SUPPORT_UNICODE */ - /* Not UTF mode */ - { - switch(ctype) - { - case OP_ANY: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (IS_NEWLINE(eptr)) break; - if (mb->partial != 0 && /* Take care with CRLF partial */ - eptr + 1 >= mb->end_subject && - NLBLOCK->nltype == NLTYPE_FIXED && - NLBLOCK->nllen == 2 && - *eptr == NLBLOCK->nl[0]) - { - mb->hitend = TRUE; - if (mb->partial > 1) RRETURN(PCRE2_ERROR_PARTIAL); - } - eptr++; - } - break; - - case OP_ALLANY: - case OP_ANYBYTE: - c = max - min; - if (c > (uint32_t)(mb->end_subject - eptr)) - { - eptr = mb->end_subject; - SCHECK_PARTIAL(); - } - else eptr += c; - break; - - case OP_ANYNL: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - c = *eptr; - if (c == CHAR_CR) - { - if (++eptr >= mb->end_subject) break; - if (*eptr == CHAR_LF) eptr++; - } - else - { - if (c != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF || - (c != CHAR_VT && c != CHAR_FF && c != CHAR_NEL -#if PCRE2_CODE_UNIT_WIDTH != 8 - && c != 0x2028 && c != 0x2029 -#endif - ))) break; - eptr++; - } - } - break; - - case OP_NOT_HSPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - switch(*eptr) - { - default: eptr++; break; - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif - goto ENDLOOP00; - } - } - ENDLOOP00: - break; - - case OP_HSPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - switch(*eptr) - { - default: goto ENDLOOP01; - HSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - HSPACE_MULTIBYTE_CASES: -#endif - eptr++; break; - } - } - ENDLOOP01: - break; - - case OP_NOT_VSPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - switch(*eptr) - { - default: eptr++; break; - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - goto ENDLOOP02; - } - } - ENDLOOP02: - break; - - case OP_VSPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - switch(*eptr) - { - default: goto ENDLOOP03; - VSPACE_BYTE_CASES: -#if PCRE2_CODE_UNIT_WIDTH != 8 - VSPACE_MULTIBYTE_CASES: -#endif - eptr++; break; - } - } - ENDLOOP03: - break; - - case OP_NOT_DIGIT: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_digit) != 0) break; - eptr++; - } - break; - - case OP_DIGIT: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_digit) == 0) break; - eptr++; - } - break; - - case OP_NOT_WHITESPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_space) != 0) break; - eptr++; - } - break; - - case OP_WHITESPACE: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_space) == 0) break; - eptr++; - } - break; - - case OP_NOT_WORDCHAR: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (MAX_255(*eptr) && (mb->ctypes[*eptr] & ctype_word) != 0) break; - eptr++; - } - break; - - case OP_WORDCHAR: - for (i = min; i < max; i++) - { - if (eptr >= mb->end_subject) - { - SCHECK_PARTIAL(); - break; - } - if (!MAX_255(*eptr) || (mb->ctypes[*eptr] & ctype_word) == 0) break; - eptr++; - } - break; - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - - if (possessive) continue; /* No backtracking */ - for (;;) - { - if (eptr == pp) goto TAIL_RECURSE; - RMATCH(eptr, ecode, offset_top, mb, eptrb, RM47); - if (rrc != MATCH_NOMATCH) RRETURN(rrc); - eptr--; - if (ctype == OP_ANYNL && eptr > pp && *eptr == CHAR_LF && - eptr[-1] == CHAR_CR) eptr--; - } - } - - /* Control never gets here */ - } - - /* There's been some horrible disaster. Arrival here can only mean there is - something seriously wrong in the code above or the OP_xxx definitions. */ - - default: - RRETURN(PCRE2_ERROR_INTERNAL); - } - - /* Do not stick any code in here without much thought; it is assumed - that "continue" in the code above comes out to here to repeat the main - loop. */ - - } /* End of main loop */ -/* Control never reaches here */ - - -/* When compiling to use the heap rather than the stack for recursive calls to -match(), the RRETURN() macro jumps here. The number that is saved in -frame->Xwhere indicates which label we actually want to return to. */ - -#ifdef HEAP_MATCH_RECURSE -#define LBL(val) case val: goto L_RM##val; -HEAP_RETURN: -switch (frame->Xwhere) - { - LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8) - LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(17) - LBL(19) LBL(24) LBL(25) LBL(26) LBL(27) LBL(29) LBL(31) LBL(33) - LBL(35) LBL(43) LBL(47) LBL(48) LBL(49) LBL(50) LBL(51) LBL(52) - LBL(53) LBL(54) LBL(55) LBL(56) LBL(57) LBL(58) LBL(63) LBL(64) - LBL(65) LBL(66) LBL(68) -#ifdef SUPPORT_WIDE_CHARS - LBL(20) LBL(21) -#endif -#ifdef SUPPORT_UNICODE - LBL(16) LBL(18) - LBL(22) LBL(23) LBL(28) LBL(30) - LBL(32) LBL(34) LBL(42) LBL(46) - LBL(36) LBL(37) LBL(38) LBL(39) LBL(40) LBL(41) LBL(44) LBL(45) - LBL(59) LBL(60) LBL(61) LBL(62) LBL(67) -#endif /* SUPPORT_UNICODE */ - default: - return PCRE2_ERROR_INTERNAL; - } -#undef LBL -#endif /* HEAP_MATCH_RECURSE */ -} - - -/*************************************************************************** -**************************************************************************** - RECURSION IN THE match() FUNCTION - -Undefine all the macros that were defined above to handle this. */ - -#ifdef HEAP_MATCH_RECURSE -#undef eptr -#undef ecode -#undef mstart -#undef offset_top -#undef eptrb -#undef flags - -#undef callpat -#undef charptr -#undef data -#undef next_ecode -#undef pp -#undef prev -#undef saved_eptr - -#undef new_recursive - -#undef cur_is_word -#undef condition -#undef prev_is_word - -#undef ctype -#undef length -#undef max -#undef min -#undef number -#undef offset -#undef op -#undef save_capture_last -#undef save_offset1 -#undef save_offset2 -#undef save_offset3 - -#undef newptrb -#endif /* HEAP_MATCH_RECURSE */ - -/* These two are defined as macros in both cases */ - -#undef fc -#undef fi - -/*************************************************************************** -***************************************************************************/ - - -#ifdef HEAP_MATCH_RECURSE -/************************************************* -* Release allocated heap frames * -*************************************************/ - -/* This function releases all the allocated frames. The base frame is on the -machine stack, and so must not be freed. - -Argument: - frame_base the address of the base frame - mb the match block - -Returns: nothing -*/ - -static void -release_match_heapframes (heapframe *frame_base, match_block *mb) -{ -heapframe *nextframe = frame_base->Xnextframe; -while (nextframe != NULL) - { - heapframe *oldframe = nextframe; - nextframe = nextframe->Xnextframe; - mb->stack_memctl.free(oldframe, mb->stack_memctl.memory_data); - } -} -#endif /* HEAP_MATCH_RECURSE */ - - - -/************************************************* -* Match a Regular Expression * -*************************************************/ - -/* This function applies a compiled pattern to a subject string and picks out -portions of the string if it matches. Two elements in the vector are set for -each substring: the offsets to the start and end of the substring. - -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 - mcontext points a PCRE2 context - -Returns: > 0 => success; value is the number of ovector pairs filled - = 0 => success, but ovector is not big enough - -1 => failed to match (PCRE2_ERROR_NOMATCH) - -2 => partial match (PCRE2_ERROR_PARTIAL) - < -2 => some kind of unexpected problem -*/ - -PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION -pcre2_match(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) -{ -int rc; -int ocount; - -const uint8_t *start_bits = NULL; - -const pcre2_real_code *re = (const pcre2_real_code *)code; - -BOOL anchored; -BOOL firstline; -BOOL has_first_cu = FALSE; -BOOL has_req_cu = FALSE; -BOOL startline; -BOOL using_temporary_offsets = FALSE; -BOOL utf; - -PCRE2_UCHAR first_cu = 0; -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; -PCRE2_SPTR start_partial = NULL; -PCRE2_SPTR match_partial = NULL; - -/* We need to have mb pointing to a match block, because the IS_NEWLINE macro -is used below, and it expects NLBLOCK to be defined as a pointer. */ - -match_block actual_match_block; -match_block *mb = &actual_match_block; - -#ifdef HEAP_MATCH_RECURSE -heapframe frame_zero; -frame_zero.Xprevframe = NULL; /* Marks the top level */ -frame_zero.Xnextframe = NULL; /* None are allocated yet */ -mb->match_frames_base = &frame_zero; -#endif - -/* A length equal to PCRE2_ZERO_TERMINATED implies a zero-terminated -subject string. */ - -if (length == PCRE2_ZERO_TERMINATED) length = PRIV(strlen)(subject); -end_subject = subject + length; - -/* Plausibility checks */ - -if ((options & ~PUBLIC_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION; -if (code == NULL || subject == NULL || match_data == NULL) - return PCRE2_ERROR_NULL; -if (start_offset > length) return PCRE2_ERROR_BADOFFSET; - -/* Check that the first field in the block is the magic number. */ - -if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC; - -/* Check the code unit width. */ - -if ((re->flags & PCRE2_MODE_MASK) != PCRE2_CODE_UNIT_WIDTH/8) - return PCRE2_ERROR_BADMODE; - -/* PCRE2_NOTEMPTY and PCRE2_NOTEMPTY_ATSTART are match-time flags in the -options variable for this function. Users of PCRE2 who are not calling the -function directly would like to have a way of setting these flags, in the same -way that they can set pcre2_compile() flags like PCRE2_NO_AUTOPOSSESS with -constructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and -(*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which can now be -transferred to the options for this function. The bits are guaranteed to be -adjacent, but do not have the same values. This bit of Boolean trickery assumes -that the match-time bits are not more significant than the flag bits. If by -accident this is not the case, a compile-time division by zero error will -occur. */ - -#define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET) -#define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART) -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)); - -/* These two settings are used in the code for checking a UTF string that -follows immediately afterwards. Other values in the mb block are used only -during interpretive pcre_match() processing, not when the JIT support is in -use, so they are set up later. */ - -utf = (re->overall_options & PCRE2_UTF) != 0; -mb->partial = ((options & PCRE2_PARTIAL_HARD) != 0)? 2 : - ((options & PCRE2_PARTIAL_SOFT) != 0)? 1 : 0; - -/* 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. 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) - { - PCRE2_SPTR check_subject = start_match; /* start_match includes offset */ - - if (start_offset > 0) - { -#if PCRE2_CODE_UNIT_WIDTH != 32 - 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 -an unsupported option is set or if JIT returns BADOPTION (which means that the -selected normal or partial matching mode was not compiled). */ - -#ifdef SUPPORT_JIT -if (re->executable_jit != NULL && (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0) - { - rc = pcre2_jit_match(code, subject, length, start_offset, options, - match_data, mcontext); - if (rc != PCRE2_ERROR_JIT_BADOPTION) return rc; - } -#endif - -/* Carry on with non-JIT matching. */ - -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. */ - -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->hasthen = (re->flags & PCRE2_HASTHEN) != 0; - -mb->moptions = options; /* Match options */ -mb->poptions = re->overall_options; /* Pattern options */ - -mb->ignore_skip_arg = 0; -mb->mark = mb->nomatch_mark = NULL; /* In case never set */ -mb->recursive = NULL; /* No recursion at top level */ -mb->ovecsave_chain = NULL; /* No ovecsave blocks yet */ -mb->hitend = FALSE; - -/* The name table is needed for finding all the numbers associated with a -given name, for condition testing. The code follows the name table. */ - -mb->name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)); -mb->name_count = re->name_count; -mb->name_entry_size = re->name_entry_size; -mb->start_code = mb->name_table + re->name_count * re->name_entry_size; - -/* Limits set in the pattern override the match context only if they are -smaller. */ - -mb->match_limit = (mcontext->match_limit < re->limit_match)? - mcontext->match_limit : re->limit_match; -mb->match_limit_recursion = (mcontext->recursion_limit < re->limit_recursion)? - mcontext->recursion_limit : re->limit_recursion; - -/* Pointers to the individual character tables */ - -mb->lcc = re->tables + lcc_offset; -mb->fcc = re->tables + fcc_offset; -mb->ctypes = re->tables + ctypes_offset; - -/* Process the \R and newline settings. */ - -mb->bsr_convention = re->bsr_convention; -mb->nltype = NLTYPE_FIXED; -switch(re->newline_convention) - { - case PCRE2_NEWLINE_CR: - mb->nllen = 1; - mb->nl[0] = CHAR_CR; - break; - - case PCRE2_NEWLINE_LF: - mb->nllen = 1; - mb->nl[0] = CHAR_NL; - break; - - case PCRE2_NEWLINE_CRLF: - mb->nllen = 2; - mb->nl[0] = CHAR_CR; - mb->nl[1] = CHAR_NL; - break; - - case PCRE2_NEWLINE_ANY: - mb->nltype = NLTYPE_ANY; - break; - - case PCRE2_NEWLINE_ANYCRLF: - mb->nltype = NLTYPE_ANYCRLF; - break; - - default: return PCRE2_ERROR_INTERNAL; - } - -/* If the expression has got more back references than the offsets supplied can -hold, we get a temporary chunk of memory to use during the matching. Otherwise, -we can use the vector supplied. The size of the ovector is three times the -value in the oveccount field. Two-thirds of it is pairs for storing matching -offsets, and the top third is working space. */ - -if (re->top_backref >= match_data->oveccount) - { - ocount = re->top_backref * 3 + 3; - mb->ovector = (PCRE2_SIZE *)(mb->memctl.malloc(ocount * sizeof(PCRE2_SIZE), - mb->memctl.memory_data)); - if (mb->ovector == NULL) return PCRE2_ERROR_NOMEMORY; - using_temporary_offsets = TRUE; - } -else - { - ocount = 3 * match_data->oveccount; - mb->ovector = match_data->ovector; - } - -mb->offset_end = ocount; -mb->offset_max = (2*ocount)/3; - -/* Reset the working variable associated with each extraction. These should -never be used unless previously set, but they get saved and restored, and so we -initialize them to avoid reading uninitialized locations. Also, unset the -offsets for the matched string. This is really just for tidiness with callouts, -in case they inspect these fields. */ - -if (ocount > 0) - { - register PCRE2_SIZE *iptr = mb->ovector + ocount; - register PCRE2_SIZE *iend = iptr - re->top_bracket; - if (iend < mb->ovector + 2) iend = mb->ovector + 2; - while (--iptr >= iend) *iptr = PCRE2_UNSET; - mb->ovector[0] = mb->ovector[1] = PCRE2_UNSET; - } - -/* Set up the first code unit to match, if available. The first_codeunit value -is never set for an anchored regular expression, but the anchoring may be -forced at run time, so we have to test for anchoring. The first code unit may -be unset for an unanchored pattern, of course. If there's no first code unit -there may be a bitmap of possible first characters. */ - -if (!anchored) - { - if ((re->flags & PCRE2_FIRSTSET) != 0) - { - has_first_cu = TRUE; - first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit); - if ((re->flags & PCRE2_FIRSTCASELESS) != 0) - { - first_cu2 = TABLE_GET(first_cu, mb->fcc, first_cu); -#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 - if (utf && first_cu > 127) first_cu2 = UCD_OTHERCASE(first_cu); -#endif - } - } - else - if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0) - start_bits = re->start_bitmap; - } - -/* For anchored or unanchored matches, there may be a "last known required -character" set. */ - -if ((re->flags & PCRE2_LASTSET) != 0) - { - has_req_cu = TRUE; - req_cu = req_cu2 = (PCRE2_UCHAR)(re->last_codeunit); - if ((re->flags & PCRE2_LASTCASELESS) != 0) - { - req_cu2 = TABLE_GET(req_cu, mb->fcc, req_cu); -#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 - if (utf && req_cu > 127) req_cu2 = UCD_OTHERCASE(req_cu); -#endif - } - } - - -/* ==========================================================================*/ - -/* Loop for handling unanchored repeated matching attempts; for anchored regexs -the loop runs just once. */ - -for(;;) - { - PCRE2_SPTR new_start_match; - mb->capture_last = 0; - - /* ----------------- Start of match optimizations ---------------- */ - - /* There are some optimizations that avoid running the match if a known - starting point is not found, or if a known later code unit is not present. - However, there is an option (settable at compile time) that disables these, - for testing and for ensuring that all callouts do actually occur. */ - - if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) - { - PCRE2_SPTR save_end_subject = end_subject; - - /* If firstline is TRUE, the start of the match is constrained to the first - line of a multiline string. That is, the match must be before or at the - first newline. Implement this by temporarily adjusting end_subject so that - we stop the optimization scans at a newline. If the match fails at the - newline, later code breaks this loop. */ - - if (firstline) - { - PCRE2_SPTR t = start_match; -#ifdef SUPPORT_UNICODE - if (utf) - { - while (t < mb->end_subject && !IS_NEWLINE(t)) - { - t++; - ACROSSCHAR(t < end_subject, *t, t++); - } - } - else -#endif - while (t < mb->end_subject && !IS_NEWLINE(t)) t++; - end_subject = t; - } - - /* 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) - { - PCRE2_UCHAR smc; - if (first_cu != first_cu2) - while (start_match < end_subject && - (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 */ - - else if (startline) - { - if (start_match > mb->start_subject + start_offset) - { -#ifdef SUPPORT_UNICODE - if (utf) - { - while (start_match < end_subject && !WAS_NEWLINE(start_match)) - { - start_match++; - ACROSSCHAR(start_match < end_subject, *start_match, - start_match++); - } - } - else -#endif - while (start_match < end_subject && !WAS_NEWLINE(start_match)) - start_match++; - - /* If we have just passed a CR and the newline option is ANY or - ANYCRLF, and we are now at a LF, advance the match position by one more - code unit. */ - - if (start_match[-1] == CHAR_CR && - (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) && - start_match < end_subject && - UCHAR21TEST(start_match) == CHAR_NL) - start_match++; - } - } - - /* Or to a non-unique first code unit if any have been identified. The - bitmap contains only 256 bits. When code units are 16 or 32 bits wide, all - code units greater than 254 set the 255 bit. */ - - else if (start_bits != NULL) - { - while (start_match < end_subject) - { - register uint32_t c = UCHAR21TEST(start_match); -#if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) c = 255; -#endif - if ((start_bits[c/8] & (1 << (c&7))) != 0) break; - start_match++; - } - } - - /* Restore fudged end_subject */ - - end_subject = save_end_subject; - - /* The following two optimizations are disabled for partial matching. */ - - if (!mb->partial) - { - /* The minimum matching length is a lower bound; no actual string of that - length may actually match the pattern. Although the value is, strictly, - in characters, we treat it as code units to avoid spending too much time - in this optimization. */ - - if (end_subject - start_match < re->minlength) - { - rc = MATCH_NOMATCH; - break; - } - - /* If req_cu is set, we know that that code unit must appear in the - subject for the match to succeed. If the first code unit is set, req_cu - must be later in the subject; otherwise the test starts at the match - point. This optimization can save a huge amount of backtracking in - patterns with nested unlimited repeats that aren't going to match. - Writing separate code for cased/caseless versions makes it go faster, as - does using an autoincrement and backing off on a match. - - HOWEVER: when the subject string is very, very long, searching to its end - can take a long time, and give bad performance on quite ordinary - patterns. This showed up when somebody was matching something like - /^\d+C/ on a 32-megabyte string... so we don't do this when the string is - sufficiently long. */ - - if (has_req_cu && end_subject - start_match < REQ_CU_MAX) - { - register PCRE2_SPTR p = start_match + (has_first_cu? 1:0); - - /* We don't need to repeat the search if we haven't yet reached the - place we found it at last time. */ - - if (p > req_cu_ptr) - { - if (req_cu != req_cu2) - { - while (p < end_subject) - { - register uint32_t pp = UCHAR21INCTEST(p); - if (pp == req_cu || pp == req_cu2) { p--; break; } - } - } - else - { - while (p < end_subject) - { - if (UCHAR21INCTEST(p) == req_cu) { p--; break; } - } - } - - /* If we can't find the required code unit, break the matching loop, - forcing a match failure. */ - - if (p >= end_subject) - { - rc = MATCH_NOMATCH; - break; - } - - /* If we have found the required code unit, save the point where we - found it, so that we don't search again next time round the loop if - the start hasn't passed this code unit yet. */ - - req_cu_ptr = p; - } - } - } - } - - /* ------------ 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. */ - - mb->start_match_ptr = start_match; - mb->start_used_ptr = start_match; - mb->last_used_ptr = start_match; - mb->match_call_count = 0; - mb->match_function_type = 0; - mb->end_offset_top = 0; - mb->skip_arg_count = 0; - rc = match(start_match, mb->start_code, start_match, 2, mb, NULL, 0); - - if (mb->hitend && start_partial == NULL) - { - start_partial = mb->start_used_ptr; - match_partial = start_match; - } - - switch(rc) - { - /* If MATCH_SKIP_ARG reaches this level it means that a MARK that matched - the SKIP's arg was not found. In this circumstance, Perl ignores the SKIP - entirely. The only way we can do that is to re-do the match at the same - point, with a flag to force SKIP with an argument to be ignored. Just - treating this case as NOMATCH does not work because it does not check other - alternatives in patterns such as A(*SKIP:A)B|AC when the subject is AC. */ - - case MATCH_SKIP_ARG: - new_start_match = start_match; - mb->ignore_skip_arg = mb->skip_arg_count; - break; - - /* SKIP passes back the next starting point explicitly, but if it is no - greater than the match we have just done, treat it as NOMATCH. */ - - case MATCH_SKIP: - if (mb->start_match_ptr > start_match) - { - new_start_match = mb->start_match_ptr; - break; - } - /* Fall through */ - - /* NOMATCH and PRUNE advance by one character. THEN at this level acts - exactly like PRUNE. Unset ignore SKIP-with-argument. */ - - case MATCH_NOMATCH: - case MATCH_PRUNE: - case MATCH_THEN: - mb->ignore_skip_arg = 0; - new_start_match = start_match + 1; -#ifdef SUPPORT_UNICODE - if (utf) - ACROSSCHAR(new_start_match < end_subject, *new_start_match, - new_start_match++); -#endif - break; - - /* COMMIT disables the bumpalong, but otherwise behaves as NOMATCH. */ - - case MATCH_COMMIT: - rc = MATCH_NOMATCH; - goto ENDLOOP; - - /* Any other return is either a match, or some kind of error. */ - - default: - goto ENDLOOP; - } - - /* Control reaches here for the various types of "no match at this point" - result. Reset the code to MATCH_NOMATCH for subsequent checking. */ - - rc = MATCH_NOMATCH; - - /* If PCRE2_FIRSTLINE is set, the match must happen before or at the first - newline in the subject (though it may continue over the newline). Therefore, - if we have just failed to match, starting at a newline, do not continue. */ - - if (firstline && IS_NEWLINE(start_match)) break; - - /* Advance to new matching position */ - - start_match = new_start_match; - - /* Break the loop if the pattern is anchored or if we have passed the end of - the subject. */ - - if (anchored || start_match > end_subject) break; - - /* If we have just passed a CR and we are now at a LF, and the pattern does - not contain any explicit matches for \r or \n, and the newline option is CRLF - or ANY or ANYCRLF, advance the match position by one more code unit. In - normal matching start_match will aways be greater than the first position at - this stage, but a failed *SKIP can cause a return at the same point, which is - why the first test exists. */ - - if (start_match > subject + start_offset && - start_match[-1] == CHAR_CR && - start_match < end_subject && - *start_match == CHAR_NL && - (re->flags & PCRE2_HASCRORLF) == 0 && - (mb->nltype == NLTYPE_ANY || - mb->nltype == NLTYPE_ANYCRLF || - mb->nllen == 2)) - start_match++; - - mb->mark = NULL; /* Reset for start of next match attempt */ - } /* End of for(;;) "bumpalong" loop */ - -/* ==========================================================================*/ - -/* When we reach here, one of the stopping conditions is true: - -(1) The match succeeded, either completely, or partially; - -(2) The pattern is anchored or the match was failed by (*COMMIT); - -(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 - the subject. - -(5) Some kind of error occurred. - -*/ - -ENDLOOP: - -#ifdef HEAP_MATCH_RECURSE -release_match_heapframes(&frame_zero, mb); -#endif - -/* Release any frames that were saved from recursions. */ - -while (mb->ovecsave_chain != NULL) - { - ovecsave_frame *this = mb->ovecsave_chain; - mb->ovecsave_chain = this->next; - mb->memctl.free(this, mb->memctl.memory_data); - } - -/* Fill in fields that are always returned in the match data. */ - -match_data->code = re; -match_data->subject = subject; -match_data->mark = mb->mark; -match_data->matchedby = PCRE2_MATCHEDBY_INTERPRETER; - -/* Handle a fully successful match. */ - -if (rc == MATCH_MATCH || rc == MATCH_ACCEPT) - { - uint32_t arg_offset_max = 2 * match_data->oveccount; - - /* When the offset vector is big enough to deal with any backreferences, - captured substring offsets will already be set up. In the case where we had - to get some local memory to hold offsets for backreference processing, copy - those that we can. In this case there need not be overflow if certain parts - of the pattern were not used, even though there are more capturing - parentheses than vector slots. */ - - if (using_temporary_offsets) - { - if (arg_offset_max >= 4) - { - memcpy(match_data->ovector + 2, mb->ovector + 2, - (arg_offset_max - 2) * sizeof(PCRE2_SIZE)); - } - if (mb->end_offset_top > arg_offset_max) mb->capture_last |= OVFLBIT; - mb->memctl.free(mb->ovector, mb->memctl.memory_data); - } - - /* Set the return code to the number of captured strings, or 0 if there were - too many to fit into the ovector. */ - - match_data->rc = ((mb->capture_last & OVFLBIT) != 0)? - 0 : mb->end_offset_top/2; - - /* If there is space in the offset vector, set any pairs that follow the - highest-numbered captured string but are less than the number of capturing - groups in the pattern (and are within the ovector) to PCRE2_UNSET. It is - documented that this happens. In earlier versions, the whole set of potential - capturing offsets was initialized each time round the loop, but this is - handled differently now. "Gaps" are set to PCRE2_UNSET dynamically instead - (this fixed a bug). Thus, it is only those at the end that need setting here. - We can't just mark them all unset at the start of the whole thing because - they may get set in one branch that is not the final matching branch. */ - - if (mb->end_offset_top/2 <= re->top_bracket) - { - register PCRE2_SIZE *iptr, *iend; - int resetcount = re->top_bracket + 1; - if (resetcount > match_data->oveccount) resetcount = match_data->oveccount; - iptr = match_data->ovector + mb->end_offset_top; - iend = match_data->ovector + 2 * resetcount; - while (iptr < iend) *iptr++ = PCRE2_UNSET; - } - - /* If there is space, set up the whole thing as substring 0. The value of - mb->start_match_ptr might be modified if \K was encountered on the success - matching path. */ - - if (match_data->oveccount < 1) rc = 0; else - { - match_data->ovector[0] = mb->start_match_ptr - mb->start_subject; - match_data->ovector[1] = mb->end_match_ptr - mb->start_subject; - } - - /* Set the remaining returned values */ - - match_data->startchar = start_match - subject; - match_data->leftchar = mb->start_used_ptr - subject; - match_data->rightchar = ((mb->last_used_ptr > mb->end_match_ptr)? - mb->last_used_ptr : mb->end_match_ptr) - subject; - return match_data->rc; - } - -/* Control gets here if there has been a partial match, an error, or if the -overall match attempt has failed at all permitted starting positions. Any mark -data is in the nomatch_mark field. */ - -match_data->mark = mb->nomatch_mark; - -/* For anything other than nomatch or partial match, just return the code. */ - -if (rc != MATCH_NOMATCH && rc != PCRE2_ERROR_PARTIAL) - match_data->rc = rc; - -/* Else handle a partial match. */ - -else if (match_partial != NULL) - { - if (match_data->oveccount > 0) - { - match_data->ovector[0] = match_partial - subject; - match_data->ovector[1] = end_subject - subject; - } - match_data->startchar = match_partial - subject; - match_data->leftchar = start_partial - subject; - match_data->rightchar = end_subject - subject; - match_data->rc = PCRE2_ERROR_PARTIAL; - } - -/* Else this is the classic nomatch case. */ - -else match_data->rc = PCRE2_ERROR_NOMATCH; - -/* Free any temporary offsets. */ - -if (using_temporary_offsets) - mb->memctl.free(mb->ovector, mb->memctl.memory_data); -return match_data->rc; -} - -/* End of pcre2_match.c */ diff --git a/pcre2-10.22/src/pcre2_ucd.c b/pcre2-10.22/src/pcre2_ucd.c deleted file mode 100644 index 116f537b3..000000000 --- a/pcre2-10.22/src/pcre2_ucd.c +++ /dev/null @@ -1,3747 +0,0 @@ -/* This module is generated by the maint/MultiStage2.py script. -Do not modify it by hand. Instead modify the script and run it -to regenerate this code. - -As well as being part of the PCRE2 library, this module is #included -by the pcre2test program, which redefines the PRIV macro to change -table names from _pcre2_xxx to xxxx, thereby avoiding name clashes -with the library. At present, just one of these tables is actually -needed. */ - -#ifndef PCRE2_PCRE2TEST - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "pcre2_internal.h" - -#endif /* PCRE2_PCRE2TEST */ - -/* Unicode character database. */ -/* This file was autogenerated by the MultiStage2.py script. */ -/* 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. -This module should not be referenced otherwise, so -it should not matter whether it is compiled or not. However -a comment was received about space saving - maybe the guy linked -all the modules rather than using a library - so we include a -condition to cut out the tables when not needed. But don't leave -a totally empty module because some compilers barf at that. -Instead, just supply small dummy tables. */ - -#ifndef SUPPORT_UNICODE -const ucd_record PRIV(ucd_records)[] = {{0,0,0,0,0 }}; -const uint8_t PRIV(ucd_stage1)[] = {0}; -const uint16_t PRIV(ucd_stage2)[] = {0}; -const uint32_t PRIV(ucd_caseless_sets)[] = {0}; -#else - -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 -field names will be different): - -typedef struct { -uint8_t property_0; -uint8_t property_1; -uint8_t property_2; -uint8_t property_3; -pcre_int32 property_4; -} ucd_record; -*/ - - -const uint32_t PRIV(ucd_caseless_sets)[] = { - NOTACHAR, - 0x0053, 0x0073, 0x017f, NOTACHAR, - 0x01c4, 0x01c5, 0x01c6, NOTACHAR, - 0x01c7, 0x01c8, 0x01c9, NOTACHAR, - 0x01ca, 0x01cb, 0x01cc, NOTACHAR, - 0x01f1, 0x01f2, 0x01f3, NOTACHAR, - 0x0345, 0x0399, 0x03b9, 0x1fbe, NOTACHAR, - 0x00b5, 0x039c, 0x03bc, NOTACHAR, - 0x03a3, 0x03c2, 0x03c3, NOTACHAR, - 0x0392, 0x03b2, 0x03d0, NOTACHAR, - 0x0398, 0x03b8, 0x03d1, 0x03f4, NOTACHAR, - 0x03a6, 0x03c6, 0x03d5, NOTACHAR, - 0x03a0, 0x03c0, 0x03d6, NOTACHAR, - 0x039a, 0x03ba, 0x03f0, NOTACHAR, - 0x03a1, 0x03c1, 0x03f1, NOTACHAR, - 0x0395, 0x03b5, 0x03f5, NOTACHAR, - 0x1e60, 0x1e61, 0x1e9b, NOTACHAR, - 0x03a9, 0x03c9, 0x2126, NOTACHAR, - 0x004b, 0x006b, 0x212a, NOTACHAR, - 0x00c5, 0x00e5, 0x212b, NOTACHAR, -}; - -/* When #included in pcre2test, we don't need this large table. */ - -#ifndef PCRE2_PCRE2TEST - -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 */ - { 9, 29, 12, 0, 0, }, /* 3 */ - { 9, 21, 12, 0, 0, }, /* 4 */ - { 9, 23, 12, 0, 0, }, /* 5 */ - { 9, 22, 12, 0, 0, }, /* 6 */ - { 9, 18, 12, 0, 0, }, /* 7 */ - { 9, 25, 12, 0, 0, }, /* 8 */ - { 9, 17, 12, 0, 0, }, /* 9 */ - { 9, 13, 12, 0, 0, }, /* 10 */ - { 33, 9, 12, 0, 32, }, /* 11 */ - { 33, 9, 12, 71, 32, }, /* 12 */ - { 33, 9, 12, 1, 32, }, /* 13 */ - { 9, 24, 12, 0, 0, }, /* 14 */ - { 9, 16, 12, 0, 0, }, /* 15 */ - { 33, 5, 12, 0, -32, }, /* 16 */ - { 33, 5, 12, 71, -32, }, /* 17 */ - { 33, 5, 12, 1, -32, }, /* 18 */ - { 9, 26, 12, 0, 0, }, /* 19 */ - { 33, 7, 12, 0, 0, }, /* 20 */ - { 9, 20, 12, 0, 0, }, /* 21 */ - { 9, 1, 2, 0, 0, }, /* 22 */ - { 9, 15, 12, 0, 0, }, /* 23 */ - { 9, 5, 12, 26, 775, }, /* 24 */ - { 9, 19, 12, 0, 0, }, /* 25 */ - { 33, 9, 12, 75, 32, }, /* 26 */ - { 33, 5, 12, 0, 7615, }, /* 27 */ - { 33, 5, 12, 75, -32, }, /* 28 */ - { 33, 5, 12, 0, 121, }, /* 29 */ - { 33, 9, 12, 0, 1, }, /* 30 */ - { 33, 5, 12, 0, -1, }, /* 31 */ - { 33, 9, 12, 0, 0, }, /* 32 */ - { 33, 5, 12, 0, 0, }, /* 33 */ - { 33, 9, 12, 0, -121, }, /* 34 */ - { 33, 5, 12, 1, -268, }, /* 35 */ - { 33, 5, 12, 0, 195, }, /* 36 */ - { 33, 9, 12, 0, 210, }, /* 37 */ - { 33, 9, 12, 0, 206, }, /* 38 */ - { 33, 9, 12, 0, 205, }, /* 39 */ - { 33, 9, 12, 0, 79, }, /* 40 */ - { 33, 9, 12, 0, 202, }, /* 41 */ - { 33, 9, 12, 0, 203, }, /* 42 */ - { 33, 9, 12, 0, 207, }, /* 43 */ - { 33, 5, 12, 0, 97, }, /* 44 */ - { 33, 9, 12, 0, 211, }, /* 45 */ - { 33, 9, 12, 0, 209, }, /* 46 */ - { 33, 5, 12, 0, 163, }, /* 47 */ - { 33, 9, 12, 0, 213, }, /* 48 */ - { 33, 5, 12, 0, 130, }, /* 49 */ - { 33, 9, 12, 0, 214, }, /* 50 */ - { 33, 9, 12, 0, 218, }, /* 51 */ - { 33, 9, 12, 0, 217, }, /* 52 */ - { 33, 9, 12, 0, 219, }, /* 53 */ - { 33, 5, 12, 0, 56, }, /* 54 */ - { 33, 9, 12, 5, 2, }, /* 55 */ - { 33, 8, 12, 5, 1, }, /* 56 */ - { 33, 5, 12, 5, -2, }, /* 57 */ - { 33, 9, 12, 9, 2, }, /* 58 */ - { 33, 8, 12, 9, 1, }, /* 59 */ - { 33, 5, 12, 9, -2, }, /* 60 */ - { 33, 9, 12, 13, 2, }, /* 61 */ - { 33, 8, 12, 13, 1, }, /* 62 */ - { 33, 5, 12, 13, -2, }, /* 63 */ - { 33, 5, 12, 0, -79, }, /* 64 */ - { 33, 9, 12, 17, 2, }, /* 65 */ - { 33, 8, 12, 17, 1, }, /* 66 */ - { 33, 5, 12, 17, -2, }, /* 67 */ - { 33, 9, 12, 0, -97, }, /* 68 */ - { 33, 9, 12, 0, -56, }, /* 69 */ - { 33, 9, 12, 0, -130, }, /* 70 */ - { 33, 9, 12, 0, 10795, }, /* 71 */ - { 33, 9, 12, 0, -163, }, /* 72 */ - { 33, 9, 12, 0, 10792, }, /* 73 */ - { 33, 5, 12, 0, 10815, }, /* 74 */ - { 33, 9, 12, 0, -195, }, /* 75 */ - { 33, 9, 12, 0, 69, }, /* 76 */ - { 33, 9, 12, 0, 71, }, /* 77 */ - { 33, 5, 12, 0, 10783, }, /* 78 */ - { 33, 5, 12, 0, 10780, }, /* 79 */ - { 33, 5, 12, 0, 10782, }, /* 80 */ - { 33, 5, 12, 0, -210, }, /* 81 */ - { 33, 5, 12, 0, -206, }, /* 82 */ - { 33, 5, 12, 0, -205, }, /* 83 */ - { 33, 5, 12, 0, -202, }, /* 84 */ - { 33, 5, 12, 0, -203, }, /* 85 */ - { 33, 5, 12, 0, 42319, }, /* 86 */ - { 33, 5, 12, 0, 42315, }, /* 87 */ - { 33, 5, 12, 0, -207, }, /* 88 */ - { 33, 5, 12, 0, 42280, }, /* 89 */ - { 33, 5, 12, 0, 42308, }, /* 90 */ - { 33, 5, 12, 0, -209, }, /* 91 */ - { 33, 5, 12, 0, -211, }, /* 92 */ - { 33, 5, 12, 0, 10743, }, /* 93 */ - { 33, 5, 12, 0, 42305, }, /* 94 */ - { 33, 5, 12, 0, 10749, }, /* 95 */ - { 33, 5, 12, 0, -213, }, /* 96 */ - { 33, 5, 12, 0, -214, }, /* 97 */ - { 33, 5, 12, 0, 10727, }, /* 98 */ - { 33, 5, 12, 0, -218, }, /* 99 */ - { 33, 5, 12, 0, 42282, }, /* 100 */ - { 33, 5, 12, 0, -69, }, /* 101 */ - { 33, 5, 12, 0, -217, }, /* 102 */ - { 33, 5, 12, 0, -71, }, /* 103 */ - { 33, 5, 12, 0, -219, }, /* 104 */ - { 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 */ - { 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 */ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* U+0000 */ - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* U+0800 */ - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 41, 41, 42, 43, 44, 45, /* U+1000 */ - 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* U+1800 */ - 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 71, 72, 73, 71, 74, 75, /* U+2000 */ - 76, 76, 66, 77, 66, 66, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, /* U+2800 */ - 88, 89, 90, 91, 92, 93, 94, 71, 95, 95, 95, 95, 95, 95, 95, 95, /* U+3000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+3800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+4000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 96, 95, 95, 95, 95, /* U+4800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+5000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+5800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+6000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+6800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+7000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+7800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+8000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+8800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+9000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 97, /* U+9800 */ - 98, 99, 99, 99, 99, 99, 99, 99, 99,100,101,101,102,103,104,105, /* U+A000 */ -106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,114, /* U+A800 */ -115,116,117,118,119,120,114,115,116,117,118,119,120,114,115,116, /* U+B000 */ -117,118,119,120,114,115,116,117,118,119,120,114,115,116,117,118, /* U+B800 */ -119,120,114,115,116,117,118,119,120,114,115,116,117,118,119,120, /* U+C000 */ -114,115,116,117,118,119,120,114,115,116,117,118,119,120,114,115, /* U+C800 */ -116,117,118,119,120,114,115,116,117,118,119,120,114,115,116,121, /* U+D000 */ -122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122, /* U+D800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+E000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+E800 */ -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,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 */ -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,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 */ -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 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+18800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+19000 */ -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 */ -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,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 */ -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 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+21800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+22000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+22800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+23000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+23800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+24000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+24800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+25000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+25800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+26000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+26800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+27000 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+27800 */ - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, /* U+28000 */ - 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,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,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,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 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+31800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+32000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+32800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+33000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+33800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+34000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+34800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+35000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+35800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+36000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+36800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+37000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+37800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+38000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+38800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+39000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+39800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3A000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3A800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3B000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3B800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3C000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3C800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3D000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3D800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3E000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3E800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3F000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+3F800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+40000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+40800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+41000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+41800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+42000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+42800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+43000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+43800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+44000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+44800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+45000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+45800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+46000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+46800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+47000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+47800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+48000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+48800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+49000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+49800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4A000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4A800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4B000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4B800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4C000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4C800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4D000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4D800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4E000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4E800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4F000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+4F800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+50000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+50800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+51000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+51800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+52000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+52800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+53000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+53800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+54000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+54800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+55000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+55800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+56000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+56800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+57000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+57800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+58000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+58800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+59000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+59800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5A000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5A800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5B000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5B800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5C000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5C800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5D000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5D800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5E000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5E800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5F000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+5F800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+60000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+60800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+61000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+61800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+62000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+62800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+63000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+63800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+64000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+64800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+65000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+65800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+66000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+66800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+67000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+67800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+68000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+68800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+69000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+69800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6A000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6A800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6B000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6B800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6C000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6C800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6D000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6D800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6E000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6E800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6F000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+6F800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+70000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+70800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+71000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+71800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+72000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+72800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+73000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+73800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+74000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+74800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+75000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+75800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+76000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+76800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+77000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+77800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+78000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+78800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+79000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+79800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7A000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7A800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7B000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7B800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7C000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7C800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7D000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7D800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7E000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7E800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7F000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+7F800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+80000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+80800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+81000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+81800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+82000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+82800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+83000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+83800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+84000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+84800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+85000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+85800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+86000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+86800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+87000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+87800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+88000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+88800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+89000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+89800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8A000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8A800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8B000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8B800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8C000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8C800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8D000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8D800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8E000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8E800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8F000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+8F800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+90000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+90800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+91000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+91800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+92000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+92800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+93000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+93800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+94000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+94800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+95000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+95800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+96000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+96800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+97000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+97800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+98000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+98800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+99000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+99800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9A000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9A800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9B000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9B800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9C000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9C800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9D000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9D800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9E000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9E800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9F000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+9F800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A0000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A0800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A1000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A1800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A2000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A2800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A3000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A3800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A4000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A4800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A5000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A5800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A6000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A6800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A7000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A7800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A8000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A8800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A9000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+A9800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AA000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AA800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AB000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AB800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AC000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AC800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AD000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AD800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AE000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AE800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AF000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+AF800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B0000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B0800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B1000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B1800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B2000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B2800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B3000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B3800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B4000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B4800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B5000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B5800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B6000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B6800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B7000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B7800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B8000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B8800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B9000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+B9800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BA000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BA800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BB000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BB800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BC000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BC800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BD000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BD800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BE000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BE800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BF000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+BF800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C0000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C0800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C1000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C1800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C2000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C2800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C3000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C3800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C4000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C4800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C5000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C5800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C6000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C6800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C7000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C7800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C8000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C8800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C9000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+C9800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CA000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CA800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CB000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CB800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CC000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CC800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CD000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CD800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CE000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CE800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CF000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+CF800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D0000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D0800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D1000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D1800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D2000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D2800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D3000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D3800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D4000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D4800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D5000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D5800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D6000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D6800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D7000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D7800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D8000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D8800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D9000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+D9800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DA000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DA800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DB000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DB800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DC000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DC800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DD000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DD800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+DE000 */ -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 */ -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 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E2800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E3000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E3800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E4000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E4800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E5000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E5800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E6000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E6800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E7000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E7800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E8000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E8800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E9000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+E9800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EA000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EA800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EB000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EB800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EC000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EC800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+ED000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+ED800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EE000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EE800 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EF000 */ -139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, /* U+EF800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F0000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F0800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F1000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F1800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F2000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F2800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F3000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F3800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F4000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F4800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F5000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F5800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F6000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F6800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F7000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F7800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F8000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F8800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F9000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+F9800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FA000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FA800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FB000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FB800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FC000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FC800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FD000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+FD800 */ -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,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 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+101800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+102000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+102800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+103000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+103800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+104000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+104800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+105000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+105800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+106000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+106800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+107000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+107800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+108000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+108800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+109000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+109800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10A000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10A800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10B000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10B800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10C000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10C800 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10D000 */ -123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, /* U+10D800 */ -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,235, /* U+10F800 */ -}; - -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, - 3, 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, 12, 11, 11, 11, 11, - 11, 11, 11, 13, 11, 11, 11, 11, 11, 11, 11, 6, 4, 7, 14, 15, - 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 16, 16, - 16, 16, 16, 18, 16, 16, 16, 16, 16, 16, 16, 6, 8, 7, 8, 0, - -/* block 1 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3, 4, 5, 5, 5, 5, 19, 4, 14, 19, 20, 21, 8, 22, 19, 14, - 19, 8, 23, 23, 14, 24, 4, 4, 14, 23, 20, 25, 23, 23, 23, 4, - 11, 11, 11, 11, 11, 26, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 8, 11, 11, 11, 11, 11, 11, 11, 27, - 16, 16, 16, 16, 16, 28, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 16, 16, 16, 16, 29, - -/* block 2 */ - 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, - 32, 33, 30, 31, 30, 31, 30, 31, 33, 30, 31, 30, 31, 30, 31, 30, - 31, 30, 31, 30, 31, 30, 31, 30, 31, 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, 34, 30, 31, 30, 31, 30, 31, 35, - -/* block 3 */ - 36, 37, 30, 31, 30, 31, 38, 30, 31, 39, 39, 30, 31, 33, 40, 41, - 42, 30, 31, 39, 43, 44, 45, 46, 30, 31, 47, 33, 45, 48, 49, 50, - 30, 31, 30, 31, 30, 31, 51, 30, 31, 51, 33, 33, 30, 31, 51, 30, - 31, 52, 52, 30, 31, 30, 31, 53, 30, 31, 33, 20, 30, 31, 33, 54, - 20, 20, 20, 20, 55, 56, 57, 58, 59, 60, 61, 62, 63, 30, 31, 30, - 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 64, 30, 31, - 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, - 33, 65, 66, 67, 30, 31, 68, 69, 30, 31, 30, 31, 30, 31, 30, 31, - -/* block 4 */ - 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, - 70, 33, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, - 30, 31, 30, 31, 33, 33, 33, 33, 33, 33, 71, 30, 31, 72, 73, 74, - 74, 30, 31, 75, 76, 77, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, - 78, 79, 80, 81, 82, 33, 83, 83, 33, 84, 33, 85, 86, 33, 33, 33, - 83, 87, 33, 88, 33, 89, 90, 33, 91, 92, 33, 93, 94, 33, 33, 92, - 33, 95, 96, 33, 33, 97, 33, 33, 33, 33, 33, 33, 33, 98, 33, 33, - -/* 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,105,106, 33, - 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, -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 */ -110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, -110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110, -110,110,110,110,110,110,110,110,110,110,110,110,110,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 */ -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 */ -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, -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 */ -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 */ -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 */ -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 */ -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 */ -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,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 */ -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, -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 */ -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 */ -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 */ -115,115,115,115,115,115,115,115,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, - -/* block 18 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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,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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,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 */ -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,327,327,327,327,327,327,327,327,327,327,327, -327,327,327,327,327,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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,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,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,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, - 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, 30, 31, - 30, 31, 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,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, - 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, - -/* block 60 */ -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 */ -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,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,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,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,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, 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,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, -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 */ -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, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 8, 8, - 19, 19, 8, 19, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - -/* block 66 */ - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - -/* block 67 */ - 19, 19, 19, 19, 19, 19, 19, 19, 6, 7, 6, 7, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 8, 8, 19, 19, 19, 19, 19, 19, 19, 6, 7, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 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, 19, 19, 19, - -/* block 68 */ - 19, 19, 19, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 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,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,115,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, - -/* block 70 */ - 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,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, -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 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 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 72 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 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, 19, 19, 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, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 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, 8, 8, - -/* block 73 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 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, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - -/* block 74 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 6, 7, 6, 7, 6, 7, 6, 7, - 6, 7, 6, 7, 6, 7, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - -/* block 75 */ - 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, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 8, 8, 8, 8, 8, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - -/* block 76 */ -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, - 7, 6, 7, 6, 7, 6, 7, 6, 7, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 6, 7, 6, 7, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 7, 8, 8, - -/* block 78 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 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,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,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,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 */ -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,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 */ -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 */ -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,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 */ -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,108, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, 9, 4, 4, 4, 4, - 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 */ -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 */ -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 */ -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,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,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 */ -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 */ -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 */ -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, -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,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 */ -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, -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, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 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, 23, 23, 23, 23, 23, 23, 23, - 19, 19, 19, 19, 19, 19, 19, 19, 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,115, - -/* block 94 */ -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, -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 */ -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,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 */ -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,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 */ -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,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 */ -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 */ -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 */ -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,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 */ -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 */ -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 */ -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,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, -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,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,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 */ -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 */ -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 */ -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,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 */ -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 */ -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 */ -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 */ -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,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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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,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 */ -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 */ -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 */ -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,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 */ -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,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,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 */ -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,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 */ -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,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 */ -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,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 */ -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 */ -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,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 */ -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200, -200,200,200,200,200,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 */ -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,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 */ -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 */ -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 */ -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,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,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 */ -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,110,115,115, - -/* block 139 */ -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -115,115,115,115,115,115,115,115,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 */ -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 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 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 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 */ -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 */ -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 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 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 */ -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,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,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 */ -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, - -/* block 199 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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 */ -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, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 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, 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,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,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, -115,115,115,115,115,115,115,115,115,115,115,115,115,115,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,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, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 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, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 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, 19, 19, 19, 19, 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, 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 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 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 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 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 */ - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 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 */ - 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 */ - 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 */ -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 */ - 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 */ -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, -485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,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, - -}; - -#if UCD_BLOCK_SIZE != 128 -#error Please correct UCD_BLOCK_SIZE in pcre2_internal.h -#endif -#endif /* SUPPORT_UNICODE */ - -#endif /* PCRE2_PCRE2TEST */ diff --git a/pcre2-10.22/src/sljit/sljitNativeMIPS_32.c b/pcre2-10.22/src/sljit/sljitNativeMIPS_32.c deleted file mode 100644 index 5096e4f55..000000000 --- a/pcre2-10.22/src/sljit/sljitNativeMIPS_32.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Stack-less Just-In-Time compiler - * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) 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 HOLDER(S) 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. - */ - -/* mips 32-bit arch dependent functions. */ - -static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_ar, sljit_sw imm) -{ - if (!(imm & ~0xffff)) - return push_inst(compiler, ORI | SA(0) | TA(dst_ar) | IMM(imm), dst_ar); - - if (imm < 0 && imm >= SIMM_MIN) - return push_inst(compiler, ADDIU | SA(0) | TA(dst_ar) | IMM(imm), dst_ar); - - FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(imm >> 16), dst_ar)); - return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS; -} - -#define EMIT_LOGICAL(op_imm, op_norm) \ - if (flags & SRC2_IMM) { \ - if (op & SLJIT_SET_E) \ - FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \ - if (CHECK_FLAGS(SLJIT_SET_E)) \ - FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \ - } \ - else { \ - if (op & SLJIT_SET_E) \ - FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ - if (CHECK_FLAGS(SLJIT_SET_E)) \ - FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | D(dst), DR(dst))); \ - } - -#define EMIT_SHIFT(op_imm, op_v) \ - if (flags & SRC2_IMM) { \ - if (op & SLJIT_SET_E) \ - FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); \ - if (CHECK_FLAGS(SLJIT_SET_E)) \ - FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | SH_IMM(src2), DR(dst))); \ - } \ - else { \ - if (op & SLJIT_SET_E) \ - FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ - if (CHECK_FLAGS(SLJIT_SET_E)) \ - FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | D(dst), DR(dst))); \ - } - -static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, - sljit_s32 dst, sljit_s32 src1, sljit_sw src2) -{ - switch (GET_OPCODE(op)) { - case SLJIT_MOV: - case SLJIT_MOV_U32: - case SLJIT_MOV_S32: - case SLJIT_MOV_P: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if (dst != src2) - return push_inst(compiler, ADDU | S(src2) | TA(0) | D(dst), DR(dst)); - return SLJIT_SUCCESS; - - case SLJIT_MOV_U8: - case SLJIT_MOV_S8: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { - if (op == SLJIT_MOV_S8) { -#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) - return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst)); -#else - FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(24), DR(dst))); - return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(24), DR(dst)); -#endif - } - return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst)); - } - else if (dst != src2) - SLJIT_ASSERT_STOP(); - return SLJIT_SUCCESS; - - case SLJIT_MOV_U16: - case SLJIT_MOV_S16: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { - if (op == SLJIT_MOV_S16) { -#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) - return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst)); -#else - FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(16), DR(dst))); - return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(16), DR(dst)); -#endif - } - return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst)); - } - else if (dst != src2) - SLJIT_ASSERT_STOP(); - return SLJIT_SUCCESS; - - case SLJIT_NOT: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if (op & SLJIT_SET_E) - FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (CHECK_FLAGS(SLJIT_SET_E)) - FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst))); - return SLJIT_SUCCESS; - - case SLJIT_CLZ: - SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); -#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) - if (op & SLJIT_SET_E) - FAIL_IF(push_inst(compiler, CLZ | S(src2) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (CHECK_FLAGS(SLJIT_SET_E)) - FAIL_IF(push_inst(compiler, CLZ | S(src2) | T(dst) | D(dst), DR(dst))); -#else - if (SLJIT_UNLIKELY(flags & UNUSED_DEST)) { - FAIL_IF(push_inst(compiler, SRL | T(src2) | DA(EQUAL_FLAG) | SH_IMM(31), EQUAL_FLAG)); - return push_inst(compiler, XORI | SA(EQUAL_FLAG) | TA(EQUAL_FLAG) | IMM(1), EQUAL_FLAG); - } - /* Nearly all instructions are unmovable in the following sequence. */ - FAIL_IF(push_inst(compiler, ADDU | S(src2) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); - /* Check zero. */ - FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG1) | TA(0) | IMM(5), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, ORI | SA(0) | T(dst) | IMM(32), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(dst) | IMM(-1), DR(dst))); - /* Loop for searching the highest bit. */ - FAIL_IF(push_inst(compiler, ADDIU | S(dst) | T(dst) | IMM(1), DR(dst))); - FAIL_IF(push_inst(compiler, BGEZ | S(TMP_REG1) | IMM(-2), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, SLL | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), UNMOVABLE_INS)); - if (op & SLJIT_SET_E) - return push_inst(compiler, ADDU | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG); -#endif - return SLJIT_SUCCESS; - - case SLJIT_ADD: - if (flags & SRC2_IMM) { - if (op & SLJIT_SET_O) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - else - FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - } - if (op & SLJIT_SET_E) - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); - if (op & (SLJIT_SET_C | SLJIT_SET_O)) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG)); - else { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG)); - FAIL_IF(push_inst(compiler, OR | S(src1) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG)); - } - } - /* dst may be the same as src1 or src2. */ - if (CHECK_FLAGS(SLJIT_SET_E)) - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(src2), DR(dst))); - } - else { - if (op & SLJIT_SET_O) - FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - if (op & SLJIT_SET_E) - FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (op & (SLJIT_SET_C | SLJIT_SET_O)) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG)); - /* dst may be the same as src1 or src2. */ - if (CHECK_FLAGS(SLJIT_SET_E)) - FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | D(dst), DR(dst))); - } - - /* a + b >= a | b (otherwise, the carry should be set to 1). */ - if (op & (SLJIT_SET_C | SLJIT_SET_O)) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG)); - if (!(op & SLJIT_SET_O)) - return SLJIT_SUCCESS; - FAIL_IF(push_inst(compiler, SLL | TA(ULESS_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - return push_inst(compiler, SLL | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG); - - case SLJIT_ADDC: - if (flags & SRC2_IMM) { - if (op & SLJIT_SET_C) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG)); - else { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG)); - FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - } - } - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(src2), DR(dst))); - } else { - if (op & SLJIT_SET_C) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - /* dst may be the same as src1 or src2. */ - FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | D(dst), DR(dst))); - } - if (op & SLJIT_SET_C) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - - FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst))); - if (!(op & SLJIT_SET_C)) - return SLJIT_SUCCESS; - - /* Set ULESS_FLAG (dst == 0) && (ULESS_FLAG == 1). */ - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG)); - /* Set carry flag. */ - return push_inst(compiler, OR | SA(ULESS_FLAG) | TA(OVERFLOW_FLAG) | DA(ULESS_FLAG), ULESS_FLAG); - - case SLJIT_SUB: - if ((flags & SRC2_IMM) && ((op & (SLJIT_SET_U | SLJIT_SET_S)) || src2 == SIMM_MIN)) { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); - src2 = TMP_REG2; - flags &= ~SRC2_IMM; - } - - if (flags & SRC2_IMM) { - if (op & SLJIT_SET_O) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - else - FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - } - if (op & SLJIT_SET_E) - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); - if (op & (SLJIT_SET_C | SLJIT_SET_O)) - FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG)); - /* dst may be the same as src1 or src2. */ - if (CHECK_FLAGS(SLJIT_SET_E)) - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst))); - } - else { - if (op & SLJIT_SET_O) - FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - if (op & SLJIT_SET_E) - FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (op & (SLJIT_SET_U | SLJIT_SET_C | SLJIT_SET_O)) - FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG)); - if (op & SLJIT_SET_U) - FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(UGREATER_FLAG), UGREATER_FLAG)); - if (op & SLJIT_SET_S) { - FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(LESS_FLAG), LESS_FLAG)); - FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(GREATER_FLAG), GREATER_FLAG)); - } - /* dst may be the same as src1 or src2. */ - if (CHECK_FLAGS(SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_C)) - FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst))); - } - - if (!(op & SLJIT_SET_O)) - return SLJIT_SUCCESS; - FAIL_IF(push_inst(compiler, SLL | TA(ULESS_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - return push_inst(compiler, SRL | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG); - - case SLJIT_SUBC: - if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { - FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); - src2 = TMP_REG2; - flags &= ~SRC2_IMM; - } - - if (flags & SRC2_IMM) { - if (op & SLJIT_SET_C) - FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG)); - /* dst may be the same as src1 or src2. */ - FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst))); - } - else { - if (op & SLJIT_SET_C) - FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - /* dst may be the same as src1 or src2. */ - FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst))); - } - - if (op & SLJIT_SET_C) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(LESS_FLAG), LESS_FLAG)); - - FAIL_IF(push_inst(compiler, SUBU | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst))); - return (op & SLJIT_SET_C) ? push_inst(compiler, OR | SA(OVERFLOW_FLAG) | TA(LESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG) : SLJIT_SUCCESS; - - case SLJIT_MUL: - SLJIT_ASSERT(!(flags & SRC2_IMM)); - if (!(op & SLJIT_SET_O)) { -#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) - return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst)); -#else - FAIL_IF(push_inst(compiler, MULT | S(src1) | T(src2), MOVABLE_INS)); - return push_inst(compiler, MFLO | D(dst), DR(dst)); -#endif - } - FAIL_IF(push_inst(compiler, MULT | S(src1) | T(src2), MOVABLE_INS)); - FAIL_IF(push_inst(compiler, MFHI | DA(ULESS_FLAG), ULESS_FLAG)); - FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst))); - FAIL_IF(push_inst(compiler, SRA | T(dst) | DA(UGREATER_FLAG) | SH_IMM(31), UGREATER_FLAG)); - return push_inst(compiler, SUBU | SA(ULESS_FLAG) | TA(UGREATER_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG); - - case SLJIT_AND: - EMIT_LOGICAL(ANDI, AND); - return SLJIT_SUCCESS; - - case SLJIT_OR: - EMIT_LOGICAL(ORI, OR); - return SLJIT_SUCCESS; - - case SLJIT_XOR: - EMIT_LOGICAL(XORI, XOR); - return SLJIT_SUCCESS; - - case SLJIT_SHL: - EMIT_SHIFT(SLL, SLLV); - return SLJIT_SUCCESS; - - case SLJIT_LSHR: - EMIT_SHIFT(SRL, SRLV); - return SLJIT_SUCCESS; - - case SLJIT_ASHR: - EMIT_SHIFT(SRA, SRAV); - return SLJIT_SUCCESS; - } - - SLJIT_ASSERT_STOP(); - return SLJIT_SUCCESS; -} - -static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value) -{ - FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 16), DR(dst))); - return push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value), DR(dst)); -} - -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) -{ - sljit_ins *inst = (sljit_ins*)addr; - - inst[0] = (inst[0] & 0xffff0000) | ((new_addr >> 16) & 0xffff); - inst[1] = (inst[1] & 0xffff0000) | (new_addr & 0xffff); - SLJIT_CACHE_FLUSH(inst, inst + 2); -} - -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant) -{ - sljit_ins *inst = (sljit_ins*)addr; - - inst[0] = (inst[0] & 0xffff0000) | ((new_constant >> 16) & 0xffff); - inst[1] = (inst[1] & 0xffff0000) | (new_constant & 0xffff); - SLJIT_CACHE_FLUSH(inst, inst + 2); -} diff --git a/pcre2-10.22/src/sljit/sljitNativeX86_32.c b/pcre2-10.22/src/sljit/sljitNativeX86_32.c deleted file mode 100644 index 78f3dcb06..000000000 --- a/pcre2-10.22/src/sljit/sljitNativeX86_32.c +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Stack-less Just-In-Time compiler - * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) 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 HOLDER(S) 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. - */ - -/* x86 32-bit arch dependent functions. */ - -static sljit_s32 emit_do_imm(struct sljit_compiler *compiler, sljit_u8 opcode, sljit_sw imm) -{ - sljit_u8 *inst; - - inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + sizeof(sljit_sw)); - FAIL_IF(!inst); - INC_SIZE(1 + sizeof(sljit_sw)); - *inst++ = opcode; - sljit_unaligned_store_sw(inst, imm); - return SLJIT_SUCCESS; -} - -static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_s32 type) -{ - if (type == SLJIT_JUMP) { - *code_ptr++ = JMP_i32; - jump->addr++; - } - else if (type >= SLJIT_FAST_CALL) { - *code_ptr++ = CALL_i32; - jump->addr++; - } - else { - *code_ptr++ = GROUP_0F; - *code_ptr++ = get_jump_code(type); - jump->addr += 2; - } - - if (jump->flags & JUMP_LABEL) - jump->flags |= PATCH_MW; - else - sljit_unaligned_store_sw(code_ptr, jump->u.target - (jump->addr + 4)); - code_ptr += 4; - - return code_ptr; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, - sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) -{ - sljit_s32 size; - sljit_u8 *inst; - - CHECK_ERROR(); - 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); - - compiler->args = args; - compiler->flags_saved = 0; - - size = 1 + (scratches > 7 ? (scratches - 7) : 0) + (saveds <= 3 ? saveds : 3); -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - size += (args > 0 ? (args * 2) : 0) + (args > 2 ? 2 : 0); -#else - size += (args > 0 ? (2 + args * 3) : 0); -#endif - inst = (sljit_u8*)ensure_buf(compiler, 1 + size); - FAIL_IF(!inst); - - INC_SIZE(size); - PUSH_REG(reg_map[TMP_REG1]); -#if !(defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (args > 0) { - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_map[TMP_REG1] << 3) | 0x4 /* esp */; - } -#endif - if (saveds > 2 || scratches > 7) - PUSH_REG(reg_map[SLJIT_S2]); - if (saveds > 1 || scratches > 8) - PUSH_REG(reg_map[SLJIT_S1]); - if (saveds > 0 || scratches > 9) - PUSH_REG(reg_map[SLJIT_S0]); - -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (args > 0) { - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_map[SLJIT_S0] << 3) | reg_map[SLJIT_R2]; - } - if (args > 1) { - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_map[SLJIT_S1] << 3) | reg_map[SLJIT_R1]; - } - if (args > 2) { - *inst++ = MOV_r_rm; - *inst++ = MOD_DISP8 | (reg_map[SLJIT_S2] << 3) | 0x4 /* esp */; - *inst++ = 0x24; - *inst++ = sizeof(sljit_sw) * (3 + 2); /* saveds >= 3 as well. */ - } -#else - if (args > 0) { - *inst++ = MOV_r_rm; - *inst++ = MOD_DISP8 | (reg_map[SLJIT_S0] << 3) | reg_map[TMP_REG1]; - *inst++ = sizeof(sljit_sw) * 2; - } - if (args > 1) { - *inst++ = MOV_r_rm; - *inst++ = MOD_DISP8 | (reg_map[SLJIT_S1] << 3) | reg_map[TMP_REG1]; - *inst++ = sizeof(sljit_sw) * 3; - } - if (args > 2) { - *inst++ = MOV_r_rm; - *inst++ = MOD_DISP8 | (reg_map[SLJIT_S2] << 3) | reg_map[TMP_REG1]; - *inst++ = sizeof(sljit_sw) * 4; - } -#endif - - SLJIT_COMPILE_ASSERT(SLJIT_LOCALS_OFFSET >= (2 + 4) * sizeof(sljit_uw), require_at_least_two_words); -#if defined(__APPLE__) - /* Ignore pushed registers and SLJIT_LOCALS_OFFSET when computing the aligned local size. */ - saveds = (2 + (scratches > 7 ? (scratches - 7) : 0) + (saveds <= 3 ? saveds : 3)) * sizeof(sljit_uw); - local_size = ((SLJIT_LOCALS_OFFSET + saveds + local_size + 15) & ~15) - saveds; -#else - if (options & SLJIT_DOUBLE_ALIGNMENT) { - local_size = SLJIT_LOCALS_OFFSET + ((local_size + 7) & ~7); - - inst = (sljit_u8*)ensure_buf(compiler, 1 + 17); - FAIL_IF(!inst); - - INC_SIZE(17); - inst[0] = MOV_r_rm; - inst[1] = MOD_REG | (reg_map[TMP_REG1] << 3) | reg_map[SLJIT_SP]; - inst[2] = GROUP_F7; - inst[3] = MOD_REG | (0 << 3) | reg_map[SLJIT_SP]; - sljit_unaligned_store_sw(inst + 4, 0x4); - inst[8] = JNE_i8; - inst[9] = 6; - inst[10] = GROUP_BINARY_81; - inst[11] = MOD_REG | (5 << 3) | reg_map[SLJIT_SP]; - sljit_unaligned_store_sw(inst + 12, 0x4); - inst[16] = PUSH_r + reg_map[TMP_REG1]; - } - else - local_size = SLJIT_LOCALS_OFFSET + ((local_size + 3) & ~3); -#endif - - compiler->local_size = local_size; -#ifdef _WIN32 - if (local_size > 1024) { -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - FAIL_IF(emit_do_imm(compiler, MOV_r_i32 + reg_map[SLJIT_R0], local_size)); -#else - local_size -= SLJIT_LOCALS_OFFSET; - FAIL_IF(emit_do_imm(compiler, MOV_r_i32 + reg_map[SLJIT_R0], local_size)); - FAIL_IF(emit_non_cum_binary(compiler, SUB_r_rm, SUB_rm_r, SUB, SUB_EAX_i32, - SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, SLJIT_LOCALS_OFFSET)); -#endif - FAIL_IF(sljit_emit_ijump(compiler, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_grow_stack))); - } -#endif - - SLJIT_ASSERT(local_size > 0); - return emit_non_cum_binary(compiler, SUB_r_rm, SUB_rm_r, SUB, SUB_EAX_i32, - SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size); -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, - sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 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); - - compiler->args = args; - -#if defined(__APPLE__) - saveds = (2 + (scratches > 7 ? (scratches - 7) : 0) + (saveds <= 3 ? saveds : 3)) * sizeof(sljit_uw); - compiler->local_size = ((SLJIT_LOCALS_OFFSET + saveds + local_size + 15) & ~15) - saveds; -#else - if (options & SLJIT_DOUBLE_ALIGNMENT) - compiler->local_size = SLJIT_LOCALS_OFFSET + ((local_size + 7) & ~7); - else - compiler->local_size = SLJIT_LOCALS_OFFSET + ((local_size + 3) & ~3); -#endif - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) -{ - sljit_s32 size; - sljit_u8 *inst; - - CHECK_ERROR(); - CHECK(check_sljit_emit_return(compiler, op, src, srcw)); - SLJIT_ASSERT(compiler->args >= 0); - - compiler->flags_saved = 0; - FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); - - SLJIT_ASSERT(compiler->local_size > 0); - FAIL_IF(emit_cum_binary(compiler, ADD_r_rm, ADD_rm_r, ADD, ADD_EAX_i32, - SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, compiler->local_size)); - -#if !defined(__APPLE__) - if (compiler->options & SLJIT_DOUBLE_ALIGNMENT) { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 3); - FAIL_IF(!inst); - - INC_SIZE(3); - inst[0] = MOV_r_rm; - inst[1] = (reg_map[SLJIT_SP] << 3) | 0x4 /* SIB */; - inst[2] = (4 << 3) | reg_map[SLJIT_SP]; - } -#endif - - size = 2 + (compiler->scratches > 7 ? (compiler->scratches - 7) : 0) + - (compiler->saveds <= 3 ? compiler->saveds : 3); -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (compiler->args > 2) - size += 2; -#else - if (compiler->args > 0) - size += 2; -#endif - inst = (sljit_u8*)ensure_buf(compiler, 1 + size); - FAIL_IF(!inst); - - INC_SIZE(size); - - if (compiler->saveds > 0 || compiler->scratches > 9) - POP_REG(reg_map[SLJIT_S0]); - if (compiler->saveds > 1 || compiler->scratches > 8) - POP_REG(reg_map[SLJIT_S1]); - if (compiler->saveds > 2 || compiler->scratches > 7) - POP_REG(reg_map[SLJIT_S2]); - POP_REG(reg_map[TMP_REG1]); -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (compiler->args > 2) - RET_I16(sizeof(sljit_sw)); - else - RET(); -#else - RET(); -#endif - - return SLJIT_SUCCESS; -} - -/* --------------------------------------------------------------------- */ -/* Operators */ -/* --------------------------------------------------------------------- */ - -/* Size contains the flags as well. */ -static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32 size, - /* The register or immediate operand. */ - sljit_s32 a, sljit_sw imma, - /* The general operand (not immediate). */ - sljit_s32 b, sljit_sw immb) -{ - sljit_u8 *inst; - sljit_u8 *buf_ptr; - sljit_s32 flags = size & ~0xf; - sljit_s32 inst_size; - - /* Both cannot be switched on. */ - SLJIT_ASSERT((flags & (EX86_BIN_INS | EX86_SHIFT_INS)) != (EX86_BIN_INS | EX86_SHIFT_INS)); - /* Size flags not allowed for typed instructions. */ - SLJIT_ASSERT(!(flags & (EX86_BIN_INS | EX86_SHIFT_INS)) || (flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) == 0); - /* Both size flags cannot be switched on. */ - SLJIT_ASSERT((flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) != (EX86_BYTE_ARG | EX86_HALF_ARG)); - /* SSE2 and immediate is not possible. */ - SLJIT_ASSERT(!(a & SLJIT_IMM) || !(flags & EX86_SSE2)); - SLJIT_ASSERT((flags & (EX86_PREF_F2 | EX86_PREF_F3)) != (EX86_PREF_F2 | EX86_PREF_F3) - && (flags & (EX86_PREF_F2 | EX86_PREF_66)) != (EX86_PREF_F2 | EX86_PREF_66) - && (flags & (EX86_PREF_F3 | EX86_PREF_66)) != (EX86_PREF_F3 | EX86_PREF_66)); - - size &= 0xf; - inst_size = size; - - if (flags & (EX86_PREF_F2 | EX86_PREF_F3)) - inst_size++; - if (flags & EX86_PREF_66) - inst_size++; - - /* Calculate size of b. */ - inst_size += 1; /* mod r/m byte. */ - if (b & SLJIT_MEM) { - if ((b & REG_MASK) == SLJIT_UNUSED) - inst_size += sizeof(sljit_sw); - else if (immb != 0 && !(b & OFFS_REG_MASK)) { - /* Immediate operand. */ - if (immb <= 127 && immb >= -128) - inst_size += sizeof(sljit_s8); - else - inst_size += sizeof(sljit_sw); - } - - if ((b & REG_MASK) == SLJIT_SP && !(b & OFFS_REG_MASK)) - b |= TO_OFFS_REG(SLJIT_SP); - - if ((b & OFFS_REG_MASK) != SLJIT_UNUSED) - inst_size += 1; /* SIB byte. */ - } - - /* Calculate size of a. */ - if (a & SLJIT_IMM) { - if (flags & EX86_BIN_INS) { - if (imma <= 127 && imma >= -128) { - inst_size += 1; - flags |= EX86_BYTE_ARG; - } else - inst_size += 4; - } - else if (flags & EX86_SHIFT_INS) { - imma &= 0x1f; - if (imma != 1) { - inst_size ++; - flags |= EX86_BYTE_ARG; - } - } else if (flags & EX86_BYTE_ARG) - inst_size++; - else if (flags & EX86_HALF_ARG) - inst_size += sizeof(short); - else - inst_size += sizeof(sljit_sw); - } - else - SLJIT_ASSERT(!(flags & EX86_SHIFT_INS) || a == SLJIT_PREF_SHIFT_REG); - - inst = (sljit_u8*)ensure_buf(compiler, 1 + inst_size); - PTR_FAIL_IF(!inst); - - /* Encoding the byte. */ - INC_SIZE(inst_size); - if (flags & EX86_PREF_F2) - *inst++ = 0xf2; - if (flags & EX86_PREF_F3) - *inst++ = 0xf3; - if (flags & EX86_PREF_66) - *inst++ = 0x66; - - buf_ptr = inst + size; - - /* Encode mod/rm byte. */ - if (!(flags & EX86_SHIFT_INS)) { - if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM)) - *inst = (flags & EX86_BYTE_ARG) ? GROUP_BINARY_83 : GROUP_BINARY_81; - - if ((a & SLJIT_IMM) || (a == 0)) - *buf_ptr = 0; - else if (!(flags & EX86_SSE2_OP1)) - *buf_ptr = reg_map[a] << 3; - else - *buf_ptr = a << 3; - } - else { - if (a & SLJIT_IMM) { - if (imma == 1) - *inst = GROUP_SHIFT_1; - else - *inst = GROUP_SHIFT_N; - } else - *inst = GROUP_SHIFT_CL; - *buf_ptr = 0; - } - - if (!(b & SLJIT_MEM)) - *buf_ptr++ |= MOD_REG + ((!(flags & EX86_SSE2_OP2)) ? reg_map[b] : b); - else if ((b & REG_MASK) != SLJIT_UNUSED) { - if ((b & OFFS_REG_MASK) == SLJIT_UNUSED || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP)) { - if (immb != 0) { - if (immb <= 127 && immb >= -128) - *buf_ptr |= 0x40; - else - *buf_ptr |= 0x80; - } - - if ((b & OFFS_REG_MASK) == SLJIT_UNUSED) - *buf_ptr++ |= reg_map[b & REG_MASK]; - else { - *buf_ptr++ |= 0x04; - *buf_ptr++ = reg_map[b & REG_MASK] | (reg_map[OFFS_REG(b)] << 3); - } - - if (immb != 0) { - if (immb <= 127 && immb >= -128) - *buf_ptr++ = immb; /* 8 bit displacement. */ - else { - sljit_unaligned_store_sw(buf_ptr, immb); /* 32 bit displacement. */ - buf_ptr += sizeof(sljit_sw); - } - } - } - else { - *buf_ptr++ |= 0x04; - *buf_ptr++ = reg_map[b & REG_MASK] | (reg_map[OFFS_REG(b)] << 3) | (immb << 6); - } - } - else { - *buf_ptr++ |= 0x05; - sljit_unaligned_store_sw(buf_ptr, immb); /* 32 bit displacement. */ - buf_ptr += sizeof(sljit_sw); - } - - if (a & SLJIT_IMM) { - if (flags & EX86_BYTE_ARG) - *buf_ptr = imma; - else if (flags & EX86_HALF_ARG) - sljit_unaligned_store_s16(buf_ptr, imma); - else if (!(flags & EX86_SHIFT_INS)) - sljit_unaligned_store_sw(buf_ptr, imma); - } - - return !(flags & EX86_SHIFT_INS) ? inst : (inst + 1); -} - -/* --------------------------------------------------------------------- */ -/* Call / return instructions */ -/* --------------------------------------------------------------------- */ - -static SLJIT_INLINE sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 type) -{ - sljit_u8 *inst; - -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - inst = (sljit_u8*)ensure_buf(compiler, type >= SLJIT_CALL3 ? 1 + 2 + 1 : 1 + 2); - FAIL_IF(!inst); - INC_SIZE(type >= SLJIT_CALL3 ? 2 + 1 : 2); - - if (type >= SLJIT_CALL3) - PUSH_REG(reg_map[SLJIT_R2]); - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_map[SLJIT_R2] << 3) | reg_map[SLJIT_R0]; -#else - inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 * (type - SLJIT_CALL0)); - FAIL_IF(!inst); - INC_SIZE(4 * (type - SLJIT_CALL0)); - - *inst++ = MOV_rm_r; - *inst++ = MOD_DISP8 | (reg_map[SLJIT_R0] << 3) | 0x4 /* SIB */; - *inst++ = (0x4 /* none*/ << 3) | reg_map[SLJIT_SP]; - *inst++ = 0; - if (type >= SLJIT_CALL2) { - *inst++ = MOV_rm_r; - *inst++ = MOD_DISP8 | (reg_map[SLJIT_R1] << 3) | 0x4 /* SIB */; - *inst++ = (0x4 /* none*/ << 3) | reg_map[SLJIT_SP]; - *inst++ = sizeof(sljit_sw); - } - if (type >= SLJIT_CALL3) { - *inst++ = MOV_rm_r; - *inst++ = MOD_DISP8 | (reg_map[SLJIT_R2] << 3) | 0x4 /* SIB */; - *inst++ = (0x4 /* none*/ << 3) | reg_map[SLJIT_SP]; - *inst++ = 2 * sizeof(sljit_sw); - } -#endif - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) -{ - sljit_u8 *inst; - - CHECK_ERROR(); - CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); - ADJUST_LOCAL_OFFSET(dst, dstw); - - CHECK_EXTRA_REGS(dst, dstw, (void)0); - - /* For UNUSED dst. Uncommon, but possible. */ - if (dst == SLJIT_UNUSED) - dst = TMP_REG1; - - if (FAST_IS_REG(dst)) { - /* Unused dest is possible here. */ - inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); - FAIL_IF(!inst); - - INC_SIZE(1); - POP_REG(reg_map[dst]); - return SLJIT_SUCCESS; - } - - /* Memory. */ - inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw); - FAIL_IF(!inst); - *inst++ = POP_rm; - return SLJIT_SUCCESS; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw) -{ - sljit_u8 *inst; - - CHECK_ERROR(); - CHECK(check_sljit_emit_fast_return(compiler, src, srcw)); - ADJUST_LOCAL_OFFSET(src, srcw); - - CHECK_EXTRA_REGS(src, srcw, (void)0); - - if (FAST_IS_REG(src)) { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 1); - FAIL_IF(!inst); - - INC_SIZE(1 + 1); - PUSH_REG(reg_map[src]); - } - else if (src & SLJIT_MEM) { - inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw); - FAIL_IF(!inst); - *inst++ = GROUP_FF; - *inst |= PUSH_rm; - - inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); - FAIL_IF(!inst); - INC_SIZE(1); - } - else { - /* SLJIT_IMM. */ - inst = (sljit_u8*)ensure_buf(compiler, 1 + 5 + 1); - FAIL_IF(!inst); - - INC_SIZE(5 + 1); - *inst++ = PUSH_i32; - sljit_unaligned_store_sw(inst, srcw); - inst += sizeof(sljit_sw); - } - - RET(); - return SLJIT_SUCCESS; -} diff --git a/pcre2-10.22/132html b/pcre2-10.32/132html similarity index 96% rename from pcre2-10.22/132html rename to pcre2-10.32/132html index 3a16a59e0..1bd62ba24 100755 --- a/pcre2-10.22/132html +++ b/pcre2-10.32/132html @@ -109,8 +109,9 @@ while () # Handling .sp is subtle. If it is inside a literal section, do nothing if # the next line is a non literal text line; similarly, if not inside a # literal section, do nothing if a literal follows, unless we are inside - # a .nf/.ne section. The point being that the
 and 
that delimit - # literal sections will do the spacing. Always skip if no previous output. + # a .nf/.fi section or about to enter one. The point being that the
+    # and 
that delimit literal sections will do the spacing. Always skip + # if no previous output. elsif (/^\.sp/) { @@ -123,7 +124,7 @@ while () } else { - print TEMP "
\n
\n" if ($innf || !/^[\s.]/); + print TEMP "
\n
\n" if ($innf || /^\.nf/ || !/^[\s.]/); } redo; # Now process the lookahead line we just read } diff --git a/pcre2-10.22/AUTHORS b/pcre2-10.32/AUTHORS similarity index 82% rename from pcre2-10.22/AUTHORS rename to pcre2-10.32/AUTHORS index d9a0e1569..d5592bbc5 100644 --- a/pcre2-10.22/AUTHORS +++ b/pcre2-10.32/AUTHORS @@ -8,7 +8,7 @@ Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. -Copyright (c) 1997-2016 University of Cambridge +Copyright (c) 1997-2018 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-2016 Zoltan Herczeg +Copyright(c) 2010-2018 Zoltan Herczeg All rights reserved. @@ -30,7 +30,7 @@ Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu -Copyright(c) 2009-2016 Zoltan Herczeg +Copyright(c) 2009-2018 Zoltan Herczeg All rights reserved. #### diff --git a/pcre2-10.22/CMakeLists.txt b/pcre2-10.32/CMakeLists.txt similarity index 90% rename from pcre2-10.22/CMakeLists.txt rename to pcre2-10.32/CMakeLists.txt index 1322d733d..1a2c95ba3 100644 --- a/pcre2-10.22/CMakeLists.txt +++ b/pcre2-10.32/CMakeLists.txt @@ -74,6 +74,14 @@ # 2016-03-01 PH applied Chris Wilson's patch for MSVC static # 2016-06-24 PH applied Chris Wilson's second patch, putting the first under # a new option instead of being unconditional. +# 2016-10-05 PH fixed a typo (PCRE should be PCRE2) in above patch +# fix by David Gaussmann +# 2016-10-07 PH added PCREGREP_MAX_BUFSIZE +# 2017-03-11 PH turned HEAP_MATCH_RECURSE into a NO-OP for 10.30 +# 2017-04-08 PH added HEAP_LIMIT +# 2017-06-15 ZH added SUPPORT_JIT_SEALLOC support +# 2018-06-19 PH added checks for stdint.h and inttypes.h +# 2018-06-27 PH added Daniel's patch to increase the stack for MSVC PROJECT(PCRE2 C) @@ -107,6 +115,18 @@ CHECK_INCLUDE_FILE(sys/types.h HAVE_SYS_TYPES_H) CHECK_INCLUDE_FILE(unistd.h HAVE_UNISTD_H) CHECK_INCLUDE_FILE(windows.h HAVE_WINDOWS_H) +IF(HAVE_INTTYPES_H) + SET(PCRE2_HAVE_INTTYPES_H 1) +ELSE(HAVE_INTTYPES_H) + SET(PCRE2_HAVE_INTTYPES_H 0) +ENDIF(HAVE_INTTYPES_H) + +IF(HAVE_STDINT_H) + SET(PCRE2_HAVE_STDINT_H 1) +ELSE(HAVE_STDINT_H) + SET(PCRE2_HAVE_STDINT_H 0) +ENDIF(HAVE_STDINT_H) + CHECK_FUNCTION_EXISTS(bcopy HAVE_BCOPY) CHECK_FUNCTION_EXISTS(memmove HAVE_MEMMOVE) CHECK_FUNCTION_EXISTS(strerror HAVE_STRERROR) @@ -139,24 +159,33 @@ SET(PCRE2_LINK_SIZE "2" CACHE STRING SET(PCRE2_PARENS_NEST_LIMIT "250" CACHE STRING "Default nested parentheses limit. See PARENS_NEST_LIMIT in config.h.in for details.") +SET(PCRE2_HEAP_LIMIT "20000000" CACHE STRING + "Default limit on heap memory (kibibytes). See HEAP_LIMIT in config.h.in for details.") + SET(PCRE2_MATCH_LIMIT "10000000" CACHE STRING "Default limit on internal looping. See MATCH_LIMIT in config.h.in for details.") -SET(PCRE2_MATCH_LIMIT_RECURSION "MATCH_LIMIT" CACHE STRING - "Default limit on internal recursion. See MATCH_LIMIT_RECURSION in config.h.in for details.") +SET(PCRE2_MATCH_LIMIT_DEPTH "MATCH_LIMIT" CACHE STRING + "Default limit on internal depth of search. See MATCH_LIMIT_DEPTH in config.h.in for details.") SET(PCRE2GREP_BUFSIZE "20480" CACHE STRING - "Buffer size parameter for pcre2grep. See PCRE2GREP_BUFSIZE in config.h.in for details.") + "Buffer starting size parameter for pcre2grep. See PCRE2GREP_BUFSIZE in config.h.in for details.") + +SET(PCRE2GREP_MAX_BUFSIZE "1048576" CACHE STRING + "Buffer maximum size parameter for pcre2grep. See PCRE2GREP_MAX_BUFSIZE in config.h.in for details.") SET(PCRE2_NEWLINE "LF" CACHE STRING - "What to recognize as a newline (one of CR, LF, CRLF, ANY, ANYCRLF).") + "What to recognize as a newline (one of CR, LF, CRLF, ANY, ANYCRLF, NUL).") SET(PCRE2_HEAP_MATCH_RECURSE OFF CACHE BOOL - "If ON, then don't use stack recursion when matching. See HEAP_MATCH_RECURSE in config.h.in for details.") + "Obsolete option: do not use") SET(PCRE2_SUPPORT_JIT OFF CACHE BOOL "Enable support for Just-in-time compiling.") +SET(PCRE2_SUPPORT_JIT_SEALLOC OFF CACHE BOOL + "Enable SELinux compatible execmem allocator in JIT.") + SET(PCRE2_SUPPORT_PCRE2GREP_JIT ON CACHE BOOL "Enable use of Just-in-time compiling in pcre2grep.") @@ -190,7 +219,7 @@ IF (MINGW) ENDIF(MINGW) IF(MSVC) - OPTION(PCRE_STATIC_RUNTIME + OPTION(PCRE2_STATIC_RUNTIME "ON=Compile against the static runtime (/MT)." OFF) OPTION(INSTALL_MSVC_PDB @@ -277,6 +306,10 @@ IF(PCRE2_SUPPORT_JIT) SET(SUPPORT_JIT 1) ENDIF(PCRE2_SUPPORT_JIT) +IF(PCRE2_SUPPORT_JIT_SEALLOC) + SET(SLJIT_PROT_EXECUTABLE_ALLOCATOR 1) +ENDIF(PCRE2_SUPPORT_JIT_SEALLOC) + IF(PCRE2_SUPPORT_PCRE2GREP_JIT) SET(SUPPORT_PCRE2GREP_JIT 1) ENDIF(PCRE2_SUPPORT_PCRE2GREP_JIT) @@ -333,6 +366,9 @@ ENDIF(PCRE2_NEWLINE STREQUAL "ANY") IF(PCRE2_NEWLINE STREQUAL "ANYCRLF") SET(NEWLINE_DEFAULT "5") ENDIF(PCRE2_NEWLINE STREQUAL "ANYCRLF") +IF(PCRE2_NEWLINE STREQUAL "NUL") + SET(NEWLINE_DEFAULT "6") +ENDIF(PCRE2_NEWLINE STREQUAL "NUL") IF(NEWLINE_DEFAULT STREQUAL "") MESSAGE(FATAL_ERROR "The PCRE2_NEWLINE variable must be set to one of the following values: \"LF\", \"CR\", \"CRLF\", \"ANY\", \"ANYCRLF\".") @@ -347,10 +383,6 @@ IF(PCRE2_EBCDIC_NL25) SET(EBCDIC_NL25 1) ENDIF(PCRE2_EBCDIC_NL25) -IF(PCRE2_HEAP_MATCH_RECURSE) - SET(HEAP_MATCH_RECURSE 1) -ENDIF(PCRE2_HEAP_MATCH_RECURSE) - # Output files CONFIGURE_FILE(config-cmake.h.in @@ -411,8 +443,10 @@ SET(PCRE2_SOURCES src/pcre2_compile.c src/pcre2_config.c src/pcre2_context.c + src/pcre2_convert.c src/pcre2_dfa_match.c src/pcre2_error.c + src/pcre2_extuni.c src/pcre2_find_bracket.c src/pcre2_jit_compile.c src/pcre2_maketables.c @@ -505,18 +539,18 @@ ADD_LIBRARY(pcre2-8 ${PCRE2_HEADERS} ${PCRE2_SOURCES} ${PROJECT_BINARY_DIR}/conf SET_PROPERTY(TARGET pcre2-8 PROPERTY COMPILE_DEFINITIONS PCRE2_CODE_UNIT_WIDTH=8) SET(targets ${targets} pcre2-8) -ADD_LIBRARY(pcre2posix ${PCRE2POSIX_HEADERS} ${PCRE2POSIX_SOURCES}) -SET_PROPERTY(TARGET pcre2posix +ADD_LIBRARY(pcre2-posix ${PCRE2POSIX_HEADERS} ${PCRE2POSIX_SOURCES}) +SET_PROPERTY(TARGET pcre2-posix PROPERTY COMPILE_DEFINITIONS PCRE2_CODE_UNIT_WIDTH=8) -SET(targets ${targets} pcre2posix) -TARGET_LINK_LIBRARIES(pcre2posix pcre2-8) +SET(targets ${targets} pcre2-posix) +TARGET_LINK_LIBRARIES(pcre2-posix pcre2-8) IF(MINGW AND NOT PCRE2_STATIC) IF(NON_STANDARD_LIB_PREFIX) - SET_TARGET_PROPERTIES(pcre2-8 pcre2posix PROPERTIES PREFIX "") + SET_TARGET_PROPERTIES(pcre2-8 pcre2-posix PROPERTIES PREFIX "") ENDIF(NON_STANDARD_LIB_PREFIX) IF(NON_STANDARD_LIB_SUFFIX) - SET_TARGET_PROPERTIES(pcre2-8 pcre2posix PROPERTIES SUFFIX "-0.dll") + SET_TARGET_PROPERTIES(pcre2-8 pcre2-posix PROPERTIES SUFFIX "-0.dll") ENDIF(NON_STANDARD_LIB_SUFFIX) ENDIF(MINGW AND NOT PCRE2_STATIC) ENDIF(PCRE2_BUILD_PCRE2_8) @@ -564,7 +598,7 @@ IF(PCRE2_BUILD_PCRE2GREP) SET_PROPERTY(TARGET pcre2grep PROPERTY COMPILE_DEFINITIONS PCRE2_CODE_UNIT_WIDTH=8) SET(targets ${targets} pcre2grep) - TARGET_LINK_LIBRARIES(pcre2grep pcre2posix ${PCRE2GREP_LIBS}) + TARGET_LINK_LIBRARIES(pcre2grep pcre2-posix ${PCRE2GREP_LIBS}) ENDIF(PCRE2_BUILD_PCRE2GREP) # Testing @@ -574,10 +608,17 @@ IF(PCRE2_BUILD_TESTS) SET(PCRE2TEST_SOURCES src/pcre2test.c) + IF(MSVC) + # This is needed to avoid a stack overflow error in the standard tests. The + # flag should be indicated with a forward-slash instead of a hyphen, but + # then CMake treats it as a file path. + SET(PCRE2TEST_LINKER_FLAGS -STACK:2500000) + ENDIF(MSVC) + ADD_EXECUTABLE(pcre2test ${PCRE2TEST_SOURCES}) SET(targets ${targets} pcre2test) IF(PCRE2_BUILD_PCRE2_8) - LIST(APPEND PCRE2TEST_LIBS pcre2posix pcre2-8) + LIST(APPEND PCRE2TEST_LIBS pcre2-posix pcre2-8) ENDIF(PCRE2_BUILD_PCRE2_8) IF(PCRE2_BUILD_PCRE2_16) LIST(APPEND PCRE2TEST_LIBS pcre2-16) @@ -585,7 +626,7 @@ IF(PCRE2_BUILD_TESTS) IF(PCRE2_BUILD_PCRE2_32) LIST(APPEND PCRE2TEST_LIBS pcre2-32) ENDIF(PCRE2_BUILD_PCRE2_32) - TARGET_LINK_LIBRARIES(pcre2test ${PCRE2TEST_LIBS}) + TARGET_LINK_LIBRARIES(pcre2test ${PCRE2TEST_LIBS} ${PCRE2TEST_LINKER_FLAGS}) IF(PCRE2_SUPPORT_JIT) ADD_EXECUTABLE(pcre2_jit_test src/pcre2_jit_test.c) @@ -732,6 +773,10 @@ ELSE(BUILD_SHARED_LIBS) SET(BUILD_STATIC_LIBS ON) ENDIF(BUILD_SHARED_LIBS) +IF(PCRE2_HEAP_MATCH_RECURSE) + MESSAGE(WARNING "HEAP_MATCH_RECURSE is obsolete and does nothing.") +ENDIF(PCRE2_HEAP_MATCH_RECURSE) + IF(PCRE2_SHOW_REPORT) STRING(TOUPPER "${CMAKE_BUILD_TYPE}" buildtype) IF (CMAKE_C_FLAGS) @@ -749,6 +794,7 @@ IF(PCRE2_SHOW_REPORT) MESSAGE(STATUS " Build 16 bit PCRE2 library ...... : ${PCRE2_BUILD_PCRE2_16}") MESSAGE(STATUS " Build 32 bit PCRE2 library ...... : ${PCRE2_BUILD_PCRE2_32}") MESSAGE(STATUS " Enable JIT compiling support .... : ${PCRE2_SUPPORT_JIT}") + MESSAGE(STATUS " Use SELinux allocator in JIT .... : ${PCRE2_SUPPORT_JIT_SEALLOC}") 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}") @@ -756,11 +802,11 @@ IF(PCRE2_SHOW_REPORT) MESSAGE(STATUS " EBCDIC coding ................... : ${PCRE2_EBCDIC}") MESSAGE(STATUS " EBCDIC coding with NL=0x25 ...... : ${PCRE2_EBCDIC_NL25}") MESSAGE(STATUS " Rebuild char tables ............. : ${PCRE2_REBUILD_CHARTABLES}") - MESSAGE(STATUS " Use heap recursion .............. : ${PCRE2_HEAP_MATCH_RECURSE}") MESSAGE(STATUS " Internal link size .............. : ${PCRE2_LINK_SIZE}") MESSAGE(STATUS " Parentheses nest limit .......... : ${PCRE2_PARENS_NEST_LIMIT}") + MESSAGE(STATUS " Heap limit ...................... : ${PCRE2_HEAP_LIMIT}") MESSAGE(STATUS " Match limit ..................... : ${PCRE2_MATCH_LIMIT}") - MESSAGE(STATUS " Match limit recursion ........... : ${PCRE2_MATCH_LIMIT_RECURSION}") + MESSAGE(STATUS " Match depth limit ............... : ${PCRE2_MATCH_LIMIT_DEPTH}") MESSAGE(STATUS " Build shared libs ............... : ${BUILD_SHARED_LIBS}") MESSAGE(STATUS " Build static libs ............... : ${BUILD_STATIC_LIBS}") MESSAGE(STATUS " Build pcre2grep ................. : ${PCRE2_BUILD_PCRE2GREP}") diff --git a/pcre2-10.22/COPYING b/pcre2-10.32/COPYING similarity index 100% rename from pcre2-10.22/COPYING rename to pcre2-10.32/COPYING diff --git a/pcre2-10.32/ChangeLog b/pcre2-10.32/ChangeLog new file mode 100644 index 000000000..06b69f8dc --- /dev/null +++ b/pcre2-10.32/ChangeLog @@ -0,0 +1,1867 @@ +Change Log for PCRE2 +-------------------- + + +Version 10.32-RC1 10-September-2018 +----------------------------------- + +1. When matching using the the REG_STARTEND feature of the POSIX API with a +non-zero starting offset, unset capturing groups with lower numbers than a +group that did capture something were not being correctly returned as "unset" +(that is, with offset values of -1). + +2. When matching using the POSIX API, pcre2test used to omit listing unset +groups altogether. Now it shows those that come before any actual captures as +"", as happens for non-POSIX matching. + +3. Running "pcre2test -C" always stated "\R matches CR, LF, or CRLF only", +whatever the build configuration was. It now correctly says "\R matches all +Unicode newlines" in the default case when --enable-bsr-anycrlf has not been +specified. Similarly, running "pcre2test -C bsr" never produced the result +ANY. + +4. Matching the pattern /(*UTF)\C[^\v]+\x80/ against an 8-bit string containing +multi-code-unit characters caused bad behaviour and possibly a crash. This +issue was fixed for other kinds of repeat in release 10.20 by change 19, but +repeating character classes were overlooked. + +5. pcre2grep now supports the inclusion of binary zeros in patterns that are +read from files via the -f option. + +6. A small fix to pcre2grep to avoid compiler warnings for -Wformat-overflow=2. + +7. Added --enable-jit=auto support to configure.ac. + +8. Added some dummy variables to the heapframe structure in 16-bit and 32-bit +modes for the benefit of m68k, where pointers can be 16-bit aligned. The +dummies force 32-bit alignment and this ensures that the structure is a +multiple of PCRE2_SIZE, a requirement that is tested at compile time. In other +architectures, alignment requirements take care of this automatically. + +9. When returning an error from pcre2_pattern_convert(), ensure the error +offset is set zero for early errors. + +10. A number of patches for Windows support from Daniel Richard G: + + (a) List of error numbers in Runtest.bat corrected (it was not the same as in + Runtest). + + (b) pcre2grep snprintf() workaround as used elsewhere in the tree. + + (c) Support for non-C99 snprintf() that returns -1 in the overflow case. + +11. Minor tidy of pcre2_dfa_match() code. + +12. Refactored pcre2_dfa_match() so that the internal recursive calls no longer +use the stack for local workspace and local ovectors. Instead, an initial block +of stack is reserved, but if this is insufficient, heap memory is used. The +heap limit parameter now applies to pcre2_dfa_match(). + +13. If a "find limits" test of DFA matching in pcre2test resulted in too many +matches for the ovector, no matches were displayed. + +14. Removed an occurrence of ctrl/Z from test 6 because Windows treats it as +EOF. The test looks to have come from a fuzzer. + +15. If PCRE2 was built with a default match limit a lot greater than the +default default of 10 000 000, some JIT tests of the match limit no longer +failed. All such tests now set 10 000 000 as the upper limit. + +16. Another Windows related patch for pcregrep to ensure that WIN32 is +undefined under Cygwin. + +17. Test for the presence of stdint.h and inttypes.h in configure and CMake and +include whichever exists (stdint preferred) instead of unconditionally +including stdint. This makes life easier for old and non-standard systems. + +18. Further changes to improve portability, especially to old and or non- +standard systems: + + (a) Put all printf arguments in RunGrepTest into single, not double, quotes, + and use \0 not \x00 for binary zero. + + (b) Avoid the use of C++ (i.e. BCPL) // comments. + + (c) Parameterize the use of %zu in pcre2test to make it like %td. For both of + these now, if using MSVC or a standard C before C99, %lu is used with a + cast if necessary. + +19. Applied a contributed patch to CMakeLists.txt to increase the stack size +when linking pcre2test with MSVC. This gets rid of a stack overflow error in +the standard set of tests. + +20. Output a warning in pcre2test when ignoring the "altglobal" modifier when +it is given with the "replace" modifier. + +21. In both pcre2test and pcre2_substitute(), with global matching, a pattern +that matched an empty string, but never at the starting match offset, was not +handled in a Perl-compatible way. The pattern /(a(*:1))(?>b)(*SKIP:1)x|.*/ matched against "abc", where the *SKIP +shouldn't find a MARK (because is in an atomic group), but it did. + +26. Upgraded the perltest.sh script: (1) #pattern lines can now be used to set +a list of modifiers for all subsequent patterns - only those that the script +recognizes are meaningful; (2) #subject lines can be used to set or unset a +default "mark" modifier; (3) Unsupported #command lines give a warning when +they are ignored; (4) Mark data is output only if the "mark" modifier is +present. + +27. (*ACCEPT:ARG), (*FAIL:ARG), and (*COMMIT:ARG) are now supported. + +28. A (*MARK) name was not being passed back for positive assertions that were +terminated by (*ACCEPT). + +29. Add support for \N{U+dddd}, but only in Unicode mode. + +30. Add support for (?^) for unsetting all imnsx options. + +31. The PCRE2_EXTENDED (/x) option only ever discarded space characters whose +code point was less than 256 and that were recognized by the lookup table +generated by pcre2_maketables(), which uses isspace() to identify white space. +Now, when Unicode support is compiled, PCRE2_EXTENDED also discards U+0085, +U+200E, U+200F, U+2028, and U+2029, which are additional characters defined by +Unicode as "Pattern White Space". This makes PCRE2 compatible with Perl. + +32. In certain circumstances, option settings within patterns were not being +correctly processed. For example, the pattern /((?i)A)(?m)B/ incorrectly +matched "ab". (The (?m) setting lost the fact that (?i) should be reset at the +end of its group during the parse process, but without another setting such as +(?m) the compile phase got it right.) This bug was introduced by the +refactoring in release 10.23. + +33. PCRE2 uses bcopy() if available when memmove() is not, and it used just to +define memmove() as function call to bcopy(). This hasn't been tested for a +long time because in pcre2test the result of memmove() was being used, whereas +bcopy() doesn't return a result. This feature is now refactored always to call +an emulation function when there is no memmove(). The emulation makes use of +bcopy() when available. + +34. When serializing a pattern, set the memctl, executable_jit, and tables +fields (that is, all the fields that contain pointers) to zeros so that the +result of serializing is always the same. These fields are re-set when the +pattern is deserialized. + +35. In a pattern such as /[^\x{100}-\x{ffff}]*[\x80-\xff]/ which has a repeated +negative class with no characters less than 0x100 followed by a positive class +with only characters less than 0x100, the first class was incorrectly being +auto-possessified, causing incorrect match failures. + +36. Removed the character type bit ctype_meta, which dates from PCRE1 and is +not used in PCRE2. + +37. Tidied up unnecessarily complicated macros used in the escapes table. + +38. Since 10.21, the new testoutput8-16-4 file has accidentally been omitted +from distribution tarballs, owing to a typo in Makefile.am which had +testoutput8-16-3 twice. Now fixed. + +39. If the only branch in a conditional subpattern was anchored, the whole +subpattern was treated as anchored, when it should not have been, since the +assumed empty second branch cannot be anchored. Demonstrated by test patterns +such as /(?(1)^())b/ or /(?(?=^))b/. + +40. A repeated conditional subpattern that could match an empty string was +always assumed to be unanchored. Now it it checked just like any other +repeated conditional subpattern, and can be found to be anchored if the minimum +quantifier is one or more. I can't see much use for a repeated anchored +pattern, but the behaviour is now consistent. + +41. Minor addition to pcre2_jit_compile.c to avoid static analyzer complaint +(for an event that could never occur but you had to have external information +to know that). + +42. If before the first match in a file that was being searched by pcre2grep +there was a line that was sufficiently long to cause the input buffer to be +expanded, the variable holding the location of the end of the previous match +was being adjusted incorrectly, and could cause an overflow warning from a code +sanitizer. However, as the value is used only to print pending "after" lines +when the next match is reached (and there are no such lines in this case) this +bug could do no damage. + + +Version 10.31 12-February-2018 +------------------------------ + +1. Fix typo (missing ]) in VMS code in pcre2test.c. + +2. Replace the replicated code for matching extended Unicode grapheme sequences +(which got a lot more complicated by change 10.30/49) by a single subroutine +that is called by both pcre2_match() and pcre2_dfa_match(). + +3. Add idempotent guard to pcre2_internal.h. + +4. Add new pcre2_config() options: PCRE2_CONFIG_NEVER_BACKSLASH_C and +PCRE2_CONFIG_COMPILED_WIDTHS. + +5. Cut out \C tests in the JIT regression tests when NEVER_BACKSLASH_C is +defined (e.g. by --enable-never-backslash-C). + +6. Defined public names for all the pcre2_compile() error numbers, and used +the public names in pcre2_convert.c. + +7. Fixed a small memory leak in pcre2test (convert contexts). + +8. Added two casts to compile.c and one to match.c to avoid compiler warnings. + +9. Added code to pcre2grep when compiled under VMS to set the symbol +PCRE2GREP_RC to the exit status, because VMS does not distinguish between +exit(0) and exit(1). + +10. Added the -LM (list modifiers) option to pcre2test. Also made -C complain +about a bad option only if the following argument item does not start with a +hyphen. + +11. pcre2grep was truncating components of file names to 128 characters when +processing files with the -r option, and also (some very odd code) truncating +path names to 512 characters. There is now a check on the absolute length of +full path file names, which may be up to 2047 characters long. + +12. When an assertion contained (*ACCEPT) it caused all open capturing groups +to be closed (as for a non-assertion ACCEPT), which was wrong and could lead to +misbehaviour for subsequent references to groups that started outside the +assertion. ACCEPT in an assertion now closes only those groups that were +started within that assertion. Fixes oss-fuzz issues 3852 and 3891. + +13. Multiline matching in pcre2grep was misbehaving if the pattern matched +within a line, and then matched again at the end of the line and over into +subsequent lines. Behaviour was different with and without colouring, and +sometimes context lines were incorrectly printed and/or line endings were lost. +All these issues should now be fixed. + +14. If --line-buffered was specified for pcre2grep when input was from a +compressed file (.gz or .bz2) a segfault occurred. (Line buffering should be +ignored for compressed files.) + +15. Although pcre2_jit_match checks whether the pattern is compiled +in a given mode, it was also expected that at least one mode is available. +This is fixed and pcre2_jit_match returns with PCRE2_ERROR_JIT_BADOPTION +when the pattern is not optimized by JIT at all. + +16. The line number and related variables such as match counts in pcre2grep +were all int variables, causing overflow when files with more than 2147483647 +lines were processed (assuming 32-bit ints). They have all been changed to +unsigned long ints. + +17. If a backreference with a minimum repeat count of zero was first in a +pattern, apart from assertions, an incorrect first matching character could be +recorded. For example, for the pattern /(?=(a))\1?b/, "b" was incorrectly set +as the first character of a match. + +18. Characters in a leading positive assertion are considered for recording a +first character of a match when the rest of the pattern does not provide one. +However, a character in a non-assertive group within a leading assertion such +as in the pattern /(?=(a))\1?b/ caused this process to fail. This was an +infelicity rather than an outright bug, because it did not affect the result of +a match, just its speed. (In fact, in this case, the starting 'a' was +subsequently picked up in the study.) + +19. A minor tidy in pcre2_match(): making all PCRE2_ERROR_ returns use "return" +instead of "RRETURN" saves unwinding the backtracks in these cases (only one +didn't). + +20. Allocate a single callout block on the stack at the start of pcre2_match() +and set its never-changing fields once only. Do the same for pcre2_dfa_match(). + +21. Save the extra compile options (set in the compile context) with the +compiled pattern (they were not previously saved), add PCRE2_INFO_EXTRAOPTIONS +to retrieve them, and update pcre2test to show them. + +22. Added PCRE2_CALLOUT_STARTMATCH and PCRE2_CALLOUT_BACKTRACK bits to a new +field callout_flags in callout blocks. The bits are set by pcre2_match(), but +not by JIT or pcre2_dfa_match(). Their settings are shown in pcre2test callouts +if the callout_extra subject modifier is set. These bits are provided to help +with tracking how a backtracking match is proceeding. + +23. Updated the pcre2demo.c demonstration program, which was missing the extra +code for -g that handles the case when \K in an assertion causes the match to +end at the original start point. Also arranged for it to detect when \K causes +the end of a match to be before its start. + +24. Similar to 23 above, strange things (including loops) could happen in +pcre2grep when \K was used in an assertion when --colour was used or in +multiline mode. The "end at original start point" bug is fixed, and if the end +point is found to be before the start point, they are swapped. + +25. When PCRE2_FIRSTLINE without PCRE2_NO_START_OPTIMIZE was used in non-JIT +matching (both pcre2_match() and pcre2_dfa_match()) and the matched string +started with the first code unit of a newline sequence, matching failed because +it was not tried at the newline. + +26. Code for giving up a non-partial match after failing to find a starting +code unit anywhere in the subject was missing when searching for one of a +number of code units (the bitmap case) in both pcre2_match() and +pcre2_dfa_match(). This was a missing optimization rather than a bug. + +27. Tidied up the ACROSSCHAR macro to be like FORWARDCHAR and BACKCHAR, using a +pointer argument rather than a code unit value. This should not have affected +the generated code. + +28. The JIT compiler has been updated. + +29. Avoid pointer overflow for unset captures in pcre2_substring_list_get(). +This could not actually cause a crash because it was always used in a memcpy() +call with zero length. + +30. Some internal structures have a variable-length ovector[] as their last +element. Their actual memory is obtained dynamically, giving an ovector of +appropriate length. However, they are defined in the structure as +ovector[NUMBER], where NUMBER is large so that array bound checkers don't +grumble. The value of NUMBER was 10000, but a fuzzer exceeded 5000 capturing +groups, making the ovector larger than this. The number has been increased to +131072, which allows for the maximum number of captures (65535) plus the +overall match. This fixes oss-fuzz issue 5415. + +31. Auto-possessification at the end of a capturing group was dependent on what +follows the group (e.g. /(a+)b/ would auto-possessify the a+) but this caused +incorrect behaviour when the group was called recursively from elsewhere in the +pattern where something different might follow. This bug is an unforseen +consequence of change #1 for 10.30 - the implementation of backtracking into +recursions. Iterators at the ends of capturing groups are no longer considered +for auto-possessification if the pattern contains any recursions. Fixes +Bugzilla #2232. + + +Version 10.30 14-August-2017 +---------------------------- + +1. The main interpreter, pcre2_match(), has been refactored into a new version +that does not use recursive function calls (and therefore the stack) for +remembering backtracking positions. This makes --disable-stack-for-recursion a +NOOP. The new implementation allows backtracking into recursive group calls in +patterns, making it more compatible with Perl, and also fixes some other +hard-to-do issues such as #1887 in Bugzilla. The code is also cleaner because +the old code had a number of fudges to try to reduce stack usage. It seems to +run no slower than the old code. + +A number of bugs in the refactored code were subsequently fixed during testing +before release, but after the code was made available in the repository. These +bugs were never in fully released code, but are noted here for the record. + + (a) If a pattern had fewer capturing parentheses than the ovector supplied in + the match data block, a memory error (detectable by ASAN) occurred after + a match, because the external block was being set from non-existent + internal ovector fields. Fixes oss-fuzz issue 781. + + (b) A pattern with very many capturing parentheses (when the internal frame + size was greater than the initial frame vector on the stack) caused a + crash. A vector on the heap is now set up at the start of matching if the + vector on the stack is not big enough to handle at least 10 frames. + Fixes oss-fuzz issue 783. + + (c) Handling of (*VERB)s in recursions was wrong in some cases. + + (d) Captures in negative assertions that were used as conditions were not + happening if the assertion matched via (*ACCEPT). + + (e) Mark values were not being passed out of recursions. + + (f) Refactor some code in do_callout() to avoid picky compiler warnings about + negative indices. Fixes oss-fuzz issue 1454. + + (g) Similarly refactor the way the variable length ovector is addressed for + similar reasons. Fixes oss-fuzz issue 1465. + +2. Now that pcre2_match() no longer uses recursive function calls (see above), +the "match limit recursion" value seems misnamed. It still exists, and limits +the depth of tree that is searched. To avoid future confusion, it has been +renamed as "depth limit" in all relevant places (--with-depth-limit, +(*LIMIT_DEPTH), pcre2_set_depth_limit(), etc) but the old names are still +available for backwards compatibility. + +3. Hardened pcre2test so as to reduce the number of bugs reported by fuzzers: + + (a) Check for malloc failures when getting memory for the ovector (POSIX) or + the match data block (non-POSIX). + +4. In the 32-bit library in non-UTF mode, an attempt to find a Unicode property +for a character with a code point greater than 0x10ffff (the Unicode maximum) +caused a crash. + +5. If a lookbehind assertion that contained a back reference to a group +appearing later in the pattern was compiled with the PCRE2_ANCHORED option, +undefined actions (often a segmentation fault) could occur, depending on what +other options were set. An example assertion is (?" should be ">=" in opcode check in pcre2_auto_possess.c. + (b) Added some casts to avoid "suspicious implicit sign extension". + (c) Resource leaks in pcre2test in rare error cases. + (d) Avoid warning for never-use case OP_TABLE_LENGTH which is just a fudge + for checking at compile time that tables are the right size. + (e) Add missing "fall through" comment. + +29. Implemented PCRE2_EXTENDED_MORE and related /xx and (?xx) features. + +30. Implement (?n: for PCRE2_NO_AUTO_CAPTURE, because Perl now has this. + +31. If more than one of "push", "pushcopy", or "pushtablescopy" were set in +pcre2test, a crash could occur. + +32. Make -bigstack in RunTest allocate a 64MiB stack (instead of 16MiB) so +that all the tests can run with clang's sanitizing options. + +33. Implement extra compile options in the compile context and add the first +one: PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES. + +34. Implement newline type PCRE2_NEWLINE_NUL. + +35. A lookbehind assertion that had a zero-length branch caused undefined +behaviour when processed by pcre2_dfa_match(). This is oss-fuzz issue 1859. + +36. The match limit value now also applies to pcre2_dfa_match() as there are +patterns that can use up a lot of resources without necessarily recursing very +deeply. (Compare item 10.23/36.) This should fix oss-fuzz #1761. + +37. Implement PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL. + +38. Fix returned offsets from regexec() when REG_STARTEND is used with a +starting offset greater than zero. + +39. Implement REG_PEND (GNU extension) for the POSIX wrapper. + +40. Implement the subject_literal modifier in pcre2test, and allow jitstack on +pattern lines. + +41. Implement PCRE2_LITERAL and use it to support REG_NOSPEC. + +42. Implement PCRE2_EXTRA_MATCH_LINE and PCRE2_EXTRA_MATCH_WORD for the benefit +of pcre2grep. + +43. Re-implement pcre2grep's -F, -w, and -x options using PCRE2_LITERAL, +PCRE2_EXTRA_MATCH_WORD, and PCRE2_EXTRA_MATCH_LINE. This fixes two bugs: + + (a) The -F option did not work for fixed strings containing \E. + (b) The -w option did not work for patterns with multiple branches. + +44. Added configuration options for the SELinux compatible execmem allocator in +JIT. + +45. Increased the limit for searching for a "must be present" code unit in +subjects from 1000 to 2000 for 8-bit searches, since they use memchr() and are +much faster. + +46. Arrange for anchored patterns to record and use "first code unit" data, +because this can give a fast "no match" without searching for a "required code +unit". Previously only non-anchored patterns did this. + +47. Upgraded the Unicode tables from Unicode 8.0.0 to Unicode 10.0.0. + +48. Add the callout_no_where modifier to pcre2test. + +49. Update extended grapheme breaking rules to the latest set that are in +Unicode Standard Annex #29. + +50. Added experimental foreign pattern conversion facilities +(pcre2_pattern_convert() and friends). + +51. Change the macro FWRITE, used in pcre2grep, to FWRITE_IGNORE because FWRITE +is defined in a system header in cygwin. Also modified some of the #ifdefs in +pcre2grep related to Windows and Cygwin support. + +52. Change 3(g) for 10.23 was a bit too zealous. If a hyphen that follows a +character class is the last character in the class, Perl does not give a +warning. PCRE2 now also treats this as a literal. + +53. Related to 52, though PCRE2 was throwing an error for [[:digit:]-X] it was +not doing so for [\d-X] (and similar escapes), as is documented. + +54. Fixed a MIPS issue in the JIT compiler reported by Joshua Kinard. + +55. Fixed a "maybe uninitialized" warning for class_uchardata in \p handling in +pcre2_compile() which could never actually trigger (code should have been cut +out when Unicode support is disabled). + + +Version 10.23 14-February-2017 +------------------------------ + +1. Extended pcre2test with the utf8_input modifier so that it is able to +generate all possible 16-bit and 32-bit code unit values in non-UTF modes. + +2. In any wide-character mode (8-bit UTF or any 16-bit or 32-bit mode), without +PCRE2_UCP set, a negative character type such as \D in a positive class should +cause all characters greater than 255 to match, whatever else is in the class. +There was a bug that caused this not to happen if a Unicode property item was +added to such a class, for example [\D\P{Nd}] or [\W\pL]. + +3. There has been a major re-factoring of the pcre2_compile.c file. Most syntax +checking is now done in the pre-pass that identifies capturing groups. This has +reduced the amount of duplication and made the code tidier. While doing this, +some minor bugs and Perl incompatibilities were fixed, including: + + (a) \Q\E in the middle of a quantifier such as A+\Q\E+ is now ignored instead + of giving an invalid quantifier error. + + (b) {0} can now be used after a group in a lookbehind assertion; previously + this caused an "assertion is not fixed length" error. + + (c) Perl always treats (?(DEFINE) as a "define" group, even if a group with + the name "DEFINE" exists. PCRE2 now does likewise. + + (d) A recursion condition test such as (?(R2)...) must now refer to an + existing subpattern. + + (e) A conditional recursion test such as (?(R)...) misbehaved if there was a + group whose name began with "R". + + (f) When testing zero-terminated patterns under valgrind, the terminating + zero is now marked "no access". This catches bugs that would otherwise + show up only with non-zero-terminated patterns. + + (g) A hyphen appearing immediately after a POSIX character class (for example + /[[:ascii:]-z]/) now generates an error. Perl does accept this as a + literal, but gives a warning, so it seems best to fail it in PCRE. + + (h) An empty \Q\E sequence may appear after a callout that precedes an + assertion condition (it is, of course, ignored). + +One effect of the refactoring is that some error numbers and messages have +changed, and the pattern offset given for compiling errors is not always the +right-most character that has been read. In particular, for a variable-length +lookbehind assertion it now points to the start of the assertion. Another +change is that when a callout appears before a group, the "length of next +pattern item" that is passed now just gives the length of the opening +parenthesis item, not the length of the whole group. A length of zero is now +given only for a callout at the end of the pattern. Automatic callouts are no +longer inserted before and after explicit callouts in the pattern. + +A number of bugs in the refactored code were subsequently fixed during testing +before release, but after the code was made available in the repository. Many +of the bugs were discovered by fuzzing testing. Several of them were related to +the change from assuming a zero-terminated pattern (which previously had +required non-zero terminated strings to be copied). These bugs were never in +fully released code, but are noted here for the record. + + (a) An overall recursion such as (?0) inside a lookbehind assertion was not + being diagnosed as an error. + + (b) In utf mode, the length of a *MARK (or other verb) name was being checked + in characters instead of code units, which could lead to bad code being + compiled, leading to unpredictable behaviour. + + (c) In extended /x mode, characters whose code was greater than 255 caused + a lookup outside one of the global tables. A similar bug existed for wide + characters in *VERB names. + + (d) The amount of memory needed for a compiled pattern was miscalculated if a + lookbehind contained more than one toplevel branch and the first branch + was of length zero. + + (e) In UTF-8 or UTF-16 modes with PCRE2_EXTENDED (/x) set and a non-zero- + terminated pattern, if a # comment ran on to the end of the pattern, one + or more code units past the end were being read. + + (f) An unterminated repeat at the end of a non-zero-terminated pattern (e.g. + "{2,2") could cause reading beyond the pattern. + + (g) When reading a callout string, if the end delimiter was at the end of the + pattern one further code unit was read. + + (h) An unterminated number after \g' could cause reading beyond the pattern. + + (i) An insufficient memory size was being computed for compiling with + PCRE2_AUTO_CALLOUT. + + (j) A conditional group with an assertion condition used more memory than was + allowed for it during parsing, so too many of them could therefore + overrun a buffer. + + (k) If parsing a pattern exactly filled the buffer, the internal test for + overrun did not check when the final META_END item was added. + + (l) If a lookbehind contained a subroutine call, and the called group + contained an option setting such as (?s), and the PCRE2_ANCHORED option + was set, unpredictable behaviour could occur. The underlying bug was + incorrect code and insufficient checking while searching for the end of + the called subroutine in the parsed pattern. + + (m) Quantifiers following (*VERB)s were not being diagnosed as errors. + + (n) The use of \Q...\E in a (*VERB) name when PCRE2_ALT_VERBNAMES and + PCRE2_AUTO_CALLOUT were both specified caused undetermined behaviour. + + (o) If \Q was preceded by a quantified item, and the following \E was + followed by '?' or '+', and there was at least one literal character + between them, an internal error "unexpected repeat" occurred (example: + /.+\QX\E+/). + + (p) A buffer overflow could occur while sorting the names in the group name + list (depending on the order in which the names were seen). + + (q) A conditional group that started with a callout was not doing the right + check for a following assertion, leading to compiling bad code. Example: + /(?(C'XX))?!XX/ + + (r) If a character whose code point was greater than 0xffff appeared within + a lookbehind that was within another lookbehind, the calculation of the + lookbehind length went wrong and could provoke an internal error. + + (t) The sequence \E- or \Q\E- after a POSIX class in a character class caused + an internal error. Now the hyphen is treated as a literal. + +4. Back references are now permitted in lookbehind assertions when there are +no duplicated group numbers (that is, (?| has not been used), and, if the +reference is by name, there is only one group of that name. The referenced +group must, of course be of fixed length. + +5. pcre2test has been upgraded so that, when run under valgrind with valgrind +support enabled, reading past the end of the pattern is detected, both when +compiling and during callout processing. + +6. \g{+} (e.g. \g{+2} ) is now supported. It is a "forward back +reference" and can be useful in repetitions (compare \g{-} ). Perl does +not recognize this syntax. + +7. Automatic callouts are no longer generated before and after callouts in the +pattern. + +8. When pcre2test was outputing information from a callout, the caret indicator +for the current position in the subject line was incorrect if it was after an +escape sequence for a character whose code point was greater than \x{ff}. + +9. Change 19 for 10.22 had a typo (PCRE_STATIC_RUNTIME should be +PCRE2_STATIC_RUNTIME). Fix from David Gaussmann. + +10. Added --max-buffer-size to pcre2grep, to allow for automatic buffer +expansion when long lines are encountered. Original patch by Dmitry +Cherniachenko. + +11. If pcre2grep was compiled with JIT support, but the library was compiled +without it (something that neither ./configure nor CMake allow, but it can be +done by editing config.h), pcre2grep was giving a JIT error. Now it detects +this situation and does not try to use JIT. + +12. Added some "const" qualifiers to variables in pcre2grep. + +13. Added Dmitry Cherniachenko's patch for colouring output in Windows +(untested by me). Also, look for GREP_COLOUR or GREP_COLOR if the environment +variables PCRE2GREP_COLOUR and PCRE2GREP_COLOR are not found. + +14. Add the -t (grand total) option to pcre2grep. + +15. A number of bugs have been mended relating to match start-up optimizations +when the first thing in a pattern is a positive lookahead. These all applied +only when PCRE2_NO_START_OPTIMIZE was *not* set: + + (a) A pattern such as (?=.*X)X$ was incorrectly optimized as if it needed + both an initial 'X' and a following 'X'. + (b) Some patterns starting with an assertion that started with .* were + incorrectly optimized as having to match at the start of the subject or + after a newline. There are cases where this is not true, for example, + (?=.*[A-Z])(?=.{8,16})(?!.*[\s]) matches after the start in lines that + start with spaces. Starting .* in an assertion is no longer taken as an + indication of matching at the start (or after a newline). + +16. The "offset" modifier in pcre2test was not being ignored (as documented) +when the POSIX API was in use. + +17. Added --enable-fuzz-support to "configure", causing an non-installed +library containing a test function that can be called by fuzzers to be +compiled. A non-installed binary to run the test function locally, called +pcre2fuzzcheck is also compiled. + +18. A pattern with PCRE2_DOTALL (/s) set but not PCRE2_NO_DOTSTAR_ANCHOR, and +which started with .* inside a positive lookahead was incorrectly being +compiled as implicitly anchored. + +19. Removed all instances of "register" declarations, as they are considered +obsolete these days and in any case had become very haphazard. + +20. Add strerror() to pcre2test for failed file opening. + +21. Make pcre2test -C list valgrind support when it is enabled. + +22. Add the use_length modifier to pcre2test. + +23. Fix an off-by-one bug in pcre2test for the list of names for 'get' and +'copy' modifiers. + +24. Add PCRE2_CALL_CONVENTION into the prototype declarations in pcre2.h as it +is apparently needed there as well as in the function definitions. (Why did +nobody ask for this in PCRE1?) + +25. Change the _PCRE2_H and _PCRE2_UCP_H guard macros in the header files to +PCRE2_H_IDEMPOTENT_GUARD and PCRE2_UCP_H_IDEMPOTENT_GUARD to be more standard +compliant and unique. + +26. pcre2-config --libs-posix was listing -lpcre2posix instead of +-lpcre2-posix. Also, the CMake build process was building the library with the +wrong name. + +27. In pcre2test, give some offset information for errors in hex patterns. +This uses the C99 formatting sequence %td, except for MSVC which doesn't +support it - %lu is used instead. + +28. Implemented pcre2_code_copy_with_tables(), and added pushtablescopy to +pcre2test for testing it. + +29. Fix small memory leak in pcre2test. + +30. Fix out-of-bounds read for partial matching of /./ against an empty string +when the newline type is CRLF. + +31. Fix a bug in pcre2test that caused a crash when a locale was set either in +the current pattern or a previous one and a wide character was matched. + +32. The appearance of \p, \P, or \X in a substitution string when +PCRE2_SUBSTITUTE_EXTENDED was set caused a segmentation fault (NULL +dereference). + +33. If the starting offset was specified as greater than the subject length in +a call to pcre2_substitute() an out-of-bounds memory reference could occur. + +34. When PCRE2 was compiled to use the heap instead of the stack for recursive +calls to match(), a repeated minimizing caseless back reference, or a +maximizing one where the two cases had different numbers of code units, +followed by a caseful back reference, could lose the caselessness of the first +repeated back reference (example: /(Z)(a)\2{1,2}?(?-i)\1X/i should match ZaAAZX +but didn't). + +35. When a pattern is too complicated, PCRE2 gives up trying to find a minimum +matching length and just records zero. Typically this happens when there are +too many nested or recursive back references. If the limit was reached in +certain recursive cases it failed to be triggered and an internal error could +be the result. + +36. The pcre2_dfa_match() function now takes note of the recursion limit for +the internal recursive calls that are used for lookrounds and recursions within +the pattern. + +37. More refactoring has got rid of the internal could_be_empty_branch() +function (around 400 lines of code, including comments) by keeping track of +could-be-emptiness as the pattern is compiled instead of scanning compiled +groups. (This would have been much harder before the refactoring of #3 above.) +This lifts a restriction on the number of branches in a group (more than about +1100 would give "pattern is too complicated"). + +38. Add the "-ac" command line option to pcre2test as a synonym for "-pattern +auto_callout". + +39. In a library with Unicode support, incorrect data was compiled for a +pattern with PCRE2_UCP set without PCRE2_UTF if a class required all wide +characters to match (for example, /[\s[:^ascii:]]/). + +40. The callout_error modifier has been added to pcre2test to make it possible +to return PCRE2_ERROR_CALLOUT from a callout. + +41. A minor change to pcre2grep: colour reset is now "[0m" instead of +"[00m". + +42. The limit in the auto-possessification code that was intended to catch +overly-complicated patterns and not spend too much time auto-possessifying was +being reset too often, resulting in very long compile times for some patterns. +Now such patterns are no longer completely auto-possessified. + +43. Applied Jason Hood's revised patch for RunTest.bat. + +44. Added a new Windows script RunGrepTest.bat, courtesy of Jason Hood. + +45. Minor cosmetic fix to pcre2test: move a variable that is not used under +Windows into the "not Windows" code. + +46. Applied Jason Hood's patches to upgrade pcre2grep under Windows and tidy +some of the code: + + * normalised the Windows condition by ensuring WIN32 is defined; + * enables the callout feature under Windows; + * adds globbing (Microsoft's implementation expands quoted args), + using a tweaked opendirectory; + * implements the is_*_tty functions for Windows; + * --color=always will write the ANSI sequences to file; + * add sequences 4 (underline works on Win10) and 5 (blink as bright + background, relatively standard on DOS/Win); + * remove the (char *) casts for the now-const strings; + * remove GREP_COLOUR (grep's command line allowed the 'u', but not + the environment), parsing GREP_COLORS instead; + * uses the current colour if not set, rather than black; + * add print_match for the undefined case; + * fixes a typo. + +In addition, colour settings containing anything other than digits and +semicolon are ignored, and the colour controls are no longer output for empty +strings. + +47. Detecting patterns that are too large inside the length-measuring loop +saves processing ridiculously long patterns to their end. + +48. Ignore PCRE2_CASELESS when processing \h, \H, \v, and \V in classes as it +just wastes time. In the UTF case it can also produce redundant entries in +XCLASS lists caused by characters with multiple other cases and pairs of +characters in the same "not-x" sublists. + +49. A pattern such as /(?=(a\K))/ can report the end of the match being before +its start; pcre2test was not handling this correctly when using the POSIX +interface (it was OK with the native interface). + +50. In pcre2grep, ignore all JIT compile errors. This means that pcre2grep will +continue to work, falling back to interpretation if anything goes wrong with +JIT. + +51. Applied patches from Christian Persch to configure.ac to make use of the +AC_USE_SYSTEM_EXTENSIONS macro and to test for functions used by the JIT +modules. + +52. Minor fixes to pcre2grep from Jason Hood: + * fixed some spacing; + * Windows doesn't usually use single quotes, so I've added a define + to use appropriate quotes [in an example]; + * LC_ALL was displayed as "LCC_ALL"; + * numbers 11, 12 & 13 should end in "th"; + * use double quotes in usage message. + +53. When autopossessifying, skip empty branches without recursion, to reduce +stack usage for the benefit of clang with -fsanitize-address, which uses huge +stack frames. Example pattern: /X?(R||){3335}/. Fixes oss-fuzz issue 553. + +54. A pattern with very many explicit back references to a group that is a long +way from the start of the pattern could take a long time to compile because +searching for the referenced group in order to find the minimum length was +being done repeatedly. Now up to 128 group minimum lengths are cached and the +attempt to find a minimum length is abandoned if there is a back reference to a +group whose number is greater than 128. (In that case, the pattern is so +complicated that this optimization probably isn't worth it.) This fixes +oss-fuzz issue 557. + +55. Issue 32 for 10.22 below was not correctly fixed. If pcre2grep in multiline +mode with --only-matching matched several lines, it restarted scanning at the +next line instead of moving on to the end of the matched string, which can be +several lines after the start. + +56. Applied Jason Hood's new patch for RunGrepTest.bat that updates it in line +with updates to the non-Windows version. + + + +Version 10.22 29-July-2016 +-------------------------- + +1. Applied Jason Hood's patches to RunTest.bat and testdata/wintestoutput3 +to fix problems with running the tests under Windows. + +2. Implemented a facility for quoting literal characters within hexadecimal +patterns in pcre2test, to make it easier to create patterns with just a few +non-printing characters. + +3. Binary zeros are not supported in pcre2test input files. It now detects them +and gives an error. + +4. Updated the valgrind parameters in RunTest: (a) changed smc-check=all to +smc-check=all-non-file; (b) changed obj:* in the suppression file to obj:??? so +that it matches only unknown objects. + +5. Updated the maintenance script maint/ManyConfigTests to make it easier to +select individual groups of tests. + +6. When the POSIX wrapper function regcomp() is called, the REG_NOSUB option +used to set PCRE2_NO_AUTO_CAPTURE when calling pcre2_compile(). However, this +disables the use of back references (and subroutine calls), which are supported +by other implementations of regcomp() with RE_NOSUB. Therefore, REG_NOSUB no +longer causes PCRE2_NO_AUTO_CAPTURE to be set, though it still ignores nmatch +and pmatch when regexec() is called. + +7. Because of 6 above, pcre2test has been modified with a new modifier called +posix_nosub, to call regcomp() with REG_NOSUB. Previously the no_auto_capture +modifier had this effect. That option is now ignored when the POSIX API is in +use. + +8. Minor tidies to the pcre2demo.c sample program, including more comments +about its 8-bit-ness. + +9. Detect unmatched closing parentheses and give the error in the pre-scan +instead of later. Previously the pre-scan carried on and could give a +misleading incorrect error message. For example, /(?J)(?'a'))(?'a')/ gave a +message about invalid duplicate group names. + +10. It has happened that pcre2test was accidentally linked with another POSIX +regex library instead of libpcre2-posix. In this situation, a call to regcomp() +(in the other library) may succeed, returning zero, but of course putting its +own data into the regex_t block. In one example the re_pcre2_code field was +left as NULL, which made pcre2test think it had not got a compiled POSIX regex, +so it treated the next line as another pattern line, resulting in a confusing +error message. A check has been added to pcre2test to see if the data returned +from a successful call of regcomp() are valid for PCRE2's regcomp(). If they +are not, an error message is output and the pcre2test run is abandoned. The +message points out the possibility of a mis-linking. Hopefully this will avoid +some head-scratching the next time this happens. + +11. A pattern such as /(?<=((?C)0))/, which has a callout inside a lookbehind +assertion, caused pcre2test to output a very large number of spaces when the +callout was taken, making the program appearing to loop. + +12. A pattern that included (*ACCEPT) in the middle of a sufficiently deeply +nested set of parentheses of sufficient size caused an overflow of the +compiling workspace (which was diagnosed, but of course is not desirable). + +13. Detect missing closing parentheses during the pre-pass for group +identification. + +14. Changed some integer variable types and put in a number of casts, following +a report of compiler warnings from Visual Studio 2013 and a few tests with +gcc's -Wconversion (which still throws up a lot). + +15. Implemented pcre2_code_copy(), and added pushcopy and #popcopy to pcre2test +for testing it. + +16. Change 66 for 10.21 introduced the use of snprintf() in PCRE2's version of +regerror(). When the error buffer is too small, my version of snprintf() puts a +binary zero in the final byte. Bug #1801 seems to show that other versions do +not do this, leading to bad output from pcre2test when it was checking for +buffer overflow. It no longer assumes a binary zero at the end of a too-small +regerror() buffer. + +17. Fixed typo ("&&" for "&") in pcre2_study(). Fortunately, this could not +actually affect anything, by sheer luck. + +18. Two minor fixes for MSVC compilation: (a) removal of apparently incorrect +"const" qualifiers in pcre2test and (b) defining snprintf as _snprintf for +older MSVC compilers. This has been done both in src/pcre2_internal.h for most +of the library, and also in src/pcre2posix.c, which no longer includes +pcre2_internal.h (see 24 below). + +19. Applied Chris Wilson's patch (Bugzilla #1681) to CMakeLists.txt for MSVC +static compilation. Subsequently applied Chris Wilson's second patch, putting +the first patch under a new option instead of being unconditional when +PCRE_STATIC is set. + +20. Updated pcre2grep to set stdout as binary when run under Windows, so as not +to convert \r\n at the ends of reflected lines into \r\r\n. This required +ensuring that other output that is written to stdout (e.g. file names) uses the +appropriate line terminator: \r\n for Windows, \n otherwise. + +21. When a line is too long for pcre2grep's internal buffer, show the maximum +length in the error message. + +22. Added support for string callouts to pcre2grep (Zoltan's patch with PH +additions). + +23. RunTest.bat was missing a "set type" line for test 22. + +24. The pcre2posix.c file was including pcre2_internal.h, and using some +"private" knowledge of the data structures. This is unnecessary; the code has +been re-factored and no longer includes pcre2_internal.h. + +25. A racing condition is fixed in JIT reported by Mozilla. + +26. Minor code refactor to avoid "array subscript is below array bounds" +compiler warning. + +27. Minor code refactor to avoid "left shift of negative number" warning. + +28. Add a bit more sanity checking to pcre2_serialize_decode() and document +that it expects trusted data. + +29. Fix typo in pcre2_jit_test.c + +30. Due to an oversight, pcre2grep was not making use of JIT when available. +This is now fixed. + +31. The RunGrepTest script is updated to use the valgrind suppressions file +when testing with JIT under valgrind (compare 10.21/51 below). The suppressions +file is updated so that is now the same as for PCRE1: it suppresses the +Memcheck warnings Addr16 and Cond in unknown objects (that is, JIT-compiled +code). Also changed smc-check=all to smc-check=all-non-file as was done for +RunTest (see 4 above). + +32. Implemented the PCRE2_NO_JIT option for pcre2_match(). + +33. Fix typo that gave a compiler error when JIT not supported. + +34. Fix comment describing the returns from find_fixedlength(). + +35. Fix potential negative index in pcre2test. + +36. Calls to pcre2_get_error_message() with error numbers that are never +returned by PCRE2 functions were returning empty strings. Now the error code +PCRE2_ERROR_BADDATA is returned. A facility has been added to pcre2test to +show the texts for given error numbers (i.e. to call pcre2_get_error_message() +and display what it returns) and a few representative error codes are now +checked in RunTest. + +37. Added "&& !defined(__INTEL_COMPILER)" to the test for __GNUC__ in +pcre2_match.c, in anticipation that this is needed for the same reason it was +recently added to pcrecpp.cc in PCRE1. + +38. Using -o with -M in pcre2grep could cause unnecessary repeated output when +the match extended over a line boundary, as it tried to find more matches "on +the same line" - but it was already over the end. + +39. Allow \C in lookbehinds and DFA matching in UTF-32 mode (by converting it +to the same code as '.' when PCRE2_DOTALL is set). + +40. Fix two clang compiler warnings in pcre2test when only one code unit width +is supported. + +41. Upgrade RunTest to automatically re-run test 2 with a large (64MiB) stack +if it fails when running the interpreter with a 16MiB stack (and if changing +the stack size via pcre2test is possible). This avoids having to manually set a +large stack size when testing with clang. + +42. Fix register overwite in JIT when SSE2 acceleration is enabled. + +43. Detect integer overflow in pcre2test pattern and data repetition counts. + +44. In pcre2test, ignore "allcaptures" after DFA matching. + +45. Fix unaligned accesses on x86. Patch by Marc Mutz. + +46. Fix some more clang compiler warnings. + + +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 (?imsxJU) 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 +-------------------------- + +1. Callouts with string arguments have been added. + +2. Assertion code generator in JIT has been optimized. + +3. The invalid pattern (?(?C) has a missing assertion condition at the end. The +pcre2_compile() function read past the end of the input before diagnosing an +error. This bug was discovered by the LLVM fuzzer. + +4. Implemented pcre2_callout_enumerate(). + +5. Fix JIT compilation of conditional blocks whose assertion is converted to +(*FAIL). E.g: /(?(?!))/. + +6. The pattern /(?(?!)^)/ caused references to random memory. This bug was +discovered by the LLVM fuzzer. + +7. The assertion (?!) is optimized to (*FAIL). This was not handled correctly +when this assertion was used as a condition, for example (?(?!)a|b). In +pcre2_match() it worked by luck; in pcre2_dfa_match() it gave an incorrect +error about an unsupported item. + +8. For some types of pattern, for example /Z*(|d*){216}/, the auto- +possessification code could take exponential time to complete. A recursion +depth limit of 1000 has been imposed to limit the resources used by this +optimization. This infelicity was discovered by the LLVM fuzzer. + +9. A pattern such as /(*UTF)[\S\V\H]/, which contains a negated special class +such as \S in non-UCP mode, explicit wide characters (> 255) can be ignored +because \S ensures they are all in the class. The code for doing this was +interacting badly with the code for computing the amount of space needed to +compile the pattern, leading to a buffer overflow. This bug was discovered by +the LLVM fuzzer. + +10. A pattern such as /((?2)+)((?1))/ which has mutual recursion nested inside +other kinds of group caused stack overflow at compile time. This bug was +discovered by the LLVM fuzzer. + +11. A pattern such as /(?1)(?#?'){8}(a)/ which had a parenthesized comment +between a subroutine call and its quantifier was incorrectly compiled, leading +to buffer overflow or other errors. This bug was discovered by the LLVM fuzzer. + +12. The illegal pattern /(?(?.*!.*)?)/ was not being diagnosed as missing an +assertion after (?(. The code was failing to check the character after (?(?< +for the ! or = that would indicate a lookbehind assertion. This bug was +discovered by the LLVM fuzzer. + +13. A pattern such as /X((?2)()*+){2}+/ which has a possessive quantifier with +a fixed maximum following a group that contains a subroutine reference was +incorrectly compiled and could trigger buffer overflow. This bug was discovered +by the LLVM fuzzer. + +14. Negative relative recursive references such as (?-7) to non-existent +subpatterns were not being diagnosed and could lead to unpredictable behaviour. +This bug was discovered by the LLVM fuzzer. + +15. The bug fixed in 14 was due to an integer variable that was unsigned when +it should have been signed. Some other "int" variables, having been checked, +have either been changed to uint32_t or commented as "must be signed". + +16. A mutual recursion within a lookbehind assertion such as (?<=((?2))((?1))) +caused a stack overflow instead of the diagnosis of a non-fixed length +lookbehind assertion. This bug was discovered by the LLVM fuzzer. + +17. The use of \K in a positive lookbehind assertion in a non-anchored pattern +(e.g. /(?<=\Ka)/) could make pcre2grep loop. + +18. There was a similar problem to 17 in pcre2test for global matches, though +the code there did catch the loop. + +19. If a greedy quantified \X was preceded by \C in UTF mode (e.g. \C\X*), +and a subsequent item in the pattern caused a non-match, backtracking over the +repeated \X did not stop, but carried on past the start of the subject, causing +reference to random memory and/or a segfault. There were also some other cases +where backtracking after \C could crash. This set of bugs was discovered by the +LLVM fuzzer. + +20. The function for finding the minimum length of a matching string could take +a very long time if mutual recursion was present many times in a pattern, for +example, /((?2){73}(?2))((?1))/. A better mutual recursion detection method has +been implemented. This infelicity was discovered by the LLVM fuzzer. + +21. Implemented PCRE2_NEVER_BACKSLASH_C. + +22. The feature for string replication in pcre2test could read from freed +memory if the replication required a buffer to be extended, and it was not +working properly in 16-bit and 32-bit modes. This issue was discovered by a +fuzzer: see http://lcamtuf.coredump.cx/afl/. + +23. Added the PCRE2_ALT_CIRCUMFLEX option. + +24. Adjust the treatment of \8 and \9 to be the same as the current Perl +behaviour. + +25. Static linking against the PCRE2 library using the pkg-config module was +failing on missing pthread symbols. + +26. If a group that contained a recursive back reference also contained a +forward reference subroutine call followed by a non-forward-reference +subroutine call, for example /.((?2)(?R)\1)()/, pcre2_compile() failed to +compile correct code, leading to undefined behaviour or an internally detected +error. This bug was discovered by the LLVM fuzzer. + +27. Quantification of certain items (e.g. atomic back references) could cause +incorrect code to be compiled when recursive forward references were involved. +For example, in this pattern: /(?1)()((((((\1++))\x85)+)|))/. This bug was +discovered by the LLVM fuzzer. + +28. A repeated conditional group whose condition was a reference by name caused +a buffer overflow if there was more than one group with the given name. This +bug was discovered by the LLVM fuzzer. + +29. A recursive back reference by name within a group that had the same name as +another group caused a buffer overflow. For example: /(?J)(?'d'(?'d'\g{d}))/. +This bug was discovered by the LLVM fuzzer. + +30. A forward reference by name to a group whose number is the same as the +current group, for example in this pattern: /(?|(\k'Pm')|(?'Pm'))/, caused a +buffer overflow at compile time. This bug was discovered by the LLVM fuzzer. + +31. Fix -fsanitize=undefined warnings for left shifts of 1 by 31 (it treats 1 +as an int; fixed by writing it as 1u). + +32. Fix pcre2grep compile when -std=c99 is used with gcc, though it still gives +a warning for "fileno" unless -std=gnu99 us used. + +33. A lookbehind assertion within a set of mutually recursive subpatterns could +provoke a buffer overflow. This bug was discovered by the LLVM fuzzer. + +34. Give an error for an empty subpattern name such as (?''). + +35. Make pcre2test give an error if a pattern that follows #forbud_utf contains +\P, \p, or \X. + +36. The way named subpatterns are handled has been refactored. There is now a +pre-pass over the regex which does nothing other than identify named +subpatterns and count the total captures. This means that information about +named patterns is known before the rest of the compile. In particular, it means +that forward references can be checked as they are encountered. Previously, the +code for handling forward references was contorted and led to several errors in +computing the memory requirements for some patterns, leading to buffer +overflows. + +37. There was no check for integer overflow in subroutine calls such as (?123). + +38. The table entry for \l in EBCDIC environments was incorrect, leading to its +being treated as a literal 'l' instead of causing an error. + +39. If a non-capturing group containing a conditional group that could match +an empty string was repeated, it was not identified as matching an empty string +itself. For example: /^(?:(?(1)x|)+)+$()/. + +40. In an EBCDIC environment, pcretest was mishandling the escape sequences +\a and \e in test subject lines. + +41. In an EBCDIC environment, \a in a pattern was converted to the ASCII +instead of the EBCDIC value. + +42. The handling of \c in an EBCDIC environment has been revised so that it is +now compatible with the specification in Perl's perlebcdic page. + +43. Single character repetition in JIT has been improved. 20-30% speedup +was achieved on certain patterns. + +44. The EBCDIC character 0x41 is a non-breaking space, equivalent to 0xa0 in +ASCII/Unicode. This has now been added to the list of characters that are +recognized as white space in EBCDIC. + +45. When PCRE2 was compiled without Unicode support, the use of \p and \P gave +an error (correctly) when used outside a class, but did not give an error +within a class. + +46. \h within a class was incorrectly compiled in EBCDIC environments. + +47. JIT should return with error when the compiled pattern requires +more stack space than the maximum. + +48. Fixed a memory leak in pcre2grep when a locale is set. + + +Version 10.10 06-March-2015 +--------------------------- + +1. When a pattern is compiled, it remembers the highest back reference so that +when matching, if the ovector is too small, extra memory can be obtained to +use instead. A conditional subpattern whose condition is a check on a capture +having happened, such as, for example in the pattern /^(?:(a)|b)(?(1)A|B)/, is +another kind of back reference, but it was not setting the highest +backreference number. This mattered only if pcre2_match() was called with an +ovector that was too small to hold the capture, and there was no other kind of +back reference (a situation which is probably quite rare). The effect of the +bug was that the condition was always treated as FALSE when the capture could +not be consulted, leading to a incorrect behaviour by pcre2_match(). This bug +has been fixed. + +2. Functions for serialization and deserialization of sets of compiled patterns +have been added. + +3. The value that is returned by PCRE2_INFO_SIZE has been corrected to remove +excess code units at the end of the data block that may occasionally occur if +the code for calculating the size over-estimates. This change stops the +serialization code copying uninitialized data, to which valgrind objects. The +documentation of PCRE2_INFO_SIZE was incorrect in stating that the size did not +include the general overhead. This has been corrected. + +4. All code units in every slot in the table of group names are now set, again +in order to avoid accessing uninitialized data when serializing. + +5. The (*NO_JIT) feature is implemented. + +6. If a bug that caused pcre2_compile() to use more memory than allocated was +triggered when using valgrind, the code in (3) above passed a stupidly large +value to valgrind. This caused a crash instead of an "internal error" return. + +7. A reference to a duplicated named group (either a back reference or a test +for being set in a conditional) that occurred in a part of the pattern where +PCRE2_DUPNAMES was not set caused the amount of memory needed for the pattern +to be incorrectly calculated, leading to overwriting. + +8. A mutually recursive set of back references such as (\2)(\1) caused a +segfault at compile time (while trying to find the minimum matching length). +The infinite loop is now broken (with the minimum length unset, that is, zero). + +9. If an assertion that was used as a condition was quantified with a minimum +of zero, matching went wrong. In particular, if the whole group had unlimited +repetition and could match an empty string, a segfault was likely. The pattern +(?(?=0)?)+ is an example that caused this. Perl allows assertions to be +quantified, but not if they are being used as conditions, so the above pattern +is faulted by Perl. PCRE2 has now been changed so that it also rejects such +patterns. + +10. The error message for an invalid quantifier has been changed from "nothing +to repeat" to "quantifier does not follow a repeatable item". + +11. If a bad UTF string is compiled with NO_UTF_CHECK, it may succeed, but +scanning the compiled pattern in subsequent auto-possessification can get out +of step and lead to an unknown opcode. Previously this could have caused an +infinite loop. Now it generates an "internal error" error. This is a tidyup, +not a bug fix; passing bad UTF with NO_UTF_CHECK is documented as having an +undefined outcome. + +12. A UTF pattern containing a "not" match of a non-ASCII character and a +subroutine reference could loop at compile time. Example: /[^\xff]((?1))/. + +13. The locale test (RunTest 3) has been upgraded. It now checks that a locale +that is found in the output of "locale -a" can actually be set by pcre2test +before it is accepted. Previously, in an environment where a locale was listed +but would not set (an example does exist), the test would "pass" without +actually doing anything. Also the fr_CA locale has been added to the list of +locales that can be used. + +14. Fixed a bug in pcre2_substitute(). If a replacement string ended in a +capturing group number without parentheses, the last character was incorrectly +literally included at the end of the replacement string. + +15. A possessive capturing group such as (a)*+ with a minimum repeat of zero +failed to allow the zero-repeat case if pcre2_match() was called with an +ovector too small to capture the group. + +16. Improved error message in pcre2test when setting the stack size (-S) fails. + +17. Fixed two bugs in CMakeLists.txt: (1) Some lines had got lost in the +transfer from PCRE1, meaning that CMake configuration failed if "build tests" +was selected. (2) The file src/pcre2_serialize.c had not been added to the list +of PCRE2 sources, which caused a failure to build pcre2test. + +18. Fixed typo in pcre2_serialize.c (DECL instead of DEFN) that causes problems +only on Windows. + +19. Use binary input when reading back saved serialized patterns in pcre2test. + +20. Added RunTest.bat for running the tests under Windows. + +21. "make distclean" was not removing config.h, a file that may be created for +use with CMake. + +22. A pattern such as "((?2){0,1999}())?", which has a group containing a +forward reference repeated a large (but limited) number of times within a +repeated outer group that has a zero minimum quantifier, caused incorrect code +to be compiled, leading to the error "internal error: previously-checked +referenced subpattern not found" when an incorrect memory address was read. +This bug was reported as "heap overflow", discovered by Kai Lu of Fortinet's +FortiGuard Labs. (Added 24-March-2015: CVE-2015-2325 was given to this.) + +23. A pattern such as "((?+1)(\1))/" containing a forward reference subroutine +call within a group that also contained a recursive back reference caused +incorrect code to be compiled. This bug was reported as "heap overflow", +discovered by Kai Lu of Fortinet's FortiGuard Labs. (Added 24-March-2015: +CVE-2015-2326 was given to this.) + +24. Computing the size of the JIT read-only data in advance has been a source +of various issues, and new ones are still appear unfortunately. To fix +existing and future issues, size computation is eliminated from the code, +and replaced by on-demand memory allocation. + +25. A pattern such as /(?i)[A-`]/, where characters in the other case are +adjacent to the end of the range, and the range contained characters with more +than one other case, caused incorrect behaviour when compiled in UTF mode. In +that example, the range a-j was left out of the class. + + +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. + +The code of the library was heavily revised as part of the new API +implementation. Details of each and every modification were not individually +logged. In addition to the API changes, the following changes were made. They +are either new functionality, or bug fixes and other noticeable changes of +behaviour that were implemented after the code had been forked. + +1. Including Unicode support at build time is now enabled by default, but it +can optionally be disabled. It is not enabled by default at run time (no +change). + +2. The test program, now called pcre2test, was re-specified and almost +completely re-written. Its input is not compatible with input for pcretest. + +3. Patterns may start with (*NOTEMPTY) or (*NOTEMPTY_ATSTART) to set the +PCRE2_NOTEMPTY or PCRE2_NOTEMPTY_ATSTART options for every subject line that is +matched by that pattern. + +4. For the benefit of those who use PCRE2 via some other application, that is, +not writing the function calls themselves, it is possible to check the PCRE2 +version by matching a pattern such as /(?(VERSION>=10)yes|no)/ against a +string such as "yesno". + +5. There are case-equivalent Unicode characters whose encodings use different +numbers of code units in UTF-8. U+023A and U+2C65 are one example. (It is +theoretically possible for this to happen in UTF-16 too.) If a backreference to +a group containing one of these characters was greedily repeated, and during +the match a backtrack occurred, the subject might be backtracked by the wrong +number of code units. For example, if /^(\x{23a})\1*(.)/ is matched caselessly +(and in UTF-8 mode) against "\x{23a}\x{2c65}\x{2c65}\x{2c65}", group 2 should +capture the final character, which is the three bytes E2, B1, and A5 in UTF-8. +Incorrect backtracking meant that group 2 captured only the last two bytes. +This bug has been fixed; the new code is slower, but it is used only when the +strings matched by the repetition are not all the same length. + +6. A pattern such as /()a/ was not setting the "first character must be 'a'" +information. This applied to any pattern with a group that matched no +characters, for example: /(?:(?=.)|(? start of atomic group +META_CIRCUMFLEX ^ metacharacter +META_CLASS [ start of non-empty class +META_CLASS_EMPTY [] empty class - only with PCRE2_ALLOW_EMPTY_CLASS +META_CLASS_EMPTY_NOT [^] negative empty class - ditto +META_CLASS_END ] end of non-empty class +META_CLASS_NOT [^ start non-empty negative class +META_COMMIT (*COMMIT) +META_COND_ASSERT (?(?assertion) +META_DOLLAR $ metacharacter +META_DOT . metacharacter +META_END End of pattern (this value is 0x80000000) +META_FAIL (*FAIL) +META_KET ) closing parenthesis +META_LOOKAHEAD (?= start of lookahead +META_LOOKAHEADNOT (?! start of negative lookahead +META_NOCAPTURE (?: no capture parens +META_PLUS + +META_PLUS_PLUS ++ +META_PLUS_QUERY +? +META_PRUNE (*PRUNE) - no argument +META_QUERY ? +META_QUERY_PLUS ?+ +META_QUERY_QUERY ?? +META_RANGE_ESCAPED hyphen in class range with at least one escape +META_RANGE_LITERAL hyphen in class range defined literally +META_SKIP (*SKIP) - no argument +META_THEN (*THEN) - no argument + +The two RANGE values occur only in character classes. They are positioned +between two literals that define the start and end of the range. In an EBCDIC +evironment it is necessary to know whether either of the range values was +specified as an escape. In an ASCII/Unicode environment the distinction is not +relevant. + +The following have data in the lower 16 bits, and may be followed by other data +elements: + +META_ALT | alternation +META_BACKREF back reference +META_CAPTURE start of capturing group +META_ESCAPE non-literal escape sequence +META_RECURSE recursion call + +If the data for META_ALT is non-zero, it is inside a lookbehind, and the data +is the length of its branch, for which OP_REVERSE must be generated. + +META_BACKREF, META_CAPTURE, and META_RECURSE have the capture group number as +their data in the lower 16 bits of the element. + +META_BACKREF is followed by an offset if the back reference group number is 10 +or more. The offsets of the first ocurrences of references to groups whose +numbers are less than 10 are put in cb->small_ref_offset[] (only the first +occurrence is useful). On 64-bit systems this avoids using more than two parsed +pattern elements for items such as \3. The offset is used when an error occurs +because the reference is to a non-existent group. + +META_RECURSE is always followed by an offset, for use in error messages. + +META_ESCAPE has an ESC_xxx value as its data. For ESC_P and ESC_p, the next +element contains the 16-bit type and data property values, packed together. +ESC_g and ESC_k are used only for named references - numerical ones are turned +into META_RECURSE or META_BACKREF as appropriate. ESC_g and ESC_k are followed +by a length and an offset into the pattern to specify the name. + +The following have one data item that follows in the next vector element: + +META_BIGVALUE Next is a literal >= META_END +META_OPTIONS (?i) and friends (data is new option bits) +META_POSIX POSIX class item (data identifies the class) +META_POSIX_NEG negative POSIX class item (ditto) + +The following are followed by a length element, then a number of character code +values (which should match with the length): + +META_MARK (*MARK:xxxx) +META_COMMIT_ARG )*COMMIT:xxxx) +META_PRUNE_ARG (*PRUNE:xxx) +META_SKIP_ARG (*SKIP:xxxx) +META_THEN_ARG (*THEN:xxxx) + +The following are followed by a length element, then an offset in the pattern +that identifies the name: + +META_COND_NAME (?() or (?('name') or (?(name) +META_COND_RNAME (?(R&name) +META_COND_RNUMBER (?(Rdigits) +META_RECURSE_BYNAME (?&name) +META_BACKREF_BYNAME \k'name' + +META_COND_RNUMBER is used for names that start with R and continue with digits, +because this is an ambiguous case. It could be a back reference to a group with +that name, or it could be a recursion test on a numbered group. + +This one is followed by an offset, for use in error messages, then a number: + +META_COND_NUMBER (?([+-]digits) + +The following is followed just by an offset, for use in error messages: + +META_COND_DEFINE (?(DEFINE) + +The following are also followed just by an offset, but also the lower 16 bits +of the main word contain the length of the first branch of the lookbehind +group; this is used when generating OP_REVERSE for that branch. + +META_LOOKBEHIND (?<= +META_LOOKBEHINDNOT (?' and 1 for '>='; +the next two are the major and minor numbers: + +META_COND_VERSION (?(VERSIONx.y) + +Callouts are converted into one of two items: + +META_CALLOUT_NUMBER (?C with numerical argument +META_CALLOUT_STRING (?C with string argument + +In both cases, the next two elements contain the offset and length of the next +item in the pattern. Then there is either one callout number, or a length and +an offset for the string argument. The length includes both delimiters. Traditional matching function @@ -154,9 +346,14 @@ Changeable options ------------------ The /i, /m, or /s options (PCRE2_CASELESS, PCRE2_MULTILINE, PCRE2_DOTALL, and -some others) may change in the middle of patterns. Their processing is handled -entirely at compile time by generating different opcodes for the different -settings. The runtime functions do not need to keep track of an options state. +others) may be changed in the middle of patterns by items such as (?i). Their +processing is handled entirely at compile time by generating different opcodes +for the different settings. The runtime functions do not need to keep track of +an option's state. + +PCRE2_DUPNAMES, PCRE2_EXTENDED, PCRE2_EXTENDED_MORE, and PCRE2_NO_AUTO_CAPTURE +are tracked and processed during the parsing pre-pass. The others are handled +from META_OPTIONS items during the main compile phase. Format of compiled patterns @@ -174,19 +371,19 @@ default value for LINK_SIZE is 2, except for the 32-bit library, where it can only be 4. The 8-bit library can be compiled to used 3-byte or 4-byte values, and the 16-bit library can be compiled to use 4-byte values, though this impairs performance. Specifing a LINK_SIZE larger than 2 for these libraries is -necessary only when patterns whose compiled length is greater than 64K code +necessary only when patterns whose compiled length is greater than 65535 code units are going to be processed. When a LINK_SIZE value uses more than one code unit, the most significant unit is first. In this description, we assume the "normal" compilation options. Data values that are counts (e.g. quantifiers) are always two bytes long in 8-bit mode -(most significant byte first), or one code unit in 16-bit and 32-bit modes. +(most significant byte first), and one code unit in 16-bit and 32-bit modes. Opcodes with no following data ------------------------------ -These items are all just one unit long +These items are all just one unit long: OP_END end of pattern OP_ANY match any one character other than newline @@ -220,42 +417,58 @@ These items are all just one unit long OP_ACCEPT ) These are Perl 5.10's "backtracking control OP_COMMIT ) verbs". If OP_ACCEPT is inside capturing OP_FAIL ) parentheses, it may be preceded by one or more - OP_PRUNE ) OP_CLOSE, each followed by a count that + OP_PRUNE ) OP_CLOSE, each followed by a number that OP_SKIP ) indicates which parentheses must be closed. OP_THEN ) OP_ASSERT_ACCEPT is used when (*ACCEPT) is encountered within an assertion. -This ends the assertion, not the entire pattern match. The assertion (?!) is +This ends the assertion, not the entire pattern match. The assertion (?!) is always optimized to OP_FAIL. OP_ALLANY is used for '.' when PCRE2_DOTALL is set. It is also used for \C in -non-UTF modes and in UTF-32 mode (since one code unit still equals one +non-UTF modes and in UTF-32 mode (since one code unit still equals one character). Another use is for [^] when empty classes are permitted (PCRE2_ALLOW_EMPTY_CLASS is set). -Backtracking control verbs with optional data ---------------------------------------------- +Backtracking control verbs +-------------------------- -(*THEN) without an argument generates the opcode OP_THEN and no following data. -OP_MARK is followed by the mark name, preceded by a length in one code unit, -and followed by a binary zero. For (*PRUNE), (*SKIP), and (*THEN) with -arguments, the opcodes OP_PRUNE_ARG, OP_SKIP_ARG, and OP_THEN_ARG are used, -with the name following in the same format as OP_MARK. +Verbs with no arguments generate opcodes with no following data (as listed +in the section above). + +(*MARK:NAME) generates OP_MARK followed by the mark name, preceded by a +length in one code unit, and followed by a binary zero. The name length is +limited by the size of the code unit. + +(*ACCEPT:NAME) and (*FAIL:NAME) are compiled as (*MARK:NAME)(*ACCEPT) and +(*MARK:NAME)(*FAIL) respectively. + +For (*COMMIT:NAME), (*PRUNE:NAME), (*SKIP:NAME), and (*THEN:NAME), the opcodes +OP_COMMIT_ARG, OP_PRUNE_ARG, OP_SKIP_ARG, and OP_THEN_ARG are used, with the +name following in the same format as for OP_MARK. Matching literal characters --------------------------- The OP_CHAR opcode is followed by a single character that is to be matched -casefully. For caseless matching, OP_CHARI is used. In UTF-8 or UTF-16 modes, -the character may be more than one code unit long. In UTF-32 mode, characters -are always exactly one code unit long. +casefully. For caseless matching of characters that have at most two +case-equivalent code points, OP_CHARI is used. In UTF-8 or UTF-16 modes, the +character may be more than one code unit long. In UTF-32 mode, characters are +always exactly one code unit long. If there is only one character in a character class, OP_CHAR or OP_CHARI is used for a positive class, and OP_NOT or OP_NOTI for a negative one (that is, for something like [^a]). +Caseless matching (positive or negative) of characters that have more than two +case-equivalent code points (which is possible only in UTF mode) is handled by +compiling a Unicode property item (see below), with the pseudo-property +PT_CLIST. The value of this property is an offset in a vector called +"ucd_caseless_sets" which identifies the start of a short list of equivalent +characters, terminated by the value NOTACHAR (0xffffffff). + Repeating single characters --------------------------- @@ -331,7 +544,8 @@ Each is followed by two code units that encode the desired property as a type and a value. The types are a set of #defines of the form PT_xxx, and the values are enumerations of the form ucp_xx, defined in the pcre2_ucp.h source file. The value is relevant only for PT_GC (General Category), PT_PC (Particular -Category), and PT_SC (Script). +Category), PT_SC (Script), and the pseudo-property PT_CLIST, which is used to +identify a list of case-equivalent characters when there are three or more. Repeats of these items use the OP_TYPESTAR etc. set of opcodes, followed by three code units: OP_PROP or OP_NOTPROP, and then the desired property type and @@ -343,7 +557,10 @@ Character classes If there is only one character in a class, OP_CHAR or OP_CHARI is used for a positive class, and OP_NOT or OP_NOTI for a negative one (that is, for -something like [^a]). +something like [^a]), except when caselessly matching a character that has more +than two case-equivalent code points (which can happen only in UTF mode). In +this case a Unicode property item is used, as described above in "Matching +literal characters". A set of repeating opcodes (called OP_NOTSTAR etc.) are used for repeated, negated, single-character classes. The normal single-character opcodes @@ -364,8 +581,8 @@ do. For classes containing characters with values greater than 255 or that contain \p or \P, OP_XCLASS is used. It optionally uses a bit map if any acceptable code points are less than 256, followed by a list of pairs (for a range) and/or -single characters and/or properties. In caseless mode, both cases are -explicitly listed. +single characters and/or properties. In caseless mode, all equivalent +characters are explicitly listed. OP_XCLASS is followed by a LINK_SIZE value containing the total length of the opcode and its data. This is followed by a code unit containing flag bits: @@ -422,8 +639,8 @@ opcode to see if it is one of these: OP_CRMINRANGE OP_CRPOSRANGE -All but the last three are single-code-unit items, with no data. The others are -followed by the minimum and maximum repeat counts. +All but the last three are single-code-unit items, with no data. The range +opcodes are followed by the minimum and maximum repeat counts. Brackets and alternation @@ -438,16 +655,17 @@ myself, can be round, square, curly, or pointy. Hence this usage rather than Non-capturing brackets use the opcode OP_BRA, capturing brackets use OP_CBRA. A bracket opcode is followed by a LINK_SIZE value which gives the offset to the -next alternative OP_ALT or, if there aren't any branches, to the matching -OP_KET opcode. Each OP_ALT is followed by a LINK_SIZE value giving the offset -to the next one, or to the OP_KET opcode. For capturing brackets, the bracket -number is a count that immediately follows the offset. +next alternative OP_ALT or, if there aren't any branches, to the terminating +opcode. Each OP_ALT is followed by a LINK_SIZE value giving the offset to the +next one, or to the final opcode. For capturing brackets, the bracket number is +a count that immediately follows the offset. -OP_KET is used for subpatterns that do not repeat indefinitely, and OP_KETRMIN -and OP_KETRMAX are used for indefinite repetitions, minimally or maximally -respectively (see below for possessive repetitions). All three are followed by -a LINK_SIZE value giving (as a positive number) the offset back to the matching -bracket opcode. +There are several opcodes that mark the end of a subpattern group. OP_KET is +used for subpatterns that do not repeat indefinitely, OP_KETRMIN and +OP_KETRMAX are used for indefinite repetitions, minimally or maximally +respectively, and OP_KETRPOS for possessive repetitions (see below for more +details). All four are followed by a LINK_SIZE value giving (as a positive +number) the offset back to the matching bracket opcode. If a subpattern is quantified such that it is permitted to match zero times, it is preceded by one of OP_BRAZERO, OP_BRAMINZERO, or OP_SKIPZERO. These are @@ -488,17 +706,9 @@ repetition is zero, the group is preceded by OP_BRAPOSZERO. Once-only (atomic) groups ------------------------- -These are just like other subpatterns, but they start with the opcode -OP_ONCE or OP_ONCE_NC. The former is used when there are no capturing brackets -within the atomic group; the latter when there are. The distinction is needed -for when there is a backtrack to before the group - any captures within the -group must be reset, so it is necessary to retain backtracking points inside -the group, even after it is complete, in order to do this. When there are no -captures in an atomic group, all the backtracking can be discarded when it is -complete. This is more efficient, and also uses less stack. - +These are just like other subpatterns, but they start with the opcode OP_ONCE. The check for matching an empty string in an unbounded repeat is handled -entirely at runtime, so there are just these two opcodes for atomic groups. +entirely at runtime, so there is just this one opcode for atomic groups. Assertions @@ -544,14 +754,14 @@ tests the PCRE2 version number. This compiles into one of the opcodes OP_TRUE or OP_FALSE. If a condition is not a back reference, recursion test, DEFINE, or VERSION, it -must start with an assertion, whose opcode normally immediately follows OP_COND -or OP_SCOND. However, if automatic callouts are enabled, a callout is inserted -immediately before the assertion. It is also possible to insert a manual -callout at this point. Only assertion conditions may have callouts preceding -the condition. +must start with a parenthesized assertion, whose opcode normally immediately +follows OP_COND or OP_SCOND. However, if automatic callouts are enabled, a +callout is inserted immediately before the assertion. It is also possible to +insert a manual callout at this point. Only assertion conditions may have +callouts preceding the condition. -A condition that is the negative assertion (?!) is optimized to OP_FAIL in all -parts of the pattern, so this is another opcode that may appear as a condition. +A condition that is the negative assertion (?!) is optimized to OP_FAIL in all +parts of the pattern, so this is another opcode that may appear as a condition. It is treated the same as OP_FALSE. @@ -561,21 +771,28 @@ Recursion Recursion either matches the current pattern, or some subexpression. The opcode OP_RECURSE is followed by a LINK_SIZE value that is the offset to the starting bracket from the start of the whole pattern. OP_RECURSE is also used for -"subroutine" calls, even though they are not strictly a recursion. Repeated -recursions are automatically wrapped inside OP_ONCE brackets, because otherwise -some patterns broke them. A non-repeated recursion is not wrapped in OP_ONCE -brackets, but it is nevertheless still treated as an atomic group. +"subroutine" calls, even though they are not strictly a recursion. Up till +release 10.30 recursions were treated as atomic groups, making them +incompatible with Perl (but PCRE had them well before Perl did). From 10.30, +backtracking into recursions is supported. + +Repeated recursions used to be wrapped inside OP_ONCE brackets, which not only +forced no backtracking, but also allowed repetition to be handled as for other +bracketed groups. From 10.30 onwards, repeated recursions are duplicated for +their minimum repetitions, and then wrapped in non-capturing brackets for the +remainder. For example, (?1){3} is treated as (?1)(?1)(?1), and (?1){2,4} is +treated as (?1)(?1)(?:(?1)){0,2}. -Callout -------- +Callouts +-------- -A callout can nowadays have either a numerical argument or a string argument. -These use OP_CALLOUT or OP_CALLOUT_STR, respectively. In each case these are -followed by two LINK_SIZE values giving the offset in the pattern string to the -start of the following item, and another count giving the length of this item. -These values make it possible for pcre2test to output useful tracing -information using callouts. +A callout may have either a numerical argument or a string argument. These use +OP_CALLOUT or OP_CALLOUT_STR, respectively. In each case these are followed by +two LINK_SIZE values giving the offset in the pattern string to the start of +the following item, and another count giving the length of this item. These +values make it possible for pcre2test to output useful tracing information +using callouts. In the case of a numeric callout, after these two values there is a single code unit containing the callout number, in the range 0-255, with 255 being used for @@ -593,17 +810,17 @@ the actual string is passed, but the delimiter can be accessed as string[-1] if the application needs it. In the 8-bit library, the callout in /X(?C'abc')Y/ is compiled as the following bytes (decimal numbers represent binary values): - [OP_CALLOUT] [0] [10] [0] [1] [0] [14] [0] [5] ['] [a] [b] [c] [0] - -------- ------- -------- ------- - | | | | - ------- LINK_SIZE items ------ + [OP_CALLOUT_STR] [0] [10] [0] [1] [0] [14] [0] [5] ['] [a] [b] [c] [0] + -------- ------- -------- ------- + | | | | + ------- LINK_SIZE items ------ Opcode table checking --------------------- The last opcode that is defined in pcre2_internal.h is OP_TABLE_LENGTH. This is -not a real opcode, but is used to check that tables indexed by opcode are the -correct length, in order to catch updating errors. +not a real opcode, but is used to check at compile time that tables indexed by +opcode are the correct length, in order to catch updating errors. Philip Hazel -June 2016 +20 July 2018 diff --git a/pcre2-10.32/INSTALL b/pcre2-10.32/INSTALL new file mode 100644 index 000000000..8865734f8 --- /dev/null +++ b/pcre2-10.32/INSTALL @@ -0,0 +1,368 @@ +Installation Instructions +************************* + + Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software +Foundation, Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell command './configure && make && make install' +should configure, build, and install this package. The following +more-detailed instructions are generic; see the 'README' file for +instructions specific to this package. Some packages provide this +'INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The 'configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a 'Makefile' in each directory of the package. +It may also create one or more '.h' files containing system-dependent +definitions. Finally, it creates a shell script 'config.status' that +you can run in the future to recreate the current configuration, and a +file 'config.log' containing compiler output (useful mainly for +debugging 'configure'). + + It can also use an optional file (typically called 'config.cache' and +enabled with '--cache-file=config.cache' or simply '-C') that saves the +results of its tests to speed up reconfiguring. Caching is disabled by +default to prevent problems with accidental use of stale cache files. + + If you need to do unusual things to compile the package, please try +to figure out how 'configure' could check whether to do them, and mail +diffs or instructions to the address given in the 'README' so they can +be considered for the next release. If you are using the cache, and at +some point 'config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file 'configure.ac' (or 'configure.in') is used to create +'configure' by a program called 'autoconf'. You need 'configure.ac' if +you want to change it or regenerate 'configure' using a newer version of +'autoconf'. + + The simplest way to compile this package is: + + 1. 'cd' to the directory containing the package's source code and type + './configure' to configure the package for your system. + + Running 'configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type 'make' to compile the package. + + 3. Optionally, type 'make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type 'make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the 'make install' phase executed with root + privileges. + + 5. Optionally, type 'make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior 'make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing 'make clean'. To also remove the + files that 'configure' created (so you can compile the package for + a different kind of computer), type 'make distclean'. There is + also a 'make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type 'make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide 'make + distcheck', which can by used by developers to test that all other + targets like 'make install' and 'make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the 'configure' script does not know about. Run './configure --help' +for details on some of the pertinent environment variables. + + You can give 'configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here is +an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU 'make'. 'cd' to the +directory where you want the object files and executables to go and run +the 'configure' script. 'configure' automatically checks for the source +code in the directory that 'configure' is in and in '..'. This is known +as a "VPATH" build. + + With a non-GNU 'make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use 'make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple '-arch' options to the +compiler but only a single '-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the 'lipo' tool if you have problems. + +Installation Names +================== + + By default, 'make install' installs the package's commands under +'/usr/local/bin', include files under '/usr/local/include', etc. You +can specify an installation prefix other than '/usr/local' by giving +'configure' the option '--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option '--exec-prefix=PREFIX' to 'configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like '--bindir=DIR' to specify different values for particular +kinds of files. Run 'configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the default +for these options is expressed in terms of '${prefix}', so that +specifying just '--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to 'configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +'make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, 'make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +'${prefix}'. Any directories that were specified during 'configure', +but not in terms of '${prefix}', must each be overridden at install time +for the entire installation to be relocated. The approach of makefile +variable overrides for each directory variable is required by the GNU +Coding Standards, and ideally causes no recompilation. However, some +platforms have known limitations with the semantics of shared libraries +that end up requiring recompilation when using this method, particularly +noticeable in packages that use GNU Libtool. + + The second method involves providing the 'DESTDIR' variable. For +example, 'make install DESTDIR=/alternate/directory' will prepend +'/alternate/directory' before all installation names. The approach of +'DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of '${prefix}' +at 'configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving 'configure' the +option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. + + Some packages pay attention to '--enable-FEATURE' options to +'configure', where FEATURE indicates an optional part of the package. +They may also pay attention to '--with-PACKAGE' options, where PACKAGE +is something like 'gnu-as' or 'x' (for the X Window System). The +'README' should mention any '--enable-' and '--with-' options that the +package recognizes. + + For packages that use the X Window System, 'configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the 'configure' options '--x-includes=DIR' and +'--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of 'make' will be. For these packages, running './configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with 'make V=1'; while running './configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with 'make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC +is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX 'make' updates targets which have the same time stamps as their +prerequisites, which makes it generally unusable when shipped generated +files such as 'configure' are involved. Use GNU 'make' instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its '' header file. The option '-nodtk' can be used as a +workaround. If GNU CC is not installed, it is therefore recommended to +try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put '/usr/ucb' early in your 'PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in '/usr/bin'. So, if you need '/usr/ucb' +in your 'PATH', put it _after_ '/usr/bin'. + + On Haiku, software installed for all users goes in '/boot/common', +not '/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features 'configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, 'configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +'--build=TYPE' option. TYPE can either be a short name for the system +type, such as 'sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file 'config.sub' for the possible values of each field. If +'config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option '--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with '--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for 'configure' scripts to share, +you can create a site shell script called 'config.site' that gives +default values for variables like 'CC', 'cache_file', and 'prefix'. +'configure' looks for 'PREFIX/share/config.site' if it exists, then +'PREFIX/etc/config.site' if it exists. Or, you can set the +'CONFIG_SITE' environment variable to the location of the site script. +A warning: not all 'configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to 'configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the 'configure' command line, using 'VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified 'gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an +Autoconf limitation. Until the limitation is lifted, you can use this +workaround: + + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +'configure' Invocation +====================== + + 'configure' recognizes the following options to control how it +operates. + +'--help' +'-h' + Print a summary of all of the options to 'configure', and exit. + +'--help=short' +'--help=recursive' + Print a summary of the options unique to this package's + 'configure', and exit. The 'short' variant lists options used only + in the top level, while the 'recursive' variant lists options also + present in any nested packages. + +'--version' +'-V' + Print the version of Autoconf used to generate the 'configure' + script, and exit. + +'--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally 'config.cache'. FILE defaults to '/dev/null' to + disable caching. + +'--config-cache' +'-C' + Alias for '--cache-file=config.cache'. + +'--quiet' +'--silent' +'-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to '/dev/null' (any error + messages will still be shown). + +'--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + 'configure' can determine that directory automatically. + +'--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: for + more details, including other options available for fine-tuning the + installation locations. + +'--no-create' +'-n' + Run the configure checks, but stop before creating any output + files. + +'configure' also accepts some other, not widely useful, options. Run +'configure --help' for more details. diff --git a/pcre2-10.22/LICENCE b/pcre2-10.32/LICENCE similarity index 69% rename from pcre2-10.22/LICENCE rename to pcre2-10.32/LICENCE index 6600a6590..b0f8804ff 100644 --- a/pcre2-10.22/LICENCE +++ b/pcre2-10.32/LICENCE @@ -4,10 +4,11 @@ PCRE2 LICENCE PCRE2 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. -Release 10 of PCRE2 is distributed under the terms of the "BSD" licence, as -specified below. The documentation for PCRE2, supplied in the "doc" -directory, is distributed under the same terms as the software itself. The data -in the testdata directory is not copyrighted and is in the public domain. +Releases 10.00 and above of PCRE2 are distributed under the terms of the "BSD" +licence, as specified below, with one exemption for certain binary +redistributions. The documentation for PCRE2, supplied in the "doc" directory, +is distributed under the same terms as the software itself. The data in the +testdata directory is not copyrighted and is in the public domain. The basic library functions are written in C and are freestanding. Also included in the distribution is a just-in-time compiler that can be used to @@ -25,7 +26,7 @@ Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. -Copyright (c) 1997-2016 University of Cambridge +Copyright (c) 1997-2018 University of Cambridge All rights reserved. @@ -34,9 +35,9 @@ PCRE2 JUST-IN-TIME COMPILATION SUPPORT Written by: Zoltan Herczeg Email local part: hzmester -Emain domain: freemail.hu +Email domain: freemail.hu -Copyright(c) 2010-2016 Zoltan Herczeg +Copyright(c) 2010-2018 Zoltan Herczeg All rights reserved. @@ -45,9 +46,9 @@ STACK-LESS JUST-IN-TIME COMPILER Written by: Zoltan Herczeg Email local part: hzmester -Emain domain: freemail.hu +Email domain: freemail.hu -Copyright(c) 2009-2016 Zoltan Herczeg +Copyright(c) 2009-2018 Zoltan Herczeg All rights reserved. @@ -57,11 +58,11 @@ THE "BSD" LICENCE 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, + * Redistributions of source code must retain the above copyright notices, 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 + notices, 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 any @@ -80,4 +81,14 @@ 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. + +EXEMPTION FOR BINARY LIBRARY-LIKE PACKAGES +------------------------------------------ + +The second condition in the BSD licence (covering binary redistributions) does +not apply all the way down a chain of software. If binary package A includes +PCRE2, it must respect the condition, but if package B is software that +includes package A, the condition is not imposed on package B unless it uses +PCRE2 independently. + End diff --git a/pcre2-10.22/Makefile.am b/pcre2-10.32/Makefile.am similarity index 91% rename from pcre2-10.22/Makefile.am rename to pcre2-10.32/Makefile.am index 38f1d41b8..a4bcdf6e4 100644 --- a/pcre2-10.22/Makefile.am +++ b/pcre2-10.32/Makefile.am @@ -2,7 +2,10 @@ AUTOMAKE_OPTIONS = subdir-objects ACLOCAL_AMFLAGS = -I m4 -AM_CPPFLAGS = -I$(builddir)/src -I$(srcdir)/src + +## This seems to have become necessary for building in non-source directory. + +AM_CPPFLAGS="-I$(srcdir)/src" ## Specify the documentation files that are distributed. @@ -26,12 +29,17 @@ dist_html_DATA = \ doc/html/pcre2.html \ doc/html/pcre2_callout_enumerate.html \ doc/html/pcre2_code_copy.html \ + doc/html/pcre2_code_copy_with_tables.html \ doc/html/pcre2_code_free.html \ doc/html/pcre2_compile.html \ doc/html/pcre2_compile_context_copy.html \ doc/html/pcre2_compile_context_create.html \ doc/html/pcre2_compile_context_free.html \ doc/html/pcre2_config.html \ + doc/html/pcre2_convert_context_copy.html \ + doc/html/pcre2_convert_context_create.html \ + doc/html/pcre2_convert_context_free.html \ + doc/html/pcre2_converted_pattern_free.html \ doc/html/pcre2_dfa_match.html \ doc/html/pcre2_general_context_copy.html \ doc/html/pcre2_general_context_create.html \ @@ -55,6 +63,7 @@ dist_html_DATA = \ doc/html/pcre2_match_data_create.html \ doc/html/pcre2_match_data_create_from_pattern.html \ doc/html/pcre2_match_data_free.html \ + doc/html/pcre2_pattern_convert.html \ doc/html/pcre2_pattern_info.html \ doc/html/pcre2_serialize_decode.html \ doc/html/pcre2_serialize_encode.html \ @@ -63,8 +72,14 @@ dist_html_DATA = \ doc/html/pcre2_set_bsr.html \ doc/html/pcre2_set_callout.html \ doc/html/pcre2_set_character_tables.html \ + doc/html/pcre2_set_compile_extra_options.html \ doc/html/pcre2_set_compile_recursion_guard.html \ + doc/html/pcre2_set_depth_limit.html \ + doc/html/pcre2_set_glob_escape.html \ + doc/html/pcre2_set_glob_separator.html \ + doc/html/pcre2_set_heap_limit.html \ doc/html/pcre2_set_match_limit.html \ + doc/html/pcre2_set_max_pattern_length.html \ doc/html/pcre2_set_offset_limit.html \ doc/html/pcre2_set_newline.html \ doc/html/pcre2_set_parens_nest_limit.html \ @@ -86,6 +101,7 @@ dist_html_DATA = \ doc/html/pcre2build.html \ doc/html/pcre2callout.html \ doc/html/pcre2compat.html \ + doc/html/pcre2convert.html \ doc/html/pcre2demo.html \ doc/html/pcre2grep.html \ doc/html/pcre2jit.html \ @@ -97,7 +113,6 @@ dist_html_DATA = \ doc/html/pcre2posix.html \ doc/html/pcre2sample.html \ doc/html/pcre2serialize.html \ - doc/html/pcre2stack.html \ doc/html/pcre2syntax.html \ doc/html/pcre2test.html \ doc/html/pcre2unicode.html @@ -107,12 +122,17 @@ dist_man_MANS = \ doc/pcre2.3 \ doc/pcre2_callout_enumerate.3 \ doc/pcre2_code_copy.3 \ + doc/pcre2_code_copy_with_tables.3 \ doc/pcre2_code_free.3 \ doc/pcre2_compile.3 \ doc/pcre2_compile_context_copy.3 \ doc/pcre2_compile_context_create.3 \ doc/pcre2_compile_context_free.3 \ doc/pcre2_config.3 \ + doc/pcre2_convert_context_copy.3 \ + doc/pcre2_convert_context_create.3 \ + doc/pcre2_convert_context_free.3 \ + doc/pcre2_converted_pattern_free.3 \ doc/pcre2_dfa_match.3 \ doc/pcre2_general_context_copy.3 \ doc/pcre2_general_context_create.3 \ @@ -136,6 +156,7 @@ dist_man_MANS = \ doc/pcre2_match_data_create.3 \ doc/pcre2_match_data_create_from_pattern.3 \ doc/pcre2_match_data_free.3 \ + doc/pcre2_pattern_convert.3 \ doc/pcre2_pattern_info.3 \ doc/pcre2_serialize_decode.3 \ doc/pcre2_serialize_encode.3 \ @@ -144,8 +165,14 @@ dist_man_MANS = \ doc/pcre2_set_bsr.3 \ doc/pcre2_set_callout.3 \ doc/pcre2_set_character_tables.3 \ + doc/pcre2_set_compile_extra_options.3 \ doc/pcre2_set_compile_recursion_guard.3 \ + doc/pcre2_set_depth_limit.3 \ + doc/pcre2_set_glob_escape.3 \ + doc/pcre2_set_glob_separator.3 \ + doc/pcre2_set_heap_limit.3 \ doc/pcre2_set_match_limit.3 \ + doc/pcre2_set_max_pattern_length.3 \ doc/pcre2_set_offset_limit.3 \ doc/pcre2_set_newline.3 \ doc/pcre2_set_parens_nest_limit.3 \ @@ -167,6 +194,7 @@ dist_man_MANS = \ doc/pcre2build.3 \ doc/pcre2callout.3 \ doc/pcre2compat.3 \ + doc/pcre2convert.3 \ doc/pcre2demo.3 \ doc/pcre2grep.1 \ doc/pcre2jit.3 \ @@ -178,7 +206,6 @@ dist_man_MANS = \ doc/pcre2posix.3 \ doc/pcre2sample.3 \ doc/pcre2serialize.3 \ - doc/pcre2stack.3 \ doc/pcre2syntax.3 \ doc/pcre2test.1 \ doc/pcre2unicode.3 @@ -204,7 +231,7 @@ noinst_PROGRAMS = # and 'make maintainer-clean'. CLEANFILES = -DISTCLEANFILES = src/config.h.in~ config.h +DISTCLEANFILES = src/config.h.in~ config.h pcre2.h.generic MAINTAINERCLEANFILES = # Additional files to bundle with the distribution, over and above what @@ -321,8 +348,10 @@ COMMON_SOURCES = \ src/pcre2_compile.c \ src/pcre2_config.c \ src/pcre2_context.c \ + src/pcre2_convert.c \ src/pcre2_dfa_match.c \ src/pcre2_error.c \ + src/pcre2_extuni.c \ src/pcre2_find_bracket.c \ src/pcre2_internal.h \ src/pcre2_intmodedep.h \ @@ -414,6 +443,7 @@ EXTRA_DIST += \ src/sljit/sljitNativeX86_32.c \ src/sljit/sljitNativeX86_64.c \ src/sljit/sljitNativeX86_common.c \ + src/sljit/sljitProtExecAllocator.c \ src/sljit/sljitUtils.c # Some of the JIT sources are also in separate files that are #included. @@ -471,7 +501,7 @@ libpcre2_posix_la_CFLAGS += $(GCOV_CFLAGS) endif # WITH_GCOV endif # WITH_PCRE2_8 -## Build pcre2grep if the 8-bit library is enabled +## Build pcre2grep and optional fuzzer stuff if the 8-bit library is enabled if WITH_PCRE2_8 bin_PROGRAMS += pcre2grep @@ -483,6 +513,22 @@ if WITH_GCOV pcre2grep_CFLAGS += $(GCOV_CFLAGS) pcre2grep_LDADD += $(GCOV_LIBS) endif # WITH_GCOV + +## If fuzzer support is enabled, build a non-distributed library containing the +## fuzzing function. Also build the standalone checking binary from the same +## source but using -DSTANDALONE. + +if WITH_FUZZ_SUPPORT +noinst_LIBRARIES = .libs/libpcre2-fuzzsupport.a +_libs_libpcre2_fuzzsupport_a_SOURCES = src/pcre2_fuzzsupport.c +_libs_libpcre2_fuzzsupport_a_CFLAGS = $(AM_CFLAGS) +_libs_libpcre2_fuzzsupport_a_LIBADD = + +noinst_PROGRAMS += pcre2fuzzcheck +pcre2fuzzcheck_SOURCES = src/pcre2_fuzzsupport.c +pcre2fuzzcheck_CFLAGS = -DSTANDALONE $(AM_CFLAGS) +pcre2fuzzcheck_LDADD = libpcre2-8.la +endif # WITH FUZZ_SUPPORT endif # WITH_PCRE2_8 ## -------- Testing ---------- @@ -543,17 +589,17 @@ endif # WITH_GCOV ## The main library tests. Each test is a binary plus a script that runs that ## binary in various ways. We install these test binaries in case folks find it -## helpful. +## helpful. The two .bat files are for running the tests under Windows. TESTS += RunTest -dist_noinst_SCRIPTS += RunTest - EXTRA_DIST += RunTest.bat +dist_noinst_SCRIPTS += RunTest ## When the 8-bit library is configured, pcre2grep will have been built. if WITH_PCRE2_8 TESTS += RunGrepTest +EXTRA_DIST += RunGrepTest.bat dist_noinst_SCRIPTS += RunGrepTest endif # WITH_PCRE2_8 @@ -565,6 +611,7 @@ EXTRA_DIST += \ testdata/grepinput \ testdata/grepinput3 \ testdata/grepinput8 \ + testdata/grepinputM \ testdata/grepinputv \ testdata/grepinputx \ testdata/greplist \ @@ -596,6 +643,8 @@ EXTRA_DIST += \ testdata/testinput21 \ testdata/testinput22 \ testdata/testinput23 \ + testdata/testinput24 \ + testdata/testinput25 \ testdata/testinputEBC \ testdata/testoutput1 \ testdata/testoutput2 \ @@ -608,7 +657,7 @@ EXTRA_DIST += \ testdata/testoutput7 \ testdata/testoutput8-16-2 \ testdata/testoutput8-16-3 \ - testdata/testoutput8-16-3 \ + testdata/testoutput8-16-4 \ testdata/testoutput8-32-2 \ testdata/testoutput8-32-3 \ testdata/testoutput8-32-4 \ @@ -636,6 +685,8 @@ EXTRA_DIST += \ testdata/testoutput22-32 \ testdata/testoutput22-8 \ testdata/testoutput23 \ + testdata/testoutput24 \ + testdata/testoutput25 \ testdata/testoutputEBC \ testdata/valgrind-jit.supp \ testdata/wintestinput3 \ diff --git a/pcre2-10.22/Makefile.in b/pcre2-10.32/Makefile.in similarity index 88% rename from pcre2-10.22/Makefile.in rename to pcre2-10.32/Makefile.in index 9aead2f54..597b17152 100644 --- a/pcre2-10.22/Makefile.in +++ b/pcre2-10.32/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -18,6 +18,7 @@ + VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ @@ -92,9 +93,9 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -TESTS = $(am__EXEEXT_3) RunTest $(am__append_29) +TESTS = $(am__EXEEXT_4) RunTest $(am__append_30) bin_PROGRAMS = $(am__EXEEXT_1) pcre2test$(EXEEXT) -noinst_PROGRAMS = $(am__EXEEXT_2) $(am__EXEEXT_3) +noinst_PROGRAMS = $(am__EXEEXT_2) $(am__EXEEXT_3) $(am__EXEEXT_4) @WITH_REBUILD_CHARTABLES_TRUE@am__append_1 = dftables @WITH_PCRE2_8_TRUE@am__append_2 = libpcre2-8.la @WITH_PCRE2_16_TRUE@am__append_3 = libpcre2-16.la @@ -110,25 +111,27 @@ noinst_PROGRAMS = $(am__EXEEXT_2) $(am__EXEEXT_3) @WITH_PCRE2_8_TRUE@am__append_13 = pcre2grep @WITH_GCOV_TRUE@@WITH_PCRE2_8_TRUE@am__append_14 = $(GCOV_CFLAGS) @WITH_GCOV_TRUE@@WITH_PCRE2_8_TRUE@am__append_15 = $(GCOV_LIBS) -@WITH_JIT_TRUE@am__append_16 = pcre2_jit_test +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@am__append_16 = pcre2fuzzcheck @WITH_JIT_TRUE@am__append_17 = pcre2_jit_test -@WITH_JIT_TRUE@@WITH_PCRE2_8_TRUE@am__append_18 = libpcre2-8.la -@WITH_JIT_TRUE@@WITH_PCRE2_16_TRUE@am__append_19 = libpcre2-16.la -@WITH_JIT_TRUE@@WITH_PCRE2_32_TRUE@am__append_20 = libpcre2-32.la -@WITH_GCOV_TRUE@@WITH_JIT_TRUE@am__append_21 = $(GCOV_CFLAGS) -@WITH_GCOV_TRUE@@WITH_JIT_TRUE@am__append_22 = $(GCOV_LIBS) -@WITH_PCRE2_8_TRUE@am__append_23 = libpcre2-8.la libpcre2-posix.la -@WITH_PCRE2_16_TRUE@am__append_24 = libpcre2-16.la -@WITH_PCRE2_32_TRUE@am__append_25 = libpcre2-32.la -@WITH_VALGRIND_TRUE@am__append_26 = $(VALGRIND_CFLAGS) -@WITH_GCOV_TRUE@am__append_27 = $(GCOV_CFLAGS) -@WITH_GCOV_TRUE@am__append_28 = $(GCOV_LIBS) -@WITH_PCRE2_8_TRUE@am__append_29 = RunGrepTest +@WITH_JIT_TRUE@am__append_18 = pcre2_jit_test +@WITH_JIT_TRUE@@WITH_PCRE2_8_TRUE@am__append_19 = libpcre2-8.la +@WITH_JIT_TRUE@@WITH_PCRE2_16_TRUE@am__append_20 = libpcre2-16.la +@WITH_JIT_TRUE@@WITH_PCRE2_32_TRUE@am__append_21 = libpcre2-32.la +@WITH_GCOV_TRUE@@WITH_JIT_TRUE@am__append_22 = $(GCOV_CFLAGS) +@WITH_GCOV_TRUE@@WITH_JIT_TRUE@am__append_23 = $(GCOV_LIBS) +@WITH_PCRE2_8_TRUE@am__append_24 = libpcre2-8.la libpcre2-posix.la +@WITH_PCRE2_16_TRUE@am__append_25 = libpcre2-16.la +@WITH_PCRE2_32_TRUE@am__append_26 = libpcre2-32.la +@WITH_VALGRIND_TRUE@am__append_27 = $(VALGRIND_CFLAGS) +@WITH_GCOV_TRUE@am__append_28 = $(GCOV_CFLAGS) +@WITH_GCOV_TRUE@am__append_29 = $(GCOV_LIBS) @WITH_PCRE2_8_TRUE@am__append_30 = RunGrepTest -@WITH_PCRE2_8_TRUE@am__append_31 = libpcre2-8.pc libpcre2-posix.pc -@WITH_PCRE2_16_TRUE@am__append_32 = libpcre2-16.pc -@WITH_PCRE2_32_TRUE@am__append_33 = libpcre2-32.pc -@WITH_GCOV_FALSE@am__append_34 = src/*.gcda src/*.gcno +@WITH_PCRE2_8_TRUE@am__append_31 = RunGrepTest.bat +@WITH_PCRE2_8_TRUE@am__append_32 = RunGrepTest +@WITH_PCRE2_8_TRUE@am__append_33 = libpcre2-8.pc libpcre2-posix.pc +@WITH_PCRE2_16_TRUE@am__append_34 = libpcre2-16.pc +@WITH_PCRE2_32_TRUE@am__append_35 = libpcre2-32.pc +@WITH_GCOV_FALSE@am__append_36 = src/*.gcda src/*.gcno subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ @@ -150,6 +153,20 @@ CONFIG_HEADER = $(top_builddir)/src/config.h CONFIG_CLEAN_FILES = libpcre2-8.pc libpcre2-16.pc libpcre2-32.pc \ libpcre2-posix.pc pcre2-config src/pcre2.h CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +_libs_libpcre2_fuzzsupport_a_AR = $(AR) $(ARFLAGS) +_libs_libpcre2_fuzzsupport_a_DEPENDENCIES = +am___libs_libpcre2_fuzzsupport_a_SOURCES_DIST = \ + src/pcre2_fuzzsupport.c +am__dirstamp = $(am__leading_dot)dirstamp +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@am__libs_libpcre2_fuzzsupport_a_OBJECTS = src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.$(OBJEXT) +_libs_libpcre2_fuzzsupport_a_OBJECTS = \ + $(am__libs_libpcre2_fuzzsupport_a_OBJECTS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -186,23 +203,24 @@ 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_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 \ - src/pcre2_pattern_info.c src/pcre2_serialize.c \ - src/pcre2_string_utils.c src/pcre2_study.c \ - src/pcre2_substitute.c src/pcre2_substring.c \ + src/pcre2_convert.c src/pcre2_dfa_match.c src/pcre2_error.c \ + src/pcre2_extuni.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 src/pcre2_pattern_info.c \ + src/pcre2_serialize.c src/pcre2_string_utils.c \ + src/pcre2_study.c src/pcre2_substitute.c src/pcre2_substring.c \ src/pcre2_tables.c src/pcre2_ucd.c src/pcre2_ucp.h \ src/pcre2_valid_utf.c src/pcre2_xclass.c -am__dirstamp = $(am__leading_dot)dirstamp am__objects_1 = src/libpcre2_16_la-pcre2_auto_possess.lo \ src/libpcre2_16_la-pcre2_compile.lo \ src/libpcre2_16_la-pcre2_config.lo \ src/libpcre2_16_la-pcre2_context.lo \ + src/libpcre2_16_la-pcre2_convert.lo \ src/libpcre2_16_la-pcre2_dfa_match.lo \ src/libpcre2_16_la-pcre2_error.lo \ + src/libpcre2_16_la-pcre2_extuni.lo \ src/libpcre2_16_la-pcre2_find_bracket.lo \ src/libpcre2_16_la-pcre2_jit_compile.lo \ src/libpcre2_16_la-pcre2_maketables.lo \ @@ -237,22 +255,24 @@ 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_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 \ - src/pcre2_pattern_info.c src/pcre2_serialize.c \ - src/pcre2_string_utils.c src/pcre2_study.c \ - src/pcre2_substitute.c src/pcre2_substring.c \ + src/pcre2_convert.c src/pcre2_dfa_match.c src/pcre2_error.c \ + src/pcre2_extuni.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 src/pcre2_pattern_info.c \ + src/pcre2_serialize.c src/pcre2_string_utils.c \ + src/pcre2_study.c src/pcre2_substitute.c src/pcre2_substring.c \ src/pcre2_tables.c src/pcre2_ucd.c src/pcre2_ucp.h \ src/pcre2_valid_utf.c src/pcre2_xclass.c am__objects_3 = src/libpcre2_32_la-pcre2_auto_possess.lo \ src/libpcre2_32_la-pcre2_compile.lo \ src/libpcre2_32_la-pcre2_config.lo \ src/libpcre2_32_la-pcre2_context.lo \ + src/libpcre2_32_la-pcre2_convert.lo \ src/libpcre2_32_la-pcre2_dfa_match.lo \ src/libpcre2_32_la-pcre2_error.lo \ + src/libpcre2_32_la-pcre2_extuni.lo \ src/libpcre2_32_la-pcre2_find_bracket.lo \ src/libpcre2_32_la-pcre2_jit_compile.lo \ src/libpcre2_32_la-pcre2_maketables.lo \ @@ -283,22 +303,24 @@ 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_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 \ - src/pcre2_pattern_info.c src/pcre2_serialize.c \ - src/pcre2_string_utils.c src/pcre2_study.c \ - src/pcre2_substitute.c src/pcre2_substring.c \ + src/pcre2_convert.c src/pcre2_dfa_match.c src/pcre2_error.c \ + src/pcre2_extuni.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 src/pcre2_pattern_info.c \ + src/pcre2_serialize.c src/pcre2_string_utils.c \ + src/pcre2_study.c src/pcre2_substitute.c src/pcre2_substring.c \ src/pcre2_tables.c src/pcre2_ucd.c src/pcre2_ucp.h \ src/pcre2_valid_utf.c src/pcre2_xclass.c am__objects_5 = src/libpcre2_8_la-pcre2_auto_possess.lo \ src/libpcre2_8_la-pcre2_compile.lo \ src/libpcre2_8_la-pcre2_config.lo \ src/libpcre2_8_la-pcre2_context.lo \ + src/libpcre2_8_la-pcre2_convert.lo \ src/libpcre2_8_la-pcre2_dfa_match.lo \ src/libpcre2_8_la-pcre2_error.lo \ + src/libpcre2_8_la-pcre2_extuni.lo \ src/libpcre2_8_la-pcre2_find_bracket.lo \ src/libpcre2_8_la-pcre2_jit_compile.lo \ src/libpcre2_8_la-pcre2_maketables.lo \ @@ -337,7 +359,8 @@ libpcre2_posix_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ @WITH_PCRE2_8_TRUE@am_libpcre2_posix_la_rpath = -rpath $(libdir) @WITH_PCRE2_8_TRUE@am__EXEEXT_1 = pcre2grep$(EXEEXT) @WITH_REBUILD_CHARTABLES_TRUE@am__EXEEXT_2 = dftables$(EXEEXT) -@WITH_JIT_TRUE@am__EXEEXT_3 = pcre2_jit_test$(EXEEXT) +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@am__EXEEXT_3 = pcre2fuzzcheck$(EXEEXT) +@WITH_JIT_TRUE@am__EXEEXT_4 = pcre2_jit_test$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) am__dftables_SOURCES_DIST = src/dftables.c @WITH_REBUILD_CHARTABLES_TRUE@am_dftables_OBJECTS = \ @@ -351,13 +374,22 @@ pcre2_jit_test_OBJECTS = $(am_pcre2_jit_test_OBJECTS) am__DEPENDENCIES_1 = @WITH_GCOV_TRUE@@WITH_JIT_TRUE@am__DEPENDENCIES_2 = \ @WITH_GCOV_TRUE@@WITH_JIT_TRUE@ $(am__DEPENDENCIES_1) -@WITH_JIT_TRUE@pcre2_jit_test_DEPENDENCIES = $(am__append_18) \ -@WITH_JIT_TRUE@ $(am__append_19) $(am__append_20) \ +@WITH_JIT_TRUE@pcre2_jit_test_DEPENDENCIES = $(am__append_19) \ +@WITH_JIT_TRUE@ $(am__append_20) $(am__append_21) \ @WITH_JIT_TRUE@ $(am__DEPENDENCIES_2) pcre2_jit_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(pcre2_jit_test_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o \ $@ +am__pcre2fuzzcheck_SOURCES_DIST = src/pcre2_fuzzsupport.c +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@am_pcre2fuzzcheck_OBJECTS = src/pcre2fuzzcheck-pcre2_fuzzsupport.$(OBJEXT) +pcre2fuzzcheck_OBJECTS = $(am_pcre2fuzzcheck_OBJECTS) +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@pcre2fuzzcheck_DEPENDENCIES = \ +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@ libpcre2-8.la +pcre2fuzzcheck_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(pcre2fuzzcheck_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o \ + $@ am__pcre2grep_SOURCES_DIST = src/pcre2grep.c @WITH_PCRE2_8_TRUE@am_pcre2grep_OBJECTS = \ @WITH_PCRE2_8_TRUE@ src/pcre2grep-pcre2grep.$(OBJEXT) @@ -373,8 +405,8 @@ pcre2grep_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ am_pcre2test_OBJECTS = src/pcre2test-pcre2test.$(OBJEXT) pcre2test_OBJECTS = $(am_pcre2test_OBJECTS) @WITH_GCOV_TRUE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1) -pcre2test_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__append_23) \ - $(am__append_24) $(am__append_25) $(am__DEPENDENCIES_4) +pcre2test_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__append_24) \ + $(am__append_25) $(am__append_26) $(am__DEPENDENCIES_4) pcre2test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(pcre2test_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ @@ -414,18 +446,21 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = -SOURCES = $(libpcre2_16_la_SOURCES) $(nodist_libpcre2_16_la_SOURCES) \ +SOURCES = $(_libs_libpcre2_fuzzsupport_a_SOURCES) \ + $(libpcre2_16_la_SOURCES) $(nodist_libpcre2_16_la_SOURCES) \ $(libpcre2_32_la_SOURCES) $(nodist_libpcre2_32_la_SOURCES) \ $(libpcre2_8_la_SOURCES) $(nodist_libpcre2_8_la_SOURCES) \ $(libpcre2_posix_la_SOURCES) $(dftables_SOURCES) \ - $(pcre2_jit_test_SOURCES) $(pcre2grep_SOURCES) \ - $(pcre2test_SOURCES) -DIST_SOURCES = $(am__libpcre2_16_la_SOURCES_DIST) \ + $(pcre2_jit_test_SOURCES) $(pcre2fuzzcheck_SOURCES) \ + $(pcre2grep_SOURCES) $(pcre2test_SOURCES) +DIST_SOURCES = $(am___libs_libpcre2_fuzzsupport_a_SOURCES_DIST) \ + $(am__libpcre2_16_la_SOURCES_DIST) \ $(am__libpcre2_32_la_SOURCES_DIST) \ $(am__libpcre2_8_la_SOURCES_DIST) \ $(am__libpcre2_posix_la_SOURCES_DIST) \ $(am__dftables_SOURCES_DIST) \ $(am__pcre2_jit_test_SOURCES_DIST) \ + $(am__pcre2fuzzcheck_SOURCES_DIST) \ $(am__pcre2grep_SOURCES_DIST) $(pcre2test_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ @@ -714,7 +749,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@ @@ -733,6 +767,8 @@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PCRE2_DATE = @PCRE2_DATE@ +PCRE2_HAVE_INTTYPES_H = @PCRE2_HAVE_INTTYPES_H@ +PCRE2_HAVE_STDINT_H = @PCRE2_HAVE_STDINT_H@ PCRE2_MAJOR = @PCRE2_MAJOR@ PCRE2_MINOR = @PCRE2_MINOR@ PCRE2_PRERELEASE = @PCRE2_PRERELEASE@ @@ -812,7 +848,7 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = subdir-objects ACLOCAL_AMFLAGS = -I m4 -AM_CPPFLAGS = -I$(builddir)/src -I$(srcdir)/src +AM_CPPFLAGS = "-I$(srcdir)/src" dist_doc_DATA = \ AUTHORS \ COPYING \ @@ -833,12 +869,17 @@ dist_html_DATA = \ doc/html/pcre2.html \ doc/html/pcre2_callout_enumerate.html \ doc/html/pcre2_code_copy.html \ + doc/html/pcre2_code_copy_with_tables.html \ doc/html/pcre2_code_free.html \ doc/html/pcre2_compile.html \ doc/html/pcre2_compile_context_copy.html \ doc/html/pcre2_compile_context_create.html \ doc/html/pcre2_compile_context_free.html \ doc/html/pcre2_config.html \ + doc/html/pcre2_convert_context_copy.html \ + doc/html/pcre2_convert_context_create.html \ + doc/html/pcre2_convert_context_free.html \ + doc/html/pcre2_converted_pattern_free.html \ doc/html/pcre2_dfa_match.html \ doc/html/pcre2_general_context_copy.html \ doc/html/pcre2_general_context_create.html \ @@ -862,6 +903,7 @@ dist_html_DATA = \ doc/html/pcre2_match_data_create.html \ doc/html/pcre2_match_data_create_from_pattern.html \ doc/html/pcre2_match_data_free.html \ + doc/html/pcre2_pattern_convert.html \ doc/html/pcre2_pattern_info.html \ doc/html/pcre2_serialize_decode.html \ doc/html/pcre2_serialize_encode.html \ @@ -870,8 +912,14 @@ dist_html_DATA = \ doc/html/pcre2_set_bsr.html \ doc/html/pcre2_set_callout.html \ doc/html/pcre2_set_character_tables.html \ + doc/html/pcre2_set_compile_extra_options.html \ doc/html/pcre2_set_compile_recursion_guard.html \ + doc/html/pcre2_set_depth_limit.html \ + doc/html/pcre2_set_glob_escape.html \ + doc/html/pcre2_set_glob_separator.html \ + doc/html/pcre2_set_heap_limit.html \ doc/html/pcre2_set_match_limit.html \ + doc/html/pcre2_set_max_pattern_length.html \ doc/html/pcre2_set_offset_limit.html \ doc/html/pcre2_set_newline.html \ doc/html/pcre2_set_parens_nest_limit.html \ @@ -893,6 +941,7 @@ dist_html_DATA = \ doc/html/pcre2build.html \ doc/html/pcre2callout.html \ doc/html/pcre2compat.html \ + doc/html/pcre2convert.html \ doc/html/pcre2demo.html \ doc/html/pcre2grep.html \ doc/html/pcre2jit.html \ @@ -904,7 +953,6 @@ dist_html_DATA = \ doc/html/pcre2posix.html \ doc/html/pcre2sample.html \ doc/html/pcre2serialize.html \ - doc/html/pcre2stack.html \ doc/html/pcre2syntax.html \ doc/html/pcre2test.html \ doc/html/pcre2unicode.html @@ -914,12 +962,17 @@ dist_man_MANS = \ doc/pcre2.3 \ doc/pcre2_callout_enumerate.3 \ doc/pcre2_code_copy.3 \ + doc/pcre2_code_copy_with_tables.3 \ doc/pcre2_code_free.3 \ doc/pcre2_compile.3 \ doc/pcre2_compile_context_copy.3 \ doc/pcre2_compile_context_create.3 \ doc/pcre2_compile_context_free.3 \ doc/pcre2_config.3 \ + doc/pcre2_convert_context_copy.3 \ + doc/pcre2_convert_context_create.3 \ + doc/pcre2_convert_context_free.3 \ + doc/pcre2_converted_pattern_free.3 \ doc/pcre2_dfa_match.3 \ doc/pcre2_general_context_copy.3 \ doc/pcre2_general_context_create.3 \ @@ -943,6 +996,7 @@ dist_man_MANS = \ doc/pcre2_match_data_create.3 \ doc/pcre2_match_data_create_from_pattern.3 \ doc/pcre2_match_data_free.3 \ + doc/pcre2_pattern_convert.3 \ doc/pcre2_pattern_info.3 \ doc/pcre2_serialize_decode.3 \ doc/pcre2_serialize_encode.3 \ @@ -951,8 +1005,14 @@ dist_man_MANS = \ doc/pcre2_set_bsr.3 \ doc/pcre2_set_callout.3 \ doc/pcre2_set_character_tables.3 \ + doc/pcre2_set_compile_extra_options.3 \ doc/pcre2_set_compile_recursion_guard.3 \ + doc/pcre2_set_depth_limit.3 \ + doc/pcre2_set_glob_escape.3 \ + doc/pcre2_set_glob_separator.3 \ + doc/pcre2_set_heap_limit.3 \ doc/pcre2_set_match_limit.3 \ + doc/pcre2_set_max_pattern_length.3 \ doc/pcre2_set_offset_limit.3 \ doc/pcre2_set_newline.3 \ doc/pcre2_set_parens_nest_limit.3 \ @@ -974,6 +1034,7 @@ dist_man_MANS = \ doc/pcre2build.3 \ doc/pcre2callout.3 \ doc/pcre2compat.3 \ + doc/pcre2convert.3 \ doc/pcre2demo.3 \ doc/pcre2grep.1 \ doc/pcre2jit.3 \ @@ -985,7 +1046,6 @@ dist_man_MANS = \ doc/pcre2posix.3 \ doc/pcre2sample.3 \ doc/pcre2serialize.3 \ - doc/pcre2stack.3 \ doc/pcre2syntax.3 \ doc/pcre2test.1 \ doc/pcre2unicode.3 @@ -995,7 +1055,7 @@ dist_man_MANS = \ lib_LTLIBRARIES = $(am__append_2) $(am__append_3) $(am__append_4) \ $(am__append_11) check_SCRIPTS = -dist_noinst_SCRIPTS = RunTest $(am__append_30) +dist_noinst_SCRIPTS = RunTest $(am__append_32) # Additional files to delete on 'make clean', 'make distclean', # and 'make maintainer-clean'. @@ -1006,7 +1066,8 @@ CLEANFILES = src/pcre2_chartables.c testSinput test3input test3output \ test3outputA test3outputB testtry teststdout teststderr \ teststderrgrep testtemp1grep testtemp2grep testtrygrep \ testNinputgrep -DISTCLEANFILES = src/config.h.in~ config.h $(am__append_34) +DISTCLEANFILES = src/config.h.in~ config.h pcre2.h.generic \ + $(am__append_36) MAINTAINERCLEANFILES = src/pcre2.h.generic src/config.h.generic # Additional files to bundle with the distribution, over and above what @@ -1049,27 +1110,29 @@ EXTRA_DIST = m4/ax_pthread.m4 m4/pcre2_visibility.m4 \ src/sljit/sljitNativeTILEGX-encoder.c \ src/sljit/sljitNativeTILEGX_64.c src/sljit/sljitNativeX86_32.c \ src/sljit/sljitNativeX86_64.c \ - src/sljit/sljitNativeX86_common.c src/sljit/sljitUtils.c \ + src/sljit/sljitNativeX86_common.c \ + src/sljit/sljitProtExecAllocator.c src/sljit/sljitUtils.c \ src/pcre2_jit_match.c src/pcre2_jit_misc.c \ - src/pcre2_printint.c RunTest.bat testdata/grepbinary \ - testdata/grepfilelist testdata/grepinput testdata/grepinput3 \ - testdata/grepinput8 testdata/grepinputv testdata/grepinputx \ - testdata/greplist testdata/grepoutput testdata/grepoutput8 \ - testdata/grepoutputC testdata/grepoutputN testdata/greppatN4 \ - testdata/testinput1 testdata/testinput2 testdata/testinput3 \ - testdata/testinput4 testdata/testinput5 testdata/testinput6 \ - testdata/testinput7 testdata/testinput8 testdata/testinput9 \ - testdata/testinput10 testdata/testinput11 testdata/testinput12 \ - testdata/testinput13 testdata/testinput14 testdata/testinput15 \ - testdata/testinput16 testdata/testinput17 testdata/testinput18 \ - testdata/testinput19 testdata/testinput20 testdata/testinput21 \ - testdata/testinput22 testdata/testinput23 \ + src/pcre2_printint.c RunTest.bat $(am__append_31) \ + testdata/grepbinary testdata/grepfilelist testdata/grepinput \ + testdata/grepinput3 testdata/grepinput8 testdata/grepinputM \ + testdata/grepinputv testdata/grepinputx testdata/greplist \ + testdata/grepoutput testdata/grepoutput8 testdata/grepoutputC \ + testdata/grepoutputN testdata/greppatN4 testdata/testinput1 \ + testdata/testinput2 testdata/testinput3 testdata/testinput4 \ + testdata/testinput5 testdata/testinput6 testdata/testinput7 \ + testdata/testinput8 testdata/testinput9 testdata/testinput10 \ + testdata/testinput11 testdata/testinput12 testdata/testinput13 \ + testdata/testinput14 testdata/testinput15 testdata/testinput16 \ + testdata/testinput17 testdata/testinput18 testdata/testinput19 \ + testdata/testinput20 testdata/testinput21 testdata/testinput22 \ + testdata/testinput23 testdata/testinput24 testdata/testinput25 \ testdata/testinputEBC testdata/testoutput1 \ testdata/testoutput2 testdata/testoutput3 \ testdata/testoutput3A testdata/testoutput3B \ testdata/testoutput4 testdata/testoutput5 testdata/testoutput6 \ testdata/testoutput7 testdata/testoutput8-16-2 \ - testdata/testoutput8-16-3 testdata/testoutput8-16-3 \ + testdata/testoutput8-16-3 testdata/testoutput8-16-4 \ testdata/testoutput8-32-2 testdata/testoutput8-32-3 \ testdata/testoutput8-32-4 testdata/testoutput8-8-2 \ testdata/testoutput8-8-3 testdata/testoutput8-8-4 \ @@ -1083,7 +1146,8 @@ EXTRA_DIST = m4/ax_pthread.m4 m4/pcre2_visibility.m4 \ testdata/testoutput19 testdata/testoutput20 \ testdata/testoutput21 testdata/testoutput22-16 \ testdata/testoutput22-32 testdata/testoutput22-8 \ - testdata/testoutput23 testdata/testoutputEBC \ + testdata/testoutput23 testdata/testoutput24 \ + testdata/testoutput25 testdata/testoutputEBC \ testdata/valgrind-jit.supp testdata/wintestinput3 \ testdata/wintestoutput3 perltest.sh src/pcre2demo.c \ cmake/COPYING-CMAKE-SCRIPTS \ @@ -1106,8 +1170,10 @@ COMMON_SOURCES = \ src/pcre2_compile.c \ src/pcre2_config.c \ src/pcre2_context.c \ + src/pcre2_convert.c \ src/pcre2_dfa_match.c \ src/pcre2_error.c \ + src/pcre2_extuni.c \ src/pcre2_find_bracket.c \ src/pcre2_internal.h \ src/pcre2_intmodedep.h \ @@ -1175,19 +1241,26 @@ COMMON_SOURCES = \ @WITH_PCRE2_8_TRUE@pcre2grep_CFLAGS = $(AM_CFLAGS) $(am__append_14) @WITH_PCRE2_8_TRUE@pcre2grep_LDADD = $(LIBZ) $(LIBBZ2) libpcre2-8.la \ @WITH_PCRE2_8_TRUE@ $(am__append_15) +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@noinst_LIBRARIES = .libs/libpcre2-fuzzsupport.a +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@_libs_libpcre2_fuzzsupport_a_SOURCES = src/pcre2_fuzzsupport.c +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@_libs_libpcre2_fuzzsupport_a_CFLAGS = $(AM_CFLAGS) +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@_libs_libpcre2_fuzzsupport_a_LIBADD = +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@pcre2fuzzcheck_SOURCES = src/pcre2_fuzzsupport.c +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@pcre2fuzzcheck_CFLAGS = -DSTANDALONE $(AM_CFLAGS) +@WITH_FUZZ_SUPPORT_TRUE@@WITH_PCRE2_8_TRUE@pcre2fuzzcheck_LDADD = libpcre2-8.la @WITH_JIT_TRUE@pcre2_jit_test_SOURCES = src/pcre2_jit_test.c -@WITH_JIT_TRUE@pcre2_jit_test_CFLAGS = $(AM_CFLAGS) $(am__append_21) -@WITH_JIT_TRUE@pcre2_jit_test_LDADD = $(am__append_18) \ -@WITH_JIT_TRUE@ $(am__append_19) $(am__append_20) \ -@WITH_JIT_TRUE@ $(am__append_22) +@WITH_JIT_TRUE@pcre2_jit_test_CFLAGS = $(AM_CFLAGS) $(am__append_22) +@WITH_JIT_TRUE@pcre2_jit_test_LDADD = $(am__append_19) \ +@WITH_JIT_TRUE@ $(am__append_20) $(am__append_21) \ +@WITH_JIT_TRUE@ $(am__append_23) pcre2test_SOURCES = src/pcre2test.c -pcre2test_CFLAGS = $(AM_CFLAGS) $(am__append_26) $(am__append_27) -pcre2test_LDADD = $(LIBREADLINE) $(am__append_23) $(am__append_24) \ - $(am__append_25) $(am__append_28) +pcre2test_CFLAGS = $(AM_CFLAGS) $(am__append_27) $(am__append_28) +pcre2test_LDADD = $(LIBREADLINE) $(am__append_24) $(am__append_25) \ + $(am__append_26) $(am__append_29) # We have .pc files for pkg-config users. pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = $(am__append_31) $(am__append_32) $(am__append_33) +pkgconfig_DATA = $(am__append_33) $(am__append_34) $(am__append_35) # gcov/lcov code coverage reporting # @@ -1219,7 +1292,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*) \ @@ -1245,9 +1318,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): @@ -1258,7 +1331,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 $@ @@ -1278,6 +1351,25 @@ pcre2-config: $(top_builddir)/config.status $(srcdir)/pcre2-config.in src/pcre2.h: $(top_builddir)/config.status $(top_srcdir)/src/pcre2.h.in cd $(top_builddir) && $(SHELL) ./config.status $@ +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +src/$(am__dirstamp): + @$(MKDIR_P) src + @: > src/$(am__dirstamp) +src/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/$(DEPDIR) + @: > src/$(DEPDIR)/$(am__dirstamp) +src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.$(OBJEXT): \ + src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) +.libs/$(am__dirstamp): + @$(MKDIR_P) .libs + @: > .libs/$(am__dirstamp) + +.libs/libpcre2-fuzzsupport.a: $(_libs_libpcre2_fuzzsupport_a_OBJECTS) $(_libs_libpcre2_fuzzsupport_a_DEPENDENCIES) $(EXTRA__libs_libpcre2_fuzzsupport_a_DEPENDENCIES) .libs/$(am__dirstamp) + $(AM_V_at)-rm -f .libs/libpcre2-fuzzsupport.a + $(AM_V_AR)$(_libs_libpcre2_fuzzsupport_a_AR) .libs/libpcre2-fuzzsupport.a $(_libs_libpcre2_fuzzsupport_a_OBJECTS) $(_libs_libpcre2_fuzzsupport_a_LIBADD) + $(AM_V_at)$(RANLIB) .libs/libpcre2-fuzzsupport.a + install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ @@ -1312,12 +1404,6 @@ clean-libLTLIBRARIES: echo rm -f $${locs}; \ rm -f $${locs}; \ } -src/$(am__dirstamp): - @$(MKDIR_P) src - @: > src/$(am__dirstamp) -src/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) src/$(DEPDIR) - @: > src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_16_la-pcre2_auto_possess.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_16_la-pcre2_compile.lo: src/$(am__dirstamp) \ @@ -1326,10 +1412,14 @@ src/libpcre2_16_la-pcre2_config.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_16_la-pcre2_context.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) +src/libpcre2_16_la-pcre2_convert.lo: src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) 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_extuni.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) \ @@ -1377,10 +1467,14 @@ src/libpcre2_32_la-pcre2_config.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_32_la-pcre2_context.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) +src/libpcre2_32_la-pcre2_convert.lo: src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) 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_extuni.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) \ @@ -1428,10 +1522,14 @@ src/libpcre2_8_la-pcre2_config.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/libpcre2_8_la-pcre2_context.lo: src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) +src/libpcre2_8_la-pcre2_convert.lo: src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) 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_extuni.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) \ @@ -1546,6 +1644,12 @@ src/pcre2_jit_test-pcre2_jit_test.$(OBJEXT): src/$(am__dirstamp) \ pcre2_jit_test$(EXEEXT): $(pcre2_jit_test_OBJECTS) $(pcre2_jit_test_DEPENDENCIES) $(EXTRA_pcre2_jit_test_DEPENDENCIES) @rm -f pcre2_jit_test$(EXEEXT) $(AM_V_CCLD)$(pcre2_jit_test_LINK) $(pcre2_jit_test_OBJECTS) $(pcre2_jit_test_LDADD) $(LIBS) +src/pcre2fuzzcheck-pcre2_fuzzsupport.$(OBJEXT): src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) + +pcre2fuzzcheck$(EXEEXT): $(pcre2fuzzcheck_OBJECTS) $(pcre2fuzzcheck_DEPENDENCIES) $(EXTRA_pcre2fuzzcheck_DEPENDENCIES) + @rm -f pcre2fuzzcheck$(EXEEXT) + $(AM_V_CCLD)$(pcre2fuzzcheck_LINK) $(pcre2fuzzcheck_OBJECTS) $(pcre2fuzzcheck_LDADD) $(LIBS) src/pcre2grep-pcre2grep.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) @@ -1602,14 +1706,17 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/dftables.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_auto_possess.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_chartables.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_compile.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_16_la-pcre2_config.Plo@am__quote@ @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_convert.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_extuni.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@ @@ -1632,8 +1739,10 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_32_la-pcre2_compile.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_32_la-pcre2_config.Plo@am__quote@ @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_convert.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_extuni.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@ @@ -1656,8 +1765,10 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_8_la-pcre2_compile.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_8_la-pcre2_config.Plo@am__quote@ @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_convert.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_extuni.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@ @@ -1677,6 +1788,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_8_la-pcre2_xclass.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/libpcre2_posix_la-pcre2posix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/pcre2_jit_test-pcre2_jit_test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/pcre2fuzzcheck-pcre2_fuzzsupport.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/pcre2grep-pcre2grep.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/pcre2test-pcre2test.Po@am__quote@ @@ -1704,6 +1816,20 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< +src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.o: src/pcre2_fuzzsupport.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(_libs_libpcre2_fuzzsupport_a_CFLAGS) $(CFLAGS) -MT src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.o -MD -MP -MF src/$(DEPDIR)/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.Tpo -c -o src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.o `test -f 'src/pcre2_fuzzsupport.c' || echo '$(srcdir)/'`src/pcre2_fuzzsupport.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.Tpo src/$(DEPDIR)/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_fuzzsupport.c' object='src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(_libs_libpcre2_fuzzsupport_a_CFLAGS) $(CFLAGS) -c -o src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.o `test -f 'src/pcre2_fuzzsupport.c' || echo '$(srcdir)/'`src/pcre2_fuzzsupport.c + +src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.obj: src/pcre2_fuzzsupport.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(_libs_libpcre2_fuzzsupport_a_CFLAGS) $(CFLAGS) -MT src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.obj -MD -MP -MF src/$(DEPDIR)/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.Tpo -c -o src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.obj `if test -f 'src/pcre2_fuzzsupport.c'; then $(CYGPATH_W) 'src/pcre2_fuzzsupport.c'; else $(CYGPATH_W) '$(srcdir)/src/pcre2_fuzzsupport.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.Tpo src/$(DEPDIR)/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_fuzzsupport.c' object='src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(_libs_libpcre2_fuzzsupport_a_CFLAGS) $(CFLAGS) -c -o src/_libs_libpcre2_fuzzsupport_a-pcre2_fuzzsupport.obj `if test -f 'src/pcre2_fuzzsupport.c'; then $(CYGPATH_W) 'src/pcre2_fuzzsupport.c'; else $(CYGPATH_W) '$(srcdir)/src/pcre2_fuzzsupport.c'; fi` + src/libpcre2_16_la-pcre2_auto_possess.lo: src/pcre2_auto_possess.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_auto_possess.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_16_la-pcre2_auto_possess.Tpo -c -o src/libpcre2_16_la-pcre2_auto_possess.lo `test -f 'src/pcre2_auto_possess.c' || echo '$(srcdir)/'`src/pcre2_auto_possess.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_16_la-pcre2_auto_possess.Tpo src/$(DEPDIR)/libpcre2_16_la-pcre2_auto_possess.Plo @@ -1732,6 +1858,13 @@ src/libpcre2_16_la-pcre2_context.lo: src/pcre2_context.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_context.lo `test -f 'src/pcre2_context.c' || echo '$(srcdir)/'`src/pcre2_context.c +src/libpcre2_16_la-pcre2_convert.lo: src/pcre2_convert.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_convert.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_16_la-pcre2_convert.Tpo -c -o src/libpcre2_16_la-pcre2_convert.lo `test -f 'src/pcre2_convert.c' || echo '$(srcdir)/'`src/pcre2_convert.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_16_la-pcre2_convert.Tpo src/$(DEPDIR)/libpcre2_16_la-pcre2_convert.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_convert.c' object='src/libpcre2_16_la-pcre2_convert.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_convert.lo `test -f 'src/pcre2_convert.c' || echo '$(srcdir)/'`src/pcre2_convert.c + src/libpcre2_16_la-pcre2_dfa_match.lo: src/pcre2_dfa_match.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_dfa_match.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_16_la-pcre2_dfa_match.Tpo -c -o src/libpcre2_16_la-pcre2_dfa_match.lo `test -f 'src/pcre2_dfa_match.c' || echo '$(srcdir)/'`src/pcre2_dfa_match.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_16_la-pcre2_dfa_match.Tpo src/$(DEPDIR)/libpcre2_16_la-pcre2_dfa_match.Plo @@ -1746,6 +1879,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_extuni.lo: src/pcre2_extuni.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_extuni.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_16_la-pcre2_extuni.Tpo -c -o src/libpcre2_16_la-pcre2_extuni.lo `test -f 'src/pcre2_extuni.c' || echo '$(srcdir)/'`src/pcre2_extuni.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_16_la-pcre2_extuni.Tpo src/$(DEPDIR)/libpcre2_16_la-pcre2_extuni.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_extuni.c' object='src/libpcre2_16_la-pcre2_extuni.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_extuni.lo `test -f 'src/pcre2_extuni.c' || echo '$(srcdir)/'`src/pcre2_extuni.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 @@ -1900,6 +2040,13 @@ src/libpcre2_32_la-pcre2_context.lo: src/pcre2_context.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_context.lo `test -f 'src/pcre2_context.c' || echo '$(srcdir)/'`src/pcre2_context.c +src/libpcre2_32_la-pcre2_convert.lo: src/pcre2_convert.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_convert.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_32_la-pcre2_convert.Tpo -c -o src/libpcre2_32_la-pcre2_convert.lo `test -f 'src/pcre2_convert.c' || echo '$(srcdir)/'`src/pcre2_convert.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_32_la-pcre2_convert.Tpo src/$(DEPDIR)/libpcre2_32_la-pcre2_convert.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_convert.c' object='src/libpcre2_32_la-pcre2_convert.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_convert.lo `test -f 'src/pcre2_convert.c' || echo '$(srcdir)/'`src/pcre2_convert.c + src/libpcre2_32_la-pcre2_dfa_match.lo: src/pcre2_dfa_match.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_dfa_match.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_32_la-pcre2_dfa_match.Tpo -c -o src/libpcre2_32_la-pcre2_dfa_match.lo `test -f 'src/pcre2_dfa_match.c' || echo '$(srcdir)/'`src/pcre2_dfa_match.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_32_la-pcre2_dfa_match.Tpo src/$(DEPDIR)/libpcre2_32_la-pcre2_dfa_match.Plo @@ -1914,6 +2061,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_extuni.lo: src/pcre2_extuni.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_extuni.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_32_la-pcre2_extuni.Tpo -c -o src/libpcre2_32_la-pcre2_extuni.lo `test -f 'src/pcre2_extuni.c' || echo '$(srcdir)/'`src/pcre2_extuni.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_32_la-pcre2_extuni.Tpo src/$(DEPDIR)/libpcre2_32_la-pcre2_extuni.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_extuni.c' object='src/libpcre2_32_la-pcre2_extuni.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_extuni.lo `test -f 'src/pcre2_extuni.c' || echo '$(srcdir)/'`src/pcre2_extuni.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 @@ -2068,6 +2222,13 @@ src/libpcre2_8_la-pcre2_context.lo: src/pcre2_context.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_context.lo `test -f 'src/pcre2_context.c' || echo '$(srcdir)/'`src/pcre2_context.c +src/libpcre2_8_la-pcre2_convert.lo: src/pcre2_convert.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_convert.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_8_la-pcre2_convert.Tpo -c -o src/libpcre2_8_la-pcre2_convert.lo `test -f 'src/pcre2_convert.c' || echo '$(srcdir)/'`src/pcre2_convert.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_8_la-pcre2_convert.Tpo src/$(DEPDIR)/libpcre2_8_la-pcre2_convert.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_convert.c' object='src/libpcre2_8_la-pcre2_convert.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_convert.lo `test -f 'src/pcre2_convert.c' || echo '$(srcdir)/'`src/pcre2_convert.c + src/libpcre2_8_la-pcre2_dfa_match.lo: src/pcre2_dfa_match.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_dfa_match.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_8_la-pcre2_dfa_match.Tpo -c -o src/libpcre2_8_la-pcre2_dfa_match.lo `test -f 'src/pcre2_dfa_match.c' || echo '$(srcdir)/'`src/pcre2_dfa_match.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_8_la-pcre2_dfa_match.Tpo src/$(DEPDIR)/libpcre2_8_la-pcre2_dfa_match.Plo @@ -2082,6 +2243,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_extuni.lo: src/pcre2_extuni.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_extuni.lo -MD -MP -MF src/$(DEPDIR)/libpcre2_8_la-pcre2_extuni.Tpo -c -o src/libpcre2_8_la-pcre2_extuni.lo `test -f 'src/pcre2_extuni.c' || echo '$(srcdir)/'`src/pcre2_extuni.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/libpcre2_8_la-pcre2_extuni.Tpo src/$(DEPDIR)/libpcre2_8_la-pcre2_extuni.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_extuni.c' object='src/libpcre2_8_la-pcre2_extuni.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_extuni.lo `test -f 'src/pcre2_extuni.c' || echo '$(srcdir)/'`src/pcre2_extuni.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 @@ -2229,6 +2397,20 @@ src/pcre2_jit_test-pcre2_jit_test.obj: src/pcre2_jit_test.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcre2_jit_test_CFLAGS) $(CFLAGS) -c -o src/pcre2_jit_test-pcre2_jit_test.obj `if test -f 'src/pcre2_jit_test.c'; then $(CYGPATH_W) 'src/pcre2_jit_test.c'; else $(CYGPATH_W) '$(srcdir)/src/pcre2_jit_test.c'; fi` +src/pcre2fuzzcheck-pcre2_fuzzsupport.o: src/pcre2_fuzzsupport.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcre2fuzzcheck_CFLAGS) $(CFLAGS) -MT src/pcre2fuzzcheck-pcre2_fuzzsupport.o -MD -MP -MF src/$(DEPDIR)/pcre2fuzzcheck-pcre2_fuzzsupport.Tpo -c -o src/pcre2fuzzcheck-pcre2_fuzzsupport.o `test -f 'src/pcre2_fuzzsupport.c' || echo '$(srcdir)/'`src/pcre2_fuzzsupport.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/pcre2fuzzcheck-pcre2_fuzzsupport.Tpo src/$(DEPDIR)/pcre2fuzzcheck-pcre2_fuzzsupport.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_fuzzsupport.c' object='src/pcre2fuzzcheck-pcre2_fuzzsupport.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcre2fuzzcheck_CFLAGS) $(CFLAGS) -c -o src/pcre2fuzzcheck-pcre2_fuzzsupport.o `test -f 'src/pcre2_fuzzsupport.c' || echo '$(srcdir)/'`src/pcre2_fuzzsupport.c + +src/pcre2fuzzcheck-pcre2_fuzzsupport.obj: src/pcre2_fuzzsupport.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcre2fuzzcheck_CFLAGS) $(CFLAGS) -MT src/pcre2fuzzcheck-pcre2_fuzzsupport.obj -MD -MP -MF src/$(DEPDIR)/pcre2fuzzcheck-pcre2_fuzzsupport.Tpo -c -o src/pcre2fuzzcheck-pcre2_fuzzsupport.obj `if test -f 'src/pcre2_fuzzsupport.c'; then $(CYGPATH_W) 'src/pcre2_fuzzsupport.c'; else $(CYGPATH_W) '$(srcdir)/src/pcre2_fuzzsupport.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/pcre2fuzzcheck-pcre2_fuzzsupport.Tpo src/$(DEPDIR)/pcre2fuzzcheck-pcre2_fuzzsupport.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/pcre2_fuzzsupport.c' object='src/pcre2fuzzcheck-pcre2_fuzzsupport.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcre2fuzzcheck_CFLAGS) $(CFLAGS) -c -o src/pcre2fuzzcheck-pcre2_fuzzsupport.obj `if test -f 'src/pcre2_fuzzsupport.c'; then $(CYGPATH_W) 'src/pcre2_fuzzsupport.c'; else $(CYGPATH_W) '$(srcdir)/src/pcre2_fuzzsupport.c'; fi` + src/pcre2grep-pcre2grep.o: src/pcre2grep.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(pcre2grep_CFLAGS) $(CFLAGS) -MT src/pcre2grep-pcre2grep.o -MD -MP -MF src/$(DEPDIR)/pcre2grep-pcre2grep.Tpo -c -o src/pcre2grep-pcre2grep.o `test -f 'src/pcre2grep.c' || echo '$(srcdir)/'`src/pcre2grep.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/$(DEPDIR)/pcre2grep-pcre2grep.Tpo src/$(DEPDIR)/pcre2grep-pcre2grep.Po @@ -2734,7 +2916,7 @@ distdir: $(DISTFILES) ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir - tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 @@ -2759,7 +2941,7 @@ dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 - shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip @@ -2776,7 +2958,7 @@ dist dist-all: distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ - GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ @@ -2786,7 +2968,7 @@ distcheck: dist *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ - GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac @@ -2860,8 +3042,8 @@ check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am -all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) $(MANS) $(DATA) \ - $(HEADERS) +all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) \ + $(MANS) $(DATA) $(HEADERS) install-binPROGRAMS: install-libLTLIBRARIES installdirs: @@ -2899,6 +3081,7 @@ clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f .libs/$(am__dirstamp) -rm -f src/$(DEPDIR)/$(am__dirstamp) -rm -f src/$(am__dirstamp) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) @@ -2912,7 +3095,8 @@ maintainer-clean-generic: clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ - clean-libtool clean-local clean-noinstPROGRAMS mostlyclean-am + clean-libtool clean-local clean-noinstLIBRARIES \ + clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) @@ -2997,29 +3181,30 @@ uninstall-man: uninstall-man1 uninstall-man3 .PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-TESTS \ check-am clean clean-binPROGRAMS clean-cscope clean-generic \ clean-libLTLIBRARIES clean-libtool clean-local \ - clean-noinstPROGRAMS cscope cscopelist-am ctags ctags-am dist \ - dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ - dist-xz dist-zip distcheck distclean distclean-compile \ - distclean-generic distclean-hdr distclean-libtool \ - distclean-local distclean-tags distcleancheck distdir \ - distuninstallcheck dvi dvi-am html html-am info info-am \ - install install-am install-binPROGRAMS install-binSCRIPTS \ - install-data install-data-am install-dist_docDATA \ - install-dist_htmlDATA install-dvi install-dvi-am install-exec \ - install-exec-am install-html install-html-am \ - install-includeHEADERS install-info install-info-am \ - install-libLTLIBRARIES install-man install-man1 install-man3 \ - install-nodist_includeHEADERS install-pdf install-pdf-am \ - install-pkgconfigDATA install-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - recheck tags tags-am uninstall uninstall-am \ - uninstall-binPROGRAMS uninstall-binSCRIPTS \ - uninstall-dist_docDATA uninstall-dist_htmlDATA \ - uninstall-includeHEADERS uninstall-libLTLIBRARIES \ - uninstall-man uninstall-man1 uninstall-man3 \ - uninstall-nodist_includeHEADERS uninstall-pkgconfigDATA + clean-noinstLIBRARIES clean-noinstPROGRAMS cscope \ + cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ + dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ + distcheck distclean distclean-compile distclean-generic \ + distclean-hdr distclean-libtool distclean-local distclean-tags \ + distcleancheck distdir distuninstallcheck dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-binSCRIPTS install-data install-data-am \ + install-dist_docDATA install-dist_htmlDATA install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-includeHEADERS install-info \ + install-info-am install-libLTLIBRARIES install-man \ + install-man1 install-man3 install-nodist_includeHEADERS \ + install-pdf install-pdf-am install-pkgconfigDATA install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \ + uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-binSCRIPTS uninstall-dist_docDATA \ + uninstall-dist_htmlDATA uninstall-includeHEADERS \ + uninstall-libLTLIBRARIES uninstall-man uninstall-man1 \ + uninstall-man3 uninstall-nodist_includeHEADERS \ + uninstall-pkgconfigDATA .PRECIOUS: Makefile diff --git a/pcre2-10.32/NEWS b/pcre2-10.32/NEWS new file mode 100644 index 000000000..94345b3cb --- /dev/null +++ b/pcre2-10.32/NEWS @@ -0,0 +1,248 @@ +News about PCRE2 releases +------------------------- + + +Version 10.32 10-September-2018 +------------------------------- + +This is another mainly bugfix and tidying release with a few minor +enhancements. These are the main ones: + +1. pcre2grep now supports the inclusion of binary zeros in patterns that are +read from files via the -f option. + +2. ./configure now supports --enable-jit=auto, which automatically enables JIT +if the hardware supports it. + +3. In pcre2_dfa_match(), internal recursive calls no longer use the stack for +local workspace and local ovectors. Instead, an initial block of stack is +reserved, but if this is insufficient, heap memory is used. The heap limit +parameter now applies to pcre2_dfa_match(). + +4. Updated to Unicode version 11.0.0. + +5. (*ACCEPT:ARG), (*FAIL:ARG), and (*COMMIT:ARG) are now supported. + +6. Added support for \N{U+dddd}, but only in Unicode mode. + +7. Added support for (?^) to unset all imnsx options. + + +Version 10.31 12-February-2018 +------------------------------ + +This is mainly a bugfix and tidying release (see ChangeLog for full details). +However, there are some minor enhancements. + +1. New pcre2_config() options: PCRE2_CONFIG_NEVER_BACKSLASH_C and +PCRE2_CONFIG_COMPILED_WIDTHS. + +2. New pcre2_pattern_info() option PCRE2_INFO_EXTRAOPTIONS to retrieve the +extra compile time options. + +3. There are now public names for all the pcre2_compile() error numbers. + +4. Added PCRE2_CALLOUT_STARTMATCH and PCRE2_CALLOUT_BACKTRACK bits to a new +field callout_flags in callout blocks. + + +Version 10.30 14-August-2017 +---------------------------- + +The full list of changes that includes bugfixes and tidies is, as always, in +ChangeLog. These are the most important new features: + +1. The main interpreter, pcre2_match(), has been refactored into a new version +that does not use recursive function calls (and therefore the system stack) for +remembering backtracking positions. This makes --disable-stack-for-recursion a +NOOP. The new implementation allows backtracking into recursive group calls in +patterns, making it more compatible with Perl, and also fixes some other +previously hard-to-do issues. For patterns that have a lot of backtracking, the +heap is now used, and there is an explicit limit on the amount, settable by +pcre2_set_heap_limit() or (*LIMIT_HEAP=xxx). The "recursion limit" is retained, +but is renamed as "depth limit" (though the old names remain for +compatibility). + +There is also a change in the way callouts from pcre2_match() are handled. The +offset_vector field in the callout block is no longer a pointer to the +actual ovector that was passed to the matching function in the match data +block. Instead it points to an internal ovector of a size large enough to hold +all possible captured substrings in the pattern. + +2. The new option PCRE2_ENDANCHORED insists that a pattern match must end at +the end of the subject. + +3. The new option PCRE2_EXTENDED_MORE implements Perl's /xx feature, and +pcre2test is upgraded to support it. Setting within the pattern by (?xx) is +also supported. + +4. (?n) can be used to set PCRE2_NO_AUTO_CAPTURE, because Perl now has this. + +5. Additional compile options in the compile context are now available, and the +first two are: PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES and +PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL. + +6. The newline type PCRE2_NEWLINE_NUL is now available. + +7. The match limit value now also applies to pcre2_dfa_match() as there are +patterns that can use up a lot of resources without necessarily recursing very +deeply. + +8. The option REG_PEND (a GNU extension) is now available for the POSIX +wrapper. Also there is a new option PCRE2_LITERAL which is used to support +REG_NOSPEC. + +9. PCRE2_EXTRA_MATCH_LINE and PCRE2_EXTRA_MATCH_WORD are implemented for the +benefit of pcre2grep, and pcre2grep's -F, -w, and -x options are re-implemented +using PCRE2_LITERAL, PCRE2_EXTRA_MATCH_WORD, and PCRE2_EXTRA_MATCH_LINE. This +is tidier and also fixes some bugs. + +10. The Unicode tables are upgraded from Unicode 8.0.0 to Unicode 10.0.0. + +11. There are some experimental functions for converting foreign patterns +(globs and POSIX patterns) into PCRE2 patterns. + + +Version 10.23 14-February-2017 +------------------------------ + +1. ChangeLog has the details of a lot of bug fixes and tidies. + +2. There has been a major re-factoring of the pcre2_compile.c file. Most syntax +checking is now done in the pre-pass that identifies capturing groups. This has +reduced the amount of duplication and made the code tidier. While doing this, +some minor bugs and Perl incompatibilities were fixed (see ChangeLog for +details.) + +3. Back references are now permitted in lookbehind assertions when there are +no duplicated group numbers (that is, (?| has not been used), and, if the +reference is by name, there is only one group of that name. The referenced +group must, of course be of fixed length. + +4. \g{+} (e.g. \g{+2} ) is now supported. It is a "forward back +reference" and can be useful in repetitions (compare \g{-} ). Perl does +not recognize this syntax. + +5. pcre2grep now automatically expands its buffer up to a maximum set by +--max-buffer-size. + +6. The -t option (grand total) has been added to pcre2grep. + +7. A new function called pcre2_code_copy_with_tables() exists to copy a +compiled pattern along with a private copy of the character tables that is +uses. + +8. A user supplied a number of patches to upgrade pcre2grep under Windows and +tidy the code. + +9. Several updates have been made to pcre2test and test scripts (see +ChangeLog). + + +Version 10.22 29-July-2016 +-------------------------- + +1. ChangeLog has the details of a number of bug fixes. + +2. The POSIX wrapper function regcomp() did not used to support back references +and subroutine calls if called with the REG_NOSUB option. It now does. + +3. A new function, pcre2_code_copy(), is added, to make a copy of a compiled +pattern. + +4. Support for string callouts is added to pcre2grep. + +5. Added the PCRE2_NO_JIT option to pcre2_match(). + +6. The pcre2_get_error_message() function now returns with a negative error +code if the error number it is given is unknown. + +7. Several updates have been made to pcre2test and test scripts (see +ChangeLog). + + +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.22/NON-AUTOTOOLS-BUILD b/pcre2-10.32/NON-AUTOTOOLS-BUILD similarity index 87% rename from pcre2-10.22/NON-AUTOTOOLS-BUILD rename to pcre2-10.32/NON-AUTOTOOLS-BUILD index ceb92450c..b742ed348 100644 --- a/pcre2-10.22/NON-AUTOTOOLS-BUILD +++ b/pcre2-10.32/NON-AUTOTOOLS-BUILD @@ -1,10 +1,6 @@ Building PCRE2 without using autotools -------------------------------------- -This document has been converted from the PCRE1 document. I have removed a -number of sections about building in various environments, as they applied only -to PCRE1 and are probably out of date. - This document contains the following sections: General @@ -14,6 +10,7 @@ This document contains the following sections: Calling conventions in Windows environments Comments about Win32 builds Building PCRE2 on Windows with CMake + Building PCRE2 on Windows with Visual Studio Testing with RunTest.bat Building PCRE2 on native z/OS and z/VM @@ -49,7 +46,7 @@ can skip ahead to the CMake section. macro settings that it contains to whatever is appropriate for your environment. In particular, you can alter the definition of the NEWLINE macro to specify what character(s) you want to be interpreted as line - terminators. + terminators by default. When you compile any of the PCRE2 modules, you must specify -DHAVE_CONFIG_H to your compiler so that src/config.h is included in the @@ -95,8 +92,10 @@ can skip ahead to the CMake section. pcre2_compile.c pcre2_config.c pcre2_context.c + pcre2_convert.c pcre2_dfa_match.c pcre2_error.c + pcre2_extuni.c pcre2_find_bracket.c pcre2_jit_compile.c pcre2_maketables.c @@ -123,10 +122,14 @@ can skip ahead to the CMake section. Note that you must compile pcre2_jit_compile.c, even if you have not defined SUPPORT_JIT in src/config.h, because when JIT support is not configured, dummy functions are compiled. When JIT support IS configured, - pcre2_compile.c #includes other files from the sljit subdirectory, where - there should be 16 files, all of whose names begin with "sljit". It also - #includes src/pcre2_jit_match.c and src/pcre2_jit_misc.c, so you should - not compile these yourself. + pcre2_jit_compile.c #includes other files from the sljit subdirectory, + all of whose names begin with "sljit". It also #includes + src/pcre2_jit_match.c and src/pcre2_jit_misc.c, so you should not compile + these yourself. + + Note also that the pcre2_fuzzsupport.c file contains special code that is + useful to those who want to run fuzzing tests on the PCRE2 library. Unless + you are doing that, you can ignore it. (5) Now link all the compiled code into an object library in whichever form your system keeps such libraries. This is the basic PCRE2 C 8-bit library. @@ -174,26 +177,18 @@ can skip ahead to the CMake section. (11) If you want to use the pcre2grep command, compile and link src/pcre2grep.c; it uses only the basic 8-bit PCRE2 library (it does not - need the pcre2posix library). + need the pcre2posix library). If you have built the PCRE2 library with JIT + support by defining SUPPORT_JIT in src/config.h, you can also define + SUPPORT_PCRE2GREP_JIT, which causes pcre2grep to make use of JIT (unless + it is run with --no-jit). If you define SUPPORT_PCRE2GREP_JIT without + defining SUPPORT_JIT, pcre2grep does not try to make use of JIT. STACK SIZE IN WINDOWS ENVIRONMENTS -The default processor stack size of 1Mb in some Windows environments is too -small for matching patterns that need much recursion. In particular, test 2 may -fail because of this. Normally, running out of stack causes a crash, but there -have been cases where the test program has just died silently. See your linker -documentation for how to increase stack size if you experience problems. If you -are using CMake (see "BUILDING PCRE2 ON WINDOWS WITH CMAKE" below) and the gcc -compiler, you can increase the stack size for pcre2test and pcre2grep by -setting the CMAKE_EXE_LINKER_FLAGS variable to "-Wl,--stack,8388608" (for -example). The Linux default of 8Mb is a reasonable choice for the stack, though -even that can be too small for some pattern/subject combinations. - -PCRE2 has a compile configuration option to disable the use of stack for -recursion so that heap is used instead. However, pattern matching is -significantly slower when this is done. There is more about stack usage in the -"pcre2stack" documentation. +Prior to release 10.30 the default system stack size of 1MiB in some Windows +environments caused issues with some tests. This should no longer be the case +for 10.30 and later releases. LINKING PROGRAMS IN WINDOWS ENVIRONMENTS @@ -336,6 +331,18 @@ cache can be deleted by selecting "File > Delete Cache". available for review in Testing\Temporary under your build dir. +BUILDING PCRE2 ON WINDOWS WITH VISUAL STUDIO + +The code currently cannot be compiled without a stdint.h header, which is +available only in relatively recent versions of Visual Studio. However, this +portable and permissively-licensed implementation of the header worked without +issue: + + http://www.azillionmonkeys.com/qed/pstdint.h + +Just rename it and drop it into the top level of the build tree. + + TESTING WITH RUNTEST.BAT If configured with CMake, building the test project ("make test" or building @@ -375,18 +382,19 @@ BUILDING PCRE2 ON NATIVE Z/OS AND Z/VM z/OS and z/VM are operating systems for mainframe computers, produced by IBM. The character code used is EBCDIC, not ASCII or Unicode. In z/OS, UNIX APIs and applications can be supported through UNIX System Services, and in such an -environment PCRE2 can be built in the same way as in other systems. However, in -native z/OS (without UNIX System Services) and in z/VM, special ports are -required. For details, please see this web site: +environment it should be possible to build PCRE2 in the same way as in other +systems, with the EBCDIC related configuration settings, but it is not known if +anybody has tried this. - http://www.zaconsultants.net +In native z/OS (without UNIX System Services) and in z/VM, special ports are +required. For details, please see file 939 on this web site: -The site currently has ports for PCRE1 releases, but PCRE2 should follow in due -course. + http://www.cbttape.org -You may also download PCRE1 from WWW.CBTTAPE.ORG, file 882. Everything, source -and executable, is in EBCDIC and native z/OS file formats and this is the -recommended download site. +Everything in that location, source and executable, is in EBCDIC and native +z/OS file formats. The port provides an API for LE languages such as COBOL and +for the z/OS and z/VM versions of the Rexx languages. -============================= -Last Updated: 16 July 2015 +=========================== +Last Updated: 19 April 2018 +=========================== diff --git a/pcre2-10.22/PrepareRelease b/pcre2-10.32/PrepareRelease similarity index 98% rename from pcre2-10.22/PrepareRelease rename to pcre2-10.32/PrepareRelease index 114fce01d..9aa6b7d50 100755 --- a/pcre2-10.22/PrepareRelease +++ b/pcre2-10.32/PrepareRelease @@ -66,7 +66,7 @@ End echo "Making pcre2.txt" for file in pcre2 pcre2api pcre2build pcre2callout pcre2compat pcre2jit \ pcre2limits pcre2matching pcre2partial pcre2pattern pcre2perform \ - pcre2posix pcre2sample pcre2serialize pcre2stack pcre2syntax \ + pcre2posix pcre2sample pcre2serialize pcre2syntax \ pcre2unicode ; do echo " Processing $file.3" nroff -c -man $file.3 >$file.rawtxt @@ -146,7 +146,6 @@ for file in *.3 ; do toc=-toc if [ `expr $base : '.*_'` -ne 0 ] ; then toc="" ; fi if [ "$base" = "pcre2sample" ] || \ - [ "$base" = "pcre2stack" ] || \ [ "$base" = "pcre2compat" ] || \ [ "$base" = "pcre2limits" ] || \ [ "$base" = "pcre2unicode" ] ; then @@ -197,8 +196,10 @@ files="\ src/pcre2_compile.c \ src/pcre2_config.c \ src/pcre2_context.c \ + src/pcre2_convert.c \ src/pcre2_dfa_match.c \ src/pcre2_error.c \ + src/pcre2_extuni.c \ src/pcre2_find_bracket.c \ src/pcre2_internal.h \ src/pcre2_intmodedep.h \ diff --git a/pcre2-10.22/README b/pcre2-10.32/README similarity index 81% rename from pcre2-10.22/README rename to pcre2-10.32/README index 03d67f65f..2eb621b0f 100644 --- a/pcre2-10.22/README +++ b/pcre2-10.32/README @@ -15,8 +15,8 @@ subscribe or manage your subscription here: https://lists.exim.org/mailman/listinfo/pcre-dev -Please read the NEWS file if you are upgrading from a previous release. -The contents of this README file are: +Please read the NEWS file if you are upgrading from a previous release. The +contents of this README file are: The PCRE2 APIs Documentation for PCRE2 @@ -44,8 +44,8 @@ wrappers. The distribution does contain a set of C wrapper functions for the 8-bit library that are based on the POSIX regular expression API (see the pcre2posix -man page). These can be found in a library called libpcre2posix. Note that this -just provides a POSIX calling interface to PCRE2; the regular expressions +man page). These can be found in a library called libpcre2-posix. Note that +this just provides a POSIX calling interface to PCRE2; the regular expressions themselves still follow Perl syntax and semantics. The POSIX API is restricted, and does not give full access to all of PCRE2's facilities. @@ -58,8 +58,8 @@ renamed or pointed at by a link. If you are using the POSIX interface to PCRE2 and there is already a POSIX regex library installed on your system, as well as worrying about the regex.h header file (as mentioned above), you must also take care when linking programs -to ensure that they link with PCRE2's libpcre2posix library. Otherwise they may -pick up the POSIX functions of the same name from the other library. +to ensure that they link with PCRE2's libpcre2-posix library. Otherwise they +may pick up the POSIX functions of the same name from the other library. One way of avoiding this confusion is to compile PCRE2 with the addition of -Dregcomp=PCRE2regcomp (and similarly for the other POSIX functions) to the @@ -95,10 +95,9 @@ PCRE2 documentation is supplied in two other forms: Building PCRE2 on non-Unix-like systems --------------------------------------- -For a non-Unix-like system, please read the comments in the file -NON-AUTOTOOLS-BUILD, though if your system supports the use of "configure" and -"make" you may be able to build PCRE2 using autotools in the same way as for -many Unix-like systems. +For a non-Unix-like system, please read the file NON-AUTOTOOLS-BUILD, though if +your system supports the use of "configure" and "make" you may be able to build +PCRE2 using autotools in the same way as for many Unix-like systems. PCRE2 can also be configured using CMake, which can be run in various ways (command line, GUI, etc). This creates Makefiles, solution files, etc. The file @@ -172,21 +171,26 @@ library. They are also documented in the pcre2build man page. give large performance improvements on certain platforms, add --enable-jit to the "configure" command. This support is available only for certain hardware architectures. If you try to enable it on an unsupported architecture, there - will be a compile time error. + will be a compile time error. If in doubt, use --enable-jit=auto, which + enables JIT only if the current hardware is supported. -. If you do not want to make use of the support for UTF-8 Unicode character - strings in the 8-bit library, UTF-16 Unicode character strings in the 16-bit - library, or UTF-32 Unicode character strings in the 32-bit library, you can - add --disable-unicode to the "configure" command. This reduces the size of - the libraries. It is not possible to configure one library with Unicode - support, and another without, in the same configuration. +. If you are enabling JIT under SELinux you may also want to add + --enable-jit-sealloc, which enables the use of an execmem allocator in JIT + that is compatible with SELinux. This has no effect if JIT is not enabled. + +. If you do not want to make use of the default support for UTF-8 Unicode + character strings in the 8-bit library, UTF-16 Unicode character strings in + the 16-bit library, or UTF-32 Unicode character strings in the 32-bit + library, you can add --disable-unicode to the "configure" command. This + reduces the size of the libraries. It is not possible to configure one + library with Unicode support, and another without, in the same configuration. + It is also not possible to use --enable-ebcdic (see below) with Unicode + support, so if this option is set, you must also use --disable-unicode. When Unicode support is available, the use of a UTF encoding still has to be enabled by setting the PCRE2_UTF option at run time or starting a pattern with (*UTF). When PCRE2 is compiled with Unicode support, its input can only - either be ASCII or UTF-8/16/32, even when running on EBCDIC platforms. It is - not possible to use both --enable-unicode and --enable-ebcdic at the same - time. + either be ASCII or UTF-8/16/32, even when running on EBCDIC platforms. As well as supporting UTF strings, Unicode support includes support for the \P, \p, and \X sequences that recognize Unicode character properties. @@ -196,20 +200,14 @@ library. They are also documented in the pcre2build man page. or starting a pattern with (*UCP). . You can build PCRE2 to recognize either CR or LF or the sequence CRLF, or any - of the preceding, or any of the Unicode newline sequences, as indicating the - end of a line. Whatever you specify at build time is the default; the caller - of PCRE2 can change the selection at run time. The default newline indicator - is a single LF character (the Unix standard). You can specify the default - newline indicator by adding --enable-newline-is-cr, --enable-newline-is-lf, - --enable-newline-is-crlf, --enable-newline-is-anycrlf, or - --enable-newline-is-any to the "configure" command, respectively. - - If you specify --enable-newline-is-cr or --enable-newline-is-crlf, some of - the standard tests will fail, because the lines in the test files end with - LF. Even if the files are edited to change the line endings, there are likely - to be some failures. With --enable-newline-is-anycrlf or - --enable-newline-is-any, many tests should succeed, but there may be some - failures. + of the preceding, or any of the Unicode newline sequences, or the NUL (zero) + character as indicating the end of a line. Whatever you specify at build time + is the default; the caller of PCRE2 can change the selection at run time. The + default newline indicator is a single LF character (the Unix standard). You + can specify the default newline indicator by adding --enable-newline-is-cr, + --enable-newline-is-lf, --enable-newline-is-crlf, + --enable-newline-is-anycrlf, --enable-newline-is-any, or + --enable-newline-is-nul to the "configure" command, respectively. . By default, the sequence \R in a pattern matches any Unicode line ending sequence. This is independent of the option specifying what PCRE2 considers @@ -231,49 +229,47 @@ library. They are also documented in the pcre2build man page. --with-parens-nest-limit=500 -. PCRE2 has a counter that can be set to limit the amount of resources it uses - when matching a pattern. If the limit is exceeded during a match, the match - fails. The default is ten million. You can change the default by setting, for - example, +. PCRE2 has a counter that can be set to limit the amount of computing resource + it uses when matching a pattern. If the limit is exceeded during a match, the + match fails. The default is ten million. You can change the default by + setting, for example, --with-match-limit=500000 on the "configure" command. This is just the default; individual calls to - pcre2_match() can supply their own value. There is more discussion on the - pcre2api man page. + pcre2_match() or pcre2_dfa_match() can supply their own value. There is more + discussion in the pcre2api man page (search for pcre2_set_match_limit). -. There is a separate counter that limits the depth of recursive function calls - during a matching process. This also has a default of ten million, which is - essentially "unlimited". You can change the default by setting, for example, +. There is a separate counter that limits the depth of nested backtracking + (pcre2_match()) or nested function calls (pcre2_dfa_match()) during a + matching process, which indirectly limits the amount of heap memory that is + used, and in the case of pcre2_dfa_match() the amount of stack as well. This + counter also has a default of ten million, which is essentially "unlimited". + You can change the default by setting, for example, - --with-match-limit-recursion=500000 + --with-match-limit-depth=5000 - Recursive function calls use up the runtime stack; running out of stack can - cause programs to crash in strange ways. There is a discussion about stack - sizes in the pcre2stack man page. + There is more discussion in the pcre2api man page (search for + pcre2_set_depth_limit). + +. You can also set an explicit limit on the amount of heap memory used by + the pcre2_match() and pcre2_dfa_match() interpreters: + + --with-heap-limit=500 + + The units are kibibytes (units of 1024 bytes). This limit does not apply when + the JIT optimization (which has its own memory control features) is used. + There is more discussion on the pcre2api man page (search for + pcre2_set_heap_limit). . In the 8-bit library, the default maximum compiled pattern size is around - 64K. You can increase this by adding --with-link-size=3 to the "configure" - command. PCRE2 then uses three bytes instead of two for offsets to different - parts of the compiled pattern. In the 16-bit library, --with-link-size=3 is - the same as --with-link-size=4, which (in both libraries) uses four-byte - offsets. Increasing the internal link size reduces performance in the 8-bit - and 16-bit libraries. In the 32-bit library, the link size setting is - ignored, as 4-byte offsets are always used. - -. You can build PCRE2 so that its internal match() function that is called from - pcre2_match() does not call itself recursively. Instead, it uses memory - blocks obtained from the heap to save data that would otherwise be saved on - the stack. To build PCRE2 like this, use - - --disable-stack-for-recursion - - on the "configure" command. PCRE2 runs more slowly in this mode, but it may - be necessary in environments with limited stack sizes. This applies only to - the normal execution of the pcre2_match() function; if JIT support is being - successfully used, it is not relevant. Equally, it does not apply to - pcre2_dfa_match(), which does not use deeply nested recursion. There is a - discussion about stack sizes in the pcre2stack man page. + 64 kibibytes. You can increase this by adding --with-link-size=3 to the + "configure" command. PCRE2 then uses three bytes instead of two for offsets + to different parts of the compiled pattern. In the 16-bit library, + --with-link-size=3 is the same as --with-link-size=4, which (in both + libraries) uses four-byte offsets. Increasing the internal link size reduces + performance in the 8-bit and 16-bit libraries. In the 32-bit library, the + link size setting is ignored, as 4-byte offsets are always used. . For speed, PCRE2 uses four tables for manipulating and identifying characters whose code point values are less than 256. By default, it uses a set of @@ -324,10 +320,10 @@ library. They are also documented in the pcre2build man page. . When JIT support is enabled, pcre2grep automatically makes use of it, unless you add --disable-pcre2grep-jit to the "configure" command. -. On non-Windows sytems there is support for calling external scripts during - matching in the pcre2grep command via PCRE2's callout facility with string - arguments. This support can be disabled by adding --disable-pcre2grep-callout - to the "configure" command. +. There is support for calling external programs during matching in the + pcre2grep command, using PCRE2's callout facility with string arguments. This + support can be disabled by adding --disable-pcre2grep-callout to the + "configure" command. . The pcre2grep program currently supports only 8-bit data files, and so requires the 8-bit PCRE2 library. It is possible to compile pcre2grep to use @@ -339,12 +335,23 @@ library. They are also documented in the pcre2build man page. Of course, the relevant libraries must be installed on your system. -. The default size (in bytes) of the internal buffer used by pcre2grep can be - set by, for example: +. The default starting size (in bytes) of the internal buffer used by pcre2grep + can be set by, for example: --with-pcre2grep-bufsize=51200 - The value must be a plain integer. The default is 20480. + The value must be a plain integer. The default is 20480. The amount of memory + used by pcre2grep is actually three times this number, to allow for "before" + and "after" lines. If very long lines are encountered, the buffer is + automatically enlarged, up to a fixed maximum size. + +. The default maximum size of pcre2grep's internal buffer can be set by, for + example: + + --with-pcre2grep-max-bufsize=2097152 + + The default is either 1048576 or the value of --with-pcre2grep-bufsize, + whichever is the larger. . It is possible to compile pcre2test so that it links with the libreadline or libedit libraries, by specifying, respectively, @@ -369,6 +376,29 @@ library. They are also documented in the pcre2build man page. tgetflag, or tgoto, this is the problem, and linking with the ncurses library should fix it. +. There is a special option called --enable-fuzz-support for use by people who + want to run fuzzing tests on PCRE2. At present this applies only to the 8-bit + library. If set, it causes an extra library called libpcre2-fuzzsupport.a to + be built, but not installed. This contains a single function called + LLVMFuzzerTestOneInput() whose arguments are a pointer to a string and the + length of the string. When called, this function tries to compile the string + as a pattern, and if that succeeds, to match it. This is done both with no + options and with some random options bits that are generated from the string. + Setting --enable-fuzz-support also causes a binary called pcre2fuzzcheck to + be created. This is normally run under valgrind or used when PCRE2 is + compiled with address sanitizing enabled. It calls the fuzzing function and + outputs information about it is doing. The input strings are specified by + arguments: if an argument starts with "=" the rest of it is a literal input + string. Otherwise, it is assumed to be a file name, and the contents of the + file are the test string. + +. Releases before 10.30 could be compiled with --disable-stack-for-recursion, + which caused pcre2_match() to use individual blocks on the heap for + backtracking instead of recursive function calls (which use the stack). This + is now obsolete since pcre2_match() was refactored always to use the heap (in + a much more efficient way than before). This option is retained for backwards + compatibility, but has no effect other than to output a warning. + The "configure" script builds the following files for the basic C library: . Makefile the makefile that builds the library @@ -543,7 +573,7 @@ script creates the .txt and HTML forms of the documentation from the man pages. Testing PCRE2 ------------- +------------- To test the basic PCRE2 library on a Unix-like system, run the RunTest script. There is another script called RunGrepTest that tests the pcre2grep command. @@ -635,32 +665,43 @@ with the perltest.sh script, and test 5 checking PCRE2-specific things. Tests 6 and 7 check the pcre2_dfa_match() alternative matching function, in non-UTF mode and UTF-mode with Unicode property support, respectively. -Test 8 checks some internal offsets and code size features; it is run only when -the default "link size" of 2 is set (in other cases the sizes change) and when -Unicode support is enabled. +Test 8 checks some internal offsets and code size features, but it is run only +when Unicode support is enabled. The output 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. Tests 9 and 10 are run only in 8-bit mode, and tests 11 and 12 are run only in 16-bit and 32-bit modes. These are tests that generate different output in 8-bit mode. Each pair are for general cases and Unicode support, respectively. + Test 13 checks the handling of non-UTF characters greater than 255 by pcre2_dfa_match() in 16-bit and 32-bit modes. -Test 14 contains a number of tests that must not be run with JIT. They check, +Test 14 contains some special UTF and UCP tests that give different output for +different code unit widths. + +Test 15 contains a number of tests that must not be run with JIT. They check, among other non-JIT things, the match-limiting features of the intepretive matcher. -Test 15 is run only when JIT support is not available. It checks that an +Test 16 is run only when JIT support is not available. It checks that an attempt to use JIT has the expected behaviour. -Test 16 is run only when JIT support is available. It checks JIT complete and +Test 17 is run only when JIT support is available. It checks JIT complete and partial modes, match-limiting under JIT, and other JIT-specific features. -Tests 17 and 18 are run only in 8-bit mode. They check the POSIX interface to +Tests 18 and 19 are run only in 8-bit mode. They check the POSIX interface to the 8-bit library, without and with Unicode support, respectively. -Test 19 checks the serialization functions by writing a set of compiled +Test 20 checks the serialization functions by writing a set of compiled patterns to a file, and then reloading and checking them. +Tests 21 and 22 test \C support when the use of \C is not locked out, without +and with UTF support, respectively. Test 23 tests \C when it is locked out. + +Tests 24 and 25 test the experimental pattern conversion functions, without and +with UTF support, respectively. + Character tables ---------------- @@ -679,7 +720,7 @@ specified for ./configure, a different version of pcre2_chartables.c is built by the program dftables (compiled from dftables.c), which uses the ANSI C character handling functions such as isalnum(), isalpha(), isupper(), islower(), etc. to build the table sources. This means that the default C -locale which is set for your system will control the contents of these default +locale that is set for your system will control the contents of these default tables. You can change the default tables by editing pcre2_chartables.c and then re-building PCRE2. If you do this, you should take care to ensure that the file does not get automatically re-generated. The best way to do this is to @@ -734,8 +775,10 @@ The distribution should contain the files listed below. src/pcre2_compile.c ) src/pcre2_config.c ) src/pcre2_context.c ) + src/pcre2_convert.c ) src/pcre2_dfa_match.c ) src/pcre2_error.c ) + src/pcre2_extuni.c ) src/pcre2_find_bracket.c ) src/pcre2_jit_compile.c ) src/pcre2_jit_match.c ) sources for the functions in the library, @@ -757,6 +800,7 @@ The distribution should contain the files listed below. src/pcre2_xclass.c ) src/pcre2_printint.c debugging function that is used by pcre2test, + src/pcre2_fuzzsupport.c function for (optional) fuzzing support src/config.h.in template for config.h, when built by "configure" src/pcre2.h.in template for pcre2.h when built by "configure" @@ -772,7 +816,6 @@ The distribution should contain the files listed below. src/pcre2demo.c simple demonstration of coding calls to PCRE2 src/pcre2grep.c source of a grep utility that uses PCRE2 src/pcre2test.c comprehensive test program - src/pcre2_printint.c part of pcre2test src/pcre2_jit_test.c JIT test program (C) Auxiliary files: @@ -814,7 +857,7 @@ The distribution should contain the files listed below. libpcre2-8.pc.in template for libpcre2-8.pc for pkg-config libpcre2-16.pc.in template for libpcre2-16.pc for pkg-config libpcre2-32.pc.in template for libpcre2-32.pc for pkg-config - libpcre2posix.pc.in template for libpcre2posix.pc for pkg-config + libpcre2-posix.pc.in template for libpcre2-posix.pc for pkg-config ltmain.sh file used to build a libtool script missing ) common stub for a few missing GNU programs while ) installing, generated by automake @@ -837,12 +880,12 @@ The distribution should contain the files listed below. (E) Auxiliary files for building PCRE2 "by hand" - pcre2.h.generic ) a version of the public PCRE2 header file + src/pcre2.h.generic ) a version of the public PCRE2 header file ) for use in non-"configure" environments - config.h.generic ) a version of config.h for use in non-"configure" + src/config.h.generic ) a version of config.h for use in non-"configure" ) environments Philip Hazel Email local part: ph10 Email domain: cam.ac.uk -Last updated: 01 April 2016 +Last updated: 17 June 2018 diff --git a/pcre2-10.22/RunGrepTest b/pcre2-10.32/RunGrepTest similarity index 81% rename from pcre2-10.22/RunGrepTest rename to pcre2-10.32/RunGrepTest index a3e13120c..74ff4c13c 100755 --- a/pcre2-10.22/RunGrepTest +++ b/pcre2-10.32/RunGrepTest @@ -4,6 +4,12 @@ # itself. What we are checking here is the file handling and options that are # supported by pcre2grep. This script must be run in the build directory. +# CODING CONVENTIONS: +# * Put printf arguments in single, not double quotes to avoid unwanted +# escaping. +# * Use \0 for binary zero in printf, not \x0, for the benefit of older +# versions. + # Set the C locale, so that sort(1) behaves predictably. LC_ALL=C @@ -11,7 +17,8 @@ export LC_ALL # Remove any non-default colouring and aliases that the caller may have set. -unset PCRE2GREP_COLOUR PCRE2GREP_COLOR +unset PCRE2GREP_COLOUR PCRE2GREP_COLOR PCREGREP_COLOUR PCREGREP_COLOR +unset GREP_COLOR GREP_COLORS unset cp ls mv rm # Remember the current (build) directory, set the program to be tested, and @@ -22,12 +29,12 @@ pcre2grep=$builddir/pcre2grep pcre2test=$builddir/pcre2test if [ ! -x $pcre2grep ] ; then - echo "** $pcre2grep does not exist or is not execuatble." + echo "** $pcre2grep does not exist or is not executable." exit 1 fi if [ ! -x $pcre2test ] ; then - echo "** $pcre2test does not exist or is not execuatble." + echo "** $pcre2test does not exist or is not executable." exit 1 fi @@ -247,7 +254,7 @@ echo "---------------------------- Test 35 -----------------------------" >>test echo "RC=$?" >>testtrygrep echo "---------------------------- Test 36 -----------------------------" >>testtrygrep -(cd $srcdir; $valgrind $vjs $pcre2grep -L -r --include=grepinput --exclude 'grepinput$' --exclude=grepinput8 --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -L -r --include=grepinput --exclude 'grepinput$' --exclude=grepinput8 --exclude=grepinputM --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtrygrep echo "RC=$?" >>testtrygrep echo "---------------------------- Test 37 -----------------------------" >>testtrygrep @@ -390,6 +397,12 @@ echo "RC=$?" >>testtrygrep echo "---------------------------- Test 70 -----------------------------" >>testtrygrep (cd $srcdir; $valgrind $vjs $pcre2grep --color=always -M "triple:\t.*\n\n" ./testdata/grepinput3) >>testtrygrep echo "RC=$?" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep --color=always -M -n "triple:\t.*\n\n" ./testdata/grepinput3) >>testtrygrep +echo "RC=$?" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -M "triple:\t.*\n\n" ./testdata/grepinput3) >>testtrygrep +echo "RC=$?" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -M -n "triple:\t.*\n\n" ./testdata/grepinput3) >>testtrygrep +echo "RC=$?" >>testtrygrep echo "---------------------------- Test 71 -----------------------------" >>testtrygrep (cd $srcdir; $valgrind $vjs $pcre2grep -o "^01|^02|^03" ./testdata/grepinput) >>testtrygrep @@ -440,7 +453,7 @@ echo "---------------------------- Test 82 -----------------------------" >>test echo "RC=$?" >>testtrygrep echo "---------------------------- Test 83 -----------------------------" >>testtrygrep -(cd $srcdir; $valgrind $vjs $pcre2grep --buffer-size=100 "^a" ./testdata/grepinput3) >>testtrygrep 2>&1 +(cd $srcdir; $valgrind $vjs $pcre2grep --buffer-size=10 --max-buffer-size=100 "^a" ./testdata/grepinput3) >>testtrygrep 2>&1 echo "RC=$?" >>testtrygrep echo "---------------------------- Test 84 -----------------------------" >>testtrygrep @@ -493,25 +506,25 @@ echo "---------------------------- Test 95 -----------------------------" >>test echo "RC=$?" >>testtrygrep echo "---------------------------- Test 96 -----------------------------" >>testtrygrep -(cd $srcdir; $valgrind $vjs $pcre2grep -L -r --include-dir=testdata --exclude '^(?!grepinput)' 'fox' ./test* | sort) >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -L -r --include-dir=testdata --exclude '^(?!grepinput)' --exclude=grepinputM 'fox' ./test* | sort) >>testtrygrep echo "RC=$?" >>testtrygrep echo "---------------------------- Test 97 -----------------------------" >>testtrygrep echo "grepinput$" >testtemp1grep echo "grepinput8" >>testtemp1grep -(cd $srcdir; $valgrind $vjs $pcre2grep -L -r --include=grepinput --exclude-from $builddir/testtemp1grep --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -L -r --include=grepinput --exclude=grepinputM --exclude-from $builddir/testtemp1grep --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtrygrep echo "RC=$?" >>testtrygrep echo "---------------------------- Test 98 -----------------------------" >>testtrygrep echo "grepinput$" >testtemp1grep echo "grepinput8" >>testtemp1grep -(cd $srcdir; $valgrind $vjs $pcre2grep -L -r --exclude=grepinput3 --include=grepinput --exclude-from $builddir/testtemp1grep --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -L -r --exclude=grepinput3 --exclude=grepinputM --include=grepinput --exclude-from $builddir/testtemp1grep --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtrygrep echo "RC=$?" >>testtrygrep echo "---------------------------- Test 99 -----------------------------" >>testtrygrep echo "grepinput$" >testtemp1grep echo "grepinput8" >testtemp2grep -(cd $srcdir; $valgrind $vjs $pcre2grep -L -r --include grepinput --exclude-from $builddir/testtemp1grep --exclude-from=$builddir/testtemp2grep --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -L -r --include grepinput --exclude=grepinputM --exclude-from $builddir/testtemp1grep --exclude-from=$builddir/testtemp2grep --exclude-dir='^\.' 'fox' ./testdata | sort) >>testtrygrep echo "RC=$?" >>testtrygrep echo "---------------------------- Test 100 ------------------------------" >>testtrygrep @@ -568,6 +581,79 @@ echo "---------------------------- Test 112 -----------------------------" >>tes (cd $srcdir; $valgrind $vjs $pcre2grep --file-offsets -M 'match (\d+):\n (.)\n' testdata/grepinput) >>testtrygrep echo "RC=$?" >>testtrygrep +echo "---------------------------- Test 113 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep --total-count 'the' testdata/grepinput*) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 114 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -tc 'the' testdata/grepinput*) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 115 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -tlc 'the' testdata/grepinput*) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 116 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep --exclude=grepinputM -th 'the' testdata/grepinput*) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 117 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -tch 'the' testdata/grepinput*) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 118 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -tL 'the' testdata/grepinput*) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 119 -----------------------------" >>testtrygrep +printf '123\n456\n789\n---abc\ndef\nxyz\n---\n' >testNinputgrep +$valgrind $vjs $pcre2grep -Mo '(\n|[^-])*---' testNinputgrep >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 120 ------------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -HO '$0:$2$1$3' '(\w+) binary (\w+)(\.)?' ./testdata/grepinput) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 121 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -F '\E and (regex)' testdata/grepinputv) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 122 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -w 'cat|dog' testdata/grepinputv) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 123 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -w 'dog|cat' testdata/grepinputv) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 124 -----------------------------" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -Mn --colour=always 'start[\s]+end' testdata/grepinputM) >>testtrygrep +echo "RC=$?" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -Mn --colour=always -A2 'start[\s]+end' testdata/grepinputM) >>testtrygrep +echo "RC=$?" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -Mn 'start[\s]+end' testdata/grepinputM) >>testtrygrep +echo "RC=$?" >>testtrygrep +(cd $srcdir; $valgrind $vjs $pcre2grep -Mn -A2 'start[\s]+end' testdata/grepinputM) >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 125 -----------------------------" >>testtrygrep +printf 'abcd\n' >testNinputgrep +$valgrind $vjs $pcre2grep --colour=always '(?<=\K.)' testNinputgrep >>testtrygrep +echo "RC=$?" >>testtrygrep +$valgrind $vjs $pcre2grep --colour=always '(?=.\K)' testNinputgrep >>testtrygrep +echo "RC=$?" >>testtrygrep +$valgrind $vjs $pcre2grep --colour=always '(?<=\K[ac])' testNinputgrep >>testtrygrep +echo "RC=$?" >>testtrygrep +$valgrind $vjs $pcre2grep --colour=always '(?=[ac]\K)' testNinputgrep >>testtrygrep +echo "RC=$?" >>testtrygrep + +echo "---------------------------- Test 126 -----------------------------" >>testtrygrep +printf 'Next line pattern has binary zero\nABC\0XYZ\n' >testtemp1grep +printf 'ABC\0XYZ\nABCDEF\nDEFABC\n' >testtemp2grep +$valgrind $vjs $pcre2grep -a -f testtemp1grep testtemp2grep >>testtrygrep +echo "RC=$?" >>testtrygrep + + # Now compare the results. $cf $srcdir/testdata/grepoutput testtrygrep @@ -607,27 +693,42 @@ fi # starts with a hyphen. These tests are run in the build directory. echo "Testing pcre2grep newline settings" -printf "abc\rdef\r\nghi\njkl" >testNinputgrep +printf 'abc\rdef\r\nghi\njkl' >testNinputgrep -printf "%c--------------------------- Test N1 ------------------------------\r\n" - >testtrygrep +printf '%c--------------------------- Test N1 ------------------------------\r\n' - >testtrygrep $valgrind $vjs $pcre2grep -n -N CR "^(abc|def|ghi|jkl)" testNinputgrep >>testtrygrep -printf "%c--------------------------- Test N2 ------------------------------\r\n" - >>testtrygrep +printf '%c--------------------------- Test N2 ------------------------------\r\n' - >>testtrygrep $valgrind $vjs $pcre2grep -n --newline=crlf "^(abc|def|ghi|jkl)" testNinputgrep >>testtrygrep -printf "%c--------------------------- Test N3 ------------------------------\r\n" - >>testtrygrep +printf '%c--------------------------- Test N3 ------------------------------\r\n' - >>testtrygrep pattern=`printf 'def\rjkl'` $valgrind $vjs $pcre2grep -n --newline=cr -F "$pattern" testNinputgrep >>testtrygrep -printf "%c--------------------------- Test N4 ------------------------------\r\n" - >>testtrygrep +printf '%c--------------------------- Test N4 ------------------------------\r\n' - >>testtrygrep $valgrind $vjs $pcre2grep -n --newline=crlf -F -f $srcdir/testdata/greppatN4 testNinputgrep >>testtrygrep -printf "%c--------------------------- Test N5 ------------------------------\r\n" - >>testtrygrep +printf '%c--------------------------- Test N5 ------------------------------\r\n' - >>testtrygrep $valgrind $vjs $pcre2grep -n --newline=any "^(abc|def|ghi|jkl)" testNinputgrep >>testtrygrep -printf "%c--------------------------- Test N6 ------------------------------\r\n" - >>testtrygrep +printf '%c--------------------------- Test N6 ------------------------------\r\n' - >>testtrygrep $valgrind $vjs $pcre2grep -n --newline=anycrlf "^(abc|def|ghi|jkl)" testNinputgrep >>testtrygrep +# It seems impossible to handle NUL characters easily in Solaris (aka SunOS). +# The version of sed explicitly doesn't like them. For the moment, we just +# don't run this test under SunOS. Fudge the output so that the comparison +# works. A similar problem has also been reported for MacOS (Darwin). + +printf '%c--------------------------- Test N7 ------------------------------\r\n' - >>testtrygrep +uname=`uname` +if [ "$uname" != "SunOS" -a "$uname" != "Darwin" ] ; then + printf 'abc\0def' >testNinputgrep + $valgrind $vjs $pcre2grep -na --newline=nul "^(abc|def)" testNinputgrep | sed 's/\x00/ZERO/' >>testtrygrep + echo "" >>testtrygrep +else + echo '1:abcZERO2:def' >>testtrygrep +fi + $cf $srcdir/testdata/grepoutputN testtrygrep if [ $? != 0 ] ; then exit 1; fi @@ -637,6 +738,9 @@ if $valgrind $vjs $pcre2grep --help | $valgrind $vjs $pcre2grep -q 'Callout scri echo "Testing pcre2grep script callouts" $valgrind $vjs $pcre2grep '(T)(..(.))(?C"/bin/echo|Arg1: [$1] [$2] [$3]|Arg2: $|${1}$| ($4) ($14) ($0)")()' $srcdir/testdata/grepinputv >testtrygrep $valgrind $vjs $pcre2grep '(T)(..(.))()()()()()()()(..)(?C"/bin/echo|Arg1: [$11] [${11}]")' $srcdir/testdata/grepinputv >>testtrygrep + $valgrind $vjs $pcre2grep '(T)(?C"|$0:$1$n")' $srcdir/testdata/grepinputv >>testtrygrep + $valgrind $vjs $pcre2grep '(T)(?C"|$1$n")(*F)' $srcdir/testdata/grepinputv >>testtrygrep + # The above has no newline, which 'diff -ub' ignores, so add one. $cf $srcdir/testdata/grepoutputC testtrygrep if [ $? != 0 ] ; then exit 1; fi else diff --git a/pcre2-10.22/RunTest b/pcre2-10.32/RunTest similarity index 83% rename from pcre2-10.22/RunTest rename to pcre2-10.32/RunTest index d0eec77e0..39f04d406 100755 --- a/pcre2-10.22/RunTest +++ b/pcre2-10.32/RunTest @@ -78,7 +78,9 @@ title20="Test 20: Serialization and code copy 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 +title24="Test 24: Non-UTF pattern conversion tests" +title25="Test 25: UTF pattern conversion tests" +maxtest=25 if [ $# -eq 1 -a "$1" = "list" ]; then echo $title0 @@ -105,6 +107,8 @@ if [ $# -eq 1 -a "$1" = "list" ]; then echo $title21 echo $title22 echo $title23 + echo $title24 + echo $title25 exit 0 fi @@ -232,6 +236,8 @@ do20=no do21=no do22=no do23=no +do24=no +do25=no while [ $# -gt 0 ] ; do case $1 in @@ -259,6 +265,8 @@ while [ $# -gt 0 ] ; do 21) do21=yes;; 22) do22=yes;; 23) do23=yes;; + 24) do24=yes;; + 25) do25=yes;; -8) arg8=yes;; -16) arg16=yes;; -32) arg32=yes;; @@ -308,27 +316,14 @@ if [ $link_size -gt 4 ] ; then exit 1 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 by gcc with -fsanitize=address. If "bigstack" is on the command -# line, set even bigger numbers. 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. Test 2 now has code to automatically try again with a 64M -# stack if it crashes when test2stack is "-S 16" when matching with the -# interpreter. +# If it is possible to set the system stack size and -bigstack was given, +# set up a large stack. $sim ./pcre2test -S 1 /dev/null /dev/null -if [ $? -eq 0 ] ; then - if [ "$bigstack" = "" ] ; then - test2stack="-S 16" - defaultstack="" - else - test2stack="-S 1024" - defaultstack="-S 64" - fi +if [ $? -eq 0 -a "$bigstack" != "" ] ; then + setstack="-S 64" else - test2stack="" - defaultstack="" + setstack="" fi # All of 8-bit, 16-bit, and 32-bit character strings may be supported, but only @@ -420,7 +415,8 @@ if [ $do0 = no -a $do1 = no -a $do2 = no -a $do3 = 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 -a \ - $do20 = no -a $do21 = no -a $do22 = no -a $do23 = no \ + $do20 = no -a $do21 = no -a $do22 = no -a $do23 = no -a \ + $do24 = no -a $do25 = no \ ]; then do0=yes do1=yes @@ -446,6 +442,8 @@ if [ $do0 = no -a $do1 = no -a $do2 = no -a $do3 = no -a \ do21=yes do22=yes do23=yes + do24=yes + do25=yes fi # Handle any explicit skips at this stage, so that an argument list may consist @@ -476,7 +474,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $do0 = yes ] ; then echo $title0 - echo '/abc/jit,memory' >testSinput + echo '/abc/jit,memory,framesize' >testSinput echo ' abc' >>testSinput echo '' >testtry checkspecial '-C' @@ -490,7 +488,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $do1 = yes ] ; then echo $title1 for opt in "" $jitopt; do - $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput1 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $testdata/testinput1 testtry checkresult $? 1 "$opt" done fi @@ -500,34 +498,10 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $do2 = yes ] ; then echo $title2 "(excluding UTF-$bits)" for opt in "" $jitopt; do - $sim $valgrind ${opt:+$vjs} ./pcre2test -q $test2stack $bmode $opt $testdata/testinput2 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $testdata/testinput2 testtry if [ $? = 0 ] ; then - $sim $valgrind ${opt:+$vjs} ./pcre2test -q $bmode $opt -error -63,-62,-2,-1,0,100,188,189 >>testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $bmode $opt -error -70,-62,-2,-1,0,100,101,191,200 >>testtry checkresult $? 2 "$opt" - else - echo " " - echo "** Test 2, when run under the interpreter, requires a lot of stack." - echo "** If it has crashed with a segmentation fault, it may be that you" - echo "** do not have enough stack available by default. Please see the" - echo "** 'pcre2stack' man page for a discussion of PCRE2's stack usage." - if [ "$test2stack" != "-S 16" -o "$opt" != "" ]; then - echo " " - exit 1 - fi - echo " " - echo "** Trying again with an increased stack size." - echo " " - echo $title2 "(excluding UTF-$bits) (64M stack)" - $sim $valgrind ${opt:+$vjs} ./pcre2test -q -S 64 $bmode $opt $testdata/testinput2 testtry - if [ $? = 0 ] ; then - $sim $valgrind ${opt:+$vjs} ./pcre2test -q $bmode $opt -error -63,-62,-2,-1,0,100,188,189 >>testtry - checkresult $? 2 "$opt" - else - echo " " - echo "** Failed with an increased stack size. Tests abandoned." - echo " " - exit 1 - fi fi done fi @@ -577,7 +551,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ "$locale" != "" ] ; then echo $title3 "(using '$locale' locale)" for opt in "" $jitopt; do - $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $infile testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $infile testtry if [ $? = 0 ] ; then case "$opt" in -jit) with=" with JIT";; @@ -614,7 +588,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 ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput4 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $testdata/testinput4 testtry checkresult $? 4 "$opt" done fi @@ -626,7 +600,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 ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput5 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $testdata/testinput5 testtry checkresult $? 5 "$opt" done fi @@ -636,7 +610,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $do6 = yes ] ; then echo $title6 - $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput6 testtry + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput6 testtry checkresult $? 6 "" fi @@ -645,7 +619,7 @@ 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 $defaultstack $bmode $opt $testdata/testinput7 testtry + $sim $valgrind ./pcre2test -q $setstack $bmode $opt $testdata/testinput7 testtry checkresult $? 7 "" fi fi @@ -663,7 +637,7 @@ 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 $defaultstack $bmode $testdata/testinput8 testtry + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput8 testtry checkresult $? 8-$bits-$link_size "" fi fi @@ -676,7 +650,7 @@ for bmode in "$test8" "$test16" "$test32"; do echo " Skipped when running 16/32-bit tests" else for opt in "" $jitopt; do - $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput9 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $testdata/testinput9 testtry checkresult $? 9 "$opt" done fi @@ -692,7 +666,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 ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput10 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $testdata/testinput10 testtry checkresult $? 10 "$opt" done fi @@ -706,7 +680,7 @@ for bmode in "$test8" "$test16" "$test32"; do echo " Skipped when running 8-bit tests" else for opt in "" $jitopt; do - $sim $valgrind ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput11 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $testdata/testinput11 testtry checkresult $? 11-$bits "$opt" done fi @@ -723,7 +697,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 ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput12 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $testdata/testinput12 testtry checkresult $? 12-$bits "$opt" done fi @@ -736,7 +710,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ "$bits" = "8" ] ; then echo " Skipped when running 8-bit tests" else - $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput13 testtry + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput13 testtry checkresult $? 13 "" fi fi @@ -748,7 +722,7 @@ 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 $defaultstack $bmode $opt $testdata/testinput14 testtry + $sim $valgrind ./pcre2test -q $setstack $bmode $opt $testdata/testinput14 testtry checkresult $? 14-$bits "" fi fi @@ -757,7 +731,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $do15 = yes ] ; then echo $title15 - $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput15 testtry + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput15 testtry checkresult $? 15 "" fi @@ -768,7 +742,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $jit -ne 0 ] ; then echo " Skipped because JIT is available" else - $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput16 testtry + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput16 testtry checkresult $? 16 "" fi fi @@ -780,7 +754,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $jit -eq 0 -o "$nojit" = "yes" ] ; then echo " Skipped because JIT is not available or nojit was specified" else - $sim $valgrind $vjs ./pcre2test -q $defaultstack $bmode $testdata/testinput17 testtry + $sim $valgrind $vjs ./pcre2test -q $setstack $bmode $testdata/testinput17 testtry checkresult $? 17 "" fi fi @@ -792,7 +766,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ "$bits" = "16" -o "$bits" = "32" ] ; then echo " Skipped when running 16/32-bit tests" else - $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput18 testtry + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput18 testtry checkresult $? 18 "" fi fi @@ -806,7 +780,7 @@ for bmode in "$test8" "$test16" "$test32"; do elif [ $utf -eq 0 ] ; then echo " Skipped because UTF-$bits support is not available" else - $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput19 testtry + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput19 testtry checkresult $? 19 "" fi fi @@ -815,7 +789,7 @@ for bmode in "$test8" "$test16" "$test32"; do if [ $do20 = yes ] ; then echo $title20 - $sim $valgrind ./pcre2test -q $defaultstack $bmode $testdata/testinput20 testtry + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput20 testtry checkresult $? 20 "" fi @@ -827,7 +801,7 @@ for bmode in "$test8" "$test16" "$test32"; do 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 + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $testdata/testinput21 testtry checkresult $? 21 "$opt" done fi @@ -843,7 +817,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 ${opt:+$vjs} ./pcre2test -q $defaultstack $bmode $opt $testdata/testinput22 testtry + $sim $valgrind ${opt:+$vjs} ./pcre2test -q $setstack $bmode $opt $testdata/testinput22 testtry checkresult $? 22-$bits "$opt" done fi @@ -856,11 +830,31 @@ for bmode in "$test8" "$test16" "$test32"; do 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 + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput23 testtry checkresult $? 23 "" fi fi + # Non-UTF pattern conversion tests + + if [ "$do24" = yes ] ; then + echo $title24 + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput24 testtry + checkresult $? 24 "" + fi + + # UTF pattern conversion tests + + if [ "$do25" = yes ] ; then + echo $title25 + if [ $utf -eq 0 ] ; then + echo " Skipped because UTF-$bits support is not available" + else + $sim $valgrind ./pcre2test -q $setstack $bmode $testdata/testinput25 testtry + checkresult $? 25 "" + fi + fi + # End of loop for 8/16/32-bit tests done diff --git a/pcre2-10.22/aclocal.m4 b/pcre2-10.32/aclocal.m4 similarity index 93% rename from pcre2-10.22/aclocal.m4 rename to pcre2-10.32/aclocal.m4 index d8d43fb0d..cc10b262e 100644 --- a/pcre2-10.22/aclocal.m4 +++ b/pcre2-10.32/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.15 -*- Autoconf -*- +# generated automatically by aclocal 1.15.1 -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2017 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -20,9 +20,9 @@ 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'.])]) -dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- -dnl serial 11 (pkg-config-0.29.1) -dnl +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 11 (pkg-config-0.29.1) + dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl @@ -296,7 +296,75 @@ AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR -# Copyright (C) 2002-2014 Free Software Foundation, Inc. +dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------ +dnl +dnl Prepare a "--with-" configure option using the lowercase +dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and +dnl PKG_CHECK_MODULES in a single macro. +AC_DEFUN([PKG_WITH_MODULES], +[ +m4_pushdef([with_arg], m4_tolower([$1])) + +m4_pushdef([description], + [m4_default([$5], [build with ]with_arg[ support])]) + +m4_pushdef([def_arg], [m4_default([$6], [auto])]) +m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) +m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) + +m4_case(def_arg, + [yes],[m4_pushdef([with_without], [--without-]with_arg)], + [m4_pushdef([with_without],[--with-]with_arg)]) + +AC_ARG_WITH(with_arg, + AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, + [AS_TR_SH([with_]with_arg)=def_arg]) + +AS_CASE([$AS_TR_SH([with_]with_arg)], + [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], + [auto],[PKG_CHECK_MODULES([$1],[$2], + [m4_n([def_action_if_found]) $3], + [m4_n([def_action_if_not_found]) $4])]) + +m4_popdef([with_arg]) +m4_popdef([description]) +m4_popdef([def_arg]) + +])dnl PKG_WITH_MODULES + +dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ----------------------------------------------- +dnl +dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES +dnl check._[VARIABLE-PREFIX] is exported as make variable. +AC_DEFUN([PKG_HAVE_WITH_MODULES], +[ +PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) + +AM_CONDITIONAL([HAVE_][$1], + [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) +])dnl PKG_HAVE_WITH_MODULES + +dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------------------ +dnl +dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after +dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make +dnl and preprocessor variable. +AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], +[ +PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) + +AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], + [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) +])dnl PKG_HAVE_DEFINE_WITH_MODULES + +# Copyright (C) 2002-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -311,7 +379,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.15' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.15], [], +m4_if([$1], [1.15.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -327,12 +395,12 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.15])dnl +[AM_AUTOMAKE_VERSION([1.15.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) -# Copyright (C) 2011-2014 Free Software Foundation, Inc. +# Copyright (C) 2011-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -394,7 +462,7 @@ AC_SUBST([AR])dnl # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -446,7 +514,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd` # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# Copyright (C) 1997-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -477,7 +545,7 @@ AC_CONFIG_COMMANDS_PRE( Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -668,7 +736,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -744,7 +812,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -941,7 +1009,7 @@ for _am_header in $config_headers :; do done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -962,7 +1030,7 @@ if test x"${install_sh+set}" != xset; then fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# Copyright (C) 2003-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -981,45 +1049,9 @@ 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. +# Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1069,7 +1101,7 @@ rm -f confinc confmf # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# Copyright (C) 1997-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1108,7 +1140,7 @@ fi # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1137,7 +1169,7 @@ AC_DEFUN([_AM_SET_OPTIONS], AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1184,7 +1216,7 @@ AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1203,7 +1235,7 @@ AC_DEFUN([AM_RUN_LOG], # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1284,7 +1316,7 @@ AC_CONFIG_COMMANDS_PRE( rm -f conftest.file ]) -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1344,7 +1376,7 @@ AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1372,7 +1404,7 @@ fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2014 Free Software Foundation, Inc. +# Copyright (C) 2006-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1391,7 +1423,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2014 Free Software Foundation, Inc. +# Copyright (C) 2004-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff --git a/pcre2-10.22/ar-lib b/pcre2-10.32/ar-lib similarity index 99% rename from pcre2-10.22/ar-lib rename to pcre2-10.32/ar-lib index 463b9ec02..05094d34c 100755 --- a/pcre2-10.22/ar-lib +++ b/pcre2-10.32/ar-lib @@ -4,7 +4,7 @@ me=ar-lib scriptversion=2012-03-01.08; # UTC -# Copyright (C) 2010-2014 Free Software Foundation, Inc. +# Copyright (C) 2010-2017 Free Software Foundation, Inc. # Written by Peter Rosin . # # This program is free software; you can redistribute it and/or modify diff --git a/pcre2-10.22/cmake/COPYING-CMAKE-SCRIPTS b/pcre2-10.32/cmake/COPYING-CMAKE-SCRIPTS similarity index 100% rename from pcre2-10.22/cmake/COPYING-CMAKE-SCRIPTS rename to pcre2-10.32/cmake/COPYING-CMAKE-SCRIPTS diff --git a/pcre2-10.22/cmake/FindEditline.cmake b/pcre2-10.32/cmake/FindEditline.cmake similarity index 100% rename from pcre2-10.22/cmake/FindEditline.cmake rename to pcre2-10.32/cmake/FindEditline.cmake diff --git a/pcre2-10.22/cmake/FindPackageHandleStandardArgs.cmake b/pcre2-10.32/cmake/FindPackageHandleStandardArgs.cmake similarity index 100% rename from pcre2-10.22/cmake/FindPackageHandleStandardArgs.cmake rename to pcre2-10.32/cmake/FindPackageHandleStandardArgs.cmake diff --git a/pcre2-10.22/cmake/FindReadline.cmake b/pcre2-10.32/cmake/FindReadline.cmake similarity index 100% rename from pcre2-10.22/cmake/FindReadline.cmake rename to pcre2-10.32/cmake/FindReadline.cmake diff --git a/pcre2-10.22/compile b/pcre2-10.32/compile similarity index 97% rename from pcre2-10.22/compile rename to pcre2-10.32/compile index a85b723c7..2ab71e4ea 100755 --- a/pcre2-10.22/compile +++ b/pcre2-10.32/compile @@ -1,9 +1,9 @@ #! /bin/sh # Wrapper for compilers which do not understand '-c -o'. -scriptversion=2012-10-14.11; # UTC +scriptversion=2016-01-11.22; # UTC -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2017 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify @@ -255,7 +255,8 @@ EOF echo "compile $scriptversion" exit $? ;; - cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac @@ -342,6 +343,6 @@ exit $ret # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff --git a/pcre2-10.22/config-cmake.h.in b/pcre2-10.32/config-cmake.h.in similarity index 85% rename from pcre2-10.22/config-cmake.h.in rename to pcre2-10.32/config-cmake.h.in index 0cfd8b134..9b4f15da4 100644 --- a/pcre2-10.22/config-cmake.h.in +++ b/pcre2-10.32/config-cmake.h.in @@ -25,6 +25,7 @@ #cmakedefine SUPPORT_LIBZ 1 #cmakedefine SUPPORT_JIT 1 +#cmakedefine SLJIT_PROT_EXECUTABLE_ALLOCATOR 1 #cmakedefine SUPPORT_PCRE2GREP_JIT 1 #cmakedefine SUPPORT_UNICODE 1 #cmakedefine SUPPORT_VALGRIND 1 @@ -36,11 +37,13 @@ #cmakedefine NEVER_BACKSLASH_C 1 #define LINK_SIZE @PCRE2_LINK_SIZE@ +#define HEAP_LIMIT @PCRE2_HEAP_LIMIT@ #define MATCH_LIMIT @PCRE2_MATCH_LIMIT@ -#define MATCH_LIMIT_RECURSION @PCRE2_MATCH_LIMIT_RECURSION@ +#define MATCH_LIMIT_DEPTH @PCRE2_MATCH_LIMIT_DEPTH@ #define NEWLINE_DEFAULT @NEWLINE_DEFAULT@ #define PARENS_NEST_LIMIT @PCRE2_PARENS_NEST_LIMIT@ #define PCRE2GREP_BUFSIZE @PCRE2GREP_BUFSIZE@ +#define PCRE2GREP_MAX_BUFSIZE @PCRE2GREP_MAX_BUFSIZE@ #define MAX_NAME_SIZE 32 #define MAX_NAME_COUNT 10000 diff --git a/pcre2-10.22/config.guess b/pcre2-10.32/config.guess similarity index 89% rename from pcre2-10.22/config.guess rename to pcre2-10.32/config.guess index 6c32c8645..2193702b1 100755 --- a/pcre2-10.22/config.guess +++ b/pcre2-10.32/config.guess @@ -1,8 +1,8 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2014 Free Software Foundation, Inc. +# Copyright 1992-2017 Free Software Foundation, Inc. -timestamp='2014-11-04' +timestamp='2017-05-27' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -27,7 +27,7 @@ timestamp='2014-11-04' # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to . @@ -50,7 +50,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2014 Free Software Foundation, Inc. +Copyright 1992-2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -168,19 +168,29 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || \ + echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. + # to ELF recently (or will in the future) and ABI. case "${UNAME_MACHINE_ARCH}" in + earm*) + os=netbsdelf + ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ @@ -197,6 +207,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in os=netbsd ;; esac + # Determine ABI tags. + case "${UNAME_MACHINE_ARCH}" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` + ;; + esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need @@ -207,13 +224,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" + echo "${machine}-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` @@ -223,6 +240,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} + exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; @@ -235,6 +256,9 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; + *:Sortix:*:*) + echo ${UNAME_MACHINE}-unknown-sortix + exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) @@ -251,42 +275,42 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; + UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; + UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; + UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; + UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; + UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; + UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; + UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; + UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; + UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 @@ -359,16 +383,16 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build - SUN_ARCH="i386" + SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then - SUN_ARCH="x86_64" + SUN_ARCH=x86_64 fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` @@ -393,7 +417,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} @@ -618,13 +642,13 @@ EOF sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi @@ -663,11 +687,11 @@ EOF exit (0); } EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = "hppa2.0w" ] + if [ ${HP_ARCH} = hppa2.0w ] then eval $set_cc_for_build @@ -680,12 +704,12 @@ EOF # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then - HP_ARCH="hppa2.0w" + HP_ARCH=hppa2.0w else - HP_ARCH="hppa64" + HP_ARCH=hppa64 fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} @@ -790,14 +814,14 @@ EOF echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) @@ -813,10 +837,11 @@ EOF UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; esac + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin @@ -879,7 +904,7 @@ EOF exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix @@ -902,7 +927,7 @@ EOF EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) @@ -933,6 +958,9 @@ EOF crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; + e2k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; @@ -945,6 +973,9 @@ EOF ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; + k1om:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; @@ -970,6 +1001,9 @@ EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; + mips64el:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; @@ -1002,6 +1036,9 @@ EOF ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; @@ -1021,7 +1058,7 @@ EOF echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} @@ -1100,7 +1137,7 @@ EOF # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that + # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; @@ -1249,6 +1286,9 @@ EOF SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux${UNAME_RELEASE} + exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; @@ -1262,16 +1302,23 @@ EOF UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub @@ -1286,7 +1333,7 @@ EOF exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then + if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi @@ -1295,15 +1342,18 @@ EOF *:QNX:*:4*) echo i386-pc-qnx exit ;; - NEO-?:NONSTOP_KERNEL:*:*) + NEO-*:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; - NSR-?:NONSTOP_KERNEL:*:*) + NSR-*:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; + NSX-*:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk${UNAME_RELEASE} + exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; @@ -1317,7 +1367,7 @@ EOF # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - if test "$cputype" = "386"; then + if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" @@ -1359,7 +1409,7 @@ EOF echo i386-pc-xenix exit ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos @@ -1370,23 +1420,25 @@ EOF x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; esac cat >&2 < in order to provide the needed -information to handle your system. +If $0 has already been updated, send the following data and any +information you think might be pertinent to config-patches@gnu.org to +provide the necessary information to handle your system. config.guess timestamp = $timestamp diff --git a/pcre2-10.22/config.sub b/pcre2-10.32/config.sub similarity index 96% rename from pcre2-10.22/config.sub rename to pcre2-10.32/config.sub index 7ffe37378..40ea5dfe1 100755 --- a/pcre2-10.22/config.sub +++ b/pcre2-10.32/config.sub @@ -1,8 +1,8 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2014 Free Software Foundation, Inc. +# Copyright 1992-2017 Free Software Foundation, Inc. -timestamp='2014-12-03' +timestamp='2017-04-02' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ timestamp='2014-12-03' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -53,8 +53,7 @@ timestamp='2014-12-03' me=`echo "$0" | sed -e 's,.*/,,'` usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. @@ -68,7 +67,7 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright 1992-2014 Free Software Foundation, Inc. +Copyright 1992-2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -117,8 +116,8 @@ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | \ - kopensolaris*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ + kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` @@ -255,15 +254,16 @@ case $basic_machine in | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ + | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ - | epiphany \ - | fido | fr30 | frv \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ - | i370 | i860 | i960 | ia64 \ + | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ @@ -301,11 +301,12 @@ case $basic_machine in | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ @@ -314,6 +315,7 @@ case $basic_machine in | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ + | wasm32 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) @@ -376,17 +378,18 @@ case $basic_machine in | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ + | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ + | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ + | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ @@ -427,13 +430,15 @@ case $basic_machine in | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pru-* \ | pyramid-* \ + | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ @@ -442,6 +447,7 @@ case $basic_machine in | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ + | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ @@ -518,6 +524,9 @@ case $basic_machine in basic_machine=i386-pc os=-aros ;; + asmjs) + basic_machine=asmjs-unknown + ;; aux) basic_machine=m68k-apple os=-aux @@ -638,6 +647,14 @@ case $basic_machine in basic_machine=m68k-bull os=-sysv3 ;; + e500v[12]) + basic_machine=powerpc-unknown + os=$os"spe" + ;; + e500v[12]-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + os=$os"spe" + ;; ebmon29k) basic_machine=a29k-amd os=-ebmon @@ -933,6 +950,9 @@ case $basic_machine in nsr-tandem) basic_machine=nsr-tandem ;; + nsx-tandem) + basic_machine=nsx-tandem + ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf @@ -1017,7 +1037,7 @@ case $basic_machine in ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; - ppcle | powerpclittle | ppc-le | powerpc-little) + ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) @@ -1027,7 +1047,7 @@ case $basic_machine in ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) + ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) @@ -1228,6 +1248,9 @@ case $basic_machine in basic_machine=a29k-wrs os=-vxworks ;; + wasm32) + basic_machine=wasm32-unknown + ;; w65*) basic_machine=w65-wdc os=-none @@ -1373,18 +1396,18 @@ case $os in | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* \ + | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -bitrig* | -openbsd* | -solidbsd* \ + | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* | -cegcc* \ + | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ @@ -1393,7 +1416,8 @@ case $os in | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ + | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) @@ -1525,6 +1549,8 @@ case $os in ;; -nacl*) ;; + -ios) + ;; -none) ;; *) @@ -1620,6 +1646,9 @@ case $basic_machine in sparc-* | *-sun) os=-sunos4.1.1 ;; + pru-*) + os=-elf + ;; *-be) os=-beos ;; diff --git a/pcre2-10.22/configure b/pcre2-10.32/configure similarity index 95% rename from pcre2-10.22/configure rename to pcre2-10.32/configure index 622da4fa8..6091d75bd 100755 --- a/pcre2-10.22/configure +++ b/pcre2-10.32/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.22. +# Generated by GNU Autoconf 2.69 for PCRE2 10.32. # # # 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.22' -PACKAGE_STRING='PCRE2 10.22' +PACKAGE_VERSION='10.32' +PACKAGE_STRING='PCRE2 10.32' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -659,6 +659,8 @@ PTHREAD_CC ax_pthread_config PCRE2_STATIC_CFLAG LIBREADLINE +WITH_FUZZ_SUPPORT_FALSE +WITH_FUZZ_SUPPORT_TRUE WITH_VALGRIND_FALSE WITH_VALGRIND_TRUE WITH_UNICODE_FALSE @@ -675,6 +677,8 @@ WITH_PCRE2_16_FALSE WITH_PCRE2_16_TRUE WITH_PCRE2_8_FALSE WITH_PCRE2_8_TRUE +PCRE2_HAVE_INTTYPES_H +PCRE2_HAVE_STDINT_H enable_pcre2_32 enable_pcre2_16 enable_pcre2_8 @@ -712,6 +716,8 @@ build_os build_vendor build_cpu build +ac_ct_AR +AR EGREP GREP CPP @@ -732,11 +738,6 @@ CPPFLAGS LDFLAGS CFLAGS CC -ac_ct_AR -AR -MAINT -MAINTAINER_MODE_FALSE -MAINTAINER_MODE_TRUE AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V @@ -806,7 +807,6 @@ ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules -enable_maintainer_mode enable_dependency_tracking enable_shared enable_static @@ -824,6 +824,7 @@ enable_pcre2_16 enable_pcre2_32 enable_debug enable_jit +enable_jit_sealloc enable_pcre2grep_jit enable_pcre2grep_callout enable_rebuild_chartables @@ -833,22 +834,27 @@ enable_newline_is_lf enable_newline_is_crlf enable_newline_is_anycrlf enable_newline_is_any +enable_newline_is_nul enable_bsr_anycrlf enable_never_backslash_C enable_ebcdic enable_ebcdic_nl25 -enable_stack_for_recursion enable_pcre2grep_libz enable_pcre2grep_libbz2 with_pcre2grep_bufsize +with_pcre2grep_max_bufsize enable_pcre2test_libedit enable_pcre2test_libreadline with_link_size with_parens_nest_limit +with_heap_limit with_match_limit +with_match_limit_depth with_match_limit_recursion enable_valgrind enable_coverage +enable_fuzz_support +enable_stack_for_recursion ' ac_precious_vars='build_alias host_alias @@ -1407,7 +1413,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.22 to adapt to many kinds of systems. +\`configure' configures PCRE2 10.32 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1477,7 +1483,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of PCRE2 10.22:";; + short | recursive ) echo "Configuration of PCRE2 10.32:";; esac cat <<\_ACEOF @@ -1487,9 +1493,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 @@ -1505,6 +1508,7 @@ Optional Features: --enable-pcre2-32 enable 32 bit character support --enable-debug enable debugging code --enable-jit enable Just-In-Time compiling support + --enable-jit-sealloc enable SELinux compatible execmem allocator in JIT --disable-pcre2grep-jit disable JIT support in pcre2grep --disable-pcre2grep-callout disable callout script support in pcre2grep @@ -1518,6 +1522,7 @@ Optional Features: --enable-newline-is-anycrlf use CR, LF, or CRLF as newline sequence --enable-newline-is-any use any valid Unicode newline sequence + --enable-newline-is-nul use NUL (binary zero) as newline character --enable-bsr-anycrlf \R matches only CR, LF, CRLF by default --enable-never-backslash-C use of \C causes an error @@ -1526,8 +1531,6 @@ Optional Features: environments; it implies --enable-rebuild-chartables --enable-ebcdic-nl25 set EBCDIC code for NL to 0x25 instead of 0x15; it implies --enable-ebcdic - --disable-stack-for-recursion - don't use stack recursion when matching --enable-pcre2grep-libz link pcre2grep with libz to handle .gz files --enable-pcre2grep-libbz2 link pcre2grep with libbz2 to handle .bz2 files @@ -1535,8 +1538,9 @@ Optional Features: link pcre2test with libedit --enable-pcre2test-libreadline link pcre2test with libreadline - --enable-valgrind valgrind support + --enable-valgrind enable valgrind support --enable-coverage enable code coverage reports using gcov + --enable-fuzz-support enable fuzzer support Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1550,15 +1554,22 @@ Optional Packages: --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --with-pcre2grep-bufsize=N - pcre2grep buffer size (default=20480, minimum=8192) + pcre2grep initial buffer size (default=20480, + minimum=8192) + --with-pcre2grep-max-bufsize=N + pcre2grep maximum buffer size (default=1048576, + minimum=8192) --with-link-size=N internal link size (2, 3, or 4 allowed; default=2) --with-parens-nest-limit=N nested parentheses limit (default=250) + --with-heap-limit=N default limit on heap memory (kibibytes, + default=20000000) --with-match-limit=N default limit on internal looping (default=10000000) - --with-match-limit-recursion=N - default limit on internal recursion + --with-match-limit-depth=N + default limit on match tree depth (default=MATCH_LIMIT) + Some influential environment variables: CC C compiler command CFLAGS C compiler flags @@ -1648,7 +1659,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -PCRE2 configure 10.22 +PCRE2 configure 10.32 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1737,6 +1748,93 @@ fi } # ac_fn_c_try_cpp +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes @@ -1999,93 +2097,6 @@ $as_echo "$ac_res" >&6; } } # ac_fn_c_check_func -# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists, giving a warning if it cannot be compiled using -# the include files in INCLUDES and setting the cache variable VAR -# accordingly. -ac_fn_c_check_header_mongrel () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -else - # Is the header compilable? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 -$as_echo_n "checking $2 usability... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_header_compiler=yes -else - ac_header_compiler=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 -$as_echo "$ac_header_compiler" >&6; } - -# Is the header present? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 -$as_echo_n "checking $2 presence... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <$2> -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - ac_header_preproc=yes -else - ac_header_preproc=no -fi -rm -f conftest.err conftest.i conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 -$as_echo "$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( - yes:no: ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 -$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; - no:yes:* ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 -$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 -$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 -$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 -$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; -esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=\$ac_header_compiler" -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_mongrel - # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache @@ -2143,7 +2154,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.22, which was +It was created by PCRE2 $as_me 10.32, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3007,7 +3018,7 @@ fi # Define the identity of the package. PACKAGE='pcre2' - VERSION='10.22' + VERSION='10.32' cat >>confdefs.h <<_ACEOF @@ -3142,97 +3153,18 @@ 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 was added at the suggestion of libtoolize (03-Jan-10) -# END FISH PATCH - -# This is a new thing required to stop a warning from automake 1.12 -DEPDIR="${am__leading_dot}deps" - -ac_config_commands="$ac_config_commands depfiles" - - -am_make=${MAKE-make} -cat > confinc << 'END' -am__doit: - @echo this is the am__doit target -.PHONY: am__doit -END -# If we don't find an include directive, just comment out the code. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 -$as_echo_n "checking for style of include used by $am_make... " >&6; } -am__include="#" -am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD - ;; - esac -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 -$as_echo "$_am_result" >&6; } -rm -f confinc confmf - -# Check whether --enable-dependency-tracking was given. -if test "${enable_dependency_tracking+set}" = set; then : - enableval=$enable_dependency_tracking; -fi - -if test "x$enable_dependency_tracking" != xno; then - am_depcomp="$ac_aux_dir/depcomp" - AMDEPBACKSLASH='\' - am__nodep='_no' -fi - if test "x$enable_dependency_tracking" != xno; then - AMDEP_TRUE= - AMDEP_FALSE='#' -else - AMDEP_TRUE='#' - AMDEP_FALSE= -fi +# The default CFLAGS in Autoconf are "-g -O2" for gcc and just "-g" for any +# other compiler. There doesn't seem to be a standard way of getting rid of the +# -g (which I don't think is needed for a production library). This fudge seems +# to achieve the necessary. First, we remember the externally set values of +# CFLAGS. Then call the AC_PROG_CC macro to find the compiler - if CFLAGS is +# not set, it will be set to Autoconf's defaults. Afterwards, if the original +# values were not set, remove the -g from the Autoconf defaults. +remember_set_CFLAGS="$CFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -4081,918 +4013,69 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu +DEPDIR="${am__leading_dot}deps" -depcc="$CC" am_compiler_list= - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 -$as_echo_n "checking dependency style of $depcc... " >&6; } -if ${am_cv_CC_dependencies_compiler_type+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then - # We make a subdir and do the tests there. Otherwise we can end up - # making bogus files that we don't know about and never remove. For - # instance it was reported that on HP-UX the gcc test will end up - # making a dummy file named 'D' -- because '-MD' means "put the output - # in D". - rm -rf conftest.dir - mkdir conftest.dir - # Copy depcomp to subdir because otherwise we won't find it if we're - # using a relative directory. - cp "$am_depcomp" conftest.dir - cd conftest.dir - # We will build objects and dependencies in a subdirectory because - # it helps to detect inapplicable dependency modes. For instance - # both Tru64's cc and ICC support -MD to output dependencies as a - # side effect of compilation, but ICC will put the dependencies in - # the current directory while Tru64 will put them in the object - # directory. - mkdir sub - - am_cv_CC_dependencies_compiler_type=none - if test "$am_compiler_list" = ""; then - am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` - fi - am__universal=false - case " $depcc " in #( - *\ -arch\ *\ -arch\ *) am__universal=true ;; - esac - - for depmode in $am_compiler_list; do - # Setup a source with many dependencies, because some compilers - # like to wrap large dependency lists on column 80 (with \), and - # we should not choose a depcomp mode which is confused by this. - # - # We need to recreate these files for each test, as the compiler may - # overwrite some of them when testing with obscure command lines. - # This happens at least with the AIX C compiler. - : > sub/conftest.c - for i in 1 2 3 4 5 6; do - echo '#include "conftst'$i'.h"' >> sub/conftest.c - # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with - # Solaris 10 /bin/sh. - echo '/* dummy */' > sub/conftst$i.h - done - echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - - # We check with '-c' and '-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle '-M -o', and we need to detect this. Also, some Intel - # versions had trouble with output in subdirs. - am__obj=sub/conftest.${OBJEXT-o} - am__minus_obj="-o $am__obj" - case $depmode in - gcc) - # This depmode causes a compiler race in universal mode. - test "$am__universal" = false || continue - ;; - nosideeffect) - # After this tag, mechanisms are not by side-effect, so they'll - # only be used when explicitly requested. - if test "x$enable_dependency_tracking" = xyes; then - continue - else - break - fi - ;; - msvc7 | msvc7msys | msvisualcpp | msvcmsys) - # This compiler won't grok '-c -o', but also, the minuso test has - # not run yet. These depmodes are late enough in the game, and - # so weak that their functioning should not be impacted. - am__obj=conftest.${OBJEXT-o} - am__minus_obj= - ;; - none) break ;; - esac - if depmode=$depmode \ - source=sub/conftest.c object=$am__obj \ - depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ - >/dev/null 2>conftest.err && - grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep $am__obj sub/conftest.Po > /dev/null 2>&1 && - ${MAKE-make} -s -f confmf > /dev/null 2>&1; then - # icc doesn't choke on unknown options, it will just issue warnings - # or remarks (even with -Werror). So we grep stderr for any message - # that says an option was ignored or not supported. - # When given -MP, icc 7.0 and 7.1 complain thusly: - # icc: Command line warning: ignoring option '-M'; no argument required - # The diagnosis changed in icc 8.0: - # icc: Command line remark: option '-MP' not supported - if (grep 'ignoring option' conftest.err || - grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else - am_cv_CC_dependencies_compiler_type=$depmode - break - fi - fi - done - - cd .. - rm -rf conftest.dir -else - am_cv_CC_dependencies_compiler_type=none -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 -$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } -CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type - - if - test "x$enable_dependency_tracking" != xno \ - && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then - am__fastdepCC_TRUE= - am__fastdepCC_FALSE='#' -else - am__fastdepCC_TRUE='#' - am__fastdepCC_FALSE= -fi +ac_config_commands="$ac_config_commands depfiles" - -if test -n "$ac_tool_prefix"; then - for ac_prog in ar lib "link -lib" - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AR+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$AR"; then - ac_cv_prog_AR="$AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_AR="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AR=$ac_cv_prog_AR -if test -n "$AR"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 -$as_echo "$AR" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$AR" && break - done -fi -if test -z "$AR"; then - ac_ct_AR=$AR - for ac_prog in ar lib "link -lib" -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_AR+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_AR"; then - ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_AR="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_AR=$ac_cv_prog_ac_ct_AR -if test -n "$ac_ct_AR"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 -$as_echo "$ac_ct_AR" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_AR" && break -done - - if test "x$ac_ct_AR" = x; then - AR="false" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - AR=$ac_ct_AR - fi -fi - -: ${AR=ar} - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5 -$as_echo_n "checking the archiver ($AR) interface... " >&6; } -if ${am_cv_ar_interface+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - am_cv_ar_interface=ar - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -int some_variable = 0; -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5' - { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 - (eval $am_ar_try) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if test "$ac_status" -eq 0; then - am_cv_ar_interface=ar - else - am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5' - { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 - (eval $am_ar_try) 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if test "$ac_status" -eq 0; then - am_cv_ar_interface=lib - else - am_cv_ar_interface=unknown - fi - fi - rm -f conftest.lib libconftest.a - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5 -$as_echo "$am_cv_ar_interface" >&6; } - -case $am_cv_ar_interface in -ar) - ;; -lib) - # Microsoft lib, so override with the ar-lib wrapper script. - # FIXME: It is wrong to rewrite AR. - # But if we don't then we get into trouble of one sort or another. - # A longer-term fix would be to have automake use am__AR in this case, - # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something - # similar. - AR="$am_aux_dir/ar-lib $AR" - ;; -unknown) - as_fn_error $? "could not determine $AR interface" "$LINENO" 5 +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU ;; esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi -# This was added at the suggestion of libtoolize (03-Jan-10) +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi -# The default CFLAGS in Autoconf are "-g -O2" for gcc and just "-g" for any -# other compiler. There doesn't seem to be a standard way of getting rid of the -# -g (which I don't think is needed for a production library). This fudge seems -# to achieve the necessary. First, we remember the externally set values of -# CFLAGS. Then call the AC_PROG_CC macro to find the compiler - if CFLAGS is -# not set, it will be set to Autoconf's defaults. Afterwards, if the original -# values were not set, remove the -g from the Autoconf defaults. - -remember_set_CFLAGS="$CFLAGS" - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + AMDEP_TRUE='#' + AMDEP_FALSE= fi -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 -$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } -if ${am_cv_prog_cc_c_o+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF - # Make sure it works both with $CC and with simple cc. - # Following AC_PROG_CC_C_O, we do the test twice because some - # compilers refuse to overwrite an existing .o file with -o, - # though they will create one. - am_cv_prog_cc_c_o=yes - for am_i in 1 2; do - if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 - ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } \ - && test -f conftest2.$ac_objext; then - : OK - else - am_cv_prog_cc_c_o=no - break - fi - done - rm -f core conftest* - unset am_i -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 -$as_echo "$am_cv_prog_cc_c_o" >&6; } -if test "$am_cv_prog_cc_c_o" != yes; then - # Losing compiler, so override with the script. - # FIXME: It is wrong to rewrite CC. - # But if we don't then we get into trouble of one sort or another. - # A longer-term fix would be to have automake use am__CC in this case, - # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" - CC="$am_aux_dir/compile $CC" -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - depcc="$CC" am_compiler_list= @@ -5124,18 +4207,6 @@ fi -if test "x$remember_set_CFLAGS" = "x" -then - if test "$CFLAGS" = "-g -O2" - then - CFLAGS="-O2" - elif test "$CFLAGS" = "-g" - then - CFLAGS="" - fi -fi - -# Check for a 64-bit integer type ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -5533,6 +4604,253 @@ fi done + + ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" +if test "x$ac_cv_header_minix_config_h" = xyes; then : + MINIX=yes +else + MINIX= +fi + + + if test "$MINIX" = yes; then + +$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h + + +$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h + + +$as_echo "#define _MINIX 1" >>confdefs.h + + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 +$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } +if ${ac_cv_safe_to_define___extensions__+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +# define __EXTENSIONS__ 1 + $ac_includes_default +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_safe_to_define___extensions__=yes +else + ac_cv_safe_to_define___extensions__=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 +$as_echo "$ac_cv_safe_to_define___extensions__" >&6; } + test $ac_cv_safe_to_define___extensions__ = yes && + $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h + + $as_echo "#define _ALL_SOURCE 1" >>confdefs.h + + $as_echo "#define _GNU_SOURCE 1" >>confdefs.h + + $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h + + $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h + + + +if test "x$remember_set_CFLAGS" = "x" +then + if test "$CFLAGS" = "-g -O2" + then + CFLAGS="-O2" + elif test "$CFLAGS" = "-g" + then + CFLAGS="" + fi +fi + +# This is a new thing required to stop a warning from automake 1.12 +if test -n "$ac_tool_prefix"; then + for ac_prog in ar lib "link -lib" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar lib "link -lib" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5 +$as_echo_n "checking the archiver ($AR) interface... " >&6; } +if ${am_cv_ar_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + am_cv_ar_interface=ar + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int some_variable = 0; +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 + (eval $am_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + am_cv_ar_interface=ar + else + am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 + (eval $am_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + am_cv_ar_interface=lib + else + am_cv_ar_interface=unknown + fi + fi + rm -f conftest.lib libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5 +$as_echo "$am_cv_ar_interface" >&6; } + +case $am_cv_ar_interface in +ar) + ;; +lib) + # Microsoft lib, so override with the ar-lib wrapper script. + # FIXME: It is wrong to rewrite AR. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__AR in this case, + # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something + # similar. + AR="$am_aux_dir/ar-lib $AR" + ;; +unknown) + as_fn_error $? "could not determine $AR interface" "$LINENO" 5 + ;; +esac + + +# Check for a 64-bit integer type ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" case $ac_cv_c_int64_t in #( no|yes) ;; #( @@ -5928,8 +5246,8 @@ esac -macro_version='2.4.6' -macro_revision='2.4.6' +macro_version='2.4.6.40-6ca5-dirty' +macro_revision='2.4.6.40' @@ -7384,13 +6702,29 @@ esac fi : ${AR=ar} -: ${AR_FLAGS=cru} +# Use ARFLAGS variable as AR's operation code to sync the variable naming with +# Automake. If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have +# higher priority because thats what people were doing historically (setting +# ARFLAGS for automake and AR_FLAGS for libtool). FIXME: Make the AR_FLAGS +# variable obsoleted/removed. + +test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr} +lt_ar_flags=$AR_FLAGS + + + + + + +# Make AR_FLAGS overridable by 'make ARFLAGS='. Don't try to run-time override +# by AR_FLAGS because that was never working and AR_FLAGS is about to die. + @@ -9108,8 +8442,8 @@ int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 - echo "$AR cru libconftest.a conftest.o" >&5 - $AR cru libconftest.a conftest.o 2>&5 + echo "$AR $AR_FLAGS libconftest.a conftest.o" >&5 + $AR $AR_FLAGS libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF @@ -10603,6 +9937,7 @@ _LT_EOF emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes + file_list_spec='@' ;; interix[3-9]*) @@ -10820,7 +10155,7 @@ _LT_EOF if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else - export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no @@ -11457,6 +10792,7 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; } emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes + file_list_spec='@' ;; osf3*) @@ -13400,30 +12736,41 @@ striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } -if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then - test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" - test -z "$striplib" && striplib="$STRIP --strip-unneeded" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } +if test -z "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } else -# FIXME - insert some real tests, host_os isn't really good enough - case $host_os in - darwin*) - if test -n "$STRIP"; then + if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + old_striplib="$STRIP --strip-debug" + striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + case $host_os in + darwin*) + # FIXME - insert some real tests, host_os isn't really good enough striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - else + ;; + freebsd*) + if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then + old_striplib="$STRIP --strip-debug" + striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - fi - ;; - *) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - ;; - esac + ;; + esac + fi fi @@ -13631,9 +12978,9 @@ _ACEOF # Versioning PCRE2_MAJOR="10" -PCRE2_MINOR="22" +PCRE2_MINOR="32" PCRE2_PRERELEASE="" -PCRE2_DATE="2016-07-29" +PCRE2_DATE="2018-09-10" if test "$PCRE2_MINOR" = "08" -o "$PCRE2_MINOR" = "09" then @@ -13732,6 +13079,41 @@ else fi +# This code enables JIT if the hardware supports it. +if test "$enable_jit" = "auto"; then + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #define SLJIT_CONFIG_AUTO 1 + #include "src/sljit/sljitConfigInternal.h" + #if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) + #error unsupported + #endif +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + enable_jit=yes +else + enable_jit=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + echo checking for JIT support on this hardware... $enable_jit +fi + +# Handle --enable-jit-sealloc (disabled by default) +# Check whether --enable-jit-sealloc was given. +if test "${enable_jit_sealloc+set}" = set; then : + enableval=$enable_jit_sealloc; +else + enable_jit_sealloc=no +fi + + # Handle --disable-pcre2grep-jit (enabled by default) # Check whether --enable-pcre2grep-jit was given. if test "${enable_pcre2grep_jit+set}" = set; then : @@ -13741,19 +13123,14 @@ else fi -# Handle --disable-pcre2grep-callout (enabled by default) but not supported -# for Windows. -if test "$HAVE_WINDOWS_H" != "1"; then - # Check whether --enable-pcre2grep-callout was given. +# Handle --disable-pcre2grep-callout (enabled by default) +# Check whether --enable-pcre2grep-callout was given. if test "${enable_pcre2grep_callout+set}" = set; then : enableval=$enable_pcre2grep_callout; else enable_pcre2grep_callout=yes fi -else - enable_pcre2grep_callout=no -fi # Handle --enable-rebuild-chartables # Check whether --enable-rebuild-chartables was given. @@ -13800,6 +13177,11 @@ if test "${enable_newline_is_any+set}" = set; then : enableval=$enable_newline_is_any; ac_pcre2_newline=any fi +# Check whether --enable-newline-is-nul was given. +if test "${enable_newline_is_nul+set}" = set; then : + enableval=$enable_newline_is_nul; ac_pcre2_newline=nul +fi + enable_newline="$ac_pcre2_newline" # Handle --enable-bsr-anycrlf @@ -13838,15 +13220,6 @@ else fi -# Handle --disable-stack-for-recursion -# Check whether --enable-stack-for-recursion was given. -if test "${enable_stack_for_recursion+set}" = set; then : - enableval=$enable_stack_for_recursion; -else - enable_stack_for_recursion=yes -fi - - # Handle --enable-pcre2grep-libz # Check whether --enable-pcre2grep-libz was given. if test "${enable_pcre2grep_libz+set}" = set; then : @@ -13875,6 +13248,16 @@ else fi +# Handle --with-pcre2grep-max-bufsize=N + +# Check whether --with-pcre2grep-max-bufsize was given. +if test "${with_pcre2grep_max_bufsize+set}" = set; then : + withval=$with_pcre2grep_max_bufsize; +else + with_pcre2grep_max_bufsize=1048576 +fi + + # Handle --enable-pcre2test-libedit # Check whether --enable-pcre2test-libedit was given. if test "${enable_pcre2test_libedit+set}" = set; then : @@ -13913,6 +13296,16 @@ else fi +# Handle --with-heap-limit + +# Check whether --with-heap-limit was given. +if test "${with_heap_limit+set}" = set; then : + withval=$with_heap_limit; +else + with_heap_limit=20000000 +fi + + # Handle --with-match-limit=N # Check whether --with-match-limit was given. @@ -13923,20 +13316,30 @@ else fi -# Handle --with-match-limit_recursion=N +# Handle --with-match-limit-depth=N +# Recognize old synonym --with-match-limit-recursion # -# Note: In config.h, the default is to define MATCH_LIMIT_RECURSION -# symbolically as MATCH_LIMIT, which in turn is defined to be some numeric -# value (e.g. 10000000). MATCH_LIMIT_RECURSION can otherwise be set to some -# different numeric value (or even the same numeric value as MATCH_LIMIT, -# though no longer defined in terms of the latter). +# Note: In config.h, the default is to define MATCH_LIMIT_DEPTH symbolically as +# MATCH_LIMIT, which in turn is defined to be some numeric value (e.g. +# 10000000). MATCH_LIMIT_DEPTH can otherwise be set to some different numeric +# value (or even the same numeric value as MATCH_LIMIT, though no longer +# defined in terms of the latter). # +# Check whether --with-match-limit-depth was given. +if test "${with_match_limit_depth+set}" = set; then : + withval=$with_match_limit_depth; +else + with_match_limit_depth=MATCH_LIMIT +fi + + + # Check whether --with-match-limit-recursion was given. if test "${with_match_limit_recursion+set}" = set; then : withval=$with_match_limit_recursion; else - with_match_limit_recursion=MATCH_LIMIT + with_match_limit_recursion=UNSET fi @@ -13958,6 +13361,31 @@ else fi +# Handle --enable-fuzz-support +# Check whether --enable-fuzz_support was given. +if test "${enable_fuzz_support+set}" = set; then : + enableval=$enable_fuzz_support; +else + enable_fuzz_support=no +fi + + +# Handle --disable-stack-for-recursion +# This option became obsolete at release 10.30. +# Check whether --enable-stack-for-recursion was given. +if test "${enable_stack_for_recursion+set}" = set; then : + enableval=$enable_stack_for_recursion; +else + enable_stack_for_recursion=yes +fi + + +# Original code +# AC_ARG_ENABLE(stack-for-recursion, +# AS_HELP_STRING([--disable-stack-for-recursion], +# [don't use stack recursion when matching]), +# , enable_stack_for_recursion=yes) + # Set the default value for pcre2-8 if test "x$enable_pcre2_8" = "xunset" then @@ -13997,6 +13425,7 @@ case "$enable_newline" in crlf) ac_pcre2_newline_value=3 ;; any) ac_pcre2_newline_value=4 ;; anycrlf) ac_pcre2_newline_value=5 ;; + nul) ac_pcre2_newline_value=6 ;; *) as_fn_error $? "invalid argument \"$enable_newline\" to --enable-newline option" "$LINENO" 5 ;; @@ -14184,6 +13613,37 @@ fi done +for ac_header in stdint.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default" +if test "x$ac_cv_header_stdint_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STDINT_H 1 +_ACEOF + PCRE2_HAVE_STDINT_H=1 +else + PCRE2_HAVE_STDINT_H=0 +fi + +done + +for ac_header in inttypes.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default" +if test "x$ac_cv_header_inttypes_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_INTTYPES_H 1 +_ACEOF + PCRE2_HAVE_INTTYPES_H=1 +else + PCRE2_HAVE_INTTYPES_H=0 +fi + +done + + + + # Conditional compilation if test "x$enable_pcre2_8" = "xyes"; then WITH_PCRE2_8_TRUE= @@ -14249,6 +13709,19 @@ else WITH_VALGRIND_FALSE= fi + if test "x$enable_fuzz_support" = "xyes"; then + WITH_FUZZ_SUPPORT_TRUE= + WITH_FUZZ_SUPPORT_FALSE='#' +else + WITH_FUZZ_SUPPORT_TRUE='#' + WITH_FUZZ_SUPPORT_FALSE= +fi + + +if test "$enable_fuzz_support" = "yes" -a "$enable_pcre2_8" = "no"; then + echo "** ERROR: Fuzzer support requires the 8-bit library" + exit 1 +fi # Checks for typedefs, structures, and compiler characteristics. @@ -14347,7 +13820,7 @@ fi # Checks for library functions. -for ac_func in bcopy memmove strerror +for ac_func in bcopy memmove strerror mkostemp secure_getenv do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -14850,8 +14323,6 @@ fi fi -# This facilitates -ansi builds under Linux - PCRE2_STATIC_CFLAG="" if test "x$enable_shared" = "xno" ; then @@ -15298,27 +14769,27 @@ else enable_pcre2grep_jit="no" fi +if test "$enable_jit_sealloc" = "yes"; then + +$as_echo "#define SLJIT_PROT_EXECUTABLE_ALLOCATOR 1" >>confdefs.h + +fi + if test "$enable_pcre2grep_jit" = "yes"; then $as_echo "#define SUPPORT_PCRE2GREP_JIT /**/" >>confdefs.h fi -# Currently pcre2grep callout string is not supported under Windows. - if test "$enable_pcre2grep_callout" = "yes"; then if test "$HAVE_WINDOWS_H" != "1"; then if test "$HAVE_SYS_WAIT_H" != "1"; then as_fn_error $? "Callout script support needs sys/wait.h." "$LINENO" 5 fi + fi $as_echo "#define SUPPORT_PCRE2GREP_CALLOUT /**/" >>confdefs.h - else - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Callout script support is not available for Windows: disabled" >&5 -$as_echo "$as_me: WARNING: Callout script support is not available for Windows: disabled" >&2;} - enable_pcre2grep_callout=no - fi fi if test "$enable_unicode" = "yes"; then @@ -15327,12 +14798,6 @@ $as_echo "#define SUPPORT_UNICODE /**/" >>confdefs.h fi -if test "$enable_stack_for_recursion" = "no"; then - -$as_echo "#define HEAP_MATCH_RECURSE /**/" >>confdefs.h - -fi - if test "$enable_pcre2grep_libz" = "yes"; then $as_echo "#define SUPPORT_LIBZ /**/" >>confdefs.h @@ -15351,7 +14816,15 @@ $as_echo "$as_me: WARNING: $with_pcre2grep_bufsize is too small for --with-pcre2 with_pcre2grep_bufsize="8192" else if test $? -gt 1 ; then - as_fn_error $? "Bad value for --with-pcre2grep-bufsize" "$LINENO" 5 + as_fn_error $? "Bad value for --with-pcre2grep-bufsize" "$LINENO" 5 + fi +fi + +if test $with_pcre2grep_max_bufsize -lt $with_pcre2grep_bufsize ; then + with_pcre2grep_max_bufsize="$with_pcre2grep_bufsize" +else + if test $? -gt 1 ; then + as_fn_error $? "Bad value for --with-pcre2grep-max-bufsize" "$LINENO" 5 fi fi @@ -15361,6 +14834,12 @@ cat >>confdefs.h <<_ACEOF _ACEOF + +cat >>confdefs.h <<_ACEOF +#define PCRE2GREP_MAX_BUFSIZE $with_pcre2grep_max_bufsize +_ACEOF + + if test "$enable_pcre2test_libedit" = "yes"; then $as_echo "#define SUPPORT_LIBEDIT /**/" >>confdefs.h @@ -15408,9 +14887,30 @@ cat >>confdefs.h <<_ACEOF _ACEOF +# --with-match-limit-recursion is an obsolete synonym for --with-match-limit-depth + +if test "$with_match_limit_recursion" != "UNSET"; then +cat <>confdefs.h <<_ACEOF -#define MATCH_LIMIT_RECURSION $with_match_limit_recursion +#define MATCH_LIMIT_DEPTH $with_match_limit_depth +_ACEOF + + + +cat >>confdefs.h <<_ACEOF +#define HEAP_LIMIT $with_heap_limit _ACEOF @@ -15464,16 +14964,16 @@ esac # are m4 variables, assigned above. EXTRA_LIBPCRE2_8_LDFLAGS="$EXTRA_LIBPCRE2_8_LDFLAGS \ - $NO_UNDEFINED -version-info 4:0:4" + $NO_UNDEFINED -version-info 7:1:7" EXTRA_LIBPCRE2_16_LDFLAGS="$EXTRA_LIBPCRE2_16_LDFLAGS \ - $NO_UNDEFINED -version-info 4:0:4" + $NO_UNDEFINED -version-info 7:1:7" EXTRA_LIBPCRE2_32_LDFLAGS="$EXTRA_LIBPCRE2_32_LDFLAGS \ - $NO_UNDEFINED -version-info 4:0:4" + $NO_UNDEFINED -version-info 7:1:7" EXTRA_LIBPCRE2_POSIX_LDFLAGS="$EXTRA_LIBPCRE2_POSIX_LDFLAGS \ - $NO_UNDEFINED -version-info 1:0:0" + $NO_UNDEFINED -version-info 2:1:0" @@ -15482,7 +14982,7 @@ EXTRA_LIBPCRE2_POSIX_LDFLAGS="$EXTRA_LIBPCRE2_POSIX_LDFLAGS \ # When we run 'make distcheck', use these arguments. Turning off compiler # optimization makes it run faster. -DISTCHECK_CONFIGURE_FLAGS="CFLAGS='' CXXFLAGS='' --enable-pcre2-16 --enable-pcre2-32 --enable-jit --enable-utf" +DISTCHECK_CONFIGURE_FLAGS="CFLAGS='' CXXFLAGS='' --enable-pcre2-16 --enable-pcre2-32 --enable-jit" # Check that, if --enable-pcre2grep-libz or --enable-pcre2grep-libbz2 is @@ -16071,10 +15571,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 @@ -16083,10 +15579,6 @@ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then - as_fn_error $? "conditional \"am__fastdepCC\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi if test -z "${WITH_PCRE2_8_TRUE}" && test -z "${WITH_PCRE2_8_FALSE}"; then as_fn_error $? "conditional \"WITH_PCRE2_8\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -16119,6 +15611,10 @@ if test -z "${WITH_VALGRIND_TRUE}" && test -z "${WITH_VALGRIND_FALSE}"; then as_fn_error $? "conditional \"WITH_VALGRIND\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${WITH_FUZZ_SUPPORT_TRUE}" && test -z "${WITH_FUZZ_SUPPORT_FALSE}"; then + as_fn_error $? "conditional \"WITH_FUZZ_SUPPORT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${WITH_GCOV_TRUE}" && test -z "${WITH_GCOV_FALSE}"; then as_fn_error $? "conditional \"WITH_GCOV\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -16520,7 +16016,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.22, which was +This file was extended by PCRE2 $as_me 10.32, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -16586,7 +16082,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.22 +PCRE2 config.status 10.32 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -16758,6 +16254,7 @@ file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +lt_ar_flags='`$ECHO "$lt_ar_flags" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' @@ -16887,7 +16384,6 @@ file_magic_glob \ want_nocaseglob \ sharedlib_from_linklib_cmd \ AR \ -AR_FLAGS \ archiver_list_spec \ STRIP \ RANLIB \ @@ -17862,8 +17358,11 @@ sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR +# Flags to create an archive (by configure). +lt_ar_flags=$lt_ar_flags + # Flags to create an archive. -AR_FLAGS=$lt_AR_FLAGS +AR_FLAGS=\${ARFLAGS-"\$lt_ar_flags"} # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec @@ -18287,6 +17786,15 @@ $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi +# --disable-stack-for-recursion is obsolete and has no effect. + +if test "$enable_stack_for_recursion" = "no"; then +cat <, 1996 -# Copyright (C) 1996-2015 Free Software Foundation, Inc. +# Copyright (C) 1996-2017 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. @@ -31,8 +31,8 @@ PROGRAM=libtool PACKAGE=libtool -VERSION=2.4.6 -package_revision=2.4.6 +VERSION=2.4.6.40-6ca5-dirty +package_revision=2.4.6.40 ## ------ ## @@ -64,34 +64,25 @@ package_revision=2.4.6 # libraries, which are installed to $pkgauxdir. # Set a version string for this script. -scriptversion=2015-01-20.17; # UTC +scriptversion=2017-04-19.12; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 -# Copyright (C) 2004-2015 Free Software Foundation, Inc. -# This is free software; see the source for copying conditions. There is NO -# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# This is free software. There is NO warranty; not even for +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Copyright (C) 2004-2017 Bootstrap Authors +# +# This file is dual licensed under the terms of the MIT license +# , and GPL version 3 or later +# . You must apply one of +# these licenses when using or redistributing this software or any of +# the files within it. See the URLs above, or the file `LICENSE` +# included in the Bootstrap distribution for the full license texts. -# 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. - -# As a special exception to the GNU General Public License, if you distribute -# this file as part of a program or library that is built using GNU Libtool, -# you may include this file under the same distribution terms that you use -# for the rest of that program. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNES 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 . - -# Please report bugs or propose patches to gary@gnu.org. +# Please report bugs or propose patches to: +# ## ------ ## @@ -140,9 +131,6 @@ do fi" done -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - # Make sure IFS has a sensible default sp=' ' nl=' @@ -159,6 +147,26 @@ if test "${PATH_SEPARATOR+set}" != set; then fi +# func_unset VAR +# -------------- +# Portably unset VAR. +# In some shells, an 'unset VAR' statement leaves a non-zero return +# status if VAR is already unset, which might be problematic if the +# statement is used at the end of a function (thus poisoning its return +# value) or when 'set -e' is active (causing even a spurious abort of +# the script in this case). +func_unset () +{ + { eval $1=; (eval unset $1) >/dev/null 2>&1 && eval unset $1 || : ; } +} + + +# Make sure CDPATH doesn't cause `cd` commands to output the target dir. +func_unset CDPATH + +# Make sure ${,E,F}GREP behave sanely. +func_unset GREP_OPTIONS + ## ------------------------- ## ## Locate command utilities. ## @@ -259,7 +267,7 @@ test -z "$SED" && { rm -f conftest.in conftest.tmp conftest.nl conftest.out } - func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin + func_path_progs "sed gsed" func_check_prog_sed "$PATH:/usr/xpg4/bin" rm -f conftest.sed SED=$func_path_progs_result } @@ -295,7 +303,7 @@ test -z "$GREP" && { rm -f conftest.in conftest.tmp conftest.nl conftest.out } - func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin + func_path_progs "grep ggrep" func_check_prog_grep "$PATH:/usr/xpg4/bin" GREP=$func_path_progs_result } @@ -580,16 +588,16 @@ if test yes = "$_G_HAVE_PLUSEQ_OP"; then { $debug_cmd - func_quote_for_eval "$2" - eval "$1+=\\ \$func_quote_for_eval_result" + func_quote_arg pretty "$2" + eval "$1+=\\ \$func_quote_arg_result" }' else func_append_quoted () { $debug_cmd - func_quote_for_eval "$2" - eval "$1=\$$1\\ \$func_quote_for_eval_result" + func_quote_arg pretty "$2" + eval "$1=\$$1\\ \$func_quote_arg_result" } fi @@ -1091,85 +1099,199 @@ func_relative_path () } -# func_quote_for_eval ARG... -# -------------------------- -# Aesthetically quote ARGs to be evaled later. -# This function returns two values: -# i) func_quote_for_eval_result -# double-quoted, suitable for a subsequent eval -# ii) func_quote_for_eval_unquoted_result -# has all characters that are still active within double -# quotes backslashified. -func_quote_for_eval () +# func_quote_portable EVAL ARG +# ---------------------------- +# Internal function to portably implement func_quote_arg. Note that we still +# keep attention to performance here so we as much as possible try to avoid +# calling sed binary (so far O(N) complexity as long as func_append is O(1)). +func_quote_portable () { $debug_cmd - func_quote_for_eval_unquoted_result= - func_quote_for_eval_result= - while test 0 -lt $#; do - case $1 in + func_quote_portable_result=$2 + + # one-time-loop (easy break) + while true + do + if $1; then + func_quote_portable_result=`$ECHO "$2" | $SED \ + -e "$sed_double_quote_subst" -e "$sed_double_backslash"` + break + fi + + # Quote for eval. + case $func_quote_portable_result in *[\\\`\"\$]*) - _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; - *) - _G_unquoted_arg=$1 ;; - esac - if test -n "$func_quote_for_eval_unquoted_result"; then - func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" - else - func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" - fi + case $func_quote_portable_result in + *[\[\*\?]*) + func_quote_portable_result=`$ECHO "$func_quote_portable_result" \ + | $SED "$sed_quote_subst"` + break + ;; + esac - case $_G_unquoted_arg in - # Double-quote args containing shell metacharacters to delay - # word splitting, command substitution and variable expansion - # for a subsequent eval. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - _G_quoted_arg=\"$_G_unquoted_arg\" + func_quote_portable_old_IFS=$IFS + for _G_char in '\' '`' '"' '$' + do + # STATE($1) PREV($2) SEPARATOR($3) + set start "" "" + func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy + IFS=$_G_char + for _G_part in $func_quote_portable_result + do + case $1 in + quote) + func_append func_quote_portable_result "$3$2" + set quote "$_G_part" "\\$_G_char" + ;; + start) + set first "" "" + func_quote_portable_result= + ;; + first) + set quote "$_G_part" "" + ;; + esac + done + done + IFS=$func_quote_portable_old_IFS ;; - *) - _G_quoted_arg=$_G_unquoted_arg - ;; + *) ;; esac - - if test -n "$func_quote_for_eval_result"; then - func_append func_quote_for_eval_result " $_G_quoted_arg" - else - func_append func_quote_for_eval_result "$_G_quoted_arg" - fi - shift + break done + + func_quote_portable_unquoted_result=$func_quote_portable_result + case $func_quote_portable_result in + # double-quote args containing shell metacharacters to delay + # word splitting, command substitution and variable expansion + # for a subsequent eval. + # many bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + func_quote_portable_result=\"$func_quote_portable_result\" + ;; + esac } -# func_quote_for_expand ARG -# ------------------------- -# Aesthetically quote ARG to be evaled later; same as above, -# but do not quote variable references. -func_quote_for_expand () +# func_quotefast_eval ARG +# ----------------------- +# Quote one ARG (internal). This is equivalent to 'func_quote_arg eval ARG', +# but optimized for speed. Result is stored in $func_quotefast_eval. +if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then + printf -v _GL_test_printf_tilde %q '~' + if test '\~' = "$_GL_test_printf_tilde"; then + func_quotefast_eval () + { + printf -v func_quotefast_eval_result %q "$1" + } + else + # Broken older Bash implementations. Make those faster too if possible. + func_quotefast_eval () + { + case $1 in + '~'*) + func_quote_portable false "$1" + func_quotefast_eval_result=$func_quote_portable_result + ;; + *) + printf -v func_quotefast_eval_result %q "$1" + ;; + esac + } + fi +else + func_quotefast_eval () + { + func_quote_portable false "$1" + func_quotefast_eval_result=$func_quote_portable_result + } +fi + + +# func_quote_arg MODEs ARG +# ------------------------ +# Quote one ARG to be evaled later. MODEs argument may contain zero or more +# specifiers listed below separated by ',' character. This function returns two +# values: +# i) func_quote_arg_result +# double-quoted (when needed), suitable for a subsequent eval +# ii) func_quote_arg_unquoted_result +# has all characters that are still active within double +# quotes backslashified. Available only if 'unquoted' is specified. +# +# Available modes: +# ---------------- +# 'eval' (default) +# - escape shell special characters +# 'expand' +# - the same as 'eval'; but do not quote variable references +# 'pretty' +# - request aesthetic output, i.e. '"a b"' instead of 'a\ b'. This might +# be used later in func_quote to get output like: 'echo "a b"' instead +# of 'echo a\ b'. This is slower than default on some shells. +# 'unquoted' +# - produce also $func_quote_arg_unquoted_result which does not contain +# wrapping double-quotes. +# +# Examples for 'func_quote_arg pretty,unquoted string': +# +# string | *_result | *_unquoted_result +# ------------+-----------------------+------------------- +# " | \" | \" +# a b | "a b" | a b +# "a b" | "\"a b\"" | \"a b\" +# * | "*" | * +# z="${x-$y}" | "z=\"\${x-\$y}\"" | z=\"\${x-\$y}\" +# +# Examples for 'func_quote_arg pretty,unquoted,expand string': +# +# string | *_result | *_unquoted_result +# --------------+---------------------+-------------------- +# z="${x-$y}" | "z=\"${x-$y}\"" | z=\"${x-$y}\" +func_quote_arg () { - $debug_cmd - - case $1 in - *[\\\`\"]*) - _G_arg=`$ECHO "$1" | $SED \ - -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; - *) - _G_arg=$1 ;; - esac - - case $_G_arg in - # Double-quote args containing shell metacharacters to delay - # word splitting and command substitution for a subsequent eval. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - _G_arg=\"$_G_arg\" + _G_quote_expand=false + case ,$1, in + *,expand,*) + _G_quote_expand=: ;; esac - func_quote_for_expand_result=$_G_arg + case ,$1, in + *,pretty,*|*,expand,*|*,unquoted,*) + func_quote_portable $_G_quote_expand "$2" + func_quote_arg_result=$func_quote_portable_result + func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result + ;; + *) + # Faster quote-for-eval for some shells. + func_quotefast_eval "$2" + func_quote_arg_result=$func_quotefast_eval_result + ;; + esac +} + + +# func_quote MODEs ARGs... +# ------------------------ +# Quote all ARGs to be evaled later and join them into single command. See +# func_quote_arg's description for more info. +func_quote () +{ + $debug_cmd + _G_func_quote_mode=$1 ; shift + func_quote_result= + while test 0 -lt $#; do + func_quote_arg "$_G_func_quote_mode" "$1" + if test -n "$func_quote_result"; then + func_append func_quote_result " $func_quote_arg_result" + else + func_append func_quote_result "$func_quote_arg_result" + fi + shift + done } @@ -1215,8 +1337,8 @@ func_show_eval () _G_cmd=$1 _G_fail_exp=${2-':'} - func_quote_for_expand "$_G_cmd" - eval "func_notquiet $func_quote_for_expand_result" + func_quote_arg pretty,expand "$_G_cmd" + eval "func_notquiet $func_quote_arg_result" $opt_dry_run || { eval "$_G_cmd" @@ -1241,8 +1363,8 @@ func_show_eval_locale () _G_fail_exp=${2-':'} $opt_quiet || { - func_quote_for_expand "$_G_cmd" - eval "func_echo $func_quote_for_expand_result" + func_quote_arg expand,pretty "$_G_cmd" + eval "func_echo $func_quote_arg_result" } $opt_dry_run || { @@ -1369,30 +1491,26 @@ func_lt_ver () # End: #! /bin/sh -# Set a version string for this script. -scriptversion=2014-01-07.03; # UTC - # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 -# Copyright (C) 2010-2015 Free Software Foundation, Inc. -# This is free software; see the source for copying conditions. There is NO -# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# This is free software. There is NO warranty; not even for +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Copyright (C) 2010-2017 Bootstrap Authors +# +# This file is dual licensed under the terms of the MIT license +# , and GPL version 3 or later +# . You must apply one of +# these licenses when using or redistributing this software or any of +# the files within it. See the URLs above, or the file `LICENSE` +# included in the Bootstrap distribution for the full license texts. -# 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. +# Please report bugs or propose patches to: +# -# 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 . - -# Please report bugs or propose patches to gary@gnu.org. +# Set a version string for this script. +scriptversion=2016-03-06.01; # UTC ## ------ ## @@ -1415,7 +1533,7 @@ scriptversion=2014-01-07.03; # UTC # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file -# starting with '# Written by ' and ending with '# warranty; '. +# starting with '# Written by ' and ending with '# Copyright'. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the @@ -1427,7 +1545,7 @@ scriptversion=2014-01-07.03; # UTC # to display verbose messages only when your user has specified # '--verbose'. # -# After sourcing this file, you can plug processing for additional +# After sourcing this file, you can plug in processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. @@ -1476,8 +1594,8 @@ fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## # This section contains functions for adding, removing, and running hooks -# to the main code. A hook is just a named list of of function, that can -# be run in order later on. +# in the main code. A hook is just a list of function names that can be +# run in order later on. # func_hookable FUNC_NAME # ----------------------- @@ -1510,7 +1628,8 @@ func_add_hook () # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ -# Remove HOOK_FUNC from the list of functions called by FUNC_NAME. +# Remove HOOK_FUNC from the list of hook functions to be called by +# FUNC_NAME. func_remove_hook () { $debug_cmd @@ -1519,10 +1638,28 @@ func_remove_hook () } +# func_propagate_result FUNC_NAME_A FUNC_NAME_B +# --------------------------------------------- +# If the *_result variable of FUNC_NAME_A _is set_, assign its value to +# *_result variable of FUNC_NAME_B. +func_propagate_result () +{ + $debug_cmd + + func_propagate_result_result=: + if eval "test \"\${${1}_result+set}\" = set" + then + eval "${2}_result=\$${1}_result" + else + func_propagate_result_result=false + fi +} + + # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. -# It is assumed that the list of hook functions contains nothing more +# It's assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. @@ -1532,22 +1669,19 @@ func_run_hooks () case " $hookable_fns " in *" $1 "*) ;; - *) func_fatal_error "'$1' does not support hook funcions.n" ;; + *) func_fatal_error "'$1' does not support hook functions." ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do - eval $_G_hook '"$@"' - - # store returned options list back into positional - # parameters for next 'cmd' execution. - eval _G_hook_result=\$${_G_hook}_result - eval set dummy "$_G_hook_result"; shift + func_unset "${_G_hook}_result" + eval $_G_hook '${1+"$@"}' + func_propagate_result $_G_hook func_run_hooks + if $func_propagate_result_result; then + eval set dummy "$func_run_hooks_result"; shift + fi done - - func_quote_for_eval ${1+"$@"} - func_run_hooks_result=$func_quote_for_eval_result } @@ -1557,10 +1691,18 @@ func_run_hooks () ## --------------- ## # In order to add your own option parsing hooks, you must accept the -# full positional parameter list in your hook function, remove any -# options that you action, and then pass back the remaining unprocessed -# options in '_result', escaped suitably for -# 'eval'. Like this: +# full positional parameter list from your hook function. You may remove +# or edit any options that you action, and then pass back the remaining +# unprocessed options in '_result', escaped +# suitably for 'eval'. +# +# The '_result' variable is automatically unset +# before your hook gets called; for best performance, only set the +# *_result variable when necessary (i.e. don't call the 'func_quote' +# function unnecessarily because it can be an expensive operation on some +# machines). +# +# Like this: # # my_options_prep () # { @@ -1570,9 +1712,8 @@ func_run_hooks () # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' -# -# func_quote_for_eval ${1+"$@"} -# my_options_prep_result=$func_quote_for_eval_result +# # No change in '$@' (ignored completely by this hook). Leave +# # my_options_prep_result variable intact. # } # func_add_hook func_options_prep my_options_prep # @@ -1581,25 +1722,36 @@ func_run_hooks () # { # $debug_cmd # -# # Note that for efficiency, we parse as many options as we can +# args_changed=false +# +# # Note that, for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in -# --silent|-s) opt_silent=: ;; +# --silent|-s) opt_silent=: +# args_changed=: +# ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift +# args_changed=: # ;; -# *) set dummy "$_G_opt" "$*"; shift; break ;; +# *) # Make sure the first unrecognised option "$_G_opt" +# # is added back to "$@" in case we need it later, +# # if $args_changed was set to 'true'. +# set dummy "$_G_opt" ${1+"$@"}; shift; break ;; # esac # done # -# func_quote_for_eval ${1+"$@"} -# my_silent_option_result=$func_quote_for_eval_result +# # Only call 'func_quote' here if we processed at least one argument. +# if $args_changed; then +# func_quote eval ${1+"$@"} +# my_silent_option_result=$func_quote_result +# fi # } # func_add_hook func_parse_options my_silent_option # @@ -1610,17 +1762,26 @@ func_run_hooks () # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." -# -# func_quote_for_eval ${1+"$@"} -# my_option_validation_result=$func_quote_for_eval_result # } # func_add_hook func_validate_options my_option_validation # -# You'll alse need to manually amend $usage_message to reflect the extra +# You'll also need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. +# func_options_finish [ARG]... +# ---------------------------- +# Finishing the option parse loop (call 'func_options' hooks ATM). +func_options_finish () +{ + $debug_cmd + + func_run_hooks func_options ${1+"$@"} + func_propagate_result func_run_hooks func_options_finish +} + + # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the @@ -1630,17 +1791,27 @@ func_options () { $debug_cmd - func_options_prep ${1+"$@"} - eval func_parse_options \ - ${func_options_prep_result+"$func_options_prep_result"} - eval func_validate_options \ - ${func_parse_options_result+"$func_parse_options_result"} + _G_options_quoted=false - eval func_run_hooks func_options \ - ${func_validate_options_result+"$func_validate_options_result"} + for my_func in options_prep parse_options validate_options options_finish + do + func_unset func_${my_func}_result + func_unset func_run_hooks_result + eval func_$my_func '${1+"$@"}' + func_propagate_result func_$my_func func_options + if $func_propagate_result_result; then + eval set dummy "$func_options_result"; shift + _G_options_quoted=: + fi + done - # save modified positional parameters for caller - func_options_result=$func_run_hooks_result + $_G_options_quoted || { + # As we (func_options) are top-level options-parser function and + # nobody quoted "$@" for us yet, we need to do it explicitly for + # caller. + func_quote eval ${1+"$@"} + func_options_result=$func_quote_result + } } @@ -1649,9 +1820,8 @@ func_options () # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and -# needs to propogate that back to rest of this script, then the complete -# modified list must be put in 'func_run_hooks_result' before -# returning. +# needs to propagate that back to rest of this script, then the complete +# modified list must be put in 'func_run_hooks_result' before returning. func_hookable func_options_prep func_options_prep () { @@ -1662,9 +1832,7 @@ func_options_prep () opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} - - # save modified positional parameters for caller - func_options_prep_result=$func_run_hooks_result + func_propagate_result func_run_hooks func_options_prep } @@ -1676,25 +1844,32 @@ func_parse_options () { $debug_cmd - func_parse_options_result= - + _G_parse_options_requote=false # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} - - # Adjust func_parse_options positional parameters to match - eval set dummy "$func_run_hooks_result"; shift + func_propagate_result func_run_hooks func_parse_options + if $func_propagate_result_result; then + eval set dummy "$func_parse_options_result"; shift + # Even though we may have changed "$@", we passed the "$@" array + # down into the hook and it quoted it for us (because we are in + # this if-branch). No need to quote it again. + _G_parse_options_requote=false + fi # Break out of the loop if we already parsed every option. test $# -gt 0 || break + # We expect that one of the options parsed in this function matches + # and thus we remove _G_opt from "$@" and need to re-quote. + _G_match_parse_options=: _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' - func_echo "enabling shell trace mode" + func_echo "enabling shell trace mode" >&2 $debug_cmd ;; @@ -1704,7 +1879,10 @@ func_parse_options () ;; --warnings|--warning|-W) - test $# = 0 && func_missing_arg $_G_opt && break + if test $# = 0 && func_missing_arg $_G_opt; then + _G_parse_options_requote=: + break + fi case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above @@ -1757,15 +1935,24 @@ func_parse_options () shift ;; - --) break ;; + --) _G_parse_options_requote=: ; break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; - *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; + *) set dummy "$_G_opt" ${1+"$@"}; shift + _G_match_parse_options=false + break + ;; esac + + if $_G_match_parse_options; then + _G_parse_options_requote=: + fi done - # save modified positional parameters for caller - func_quote_for_eval ${1+"$@"} - func_parse_options_result=$func_quote_for_eval_result + if $_G_parse_options_requote; then + # save modified positional parameters for caller + func_quote eval ${1+"$@"} + func_parse_options_result=$func_quote_result + fi } @@ -1782,12 +1969,10 @@ func_validate_options () test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} + func_propagate_result func_run_hooks func_validate_options # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE - - # save modified positional parameters for caller - func_validate_options_result=$func_run_hooks_result } @@ -1843,8 +2028,8 @@ func_missing_arg () # func_split_equals STRING # ------------------------ -# Set func_split_equals_lhs and func_split_equals_rhs shell variables after -# splitting STRING at the '=' sign. +# Set func_split_equals_lhs and func_split_equals_rhs shell variables +# after splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ @@ -1859,8 +2044,9 @@ then func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} - test "x$func_split_equals_lhs" = "x$1" \ - && func_split_equals_rhs= + if test "x$func_split_equals_lhs" = "x$1"; then + func_split_equals_rhs= + fi }' else # ...otherwise fall back to using expr, which is often a shell builtin. @@ -1938,31 +2124,44 @@ func_usage_message () # func_version # ------------ # Echo version message to standard output and exit. +# The version message is extracted from the calling file's header +# comments, with leading '# ' stripped: +# 1. First display the progname and version +# 2. Followed by the header comment line matching /^# Written by / +# 3. Then a blank line followed by the first following line matching +# /^# Copyright / +# 4. Immediately followed by any lines between the previous matches, +# except lines preceding the intervening completely blank line. +# For example, see the header comments of this file. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' - /(C)/!b go - :more - /\./!{ - N - s|\n# | | - b more + /^# Written by /!b + s|^# ||; p; n + + :fwd2blnk + /./ { + n + b fwd2blnk } - :go - /^# Written by /,/# warranty; / { - s|^# || - s|^# *$|| - s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| - p + p; n + + :holdwrnt + s|^# || + s|^# *$|| + /^Copyright /!{ + /./H + n + b holdwrnt } - /^# Written by / { - s|^# || - p - } - /^warranty; /q' < "$progpath" + + s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| + G + s|\(\n\)\n*|\1|g + p; q' < "$progpath" exit $? } @@ -1977,7 +2176,7 @@ func_version () # End: # Set a version string. -scriptversion='(GNU libtool) 2.4.6' +scriptversion='(GNU libtool) 2.4.6.40-6ca5-dirty' # func_echo ARG... @@ -2068,12 +2267,12 @@ include the following information: compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) - version: $progname (GNU libtool) 2.4.6 + version: $progname (GNU libtool) 2.4.6.40-6ca5-dirty automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . -GNU libtool home page: . +GNU libtool home page: . General help using GNU software: ." exit 0 } @@ -2124,7 +2323,7 @@ fi # a configuration failure hint, and exit. func_fatal_configuration () { - func__fatal_error ${1+"$@"} \ + func_fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } @@ -2270,6 +2469,8 @@ libtool_options_prep () nonopt= preserve_args= + _G_rc_lt_options_prep=: + # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) @@ -2293,11 +2494,16 @@ libtool_options_prep () uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; + *) + _G_rc_lt_options_prep=false + ;; esac - # Pass back the list of options. - func_quote_for_eval ${1+"$@"} - libtool_options_prep_result=$func_quote_for_eval_result + if $_G_rc_lt_options_prep; then + # Pass back the list of options. + func_quote eval ${1+"$@"} + libtool_options_prep_result=$func_quote_result + fi } func_add_hook func_options_prep libtool_options_prep @@ -2309,9 +2515,12 @@ libtool_parse_options () { $debug_cmd + _G_rc_lt_parse_options=false + # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do + _G_match_lt_parse_options=: _G_opt=$1 shift case $_G_opt in @@ -2386,15 +2595,20 @@ libtool_parse_options () func_append preserve_args " $_G_opt" ;; - # An option not handled by this hook function: - *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; + # An option not handled by this hook function: + *) set dummy "$_G_opt" ${1+"$@"} ; shift + _G_match_lt_parse_options=false + break + ;; esac + $_G_match_lt_parse_options && _G_rc_lt_parse_options=: done - - # save modified positional parameters for caller - func_quote_for_eval ${1+"$@"} - libtool_parse_options_result=$func_quote_for_eval_result + if $_G_rc_lt_parse_options; then + # save modified positional parameters for caller + func_quote eval ${1+"$@"} + libtool_parse_options_result=$func_quote_result + fi } func_add_hook func_parse_options libtool_parse_options @@ -2451,8 +2665,8 @@ libtool_validate_options () } # Pass back the unparsed argument list - func_quote_for_eval ${1+"$@"} - libtool_validate_options_result=$func_quote_for_eval_result + func_quote eval ${1+"$@"} + libtool_validate_options_result=$func_quote_result } func_add_hook func_validate_options libtool_validate_options @@ -3418,8 +3632,8 @@ func_mode_compile () esac done - func_quote_for_eval "$libobj" - test "X$libobj" != "X$func_quote_for_eval_result" \ + func_quote_arg pretty "$libobj" + test "X$libobj" != "X$func_quote_arg_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" @@ -3492,8 +3706,8 @@ compiler." func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result - func_quote_for_eval "$srcfile" - qsrcfile=$func_quote_for_eval_result + func_quote_arg pretty "$srcfile" + qsrcfile=$func_quote_arg_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then @@ -4096,8 +4310,8 @@ func_mode_install () case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. - func_quote_for_eval "$nonopt" - install_prog="$func_quote_for_eval_result " + func_quote_arg pretty "$nonopt" + install_prog="$func_quote_arg_result " arg=$1 shift else @@ -4107,8 +4321,8 @@ func_mode_install () # The real first argument should be the name of the installation program. # Aesthetically quote it. - func_quote_for_eval "$arg" - func_append install_prog "$func_quote_for_eval_result" + func_quote_arg pretty "$arg" + func_append install_prog "$func_quote_arg_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; @@ -4165,12 +4379,12 @@ func_mode_install () esac # Aesthetically quote the argument. - func_quote_for_eval "$arg" - func_append install_prog " $func_quote_for_eval_result" + func_quote_arg pretty "$arg" + func_append install_prog " $func_quote_arg_result" if test -n "$arg2"; then - func_quote_for_eval "$arg2" + func_quote_arg pretty "$arg2" fi - func_append install_shared_prog " $func_quote_for_eval_result" + func_append install_shared_prog " $func_quote_arg_result" done test -z "$install_prog" && \ @@ -4181,8 +4395,8 @@ func_mode_install () if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else - func_quote_for_eval "$install_override_mode" - func_append install_shared_prog " -m $func_quote_for_eval_result" + func_quote_arg pretty "$install_override_mode" + func_append install_shared_prog " -m $func_quote_arg_result" fi fi @@ -4478,8 +4692,8 @@ func_mode_install () relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { - func_quote_for_expand "$relink_command" - eval "func_echo $func_quote_for_expand_result" + func_quote_arg expand,pretty "$relink_command" + eval "func_echo $func_quote_arg_result" } if eval "$relink_command"; then : else @@ -5258,7 +5472,8 @@ else if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" - qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` + func_quote_arg pretty "$ECHO" + qECHO=$func_quote_arg_result $ECHO "\ # A function that is used when there is no print builtin or printf. @@ -5268,7 +5483,7 @@ func_fallback_echo () \$1 _LTECHO_EOF' } - ECHO=\"$qECHO\" + ECHO=$qECHO fi # Very basic option parsing. These options are (a) specific to @@ -6611,9 +6826,9 @@ func_mode_link () while test "$#" -gt 0; do arg=$1 shift - func_quote_for_eval "$arg" - qarg=$func_quote_for_eval_unquoted_result - func_append libtool_args " $func_quote_for_eval_result" + func_quote_arg pretty,unquoted "$arg" + qarg=$func_quote_arg_unquoted_result + func_append libtool_args " $func_quote_arg_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then @@ -7211,9 +7426,9 @@ func_mode_link () save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs - func_quote_for_eval "$flag" - func_append arg " $func_quote_for_eval_result" - func_append compiler_flags " $func_quote_for_eval_result" + func_quote_arg pretty "$flag" + func_append arg " $func_quote_arg_result" + func_append compiler_flags " $func_quote_arg_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" @@ -7227,10 +7442,10 @@ func_mode_link () save_ifs=$IFS; IFS=, for flag in $args; do IFS=$save_ifs - func_quote_for_eval "$flag" - func_append arg " $wl$func_quote_for_eval_result" - func_append compiler_flags " $wl$func_quote_for_eval_result" - func_append linker_flags " $func_quote_for_eval_result" + func_quote_arg pretty "$flag" + func_append arg " $wl$func_quote_arg_result" + func_append compiler_flags " $wl$func_quote_arg_result" + func_append linker_flags " $func_quote_arg_result" done IFS=$save_ifs func_stripname ' ' '' "$arg" @@ -7254,8 +7469,8 @@ func_mode_link () # -msg_* for osf cc -msg_*) - func_quote_for_eval "$arg" - arg=$func_quote_for_eval_result + func_quote_arg pretty "$arg" + arg=$func_quote_arg_result ;; # Flags to be passed through unchanged, with rationale: @@ -7272,12 +7487,16 @@ func_mode_link () # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang + # -fsanitize=* Clang/GCC memory and address sanitizer + # -fuse-ld=* Linker select flags for GCC -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ - -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*) - func_quote_for_eval "$arg" - arg=$func_quote_for_eval_result + -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ + -specs=*|-fsanitize=*|-fuse-ld=*) + func_quote_arg pretty "$arg" + arg=$func_quote_arg_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" @@ -7298,15 +7517,15 @@ func_mode_link () continue else # Otherwise treat like 'Some other compiler flag' below - func_quote_for_eval "$arg" - arg=$func_quote_for_eval_result + func_quote_arg pretty "$arg" + arg=$func_quote_arg_result fi ;; # Some other compiler flag. -* | +*) - func_quote_for_eval "$arg" - arg=$func_quote_for_eval_result + func_quote_arg pretty "$arg" + arg=$func_quote_arg_result ;; *.$objext) @@ -7426,8 +7645,8 @@ func_mode_link () *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. - func_quote_for_eval "$arg" - arg=$func_quote_for_eval_result + func_quote_arg pretty "$arg" + arg=$func_quote_arg_result ;; esac # arg @@ -9933,8 +10152,8 @@ EOF for cmd in $concat_cmds; do IFS=$save_ifs $opt_quiet || { - func_quote_for_expand "$cmd" - eval "func_echo $func_quote_for_expand_result" + func_quote_arg expand,pretty "$cmd" + eval "func_echo $func_quote_arg_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? @@ -10027,8 +10246,8 @@ EOF eval cmd=\"$cmd\" IFS=$save_ifs $opt_quiet || { - func_quote_for_expand "$cmd" - eval "func_echo $func_quote_for_expand_result" + func_quote_arg expand,pretty "$cmd" + eval "func_echo $func_quote_arg_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? @@ -10502,12 +10721,13 @@ EOF elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else - func_quote_for_eval "$var_value" - relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + func_quote_arg pretty "$var_value" + relink_command="$var=$func_quote_arg_result; export $var; $relink_command" fi done - relink_command="(cd `pwd`; $relink_command)" - relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + func_quote eval cd "`pwd`" + func_quote_arg pretty,unquoted "($func_quote_result; $relink_command)" + relink_command=$func_quote_arg_unquoted_result fi # Only actually do things if not in dry run mode. @@ -10747,13 +10967,15 @@ EOF elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else - func_quote_for_eval "$var_value" - relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + func_quote_arg pretty,unquoted "$var_value" + relink_command="$var=$func_quote_arg_unquoted_result; export $var; $relink_command" fi done # Quote the link command for shipping. - relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" - relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + func_quote eval cd "`pwd`" + relink_command="($func_quote_result; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + func_quote_arg pretty,unquoted "$relink_command" + relink_command=$func_quote_arg_unquoted_result if test yes = "$hardcode_automatic"; then relink_command= fi diff --git a/pcre2-10.22/m4/ax_pthread.m4 b/pcre2-10.32/m4/ax_pthread.m4 similarity index 100% rename from pcre2-10.22/m4/ax_pthread.m4 rename to pcre2-10.32/m4/ax_pthread.m4 diff --git a/pcre2-10.22/m4/libtool.m4 b/pcre2-10.32/m4/libtool.m4 similarity index 99% rename from pcre2-10.22/m4/libtool.m4 rename to pcre2-10.32/m4/libtool.m4 index a3bc337b7..597c6042b 100644 --- a/pcre2-10.22/m4/libtool.m4 +++ b/pcre2-10.32/m4/libtool.m4 @@ -1,6 +1,6 @@ # libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # -# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. +# Copyright (C) 1996-2001, 2003-2017 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives @@ -1042,8 +1042,8 @@ int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD - echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD - $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$AR $AR_FLAGS libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR $AR_FLAGS libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF @@ -1493,9 +1493,22 @@ need_locks=$enable_libtool_lock m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} -: ${AR_FLAGS=cru} _LT_DECL([], [AR], [1], [The archiver]) -_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) + +# Use ARFLAGS variable as AR's operation code to sync the variable naming with +# Automake. If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have +# higher priority because thats what people were doing historically (setting +# ARFLAGS for automake and AR_FLAGS for libtool). FIXME: Make the AR_FLAGS +# variable obsoleted/removed. + +test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr} +lt_ar_flags=$AR_FLAGS +_LT_DECL([], [lt_ar_flags], [0], [Flags to create an archive (by configure)]) + +# Make AR_FLAGS overridable by 'make ARFLAGS='. Don't try to run-time override +# by AR_FLAGS because that was never working and AR_FLAGS is about to die. +_LT_DECL([], [AR_FLAGS], [\@S|@{ARFLAGS-"\@S|@lt_ar_flags"}], + [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no @@ -2207,26 +2220,35 @@ m4_defun([_LT_CMD_STRIPLIB], striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) -if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then - test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" - test -z "$striplib" && striplib="$STRIP --strip-unneeded" - AC_MSG_RESULT([yes]) +if test -z "$STRIP"; then + AC_MSG_RESULT([no]) else -# FIXME - insert some real tests, host_os isn't really good enough - case $host_os in - darwin*) - if test -n "$STRIP"; then + if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + old_striplib="$STRIP --strip-debug" + striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) + else + case $host_os in + darwin*) + # FIXME - insert some real tests, host_os isn't really good enough striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) - else + ;; + freebsd*) + if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then + old_striplib="$STRIP --strip-debug" + striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) AC_MSG_RESULT([no]) - fi - ;; - *) - AC_MSG_RESULT([no]) - ;; - esac + ;; + esac + fi fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) @@ -4919,7 +4941,7 @@ m4_if([$1], [CXX], [ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else - _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) @@ -5156,6 +5178,7 @@ _LT_EOF emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' ;; interix[[3-9]]*) @@ -5373,7 +5396,7 @@ _LT_EOF if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else - _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no @@ -5861,6 +5884,7 @@ _LT_EOF emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' ;; osf3*) @@ -6730,6 +6754,7 @@ if test yes != "$_lt_caught_CXX_error"; then emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' ;; dgux*) diff --git a/pcre2-10.22/m4/ltoptions.m4 b/pcre2-10.32/m4/ltoptions.m4 similarity index 99% rename from pcre2-10.22/m4/ltoptions.m4 rename to pcre2-10.32/m4/ltoptions.m4 index 94b082976..621bd18b2 100644 --- a/pcre2-10.22/m4/ltoptions.m4 +++ b/pcre2-10.32/m4/ltoptions.m4 @@ -1,6 +1,6 @@ # Helper functions for option handling. -*- Autoconf -*- # -# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software +# Copyright (C) 2004-2005, 2007-2009, 2011-2017 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # diff --git a/pcre2-10.22/m4/ltsugar.m4 b/pcre2-10.32/m4/ltsugar.m4 similarity index 98% rename from pcre2-10.22/m4/ltsugar.m4 rename to pcre2-10.32/m4/ltsugar.m4 index 48bc9344a..ab69a6b96 100644 --- a/pcre2-10.22/m4/ltsugar.m4 +++ b/pcre2-10.32/m4/ltsugar.m4 @@ -1,6 +1,6 @@ # ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # -# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software +# Copyright (C) 2004-2005, 2007-2008, 2011-2017 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # diff --git a/pcre2-10.22/m4/ltversion.m4 b/pcre2-10.32/m4/ltversion.m4 similarity index 65% rename from pcre2-10.22/m4/ltversion.m4 rename to pcre2-10.32/m4/ltversion.m4 index fa04b52a3..8250ea47c 100644 --- a/pcre2-10.22/m4/ltversion.m4 +++ b/pcre2-10.32/m4/ltversion.m4 @@ -1,6 +1,6 @@ # ltversion.m4 -- version numbers -*- Autoconf -*- # -# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. +# Copyright (C) 2004, 2011-2017 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives @@ -9,15 +9,15 @@ # @configure_input@ -# serial 4179 ltversion.m4 +# serial 4219 ltversion.m4 # This file is part of GNU Libtool -m4_define([LT_PACKAGE_VERSION], [2.4.6]) -m4_define([LT_PACKAGE_REVISION], [2.4.6]) +m4_define([LT_PACKAGE_VERSION], [2.4.6.40-6ca5-dirty]) +m4_define([LT_PACKAGE_REVISION], [2.4.6.40]) AC_DEFUN([LTVERSION_VERSION], -[macro_version='2.4.6' -macro_revision='2.4.6' +[macro_version='2.4.6.40-6ca5-dirty' +macro_revision='2.4.6.40' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) diff --git a/pcre2-10.22/m4/lt~obsolete.m4 b/pcre2-10.32/m4/lt~obsolete.m4 similarity index 98% rename from pcre2-10.22/m4/lt~obsolete.m4 rename to pcre2-10.32/m4/lt~obsolete.m4 index c6b26f88f..9919d4de5 100644 --- a/pcre2-10.22/m4/lt~obsolete.m4 +++ b/pcre2-10.32/m4/lt~obsolete.m4 @@ -1,6 +1,6 @@ # lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # -# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software +# Copyright (C) 2004-2005, 2007, 2009, 2011-2017 Free Software # Foundation, Inc. # Written by Scott James Remnant, 2004. # diff --git a/pcre2-10.22/m4/pcre2_visibility.m4 b/pcre2-10.32/m4/pcre2_visibility.m4 similarity index 100% rename from pcre2-10.22/m4/pcre2_visibility.m4 rename to pcre2-10.32/m4/pcre2_visibility.m4 diff --git a/pcre2-10.22/missing b/pcre2-10.32/missing similarity index 98% rename from pcre2-10.22/missing rename to pcre2-10.32/missing index f62bbae30..c6e379584 100755 --- a/pcre2-10.22/missing +++ b/pcre2-10.32/missing @@ -1,9 +1,9 @@ #! /bin/sh # Common wrapper for a few potentially missing GNU programs. -scriptversion=2013-10-28.13; # UTC +scriptversion=2016-01-11.22; # UTC -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2017 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify @@ -210,6 +210,6 @@ exit $st # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff --git a/pcre2-10.22/pcre2-config.in b/pcre2-10.32/pcre2-config.in similarity index 97% rename from pcre2-10.22/pcre2-config.in rename to pcre2-10.32/pcre2-config.in index 932160ef5..74271c088 100644 --- a/pcre2-10.22/pcre2-config.in +++ b/pcre2-10.32/pcre2-config.in @@ -86,7 +86,7 @@ while test $# -gt 0; do ;; --libs-posix) if test @enable_pcre2_8@ = yes ; then - echo $libS$libR -lpcre2posix -lpcre2-8 + echo $libS$libR -lpcre2-posix -lpcre2-8 else echo "${usage}" 1>&2 fi diff --git a/pcre2-10.22/perltest.sh b/pcre2-10.32/perltest.sh similarity index 68% rename from pcre2-10.22/perltest.sh rename to pcre2-10.32/perltest.sh index 9cf7b17f7..5e6c466bf 100755 --- a/pcre2-10.22/perltest.sh +++ b/pcre2-10.32/perltest.sh @@ -1,14 +1,17 @@ #! /bin/sh # Script for testing regular expressions with perl to check that PCRE2 handles -# them the same. The Perl code has to have "use utf8" and "require Encode" at -# the start when running UTF-8 tests, but *not* for non-utf8 tests. (The -# "require" would actually be OK for non-utf8-tests, but is not always -# installed, so this way the script will always run for these tests.) +# them the same. If the first argument to this script is "-w", Perl is also +# called with "-w", which turns on its warning mode. +# +# The Perl code has to have "use utf8" and "require Encode" at the start when +# running UTF-8 tests, but *not* for non-utf8 tests. (The "require" would +# actually be OK for non-utf8-tests, but is not always installed, so this way +# the script will always run for these tests.) # # The desired effect is achieved by making this a shell script that passes the -# Perl script to Perl through a pipe. If the first argument is "-utf8", a -# suitable prefix is set up. +# Perl script to Perl through a pipe. If the first argument (possibly after +# removing "-w") is "-utf8", a suitable prefix is set up. # # The remaining arguments, if any, are passed to Perl. They are an input file # and an output file. If there is one argument, the output is written to @@ -17,7 +20,14 @@ # of the contorted piping input.) perl=perl +perlarg='' prefix='' + +if [ $# -gt 0 -a "$1" = "-w" ] ; then + perlarg="-w" + shift +fi + if [ $# -gt 0 -a "$1" = "-utf8" ] ; then prefix="use utf8; require Encode;" shift @@ -32,13 +42,26 @@ fi # aftertext interpreted as "print $' afterwards" # afteralltext ignored # dupnames ignored (Perl always allows) -# mark ignored +# jitstack ignored +# mark show mark information # no_auto_possess ignored -# no_start_optimize ignored +# no_start_optimize insert (??{""}) at pattern start (disables optimizing) +# -no_start_optimize ignored +# subject_literal does not process subjects for escapes # ucp sets Perl's /u modifier # utf invoke UTF-8 functionality # -# The data lines must not have any pcre2test modifiers. They are processed as +# Comment lines are ignored. The #pattern command can be used to set modifiers +# that will be added to each subsequent pattern, after any modifiers it may +# already have. NOTE: this is different to pcre2test where #pattern sets +# defaults which can be overridden on individual patterns. The #subject command +# may be used to set or unset a default "mark" modifier for data lines. This is +# the only use of #subject that is supported. The #perltest, #forbid_utf, and +# #newline_default commands, which are needed in the relevant pcre2test files, +# are ignored. Any other #-command is ignored, with a warning message. +# +# The data lines must not have any pcre2test modifiers. Unless +# "subject_literal" is on the pattern, data lines are processed as # Perl double-quoted strings, so if they contain " $ or @ characters, these # have to be escaped. For this reason, all such characters in the # Perl-compatible testinput1 and testinput4 files are escaped so that they can @@ -114,7 +137,42 @@ for (;;) printf " re> " if $interact; last if ! ($_ = <$infile>); printf $outfile "$_" if ! $interact; - next if ($_ =~ /^\s*$/ || $_ =~ /^#/); + next if ($_ =~ /^\s*$/ || $_ =~ /^#[\s!]/); + + # A few of pcre2test's #-commands are supported, or just ignored. Any others + # cause an error. + + if ($_ =~ /^#pattern(.*)/) + { + $extra_modifiers = $1; + chomp($extra_modifiers); + $extra_modifiers =~ s/\s+$//; + next; + } + elsif ($_ =~ /^#subject(.*)/) + { + $mod = $1; + chomp($mod); + $mod =~ s/\s+$//; + if ($mod =~ s/(-?)mark,?//) + { + $minus = $1; + $default_show_mark = ($minus =~ /^$/); + } + if ($mod !~ /^\s*$/) + { + printf $outfile "** Warning: \"$mod\" in #subject ignored\n"; + } + next; + } + elsif ($_ =~ /^#/) + { + if ($_ !~ /^#newline_default|^#perltest|^#forbid_utf/) + { + printf $outfile "** Warning: #-command ignored: %s", $_; + } + next; + } $pattern = $_; @@ -133,12 +191,18 @@ for (;;) $pattern =~ /^\s*((.).*\2)(.*)$/s; $pat = $1; - $mod = $3; + $del = $2; + $mod = "$3,$extra_modifiers"; + $mod =~ s/^,\s*//; # The private "aftertext" modifier means "print $' afterwards". $showrest = ($mod =~ s/aftertext,?//); + # The "subject_literal" modifer disables escapes in subjects. + + $subject_literal = ($mod =~ s/subject_literal,?//); + # "allaftertext" is used by pcre2test to print remainders after captures $mod =~ s/allaftertext,?//; @@ -151,18 +215,28 @@ for (;;) $mod =~ s/dupnames,?//; - # Remove "mark" (asks pcre2test to check MARK data) */ + # Remove "jitstack". - $mod =~ s/mark,?//; + $mod =~ s/jitstack=\d+,?//; + + # The "mark" modifier requests checking of MARK data */ + + $show_mark = $default_show_mark | ($mod =~ s/mark,?//); # "ucp" asks pcre2test to set PCRE2_UCP; change this to /u for Perl $mod =~ s/ucp,?/u/; - # Remove "no_auto_possess" and "no_start_optimize" (disable PCRE2 optimizations) + # Remove "no_auto_possess". $mod =~ s/no_auto_possess,?//; - $mod =~ s/no_start_optimize,?//; + + # Use no_start_optimize (disable PCRE2 start-up optimization) to disable Perl + # optimization by inserting (??{""}) at the start of the pattern. We may + # also encounter -no_start_optimize from a #pattern setting. + + $mod =~ s/-no_start_optimize,?//; + if ($mod =~ s/no_start_optimize,?//) { $pat =~ s/$del/$del(??{""})/; } # Add back retained modifiers and check that the pattern is valid. @@ -212,7 +286,14 @@ for (;;) last if ($_ eq ""); next if $_ =~ /^\\=(?:\s|$)/; # Comment line - $x = eval "\"$_\""; # To get escapes processed + if ($subject_literal) + { + $x = $_; + } + else + { + $x = eval "\"$_\""; # To get escapes processed + } # Empty array for holding results, ensure $REGERROR and $REGMARK are # unset, then do the matching. @@ -251,7 +332,7 @@ for (;;) elsif (scalar(@subs) == 0) { printf $outfile "No match"; - if (defined $REGERROR && $REGERROR != 1) + if ($show_mark && defined $REGERROR && $REGERROR != 1) { printf $outfile (", mark = %s", &pchars($REGERROR)); } printf $outfile "\n"; } @@ -279,7 +360,7 @@ for (;;) # set and the input pattern was a UTF-8 string. We can, however, force # it to be so marked. - if (defined $REGMARK && $REGMARK != 1) + if ($show_mark && defined $REGMARK && $REGMARK != 1) { $xx = $REGMARK; $xx = Encode::decode_utf8($xx) if $utf8; @@ -292,6 +373,6 @@ for (;;) # printf $outfile "\n"; PERLEND -) | $perl - $@ +) | $perl $perlarg - $@ # End diff --git a/pcre2-10.22/src/config.h.generic b/pcre2-10.32/src/config.h.generic similarity index 70% rename from pcre2-10.22/src/config.h.generic rename to pcre2-10.32/src/config.h.generic index 8a71be01e..89a52ef84 100644 --- a/pcre2-10.22/src/config.h.generic +++ b/pcre2-10.32/src/config.h.generic @@ -18,10 +18,10 @@ to set the macro values. In this case, you do not have to set -DHAVE_CONFIG_H, but if you do, default values will be taken from config.h for non-boolean macros that are not defined on the command line. -Boolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be defined -(conventionally to 1) for TRUE, and not defined at all for FALSE. All such -macros are listed as a commented #undef in config.h.generic. Macros such as -MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are +Boolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be +defined (conventionally to 1) for TRUE, and not defined at all for FALSE. All +such macros are listed as a commented #undef in config.h.generic. Macros such +as MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are surrounded by #ifndef/#endif lines so that the value can be overridden by -D. PCRE2 uses memmove() if HAVE_MEMMOVE is defined; otherwise it uses bcopy() if @@ -78,6 +78,9 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to 1 if you have the header file. */ /* #undef HAVE_MEMORY_H */ +/* Define to 1 if you have the `mkostemp' function. */ +/* #undef HAVE_MKOSTEMP */ + /* Define if you have POSIX threads libraries and header files. */ /* #undef HAVE_PTHREAD */ @@ -90,6 +93,9 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to 1 if you have the header file. */ /* #undef HAVE_READLINE_READLINE_H */ +/* Define to 1 if you have the `secure_getenv' function. */ +/* #undef HAVE_SECURE_GETENV */ + /* Define to 1 if you have the header file. */ /* #undef HAVE_STDINT_H */ @@ -126,19 +132,18 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to 1 if you have the header file. */ /* #undef HAVE_ZLIB_H */ -/* PCRE2 uses recursive function calls to handle backtracking while matching. - This can sometimes be a problem on systems that have stacks of limited - size. Define HEAP_MATCH_RECURSE to any value to get a version that doesn't - use recursion in the match() function; instead it creates its own stack by - steam using memory from the heap. For more detail, see the comments and - other stuff just above the match() function. */ -/* #undef HEAP_MATCH_RECURSE */ +/* This limits the amount of memory that may be used while matching a pattern. + It applies to both pcre2_match() and pcre2_dfa_match(). It does not apply + to JIT matching. The value is in kibibytes (units of 1024 bytes). */ +#ifndef HEAP_LIMIT +#define HEAP_LIMIT 20000000 +#endif /* 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 allows for - compiled patterns up to 64K long. This covers the vast majority of cases. - However, PCRE2 can also be compiled to use 3 or 4 bytes instead. This - allows for longer patterns in extreme cases. */ + compiled patterns up to 65535 code units long. This covers the vast + majority of cases. However, PCRE2 can also be compiled to use 3 or 4 bytes + instead. This allows for longer patterns in extreme cases. */ #ifndef LINK_SIZE #define LINK_SIZE 2 #endif @@ -150,25 +155,28 @@ sure both macros are undefined; an emulation function will then be used. */ #endif /* The value of MATCH_LIMIT determines the default number of times the - internal match() function can be called during a single execution of - pcre2_match(). There is a runtime interface for setting a different limit. - The limit exists in order to catch runaway regular expressions that take - for ever to determine that they do not match. The default is set very large - so that it does not accidentally catch legitimate cases. */ + pcre2_match() function can record a backtrack position during a single + matching attempt. The value is also used to limit a loop counter in + pcre2_dfa_match(). There is a runtime interface for setting a different + limit. The limit exists in order to catch runaway regular expressions that + take for ever to determine that they do not match. The default is set very + large so that it does not accidentally catch legitimate cases. */ #ifndef MATCH_LIMIT #define MATCH_LIMIT 10000000 #endif -/* The above limit applies to all calls of match(), whether or not they - increase the recursion depth. In some environments it is desirable to limit - the depth of recursive calls of match() more strictly, in order to restrict - the maximum amount of stack (or heap, if HEAP_MATCH_RECURSE is defined) - that is used. The value of MATCH_LIMIT_RECURSION applies only to recursive - calls of match(). To have any useful effect, it must be less than the value - of MATCH_LIMIT. The default is to use the same value as MATCH_LIMIT. There - is a runtime method for setting a different limit. */ -#ifndef MATCH_LIMIT_RECURSION -#define MATCH_LIMIT_RECURSION MATCH_LIMIT +/* The above limit applies to all backtracks, whether or not they are nested. + In some environments it is desirable to limit the nesting of backtracking + (that is, the depth of tree that is searched) more strictly, in order to + restrict the maximum amount of heap memory that is used. The value of + MATCH_LIMIT_DEPTH provides this facility. To have any useful effect, it + must be less than the value of MATCH_LIMIT. The default is to use the same + value as MATCH_LIMIT. There is a runtime method for setting a different + limit. In the case of pcre2_dfa_match(), this limit controls the depth of + the internal nested function calls that are used for pattern recursions, + lookarounds, and atomic groups. */ +#ifndef MATCH_LIMIT_DEPTH +#define MATCH_LIMIT_DEPTH MATCH_LIMIT #endif /* This limit is parameterized just in case anybody ever wants to change it. @@ -190,8 +198,8 @@ sure both macros are undefined; an emulation function will then be used. */ /* 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 - (ANYCRLF). */ + at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), 5 + (ANYCRLF), and 6 (NUL). */ #ifndef NEWLINE_DEFAULT #define NEWLINE_DEFAULT 2 #endif @@ -206,7 +214,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.22" +#define PACKAGE_STRING "PCRE2 10.32" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "pcre2" @@ -215,7 +223,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.22" +#define PACKAGE_VERSION "10.32" /* 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 @@ -224,15 +232,24 @@ sure both macros are undefined; an emulation function will then be used. */ #define PARENS_NEST_LIMIT 250 #endif -/* The value of PCRE2GREP_BUFSIZE determines the size of buffer used by - pcre2grep to hold parts of the file it is searching. This is also the - minimum value. The actual amount of memory used by pcre2grep is three times - this number, because it allows for the buffering of "before" and "after" - lines. */ +/* The value of PCRE2GREP_BUFSIZE is the starting size of the buffer used by + pcre2grep to hold parts of the file it is searching. The buffer will be + expanded up to PCRE2GREP_MAX_BUFSIZE if necessary, for files containing + very long lines. The actual amount of memory used by pcre2grep is three + times this number, because it allows for the buffering of "before" and + "after" lines. */ #ifndef PCRE2GREP_BUFSIZE #define PCRE2GREP_BUFSIZE 20480 #endif +/* The value of PCRE2GREP_MAX_BUFSIZE specifies the maximum size of the buffer + used by pcre2grep to hold parts of the file it is searching. The actual + amount of memory used by pcre2grep is three times this number, because it + allows for the buffering of "before" and "after" lines. */ +#ifndef PCRE2GREP_MAX_BUFSIZE +#define PCRE2GREP_MAX_BUFSIZE 1048576 +#endif + /* Define to any value to include debugging code. */ /* #undef PCRE2_DEBUG */ @@ -254,6 +271,11 @@ sure both macros are undefined; an emulation function will then be used. */ your system. */ /* #undef PTHREAD_CREATE_JOINABLE */ +/* Define to any non-zero number to enable support for SELinux compatible + executable memory allocator in JIT. Note that this will have no effect + unless SUPPORT_JIT is also defined. */ +/* #undef SLJIT_PROT_EXECUTABLE_ALLOCATOR */ + /* Define to 1 if you have the ANSI C header files. */ /* #undef STDC_HEADERS */ @@ -277,7 +299,8 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to any value to enable callout script support in pcre2grep. */ /* #undef SUPPORT_PCRE2GREP_CALLOUT */ -/* Define to any value to enable JIT support in pcre2grep. */ +/* Define to any value to enable JIT support in pcre2grep. Note that this will + have no effect unless SUPPORT_JIT is also defined. */ /* #undef SUPPORT_PCRE2GREP_JIT */ /* Define to any value to enable the 16 bit PCRE2 library. */ @@ -298,8 +321,39 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to any value for valgrind support to find invalid memory reads. */ /* #undef SUPPORT_VALGRIND */ +/* 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 + /* Version number of package */ -#define VERSION "10.22" +#define VERSION "10.32" + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* 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 */ /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ diff --git a/pcre2-10.22/src/config.h.in b/pcre2-10.32/src/config.h.in similarity index 70% rename from pcre2-10.22/src/config.h.in rename to pcre2-10.32/src/config.h.in index d4821af94..d8a5280f5 100644 --- a/pcre2-10.22/src/config.h.in +++ b/pcre2-10.32/src/config.h.in @@ -18,10 +18,10 @@ to set the macro values. In this case, you do not have to set -DHAVE_CONFIG_H, but if you do, default values will be taken from config.h for non-boolean macros that are not defined on the command line. -Boolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be defined -(conventionally to 1) for TRUE, and not defined at all for FALSE. All such -macros are listed as a commented #undef in config.h.generic. Macros such as -MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are +Boolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be +defined (conventionally to 1) for TRUE, and not defined at all for FALSE. All +such macros are listed as a commented #undef in config.h.generic. Macros such +as MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are surrounded by #ifndef/#endif lines so that the value can be overridden by -D. PCRE2 uses memmove() if HAVE_MEMMOVE is defined; otherwise it uses bcopy() if @@ -78,6 +78,9 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H +/* Define to 1 if you have the `mkostemp' function. */ +#undef HAVE_MKOSTEMP + /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD @@ -90,6 +93,9 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to 1 if you have the header file. */ #undef HAVE_READLINE_READLINE_H +/* Define to 1 if you have the `secure_getenv' function. */ +#undef HAVE_SECURE_GETENV + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H @@ -126,41 +132,41 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to 1 if you have the header file. */ #undef HAVE_ZLIB_H -/* PCRE2 uses recursive function calls to handle backtracking while matching. - This can sometimes be a problem on systems that have stacks of limited - size. Define HEAP_MATCH_RECURSE to any value to get a version that doesn't - use recursion in the match() function; instead it creates its own stack by - steam using memory from the heap. For more detail, see the comments and - other stuff just above the match() function. */ -#undef HEAP_MATCH_RECURSE +/* This limits the amount of memory that may be used while matching a pattern. + It applies to both pcre2_match() and pcre2_dfa_match(). It does not apply + to JIT matching. The value is in kibibytes (units of 1024 bytes). */ +#undef HEAP_LIMIT /* 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 allows for - compiled patterns up to 64K long. This covers the vast majority of cases. - However, PCRE2 can also be compiled to use 3 or 4 bytes instead. This - allows for longer patterns in extreme cases. */ + compiled patterns up to 65535 code units long. This covers the vast + majority of cases. However, PCRE2 can also be compiled to use 3 or 4 bytes + instead. This allows for longer patterns in extreme cases. */ #undef LINK_SIZE /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* The value of MATCH_LIMIT determines the default number of times the - internal match() function can be called during a single execution of - pcre2_match(). There is a runtime interface for setting a different limit. - The limit exists in order to catch runaway regular expressions that take - for ever to determine that they do not match. The default is set very large - so that it does not accidentally catch legitimate cases. */ + pcre2_match() function can record a backtrack position during a single + matching attempt. The value is also used to limit a loop counter in + pcre2_dfa_match(). There is a runtime interface for setting a different + limit. The limit exists in order to catch runaway regular expressions that + take for ever to determine that they do not match. The default is set very + large so that it does not accidentally catch legitimate cases. */ #undef MATCH_LIMIT -/* The above limit applies to all calls of match(), whether or not they - increase the recursion depth. In some environments it is desirable to limit - the depth of recursive calls of match() more strictly, in order to restrict - the maximum amount of stack (or heap, if HEAP_MATCH_RECURSE is defined) - that is used. The value of MATCH_LIMIT_RECURSION applies only to recursive - calls of match(). To have any useful effect, it must be less than the value - of MATCH_LIMIT. The default is to use the same value as MATCH_LIMIT. There - is a runtime method for setting a different limit. */ -#undef MATCH_LIMIT_RECURSION +/* The above limit applies to all backtracks, whether or not they are nested. + In some environments it is desirable to limit the nesting of backtracking + (that is, the depth of tree that is searched) more strictly, in order to + restrict the maximum amount of heap memory that is used. The value of + MATCH_LIMIT_DEPTH provides this facility. To have any useful effect, it + must be less than the value of MATCH_LIMIT. The default is to use the same + value as MATCH_LIMIT. There is a runtime method for setting a different + limit. In the case of pcre2_dfa_match(), this limit controls the depth of + the internal nested function calls that are used for pattern recursions, + lookarounds, and atomic groups. */ +#undef MATCH_LIMIT_DEPTH /* This limit is parameterized just in case anybody ever wants to change it. Care must be taken if it is increased, because it guards against integer @@ -177,8 +183,8 @@ sure both macros are undefined; an emulation function will then be used. */ /* 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 - (ANYCRLF). */ + at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), 5 + (ANYCRLF), and 6 (NUL). */ #undef NEWLINE_DEFAULT /* Name of package */ @@ -207,13 +213,20 @@ sure both macros are undefined; an emulation function will then be used. */ stack that is used while compiling a pattern. */ #undef PARENS_NEST_LIMIT -/* The value of PCRE2GREP_BUFSIZE determines the size of buffer used by - pcre2grep to hold parts of the file it is searching. This is also the - minimum value. The actual amount of memory used by pcre2grep is three times - this number, because it allows for the buffering of "before" and "after" - lines. */ +/* The value of PCRE2GREP_BUFSIZE is the starting size of the buffer used by + pcre2grep to hold parts of the file it is searching. The buffer will be + expanded up to PCRE2GREP_MAX_BUFSIZE if necessary, for files containing + very long lines. The actual amount of memory used by pcre2grep is three + times this number, because it allows for the buffering of "before" and + "after" lines. */ #undef PCRE2GREP_BUFSIZE +/* The value of PCRE2GREP_MAX_BUFSIZE specifies the maximum size of the buffer + used by pcre2grep to hold parts of the file it is searching. The actual + amount of memory used by pcre2grep is three times this number, because it + allows for the buffering of "before" and "after" lines. */ +#undef PCRE2GREP_MAX_BUFSIZE + /* to make a symbol visible */ #undef PCRE2POSIX_EXP_DECL @@ -245,6 +258,11 @@ sure both macros are undefined; an emulation function will then be used. */ your system. */ #undef PTHREAD_CREATE_JOINABLE +/* Define to any non-zero number to enable support for SELinux compatible + executable memory allocator in JIT. Note that this will have no effect + unless SUPPORT_JIT is also defined. */ +#undef SLJIT_PROT_EXECUTABLE_ALLOCATOR + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS @@ -268,7 +286,8 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to any value to enable callout script support in pcre2grep. */ #undef SUPPORT_PCRE2GREP_CALLOUT -/* Define to any value to enable JIT support in pcre2grep. */ +/* Define to any value to enable JIT support in pcre2grep. Note that this will + have no effect unless SUPPORT_JIT is also defined. */ #undef SUPPORT_PCRE2GREP_JIT /* Define to any value to enable the 16 bit PCRE2 library. */ @@ -289,9 +308,41 @@ sure both macros are undefined; an emulation function will then be used. */ /* Define to any value for valgrind support to find invalid memory reads. */ #undef SUPPORT_VALGRIND +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + /* Version number of package */ #undef VERSION +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* 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 + /* Define to empty if `const' does not conform to ANSI C. */ #undef const diff --git a/pcre2-10.22/src/dftables.c b/pcre2-10.32/src/dftables.c similarity index 87% rename from pcre2-10.22/src/dftables.c rename to pcre2-10.32/src/dftables.c index dfb90b594..c0af36252 100644 --- a/pcre2-10.22/src/dftables.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -104,6 +104,14 @@ fprintf(f, "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"); +fprintf(f, + "/*The dftables program (which is distributed with PCRE2) can be used to\n" + "build alternative versions of this file. This is necessary if you are\n" + "running in an EBCDIC environment, or if you want to default to a different\n" + "encoding, for example ISO-8859-1. When dftables is run, it creates these\n" + "tables in the current locale. This happens automatically if PCRE2 is\n" + "configured with --enable-rebuild-chartables. */\n\n"); + /* Force config.h in z/OS */ #if defined NATIVE_ZOS @@ -115,7 +123,7 @@ fprintf(f, #endif fprintf(f, - "/* The following #includes are present because without them gcc 4.x may remove\n" + "/* The following #include is present because without it 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" @@ -153,11 +161,10 @@ for (i = 0; i < 256; i++) fprintf(f, ",\n\n"); fprintf(f, - "/* This table contains bit maps for various character classes.\n" - "Each map is 32 bytes long and the bits run from the least\n" - "significant end of each byte. The classes that have their own\n" - "maps are: space, xdigit, digit, upper, lower, word, graph\n" - "print, punct, and cntrl. Other classes are built from combinations. */\n\n"); + "/* This table contains bit maps for various character classes. Each map is 32\n" + "bytes long and the bits run from the least significant end of each byte. The\n" + "classes that have their own maps are: space, xdigit, digit, upper, lower, word,\n" + "graph print, punct, and cntrl. Other classes are built from combinations. */\n\n"); fprintf(f, " "); for (i = 0; i < cbit_length; i++) @@ -178,10 +185,8 @@ fprintf(f, " 0x%02x letter\n" " 0x%02x decimal digit\n" " 0x%02x hexadecimal digit\n" - " 0x%02x alphanumeric or '_'\n" - " 0x%02x regular expression metacharacter or binary zero\n*/\n\n", - ctype_space, ctype_letter, ctype_digit, ctype_xdigit, ctype_word, - ctype_meta); + " 0x%02x alphanumeric or '_'\n*/\n\n", + ctype_space, ctype_letter, ctype_digit, ctype_xdigit, ctype_word); fprintf(f, " "); for (i = 0; i < 256; i++) diff --git a/pcre2-10.22/src/pcre2.h.generic b/pcre2-10.32/src/pcre2.h.generic similarity index 56% rename from pcre2-10.22/src/pcre2.h.generic rename to pcre2-10.32/src/pcre2.h.generic index 20d221b80..3d2feb7a6 100644 --- a/pcre2-10.22/src/pcre2.h.generic +++ b/pcre2-10.32/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) 2016 University of Cambridge + Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -36,15 +36,21 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#ifndef _PCRE2_H -#define _PCRE2_H +#ifndef PCRE2_H_IDEMPOTENT_GUARD +#define PCRE2_H_IDEMPOTENT_GUARD /* The current PCRE version information. */ -#define PCRE2_MAJOR 10 -#define PCRE2_MINOR 22 -#define PCRE2_PRERELEASE -#define PCRE2_DATE 2016-07-29 +#define PCRE2_MAJOR 10 +#define PCRE2_MINOR 32 +#define PCRE2_PRERELEASE +#define PCRE2_DATE 2018-09-10 + +/* For the benefit of systems without stdint.h, an alternative is to use +inttypes.h. The existence of these headers is checked by configure or CMake. */ + +#define PCRE2_HAVE_STDINT_H 1 +#define PCRE2_HAVE_INTTYPES_H 1 /* 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 @@ -67,12 +73,32 @@ don't change existing definitions of PCRE2_EXP_DECL. */ # endif #endif -/* Have to include limits.h, stdlib.h and stdint.h to ensure that size_t and -uint8_t, UCHAR_MAX, etc are defined. */ +/* When compiling with the MSVC compiler, it is sometimes necessary to include +a "calling convention" before exported function names. (This is secondhand +information; I know nothing about MSVC myself). For example, something like + + void __cdecl function(....) + +might be needed. In order so make this easy, all the exported functions have +PCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not +set, we ensure here that it has no effect. */ + +#ifndef PCRE2_CALL_CONVENTION +#define PCRE2_CALL_CONVENTION +#endif + +/* Have to include limits.h, stdlib.h and stdint.h (or inttypes.h) to ensure +that size_t and uint8_t, UCHAR_MAX, etc are defined. If the system has neither +header, the relevant values must be provided by some other means. */ #include #include + +#if PCRE2_HAVE_STDINT_H #include +#elif PCRE2_HAVE_INTTYPES_H +#include +#endif /* Allow for C++ users compiling this directly. */ @@ -87,6 +113,7 @@ others can be added next to them */ #define PCRE2_ANCHORED 0x80000000u #define PCRE2_NO_UTF_CHECK 0x40000000u +#define PCRE2_ENDANCHORED 0x20000000u /* The following option bits can be passed only to pcre2_compile(). However, they may affect compilation, JIT compilation, and/or interpretive execution. @@ -122,6 +149,15 @@ D is inspected during pcre2_dfa_match() execution #define PCRE2_ALT_CIRCUMFLEX 0x00200000u /* J M D */ #define PCRE2_ALT_VERBNAMES 0x00400000u /* C */ #define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */ +#define PCRE2_EXTENDED_MORE 0x01000000u /* C */ +#define PCRE2_LITERAL 0x02000000u /* C */ + +/* An additional compile options word is available in the compile context. */ + +#define PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES 0x00000001u /* C */ +#define PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL 0x00000002u /* C */ +#define PCRE2_EXTRA_MATCH_WORD 0x00000004u /* C */ +#define PCRE2_EXTRA_MATCH_LINE 0x00000008u /* C */ /* These are for pcre2_jit_compile(). */ @@ -160,6 +196,16 @@ ignored for pcre2_jit_match(). */ #define PCRE2_NO_JIT 0x00002000u +/* Options for pcre2_pattern_convert(). */ + +#define PCRE2_CONVERT_UTF 0x00000001u +#define PCRE2_CONVERT_NO_UTF_CHECK 0x00000002u +#define PCRE2_CONVERT_POSIX_BASIC 0x00000004u +#define PCRE2_CONVERT_POSIX_EXTENDED 0x00000008u +#define PCRE2_CONVERT_GLOB 0x00000010u +#define PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR 0x00000030u +#define PCRE2_CONVERT_GLOB_NO_STARSTAR 0x00000050u + /* 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 greater than zero. */ @@ -169,11 +215,112 @@ greater than zero. */ #define PCRE2_NEWLINE_CRLF 3 #define PCRE2_NEWLINE_ANY 4 #define PCRE2_NEWLINE_ANYCRLF 5 +#define PCRE2_NEWLINE_NUL 6 #define PCRE2_BSR_UNICODE 1 #define PCRE2_BSR_ANYCRLF 2 -/* Error codes: no match and partial match are "expected" errors. */ +/* Error codes for pcre2_compile(). Some of these are also used by +pcre2_pattern_convert(). */ + +#define PCRE2_ERROR_END_BACKSLASH 101 +#define PCRE2_ERROR_END_BACKSLASH_C 102 +#define PCRE2_ERROR_UNKNOWN_ESCAPE 103 +#define PCRE2_ERROR_QUANTIFIER_OUT_OF_ORDER 104 +#define PCRE2_ERROR_QUANTIFIER_TOO_BIG 105 +#define PCRE2_ERROR_MISSING_SQUARE_BRACKET 106 +#define PCRE2_ERROR_ESCAPE_INVALID_IN_CLASS 107 +#define PCRE2_ERROR_CLASS_RANGE_ORDER 108 +#define PCRE2_ERROR_QUANTIFIER_INVALID 109 +#define PCRE2_ERROR_INTERNAL_UNEXPECTED_REPEAT 110 +#define PCRE2_ERROR_INVALID_AFTER_PARENS_QUERY 111 +#define PCRE2_ERROR_POSIX_CLASS_NOT_IN_CLASS 112 +#define PCRE2_ERROR_POSIX_NO_SUPPORT_COLLATING 113 +#define PCRE2_ERROR_MISSING_CLOSING_PARENTHESIS 114 +#define PCRE2_ERROR_BAD_SUBPATTERN_REFERENCE 115 +#define PCRE2_ERROR_NULL_PATTERN 116 +#define PCRE2_ERROR_BAD_OPTIONS 117 +#define PCRE2_ERROR_MISSING_COMMENT_CLOSING 118 +#define PCRE2_ERROR_PARENTHESES_NEST_TOO_DEEP 119 +#define PCRE2_ERROR_PATTERN_TOO_LARGE 120 +#define PCRE2_ERROR_HEAP_FAILED 121 +#define PCRE2_ERROR_UNMATCHED_CLOSING_PARENTHESIS 122 +#define PCRE2_ERROR_INTERNAL_CODE_OVERFLOW 123 +#define PCRE2_ERROR_MISSING_CONDITION_CLOSING 124 +#define PCRE2_ERROR_LOOKBEHIND_NOT_FIXED_LENGTH 125 +#define PCRE2_ERROR_ZERO_RELATIVE_REFERENCE 126 +#define PCRE2_ERROR_TOO_MANY_CONDITION_BRANCHES 127 +#define PCRE2_ERROR_CONDITION_ASSERTION_EXPECTED 128 +#define PCRE2_ERROR_BAD_RELATIVE_REFERENCE 129 +#define PCRE2_ERROR_UNKNOWN_POSIX_CLASS 130 +#define PCRE2_ERROR_INTERNAL_STUDY_ERROR 131 +#define PCRE2_ERROR_UNICODE_NOT_SUPPORTED 132 +#define PCRE2_ERROR_PARENTHESES_STACK_CHECK 133 +#define PCRE2_ERROR_CODE_POINT_TOO_BIG 134 +#define PCRE2_ERROR_LOOKBEHIND_TOO_COMPLICATED 135 +#define PCRE2_ERROR_LOOKBEHIND_INVALID_BACKSLASH_C 136 +#define PCRE2_ERROR_UNSUPPORTED_ESCAPE_SEQUENCE 137 +#define PCRE2_ERROR_CALLOUT_NUMBER_TOO_BIG 138 +#define PCRE2_ERROR_MISSING_CALLOUT_CLOSING 139 +#define PCRE2_ERROR_ESCAPE_INVALID_IN_VERB 140 +#define PCRE2_ERROR_UNRECOGNIZED_AFTER_QUERY_P 141 +#define PCRE2_ERROR_MISSING_NAME_TERMINATOR 142 +#define PCRE2_ERROR_DUPLICATE_SUBPATTERN_NAME 143 +#define PCRE2_ERROR_INVALID_SUBPATTERN_NAME 144 +#define PCRE2_ERROR_UNICODE_PROPERTIES_UNAVAILABLE 145 +#define PCRE2_ERROR_MALFORMED_UNICODE_PROPERTY 146 +#define PCRE2_ERROR_UNKNOWN_UNICODE_PROPERTY 147 +#define PCRE2_ERROR_SUBPATTERN_NAME_TOO_LONG 148 +#define PCRE2_ERROR_TOO_MANY_NAMED_SUBPATTERNS 149 +#define PCRE2_ERROR_CLASS_INVALID_RANGE 150 +#define PCRE2_ERROR_OCTAL_BYTE_TOO_BIG 151 +#define PCRE2_ERROR_INTERNAL_OVERRAN_WORKSPACE 152 +#define PCRE2_ERROR_INTERNAL_MISSING_SUBPATTERN 153 +#define PCRE2_ERROR_DEFINE_TOO_MANY_BRANCHES 154 +#define PCRE2_ERROR_BACKSLASH_O_MISSING_BRACE 155 +#define PCRE2_ERROR_INTERNAL_UNKNOWN_NEWLINE 156 +#define PCRE2_ERROR_BACKSLASH_G_SYNTAX 157 +#define PCRE2_ERROR_PARENS_QUERY_R_MISSING_CLOSING 158 +/* Error 159 is obsolete and should now never occur */ +#define PCRE2_ERROR_VERB_ARGUMENT_NOT_ALLOWED 159 +#define PCRE2_ERROR_VERB_UNKNOWN 160 +#define PCRE2_ERROR_SUBPATTERN_NUMBER_TOO_BIG 161 +#define PCRE2_ERROR_SUBPATTERN_NAME_EXPECTED 162 +#define PCRE2_ERROR_INTERNAL_PARSED_OVERFLOW 163 +#define PCRE2_ERROR_INVALID_OCTAL 164 +#define PCRE2_ERROR_SUBPATTERN_NAMES_MISMATCH 165 +#define PCRE2_ERROR_MARK_MISSING_ARGUMENT 166 +#define PCRE2_ERROR_INVALID_HEXADECIMAL 167 +#define PCRE2_ERROR_BACKSLASH_C_SYNTAX 168 +#define PCRE2_ERROR_BACKSLASH_K_SYNTAX 169 +#define PCRE2_ERROR_INTERNAL_BAD_CODE_LOOKBEHINDS 170 +#define PCRE2_ERROR_BACKSLASH_N_IN_CLASS 171 +#define PCRE2_ERROR_CALLOUT_STRING_TOO_LONG 172 +#define PCRE2_ERROR_UNICODE_DISALLOWED_CODE_POINT 173 +#define PCRE2_ERROR_UTF_IS_DISABLED 174 +#define PCRE2_ERROR_UCP_IS_DISABLED 175 +#define PCRE2_ERROR_VERB_NAME_TOO_LONG 176 +#define PCRE2_ERROR_BACKSLASH_U_CODE_POINT_TOO_BIG 177 +#define PCRE2_ERROR_MISSING_OCTAL_OR_HEX_DIGITS 178 +#define PCRE2_ERROR_VERSION_CONDITION_SYNTAX 179 +#define PCRE2_ERROR_INTERNAL_BAD_CODE_AUTO_POSSESS 180 +#define PCRE2_ERROR_CALLOUT_NO_STRING_DELIMITER 181 +#define PCRE2_ERROR_CALLOUT_BAD_STRING_DELIMITER 182 +#define PCRE2_ERROR_BACKSLASH_C_CALLER_DISABLED 183 +#define PCRE2_ERROR_QUERY_BARJX_NEST_TOO_DEEP 184 +#define PCRE2_ERROR_BACKSLASH_C_LIBRARY_DISABLED 185 +#define PCRE2_ERROR_PATTERN_TOO_COMPLICATED 186 +#define PCRE2_ERROR_LOOKBEHIND_TOO_LONG 187 +#define PCRE2_ERROR_PATTERN_STRING_TOO_LONG 188 +#define PCRE2_ERROR_INTERNAL_BAD_CODE 189 +#define PCRE2_ERROR_INTERNAL_BAD_CODE_IN_SKIP 190 +#define PCRE2_ERROR_NO_SURROGATES_IN_UTF16 191 +#define PCRE2_ERROR_BAD_LITERAL_OPTIONS 192 +#define PCRE2_ERROR_SUPPORTED_ONLY_IN_UNICODE 193 +#define PCRE2_ERROR_INVALID_HYPHEN_IN_OPTIONS 194 + + +/* "Expected" matching error codes: no match and partial match. */ #define PCRE2_ERROR_NOMATCH (-1) #define PCRE2_ERROR_PARTIAL (-2) @@ -213,10 +360,10 @@ greater than zero. */ #define PCRE2_ERROR_UTF32_ERR1 (-27) #define PCRE2_ERROR_UTF32_ERR2 (-28) -/* Error codes for pcre2[_dfa]_match(), substring extraction functions, context -functions, and serializing functions. They are in numerical order. Originally -they were in alphabetical order too, but now that PCRE2 is released, the -numbers must not be changed. */ +/* Miscellaneous error codes for pcre2[_dfa]_match(), substring extraction +functions, context functions, and serializing functions. They are in numerical +order. Originally they were in alphabetical order too, but now that PCRE2 is +released, the numbers must not be changed. */ #define PCRE2_ERROR_BADDATA (-29) #define PCRE2_ERROR_MIXEDTABLES (-30) /* Name was changed */ @@ -242,7 +389,8 @@ numbers must not be changed. */ #define PCRE2_ERROR_NOUNIQUESUBSTRING (-50) #define PCRE2_ERROR_NULL (-51) #define PCRE2_ERROR_RECURSELOOP (-52) -#define PCRE2_ERROR_RECURSIONLIMIT (-53) +#define PCRE2_ERROR_DEPTHLIMIT (-53) +#define PCRE2_ERROR_RECURSIONLIMIT (-53) /* Obsolete synonym */ #define PCRE2_ERROR_UNAVAILABLE (-54) #define PCRE2_ERROR_UNSET (-55) #define PCRE2_ERROR_BADOFFSETLIMIT (-56) @@ -252,6 +400,10 @@ numbers must not be changed. */ #define PCRE2_ERROR_BADSUBSPATTERN (-60) #define PCRE2_ERROR_TOOMANYREPLACE (-61) #define PCRE2_ERROR_BADSERIALIZEDDATA (-62) +#define PCRE2_ERROR_HEAPLIMIT (-63) +#define PCRE2_ERROR_CONVERT_SYNTAX (-64) +#define PCRE2_ERROR_INTERNAL_DUPMATCH (-65) + /* Request types for pcre2_pattern_info() */ @@ -276,9 +428,13 @@ numbers must not be changed. */ #define PCRE2_INFO_NAMEENTRYSIZE 18 #define PCRE2_INFO_NAMETABLE 19 #define PCRE2_INFO_NEWLINE 20 -#define PCRE2_INFO_RECURSIONLIMIT 21 +#define PCRE2_INFO_DEPTHLIMIT 21 +#define PCRE2_INFO_RECURSIONLIMIT 21 /* Obsolete synonym */ #define PCRE2_INFO_SIZE 22 #define PCRE2_INFO_HASBACKSLASHC 23 +#define PCRE2_INFO_FRAMESIZE 24 +#define PCRE2_INFO_HEAPLIMIT 25 +#define PCRE2_INFO_EXTRAOPTIONS 26 /* Request types for pcre2_config(). */ @@ -289,11 +445,16 @@ numbers must not be changed. */ #define PCRE2_CONFIG_MATCHLIMIT 4 #define PCRE2_CONFIG_NEWLINE 5 #define PCRE2_CONFIG_PARENSLIMIT 6 -#define PCRE2_CONFIG_RECURSIONLIMIT 7 -#define PCRE2_CONFIG_STACKRECURSE 8 +#define PCRE2_CONFIG_DEPTHLIMIT 7 +#define PCRE2_CONFIG_RECURSIONLIMIT 7 /* Obsolete synonym */ +#define PCRE2_CONFIG_STACKRECURSE 8 /* Obsolete */ #define PCRE2_CONFIG_UNICODE 9 #define PCRE2_CONFIG_UNICODE_VERSION 10 #define PCRE2_CONFIG_VERSION 11 +#define PCRE2_CONFIG_HEAPLIMIT 12 +#define PCRE2_CONFIG_NEVER_BACKSLASH_C 13 +#define PCRE2_CONFIG_COMPILED_WIDTHS 14 + /* Types for code units in patterns and subject strings. */ @@ -328,6 +489,9 @@ typedef struct pcre2_real_compile_context pcre2_compile_context; \ struct pcre2_real_match_context; \ typedef struct pcre2_real_match_context pcre2_match_context; \ \ +struct pcre2_real_convert_context; \ +typedef struct pcre2_real_convert_context pcre2_convert_context; \ +\ struct pcre2_real_code; \ typedef struct pcre2_real_code pcre2_code; \ \ @@ -346,6 +510,11 @@ without changing the API of the function, thereby allowing old clients to work without modification. Define the generic version in a macro; the width-specific versions are generated from this macro below. */ +/* Flags for the callout_flags field. These are cleared after a callout. */ + +#define PCRE2_CALLOUT_STARTMATCH 0x00000001u /* Set for each bumpalong */ +#define PCRE2_CALLOUT_BACKTRACK 0x00000002u /* Set after a backtrack */ + #define PCRE2_STRUCTURE_LIST \ typedef struct pcre2_callout_block { \ uint32_t version; /* Identifies version of block */ \ @@ -365,6 +534,8 @@ typedef struct pcre2_callout_block { \ PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \ PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \ PCRE2_SPTR callout_string; /* String compiled into pattern */ \ + /* ------------------- Added for Version 2 -------------------------- */ \ + uint32_t callout_flags; /* See above for list */ \ /* ------------------------------------------------------------------ */ \ } pcre2_callout_block; \ \ @@ -386,170 +557,220 @@ expanded for each width below. Start with functions that give general information. */ #define PCRE2_GENERAL_INFO_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_config(uint32_t, void *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION pcre2_config(uint32_t, void *); /* Functions for manipulating contexts. */ #define PCRE2_GENERAL_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_general_context *pcre2_general_context_copy(pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_general_context *pcre2_general_context_create( \ - void *(*)(PCRE2_SIZE, void *), \ - void (*)(void *, void *), void *); \ -PCRE2_EXP_DECL void pcre2_general_context_free(pcre2_general_context *); +PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ + *pcre2_general_context_copy(pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ + *pcre2_general_context_create(void *(*)(PCRE2_SIZE, void *), \ + void (*)(void *, void *), void *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_general_context_free(pcre2_general_context *); #define PCRE2_COMPILE_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_compile_context *pcre2_compile_context_copy(pcre2_compile_context *); \ -PCRE2_EXP_DECL \ - pcre2_compile_context *pcre2_compile_context_create(pcre2_general_context *);\ -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); \ -PCRE2_EXP_DECL int pcre2_set_compile_recursion_guard(\ - pcre2_compile_context *, int (*)(uint32_t, void *), \ - void *); +PCRE2_EXP_DECL pcre2_compile_context PCRE2_CALL_CONVENTION \ + *pcre2_compile_context_copy(pcre2_compile_context *); \ +PCRE2_EXP_DECL pcre2_compile_context PCRE2_CALL_CONVENTION \ + *pcre2_compile_context_create(pcre2_general_context *);\ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_compile_context_free(pcre2_compile_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_bsr(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_character_tables(pcre2_compile_context *, const unsigned char *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_compile_extra_options(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_max_pattern_length(pcre2_compile_context *, PCRE2_SIZE); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_newline(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_parens_nest_limit(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_compile_recursion_guard(pcre2_compile_context *, \ + int (*)(uint32_t, void *), void *); #define PCRE2_MATCH_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_match_context *pcre2_match_context_copy(pcre2_match_context *); \ -PCRE2_EXP_DECL \ - pcre2_match_context *pcre2_match_context_create(pcre2_general_context *); \ -PCRE2_EXP_DECL void pcre2_match_context_free(pcre2_match_context *); \ -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( \ - pcre2_match_context *, void *(*)(PCRE2_SIZE, void *), \ - void (*)(void *, void *), void *); +PCRE2_EXP_DECL pcre2_match_context PCRE2_CALL_CONVENTION \ + *pcre2_match_context_copy(pcre2_match_context *); \ +PCRE2_EXP_DECL pcre2_match_context PCRE2_CALL_CONVENTION \ + *pcre2_match_context_create(pcre2_general_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_match_context_free(pcre2_match_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_callout(pcre2_match_context *, \ + int (*)(pcre2_callout_block *, void *), void *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_depth_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_heap_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_match_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_offset_limit(pcre2_match_context *, PCRE2_SIZE); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_recursion_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_recursion_memory_management(pcre2_match_context *, \ + void *(*)(PCRE2_SIZE, void *), void (*)(void *, void *), void *); + +#define PCRE2_CONVERT_CONTEXT_FUNCTIONS \ +PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \ + *pcre2_convert_context_copy(pcre2_convert_context *); \ +PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \ + *pcre2_convert_context_create(pcre2_general_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_convert_context_free(pcre2_convert_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_glob_escape(pcre2_convert_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_glob_separator(pcre2_convert_context *, uint32_t); /* Functions concerned with compiling a pattern to PCRE internal code. */ #define PCRE2_COMPILE_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_code *pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, \ - int *, PCRE2_SIZE *, pcre2_compile_context *); \ -PCRE2_EXP_DECL void pcre2_code_free(pcre2_code *); \ -PCRE2_EXP_DECL \ - pcre2_code *pcre2_code_copy(const pcre2_code *); +PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ + *pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, \ + pcre2_compile_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_code_free(pcre2_code *); \ +PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ + *pcre2_code_copy(const pcre2_code *); \ +PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ + *pcre2_code_copy_with_tables(const pcre2_code *); /* Functions that give information about a compiled pattern. */ #define PCRE2_PATTERN_INFO_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_pattern_info(const pcre2_code *, uint32_t, \ - void *); \ -PCRE2_EXP_DECL int pcre2_callout_enumerate(const pcre2_code *, \ - int (*)(pcre2_callout_enumerate_block *, void *), \ - void *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_pattern_info(const pcre2_code *, uint32_t, void *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_callout_enumerate(const pcre2_code *, \ + int (*)(pcre2_callout_enumerate_block *, void *), void *); /* Functions for running a match and inspecting the result. */ #define PCRE2_MATCH_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_match_data *pcre2_match_data_create(uint32_t, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_match_data *pcre2_match_data_create_from_pattern(\ - const pcre2_code *, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL int pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, \ - PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *, int *, \ - PCRE2_SIZE); \ -PCRE2_EXP_DECL int pcre2_match(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *); \ -PCRE2_EXP_DECL void pcre2_match_data_free(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SPTR pcre2_get_mark(pcre2_match_data *); \ -PCRE2_EXP_DECL uint32_t pcre2_get_ovector_count(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SIZE *pcre2_get_ovector_pointer(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SIZE pcre2_get_startchar(pcre2_match_data *); +PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ + *pcre2_match_data_create(uint32_t, pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ + *pcre2_match_data_create_from_pattern(const pcre2_code *, \ + pcre2_general_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *, int *, PCRE2_SIZE); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_match_data_free(pcre2_match_data *); \ +PCRE2_EXP_DECL PCRE2_SPTR PCRE2_CALL_CONVENTION \ + pcre2_get_mark(pcre2_match_data *); \ +PCRE2_EXP_DECL uint32_t PCRE2_CALL_CONVENTION \ + pcre2_get_ovector_count(pcre2_match_data *); \ +PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ + *pcre2_get_ovector_pointer(pcre2_match_data *); \ +PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ + pcre2_get_startchar(pcre2_match_data *); /* Convenience functions for handling matched substrings. */ #define PCRE2_SUBSTRING_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_substring_copy_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_UCHAR *, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_copy_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_UCHAR *, PCRE2_SIZE *); \ -PCRE2_EXP_DECL void pcre2_substring_free(PCRE2_UCHAR *); \ -PCRE2_EXP_DECL int pcre2_substring_get_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_UCHAR **, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_get_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_UCHAR **, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_length_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_length_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_nametable_scan(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SPTR *, PCRE2_SPTR *); \ -PCRE2_EXP_DECL int pcre2_substring_number_from_name(\ - const pcre2_code *, PCRE2_SPTR); \ -PCRE2_EXP_DECL void pcre2_substring_list_free(PCRE2_SPTR *); \ -PCRE2_EXP_DECL int pcre2_substring_list_get(pcre2_match_data *, \ - PCRE2_UCHAR ***, PCRE2_SIZE **); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_copy_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR *, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_copy_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR *, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_substring_free(PCRE2_UCHAR *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_get_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR **, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_get_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR **, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_length_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_length_bynumber(pcre2_match_data *, uint32_t, PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_nametable_scan(const pcre2_code *, PCRE2_SPTR, PCRE2_SPTR *, \ + PCRE2_SPTR *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_number_from_name(const pcre2_code *, PCRE2_SPTR); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_substring_list_free(PCRE2_SPTR *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_list_get(pcre2_match_data *, PCRE2_UCHAR ***, PCRE2_SIZE **); /* Functions for serializing / deserializing compiled patterns. */ #define PCRE2_SERIALIZE_FUNCTIONS \ -PCRE2_EXP_DECL int32_t pcre2_serialize_encode(const pcre2_code **, \ - int32_t, uint8_t **, PCRE2_SIZE *, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL int32_t pcre2_serialize_decode(pcre2_code **, int32_t, \ - const uint8_t *, pcre2_general_context *); \ -PCRE2_EXP_DECL int32_t pcre2_serialize_get_number_of_codes(const uint8_t *); \ -PCRE2_EXP_DECL void pcre2_serialize_free(uint8_t *); +PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ + pcre2_serialize_encode(const pcre2_code **, int32_t, uint8_t **, \ + PCRE2_SIZE *, pcre2_general_context *); \ +PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ + pcre2_serialize_decode(pcre2_code **, int32_t, const uint8_t *, \ + pcre2_general_context *); \ +PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ + pcre2_serialize_get_number_of_codes(const uint8_t *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_serialize_free(uint8_t *); /* Convenience function for match + substitute. */ #define PCRE2_SUBSTITUTE_FUNCTION \ -PCRE2_EXP_DECL int pcre2_substitute(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_UCHAR *, \ - PCRE2_SIZE *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substitute(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *, PCRE2_SPTR, \ + PCRE2_SIZE, PCRE2_UCHAR *, PCRE2_SIZE *); + + +/* Functions for converting pattern source strings. */ + +#define PCRE2_CONVERT_FUNCTIONS \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_pattern_convert(PCRE2_SPTR, PCRE2_SIZE, uint32_t, PCRE2_UCHAR **, \ + PCRE2_SIZE *, pcre2_convert_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_converted_pattern_free(PCRE2_UCHAR *); /* Functions for JIT processing */ #define PCRE2_JIT_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_jit_compile(pcre2_code *, uint32_t); \ -PCRE2_EXP_DECL int pcre2_jit_match(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *); \ -PCRE2_EXP_DECL void pcre2_jit_free_unused_memory(pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_jit_stack *pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL void pcre2_jit_stack_assign(pcre2_match_context *, \ - pcre2_jit_callback, void *); \ -PCRE2_EXP_DECL void pcre2_jit_stack_free(pcre2_jit_stack *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_jit_compile(pcre2_code *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_jit_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_jit_free_unused_memory(pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_jit_stack PCRE2_CALL_CONVENTION \ + *pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, pcre2_general_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_jit_stack_assign(pcre2_match_context *, pcre2_jit_callback, void *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_jit_stack_free(pcre2_jit_stack *); /* Other miscellaneous functions. */ #define PCRE2_OTHER_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \ -PCRE2_EXP_DECL \ - const uint8_t *pcre2_maketables(pcre2_general_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \ +PCRE2_EXP_DECL const uint8_t PCRE2_CALL_CONVENTION \ + *pcre2_maketables(pcre2_general_context *); \ /* Define macros that generate width-specific names from generic versions. The @@ -576,6 +797,7 @@ pcre2_compile are called by application code. */ #define pcre2_real_code PCRE2_SUFFIX(pcre2_real_code_) #define pcre2_real_general_context PCRE2_SUFFIX(pcre2_real_general_context_) #define pcre2_real_compile_context PCRE2_SUFFIX(pcre2_real_compile_context_) +#define pcre2_real_convert_context PCRE2_SUFFIX(pcre2_real_convert_context_) #define pcre2_real_match_context PCRE2_SUFFIX(pcre2_real_match_context_) #define pcre2_real_jit_stack PCRE2_SUFFIX(pcre2_real_jit_stack_) #define pcre2_real_match_data PCRE2_SUFFIX(pcre2_real_match_data_) @@ -587,6 +809,7 @@ pcre2_compile are called by application code. */ #define pcre2_callout_enumerate_block PCRE2_SUFFIX(pcre2_callout_enumerate_block_) #define pcre2_general_context PCRE2_SUFFIX(pcre2_general_context_) #define pcre2_compile_context PCRE2_SUFFIX(pcre2_compile_context_) +#define pcre2_convert_context PCRE2_SUFFIX(pcre2_convert_context_) #define pcre2_match_context PCRE2_SUFFIX(pcre2_match_context_) #define pcre2_match_data PCRE2_SUFFIX(pcre2_match_data_) @@ -595,12 +818,17 @@ pcre2_compile are called by application code. */ #define pcre2_callout_enumerate PCRE2_SUFFIX(pcre2_callout_enumerate_) #define pcre2_code_copy PCRE2_SUFFIX(pcre2_code_copy_) +#define pcre2_code_copy_with_tables PCRE2_SUFFIX(pcre2_code_copy_with_tables_) #define pcre2_code_free PCRE2_SUFFIX(pcre2_code_free_) #define pcre2_compile PCRE2_SUFFIX(pcre2_compile_) #define pcre2_compile_context_copy PCRE2_SUFFIX(pcre2_compile_context_copy_) #define pcre2_compile_context_create PCRE2_SUFFIX(pcre2_compile_context_create_) #define pcre2_compile_context_free PCRE2_SUFFIX(pcre2_compile_context_free_) #define pcre2_config PCRE2_SUFFIX(pcre2_config_) +#define pcre2_convert_context_copy PCRE2_SUFFIX(pcre2_convert_context_copy_) +#define pcre2_convert_context_create PCRE2_SUFFIX(pcre2_convert_context_create_) +#define pcre2_convert_context_free PCRE2_SUFFIX(pcre2_convert_context_free_) +#define pcre2_converted_pattern_free PCRE2_SUFFIX(pcre2_converted_pattern_free_) #define pcre2_dfa_match PCRE2_SUFFIX(pcre2_dfa_match_) #define pcre2_general_context_copy PCRE2_SUFFIX(pcre2_general_context_copy_) #define pcre2_general_context_create PCRE2_SUFFIX(pcre2_general_context_create_) @@ -624,6 +852,7 @@ pcre2_compile are called by application code. */ #define pcre2_match_data_create PCRE2_SUFFIX(pcre2_match_data_create_) #define pcre2_match_data_create_from_pattern PCRE2_SUFFIX(pcre2_match_data_create_from_pattern_) #define pcre2_match_data_free PCRE2_SUFFIX(pcre2_match_data_free_) +#define pcre2_pattern_convert PCRE2_SUFFIX(pcre2_pattern_convert_) #define pcre2_pattern_info PCRE2_SUFFIX(pcre2_pattern_info_) #define pcre2_serialize_decode PCRE2_SUFFIX(pcre2_serialize_decode_) #define pcre2_serialize_encode PCRE2_SUFFIX(pcre2_serialize_encode_) @@ -632,14 +861,17 @@ pcre2_compile are called by application code. */ #define pcre2_set_bsr PCRE2_SUFFIX(pcre2_set_bsr_) #define pcre2_set_callout PCRE2_SUFFIX(pcre2_set_callout_) #define pcre2_set_character_tables PCRE2_SUFFIX(pcre2_set_character_tables_) +#define pcre2_set_compile_extra_options PCRE2_SUFFIX(pcre2_set_compile_extra_options_) #define pcre2_set_compile_recursion_guard PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_) +#define pcre2_set_depth_limit PCRE2_SUFFIX(pcre2_set_depth_limit_) +#define pcre2_set_glob_escape PCRE2_SUFFIX(pcre2_set_glob_escape_) +#define pcre2_set_glob_separator PCRE2_SUFFIX(pcre2_set_glob_separator_) +#define pcre2_set_heap_limit PCRE2_SUFFIX(pcre2_set_heap_limit_) #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_) #define pcre2_substring_copy_byname PCRE2_SUFFIX(pcre2_substring_copy_byname_) #define pcre2_substring_copy_bynumber PCRE2_SUFFIX(pcre2_substring_copy_bynumber_) @@ -653,6 +885,11 @@ pcre2_compile are called by application code. */ #define pcre2_substring_nametable_scan PCRE2_SUFFIX(pcre2_substring_nametable_scan_) #define pcre2_substring_number_from_name PCRE2_SUFFIX(pcre2_substring_number_from_name_) +/* Keep this old function name for backwards compatibility */ +#define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_) + +/* Keep this obsolete function for backwards compatibility: it is now a noop. */ +#define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_) /* Now generate all three sets of width-specific structures and function prototypes. */ @@ -663,6 +900,8 @@ PCRE2_STRUCTURE_LIST \ PCRE2_GENERAL_INFO_FUNCTIONS \ PCRE2_GENERAL_CONTEXT_FUNCTIONS \ PCRE2_COMPILE_CONTEXT_FUNCTIONS \ +PCRE2_CONVERT_CONTEXT_FUNCTIONS \ +PCRE2_CONVERT_FUNCTIONS \ PCRE2_MATCH_CONTEXT_FUNCTIONS \ PCRE2_COMPILE_FUNCTIONS \ PCRE2_PATTERN_INFO_FUNCTIONS \ @@ -692,6 +931,7 @@ PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS #undef PCRE2_GENERAL_INFO_FUNCTIONS #undef PCRE2_GENERAL_CONTEXT_FUNCTIONS #undef PCRE2_COMPILE_CONTEXT_FUNCTIONS +#undef PCRE2_CONVERT_CONTEXT_FUNCTIONS #undef PCRE2_MATCH_CONTEXT_FUNCTIONS #undef PCRE2_COMPILE_FUNCTIONS #undef PCRE2_PATTERN_INFO_FUNCTIONS @@ -729,4 +969,6 @@ PCRE2_SUFFIX a no-op. Otherwise, generate an error. */ } /* extern "C" */ #endif -#endif /* End of pcre2.h */ +#endif /* PCRE2_H_IDEMPOTENT_GUARD */ + +/* End of pcre2.h */ diff --git a/pcre2-10.22/src/pcre2.h.in b/pcre2-10.32/src/pcre2.h.in similarity index 56% rename from pcre2-10.22/src/pcre2.h.in rename to pcre2-10.32/src/pcre2.h.in index e1d944dc8..a9396e028 100644 --- a/pcre2-10.22/src/pcre2.h.in +++ b/pcre2-10.32/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) 2016 University of Cambridge + Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -36,15 +36,21 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ -#ifndef _PCRE2_H -#define _PCRE2_H +#ifndef PCRE2_H_IDEMPOTENT_GUARD +#define PCRE2_H_IDEMPOTENT_GUARD /* The current PCRE version information. */ -#define PCRE2_MAJOR @PCRE2_MAJOR@ -#define PCRE2_MINOR @PCRE2_MINOR@ -#define PCRE2_PRERELEASE @PCRE2_PRERELEASE@ -#define PCRE2_DATE @PCRE2_DATE@ +#define PCRE2_MAJOR @PCRE2_MAJOR@ +#define PCRE2_MINOR @PCRE2_MINOR@ +#define PCRE2_PRERELEASE @PCRE2_PRERELEASE@ +#define PCRE2_DATE @PCRE2_DATE@ + +/* For the benefit of systems without stdint.h, an alternative is to use +inttypes.h. The existence of these headers is checked by configure or CMake. */ + +#define PCRE2_HAVE_STDINT_H @PCRE2_HAVE_STDINT_H@ +#define PCRE2_HAVE_INTTYPES_H @PCRE2_HAVE_INTTYPES_H@ /* 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 @@ -67,12 +73,32 @@ don't change existing definitions of PCRE2_EXP_DECL. */ # endif #endif -/* Have to include limits.h, stdlib.h and stdint.h to ensure that size_t and -uint8_t, UCHAR_MAX, etc are defined. */ +/* When compiling with the MSVC compiler, it is sometimes necessary to include +a "calling convention" before exported function names. (This is secondhand +information; I know nothing about MSVC myself). For example, something like + + void __cdecl function(....) + +might be needed. In order so make this easy, all the exported functions have +PCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not +set, we ensure here that it has no effect. */ + +#ifndef PCRE2_CALL_CONVENTION +#define PCRE2_CALL_CONVENTION +#endif + +/* Have to include limits.h, stdlib.h and stdint.h (or inttypes.h) to ensure +that size_t and uint8_t, UCHAR_MAX, etc are defined. If the system has neither +header, the relevant values must be provided by some other means. */ #include #include + +#if PCRE2_HAVE_STDINT_H #include +#elif PCRE2_HAVE_INTTYPES_H +#include +#endif /* Allow for C++ users compiling this directly. */ @@ -87,6 +113,7 @@ others can be added next to them */ #define PCRE2_ANCHORED 0x80000000u #define PCRE2_NO_UTF_CHECK 0x40000000u +#define PCRE2_ENDANCHORED 0x20000000u /* The following option bits can be passed only to pcre2_compile(). However, they may affect compilation, JIT compilation, and/or interpretive execution. @@ -122,6 +149,15 @@ D is inspected during pcre2_dfa_match() execution #define PCRE2_ALT_CIRCUMFLEX 0x00200000u /* J M D */ #define PCRE2_ALT_VERBNAMES 0x00400000u /* C */ #define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */ +#define PCRE2_EXTENDED_MORE 0x01000000u /* C */ +#define PCRE2_LITERAL 0x02000000u /* C */ + +/* An additional compile options word is available in the compile context. */ + +#define PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES 0x00000001u /* C */ +#define PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL 0x00000002u /* C */ +#define PCRE2_EXTRA_MATCH_WORD 0x00000004u /* C */ +#define PCRE2_EXTRA_MATCH_LINE 0x00000008u /* C */ /* These are for pcre2_jit_compile(). */ @@ -160,6 +196,16 @@ ignored for pcre2_jit_match(). */ #define PCRE2_NO_JIT 0x00002000u +/* Options for pcre2_pattern_convert(). */ + +#define PCRE2_CONVERT_UTF 0x00000001u +#define PCRE2_CONVERT_NO_UTF_CHECK 0x00000002u +#define PCRE2_CONVERT_POSIX_BASIC 0x00000004u +#define PCRE2_CONVERT_POSIX_EXTENDED 0x00000008u +#define PCRE2_CONVERT_GLOB 0x00000010u +#define PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR 0x00000030u +#define PCRE2_CONVERT_GLOB_NO_STARSTAR 0x00000050u + /* 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 greater than zero. */ @@ -169,11 +215,112 @@ greater than zero. */ #define PCRE2_NEWLINE_CRLF 3 #define PCRE2_NEWLINE_ANY 4 #define PCRE2_NEWLINE_ANYCRLF 5 +#define PCRE2_NEWLINE_NUL 6 #define PCRE2_BSR_UNICODE 1 #define PCRE2_BSR_ANYCRLF 2 -/* Error codes: no match and partial match are "expected" errors. */ +/* Error codes for pcre2_compile(). Some of these are also used by +pcre2_pattern_convert(). */ + +#define PCRE2_ERROR_END_BACKSLASH 101 +#define PCRE2_ERROR_END_BACKSLASH_C 102 +#define PCRE2_ERROR_UNKNOWN_ESCAPE 103 +#define PCRE2_ERROR_QUANTIFIER_OUT_OF_ORDER 104 +#define PCRE2_ERROR_QUANTIFIER_TOO_BIG 105 +#define PCRE2_ERROR_MISSING_SQUARE_BRACKET 106 +#define PCRE2_ERROR_ESCAPE_INVALID_IN_CLASS 107 +#define PCRE2_ERROR_CLASS_RANGE_ORDER 108 +#define PCRE2_ERROR_QUANTIFIER_INVALID 109 +#define PCRE2_ERROR_INTERNAL_UNEXPECTED_REPEAT 110 +#define PCRE2_ERROR_INVALID_AFTER_PARENS_QUERY 111 +#define PCRE2_ERROR_POSIX_CLASS_NOT_IN_CLASS 112 +#define PCRE2_ERROR_POSIX_NO_SUPPORT_COLLATING 113 +#define PCRE2_ERROR_MISSING_CLOSING_PARENTHESIS 114 +#define PCRE2_ERROR_BAD_SUBPATTERN_REFERENCE 115 +#define PCRE2_ERROR_NULL_PATTERN 116 +#define PCRE2_ERROR_BAD_OPTIONS 117 +#define PCRE2_ERROR_MISSING_COMMENT_CLOSING 118 +#define PCRE2_ERROR_PARENTHESES_NEST_TOO_DEEP 119 +#define PCRE2_ERROR_PATTERN_TOO_LARGE 120 +#define PCRE2_ERROR_HEAP_FAILED 121 +#define PCRE2_ERROR_UNMATCHED_CLOSING_PARENTHESIS 122 +#define PCRE2_ERROR_INTERNAL_CODE_OVERFLOW 123 +#define PCRE2_ERROR_MISSING_CONDITION_CLOSING 124 +#define PCRE2_ERROR_LOOKBEHIND_NOT_FIXED_LENGTH 125 +#define PCRE2_ERROR_ZERO_RELATIVE_REFERENCE 126 +#define PCRE2_ERROR_TOO_MANY_CONDITION_BRANCHES 127 +#define PCRE2_ERROR_CONDITION_ASSERTION_EXPECTED 128 +#define PCRE2_ERROR_BAD_RELATIVE_REFERENCE 129 +#define PCRE2_ERROR_UNKNOWN_POSIX_CLASS 130 +#define PCRE2_ERROR_INTERNAL_STUDY_ERROR 131 +#define PCRE2_ERROR_UNICODE_NOT_SUPPORTED 132 +#define PCRE2_ERROR_PARENTHESES_STACK_CHECK 133 +#define PCRE2_ERROR_CODE_POINT_TOO_BIG 134 +#define PCRE2_ERROR_LOOKBEHIND_TOO_COMPLICATED 135 +#define PCRE2_ERROR_LOOKBEHIND_INVALID_BACKSLASH_C 136 +#define PCRE2_ERROR_UNSUPPORTED_ESCAPE_SEQUENCE 137 +#define PCRE2_ERROR_CALLOUT_NUMBER_TOO_BIG 138 +#define PCRE2_ERROR_MISSING_CALLOUT_CLOSING 139 +#define PCRE2_ERROR_ESCAPE_INVALID_IN_VERB 140 +#define PCRE2_ERROR_UNRECOGNIZED_AFTER_QUERY_P 141 +#define PCRE2_ERROR_MISSING_NAME_TERMINATOR 142 +#define PCRE2_ERROR_DUPLICATE_SUBPATTERN_NAME 143 +#define PCRE2_ERROR_INVALID_SUBPATTERN_NAME 144 +#define PCRE2_ERROR_UNICODE_PROPERTIES_UNAVAILABLE 145 +#define PCRE2_ERROR_MALFORMED_UNICODE_PROPERTY 146 +#define PCRE2_ERROR_UNKNOWN_UNICODE_PROPERTY 147 +#define PCRE2_ERROR_SUBPATTERN_NAME_TOO_LONG 148 +#define PCRE2_ERROR_TOO_MANY_NAMED_SUBPATTERNS 149 +#define PCRE2_ERROR_CLASS_INVALID_RANGE 150 +#define PCRE2_ERROR_OCTAL_BYTE_TOO_BIG 151 +#define PCRE2_ERROR_INTERNAL_OVERRAN_WORKSPACE 152 +#define PCRE2_ERROR_INTERNAL_MISSING_SUBPATTERN 153 +#define PCRE2_ERROR_DEFINE_TOO_MANY_BRANCHES 154 +#define PCRE2_ERROR_BACKSLASH_O_MISSING_BRACE 155 +#define PCRE2_ERROR_INTERNAL_UNKNOWN_NEWLINE 156 +#define PCRE2_ERROR_BACKSLASH_G_SYNTAX 157 +#define PCRE2_ERROR_PARENS_QUERY_R_MISSING_CLOSING 158 +/* Error 159 is obsolete and should now never occur */ +#define PCRE2_ERROR_VERB_ARGUMENT_NOT_ALLOWED 159 +#define PCRE2_ERROR_VERB_UNKNOWN 160 +#define PCRE2_ERROR_SUBPATTERN_NUMBER_TOO_BIG 161 +#define PCRE2_ERROR_SUBPATTERN_NAME_EXPECTED 162 +#define PCRE2_ERROR_INTERNAL_PARSED_OVERFLOW 163 +#define PCRE2_ERROR_INVALID_OCTAL 164 +#define PCRE2_ERROR_SUBPATTERN_NAMES_MISMATCH 165 +#define PCRE2_ERROR_MARK_MISSING_ARGUMENT 166 +#define PCRE2_ERROR_INVALID_HEXADECIMAL 167 +#define PCRE2_ERROR_BACKSLASH_C_SYNTAX 168 +#define PCRE2_ERROR_BACKSLASH_K_SYNTAX 169 +#define PCRE2_ERROR_INTERNAL_BAD_CODE_LOOKBEHINDS 170 +#define PCRE2_ERROR_BACKSLASH_N_IN_CLASS 171 +#define PCRE2_ERROR_CALLOUT_STRING_TOO_LONG 172 +#define PCRE2_ERROR_UNICODE_DISALLOWED_CODE_POINT 173 +#define PCRE2_ERROR_UTF_IS_DISABLED 174 +#define PCRE2_ERROR_UCP_IS_DISABLED 175 +#define PCRE2_ERROR_VERB_NAME_TOO_LONG 176 +#define PCRE2_ERROR_BACKSLASH_U_CODE_POINT_TOO_BIG 177 +#define PCRE2_ERROR_MISSING_OCTAL_OR_HEX_DIGITS 178 +#define PCRE2_ERROR_VERSION_CONDITION_SYNTAX 179 +#define PCRE2_ERROR_INTERNAL_BAD_CODE_AUTO_POSSESS 180 +#define PCRE2_ERROR_CALLOUT_NO_STRING_DELIMITER 181 +#define PCRE2_ERROR_CALLOUT_BAD_STRING_DELIMITER 182 +#define PCRE2_ERROR_BACKSLASH_C_CALLER_DISABLED 183 +#define PCRE2_ERROR_QUERY_BARJX_NEST_TOO_DEEP 184 +#define PCRE2_ERROR_BACKSLASH_C_LIBRARY_DISABLED 185 +#define PCRE2_ERROR_PATTERN_TOO_COMPLICATED 186 +#define PCRE2_ERROR_LOOKBEHIND_TOO_LONG 187 +#define PCRE2_ERROR_PATTERN_STRING_TOO_LONG 188 +#define PCRE2_ERROR_INTERNAL_BAD_CODE 189 +#define PCRE2_ERROR_INTERNAL_BAD_CODE_IN_SKIP 190 +#define PCRE2_ERROR_NO_SURROGATES_IN_UTF16 191 +#define PCRE2_ERROR_BAD_LITERAL_OPTIONS 192 +#define PCRE2_ERROR_SUPPORTED_ONLY_IN_UNICODE 193 +#define PCRE2_ERROR_INVALID_HYPHEN_IN_OPTIONS 194 + + +/* "Expected" matching error codes: no match and partial match. */ #define PCRE2_ERROR_NOMATCH (-1) #define PCRE2_ERROR_PARTIAL (-2) @@ -213,10 +360,10 @@ greater than zero. */ #define PCRE2_ERROR_UTF32_ERR1 (-27) #define PCRE2_ERROR_UTF32_ERR2 (-28) -/* Error codes for pcre2[_dfa]_match(), substring extraction functions, context -functions, and serializing functions. They are in numerical order. Originally -they were in alphabetical order too, but now that PCRE2 is released, the -numbers must not be changed. */ +/* Miscellaneous error codes for pcre2[_dfa]_match(), substring extraction +functions, context functions, and serializing functions. They are in numerical +order. Originally they were in alphabetical order too, but now that PCRE2 is +released, the numbers must not be changed. */ #define PCRE2_ERROR_BADDATA (-29) #define PCRE2_ERROR_MIXEDTABLES (-30) /* Name was changed */ @@ -242,7 +389,8 @@ numbers must not be changed. */ #define PCRE2_ERROR_NOUNIQUESUBSTRING (-50) #define PCRE2_ERROR_NULL (-51) #define PCRE2_ERROR_RECURSELOOP (-52) -#define PCRE2_ERROR_RECURSIONLIMIT (-53) +#define PCRE2_ERROR_DEPTHLIMIT (-53) +#define PCRE2_ERROR_RECURSIONLIMIT (-53) /* Obsolete synonym */ #define PCRE2_ERROR_UNAVAILABLE (-54) #define PCRE2_ERROR_UNSET (-55) #define PCRE2_ERROR_BADOFFSETLIMIT (-56) @@ -252,6 +400,10 @@ numbers must not be changed. */ #define PCRE2_ERROR_BADSUBSPATTERN (-60) #define PCRE2_ERROR_TOOMANYREPLACE (-61) #define PCRE2_ERROR_BADSERIALIZEDDATA (-62) +#define PCRE2_ERROR_HEAPLIMIT (-63) +#define PCRE2_ERROR_CONVERT_SYNTAX (-64) +#define PCRE2_ERROR_INTERNAL_DUPMATCH (-65) + /* Request types for pcre2_pattern_info() */ @@ -276,9 +428,13 @@ numbers must not be changed. */ #define PCRE2_INFO_NAMEENTRYSIZE 18 #define PCRE2_INFO_NAMETABLE 19 #define PCRE2_INFO_NEWLINE 20 -#define PCRE2_INFO_RECURSIONLIMIT 21 +#define PCRE2_INFO_DEPTHLIMIT 21 +#define PCRE2_INFO_RECURSIONLIMIT 21 /* Obsolete synonym */ #define PCRE2_INFO_SIZE 22 #define PCRE2_INFO_HASBACKSLASHC 23 +#define PCRE2_INFO_FRAMESIZE 24 +#define PCRE2_INFO_HEAPLIMIT 25 +#define PCRE2_INFO_EXTRAOPTIONS 26 /* Request types for pcre2_config(). */ @@ -289,11 +445,16 @@ numbers must not be changed. */ #define PCRE2_CONFIG_MATCHLIMIT 4 #define PCRE2_CONFIG_NEWLINE 5 #define PCRE2_CONFIG_PARENSLIMIT 6 -#define PCRE2_CONFIG_RECURSIONLIMIT 7 -#define PCRE2_CONFIG_STACKRECURSE 8 +#define PCRE2_CONFIG_DEPTHLIMIT 7 +#define PCRE2_CONFIG_RECURSIONLIMIT 7 /* Obsolete synonym */ +#define PCRE2_CONFIG_STACKRECURSE 8 /* Obsolete */ #define PCRE2_CONFIG_UNICODE 9 #define PCRE2_CONFIG_UNICODE_VERSION 10 #define PCRE2_CONFIG_VERSION 11 +#define PCRE2_CONFIG_HEAPLIMIT 12 +#define PCRE2_CONFIG_NEVER_BACKSLASH_C 13 +#define PCRE2_CONFIG_COMPILED_WIDTHS 14 + /* Types for code units in patterns and subject strings. */ @@ -328,6 +489,9 @@ typedef struct pcre2_real_compile_context pcre2_compile_context; \ struct pcre2_real_match_context; \ typedef struct pcre2_real_match_context pcre2_match_context; \ \ +struct pcre2_real_convert_context; \ +typedef struct pcre2_real_convert_context pcre2_convert_context; \ +\ struct pcre2_real_code; \ typedef struct pcre2_real_code pcre2_code; \ \ @@ -346,6 +510,11 @@ without changing the API of the function, thereby allowing old clients to work without modification. Define the generic version in a macro; the width-specific versions are generated from this macro below. */ +/* Flags for the callout_flags field. These are cleared after a callout. */ + +#define PCRE2_CALLOUT_STARTMATCH 0x00000001u /* Set for each bumpalong */ +#define PCRE2_CALLOUT_BACKTRACK 0x00000002u /* Set after a backtrack */ + #define PCRE2_STRUCTURE_LIST \ typedef struct pcre2_callout_block { \ uint32_t version; /* Identifies version of block */ \ @@ -365,6 +534,8 @@ typedef struct pcre2_callout_block { \ PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \ PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \ PCRE2_SPTR callout_string; /* String compiled into pattern */ \ + /* ------------------- Added for Version 2 -------------------------- */ \ + uint32_t callout_flags; /* See above for list */ \ /* ------------------------------------------------------------------ */ \ } pcre2_callout_block; \ \ @@ -386,170 +557,220 @@ expanded for each width below. Start with functions that give general information. */ #define PCRE2_GENERAL_INFO_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_config(uint32_t, void *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION pcre2_config(uint32_t, void *); /* Functions for manipulating contexts. */ #define PCRE2_GENERAL_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_general_context *pcre2_general_context_copy(pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_general_context *pcre2_general_context_create( \ - void *(*)(PCRE2_SIZE, void *), \ - void (*)(void *, void *), void *); \ -PCRE2_EXP_DECL void pcre2_general_context_free(pcre2_general_context *); +PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ + *pcre2_general_context_copy(pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_general_context PCRE2_CALL_CONVENTION \ + *pcre2_general_context_create(void *(*)(PCRE2_SIZE, void *), \ + void (*)(void *, void *), void *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_general_context_free(pcre2_general_context *); #define PCRE2_COMPILE_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_compile_context *pcre2_compile_context_copy(pcre2_compile_context *); \ -PCRE2_EXP_DECL \ - pcre2_compile_context *pcre2_compile_context_create(pcre2_general_context *);\ -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); \ -PCRE2_EXP_DECL int pcre2_set_compile_recursion_guard(\ - pcre2_compile_context *, int (*)(uint32_t, void *), \ - void *); +PCRE2_EXP_DECL pcre2_compile_context PCRE2_CALL_CONVENTION \ + *pcre2_compile_context_copy(pcre2_compile_context *); \ +PCRE2_EXP_DECL pcre2_compile_context PCRE2_CALL_CONVENTION \ + *pcre2_compile_context_create(pcre2_general_context *);\ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_compile_context_free(pcre2_compile_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_bsr(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_character_tables(pcre2_compile_context *, const unsigned char *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_compile_extra_options(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_max_pattern_length(pcre2_compile_context *, PCRE2_SIZE); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_newline(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_parens_nest_limit(pcre2_compile_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_compile_recursion_guard(pcre2_compile_context *, \ + int (*)(uint32_t, void *), void *); #define PCRE2_MATCH_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_match_context *pcre2_match_context_copy(pcre2_match_context *); \ -PCRE2_EXP_DECL \ - pcre2_match_context *pcre2_match_context_create(pcre2_general_context *); \ -PCRE2_EXP_DECL void pcre2_match_context_free(pcre2_match_context *); \ -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( \ - pcre2_match_context *, void *(*)(PCRE2_SIZE, void *), \ - void (*)(void *, void *), void *); +PCRE2_EXP_DECL pcre2_match_context PCRE2_CALL_CONVENTION \ + *pcre2_match_context_copy(pcre2_match_context *); \ +PCRE2_EXP_DECL pcre2_match_context PCRE2_CALL_CONVENTION \ + *pcre2_match_context_create(pcre2_general_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_match_context_free(pcre2_match_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_callout(pcre2_match_context *, \ + int (*)(pcre2_callout_block *, void *), void *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_depth_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_heap_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_match_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_offset_limit(pcre2_match_context *, PCRE2_SIZE); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_recursion_limit(pcre2_match_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_recursion_memory_management(pcre2_match_context *, \ + void *(*)(PCRE2_SIZE, void *), void (*)(void *, void *), void *); + +#define PCRE2_CONVERT_CONTEXT_FUNCTIONS \ +PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \ + *pcre2_convert_context_copy(pcre2_convert_context *); \ +PCRE2_EXP_DECL pcre2_convert_context PCRE2_CALL_CONVENTION \ + *pcre2_convert_context_create(pcre2_general_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_convert_context_free(pcre2_convert_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_glob_escape(pcre2_convert_context *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_set_glob_separator(pcre2_convert_context *, uint32_t); /* Functions concerned with compiling a pattern to PCRE internal code. */ #define PCRE2_COMPILE_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_code *pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, \ - int *, PCRE2_SIZE *, pcre2_compile_context *); \ -PCRE2_EXP_DECL void pcre2_code_free(pcre2_code *); \ -PCRE2_EXP_DECL \ - pcre2_code *pcre2_code_copy(const pcre2_code *); +PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ + *pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, \ + pcre2_compile_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_code_free(pcre2_code *); \ +PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ + *pcre2_code_copy(const pcre2_code *); \ +PCRE2_EXP_DECL pcre2_code PCRE2_CALL_CONVENTION \ + *pcre2_code_copy_with_tables(const pcre2_code *); /* Functions that give information about a compiled pattern. */ #define PCRE2_PATTERN_INFO_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_pattern_info(const pcre2_code *, uint32_t, \ - void *); \ -PCRE2_EXP_DECL int pcre2_callout_enumerate(const pcre2_code *, \ - int (*)(pcre2_callout_enumerate_block *, void *), \ - void *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_pattern_info(const pcre2_code *, uint32_t, void *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_callout_enumerate(const pcre2_code *, \ + int (*)(pcre2_callout_enumerate_block *, void *), void *); /* Functions for running a match and inspecting the result. */ #define PCRE2_MATCH_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_match_data *pcre2_match_data_create(uint32_t, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_match_data *pcre2_match_data_create_from_pattern(\ - const pcre2_code *, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL int pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, \ - PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *, int *, \ - PCRE2_SIZE); \ -PCRE2_EXP_DECL int pcre2_match(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *); \ -PCRE2_EXP_DECL void pcre2_match_data_free(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SPTR pcre2_get_mark(pcre2_match_data *); \ -PCRE2_EXP_DECL uint32_t pcre2_get_ovector_count(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SIZE *pcre2_get_ovector_pointer(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SIZE pcre2_get_startchar(pcre2_match_data *); +PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ + *pcre2_match_data_create(uint32_t, pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_match_data PCRE2_CALL_CONVENTION \ + *pcre2_match_data_create_from_pattern(const pcre2_code *, \ + pcre2_general_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *, int *, PCRE2_SIZE); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_match_data_free(pcre2_match_data *); \ +PCRE2_EXP_DECL PCRE2_SPTR PCRE2_CALL_CONVENTION \ + pcre2_get_mark(pcre2_match_data *); \ +PCRE2_EXP_DECL uint32_t PCRE2_CALL_CONVENTION \ + pcre2_get_ovector_count(pcre2_match_data *); \ +PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ + *pcre2_get_ovector_pointer(pcre2_match_data *); \ +PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \ + pcre2_get_startchar(pcre2_match_data *); /* Convenience functions for handling matched substrings. */ #define PCRE2_SUBSTRING_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_substring_copy_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_UCHAR *, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_copy_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_UCHAR *, PCRE2_SIZE *); \ -PCRE2_EXP_DECL void pcre2_substring_free(PCRE2_UCHAR *); \ -PCRE2_EXP_DECL int pcre2_substring_get_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_UCHAR **, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_get_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_UCHAR **, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_length_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_length_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_nametable_scan(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SPTR *, PCRE2_SPTR *); \ -PCRE2_EXP_DECL int pcre2_substring_number_from_name(\ - const pcre2_code *, PCRE2_SPTR); \ -PCRE2_EXP_DECL void pcre2_substring_list_free(PCRE2_SPTR *); \ -PCRE2_EXP_DECL int pcre2_substring_list_get(pcre2_match_data *, \ - PCRE2_UCHAR ***, PCRE2_SIZE **); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_copy_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR *, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_copy_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR *, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_substring_free(PCRE2_UCHAR *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_get_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR **, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_get_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR **, \ + PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_length_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_length_bynumber(pcre2_match_data *, uint32_t, PCRE2_SIZE *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_nametable_scan(const pcre2_code *, PCRE2_SPTR, PCRE2_SPTR *, \ + PCRE2_SPTR *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_number_from_name(const pcre2_code *, PCRE2_SPTR); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_substring_list_free(PCRE2_SPTR *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substring_list_get(pcre2_match_data *, PCRE2_UCHAR ***, PCRE2_SIZE **); /* Functions for serializing / deserializing compiled patterns. */ #define PCRE2_SERIALIZE_FUNCTIONS \ -PCRE2_EXP_DECL int32_t pcre2_serialize_encode(const pcre2_code **, \ - int32_t, uint8_t **, PCRE2_SIZE *, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL int32_t pcre2_serialize_decode(pcre2_code **, int32_t, \ - const uint8_t *, pcre2_general_context *); \ -PCRE2_EXP_DECL int32_t pcre2_serialize_get_number_of_codes(const uint8_t *); \ -PCRE2_EXP_DECL void pcre2_serialize_free(uint8_t *); +PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ + pcre2_serialize_encode(const pcre2_code **, int32_t, uint8_t **, \ + PCRE2_SIZE *, pcre2_general_context *); \ +PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ + pcre2_serialize_decode(pcre2_code **, int32_t, const uint8_t *, \ + pcre2_general_context *); \ +PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \ + pcre2_serialize_get_number_of_codes(const uint8_t *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_serialize_free(uint8_t *); /* Convenience function for match + substitute. */ #define PCRE2_SUBSTITUTE_FUNCTION \ -PCRE2_EXP_DECL int pcre2_substitute(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_UCHAR *, \ - PCRE2_SIZE *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_substitute(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *, PCRE2_SPTR, \ + PCRE2_SIZE, PCRE2_UCHAR *, PCRE2_SIZE *); + + +/* Functions for converting pattern source strings. */ + +#define PCRE2_CONVERT_FUNCTIONS \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_pattern_convert(PCRE2_SPTR, PCRE2_SIZE, uint32_t, PCRE2_UCHAR **, \ + PCRE2_SIZE *, pcre2_convert_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_converted_pattern_free(PCRE2_UCHAR *); /* Functions for JIT processing */ #define PCRE2_JIT_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_jit_compile(pcre2_code *, uint32_t); \ -PCRE2_EXP_DECL int pcre2_jit_match(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *); \ -PCRE2_EXP_DECL void pcre2_jit_free_unused_memory(pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_jit_stack *pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL void pcre2_jit_stack_assign(pcre2_match_context *, \ - pcre2_jit_callback, void *); \ -PCRE2_EXP_DECL void pcre2_jit_stack_free(pcre2_jit_stack *); +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_jit_compile(pcre2_code *, uint32_t); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_jit_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \ + uint32_t, pcre2_match_data *, pcre2_match_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_jit_free_unused_memory(pcre2_general_context *); \ +PCRE2_EXP_DECL pcre2_jit_stack PCRE2_CALL_CONVENTION \ + *pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, pcre2_general_context *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_jit_stack_assign(pcre2_match_context *, pcre2_jit_callback, void *); \ +PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \ + pcre2_jit_stack_free(pcre2_jit_stack *); /* Other miscellaneous functions. */ #define PCRE2_OTHER_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \ -PCRE2_EXP_DECL \ - const uint8_t *pcre2_maketables(pcre2_general_context *); \ +PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \ + pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \ +PCRE2_EXP_DECL const uint8_t PCRE2_CALL_CONVENTION \ + *pcre2_maketables(pcre2_general_context *); \ /* Define macros that generate width-specific names from generic versions. The @@ -576,6 +797,7 @@ pcre2_compile are called by application code. */ #define pcre2_real_code PCRE2_SUFFIX(pcre2_real_code_) #define pcre2_real_general_context PCRE2_SUFFIX(pcre2_real_general_context_) #define pcre2_real_compile_context PCRE2_SUFFIX(pcre2_real_compile_context_) +#define pcre2_real_convert_context PCRE2_SUFFIX(pcre2_real_convert_context_) #define pcre2_real_match_context PCRE2_SUFFIX(pcre2_real_match_context_) #define pcre2_real_jit_stack PCRE2_SUFFIX(pcre2_real_jit_stack_) #define pcre2_real_match_data PCRE2_SUFFIX(pcre2_real_match_data_) @@ -587,6 +809,7 @@ pcre2_compile are called by application code. */ #define pcre2_callout_enumerate_block PCRE2_SUFFIX(pcre2_callout_enumerate_block_) #define pcre2_general_context PCRE2_SUFFIX(pcre2_general_context_) #define pcre2_compile_context PCRE2_SUFFIX(pcre2_compile_context_) +#define pcre2_convert_context PCRE2_SUFFIX(pcre2_convert_context_) #define pcre2_match_context PCRE2_SUFFIX(pcre2_match_context_) #define pcre2_match_data PCRE2_SUFFIX(pcre2_match_data_) @@ -595,12 +818,17 @@ pcre2_compile are called by application code. */ #define pcre2_callout_enumerate PCRE2_SUFFIX(pcre2_callout_enumerate_) #define pcre2_code_copy PCRE2_SUFFIX(pcre2_code_copy_) +#define pcre2_code_copy_with_tables PCRE2_SUFFIX(pcre2_code_copy_with_tables_) #define pcre2_code_free PCRE2_SUFFIX(pcre2_code_free_) #define pcre2_compile PCRE2_SUFFIX(pcre2_compile_) #define pcre2_compile_context_copy PCRE2_SUFFIX(pcre2_compile_context_copy_) #define pcre2_compile_context_create PCRE2_SUFFIX(pcre2_compile_context_create_) #define pcre2_compile_context_free PCRE2_SUFFIX(pcre2_compile_context_free_) #define pcre2_config PCRE2_SUFFIX(pcre2_config_) +#define pcre2_convert_context_copy PCRE2_SUFFIX(pcre2_convert_context_copy_) +#define pcre2_convert_context_create PCRE2_SUFFIX(pcre2_convert_context_create_) +#define pcre2_convert_context_free PCRE2_SUFFIX(pcre2_convert_context_free_) +#define pcre2_converted_pattern_free PCRE2_SUFFIX(pcre2_converted_pattern_free_) #define pcre2_dfa_match PCRE2_SUFFIX(pcre2_dfa_match_) #define pcre2_general_context_copy PCRE2_SUFFIX(pcre2_general_context_copy_) #define pcre2_general_context_create PCRE2_SUFFIX(pcre2_general_context_create_) @@ -624,6 +852,7 @@ pcre2_compile are called by application code. */ #define pcre2_match_data_create PCRE2_SUFFIX(pcre2_match_data_create_) #define pcre2_match_data_create_from_pattern PCRE2_SUFFIX(pcre2_match_data_create_from_pattern_) #define pcre2_match_data_free PCRE2_SUFFIX(pcre2_match_data_free_) +#define pcre2_pattern_convert PCRE2_SUFFIX(pcre2_pattern_convert_) #define pcre2_pattern_info PCRE2_SUFFIX(pcre2_pattern_info_) #define pcre2_serialize_decode PCRE2_SUFFIX(pcre2_serialize_decode_) #define pcre2_serialize_encode PCRE2_SUFFIX(pcre2_serialize_encode_) @@ -632,14 +861,17 @@ pcre2_compile are called by application code. */ #define pcre2_set_bsr PCRE2_SUFFIX(pcre2_set_bsr_) #define pcre2_set_callout PCRE2_SUFFIX(pcre2_set_callout_) #define pcre2_set_character_tables PCRE2_SUFFIX(pcre2_set_character_tables_) +#define pcre2_set_compile_extra_options PCRE2_SUFFIX(pcre2_set_compile_extra_options_) #define pcre2_set_compile_recursion_guard PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_) +#define pcre2_set_depth_limit PCRE2_SUFFIX(pcre2_set_depth_limit_) +#define pcre2_set_glob_escape PCRE2_SUFFIX(pcre2_set_glob_escape_) +#define pcre2_set_glob_separator PCRE2_SUFFIX(pcre2_set_glob_separator_) +#define pcre2_set_heap_limit PCRE2_SUFFIX(pcre2_set_heap_limit_) #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_) #define pcre2_substring_copy_byname PCRE2_SUFFIX(pcre2_substring_copy_byname_) #define pcre2_substring_copy_bynumber PCRE2_SUFFIX(pcre2_substring_copy_bynumber_) @@ -653,6 +885,11 @@ pcre2_compile are called by application code. */ #define pcre2_substring_nametable_scan PCRE2_SUFFIX(pcre2_substring_nametable_scan_) #define pcre2_substring_number_from_name PCRE2_SUFFIX(pcre2_substring_number_from_name_) +/* Keep this old function name for backwards compatibility */ +#define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_) + +/* Keep this obsolete function for backwards compatibility: it is now a noop. */ +#define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_) /* Now generate all three sets of width-specific structures and function prototypes. */ @@ -663,6 +900,8 @@ PCRE2_STRUCTURE_LIST \ PCRE2_GENERAL_INFO_FUNCTIONS \ PCRE2_GENERAL_CONTEXT_FUNCTIONS \ PCRE2_COMPILE_CONTEXT_FUNCTIONS \ +PCRE2_CONVERT_CONTEXT_FUNCTIONS \ +PCRE2_CONVERT_FUNCTIONS \ PCRE2_MATCH_CONTEXT_FUNCTIONS \ PCRE2_COMPILE_FUNCTIONS \ PCRE2_PATTERN_INFO_FUNCTIONS \ @@ -692,6 +931,7 @@ PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS #undef PCRE2_GENERAL_INFO_FUNCTIONS #undef PCRE2_GENERAL_CONTEXT_FUNCTIONS #undef PCRE2_COMPILE_CONTEXT_FUNCTIONS +#undef PCRE2_CONVERT_CONTEXT_FUNCTIONS #undef PCRE2_MATCH_CONTEXT_FUNCTIONS #undef PCRE2_COMPILE_FUNCTIONS #undef PCRE2_PATTERN_INFO_FUNCTIONS @@ -729,4 +969,6 @@ PCRE2_SUFFIX a no-op. Otherwise, generate an error. */ } /* extern "C" */ #endif -#endif /* End of pcre2.h */ +#endif /* PCRE2_H_IDEMPOTENT_GUARD */ + +/* End of pcre2.h */ diff --git a/pcre2-10.22/src/pcre2_auto_possess.c b/pcre2-10.32/src/pcre2_auto_possess.c similarity index 93% rename from pcre2-10.22/src/pcre2_auto_possess.c rename to pcre2-10.32/src/pcre2_auto_possess.c index 8d0fa896e..2ce152e95 100644 --- a/pcre2-10.22/src/pcre2_auto_possess.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -505,7 +505,7 @@ which case the base cannot be possessified. utf TRUE in UTF mode cb compile data block base_list the data list of the base opcode - base_end the end of the data list + base_end the end of the base opcode rec_limit points to recursion depth counter Returns: TRUE if the auto-possessification is possible @@ -558,54 +558,82 @@ for(;;) continue; } + /* At the end of a branch, skip to the end of the group. */ + if (c == OP_ALT) { do code += GET(code, 1); while (*code == OP_ALT); c = *code; } + /* Inspect the next opcode. */ + switch(c) { - case OP_END: - case OP_KETRPOS: - /* TRUE only in greedy case. The non-greedy case could be replaced by - an OP_EXACT, but it is probably not worth it. (And note that OP_EXACT - uses more memory, which we cannot get at this stage.) */ + /* We can always possessify a greedy iterator at the end of the pattern, + which is reached after skipping over the final OP_KET. A non-greedy + iterator must never be possessified. */ + case OP_END: return base_list[1] != 0; + /* When an iterator is at the end of certain kinds of group we can inspect + what follows the group by skipping over the closing ket. Note that this + does not apply to OP_KETRMAX or OP_KETRMIN because what follows any given + iteration is variable (could be another iteration or could be the next + item). As these two opcodes are not listed in the next switch, they will + end up as the next code to inspect, and return FALSE by virtue of being + unsupported. */ + case OP_KET: - /* If the bracket is capturing, and referenced by an OP_RECURSE, or - it is an atomic sub-pattern (assert, once, etc.) the non-greedy case - cannot be converted to a possessive form. */ + case OP_KETRPOS: + /* The non-greedy case cannot be converted to a possessive form. */ if (base_list[1] == 0) return FALSE; + /* If the bracket is capturing it might be referenced by an OP_RECURSE + so its last iterator can never be possessified if the pattern contains + recursions. (This could be improved by keeping a list of group numbers that + are called by recursion.) */ + switch(*(code - GET(code, 1))) { + case OP_CBRA: + case OP_SCBRA: + case OP_CBRAPOS: + case OP_SCBRAPOS: + if (cb->had_recurse) return FALSE; + break; + + /* Atomic sub-patterns and assertions can always auto-possessify their + last iterator. However, if the group was entered as a result of checking + a previous iterator, this is not possible. */ + case OP_ASSERT: case OP_ASSERT_NOT: case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: - /* Atomic sub-patterns and assertions can always auto-possessify their - last iterator. However, if the group was entered as a result of checking - a previous iterator, this is not possible. */ return !entered_a_group; } + /* Skip over the bracket and inspect what comes next. */ + code += PRIV(OP_lengths)[c]; continue; + /* Handle cases where the next item is a group. */ + case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_CBRA: next_code = code + GET(code, 1); code += PRIV(OP_lengths)[c]; + /* Check each branch. We have to recurse a level for all but the last + branch. */ + while (*next_code == OP_ALT) { if (!compare_opcodes(code, utf, cb, base_list, base_end, rec_limit)) @@ -621,8 +649,8 @@ for(;;) case OP_BRAMINZERO: next_code = code + 1; - if (*next_code != OP_BRA && *next_code != OP_CBRA - && *next_code != OP_ONCE && *next_code != OP_ONCE_NC) return FALSE; + if (*next_code != OP_BRA && *next_code != OP_CBRA && + *next_code != OP_ONCE) return FALSE; do next_code += GET(next_code, 1); while (*next_code == OP_ALT); @@ -635,11 +663,15 @@ for(;;) code += PRIV(OP_lengths)[c]; continue; + /* The next opcode does not need special handling; fall through and use it + to see if the base can be possessified. */ + default: break; } - /* Check for a supported opcode, and load its properties. */ + /* We now have the next appropriate opcode to compare with the base. Check + for a supported opcode, and load its properties. */ code = get_chr_property_list(code, utf, cb->fcc, list); if (code == NULL) return FALSE; /* Unsupported */ @@ -698,7 +730,7 @@ for(;;) if ((*xclass_flags & XCL_MAP) == 0) { /* No bits are set for characters < 256. */ - if (list[1] == 0) return TRUE; + if (list[1] == 0) return (*xclass_flags & XCL_NOT) == 0; /* Might be an empty repeat. */ continue; } @@ -1046,8 +1078,10 @@ but some compilers complain about an unreachable statement. */ /* Replaces single character iterations with their possessive alternatives if appropriate. This function modifies the compiled opcode! Hitting a -non-existant opcode may indicate a bug in PCRE2, but it can also be caused if a -bad UTF string was compiled with PCRE2_NO_UTF_CHECK. +non-existent opcode may indicate a bug in PCRE2, but it can also be caused if a +bad UTF string was compiled with PCRE2_NO_UTF_CHECK. The rec_limit catches +overly complicated or large patterns. In these cases, the check just stops, +leaving the remainder of the pattern unpossessified. Arguments: code points to start of the byte code @@ -1061,17 +1095,17 @@ Returns: 0 for success int PRIV(auto_possessify)(PCRE2_UCHAR *code, BOOL utf, const compile_block *cb) { -register PCRE2_UCHAR c; +PCRE2_UCHAR c; PCRE2_SPTR end; PCRE2_UCHAR *repeat_opcode; uint32_t list[8]; -int rec_limit; +int rec_limit = 1000; /* Was 10,000 but clang+ASAN uses a lot of stack. */ for (;;) { c = *code; - if (c > OP_TABLE_LENGTH) return -1; /* Something gone wrong */ + if (c >= OP_TABLE_LENGTH) return -1; /* Something gone wrong */ if (c >= OP_STAR && c <= OP_TYPEPOSUPTO) { @@ -1080,7 +1114,6 @@ for (;;) get_chr_property_list(code, utf, cb->fcc, list) : NULL; list[1] = c == OP_STAR || c == OP_PLUS || c == OP_QUERY || c == OP_UPTO; - rec_limit = 1000; if (end != NULL && compare_opcodes(end, utf, cb, list, end, &rec_limit)) { switch(c) @@ -1137,7 +1170,6 @@ for (;;) list[1] = (c & 1) == 0; - rec_limit = 1000; if (compare_opcodes(end, utf, cb, list, end, &rec_limit)) { switch (c) @@ -1203,6 +1235,7 @@ for (;;) #endif case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: diff --git a/pcre2-10.22/src/pcre2_chartables.c.dist b/pcre2-10.32/src/pcre2_chartables.c.dist similarity index 80% rename from pcre2-10.22/src/pcre2_chartables.c.dist rename to pcre2-10.32/src/pcre2_chartables.c.dist index 203cb1a4a..4046500c0 100644 --- a/pcre2-10.22/src/pcre2_chartables.c.dist +++ b/pcre2-10.32/src/pcre2_chartables.c.dist @@ -2,23 +2,24 @@ * Perl-Compatible Regular Expressions * *************************************************/ -/* This file contains character tables that are used when no external tables -are passed to PCRE2 by the application that calls it. The tables are used only -for characters whose code values are less than 256. +/* This file was automatically written by the dftables auxiliary +program. It contains character tables that are used when no external +tables are passed to PCRE2 by the application that calls it. The tables +are used only for characters whose code values are less than 256. */ -This is a default version of the tables that assumes ASCII encoding. A program -called dftables (which is distributed with PCRE2) can be used to build -alternative versions of this file. This is necessary if you are running in an -EBCDIC environment, or if you want to default to a different encoding, for -example ISO-8859-1. When dftables is run, it creates these tables in the -current locale. If PCRE2 is configured with --enable-rebuild-chartables, this -happens automatically. +/*The dftables program (which is distributed with PCRE2) can be used to +build alternative versions of this file. This is necessary if you are +running in an EBCDIC environment, or if you want to default to a different +encoding, for example ISO-8859-1. When dftables is run, it creates these +tables in the current locale. This happens automatically if PCRE2 is +configured with --enable-rebuild-chartables. */ -The following #includes are present because without them gcc 4.x may remove the -array definition from the final binary if PCRE2 is built into a static library -and dead code stripping is activated. This leads to link errors. Pulling in the -header ensures that the array gets flagged as "someone outside this compilation -unit might reference this" and so it will always be supplied to the linker. */ +/* The following #include is present because without it gcc 4.x may remove +the array definition from the final binary if PCRE2 is built into a static +library and dead code stripping is activated. This leads to link errors. +Pulling in the header ensures that the array gets flagged as "someone +outside this compilation unit might reference this" and so it will always +be supplied to the linker. */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -101,7 +102,7 @@ const uint8_t PRIV(default_tables)[] = { /* This table contains bit maps for various character classes. Each map is 32 bytes long and the bits run from the least significant end of each byte. The classes that have their own maps are: space, xdigit, digit, upper, lower, word, -graph, print, punct, and cntrl. Other classes are built from combinations. */ +graph print, punct, and cntrl. Other classes are built from combinations. */ 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, @@ -159,25 +160,24 @@ graph, print, punct, and cntrl. Other classes are built from combinations. */ 0x04 decimal digit 0x08 hexadecimal digit 0x10 alphanumeric or '_' - 0x80 regular expression metacharacter or binary zero */ - 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ - 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ - 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - ' */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ( - / */ 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ - 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x00, /* 8 - ? */ 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ - 0x12,0x12,0x12,0x80,0x80,0x00,0x80,0x10, /* X - _ */ + 0x12,0x12,0x12,0x00,0x00,0x00,0x00,0x10, /* X - _ */ 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ - 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ + 0x12,0x12,0x12,0x00,0x00,0x00,0x00,0x00, /* x -127 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ diff --git a/pcre2-10.32/src/pcre2_compile.c b/pcre2-10.32/src/pcre2_compile.c new file mode 100644 index 000000000..6bb1de361 --- /dev/null +++ b/pcre2-10.32/src/pcre2_compile.c @@ -0,0 +1,9921 @@ +/************************************************* +* 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-2018 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 + +#define NLBLOCK cb /* Block containing newline information */ +#define PSSTART start_pattern /* Field containing processed string start */ +#define PSEND end_pattern /* Field containing processed string end */ + +#include "pcre2_internal.h" + +/* In rare error cases debugging might require calling pcre2_printint(). */ + +#if 0 +#ifdef EBCDIC +#define PRINTABLE(c) ((c) >= 64 && (c) < 255) +#else +#define PRINTABLE(c) ((c) >= 32 && (c) < 127) +#endif +#include "pcre2_printint.c" +#define DEBUG_CALL_PRINTINT +#endif + +/* Other debugging code can be enabled by these defines. */ + +/* #define DEBUG_SHOW_CAPTURES */ +/* #define DEBUG_SHOW_PARSED */ + +/* There are a few things that vary with different code unit sizes. Handle them +by defining macros in order to minimize #if usage. */ + +#if PCRE2_CODE_UNIT_WIDTH == 8 +#define STRING_UTFn_RIGHTPAR STRING_UTF8_RIGHTPAR, 5 +#define XDIGIT(c) xdigitab[c] + +#else /* Either 16-bit or 32-bit */ +#define XDIGIT(c) (MAX_255(c)? xdigitab[c] : 0xff) + +#if PCRE2_CODE_UNIT_WIDTH == 16 +#define STRING_UTFn_RIGHTPAR STRING_UTF16_RIGHTPAR, 6 + +#else /* 32-bit */ +#define STRING_UTFn_RIGHTPAR STRING_UTF32_RIGHTPAR, 6 +#endif +#endif + +/* Macros to store and retrieve a PCRE2_SIZE value in the parsed pattern, which +consists of uint32_t elements. Assume that if uint32_t can't hold it, two of +them will be able to (i.e. assume a 64-bit world). */ + +#if PCRE2_SIZE_MAX <= UINT32_MAX +#define PUTOFFSET(s,p) *p++ = s +#define GETOFFSET(s,p) s = *p++ +#define GETPLUSOFFSET(s,p) s = *(++p) +#define READPLUSOFFSET(s,p) s = p[1] +#define SKIPOFFSET(p) p++ +#define SIZEOFFSET 1 +#else +#define PUTOFFSET(s,p) \ + { *p++ = (uint32_t)(s >> 32); *p++ = (uint32_t)(s & 0xffffffff); } +#define GETOFFSET(s,p) \ + { s = ((PCRE2_SIZE)p[0] << 32) | (PCRE2_SIZE)p[1]; p += 2; } +#define GETPLUSOFFSET(s,p) \ + { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; p += 2; } +#define READPLUSOFFSET(s,p) \ + { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; } +#define SKIPOFFSET(p) p += 2 +#define SIZEOFFSET 2 +#endif + +/* Macros for manipulating elements of the parsed pattern vector. */ + +#define META_CODE(x) (x & 0xffff0000u) +#define META_DATA(x) (x & 0x0000ffffu) +#define META_DIFF(x,y) ((x-y)>>16) + +/* Function definitions to allow mutual recursion */ + +#ifdef SUPPORT_UNICODE +static unsigned int + add_list_to_class_internal(uint8_t *, PCRE2_UCHAR **, uint32_t, + compile_block *, const uint32_t *, unsigned int); +#endif + +static int + compile_regex(uint32_t, PCRE2_UCHAR **, uint32_t **, int *, uint32_t, + uint32_t *, int32_t *, uint32_t *, int32_t *, branch_chain *, + compile_block *, PCRE2_SIZE *); + +static int + get_branchlength(uint32_t **, int *, int *, parsed_recurse_check *, + compile_block *); + +static BOOL + set_lookbehind_lengths(uint32_t **, int *, int *, parsed_recurse_check *, + compile_block *); + + + +/************************************************* +* Code parameters and static tables * +*************************************************/ + +#define MAX_GROUP_NUMBER 65535u +#define MAX_REPEAT_COUNT 65535u +#define REPEAT_UNLIMITED (MAX_REPEAT_COUNT+1) + +/* COMPILE_WORK_SIZE specifies the size of stack workspace, which is used in +different ways in the different pattern scans. The parsing and group- +identifying pre-scan uses it to handle nesting, and needs it to be 16-bit +aligned for this. Having defined the size in code units, we set up +C16_WORK_SIZE as the number of elements in the 16-bit vector. + +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. + +In the real compile phase, this workspace is not currently used. */ + +#define COMPILE_WORK_SIZE (3000*LINK_SIZE) /* Size in code units */ + +#define C16_WORK_SIZE \ + ((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint16_t)) + +/* A uint32_t vector is used for caching information about the size of +capturing groups, to improve performance. A default is created on the stack of +this size. */ + +#define GROUPINFO_DEFAULT_SIZE 256 + +/* 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. */ + +#define WORK_SIZE_SAFETY_MARGIN (100) + +/* This value determines the size of the initial vector that is used for +remembering named groups during the pre-compile. It is allocated on the stack, +but if it is too small, it is expanded, in a similar way to the workspace. The +value is the number of slots in the list. */ + +#define NAMED_GROUP_LIST_SIZE 20 + +/* The pre-compiling pass over the pattern creates a parsed pattern in a vector +of uint32_t. For short patterns this lives on the stack, with this size. Heap +memory is used for longer patterns. */ + +#define PARSED_PATTERN_DEFAULT_SIZE 1024 + +/* Maximum length value to check against when making sure that the variable +that holds the compiled pattern length does not overflow. We make it a bit less +than INT_MAX to allow for adding in group terminating code units, so that we +don't have to check them every time. */ + +#define OFLOW_MAX (INT_MAX - 20) + +/* Code values for parsed patterns, which are stored in a vector of 32-bit +unsigned ints. Values less than META_END are literal data values. The coding +for identifying the item is in the top 16-bits, leaving 16 bits for the +additional data that some of them need. The META_CODE, META_DATA, and META_DIFF +macros are used to manipulate parsed pattern elements. + +NOTE: When these definitions are changed, the table of extra lengths for each +code (meta_extra_lengths, just below) must be updated to remain in step. */ + +#define META_END 0x80000000u /* End of pattern */ + +#define META_ALT 0x80010000u /* alternation */ +#define META_ATOMIC 0x80020000u /* atomic group */ +#define META_BACKREF 0x80030000u /* Back ref */ +#define META_BACKREF_BYNAME 0x80040000u /* \k'name' */ +#define META_BIGVALUE 0x80050000u /* Next is a literal > META_END */ +#define META_CALLOUT_NUMBER 0x80060000u /* (?C with numerical argument */ +#define META_CALLOUT_STRING 0x80070000u /* (?C with string argument */ +#define META_CAPTURE 0x80080000u /* Capturing parenthesis */ +#define META_CIRCUMFLEX 0x80090000u /* ^ metacharacter */ +#define META_CLASS 0x800a0000u /* start non-empty class */ +#define META_CLASS_EMPTY 0x800b0000u /* empty class */ +#define META_CLASS_EMPTY_NOT 0x800c0000u /* negative empty class */ +#define META_CLASS_END 0x800d0000u /* end of non-empty class */ +#define META_CLASS_NOT 0x800e0000u /* start non-empty negative class */ +#define META_COND_ASSERT 0x800f0000u /* (?(?assertion)... */ +#define META_COND_DEFINE 0x80100000u /* (?(DEFINE)... */ +#define META_COND_NAME 0x80110000u /* (?()... */ +#define META_COND_NUMBER 0x80120000u /* (?(digits)... */ +#define META_COND_RNAME 0x80130000u /* (?(R&name)... */ +#define META_COND_RNUMBER 0x80140000u /* (?(Rdigits)... */ +#define META_COND_VERSION 0x80150000u /* (?(VERSIONx.y)... */ +#define META_DOLLAR 0x80160000u /* $ metacharacter */ +#define META_DOT 0x80170000u /* . metacharacter */ +#define META_ESCAPE 0x80180000u /* \d and friends */ +#define META_KET 0x80190000u /* closing parenthesis */ +#define META_NOCAPTURE 0x801a0000u /* no capture parens */ +#define META_OPTIONS 0x801b0000u /* (?i) and friends */ +#define META_POSIX 0x801c0000u /* POSIX class item */ +#define META_POSIX_NEG 0x801d0000u /* negative POSIX class item */ +#define META_RANGE_ESCAPED 0x801e0000u /* range with at least one escape */ +#define META_RANGE_LITERAL 0x801f0000u /* range defined literally */ +#define META_RECURSE 0x80200000u /* Recursion */ +#define META_RECURSE_BYNAME 0x80210000u /* (?&name) */ + +/* These must be kept together to make it easy to check that an assertion +is present where expected in a conditional group. */ + +#define META_LOOKAHEAD 0x80220000u /* (?= */ +#define META_LOOKAHEADNOT 0x80230000u /* (?! */ +#define META_LOOKBEHIND 0x80240000u /* (?<= */ +#define META_LOOKBEHINDNOT 0x80250000u /* (?= 10 */ + 1+SIZEOFFSET, /* META_BACKREF_BYNAME */ + 1, /* META_BIGVALUE */ + 3, /* META_CALLOUT_NUMBER */ + 3+SIZEOFFSET, /* META_CALLOUT_STRING */ + 0, /* META_CAPTURE */ + 0, /* META_CIRCUMFLEX */ + 0, /* META_CLASS */ + 0, /* META_CLASS_EMPTY */ + 0, /* META_CLASS_EMPTY_NOT */ + 0, /* META_CLASS_END */ + 0, /* META_CLASS_NOT */ + 0, /* META_COND_ASSERT */ + SIZEOFFSET, /* META_COND_DEFINE */ + 1+SIZEOFFSET, /* META_COND_NAME */ + 1+SIZEOFFSET, /* META_COND_NUMBER */ + 1+SIZEOFFSET, /* META_COND_RNAME */ + 1+SIZEOFFSET, /* META_COND_RNUMBER */ + 3, /* META_COND_VERSION */ + 0, /* META_DOLLAR */ + 0, /* META_DOT */ + 0, /* META_ESCAPE - more for ESC_P, ESC_p, ESC_g, ESC_k */ + 0, /* META_KET */ + 0, /* META_NOCAPTURE */ + 1, /* META_OPTIONS */ + 1, /* META_POSIX */ + 1, /* META_POSIX_NEG */ + 0, /* META_RANGE_ESCAPED */ + 0, /* META_RANGE_LITERAL */ + SIZEOFFSET, /* META_RECURSE */ + 1+SIZEOFFSET, /* META_RECURSE_BYNAME */ + 0, /* META_LOOKAHEAD */ + 0, /* META_LOOKAHEADNOT */ + SIZEOFFSET, /* META_LOOKBEHIND */ + SIZEOFFSET, /* META_LOOKBEHINDNOT */ + 1, /* META_MARK - plus the string length */ + 0, /* META_ACCEPT */ + 0, /* META_FAIL */ + 0, /* META_COMMIT */ + 1, /* META_COMMIT_ARG - plus the string length */ + 0, /* META_PRUNE */ + 1, /* META_PRUNE_ARG - plus the string length */ + 0, /* META_SKIP */ + 1, /* META_SKIP_ARG - plus the string length */ + 0, /* META_THEN */ + 1, /* META_THEN_ARG - plus the string length */ + 0, /* META_ASTERISK */ + 0, /* META_ASTERISK_PLUS */ + 0, /* META_ASTERISK_QUERY */ + 0, /* META_PLUS */ + 0, /* META_PLUS_PLUS */ + 0, /* META_PLUS_QUERY */ + 0, /* META_QUERY */ + 0, /* META_QUERY_PLUS */ + 0, /* META_QUERY_QUERY */ + 2, /* META_MINMAX */ + 2, /* META_MINMAX_PLUS */ + 2 /* META_MINMAX_QUERY */ +}; + +/* Types for skipping parts of a parsed pattern. */ + +enum { PSKIP_ALT, PSKIP_CLASS, PSKIP_KET }; + +/* Macro for setting individual bits in class bitmaps. It took some +experimenting to figure out how to stop gcc 5.3.0 from warning with +-Wconversion. This version gets a warning: + + #define SETBIT(a,b) a[(b)/8] |= (uint8_t)(1 << ((b)&7)) + +Let's hope the apparently less efficient version isn't actually so bad if the +compiler is clever with identical subexpressions. */ + +#define SETBIT(a,b) a[(b)/8] = (uint8_t)(a[(b)/8] | (1 << ((b)&7))) + +/* Private flags added to firstcu and reqcu. */ + +#define REQ_CASELESS (1 << 0) /* Indicates caselessness */ +#define REQ_VARY (1 << 1) /* reqcu followed non-literal item */ +/* Negative values for the firstcu and reqcu flags */ +#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_FIXED_LENGTH 0x80000000u +#define GI_NOT_FIXED_LENGTH 0x40000000u +#define GI_FIXED_LENGTH_MASK 0x0000ffffu + +/* This simple test for a decimal digit works for both ASCII/Unicode and EBCDIC +and is fast (a good compiler can turn it into a subtraction and unsigned +comparison). */ + +#define IS_DIGIT(x) ((x) >= CHAR_0 && (x) <= CHAR_9) + +/* Table to identify hex digits. The tables in chartables are dependent on the +locale, and may mark arbitrary characters as digits. We want to recognize only +0-9, a-z, and A-Z as hex digits, which is why we have a private table here. It +costs 256 bytes, but it is a lot faster than doing character value tests (at +least in some simple cases I timed), and in some applications one wants PCRE2 +to compile efficiently as well as match efficiently. The value in the table is +the binary hex digit value, or 0xff for non-hex digits. */ + +/* This is the "normal" case, for ASCII systems, and EBCDIC systems running in +UTF-8 mode. */ + +#ifndef EBCDIC +static const uint8_t xdigitab[] = + { + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - ' */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ( - / */ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 */ + 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff, /* 8 - ? */ + 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* @ - G */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H - O */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* P - W */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* X - _ */ + 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* ` - g */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h - o */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* p - w */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* x -127 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 128-135 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 136-143 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144-151 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 152-159 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160-167 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 168-175 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 176-183 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 192-199 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 2ff-207 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 208-215 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 216-223 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 224-231 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 232-239 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 240-247 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};/* 248-255 */ + +#else + +/* This is the "abnormal" case, for EBCDIC systems not running in UTF-8 mode. */ + +static const uint8_t xdigitab[] = + { + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 10 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 32- 39 20 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 40- 47 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 48- 55 30 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 56- 63 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - 71 40 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 72- | */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* & - 87 50 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 88- 95 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - -103 60 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 104- ? */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 112-119 70 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 120- " */ + 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* 128- g 80 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h -143 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144- p 90 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* q -159 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160- x A0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* y -175 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ^ -183 B0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */ + 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* { - G C0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H -207 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* } - P D0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Q -223 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* \ - X E0 */ + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Y -239 */ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 F0 */ + 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff};/* 8 -255 */ +#endif /* EBCDIC */ + + +/* Table for handling alphanumeric escaped characters. Positive returns are +simple data values; negative values are for special things like \d and so on. +Zero means further processing is needed (for things like \x), or the escape is +invalid. */ + +/* This is the "normal" table for ASCII systems or for EBCDIC systems running +in UTF-8 mode. It runs from '0' to 'z'. */ + +#ifndef EBCDIC +#define ESCAPES_FIRST CHAR_0 +#define ESCAPES_LAST CHAR_z +#define UPPER_CASE(c) (c-32) + +static const short int escapes[] = { + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + CHAR_COLON, CHAR_SEMICOLON, + CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN, + CHAR_GREATER_THAN_SIGN, CHAR_QUESTION_MARK, + CHAR_COMMERCIAL_AT, -ESC_A, + -ESC_B, -ESC_C, + -ESC_D, -ESC_E, + 0, -ESC_G, + -ESC_H, 0, + 0, -ESC_K, + 0, 0, + -ESC_N, 0, + -ESC_P, -ESC_Q, + -ESC_R, -ESC_S, + 0, 0, + -ESC_V, -ESC_W, + -ESC_X, 0, + -ESC_Z, CHAR_LEFT_SQUARE_BRACKET, + CHAR_BACKSLASH, CHAR_RIGHT_SQUARE_BRACKET, + CHAR_CIRCUMFLEX_ACCENT, CHAR_UNDERSCORE, + CHAR_GRAVE_ACCENT, CHAR_BEL, + -ESC_b, 0, + -ESC_d, CHAR_ESC, + CHAR_FF, 0, + -ESC_h, 0, + 0, -ESC_k, + 0, 0, + CHAR_LF, 0, + -ESC_p, 0, + CHAR_CR, -ESC_s, + CHAR_HT, 0, + -ESC_v, -ESC_w, + 0, 0, + -ESC_z +}; + +#else + +/* This is the "abnormal" table for EBCDIC systems without UTF-8 support. +It runs from 'a' to '9'. For some minimal testing of EBCDIC features, the code +is sometimes compiled on an ASCII system. In this case, we must not use CHAR_a +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 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 UPPER_CASE(c) (c-32) +#endif + +static const short int escapes[] = { +/* 80 */ CHAR_BEL, -ESC_b, 0, -ESC_d, CHAR_ESC, CHAR_FF, 0, +/* 88 */ -ESC_h, 0, 0, '{', 0, 0, 0, 0, +/* 90 */ 0, 0, -ESC_k, 0, 0, CHAR_LF, 0, -ESC_p, +/* 98 */ 0, CHAR_CR, 0, '}', 0, 0, 0, 0, +/* A0 */ 0, '~', -ESC_s, CHAR_HT, 0, -ESC_v, -ESC_w, 0, +/* A8 */ 0, -ESC_z, 0, 0, 0, '[', 0, 0, +/* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, +/* B8 */ 0, 0, 0, 0, 0, ']', '=', '-', +/* C0 */ '{', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G, +/* C8 */ -ESC_H, 0, 0, 0, 0, 0, 0, 0, +/* D0 */ '}', 0, -ESC_K, 0, 0, -ESC_N, 0, -ESC_P, +/* D8 */ -ESC_Q, -ESC_R, 0, 0, 0, 0, 0, 0, +/* E0 */ '\\', 0, -ESC_S, 0, 0, -ESC_V, -ESC_W, -ESC_X, +/* E8 */ 0, -ESC_Z, 0, 0, 0, 0, 0, 0, +/* F0 */ 0, 0, 0, 0, 0, 0, 0, 0, +/* F8 */ 0, 0 +}; + +/* We also need a table of characters that may follow \c in an EBCDIC +environment for characters 0-31. */ + +static unsigned char ebcdic_escape_c[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; + +#endif /* EBCDIC */ + + +/* Table of special "verbs" like (*PRUNE). This is a short table, so it is +searched linearly. Put all the names into a single string, in order to reduce +the number of relocations when a shared library is dynamically linked. The +string is built from string macros so that it works in UTF-8 mode on EBCDIC +platforms. */ + +typedef struct verbitem { + unsigned int len; /* Length of verb name */ + uint32_t meta; /* Base META_ code */ + int has_arg; /* Argument requirement */ +} verbitem; + +static const char verbnames[] = + "\0" /* Empty name is a shorthand for MARK */ + STRING_MARK0 + STRING_ACCEPT0 + STRING_F0 + STRING_FAIL0 + STRING_COMMIT0 + STRING_PRUNE0 + STRING_SKIP0 + STRING_THEN; + +static const verbitem verbs[] = { + { 0, META_MARK, +1 }, /* > 0 => must have an argument */ + { 4, META_MARK, +1 }, + { 6, META_ACCEPT, -1 }, /* < 0 => Optional argument, convert to pre-MARK */ + { 1, META_FAIL, -1 }, + { 4, META_FAIL, -1 }, + { 6, META_COMMIT, 0 }, + { 5, META_PRUNE, 0 }, /* Optional argument; bump META code if found */ + { 4, META_SKIP, 0 }, + { 4, META_THEN, 0 } +}; + +static const int verbcount = sizeof(verbs)/sizeof(verbitem); + +/* Verb opcodes, indexed by their META code offset from META_MARK. */ + +static const uint32_t verbops[] = { + OP_MARK, OP_ACCEPT, OP_FAIL, OP_COMMIT, OP_COMMIT_ARG, OP_PRUNE, + OP_PRUNE_ARG, OP_SKIP, OP_SKIP_ARG, OP_THEN, OP_THEN_ARG }; + +/* Offsets from OP_STAR for case-independent and negative repeat opcodes. */ + +static uint32_t chartypeoffset[] = { + OP_STAR - OP_STAR, OP_STARI - OP_STAR, + OP_NOTSTAR - OP_STAR, OP_NOTSTARI - OP_STAR }; + +/* Tables of names of POSIX character classes and their lengths. The names are +now all in a single string, to reduce the number of relocations when a shared +library is dynamically loaded. The list of lengths is terminated by a zero +length entry. The first three must be alpha, lower, upper, as this is assumed +for handling case independence. The indices for graph, print, and punct are +needed, so identify them. */ + +static const char posix_names[] = + STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0 + STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0 + STRING_graph0 STRING_print0 STRING_punct0 STRING_space0 + STRING_word0 STRING_xdigit; + +static const uint8_t posix_name_lengths[] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; + +#define PC_GRAPH 8 +#define PC_PRINT 9 +#define PC_PUNCT 10 + +/* Table of class bit maps for each POSIX class. Each class is formed from a +base map, with an optional addition or removal of another map. Then, for some +classes, there is some additional tweaking: for [:blank:] the vertical space +characters are removed, and for [:alpha:] and [:alnum:] the underscore +character is removed. The triples in the table consist of the base map offset, +second map offset or -1 if no second map, and a non-negative value for map +addition or a negative value for map subtraction (if there are two maps). The +absolute value of the third field has these meanings: 0 => no tweaking, 1 => +remove vertical space characters, 2 => remove underscore. */ + +static const int posix_class_maps[] = { + cbit_word, cbit_digit, -2, /* alpha */ + cbit_lower, -1, 0, /* lower */ + cbit_upper, -1, 0, /* upper */ + cbit_word, -1, 2, /* alnum - word without underscore */ + cbit_print, cbit_cntrl, 0, /* ascii */ + cbit_space, -1, 1, /* blank - a GNU extension */ + cbit_cntrl, -1, 0, /* cntrl */ + cbit_digit, -1, 0, /* digit */ + cbit_graph, -1, 0, /* graph */ + cbit_print, -1, 0, /* print */ + cbit_punct, -1, 0, /* punct */ + cbit_space, -1, 0, /* space */ + cbit_word, -1, 0, /* word - a Perl extension */ + cbit_xdigit,-1, 0 /* xdigit */ +}; + +#ifdef SUPPORT_UNICODE + +/* The POSIX class Unicode property substitutes that are used in UCP mode must +be in the order of the POSIX class names, defined above. */ + +static int posix_substitutes[] = { + PT_GC, ucp_L, /* alpha */ + PT_PC, ucp_Ll, /* lower */ + PT_PC, ucp_Lu, /* upper */ + PT_ALNUM, 0, /* alnum */ + -1, 0, /* ascii, treat as non-UCP */ + -1, 1, /* blank, treat as \h */ + PT_PC, ucp_Cc, /* cntrl */ + PT_PC, ucp_Nd, /* digit */ + PT_PXGRAPH, 0, /* graph */ + PT_PXPRINT, 0, /* print */ + PT_PXPUNCT, 0, /* punct */ + PT_PXSPACE, 0, /* space */ /* Xps is POSIX space, but from 8.34 */ + PT_WORD, 0, /* word */ /* Perl and POSIX space are the same */ + -1, 0 /* xdigit, treat as non-UCP */ +}; +#define POSIX_SUBSIZE (sizeof(posix_substitutes) / (2*sizeof(uint32_t))) +#endif /* SUPPORT_UNICODE */ + +/* Masks for checking option settings. When PCRE2_LITERAL is set, only a subset +are allowed. */ + +#define PUBLIC_LITERAL_COMPILE_OPTIONS \ + (PCRE2_ANCHORED|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_ENDANCHORED| \ + PCRE2_FIRSTLINE|PCRE2_LITERAL|PCRE2_NO_START_OPTIMIZE| \ + PCRE2_NO_UTF_CHECK|PCRE2_USE_OFFSET_LIMIT|PCRE2_UTF) + +#define PUBLIC_COMPILE_OPTIONS \ + (PUBLIC_LITERAL_COMPILE_OPTIONS| \ + PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \ + PCRE2_ALT_VERBNAMES|PCRE2_DOLLAR_ENDONLY|PCRE2_DOTALL|PCRE2_DUPNAMES| \ + PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|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_UCP|PCRE2_UNGREEDY) + +#define PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS \ + (PCRE2_EXTRA_MATCH_LINE|PCRE2_EXTRA_MATCH_WORD) + +#define PUBLIC_COMPILE_EXTRA_OPTIONS \ + (PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS| \ + PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES|PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) + +/* 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 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, + ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20, + ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29, ERR30, + ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40, + ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, ERR50, + 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, ERR85, ERR86, ERR87, ERR88, ERR89, ERR90, + ERR91, ERR92, ERR93, ERR94 }; + +/* 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 +compatibility, (*UTFn) is supported in the relevant libraries, but (*UTF) is +generic and always supported. */ + +enum { PSO_OPT, /* Value is an option bit */ + PSO_FLG, /* Value is a flag bit */ + PSO_NL, /* Value is a newline type */ + PSO_BSR, /* Value is a \R type */ + PSO_LIMH, /* Read integer value for heap limit */ + PSO_LIMM, /* Read integer value for match limit */ + PSO_LIMD }; /* Read integer value for depth limit */ + +typedef struct pso { + const uint8_t *name; + uint16_t length; + uint16_t type; + uint32_t value; +} pso; + +/* NB: STRING_UTFn_RIGHTPAR contains the length as well */ + +static pso pso_list[] = { + { (uint8_t *)STRING_UTFn_RIGHTPAR, PSO_OPT, PCRE2_UTF }, + { (uint8_t *)STRING_UTF_RIGHTPAR, 4, PSO_OPT, PCRE2_UTF }, + { (uint8_t *)STRING_UCP_RIGHTPAR, 4, PSO_OPT, PCRE2_UCP }, + { (uint8_t *)STRING_NOTEMPTY_RIGHTPAR, 9, PSO_FLG, PCRE2_NOTEMPTY_SET }, + { (uint8_t *)STRING_NOTEMPTY_ATSTART_RIGHTPAR, 17, PSO_FLG, PCRE2_NE_ATST_SET }, + { (uint8_t *)STRING_NO_AUTO_POSSESS_RIGHTPAR, 16, PSO_OPT, PCRE2_NO_AUTO_POSSESS }, + { (uint8_t *)STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR, 18, PSO_OPT, PCRE2_NO_DOTSTAR_ANCHOR }, + { (uint8_t *)STRING_NO_JIT_RIGHTPAR, 7, PSO_FLG, PCRE2_NOJIT }, + { (uint8_t *)STRING_NO_START_OPT_RIGHTPAR, 13, PSO_OPT, PCRE2_NO_START_OPTIMIZE }, + { (uint8_t *)STRING_LIMIT_HEAP_EQ, 11, PSO_LIMH, 0 }, + { (uint8_t *)STRING_LIMIT_MATCH_EQ, 12, PSO_LIMM, 0 }, + { (uint8_t *)STRING_LIMIT_DEPTH_EQ, 12, PSO_LIMD, 0 }, + { (uint8_t *)STRING_LIMIT_RECURSION_EQ, 16, PSO_LIMD, 0 }, + { (uint8_t *)STRING_CR_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_CR }, + { (uint8_t *)STRING_LF_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_LF }, + { (uint8_t *)STRING_CRLF_RIGHTPAR, 5, PSO_NL, PCRE2_NEWLINE_CRLF }, + { (uint8_t *)STRING_ANY_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_ANY }, + { (uint8_t *)STRING_NUL_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_NUL }, + { (uint8_t *)STRING_ANYCRLF_RIGHTPAR, 8, PSO_NL, PCRE2_NEWLINE_ANYCRLF }, + { (uint8_t *)STRING_BSR_ANYCRLF_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_ANYCRLF }, + { (uint8_t *)STRING_BSR_UNICODE_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_UNICODE } +}; + +/* 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 DEBUG_SHOW_PARSED +/************************************************* +* Show the parsed pattern for debugging * +*************************************************/ + +/* For debugging the pre-scan, this code, which outputs the parsed data vector, +can be enabled. */ + +static void show_parsed(compile_block *cb) +{ +uint32_t *pptr = cb->parsed_pattern; + +for (;;) + { + int max, min; + PCRE2_SIZE offset; + uint32_t i; + uint32_t length; + uint32_t meta_arg = META_DATA(*pptr); + + fprintf(stderr, "+++ %02d %.8x ", (int)(pptr - cb->parsed_pattern), *pptr); + + if (*pptr < META_END) + { + if (*pptr > 32 && *pptr < 128) fprintf(stderr, "%c", *pptr); + pptr++; + } + + else switch (META_CODE(*pptr++)) + { + default: + fprintf(stderr, "**** OOPS - unknown META value - giving up ****\n"); + return; + + case META_END: + fprintf(stderr, "META_END\n"); + return; + + case META_CAPTURE: + fprintf(stderr, "META_CAPTURE %d", meta_arg); + break; + + case META_RECURSE: + GETOFFSET(offset, pptr); + fprintf(stderr, "META_RECURSE %d %zd", meta_arg, offset); + break; + + case META_BACKREF: + if (meta_arg < 10) + offset = cb->small_ref_offset[meta_arg]; + else + GETOFFSET(offset, pptr); + fprintf(stderr, "META_BACKREF %d %zd", meta_arg, offset); + break; + + case META_ESCAPE: + if (meta_arg == ESC_P || meta_arg == ESC_p) + { + uint32_t ptype = *pptr >> 16; + uint32_t pvalue = *pptr++ & 0xffff; + fprintf(stderr, "META \\%c %d %d", (meta_arg == ESC_P)? 'P':'p', + ptype, pvalue); + } + else + { + uint32_t cc; + /* There's just one escape we might have here that isn't negated in the + escapes table. */ + if (meta_arg == ESC_g) cc = CHAR_g; + else for (cc = ESCAPES_FIRST; cc <= ESCAPES_LAST; cc++) + { + if (meta_arg == (uint32_t)(-escapes[cc - ESCAPES_FIRST])) break; + } + if (cc > ESCAPES_LAST) cc = CHAR_QUESTION_MARK; + fprintf(stderr, "META \\%c", cc); + } + break; + + case META_MINMAX: + min = *pptr++; + max = *pptr++; + if (max != REPEAT_UNLIMITED) + fprintf(stderr, "META {%d,%d}", min, max); + else + fprintf(stderr, "META {%d,}", min); + break; + + case META_MINMAX_QUERY: + min = *pptr++; + max = *pptr++; + if (max != REPEAT_UNLIMITED) + fprintf(stderr, "META {%d,%d}?", min, max); + else + fprintf(stderr, "META {%d,}?", min); + break; + + case META_MINMAX_PLUS: + min = *pptr++; + max = *pptr++; + if (max != REPEAT_UNLIMITED) + fprintf(stderr, "META {%d,%d}+", min, max); + else + fprintf(stderr, "META {%d,}+", min); + break; + + case META_BIGVALUE: fprintf(stderr, "META_BIGVALUE %.8x", *pptr++); break; + case META_CIRCUMFLEX: fprintf(stderr, "META_CIRCUMFLEX"); break; + case META_COND_ASSERT: fprintf(stderr, "META_COND_ASSERT"); break; + case META_DOLLAR: fprintf(stderr, "META_DOLLAR"); break; + case META_DOT: fprintf(stderr, "META_DOT"); break; + case META_ASTERISK: fprintf(stderr, "META *"); break; + case META_ASTERISK_QUERY: fprintf(stderr, "META *?"); break; + case META_ASTERISK_PLUS: fprintf(stderr, "META *+"); break; + case META_PLUS: fprintf(stderr, "META +"); break; + case META_PLUS_QUERY: fprintf(stderr, "META +?"); break; + case META_PLUS_PLUS: fprintf(stderr, "META ++"); break; + case META_QUERY: fprintf(stderr, "META ?"); break; + case META_QUERY_QUERY: fprintf(stderr, "META ??"); break; + case META_QUERY_PLUS: fprintf(stderr, "META ?+"); break; + + case META_ATOMIC: fprintf(stderr, "META (?>"); break; + case META_NOCAPTURE: fprintf(stderr, "META (?:"); break; + case META_LOOKAHEAD: fprintf(stderr, "META (?="); break; + case META_LOOKAHEADNOT: fprintf(stderr, "META (?!"); break; + case META_KET: fprintf(stderr, "META )"); break; + case META_ALT: fprintf(stderr, "META | %d", meta_arg); break; + + case META_CLASS: fprintf(stderr, "META ["); break; + case META_CLASS_NOT: fprintf(stderr, "META [^"); break; + case META_CLASS_END: fprintf(stderr, "META ]"); break; + case META_CLASS_EMPTY: fprintf(stderr, "META []"); break; + case META_CLASS_EMPTY_NOT: fprintf(stderr, "META [^]"); break; + + case META_RANGE_LITERAL: fprintf(stderr, "META - (literal)"); break; + case META_RANGE_ESCAPED: fprintf(stderr, "META - (escaped)"); break; + + case META_POSIX: fprintf(stderr, "META_POSIX %d", *pptr++); break; + case META_POSIX_NEG: fprintf(stderr, "META_POSIX_NEG %d", *pptr++); break; + + case META_ACCEPT: fprintf(stderr, "META (*ACCEPT)"); break; + case META_FAIL: fprintf(stderr, "META (*FAIL)"); break; + case META_COMMIT: fprintf(stderr, "META (*COMMIT)"); break; + case META_PRUNE: fprintf(stderr, "META (*PRUNE)"); break; + case META_SKIP: fprintf(stderr, "META (*SKIP)"); break; + case META_THEN: fprintf(stderr, "META (*THEN)"); break; + + case META_OPTIONS: fprintf(stderr, "META_OPTIONS 0x%02x", *pptr++); break; + + case META_LOOKBEHIND: + fprintf(stderr, "META (?<= %d offset=", meta_arg); + GETOFFSET(offset, pptr); + fprintf(stderr, "%zd", offset); + break; + + case META_LOOKBEHINDNOT: + fprintf(stderr, "META (?="); + fprintf(stderr, "%d.", *pptr++); + fprintf(stderr, "%d)", *pptr++); + break; + + case META_COND_NAME: + fprintf(stderr, "META (?() length=%d offset=", *pptr++); + GETOFFSET(offset, pptr); + fprintf(stderr, "%zd", offset); + break; + + case META_COND_RNAME: + fprintf(stderr, "META (?(R&name) length=%d offset=", *pptr++); + GETOFFSET(offset, pptr); + fprintf(stderr, "%zd", offset); + break; + + /* This is kept as a name, because it might be. */ + + case META_COND_RNUMBER: + fprintf(stderr, "META (?(Rnumber) length=%d offset=", *pptr++); + GETOFFSET(offset, pptr); + fprintf(stderr, "%zd", offset); + break; + + case META_MARK: + fprintf(stderr, "META (*MARK:"); + goto SHOWARG; + + case META_COMMIT_ARG: + fprintf(stderr, "META (*COMMIT:"); + goto SHOWARG; + + case META_PRUNE_ARG: + fprintf(stderr, "META (*PRUNE:"); + goto SHOWARG; + + case META_SKIP_ARG: + fprintf(stderr, "META (*SKIP:"); + goto SHOWARG; + + case META_THEN_ARG: + fprintf(stderr, "META (*THEN:"); + SHOWARG: + length = *pptr++; + for (i = 0; i < length; i++) + { + uint32_t cc = *pptr++; + if (cc > 32 && cc < 128) fprintf(stderr, "%c", cc); + else fprintf(stderr, "\\x{%x}", cc); + } + fprintf(stderr, ") length=%u", length); + break; + } + fprintf(stderr, "\n"); + } +return; +} +#endif /* DEBUG_SHOW_PARSED */ + + + +/************************************************* +* Copy compiled code * +*************************************************/ + +/* Compiled JIT code cannot be copied, so the new compiled block has no +associated JIT data. */ + +PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION +pcre2_code_copy(const pcre2_code *code) +{ +PCRE2_SIZE* ref_count; +pcre2_code *newcode; + +if (code == NULL) return NULL; +newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data); +if (newcode == NULL) return NULL; +memcpy(newcode, code, code->blocksize); +newcode->executable_jit = NULL; + +/* If the code is one that has been deserialized, increment the reference count +in the decoded tables. */ + +if ((code->flags & PCRE2_DEREF_TABLES) != 0) + { + ref_count = (PCRE2_SIZE *)(code->tables + tables_length); + (*ref_count)++; + } + +return newcode; +} + + + +/************************************************* +* Copy compiled code and character tables * +*************************************************/ + +/* Compiled JIT code cannot be copied, so the new compiled block has no +associated JIT data. This version of code_copy also makes a separate copy of +the character tables. */ + +PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION +pcre2_code_copy_with_tables(const pcre2_code *code) +{ +PCRE2_SIZE* ref_count; +pcre2_code *newcode; +uint8_t *newtables; + +if (code == NULL) return NULL; +newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data); +if (newcode == NULL) return NULL; +memcpy(newcode, code, code->blocksize); +newcode->executable_jit = NULL; + +newtables = code->memctl.malloc(tables_length + sizeof(PCRE2_SIZE), + code->memctl.memory_data); +if (newtables == NULL) + { + code->memctl.free((void *)newcode, code->memctl.memory_data); + return NULL; + } +memcpy(newtables, code->tables, tables_length); +ref_count = (PCRE2_SIZE *)(newtables + tables_length); +*ref_count = 1; + +newcode->tables = newtables; +newcode->flags |= PCRE2_DEREF_TABLES; +return newcode; +} + + + +/************************************************* +* Free compiled code * +*************************************************/ + +PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION +pcre2_code_free(pcre2_code *code) +{ +PCRE2_SIZE* ref_count; + +if (code != NULL) + { + if (code->executable_jit != NULL) + PRIV(jit_free)(code->executable_jit, &code->memctl); + + if ((code->flags & PCRE2_DEREF_TABLES) != 0) + { + /* Decoded tables belong to the codes after deserialization, and they must + be freed when there are no more reference to them. The *ref_count should + always be > 0. */ + + ref_count = (PCRE2_SIZE *)(code->tables + tables_length); + if (*ref_count > 0) + { + (*ref_count)--; + if (*ref_count == 0) + code->memctl.free((void *)code->tables, code->memctl.memory_data); + } + } + + code->memctl.free(code, code->memctl.memory_data); + } +} + + + +/************************************************* +* Read a number, possibly signed * +*************************************************/ + +/* This function is used to read numbers in the pattern. The initial pointer +must be the sign or first digit of the number. When relative values (introduced +by + or -) are allowed, they are relative group numbers, and the result must be +greater than zero. + +Arguments: + ptrptr points to the character pointer variable + ptrend points to the end of the input string + allow_sign if < 0, sign not allowed; if >= 0, sign is relative to this + max_value the largest number allowed + max_error the error to give for an over-large number + intptr where to put the result + errcodeptr where to put an error code + +Returns: TRUE - a number was read + FALSE - errorcode == 0 => no number was found + errorcode != 0 => an error occurred +*/ + +static BOOL +read_number(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, int32_t allow_sign, + uint32_t max_value, uint32_t max_error, int *intptr, int *errorcodeptr) +{ +int sign = 0; +uint32_t n = 0; +PCRE2_SPTR ptr = *ptrptr; +BOOL yield = FALSE; + +*errorcodeptr = 0; + +if (allow_sign >= 0 && ptr < ptrend) + { + if (*ptr == CHAR_PLUS) + { + sign = +1; + max_value -= allow_sign; + ptr++; + } + else if (*ptr == CHAR_MINUS) + { + sign = -1; + ptr++; + } + } + +if (ptr >= ptrend || !IS_DIGIT(*ptr)) return FALSE; +while (ptr < ptrend && IS_DIGIT(*ptr)) + { + n = n * 10 + *ptr++ - CHAR_0; + if (n > max_value) + { + *errorcodeptr = max_error; + goto EXIT; + } + } + +if (allow_sign >= 0 && sign != 0) + { + if (n == 0) + { + *errorcodeptr = ERR26; /* +0 and -0 are not allowed */ + goto EXIT; + } + + if (sign > 0) n += allow_sign; + else if ((int)n > allow_sign) + { + *errorcodeptr = ERR15; /* Non-existent subpattern */ + goto EXIT; + } + else n = allow_sign + 1 - n; + } + +yield = TRUE; + +EXIT: +*intptr = n; +*ptrptr = ptr; +return yield; +} + + + +/************************************************* +* Read repeat counts * +*************************************************/ + +/* Read an item of the form {n,m} and return the values if non-NULL pointers +are supplied. Repeat counts must be less than 65536 (MAX_REPEAT_COUNT); a +larger value is used for "unlimited". We have to use signed arguments for +read_number() because it is capable of returning a signed value. + +Arguments: + ptrptr points to pointer to character after'{' + ptrend pointer to end of input + minp if not NULL, pointer to int for min + maxp if not NULL, pointer to int for max (-1 if no max) + returned as -1 if no max + errorcodeptr points to error code variable + +Returns: FALSE if not a repeat quantifier, errorcode set zero + FALSE on error, with errorcode set non-zero + TRUE on success, with pointer updated to point after '}' +*/ + +static BOOL +read_repeat_counts(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *minp, + uint32_t *maxp, int *errorcodeptr) +{ +PCRE2_SPTR p = *ptrptr; +BOOL yield = FALSE; +int32_t min = 0; +int32_t max = REPEAT_UNLIMITED; /* This value is larger than MAX_REPEAT_COUNT */ + +/* NB read_number() initializes the error code to zero. The only error is for a +number that is too big. */ + +if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &min, errorcodeptr)) + goto EXIT; + +if (p >= ptrend) goto EXIT; + +if (*p == CHAR_RIGHT_CURLY_BRACKET) + { + p++; + max = min; + } + +else + { + if (*p++ != CHAR_COMMA || p >= ptrend) goto EXIT; + if (*p != CHAR_RIGHT_CURLY_BRACKET) + { + if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &max, + errorcodeptr) || p >= ptrend || *p != CHAR_RIGHT_CURLY_BRACKET) + goto EXIT; + if (max < min) + { + *errorcodeptr = ERR4; + goto EXIT; + } + } + p++; + } + +yield = TRUE; +if (minp != NULL) *minp = (uint32_t)min; +if (maxp != NULL) *maxp = (uint32_t)max; + +/* Update the pattern pointer on success, or after an error, but not when +the result is "not a repeat quantifier". */ + +EXIT: +if (yield || *errorcodeptr != 0) *ptrptr = p; +return yield; + + + +} + + + +/************************************************* +* Handle escapes * +*************************************************/ + +/* This function is called when a \ has been encountered. It either returns a +positive value for a simple escape such as \d, or 0 for a data character, which +is placed in chptr. A backreference to group n is returned as negative n. On +entry, ptr is pointing at the character after \. On exit, it points after 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 in the case +of escapes that have further processing, only sequences that define a data +character are recognised. The isclass argument is not relevant; the options +argument is the final value of the compiled pattern's options. + +Arguments: + 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 + isclass TRUE if inside a character class + cb compile data block + +Returns: zero => a data character + positive => a special escape sequence + negative => a numerical back reference + on error, errorcodeptr is set non-zero +*/ + +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; +uint32_t c, cc; +int escape = 0; +int i; + +/* 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 */ +*errorcodeptr = 0; /* Be optimistic */ + +/* 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. */ + +if (c < ESCAPES_FIRST || c > ESCAPES_LAST) {} /* Definitely literal */ + +else if ((i = escapes[c - ESCAPES_FIRST]) != 0) + { + if (i > 0) c = (uint32_t)i; else /* Positive is a data character */ + { + escape = -i; /* Else return a special escape */ + if (cb != NULL && (escape == ESC_P || escape == ESC_p || escape == ESC_X)) + cb->external_flags |= PCRE2_HASBKPORX; /* Note \P, \p, or \X */ + + /* Perl supports \N{name} for character names and \N{U+dddd} for numerical + Unicode code points, as well as plain \N for "not newline". PCRE does not + support \N{name}. However, it does support quantification such as \N{2,3}, + so if \N{ is not followed by U+dddd we check for a quantifier. */ + + if (escape == ESC_N && ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET) + { + PCRE2_SPTR p = ptr + 1; + + /* \N{U+ can be handled by the \x{ code. However, this construction is + not valid in EBCDIC environments because it specifies a Unicode + character, not a codepoint in the local code. For example \N{U+0041} + must be "A" in all environments. Also, in Perl, \N{U+ forces Unicode + casing semantics for the entire pattern, so allow it only in UTF (i.e. + Unicode) mode. */ + + if (ptrend - p > 1 && *p == CHAR_U && p[1] == CHAR_PLUS) + { +#ifdef EBCDIC + *errorcodeptr = ERR93; +#else + if (utf) + { + ptr = p + 1; + escape = 0; /* Not a fancy escape after all */ + goto COME_FROM_NU; + } + else *errorcodeptr = ERR93; +#endif + } + + /* Give an error if what follows is not a quantifier, but don't override + an error set by the quantifier reader (e.g. number overflow). */ + + else + { + if (!read_repeat_counts(&p, ptrend, NULL, NULL, errorcodeptr) && + *errorcodeptr == 0) + *errorcodeptr = ERR37; + } + } + } + } + +/* 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 + { + PCRE2_SPTR oldptr; + BOOL overflow; + 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 + error. */ + + case CHAR_F: + case CHAR_l: + case CHAR_L: + *errorcodeptr = ERR37; + break; + + /* \u is unrecognized when PCRE2_ALT_BSUX is not set. When it is treated + specially, \u must be followed by four hex digits. Otherwise it is a + lowercase u letter. */ + + case CHAR_u: + if ((options & PCRE2_ALT_BSUX) == 0) *errorcodeptr = ERR37; else + { + uint32_t xc; + if (ptrend - ptr < 4) break; /* Less than 4 chars */ + if ((cc = XDIGIT(ptr[0])) == 0xff) break; /* Not a hex digit */ + if ((xc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ + cc = (cc << 4) | xc; + if ((xc = XDIGIT(ptr[2])) == 0xff) break; /* Not a hex digit */ + cc = (cc << 4) | xc; + if ((xc = XDIGIT(ptr[3])) == 0xff) break; /* Not a hex digit */ + c = (cc << 4) | xc; + ptr += 4; + if (utf) + { + if (c > 0x10ffffU) *errorcodeptr = ERR77; + else + if (c >= 0xd800 && c <= 0xdfff && + (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0) + *errorcodeptr = ERR73; + } + else if (c > MAX_NON_UTF_CHAR) *errorcodeptr = ERR77; + } + break; + + /* \U is unrecognized unless PCRE2_ALT_BSUX is set, in which case it is an + upper case letter. */ + + case CHAR_U: + if ((options & PCRE2_ALT_BSUX) == 0) *errorcodeptr = ERR37; + break; + + /* In a character class, \g is just a literal "g". Outside a character + class, \g must be followed by one of a number of specific things: + + (1) A number, either plain or braced. If positive, it is an absolute + backreference. If negative, it is a relative backreference. This is a Perl + 5.10 feature. + + (2) Perl 5.10 also supports \g{name} as a reference to a named group. This + is part of Perl's movement towards a unified syntax for back references. As + this is synonymous with \k{name}, we fudge it up by pretending it really + was \k{name}. + + (3) For Oniguruma compatibility we also support \g followed by a name or a + number either in angle brackets or in single quotes. However, these are + (possibly recursive) subroutine calls, _not_ backreferences. We return + the ESC_g code. + + Summary: Return a negative number for a numerical back reference, ESC_k for + a named back reference, and ESC_g for a named or numbered subroutine call. + */ + + case CHAR_g: + if (isclass) break; + + if (ptr >= ptrend) + { + *errorcodeptr = ERR57; + break; + } + + if (*ptr == CHAR_LESS_THAN_SIGN || *ptr == CHAR_APOSTROPHE) + { + escape = ESC_g; + break; + } + + /* If there is a brace delimiter, try to read a numerical reference. If + there isn't one, assume we have a name and treat it as \k. */ + + if (*ptr == CHAR_LEFT_CURLY_BRACKET) + { + PCRE2_SPTR p = ptr + 1; + if (!read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s, + errorcodeptr)) + { + if (*errorcodeptr == 0) escape = ESC_k; /* No number found */ + break; + } + if (p >= ptrend || *p != CHAR_RIGHT_CURLY_BRACKET) + { + *errorcodeptr = ERR57; + break; + } + ptr = p + 1; + } + + /* Read an undelimited number */ + + else + { + if (!read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s, + errorcodeptr)) + { + if (*errorcodeptr == 0) *errorcodeptr = ERR57; /* No number found */ + break; + } + } + + if (s <= 0) + { + *errorcodeptr = ERR15; + break; + } + + escape = -s; + break; + + /* The handling of escape sequences consisting of a string of digits + starting with one that is not zero is not straightforward. Perl has changed + over the years. Nowadays \g{} for backreferences and \o{} for octal are + recommended to avoid the ambiguities in the old syntax. + + Outside a character class, the digits are read as a decimal number. If the + 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). + + Inside a character class, \ followed by a digit is always either a literal + 8 or 9 or an octal number. */ + + 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: + + if (!isclass) + { + oldptr = ptr; + ptr--; /* Back to the digit */ + if (!read_number(&ptr, ptrend, -1, INT_MAX/10 - 1, ERR61, &s, + errorcodeptr)) + break; + + /* \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[-1] >= CHAR_8 || s <= (int)cb->bracount) + { + if (s > (int)MAX_GROUP_NUMBER) *errorcodeptr = ERR61; + else escape = -s; /* Indicates a back reference */ + break; + } + ptr = oldptr; /* Put the pointer back and fall through */ + } + + /* Handle a digit following \ when the number is not a back reference, or + we are within a character class. If the first digit is 8 or 9, Perl used to + generate a binary zero and then treat the digit as a following literal. At + least by Perl 5.18 this changed so as not to insert the binary zero. */ + + if (c >= CHAR_8) break; + + /* Fall through */ + + /* \0 always starts an octal number, but we may drop through to here with a + larger first octal digit. The original code used just to take the least + significant 8 bits of octal numbers (I think this is what early Perls used + to do). Nowadays we allow for larger numbers in UTF-8 mode and 16-bit mode, + but no more than 3 octal digits. */ + + case CHAR_0: + c -= CHAR_0; + while(i++ < 2 && ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) + c = c * 8 + *ptr++ - CHAR_0; +#if PCRE2_CODE_UNIT_WIDTH == 8 + if (!utf && c > 0xff) *errorcodeptr = ERR51; +#endif + break; + + /* \o is a relatively new Perl feature, supporting a more general way of + specifying character codes in octal. The only supported form is \o{ddd}. */ + + case CHAR_o: + if (ptr >= ptrend || *ptr++ != CHAR_LEFT_CURLY_BRACKET) + { + ptr--; + *errorcodeptr = ERR55; + } + else if (ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET) + *errorcodeptr = ERR78; + else + { + c = 0; + overflow = FALSE; + while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) + { + cc = *ptr++; + if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */ +#if PCRE2_CODE_UNIT_WIDTH == 32 + if (c >= 0x20000000l) { overflow = TRUE; break; } +#endif + 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 + if (c > (utf ? 0x10ffffU : 0xffffU)) { overflow = TRUE; break; } +#elif PCRE2_CODE_UNIT_WIDTH == 32 + if (utf && c > 0x10ffffU) { overflow = TRUE; break; } +#endif + } + if (overflow) + { + while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) ptr++; + *errorcodeptr = ERR34; + } + else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET) + { + if (utf && c >= 0xd800 && c <= 0xdfff && (cb == NULL || + (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)) + { + ptr--; + *errorcodeptr = ERR73; + } + } + else + { + ptr--; + *errorcodeptr = ERR64; + } + } + break; + + /* \x is complicated. When PCRE2_ALT_BSUX is set, \x must be followed by + two hexadecimal digits. Otherwise it is a lowercase x letter. */ + + case CHAR_x: + if ((options & PCRE2_ALT_BSUX) != 0) + { + uint32_t xc; + if (ptrend - ptr < 2) break; /* Less than 2 characters */ + if ((cc = XDIGIT(ptr[0])) == 0xff) break; /* Not a hex digit */ + if ((xc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */ + c = (cc << 4) | xc; + ptr += 2; + } /* End PCRE2_ALT_BSUX handling */ + + /* Handle \x in Perl's style. \x{ddd} is a character number which can be + greater than 0xff in UTF-8 or non-8bit mode, but only if the ddd are hex + digits. If not, { used to be treated as a data character. However, Perl + seems to read hex digits up to the first non-such, and ignore the rest, so + that, for example \x{zz} matches a binary zero. This seems crazy, so PCRE + now gives an error. */ + + else + { + if (ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET) + { +#ifndef EBCDIC + COME_FROM_NU: +#endif + if (++ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET) + { + *errorcodeptr = ERR78; + break; + } + c = 0; + overflow = FALSE; + + while (ptr < ptrend && (cc = XDIGIT(*ptr)) != 0xff) + { + ptr++; + if (c == 0 && cc == 0) continue; /* Leading zeroes */ +#if PCRE2_CODE_UNIT_WIDTH == 32 + if (c >= 0x10000000l) { overflow = TRUE; break; } +#endif + c = (c << 4) | cc; + if ((utf && c > 0x10ffffU) || (!utf && c > MAX_NON_UTF_CHAR)) + { + overflow = TRUE; + break; + } + } + + if (overflow) + { + while (ptr < ptrend && XDIGIT(*ptr) != 0xff) ptr++; + *errorcodeptr = ERR34; + } + else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET) + { + if (utf && c >= 0xd800 && c <= 0xdfff && (cb == NULL || + (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)) + { + ptr--; + *errorcodeptr = ERR73; + } + } + + /* If the sequence of hex digits does not end with '}', give an error. + We used just to recognize this construct and fall through to the normal + \x handling, but nowadays Perl gives an error, which seems much more + sensible, so we do too. */ + + else + { + ptr--; + *errorcodeptr = ERR67; + } + } /* End of \x{} processing */ + + /* Read a up to two hex digits after \x */ + + else + { + c = 0; + if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */ + ptr++; + c = cc; + if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */ + ptr++; + c = (c << 4) | cc; + } /* End of \xdd handling */ + } /* End of Perl-style \x handling */ + break; + + /* The handling of \c is different in ASCII and EBCDIC environments. In an + ASCII (or Unicode) environment, an error is given if the character + following \c is not a printable ASCII character. Otherwise, the following + character is upper-cased if it is a letter, and after that the 0x40 bit is + flipped. The result is the value of the escape. + + In an EBCDIC environment the handling of \c is compatible with the + specification in the perlebcdic document. The following character must be + a letter or one of small number of special characters. These provide a + means of defining the character values 0-31. + + For testing the EBCDIC handling of \c in an ASCII environment, recognize + the EBCDIC value of 'c' explicitly. */ + +#if defined EBCDIC && 'a' != 0x81 + case 0x83: +#else + case CHAR_c: +#endif + if (ptr >= ptrend) + { + *errorcodeptr = ERR2; + break; + } + c = *ptr; + if (c >= CHAR_a && c <= CHAR_z) c = UPPER_CASE(c); + + /* Handle \c in an ASCII/Unicode environment. */ + +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (c < 32 || c > 126) /* Excludes all non-printable ASCII */ + { + *errorcodeptr = ERR68; + break; + } + c ^= 0x40; + + /* Handle \c in an EBCDIC environment. The special case \c? is converted to + 255 (0xff) or 95 (0x5f) if other character suggest we are using th POSIX-BC + encoding. (This is the way Perl indicates that it handles \c?.) The other + valid sequences correspond to a list of specific characters. */ + +#else + if (c == CHAR_QUESTION_MARK) + c = ('\\' == 188 && '`' == 74)? 0x5f : 0xff; + else + { + for (i = 0; i < 32; i++) + { + if (c == ebcdic_escape_c[i]) break; + } + if (i < 32) c = i; else *errorcodeptr = ERR68; + } +#endif /* EBCDIC */ + + ptr++; + break; + + /* Any other alphanumeric following \ is an error. Perl gives an error only + if in warning mode, but PCRE doesn't have a warning mode. */ + + default: + *errorcodeptr = ERR3; + *ptrptr = ptr - 1; /* Point to the character at fault */ + return 0; + } + } + +/* Set the pointer to the next character before returning. */ + +*ptrptr = ptr; +*chptr = c; +return escape; +} + + + +#ifdef SUPPORT_UNICODE +/************************************************* +* Handle \P and \p * +*************************************************/ + +/* This function is called after \P or \p has been encountered, provided that +PCRE2 is compiled with support for UTF and Unicode properties. On entry, the +contents of ptrptr are pointing after the P or p. On exit, it is left pointing +after the final code unit of the escape sequence. + +Arguments: + ptrptr the pattern position pointer + negptr a boolean that is set TRUE for negation else FALSE + ptypeptr an unsigned int that is set to the type value + pdataptr an unsigned int that is set to the detailed property value + errorcodeptr the error code variable + cb the compile data + +Returns: TRUE if the type value was found, or FALSE for an invalid type +*/ + +static BOOL +get_ucp(PCRE2_SPTR *ptrptr, BOOL *negptr, uint16_t *ptypeptr, + uint16_t *pdataptr, int *errorcodeptr, compile_block *cb) +{ +PCRE2_UCHAR c; +PCRE2_SIZE i, bot, top; +PCRE2_SPTR ptr = *ptrptr; +PCRE2_UCHAR name[32]; + +if (ptr >= cb->end_pattern) goto ERROR_RETURN; +c = *ptr++; +*negptr = FALSE; + +/* \P or \p can be followed by a name in {}, optionally preceded by ^ for +negation. */ + +if (c == CHAR_LEFT_CURLY_BRACKET) + { + if (ptr >= cb->end_pattern) goto ERROR_RETURN; + if (*ptr == CHAR_CIRCUMFLEX_ACCENT) + { + *negptr = TRUE; + ptr++; + } + for (i = 0; i < (int)(sizeof(name) / sizeof(PCRE2_UCHAR)) - 1; i++) + { + if (ptr >= cb->end_pattern) goto ERROR_RETURN; + c = *ptr++; + if (c == CHAR_NUL) goto ERROR_RETURN; + if (c == CHAR_RIGHT_CURLY_BRACKET) break; + name[i] = c; + } + if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN; + name[i] = 0; + } + +/* Otherwise there is just one following character, which must be an ASCII +letter. */ + +else if (MAX_255(c) && (cb->ctypes[c] & ctype_letter) != 0) + { + name[0] = c; + name[1] = 0; + } +else goto ERROR_RETURN; + +*ptrptr = ptr; + +/* Search for a recognized property name using binary chop. */ + +bot = 0; +top = PRIV(utt_size); + +while (bot < top) + { + int r; + i = (bot + top) >> 1; + r = PRIV(strcmp_c8)(name, PRIV(utt_names) + PRIV(utt)[i].name_offset); + if (r == 0) + { + *ptypeptr = PRIV(utt)[i].type; + *pdataptr = PRIV(utt)[i].value; + return TRUE; + } + if (r > 0) bot = i + 1; else top = i; + } +*errorcodeptr = ERR47; /* Unrecognized name */ +return FALSE; + +ERROR_RETURN: /* Malformed \P or \p */ +*errorcodeptr = ERR46; +*ptrptr = ptr; +return FALSE; +} +#endif + + + +/************************************************* +* Check for POSIX class syntax * +*************************************************/ + +/* This function is called when the sequence "[:" or "[." or "[=" is +encountered in a character class. It checks whether this is followed by a +sequence of characters terminated by a matching ":]" or ".]" or "=]". If we +reach an unescaped ']' without the special preceding character, return FALSE. + +Originally, this function only recognized a sequence of letters between the +terminators, but it seems that Perl recognizes any sequence of characters, +though of course unknown POSIX names are subsequently rejected. Perl gives an +"Unknown POSIX class" error for [:f\oo:] for example, where previously PCRE +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 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. 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 +[:a[:abc]b][b:] it gives unknown POSIX class "[:abc]b][b:]", which does not +seem right at all. PCRE does not allow closing square brackets in POSIX class +names. + +Arguments: + ptr pointer to the character after the initial [ (colon, dot, equals) + ptrend pointer to the end of the pattern + endptr where to return a pointer to the terminating ':', '.', or '=' + +Returns: TRUE or FALSE +*/ + +static BOOL +check_posix_syntax(PCRE2_SPTR ptr, PCRE2_SPTR ptrend, PCRE2_SPTR *endptr) +{ +PCRE2_UCHAR terminator; /* Don't combine these lines; the Solaris cc */ +terminator = *ptr++; /* compiler warns about "non-constant" initializer. */ + +for (; ptrend - ptr >= 2; ptr++) + { + 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) + { + *endptr = ptr; + return TRUE; + } + } + +return FALSE; +} + + + +/************************************************* +* Check POSIX class name * +*************************************************/ + +/* This function is called to check the name given in a POSIX-style class entry +such as [:alnum:]. + +Arguments: + ptr points to the first letter + len the length of the name + +Returns: a value representing the name, or -1 if unknown +*/ + +static int +check_posix_name(PCRE2_SPTR ptr, int len) +{ +const char *pn = posix_names; +int yield = 0; +while (posix_name_lengths[yield] != 0) + { + if (len == posix_name_lengths[yield] && + PRIV(strncmp_c8)(ptr, pn, (unsigned int)len) == 0) return yield; + pn += posix_name_lengths[yield] + 1; + yield++; + } +return -1; +} + + + +/************************************************* +* Read a subpattern or VERB name * +*************************************************/ + +/* This function is called from parse_regex() below whenever it needs to read +the name of a subpattern or a (*VERB). The initial pointer must be to the +character before the name. If that character is '*' we are reading a verb name. +The pointer is updated to point after the name, for a VERB, or after tha name's +terminator for a subpattern name. Returning both the offset and the name +pointer is redundant information, but some callers use one and some the other, +so it is simplest just to return both. + +Arguments: + ptrptr points to the character pointer variable + ptrend points to the end of the input string + terminator the terminator of a subpattern name must be this + offsetptr where to put the offset from the start of the pattern + nameptr where to put a pointer to the name in the input + namelenptr where to put the length of the name + errcodeptr where to put an error code + cb pointer to the compile data block + +Returns: TRUE if a name was read + FALSE otherwise, with error code set +*/ + +static BOOL +read_name(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t terminator, + PCRE2_SIZE *offsetptr, PCRE2_SPTR *nameptr, uint32_t *namelenptr, + int *errorcodeptr, compile_block *cb) +{ +PCRE2_SPTR ptr = *ptrptr; +BOOL is_verb = (*ptr == CHAR_ASTERISK); +uint32_t namelen = 0; +uint32_t ctype = is_verb? ctype_letter : ctype_word; + +if (++ptr >= ptrend) + { + *errorcodeptr = is_verb? ERR60: /* Verb not recognized or malformed */ + ERR62; /* Subpattern name expected */ + goto FAILED; + } + +*nameptr = ptr; +*offsetptr = (PCRE2_SIZE)(ptr - cb->start_pattern); + +if (IS_DIGIT(*ptr)) + { + *errorcodeptr = ERR44; /* Group name must not start with digit */ + goto FAILED; + } + +while (ptr < ptrend && MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype) != 0) + { + ptr++; + namelen++; + if (namelen > MAX_NAME_SIZE) + { + *errorcodeptr = ERR48; + goto FAILED; + } + } + +/* Subpattern names must not be empty, and their terminator is checked here. +(What follows a verb name is checked separately.) */ + +if (!is_verb) + { + if (namelen == 0) + { + *errorcodeptr = ERR62; /* Subpattern name expected */ + goto FAILED; + } + if (ptr >= ptrend || *ptr != (PCRE2_UCHAR)terminator) + { + *errorcodeptr = ERR42; + goto FAILED; + } + ptr++; + } + +*namelenptr = namelen; +*ptrptr = ptr; +return TRUE; + +FAILED: +*ptrptr = ptr; +return FALSE; +} + + + +/************************************************* +* Manage callouts at start of cycle * +*************************************************/ + +/* At the start of a new item in parse_regex() we are able to record the +details of the previous item in a prior callout, and also to set up an +automatic callout if enabled. Avoid having two adjacent automatic callouts, +which would otherwise happen for items such as \Q that contribute nothing to +the parsed pattern. + +Arguments: + ptr current pattern pointer + pcalloutptr points to a pointer to previous callout, or NULL + auto_callout TRUE if auto_callouts are enabled + parsed_pattern the parsed pattern pointer + cb compile block + +Returns: possibly updated parsed_pattern pointer. +*/ + +static uint32_t * +manage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, BOOL auto_callout, + uint32_t *parsed_pattern, compile_block *cb) +{ +uint32_t *previous_callout = *pcalloutptr; + +if (previous_callout != NULL) previous_callout[2] = (uint32_t)(ptr - + cb->start_pattern - (PCRE2_SIZE)previous_callout[1]); + +if (!auto_callout) previous_callout = NULL; else + { + if (previous_callout == NULL || + previous_callout != parsed_pattern - 4 || + previous_callout[3] != 255) + { + previous_callout = parsed_pattern; /* Set up new automatic callout */ + parsed_pattern += 4; + previous_callout[0] = META_CALLOUT_NUMBER; + previous_callout[2] = 0; + previous_callout[3] = 255; + } + previous_callout[1] = (uint32_t)(ptr - cb->start_pattern); + } + +*pcalloutptr = previous_callout; +return parsed_pattern; +} + + + +/************************************************* +* Parse regex and identify named groups * +*************************************************/ + +/* This function is called first of all. It scans the pattern and does two +things: (1) It identifies capturing groups and makes a table of named capturing +groups so that information about them is fully available to both the compiling +scans. (2) It writes a parsed version of the pattern with comments omitted and +escapes processed into the parsed_pattern vector. + +Arguments: + ptr points to the start of the pattern + options compiling dynamic options (may change during the scan) + has_lookbehind points to a boolean, set TRUE if a lookbehind is found + cb pointer to the compile data block + +Returns: zero on success or a non-zero error code, with the + error offset placed in the cb field +*/ + +/* A structure and some flags for dealing with nested groups. */ + +typedef struct nest_save { + uint16_t nest_depth; + uint16_t reset_group; + uint16_t max_group; + uint16_t flags; + uint32_t options; +} nest_save; + +#define NSF_RESET 0x0001u +#define NSF_CONDASSERT 0x0002u + +/* Options that are changeable within the pattern must be tracked during +parsing. Some (e.g. PCRE2_EXTENDED) are implemented entirely during parsing, +but all must be tracked so that META_OPTIONS items set the correct values for +the main compiling phase. */ + +#define PARSE_TRACKED_OPTIONS (PCRE2_CASELESS|PCRE2_DOTALL|PCRE2_DUPNAMES| \ + PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE| \ + PCRE2_UNGREEDY) + +/* States used for analyzing ranges in character classes. The two OK values +must be last. */ + +enum { RANGE_NO, RANGE_STARTED, RANGE_OK_ESCAPED, RANGE_OK_LITERAL }; + +/* Only in 32-bit mode can there be literals > META_END. A macros encapsulates +the storing of literal values in the parsed pattern. */ + +#if PCRE2_CODE_UNIT_WIDTH == 32 +#define PARSED_LITERAL(c, p) \ + { \ + if (c >= META_END) *p++ = META_BIGVALUE; \ + *p++ = c; \ + okquantifier = TRUE; \ + } +#else +#define PARSED_LITERAL(c, p) *p++ = c; okquantifier = TRUE; +#endif + +/* Here's the actual function. */ + +static int parse_regex(PCRE2_SPTR ptr, uint32_t options, BOOL *has_lookbehind, + compile_block *cb) +{ +uint32_t c; +uint32_t delimiter; +uint32_t namelen; +uint32_t class_range_state; +uint32_t *verblengthptr = NULL; /* Value avoids compiler warning */ +uint32_t *previous_callout = NULL; +uint32_t *parsed_pattern = cb->parsed_pattern; +uint32_t *parsed_pattern_end = cb->parsed_pattern_end; +uint32_t meta_quantifier = 0; +uint32_t add_after_mark = 0; +uint16_t nest_depth = 0; +int after_manual_callout = 0; +int expect_cond_assert = 0; +int errorcode = 0; +int escape; +int i; +BOOL inescq = FALSE; +BOOL inverbname = FALSE; +BOOL utf = (options & PCRE2_UTF) != 0; +BOOL auto_callout = (options & PCRE2_AUTO_CALLOUT) != 0; +BOOL isdupname; +BOOL negate_class; +BOOL okquantifier = FALSE; +PCRE2_SPTR thisptr; +PCRE2_SPTR name; +PCRE2_SPTR ptrend = cb->end_pattern; +PCRE2_SPTR verbnamestart = NULL; /* Value avoids compiler warning */ +named_group *ng; +nest_save *top_nest, *end_nests; + +/* Insert leading items for word and line matching (features provided for the +benefit of pcre2grep). */ + +if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_LINE) != 0) + { + *parsed_pattern++ = META_CIRCUMFLEX; + *parsed_pattern++ = META_NOCAPTURE; + } +else if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_WORD) != 0) + { + *parsed_pattern++ = META_ESCAPE + ESC_b; + *parsed_pattern++ = META_NOCAPTURE; + } + +/* If the pattern is actually a literal string, process it separately to avoid +cluttering up the main loop. */ + +if ((options & PCRE2_LITERAL) != 0) + { + while (ptr < ptrend) + { + if (parsed_pattern >= parsed_pattern_end) + { + errorcode = ERR63; /* Internal error (parsed pattern overflow) */ + goto FAILED; + } + thisptr = ptr; + GETCHARINCTEST(c, ptr); + if (auto_callout) + parsed_pattern = manage_callouts(thisptr, &previous_callout, + auto_callout, parsed_pattern, cb); + PARSED_LITERAL(c, parsed_pattern); + } + goto PARSED_END; + } + +/* Process a real regex which may contain meta-characters. */ + +top_nest = NULL; +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))); + +/* PCRE2_EXTENDED_MORE implies PCRE2_EXTENDED */ + +if ((options & PCRE2_EXTENDED_MORE) != 0) options |= PCRE2_EXTENDED; + +/* Now scan the pattern */ + +while (ptr < ptrend) + { + int prev_expect_cond_assert; + uint32_t min_repeat, max_repeat; + uint32_t set, unset, *optset; + uint32_t terminator; + uint32_t prev_meta_quantifier; + BOOL prev_okquantifier; + PCRE2_SPTR tempptr; + PCRE2_SIZE offset; + + if (parsed_pattern >= parsed_pattern_end) + { + errorcode = ERR63; /* Internal error (parsed pattern overflow) */ + goto FAILED; + } + + if (nest_depth > cb->cx->parens_nest_limit) + { + errorcode = ERR19; + goto FAILED; /* Parentheses too deeply nested */ + } + + /* Get next input character, save its position for callout handling. */ + + thisptr = ptr; + GETCHARINCTEST(c, ptr); + + /* Copy quoted literals until \E, allowing for the possibility of automatic + callouts, except when processing a (*VERB) "name". */ + + if (inescq) + { + if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E) + { + inescq = FALSE; + ptr++; /* Skip E */ + } + else + { + if (expect_cond_assert > 0) /* A literal is not allowed if we are */ + { /* expecting a conditional assertion, */ + ptr--; /* but an empty \Q\E sequence is OK. */ + errorcode = ERR28; + goto FAILED; + } + if (!inverbname && after_manual_callout-- <= 0) + parsed_pattern = manage_callouts(thisptr, &previous_callout, + auto_callout, parsed_pattern, cb); + PARSED_LITERAL(c, parsed_pattern); + meta_quantifier = 0; + } + continue; /* Next character */ + } + + /* If we are processing the "name" part of a (*VERB:NAME) item, all + characters up to the closing parenthesis are literals except when + PCRE2_ALT_VERBNAMES is set. That causes backslash interpretation, but only \Q + and \E and escaped characters are allowed (no character types such as \d). If + PCRE2_EXTENDED is also set, we must ignore white space and # comments. Do + this by not entering the special (*VERB:NAME) processing - they are then + picked up below. Note that c is a character, not a code unit, so we must not + use MAX_255 to test its size because MAX_255 tests code units and is assumed + TRUE in 8-bit mode. */ + + if (inverbname && + ( + /* EITHER: not both options set */ + ((options & (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) != + (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) || +#ifdef SUPPORT_UNICODE + /* OR: character > 255 AND not Unicode Pattern White Space */ + (c > 255 && (c|1) != 0x200f && (c|1) != 0x2029) || +#endif + /* OR: not a # comment or isspace() white space */ + (c < 256 && c != CHAR_NUMBER_SIGN && (cb->ctypes[c] & ctype_space) == 0 +#ifdef SUPPORT_UNICODE + /* and not CHAR_NEL when Unicode is supported */ + && c != CHAR_NEL +#endif + ))) + { + PCRE2_SIZE verbnamelength; + + switch(c) + { + default: + PARSED_LITERAL(c, parsed_pattern); + break; + + case CHAR_RIGHT_PARENTHESIS: + inverbname = FALSE; + okquantifier = FALSE; /* Was probably set by literals */ + /* This is the length in characters */ + verbnamelength = (PCRE2_SIZE)(parsed_pattern - verblengthptr - 1); + /* But the limit on the length is in code units */ + if (ptr - verbnamestart - 1 > (int)MAX_MARK) + { + ptr--; + errorcode = ERR76; + goto FAILED; + } + *verblengthptr = (uint32_t)verbnamelength; + + /* If this name was on a verb such as (*ACCEPT) which does not continue, + a (*MARK) was generated for the name. We now add the original verb as the + next item. */ + + if (add_after_mark != 0) + { + *parsed_pattern++ = add_after_mark; + add_after_mark = 0; + } + break; + + case CHAR_BACKSLASH: + if ((options & PCRE2_ALT_VERBNAMES) != 0) + { + escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options, + FALSE, cb); + if (errorcode != 0) goto FAILED; + } + else escape = 0; /* Treat all as literal */ + + switch(escape) + { + case 0: + PARSED_LITERAL(c, parsed_pattern); + break; + + case ESC_Q: + inescq = TRUE; + break; + + case ESC_E: /* Ignore */ + break; + + default: + errorcode = ERR40; /* Invalid in verb name */ + goto FAILED; + } + } + continue; /* Next character in pattern */ + } + + /* Not a verb name character. At this point we must process everything that + must not change the quantification state. This is mainly comments, but we + handle \Q and \E here as well, so that an item such as A\Q\E+ is treated as + A+, as in Perl. An isolated \E is ignored. */ + + if (c == CHAR_BACKSLASH && ptr < ptrend) + { + if (*ptr == CHAR_Q || *ptr == CHAR_E) + { + inescq = *ptr == CHAR_Q; + ptr++; + continue; + } + } + + /* Skip over whitespace and # comments in extended mode. Note that c is a + character, not a code unit, so we must not use MAX_255 to test its size + because MAX_255 tests code units and is assumed TRUE in 8-bit mode. The + whitespace characters are those designated as "Pattern White Space" by + Unicode, which are the isspace() characters plus CHAR_NEL (newline), which is + U+0085 in Unicode, plus U+200E, U+200F, U+2028, and U+2029. These are a + subset of space characters that match \h and \v. */ + + if ((options & PCRE2_EXTENDED) != 0) + { + if (c < 256 && (cb->ctypes[c] & ctype_space) != 0) continue; +#ifdef SUPPORT_UNICODE + if (c == CHAR_NEL || (c|1) == 0x200f || (c|1) == 0x2029) continue; +#endif + if (c == CHAR_NUMBER_SIGN) + { + while (ptr < ptrend) + { + 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) FORWARDCHARTEST(ptr, ptrend); +#endif + } + continue; /* Next character in pattern */ + } + } + + /* Skip over bracketed comments */ + + if (c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 2 && + ptr[0] == CHAR_QUESTION_MARK && ptr[1] == CHAR_NUMBER_SIGN) + { + while (++ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS); + if (ptr >= ptrend) + { + errorcode = ERR18; /* A special error for missing ) in a comment */ + goto FAILED; /* to make it easier to debug. */ + } + ptr++; + continue; /* Next character in pattern */ + } + + /* If the next item is not a quantifier, fill in length of any previous + callout and create an auto callout if required. */ + + if (c != CHAR_ASTERISK && c != CHAR_PLUS && c != CHAR_QUESTION_MARK && + (c != CHAR_LEFT_CURLY_BRACKET || + (tempptr = ptr, + !read_repeat_counts(&tempptr, ptrend, NULL, NULL, &errorcode)))) + { + if (after_manual_callout-- <= 0) + parsed_pattern = manage_callouts(thisptr, &previous_callout, auto_callout, + parsed_pattern, cb); + } + + /* If expect_cond_assert is 2, we have just passed (?( and are expecting an + assertion, possibly preceded by a callout. If the value is 1, we have just + had the callout and expect an assertion. There must be at least 3 more + characters in all cases. When expect_cond_assert is 2, we know that the + current character is an opening parenthesis, as otherwise we wouldn't be + here. However, when it is 1, we need to check, and it's easiest just to check + always. Note that expect_cond_assert may be negative, since all callouts just + decrement it. */ + + if (expect_cond_assert > 0) + { + BOOL ok = c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 3 && + ptr[0] == CHAR_QUESTION_MARK; + if (ok) switch(ptr[1]) + { + case CHAR_C: + ok = expect_cond_assert == 2; + break; + + case CHAR_EQUALS_SIGN: + case CHAR_EXCLAMATION_MARK: + break; + + case CHAR_LESS_THAN_SIGN: + ok = ptr[2] == CHAR_EQUALS_SIGN || ptr[2] == CHAR_EXCLAMATION_MARK; + break; + + default: + ok = FALSE; + } + + if (!ok) + { + ptr--; /* Adjust error offset */ + errorcode = ERR28; + goto FAILED; + } + } + + /* Remember whether we are expecting a conditional assertion, and set the + default for this item. */ + + prev_expect_cond_assert = expect_cond_assert; + expect_cond_assert = 0; + + /* Remember quantification status for the previous significant item, then set + default for this item. */ + + prev_okquantifier = okquantifier; + prev_meta_quantifier = meta_quantifier; + okquantifier = FALSE; + meta_quantifier = 0; + + /* If the previous significant item was a quantifier, adjust the parsed code + if there is a following modifier. The base meta value is always followed by + the PLUS and QUERY values, in that order. We do this here rather than after + reading a quantifier so that intervening comments and /x whitespace can be + ignored without having to replicate code. */ + + if (prev_meta_quantifier != 0 && (c == CHAR_QUESTION_MARK || c == CHAR_PLUS)) + { + parsed_pattern[(prev_meta_quantifier == META_MINMAX)? -3 : -1] = + prev_meta_quantifier + ((c == CHAR_QUESTION_MARK)? + 0x00020000u : 0x00010000u); + continue; /* Next character in pattern */ + } + + + /* Process the next item in the main part of a pattern. */ + + switch(c) + { + default: /* Non-special character */ + PARSED_LITERAL(c, parsed_pattern); + break; + + + /* ---- Escape sequence ---- */ + + case CHAR_BACKSLASH: + tempptr = ptr; + escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options, + FALSE, cb); + if (errorcode != 0) + { + ESCAPE_FAILED: + if ((cb->cx->extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0) + goto FAILED; + ptr = tempptr; + if (ptr >= ptrend) c = CHAR_BACKSLASH; else + { + GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ + } + escape = 0; /* Treat as literal character */ + } + + /* The escape was a data escape or literal character. */ + + if (escape == 0) + { + PARSED_LITERAL(c, parsed_pattern); + } + + /* The escape was a back (or forward) reference. We keep the offset in + order to give a more useful diagnostic for a bad forward reference. For + references to groups numbered less than 10 we can't use more than two items + in parsed_pattern because they may be just two characters in the input (and + in a 64-bit world an offset may need two elements). So for them, the offset + of the first occurrent is held in a special vector. */ + + else if (escape < 0) + { + offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 1); + escape = -escape; + *parsed_pattern++ = META_BACKREF | (uint32_t)escape; + if (escape < 10) + { + if (cb->small_ref_offset[escape] == PCRE2_UNSET) + cb->small_ref_offset[escape] = offset; + } + else + { + PUTOFFSET(offset, parsed_pattern); + } + okquantifier = TRUE; + } + + /* The escape was a character class such as \d etc. or other special + escape indicator such as \A or \X. Most of them generate just a single + parsed item, but \P and \p are followed by a 16-bit type and a 16-bit + value. They are supported only when Unicode is available. The type and + value are packed into a single 32-bit value so that the whole sequences + uses only two elements in the parsed_vector. This is because the same + coding is used if \d (for example) is turned into \p{Nd} when PCRE2_UCP is + set. + + There are also some cases where the escape sequence is followed by a name: + \k{name}, \k, and \k'name' are backreferences by name, and \g + and \g'name' are subroutine calls by name; \g{name} is a synonym for + \k{name}. Note that \g and \g'number' are handled by check_escape() + and returned as a negative value (handled above). A name is coded as an + offset into the pattern and a length. */ + + else switch (escape) + { + case ESC_C: +#ifdef NEVER_BACKSLASH_C + errorcode = ERR85; + goto ESCAPE_FAILED; +#else + if ((options & PCRE2_NEVER_BACKSLASH_C) != 0) + { + errorcode = ERR83; + goto ESCAPE_FAILED; + } +#endif + okquantifier = TRUE; + *parsed_pattern++ = META_ESCAPE + escape; + break; + + case ESC_X: +#ifndef SUPPORT_UNICODE + errorcode = ERR45; /* Supported only with Unicode support */ + goto ESCAPE_FAILED; +#endif + case ESC_H: + case ESC_h: + case ESC_N: + case ESC_R: + case ESC_V: + case ESC_v: + okquantifier = TRUE; + *parsed_pattern++ = META_ESCAPE + escape; + break; + + default: /* \A, \B, \b, \G, \K, \Z, \z cannot be quantified. */ + *parsed_pattern++ = META_ESCAPE + escape; + break; + + /* Escapes that change in UCP mode. Note that PCRE2_UCP will never be set + without Unicode support because it is checked when pcre2_compile() is + called. */ + + case ESC_d: + case ESC_D: + case ESC_s: + case ESC_S: + case ESC_w: + case ESC_W: + okquantifier = TRUE; + if ((options & PCRE2_UCP) == 0) + { + *parsed_pattern++ = META_ESCAPE + escape; + } + else + { + *parsed_pattern++ = META_ESCAPE + + ((escape == ESC_d || escape == ESC_s || escape == ESC_w)? + ESC_p : ESC_P); + switch(escape) + { + case ESC_d: + case ESC_D: + *parsed_pattern++ = (PT_PC << 16) | ucp_Nd; + break; + + case ESC_s: + case ESC_S: + *parsed_pattern++ = PT_SPACE << 16; + break; + + case ESC_w: + case ESC_W: + *parsed_pattern++ = PT_WORD << 16; + break; + } + } + break; + + /* Unicode property matching */ + + case ESC_P: + case ESC_p: +#ifdef SUPPORT_UNICODE + { + BOOL negated; + uint16_t ptype = 0, pdata = 0; + if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb)) + goto ESCAPE_FAILED; + if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P; + *parsed_pattern++ = META_ESCAPE + escape; + *parsed_pattern++ = (ptype << 16) | pdata; + okquantifier = TRUE; + } +#else + errorcode = ERR45; + goto ESCAPE_FAILED; +#endif + break; /* End \P and \p */ + + /* When \g is used with quotes or angle brackets as delimiters, it is a + numerical or named subroutine call, and control comes here. When used + with brace delimiters it is a numberical back reference and does not come + here because check_escape() returns it directly as a reference. \k is + always a named back reference. */ + + case ESC_g: + case ESC_k: + if (ptr >= ptrend || (*ptr != CHAR_LEFT_CURLY_BRACKET && + *ptr != CHAR_LESS_THAN_SIGN && *ptr != CHAR_APOSTROPHE)) + { + errorcode = (escape == ESC_g)? ERR57 : ERR69; + goto ESCAPE_FAILED; + } + terminator = (*ptr == CHAR_LESS_THAN_SIGN)? + CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)? + CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET; + + /* For a non-braced \g, check for a numerical recursion. */ + + if (escape == ESC_g && terminator != CHAR_RIGHT_CURLY_BRACKET) + { + PCRE2_SPTR p = ptr + 1; + + if (read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i, + &errorcode)) + { + if (p >= ptrend || *p != terminator) + { + errorcode = ERR57; + goto ESCAPE_FAILED; + } + ptr = p; + goto SET_RECURSION; + } + if (errorcode != 0) goto ESCAPE_FAILED; + } + + /* Not a numerical recursion */ + + if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen, + &errorcode, cb)) goto ESCAPE_FAILED; + + /* \k and \g when used with braces are back references, whereas \g used + with quotes or angle brackets is a recursion */ + + *parsed_pattern++ = + (escape == ESC_k || terminator == CHAR_RIGHT_CURLY_BRACKET)? + META_BACKREF_BYNAME : META_RECURSE_BYNAME; + *parsed_pattern++ = namelen; + + PUTOFFSET(offset, parsed_pattern); + okquantifier = TRUE; + break; /* End special escape processing */ + } + break; /* End escape sequence processing */ + + + /* ---- Single-character special items ---- */ + + case CHAR_CIRCUMFLEX_ACCENT: + *parsed_pattern++ = META_CIRCUMFLEX; + break; + + case CHAR_DOLLAR_SIGN: + *parsed_pattern++ = META_DOLLAR; + break; + + case CHAR_DOT: + *parsed_pattern++ = META_DOT; + okquantifier = TRUE; + break; + + + /* ---- Single-character quantifiers ---- */ + + case CHAR_ASTERISK: + meta_quantifier = META_ASTERISK; + goto CHECK_QUANTIFIER; + + case CHAR_PLUS: + meta_quantifier = META_PLUS; + goto CHECK_QUANTIFIER; + + case CHAR_QUESTION_MARK: + meta_quantifier = META_QUERY; + goto CHECK_QUANTIFIER; + + + /* ---- Potential {n,m} quantifier ---- */ + + case CHAR_LEFT_CURLY_BRACKET: + if (!read_repeat_counts(&ptr, ptrend, &min_repeat, &max_repeat, + &errorcode)) + { + if (errorcode != 0) goto FAILED; /* Error in quantifier. */ + PARSED_LITERAL(c, parsed_pattern); /* Not a quantifier */ + break; /* No more quantifier processing */ + } + meta_quantifier = META_MINMAX; + /* Fall through */ + + + /* ---- Quantifier post-processing ---- */ + + /* Check that a quantifier is allowed after the previous item. */ + + CHECK_QUANTIFIER: + if (!prev_okquantifier) + { + errorcode = ERR9; + goto FAILED_BACK; + } + + /* Now we can put the quantifier into the parsed pattern vector. At this + stage, we have only the basic quantifier. The check for a following + or ? + modifier happens at the top of the loop, after any intervening comments + have been removed. */ + + *parsed_pattern++ = meta_quantifier; + if (c == CHAR_LEFT_CURLY_BRACKET) + { + *parsed_pattern++ = min_repeat; + *parsed_pattern++ = max_repeat; + } + break; + + + /* ---- Character class ---- */ + + case CHAR_LEFT_SQUARE_BRACKET: + okquantifier = TRUE; + + /* 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. */ + + if (ptrend - ptr >= 6 && + (PRIV(strncmp_c8)(ptr, STRING_WEIRD_STARTWORD, 6) == 0 || + PRIV(strncmp_c8)(ptr, STRING_WEIRD_ENDWORD, 6) == 0)) + { + *parsed_pattern++ = META_ESCAPE + ESC_b; + + if (ptr[2] == CHAR_LESS_THAN_SIGN) + { + *parsed_pattern++ = META_LOOKAHEAD; + } + else + { + *parsed_pattern++ = META_LOOKBEHIND; + *has_lookbehind = TRUE; + + /* The offset is used only for the "non-fixed length" error; this won't + occur here, so just store zero. */ + + PUTOFFSET((PCRE2_SIZE)0, parsed_pattern); + } + + if ((options & PCRE2_UCP) == 0) + *parsed_pattern++ = META_ESCAPE + ESC_w; + else + { + *parsed_pattern++ = META_ESCAPE + ESC_p; + *parsed_pattern++ = PT_WORD << 16; + } + *parsed_pattern++ = META_KET; + ptr += 6; + break; + } + + /* 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. */ + + if (ptr < ptrend && (*ptr == CHAR_COLON || *ptr == CHAR_DOT || + *ptr == CHAR_EQUALS_SIGN) && + check_posix_syntax(ptr, ptrend, &tempptr)) + { + errorcode = (*ptr-- == CHAR_COLON)? ERR12 : ERR13; + goto FAILED; + } + + /* Process a regular character class. If the first character is '^', set + the negation flag. If the first few characters (either before or after ^) + are \Q\E or \E or space or tab in extended-more mode, we skip them too. + This makes for compatibility with Perl. */ + + negate_class = FALSE; + while (ptr < ptrend) + { + GETCHARINCTEST(c, ptr); + if (c == CHAR_BACKSLASH) + { + if (ptr < ptrend && *ptr == CHAR_E) ptr++; + else if (ptrend - ptr >= 3 && + PRIV(strncmp_c8)(ptr, STR_Q STR_BACKSLASH STR_E, 3) == 0) + ptr += 3; + else + break; + } + else if ((options & PCRE2_EXTENDED_MORE) != 0 && + (c == CHAR_SPACE || c == CHAR_HT)) /* Note: just these two */ + continue; + else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) + negate_class = TRUE; + else break; + } + + /* Now the real contents of the class; c has the first "real" character. + Empty classes are permitted only if the option is set. */ + + if (c == CHAR_RIGHT_SQUARE_BRACKET && + (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0) + { + *parsed_pattern++ = negate_class? META_CLASS_EMPTY_NOT : META_CLASS_EMPTY; + break; /* End of class processing */ + } + + /* Process a non-empty class. */ + + *parsed_pattern++ = negate_class? META_CLASS_NOT : META_CLASS; + class_range_state = RANGE_NO; + + /* 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 ranges where both values are literal; [\xC1-\xE9] is different to [A-Z] + in this respect. In order to accommodate this, we keep track of whether + character values are literal or not, and a state variable for handling + ranges. */ + + /* Loop for the contents of the class */ + + for (;;) + { + BOOL char_is_literal = TRUE; + + /* Inside \Q...\E everything is literal except \E */ + + if (inescq) + { + if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E) + { + inescq = FALSE; /* Reset literal state */ + ptr++; /* Skip the 'E' */ + goto CLASS_CONTINUE; + } + goto CLASS_LITERAL; + } + + /* Skip over space and tab (only) in extended-more mode. */ + + if ((options & PCRE2_EXTENDED_MORE) != 0 && + (c == CHAR_SPACE || c == CHAR_HT)) + goto CLASS_CONTINUE; + + /* Handle POSIX class names. Perl allows a negation extension of the + form [:^name:]. A square bracket that doesn't match the syntax is + treated as a literal. We also recognize the POSIX constructions + [.ch.] and [=ch=] ("collating elements") and fault them, as Perl + 5.6 and 5.8 do. */ + + if (c == CHAR_LEFT_SQUARE_BRACKET && + ptrend - ptr >= 3 && + (*ptr == CHAR_COLON || *ptr == CHAR_DOT || + *ptr == CHAR_EQUALS_SIGN) && + check_posix_syntax(ptr, ptrend, &tempptr)) + { + BOOL posix_negate = FALSE; + int posix_class; + + /* Perl treats a hyphen before a POSIX class as a literal, not the + start of a range. However, it gives a warning in its warning mode. PCRE + does not have a warning mode, so we give an error, because this is + likely an error on the user's part. */ + + if (class_range_state == RANGE_STARTED) + { + errorcode = ERR50; + goto FAILED; + } + + if (*ptr != CHAR_COLON) + { + errorcode = ERR13; + goto FAILED_BACK; + } + + if (*(++ptr) == CHAR_CIRCUMFLEX_ACCENT) + { + posix_negate = TRUE; + ptr++; + } + + posix_class = check_posix_name(ptr, (int)(tempptr - ptr)); + if (posix_class < 0) + { + errorcode = ERR30; + goto FAILED; + } + ptr = tempptr + 2; + + /* Perl treats a hyphen after a POSIX class as a literal, not the + start of a range. However, it gives a warning in its warning mode + unless the hyphen is the last character in the class. PCRE does not + have a warning mode, so we give an error, because this is likely an + error on the user's part. */ + + if (ptr < ptrend - 1 && *ptr == CHAR_MINUS && + ptr[1] != CHAR_RIGHT_SQUARE_BRACKET) + { + errorcode = ERR50; + goto FAILED; + } + + /* Set "a hyphen is not the start of a range" for the -] case, and also + in case the POSIX class is followed by \E or \Q\E (possibly repeated - + fuzzers do that kind of thing) and *then* a hyphen. This causes that + hyphen to be treated as a literal. I don't think it's worth setting up + special apparatus to do otherwise. */ + + class_range_state = RANGE_NO; + + /* When PCRE2_UCP is set, some of the POSIX classes are converted to + use Unicode properties \p or \P or, in one case, \h or \H. The + substitutes table has two values per class, containing the type and + value of a \p or \P item. The special cases are specified with a + negative type: a non-zero value causes \h or \H to be used, and a zero + value falls through to behave like a non-UCP POSIX class. */ + +#ifdef SUPPORT_UNICODE + if ((options & PCRE2_UCP) != 0) + { + int ptype = posix_substitutes[2*posix_class]; + int pvalue = posix_substitutes[2*posix_class + 1]; + if (ptype >= 0) + { + *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_P : ESC_p); + *parsed_pattern++ = (ptype << 16) | pvalue; + goto CLASS_CONTINUE; + } + + if (pvalue != 0) + { + *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_H : ESC_h); + goto CLASS_CONTINUE; + } + + /* Fall through */ + } +#endif /* SUPPORT_UNICODE */ + + /* Non-UCP POSIX class */ + + *parsed_pattern++ = posix_negate? META_POSIX_NEG : META_POSIX; + *parsed_pattern++ = posix_class; + } + + /* Handle potential start of range */ + + else if (c == CHAR_MINUS && class_range_state >= RANGE_OK_ESCAPED) + { + *parsed_pattern++ = (class_range_state == RANGE_OK_LITERAL)? + META_RANGE_LITERAL : META_RANGE_ESCAPED; + class_range_state = RANGE_STARTED; + } + + /* Handle a literal character */ + + else if (c != CHAR_BACKSLASH) + { + CLASS_LITERAL: + if (class_range_state == RANGE_STARTED) + { + if (c == parsed_pattern[-2]) /* Optimize one-char range */ + parsed_pattern--; + else if (parsed_pattern[-2] > c) /* Check range is in order */ + { + errorcode = ERR8; + goto FAILED_BACK; + } + else + { + if (!char_is_literal && parsed_pattern[-1] == META_RANGE_LITERAL) + parsed_pattern[-1] = META_RANGE_ESCAPED; + PARSED_LITERAL(c, parsed_pattern); + } + class_range_state = RANGE_NO; + } + else /* Potential start of range */ + { + class_range_state = char_is_literal? + RANGE_OK_LITERAL : RANGE_OK_ESCAPED; + PARSED_LITERAL(c, parsed_pattern); + } + } + + /* Handle escapes in a class */ + + else + { + tempptr = ptr; + escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, + options, TRUE, cb); + if (errorcode != 0) + { + CLASS_ESCAPE_FAILED: + if ((cb->cx->extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0) + goto FAILED; + ptr = tempptr; + if (ptr >= ptrend) c = CHAR_BACKSLASH; else + { + GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ + } + escape = 0; /* Treat as literal character */ + } + + if (escape == 0) /* Escaped character code point is in c */ + { + char_is_literal = FALSE; + goto CLASS_LITERAL; + } + + /* These three escapes do not alter the class range state. */ + + if (escape == ESC_b) + { + c = CHAR_BS; /* \b is backspace in a class */ + char_is_literal = FALSE; + goto CLASS_LITERAL; + } + + else if (escape == ESC_Q) + { + inescq = TRUE; /* Enter literal mode */ + goto CLASS_CONTINUE; + } + + else if (escape == ESC_E) /* Ignore orphan \E */ + goto CLASS_CONTINUE; + + /* The second part of a range can be a single-character escape + sequence (detected above), but not any of the other escapes. Perl + treats a hyphen as a literal in such circumstances. However, in Perl's + warning mode, a warning is given, so PCRE now faults it, as it is + almost certainly a mistake on the user's part. */ + + if (class_range_state == RANGE_STARTED) + { + errorcode = ERR50; + goto CLASS_ESCAPE_FAILED; + } + + /* Of the remaining escapes, only those that define characters are + allowed in a class. None may start a range. */ + + class_range_state = RANGE_NO; + switch(escape) + { + case ESC_N: + errorcode = ERR71; /* Not supported in a class */ + goto CLASS_ESCAPE_FAILED; + + case ESC_H: + case ESC_h: + case ESC_V: + case ESC_v: + *parsed_pattern++ = META_ESCAPE + escape; + break; + + /* These escapes are converted to Unicode property tests when + PCRE2_UCP is set. */ + + case ESC_d: + case ESC_D: + case ESC_s: + case ESC_S: + case ESC_w: + case ESC_W: + if ((options & PCRE2_UCP) == 0) + { + *parsed_pattern++ = META_ESCAPE + escape; + } + else + { + *parsed_pattern++ = META_ESCAPE + + ((escape == ESC_d || escape == ESC_s || escape == ESC_w)? + ESC_p : ESC_P); + switch(escape) + { + case ESC_d: + case ESC_D: + *parsed_pattern++ = (PT_PC << 16) | ucp_Nd; + break; + + case ESC_s: + case ESC_S: + *parsed_pattern++ = PT_SPACE << 16; + break; + + case ESC_w: + case ESC_W: + *parsed_pattern++ = PT_WORD << 16; + break; + } + } + break; + + /* Explicit Unicode property matching */ + + case ESC_P: + case ESC_p: +#ifdef SUPPORT_UNICODE + { + BOOL negated; + uint16_t ptype = 0, pdata = 0; + if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb)) + goto FAILED; + if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P; + *parsed_pattern++ = META_ESCAPE + escape; + *parsed_pattern++ = (ptype << 16) | pdata; + } +#else + errorcode = ERR45; + goto CLASS_ESCAPE_FAILED; +#endif + break; /* End \P and \p */ + + default: /* All others are not allowed in a class */ + errorcode = ERR7; + ptr--; + goto CLASS_ESCAPE_FAILED; + } + + /* Perl gives a warning unless a following hyphen is the last character + in the class. PCRE throws an error. */ + + if (ptr < ptrend - 1 && *ptr == CHAR_MINUS && + ptr[1] != CHAR_RIGHT_SQUARE_BRACKET) + { + errorcode = ERR50; + goto FAILED; + } + } + + /* Proceed to next thing in the class. */ + + CLASS_CONTINUE: + if (ptr >= ptrend) + { + errorcode = ERR6; /* Missing terminating ']' */ + goto FAILED; + } + GETCHARINCTEST(c, ptr); + if (c == CHAR_RIGHT_SQUARE_BRACKET && !inescq) break; + } /* End of class-processing loop */ + + if (class_range_state == RANGE_STARTED) + { + parsed_pattern[-1] = CHAR_MINUS; + class_range_state = RANGE_NO; + } + + *parsed_pattern++ = META_CLASS_END; + break; /* End of character class */ + + + /* ---- Opening parenthesis ---- */ + + case CHAR_LEFT_PARENTHESIS: + if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + + /* If ( is not followed by ? it is either a capture or a special verb. */ + + if (*ptr != CHAR_QUESTION_MARK) + { + const char *vn; + + /* Handle capturing brackets (or non-capturing if auto-capture is turned + off). */ + + if (*ptr != CHAR_ASTERISK) + { + nest_depth++; + if ((options & PCRE2_NO_AUTO_CAPTURE) == 0) + { + cb->bracount++; + *parsed_pattern++ = META_CAPTURE | cb->bracount; + } + else *parsed_pattern++ = META_NOCAPTURE; + } + + + /* ---- Handle (*VERB) and (*VERB:NAME) ---- */ + + /* Do nothing for (*) so it gives a "bad quantifier" error rather than + "(*MARK) must have an argument". */ + + else if (ptrend - ptr > 1 && ptr[1] != CHAR_RIGHT_PARENTHESIS) + { + vn = verbnames; + if (!read_name(&ptr, ptrend, 0, &offset, &name, &namelen, &errorcode, + cb)) goto FAILED; + if (ptr >= ptrend || (*ptr != CHAR_COLON && + *ptr != CHAR_RIGHT_PARENTHESIS)) + { + errorcode = ERR60; /* Malformed */ + goto FAILED; + } + + /* Scan the table of verb names */ + + for (i = 0; i < verbcount; i++) + { + if (namelen == verbs[i].len && + PRIV(strncmp_c8)(name, vn, namelen) == 0) + break; + vn += verbs[i].len + 1; + } + + if (i >= verbcount) + { + errorcode = ERR60; /* Verb not recognized */ + goto FAILED; + } + + /* An empty argument is treated as no argument. */ + + if (*ptr == CHAR_COLON && ptr + 1 < ptrend && + ptr[1] == CHAR_RIGHT_PARENTHESIS) + ptr++; /* Advance to the closing parens */ + + /* Check for mandatory non-empty argument; this is (*MARK) */ + + if (verbs[i].has_arg > 0 && *ptr != CHAR_COLON) + { + errorcode = ERR66; + goto FAILED; + } + + /* It appears that Perl allows any characters whatsoever, other than a + closing parenthesis, to appear in arguments ("names"), so we no longer + insist on 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. We set inverbname TRUE here, and let the + main loop take care of this so that escape and \x processing is done by + the main code above. */ + + if (*ptr++ == CHAR_COLON) /* Skip past : or ) */ + { + /* Some optional arguments can be treated as a preceding (*MARK) */ + + if (verbs[i].has_arg < 0) + { + add_after_mark = verbs[i].meta; + *parsed_pattern++ = META_MARK; + } + + /* The remaining verbs with arguments (except *MARK) need a different + opcode. */ + + else + { + *parsed_pattern++ = verbs[i].meta + + ((verbs[i].meta != META_MARK)? 0x00010000u:0); + } + + /* Set up for reading the name in the main loop. */ + + verblengthptr = parsed_pattern++; + verbnamestart = ptr; + inverbname = TRUE; + } + else /* No verb "name" argument */ + { + *parsed_pattern++ = verbs[i].meta; + } + } /* End of (*VERB) handling */ + break; /* Done with this parenthesis */ + } /* End of groups that don't start with (? */ + + + /* ---- Items starting (? ---- */ + + /* The type of item is determined by what follows (?. Handle (?| and option + changes under "default" because both need a new block on the nest stack. + Comments starting with (?# are handled above. Note that there is some + ambiguity about the sequence (?- because if a digit follows it's a relative + recursion or subroutine call whereas otherwise it's an option unsetting. */ + + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + + switch(*ptr) + { + default: + if (*ptr == CHAR_MINUS && ptrend - ptr > 1 && IS_DIGIT(ptr[1])) + goto RECURSION_BYNUMBER; /* The + case is handled by CHAR_PLUS */ + + /* We now have either (?| or a (possibly empty) option setting, + optionally followed by a non-capturing group. */ + + nest_depth++; + if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); + else if (++top_nest >= end_nests) + { + errorcode = ERR84; + goto FAILED; + } + top_nest->nest_depth = nest_depth; + top_nest->flags = 0; + top_nest->options = options & PARSE_TRACKED_OPTIONS; + + /* Start of non-capturing group that resets the capture count for each + branch. */ + + if (*ptr == CHAR_VERTICAL_LINE) + { + top_nest->reset_group = (uint16_t)cb->bracount; + top_nest->max_group = (uint16_t)cb->bracount; + top_nest->flags |= NSF_RESET; + cb->external_flags |= PCRE2_DUPCAPUSED; + *parsed_pattern++ = META_NOCAPTURE; + ptr++; + } + + /* Scan for options imnsxJU to be set or unset. */ + + else + { + BOOL hyphenok = TRUE; + uint32_t oldoptions = options; + + top_nest->reset_group = 0; + top_nest->max_group = 0; + set = unset = 0; + optset = &set; + + /* ^ at the start unsets imnsx and disables the subsequent use of - */ + + if (ptr < ptrend && *ptr == CHAR_CIRCUMFLEX_ACCENT) + { + options &= ~(PCRE2_CASELESS|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE| + PCRE2_DOTALL|PCRE2_EXTENDED|PCRE2_EXTENDED_MORE); + hyphenok = FALSE; + ptr++; + } + + while (ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS && + *ptr != CHAR_COLON) + { + switch (*ptr++) + { + case CHAR_MINUS: + if (!hyphenok) + { + errorcode = ERR94; + ptr--; /* Correct the offset */ + goto FAILED; + } + optset = &unset; + hyphenok = FALSE; + break; + + case CHAR_J: /* Record that it changed in the external options */ + *optset |= PCRE2_DUPNAMES; + cb->external_flags |= PCRE2_JCHANGED; + break; + + case CHAR_i: *optset |= PCRE2_CASELESS; break; + case CHAR_m: *optset |= PCRE2_MULTILINE; break; + case CHAR_n: *optset |= PCRE2_NO_AUTO_CAPTURE; break; + case CHAR_s: *optset |= PCRE2_DOTALL; break; + case CHAR_U: *optset |= PCRE2_UNGREEDY; break; + + /* If x appears twice it sets the extended extended option. */ + + case CHAR_x: + *optset |= PCRE2_EXTENDED; + if (ptr < ptrend && *ptr == CHAR_x) + { + *optset |= PCRE2_EXTENDED_MORE; + ptr++; + } + break; + + default: + errorcode = ERR11; + ptr--; /* Correct the offset */ + goto FAILED; + } + } + + /* If we are setting extended without extended-more, ensure that any + existing extended-more gets unset. Also, unsetting extended must also + unset extended-more. */ + + if ((set & (PCRE2_EXTENDED|PCRE2_EXTENDED_MORE)) == PCRE2_EXTENDED || + (unset & PCRE2_EXTENDED) != 0) + unset |= PCRE2_EXTENDED_MORE; + + options = (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. + In this case, if the previous level set up a nest block, discard the + one we have just created. Otherwise adjust it for the previous level. + If the options ended with ':' we are starting a non-capturing group, + possibly with an options setting. */ + + if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + if (*ptr++ == CHAR_RIGHT_PARENTHESIS) + { + nest_depth--; /* This is not a nested group after all. */ + if (top_nest > (nest_save *)(cb->start_workspace) && + (top_nest-1)->nest_depth == nest_depth) top_nest--; + else top_nest->nest_depth = nest_depth; + } + else *parsed_pattern++ = META_NOCAPTURE; + + /* If nothing changed, no need to record. */ + + if (options != oldoptions) + { + *parsed_pattern++ = META_OPTIONS; + *parsed_pattern++ = options; + } + } /* End options processing */ + break; /* End default case after (? */ + + + /* ---- Python syntax support ---- */ + + case CHAR_P: + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + + /* (?P is the same as (?, which defines a named group. */ + + if (*ptr == CHAR_LESS_THAN_SIGN) + { + terminator = CHAR_GREATER_THAN_SIGN; + goto DEFINE_NAME; + } + + /* (?P>name) is the same as (?&name), which is a recursion or subroutine + call. */ + + if (*ptr == CHAR_GREATER_THAN_SIGN) goto RECURSE_BY_NAME; + + /* (?P=name) is the same as \k, a back reference by name. Anything + else after (?P is an error. */ + + if (*ptr != CHAR_EQUALS_SIGN) + { + errorcode = ERR41; + goto FAILED; + } + if (!read_name(&ptr, ptrend, CHAR_RIGHT_PARENTHESIS, &offset, &name, + &namelen, &errorcode, cb)) goto FAILED; + *parsed_pattern++ = META_BACKREF_BYNAME; + *parsed_pattern++ = namelen; + PUTOFFSET(offset, parsed_pattern); + okquantifier = TRUE; + break; /* End of (?P processing */ + + + /* ---- Recursion/subroutine calls by number ---- */ + + case CHAR_R: + i = 0; /* (?R) == (?R0) */ + ptr++; + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR58; + goto FAILED; + } + goto SET_RECURSION; + + /* An item starting (?- followed by a digit comes here via the "default" + case because (?- followed by a non-digit is an options setting. */ + + case CHAR_PLUS: + if (ptrend - ptr < 2 || !IS_DIGIT(ptr[1])) + { + errorcode = ERR29; /* Missing number */ + goto FAILED; + } + /* Fall through */ + + 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: + RECURSION_BYNUMBER: + if (!read_number(&ptr, ptrend, + (IS_DIGIT(*ptr))? -1:(int)(cb->bracount), /* + and - are relative */ + MAX_GROUP_NUMBER, ERR61, + &i, &errorcode)) goto FAILED; + if (i < 0) /* NB (?0) is permitted */ + { + errorcode = ERR15; /* Unknown group */ + goto FAILED_BACK; + } + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + goto UNCLOSED_PARENTHESIS; + + SET_RECURSION: + *parsed_pattern++ = META_RECURSE | (uint32_t)i; + offset = (PCRE2_SIZE)(ptr - cb->start_pattern); + ptr++; + PUTOFFSET(offset, parsed_pattern); + okquantifier = TRUE; + break; /* End of recursive call by number handling */ + + + /* ---- Recursion/subroutine calls by name ---- */ + + case CHAR_AMPERSAND: + RECURSE_BY_NAME: + if (!read_name(&ptr, ptrend, CHAR_RIGHT_PARENTHESIS, &offset, &name, + &namelen, &errorcode, cb)) goto FAILED; + *parsed_pattern++ = META_RECURSE_BYNAME; + *parsed_pattern++ = namelen; + PUTOFFSET(offset, parsed_pattern); + okquantifier = TRUE; + break; + + /* ---- Callout with numerical or string argument ---- */ + + case CHAR_C: + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + + /* If the previous item was a condition starting (?(? an assertion, + optionally preceded by a callout, is expected. This is checked later on, + during actual compilation. However we need to identify this kind of + assertion in this pass because it must not be qualified. The value of + expect_cond_assert is set to 2 after (?(? is processed. We decrement it + for a callout - still leaving a positive value that identifies the + assertion. Multiple callouts or any other items will make it zero or + less, which doesn't matter because they will cause an error later. */ + + expect_cond_assert = prev_expect_cond_assert - 1; + + /* If previous_callout is not NULL, it means this follows a previous + callout. If it was a manual callout, do nothing; this means its "length + of next pattern item" field will remain zero. If it was an automatic + callout, abolish it. */ + + if (previous_callout != NULL && (options & PCRE2_AUTO_CALLOUT) != 0 && + previous_callout == parsed_pattern - 4 && + parsed_pattern[-1] == 255) + parsed_pattern = previous_callout; + + /* Save for updating next pattern item length, and skip one item before + completing. */ + + previous_callout = parsed_pattern; + after_manual_callout = 1; + + /* Handle a string argument; specific delimiter is required. */ + + if (*ptr != CHAR_RIGHT_PARENTHESIS && !IS_DIGIT(*ptr)) + { + PCRE2_SIZE calloutlength; + PCRE2_SPTR startptr = 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; + } + + *parsed_pattern = META_CALLOUT_STRING; + parsed_pattern += 3; /* Skip pattern info */ + + for (;;) + { + if (++ptr >= ptrend) + { + errorcode = ERR81; + ptr = startptr; /* To give a more useful message */ + goto FAILED; + } + if (*ptr == delimiter && (++ptr >= ptrend || *ptr != delimiter)) + break; + } + + calloutlength = (PCRE2_SIZE)(ptr - startptr); + if (calloutlength > UINT32_MAX) + { + errorcode = ERR72; + goto FAILED; + } + *parsed_pattern++ = (uint32_t)calloutlength; + offset = (PCRE2_SIZE)(startptr - cb->start_pattern); + PUTOFFSET(offset, parsed_pattern); + } + + /* Handle a callout with an optional numerical argument, which must be + less than or equal to 255. A missing argument gives 0. */ + + else + { + int n = 0; + *parsed_pattern = META_CALLOUT_NUMBER; /* Numerical callout */ + parsed_pattern += 3; /* Skip pattern info */ + while (ptr < ptrend && IS_DIGIT(*ptr)) + { + n = n * 10 + *ptr++ - CHAR_0; + if (n > 255) + { + errorcode = ERR38; + goto FAILED; + } + } + *parsed_pattern++ = n; + } + + /* Both formats must have a closing parenthesis */ + + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR39; + goto FAILED; + } + ptr++; + + /* Remember the offset to the next item in the pattern, and set a default + length. This should get updated after the next item is read. */ + + previous_callout[1] = (uint32_t)(ptr - cb->start_pattern); + previous_callout[2] = 0; + break; /* End callout */ + + + /* ---- Conditional group ---- */ + + /* A condition can be an assertion, a number (referring to a numbered + group's having been set), a name (referring to a named group), or 'R', + referring to overall recursion. R and R&name are also permitted + for recursion state tests. Numbers may be preceded by + or - to specify a + relative group number. + + There are several syntaxes for testing a named group: (?(name)) is used + by Python; Perl 5.10 onwards uses (?() or (?('name')). + + There are two unfortunate ambiguities. 'R' can be the recursive thing or + the name 'R' (and similarly for 'R' followed by digits). 'DEFINE' can be + the Perl DEFINE feature or the Python named test. We look for a name + first; if not found, we try the other case. + + For compatibility with auto-callouts, we allow a callout to be specified + before a condition that is an assertion. */ + + case CHAR_LEFT_PARENTHESIS: + if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS; + nest_depth++; + + /* If the next character is ? there must be an assertion next (optionally + preceded by a callout). We do not check this here, but instead we set + expect_cond_assert to 2. If this is still greater than zero (callouts + decrement it) when the next assertion is read, it will be marked as a + condition that must not be repeated. A value greater than zero also + causes checking that an assertion (possibly with callout) follows. */ + + if (*ptr == CHAR_QUESTION_MARK) + { + *parsed_pattern++ = META_COND_ASSERT; + ptr--; /* Pull pointer back to the opening parenthesis. */ + expect_cond_assert = 2; + break; /* End of conditional */ + } + + /* Handle (?([+-]number)... */ + + if (read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i, + &errorcode)) + { + if (i <= 0) + { + errorcode = ERR15; + goto FAILED; + } + *parsed_pattern++ = META_COND_NUMBER; + offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2); + PUTOFFSET(offset, parsed_pattern); + *parsed_pattern++ = i; + } + else if (errorcode != 0) goto FAILED; /* Number too big */ + + /* No number found. Handle the special case (?(VERSION[>]=n.m)... */ + + else if (ptrend - ptr >= 10 && + PRIV(strncmp_c8)(ptr, STRING_VERSION, 7) == 0 && + ptr[7] != CHAR_RIGHT_PARENTHESIS) + { + uint32_t ge = 0; + int major = 0; + int minor = 0; + + ptr += 7; + if (*ptr == CHAR_GREATER_THAN_SIGN) + { + ge = 1; + ptr++; + } + + /* NOTE: cannot write IS_DIGIT(*(++ptr)) here because IS_DIGIT + references its argument twice. */ + + if (*ptr != CHAR_EQUALS_SIGN || (ptr++, !IS_DIGIT(*ptr))) + goto BAD_VERSION_CONDITION; + + if (!read_number(&ptr, ptrend, -1, 1000, ERR79, &major, &errorcode)) + goto FAILED; + + if (ptr >= ptrend) goto BAD_VERSION_CONDITION; + if (*ptr == CHAR_DOT) + { + if (++ptr >= ptrend || !IS_DIGIT(*ptr)) goto BAD_VERSION_CONDITION; + minor = (*ptr++ - CHAR_0) * 10; + if (IS_DIGIT(*ptr)) minor += *ptr++ - CHAR_0; + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + goto BAD_VERSION_CONDITION; + } + + *parsed_pattern++ = META_COND_VERSION; + *parsed_pattern++ = ge; + *parsed_pattern++ = major; + *parsed_pattern++ = minor; + } + + /* All the remaining cases now require us to read a name. We cannot at + this stage distinguish ambiguous cases such as (?(R12) which might be a + recursion test by number or a name, because the named groups have not yet + all been identified. Those cases are treated as names, but given a + different META code. */ + + else + { + BOOL was_r_ampersand = FALSE; + + if (*ptr == CHAR_R && ptrend - ptr > 1 && ptr[1] == CHAR_AMPERSAND) + { + terminator = CHAR_RIGHT_PARENTHESIS; + was_r_ampersand = TRUE; + ptr++; + } + else if (*ptr == CHAR_LESS_THAN_SIGN) + terminator = CHAR_GREATER_THAN_SIGN; + else if (*ptr == CHAR_APOSTROPHE) + terminator = CHAR_APOSTROPHE; + else + { + terminator = CHAR_RIGHT_PARENTHESIS; + ptr--; /* Point to char before name */ + } + if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen, + &errorcode, cb)) goto FAILED; + + /* Handle (?(R&name) */ + + if (was_r_ampersand) + { + *parsed_pattern = META_COND_RNAME; + ptr--; /* Back to closing parens */ + } + + /* Handle (?(name). If the name is "DEFINE" we identify it with a + special code. Likewise if the name consists of R followed only by + digits. Otherwise, handle it like a quoted name. */ + + else if (terminator == CHAR_RIGHT_PARENTHESIS) + { + if (namelen == 6 && PRIV(strncmp_c8)(name, STRING_DEFINE, 6) == 0) + *parsed_pattern = META_COND_DEFINE; + else + { + for (i = 1; i < (int)namelen; i++) + if (!IS_DIGIT(name[i])) break; + *parsed_pattern = (*name == CHAR_R && i >= (int)namelen)? + META_COND_RNUMBER : META_COND_NAME; + } + ptr--; /* Back to closing parens */ + } + + /* Handle (?('name') or (?() */ + + else *parsed_pattern = META_COND_NAME; + + /* All these cases except DEFINE end with the name length and offset; + DEFINE just has an offset (for the "too many branches" error). */ + + if (*parsed_pattern++ != META_COND_DEFINE) *parsed_pattern++ = namelen; + PUTOFFSET(offset, parsed_pattern); + } /* End cases that read a name */ + + /* Check the closing parenthesis of the condition */ + + if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR24; + goto FAILED; + } + ptr++; + break; /* End of condition processing */ + + + /* ---- Atomic group ---- */ + + case CHAR_GREATER_THAN_SIGN: + *parsed_pattern++ = META_ATOMIC; + nest_depth++; + ptr++; + break; + + + /* ---- Lookahead assertions ---- */ + + case CHAR_EQUALS_SIGN: + *parsed_pattern++ = META_LOOKAHEAD; + ptr++; + goto POST_ASSERTION; + + case CHAR_EXCLAMATION_MARK: + *parsed_pattern++ = META_LOOKAHEADNOT; + ptr++; + goto POST_ASSERTION; + + + /* ---- Lookbehind assertions ---- */ + + /* (?< followed by = or ! is a lookbehind assertion. Otherwise (?< is the + start of the name of a capturing group. */ + + case CHAR_LESS_THAN_SIGN: + if (ptrend - ptr <= 1 || + (ptr[1] != CHAR_EQUALS_SIGN && ptr[1] != CHAR_EXCLAMATION_MARK)) + { + terminator = CHAR_GREATER_THAN_SIGN; + goto DEFINE_NAME; + } + *parsed_pattern++ = (ptr[1] == CHAR_EQUALS_SIGN)? + META_LOOKBEHIND : META_LOOKBEHINDNOT; + *has_lookbehind = TRUE; + offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2); + PUTOFFSET(offset, parsed_pattern); + ptr += 2; + /* Fall through */ + + /* If the previous item was a condition starting (?(? an assertion, + optionally preceded by a callout, is expected. This is checked later on, + during actual compilation. However we need to identify this kind of + assertion in this pass because it must not be qualified. The value of + expect_cond_assert is set to 2 after (?(? is processed. We decrement it + for a callout - still leaving a positive value that identifies the + assertion. Multiple callouts or any other items will make it zero or + less, which doesn't matter because they will cause an error later. */ + + POST_ASSERTION: + nest_depth++; + if (prev_expect_cond_assert > 0) + { + if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace); + else if (++top_nest >= end_nests) + { + errorcode = ERR84; + goto FAILED; + } + top_nest->nest_depth = nest_depth; + top_nest->flags = NSF_CONDASSERT; + top_nest->options = options & PARSE_TRACKED_OPTIONS; + } + break; + + + /* ---- Define a named group ---- */ + + /* A named group may be defined as (?'name') or (?). In the latter + case we jump to DEFINE_NAME from the disambiguation of (?< above with the + terminator set to '>'. */ + + case CHAR_APOSTROPHE: + terminator = CHAR_APOSTROPHE; /* Terminator */ + + DEFINE_NAME: + if (!read_name(&ptr, ptrend, terminator, &offset, &name, &namelen, + &errorcode, cb)) goto FAILED; + + /* We have a name for this capturing group. It is also assigned a number, + which is its primary means of identification. */ + + cb->bracount++; + *parsed_pattern++ = META_CAPTURE | cb->bracount; + nest_depth++; + + /* Check not too many names */ + + if (cb->names_found >= MAX_NAME_COUNT) + { + errorcode = ERR49; + goto FAILED; + } + + /* Adjust the entry size to accommodate the longest name found. */ + + if (namelen + IMM2_SIZE + 1 > cb->name_entry_size) + cb->name_entry_size = (uint16_t)(namelen + IMM2_SIZE + 1); + + /* Scan the list to check for duplicates. For duplicate names, if the + number is the same, break the loop, which causes the name to be + discarded; otherwise, if DUPNAMES is not set, give an error. + If it is set, allow the name with a different number, but continue + scanning in case this is a duplicate with the same number. For + non-duplicate names, give an error if the number is duplicated. */ + + isdupname = FALSE; + ng = cb->named_groups; + for (i = 0; i < cb->names_found; i++, ng++) + { + if (namelen == ng->length && + PRIV(strncmp)(name, ng->name, (PCRE2_SIZE)namelen) == 0) + { + if (ng->number == cb->bracount) break; + if ((options & PCRE2_DUPNAMES) == 0) + { + errorcode = ERR43; + goto FAILED; + } + isdupname = ng->isdup = TRUE; /* Mark as a duplicate */ + cb->dupnames = TRUE; /* Duplicate names exist */ + } + else if (ng->number == cb->bracount) + { + errorcode = ERR65; + goto FAILED; + } + } + + if (i < cb->names_found) break; /* Ignore duplicate with same number */ + + /* Increase the list size if necessary */ + + if (cb->names_found >= cb->named_group_list_size) + { + uint32_t newsize = cb->named_group_list_size * 2; + named_group *newspace = + cb->cx->memctl.malloc(newsize * sizeof(named_group), + cb->cx->memctl.memory_data); + if (newspace == NULL) + { + errorcode = ERR21; + goto FAILED; + } + + memcpy(newspace, cb->named_groups, + cb->named_group_list_size * sizeof(named_group)); + if (cb->named_group_list_size > NAMED_GROUP_LIST_SIZE) + cb->cx->memctl.free((void *)cb->named_groups, + cb->cx->memctl.memory_data); + cb->named_groups = newspace; + cb->named_group_list_size = newsize; + } + + /* Add this name to the list */ + + cb->named_groups[cb->names_found].name = name; + cb->named_groups[cb->names_found].length = (uint16_t)namelen; + cb->named_groups[cb->names_found].number = cb->bracount; + cb->named_groups[cb->names_found].isdup = (uint16_t)isdupname; + cb->names_found++; + break; + } /* End of (? switch */ + break; /* End of ( handling */ + + + /* ---- Branch terminators ---- */ + + /* Alternation: reset the capture count if we are in a (?| group. */ + + case CHAR_VERTICAL_LINE: + if (top_nest != NULL && top_nest->nest_depth == nest_depth && + (top_nest->flags & NSF_RESET) != 0) + { + if (cb->bracount > top_nest->max_group) + top_nest->max_group = (uint16_t)cb->bracount; + cb->bracount = top_nest->reset_group; + } + *parsed_pattern++ = META_ALT; + break; + + /* End of group; reset the capture count to the maximum if we are in a (?| + group and/or reset the options that are tracked during parsing. Disallow + quantifier for a condition that is an assertion. */ + + case CHAR_RIGHT_PARENTHESIS: + okquantifier = TRUE; + if (top_nest != NULL && top_nest->nest_depth == nest_depth) + { + options = (options & ~PARSE_TRACKED_OPTIONS) | top_nest->options; + if ((top_nest->flags & NSF_RESET) != 0 && + top_nest->max_group > cb->bracount) + cb->bracount = top_nest->max_group; + if ((top_nest->flags & NSF_CONDASSERT) != 0) + okquantifier = FALSE; + if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL; + else top_nest--; + } + if (nest_depth == 0) /* Unmatched closing parenthesis */ + { + errorcode = ERR22; + goto FAILED_BACK; + } + nest_depth--; + *parsed_pattern++ = META_KET; + break; + } /* End of switch on pattern character */ + } /* End of main character scan loop */ + +/* End of pattern reached. Check for missing ) at the end of a verb name. */ + +if (inverbname && ptr >= ptrend) + { + errorcode = ERR60; + goto FAILED; + } + +/* Manage callout for the final item */ + +PARSED_END: +parsed_pattern = manage_callouts(ptr, &previous_callout, auto_callout, + parsed_pattern, cb); + +/* Insert trailing items for word and line matching (features provided for the +benefit of pcre2grep). */ + +if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_LINE) != 0) + { + *parsed_pattern++ = META_KET; + *parsed_pattern++ = META_DOLLAR; + } +else if ((cb->cx->extra_options & PCRE2_EXTRA_MATCH_WORD) != 0) + { + *parsed_pattern++ = META_KET; + *parsed_pattern++ = META_ESCAPE + ESC_b; + } + +/* Terminate the parsed pattern, then return success if all groups are closed. +Otherwise we have unclosed parentheses. */ + +if (parsed_pattern >= parsed_pattern_end) + { + errorcode = ERR63; /* Internal error (parsed pattern overflow) */ + goto FAILED; + } + +*parsed_pattern = META_END; +if (nest_depth == 0) return 0; + +UNCLOSED_PARENTHESIS: +errorcode = ERR14; + +/* Come here for all failures. */ + +FAILED: +cb->erroroffset = (PCRE2_SIZE)(ptr - cb->start_pattern); +return errorcode; + +/* Some errors need to indicate the previous character. */ + +FAILED_BACK: +ptr--; +goto FAILED; + +/* This failure happens several times. */ + +BAD_VERSION_CONDITION: +errorcode = ERR79; +goto FAILED; +} + + + +/************************************************* +* Find first significant opcode * +*************************************************/ + +/* This is called by several functions that scan a compiled expression looking +for a fixed first character, or an anchoring opcode etc. It skips over things +that do not influence this. For some calls, it makes sense to skip negative +forward and all backward assertions, and also the \b assertion; for others it +does not. + +Arguments: + code pointer to the start of the group + skipassert TRUE if certain assertions are to be skipped + +Returns: pointer to the first significant opcode +*/ + +static const PCRE2_UCHAR* +first_significant_code(PCRE2_SPTR code, BOOL skipassert) +{ +for (;;) + { + switch ((int)*code) + { + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + if (!skipassert) return code; + do code += GET(code, 1); while (*code == OP_ALT); + code += PRIV(OP_lengths)[*code]; + break; + + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + if (!skipassert) return code; + /* Fall through */ + + case OP_CALLOUT: + case OP_CREF: + case OP_DNCREF: + case OP_RREF: + case OP_DNRREF: + case OP_FALSE: + case OP_TRUE: + code += PRIV(OP_lengths)[*code]; + break; + + case OP_CALLOUT_STR: + code += GET(code, 1 + 2*LINK_SIZE); + break; + + case OP_SKIPZERO: + code += 2 + GET(code, 2) + LINK_SIZE; + break; + + case OP_COND: + case OP_SCOND: + if (code[1+LINK_SIZE] != OP_FALSE || /* Not DEFINE */ + code[GET(code, 1)] != OP_KET) /* More than one branch */ + return code; + code += GET(code, 1) + 1 + LINK_SIZE; + break; + + default: + return code; + } + } +/* Control never reaches here */ +} + + + +#ifdef SUPPORT_UNICODE +/************************************************* +* Get othercase range * +*************************************************/ + +/* This function is passed the start and end of a class range in UCP mode. It +searches up the characters, looking for ranges of characters in the "other" +case. Each call returns the next one, updating the start address. A character +with multiple other cases is returned on its own with a special return value. + +Arguments: + cptr points to starting character value; updated + d end value + ocptr where to put start of othercase range + odptr where to put end of othercase range + +Yield: -1 when no more + 0 when a range is returned + >0 the CASESET offset for char with multiple other cases + in this case, ocptr contains the original +*/ + +static int +get_othercase_range(uint32_t *cptr, uint32_t d, uint32_t *ocptr, + uint32_t *odptr) +{ +uint32_t c, othercase, next; +unsigned int co; + +/* Find the first character that has an other case. If it has multiple other +cases, return its case offset value. */ + +for (c = *cptr; c <= d; c++) + { + if ((co = UCD_CASESET(c)) != 0) + { + *ocptr = c++; /* Character that has the set */ + *cptr = c; /* Rest of input range */ + return (int)co; + } + if ((othercase = UCD_OTHERCASE(c)) != c) break; + } + +if (c > d) return -1; /* Reached end of range */ + +/* Found a character that has a single other case. Search for the end of the +range, which is either the end of the input range, or a character that has zero +or more than one other cases. */ + +*ocptr = othercase; +next = othercase + 1; + +for (++c; c <= d; c++) + { + if ((co = UCD_CASESET(c)) != 0 || UCD_OTHERCASE(c) != next) break; + next++; + } + +*odptr = next - 1; /* End of othercase range */ +*cptr = c; /* Rest of input range */ +return 0; +} +#endif /* SUPPORT_UNICODE */ + + + +/************************************************* +* Add a character or range to a class (internal) * +*************************************************/ + +/* This function packages up the logic of adding a character or range of +characters to a class. The character values in the arguments will be within the +valid values for the current mode (8-bit, 16-bit, UTF, etc). This function is +called only from within the "add to class" group of functions, some of which +are recursive and mutually recursive. The external entry point is +add_to_class(). + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cb compile data + start start of range character + end end of range character + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static unsigned int +add_to_class_internal(uint8_t *classbits, PCRE2_UCHAR **uchardptr, + uint32_t options, compile_block *cb, uint32_t start, uint32_t end) +{ +uint32_t c; +uint32_t classbits_end = (end <= 0xff ? end : 0xff); +unsigned int n8 = 0; + +/* If caseless matching is required, scan the range and process alternate +cases. In Unicode, there are 8-bit characters that have alternate cases that +are greater than 255 and vice-versa. Sometimes we can just extend the original +range. */ + +if ((options & PCRE2_CASELESS) != 0) + { +#ifdef SUPPORT_UNICODE + if ((options & PCRE2_UTF) != 0) + { + int rc; + uint32_t oc, od; + + options &= ~PCRE2_CASELESS; /* Remove for recursive calls */ + c = start; + + while ((rc = get_othercase_range(&c, end, &oc, &od)) >= 0) + { + /* Handle a single character that has more than one other case. */ + + if (rc > 0) n8 += add_list_to_class_internal(classbits, uchardptr, options, cb, + PRIV(ucd_caseless_sets) + rc, oc); + + /* Do nothing if the other case range is within the original range. */ + + else if (oc >= cb->class_range_start && od <= cb->class_range_end) continue; + + /* Extend the original range if there is overlap, noting that if oc < c, we + can't have od > end because a subrange is always shorter than the basic + range. Otherwise, use a recursive call to add the additional range. */ + + else if (oc < start && od >= start - 1) start = oc; /* Extend downwards */ + else if (od > end && oc <= end + 1) + { + end = od; /* Extend upwards */ + if (end > classbits_end) classbits_end = (end <= 0xff ? end : 0xff); + } + else n8 += add_to_class_internal(classbits, uchardptr, options, cb, oc, od); + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* Not UTF mode */ + + for (c = start; c <= classbits_end; c++) + { + SETBIT(classbits, cb->fcc[c]); + n8++; + } + } + +/* Now handle the originally supplied range. Adjust the final value according +to the bit length - this means that the same lists of (e.g.) horizontal spaces +can be used in all cases. */ + +if ((options & PCRE2_UTF) == 0 && end > MAX_NON_UTF_CHAR) + end = MAX_NON_UTF_CHAR; + +if (start > cb->class_range_start && end < cb->class_range_end) return n8; + +/* Use the bitmap for characters < 256. Otherwise use extra data.*/ + +for (c = start; c <= classbits_end; c++) + { + /* Regardless of start, c will always be <= 255. */ + SETBIT(classbits, c); + n8++; + } + +#ifdef SUPPORT_WIDE_CHARS +if (start <= 0xff) start = 0xff + 1; + +if (end >= start) + { + PCRE2_UCHAR *uchardata = *uchardptr; + +#ifdef SUPPORT_UNICODE + if ((options & PCRE2_UTF) != 0) + { + if (start < end) + { + *uchardata++ = XCL_RANGE; + uchardata += PRIV(ord2utf)(start, uchardata); + uchardata += PRIV(ord2utf)(end, uchardata); + } + else if (start == end) + { + *uchardata++ = XCL_SINGLE; + uchardata += PRIV(ord2utf)(start, uchardata); + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* Without UTF support, character values are constrained by the bit length, + and can only be > 256 for 16-bit and 32-bit libraries. */ + +#if PCRE2_CODE_UNIT_WIDTH == 8 + {} +#else + if (start < end) + { + *uchardata++ = XCL_RANGE; + *uchardata++ = start; + *uchardata++ = end; + } + else if (start == end) + { + *uchardata++ = XCL_SINGLE; + *uchardata++ = start; + } +#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ + *uchardptr = uchardata; /* Updata extra data pointer */ + } +#else /* SUPPORT_WIDE_CHARS */ + (void)uchardptr; /* Avoid compiler warning */ +#endif /* SUPPORT_WIDE_CHARS */ + +return n8; /* Number of 8-bit characters */ +} + + + +#ifdef SUPPORT_UNICODE +/************************************************* +* Add a list of characters to a class (internal) * +*************************************************/ + +/* This function is used for adding a list of case-equivalent characters to a +class when in UTF mode. This function is called only from within +add_to_class_internal(), with which it is mutually recursive. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cb contains pointers to tables etc. + p points to row of 32-bit values, terminated by NOTACHAR + except character to omit; this is used when adding lists of + case-equivalent characters to avoid including the one we + already know about + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static unsigned int +add_list_to_class_internal(uint8_t *classbits, PCRE2_UCHAR **uchardptr, + uint32_t options, compile_block *cb, const uint32_t *p, unsigned int except) +{ +unsigned int n8 = 0; +while (p[0] < NOTACHAR) + { + unsigned int n = 0; + if (p[0] != except) + { + while(p[n+1] == p[0] + n + 1) n++; + n8 += add_to_class_internal(classbits, uchardptr, options, cb, p[0], p[n]); + } + p += n + 1; + } +return n8; +} +#endif + + + +/************************************************* +* External entry point for add range to class * +*************************************************/ + +/* This function sets the overall range so that the internal functions can try +to avoid duplication when handling case-independence. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cb compile data + start start of range character + end end of range character + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static unsigned int +add_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, + compile_block *cb, uint32_t start, uint32_t end) +{ +cb->class_range_start = start; +cb->class_range_end = end; +return add_to_class_internal(classbits, uchardptr, options, cb, start, end); +} + + +/************************************************* +* External entry point for add list to class * +*************************************************/ + +/* This function is used for adding a list of horizontal or vertical whitespace +characters to a class. The list must be in order so that ranges of characters +can be detected and handled appropriately. This function sets the overall range +so that the internal functions can try to avoid duplication when handling +case-independence. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cb contains pointers to tables etc. + p points to row of 32-bit values, terminated by NOTACHAR + except character to omit; this is used when adding lists of + case-equivalent characters to avoid including the one we + already know about + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static unsigned int +add_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options, + compile_block *cb, const uint32_t *p, unsigned int except) +{ +unsigned int n8 = 0; +while (p[0] < NOTACHAR) + { + unsigned int n = 0; + if (p[0] != except) + { + while(p[n+1] == p[0] + n + 1) n++; + cb->class_range_start = p[0]; + cb->class_range_end = p[n]; + n8 += add_to_class_internal(classbits, uchardptr, options, cb, p[0], p[n]); + } + p += n + 1; + } +return n8; +} + + + +/************************************************* +* Add characters not in a list to a class * +*************************************************/ + +/* This function is used for adding the complement of a list of horizontal or +vertical whitespace to a class. The list must be in order. + +Arguments: + classbits the bit map for characters < 256 + uchardptr points to the pointer for extra data + options the options word + cb contains pointers to tables etc. + p points to row of 32-bit values, terminated by NOTACHAR + +Returns: the number of < 256 characters added + the pointer to extra data is updated +*/ + +static unsigned int +add_not_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, + uint32_t options, compile_block *cb, const uint32_t *p) +{ +BOOL utf = (options & PCRE2_UTF) != 0; +unsigned int n8 = 0; +if (p[0] > 0) + n8 += add_to_class(classbits, uchardptr, options, cb, 0, p[0] - 1); +while (p[0] < NOTACHAR) + { + while (p[1] == p[0] + 1) p++; + n8 += add_to_class(classbits, uchardptr, options, cb, p[0] + 1, + (p[1] == NOTACHAR) ? (utf ? 0x10ffffu : 0xffffffffu) : p[1] - 1); + p++; + } +return n8; +} + + + +/************************************************* +* Find details of duplicate group names * +*************************************************/ + +/* This is called from compile_branch() when it needs to know the index and +count of duplicates in the names table when processing named backreferences, +either directly, or as conditions. + +Arguments: + name points to the name + length the length of the name + indexptr where to put the index + countptr where to put the count of duplicates + errorcodeptr where to put an error code + cb the compile block + +Returns: TRUE if OK, FALSE if not, error code set +*/ + +static BOOL +find_dupname_details(PCRE2_SPTR name, uint32_t length, int *indexptr, + int *countptr, int *errorcodeptr, compile_block *cb) +{ +uint32_t i, groupnumber; +int count; +PCRE2_UCHAR *slot = cb->name_table; + +/* Find the first entry in the table */ + +for (i = 0; i < cb->names_found; i++) + { + if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) == 0 && + slot[IMM2_SIZE+length] == 0) break; + slot += cb->name_entry_size; + } + +/* This should not occur, because this function is called only when we know we +have duplicate names. Give an internal error. */ + +if (i >= cb->names_found) + { + *errorcodeptr = ERR53; + cb->erroroffset = name - cb->start_pattern; + return FALSE; + } + +/* Record the index and then see how many duplicates there are, updating the +backref map and maximum back reference as we do. */ + +*indexptr = i; +count = 0; + +for (;;) + { + count++; + groupnumber = GET2(slot,0); + cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1; + if (groupnumber > cb->top_backref) cb->top_backref = groupnumber; + if (++i >= cb->names_found) break; + slot += cb->name_entry_size; + if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) != 0 || + (slot+IMM2_SIZE)[length] != 0) break; + } + +*countptr = count; +return TRUE; +} + + + +/************************************************* +* Compile one branch * +*************************************************/ + +/* Scan the parsed pattern, compiling it into the a vector of PCRE2_UCHAR. If +the options are changed during the branch, the pointer is used to change the +external options bits. This function is used during the pre-compile phase when +we are trying to find out the amount of memory needed, as well as during the +real compile phase. The value of lengthptr distinguishes the two phases. + +Arguments: + optionsptr pointer to the option bits + codeptr points to the pointer to the current code point + pptrptr points to the current parsed pattern pointer + errorcodeptr points to error code variable + firstcuptr place to put the first required code unit + firstcuflagsptr place to put the first code unit flags, or a negative number + reqcuptr place to put the last required code unit + reqcuflagsptr place to put the last required code unit flags, or a negative number + bcptr points to current branch chain + cb contains pointers to tables etc. + lengthptr NULL during the real compile phase + points to length accumulator during pre-compile phase + +Returns: 0 There's been an error, *errorcodeptr is non-zero + +1 Success, this branch must match at least one character + -1 Success, this branch may match an empty string +*/ + +static int +compile_branch(uint32_t *optionsptr, PCRE2_UCHAR **codeptr, uint32_t **pptrptr, + int *errorcodeptr, uint32_t *firstcuptr, int32_t *firstcuflagsptr, + uint32_t *reqcuptr, int32_t *reqcuflagsptr, branch_chain *bcptr, + compile_block *cb, PCRE2_SIZE *lengthptr) +{ +int bravalue = 0; +int okreturn = -1; +int group_return = 0; +uint32_t repeat_min = 0, repeat_max = 0; /* To please picky compilers */ +uint32_t greedy_default, greedy_non_default; +uint32_t repeat_type, op_type; +uint32_t options = *optionsptr; /* May change dynamically */ +uint32_t firstcu, reqcu; +uint32_t zeroreqcu, zerofirstcu; +uint32_t escape; +uint32_t *pptr = *pptrptr; +uint32_t meta, meta_arg; +int32_t firstcuflags, reqcuflags; +int32_t zeroreqcuflags, zerofirstcuflags; +int32_t req_caseopt, reqvary, tempreqvary; +PCRE2_SIZE offset = 0; +PCRE2_SIZE length_prevgroup = 0; +PCRE2_UCHAR *code = *codeptr; +PCRE2_UCHAR *last_code = code; +PCRE2_UCHAR *orig_code = code; +PCRE2_UCHAR *tempcode; +PCRE2_UCHAR *previous = NULL; +PCRE2_UCHAR op_previous; +BOOL groupsetfirstcu = FALSE; +BOOL matched_char = FALSE; +BOOL previous_matched_char = FALSE; +const uint8_t *cbits = cb->cbits; +uint8_t classbits[32]; + +/* We can fish out the UTF setting once and for all into a BOOL, but we must +not do this for other options (e.g. PCRE2_EXTENDED) because they may change +dynamically as we process the pattern. */ + +#ifdef SUPPORT_UNICODE +BOOL utf = (options & PCRE2_UTF) != 0; +#else /* No UTF support */ +BOOL utf = FALSE; +#endif + +/* Helper variables for OP_XCLASS opcode (for characters > 255). We define +class_uchardata always so that it can be passed to add_to_class() always, +though it will not be used in non-UTF 8-bit cases. This avoids having to supply +alternative calls for the different cases. */ + +PCRE2_UCHAR *class_uchardata; +#ifdef SUPPORT_WIDE_CHARS +BOOL xclass; +PCRE2_UCHAR *class_uchardata_base; +#endif + +/* Set up the default and non-default settings for greediness */ + +greedy_default = ((options & PCRE2_UNGREEDY) != 0); +greedy_non_default = greedy_default ^ 1; + +/* Initialize no first unit, no required unit. REQ_UNSET means "no char +matching encountered yet". It gets changed to REQ_NONE if we hit something that +matches a non-fixed first unit; reqcu just remains unset if we never find one. + +When we hit a repeat whose minimum is zero, we may have to adjust these values +to take the zero repeat into account. This is implemented by setting them to +zerofirstcu and zeroreqcu when such a repeat is encountered. The individual +item types that can be repeated set these backoff variables appropriately. */ + +firstcu = reqcu = zerofirstcu = zeroreqcu = 0; +firstcuflags = reqcuflags = zerofirstcuflags = zeroreqcuflags = REQ_UNSET; + +/* The variable req_caseopt contains either the REQ_CASELESS value or zero, +according to the current setting of the caseless flag. The REQ_CASELESS value +leaves the lower 28 bit empty. It is added into the firstcu or reqcu variables +to record the case status of the value. This is used only for ASCII characters. +*/ + +req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS:0; + +/* Switch on next META item until the end of the branch */ + +for (;; pptr++) + { +#ifdef SUPPORT_WIDE_CHARS + BOOL xclass_has_prop; +#endif + BOOL negate_class; + BOOL should_flip_negation; + BOOL match_all_or_no_wide_chars; + BOOL possessive_quantifier; + BOOL note_group_empty; + int class_has_8bitchar; + int i; + uint32_t mclength; + uint32_t skipunits; + uint32_t subreqcu, subfirstcu; + uint32_t groupnumber; + uint32_t verbarglen, verbculen; + int32_t subreqcuflags, subfirstcuflags; /* Must be signed */ + open_capitem *oc; + PCRE2_UCHAR mcbuffer[8]; + + /* Get next META item in the pattern and its potential argument. */ + + meta = META_CODE(*pptr); + meta_arg = META_DATA(*pptr); + + /* If we are in the pre-compile phase, accumulate the length used for the + previous cycle of this loop, unless the next item is a quantifier. */ + + if (lengthptr != NULL) + { + if (code > cb->start_workspace + cb->workspace_size - + WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */ + { + *errorcodeptr = (code >= cb->start_workspace + cb->workspace_size)? + ERR52 : ERR86; + return 0; + } + + /* There is at least one situation where code goes backwards: this is the + case of a zero quantifier after a class (e.g. [ab]{0}). When the quantifier + is processed, the whole class is eliminated. However, it is created first, + so we have to allow memory for it. Therefore, don't ever reduce the length + at this point. */ + + if (code < last_code) code = last_code; + + /* If the next thing is not a quantifier, we add the length of the previous + item into the total, and reset the code pointer to the start of the + workspace. Otherwise leave the previous item available to be quantified. */ + + if (meta < META_ASTERISK || meta > META_MINMAX_QUERY) + { + if (OFLOW_MAX - *lengthptr < (PCRE2_SIZE)(code - orig_code)) + { + *errorcodeptr = ERR20; /* Integer overflow */ + return 0; + } + *lengthptr += (PCRE2_SIZE)(code - orig_code); + if (*lengthptr > MAX_PATTERN_SIZE) + { + *errorcodeptr = ERR20; /* Pattern is too large */ + return 0; + } + code = orig_code; + } + + /* Remember where this code item starts so we can catch the "backwards" + case above next time round. */ + + last_code = code; + } + + /* Process the next parsed pattern item. If it is not a quantifier, remember + where it starts so that it can be quantified when a quantifier follows. + Checking for the legality of quantifiers happens in parse_regex(), except for + a quantifier after an assertion that is a condition. */ + + if (meta < META_ASTERISK || meta > META_MINMAX_QUERY) + { + previous = code; + if (matched_char) okreturn = 1; + } + + previous_matched_char = matched_char; + matched_char = FALSE; + note_group_empty = FALSE; + skipunits = 0; /* Default value for most subgroups */ + + switch(meta) + { + /* ===================================================================*/ + /* The branch terminates at pattern end or | or ) */ + + case META_END: + case META_ALT: + case META_KET: + *firstcuptr = firstcu; + *firstcuflagsptr = firstcuflags; + *reqcuptr = reqcu; + *reqcuflagsptr = reqcuflags; + *codeptr = code; + *pptrptr = pptr; + return okreturn; + + + /* ===================================================================*/ + /* Handle single-character metacharacters. In multiline mode, ^ disables + the setting of any following char as a first character. */ + + case META_CIRCUMFLEX: + if ((options & PCRE2_MULTILINE) != 0) + { + if (firstcuflags == REQ_UNSET) + zerofirstcuflags = firstcuflags = REQ_NONE; + *code++ = OP_CIRCM; + } + else *code++ = OP_CIRC; + break; + + case META_DOLLAR: + *code++ = ((options & PCRE2_MULTILINE) != 0)? OP_DOLLM : OP_DOLL; + break; + + /* There can never be a first char if '.' is first, whatever happens about + repeats. The value of reqcu doesn't change either. */ + + case META_DOT: + matched_char = TRUE; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + *code++ = ((options & PCRE2_DOTALL) != 0)? OP_ALLANY: OP_ANY; + break; + + + /* ===================================================================*/ + /* Empty character classes are allowed if PCRE2_ALLOW_EMPTY_CLASS is set. + Otherwise, an initial ']' is taken as a data character. When empty classes + are allowed, [] must always fail, so generate OP_FAIL, whereas [^] must + match any character, so generate OP_ALLANY. */ + + case META_CLASS_EMPTY: + case META_CLASS_EMPTY_NOT: + matched_char = TRUE; + *code++ = (meta == META_CLASS_EMPTY_NOT)? OP_ALLANY : OP_FAIL; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + break; + + + /* ===================================================================*/ + /* Non-empty character class. If the included characters are all < 256, we + build a 32-byte bitmap of the permitted characters, except in the special + case where there is only one such character. For negated classes, we build + the map as usual, then invert it at the end. However, we use a different + opcode so that data characters > 255 can be handled correctly. + + If the class contains characters outside the 0-255 range, a different + opcode is compiled. It may optionally have a bit map for characters < 256, + but those above are are explicitly listed afterwards. A flag code unit + tells whether the bitmap is present, and whether this is a negated class or + not. */ + + case META_CLASS_NOT: + case META_CLASS: + matched_char = TRUE; + negate_class = meta == META_CLASS_NOT; + + /* We can optimize the case of a single character in a class by generating + OP_CHAR or OP_CHARI if it's positive, or OP_NOT or OP_NOTI if it's + negative. In the negative case 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. */ + + /* NOTE: at present this optimization is not effective if the only + character in a class in 32-bit, non-UCP mode has its top bit set. */ + + if (pptr[1] < META_END && pptr[2] == META_CLASS_END) + { +#ifdef SUPPORT_UNICODE + uint32_t d; +#endif + uint32_t c = pptr[1]; + + pptr += 2; /* Move on to class end */ + if (meta == META_CLASS) /* A positive one-char class can be */ + { /* handled as a normal literal character. */ + meta = c; /* Set up the character */ + goto NORMAL_CHAR_SET; + } + + /* Handle a negative one-character class */ + + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + + /* For caseless UTF mode, check whether this character has more than + one other case. If so, generate a special OP_NOTPROP item instead of + OP_NOTI. */ + +#ifdef SUPPORT_UNICODE + if (utf && (options & PCRE2_CASELESS) != 0 && + (d = UCD_CASESET(c)) != 0) + { + *code++ = OP_NOTPROP; + *code++ = PT_CLIST; + *code++ = d; + break; /* We are finished with this class */ + } +#endif + /* Char has only one other case, or UCP not available */ + + *code++ = ((options & PCRE2_CASELESS) != 0)? OP_NOTI: OP_NOT; + code += PUTCHAR(c, code); + break; /* We are finished with this class */ + } /* End of 1-char optimization */ + + /* Handle character classes that contain more than just one literal + character. */ + + /* 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 = match_all_or_no_wide_chars = FALSE; + + /* Extended class (xclass) will be used when characters > 255 + might match. */ + +#ifdef SUPPORT_WIDE_CHARS + xclass = FALSE; + class_uchardata = code + LINK_SIZE + 2; /* For XCLASS items */ + class_uchardata_base = class_uchardata; /* Save the start */ +#endif + + /* For optimization purposes, we track some properties of the class: + class_has_8bitchar will be non-zero if the class contains at least one + character with a code point less than 256; xclass_has_prop will be TRUE if + Unicode property checks are present in the class. */ + + class_has_8bitchar = 0; +#ifdef SUPPORT_WIDE_CHARS + xclass_has_prop = FALSE; +#endif + + /* Initialize the 256-bit (32-byte) bit map to all zeros. We build the map + in a temporary bit of memory, in case the class contains fewer than two + 8-bit characters because in that case the compiled code doesn't use the bit + map. */ + + memset(classbits, 0, 32 * sizeof(uint8_t)); + + /* Process items until META_CLASS_END is reached. */ + + while ((meta = *(++pptr)) != META_CLASS_END) + { + /* Handle POSIX classes such as [:alpha:] etc. */ + + if (meta == META_POSIX || meta == META_POSIX_NEG) + { + BOOL local_negate = (meta == META_POSIX_NEG); + int posix_class = *(++pptr); + int taboffset, tabopt; + uint8_t pbits[32]; + + should_flip_negation = local_negate; /* Note negative special */ + + /* If matching is caseless, upper and lower are converted to alpha. + This relies on the fact that the class table starts with alpha, + lower, upper as the first 3 entries. */ + + if ((options & PCRE2_CASELESS) != 0 && posix_class <= 2) + posix_class = 0; + + /* When PCRE2_UCP is set, some of the POSIX classes are converted to + different escape sequences that use Unicode properties \p or \P. + Others that are not available via \p or \P have to generate + XCL_PROP/XCL_NOTPROP directly, which is done here. */ + +#ifdef SUPPORT_UNICODE + if ((options & PCRE2_UCP) != 0) switch(posix_class) + { + case PC_GRAPH: + case PC_PRINT: + case PC_PUNCT: + *class_uchardata++ = local_negate? XCL_NOTPROP : XCL_PROP; + *class_uchardata++ = (PCRE2_UCHAR) + ((posix_class == PC_GRAPH)? PT_PXGRAPH : + (posix_class == PC_PRINT)? PT_PXPRINT : PT_PXPUNCT); + *class_uchardata++ = 0; + xclass_has_prop = TRUE; + goto CONTINUE_CLASS; + + /* 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. In the 8-bit library this is relevant only in + utf mode, since no wide characters can exist otherwise. */ + + default: +#if PCRE2_CODE_UNIT_WIDTH == 8 + if (utf) +#endif + match_all_or_no_wide_chars |= local_negate; + break; + } +#endif /* SUPPORT_UNICODE */ + + /* In the non-UCP case, or when UCP makes no difference, we build the + bit map for the POSIX class in a chunk of local store because we may + be adding and subtracting from it, and we don't want to subtract bits + that may be in the main map already. At the end we or the result into + the bit map that is being built. */ + + posix_class *= 3; + + /* Copy in the first table (always present) */ + + memcpy(pbits, cbits + posix_class_maps[posix_class], + 32 * sizeof(uint8_t)); + + /* If there is a second table, add or remove it as required. */ + + taboffset = posix_class_maps[posix_class + 1]; + tabopt = posix_class_maps[posix_class + 2]; + + if (taboffset >= 0) + { + if (tabopt >= 0) + for (i = 0; i < 32; i++) pbits[i] |= cbits[(int)i + taboffset]; + else + for (i = 0; i < 32; i++) pbits[i] &= ~cbits[(int)i + taboffset]; + } + + /* Now see if we need to remove any special characters. An option + value of 1 removes vertical space and 2 removes underscore. */ + + if (tabopt < 0) tabopt = -tabopt; + if (tabopt == 1) pbits[1] &= ~0x3c; + else if (tabopt == 2) pbits[11] &= 0x7f; + + /* Add the POSIX table or its complement into the main table that is + being built and we are done. */ + + if (local_negate) + for (i = 0; i < 32; i++) classbits[i] |= ~pbits[i]; + else + for (i = 0; i < 32; i++) classbits[i] |= pbits[i]; + + /* Every class contains at least one < 256 character. */ + + class_has_8bitchar = 1; + goto CONTINUE_CLASS; /* End of POSIX handling */ + } + + /* Other than POSIX classes, the only items we should encounter are + \d-type escapes and literal characters (possibly as ranges). */ + + if (meta == META_BIGVALUE) + { + meta = *(++pptr); + goto CLASS_LITERAL; + } + + /* Any other non-literal must be an escape */ + + if (meta >= META_END) + { + if (META_CODE(meta) != META_ESCAPE) + { +#ifdef DEBUG_SHOW_PARSED + fprintf(stderr, "** Unrecognized parsed pattern item 0x%.8x " + "in character class\n", meta); +#endif + *errorcodeptr = ERR89; /* Internal error - unrecognized. */ + return 0; + } + escape = META_DATA(meta); + + /* Every class contains at least one < 256 character. */ + + class_has_8bitchar++; + + switch(escape) + { + case ESC_d: + for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_digit]; + break; + + case ESC_D: + should_flip_negation = TRUE; + for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_digit]; + break; + + case ESC_w: + for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_word]; + break; + + case ESC_W: + should_flip_negation = TRUE; + for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_word]; + break; + + /* Perl 5.004 onwards omitted VT from \s, but restored it at Perl + 5.18. Before PCRE 8.34, we had to preserve the VT bit if it was + previously set by something earlier in the character class. + Luckily, the value of CHAR_VT is 0x0b in both ASCII and EBCDIC, so + we could just adjust the appropriate bit. From PCRE 8.34 we no + longer treat \s and \S specially. */ + + case ESC_s: + for (i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_space]; + break; + + case ESC_S: + should_flip_negation = TRUE; + for (i = 0; i < 32; i++) classbits[i] |= ~cbits[i+cbit_space]; + break; + + /* When adding the horizontal or vertical space lists to a class, or + their complements, disable PCRE2_CASELESS, because it justs wastes + time, and in the "not-x" UTF cases can create unwanted duplicates in + the XCLASS list (provoked by characters that have more than one other + case and by both cases being in the same "not-x" sublist). */ + + case ESC_h: + (void)add_list_to_class(classbits, &class_uchardata, + options & ~PCRE2_CASELESS, cb, PRIV(hspace_list), NOTACHAR); + break; + + case ESC_H: + (void)add_not_list_to_class(classbits, &class_uchardata, + options & ~PCRE2_CASELESS, cb, PRIV(hspace_list)); + break; + + case ESC_v: + (void)add_list_to_class(classbits, &class_uchardata, + options & ~PCRE2_CASELESS, cb, PRIV(vspace_list), NOTACHAR); + break; + + case ESC_V: + (void)add_not_list_to_class(classbits, &class_uchardata, + options & ~PCRE2_CASELESS, cb, PRIV(vspace_list)); + break; + + /* If Unicode is not supported, \P and \p are not allowed and are + faulted at parse time, so will never appear here. */ + +#ifdef SUPPORT_UNICODE + case ESC_p: + case ESC_P: + { + uint32_t ptype = *(++pptr) >> 16; + uint32_t pdata = *pptr & 0xffff; + *class_uchardata++ = (escape == ESC_p)? XCL_PROP : XCL_NOTPROP; + *class_uchardata++ = ptype; + *class_uchardata++ = pdata; + xclass_has_prop = TRUE; + class_has_8bitchar--; /* Undo! */ + } + break; +#endif + } + + goto CONTINUE_CLASS; + } /* End handling \d-type escapes */ + + /* A literal character may be followed by a range meta. At parse time + there are checks for out-of-order characters, for ranges where the two + characters are equal, and for hyphens that cannot indicate a range. At + this point, therefore, no checking is needed. */ + + else + { + uint32_t c, d; + + CLASS_LITERAL: + c = d = meta; + + /* Remember if \r or \n were explicitly used */ + + if (c == CHAR_CR || c == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF; + + /* Process a character range */ + + if (pptr[1] == META_RANGE_LITERAL || pptr[1] == META_RANGE_ESCAPED) + { +#ifdef EBCDIC + BOOL range_is_literal = (pptr[1] == META_RANGE_LITERAL); +#endif + pptr += 2; + d = *pptr; + if (d == META_BIGVALUE) d = *(++pptr); + + /* Remember an explicit \r or \n, and add the range to the class. */ + + 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 && + (d <= CHAR_z) == (d <= CHAR_z)) + { + uint32_t uc = (d <= CHAR_z)? 0 : 64; + uint32_t C = d - 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 + /* Not an EBCDIC special range */ + + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, c, d); + goto CONTINUE_CLASS; /* Go get the next char in the class */ + } /* End of range handling */ + + + /* Handle a single character. */ + + class_has_8bitchar += + add_to_class(classbits, &class_uchardata, options, cb, meta, meta); + } + + /* Continue to the next item in the class. */ + + CONTINUE_CLASS: + +#ifdef SUPPORT_WIDE_CHARS + /* If any wide characters or Unicode properties have been encountered, + set xclass = TRUE. Then, in the pre-compile phase, accumulate the length + of the extra data and reset the pointer. This is so that very large + classes that contain a zillion wide characters or Unicode property tests + do not overwrite the workspace (which is on the stack). */ + + if (class_uchardata > class_uchardata_base) + { + xclass = TRUE; + if (lengthptr != NULL) + { + *lengthptr += class_uchardata - class_uchardata_base; + class_uchardata = class_uchardata_base; + } + } +#endif + + continue; /* Needed to avoid error when not supporting wide chars */ + } /* End of main class-processing loop */ + + /* If this class is the first thing in the branch, there can be no first + char setting, whatever the repeat count. Any reqcu setting must remain + unchanged after any kind of repeat. */ + + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + + /* 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. + This applies only in UTF and 16-bit and 32-bit non-UTF modes, since there + cannot be any wide characters in 8-bit non-UTF mode. + + When there *are* properties in a positive UTF-8 or any 16-bit or 32_bit + class where \S etc is present without PCRE2_UCP, causing an extended class + to be compiled, we make sure that all characters > 255 are included by + forcing match_all_or_no_wide_chars to be true. + + If, when generating an xclass, there are no characters < 256, we can omit + the bitmap in the actual compiled code. */ + +#ifdef SUPPORT_WIDE_CHARS /* Defined for 16/32 bits, or 8-bit with Unicode */ + if (xclass && ( +#ifdef SUPPORT_UNICODE + (options & PCRE2_UCP) != 0 || +#endif + xclass_has_prop || !should_flip_negation)) + { + if (match_all_or_no_wide_chars || ( +#if PCRE2_CODE_UNIT_WIDTH == 8 + utf && +#endif + should_flip_negation && !negate_class && (options & PCRE2_UCP) == 0)) + { + *class_uchardata++ = XCL_RANGE; + if (utf) /* Will always be utf in the 8-bit library */ + { + class_uchardata += PRIV(ord2utf)(0x100, class_uchardata); + class_uchardata += PRIV(ord2utf)(MAX_UTF_CODE_POINT, class_uchardata); + } + else /* Can only happen for the 16-bit & 32-bit libraries */ + { +#if PCRE2_CODE_UNIT_WIDTH == 16 + *class_uchardata++ = 0x100; + *class_uchardata++ = 0xffffu; +#elif PCRE2_CODE_UNIT_WIDTH == 32 + *class_uchardata++ = 0x100; + *class_uchardata++ = 0xffffffffu; +#endif + } + } + *class_uchardata++ = XCL_END; /* Marks the end of extra data */ + *code++ = OP_XCLASS; + code += LINK_SIZE; + *code = negate_class? XCL_NOT:0; + if (xclass_has_prop) *code |= XCL_HASPROP; + + /* If the map is required, move up the extra data to make room for it; + otherwise just move the code pointer to the end of the extra data. */ + + if (class_has_8bitchar > 0) + { + *code++ |= XCL_MAP; + (void)memmove(code + (32 / sizeof(PCRE2_UCHAR)), code, + CU2BYTES(class_uchardata - code)); + if (negate_class && !xclass_has_prop) + for (i = 0; i < 32; i++) classbits[i] = ~classbits[i]; + memcpy(code, classbits, 32); + code = class_uchardata + (32 / sizeof(PCRE2_UCHAR)); + } + else code = class_uchardata; + + /* Now fill in the complete length of the item */ + + PUT(previous, 1, (int)(code - previous)); + break; /* End of class handling */ + } +#endif /* SUPPORT_WIDE_CHARS */ + + /* If there are no characters > 255, or they are all to be included or + excluded, set the opcode to OP_CLASS or OP_NCLASS, depending on whether the + whole class was negated and whether there were negative specials such as \S + (non-UCP) in the class. Then copy the 32-byte map into the code vector, + negating it if necessary. */ + + *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS; + if (lengthptr == NULL) /* Save time in the pre-compile phase */ + { + if (negate_class) + for (i = 0; i < 32; i++) classbits[i] = ~classbits[i]; + memcpy(code, classbits, 32); + } + code += 32 / sizeof(PCRE2_UCHAR); + break; /* End of class processing */ + + + /* ===================================================================*/ + /* Deal with (*VERB)s. */ + + /* Check for open captures before ACCEPT and close those that are within + the same assertion level, also converting ACCEPT to ASSERT_ACCEPT in an + assertion. In the first pass, just accumulate the length required; + otherwise hitting (*ACCEPT) inside many nested parentheses can cause + workspace overflow. Do not set firstcu after *ACCEPT. */ + + case META_ACCEPT: + cb->had_accept = TRUE; + for (oc = cb->open_caps; + oc != NULL && oc->assert_depth >= cb->assert_depth; + oc = oc->next) + { + if (lengthptr != NULL) + { + *lengthptr += CU2BYTES(1) + IMM2_SIZE; + } + else + { + *code++ = OP_CLOSE; + PUT2INC(code, 0, oc->number); + } + } + *code++ = (cb->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + break; + + case META_PRUNE: + case META_SKIP: + cb->had_pruneorskip = TRUE; + /* Fall through */ + case META_COMMIT: + case META_FAIL: + *code++ = verbops[(meta - META_MARK) >> 16]; + break; + + case META_THEN: + cb->external_flags |= PCRE2_HASTHEN; + *code++ = OP_THEN; + break; + + /* Handle verbs with arguments. Arguments can be very long, especially in + 16- and 32-bit modes, and can overflow the workspace in the first pass. + However, the argument length is constrained to be small enough to fit in + one code unit. This check happens in parse_regex(). In the first pass, + instead of putting the argument into memory, we just update the length + counter and set up an empty argument. */ + + case META_THEN_ARG: + cb->external_flags |= PCRE2_HASTHEN; + goto VERB_ARG; + + case META_PRUNE_ARG: + case META_SKIP_ARG: + cb->had_pruneorskip = TRUE; + /* Fall through */ + case META_MARK: + case META_COMMIT_ARG: + VERB_ARG: + *code++ = verbops[(meta - META_MARK) >> 16]; + /* The length is in characters. */ + verbarglen = *(++pptr); + verbculen = 0; + tempcode = code++; + for (i = 0; i < (int)verbarglen; i++) + { + meta = *(++pptr); +#ifdef SUPPORT_UNICODE + if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else +#endif + { + mclength = 1; + mcbuffer[0] = meta; + } + if (lengthptr != NULL) *lengthptr += mclength; else + { + memcpy(code, mcbuffer, CU2BYTES(mclength)); + code += mclength; + verbculen += mclength; + } + } + + *tempcode = verbculen; /* Fill in the code unit length */ + *code++ = 0; /* Terminating zero */ + break; + + + /* ===================================================================*/ + /* Handle options change. The new setting must be passed back for use in + subsequent branches. Reset the greedy defaults and the case value for + firstcu and reqcu. */ + + case META_OPTIONS: + *optionsptr = options = *(++pptr); + greedy_default = ((options & PCRE2_UNGREEDY) != 0); + greedy_non_default = greedy_default ^ 1; + req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS : 0; + break; + + + /* ===================================================================*/ + /* Handle conditional subpatterns. The case of (?(Rdigits) is ambiguous + because it could be a numerical check on recursion, or a name check on a + group's being set. The pre-pass sets up META_COND_RNUMBER as a name so that + we can handle it either way. We first try for a name; if not found, process + the number. */ + + case META_COND_RNUMBER: /* (?(Rdigits) */ + case META_COND_NAME: /* (?(name) or (?'name') or ?() */ + case META_COND_RNAME: /* (?(R&name) - test for recursion */ + bravalue = OP_COND; + { + int count, index; + PCRE2_SPTR name; + named_group *ng = cb->named_groups; + uint32_t length = *(++pptr); + + GETPLUSOFFSET(offset, pptr); + name = cb->start_pattern + offset; + + /* In the first pass, the names generated in the pre-pass are available, + but the main name table has not yet been created. Scan the list of names + generated in the pre-pass in order to get a number and whether or not + this name is duplicated. If it is not duplicated, we can handle it as a + numerical group. */ + + for (i = 0; i < cb->names_found; i++, ng++) + { + if (length == ng->length && + PRIV(strncmp)(name, ng->name, length) == 0) + { + if (!ng->isdup) + { + code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF; + PUT2(code, 2+LINK_SIZE, ng->number); + if (ng->number > cb->top_backref) cb->top_backref = ng->number; + skipunits = 1+IMM2_SIZE; + goto GROUP_PROCESS_NOTE_EMPTY; + } + break; /* Found a duplicated name */ + } + } + + /* If the name was not found we have a bad reference, unless we are + dealing with R, which is treated as a recursion test by number. + */ + + if (i >= cb->names_found) + { + groupnumber = 0; + if (meta == META_COND_RNUMBER) + { + for (i = 1; i < (int)length; i++) + { + groupnumber = groupnumber * 10 + name[i] - CHAR_0; + if (groupnumber > MAX_GROUP_NUMBER) + { + *errorcodeptr = ERR61; + cb->erroroffset = offset + i; + return 0; + } + } + } + + if (meta != META_COND_RNUMBER || groupnumber > cb->bracount) + { + *errorcodeptr = ERR15; + cb->erroroffset = offset; + return 0; + } + + /* (?Rdigits) treated as a recursion reference by number. A value of + zero (which is the result of both (?R) and (?R0)) means "any", and is + translated into RREF_ANY (which is 0xffff). */ + + if (groupnumber == 0) groupnumber = RREF_ANY; + code[1+LINK_SIZE] = OP_RREF; + PUT2(code, 2+LINK_SIZE, groupnumber); + skipunits = 1+IMM2_SIZE; + goto GROUP_PROCESS_NOTE_EMPTY; + } + + /* A duplicated name was found. Note that if an R name is found + (META_COND_RNUMBER), it is a reference test, not a recursion test. */ + + code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF; + + /* We have a duplicated name. In the compile pass we have to search the + main table in order to get the index and count values. */ + + count = 0; /* Values for first pass (avoids compiler warning) */ + index = 0; + if (lengthptr == NULL && !find_dupname_details(name, length, &index, + &count, errorcodeptr, cb)) return 0; + + /* Add one to the opcode to change CREF/RREF into DNCREF/DNRREF and + insert appropriate data values. */ + + code[1+LINK_SIZE]++; + skipunits = 1+2*IMM2_SIZE; + PUT2(code, 2+LINK_SIZE, index); + PUT2(code, 2+LINK_SIZE+IMM2_SIZE, count); + } + goto GROUP_PROCESS_NOTE_EMPTY; + + /* The DEFINE condition is always false. It's internal groups may never + be called, so matched_char must remain false, hence the jump to + GROUP_PROCESS rather than GROUP_PROCESS_NOTE_EMPTY. */ + + case META_COND_DEFINE: + bravalue = OP_COND; + GETPLUSOFFSET(offset, pptr); + code[1+LINK_SIZE] = OP_DEFINE; + skipunits = 1; + goto GROUP_PROCESS; + + /* Conditional test of a group's being set. */ + + case META_COND_NUMBER: + bravalue = OP_COND; + GETPLUSOFFSET(offset, pptr); + groupnumber = *(++pptr); + if (groupnumber > cb->bracount) + { + *errorcodeptr = ERR15; + cb->erroroffset = offset; + return 0; + } + if (groupnumber > cb->top_backref) cb->top_backref = groupnumber; + offset -= 2; /* Point at initial ( for too many branches error */ + code[1+LINK_SIZE] = OP_CREF; + skipunits = 1+IMM2_SIZE; + PUT2(code, 2+LINK_SIZE, groupnumber); + goto GROUP_PROCESS_NOTE_EMPTY; + + /* Test for the PCRE2 version. */ + + case META_COND_VERSION: + bravalue = OP_COND; + if (pptr[1] > 0) + code[1+LINK_SIZE] = ((PCRE2_MAJOR > pptr[2]) || + (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR >= pptr[3]))? + OP_TRUE : OP_FALSE; + else + code[1+LINK_SIZE] = (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR == pptr[3])? + OP_TRUE : OP_FALSE; + skipunits = 1; + pptr += 3; + goto GROUP_PROCESS_NOTE_EMPTY; + + /* The condition is an assertion, possibly preceded by a callout. */ + + case META_COND_ASSERT: + bravalue = OP_COND; + goto GROUP_PROCESS_NOTE_EMPTY; + + + /* ===================================================================*/ + /* Handle all kinds of nested bracketed groups. The non-capturing, + non-conditional cases are here; others come to GROUP_PROCESS via goto. */ + + case META_LOOKAHEAD: + bravalue = OP_ASSERT; + cb->assert_depth += 1; + goto GROUP_PROCESS; + + /* Optimize (?!) to (*FAIL) unless it is quantified - which is a weird + thing to do, but Perl allows all assertions to be quantified, and when + they contain capturing parentheses there may be a potential use for + this feature. Not that that applies to a quantified (?!) but we allow + it for uniformity. */ + + case META_LOOKAHEADNOT: + if (pptr[1] == META_KET && + (pptr[2] < META_ASTERISK || pptr[2] > META_MINMAX_QUERY)) + { + *code++ = OP_FAIL; + pptr++; + } + else + { + bravalue = OP_ASSERT_NOT; + cb->assert_depth += 1; + goto GROUP_PROCESS; + } + break; + + case META_LOOKBEHIND: + bravalue = OP_ASSERTBACK; + cb->assert_depth += 1; + goto GROUP_PROCESS; + + case META_LOOKBEHINDNOT: + bravalue = OP_ASSERTBACK_NOT; + cb->assert_depth += 1; + goto GROUP_PROCESS; + + case META_ATOMIC: + bravalue = OP_ONCE; + goto GROUP_PROCESS_NOTE_EMPTY; + + case META_NOCAPTURE: + bravalue = OP_BRA; + /* Fall through */ + + /* Process nested bracketed regex. The nesting depth is maintained for the + benefit of the stackguard function. The test for too deep nesting is now + done in parse_regex(). Assertion and DEFINE groups come to GROUP_PROCESS; + others come to GROUP_PROCESS_NOTE_EMPTY, to indicate that we need to take + note of whether or not they may match an empty string. */ + + GROUP_PROCESS_NOTE_EMPTY: + note_group_empty = TRUE; + + GROUP_PROCESS: + cb->parens_depth += 1; + *code = bravalue; + pptr++; + tempcode = code; + tempreqvary = cb->req_varyopt; /* Save value before group */ + length_prevgroup = 0; /* Initialize for pre-compile phase */ + + if ((group_return = + compile_regex( + options, /* The option state */ + &tempcode, /* Where to put code (updated) */ + &pptr, /* Input pointer (updated) */ + errorcodeptr, /* Where to put an error message */ + skipunits, /* Skip over bracket number */ + &subfirstcu, /* For possible first char */ + &subfirstcuflags, + &subreqcu, /* For possible last char */ + &subreqcuflags, + bcptr, /* Current branch chain */ + cb, /* Compile data block */ + (lengthptr == NULL)? NULL : /* Actual compile phase */ + &length_prevgroup /* Pre-compile phase */ + )) == 0) + return 0; /* Error */ + + cb->parens_depth -= 1; + + /* If that was a non-conditional significant group (not an assertion, not a + DEFINE) that matches at least one character, then the current item matches + a character. Conditionals are handled below. */ + + if (note_group_empty && bravalue != OP_COND && group_return > 0) + matched_char = TRUE; + + /* If we've just compiled an assertion, pop the assert depth. */ + + if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT) + cb->assert_depth -= 1; + + /* At the end of compiling, code is still pointing to the start of the + group, while tempcode has been updated to point past the end of the group. + The parsed pattern pointer (pptr) is on the closing META_KET. + + If this is a conditional bracket, check that there are no more than + two branches in the group, or just one if it's a DEFINE group. We do this + in the real compile phase, not in the pre-pass, where the whole group may + not be available. */ + + if (bravalue == OP_COND && lengthptr == NULL) + { + PCRE2_UCHAR *tc = code; + int condcount = 0; + + do { + condcount++; + tc += GET(tc,1); + } + while (*tc != OP_KET); + + /* A DEFINE group is never obeyed inline (the "condition" is always + false). It must have only one branch. Having checked this, change the + opcode to OP_FALSE. */ + + if (code[LINK_SIZE+1] == OP_DEFINE) + { + if (condcount > 1) + { + cb->erroroffset = offset; + *errorcodeptr = ERR54; + return 0; + } + code[LINK_SIZE+1] = OP_FALSE; + bravalue = OP_DEFINE; /* A flag to suppress char handling below */ + } + + /* A "normal" conditional group. If there is just one branch, we must not + make use of its firstcu or reqcu, because this is equivalent to an + empty second branch. Also, it may match an empty string. If there are two + branches, this item must match a character if the group must. */ + + else + { + if (condcount > 2) + { + cb->erroroffset = offset; + *errorcodeptr = ERR27; + return 0; + } + if (condcount == 1) subfirstcuflags = subreqcuflags = REQ_NONE; + else if (group_return > 0) matched_char = TRUE; + } + } + + /* In the pre-compile phase, update the length by the length of the group, + less the brackets at either end. Then reduce the compiled code to just a + set of non-capturing brackets so that it doesn't use much memory if it is + duplicated by a quantifier.*/ + + if (lengthptr != NULL) + { + if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE) + { + *errorcodeptr = ERR20; + return 0; + } + *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE; + code++; /* This already contains bravalue */ + PUTINC(code, 0, 1 + LINK_SIZE); + *code++ = OP_KET; + PUTINC(code, 0, 1 + LINK_SIZE); + break; /* No need to waste time with special character handling */ + } + + /* Otherwise update the main code pointer to the end of the group. */ + + code = tempcode; + + /* For a DEFINE group, required and first character settings are not + relevant. */ + + if (bravalue == OP_DEFINE) break; + + /* Handle updating of the required and first code units for other types of + group. Update for normal brackets of all kinds, and conditions with two + branches (see code above). If the bracket is followed by a quantifier with + zero repeat, we have to back off. Hence the definition of zeroreqcu and + zerofirstcu outside the main loop so that they can be accessed for the back + off. */ + + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + groupsetfirstcu = FALSE; + + if (bravalue >= OP_ONCE) /* Not an assertion */ + { + /* If we have not yet set a firstcu in this branch, take it from the + subpattern, remembering that it was set here so that a repeat of more + than one can replicate it as reqcu if necessary. If the subpattern has + no firstcu, set "none" for the whole branch. In both cases, a zero + repeat forces firstcu to "none". */ + + if (firstcuflags == REQ_UNSET && subfirstcuflags != REQ_UNSET) + { + if (subfirstcuflags >= 0) + { + firstcu = subfirstcu; + firstcuflags = subfirstcuflags; + groupsetfirstcu = TRUE; + } + else firstcuflags = REQ_NONE; + zerofirstcuflags = REQ_NONE; + } + + /* If firstcu was previously set, convert the subpattern's firstcu + into reqcu if there wasn't one, using the vary flag that was in + existence beforehand. */ + + else if (subfirstcuflags >= 0 && subreqcuflags < 0) + { + subreqcu = subfirstcu; + subreqcuflags = subfirstcuflags | tempreqvary; + } + + /* If the subpattern set a required code unit (or set a first code unit + that isn't really the first code unit - see above), set it. */ + + if (subreqcuflags >= 0) + { + reqcu = subreqcu; + reqcuflags = subreqcuflags; + } + } + + /* For a forward assertion, we take the reqcu, if set, provided that the + group has also set a firstcu. This can be helpful if the pattern that + follows the assertion doesn't set a different char. For example, it's + useful for /(?=abcde).+/. We can't set firstcu for an assertion, however + because it leads to incorrect effect for patterns such as /(?=a)a.+/ when + the "real" "a" would then become a reqcu instead of a firstcu. This is + overcome by a scan at the end if there's no firstcu, looking for an + asserted first char. A similar effect for patterns like /(?=.*X)X$/ means + we must only take the reqcu when the group also set a firstcu. Otherwise, + in that example, 'X' ends up set for both. */ + + else if (bravalue == OP_ASSERT && subreqcuflags >= 0 && + subfirstcuflags >= 0) + { + reqcu = subreqcu; + reqcuflags = subreqcuflags; + } + + break; /* End of nested group handling */ + + + /* ===================================================================*/ + /* Handle named backreferences and recursions. */ + + case META_BACKREF_BYNAME: + case META_RECURSE_BYNAME: + { + int count, index; + PCRE2_SPTR name; + BOOL is_dupname = FALSE; + named_group *ng = cb->named_groups; + uint32_t length = *(++pptr); + + GETPLUSOFFSET(offset, pptr); + name = cb->start_pattern + offset; + + /* In the first pass, the names generated in the pre-pass are available, + but the main name table has not yet been created. Scan the list of names + generated in the pre-pass in order to get a number and whether or not + this name is duplicated. */ + + groupnumber = 0; + for (i = 0; i < cb->names_found; i++, ng++) + { + if (length == ng->length && + PRIV(strncmp)(name, ng->name, length) == 0) + { + is_dupname = ng->isdup; + groupnumber = ng->number; + + /* For a recursion, that's all that is needed. We can now go to + the code above that handles numerical recursion, applying it to + the first group with the given name. */ + + if (meta == META_RECURSE_BYNAME) + { + meta_arg = groupnumber; + goto HANDLE_NUMERICAL_RECURSION; + } + + /* For a back reference, update the back reference map and the + maximum back reference. Then, for each group, we must check to + see if it is recursive, that is, it is inside the group that it + references. A flag is set so that the group can be made atomic. + */ + + cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1; + if (groupnumber > cb->top_backref) + cb->top_backref = groupnumber; + + for (oc = cb->open_caps; oc != NULL; oc = oc->next) + { + if (oc->number == groupnumber) + { + oc->flag = TRUE; + break; + } + } + } + } + + /* If the name was not found we have a bad reference. */ + + if (groupnumber == 0) + { + *errorcodeptr = ERR15; + cb->erroroffset = offset; + return 0; + } + + /* If a back reference name is not duplicated, we can handle it as + a numerical reference. */ + + if (!is_dupname) + { + meta_arg = groupnumber; + goto HANDLE_SINGLE_REFERENCE; + } + + /* If a back reference name is duplicated, we generate a different + opcode to a numerical back reference. In the second pass we must + search for the index and count in the final name table. */ + + count = 0; /* Values for first pass (avoids compiler warning) */ + index = 0; + if (lengthptr == NULL && !find_dupname_details(name, length, &index, + &count, errorcodeptr, cb)) return 0; + + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + *code++ = ((options & PCRE2_CASELESS) != 0)? OP_DNREFI : OP_DNREF; + PUT2INC(code, 0, index); + PUT2INC(code, 0, count); + } + break; + + + /* ===================================================================*/ + /* Handle a numerical callout. */ + + case META_CALLOUT_NUMBER: + code[0] = OP_CALLOUT; + PUT(code, 1, pptr[1]); /* Offset to next pattern item */ + PUT(code, 1 + LINK_SIZE, pptr[2]); /* Length of next pattern item */ + code[1 + 2*LINK_SIZE] = pptr[3]; + pptr += 3; + code += PRIV(OP_lengths)[OP_CALLOUT]; + break; + + + /* ===================================================================*/ + /* Handle a callout with a string argument. In the pre-pass we just compute + the length without generating anything. The length in pptr[3] includes both + delimiters; in the actual compile only the first one is copied, but a + terminating zero is added. Any doubled delimiters within the string make + this an overestimate, but it is not worth bothering about. */ + + case META_CALLOUT_STRING: + if (lengthptr != NULL) + { + *lengthptr += pptr[3] + (1 + 4*LINK_SIZE); + pptr += 3; + SKIPOFFSET(pptr); + } + + /* In the real compile we can copy the string. The starting delimiter is + included so that the client can discover it if they want. We also pass the + start offset to help a script language give better error messages. */ + + else + { + PCRE2_SPTR pp; + uint32_t delimiter; + uint32_t length = pptr[3]; + PCRE2_UCHAR *callout_string = code + (1 + 4*LINK_SIZE); + + code[0] = OP_CALLOUT_STR; + PUT(code, 1, pptr[1]); /* Offset to next pattern item */ + PUT(code, 1 + LINK_SIZE, pptr[2]); /* Length of next pattern item */ + + pptr += 3; + GETPLUSOFFSET(offset, pptr); /* Offset to string in pattern */ + pp = cb->start_pattern + offset; + delimiter = *callout_string++ = *pp++; + if (delimiter == CHAR_LEFT_CURLY_BRACKET) + delimiter = CHAR_RIGHT_CURLY_BRACKET; + PUT(code, 1 + 3*LINK_SIZE, (int)(offset + 1)); /* One after delimiter */ + + /* The syntax of the pattern was checked in the parsing scan. The length + includes both delimiters, but we have passed the opening one just above, + so we reduce length before testing it. The test is for > 1 because we do + not want to copy the final delimiter. This also ensures that pp[1] is + accessible. */ + + while (--length > 1) + { + if (*pp == delimiter && pp[1] == delimiter) + { + *callout_string++ = delimiter; + pp += 2; + length--; + } + else *callout_string++ = *pp++; + } + *callout_string++ = CHAR_NUL; + + /* Set the length of the entire item, the advance to its end. */ + + PUT(code, 1 + 2*LINK_SIZE, (int)(callout_string - code)); + code = callout_string; + } + break; + + + /* ===================================================================*/ + /* Handle repetition. The different types are all sorted out in the parsing + pass. */ + + case META_MINMAX_PLUS: + case META_MINMAX_QUERY: + case META_MINMAX: + repeat_min = *(++pptr); + repeat_max = *(++pptr); + goto REPEAT; + + case META_ASTERISK: + case META_ASTERISK_PLUS: + case META_ASTERISK_QUERY: + repeat_min = 0; + repeat_max = REPEAT_UNLIMITED; + goto REPEAT; + + case META_PLUS: + case META_PLUS_PLUS: + case META_PLUS_QUERY: + repeat_min = 1; + repeat_max = REPEAT_UNLIMITED; + goto REPEAT; + + case META_QUERY: + case META_QUERY_PLUS: + case META_QUERY_QUERY: + repeat_min = 0; + repeat_max = 1; + + REPEAT: + if (previous_matched_char && repeat_min > 0) matched_char = TRUE; + + /* Remember whether this is a variable length repeat, and default to + single-char opcodes. */ + + reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY; + op_type = 0; + + /* If the repeat is {1} we can ignore it. */ + + if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT; + + /* Adjust first and required code units for a zero repeat. */ + + if (repeat_min == 0) + { + firstcu = zerofirstcu; + firstcuflags = zerofirstcuflags; + reqcu = zeroreqcu; + reqcuflags = zeroreqcuflags; + } + + /* Note the greediness and possessiveness. */ + + switch (meta) + { + case META_MINMAX_PLUS: + case META_ASTERISK_PLUS: + case META_PLUS_PLUS: + case META_QUERY_PLUS: + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + break; + + case META_MINMAX_QUERY: + case META_ASTERISK_QUERY: + case META_PLUS_QUERY: + case META_QUERY_QUERY: + repeat_type = greedy_non_default; + possessive_quantifier = FALSE; + break; + + default: + repeat_type = greedy_default; + possessive_quantifier = FALSE; + break; + } + + /* Save start of previous item, in case we have to move it up in order to + insert something before it, and remember what it was. */ + + tempcode = previous; + op_previous = *previous; + + /* Now handle repetition for the different types of item. */ + + switch (op_previous) + { + /* If previous was a character or negated character match, abolish the + item and generate a repeat item instead. If a char item has a minimum of + more than one, ensure that it is set in reqcu - it might not be if a + sequence such as x{3} is the first thing in a branch because the x will + have gone into firstcu instead. */ + + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + op_type = chartypeoffset[op_previous - OP_CHAR]; + + /* Deal with UTF characters that take up more than one code unit. */ + +#ifdef MAYBE_UTF_MULTI + if (utf && NOT_FIRSTCU(code[-1])) + { + PCRE2_UCHAR *lastchar = code - 1; + BACKCHAR(lastchar); + mclength = (uint32_t)(code - lastchar); /* Length of UTF character */ + memcpy(mcbuffer, lastchar, CU2BYTES(mclength)); /* Save the char */ + } + else +#endif /* MAYBE_UTF_MULTI */ + + /* Handle the case of a single code unit - either with no UTF support, or + with UTF disabled, or for a single-code-unit UTF character. */ + { + mcbuffer[0] = code[-1]; + mclength = 1; + if (op_previous <= OP_CHARI && repeat_min > 1) + { + reqcu = mcbuffer[0]; + reqcuflags = req_caseopt | cb->req_varyopt; + } + } + goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ + + /* If previous was a character class or a back reference, we put the + repeat stuff after it, but just skip the item if the repeat was {0,0}. */ + +#ifdef SUPPORT_WIDE_CHARS + case OP_XCLASS: +#endif + case OP_CLASS: + case OP_NCLASS: + case OP_REF: + case OP_REFI: + case OP_DNREF: + case OP_DNREFI: + + if (repeat_max == 0) + { + code = previous; + goto END_REPEAT; + } + + if (repeat_min == 0 && repeat_max == REPEAT_UNLIMITED) + *code++ = OP_CRSTAR + repeat_type; + else if (repeat_min == 1 && repeat_max == REPEAT_UNLIMITED) + *code++ = OP_CRPLUS + repeat_type; + else if (repeat_min == 0 && repeat_max == 1) + *code++ = OP_CRQUERY + repeat_type; + else + { + *code++ = OP_CRRANGE + repeat_type; + PUT2INC(code, 0, repeat_min); + if (repeat_max == REPEAT_UNLIMITED) repeat_max = 0; /* 2-byte encoding for max */ + PUT2INC(code, 0, repeat_max); + } + break; + + /* If previous is OP_FAIL, it was generated by an empty class [] + (PCRE2_ALLOW_EMPTY_CLASS is set). The other ways in which OP_FAIL can be + generated, that is by (*FAIL) or (?!), disallow a quantifier at parse + time. We can just ignore this repeat. */ + + case OP_FAIL: + goto END_REPEAT; + + /* Prior to 10.30, repeated recursions were wrapped in OP_ONCE brackets + because pcre2_match() could not handle backtracking into recursively + called groups. Now that this backtracking is available, we no longer need + to do this. However, we still need to replicate recursions as we do for + groups so as to have independent backtracking points. We can replicate + for the minimum number of repeats directly. For optional repeats we now + wrap the recursion in OP_BRA brackets and make use of the bracket + repetition. */ + + case OP_RECURSE: + + /* Generate unwrapped repeats for a non-zero minimum, except when the + minimum is 1 and the maximum unlimited, because that can be handled with + OP_BRA terminated by OP_KETRMAX/MIN. When the maximum is equal to the + minimum, we just need to generate the appropriate additional copies. + Otherwise we need to generate one more, to simulate the situation when + the minimum is zero. */ + + if (repeat_min > 0 && (repeat_min != 1 || repeat_max != REPEAT_UNLIMITED)) + { + int replicate = repeat_min; + if (repeat_min == repeat_max) replicate--; + + /* In the pre-compile phase, we don't actually do the replication. We + just adjust the length as if we had. Do some paranoid checks for + potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit + integer type when available, otherwise double. */ + + if (lengthptr != NULL) + { + PCRE2_SIZE delta = replicate*(1 + LINK_SIZE); + if ((INT64_OR_DOUBLE)replicate* + (INT64_OR_DOUBLE)(1 + LINK_SIZE) > + (INT64_OR_DOUBLE)INT_MAX || + OFLOW_MAX - *lengthptr < delta) + { + *errorcodeptr = ERR20; + return 0; + } + *lengthptr += delta; + } + + else for (i = 0; i < replicate; i++) + { + memcpy(code, previous, CU2BYTES(1 + LINK_SIZE)); + previous = code; + code += 1 + LINK_SIZE; + } + + /* If the number of repeats is fixed, we are done. Otherwise, adjust + the counts and fall through. */ + + if (repeat_min == repeat_max) break; + if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min; + repeat_min = 0; + } + + /* Wrap the recursion call in OP_BRA brackets. */ + + (void)memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE)); + op_previous = *previous = OP_BRA; + PUT(previous, 1, 2 + 2*LINK_SIZE); + previous[2 + 2*LINK_SIZE] = OP_KET; + PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE); + code += 2 + 2 * LINK_SIZE; + length_prevgroup = 3 + 3*LINK_SIZE; + group_return = -1; /* Set "may match empty string" */ + + /* Now treat as a repeated OP_BRA. */ + /* Fall through */ + + /* If previous was a bracket group, we may have to replicate it in + certain cases. Note that at this point we can encounter only the "basic" + bracket opcodes such as BRA and CBRA, as this is the place where they get + converted into the more special varieties such as BRAPOS and SBRA. + Originally, PCRE did not allow repetition of assertions, but now it does, + for Perl compatibility. */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_BRA: + case OP_CBRA: + case OP_COND: + { + int len = (int)(code - previous); + PCRE2_UCHAR *bralink = NULL; + PCRE2_UCHAR *brazeroptr = NULL; + + /* Repeating a DEFINE group (or any group where the condition is always + FALSE and there is only one branch) is pointless, but Perl allows the + syntax, so we just ignore the repeat. */ + + if (op_previous == OP_COND && previous[LINK_SIZE+1] == OP_FALSE && + previous[GET(previous, 1)] != OP_ALT) + goto END_REPEAT; + + /* There is no sense in actually repeating assertions. The only + potential use of repetition is in cases when the assertion is optional. + Therefore, if the minimum is greater than zero, just ignore the repeat. + If the maximum is not zero or one, set it to 1. */ + + if (op_previous < OP_ONCE) /* Assertion */ + { + if (repeat_min > 0) goto END_REPEAT; + if (repeat_max > 1) repeat_max = 1; + } + + /* The case of a zero minimum is special because of the need to stick + OP_BRAZERO in front of it, and because the group appears once in the + data, whereas in other cases it appears the minimum number of times. For + this reason, it is simplest to treat this case separately, as otherwise + the code gets far too messy. There are several special subcases when the + minimum is zero. */ + + if (repeat_min == 0) + { + /* If the maximum is also zero, we used to just omit the group from + the output altogether, like this: + + ** if (repeat_max == 0) + ** { + ** code = previous; + ** goto END_REPEAT; + ** } + + However, that fails when a group or a subgroup within it is + referenced as a subroutine from elsewhere in the pattern, so now we + stick in OP_SKIPZERO in front of it so that it is skipped on + execution. As we don't have a list of which groups are referenced, we + cannot do this selectively. + + If the maximum is 1 or unlimited, we just have to stick in the + BRAZERO and do no more at this point. */ + + if (repeat_max <= 1 || repeat_max == REPEAT_UNLIMITED) + { + (void)memmove(previous + 1, previous, CU2BYTES(len)); + code++; + if (repeat_max == 0) + { + *previous++ = OP_SKIPZERO; + goto END_REPEAT; + } + brazeroptr = previous; /* Save for possessive optimizing */ + *previous++ = OP_BRAZERO + repeat_type; + } + + /* If the maximum is greater than 1 and limited, we have to replicate + in a nested fashion, sticking OP_BRAZERO before each set of brackets. + 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. */ + + else + { + int linkoffset; + (void)memmove(previous + 2 + LINK_SIZE, previous, CU2BYTES(len)); + code += 2 + LINK_SIZE; + *previous++ = OP_BRAZERO + repeat_type; + *previous++ = OP_BRA; + + /* We chain together the bracket link offset fields that have to be + filled in later when the ends of the brackets are reached. */ + + linkoffset = (bralink == NULL)? 0 : (int)(previous - bralink); + bralink = previous; + PUTINC(previous, 0, linkoffset); + } + + if (repeat_max != REPEAT_UNLIMITED) repeat_max--; + } + + /* 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. */ + + else + { + if (repeat_min > 1) + { + /* In the pre-compile phase, we don't actually do the replication. + We just adjust the length as if we had. Do some paranoid checks for + potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit + integer type when available, otherwise double. */ + + if (lengthptr != NULL) + { + PCRE2_SIZE delta = (repeat_min - 1)*length_prevgroup; + if ((INT64_OR_DOUBLE)(repeat_min - 1)* + (INT64_OR_DOUBLE)length_prevgroup > + (INT64_OR_DOUBLE)INT_MAX || + OFLOW_MAX - *lengthptr < delta) + { + *errorcodeptr = ERR20; + return 0; + } + *lengthptr += delta; + } + + /* This is compiling for real. If there is a set first code unit + for the group, and we have not yet set a "required code unit", set + it. */ + + else + { + if (groupsetfirstcu && reqcuflags < 0) + { + reqcu = firstcu; + reqcuflags = firstcuflags; + } + for (i = 1; (uint32_t)i < repeat_min; i++) + { + memcpy(code, previous, CU2BYTES(len)); + code += len; + } + } + } + + if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min; + } + + /* This code is common to both the zero and non-zero minimum cases. If + the maximum is limited, it replicates the group in a nested fashion, + remembering the bracket starts on a stack. In the case of a zero + minimum, the first one was set up above. In all cases the repeat_max + now specifies the number of additional copies needed. Again, we must + remember to replicate entries on the forward reference list. */ + + if (repeat_max != REPEAT_UNLIMITED) + { + /* In the pre-compile phase, we don't actually do the replication. We + just adjust the length as if we had. For each repetition we must add + 1 to the length for BRAZERO and for all but the last repetition we + must add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some + paranoid checks to avoid integer overflow. The INT64_OR_DOUBLE type + is a 64-bit integer type when available, otherwise double. */ + + if (lengthptr != NULL && repeat_max > 0) + { + PCRE2_SIZE delta = repeat_max*(length_prevgroup + 1 + 2 + 2*LINK_SIZE) - + 2 - 2*LINK_SIZE; /* Last one doesn't nest */ + if ((INT64_OR_DOUBLE)repeat_max * + (INT64_OR_DOUBLE)(length_prevgroup + 1 + 2 + 2*LINK_SIZE) + > (INT64_OR_DOUBLE)INT_MAX || + OFLOW_MAX - *lengthptr < delta) + { + *errorcodeptr = ERR20; + return 0; + } + *lengthptr += delta; + } + + /* This is compiling for real */ + + else for (i = repeat_max - 1; i >= 0; i--) + { + *code++ = OP_BRAZERO + repeat_type; + + /* All but the final copy start a new nesting, maintaining the + chain of brackets outstanding. */ + + if (i != 0) + { + int linkoffset; + *code++ = OP_BRA; + linkoffset = (bralink == NULL)? 0 : (int)(code - bralink); + bralink = code; + PUTINC(code, 0, linkoffset); + } + + memcpy(code, previous, CU2BYTES(len)); + code += len; + } + + /* Now chain through the pending brackets, and fill in their length + fields (which are holding the chain links pro tem). */ + + while (bralink != NULL) + { + int oldlinkoffset; + int linkoffset = (int)(code - bralink + 1); + PCRE2_UCHAR *bra = code - linkoffset; + oldlinkoffset = GET(bra, 1); + bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; + *code++ = OP_KET; + PUTINC(code, 0, linkoffset); + PUT(bra, 1, linkoffset); + } + } + + /* If the maximum is unlimited, set a repeater in the final copy. For + ONCE brackets, that's all we need to do. However, possessively repeated + ONCE brackets can be converted into non-capturing brackets, as the + behaviour of (?:xx)++ is the same as (?>xx)++ and this saves having to + deal with possessive ONCEs specially. + + Otherwise, when we are doing the actual compile phase, check to see + whether this group is one that could match an empty string. If so, + convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so + that runtime checking can be done. [This check is also applied to ONCE + groups at runtime, but in a different way.] + + Then, if the quantifier was possessive and the bracket is not a + 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 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 + flag so that the default action below, of wrapping everything inside + atomic brackets, does not happen. When the minimum is greater than 1, + there will be earlier copies of the group, and so we still have to wrap + the whole thing. */ + + else + { + PCRE2_UCHAR *ketcode = code - 1 - LINK_SIZE; + PCRE2_UCHAR *bracode = ketcode - GET(ketcode, 1); + + /* Convert possessive ONCE brackets to non-capturing */ + + if (*bracode == OP_ONCE && possessive_quantifier) *bracode = OP_BRA; + + /* For non-possessive ONCE brackets, all we need to do is to + set the KET. */ + + if (*bracode == OP_ONCE) *ketcode = OP_KETRMAX + repeat_type; + + /* Handle non-ONCE brackets and possessive ONCEs (which have been + converted to non-capturing above). */ + + else + { + /* In the compile phase, adjust the opcode if the group can match + an empty string. For a conditional group with only one branch, the + value of group_return will not show "could be empty", so we must + check that separately. */ + + if (lengthptr == NULL) + { + if (group_return < 0) *bracode += OP_SBRA - OP_BRA; + if (*bracode == OP_COND && bracode[GET(bracode,1)] != OP_ALT) + *bracode = OP_SCOND; + } + + /* Handle possessive quantifiers. */ + + if (possessive_quantifier) + { + /* 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. */ + + if (*bracode == OP_COND || *bracode == OP_SCOND) + { + int nlen = (int)(code - bracode); + (void)memmove(bracode + 1 + LINK_SIZE, bracode, CU2BYTES(nlen)); + code += 1 + LINK_SIZE; + nlen += 1 + LINK_SIZE; + *bracode = (*bracode == OP_COND)? OP_BRAPOS : OP_SBRAPOS; + *code++ = OP_KETRPOS; + PUTINC(code, 0, nlen); + PUT(bracode, 1, nlen); + } + + /* For non-COND brackets, we modify the BRA code and use KETRPOS. */ + + else + { + *bracode += 1; /* Switch to xxxPOS opcodes */ + *ketcode = OP_KETRPOS; + } + + /* If the minimum is zero, mark it as possessive, then unset the + possessive flag when the minimum is 0 or 1. */ + + if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO; + if (repeat_min < 2) possessive_quantifier = FALSE; + } + + /* Non-possessive quantifier */ + + else *ketcode = OP_KETRMAX + repeat_type; + } + } + } + break; + + /* If previous was a character type match (\d or similar), abolish it and + create a suitable repeat item. The code is shared with single-character + repeats by setting op_type to add a suitable offset into repeat_type. + Note the the Unicode property types will be present only when + SUPPORT_UNICODE is defined, but we don't wrap the little bits of code + here because it just makes it horribly messy. */ + + default: + if (op_previous >= OP_EODN) /* Not a character type - internal error */ + { + *errorcodeptr = ERR10; + return 0; + } + else + { + int prop_type, prop_value; + PCRE2_UCHAR *oldcode; + + op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + mclength = 0; /* Not a character */ + + if (op_previous == OP_PROP || op_previous == OP_NOTPROP) + { + prop_type = previous[1]; + prop_value = previous[2]; + } + else + { + /* Come here from just above with a character in mcbuffer/mclength. */ + OUTPUT_SINGLE_REPEAT: + prop_type = prop_value = -1; + } + + /* At this point, if prop_type == prop_value == -1 we either have a + character in mcbuffer when mclength is greater than zero, or we have + mclength zero, in which case there is a non-property character type in + op_previous. If prop_type/value are not negative, we have a property + character type in op_previous. */ + + oldcode = code; /* Save where we were */ + code = previous; /* Usually overwrite previous item */ + + /* If the maximum is zero then the minimum must also be zero; Perl allows + this case, so we do too - by simply omitting the item altogether. */ + + if (repeat_max == 0) goto END_REPEAT; + + /* Combine the op_type with the repeat_type */ + + repeat_type += op_type; + + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ + + if (repeat_min == 0) + { + if (repeat_max == REPEAT_UNLIMITED) *code++ = OP_STAR + repeat_type; + else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; + else + { + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max); + } + } + + /* A repeat minimum of 1 is optimized into some special cases. If the + maximum is unlimited, we use OP_PLUS. Otherwise, the original item is + left in place and, if the maximum is greater than 1, we use OP_UPTO with + one less than the maximum. */ + + else if (repeat_min == 1) + { + if (repeat_max == REPEAT_UNLIMITED) + *code++ = OP_PLUS + repeat_type; + else + { + code = oldcode; /* Leave previous item in place */ + if (repeat_max == 1) goto END_REPEAT; + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max - 1); + } + } + + /* The case {n,n} is just an EXACT, while the general case {n,m} is + handled as an EXACT followed by an UPTO or STAR or QUERY. */ + + else + { + *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ + PUT2INC(code, 0, repeat_min); + + /* Unless repeat_max equals repeat_min, fill in the data for EXACT, + and then generate the second opcode. For a repeated Unicode property + match, there are two extra values that define the required property, + and mclength is set zero to indicate this. */ + + if (repeat_max != repeat_min) + { + if (mclength > 0) + { + memcpy(code, mcbuffer, CU2BYTES(mclength)); + code += mclength; + } + else + { + *code++ = op_previous; + if (prop_type >= 0) + { + *code++ = prop_type; + *code++ = prop_value; + } + } + + /* Now set up the following opcode */ + + if (repeat_max == REPEAT_UNLIMITED) + *code++ = OP_STAR + repeat_type; + else + { + repeat_max -= repeat_min; + if (repeat_max == 1) + { + *code++ = OP_QUERY + repeat_type; + } + else + { + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max); + } + } + } + } + + /* Fill in the character or character type for the final opcode. */ + + if (mclength > 0) + { + memcpy(code, mcbuffer, CU2BYTES(mclength)); + code += mclength; + } + else + { + *code++ = op_previous; + if (prop_type >= 0) + { + *code++ = prop_type; + *code++ = prop_value; + } + } + } + break; + } /* End of switch on different op_previous values */ + + + /* If the character following a repeat is '+', possessive_quantifier is + TRUE. For some opcodes, there are special alternative opcodes for this + case. For anything else, we wrap the entire repeated item inside OP_ONCE + brackets. Logically, the '+' notation is just syntactic sugar, taken from + Sun's Java package, but the special opcodes can optimize it. + + Some (but not all) possessively repeated subpatterns have already been + completely handled in the code just above. For them, possessive_quantifier + is always FALSE at this stage. Note that the repeated item starts at + tempcode, not at previous, which might be the first part of a string whose + (former) last char we repeated. */ + + if (possessive_quantifier) + { + int len; + + /* Possessifying an EXACT quantifier has no effect, so we can ignore it. + However, QUERY, STAR, or UPTO may follow (for quantifiers such as {5,6}, + {5,}, or {5,10}). We skip over an EXACT item; if the length of what + remains is greater than zero, there's a further opcode that can be + handled. If not, do nothing, leaving the EXACT alone. */ + + switch(*tempcode) + { + case OP_TYPEEXACT: + tempcode += PRIV(OP_lengths)[*tempcode] + + ((tempcode[1 + IMM2_SIZE] == OP_PROP + || tempcode[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); + break; + + /* CHAR opcodes are used for exacts whose count is 1. */ + + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_EXACT: + case OP_EXACTI: + case OP_NOTEXACT: + case OP_NOTEXACTI: + tempcode += PRIV(OP_lengths)[*tempcode]; +#ifdef SUPPORT_UNICODE + if (utf && HAS_EXTRALEN(tempcode[-1])) + tempcode += GET_EXTRALEN(tempcode[-1]); +#endif + break; + + /* For the class opcodes, the repeat operator appears at the end; + adjust tempcode to point to it. */ + + case OP_CLASS: + case OP_NCLASS: + tempcode += 1 + 32/sizeof(PCRE2_UCHAR); + break; + +#ifdef SUPPORT_WIDE_CHARS + case OP_XCLASS: + tempcode += GET(tempcode, 1); + break; +#endif + } + + /* If tempcode is equal to code (which points to the end of the repeated + item), it means we have skipped an EXACT item but there is no following + QUERY, STAR, or UPTO; the value of len will be 0, and we do nothing. In + all other cases, tempcode will be pointing to the repeat opcode, and will + be less than code, so the value of len will be greater than 0. */ + + len = (int)(code - tempcode); + if (len > 0) + { + unsigned int repcode = *tempcode; + + /* There is a table for possessifying opcodes, all of which are less + than OP_CALLOUT. A zero entry means there is no possessified version. + */ + + if (repcode < OP_CALLOUT && opcode_possessify[repcode] > 0) + *tempcode = opcode_possessify[repcode]; + + /* For opcode without a special possessified version, wrap the item in + ONCE brackets. */ + + else + { + (void)memmove(tempcode + 1 + LINK_SIZE, tempcode, CU2BYTES(len)); + code += 1 + LINK_SIZE; + len += 1 + LINK_SIZE; + tempcode[0] = OP_ONCE; + *code++ = OP_KET; + PUTINC(code, 0, len); + PUT(tempcode, 1, len); + } + } + } + + /* We set the "follows varying string" flag for subsequently encountered + reqcus if it isn't already set and we have just passed a varying length + item. */ + + END_REPEAT: + cb->req_varyopt |= reqvary; + break; + + + /* ===================================================================*/ + /* Handle a 32-bit data character with a value greater than META_END. */ + + case META_BIGVALUE: + pptr++; + goto NORMAL_CHAR; + + + /* ===============================================================*/ + /* Handle a back reference by number, which is the meta argument. The + pattern offsets for back references to group numbers less than 10 are held + in a special vector, to avoid using more than two parsed pattern elements + in 64-bit environments. We only need the offset to the first occurrence, + because if that doesn't fail, subsequent ones will also be OK. */ + + case META_BACKREF: + if (meta_arg < 10) offset = cb->small_ref_offset[meta_arg]; + else GETPLUSOFFSET(offset, pptr); + + if (meta_arg > cb->bracount) + { + cb->erroroffset = offset; + *errorcodeptr = ERR15; /* Non-existent subpattern */ + return 0; + } + + /* Come here from named backref handling when the reference is to a + single group (that is, not to a duplicated name). The back reference + data will have already been updated. We must disable firstcu if not + set, to cope with cases like (?=(\w+))\1: which would otherwise set ':' + later. */ + + HANDLE_SINGLE_REFERENCE: + if (firstcuflags == REQ_UNSET) zerofirstcuflags = firstcuflags = REQ_NONE; + *code++ = ((options & PCRE2_CASELESS) != 0)? OP_REFI : OP_REF; + PUT2INC(code, 0, meta_arg); + + /* Update the map of back references, and keep the highest one. We + could do this in parse_regex() for numerical back references, but not + for named back references, because we don't know the numbers to which + named back references refer. So we do it all in this function. */ + + cb->backref_map |= (meta_arg < 32)? (1u << meta_arg) : 1; + if (meta_arg > cb->top_backref) cb->top_backref = meta_arg; + + /* Check to see if this back reference is recursive, that it, it + is inside the group that it references. A flag is set so that the + group can be made atomic. */ + + for (oc = cb->open_caps; oc != NULL; oc = oc->next) + { + if (oc->number == meta_arg) + { + oc->flag = TRUE; + break; + } + } + break; + + + /* ===============================================================*/ + /* Handle recursion by inserting the number of the called group (which is + the meta argument) 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). */ + + case META_RECURSE: + GETPLUSOFFSET(offset, pptr); + if (meta_arg > cb->bracount) + { + cb->erroroffset = offset; + *errorcodeptr = ERR15; /* Non-existent subpattern */ + return 0; + } + HANDLE_NUMERICAL_RECURSION: + *code = OP_RECURSE; + PUT(code, 1, meta_arg); + code += 1 + LINK_SIZE; + groupsetfirstcu = FALSE; + cb->had_recurse = TRUE; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + break; + + + /* ===============================================================*/ + /* Handle capturing parentheses; the number is the meta argument. */ + + case META_CAPTURE: + bravalue = OP_CBRA; + skipunits = IMM2_SIZE; + PUT2(code, 1+LINK_SIZE, meta_arg); + cb->lastcapture = meta_arg; + goto GROUP_PROCESS_NOTE_EMPTY; + + + /* ===============================================================*/ + /* Handle escape sequence items. For ones like \d, the ESC_values are + arranged to be the same as the corresponding OP_values in the default case + when PCRE2_UCP is not set (which is the only case in which they will appear + here). + + Note: \Q and \E are never seen here, as they were dealt with in + parse_pattern(). Neither are numerical back references or recursions, which + were turned into META_BACKREF or META_RECURSE items, respectively. \k and + \g, when followed by names, are turned into META_BACKREF_BYNAME or + META_RECURSE_BYNAME. */ + + case META_ESCAPE: + + /* We can test for escape sequences that consume a character because their + values lie between ESC_b and ESC_Z; this may have to change if any new ones + are ever created. For these sequences, we disable the setting of a first + character if it hasn't already been set. */ + + if (meta_arg > ESC_b && meta_arg < ESC_Z) + { + matched_char = TRUE; + if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE; + } + + /* Set values to reset to if this is followed by a zero repeat. */ + + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + + /* If Unicode is not supported, \P and \p are not allowed and are + faulted at parse time, so will never appear here. */ + +#ifdef SUPPORT_UNICODE + if (meta_arg == ESC_P || meta_arg == ESC_p) + { + uint32_t ptype = *(++pptr) >> 16; + uint32_t pdata = *pptr & 0xffff; + *code++ = (meta_arg == ESC_p)? OP_PROP : OP_NOTPROP; + *code++ = ptype; + *code++ = pdata; + break; /* End META_ESCAPE */ + } +#endif + + /* For the rest (including \X when Unicode is supported - if not it's + faulted at parse time), the OP value is the escape value when PCRE2_UCP is + not set; if it is set, these escapes do not show up here because they are + converted into Unicode property tests in parse_regex(). Note that \b and \B + do a one-character lookbehind, and \A also behaves as if it does. */ + + if (meta_arg == ESC_C) cb->external_flags |= PCRE2_HASBKC; /* Record */ + if ((meta_arg == ESC_b || meta_arg == ESC_B || meta_arg == ESC_A) && + cb->max_lookbehind == 0) + cb->max_lookbehind = 1; + + /* In non-UTF mode, and for both 32-bit modes, we turn \C into OP_ALLANY + instead of OP_ANYBYTE so that it works in DFA mode and in lookbehinds. */ + +#if PCRE2_CODE_UNIT_WIDTH == 32 + *code++ = (meta_arg == ESC_C)? OP_ALLANY : meta_arg; +#else + *code++ = (!utf && meta_arg == ESC_C)? OP_ALLANY : meta_arg; +#endif + break; /* End META_ESCAPE */ + + + /* ===================================================================*/ + /* Handle an unrecognized meta value. A parsed pattern value less than + META_END is a literal. Otherwise we have a problem. */ + + default: + if (meta >= META_END) + { +#ifdef DEBUG_SHOW_PARSED + fprintf(stderr, "** Unrecognized parsed pattern item 0x%.8x\n", *pptr); +#endif + *errorcodeptr = ERR89; /* Internal error - unrecognized. */ + return 0; + } + + /* Handle a literal character. We come here by goto in the case of a + 32-bit, non-UTF character whose value is greater than META_END. */ + + NORMAL_CHAR: + meta = *pptr; /* Get the full 32 bits */ + NORMAL_CHAR_SET: /* Character is already in meta */ + matched_char = TRUE; + + /* 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. */ + +#ifdef SUPPORT_UNICODE + if (utf && (options & PCRE2_CASELESS) != 0) + { + uint32_t caseset = UCD_CASESET(meta); + if (caseset != 0) + { + *code++ = OP_PROP; + *code++ = PT_CLIST; + *code++ = caseset; + if (firstcuflags == REQ_UNSET) + firstcuflags = zerofirstcuflags = REQ_NONE; + break; /* End handling this meta item */ + } + } +#endif + + /* Caseful matches, or not one of the multicase characters. Get the + character's code units into mcbuffer, with the length in mclength. When not + in UTF mode, the length is always 1. */ + +#ifdef SUPPORT_UNICODE + if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else +#endif + { + mclength = 1; + mcbuffer[0] = meta; + } + + /* Generate the appropriate code */ + + *code++ = ((options & PCRE2_CASELESS) != 0)? OP_CHARI : OP_CHAR; + memcpy(code, mcbuffer, CU2BYTES(mclength)); + code += mclength; + + /* Remember if \r or \n were seen */ + + if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL) + cb->external_flags |= PCRE2_HASCRORLF; + + /* Set the first and required code units appropriately. If no previous + first code unit, set it from this character, but revert to none on a zero + repeat. Otherwise, leave the firstcu value alone, and don't change it on + a zero repeat. */ + + if (firstcuflags == REQ_UNSET) + { + zerofirstcuflags = REQ_NONE; + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + + /* If the character is more than one code unit long, we can set firstcu + only if it is not to be matched caselessly. */ + + if (mclength == 1 || req_caseopt == 0) + { + firstcu = mcbuffer[0]; + firstcuflags = req_caseopt; + if (mclength != 1) + { + reqcu = code[-1]; + reqcuflags = cb->req_varyopt; + } + } + else firstcuflags = reqcuflags = REQ_NONE; + } + + /* firstcu was previously set; we can set reqcu only if the length is + 1 or the matching is caseful. */ + + else + { + zerofirstcu = firstcu; + zerofirstcuflags = firstcuflags; + zeroreqcu = reqcu; + zeroreqcuflags = reqcuflags; + if (mclength == 1 || req_caseopt == 0) + { + reqcu = code[-1]; + reqcuflags = req_caseopt | cb->req_varyopt; + } + } + break; /* End default meta handling */ + } /* End of big switch */ + } /* End of big loop */ + +/* Control never reaches here. */ +} + + + +/************************************************* +* Compile regex: a sequence of alternatives * +*************************************************/ + +/* On entry, pptr is pointing past the bracket meta, but on return it points to +the closing bracket or META_END. The code variable is pointing at the code unit +into which the BRA operator has been stored. This function is used during the +pre-compile phase when we are trying to find out the amount of memory needed, +as well as during the real compile phase. The value of lengthptr distinguishes +the two phases. + +Arguments: + options option bits, including any changes for this subpattern + codeptr -> the address of the current code pointer + pptrptr -> the address of the current parsed pattern pointer + errorcodeptr -> pointer to error code variable + skipunits skip this many code units at start (for brackets and OP_COND) + firstcuptr place to put the first required code unit + firstcuflagsptr place to put the first code unit flags, or a negative number + reqcuptr place to put the last required code unit + reqcuflagsptr place to put the last required code unit flags, or a negative number + bcptr pointer to the chain of currently open branches + cb points to the data block with tables pointers etc. + lengthptr NULL during the real compile phase + points to length accumulator during pre-compile phase + +Returns: 0 There has been an error + +1 Success, this group must match at least one character + -1 Success, this group may match an empty string +*/ + +static int +compile_regex(uint32_t options, PCRE2_UCHAR **codeptr, uint32_t **pptrptr, + int *errorcodeptr, uint32_t skipunits, uint32_t *firstcuptr, + int32_t *firstcuflagsptr, uint32_t *reqcuptr,int32_t *reqcuflagsptr, + branch_chain *bcptr, compile_block *cb, PCRE2_SIZE *lengthptr) +{ +PCRE2_UCHAR *code = *codeptr; +PCRE2_UCHAR *last_branch = code; +PCRE2_UCHAR *start_bracket = code; +BOOL lookbehind; +open_capitem capitem; +int capnumber = 0; +int okreturn = 1; +uint32_t *pptr = *pptrptr; +uint32_t firstcu, reqcu; +uint32_t lookbehindlength; +int32_t firstcuflags, reqcuflags; +uint32_t branchfirstcu, branchreqcu; +int32_t branchfirstcuflags, branchreqcuflags; +PCRE2_SIZE length; +branch_chain bc; + +/* If set, call the external function that checks for stack availability. */ + +if (cb->cx->stack_guard != NULL && + cb->cx->stack_guard(cb->parens_depth, cb->cx->stack_guard_data)) + { + *errorcodeptr= ERR33; + return 0; + } + +/* Miscellaneous initialization */ + +bc.outer = bcptr; +bc.current_branch = code; + +firstcu = reqcu = 0; +firstcuflags = reqcuflags = REQ_UNSET; + +/* 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 +lengthptr for NULL. We cannot do this by looking at the value of 'code' at the +start and end of each alternative, because compiled items are discarded during +the pre-compile phase so that the workspace is not exceeded. */ + +length = 2 + 2*LINK_SIZE + skipunits; + +/* Remember if this is a lookbehind assertion, and if it is, save its length +and skip over the pattern offset. */ + +lookbehind = *code == OP_ASSERTBACK || *code == OP_ASSERTBACK_NOT; +if (lookbehind) + { + lookbehindlength = META_DATA(pptr[-1]); + pptr += SIZEOFFSET; + } +else lookbehindlength = 0; + +/* If this is a capturing subpattern, add to the chain of open capturing items +so that we can detect them if (*ACCEPT) is encountered. Note that only OP_CBRA +need be tested here; changing this opcode to one of its variants, e.g. +OP_SCBRAPOS, happens later, after the group has been compiled. */ + +if (*code == OP_CBRA) + { + capnumber = GET2(code, 1 + LINK_SIZE); + capitem.number = capnumber; + capitem.next = cb->open_caps; + capitem.flag = FALSE; + capitem.assert_depth = cb->assert_depth; + cb->open_caps = &capitem; + } + +/* Offset is set zero to mark that this bracket is still open */ + +PUT(code, 1, 0); +code += 1 + LINK_SIZE + skipunits; + +/* Loop for each alternative branch */ + +for (;;) + { + int branch_return; + + /* Insert OP_REVERSE if this is as lookbehind assertion. */ + + if (lookbehind && lookbehindlength > 0) + { + *code++ = OP_REVERSE; + PUTINC(code, 0, lookbehindlength); + length += 1 + LINK_SIZE; + } + + /* Now compile the branch; in the pre-compile phase its length gets added + into the length. */ + + if ((branch_return = + compile_branch(&options, &code, &pptr, errorcodeptr, &branchfirstcu, + &branchfirstcuflags, &branchreqcu, &branchreqcuflags, &bc, + cb, (lengthptr == NULL)? NULL : &length)) == 0) + return 0; + + /* If a branch can match an empty string, so can the whole group. */ + + if (branch_return < 0) okreturn = -1; + + /* In the real compile phase, there is some post-processing to be done. */ + + if (lengthptr == NULL) + { + /* If this is the first branch, the firstcu and reqcu values for the + branch become the values for the regex. */ + + if (*last_branch != OP_ALT) + { + firstcu = branchfirstcu; + firstcuflags = branchfirstcuflags; + reqcu = branchreqcu; + reqcuflags = branchreqcuflags; + } + + /* If this is not the first branch, the first char and reqcu have to + match the values from all the previous branches, except that if the + previous value for reqcu didn't have REQ_VARY set, it can still match, + and we set REQ_VARY for the regex. */ + + else + { + /* If we previously had a firstcu, but it doesn't match the new branch, + we have to abandon the firstcu for the regex, but if there was + previously no reqcu, it takes on the value of the old firstcu. */ + + if (firstcuflags != branchfirstcuflags || firstcu != branchfirstcu) + { + if (firstcuflags >= 0) + { + if (reqcuflags < 0) + { + reqcu = firstcu; + reqcuflags = firstcuflags; + } + } + firstcuflags = REQ_NONE; + } + + /* If we (now or from before) have no firstcu, a firstcu from the + branch becomes a reqcu if there isn't a branch reqcu. */ + + if (firstcuflags < 0 && branchfirstcuflags >= 0 && + branchreqcuflags < 0) + { + branchreqcu = branchfirstcu; + branchreqcuflags = branchfirstcuflags; + } + + /* Now ensure that the reqcus match */ + + if (((reqcuflags & ~REQ_VARY) != (branchreqcuflags & ~REQ_VARY)) || + reqcu != branchreqcu) + reqcuflags = REQ_NONE; + else + { + reqcu = branchreqcu; + reqcuflags |= branchreqcuflags; /* To "or" REQ_VARY */ + } + } + } + + /* Handle reaching the end of the expression, either ')' or end of pattern. + In the real compile phase, go back through the alternative branches and + reverse the chain of offsets, with the field in the BRA item now becoming an + offset to the first alternative. If there are no alternatives, it points to + the end of the group. The length in the terminating ket is always the length + of the whole bracketed item. Return leaving the pointer at the terminating + char. */ + + if (META_CODE(*pptr) != META_ALT) + { + if (lengthptr == NULL) + { + PCRE2_SIZE branch_length = code - last_branch; + do + { + PCRE2_SIZE prev_length = GET(last_branch, 1); + PUT(last_branch, 1, branch_length); + branch_length = prev_length; + last_branch -= branch_length; + } + while (branch_length > 0); + } + + /* Fill in the ket */ + + *code = OP_KET; + PUT(code, 1, (int)(code - start_bracket)); + 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. In + any event, remove the block from the chain. */ + + if (capnumber > 0) + { + if (cb->open_caps->flag) + { + (void)memmove(start_bracket + 1 + LINK_SIZE, start_bracket, + CU2BYTES(code - start_bracket)); + *start_bracket = OP_ONCE; + code += 1 + LINK_SIZE; + PUT(start_bracket, 1, (int)(code - start_bracket)); + *code = OP_KET; + PUT(code, 1, (int)(code - start_bracket)); + code += 1 + LINK_SIZE; + length += 2 + 2*LINK_SIZE; + } + cb->open_caps = cb->open_caps->next; + } + + /* Set values to pass back */ + + *codeptr = code; + *pptrptr = pptr; + *firstcuptr = firstcu; + *firstcuflagsptr = firstcuflags; + *reqcuptr = reqcu; + *reqcuflagsptr = reqcuflags; + if (lengthptr != NULL) + { + if (OFLOW_MAX - *lengthptr < length) + { + *errorcodeptr = ERR20; + return 0; + } + *lengthptr += length; + } + return okreturn; + } + + /* Another branch follows. In the pre-compile phase, we can move the code + pointer back to where it was for the start of the first branch. (That is, + pretend that each branch is the only one.) + + In the real compile phase, insert an ALT node. Its length field points back + to the previous branch while the bracket remains open. At the end the chain + is reversed. It's done like this so that the start of the bracket has a + zero offset until it is closed, making it possible to detect recursion. */ + + if (lengthptr != NULL) + { + code = *codeptr + 1 + LINK_SIZE + skipunits; + length += 1 + LINK_SIZE; + } + else + { + *code = OP_ALT; + PUT(code, 1, (int)(code - last_branch)); + bc.current_branch = last_branch = code; + code += 1 + LINK_SIZE; + } + + /* Set the lookbehind length (if not in a lookbehind the value will be zero) + and then advance past the vertical bar. */ + + lookbehindlength = META_DATA(*pptr); + pptr++; + } +/* Control never reaches here */ +} + + + +/************************************************* +* Check for anchored pattern * +*************************************************/ + +/* Try to find out if this is an anchored regular expression. Consider each +alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket +all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then +it's anchored. However, if this is a multiline pattern, then only OP_SOD will +be found, because ^ generates OP_CIRCM in that mode. + +We can also consider a regex to be anchored if OP_SOM starts all its branches. +This is the code for \G, which means "match at start of match position, taking +into account the match offset". + +A branch is also implicitly anchored if it starts with .* and DOTALL is set, +because that will try the rest of the pattern at all possible matching points, +so there is no point trying again.... er .... + +.... except when the .* appears inside capturing parentheses, and there is a +subsequent back reference to those parentheses. We haven't enough information +to catch that case precisely. + +At first, the best we could do was to detect when .* was in capturing brackets +and the highest back reference was greater than or equal to that level. +However, by keeping a bitmap of the first 31 back references, we can catch some +of the more common cases more precisely. + +... A second exception is when the .* appears inside an atomic group, because +this prevents the number of characters it matches from being adjusted. + +Arguments: + code points to start of the compiled pattern + bracket_map a bitmap of which brackets we are inside while testing; this + handles up to substring 31; after that we just have to take + the less precise approach + cb points to the compile data block + atomcount atomic group level + inassert TRUE if in an assertion + +Returns: TRUE or FALSE +*/ + +static BOOL +is_anchored(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb, + int atomcount, BOOL inassert) +{ +do { + PCRE2_SPTR scode = first_significant_code( + code + PRIV(OP_lengths)[*code], FALSE); + int op = *scode; + + /* Non-capturing brackets */ + + if (op == OP_BRA || op == OP_BRAPOS || + op == OP_SBRA || op == OP_SBRAPOS) + { + if (!is_anchored(scode, bracket_map, cb, atomcount, inassert)) + return FALSE; + } + + /* Capturing brackets */ + + else if (op == OP_CBRA || op == OP_CBRAPOS || + op == OP_SCBRA || op == OP_SCBRAPOS) + { + int n = GET2(scode, 1+LINK_SIZE); + int new_map = bracket_map | ((n < 32)? (1u << n) : 1); + if (!is_anchored(scode, new_map, cb, atomcount, inassert)) return FALSE; + } + + /* Positive forward assertion */ + + else if (op == OP_ASSERT) + { + if (!is_anchored(scode, bracket_map, cb, atomcount, TRUE)) return FALSE; + } + + /* Condition. If there is no second branch, it can't be anchored. */ + + else if (op == OP_COND || op == OP_SCOND) + { + if (scode[GET(scode,1)] != OP_ALT) return FALSE; + if (!is_anchored(scode, bracket_map, cb, atomcount, inassert)) + return FALSE; + } + + /* Atomic groups */ + + else if (op == OP_ONCE) + { + if (!is_anchored(scode, bracket_map, cb, atomcount + 1, inassert)) + return FALSE; + } + + /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and + it isn't in brackets that are or may be referenced or inside an atomic + group or an assertion. Also the pattern must not contain *PRUNE or *SKIP, + because these break the feature. Consider, for example, /(?s).*?(*PRUNE)b/ + with the subject "aab", which matches "b", i.e. not at the start of a line. + There is also an option that disables auto-anchoring. */ + + else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR || + op == OP_TYPEPOSSTAR)) + { + if (scode[1] != OP_ALLANY || (bracket_map & cb->backref_map) != 0 || + atomcount > 0 || cb->had_pruneorskip || inassert || + (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0) + return FALSE; + } + + /* Check for explicit anchoring */ + + else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE; + + code += GET(code, 1); + } +while (*code == OP_ALT); /* Loop for each alternative */ +return TRUE; +} + + + +/************************************************* +* Check for starting with ^ or .* * +*************************************************/ + +/* This is called to find out if every branch starts with ^ or .* so that +"first char" processing can be done to speed things up in multiline +matching and for non-DOTALL patterns that start with .* (which must start at +the beginning or after \n). As in the case of is_anchored() (see above), we +have to take account of back references to capturing brackets that contain .* +because in that case we can't make the assumption. Also, the appearance of .* +inside atomic brackets or in an assertion, or in a pattern that contains *PRUNE +or *SKIP does not count, because once again the assumption no longer holds. + +Arguments: + code points to start of the compiled pattern or a group + bracket_map a bitmap of which brackets we are inside while testing; this + handles up to substring 31; after that we just have to take + the less precise approach + cb points to the compile data + atomcount atomic group level + inassert TRUE if in an assertion + +Returns: TRUE or FALSE +*/ + +static BOOL +is_startline(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb, + int atomcount, BOOL inassert) +{ +do { + PCRE2_SPTR scode = first_significant_code( + code + PRIV(OP_lengths)[*code], FALSE); + int op = *scode; + + /* If we are at the start of a conditional assertion group, *both* the + conditional assertion *and* what follows the condition must satisfy the test + for start of line. Other kinds of condition fail. Note that there may be an + auto-callout at the start of a condition. */ + + if (op == OP_COND) + { + scode += 1 + LINK_SIZE; + + if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT]; + else if (*scode == OP_CALLOUT_STR) scode += GET(scode, 1 + 2*LINK_SIZE); + + switch (*scode) + { + case OP_CREF: + case OP_DNCREF: + case OP_RREF: + case OP_DNRREF: + case OP_FAIL: + case OP_FALSE: + case OP_TRUE: + return FALSE; + + default: /* Assertion */ + if (!is_startline(scode, bracket_map, cb, atomcount, TRUE)) return FALSE; + do scode += GET(scode, 1); while (*scode == OP_ALT); + scode += 1 + LINK_SIZE; + break; + } + scode = first_significant_code(scode, FALSE); + op = *scode; + } + + /* Non-capturing brackets */ + + if (op == OP_BRA || op == OP_BRAPOS || + op == OP_SBRA || op == OP_SBRAPOS) + { + if (!is_startline(scode, bracket_map, cb, atomcount, inassert)) + return FALSE; + } + + /* Capturing brackets */ + + else if (op == OP_CBRA || op == OP_CBRAPOS || + op == OP_SCBRA || op == OP_SCBRAPOS) + { + int n = GET2(scode, 1+LINK_SIZE); + int new_map = bracket_map | ((n < 32)? (1u << n) : 1); + if (!is_startline(scode, new_map, cb, atomcount, inassert)) return FALSE; + } + + /* Positive forward assertions */ + + else if (op == OP_ASSERT) + { + if (!is_startline(scode, bracket_map, cb, atomcount, TRUE)) + return FALSE; + } + + /* Atomic brackets */ + + else if (op == OP_ONCE) + { + if (!is_startline(scode, bracket_map, cb, atomcount + 1, inassert)) + return FALSE; + } + + /* .* means "start at start or after \n" if it isn't in atomic brackets or + brackets that may be referenced or an assertion, and as long as the pattern + does not contain *PRUNE or *SKIP, because these break the feature. Consider, + for example, /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab", + i.e. not at the start of a line. There is also an option that disables this + optimization. */ + + else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR) + { + if (scode[1] != OP_ANY || (bracket_map & cb->backref_map) != 0 || + atomcount > 0 || cb->had_pruneorskip || inassert || + (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0) + return FALSE; + } + + /* Check for explicit circumflex; anything else gives a FALSE result. Note + in particular that this includes atomic brackets OP_ONCE because the number + of characters matched by .* cannot be adjusted inside them. */ + + else if (op != OP_CIRC && op != OP_CIRCM) return FALSE; + + /* Move on to the next alternative */ + + code += GET(code, 1); + } +while (*code == OP_ALT); /* Loop for each alternative */ +return TRUE; +} + + + +/************************************************* +* Scan compiled regex for recursion reference * +*************************************************/ + +/* This function scans through a compiled pattern until it finds an instance of +OP_RECURSE. + +Arguments: + code points to start of expression + utf TRUE in UTF mode + +Returns: pointer to the opcode for OP_RECURSE, or NULL if not found +*/ + +static PCRE2_SPTR +find_recurse(PCRE2_SPTR code, BOOL utf) +{ +for (;;) + { + PCRE2_UCHAR c = *code; + if (c == OP_END) return NULL; + if (c == OP_RECURSE) return code; + + /* 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); + + /* 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 code units 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_TYPEPOSUPTO: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP) + code += 2; + break; + + case OP_MARK: + case OP_COMMIT_ARG: + 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-unit character. The length in the table is a + minimum, so we have to arrange to skip the extra units. */ + +#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 */ + } + } +} + + + +/************************************************* +* Check for asserted fixed first code unit * +*************************************************/ + +/* During compilation, the "first code unit" settings from forward assertions +are discarded, because they can cause conflicts with actual literals that +follow. However, if we end up without a first code unit setting for an +unanchored pattern, it is worth scanning the regex to see if there is an +initial asserted first code unit. If all branches start with the same asserted +code unit, or with a non-conditional bracket all of whose alternatives start +with the same asserted code unit (recurse ad lib), then we return that code +unit, with the flags set to zero or REQ_CASELESS; otherwise return zero with +REQ_NONE in the flags. + +Arguments: + code points to start of compiled pattern + flags points to the first code unit flags + inassert non-zero if in an assertion + +Returns: the fixed first code unit, or 0 with REQ_NONE in flags +*/ + +static uint32_t +find_firstassertedcu(PCRE2_SPTR code, int32_t *flags, uint32_t inassert) +{ +uint32_t c = 0; +int cflags = REQ_NONE; + +*flags = REQ_NONE; +do { + uint32_t d; + int dflags; + int xl = (*code == OP_CBRA || *code == OP_SCBRA || + *code == OP_CBRAPOS || *code == OP_SCBRAPOS)? IMM2_SIZE:0; + PCRE2_SPTR scode = first_significant_code(code + 1+LINK_SIZE + xl, TRUE); + PCRE2_UCHAR op = *scode; + + switch(op) + { + default: + return 0; + + case OP_BRA: + case OP_BRAPOS: + case OP_CBRA: + case OP_SCBRA: + case OP_CBRAPOS: + case OP_SCBRAPOS: + case OP_ASSERT: + case OP_ONCE: + d = find_firstassertedcu(scode, &dflags, inassert + ((op==OP_ASSERT)?1:0)); + if (dflags < 0) + return 0; + if (cflags < 0) { c = d; cflags = dflags; } + else if (c != d || cflags != dflags) return 0; + break; + + case OP_EXACT: + scode += IMM2_SIZE; + /* Fall through */ + + case OP_CHAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + if (inassert == 0) return 0; + if (cflags < 0) { c = scode[1]; cflags = 0; } + else if (c != scode[1]) return 0; + break; + + case OP_EXACTI: + scode += IMM2_SIZE; + /* Fall through */ + + case OP_CHARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSPLUSI: + if (inassert == 0) return 0; + if (cflags < 0) { c = scode[1]; cflags = REQ_CASELESS; } + else if (c != scode[1]) return 0; + break; + } + + code += GET(code, 1); + } +while (*code == OP_ALT); + +*flags = cflags; +return c; +} + + + +/************************************************* +* Add an entry to the name/number table * +*************************************************/ + +/* This function is called between compiling passes to add an entry to the +name/number table, maintaining alphabetical order. Checking for permitted +and forbidden duplicates has already been done. + +Arguments: + cb the compile data block + name the name to add + length the length of the name + groupno the group number + tablecount the count of names in the table so far + +Returns: nothing +*/ + +static void +add_name_to_table(compile_block *cb, PCRE2_SPTR name, int length, + unsigned int groupno, uint32_t tablecount) +{ +uint32_t i; +PCRE2_UCHAR *slot = cb->name_table; + +for (i = 0; i < tablecount; i++) + { + int crc = memcmp(name, slot+IMM2_SIZE, CU2BYTES(length)); + if (crc == 0 && slot[IMM2_SIZE+length] != 0) + crc = -1; /* Current name is a substring */ + + /* Make space in the table and break the loop for an earlier name. For a + duplicate or later name, carry on. We do this for duplicates so that in the + simple case (when ?(| is not used) they are in order of their numbers. In all + cases they are in the order in which they appear in the pattern. */ + + if (crc < 0) + { + (void)memmove(slot + cb->name_entry_size, slot, + CU2BYTES((tablecount - i) * cb->name_entry_size)); + break; + } + + /* Continue the loop for a later or duplicate name */ + + slot += cb->name_entry_size; + } + +PUT2(slot, 0, groupno); +memcpy(slot + IMM2_SIZE, name, CU2BYTES(length)); + +/* Add a terminating zero and fill the rest of the slot with zeroes so that +the memory is all initialized. Otherwise valgrind moans about uninitialized +memory when saving serialized compiled patterns. */ + +memset(slot + IMM2_SIZE + length, 0, + CU2BYTES(cb->name_entry_size - length - IMM2_SIZE)); +} + + + +/************************************************* +* Skip in parsed pattern * +*************************************************/ + +/* This function is called to skip parts of the parsed pattern when finding the +length of a lookbehind branch. It is called after (*ACCEPT) and (*FAIL) to find +the end of the branch, it is called to skip over an internal lookaround, and it +is also called to skip to the end of a class, during which it will never +encounter nested groups (but there's no need to have special code for that). + +When called to find the end of a branch or group, pptr must point to the first +meta code inside the branch, not the branch-starting code. In other cases it +can point to the item that causes the function to be called. + +Arguments: + pptr current pointer to skip from + skiptype PSKIP_CLASS when skipping to end of class + PSKIP_ALT when META_ALT ends the skip + PSKIP_KET when only META_KET ends the skip + +Returns: new value of pptr + NULL if META_END is reached - should never occur + or for an unknown meta value - likewise +*/ + +static uint32_t * +parsed_skip(uint32_t *pptr, uint32_t skiptype) +{ +uint32_t nestlevel = 0; + +for (;; pptr++) + { + uint32_t meta = META_CODE(*pptr); + + switch(meta) + { + default: /* Just skip over most items */ + if (meta < META_END) continue; /* Literal */ + break; + + /* This should never occur. */ + + case META_END: + return NULL; + + /* The data for these items is variable in length. */ + + case META_BACKREF: /* Offset is present only if group >= 10 */ + if (META_DATA(*pptr) >= 10) pptr += SIZEOFFSET; + break; + + case META_ESCAPE: /* A few escapes are followed by data items. */ + switch (META_DATA(*pptr)) + { + case ESC_P: + case ESC_p: + pptr += 1; + break; + + case ESC_g: + case ESC_k: + pptr += 1 + SIZEOFFSET; + break; + } + break; + + case META_MARK: /* Add the length of the name. */ + case META_COMMIT_ARG: + case META_PRUNE_ARG: + case META_SKIP_ARG: + case META_THEN_ARG: + pptr += pptr[1]; + break; + + /* These are the "active" items in this loop. */ + + case META_CLASS_END: + if (skiptype == PSKIP_CLASS) return pptr; + break; + + case META_ATOMIC: + case META_CAPTURE: + case META_COND_ASSERT: + case META_COND_DEFINE: + case META_COND_NAME: + case META_COND_NUMBER: + case META_COND_RNAME: + case META_COND_RNUMBER: + case META_COND_VERSION: + case META_LOOKAHEAD: + case META_LOOKAHEADNOT: + case META_LOOKBEHIND: + case META_LOOKBEHINDNOT: + case META_NOCAPTURE: + nestlevel++; + break; + + case META_ALT: + if (nestlevel == 0 && skiptype == PSKIP_ALT) return pptr; + break; + + case META_KET: + if (nestlevel == 0) return pptr; + nestlevel--; + break; + } + + /* The extra data item length for each meta is in a table. */ + + meta = (meta >> 16) & 0x7fff; + if (meta >= sizeof(meta_extra_lengths)) return NULL; + pptr += meta_extra_lengths[meta]; + } +/* Control never reaches here */ +return pptr; +} + + + +/************************************************* +* Find length of a parsed group * +*************************************************/ + +/* This is called for nested groups within a branch of a lookbehind whose +length is being computed. If all the branches in the nested group have the same +length, that is OK. On entry, the pointer must be at the first element after +the group initializing code. On exit it points to OP_KET. Caching is used to +improve processing speed when the same capturing group occurs many times. + +Arguments: + pptrptr pointer to pointer in the parsed pattern + isinline FALSE if a reference or recursion; TRUE for inline group + errcodeptr pointer to the errorcode + lcptr pointer to the loop counter + group number of captured group or -1 for a non-capturing group + recurses chain of recurse_check to catch mutual recursion + cb pointer to the compile data + +Returns: the group length or a negative number +*/ + +static int +get_grouplength(uint32_t **pptrptr, BOOL isinline, int *errcodeptr, int *lcptr, + int group, parsed_recurse_check *recurses, compile_block *cb) +{ +int branchlength; +int grouplength = -1; + +/* The cache can be used only if there is no possibility of there being two +groups with the same number. We do not need to set the end pointer for a group +that is being processed as a back reference or recursion, but we must do so for +an inline group. */ + +if (group > 0 && (cb->external_flags & PCRE2_DUPCAPUSED) == 0) + { + uint32_t groupinfo = cb->groupinfo[group]; + if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1; + if ((groupinfo & GI_SET_FIXED_LENGTH) != 0) + { + if (isinline) *pptrptr = parsed_skip(*pptrptr, PSKIP_KET); + return groupinfo & GI_FIXED_LENGTH_MASK; + } + } + +/* Scan the group. In this case we find the end pointer of necessity. */ + +for(;;) + { + branchlength = get_branchlength(pptrptr, errcodeptr, lcptr, recurses, cb); + if (branchlength < 0) goto ISNOTFIXED; + if (grouplength == -1) grouplength = branchlength; + else if (grouplength != branchlength) goto ISNOTFIXED; + if (**pptrptr == META_KET) break; + *pptrptr += 1; /* Skip META_ALT */ + } + +if (group > 0) + cb->groupinfo[group] |= (uint32_t)(GI_SET_FIXED_LENGTH | grouplength); +return grouplength; + +ISNOTFIXED: +if (group > 0) cb->groupinfo[group] |= GI_NOT_FIXED_LENGTH; +return -1; +} + + + +/************************************************* +* Find length of a parsed branch * +*************************************************/ + +/* Return a fixed length for a branch in a lookbehind, giving an error if the +length is not fixed. If any lookbehinds are encountered on the way, they get +their length set. On entry, *pptrptr points to the first element inside the +branch. On exit it is set to point to the ALT or KET. + +Arguments: + pptrptr pointer to pointer in the parsed pattern + errcodeptr pointer to error code + lcptr pointer to loop counter + recurses chain of recurse_check to catch mutual recursion + cb pointer to compile block + +Returns: the length, or a negative value on error +*/ + +static int +get_branchlength(uint32_t **pptrptr, int *errcodeptr, int *lcptr, + parsed_recurse_check *recurses, compile_block *cb) +{ +int branchlength = 0; +int grouplength; +uint32_t lastitemlength = 0; +uint32_t *pptr = *pptrptr; +PCRE2_SIZE offset; +parsed_recurse_check this_recurse; + +/* A large and/or complex regex can take too long to process. This can happen +more often when (?| groups are present in the pattern because their length +cannot be cached. */ + +if ((*lcptr)++ > 2000) + { + *errcodeptr = ERR35; /* Lookbehind is too complicated */ + return -1; + } + +/* Scan the branch, accumulating the length. */ + +for (;; pptr++) + { + parsed_recurse_check *r; + uint32_t *gptr, *gptrend; + uint32_t escape; + uint32_t group = 0; + uint32_t itemlength = 0; + + if (*pptr < META_END) + { + itemlength = 1; + } + + else switch (META_CODE(*pptr)) + { + case META_KET: + case META_ALT: + goto EXIT; + + /* (*ACCEPT) and (*FAIL) terminate the branch, but we must skip to the + actual termination. */ + + case META_ACCEPT: + case META_FAIL: + pptr = parsed_skip(pptr, PSKIP_ALT); + if (pptr == NULL) goto PARSED_SKIP_FAILED; + goto EXIT; + + case META_MARK: + case META_COMMIT_ARG: + case META_PRUNE_ARG: + case META_SKIP_ARG: + case META_THEN_ARG: + pptr += pptr[1] + 1; + break; + + case META_CIRCUMFLEX: + case META_COMMIT: + case META_DOLLAR: + case META_PRUNE: + case META_SKIP: + case META_THEN: + break; + + case META_OPTIONS: + pptr += 1; + break; + + case META_BIGVALUE: + itemlength = 1; + pptr += 1; + break; + + case META_CLASS: + case META_CLASS_NOT: + itemlength = 1; + pptr = parsed_skip(pptr, PSKIP_CLASS); + if (pptr == NULL) goto PARSED_SKIP_FAILED; + break; + + case META_CLASS_EMPTY_NOT: + case META_DOT: + itemlength = 1; + break; + + case META_CALLOUT_NUMBER: + pptr += 3; + break; + + case META_CALLOUT_STRING: + pptr += 3 + SIZEOFFSET; + break; + + /* Only some escapes consume a character. Of those, \R and \X are never + allowed because they might match more than character. \C is allowed only in + 32-bit and non-UTF 8/16-bit modes. */ + + case META_ESCAPE: + escape = META_DATA(*pptr); + if (escape == ESC_R || escape == ESC_X) return -1; + if (escape > ESC_b && escape < ESC_Z) + { +#if PCRE2_CODE_UNIT_WIDTH != 32 + if ((cb->external_options & PCRE2_UTF) != 0 && escape == ESC_C) + { + *errcodeptr = ERR36; + return -1; + } +#endif + itemlength = 1; + if (escape == ESC_p || escape == ESC_P) pptr++; /* Skip prop data */ + } + break; + + /* Lookaheads can be ignored, but we must start the skip inside the group + so that it isn't treated as a group within the branch. */ + + case META_LOOKAHEAD: + case META_LOOKAHEADNOT: + pptr = parsed_skip(pptr + 1, PSKIP_KET); + if (pptr == NULL) goto PARSED_SKIP_FAILED; + + /* Also ignore any qualifiers that follow a lookahead assertion. */ + + switch (pptr[1]) + { + case META_ASTERISK: + case META_ASTERISK_PLUS: + case META_ASTERISK_QUERY: + case META_PLUS: + case META_PLUS_PLUS: + case META_PLUS_QUERY: + case META_QUERY: + case META_QUERY_PLUS: + case META_QUERY_QUERY: + pptr++; + break; + + case META_MINMAX: + case META_MINMAX_PLUS: + case META_MINMAX_QUERY: + pptr += 3; + break; + + default: + break; + } + break; + + /* Lookbehinds can be ignored, but must themselves be checked. */ + + case META_LOOKBEHIND: + case META_LOOKBEHINDNOT: + if (!set_lookbehind_lengths(&pptr, errcodeptr, lcptr, recurses, cb)) + return -1; + break; + + /* Back references and recursions are handled by very similar code. At this + stage, the names generated in the parsing pass are available, but the main + name table has not yet been created. So for the named varieties, scan the + list of names in order to get the number of the first one in the pattern, + and whether or not this name is duplicated. */ + + case META_BACKREF_BYNAME: + if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0) + goto ISNOTFIXED; + /* Fall through */ + + case META_RECURSE_BYNAME: + { + int i; + PCRE2_SPTR name; + BOOL is_dupname = FALSE; + named_group *ng = cb->named_groups; + uint32_t meta_code = META_CODE(*pptr); + uint32_t length = *(++pptr); + + GETPLUSOFFSET(offset, pptr); + name = cb->start_pattern + offset; + for (i = 0; i < cb->names_found; i++, ng++) + { + if (length == ng->length && PRIV(strncmp)(name, ng->name, length) == 0) + { + group = ng->number; + is_dupname = ng->isdup; + break; + } + } + + if (group == 0) + { + *errcodeptr = ERR15; /* Non-existent subpattern */ + cb->erroroffset = offset; + return -1; + } + + /* A numerical back reference can be fixed length if duplicate capturing + groups are not being used. A non-duplicate named back reference can also + be handled. */ + + if (meta_code == META_RECURSE_BYNAME || + (!is_dupname && (cb->external_flags & PCRE2_DUPCAPUSED) == 0)) + goto RECURSE_OR_BACKREF_LENGTH; /* Handle as a numbered version. */ + } + goto ISNOTFIXED; /* Duplicate name or number */ + + /* The offset values for back references < 10 are in a separate vector + because otherwise they would use more than two parsed pattern elements on + 64-bit systems. */ + + case META_BACKREF: + if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0 || + (cb->external_flags & PCRE2_DUPCAPUSED) != 0) + goto ISNOTFIXED; + group = META_DATA(*pptr); + if (group < 10) + { + offset = cb->small_ref_offset[group]; + goto RECURSE_OR_BACKREF_LENGTH; + } + + /* Fall through */ + /* For groups >= 10 - picking up group twice does no harm. */ + + /* A true recursion implies not fixed length, but a subroutine call may + be OK. Back reference "recursions" are also failed. */ + + case META_RECURSE: + group = META_DATA(*pptr); + GETPLUSOFFSET(offset, pptr); + + RECURSE_OR_BACKREF_LENGTH: + if (group > cb->bracount) + { + cb->erroroffset = offset; + *errcodeptr = ERR15; /* Non-existent subpattern */ + return -1; + } + if (group == 0) goto ISNOTFIXED; /* Local recursion */ + for (gptr = cb->parsed_pattern; *gptr != META_END; gptr++) + { + if (META_CODE(*gptr) == META_BIGVALUE) gptr++; + else if (*gptr == (META_CAPTURE | group)) break; + } + + /* We must start the search for the end of the group at the first meta code + inside the group. Otherwise it will be treated as an enclosed group. */ + + gptrend = parsed_skip(gptr + 1, PSKIP_KET); + if (gptrend == NULL) goto PARSED_SKIP_FAILED; + if (pptr > gptr && pptr < gptrend) goto ISNOTFIXED; /* Local recursion */ + for (r = recurses; r != NULL; r = r->prev) if (r->groupptr == gptr) break; + if (r != NULL) goto ISNOTFIXED; /* Mutual recursion */ + this_recurse.prev = recurses; + this_recurse.groupptr = gptr; + + /* We do not need to know the position of the end of the group, that is, + gptr is not used after the call to get_grouplength(). Setting the second + argument FALSE stops it scanning for the end when the length can be found + in the cache. */ + + gptr++; + grouplength = get_grouplength(&gptr, FALSE, errcodeptr, lcptr, group, + &this_recurse, cb); + if (grouplength < 0) + { + if (*errcodeptr == 0) goto ISNOTFIXED; + return -1; /* Error already set */ + } + itemlength = grouplength; + break; + + /* Check nested groups - advance past the initial data for each type and + then seek a fixed length with get_grouplength(). */ + + case META_COND_NAME: + case META_COND_NUMBER: + case META_COND_RNAME: + case META_COND_RNUMBER: + case META_COND_DEFINE: + pptr += 2 + SIZEOFFSET; + goto CHECK_GROUP; + + case META_COND_ASSERT: + pptr += 1; + goto CHECK_GROUP; + + case META_COND_VERSION: + pptr += 4; + goto CHECK_GROUP; + + case META_CAPTURE: + group = META_DATA(*pptr); + /* Fall through */ + + case META_ATOMIC: + case META_NOCAPTURE: + pptr++; + CHECK_GROUP: + grouplength = get_grouplength(&pptr, TRUE, errcodeptr, lcptr, group, + recurses, cb); + if (grouplength < 0) return -1; + itemlength = grouplength; + break; + + /* Exact repetition is OK; variable repetition is not. A repetition of zero + must subtract the length that has already been added. */ + + case META_MINMAX: + case META_MINMAX_PLUS: + case META_MINMAX_QUERY: + if (pptr[1] == pptr[2]) + { + if (pptr[1] == 0) branchlength -= lastitemlength; + else itemlength = (pptr[1] - 1) * lastitemlength; + pptr += 2; + break; + } + /* Fall through */ + + /* Any other item means this branch does not have a fixed length. */ + + default: + ISNOTFIXED: + *errcodeptr = ERR25; /* Not fixed length */ + return -1; + } + + /* Add the item length to the branchlength, and save it for use if the next + thing is a quantifier. */ + + branchlength += itemlength; + lastitemlength = itemlength; + + /* Ensure that the length does not overflow the limit. */ + + if (branchlength > LOOKBEHIND_MAX) + { + *errcodeptr = ERR87; + return -1; + } + } + +EXIT: +*pptrptr = pptr; +if (branchlength > cb->max_lookbehind) cb->max_lookbehind = branchlength; +return branchlength; + +PARSED_SKIP_FAILED: +*errcodeptr = ERR90; +return -1; +} + + + +/************************************************* +* Set lengths in a lookbehind * +*************************************************/ + +/* This function is called for each lookbehind, to set the lengths in its +branches. An error occurs if any branch does not have a fixed length that is +less than the maximum (65535). On exit, the pointer must be left on the final +ket. + +Arguments: + pptrptr pointer to pointer in the parsed pattern + errcodeptr pointer to error code + lcptr pointer to loop counter + recurses chain of recurse_check to catch mutual recursion + cb pointer to compile block + +Returns: TRUE if all is well + FALSE otherwise, with error code and offset set +*/ + +static BOOL +set_lookbehind_lengths(uint32_t **pptrptr, int *errcodeptr, int *lcptr, + parsed_recurse_check *recurses, compile_block *cb) +{ +PCRE2_SIZE offset; +int branchlength; +uint32_t *bptr = *pptrptr; + +READPLUSOFFSET(offset, bptr); /* Offset for error messages */ +*pptrptr += SIZEOFFSET; + +do + { + *pptrptr += 1; + branchlength = get_branchlength(pptrptr, errcodeptr, lcptr, recurses, cb); + if (branchlength < 0) + { + /* The errorcode and offset may already be set from a nested lookbehind. */ + if (*errcodeptr == 0) *errcodeptr = ERR25; + if (cb->erroroffset == PCRE2_UNSET) cb->erroroffset = offset; + return FALSE; + } + *bptr |= branchlength; /* branchlength never more than 65535 */ + bptr = *pptrptr; + } +while (*bptr == META_ALT); + +return TRUE; +} + + + +/************************************************* +* Check parsed pattern lookbehinds * +*************************************************/ + +/* This function is called at the end of parsing a pattern if any lookbehinds +were encountered. It scans the parsed pattern for them, calling +set_lookbehind_lengths() for each one. At the start, the errorcode is zero and +the error offset is marked unset. The enables the functions above not to +override settings from deeper nestings. + +Arguments cb points to the compile block +Returns: 0 on success, or an errorcode (cb->erroroffset will be set) +*/ + +static int +check_lookbehinds(compile_block *cb) +{ +uint32_t *pptr; +int errorcode = 0; +int loopcount = 0; + +cb->erroroffset = PCRE2_UNSET; + +for (pptr = cb->parsed_pattern; *pptr != META_END; pptr++) + { + if (*pptr < META_END) continue; /* Literal */ + + switch (META_CODE(*pptr)) + { + default: + return ERR70; /* Unrecognized meta code */ + + case META_ESCAPE: + if (*pptr - META_ESCAPE == ESC_P || *pptr - META_ESCAPE == ESC_p) + pptr += 1; + break; + + case META_ACCEPT: + case META_ALT: + case META_ASTERISK: + case META_ASTERISK_PLUS: + case META_ASTERISK_QUERY: + case META_ATOMIC: + case META_BACKREF: + case META_CAPTURE: + case META_CIRCUMFLEX: + case META_CLASS: + case META_CLASS_EMPTY: + case META_CLASS_EMPTY_NOT: + case META_CLASS_END: + case META_CLASS_NOT: + case META_COMMIT: + case META_COND_ASSERT: + case META_DOLLAR: + case META_DOT: + case META_FAIL: + case META_KET: + case META_LOOKAHEAD: + case META_LOOKAHEADNOT: + case META_NOCAPTURE: + case META_PLUS: + case META_PLUS_PLUS: + case META_PLUS_QUERY: + case META_PRUNE: + case META_QUERY: + case META_QUERY_PLUS: + case META_QUERY_QUERY: + case META_RANGE_ESCAPED: + case META_RANGE_LITERAL: + case META_SKIP: + case META_THEN: + break; + + case META_RECURSE: + pptr += SIZEOFFSET; + break; + + case META_BACKREF_BYNAME: + case META_COND_DEFINE: + case META_COND_NAME: + case META_COND_NUMBER: + case META_COND_RNAME: + case META_COND_RNUMBER: + case META_RECURSE_BYNAME: + pptr += 1 + SIZEOFFSET; + break; + + case META_CALLOUT_STRING: + pptr += 3 + SIZEOFFSET; + break; + + case META_BIGVALUE: + case META_OPTIONS: + case META_POSIX: + case META_POSIX_NEG: + pptr += 1; + break; + + case META_MINMAX: + case META_MINMAX_QUERY: + case META_MINMAX_PLUS: + pptr += 2; + break; + + case META_CALLOUT_NUMBER: + case META_COND_VERSION: + pptr += 3; + break; + + case META_MARK: + case META_COMMIT_ARG: + case META_PRUNE_ARG: + case META_SKIP_ARG: + case META_THEN_ARG: + pptr += 1 + pptr[1]; + break; + + case META_LOOKBEHIND: + case META_LOOKBEHINDNOT: + if (!set_lookbehind_lengths(&pptr, &errorcode, &loopcount, NULL, cb)) + return errorcode; + break; + } + } + +return 0; +} + + + +/************************************************* +* External function to compile a pattern * +*************************************************/ + +/* This function reads a regular expression in the form of a string and returns +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 PCRE2_ZERO_TERMINATED + options option bits + errorptr pointer to errorcode + erroroffset pointer to error offset + ccontext points to a compile context or is NULL + +Returns: pointer to compiled data block, or NULL on error, + with errorcode and erroroffset set +*/ + +PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION +pcre2_compile(PCRE2_SPTR pattern, PCRE2_SIZE patlen, uint32_t options, + int *errorptr, PCRE2_SIZE *erroroffset, pcre2_compile_context *ccontext) +{ +BOOL utf; /* Set TRUE for UTF mode */ +BOOL has_lookbehind = FALSE; /* Set TRUE if a lookbehind is found */ +BOOL zero_terminated; /* Set TRUE for zero-terminated pattern */ +pcre2_real_code *re = NULL; /* What we will return */ +compile_block cb; /* "Static" compile-time data */ +const uint8_t *tables; /* Char tables base pointer */ + +PCRE2_UCHAR *code; /* Current pointer in compiled code */ +PCRE2_SPTR codestart; /* Start of compiled code */ +PCRE2_SPTR ptr; /* Current pointer in pattern */ +uint32_t *pptr; /* Current pointer in parsed pattern */ + +PCRE2_SIZE length = 1; /* Allow for final END opcode */ +PCRE2_SIZE usedlength; /* Actual length used */ +PCRE2_SIZE re_blocksize; /* Size of memory block */ +PCRE2_SIZE big32count = 0; /* 32-bit literals >= 0x80000000 */ +PCRE2_SIZE parsed_size_needed; /* Needed for parsed pattern */ + +int32_t firstcuflags, reqcuflags; /* Type of first/req code unit */ +uint32_t firstcu, reqcu; /* Value of first/req code unit */ +uint32_t setflags = 0; /* NL and BSR set flags */ + +uint32_t skipatstart; /* When checking (*UTF) etc */ +uint32_t limit_heap = UINT32_MAX; +uint32_t limit_match = UINT32_MAX; /* Unset match limits */ +uint32_t limit_depth = UINT32_MAX; + +int newline = 0; /* Unset; can be set by the pattern */ +int bsr = 0; /* Unset; can be set by the pattern */ +int errorcode = 0; /* Initialize to avoid compiler warn */ +int regexrc; /* Return from compile */ + +uint32_t i; /* Local loop counter */ + +/* Comments at the head of this file explain about these variables. */ + +uint32_t stack_groupinfo[GROUPINFO_DEFAULT_SIZE]; +uint32_t stack_parsed_pattern[PARSED_PATTERN_DEFAULT_SIZE]; +named_group named_groups[NAMED_GROUP_LIST_SIZE]; + +/* The workspace is used in different ways in the different compiling phases. +It needs to be 16-bit aligned for the preliminary parsing scan. */ + +uint32_t c16workspace[C16_WORK_SIZE]; +PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c16workspace; + + +/* -------------- Check arguments and set up the pattern ----------------- */ + +/* There must be error code and offset pointers. */ + +if (errorptr == NULL || erroroffset == NULL) return NULL; +*errorptr = ERR0; +*erroroffset = 0; + +/* There must be a pattern! */ + +if (pattern == NULL) + { + *errorptr = ERR16; + return NULL; + } + +/* A NULL compile context means "use a default context" */ + +if (ccontext == NULL) + ccontext = (pcre2_compile_context *)(&PRIV(default_compile_context)); + +/* Check that all undefined public option bits are zero. */ + +if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0 || + (ccontext->extra_options & ~PUBLIC_COMPILE_EXTRA_OPTIONS) != 0) + { + *errorptr = ERR17; + return NULL; + } + +if ((options & PCRE2_LITERAL) != 0 && + ((options & ~PUBLIC_LITERAL_COMPILE_OPTIONS) != 0 || + (ccontext->extra_options & ~PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS) != 0)) + { + *errorptr = ERR92; + return NULL; + } + +/* A zero-terminated pattern is indicated by the special length value +PCRE2_ZERO_TERMINATED. Check for an overlong pattern. */ + +if ((zero_terminated = (patlen == PCRE2_ZERO_TERMINATED))) + patlen = PRIV(strlen)(pattern); + +if (patlen > ccontext->max_pattern_length) + { + *errorptr = ERR88; + return NULL; + } + +/* From here on, all returns from this function should end up going via the +EXIT label. */ + + +/* ------------ Initialize the "static" compile data -------------- */ + +tables = (ccontext->tables != NULL)? ccontext->tables : PRIV(default_tables); + +cb.lcc = tables + lcc_offset; /* Individual */ +cb.fcc = tables + fcc_offset; /* character */ +cb.cbits = tables + cbits_offset; /* tables */ +cb.ctypes = tables + ctypes_offset; + +cb.assert_depth = 0; +cb.bracount = 0; +cb.cx = ccontext; +cb.dupnames = FALSE; +cb.end_pattern = pattern + patlen; +cb.erroroffset = 0; +cb.external_flags = 0; +cb.external_options = options; +cb.groupinfo = stack_groupinfo; +cb.had_recurse = FALSE; +cb.lastcapture = 0; +cb.max_lookbehind = 0; +cb.name_entry_size = 0; +cb.name_table = NULL; +cb.named_groups = named_groups; +cb.named_group_list_size = NAMED_GROUP_LIST_SIZE; +cb.names_found = 0; +cb.open_caps = NULL; +cb.parens_depth = 0; +cb.parsed_pattern = stack_parsed_pattern; +cb.req_varyopt = 0; +cb.start_code = cworkspace; +cb.start_pattern = pattern; +cb.start_workspace = cworkspace; +cb.workspace_size = COMPILE_WORK_SIZE; + +/* Maximum back reference and backref bitmap. The bitmap records up to 31 back +references to help in deciding whether (.*) can be treated as anchored or not. +*/ + +cb.top_backref = 0; +cb.backref_map = 0; + +/* Escape sequences \1 to \9 are always back references, but as they are only +two characters long, only two elements can be used in the parsed_pattern +vector. The first contains the reference, and we'd like to use the second to +record the offset in the pattern, so that forward references to non-existent +groups can be diagnosed later with an offset. However, on 64-bit systems, +PCRE2_SIZE won't fit. Instead, we have a vector of offsets for the first +occurrence of \1 to \9, indexed by the second parsed_pattern value. All other +references have enough space for the offset to be put into the parsed pattern. +*/ + +for (i = 0; i < 10; i++) cb.small_ref_offset[i] = PCRE2_UNSET; + + +/* --------------- Start looking at the pattern --------------- */ + +/* Unless PCRE2_LITERAL is set, check for global one-time option settings at +the start of the pattern, and remember the offset to the actual regex. With +valgrind support, make the terminator of a zero-terminated pattern +inaccessible. This catches bugs that would otherwise only show up for +non-zero-terminated patterns. */ + +#ifdef SUPPORT_VALGRIND +if (zero_terminated) VALGRIND_MAKE_MEM_NOACCESS(pattern + patlen, CU2BYTES(1)); +#endif + +ptr = pattern; +skipatstart = 0; + +if ((options & PCRE2_LITERAL) == 0) + { + while (patlen - skipatstart >= 2 && + ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && + ptr[skipatstart+1] == CHAR_ASTERISK) + { + for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++) + { + uint32_t c, pp; + pso *p = pso_list + i; + + if (patlen - skipatstart - 2 >= p->length && + PRIV(strncmp_c8)(ptr + skipatstart + 2, (char *)(p->name), + p->length) == 0) + { + skipatstart += p->length + 2; + switch(p->type) + { + case PSO_OPT: + cb.external_options |= p->value; + break; + + case PSO_FLG: + setflags |= p->value; + break; + + case PSO_NL: + newline = p->value; + setflags |= PCRE2_NL_SET; + break; + + case PSO_BSR: + bsr = p->value; + setflags |= PCRE2_BSR_SET; + break; + + case PSO_LIMM: + case PSO_LIMD: + case PSO_LIMH: + c = 0; + pp = skipatstart; + if (!IS_DIGIT(ptr[pp])) + { + errorcode = ERR60; + ptr += pp; + goto HAD_EARLY_ERROR; + } + while (IS_DIGIT(ptr[pp])) + { + if (c > UINT32_MAX / 10 - 1) break; /* Integer overflow */ + c = c*10 + (ptr[pp++] - CHAR_0); + } + if (ptr[pp++] != CHAR_RIGHT_PARENTHESIS) + { + errorcode = ERR60; + ptr += pp; + goto HAD_EARLY_ERROR; + } + if (p->type == PSO_LIMH) limit_heap = c; + else if (p->type == PSO_LIMM) limit_match = c; + else limit_depth = c; + skipatstart += pp - skipatstart; + break; + } + break; /* Out of the table scan loop */ + } + } + if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */ + } + } + +/* End of pattern-start options; advance to start of real regex. */ + +ptr += skipatstart; + +/* Can't support UTF or UCP unless PCRE2 has been compiled with UTF support. */ + +#ifndef SUPPORT_UNICODE +if ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0) + { + errorcode = ERR32; + goto HAD_EARLY_ERROR; + } +#endif + +/* Check UTF. We have the original options in 'options', with that value as +modified by (*UTF) etc in cb->external_options. The extra option +PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not permitted in UTF-16 mode because the +surrogate code points cannot be represented in UTF-16. */ + +utf = (cb.external_options & PCRE2_UTF) != 0; +if (utf) + { + if ((options & PCRE2_NEVER_UTF) != 0) + { + errorcode = ERR74; + goto HAD_EARLY_ERROR; + } + if ((options & PCRE2_NO_UTF_CHECK) == 0 && + (errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0) + goto HAD_ERROR; /* Offset was set by valid_utf() */ + +#if PCRE2_CODE_UNIT_WIDTH == 16 + if ((ccontext->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) != 0) + { + errorcode = ERR91; + goto HAD_EARLY_ERROR; + } +#endif + } + +/* Check UCP lockout. */ + +if ((cb.external_options & (PCRE2_UCP|PCRE2_NEVER_UCP)) == + (PCRE2_UCP|PCRE2_NEVER_UCP)) + { + errorcode = ERR75; + goto HAD_EARLY_ERROR; + } + +/* Process the BSR setting. */ + +if (bsr == 0) bsr = ccontext->bsr_convention; + +/* Process the newline setting. */ + +if (newline == 0) newline = ccontext->newline_convention; +cb.nltype = NLTYPE_FIXED; +switch(newline) + { + case PCRE2_NEWLINE_CR: + cb.nllen = 1; + cb.nl[0] = CHAR_CR; + break; + + case PCRE2_NEWLINE_LF: + cb.nllen = 1; + cb.nl[0] = CHAR_NL; + break; + + case PCRE2_NEWLINE_NUL: + cb.nllen = 1; + cb.nl[0] = CHAR_NUL; + break; + + case PCRE2_NEWLINE_CRLF: + cb.nllen = 2; + cb.nl[0] = CHAR_CR; + cb.nl[1] = CHAR_NL; + break; + + case PCRE2_NEWLINE_ANY: + cb.nltype = NLTYPE_ANY; + break; + + case PCRE2_NEWLINE_ANYCRLF: + cb.nltype = NLTYPE_ANYCRLF; + break; + + default: + errorcode = ERR56; + goto HAD_EARLY_ERROR; + } + +/* Pre-scan the pattern to do two things: (1) Discover the named groups and +their numerical equivalents, so that this information is always available for +the remaining processing. (2) At the same time, parse the pattern and put a +processed version into the parsed_pattern vector. This has escapes interpreted +and comments removed (amongst other things). + +In all but one case, when PCRE2_AUTO_CALLOUT is not set, the number of unsigned +32-bit ints in the parsed pattern is bounded by the length of the pattern plus +one (for the terminator) plus four if PCRE2_EXTRA_WORD or PCRE2_EXTRA_LINE is +set. The exceptional case is when running in 32-bit, non-UTF mode, when literal +characters greater than META_END (0x80000000) have to be coded as two units. In +this case, therefore, we scan the pattern to check for such values. */ + +#if PCRE2_CODE_UNIT_WIDTH == 32 +if (!utf) + { + PCRE2_SPTR p; + for (p = ptr; p < cb.end_pattern; p++) if (*p >= META_END) big32count++; + } +#endif + +/* Ensure that the parsed pattern buffer is big enough. When PCRE2_AUTO_CALLOUT +is set we have to assume a numerical callout (4 elements) for each character +plus one at the end. This is overkill, but memory is plentiful these days. For +many smaller patterns the vector on the stack (which was set up above) can be +used. */ + +parsed_size_needed = patlen - skipatstart + big32count; + +if ((ccontext->extra_options & + (PCRE2_EXTRA_MATCH_WORD|PCRE2_EXTRA_MATCH_LINE)) != 0) + parsed_size_needed += 4; + +if ((options & PCRE2_AUTO_CALLOUT) != 0) + parsed_size_needed = (parsed_size_needed + 1) * 5; + +if (parsed_size_needed >= PARSED_PATTERN_DEFAULT_SIZE) + { + uint32_t *heap_parsed_pattern = ccontext->memctl.malloc( + (parsed_size_needed + 1) * sizeof(uint32_t), ccontext->memctl.memory_data); + if (heap_parsed_pattern == NULL) + { + *errorptr = ERR21; + goto EXIT; + } + cb.parsed_pattern = heap_parsed_pattern; + } +cb.parsed_pattern_end = cb.parsed_pattern + parsed_size_needed + 1; + +/* Do the parsing scan. */ + +errorcode = parse_regex(ptr, cb.external_options, &has_lookbehind, &cb); +if (errorcode != 0) goto HAD_CB_ERROR; + +/* 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, the default vector on the stack, as set up above, +can be used. Otherwise we have to get/free a special vector. The vector must be +initialized to zero. */ + +if (cb.bracount >= GROUPINFO_DEFAULT_SIZE) + { + cb.groupinfo = ccontext->memctl.malloc( + (cb.bracount + 1)*sizeof(uint32_t), ccontext->memctl.memory_data); + if (cb.groupinfo == NULL) + { + errorcode = ERR21; + cb.erroroffset = 0; + goto HAD_CB_ERROR; + } + } +memset(cb.groupinfo, 0, (cb.bracount + 1) * sizeof(uint32_t)); + +/* If there were any lookbehinds, scan the parsed pattern to figure out their +lengths. */ + +if (has_lookbehind) + { + errorcode = check_lookbehinds(&cb); + if (errorcode != 0) goto HAD_CB_ERROR; + } + +/* For debugging, there is a function that shows the parsed data vector. */ + +#ifdef DEBUG_SHOW_PARSED +fprintf(stderr, "+++ Pre-scan complete:\n"); +show_parsed(&cb); +#endif + +/* For debugging capturing information this code can be enabled. */ + +#ifdef DEBUG_SHOW_CAPTURES + { + named_group *ng = cb.named_groups; + fprintf(stderr, "+++Captures: %d\n", cb.bracount); + for (i = 0; i < cb.names_found; i++, ng++) + { + fprintf(stderr, "+++%3d %.*s\n", ng->number, ng->length, ng->name); + } + } +#endif + +/* Pretend to compile the pattern while actually just accumulating the amount +of memory required in the 'length' variable. This behaviour is triggered by +passing a non-NULL final argument to compile_regex(). We pass a block of +workspace (cworkspace) for it to compile parts of the pattern into; the +compiled code is discarded when it is no longer needed, so hopefully this +workspace will never overflow, though there is a test for its doing so. + +On error, errorcode will be set non-zero, so we don't need to look at the +result of the function. The initial options have been put into the cb block, +but we still have to pass a separate options variable (the first argument) +because the options may change as the pattern is processed. */ + +cb.erroroffset = patlen; /* For any subsequent errors that do not set it */ +pptr = cb.parsed_pattern; +code = cworkspace; +*code = OP_BRA; + +(void)compile_regex(cb.external_options, &code, &pptr, &errorcode, 0, &firstcu, + &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, &length); + +if (errorcode != 0) goto HAD_CB_ERROR; /* Offset is in cb.erroroffset */ + +/* This should be caught in compile_regex(), but just in case... */ + +if (length > MAX_PATTERN_SIZE) + { + errorcode = ERR20; + goto HAD_CB_ERROR; + } + +/* Compute the size of, and then get and initialize, the data block for storing +the compiled pattern and names table. Integer overflow should no longer be +possible because nowadays we limit the maximum value of cb.names_found and +cb.name_entry_size. */ + +re_blocksize = sizeof(pcre2_real_code) + + CU2BYTES(length + + (PCRE2_SIZE)cb.names_found * (PCRE2_SIZE)cb.name_entry_size); +re = (pcre2_real_code *) + ccontext->memctl.malloc(re_blocksize, ccontext->memctl.memory_data); +if (re == NULL) + { + errorcode = ERR21; + goto HAD_CB_ERROR; + } + +/* The compiler may put padding at the end of the pcre2_real_code structure in +order to round it up to a multiple of 4 or 8 bytes. This means that when a +compiled pattern is copied (for example, when serialized) undefined bytes are +read, and this annoys debuggers such as valgrind. To avoid this, we explicitly +write to the last 8 bytes of the structure before setting the fields. */ + +memset((char *)re + sizeof(pcre2_real_code) - 8, 0, 8); +re->memctl = ccontext->memctl; +re->tables = tables; +re->executable_jit = NULL; +memset(re->start_bitmap, 0, 32 * sizeof(uint8_t)); +re->blocksize = re_blocksize; +re->magic_number = MAGIC_NUMBER; +re->compile_options = options; +re->overall_options = cb.external_options; +re->extra_options = ccontext->extra_options; +re->flags = PCRE2_CODE_UNIT_WIDTH/8 | cb.external_flags | setflags; +re->limit_heap = limit_heap; +re->limit_match = limit_match; +re->limit_depth = limit_depth; +re->first_codeunit = 0; +re->last_codeunit = 0; +re->bsr_convention = bsr; +re->newline_convention = newline; +re->max_lookbehind = 0; +re->minlength = 0; +re->top_bracket = 0; +re->top_backref = 0; +re->name_entry_size = cb.name_entry_size; +re->name_count = cb.names_found; + +/* The basic block is immediately followed by the name table, and the compiled +code follows after that. */ + +codestart = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)) + + re->name_entry_size * re->name_count; + +/* 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. */ + +cb.parens_depth = 0; +cb.assert_depth = 0; +cb.lastcapture = 0; +cb.name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)); +cb.start_code = codestart; +cb.req_varyopt = 0; +cb.had_accept = FALSE; +cb.had_pruneorskip = FALSE; +cb.open_caps = NULL; + +/* If any named groups were found, create the name/number table from the list +created in the pre-pass. */ + +if (cb.names_found > 0) + { + named_group *ng = cb.named_groups; + for (i = 0; i < cb.names_found; i++, ng++) + add_name_to_table(&cb, ng->name, ng->length, ng->number, i); + } + +/* Set up a starting, non-extracting bracket, then compile the expression. On +error, errorcode will be set non-zero, so we don't need to look at the result +of the function here. */ + +pptr = cb.parsed_pattern; +code = (PCRE2_UCHAR *)codestart; +*code = OP_BRA; +regexrc = compile_regex(re->overall_options, &code, &pptr, &errorcode, 0, + &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, NULL); +if (regexrc < 0) re->flags |= PCRE2_MATCH_EMPTY; +re->top_bracket = cb.bracount; +re->top_backref = cb.top_backref; +re->max_lookbehind = cb.max_lookbehind; + +if (cb.had_accept) + { + reqcu = 0; /* Must disable after (*ACCEPT) */ + reqcuflags = REQ_NONE; + } + +/* Fill in the final opcode and check for disastrous overflow. If no overflow, +but the estimated length exceeds the really used length, adjust the value of +re->blocksize, and if valgrind support is configured, mark the extra allocated +memory as unaddressable, so that any out-of-bound reads can be detected. */ + +*code++ = OP_END; +usedlength = code - codestart; +if (usedlength > length) errorcode = ERR23; else + { + re->blocksize -= CU2BYTES(length - usedlength); +#ifdef SUPPORT_VALGRIND + VALGRIND_MAKE_MEM_NOACCESS(code, CU2BYTES(length - usedlength)); +#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; + unsigned 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 p, groupnumber; + + groupnumber = (int)GET(rcode, 1); + if (groupnumber == 0) rgroup = codestart; else + { + PCRE2_SPTR search_from = codestart; + rgroup = NULL; + for (i = 0, p = start; i < ccount; i++, p = (p + 1) & 7) + { + if (groupnumber == rc[p].groupnumber) + { + 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 (groupnumber > rc[p].groupnumber) search_from = rc[p].group; + } + + if (rgroup == NULL) + { + rgroup = PRIV(find_bracket)(search_from, utf, groupnumber); + if (rgroup == NULL) + { + errorcode = ERR53; + break; + } + if (--start < 0) start = RSCAN_CACHE_SIZE - 1; + rc[start].groupnumber = groupnumber; + 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. */ + +#ifdef DEBUG_CALL_PRINTINT +pcre2_printint(re, stderr, TRUE); +fprintf(stderr, "Length=%lu Used=%lu\n", length, usedlength); +#endif + +/* Unless disabled, check whether any single character iterators can be +auto-possessified. The function overwrites the appropriate opcode values, so +the type of the pointer must be cast. NOTE: the intermediate variable "temp" is +used in this code because at least one compiler gives a warning about loss of +"const" attribute if the cast (PCRE2_UCHAR *)codestart is used directly in the +function call. */ + +if (errorcode == 0 && (re->overall_options & PCRE2_NO_AUTO_POSSESS) == 0) + { + PCRE2_UCHAR *temp = (PCRE2_UCHAR *)codestart; + if (PRIV(auto_possessify)(temp, utf, &cb) != 0) errorcode = ERR80; + } + +/* Failed to compile, or error while post-processing. */ + +if (errorcode != 0) goto HAD_CB_ERROR; + +/* Successful compile. If the anchored option was not passed, set it if +we can determine that the pattern is anchored by virtue of ^ characters or \A +or anything else, such as starting with non-atomic .* when DOTALL is set and +there are no occurrences of *PRUNE or *SKIP (though there is an option to +disable this case). */ + +if ((re->overall_options & PCRE2_ANCHORED) == 0 && + is_anchored(codestart, 0, &cb, 0, FALSE)) + re->overall_options |= PCRE2_ANCHORED; + +/* Set up the first code unit or startline flag, the required code unit, and +then study the pattern. This code need not be obeyed if PCRE2_NO_START_OPTIMIZE +is set, as the data it would create will not be used. Note that a first code +unit (but not the startline flag) is useful for anchored patterns because it +can still give a quick "no match" and also avoid searching for a last code +unit. */ + +if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) + { + /* If we do not have a first code unit, see if there is one that is asserted + (these are not saved during the compile because they can cause conflicts with + actual literals that follow). */ + + if (firstcuflags < 0) + firstcu = find_firstassertedcu(codestart, &firstcuflags, 0); + + /* Save the data for a first code unit. */ + + if (firstcuflags >= 0) + { + re->first_codeunit = firstcu; + re->flags |= PCRE2_FIRSTSET; + + /* Handle caseless first code units. */ + + if ((firstcuflags & REQ_CASELESS) != 0) + { + if (firstcu < 128 || (!utf && firstcu < 255)) + { + if (cb.fcc[firstcu] != firstcu) re->flags |= PCRE2_FIRSTCASELESS; + } + + /* The first code unit is > 128 in UTF mode, or > 255 otherwise. In + 8-bit UTF mode, codepoints in the range 128-255 are introductory code + points and cannot have another case. In 16-bit and 32-bit modes, we can + check wide characters when UTF (and therefore UCP) is supported. */ + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 + else if (firstcu <= MAX_UTF_CODE_POINT && + UCD_OTHERCASE(firstcu) != firstcu) + re->flags |= PCRE2_FIRSTCASELESS; +#endif + } + } + + /* When there is no first code unit, for non-anchored patterns, see if we can + set the PCRE2_STARTLINE flag. This is helpful for multiline matches when all + branches start with ^ and also when all branches start with non-atomic .* for + non-DOTALL matches when *PRUNE and SKIP are not present. (There is an option + that disables this case.) */ + + else if ((re->overall_options & PCRE2_ANCHORED) == 0 && + is_startline(codestart, 0, &cb, 0, FALSE)) + re->flags |= PCRE2_STARTLINE; + + /* Handle the "required code unit", if one is set. In the case of an anchored + pattern, do this only if it follows a variable length item in the pattern. */ + + if (reqcuflags >= 0 && + ((re->overall_options & PCRE2_ANCHORED) == 0 || + (reqcuflags & REQ_VARY) != 0)) + { + re->last_codeunit = reqcu; + re->flags |= PCRE2_LASTSET; + + /* Handle caseless required code units as for first code units (above). */ + + if ((reqcuflags & REQ_CASELESS) != 0) + { + if (reqcu < 128 || (!utf && reqcu < 255)) + { + if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS; + } +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 + else if (reqcu <= MAX_UTF_CODE_POINT && UCD_OTHERCASE(reqcu) != reqcu) + re->flags |= PCRE2_LASTCASELESS; +#endif + } + } + + /* Finally, study the compiled pattern to set up information such as a bitmap + of starting code units and a minimum matching length. */ + + if (PRIV(study)(re) != 0) + { + errorcode = ERR31; + goto HAD_CB_ERROR; + } + } /* End of start-of-match optimizations. */ + +/* Control ends up here in all cases. When running under valgrind, make a +pattern's terminating zero defined again. If memory was obtained for the parsed +version of the pattern, free it before returning. Also free the list of named +groups if a larger one had to be obtained, and likewise the group information +vector. */ + +EXIT: +#ifdef SUPPORT_VALGRIND +if (zero_terminated) VALGRIND_MAKE_MEM_DEFINED(pattern + patlen, CU2BYTES(1)); +#endif +if (cb.parsed_pattern != stack_parsed_pattern) + ccontext->memctl.free(cb.parsed_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 != stack_groupinfo) + ccontext->memctl.free((void *)cb.groupinfo, ccontext->memctl.memory_data); +return re; /* Will be NULL after an error */ + +/* Errors discovered in parse_regex() set the offset value in the compile +block. Errors discovered before it is called must compute it from the ptr +value. After parse_regex() is called, the offset in the compile block is set to +the end of the pattern, but certain errors in compile_regex() may reset it if +an offset is available in the parsed pattern. */ + +HAD_CB_ERROR: +ptr = pattern + cb.erroroffset; + +HAD_EARLY_ERROR: +*erroroffset = ptr - pattern; + +HAD_ERROR: +*errorptr = errorcode; +pcre2_code_free(re); +re = NULL; +goto EXIT; +} + +/* End of pcre2_compile.c */ diff --git a/pcre2-10.22/src/pcre2_config.c b/pcre2-10.32/src/pcre2_config.c similarity index 89% rename from pcre2-10.22/src/pcre2_config.c rename to pcre2-10.32/src/pcre2_config.c index e99272f57..e487b1022 100644 --- a/pcre2-10.22/src/pcre2_config.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -84,13 +84,16 @@ if (where == NULL) /* Requests a length */ return PCRE2_ERROR_BADOPTION; case PCRE2_CONFIG_BSR: + case PCRE2_CONFIG_COMPILED_WIDTHS: + case PCRE2_CONFIG_DEPTHLIMIT: + case PCRE2_CONFIG_HEAPLIMIT: case PCRE2_CONFIG_JIT: case PCRE2_CONFIG_LINKSIZE: case PCRE2_CONFIG_MATCHLIMIT: + case PCRE2_CONFIG_NEVER_BACKSLASH_C: case PCRE2_CONFIG_NEWLINE: case PCRE2_CONFIG_PARENSLIMIT: - case PCRE2_CONFIG_RECURSIONLIMIT: - case PCRE2_CONFIG_STACKRECURSE: + case PCRE2_CONFIG_STACKRECURSE: /* Obsolete */ case PCRE2_CONFIG_UNICODE: return sizeof(uint32_t); @@ -116,6 +119,28 @@ switch (what) #endif break; + case PCRE2_CONFIG_COMPILED_WIDTHS: + *((uint32_t *)where) = 0 +#ifdef SUPPORT_PCRE2_8 + + 1 +#endif +#ifdef SUPPORT_PCRE2_16 + + 2 +#endif +#ifdef SUPPORT_PCRE2_32 + + 4 +#endif + ; + break; + + case PCRE2_CONFIG_DEPTHLIMIT: + *((uint32_t *)where) = MATCH_LIMIT_DEPTH; + break; + + case PCRE2_CONFIG_HEAPLIMIT: + *((uint32_t *)where) = HEAP_LIMIT; + break; + case PCRE2_CONFIG_JIT: #ifdef SUPPORT_JIT *((uint32_t *)where) = 1; @@ -147,20 +172,23 @@ switch (what) *((uint32_t *)where) = NEWLINE_DEFAULT; break; + case PCRE2_CONFIG_NEVER_BACKSLASH_C: +#ifdef NEVER_BACKSLASH_C + *((uint32_t *)where) = 1; +#else + *((uint32_t *)where) = 0; +#endif + break; + case PCRE2_CONFIG_PARENSLIMIT: *((uint32_t *)where) = PARENS_NEST_LIMIT; break; - case PCRE2_CONFIG_RECURSIONLIMIT: - *((uint32_t *)where) = MATCH_LIMIT_RECURSION; - break; + /* This is now obsolete. The stack is no longer used via recursion for + handling backtracking in pcre2_match(). */ case PCRE2_CONFIG_STACKRECURSE: -#ifdef HEAP_MATCH_RECURSE *((uint32_t *)where) = 0; -#else - *((uint32_t *)where) = 1; -#endif break; case PCRE2_CONFIG_UNICODE_VERSION: diff --git a/pcre2-10.22/src/pcre2_context.c b/pcre2-10.32/src/pcre2_context.c similarity index 77% rename from pcre2-10.22/src/pcre2_context.c rename to pcre2-10.32/src/pcre2_context.c index ae050fe92..2c14df008 100644 --- a/pcre2-10.22/src/pcre2_context.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -138,7 +138,8 @@ const pcre2_compile_context PRIV(default_compile_context) = { PCRE2_UNSET, /* Max pattern length */ BSR_DEFAULT, /* Backslash R default */ NEWLINE_DEFAULT, /* Newline convention */ - PARENS_NEST_LIMIT }; /* As it says */ + PARENS_NEST_LIMIT, /* As it says */ + 0 }; /* Extra options */ /* The create function copies the default into the new memory, but must override the default memory handling functions if a gcontext was provided. */ @@ -161,9 +162,6 @@ when no context is supplied to a match function. */ const pcre2_match_context PRIV(default_match_context) = { { default_malloc, default_free, NULL }, -#ifdef HEAP_MATCH_RECURSE - { default_malloc, default_free, NULL }, -#endif #ifdef SUPPORT_JIT NULL, NULL, @@ -171,8 +169,9 @@ const pcre2_match_context PRIV(default_match_context) = { NULL, NULL, PCRE2_UNSET, /* Offset limit */ + HEAP_LIMIT, MATCH_LIMIT, - MATCH_LIMIT_RECURSION }; + MATCH_LIMIT_DEPTH }; /* The create function copies the default into the new memory, but must override the default memory handling functions if a gcontext was provided. */ @@ -190,6 +189,36 @@ return mcontext; } +/* A default convert context is set up to save having to initialize at run time +when no context is supplied to the convert function. */ + +const pcre2_convert_context PRIV(default_convert_context) = { + { default_malloc, default_free, NULL }, /* Default memory handling */ +#ifdef _WIN32 + CHAR_BACKSLASH, /* Default path separator */ + CHAR_GRAVE_ACCENT /* Default escape character */ +#else /* Not Windows */ + CHAR_SLASH, /* Default path separator */ + CHAR_BACKSLASH /* Default escape character */ +#endif + }; + +/* The create function copies the default into the new memory, but must +override the default memory handling functions if a gcontext was provided. */ + +PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION +pcre2_convert_context_create(pcre2_general_context *gcontext) +{ +pcre2_convert_context *ccontext = PRIV(memctl_malloc)( + sizeof(pcre2_real_convert_context), (pcre2_memctl *)gcontext); +if (ccontext == NULL) return NULL; +*ccontext = PRIV(default_convert_context); +if (gcontext != NULL) + *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext); +return ccontext; +} + + /************************************************* * Context copy functions * *************************************************/ @@ -231,11 +260,22 @@ return new; +PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION +pcre2_convert_context_copy(pcre2_convert_context *ccontext) +{ +pcre2_convert_context *new = + ccontext->memctl.malloc(sizeof(pcre2_real_convert_context), + ccontext->memctl.memory_data); +if (new == NULL) return NULL; +memcpy(new, ccontext, sizeof(pcre2_real_convert_context)); +return new; +} + + /************************************************* * Context free functions * *************************************************/ - PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION pcre2_general_context_free(pcre2_general_context *gcontext) { @@ -260,6 +300,12 @@ if (mcontext != NULL) } +PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION +pcre2_convert_context_free(pcre2_convert_context *ccontext) +{ +if (ccontext != NULL) + ccontext->memctl.free(ccontext, ccontext->memctl.memory_data); +} /************************************************* @@ -271,7 +317,7 @@ data is given. Only some of the functions are able to test the validity of the data. */ -/* ------------ Compile contexts ------------ */ +/* ------------ Compile context ------------ */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_character_tables(pcre2_compile_context *ccontext, @@ -313,6 +359,7 @@ switch(newline) case PCRE2_NEWLINE_CRLF: case PCRE2_NEWLINE_ANY: case PCRE2_NEWLINE_ANYCRLF: + case PCRE2_NEWLINE_NUL: ccontext->newline_convention = newline; return 0; @@ -328,6 +375,13 @@ ccontext->parens_nest_limit = limit; return 0; } +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_compile_extra_options(pcre2_compile_context *ccontext, uint32_t options) +{ +ccontext->extra_options = options; +return 0; +} + PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_compile_recursion_guard(pcre2_compile_context *ccontext, int (*guard)(uint32_t, void *), void *user_data) @@ -338,7 +392,7 @@ return 0; } -/* ------------ Match contexts ------------ */ +/* ------------ Match context ------------ */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_callout(pcre2_match_context *mcontext, @@ -349,6 +403,13 @@ mcontext->callout_data = callout_data; return 0; } +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_heap_limit(pcre2_match_context *mcontext, uint32_t limit) +{ +mcontext->heap_limit = limit; +return 0; +} + PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_match_limit(pcre2_match_context *mcontext, uint32_t limit) { @@ -356,6 +417,13 @@ mcontext->match_limit = limit; return 0; } +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_depth_limit(pcre2_match_context *mcontext, uint32_t limit) +{ +mcontext->depth_limit = limit; +return 0; +} + PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_offset_limit(pcre2_match_context *mcontext, PCRE2_SIZE limit) { @@ -363,11 +431,13 @@ mcontext->offset_limit = limit; return 0; } +/* This function became obsolete at release 10.30. It is kept as a synonym for +backwards compatibility. */ + PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_set_recursion_limit(pcre2_match_context *mcontext, uint32_t limit) { -mcontext->recursion_limit = limit; -return 0; +return pcre2_set_depth_limit(mcontext, limit); } PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION @@ -375,17 +445,32 @@ pcre2_set_recursion_memory_management(pcre2_match_context *mcontext, void *(*mymalloc)(size_t, void *), void (*myfree)(void *, void *), void *mydata) { -#ifdef HEAP_MATCH_RECURSE -mcontext->stack_memctl.malloc = mymalloc; -mcontext->stack_memctl.free = myfree; -mcontext->stack_memctl.memory_data = mydata; -#else (void)mcontext; (void)mymalloc; (void)myfree; (void)mydata; -#endif +return 0; +} + +/* ------------ Convert context ------------ */ + +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_glob_separator(pcre2_convert_context *ccontext, uint32_t separator) +{ +if (separator != CHAR_SLASH && separator != CHAR_BACKSLASH && + separator != CHAR_DOT) return PCRE2_ERROR_BADDATA; +ccontext->glob_separator = separator; +return 0; +} + +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_set_glob_escape(pcre2_convert_context *ccontext, uint32_t escape) +{ +if (escape > 255 || (escape != 0 && !ispunct(escape))) + return PCRE2_ERROR_BADDATA; +ccontext->glob_escape = escape; return 0; } /* End of pcre2_context.c */ + diff --git a/pcre2-10.32/src/pcre2_convert.c b/pcre2-10.32/src/pcre2_convert.c new file mode 100644 index 000000000..1dd5c337d --- /dev/null +++ b/pcre2-10.32/src/pcre2_convert.c @@ -0,0 +1,1182 @@ +/************************************************* +* 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-2018 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 TYPE_OPTIONS (PCRE2_CONVERT_GLOB| \ + PCRE2_CONVERT_POSIX_BASIC|PCRE2_CONVERT_POSIX_EXTENDED) + +#define ALL_OPTIONS (PCRE2_CONVERT_UTF|PCRE2_CONVERT_NO_UTF_CHECK| \ + PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR| \ + PCRE2_CONVERT_GLOB_NO_STARSTAR| \ + TYPE_OPTIONS) + +#define DUMMY_BUFFER_SIZE 100 + +/* Generated pattern fragments */ + +#define STR_BACKSLASH_A STR_BACKSLASH STR_A +#define STR_BACKSLASH_z STR_BACKSLASH STR_z +#define STR_COLON_RIGHT_SQUARE_BRACKET STR_COLON STR_RIGHT_SQUARE_BRACKET +#define STR_DOT_STAR_LOOKBEHIND STR_DOT STR_ASTERISK STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_LESS_THAN_SIGN STR_EQUALS_SIGN +#define STR_LOOKAHEAD_NOT_DOT STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_EXCLAMATION_MARK STR_BACKSLASH STR_DOT STR_RIGHT_PARENTHESIS +#define STR_QUERY_s STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_s STR_RIGHT_PARENTHESIS +#define STR_STAR_NUL STR_LEFT_PARENTHESIS STR_ASTERISK STR_N STR_U STR_L STR_RIGHT_PARENTHESIS + +/* States for range and POSIX processing */ + +enum { RANGE_NOT_STARTED, RANGE_STARTING, RANGE_STARTED }; +enum { POSIX_START_REGEX, POSIX_ANCHORED, POSIX_NOT_BRACKET, + POSIX_CLASS_NOT_STARTED, POSIX_CLASS_STARTING, POSIX_CLASS_STARTED }; + +/* Macro to add a character string to the output buffer, checking for overflow. */ + +#define PUTCHARS(string) \ + { \ + for (s = (char *)(string); *s != 0; s++) \ + { \ + if (p >= endp) return PCRE2_ERROR_NOMEMORY; \ + *p++ = *s; \ + } \ + } + +/* Literals that must be escaped: \ ? * + | . ^ $ { } [ ] ( ) */ + +static const char *pcre2_escaped_literals = + STR_BACKSLASH STR_QUESTION_MARK STR_ASTERISK STR_PLUS + STR_VERTICAL_LINE STR_DOT STR_CIRCUMFLEX_ACCENT STR_DOLLAR_SIGN + STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET + STR_LEFT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET + STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS; + +/* Recognized escaped metacharacters in POSIX basic patterns. */ + +static const char *posix_meta_escapes = + STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS + STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET + STR_1 STR_2 STR_3 STR_4 STR_5 STR_6 STR_7 STR_8 STR_9; + + + +/************************************************* +* Convert a POSIX pattern * +*************************************************/ + +/* This function handles both basic and extended POSIX patterns. + +Arguments: + pattype the pattern type + pattern the pattern + plength length in code units + utf TRUE if UTF + use_buffer where to put the output + use_length length of use_buffer + bufflenptr where to put the used length + dummyrun TRUE if a dummy run + ccontext the convert context + +Returns: 0 => success + !0 => error code +*/ + +static int +convert_posix(uint32_t pattype, PCRE2_SPTR pattern, PCRE2_SIZE plength, + BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length, + PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext) +{ +char *s; +PCRE2_SPTR posix = pattern; +PCRE2_UCHAR *p = use_buffer; +PCRE2_UCHAR *pp = p; +PCRE2_UCHAR *endp = p + use_length - 1; /* Allow for trailing zero */ +PCRE2_SIZE convlength = 0; + +uint32_t bracount = 0; +uint32_t posix_state = POSIX_START_REGEX; +uint32_t lastspecial = 0; +BOOL extended = (pattype & PCRE2_CONVERT_POSIX_EXTENDED) != 0; +BOOL nextisliteral = FALSE; + +(void)utf; /* Not used when Unicode not supported */ +(void)ccontext; /* Not currently used */ + +/* Initialize default for error offset as end of input. */ + +*bufflenptr = plength; +PUTCHARS(STR_STAR_NUL); + +/* Now scan the input. */ + +while (plength > 0) + { + uint32_t c, sc; + int clength = 1; + + /* Add in the length of the last item, then, if in the dummy run, pull the + pointer back to the start of the (temporary) buffer and then remember the + start of the next item. */ + + convlength += p - pp; + if (dummyrun) p = use_buffer; + pp = p; + + /* Pick up the next character */ + +#ifndef SUPPORT_UNICODE + c = *posix; +#else + GETCHARLENTEST(c, posix, clength); +#endif + posix += clength; + plength -= clength; + + sc = nextisliteral? 0 : c; + nextisliteral = FALSE; + + /* Handle a character within a class. */ + + if (posix_state >= POSIX_CLASS_NOT_STARTED) + { + if (c == CHAR_RIGHT_SQUARE_BRACKET) + { + PUTCHARS(STR_RIGHT_SQUARE_BRACKET); + posix_state = POSIX_NOT_BRACKET; + } + + /* Not the end of the class */ + + else + { + switch (posix_state) + { + case POSIX_CLASS_STARTED: + if (c <= 127 && islower(c)) break; /* Remain in started state */ + posix_state = POSIX_CLASS_NOT_STARTED; + if (c == CHAR_COLON && plength > 0 && + *posix == CHAR_RIGHT_SQUARE_BRACKET) + { + PUTCHARS(STR_COLON_RIGHT_SQUARE_BRACKET); + plength--; + posix++; + continue; /* With next character after :] */ + } + /* Fall through */ + + case POSIX_CLASS_NOT_STARTED: + if (c == CHAR_LEFT_SQUARE_BRACKET) + posix_state = POSIX_CLASS_STARTING; + break; + + case POSIX_CLASS_STARTING: + if (c == CHAR_COLON) posix_state = POSIX_CLASS_STARTED; + break; + } + + if (c == CHAR_BACKSLASH) PUTCHARS(STR_BACKSLASH); + if (p + clength > endp) return PCRE2_ERROR_NOMEMORY; + memcpy(p, posix - clength, CU2BYTES(clength)); + p += clength; + } + } + + /* Handle a character not within a class. */ + + else switch(sc) + { + case CHAR_LEFT_SQUARE_BRACKET: + PUTCHARS(STR_LEFT_SQUARE_BRACKET); + +#ifdef NEVER + /* We could handle special cases [[:<:]] and [[:>:]] (which PCRE does + support) but they are not part of POSIX 1003.1. */ + + if (plength >= 6) + { + if (posix[0] == CHAR_LEFT_SQUARE_BRACKET && + posix[1] == CHAR_COLON && + (posix[2] == CHAR_LESS_THAN_SIGN || + posix[2] == CHAR_GREATER_THAN_SIGN) && + posix[3] == CHAR_COLON && + posix[4] == CHAR_RIGHT_SQUARE_BRACKET && + posix[5] == CHAR_RIGHT_SQUARE_BRACKET) + { + if (p + 6 > endp) return PCRE2_ERROR_NOMEMORY; + memcpy(p, posix, CU2BYTES(6)); + p += 6; + posix += 6; + plength -= 6; + continue; /* With next character */ + } + } +#endif + + /* Handle start of "normal" character classes */ + + posix_state = POSIX_CLASS_NOT_STARTED; + + /* Handle ^ and ] as first characters */ + + if (plength > 0) + { + if (*posix == CHAR_CIRCUMFLEX_ACCENT) + { + posix++; + plength--; + PUTCHARS(STR_CIRCUMFLEX_ACCENT); + } + if (plength > 0 && *posix == CHAR_RIGHT_SQUARE_BRACKET) + { + posix++; + plength--; + PUTCHARS(STR_RIGHT_SQUARE_BRACKET); + } + } + break; + + case CHAR_BACKSLASH: + if (plength <= 0) return PCRE2_ERROR_END_BACKSLASH; + if (extended) nextisliteral = TRUE; else + { + if (*posix < 127 && strchr(posix_meta_escapes, *posix) != NULL) + { + if (isdigit(*posix)) PUTCHARS(STR_BACKSLASH); + if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY; + lastspecial = *p++ = *posix++; + plength--; + } + else nextisliteral = TRUE; + } + break; + + case CHAR_RIGHT_PARENTHESIS: + if (!extended || bracount == 0) goto ESCAPE_LITERAL; + bracount--; + goto COPY_SPECIAL; + + case CHAR_LEFT_PARENTHESIS: + bracount++; + /* Fall through */ + + case CHAR_QUESTION_MARK: + case CHAR_PLUS: + case CHAR_LEFT_CURLY_BRACKET: + case CHAR_RIGHT_CURLY_BRACKET: + case CHAR_VERTICAL_LINE: + if (!extended) goto ESCAPE_LITERAL; + /* Fall through */ + + case CHAR_DOT: + case CHAR_DOLLAR_SIGN: + posix_state = POSIX_NOT_BRACKET; + COPY_SPECIAL: + lastspecial = c; + if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY; + *p++ = c; + break; + + case CHAR_ASTERISK: + if (lastspecial != CHAR_ASTERISK) + { + if (!extended && (posix_state < POSIX_NOT_BRACKET || + lastspecial == CHAR_LEFT_PARENTHESIS)) + goto ESCAPE_LITERAL; + goto COPY_SPECIAL; + } + break; /* Ignore second and subsequent asterisks */ + + case CHAR_CIRCUMFLEX_ACCENT: + if (extended) goto COPY_SPECIAL; + if (posix_state == POSIX_START_REGEX || + lastspecial == CHAR_LEFT_PARENTHESIS) + { + posix_state = POSIX_ANCHORED; + goto COPY_SPECIAL; + } + /* Fall through */ + + default: + if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL) + { + ESCAPE_LITERAL: + PUTCHARS(STR_BACKSLASH); + } + lastspecial = 0xff; /* Indicates nothing special */ + if (p + clength > endp) return PCRE2_ERROR_NOMEMORY; + memcpy(p, posix - clength, CU2BYTES(clength)); + p += clength; + posix_state = POSIX_NOT_BRACKET; + break; + } + } + +if (posix_state >= POSIX_CLASS_NOT_STARTED) + return PCRE2_ERROR_MISSING_SQUARE_BRACKET; +convlength += p - pp; /* Final segment */ +*bufflenptr = convlength; +*p++ = 0; +return 0; +} + + +/************************************************* +* Convert a glob pattern * +*************************************************/ + +/* Context for writing the output into a buffer. */ + +typedef struct pcre2_output_context { + PCRE2_UCHAR *output; /* current output position */ + PCRE2_SPTR output_end; /* output end */ + PCRE2_SIZE output_size; /* size of the output */ + uint8_t out_str[8]; /* string copied to the output */ +} pcre2_output_context; + + +/* Write a character into the output. + +Arguments: + out output context + chr the next character +*/ + +static void +convert_glob_write(pcre2_output_context *out, PCRE2_UCHAR chr) +{ +out->output_size++; + +if (out->output < out->output_end) + *out->output++ = chr; +} + + +/* Write a string into the output. + +Arguments: + out output context + length length of out->out_str +*/ + +static void +convert_glob_write_str(pcre2_output_context *out, PCRE2_SIZE length) +{ +uint8_t *out_str = out->out_str; +PCRE2_UCHAR *output = out->output; +PCRE2_SPTR output_end = out->output_end; +PCRE2_SIZE output_size = out->output_size; + +do + { + output_size++; + + if (output < output_end) + *output++ = *out_str++; + } +while (--length != 0); + +out->output = output; +out->output_size = output_size; +} + + +/* Prints the separator into the output. + +Arguments: + out output context + separator glob separator + with_escape backslash is needed before separator +*/ + +static void +convert_glob_print_separator(pcre2_output_context *out, + PCRE2_UCHAR separator, BOOL with_escape) +{ +if (with_escape) + convert_glob_write(out, CHAR_BACKSLASH); + +convert_glob_write(out, separator); +} + + +/* Prints a wildcard into the output. + +Arguments: + out output context + separator glob separator + with_escape backslash is needed before separator +*/ + +static void +convert_glob_print_wildcard(pcre2_output_context *out, + PCRE2_UCHAR separator, BOOL with_escape) +{ +out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET; +out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT; +convert_glob_write_str(out, 2); + +convert_glob_print_separator(out, separator, with_escape); + +convert_glob_write(out, CHAR_RIGHT_SQUARE_BRACKET); +} + + +/* Parse a posix class. + +Arguments: + from starting point of scanning the range + pattern_end end of pattern + out output context + +Returns: >0 => class index + 0 => malformed class +*/ + +static int +convert_glob_parse_class(PCRE2_SPTR *from, PCRE2_SPTR pattern_end, + pcre2_output_context *out) +{ +static const char *posix_classes = "alnum:alpha:ascii:blank:cntrl:digit:" + "graph:lower:print:punct:space:upper:word:xdigit:"; +PCRE2_SPTR start = *from + 1; +PCRE2_SPTR pattern = start; +const char *class_ptr; +PCRE2_UCHAR c; +int class_index; + +while (TRUE) + { + if (pattern >= pattern_end) return 0; + + c = *pattern++; + + if (c < CHAR_a || c > CHAR_z) break; + } + +if (c != CHAR_COLON || pattern >= pattern_end || + *pattern != CHAR_RIGHT_SQUARE_BRACKET) + return 0; + +class_ptr = posix_classes; +class_index = 1; + +while (TRUE) + { + if (*class_ptr == CHAR_NUL) return 0; + + pattern = start; + + while (*pattern == (PCRE2_UCHAR) *class_ptr) + { + if (*pattern == CHAR_COLON) + { + pattern += 2; + start -= 2; + + do convert_glob_write(out, *start++); while (start < pattern); + + *from = pattern; + return class_index; + } + pattern++; + class_ptr++; + } + + while (*class_ptr != CHAR_COLON) class_ptr++; + class_ptr++; + class_index++; + } +} + +/* Checks whether the character is in the class. + +Arguments: + class_index class index + c character + +Returns: !0 => character is found in the class + 0 => otherwise +*/ + +static BOOL +convert_glob_char_in_class(int class_index, PCRE2_UCHAR c) +{ +switch (class_index) + { + case 1: return isalnum(c); + case 2: return isalpha(c); + case 3: return 1; + case 4: return c == CHAR_HT || c == CHAR_SPACE; + case 5: return iscntrl(c); + case 6: return isdigit(c); + case 7: return isgraph(c); + case 8: return islower(c); + case 9: return isprint(c); + case 10: return ispunct(c); + case 11: return isspace(c); + case 12: return isupper(c); + case 13: return isalnum(c) || c == CHAR_UNDERSCORE; + default: return isxdigit(c); + } +} + +/* Parse a range of characters. + +Arguments: + from starting point of scanning the range + pattern_end end of pattern + out output context + separator glob separator + with_escape backslash is needed before separator + +Returns: 0 => success + !0 => error code +*/ + +static int +convert_glob_parse_range(PCRE2_SPTR *from, PCRE2_SPTR pattern_end, + pcre2_output_context *out, BOOL utf, PCRE2_UCHAR separator, + BOOL with_escape, PCRE2_UCHAR escape, BOOL no_wildsep) +{ +BOOL is_negative = FALSE; +BOOL separator_seen = FALSE; +BOOL has_prev_c; +PCRE2_SPTR pattern = *from; +PCRE2_SPTR char_start = NULL; +uint32_t c, prev_c; +int len, class_index; + +(void)utf; /* Avoid compiler warning. */ + +if (pattern >= pattern_end) + { + *from = pattern; + return PCRE2_ERROR_MISSING_SQUARE_BRACKET; + } + +if (*pattern == CHAR_EXCLAMATION_MARK + || *pattern == CHAR_CIRCUMFLEX_ACCENT) + { + pattern++; + + if (pattern >= pattern_end) + { + *from = pattern; + return PCRE2_ERROR_MISSING_SQUARE_BRACKET; + } + + is_negative = TRUE; + + out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET; + out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT; + len = 2; + + if (!no_wildsep) + { + if (with_escape) + { + out->out_str[len] = CHAR_BACKSLASH; + len++; + } + out->out_str[len] = (uint8_t) separator; + } + + convert_glob_write_str(out, len + 1); + } +else + convert_glob_write(out, CHAR_LEFT_SQUARE_BRACKET); + +has_prev_c = FALSE; +prev_c = 0; + +if (*pattern == CHAR_RIGHT_SQUARE_BRACKET) + { + out->out_str[0] = CHAR_BACKSLASH; + out->out_str[1] = CHAR_RIGHT_SQUARE_BRACKET; + convert_glob_write_str(out, 2); + has_prev_c = TRUE; + prev_c = CHAR_RIGHT_SQUARE_BRACKET; + pattern++; + } + +while (pattern < pattern_end) + { + char_start = pattern; + GETCHARINCTEST(c, pattern); + + if (c == CHAR_RIGHT_SQUARE_BRACKET) + { + convert_glob_write(out, c); + + if (!is_negative && !no_wildsep && separator_seen) + { + out->out_str[0] = CHAR_LEFT_PARENTHESIS; + out->out_str[1] = CHAR_QUESTION_MARK; + out->out_str[2] = CHAR_LESS_THAN_SIGN; + out->out_str[3] = CHAR_EXCLAMATION_MARK; + convert_glob_write_str(out, 4); + + convert_glob_print_separator(out, separator, with_escape); + convert_glob_write(out, CHAR_RIGHT_PARENTHESIS); + } + + *from = pattern; + return 0; + } + + if (pattern >= pattern_end) break; + + if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON) + { + *from = pattern; + class_index = convert_glob_parse_class(from, pattern_end, out); + + if (class_index != 0) + { + pattern = *from; + + has_prev_c = FALSE; + prev_c = 0; + + if (!is_negative && + convert_glob_char_in_class (class_index, separator)) + separator_seen = TRUE; + continue; + } + } + else if (c == CHAR_MINUS && has_prev_c && + *pattern != CHAR_RIGHT_SQUARE_BRACKET) + { + convert_glob_write(out, CHAR_MINUS); + + char_start = pattern; + GETCHARINCTEST(c, pattern); + + if (pattern >= pattern_end) break; + + if (escape != 0 && c == escape) + { + char_start = pattern; + GETCHARINCTEST(c, pattern); + } + else if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON) + { + *from = pattern; + return PCRE2_ERROR_CONVERT_SYNTAX; + } + + if (prev_c > c) + { + *from = pattern; + return PCRE2_ERROR_CONVERT_SYNTAX; + } + + if (prev_c < separator && separator < c) separator_seen = TRUE; + + has_prev_c = FALSE; + prev_c = 0; + } + else + { + if (escape != 0 && c == escape) + { + char_start = pattern; + GETCHARINCTEST(c, pattern); + + if (pattern >= pattern_end) break; + } + + has_prev_c = TRUE; + prev_c = c; + } + + if (c == CHAR_LEFT_SQUARE_BRACKET || c == CHAR_RIGHT_SQUARE_BRACKET || + c == CHAR_BACKSLASH || c == CHAR_MINUS) + convert_glob_write(out, CHAR_BACKSLASH); + + if (c == separator) separator_seen = TRUE; + + do convert_glob_write(out, *char_start++); while (char_start < pattern); + } + +*from = pattern; +return PCRE2_ERROR_MISSING_SQUARE_BRACKET; +} + + +/* Prints a (*COMMIT) into the output. + +Arguments: + out output context +*/ + +static void +convert_glob_print_commit(pcre2_output_context *out) +{ +out->out_str[0] = CHAR_LEFT_PARENTHESIS; +out->out_str[1] = CHAR_ASTERISK; +out->out_str[2] = CHAR_C; +out->out_str[3] = CHAR_O; +out->out_str[4] = CHAR_M; +out->out_str[5] = CHAR_M; +out->out_str[6] = CHAR_I; +out->out_str[7] = CHAR_T; +convert_glob_write_str(out, 8); +convert_glob_write(out, CHAR_RIGHT_PARENTHESIS); +} + + +/* Bash glob converter. + +Arguments: + pattype the pattern type + pattern the pattern + plength length in code units + utf TRUE if UTF + use_buffer where to put the output + use_length length of use_buffer + bufflenptr where to put the used length + dummyrun TRUE if a dummy run + ccontext the convert context + +Returns: 0 => success + !0 => error code +*/ + +static int +convert_glob(uint32_t options, PCRE2_SPTR pattern, PCRE2_SIZE plength, + BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length, + PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext) +{ +pcre2_output_context out; +PCRE2_SPTR pattern_start = pattern; +PCRE2_SPTR pattern_end = pattern + plength; +PCRE2_UCHAR separator = ccontext->glob_separator; +PCRE2_UCHAR escape = ccontext->glob_escape; +PCRE2_UCHAR c; +BOOL no_wildsep = (options & PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR) != 0; +BOOL no_starstar = (options & PCRE2_CONVERT_GLOB_NO_STARSTAR) != 0; +BOOL in_atomic = FALSE; +BOOL after_starstar = FALSE; +BOOL no_slash_z = FALSE; +BOOL with_escape, is_start, after_separator; +int result = 0; + +(void)utf; /* Avoid compiler warning. */ + +#ifdef SUPPORT_UNICODE +if (utf && (separator >= 128 || escape >= 128)) + { + /* Currently only ASCII characters are supported. */ + *bufflenptr = 0; + return PCRE2_ERROR_CONVERT_SYNTAX; + } +#endif + +with_escape = strchr(pcre2_escaped_literals, separator) != NULL; + +/* Initialize default for error offset as end of input. */ +out.output = use_buffer; +out.output_end = use_buffer + use_length; +out.output_size = 0; + +out.out_str[0] = CHAR_LEFT_PARENTHESIS; +out.out_str[1] = CHAR_QUESTION_MARK; +out.out_str[2] = CHAR_s; +out.out_str[3] = CHAR_RIGHT_PARENTHESIS; +convert_glob_write_str(&out, 4); + +is_start = TRUE; + +if (pattern < pattern_end && pattern[0] == CHAR_ASTERISK) + { + if (no_wildsep) + is_start = FALSE; + else if (!no_starstar && pattern + 1 < pattern_end && + pattern[1] == CHAR_ASTERISK) + is_start = FALSE; + } + +if (is_start) + { + out.out_str[0] = CHAR_BACKSLASH; + out.out_str[1] = CHAR_A; + convert_glob_write_str(&out, 2); + } + +while (pattern < pattern_end) + { + c = *pattern++; + + if (c == CHAR_ASTERISK) + { + is_start = pattern == pattern_start + 1; + + if (in_atomic) + { + convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS); + in_atomic = FALSE; + } + + if (!no_starstar && pattern < pattern_end && *pattern == CHAR_ASTERISK) + { + after_separator = is_start || (pattern[-2] == separator); + + do pattern++; while (pattern < pattern_end && + *pattern == CHAR_ASTERISK); + + if (pattern >= pattern_end) + { + no_slash_z = TRUE; + break; + } + + after_starstar = TRUE; + + if (after_separator && escape != 0 && *pattern == escape && + pattern + 1 < pattern_end && pattern[1] == separator) + pattern++; + + if (is_start) + { + if (*pattern != separator) continue; + + out.out_str[0] = CHAR_LEFT_PARENTHESIS; + out.out_str[1] = CHAR_QUESTION_MARK; + out.out_str[2] = CHAR_COLON; + out.out_str[3] = CHAR_BACKSLASH; + out.out_str[4] = CHAR_A; + out.out_str[5] = CHAR_VERTICAL_LINE; + convert_glob_write_str(&out, 6); + + convert_glob_print_separator(&out, separator, with_escape); + convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS); + + pattern++; + continue; + } + + convert_glob_print_commit(&out); + + if (!after_separator || *pattern != separator) + { + out.out_str[0] = CHAR_DOT; + out.out_str[1] = CHAR_ASTERISK; + out.out_str[2] = CHAR_QUESTION_MARK; + convert_glob_write_str(&out, 3); + continue; + } + + out.out_str[0] = CHAR_LEFT_PARENTHESIS; + out.out_str[1] = CHAR_QUESTION_MARK; + out.out_str[2] = CHAR_COLON; + out.out_str[3] = CHAR_DOT; + out.out_str[4] = CHAR_ASTERISK; + out.out_str[5] = CHAR_QUESTION_MARK; + + convert_glob_write_str(&out, 6); + + convert_glob_print_separator(&out, separator, with_escape); + + out.out_str[0] = CHAR_RIGHT_PARENTHESIS; + out.out_str[1] = CHAR_QUESTION_MARK; + out.out_str[2] = CHAR_QUESTION_MARK; + convert_glob_write_str(&out, 3); + + pattern++; + continue; + } + + if (pattern < pattern_end && *pattern == CHAR_ASTERISK) + { + do pattern++; while (pattern < pattern_end && + *pattern == CHAR_ASTERISK); + } + + if (no_wildsep) + { + if (pattern >= pattern_end) + { + no_slash_z = TRUE; + break; + } + + /* Start check must be after the end check. */ + if (is_start) continue; + } + + if (!is_start) + { + if (after_starstar) + { + out.out_str[0] = CHAR_LEFT_PARENTHESIS; + out.out_str[1] = CHAR_QUESTION_MARK; + out.out_str[2] = CHAR_GREATER_THAN_SIGN; + convert_glob_write_str(&out, 3); + in_atomic = TRUE; + } + else + convert_glob_print_commit(&out); + } + + if (no_wildsep) + convert_glob_write(&out, CHAR_DOT); + else + convert_glob_print_wildcard(&out, separator, with_escape); + + out.out_str[0] = CHAR_ASTERISK; + out.out_str[1] = CHAR_QUESTION_MARK; + if (pattern >= pattern_end) + out.out_str[1] = CHAR_PLUS; + convert_glob_write_str(&out, 2); + continue; + } + + if (c == CHAR_QUESTION_MARK) + { + if (no_wildsep) + convert_glob_write(&out, CHAR_DOT); + else + convert_glob_print_wildcard(&out, separator, with_escape); + continue; + } + + if (c == CHAR_LEFT_SQUARE_BRACKET) + { + result = convert_glob_parse_range(&pattern, pattern_end, + &out, utf, separator, with_escape, escape, no_wildsep); + if (result != 0) break; + continue; + } + + if (escape != 0 && c == escape) + { + if (pattern >= pattern_end) + { + result = PCRE2_ERROR_CONVERT_SYNTAX; + break; + } + c = *pattern++; + } + + if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL) + convert_glob_write(&out, CHAR_BACKSLASH); + + convert_glob_write(&out, c); + } + +if (result == 0) + { + if (!no_slash_z) + { + out.out_str[0] = CHAR_BACKSLASH; + out.out_str[1] = CHAR_z; + convert_glob_write_str(&out, 2); + } + + if (in_atomic) + convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS); + + convert_glob_write(&out, CHAR_NUL); + + if (!dummyrun && out.output_size != (PCRE2_SIZE) (out.output - use_buffer)) + result = PCRE2_ERROR_NOMEMORY; + } + +if (result != 0) + { + *bufflenptr = pattern - pattern_start; + return result; + } + +*bufflenptr = out.output_size - 1; +return 0; +} + + +/************************************************* +* Convert pattern * +*************************************************/ + +/* This is the external-facing function for converting other forms of pattern +into PCRE2 regular expression patterns. On error, the bufflenptr argument is +used to return an offset in the original pattern. + +Arguments: + pattern the input pattern + plength length of input, or PCRE2_ZERO_TERMINATED + options options bits + buffptr pointer to pointer to output buffer + bufflenptr pointer to length of output buffer + ccontext convert context or NULL + +Returns: 0 for success, else an error code (+ve or -ve) +*/ + +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_pattern_convert(PCRE2_SPTR pattern, PCRE2_SIZE plength, uint32_t options, + PCRE2_UCHAR **buffptr, PCRE2_SIZE *bufflenptr, + pcre2_convert_context *ccontext) +{ +int i, rc; +PCRE2_UCHAR dummy_buffer[DUMMY_BUFFER_SIZE]; +PCRE2_UCHAR *use_buffer = dummy_buffer; +PCRE2_SIZE use_length = DUMMY_BUFFER_SIZE; +BOOL utf = (options & PCRE2_CONVERT_UTF) != 0; +uint32_t pattype = options & TYPE_OPTIONS; + +if (pattern == NULL || bufflenptr == NULL) return PCRE2_ERROR_NULL; + +if ((options & ~ALL_OPTIONS) != 0 || /* Undefined bit set */ + (pattype & (~pattype+1)) != pattype || /* More than one type set */ + pattype == 0) /* No type set */ + { + *bufflenptr = 0; /* Error offset */ + return PCRE2_ERROR_BADOPTION; + } + +if (plength == PCRE2_ZERO_TERMINATED) plength = PRIV(strlen)(pattern); +if (ccontext == NULL) ccontext = + (pcre2_convert_context *)(&PRIV(default_convert_context)); + +/* Check UTF if required. */ + +#ifndef SUPPORT_UNICODE +if (utf) + { + *bufflenptr = 0; /* Error offset */ + return PCRE2_ERROR_UNICODE_NOT_SUPPORTED; + } +#else +if (utf && (options & PCRE2_CONVERT_NO_UTF_CHECK) == 0) + { + PCRE2_SIZE erroroffset; + rc = PRIV(valid_utf)(pattern, plength, &erroroffset); + if (rc != 0) + { + *bufflenptr = erroroffset; + return rc; + } + } +#endif + +/* If buffptr is not NULL, and what it points to is not NULL, we are being +provided with a buffer and a length, so set them as the buffer to use. */ + +if (buffptr != NULL && *buffptr != NULL) + { + use_buffer = *buffptr; + use_length = *bufflenptr; + } + +/* Call an individual converter, either just once (if a buffer was provided or +just the length is needed), or twice (if a memory allocation is required). */ + +for (i = 0; i < 2; i++) + { + PCRE2_UCHAR *allocated; + BOOL dummyrun = buffptr == NULL || *buffptr == NULL; + + switch(pattype) + { + case PCRE2_CONVERT_GLOB: + rc = convert_glob(options & ~PCRE2_CONVERT_GLOB, pattern, plength, utf, + use_buffer, use_length, bufflenptr, dummyrun, ccontext); + break; + + case PCRE2_CONVERT_POSIX_BASIC: + case PCRE2_CONVERT_POSIX_EXTENDED: + rc = convert_posix(pattype, pattern, plength, utf, use_buffer, use_length, + bufflenptr, dummyrun, ccontext); + break; + + default: + *bufflenptr = 0; /* Error offset */ + return PCRE2_ERROR_INTERNAL; + } + + if (rc != 0 || /* Error */ + buffptr == NULL || /* Just the length is required */ + *buffptr != NULL) /* Buffer was provided or allocated */ + return rc; + + /* Allocate memory for the buffer, with hidden space for an allocator at + the start. The next time round the loop runs the conversion for real. */ + + allocated = PRIV(memctl_malloc)(sizeof(pcre2_memctl) + + (*bufflenptr + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)ccontext); + if (allocated == NULL) return PCRE2_ERROR_NOMEMORY; + *buffptr = (PCRE2_UCHAR *)(((char *)allocated) + sizeof(pcre2_memctl)); + + use_buffer = *buffptr; + use_length = *bufflenptr + 1; + } + +/* Control should never get here. */ + +return PCRE2_ERROR_INTERNAL; +} + + +/************************************************* +* Free converted pattern * +*************************************************/ + +/* This frees a converted pattern that was put in newly-allocated memory. + +Argument: the converted pattern +Returns: nothing +*/ + +PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION +pcre2_converted_pattern_free(PCRE2_UCHAR *converted) +{ +if (converted != NULL) + { + pcre2_memctl *memctl = + (pcre2_memctl *)((char *)converted - sizeof(pcre2_memctl)); + memctl->free(memctl, memctl->memory_data); + } +} + +/* End of pcre2_convert.c */ diff --git a/pcre2-10.22/src/pcre2_dfa_match.c b/pcre2-10.32/src/pcre2_dfa_match.c similarity index 85% rename from pcre2-10.22/src/pcre2_dfa_match.c rename to pcre2-10.32/src/pcre2_dfa_match.c index 12b31b1b3..9b43237da 100644 --- a/pcre2-10.22/src/pcre2_dfa_match.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -83,7 +83,7 @@ in others, so I abandoned this code. */ #include "pcre2_internal.h" #define PUBLIC_DFA_MATCH_OPTIONS \ - (PCRE2_ANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ + (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \ PCRE2_PARTIAL_SOFT|PCRE2_DFA_SHORTEST|PCRE2_DFA_RESTART) @@ -172,7 +172,7 @@ static const uint8_t coptable[] = { 0, /* Assert not */ 0, /* Assert behind */ 0, /* Assert behind not */ - 0, 0, /* ONCE, ONCE_NC */ + 0, /* ONCE */ 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */ 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */ 0, 0, /* CREF, DNCREF */ @@ -181,7 +181,8 @@ static const uint8_t coptable[] = { 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */ 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */ - 0, 0, 0, 0, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ + 0, 0, /* COMMIT, COMMIT_ARG */ + 0, 0, 0, /* FAIL, ACCEPT, ASSERT_ACCEPT */ 0, 0, 0 /* CLOSE, SKIPZERO, DEFINE */ }; @@ -245,7 +246,7 @@ static const uint8_t poptable[] = { 0, /* Assert not */ 0, /* Assert behind */ 0, /* Assert behind not */ - 0, 0, /* ONCE, ONCE_NC */ + 0, /* ONCE */ 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */ 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */ 0, 0, /* CREF, DNCREF */ @@ -254,7 +255,8 @@ static const uint8_t poptable[] = { 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */ 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */ - 0, 0, 0, 0, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ + 0, 0, /* COMMIT, COMMIT_ARG */ + 0, 0, 0, /* FAIL, ACCEPT, ASSERT_ACCEPT */ 0, 0, 0 /* CLOSE, SKIPZERO, DEFINE */ }; @@ -292,6 +294,150 @@ typedef struct stateblock { #define INTS_PER_STATEBLOCK (int)(sizeof(stateblock)/sizeof(int)) +/* Before version 10.32 the recursive calls of internal_dfa_match() were passed +local working space and output vectors that were created on the stack. This has +caused issues for some patterns, especially in small-stack environments such as +Windows. A new scheme is now in use which sets up a vector on the stack, but if +this is too small, heap memory is used, up to the heap_limit. The main +parameters are all numbers of ints because the workspace is a vector of ints. + +The size of the starting stack vector, DFA_START_RWS_SIZE, is in bytes, and is +defined in pcre2_internal.h so as to be available to pcre2test when it is +finding the minimum heap requirement for a match. */ + +#define OVEC_UNIT (sizeof(PCRE2_SIZE)/sizeof(int)) + +#define RWS_BASE_SIZE (DFA_START_RWS_SIZE/sizeof(int)) /* Stack vector */ +#define RWS_RSIZE 1000 /* Work size for recursion */ +#define RWS_OVEC_RSIZE (1000*OVEC_UNIT) /* Ovector for recursion */ +#define RWS_OVEC_OSIZE (2*OVEC_UNIT) /* Ovector in other cases */ + +/* This structure is at the start of each workspace block. */ + +typedef struct RWS_anchor { + struct RWS_anchor *next; + unsigned int size; /* Number of ints */ + unsigned int free; /* Number of ints */ +} RWS_anchor; + +#define RWS_ANCHOR_SIZE (sizeof(RWS_anchor)/sizeof(int)) + + + +/************************************************* +* Process a callout * +*************************************************/ + +/* This function is called to perform a callout. + +Arguments: + code current code pointer + offsets points to current capture offsets + current_subject start of current subject match + ptr current position in subject + mb the match block + extracode extra code offset when called from condition + lengthptr where to return the callout length + +Returns: the return from the callout +*/ + +static int +do_callout(PCRE2_SPTR code, PCRE2_SIZE *offsets, PCRE2_SPTR current_subject, + PCRE2_SPTR ptr, dfa_match_block *mb, PCRE2_SIZE extracode, + PCRE2_SIZE *lengthptr) +{ +pcre2_callout_block *cb = mb->cb; + +*lengthptr = (code[extracode] == OP_CALLOUT)? + (PCRE2_SIZE)PRIV(OP_lengths)[OP_CALLOUT] : + (PCRE2_SIZE)GET(code, 1 + 2*LINK_SIZE + extracode); + +if (mb->callout == NULL) return 0; /* No callout provided */ + +/* Fixed fields in the callout block are set once and for all at the start of +matching. */ + +cb->offset_vector = offsets; +cb->start_match = (PCRE2_SIZE)(current_subject - mb->start_subject); +cb->current_position = (PCRE2_SIZE)(ptr - mb->start_subject); +cb->pattern_position = GET(code, 1 + extracode); +cb->next_item_length = GET(code, 1 + LINK_SIZE + extracode); + +if (code[extracode] == OP_CALLOUT) + { + cb->callout_number = code[1 + 2*LINK_SIZE + extracode]; + cb->callout_string_offset = 0; + cb->callout_string = NULL; + cb->callout_string_length = 0; + } +else + { + cb->callout_number = 0; + cb->callout_string_offset = GET(code, 1 + 3*LINK_SIZE + extracode); + cb->callout_string = code + (1 + 4*LINK_SIZE + extracode) + 1; + cb->callout_string_length = *lengthptr - (1 + 4*LINK_SIZE) - 2; + } + +return (mb->callout)(cb, mb->callout_data); +} + + + +/************************************************* +* Expand local workspace memory * +*************************************************/ + +/* This function is called when internal_dfa_match() is about to be called +recursively and there is insufficient working space left in the current +workspace block. If there's an existing next block, use it; otherwise get a new +block unless the heap limit is reached. + +Arguments: + rwsptr pointer to block pointer (updated) + ovecsize space needed for an ovector + mb the match block + +Returns: 0 rwsptr has been updated + !0 an error code +*/ + +static int +more_workspace(RWS_anchor **rwsptr, unsigned int ovecsize, dfa_match_block *mb) +{ +RWS_anchor *rws = *rwsptr; +RWS_anchor *new; + +if (rws->next != NULL) + { + new = rws->next; + } + +/* All sizes are in units of sizeof(int), except for mb->heaplimit, which is in +kibibytes. */ + +else + { + unsigned int newsize = rws->size * 2; + unsigned int heapleft = (unsigned int) + (((1024/sizeof(int))*mb->heap_limit - mb->heap_used)); + if (newsize > heapleft) newsize = heapleft; + if (newsize < RWS_RSIZE + ovecsize + RWS_ANCHOR_SIZE) + return PCRE2_ERROR_HEAPLIMIT; + new = mb->memctl.malloc(newsize*sizeof(int), mb->memctl.memory_data); + if (new == NULL) return PCRE2_ERROR_NOMEMORY; + mb->heap_used += newsize; + new->next = NULL; + new->size = newsize; + rws->next = new; + } + +new->free = new->size - RWS_ANCHOR_SIZE; +*rwsptr = new; +return 0; +} + + /************************************************* * Match a Regular Expression - DFA engine * @@ -371,18 +517,15 @@ internal_dfa_match( uint32_t offsetcount, int *workspace, int wscount, - int rlevel) + uint32_t rlevel, + int *RWS) { stateblock *active_states, *new_states, *temp_states; stateblock *next_active_state, *next_new_state; - const uint8_t *ctypes, *lcc, *fcc; PCRE2_SPTR ptr; PCRE2_SPTR end_code; -PCRE2_SPTR first_op; - dfa_recursion_info new_recursive; - int active_count, new_count, match_count; /* Some fields in the mb block are frequently referenced, so we load them into @@ -400,7 +543,8 @@ BOOL utf = FALSE; BOOL reset_could_continue = FALSE; -rlevel++; +if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT; +if (rlevel++ > mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT; offsetcount &= (uint32_t)(-2); /* Round down */ wscount -= 2; @@ -417,21 +561,15 @@ active_states = (stateblock *)(workspace + 2); next_new_state = new_states = active_states + wscount; new_count = 0; -first_op = this_start_code + 1 + LINK_SIZE + - ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA || - *this_start_code == OP_CBRAPOS || *this_start_code == OP_SCBRAPOS) - ? IMM2_SIZE:0); - /* The first thing in any (sub) pattern is a bracket of some sort. Push all the alternative states onto the list, and find out where the end is. This makes is possible to use this function recursively, when we want to stop at a matching internal ket rather than at the end. -If the first opcode in the first alternative is OP_REVERSE, we are dealing with -a backward assertion. In that case, we have to find out the maximum amount to -move back, and set up each alternative appropriately. */ +If we are dealing with a backward assertion we have to find out the maximum +amount to move back, and set up each alternative appropriately. */ -if (*first_op == OP_REVERSE) +if (*this_start_code == OP_ASSERTBACK || *this_start_code == OP_ASSERTBACK_NOT) { size_t max_back = 0; size_t gone_back; @@ -457,7 +595,8 @@ if (*first_op == OP_REVERSE) { if (current_subject <= start_subject) break; current_subject--; - ACROSSCHAR(current_subject > start_subject, *current_subject, current_subject--); + ACROSSCHAR(current_subject > start_subject, current_subject, + current_subject--); } } else @@ -476,15 +615,17 @@ if (*first_op == OP_REVERSE) if (current_subject < mb->start_used_ptr) mb->start_used_ptr = current_subject; - /* Now we can process the individual branches. */ + /* Now we can process the individual branches. There will be an OP_REVERSE at + the start of each branch, except when the length of the branch is zero. */ end_code = this_start_code; do { - size_t back = (size_t)GET(end_code, 2+LINK_SIZE); + uint32_t revlen = (end_code[1+LINK_SIZE] == OP_REVERSE)? 1 + LINK_SIZE : 0; + size_t back = (revlen == 0)? 0 : (size_t)GET(end_code, 2+LINK_SIZE); if (back <= gone_back) { - int bstate = (int)(end_code - start_code + 2 + 2*LINK_SIZE); + int bstate = (int)(end_code - start_code + 1 + LINK_SIZE + revlen); ADD_NEW_DATA(-bstate, 0, (int)(gone_back - back)); } end_code += GET(end_code, 1); @@ -697,7 +838,7 @@ for (;;) case OP_TABLE_LENGTH + ((sizeof(coptable) == OP_TABLE_LENGTH) && (sizeof(poptable) == OP_TABLE_LENGTH)): - break; + return 0; /* ========================================================================== */ /* Reached a closing bracket. If not at the end of the pattern, carry @@ -734,7 +875,7 @@ for (;;) else if (match_count > 0 && ++match_count * 2 > (int)offsetcount) match_count = 0; count = ((match_count == 0)? (int)offsetcount : match_count * 2) - 2; - if (count > 0) memmove(offsets + 2, offsets, + if (count > 0) (void)memmove(offsets + 2, offsets, (size_t)count * sizeof(PCRE2_SIZE)); if (offsetcount >= 2) { @@ -1371,25 +1512,14 @@ for (;;) if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } if (clen > 0) { - uint32_t lgb, rgb; - PCRE2_SPTR nptr = ptr + clen; int ncount = 0; if (count > 0 && codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS) { active_count--; /* Remove non-match possibility */ next_active_state--; } - lgb = UCD_GRAPHBREAK(c); - while (nptr < end_subject) - { - dlen = 1; - if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } - rgb = UCD_GRAPHBREAK(d); - if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break; - ncount++; - lgb = rgb; - nptr += dlen; - } + (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf, + &ncount); count++; ADD_NEW_DATA(-state_offset, count, ncount); } @@ -1632,8 +1762,6 @@ for (;;) ADD_ACTIVE(state_offset + 2, 0); if (clen > 0) { - uint32_t lgb, rgb; - PCRE2_SPTR nptr = ptr + clen; int ncount = 0; if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR || codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY) @@ -1641,17 +1769,8 @@ for (;;) active_count--; /* Remove non-match possibility */ next_active_state--; } - lgb = UCD_GRAPHBREAK(c); - while (nptr < end_subject) - { - dlen = 1; - if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } - rgb = UCD_GRAPHBREAK(d); - if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break; - ncount++; - lgb = rgb; - nptr += dlen; - } + (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf, + &ncount); ADD_NEW_DATA(-(state_offset + count), 0, ncount); } break; @@ -1904,25 +2023,15 @@ for (;;) count = current_state->count; /* Number already matched */ if (clen > 0) { - uint32_t lgb, rgb; - PCRE2_SPTR nptr = ptr + clen; + PCRE2_SPTR nptr; int ncount = 0; if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO) { active_count--; /* Remove non-match possibility */ next_active_state--; } - lgb = UCD_GRAPHBREAK(c); - while (nptr < end_subject) - { - dlen = 1; - if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } - rgb = UCD_GRAPHBREAK(d); - if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break; - ncount++; - lgb = rgb; - nptr += dlen; - } + nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf, + &ncount); if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0) reset_could_continue = TRUE; if (++count >= (int)GET2(code, 1)) @@ -2099,20 +2208,9 @@ for (;;) case OP_EXTUNI: if (clen > 0) { - uint32_t lgb, rgb; - PCRE2_SPTR nptr = ptr + clen; int ncount = 0; - lgb = UCD_GRAPHBREAK(c); - while (nptr < end_subject) - { - dlen = 1; - if (!utf) d = *nptr; else { GETCHARLEN(d, nptr, dlen); } - rgb = UCD_GRAPHBREAK(d); - if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break; - ncount++; - lgb = rgb; - nptr += dlen; - } + PCRE2_SPTR nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject, + end_subject, utf, &ncount); if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0) reset_could_continue = TRUE; ADD_NEW_DATA(-(state_offset + 1), 0, ncount); @@ -2136,6 +2234,7 @@ for (;;) case 0x2029: #endif /* Not EBCDIC */ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break; + /* Fall through */ case CHAR_LF: ADD_NEW(state_offset + 1, 0); @@ -2225,7 +2324,7 @@ for (;;) case OP_NOTI: if (clen > 0) { - unsigned int otherd; + uint32_t otherd; #ifdef SUPPORT_UNICODE if (utf && d >= 128) otherd = UCD_OTHERCASE(d); @@ -2539,11 +2638,13 @@ for (;;) if (isinclass) { int max = (int)GET2(ecode, 1 + IMM2_SIZE); - if (*ecode == OP_CRPOSRANGE) + + if (*ecode == OP_CRPOSRANGE && count >= (int)GET2(ecode, 1)) { active_count--; /* Remove non-match possibility */ next_active_state--; } + if (++count >= max && max != 0) /* Max 0 => no limit */ { ADD_NEW(next_state_offset + 1 + 2 * IMM2_SIZE, 0); } else @@ -2573,10 +2674,22 @@ for (;;) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: { - PCRE2_SPTR endasscode = code + GET(code, 1); - PCRE2_SIZE local_offsets[2]; int rc; - int local_workspace[1000]; + int *local_workspace; + PCRE2_SIZE *local_offsets; + PCRE2_SPTR endasscode = code + GET(code, 1); + RWS_anchor *rws = (RWS_anchor *)RWS; + + if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) + { + rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); + if (rc != 0) return rc; + RWS = (int *)rws; + } + + local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); + local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; + rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); @@ -2586,12 +2699,15 @@ for (;;) ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ - sizeof(local_offsets)/sizeof(PCRE2_SIZE), /* size of same */ + RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ - sizeof(local_workspace)/sizeof(int), /* size of same */ - rlevel); /* function recursion level */ + RWS_RSIZE, /* size of same */ + rlevel, /* function recursion level */ + RWS); /* recursion workspace */ - if (rc == PCRE2_ERROR_DFA_UITEM) return rc; + rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; + + if (rc < 0 && rc != PCRE2_ERROR_NOMATCH) return rc; if ((rc >= 0) == (codevalue == OP_ASSERT || codevalue == OP_ASSERTBACK)) { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); } } @@ -2601,8 +2717,6 @@ for (;;) case OP_COND: case OP_SCOND: { - PCRE2_SIZE local_offsets[1000]; - int local_workspace[1000]; int codelink = (int)GET(code, 1); PCRE2_UCHAR condcode; @@ -2613,45 +2727,10 @@ for (;;) if (code[LINK_SIZE + 1] == OP_CALLOUT || code[LINK_SIZE + 1] == OP_CALLOUT_STR) { - PCRE2_SIZE callout_length = (code[LINK_SIZE + 1] == OP_CALLOUT)? - (PCRE2_SIZE)PRIV(OP_lengths)[OP_CALLOUT] : - (PCRE2_SIZE)GET(code, 2 + 3*LINK_SIZE); - - rrc = 0; - if (mb->callout != NULL) - { - pcre2_callout_block cb; - cb.version = 1; - cb.capture_top = 1; - cb.capture_last = 0; - cb.offset_vector = offsets; - cb.mark = NULL; /* No (*MARK) support */ - cb.subject = start_subject; - cb.subject_length = (PCRE2_SIZE)(end_subject - start_subject); - cb.start_match = (PCRE2_SIZE)(current_subject - start_subject); - cb.current_position = (PCRE2_SIZE)(ptr - start_subject); - cb.pattern_position = GET(code, LINK_SIZE + 2); - cb.next_item_length = GET(code, LINK_SIZE + 2 + LINK_SIZE); - - if (code[LINK_SIZE + 1] == OP_CALLOUT) - { - cb.callout_number = code[2 + 3*LINK_SIZE]; - cb.callout_string_offset = 0; - cb.callout_string = NULL; - cb.callout_string_length = 0; - } - else - { - cb.callout_number = 0; - cb.callout_string_offset = GET(code, 2 + 4*LINK_SIZE); - cb.callout_string = code + (2 + 5*LINK_SIZE) + 1; - cb.callout_string_length = - callout_length - (1 + 4*LINK_SIZE) - 2; - } - - if ((rrc = (mb->callout)(&cb, mb->callout_data)) < 0) - return rrc; /* Abandon */ - } + PCRE2_SIZE callout_length; + rrc = do_callout(code, offsets, current_subject, ptr, mb, + 1 + LINK_SIZE, &callout_length); + if (rrc < 0) return rrc; /* Abandon */ if (rrc > 0) break; /* Fail this thread */ code += callout_length; /* Skip callout data */ } @@ -2694,8 +2773,22 @@ for (;;) else { int rc; + int *local_workspace; + PCRE2_SIZE *local_offsets; PCRE2_SPTR asscode = code + LINK_SIZE + 1; PCRE2_SPTR endasscode = asscode + GET(asscode, 1); + RWS_anchor *rws = (RWS_anchor *)RWS; + + if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) + { + rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); + if (rc != 0) return rc; + RWS = (int *)rws; + } + + local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); + local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; + rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); @@ -2705,12 +2798,15 @@ for (;;) ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ - sizeof(local_offsets)/sizeof(PCRE2_SIZE), /* size of same */ + RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ - sizeof(local_workspace)/sizeof(int), /* size of same */ - rlevel); /* function recursion level */ + RWS_RSIZE, /* size of same */ + rlevel, /* function recursion level */ + RWS); /* recursion workspace */ - if (rc == PCRE2_ERROR_DFA_UITEM) return rc; + rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; + + if (rc < 0 && rc != PCRE2_ERROR_NOMATCH) return rc; if ((rc >= 0) == (condcode == OP_ASSERT || condcode == OP_ASSERTBACK)) { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); } @@ -2723,13 +2819,25 @@ for (;;) /*-----------------------------------------------------------------*/ case OP_RECURSE: { + int rc; + int *local_workspace; + PCRE2_SIZE *local_offsets; + RWS_anchor *rws = (RWS_anchor *)RWS; dfa_recursion_info *ri; - PCRE2_SIZE local_offsets[1000]; - int local_workspace[1000]; PCRE2_SPTR callpat = start_code + GET(code, 1); uint32_t recno = (callpat == mb->start_code)? 0 : GET2(callpat, 1 + LINK_SIZE); - int rc; + + if (rws->free < RWS_RSIZE + RWS_OVEC_RSIZE) + { + rc = more_workspace(&rws, RWS_OVEC_RSIZE, mb); + if (rc != 0) return rc; + RWS = (int *)rws; + } + + local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); + local_workspace = ((int *)local_offsets) + RWS_OVEC_RSIZE; + rws->free -= RWS_RSIZE + RWS_OVEC_RSIZE; /* Check for repeating a recursion without advancing the subject pointer. This should catch convoluted mutual recursions. (Some simple @@ -2753,11 +2861,13 @@ for (;;) ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ - sizeof(local_offsets)/sizeof(PCRE2_SIZE), /* size of same */ + RWS_OVEC_RSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ - sizeof(local_workspace)/sizeof(int), /* size of same */ - rlevel); /* function recursion level */ + RWS_RSIZE, /* size of same */ + rlevel, /* function recursion level */ + RWS); /* recursion workspace */ + rws->free += RWS_RSIZE + RWS_OVEC_RSIZE; mb->recursive = new_recursive.prevrec; /* Done this recursion */ /* Ran out of internal offsets */ @@ -2803,10 +2913,25 @@ for (;;) case OP_SCBRAPOS: case OP_BRAPOSZERO: { + int rc; + int *local_workspace; + PCRE2_SIZE *local_offsets; PCRE2_SIZE charcount, matched_count; PCRE2_SPTR local_ptr = ptr; + RWS_anchor *rws = (RWS_anchor *)RWS; BOOL allow_zero; + if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) + { + rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); + if (rc != 0) return rc; + RWS = (int *)rws; + } + + local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); + local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; + rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; + if (codevalue == OP_BRAPOSZERO) { allow_zero = TRUE; @@ -2819,19 +2944,17 @@ for (;;) for (matched_count = 0;; matched_count++) { - PCRE2_SIZE local_offsets[2]; - int local_workspace[1000]; - - int rc = internal_dfa_match( + rc = internal_dfa_match( mb, /* fixed match data */ code, /* this subexpression's code */ local_ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ - sizeof(local_offsets)/sizeof(PCRE2_SIZE), /* size of same */ + RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ - sizeof(local_workspace)/sizeof(int), /* size of same */ - rlevel); /* function recursion level */ + RWS_RSIZE, /* size of same */ + rlevel, /* function recursion level */ + RWS); /* recursion workspace */ /* Failed to match */ @@ -2848,6 +2971,8 @@ for (;;) local_ptr += charcount; /* Advance temporary position ptr */ } + rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; + /* At this point we have matched the subpattern matched_count times, and local_ptr is pointing to the character after the end of the last match. */ @@ -2889,21 +3014,36 @@ for (;;) /*-----------------------------------------------------------------*/ case OP_ONCE: - case OP_ONCE_NC: { - PCRE2_SIZE local_offsets[2]; - int local_workspace[1000]; + int rc; + int *local_workspace; + PCRE2_SIZE *local_offsets; + RWS_anchor *rws = (RWS_anchor *)RWS; - int rc = internal_dfa_match( + if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE) + { + rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb); + if (rc != 0) return rc; + RWS = (int *)rws; + } + + local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free); + local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE; + rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE; + + rc = internal_dfa_match( mb, /* fixed match data */ code, /* this subexpression's code */ ptr, /* where we currently are */ (PCRE2_SIZE)(ptr - start_subject), /* start offset */ local_offsets, /* offset vector */ - sizeof(local_offsets)/sizeof(PCRE2_SIZE), /* size of same */ + RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */ local_workspace, /* workspace vector */ - sizeof(local_workspace)/sizeof(int), /* size of same */ - rlevel); /* function recursion level */ + RWS_RSIZE, /* size of same */ + rlevel, /* function recursion level */ + RWS); /* recursion workspace */ + + rws->free += RWS_RSIZE + RWS_OVEC_OSIZE; if (rc >= 0) { @@ -2984,44 +3124,10 @@ for (;;) case OP_CALLOUT: case OP_CALLOUT_STR: { - unsigned int callout_length = (*code == OP_CALLOUT) - ? PRIV(OP_lengths)[OP_CALLOUT] : GET(code, 1 + 2*LINK_SIZE); - rrc = 0; - - if (mb->callout != NULL) - { - pcre2_callout_block cb; - cb.version = 1; - cb.capture_top = 1; - cb.capture_last = 0; - cb.offset_vector = offsets; - cb.mark = NULL; /* No (*MARK) support */ - cb.subject = start_subject; - cb.subject_length = (PCRE2_SIZE)(end_subject - start_subject); - cb.start_match = (PCRE2_SIZE)(current_subject - start_subject); - cb.current_position = (PCRE2_SIZE)(ptr - start_subject); - cb.pattern_position = GET(code, 1); - cb.next_item_length = GET(code, 1 + LINK_SIZE); - - if (*code == OP_CALLOUT) - { - cb.callout_number = code[1 + 2*LINK_SIZE]; - cb.callout_string_offset = 0; - cb.callout_string = NULL; - cb.callout_string_length = 0; - } - else - { - cb.callout_number = 0; - cb.callout_string_offset = GET(code, 1 + 3*LINK_SIZE); - cb.callout_string = code + (1 + 4*LINK_SIZE) + 1; - cb.callout_string_length = - callout_length - (1 + 4*LINK_SIZE) - 2; - } - - if ((rrc = (mb->callout)(&cb, mb->callout_data)) < 0) - return rrc; /* Abandon */ - } + PCRE2_SIZE callout_length; + rrc = do_callout(code, offsets, current_subject, ptr, mb, 0, + &callout_length); + if (rrc < 0) return rrc; /* Abandon */ if (rrc == 0) { ADD_ACTIVE(state_offset + (int)callout_length, 0); } } @@ -3069,7 +3175,7 @@ for (;;) ) ) match_count = PCRE2_ERROR_PARTIAL; - break; /* In effect, "return", but see the comment below */ + break; /* Exit from loop along the subject string */ } /* One or more states are active for the next character. */ @@ -3077,11 +3183,13 @@ for (;;) ptr += clen; /* Advance to next subject character */ } /* Loop to move along the subject string */ -/* Control gets here from "break" a few lines above. We do it this way because -if we use "return" above, we have compiler trouble. Some compilers warn if -there's nothing here because they think the function doesn't return a value. On -the other hand, if we put a dummy statement here, some more clever compilers -complain that it can't be reached. Sigh. */ +/* Control gets here from "break" a few lines above. If we have a match and +PCRE2_ENDANCHORED is set, the match fails. */ + +if (match_count >= 0 && + ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0 && + ptr < end_subject) + match_count = PCRE2_ERROR_NOMATCH; return match_count; } @@ -3115,8 +3223,9 @@ Returns: > 0 => number of match offset pairs placed in offsets PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION pcre2_dfa_match(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, int *workspace, size_t wscount) + pcre2_match_context *mcontext, int *workspace, PCRE2_SIZE wscount) { +int rc; const pcre2_real_code *re = (const pcre2_real_code *)code; PCRE2_SPTR start_match; @@ -3125,9 +3234,9 @@ PCRE2_SPTR bumpalong_limit; PCRE2_SPTR req_cu_ptr; BOOL utf, anchored, startline, firstline; - BOOL has_first_cu = FALSE; BOOL has_req_cu = FALSE; + PCRE2_UCHAR first_cu = 0; PCRE2_UCHAR first_cu2 = 0; PCRE2_UCHAR req_cu = 0; @@ -3138,9 +3247,21 @@ const uint8_t *start_bits = NULL; /* We need to have mb pointing to a match block, because the IS_NEWLINE macro is used below, and it expects NLBLOCK to be defined as a pointer. */ +pcre2_callout_block cb; dfa_match_block actual_match_block; dfa_match_block *mb = &actual_match_block; +/* Set up a starting block of memory for use during recursive calls to +internal_dfa_match(). By putting this on the stack, it minimizes resource use +in the case when it is not needed. If this is too small, more memory is +obtained from the heap. At the start of each block is an anchor structure.*/ + +int base_recursion_workspace[RWS_BASE_SIZE]; +RWS_anchor *rws = (RWS_anchor *)base_recursion_workspace; +rws->next = NULL; +rws->size = RWS_BASE_SIZE; +rws->free = RWS_BASE_SIZE - RWS_ANCHOR_SIZE; + /* A length equal to PCRE2_ZERO_TERMINATED implies a zero-terminated subject string. */ @@ -3154,6 +3275,13 @@ if (re == NULL || subject == NULL || workspace == NULL || match_data == NULL) if (wscount < 20) return PCRE2_ERROR_DFA_WSSIZE; if (start_offset > length) return PCRE2_ERROR_BADOFFSET; +/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same +time. */ + +if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 && + ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0) + return PCRE2_ERROR_BADOPTION; + /* Check that the first field in the block is the magic number. If it is not, return with PCRE2_ERROR_BADMAGIC. */ @@ -3208,14 +3336,29 @@ startline = (re->flags & PCRE2_STARTLINE) != 0; firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0; bumpalong_limit = end_subject; -/* 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. */ +/* Initialize and set up the fixed fields in the callout block, with a pointer +in the match block. */ + +mb->cb = &cb; +cb.version = 2; +cb.subject = subject; +cb.subject_length = (PCRE2_SIZE)(end_subject - subject); +cb.callout_flags = 0; +cb.capture_top = 1; /* No capture support */ +cb.capture_last = 0; +cb.mark = NULL; /* No (*MARK) support */ + +/* Get data from the match context, if present, and fill in the remaining +fields in the match block. It is an error to set an offset limit without +setting the flag at compile time. */ if (mcontext == NULL) { mb->callout = NULL; mb->memctl = re->memctl; + mb->match_limit = PRIV(default_match_context).match_limit; + mb->match_limit_depth = PRIV(default_match_context).depth_limit; + mb->heap_limit = PRIV(default_match_context).heap_limit; } else { @@ -3228,8 +3371,20 @@ else mb->callout = mcontext->callout; mb->callout_data = mcontext->callout_data; mb->memctl = mcontext->memctl; + mb->match_limit = mcontext->match_limit; + mb->match_limit_depth = mcontext->depth_limit; + mb->heap_limit = mcontext->heap_limit; } +if (mb->match_limit > re->limit_match) + mb->match_limit = re->limit_match; + +if (mb->match_limit_depth > re->limit_depth) + mb->match_limit_depth = re->limit_depth; + +if (mb->heap_limit > re->limit_heap) + mb->heap_limit = re->limit_heap; + mb->start_code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_count * re->name_entry_size; mb->tables = re->tables; @@ -3238,6 +3393,8 @@ mb->end_subject = end_subject; mb->start_offset = start_offset; mb->moptions = options; mb->poptions = re->overall_options; +mb->match_call_count = 0; +mb->heap_used = 0; /* Process the \R and newline settings. */ @@ -3255,6 +3412,11 @@ switch(re->newline_convention) mb->nl[0] = CHAR_NL; break; + case PCRE2_NEWLINE_NUL: + mb->nllen = 1; + mb->nl[0] = CHAR_NUL; + break; + case PCRE2_NEWLINE_CRLF: mb->nllen = 2; mb->nl[0] = CHAR_CR; @@ -3321,34 +3483,27 @@ if (utf && (options & PCRE2_NO_UTF_CHECK) == 0) } #endif /* SUPPORT_UNICODE */ -/* Set up the first code unit to match, if available. The first_codeunit value -is never set for an anchored regular expression, but the anchoring may be -forced at run time, so we have to test for anchoring. The first code unit may -be unset for an unanchored pattern, of course. If there's no first code unit -there may be a bitmap of possible first characters. */ +/* Set up the first code unit to match, if available. If there's no first code +unit there may be a bitmap of possible first characters. */ -if (!anchored) +if ((re->flags & PCRE2_FIRSTSET) != 0) { - if ((re->flags & PCRE2_FIRSTSET) != 0) + has_first_cu = TRUE; + first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit); + if ((re->flags & PCRE2_FIRSTCASELESS) != 0) { - has_first_cu = TRUE; - first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit); - if ((re->flags & PCRE2_FIRSTCASELESS) != 0) - { - first_cu2 = TABLE_GET(first_cu, mb->tables + fcc_offset, first_cu); + first_cu2 = TABLE_GET(first_cu, mb->tables + fcc_offset, first_cu); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 - if (utf && first_cu > 127) - first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu); + if (utf && first_cu > 127) + first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu); #endif - } } - else - if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0) - start_bits = re->start_bitmap; } +else + if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0) + start_bits = re->start_bitmap; -/* For anchored or unanchored matches, there may be a "last known required -character" set. */ +/* There may be a "last known required code unit" set. */ if ((re->flags & PCRE2_LASTSET) != 0) { @@ -3376,8 +3531,6 @@ a match. */ for (;;) { - int rc; - /* ----------------- Start of match optimizations ---------------- */ /* There are some optimizations that avoid running the match if a known @@ -3389,13 +3542,13 @@ for (;;) if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 && (options & PCRE2_DFA_RESTART) == 0) { - PCRE2_SPTR save_end_subject = end_subject; - /* If firstline is TRUE, the start of the match is constrained to the first line of a multiline string. That is, the match must be before or at the - first newline. Implement this by temporarily adjusting end_subject so that - we stop the optimization scans at a newline. If the match fails at the - newline, later code breaks this loop. */ + first newline following the start of matching. Temporarily adjust + end_subject so that we stop the optimization scans for a first code unit + immediately after the first character of a newline (the first code unit can + legitimately be a newline). If the match fails at the newline, later code + breaks this loop. */ if (firstline) { @@ -3403,85 +3556,162 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - while (t < mb->end_subject && !IS_NEWLINE(t)) + while (t < end_subject && !IS_NEWLINE(t)) { t++; - ACROSSCHAR(t < end_subject, *t, t++); + ACROSSCHAR(t < end_subject, t, t++); } } else #endif - while (t < mb->end_subject && !IS_NEWLINE(t)) t++; + while (t < end_subject && !IS_NEWLINE(t)) t++; end_subject = t; } - /* Advance to a unique first code unit if there is one. */ + /* Anchored: check the first code unit if one is recorded. This may seem + pointless but it can help in detecting a no match case without scanning for + the required code unit. */ - if (has_first_cu) + if (anchored) { - PCRE2_UCHAR smc; - if (first_cu != first_cu2) - while (start_match < end_subject && - (smc = UCHAR21TEST(start_match)) != first_cu && smc != first_cu2) - start_match++; - else - while (start_match < end_subject && UCHAR21TEST(start_match) != first_cu) - start_match++; - } - - /* Or to just after a linebreak for a multiline match */ - - else if (startline) - { - if (start_match > mb->start_subject + start_offset) + if (has_first_cu || start_bits != NULL) { -#ifdef SUPPORT_UNICODE - if (utf) + BOOL ok = start_match < end_subject; + if (ok) { - while (start_match < end_subject && !WAS_NEWLINE(start_match)) + PCRE2_UCHAR c = UCHAR21TEST(start_match); + ok = has_first_cu && (c == first_cu || c == first_cu2); + if (!ok && start_bits != NULL) { - start_match++; - ACROSSCHAR(start_match < end_subject, *start_match, - start_match++); +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (c > 255) c = 255; +#endif + ok = (start_bits[c/8] & (1 << (c&7))) != 0; } } - else -#endif - while (start_match < end_subject && !WAS_NEWLINE(start_match)) - start_match++; - - /* If we have just passed a CR and the newline option is ANY or - ANYCRLF, and we are now at a LF, advance the match position by one more - code unit. */ - - if (start_match[-1] == CHAR_CR && - (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) && - start_match < end_subject && - UCHAR21TEST(start_match) == CHAR_NL) - start_match++; + if (!ok) break; } } - /* Or to a non-unique first code unit if any have been identified. The - bitmap contains only 256 bits. When code units are 16 or 32 bits wide, all - code units greater than 254 set the 255 bit. */ + /* Not anchored. Advance to a unique first code unit if there is one. In + 8-bit mode, the use of memchr() gives a big speed up, even though we have + to call it twice in caseless mode, in order to find the earliest occurrence + of the character in either of its cases. */ - else if (start_bits != NULL) + else { - while (start_match < end_subject) + if (has_first_cu) { - register uint32_t c = UCHAR21TEST(start_match); + if (first_cu != first_cu2) /* Caseless */ + { #if PCRE2_CODE_UNIT_WIDTH != 8 - if (c > 255) c = 255; + PCRE2_UCHAR smc; + while (start_match < end_subject && + (smc = UCHAR21TEST(start_match)) != first_cu && + smc != first_cu2) + start_match++; +#else /* 8-bit code units */ + PCRE2_SPTR pp1 = + memchr(start_match, first_cu, end_subject-start_match); + PCRE2_SPTR pp2 = + memchr(start_match, first_cu2, end_subject-start_match); + if (pp1 == NULL) + start_match = (pp2 == NULL)? end_subject : pp2; + else + start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2; #endif - if ((start_bits[c/8] & (1 << (c&7))) != 0) break; - start_match++; + } + + /* The caseful case */ + + 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 + } + + /* If we can't find the required code unit, having reached the true end + of the subject, break the bumpalong loop, to force a match failure, + except when doing partial matching, when we let the next cycle run at + the end of the subject. To see why, consider the pattern /(?<=abc)def/, + which partially matches "abc", even though the string does not contain + the starting character "d". If we have not reached the true end of the + subject (PCRE2_FIRSTLINE caused end_subject to be temporarily modified) + we also let the cycle run, because the matching string is legitimately + allowed to start with the first code unit of a newline. */ + + if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 && + start_match >= mb->end_subject) + break; } - } + + /* If there's no first code unit, advance to just after a linebreak for a + multiline match if required. */ + + else if (startline) + { + if (start_match > mb->start_subject + start_offset) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + { + start_match++; + ACROSSCHAR(start_match < end_subject, start_match, start_match++); + } + } + else +#endif + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + start_match++; + + /* If we have just passed a CR and the newline option is ANY or + ANYCRLF, and we are now at a LF, advance the match position by one + more code unit. */ + + if (start_match[-1] == CHAR_CR && + (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) && + start_match < end_subject && + UCHAR21TEST(start_match) == CHAR_NL) + start_match++; + } + } + + /* If there's no first code unit or a requirement for a multiline line + start, advance to a non-unique first code unit if any have been + identified. The bitmap contains only 256 bits. When code units are 16 or + 32 bits wide, all code units greater than 254 set the 255 bit. */ + + else if (start_bits != NULL) + { + while (start_match < end_subject) + { + uint32_t c = UCHAR21TEST(start_match); +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (c > 255) c = 255; +#endif + if ((start_bits[c/8] & (1 << (c&7))) != 0) break; + start_match++; + } + + /* See comment above in first_cu checking about the next line. */ + + if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 && + start_match >= mb->end_subject) + break; + } + } /* End of first code unit handling */ /* Restore fudged end_subject */ - end_subject = save_end_subject; + end_subject = mb->end_subject; /* The following two optimizations are disabled for partial matching. */ @@ -3492,7 +3722,7 @@ for (;;) in characters, we treat it as code units to avoid spending too much time in this optimization. */ - if (end_subject - start_match < re->minlength) return PCRE2_ERROR_NOMATCH; + if (end_subject - start_match < re->minlength) goto NOMATCH_EXIT; /* If req_cu is set, we know that that code unit must appear in the subject for the match to succeed. If the first code unit is set, req_cu @@ -3510,7 +3740,7 @@ for (;;) if (has_req_cu && end_subject - start_match < REQ_CU_MAX) { - register PCRE2_SPTR p = start_match + (has_first_cu? 1:0); + PCRE2_SPTR p = start_match + (has_first_cu? 1:0); /* We don't need to repeat the search if we haven't yet reached the place we found it at last time. */ @@ -3521,7 +3751,7 @@ for (;;) { while (p < end_subject) { - register uint32_t pp = UCHAR21INCTEST(p); + uint32_t pp = UCHAR21INCTEST(p); if (pp == req_cu || pp == req_cu2) { p--; break; } } } @@ -3569,7 +3799,8 @@ for (;;) (uint32_t)match_data->oveccount * 2, /* actual size of same */ workspace, /* workspace vector */ (int)wscount, /* size of same */ - 0); /* function recurse level */ + 0, /* function recurse level */ + base_recursion_workspace); /* initial workspace for recursion */ /* Anything other than "no match" means we are done, always; otherwise, carry on only if not anchored. */ @@ -3585,7 +3816,7 @@ for (;;) match_data->rightchar = (PCRE2_SIZE)( mb->last_used_ptr - subject); match_data->startchar = (PCRE2_SIZE)(start_match - subject); match_data->rc = rc; - return rc; + goto EXIT; } /* Advance to the next subject character unless we are at the end of a line @@ -3596,8 +3827,7 @@ for (;;) #ifdef SUPPORT_UNICODE if (utf) { - ACROSSCHAR(start_match < end_subject, *start_match, - start_match++); + ACROSSCHAR(start_match < end_subject, start_match, start_match++); } #endif if (start_match > end_subject) break; @@ -3617,8 +3847,18 @@ for (;;) } /* "Bumpalong" loop */ +NOMATCH_EXIT: +rc = PCRE2_ERROR_NOMATCH; -return PCRE2_ERROR_NOMATCH; +EXIT: +while (rws->next != NULL) + { + RWS_anchor *next = rws->next; + rws->next = next->next; + mb->memctl.free(next, mb->memctl.memory_data); + } + +return rc; } /* End of pcre2_dfa_match.c */ diff --git a/pcre2-10.22/src/pcre2_error.c b/pcre2-10.32/src/pcre2_error.c similarity index 87% rename from pcre2-10.22/src/pcre2_error.c rename to pcre2-10.32/src/pcre2_error.c index 77fd5f412..4b3b3f1bc 100644 --- a/pcre2-10.22/src/pcre2_error.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -91,13 +91,13 @@ static const unsigned char compile_error_texts[] = "failed to allocate heap memory\0" "unmatched closing parenthesis\0" "internal error: code overflow\0" - "letter or underscore expected after (?< or (?'\0" + "missing closing parenthesis for condition\0" /* 25 */ "lookbehind assertion is not fixed length\0" - "malformed number or name after (?(\0" + "a relative value of zero is not allowed\0" "conditional group contains more than two branches\0" "assertion expected after (?( or (?(?C)\0" - "(?R or (?[+-]digits must be followed by )\0" + "digit expected after (?+ or (?-\0" /* 30 */ "unknown POSIX class name\0" "internal error in pcre2_study(): should not occur\0" @@ -105,9 +105,9 @@ static const unsigned char compile_error_texts[] = "parentheses are too deeply nested (stack check)\0" "character code point value in \\x{} or \\o{} is too large\0" /* 35 */ - "invalid condition (?(0)\0" + "lookbehind is too complicated\0" "\\C is not allowed in a lookbehind assertion in UTF-" XSTRING(PCRE2_CODE_UNIT_WIDTH) " mode\0" - "PCRE does not support \\L, \\l, \\N{name}, \\U, or \\u\0" + "PCRE2 does not support \\F, \\L, \\l, \\N{name}, \\U, or \\u\0" "number after (?C is greater than 255\0" "closing parenthesis for (?C expected\0" /* 40 */ @@ -132,13 +132,14 @@ static const unsigned char compile_error_texts[] = "missing opening brace after \\o\0" "internal error: unknown newline setting\0" "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number\0" - "a numbered reference must not be zero\0" - "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\0" + "(?R (recursive pattern call) must be followed by a closing parenthesis\0" + /* "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\0" */ + "obsolete error (should not occur)\0" /* Was the above */ /* 60 */ "(*VERB) not recognized or malformed\0" - "number is too big\0" + "group number is too big\0" "subpattern name expected\0" - "digit expected after (?+\0" + "internal error: parsed pattern overflow\0" "non-octal character in \\o{} (closing brace missing?)\0" /* 65 */ "different names for subpatterns of the same number are not allowed\0" @@ -151,17 +152,17 @@ static const unsigned char compile_error_texts[] = #endif "\\k is not followed by a braced, angle-bracketed, or quoted name\0" /* 70 */ - "internal error: unknown opcode in find_fixedlength()\0" + "internal error: unknown meta code in check_lookbehinds()\0" "\\N is not supported in a class\0" - "SPARE ERROR\0" + "callout string is too long\0" "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)\0" "using UTF is disabled by the application\0" /* 75 */ "using UCP is disabled by the application\0" "name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)\0" "character code point value in \\u.... sequence is too large\0" - "digits missing in \\x{} or \\o{}\0" - "syntax error in (?(VERSION condition\0" + "digits missing in \\x{} or \\o{} or \\N{U+}\0" + "syntax error or number too big in (?(VERSION condition\0" /* 80 */ "internal error: unknown opcode in auto_possessify()\0" "missing terminating delimiter for callout with string argument\0" @@ -173,6 +174,13 @@ static const unsigned char compile_error_texts[] = "regular expression is too complicated\0" "lookbehind assertion is too long\0" "pattern string is longer than the limit set by the application\0" + "internal error: unknown code in parsed pattern\0" + /* 90 */ + "internal error: bad code value in parsed_skip()\0" + "PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not allowed in UTF-16 mode\0" + "invalid option bits with PCRE2_LITERAL\0" + "\\N{U+dddd} is supported only in Unicode (UTF) mode\0" + "invalid hyphen in option setting\0" ; /* Match-time and UTF error texts are in the same format. */ @@ -241,7 +249,7 @@ static const unsigned char match_error_texts[] = "non-unique substring name\0" "NULL argument passed\0" "nested recursion at the same subject position\0" - "recursion limit exceeded\0" + "matching depth limit exceeded\0" "requested value is not available\0" /* 55 */ "requested value is not set\0" @@ -250,9 +258,13 @@ static const unsigned char match_error_texts[] = "expected closing curly bracket in replacement string\0" "bad substitution in replacement string\0" /* 60 */ - "match with end before start is not supported\0" + "match with end before start or start moved backwards is not supported\0" "too many replacements (more than INT_MAX)\0" "bad serialized data\0" + "heap limit exceeded\0" + "invalid syntax\0" + /* 65 */ + "internal error - duplicate substitution match\0" ; @@ -268,17 +280,17 @@ distinct. Arguments: enumber error number buffer where to put the message (zero terminated) - size size of the buffer + size size of the buffer in code units Returns: length of message if all is well negative on error */ PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION -pcre2_get_error_message(int enumber, PCRE2_UCHAR *buffer, size_t size) +pcre2_get_error_message(int enumber, PCRE2_UCHAR *buffer, PCRE2_SIZE size) { const unsigned char *message; -size_t i; +PCRE2_SIZE i; int n; if (size == 0) return PCRE2_ERROR_NOMEMORY; @@ -301,8 +313,8 @@ else /* Invalid error number */ for (; n > 0; n--) { - while (*message++ != CHAR_NULL) {}; - if (*message == CHAR_NULL) return PCRE2_ERROR_BADDATA; + while (*message++ != CHAR_NUL) {}; + if (*message == CHAR_NUL) return PCRE2_ERROR_BADDATA; } for (i = 0; *message != 0; i++) diff --git a/pcre2-10.32/src/pcre2_extuni.c b/pcre2-10.32/src/pcre2_extuni.c new file mode 100644 index 000000000..237211abf --- /dev/null +++ b/pcre2-10.32/src/pcre2_extuni.c @@ -0,0 +1,148 @@ +/************************************************* +* 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-2018 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 an internal function that is used to match a Unicode +extended grapheme sequence. It is used by both pcre2_match() and +pcre2_def_match(). However, it is called only when Unicode support is being +compiled. Nevertheless, we provide a dummy function when there is no Unicode +support, because some compilers do not like functionless source files. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include "pcre2_internal.h" + + +/* Dummy function */ + +#ifndef SUPPORT_UNICODE +PCRE2_SPTR +PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject, + PCRE2_SPTR end_subject, BOOL utf, int *xcount) +{ +(void)c; +(void)eptr; +(void)start_subject; +(void)end_subject; +(void)utf; +(void)xcount; +return NULL; +} +#else + + +/************************************************* +* Match an extended grapheme sequence * +*************************************************/ + +/* +Arguments: + c the first character + eptr pointer to next character + start_subject pointer to start of subject + end_subject pointer to end of subject + utf TRUE if in UTF mode + xcount pointer to count of additional characters, + or NULL if count not needed + +Returns: pointer after the end of the sequence +*/ + +PCRE2_SPTR +PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject, + PCRE2_SPTR end_subject, BOOL utf, int *xcount) +{ +int lgb = UCD_GRAPHBREAK(c); + +while (eptr < end_subject) + { + int rgb; + int len = 1; + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + rgb = UCD_GRAPHBREAK(c); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + + /* Not breaking between Regional Indicators is allowed only if there + are an even number of preceding RIs. */ + + if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) + { + int ricount = 0; + PCRE2_SPTR bptr = eptr - 1; + if (utf) BACKCHAR(bptr); + + /* bptr is pointing to the left-hand character */ + + while (bptr > start_subject) + { + bptr--; + if (utf) + { + BACKCHAR(bptr); + GETCHAR(c, bptr); + } + else + c = *bptr; + if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break; + ricount++; + } + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this + allows any number of them before a following Extended_Pictographic. */ + + if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) || + lgb != ucp_gbExtended_Pictographic) + lgb = rgb; + + eptr += len; + if (xcount != NULL) *xcount += 1; + } + +return eptr; +} + +#endif /* SUPPORT_UNICODE */ + +/* End of pcre2_extuni.c */ diff --git a/pcre2-10.22/src/pcre2_find_bracket.c b/pcre2-10.32/src/pcre2_find_bracket.c similarity index 98% rename from pcre2-10.22/src/pcre2_find_bracket.c rename to pcre2-10.32/src/pcre2_find_bracket.c index 803e71976..70baa1394 100644 --- a/pcre2-10.22/src/pcre2_find_bracket.c +++ b/pcre2-10.32/src/pcre2_find_bracket.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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -71,7 +71,7 @@ PRIV(find_bracket)(PCRE2_SPTR code, BOOL utf, int number) { for (;;) { - register PCRE2_UCHAR c = *code; + PCRE2_UCHAR c = *code; if (c == OP_END) return NULL; @@ -131,6 +131,7 @@ for (;;) break; case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: diff --git a/pcre2-10.32/src/pcre2_fuzzsupport.c b/pcre2-10.32/src/pcre2_fuzzsupport.c new file mode 100644 index 000000000..48781ffc0 --- /dev/null +++ b/pcre2-10.32/src/pcre2_fuzzsupport.c @@ -0,0 +1,365 @@ +/*************************************************************************** +Fuzzer driver for PCRE2. Given an arbitrary string of bytes and a length, it +tries to compile and match it, deriving options from the string itself. If +STANDALONE is defined, a main program that calls the driver with the contents +of specified files is compiled, and commentary on what is happening is output. +If an argument starts with '=' the rest of it it is taken as a literal string +rather than a file name. This allows easy testing of short strings. + +Written by Philip Hazel, October 2016 +***************************************************************************/ + +#include +#include +#include +#include + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include "pcre2.h" + +#define MAX_MATCH_SIZE 1000 + +#define DFA_WORKSPACE_COUNT 100 + +#define ALLOWED_COMPILE_OPTIONS \ + (PCRE2_ANCHORED|PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \ + PCRE2_ALT_VERBNAMES|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_DOLLAR_ENDONLY| \ + PCRE2_DOTALL|PCRE2_DUPNAMES|PCRE2_ENDANCHORED|PCRE2_EXTENDED|PCRE2_FIRSTLINE| \ + PCRE2_MATCH_UNSET_BACKREF|PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C| \ + PCRE2_NO_AUTO_CAPTURE| \ + PCRE2_NO_AUTO_POSSESS|PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_NO_START_OPTIMIZE| \ + PCRE2_UCP|PCRE2_UNGREEDY|PCRE2_USE_OFFSET_LIMIT| \ + PCRE2_UTF) + +#define ALLOWED_MATCH_OPTIONS \ + (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ + PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_HARD| \ + PCRE2_PARTIAL_SOFT|PCRE2_NO_JIT) + +/* This is the callout function. Its only purpose is to halt matching if there +are more than 100 callouts, as one way of stopping too much time being spent on +fruitless matches. The callout data is a pointer to the counter. */ + +static int callout_function(pcre2_callout_block *cb, void *callout_data) +{ +(void)cb; /* Avoid unused parameter warning */ +*((uint32_t *)callout_data) += 1; +return (*((uint32_t *)callout_data) > 100)? PCRE2_ERROR_CALLOUT : 0; +} + +/* Putting in this apparently unnecessary prototype prevents gcc from giving a +"no previous prototype" warning when compiling at high warning level. */ + +int LLVMFuzzerTestOneInput(const unsigned char *, size_t); + +/* Here's the driving function. */ + +int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size) +{ +uint32_t compile_options; +uint32_t match_options; +pcre2_match_data *match_data = NULL; +pcre2_match_context *match_context = NULL; +size_t match_size; +int dfa_workspace[DFA_WORKSPACE_COUNT]; +int r1, r2; +int i; + +if (size < 1) return 0; + +/* Limiting the length of the subject for matching stops fruitless searches +in large trees taking too much time. */ + +match_size = (size > MAX_MATCH_SIZE)? MAX_MATCH_SIZE : size; + +/* Figure out some options to use. Initialize the random number to ensure +repeatability. Ensure that we get a 32-bit unsigned random number for testing +options. (RAND_MAX is required to be at least 32767, but is commonly +2147483647, which excludes the top bit.) */ + +srand((unsigned int)(data[size/2])); +r1 = rand(); +r2 = rand(); + +/* Ensure that all undefined option bits are zero (waste of time trying them) +and also that PCRE2_NO_UTF_CHECK is unset, as there is no guarantee that the +input is UTF-8. Also unset PCRE2_NEVER_UTF and PCRE2_NEVER_UCP as there is no +reason to disallow UTF and UCP. Force PCRE2_NEVER_BACKSLASH_C to be set because +\C in random patterns is highly likely to cause a crash. */ + +compile_options = + ((((uint32_t)r1 << 16) | ((uint32_t)r2 & 0xffff)) & ALLOWED_COMPILE_OPTIONS) | + PCRE2_NEVER_BACKSLASH_C; + +match_options = + ((((uint32_t)r1 << 16) | ((uint32_t)r2 & 0xffff)) & ALLOWED_MATCH_OPTIONS); + +/* Discard partial matching if PCRE2_ENDANCHORED is set, because they are not +allowed together and just give an immediate error return. */ + +if (((compile_options|match_options) & PCRE2_ENDANCHORED) != 0) + match_options &= ~(PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT); + +/* Do the compile with and without the options, and after a successful compile, +likewise do the match with and without the options. */ + +for (i = 0; i < 2; i++) + { + uint32_t callout_count; + int errorcode; + PCRE2_SIZE erroroffset; + pcre2_code *code; + +#ifdef STANDALONE + printf("Compile options %.8x never_backslash_c", compile_options); + printf("%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\n", + ((compile_options & PCRE2_ALT_BSUX) != 0)? ",alt_bsux" : "", + ((compile_options & PCRE2_ALT_CIRCUMFLEX) != 0)? ",alt_circumflex" : "", + ((compile_options & PCRE2_ALT_VERBNAMES) != 0)? ",alt_verbnames" : "", + ((compile_options & PCRE2_ALLOW_EMPTY_CLASS) != 0)? ",allow_empty_class" : "", + ((compile_options & PCRE2_ANCHORED) != 0)? ",anchored" : "", + ((compile_options & PCRE2_AUTO_CALLOUT) != 0)? ",auto_callout" : "", + ((compile_options & PCRE2_CASELESS) != 0)? ",caseless" : "", + ((compile_options & PCRE2_DOLLAR_ENDONLY) != 0)? ",dollar_endonly" : "", + ((compile_options & PCRE2_DOTALL) != 0)? ",dotall" : "", + ((compile_options & PCRE2_DUPNAMES) != 0)? ",dupnames" : "", + ((compile_options & PCRE2_ENDANCHORED) != 0)? ",endanchored" : "", + ((compile_options & PCRE2_EXTENDED) != 0)? ",extended" : "", + ((compile_options & PCRE2_FIRSTLINE) != 0)? ",firstline" : "", + ((compile_options & PCRE2_MATCH_UNSET_BACKREF) != 0)? ",match_unset_backref" : "", + ((compile_options & PCRE2_MULTILINE) != 0)? ",multiline" : "", + ((compile_options & PCRE2_NEVER_UCP) != 0)? ",never_ucp" : "", + ((compile_options & PCRE2_NEVER_UTF) != 0)? ",never_utf" : "", + ((compile_options & PCRE2_NO_AUTO_CAPTURE) != 0)? ",no_auto_capture" : "", + ((compile_options & PCRE2_NO_AUTO_POSSESS) != 0)? ",no_auto_possess" : "", + ((compile_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0)? ",no_dotstar_anchor" : "", + ((compile_options & PCRE2_NO_UTF_CHECK) != 0)? ",no_utf_check" : "", + ((compile_options & PCRE2_NO_START_OPTIMIZE) != 0)? ",no_start_optimize" : "", + ((compile_options & PCRE2_UCP) != 0)? ",ucp" : "", + ((compile_options & PCRE2_UNGREEDY) != 0)? ",ungreedy" : "", + ((compile_options & PCRE2_USE_OFFSET_LIMIT) != 0)? ",use_offset_limit" : "", + ((compile_options & PCRE2_UTF) != 0)? ",utf" : ""); +#endif + + code = pcre2_compile((PCRE2_SPTR)data, (PCRE2_SIZE)size, compile_options, + &errorcode, &erroroffset, NULL); + + /* Compilation succeeded */ + + if (code != NULL) + { + int j; + uint32_t save_match_options = match_options; + + /* Create match data and context blocks only when we first need them. Set + low match and depth limits to avoid wasting too much searching large + pattern trees. Almost all matches are going to fail. */ + + if (match_data == NULL) + { + match_data = pcre2_match_data_create(32, NULL); + if (match_data == NULL) + { +#ifdef STANDALONE + printf("** Failed to create match data block\n"); +#endif + return 0; + } + } + + if (match_context == NULL) + { + match_context = pcre2_match_context_create(NULL); + if (match_context == NULL) + { +#ifdef STANDALONE + printf("** Failed to create match context block\n"); +#endif + return 0; + } + (void)pcre2_set_match_limit(match_context, 100); + (void)pcre2_set_depth_limit(match_context, 100); + (void)pcre2_set_callout(match_context, callout_function, &callout_count); + } + + /* Match twice, with and without options. */ + + for (j = 0; j < 2; j++) + { +#ifdef STANDALONE + printf("Match options %.8x", match_options); + printf("%s%s%s%s%s%s%s%s%s%s\n", + ((match_options & PCRE2_ANCHORED) != 0)? ",anchored" : "", + ((match_options & PCRE2_ENDANCHORED) != 0)? ",endanchored" : "", + ((match_options & PCRE2_NO_JIT) != 0)? ",no_jit" : "", + ((match_options & PCRE2_NO_UTF_CHECK) != 0)? ",no_utf_check" : "", + ((match_options & PCRE2_NOTBOL) != 0)? ",notbol" : "", + ((match_options & PCRE2_NOTEMPTY) != 0)? ",notempty" : "", + ((match_options & PCRE2_NOTEMPTY_ATSTART) != 0)? ",notempty_atstart" : "", + ((match_options & PCRE2_NOTEOL) != 0)? ",noteol" : "", + ((match_options & PCRE2_PARTIAL_HARD) != 0)? ",partial_hard" : "", + ((match_options & PCRE2_PARTIAL_SOFT) != 0)? ",partial_soft" : ""); +#endif + + callout_count = 0; + errorcode = pcre2_match(code, (PCRE2_SPTR)data, (PCRE2_SIZE)match_size, 0, + match_options, match_data, match_context); + +#ifdef STANDALONE + if (errorcode >= 0) printf("Match returned %d\n", errorcode); else + { + unsigned char buffer[256]; + pcre2_get_error_message(errorcode, buffer, 256); + printf("Match failed: error %d: %s\n", errorcode, buffer); + } +#endif + + match_options = 0; /* For second time */ + } + + /* Match with DFA twice, with and without options. */ + + match_options = save_match_options & ~PCRE2_NO_JIT; /* Not valid for DFA */ + + for (j = 0; j < 2; j++) + { +#ifdef STANDALONE + printf("DFA match options %.8x", match_options); + printf("%s%s%s%s%s%s%s%s%s\n", + ((match_options & PCRE2_ANCHORED) != 0)? ",anchored" : "", + ((match_options & PCRE2_ENDANCHORED) != 0)? ",endanchored" : "", + ((match_options & PCRE2_NO_UTF_CHECK) != 0)? ",no_utf_check" : "", + ((match_options & PCRE2_NOTBOL) != 0)? ",notbol" : "", + ((match_options & PCRE2_NOTEMPTY) != 0)? ",notempty" : "", + ((match_options & PCRE2_NOTEMPTY_ATSTART) != 0)? ",notempty_atstart" : "", + ((match_options & PCRE2_NOTEOL) != 0)? ",noteol" : "", + ((match_options & PCRE2_PARTIAL_HARD) != 0)? ",partial_hard" : "", + ((match_options & PCRE2_PARTIAL_SOFT) != 0)? ",partial_soft" : ""); +#endif + + callout_count = 0; + errorcode = pcre2_dfa_match(code, (PCRE2_SPTR)data, + (PCRE2_SIZE)match_size, 0, match_options, match_data, match_context, + dfa_workspace, DFA_WORKSPACE_COUNT); + +#ifdef STANDALONE + if (errorcode >= 0) printf("Match returned %d\n", errorcode); else + { + unsigned char buffer[256]; + pcre2_get_error_message(errorcode, buffer, 256); + printf("Match failed: error %d: %s\n", errorcode, buffer); + } +#endif + + match_options = 0; /* For second time */ + } + + match_options = save_match_options; /* Reset for the second compile */ + pcre2_code_free(code); + } + + /* Compilation failed */ + + else + { + unsigned char buffer[256]; + pcre2_get_error_message(errorcode, buffer, 256); +#ifdef STANDALONE + printf("Error %d at offset %lu: %s\n", errorcode, erroroffset, buffer); +#else + if (strstr((const char *)buffer, "internal error") != NULL) abort(); +#endif + } + + compile_options = PCRE2_NEVER_BACKSLASH_C; /* For second time */ + } + +if (match_data != NULL) pcre2_match_data_free(match_data); +if (match_context != NULL) pcre2_match_context_free(match_context); + +return 0; +} + + +/* Optional main program. */ + +#ifdef STANDALONE +int main(int argc, char **argv) +{ +int i; + +if (argc < 2) + { + printf("** No arguments given\n"); + return 0; + } + +for (i = 1; i < argc; i++) + { + size_t filelen; + size_t readsize; + unsigned char *buffer; + FILE *f; + + /* Handle a literal string. Copy to an exact size buffer so that checks for + overrunning work. */ + + if (argv[i][0] == '=') + { + readsize = strlen(argv[i]) - 1; + printf("------ ------\n"); + printf("Length = %lu\n", readsize); + printf("%.*s\n", (int)readsize, argv[i]+1); + buffer = (unsigned char *)malloc(readsize); + if (buffer == NULL) + printf("** Failed to allocate %lu bytes of memory\n", readsize); + else + { + memcpy(buffer, argv[i]+1, readsize); + LLVMFuzzerTestOneInput(buffer, readsize); + free(buffer); + } + continue; + } + + /* Handle a string given in a file */ + + f = fopen(argv[i], "rb"); + if (f == NULL) + { + printf("** Failed to open %s: %s\n", argv[i], strerror(errno)); + continue; + } + + printf("------ %s ------\n", argv[i]); + + fseek(f, 0, SEEK_END); + filelen = ftell(f); + fseek(f, 0, SEEK_SET); + + buffer = (unsigned char *)malloc(filelen); + if (buffer == NULL) + { + printf("** Failed to allocate %lu bytes of memory\n", filelen); + fclose(f); + continue; + } + + readsize = fread(buffer, 1, filelen, f); + fclose(f); + + if (readsize != filelen) + printf("** File size is %lu but fread() returned %lu\n", filelen, readsize); + else + { + printf("Length = %lu\n", filelen); + LLVMFuzzerTestOneInput(buffer, filelen); + } + free(buffer); + } + +return 0; +} +#endif /* STANDALONE */ + +/* End */ diff --git a/pcre2-10.22/src/pcre2_internal.h b/pcre2-10.32/src/pcre2_internal.h similarity index 93% rename from pcre2-10.22/src/pcre2_internal.h rename to pcre2-10.32/src/pcre2_internal.h index 56908708a..8750f2f17 100644 --- a/pcre2-10.22/src/pcre2_internal.h +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -38,6 +38,9 @@ POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------------------------------------- */ +#ifndef PCRE2_INTERNAL_H_IDEMPOTENT_GUARD +#define PCRE2_INTERNAL_H_IDEMPOTENT_GUARD + /* We do not support both EBCDIC and Unicode at the same time. The "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 @@ -142,20 +145,6 @@ pcre2_match() because of the way it backtracks. */ #define PCRE2_SPTR CUSTOM_SUBJECT_PTR #endif -/* When compiling with the MSVC compiler, it is sometimes necessary to include -a "calling convention" before exported function names. (This is secondhand -information; I know nothing about MSVC myself). For example, something like - - void __cdecl function(....) - -might be needed. In order so make this easy, all the exported functions have -PCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not -set, we ensure here that it has no effect. */ - -#ifndef PCRE2_CALL_CONVENTION -#define PCRE2_CALL_CONVENTION -#endif - /* When checking for integer overflow in pcre2_compile(), we need to handle large integers. If a 64-bit integer type is available, we can use that. Otherwise we have to cast to double, which of course requires floating point @@ -176,6 +165,16 @@ by "configure". */ #define INT64_OR_DOUBLE double #endif +/* External (in the C sense) functions and tables that are private to the +libraries are always referenced using the PRIV macro. This makes it possible +for pcre2test.c to include some of the source files from the libraries using a +different PRIV definition to avoid name clashes. It also makes it clear in the +code that a non-static object is being referenced. */ + +#ifndef PRIV +#define PRIV(name) _pcre2_##name +#endif + /* When compiling for use with the Virtual Pascal compiler, these functions need to have their names changed. PCRE2 must be compiled with the -DVPCOMPAT option on the command line. */ @@ -189,50 +188,15 @@ option on the command line. */ #define memset(s,c,n) _memset(s,c,n) #else /* VPCOMPAT */ -/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(), -define a macro for memmove() if HAVE_MEMMOVE is false, provided that HAVE_BCOPY -is set. Otherwise, include an emulating function for those systems that have -neither (there some non-Unix environments where this is the case). */ +/* Otherwise, to cope with SunOS4 and other systems that lack memmove(), define +a macro that calls an emulating function. */ #ifndef HAVE_MEMMOVE -#undef memmove /* some systems may have a macro */ -#ifdef HAVE_BCOPY -#define memmove(a, b, c) bcopy(b, a, c) -#else /* HAVE_BCOPY */ -static void * -pcre2_memmove(void *d, const void *s, size_t n) -{ -size_t i; -unsigned char *dest = (unsigned char *)d; -const unsigned char *src = (const unsigned char *)s; -if (dest > src) - { - dest += n; - src += n; - for (i = 0; i < n; ++i) *(--dest) = *(--src); - return (void *)dest; - } -else - { - for (i = 0; i < n; ++i) *dest++ = *src++; - return (void *)(dest - n); - } -} -#define memmove(a, b, c) pcre2_memmove(a, b, c) -#endif /* not HAVE_BCOPY */ +#undef memmove /* Some systems may have a macro */ +#define memmove(a, b, c) PRIV(memmove)(a, b, c) #endif /* not HAVE_MEMMOVE */ #endif /* not VPCOMPAT */ -/* External (in the C sense) functions and tables that are private to the -libraries are always referenced using the PRIV macro. This makes it possible -for pcre2test.c to include some of the source files from the libraries using a -different PRIV definition to avoid name clashes. It also makes it clear in the -code that a non-static object is being referenced. */ - -#ifndef PRIV -#define PRIV(name) _pcre2_##name -#endif - /* This is an unsigned int value that no UTF character can ever have, as Unicode doesn't go beyond 0x0010ffff. */ @@ -254,6 +218,21 @@ not rely on this. */ #define COMPILE_ERROR_BASE 100 +/* The initial frames vector for remembering backtracking points in +pcre2_match() is allocated on the system stack, of this size (bytes). The size +must be a multiple of sizeof(PCRE2_SPTR) in all environments, so making it a +multiple of 8 is best. Typical frame sizes are a few hundred bytes (it depends +on the number of capturing parentheses) so 20KiB handles quite a few frames. A +larger vector on the heap is obtained for patterns that need more frames. The +maximum size of this can be limited. */ + +#define START_FRAMES_SIZE 20480 + +/* Similarly, for DFA matching, an initial internal workspace vector is +allocated on the stack. */ + +#define DFA_START_RWS_SIZE 30720 + /* Define the default BSR convention. */ #ifdef BSR_ANYCRLF @@ -561,9 +540,14 @@ enum { PCRE2_MATCHEDBY_INTERPRETER, /* pcre2_match() */ #define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ /* The maximum remaining length of subject we are prepared to search for a -req_unit match. */ +req_unit match. In 8-bit mode, memchr() is used and is much faster than the +search loop that has to be used in 16-bit and 32-bit modes. */ +#if PCRE2_CODE_UNIT_WIDTH == 8 +#define REQ_CU_MAX 2000 +#else #define REQ_CU_MAX 1000 +#endif /* Offsets for the bitmap tables in the cbits set of tables. Each table contains a set of bits for a class map. Some classes are built by combining @@ -581,14 +565,15 @@ these tables. */ #define cbit_cntrl 288 /* [:cntrl:] */ #define cbit_length 320 /* Length of the cbits table */ -/* Bit definitions for entries in the ctypes table. */ +/* Bit definitions for entries in the ctypes table. Do not change these values +without checking pcre2_jit_compile.c, which has an assertion to ensure that +ctype_word has the value 16. */ #define ctype_space 0x01 #define ctype_letter 0x02 #define ctype_digit 0x04 -#define ctype_xdigit 0x08 +#define ctype_xdigit 0x08 /* not actually used any more */ #define ctype_word 0x10 /* alphanumeric or '_' */ -#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ /* Offsets of the various tables from the base tables pointer, and total length of the tables. */ @@ -682,7 +667,7 @@ a positive value. */ /* The remaining definitions work in both environments. */ -#define CHAR_NULL '\0' +#define CHAR_NUL '\0' #define CHAR_HT '\t' #define CHAR_VT '\v' #define CHAR_FF '\f' @@ -923,6 +908,7 @@ a positive value. */ #define STRING_CRLF_RIGHTPAR "CRLF)" #define STRING_ANY_RIGHTPAR "ANY)" #define STRING_ANYCRLF_RIGHTPAR "ANYCRLF)" +#define STRING_NUL_RIGHTPAR "NUL)" #define STRING_BSR_ANYCRLF_RIGHTPAR "BSR_ANYCRLF)" #define STRING_BSR_UNICODE_RIGHTPAR "BSR_UNICODE)" #define STRING_UTF8_RIGHTPAR "UTF8)" @@ -936,7 +922,9 @@ a positive value. */ #define STRING_NO_START_OPT_RIGHTPAR "NO_START_OPT)" #define STRING_NOTEMPTY_RIGHTPAR "NOTEMPTY)" #define STRING_NOTEMPTY_ATSTART_RIGHTPAR "NOTEMPTY_ATSTART)" +#define STRING_LIMIT_HEAP_EQ "LIMIT_HEAP=" #define STRING_LIMIT_MATCH_EQ "LIMIT_MATCH=" +#define STRING_LIMIT_DEPTH_EQ "LIMIT_DEPTH=" #define STRING_LIMIT_RECURSION_EQ "LIMIT_RECURSION=" #define STRING_MARK "MARK" @@ -958,7 +946,7 @@ only. */ #define CHAR_ESC '\033' #define CHAR_DEL '\177' -#define CHAR_NULL '\0' +#define CHAR_NUL '\0' #define CHAR_SPACE '\040' #define CHAR_EXCLAMATION_MARK '\041' #define CHAR_QUOTATION_MARK '\042' @@ -1196,6 +1184,7 @@ only. */ #define STRING_CRLF_RIGHTPAR STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS #define STRING_ANY_RIGHTPAR STR_A STR_N STR_Y STR_RIGHT_PARENTHESIS #define STRING_ANYCRLF_RIGHTPAR STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS +#define STRING_NUL_RIGHTPAR STR_N STR_U STR_L STR_RIGHT_PARENTHESIS #define STRING_BSR_ANYCRLF_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS #define STRING_BSR_UNICODE_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS #define STRING_UTF8_RIGHTPAR STR_U STR_T STR_F STR_8 STR_RIGHT_PARENTHESIS @@ -1209,7 +1198,9 @@ only. */ #define STRING_NO_START_OPT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_S STR_T STR_A STR_R STR_T STR_UNDERSCORE STR_O STR_P STR_T STR_RIGHT_PARENTHESIS #define STRING_NOTEMPTY_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_RIGHT_PARENTHESIS #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_HEAP_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_H STR_E STR_A STR_P STR_EQUALS_SIGN #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_DEPTH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_D STR_E STR_P STR_T 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 @@ -1257,36 +1248,6 @@ contain characters with values greater than 255. */ #define XCL_PROP 3 /* Unicode property (2-byte property code follows) */ #define XCL_NOTPROP 4 /* Unicode inverted property (ditto) */ -/* Escape items that are just an encoding of a particular data value. These -appear in the escapes[] table in pcre2_compile.c as positive numbers. */ - -#ifndef ESC_a -#define ESC_a CHAR_BEL -#endif - -#ifndef ESC_e -#define ESC_e CHAR_ESC -#endif - -#ifndef ESC_f -#define ESC_f CHAR_FF -#endif - -#ifndef ESC_n -#define ESC_n CHAR_LF -#endif - -#ifndef ESC_r -#define ESC_r CHAR_CR -#endif - -/* We can't officially use ESC_t because it is a POSIX reserved identifier -(presumably because of all the others like size_t). */ - -#ifndef ESC_tee -#define ESC_tee CHAR_HT -#endif - /* These are escaped items that aren't just an encoding of a particular data value such as \n. They must have non-zero values, as check_escape() returns 0 for a data character. In the escapes[] table in pcre2_compile.c their values @@ -1298,23 +1259,16 @@ mode rather than an escape sequence. It is also used for [^] in JavaScript compatibility mode, and for \C in non-utf mode. In non-DOTALL mode, "." behaves like \N. -The special values ESC_DU, ESC_du, etc. are used instead of ESC_D, ESC_d, etc. -when PCRE2_UCP is set and replacement of \d etc by \p sequences is required. -They must be contiguous, and remain in order so that the replacements can be -looked up from a table. - Negative numbers are used to encode a backreference (\1, \2, \3, etc.) in -check_escape(). There are two tests in the code for an escape -greater than ESC_b and less than ESC_Z to detect the types that may be -repeated. These are the types that consume characters. If any new escapes are -put in between that don't consume a character, that code will have to change. -*/ +check_escape(). There are tests in the code for an escape greater than ESC_b +and less than ESC_Z to detect the types that may be repeated. These are the +types that consume characters. If any new escapes are put in between that don't +consume a character, that code will have to change. */ enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w, ESC_N, ESC_dum, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H, ESC_h, ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z, - ESC_E, ESC_Q, ESC_g, ESC_k, - ESC_DU, ESC_du, ESC_SU, ESC_su, ESC_WU, ESC_wu }; + ESC_E, ESC_Q, ESC_g, ESC_k }; /********************** Opcode definitions ******************/ @@ -1380,7 +1334,8 @@ enum { OP_CIRC, /* 27 Start of line - not multiline */ OP_CIRCM, /* 28 Start of line - multiline */ - /* Single characters; caseful must precede the caseless ones */ + /* Single characters; caseful must precede the caseless ones, and these + must remain in this order, and adjacent. */ OP_CHAR, /* 29 Match one character, casefully */ OP_CHARI, /* 30 Match one character, caselessly */ @@ -1530,53 +1485,55 @@ enum { OP_ASSERTBACK, /* 128 Positive lookbehind */ OP_ASSERTBACK_NOT, /* 129 Negative lookbehind */ - /* ONCE, ONCE_NC, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come immediately - after the assertions, with ONCE first, as there's a test for >= ONCE for a - subpattern that isn't an assertion. The POS versions must immediately follow - the non-POS versions in each case. */ + /* ONCE, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come immediately after the + assertions, with ONCE first, as there's a test for >= ONCE for a subpattern + that isn't an assertion. The POS versions must immediately follow the non-POS + versions in each case. */ OP_ONCE, /* 130 Atomic group, contains captures */ - OP_ONCE_NC, /* 131 Atomic group containing no captures */ - OP_BRA, /* 132 Start of non-capturing bracket */ - OP_BRAPOS, /* 133 Ditto, with unlimited, possessive repeat */ - OP_CBRA, /* 134 Start of capturing bracket */ - OP_CBRAPOS, /* 135 Ditto, with unlimited, possessive repeat */ - OP_COND, /* 136 Conditional group */ + OP_BRA, /* 131 Start of non-capturing bracket */ + OP_BRAPOS, /* 132 Ditto, with unlimited, possessive repeat */ + OP_CBRA, /* 133 Start of capturing bracket */ + OP_CBRAPOS, /* 134 Ditto, with unlimited, possessive repeat */ + OP_COND, /* 135 Conditional group */ /* These five must follow the previous five, in the same order. There's a check for >= SBRA to distinguish the two sets. */ - OP_SBRA, /* 137 Start of non-capturing bracket, check empty */ - OP_SBRAPOS, /* 138 Ditto, with unlimited, possessive repeat */ - OP_SCBRA, /* 139 Start of capturing bracket, check empty */ - OP_SCBRAPOS, /* 140 Ditto, with unlimited, possessive repeat */ - OP_SCOND, /* 141 Conditional group, check empty */ + OP_SBRA, /* 136 Start of non-capturing bracket, check empty */ + OP_SBRAPOS, /* 137 Ditto, with unlimited, possessive repeat */ + OP_SCBRA, /* 138 Start of capturing bracket, check empty */ + OP_SCBRAPOS, /* 139 Ditto, with unlimited, possessive repeat */ + OP_SCOND, /* 140 Conditional group, check empty */ /* The next two pairs must (respectively) be kept together. */ - OP_CREF, /* 142 Used to hold a capture number as condition */ - OP_DNCREF, /* 143 Used to point to duplicate names as a condition */ - OP_RREF, /* 144 Used to hold a recursion number as condition */ - OP_DNRREF, /* 145 Used to point to duplicate names as a condition */ - OP_FALSE, /* 146 Always false (used by DEFINE and VERSION) */ - OP_TRUE, /* 147 Always true (used by VERSION) */ + OP_CREF, /* 141 Used to hold a capture number as condition */ + OP_DNCREF, /* 142 Used to point to duplicate names as a condition */ + OP_RREF, /* 143 Used to hold a recursion number as condition */ + OP_DNRREF, /* 144 Used to point to duplicate names as a condition */ + OP_FALSE, /* 145 Always false (used by DEFINE and VERSION) */ + OP_TRUE, /* 146 Always true (used by VERSION) */ - OP_BRAZERO, /* 148 These two must remain together and in this */ - OP_BRAMINZERO, /* 149 order. */ - OP_BRAPOSZERO, /* 150 */ + OP_BRAZERO, /* 147 These two must remain together and in this */ + OP_BRAMINZERO, /* 148 order. */ + OP_BRAPOSZERO, /* 149 */ /* These are backtracking control verbs */ - OP_MARK, /* 151 always has an argument */ - OP_PRUNE, /* 152 */ - OP_PRUNE_ARG, /* 153 same, but with argument */ - OP_SKIP, /* 154 */ - OP_SKIP_ARG, /* 155 same, but with argument */ - OP_THEN, /* 156 */ - OP_THEN_ARG, /* 157 same, but with argument */ - OP_COMMIT, /* 158 */ + OP_MARK, /* 150 always has an argument */ + OP_PRUNE, /* 151 */ + OP_PRUNE_ARG, /* 152 same, but with argument */ + OP_SKIP, /* 153 */ + OP_SKIP_ARG, /* 154 same, but with argument */ + OP_THEN, /* 155 */ + OP_THEN_ARG, /* 156 same, but with argument */ + OP_COMMIT, /* 157 */ + OP_COMMIT_ARG, /* 158 same, but with argument */ - /* These are forced failure and success verbs */ + /* These are forced failure and success verbs. FAIL and ACCEPT do accept an + argument, but these cases can be compiled as, for example, (*MARK:X)(*FAIL) + without the need for a special opcode. */ OP_FAIL, /* 159 */ OP_ACCEPT, /* 160 */ @@ -1638,7 +1595,7 @@ some cases doesn't actually use these names at all). */ "Recurse", "Callout", "CalloutStr", \ "Alt", "Ket", "KetRmax", "KetRmin", "KetRpos", \ "Reverse", "Assert", "Assert not", "AssertB", "AssertB not", \ - "Once", "Once_NC", \ + "Once", \ "Bra", "BraPos", "CBra", "CBraPos", \ "Cond", \ "SBra", "SBraPos", "SCBra", "SCBraPos", \ @@ -1647,7 +1604,7 @@ some cases doesn't actually use these names at all). */ "Cond false", "Cond true", \ "Brazero", "Braminzero", "Braposzero", \ "*MARK", "*PRUNE", "*PRUNE", "*SKIP", "*SKIP", \ - "*THEN", "*THEN", "*COMMIT", "*FAIL", \ + "*THEN", "*THEN", "*COMMIT", "*COMMIT", "*FAIL", \ "*ACCEPT", "*ASSERT_ACCEPT", \ "Close", "Skip zero", "Define" @@ -1722,7 +1679,6 @@ in UTF-8 mode. The code that uses this table must know about such things. */ 1+LINK_SIZE, /* Assert behind */ \ 1+LINK_SIZE, /* Assert behind not */ \ 1+LINK_SIZE, /* ONCE */ \ - 1+LINK_SIZE, /* ONCE_NC */ \ 1+LINK_SIZE, /* BRA */ \ 1+LINK_SIZE, /* BRAPOS */ \ 1+LINK_SIZE+IMM2_SIZE, /* CBRA */ \ @@ -1740,7 +1696,8 @@ in UTF-8 mode. The code that uses this table must know about such things. */ 3, 1, 3, /* MARK, PRUNE, PRUNE_ARG */ \ 1, 3, /* SKIP, SKIP_ARG */ \ 1, 3, /* THEN, THEN_ARG */ \ - 1, 1, 1, 1, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ \ + 1, 3, /* COMMIT, COMMIT_ARG */ \ + 1, 1, 1, /* FAIL, ACCEPT, ASSERT_ACCEPT */ \ 1+IMM2_SIZE, 1, /* CLOSE, SKIPZERO */ \ 1 /* DEFINE */ @@ -1768,6 +1725,7 @@ typedef struct open_capitem { struct open_capitem *next; /* Chain link */ uint16_t number; /* Capture number */ uint16_t flag; /* Set TRUE if recursive back ref */ + uint16_t assert_depth; /* Assertion depth when opened */ } open_capitem; /* Layout of the UCP type table that translates property names into types and @@ -1794,10 +1752,17 @@ typedef struct { /* UCD access macros */ #define UCD_BLOCK_SIZE 128 -#define GET_UCD(ch) (PRIV(ucd_records) + \ +#define REAL_GET_UCD(ch) (PRIV(ucd_records) + \ PRIV(ucd_stage2)[PRIV(ucd_stage1)[(int)(ch) / UCD_BLOCK_SIZE] * \ UCD_BLOCK_SIZE + (int)(ch) % UCD_BLOCK_SIZE]) +#if PCRE2_CODE_UNIT_WIDTH == 32 +#define GET_UCD(ch) ((ch > MAX_UTF_CODE_POINT)? \ + PRIV(dummy_ucd_record) : REAL_GET_UCD(ch)) +#else +#define GET_UCD(ch) REAL_GET_UCD(ch) +#endif + #define UCD_CHARTYPE(ch) GET_UCD(ch)->chartype #define UCD_SCRIPT(ch) GET_UCD(ch)->script #define UCD_CATEGORY(ch) PRIV(ucp_gentype)[UCD_CHARTYPE(ch)] @@ -1852,8 +1817,12 @@ extern const uint8_t PRIV(utf8_table4)[]; #define _pcre2_callout_end_delims PCRE2_SUFFIX(_pcre2_callout_end_delims_) #define _pcre2_callout_start_delims PCRE2_SUFFIX(_pcre2_callout_start_delims_) #define _pcre2_default_compile_context PCRE2_SUFFIX(_pcre2_default_compile_context_) +#define _pcre2_default_convert_context PCRE2_SUFFIX(_pcre2_default_convert_context_) #define _pcre2_default_match_context PCRE2_SUFFIX(_pcre2_default_match_context_) #define _pcre2_default_tables PCRE2_SUFFIX(_pcre2_default_tables_) +#if PCRE2_CODE_UNIT_WIDTH == 32 +#define _pcre2_dummy_ucd_record PCRE2_SUFFIX(_pcre2_dummy_ucd_record_) +#endif #define _pcre2_hspace_list PCRE2_SUFFIX(_pcre2_hspace_list_) #define _pcre2_vspace_list PCRE2_SUFFIX(_pcre2_vspace_list_) #define _pcre2_ucd_caseless_sets PCRE2_SUFFIX(_pcre2_ucd_caseless_sets_) @@ -1872,13 +1841,17 @@ extern const uint8_t PRIV(OP_lengths)[]; extern const uint32_t PRIV(callout_end_delims)[]; extern const uint32_t PRIV(callout_start_delims)[]; extern const pcre2_compile_context PRIV(default_compile_context); +extern const pcre2_convert_context PRIV(default_convert_context); extern const pcre2_match_context PRIV(default_match_context); extern const uint8_t PRIV(default_tables)[]; extern const uint32_t PRIV(hspace_list)[]; extern const uint32_t PRIV(vspace_list)[]; extern const uint32_t PRIV(ucd_caseless_sets)[]; extern const ucd_record PRIV(ucd_records)[]; -extern const uint8_t PRIV(ucd_stage1)[]; +#if PCRE2_CODE_UNIT_WIDTH == 32 +extern const ucd_record PRIV(dummy_ucd_record)[]; +#endif +extern const uint16_t PRIV(ucd_stage1)[]; extern const uint16_t PRIV(ucd_stage2)[]; extern const uint32_t PRIV(ucp_gbtable)[]; extern const uint32_t PRIV(ucp_gentype)[]; @@ -1912,6 +1885,7 @@ is available. */ #define _pcre2_auto_possessify PCRE2_SUFFIX(_pcre2_auto_possessify_) #define _pcre2_check_escape PCRE2_SUFFIX(_pcre2_check_escape_) +#define _pcre2_extuni PCRE2_SUFFIX(_pcre2_extuni_) #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_) @@ -1935,6 +1909,8 @@ 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_extuni(uint32_t, PCRE2_SPTR, PCRE2_SPTR, PCRE2_SPTR, + BOOL, int *); extern PCRE2_SPTR _pcre2_find_bracket(PCRE2_SPTR, BOOL, int); extern BOOL _pcre2_is_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR, uint32_t *, BOOL); @@ -1955,6 +1931,15 @@ extern int _pcre2_valid_utf(PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE *); extern BOOL _pcre2_was_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR, uint32_t *, BOOL); extern BOOL _pcre2_xclass(uint32_t, PCRE2_SPTR, BOOL); + +/* This function is needed only when memmove() is not available. */ + +#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE) +#define _pcre2_memmove PCRE2_SUFFIX(_pcre2_memmove) +extern void * _pcre2_memmove(void *, const void *, size_t); +#endif + #endif /* PCRE2_CODE_UNIT_WIDTH */ +#endif /* PCRE2_INTERNAL_H_IDEMPOTENT_GUARD */ /* End of pcre2_internal.h */ diff --git a/pcre2-10.22/src/pcre2_intmodedep.h b/pcre2-10.32/src/pcre2_intmodedep.h similarity index 79% rename from pcre2-10.22/src/pcre2_intmodedep.h rename to pcre2-10.32/src/pcre2_intmodedep.h index 596d62cfd..62626d0a8 100644 --- a/pcre2-10.22/src/pcre2_intmodedep.h +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -54,6 +54,7 @@ just to undefine them all. */ #undef ACROSSCHAR #undef BACKCHAR #undef BYTES2CU +#undef CHMAX_255 #undef CU2BYTES #undef FORWARDCHAR #undef FORWARDCHARTEST @@ -140,7 +141,7 @@ values of 3 or 4 are also supported. */ #undef LINK_SIZE #define LINK_SIZE 1 #define PUT(a,n,d) \ - (a[n] = (d)) + (a[n] = (PCRE2_UCHAR)(d)) #define GET(a,n) \ (a[n]) #define MAX_PATTERN_SIZE (1 << 16) @@ -200,21 +201,26 @@ arithmetic results in a signed value. Hence the cast. */ #endif /* Other macros that are different for 8-bit mode. The MAX_255 macro checks -whether its argument is less than 256. The maximum length of a MARK name must -fit in one code unit; currently it is set to 255 or 65535. The TABLE_GET macro -is used to access elements of tables containing exactly 256 items. When code -points can be greater than 255, a check is needed before accessing these -tables. */ +whether its argument, which is assumed to be one code unit, is less than 256. +The CHMAX_255 macro does not assume one code unit. The maximum length of a MARK +name must fit in one code unit; currently it is set to 255 or 65535. The +TABLE_GET macro is used to access elements of tables containing exactly 256 +items. When code points can be greater than 255, a check is needed before +accessing these tables. */ #if PCRE2_CODE_UNIT_WIDTH == 8 #define MAX_255(c) TRUE #define MAX_MARK ((1u << 8) - 1) #ifdef SUPPORT_UNICODE #define SUPPORT_WIDE_CHARS +#define CHMAX_255(c) ((c) <= 255u) +#else +#define CHMAX_255(c) TRUE #endif /* SUPPORT_UNICODE */ #define TABLE_GET(c, table, default) ((table)[c]) #else /* Code units are 16 or 32 bits */ +#define CHMAX_255(c) ((c) <= 255u) #define MAX_255(c) ((c) <= 255u) #define MAX_MARK ((1u << 16) - 1) #define SUPPORT_WIDE_CHARS @@ -345,7 +351,7 @@ because almost all calls are already within a block of UTF-8 only code. */ /* Same as above, but it allows a fully customizable form. */ #define ACROSSCHAR(condition, eptr, action) \ - while((condition) && ((eptr) & 0xc0u) == 0x80u) action + while((condition) && ((*eptr) & 0xc0u) == 0x80u) action /* Deposit a character into memory, returning the number of code units. */ @@ -451,7 +457,7 @@ code. */ /* Same as above, but it allows a fully customizable form. */ #define ACROSSCHAR(condition, eptr, action) \ - if ((condition) && ((eptr) & 0xfc00u) == 0xdc00u) action + if ((condition) && ((*eptr) & 0xfc00u) == 0xdc00u) action /* Deposit a character into memory, returning the number of code units. */ @@ -566,15 +572,13 @@ typedef struct pcre2_real_compile_context { uint16_t bsr_convention; uint16_t newline_convention; uint32_t parens_nest_limit; + uint32_t extra_options; } pcre2_real_compile_context; /* The real match context structure. */ typedef struct pcre2_real_match_context { pcre2_memctl memctl; -#ifdef HEAP_MATCH_RECURSE - pcre2_memctl stack_memctl; -#endif #ifdef SUPPORT_JIT pcre2_jit_callback jit_callback; void *jit_callback_data; @@ -582,10 +586,19 @@ typedef struct pcre2_real_match_context { int (*callout)(pcre2_callout_block *, void *); void *callout_data; PCRE2_SIZE offset_limit; + uint32_t heap_limit; uint32_t match_limit; - uint32_t recursion_limit; + uint32_t depth_limit; } pcre2_real_match_context; +/* The real convert context structure. */ + +typedef struct pcre2_real_convert_context { + pcre2_memctl memctl; + uint32_t glob_separator; + uint32_t glob_escape; +} pcre2_real_convert_context; + /* The real compiled code structure. The type for the blocksize field is defined specially because it is required in pcre2_serialize_decode() when copying the size from possibly unaligned memory into a variable of the same @@ -610,9 +623,11 @@ typedef struct pcre2_real_code { uint32_t magic_number; /* Paranoid and endianness check */ uint32_t compile_options; /* Options passed to pcre2_compile() */ uint32_t overall_options; /* Options after processing the pattern */ + uint32_t extra_options; /* Taken from compile_context */ uint32_t flags; /* Various state flags */ + uint32_t limit_heap; /* Limit set in the pattern */ uint32_t limit_match; /* Limit set in the pattern */ - uint32_t limit_recursion; /* Limit set in the pattern */ + uint32_t limit_depth; /* Limit set in the pattern */ uint32_t first_codeunit; /* Starting code unit */ uint32_t last_codeunit; /* This codeunit must be seen */ uint16_t bsr_convention; /* What \R matches */ @@ -625,7 +640,13 @@ typedef struct pcre2_real_code { uint16_t name_count; /* Number of name entries in the table */ } pcre2_real_code; -/* The real match data structure. */ +/* The real match data structure. Define ovector as large as it can ever +actually be so that array bound checkers don't grumble. Memory for this +structure is obtained by calling pcre2_match_data_create(), which sets the size +as the offset of ovector plus a pair of elements for each capturable string, so +the size varies from call to call. As the maximum number of capturing +subpatterns is 65535 we must allow for 65536 strings to include the overall +match. (See also the heapframe structure below.) */ typedef struct pcre2_real_match_data { pcre2_memctl memctl; @@ -638,7 +659,7 @@ typedef struct pcre2_real_match_data { uint16_t matchedby; /* Type of match (normal, JIT, DFA) */ uint16_t oveccount; /* Number of pairs */ int rc; /* The return code from the match */ - PCRE2_SIZE ovector[1]; /* The first field */ + PCRE2_SIZE ovector[131072]; /* Must be last in the structure */ } pcre2_real_match_data; @@ -648,18 +669,24 @@ typedef struct pcre2_real_match_data { #ifndef PCRE2_PCRE2TEST -/* Structure for checking for mutual recursion when scanning compiled code. */ +/* Structures for checking for mutual recursion when scanning compiled or +parsed code. */ typedef struct recurse_check { struct recurse_check *prev; PCRE2_SPTR group; } recurse_check; +typedef struct parsed_recurse_check { + struct parsed_recurse_check *prev; + uint32_t *groupptr; +} parsed_recurse_check; + /* Structure for building a cache when filling in recursion offsets. */ typedef struct recurse_cache { PCRE2_SPTR group; - int recno; + int groupnumber; } recurse_cache; /* Structure for maintaining a chain of pointers to the currently incomplete @@ -693,34 +720,37 @@ 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_SPTR nestptr[2]; /* Pointer(s) saved for string substitution */ PCRE2_UCHAR *name_table; /* The name/number table */ - size_t workspace_size; /* Size of workspace */ + PCRE2_SIZE workspace_size; /* Size of workspace */ + PCRE2_SIZE small_ref_offset[10]; /* Offsets for \1 to \9 */ + PCRE2_SIZE erroroffset; /* Offset of error in pattern */ uint16_t names_found; /* Number of entries so far */ uint16_t name_entry_size; /* Size of each entry */ + uint16_t parens_depth; /* Depth of nested parentheses */ + uint16_t assert_depth; /* Depth of nested assertions */ open_capitem *open_caps; /* Chain of open capture items */ named_group *named_groups; /* Points to vector in pre-compile */ uint32_t named_group_list_size; /* Number of entries in the list */ uint32_t external_options; /* External (initial) options */ 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 bracount; /* Count of capturing parentheses */ + uint32_t lastcapture; /* Last capture encountered */ + uint32_t *parsed_pattern; /* Parsed pattern buffer */ + uint32_t *parsed_pattern_end; /* Parsed pattern should not get here */ 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 */ uint32_t nllen; /* Newline string length */ + uint32_t class_range_start; /* Overall class range start */ + uint32_t class_range_end; /* Overall class range end */ PCRE2_UCHAR nl[4]; /* Newline string when fixed length */ int max_lookbehind; /* Maximum lookbehind (characters) */ - int parens_depth; /* Depth of nested parentheses */ - int assert_depth; /* Depth of nested assertions */ 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 */ } compile_block; /* Structure for keeping the properties of the in-memory stack used @@ -731,27 +761,8 @@ typedef struct pcre2_real_jit_stack { void* stack; } pcre2_real_jit_stack; -/* Structure for keeping a chain of heap blocks used for saving ovectors -during pattern recursion when the ovector is larger than can be saved on -the system stack. */ - -typedef struct ovecsave_frame { - struct ovecsave_frame *next; /* Next frame on free chain */ - PCRE2_SIZE saved_ovec[1]; /* First vector element */ -} ovecsave_frame; - /* Structure for items in a linked list that represents an explicit recursive -call within the pattern; used by pcre_match(). */ - -typedef struct recursion_info { - struct recursion_info *prevrec; /* Previous recursion record (or NULL) */ - unsigned int group_num; /* Number of group that was called */ - PCRE2_SIZE *ovec_save; /* Pointer to saved ovector frame */ - uint32_t saved_capture_last; /* Last capture number */ - PCRE2_SPTR subject_position; /* Position at start of recursion */ -} recursion_info; - -/* A similar structure for pcre_dfa_match(). */ +call within the pattern when running pcre_dfa_match(). */ typedef struct dfa_recursion_info { struct dfa_recursion_info *prevrec; @@ -759,35 +770,90 @@ typedef struct dfa_recursion_info { uint32_t group_num; } dfa_recursion_info; -/* Structure for building a chain of data for holding the values of the subject -pointer at the start of each subpattern, so as to detect when an empty string -has been matched by a subpattern - to break infinite loops; used by -pcre2_match(). */ +/* Structure for "stack" frames that are used for remembering backtracking +positions during matching. As these are used in a vector, with the ovector item +being extended, the size of the structure must be a multiple of PCRE2_SIZE. The +only way to check this at compile time is to force an error by generating an +array with a negative size. By putting this in a typedef (which is never used), +we don't generate any code when all is well. */ -typedef struct eptrblock { - struct eptrblock *epb_prev; - PCRE2_SPTR epb_saved_eptr; -} eptrblock; +typedef struct heapframe { + + /* The first set of fields are variables that have to be preserved over calls + to RRMATCH(), but which do not need to be copied to new frames. */ + + PCRE2_SPTR ecode; /* The current position in the pattern */ + PCRE2_SPTR temp_sptr[2]; /* Used for short-term PCRE_SPTR values */ + PCRE2_SIZE length; /* Used for character, string, or code lengths */ + PCRE2_SIZE back_frame; /* Amount to subtract on RRETURN */ + PCRE2_SIZE temp_size; /* Used for short-term PCRE2_SIZE values */ + uint32_t rdepth; /* "Recursion" depth */ + uint32_t group_frame_type; /* Type information for group frames */ + uint32_t temp_32[4]; /* Used for short-term 32-bit or BOOL values */ + uint8_t return_id; /* Where to go on in internal "return" */ + uint8_t op; /* Processing opcode */ + + /* At this point, the structure is 16-bit aligned. On most architectures + the alignment requirement for a pointer will ensure that the eptr field below + is 32-bit or 64-bit aligned. However, on m68k it is fine to have a pointer + that is 16-bit aligned. We must therefore ensure that what comes between here + and eptr is an odd multiple of 16 bits so as to get back into 32-bit + alignment. This happens naturally when PCRE2_UCHAR is 8 bits wide, but needs + fudges in the other cases. In the 32-bit case the padding comes first so that + the occu field itself is 32-bit aligned. Without the padding, this structure + is no longer a multiple of PCRE2_SIZE on m68k, and the check below fails. */ + +#if PCRE2_CODE_UNIT_WIDTH == 8 + PCRE2_UCHAR occu[6]; /* Used for other case code units */ +#elif PCRE2_CODE_UNIT_WIDTH == 16 + PCRE2_UCHAR occu[2]; /* Used for other case code units */ + uint8_t unused[2]; /* Ensure 32-bit alignment (see above) */ +#else + uint8_t unused[2]; /* Ensure 32-bit alignment (see above) */ + PCRE2_UCHAR occu[1]; /* Used for other case code units */ +#endif + + /* The rest have to be copied from the previous frame whenever a new frame + becomes current. The final field is specified as a large vector so that + runtime array bound checks don't catch references to it. However, for any + specific call to pcre2_match() the memory allocated for each frame structure + allows for exactly the right size ovector for the number of capturing + parentheses. (See also the comment for pcre2_real_match_data above.) */ + + PCRE2_SPTR eptr; /* MUST BE FIRST */ + PCRE2_SPTR start_match; /* Can be adjusted by \K */ + PCRE2_SPTR mark; /* Most recent mark on the success path */ + uint32_t current_recurse; /* Current (deepest) recursion number */ + uint32_t capture_last; /* Most recent capture */ + PCRE2_SIZE last_group_offset; /* Saved offset to most recent group frame */ + PCRE2_SIZE offset_top; /* Offset after highest capture */ + PCRE2_SIZE ovector[131072]; /* Must be last in the structure */ +} heapframe; + +/* This typedef is a check that the size of the heapframe structure is a +multiple of PCRE2_SIZE. See various comments above. */ + +typedef char check_heapframe_size[ + ((sizeof(heapframe) % sizeof(PCRE2_SIZE)) == 0)? (+1):(-1)]; /* Structure for passing "static" information around between the functions doing traditional NFA matching (pcre2_match() and friends). */ typedef struct match_block { pcre2_memctl memctl; /* For general use */ -#ifdef HEAP_MATCH_RECURSE - pcre2_memctl stack_memctl; /* For "stack" frames */ -#endif - uint32_t match_call_count; /* As it says */ + PCRE2_SIZE frame_vector_size; /* Size of a backtracking frame */ + heapframe *match_frames; /* Points to vector of frames */ + heapframe *match_frames_top; /* Points after the end of the vector */ + heapframe *stack_frames; /* The original vector on the stack */ + PCRE2_SIZE heap_limit; /* As it says */ uint32_t match_limit; /* As it says */ - uint32_t match_limit_recursion; /* As it says */ + uint32_t match_limit_depth; /* As it says */ + uint32_t match_call_count; /* Number of times a new frame is created */ BOOL hitend; /* Hit the end of the subject at some point */ BOOL hasthen; /* Pattern contains (*THEN) */ const uint8_t *lcc; /* Points to lower casing table */ const uint8_t *fcc; /* Points to case-flipping table */ const uint8_t *ctypes; /* Points to table of type maps */ - PCRE2_SIZE *ovector; /* Pointer to the offset vector */ - PCRE2_SIZE offset_end; /* One past the end */ - PCRE2_SIZE offset_max; /* The maximum usable for return data */ PCRE2_SIZE start_offset; /* The start offset value */ PCRE2_SIZE end_offset_top; /* Highwater mark at end of match */ uint16_t partial; /* PARTIAL options */ @@ -798,30 +864,24 @@ typedef struct match_block { PCRE2_SPTR start_code; /* For use when recursing */ PCRE2_SPTR start_subject; /* Start of the subject string */ PCRE2_SPTR end_subject; /* End of the subject string */ - PCRE2_SPTR start_match_ptr; /* Start of matched string */ PCRE2_SPTR end_match_ptr; /* Subject position at end match */ PCRE2_SPTR start_used_ptr; /* Earliest consulted character */ PCRE2_SPTR last_used_ptr; /* Latest consulted character */ PCRE2_SPTR mark; /* Mark pointer to pass back on success */ PCRE2_SPTR nomatch_mark; /* Mark pointer to pass back on failure */ - PCRE2_SPTR once_target; /* Where to back up to for atomic groups */ + PCRE2_SPTR verb_ecode_ptr; /* For passing back info */ + PCRE2_SPTR verb_skip_ptr; /* For passing back a (*SKIP) name */ + uint32_t verb_current_recurse; /* Current recurse when (*VERB) happens */ uint32_t moptions; /* Match options */ uint32_t poptions; /* Pattern options */ - uint32_t capture_last; /* Most recent capture number + overflow flag */ uint32_t skip_arg_count; /* For counting SKIP_ARGs */ uint32_t ignore_skip_arg; /* For re-run when SKIP arg name not found */ - uint32_t match_function_type; /* Set for certain special calls of match() */ uint32_t nltype; /* Newline type */ uint32_t nllen; /* Newline string length */ PCRE2_UCHAR nl[4]; /* Newline string when fixed */ - eptrblock *eptrchain; /* Chain of eptrblocks for tail recursions */ - recursion_info *recursive; /* Linked list of recursion data */ - ovecsave_frame *ovecsave_chain; /* Linked list of free ovecsave blocks */ + pcre2_callout_block *cb; /* Points to a callout block */ void *callout_data; /* To pass back to callouts */ int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */ -#ifdef HEAP_MATCH_RECURSE - void *match_frames_base; /* For remembering malloc'd frames */ -#endif } match_block; /* A similar structure is used for the same purpose by the DFA matching @@ -836,12 +896,18 @@ typedef struct dfa_match_block { PCRE2_SPTR last_used_ptr; /* Latest consulted character */ const uint8_t *tables; /* Character tables */ PCRE2_SIZE start_offset; /* The start offset value */ + PCRE2_SIZE heap_limit; /* As it says */ + PCRE2_SIZE heap_used; /* As it says */ + uint32_t match_limit; /* As it says */ + uint32_t match_limit_depth; /* As it says */ + uint32_t match_call_count; /* Number of calls of internal function */ uint32_t moptions; /* Match options */ uint32_t poptions; /* Pattern options */ uint32_t nltype; /* Newline type */ uint32_t nllen; /* Newline string length */ PCRE2_UCHAR nl[4]; /* Newline string when fixed */ uint16_t bsr_convention; /* \R interpretation */ + pcre2_callout_block *cb; /* Points to a callout block */ void *callout_data; /* To pass back to callouts */ int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */ dfa_recursion_info *recursive; /* Linked list of recursion data */ diff --git a/pcre2-10.22/src/pcre2_jit_compile.c b/pcre2-10.32/src/pcre2_jit_compile.c similarity index 77% rename from pcre2-10.22/src/pcre2_jit_compile.c rename to pcre2-10.32/src/pcre2_jit_compile.c index 8dea90a1c..32e985b79 100644 --- a/pcre2-10.22/src/pcre2_jit_compile.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -228,7 +228,7 @@ enum control_types { type_then_trap = 1 }; -typedef int (SLJIT_CALL *jit_function)(jit_arguments *args); +typedef int (SLJIT_FUNC *jit_function)(jit_arguments *args); /* The following structure is the key data type for the recursive code generator. It is allocated by compile_matchingpath, and contains @@ -313,16 +313,25 @@ typedef struct ref_iterator_backtrack { typedef struct recurse_entry { struct recurse_entry *next; - /* Contains the function entry. */ - struct sljit_label *entry; - /* Collects the calls until the function is not created. */ - jump_list *calls; + /* Contains the function entry label. */ + struct sljit_label *entry_label; + /* Contains the function entry label. */ + struct sljit_label *backtrack_label; + /* Collects the entry calls until the function is not created. */ + jump_list *entry_calls; + /* Collects the backtrack calls until the function is not created. */ + jump_list *backtrack_calls; /* Points to the starting opcode. */ sljit_sw start; } recurse_entry; typedef struct recurse_backtrack { backtrack_common common; + /* Return to the matching path. */ + struct sljit_label *matchingpath; + /* Recursive pattern. */ + recurse_entry *entry; + /* Pattern is inlined. */ BOOL inlined_pattern; } recurse_backtrack; @@ -341,11 +350,26 @@ typedef struct then_trap_backtrack { int framesize; } then_trap_backtrack; -#define MAX_RANGE_SIZE 4 +#define MAX_N_CHARS 12 +#define MAX_DIFF_CHARS 5 + +typedef struct fast_forward_char_data { + /* Number of characters in the chars array, 255 for any character. */ + sljit_u8 count; + /* Number of last UTF-8 characters in the chars array. */ + sljit_u8 last_count; + /* Available characters in the current position. */ + PCRE2_UCHAR chars[MAX_DIFF_CHARS]; +} fast_forward_char_data; + +#define MAX_CLASS_RANGE_SIZE 4 +#define MAX_CLASS_CHARS_SIZE 3 typedef struct compiler_common { /* The sljit ceneric compiler. */ struct sljit_compiler *compiler; + /* Compiled regular expression. */ + pcre2_real_code *re; /* First byte code. */ PCRE2_SPTR start; /* Maps private data offset to each opcode. */ @@ -402,10 +426,10 @@ typedef struct compiler_common { 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. */ - BOOL positive_assert; + /* Quit is redirected by recurse, negative assertion, or positive assertion in conditional block. */ + BOOL local_quit_available; + /* Currently in a positive assertion. */ + BOOL in_positive_assertion; /* Newline control. */ int nltype; sljit_u32 nlmax; @@ -426,7 +450,7 @@ typedef struct compiler_common { /* Labels and jump lists. */ struct sljit_label *partialmatchlabel; struct sljit_label *quit_label; - struct sljit_label *forced_quit_label; + struct sljit_label *abort_label; struct sljit_label *accept_label; struct sljit_label *ff_newline_shortcut; stub_list *stubs; @@ -435,8 +459,9 @@ typedef struct compiler_common { recurse_entry *currententry; jump_list *partialmatch; jump_list *quit; - jump_list *positive_assert_quit; - jump_list *forced_quit; + jump_list *positive_assertion_quit; + jump_list *abort; + jump_list *failed_match; jump_list *accept; jump_list *calllimit; jump_list *stackalloc; @@ -500,14 +525,29 @@ typedef struct compare_context { #undef CMP /* Used for accessing the elements of the stack. */ -#define STACK(i) ((-(i) - 1) * (int)sizeof(sljit_sw)) +#define STACK(i) ((i) * (int)sizeof(sljit_sw)) + +#ifdef SLJIT_PREF_SHIFT_REG +#if SLJIT_PREF_SHIFT_REG == SLJIT_R2 +/* Nothing. */ +#elif SLJIT_PREF_SHIFT_REG == SLJIT_R3 +#define SHIFT_REG_IS_R3 +#else +#error "Unsupported shift register" +#endif +#endif #define TMP1 SLJIT_R0 +#ifdef SHIFT_REG_IS_R3 +#define TMP2 SLJIT_R3 +#define TMP3 SLJIT_R2 +#else #define TMP2 SLJIT_R2 #define TMP3 SLJIT_R3 -#define STR_PTR SLJIT_S0 -#define STR_END SLJIT_S1 -#define STACK_TOP SLJIT_R1 +#endif +#define STR_PTR SLJIT_R1 +#define STR_END SLJIT_S0 +#define STACK_TOP SLJIT_S1 #define STACK_LIMIT SLJIT_S2 #define COUNT_MATCH SLJIT_S3 #define ARGUMENTS SLJIT_S4 @@ -533,16 +573,13 @@ the start pointers when the end of the capturing group has not yet reached. */ #if PCRE2_CODE_UNIT_WIDTH == 8 #define MOV_UCHAR SLJIT_MOV_U8 -#define MOVU_UCHAR SLJIT_MOVU_U8 #define IN_UCHARS(x) (x) #elif PCRE2_CODE_UNIT_WIDTH == 16 #define MOV_UCHAR SLJIT_MOV_U16 -#define MOVU_UCHAR SLJIT_MOVU_U16 #define UCHAR_SHIFT (1) #define IN_UCHARS(x) ((x) * 2) #elif PCRE2_CODE_UNIT_WIDTH == 32 #define MOV_UCHAR SLJIT_MOV_U32 -#define MOVU_UCHAR SLJIT_MOVU_U32 #define UCHAR_SHIFT (2) #define IN_UCHARS(x) ((x) * 4) #else @@ -570,13 +607,17 @@ the start pointers when the end of the capturing group has not yet reached. */ sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)) #define CMPTO(type, src1, src1w, src2, src2w, label) \ sljit_set_label(sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)), (label)) -#define OP_FLAGS(op, dst, dstw, src, srcw, type) \ - sljit_emit_op_flags(compiler, (op), (dst), (dstw), (src), (srcw), (type)) +#define OP_FLAGS(op, dst, dstw, type) \ + sljit_emit_op_flags(compiler, (op), (dst), (dstw), (type)) +#define CMOV(type, dst_reg, src, srcw) \ + sljit_emit_cmov(compiler, (type), (dst_reg), (src), (srcw)) #define GET_LOCAL_BASE(dst, dstw, offset) \ sljit_get_local_base(compiler, (dst), (dstw), (offset)) #define READ_CHAR_MAX 0x7fffffff +#define INVALID_UTF_CHAR 888 + static PCRE2_SPTR bracketend(PCRE2_SPTR cc) { SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NOT) || (*cc >= OP_ONCE && *cc <= OP_SCOND)); @@ -606,8 +647,8 @@ return count; set_private_data_ptrs get_framesize init_frame - get_private_data_copy_length - copy_private_data + get_recurse_data_length + copy_recurse_data compile_matchingpath compile_backtrackingpath */ @@ -675,7 +716,6 @@ switch(*cc) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_BRAPOS: case OP_CBRA: @@ -799,6 +839,7 @@ switch(*cc) #endif case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: @@ -806,7 +847,7 @@ switch(*cc) default: /* All opcodes are supported now! */ - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return NULL; } } @@ -899,6 +940,7 @@ while (cc < ccend) common->control_head_ptr = 1; /* Fall through. */ + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_MARK: if (common->mark_ptr == 0) @@ -1304,7 +1346,7 @@ while (cc < ccend) if (private_data_ptr > SLJIT_MAX_LOCAL_SIZE) break; - if (repeat_check && (*cc == OP_ONCE || *cc == OP_ONCE_NC || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND)) + if (repeat_check && (*cc == OP_ONCE || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND)) { if (detect_repeat(common, cc)) { @@ -1333,7 +1375,6 @@ while (cc < ccend) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: case OP_BRAPOS: case OP_SBRA: case OP_SBRAPOS: @@ -1514,6 +1555,7 @@ while (cc < ccend) break; case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_THEN_ARG: SLJIT_ASSERT(common->mark_ptr != 0); @@ -1654,11 +1696,11 @@ if (length > 0) return stack_restore ? no_frame : no_stack; } -static void init_frame(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, int stackpos, int stacktop, BOOL recursive) +static void init_frame(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, int stackpos, int stacktop) { DEFINE_COMPILER; -BOOL setsom_found = recursive; -BOOL setmark_found = recursive; +BOOL setsom_found = FALSE; +BOOL setmark_found = FALSE; /* The last capture is a local variable even for recursions. */ BOOL capture_last_found = FALSE; int offset; @@ -1671,7 +1713,7 @@ stackpos = STACK(stackpos); if (ccend == NULL) { ccend = bracketend(cc) - (1 + LINK_SIZE); - if (recursive || (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS)) + if (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS) cc = next_opcode(common, cc); } @@ -1685,15 +1727,16 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0)); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); setsom_found = TRUE; } cc += 1; break; case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_THEN_ARG: SLJIT_ASSERT(common->mark_ptr != 0); @@ -1701,9 +1744,9 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); setmark_found = TRUE; } cc += 1 + 2 + cc[1]; @@ -1714,27 +1757,27 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0)); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); setsom_found = TRUE; } if (common->mark_ptr != 0 && !setmark_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); setmark_found = TRUE; } if (common->capture_last_ptr != 0 && !capture_last_found) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); capture_last_found = TRUE; } cc += 1 + LINK_SIZE; @@ -1748,20 +1791,20 @@ while (cc < ccend) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); capture_last_found = TRUE; } offset = (GET2(cc, 1 + LINK_SIZE)) << 1; OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, OVECTOR(offset)); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP2, 0); - stackpos += (int)sizeof(sljit_sw); + stackpos -= (int)sizeof(sljit_sw); cc += 1 + LINK_SIZE + IMM2_SIZE; break; @@ -1776,21 +1819,127 @@ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, 0); SLJIT_ASSERT(stackpos == STACK(stacktop)); } -static SLJIT_INLINE int get_private_data_copy_length(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, BOOL needs_control_head) +#define RECURSE_TMP_REG_COUNT 3 + +typedef struct delayed_mem_copy_status { + struct sljit_compiler *compiler; + int store_bases[RECURSE_TMP_REG_COUNT]; + int store_offsets[RECURSE_TMP_REG_COUNT]; + int tmp_regs[RECURSE_TMP_REG_COUNT]; + int saved_tmp_regs[RECURSE_TMP_REG_COUNT]; + int next_tmp_reg; +} delayed_mem_copy_status; + +static void delayed_mem_copy_init(delayed_mem_copy_status *status, compiler_common *common) { -int private_data_length = needs_control_head ? 3 : 2; +int i; + +for (i = 0; i < RECURSE_TMP_REG_COUNT; i++) + { + SLJIT_ASSERT(status->tmp_regs[i] >= 0); + SLJIT_ASSERT(sljit_get_register_index(status->saved_tmp_regs[i]) < 0 || status->tmp_regs[i] == status->saved_tmp_regs[i]); + + status->store_bases[i] = -1; + } +status->next_tmp_reg = 0; +status->compiler = common->compiler; +} + +static void delayed_mem_copy_move(delayed_mem_copy_status *status, int load_base, sljit_sw load_offset, + int store_base, sljit_sw store_offset) +{ +struct sljit_compiler *compiler = status->compiler; +int next_tmp_reg = status->next_tmp_reg; +int tmp_reg = status->tmp_regs[next_tmp_reg]; + +SLJIT_ASSERT(load_base > 0 && store_base > 0); + +if (status->store_bases[next_tmp_reg] == -1) + { + /* Preserve virtual registers. */ + if (sljit_get_register_index(status->saved_tmp_regs[next_tmp_reg]) < 0) + OP1(SLJIT_MOV, status->saved_tmp_regs[next_tmp_reg], 0, tmp_reg, 0); + } +else + OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0); + +OP1(SLJIT_MOV, tmp_reg, 0, SLJIT_MEM1(load_base), load_offset); +status->store_bases[next_tmp_reg] = store_base; +status->store_offsets[next_tmp_reg] = store_offset; + +status->next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT; +} + +static void delayed_mem_copy_finish(delayed_mem_copy_status *status) +{ +struct sljit_compiler *compiler = status->compiler; +int next_tmp_reg = status->next_tmp_reg; +int tmp_reg, saved_tmp_reg, i; + +for (i = 0; i < RECURSE_TMP_REG_COUNT; i++) + { + if (status->store_bases[next_tmp_reg] != -1) + { + tmp_reg = status->tmp_regs[next_tmp_reg]; + saved_tmp_reg = status->saved_tmp_regs[next_tmp_reg]; + + OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0); + + /* Restore virtual registers. */ + if (sljit_get_register_index(saved_tmp_reg) < 0) + OP1(SLJIT_MOV, tmp_reg, 0, saved_tmp_reg, 0); + } + + next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT; + } +} + +#undef RECURSE_TMP_REG_COUNT + +static int get_recurse_data_length(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, + BOOL *needs_control_head, BOOL *has_quit, BOOL *has_accept) +{ +int length = 1; int size; PCRE2_SPTR alternative; +BOOL quit_found = FALSE; +BOOL accept_found = FALSE; +BOOL setsom_found = FALSE; +BOOL setmark_found = FALSE; +BOOL capture_last_found = FALSE; +BOOL control_head_found = FALSE; + +#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD +SLJIT_ASSERT(common->control_head_ptr != 0); +control_head_found = TRUE; +#endif + /* Calculate the sum of the private machine words. */ while (cc < ccend) { size = 0; switch(*cc) { + case OP_SET_SOM: + SLJIT_ASSERT(common->has_set_som); + setsom_found = TRUE; + cc += 1; + break; + + case OP_RECURSE: + if (common->has_set_som) + setsom_found = TRUE; + if (common->mark_ptr != 0) + setmark_found = TRUE; + if (common->capture_last_ptr != 0) + capture_last_found = TRUE; + cc += 1 + LINK_SIZE; + break; + case OP_KET: if (PRIVATE_DATA(cc) != 0) { - private_data_length++; + length++; SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0); cc += PRIVATE_DATA(cc + 1); } @@ -1802,26 +1951,30 @@ while (cc < ccend) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: case OP_BRAPOS: case OP_SBRA: case OP_SBRAPOS: case OP_SCOND: - private_data_length++; + length++; SLJIT_ASSERT(PRIVATE_DATA(cc) != 0); cc += 1 + LINK_SIZE; break; case OP_CBRA: case OP_SCBRA: + length += 2; + if (common->capture_last_ptr != 0) + capture_last_found = TRUE; if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) - private_data_length++; + length++; cc += 1 + LINK_SIZE + IMM2_SIZE; break; case OP_CBRAPOS: case OP_SCBRAPOS: - private_data_length += 2; + length += 2 + 2; + if (common->capture_last_ptr != 0) + capture_last_found = TRUE; cc += 1 + LINK_SIZE + IMM2_SIZE; break; @@ -1829,13 +1982,13 @@ while (cc < ccend) /* Might be a hidden SCOND. */ alternative = cc + GET(cc, 1); if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) - private_data_length++; + length++; cc += 1 + LINK_SIZE; break; CASE_ITERATOR_PRIVATE_DATA_1 - if (PRIVATE_DATA(cc)) - private_data_length++; + if (PRIVATE_DATA(cc) != 0) + length++; cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); @@ -1843,8 +1996,8 @@ while (cc < ccend) break; CASE_ITERATOR_PRIVATE_DATA_2A - if (PRIVATE_DATA(cc)) - private_data_length += 2; + if (PRIVATE_DATA(cc) != 0) + length += 2; cc += 2; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); @@ -1852,8 +2005,8 @@ while (cc < ccend) break; CASE_ITERATOR_PRIVATE_DATA_2B - if (PRIVATE_DATA(cc)) - private_data_length += 2; + if (PRIVATE_DATA(cc) != 0) + length += 2; cc += 2 + IMM2_SIZE; #ifdef SUPPORT_UNICODE if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); @@ -1861,20 +2014,20 @@ while (cc < ccend) break; CASE_ITERATOR_TYPE_PRIVATE_DATA_1 - if (PRIVATE_DATA(cc)) - private_data_length++; + if (PRIVATE_DATA(cc) != 0) + length++; cc += 1; break; CASE_ITERATOR_TYPE_PRIVATE_DATA_2A - if (PRIVATE_DATA(cc)) - private_data_length += 2; + if (PRIVATE_DATA(cc) != 0) + length += 2; cc += 1; break; CASE_ITERATOR_TYPE_PRIVATE_DATA_2B - if (PRIVATE_DATA(cc)) - private_data_length += 2; + if (PRIVATE_DATA(cc) != 0) + length += 2; cc += 1 + IMM2_SIZE; break; @@ -1886,11 +2039,52 @@ while (cc < ccend) #else size = 1 + 32 / (int)sizeof(PCRE2_UCHAR); #endif - if (PRIVATE_DATA(cc)) - private_data_length += get_class_iterator_size(cc + size); + if (PRIVATE_DATA(cc) != 0) + length += get_class_iterator_size(cc + size); cc += size; break; + case OP_MARK: + case OP_COMMIT_ARG: + case OP_PRUNE_ARG: + case OP_THEN_ARG: + SLJIT_ASSERT(common->mark_ptr != 0); + if (!setmark_found) + setmark_found = TRUE; + if (common->control_head_ptr != 0) + control_head_found = TRUE; + if (*cc != OP_MARK) + quit_found = TRUE; + + cc += 1 + 2 + cc[1]; + break; + + case OP_PRUNE: + case OP_SKIP: + case OP_COMMIT: + quit_found = TRUE; + cc++; + break; + + case OP_SKIP_ARG: + quit_found = TRUE; + cc += 1 + 2 + cc[1]; + break; + + case OP_THEN: + SLJIT_ASSERT(common->control_head_ptr != 0); + quit_found = TRUE; + if (!control_head_found) + control_head_found = TRUE; + cc++; + break; + + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + accept_found = TRUE; + cc++; + break; + default: cc = next_opcode(common, cc); SLJIT_ASSERT(cc != NULL); @@ -1898,329 +2092,447 @@ while (cc < ccend) } } SLJIT_ASSERT(cc == ccend); -return private_data_length; + +if (control_head_found) + length++; +if (capture_last_found) + length++; +if (quit_found) + { + if (setsom_found) + length++; + if (setmark_found) + length++; + } + +*needs_control_head = control_head_found; +*has_quit = quit_found; +*has_accept = accept_found; +return length; } -static void copy_private_data(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, - BOOL save, int stackptr, int stacktop, BOOL needs_control_head) +enum copy_recurse_data_types { + recurse_copy_from_global, + recurse_copy_private_to_global, + recurse_copy_shared_to_global, + recurse_copy_kept_shared_to_global, + recurse_swap_global +}; + +static void copy_recurse_data(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, + int type, int stackptr, int stacktop, BOOL has_quit) { -DEFINE_COMPILER; -int srcw[2]; -int count, size; -BOOL tmp1next = TRUE; -BOOL tmp1empty = TRUE; -BOOL tmp2empty = TRUE; +delayed_mem_copy_status status; PCRE2_SPTR alternative; -enum { - start, - loop, - end -} status; +sljit_sw private_srcw[2]; +sljit_sw shared_srcw[3]; +sljit_sw kept_shared_srcw[2]; +int private_count, shared_count, kept_shared_count; +int from_sp, base_reg, offset, i; +BOOL setsom_found = FALSE; +BOOL setmark_found = FALSE; +BOOL capture_last_found = FALSE; +BOOL control_head_found = FALSE; -status = save ? start : loop; -stackptr = STACK(stackptr - 2); -stacktop = STACK(stacktop - 1); +#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD +SLJIT_ASSERT(common->control_head_ptr != 0); +control_head_found = TRUE; +#endif -if (!save) +switch (type) { - stackptr += (needs_control_head ? 2 : 1) * sizeof(sljit_sw); - if (stackptr < stacktop) - { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr); - stackptr += sizeof(sljit_sw); - tmp1empty = FALSE; - } - if (stackptr < stacktop) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), stackptr); - stackptr += sizeof(sljit_sw); - tmp2empty = FALSE; - } - /* The tmp1next must be TRUE in either way. */ + case recurse_copy_from_global: + from_sp = TRUE; + base_reg = STACK_TOP; + break; + + case recurse_copy_private_to_global: + case recurse_copy_shared_to_global: + case recurse_copy_kept_shared_to_global: + from_sp = FALSE; + base_reg = STACK_TOP; + break; + + default: + SLJIT_ASSERT(type == recurse_swap_global); + from_sp = FALSE; + base_reg = TMP2; + break; } -do +stackptr = STACK(stackptr); +stacktop = STACK(stacktop); + +status.tmp_regs[0] = TMP1; +status.saved_tmp_regs[0] = TMP1; + +if (base_reg != TMP2) { - count = 0; - switch(status) + status.tmp_regs[1] = TMP2; + status.saved_tmp_regs[1] = TMP2; + } +else + { + status.saved_tmp_regs[1] = RETURN_ADDR; + if (sljit_get_register_index (RETURN_ADDR) == -1) + status.tmp_regs[1] = STR_PTR; + else + status.tmp_regs[1] = RETURN_ADDR; + } + +status.saved_tmp_regs[2] = TMP3; +if (sljit_get_register_index (TMP3) == -1) + status.tmp_regs[2] = STR_END; +else + status.tmp_regs[2] = TMP3; + +delayed_mem_copy_init(&status, common); + +if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global) + { + SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global); + + if (!from_sp) + delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->recursive_head_ptr); + + if (from_sp || type == recurse_swap_global) + delayed_mem_copy_move(&status, SLJIT_SP, common->recursive_head_ptr, base_reg, stackptr); + } + +stackptr += sizeof(sljit_sw); + +#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD +if (type != recurse_copy_shared_to_global) + { + if (!from_sp) + delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->control_head_ptr); + + if (from_sp || type == recurse_swap_global) + delayed_mem_copy_move(&status, SLJIT_SP, common->control_head_ptr, base_reg, stackptr); + } + +stackptr += sizeof(sljit_sw); +#endif + +while (cc < ccend) + { + private_count = 0; + shared_count = 0; + kept_shared_count = 0; + + switch(*cc) { - case start: - SLJIT_ASSERT(save && common->recursive_head_ptr != 0); - count = 1; - srcw[0] = common->recursive_head_ptr; - if (needs_control_head) + case OP_SET_SOM: + SLJIT_ASSERT(common->has_set_som); + if (has_quit && !setsom_found) { - SLJIT_ASSERT(common->control_head_ptr != 0); - count = 2; - srcw[1] = common->control_head_ptr; + kept_shared_srcw[0] = OVECTOR(0); + kept_shared_count = 1; + setsom_found = TRUE; } - status = loop; + cc += 1; break; - case loop: - if (cc >= ccend) + case OP_RECURSE: + if (has_quit) { - status = end; - break; + if (common->has_set_som && !setsom_found) + { + kept_shared_srcw[0] = OVECTOR(0); + kept_shared_count = 1; + setsom_found = TRUE; + } + if (common->mark_ptr != 0 && !setmark_found) + { + kept_shared_srcw[kept_shared_count] = common->mark_ptr; + kept_shared_count++; + setmark_found = TRUE; + } + } + if (common->capture_last_ptr != 0 && !capture_last_found) + { + shared_srcw[0] = common->capture_last_ptr; + shared_count = 1; + capture_last_found = TRUE; + } + cc += 1 + LINK_SIZE; + break; + + case OP_KET: + if (PRIVATE_DATA(cc) != 0) + { + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0); + cc += PRIVATE_DATA(cc + 1); + } + cc += 1 + LINK_SIZE; + break; + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_BRAPOS: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCOND: + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + cc += 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_SCBRA: + offset = (GET2(cc, 1 + LINK_SIZE)) << 1; + shared_srcw[0] = OVECTOR(offset); + shared_srcw[1] = OVECTOR(offset + 1); + shared_count = 2; + + if (common->capture_last_ptr != 0 && !capture_last_found) + { + shared_srcw[2] = common->capture_last_ptr; + shared_count = 3; + capture_last_found = TRUE; } - switch(*cc) + if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) { - case OP_KET: - if (PRIVATE_DATA(cc) != 0) - { - count = 1; - srcw[0] = PRIVATE_DATA(cc); - SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0); - cc += PRIVATE_DATA(cc + 1); - } - cc += 1 + LINK_SIZE; - break; + private_count = 1; + private_srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); + } + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; - case OP_ASSERT: - case OP_ASSERT_NOT: - case OP_ASSERTBACK: - case OP_ASSERTBACK_NOT: - case OP_ONCE: - case OP_ONCE_NC: - case OP_BRAPOS: - case OP_SBRA: - case OP_SBRAPOS: - case OP_SCOND: - count = 1; - srcw[0] = PRIVATE_DATA(cc); - SLJIT_ASSERT(srcw[0] != 0); - cc += 1 + LINK_SIZE; - break; + case OP_CBRAPOS: + case OP_SCBRAPOS: + offset = (GET2(cc, 1 + LINK_SIZE)) << 1; + shared_srcw[0] = OVECTOR(offset); + shared_srcw[1] = OVECTOR(offset + 1); + shared_count = 2; - case OP_CBRA: - case OP_SCBRA: - if (common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0) - { - count = 1; - srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); - } - cc += 1 + LINK_SIZE + IMM2_SIZE; - break; + if (common->capture_last_ptr != 0 && !capture_last_found) + { + shared_srcw[2] = common->capture_last_ptr; + shared_count = 3; + capture_last_found = TRUE; + } - case OP_CBRAPOS: - case OP_SCBRAPOS: - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); - SLJIT_ASSERT(srcw[0] != 0 && srcw[1] != 0); - cc += 1 + LINK_SIZE + IMM2_SIZE; - break; + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; - case OP_COND: - /* Might be a hidden SCOND. */ - alternative = cc + GET(cc, 1); - if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) - { - count = 1; - srcw[0] = PRIVATE_DATA(cc); - SLJIT_ASSERT(srcw[0] != 0); - } - cc += 1 + LINK_SIZE; - break; + case OP_COND: + /* Might be a hidden SCOND. */ + alternative = cc + GET(cc, 1); + if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) + { + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + } + cc += 1 + LINK_SIZE; + break; - CASE_ITERATOR_PRIVATE_DATA_1 - if (PRIVATE_DATA(cc)) - { - count = 1; - srcw[0] = PRIVATE_DATA(cc); - } - cc += 2; + CASE_ITERATOR_PRIVATE_DATA_1 + if (PRIVATE_DATA(cc)) + { + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + } + cc += 2; #ifdef SUPPORT_UNICODE - if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif - break; + break; - CASE_ITERATOR_PRIVATE_DATA_2A - if (PRIVATE_DATA(cc)) - { - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); - } - cc += 2; + CASE_ITERATOR_PRIVATE_DATA_2A + if (PRIVATE_DATA(cc)) + { + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); + } + cc += 2; #ifdef SUPPORT_UNICODE - if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif - break; + break; - CASE_ITERATOR_PRIVATE_DATA_2B - if (PRIVATE_DATA(cc)) - { - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); - } - cc += 2 + IMM2_SIZE; + CASE_ITERATOR_PRIVATE_DATA_2B + if (PRIVATE_DATA(cc)) + { + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = PRIVATE_DATA(cc) + sizeof(sljit_sw); + } + cc += 2 + IMM2_SIZE; #ifdef SUPPORT_UNICODE - if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); #endif - break; + break; - CASE_ITERATOR_TYPE_PRIVATE_DATA_1 - if (PRIVATE_DATA(cc)) - { - count = 1; - srcw[0] = PRIVATE_DATA(cc); - } - cc += 1; - break; + CASE_ITERATOR_TYPE_PRIVATE_DATA_1 + if (PRIVATE_DATA(cc)) + { + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + } + cc += 1; + break; - CASE_ITERATOR_TYPE_PRIVATE_DATA_2A - if (PRIVATE_DATA(cc)) - { - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = srcw[0] + sizeof(sljit_sw); - } - cc += 1; - break; + CASE_ITERATOR_TYPE_PRIVATE_DATA_2A + if (PRIVATE_DATA(cc)) + { + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = private_srcw[0] + sizeof(sljit_sw); + } + cc += 1; + break; - CASE_ITERATOR_TYPE_PRIVATE_DATA_2B - if (PRIVATE_DATA(cc)) - { - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = srcw[0] + sizeof(sljit_sw); - } - cc += 1 + IMM2_SIZE; - break; + CASE_ITERATOR_TYPE_PRIVATE_DATA_2B + if (PRIVATE_DATA(cc)) + { + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = private_srcw[0] + sizeof(sljit_sw); + } + cc += 1 + IMM2_SIZE; + break; - case OP_CLASS: - case OP_NCLASS: + case OP_CLASS: + case OP_NCLASS: #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 - case OP_XCLASS: - size = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR); + case OP_XCLASS: + i = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR); #else - size = 1 + 32 / (int)sizeof(PCRE2_UCHAR); + i = 1 + 32 / (int)sizeof(PCRE2_UCHAR); #endif - if (PRIVATE_DATA(cc)) - switch(get_class_iterator_size(cc + size)) - { - case 1: - count = 1; - srcw[0] = PRIVATE_DATA(cc); - break; + if (PRIVATE_DATA(cc) != 0) + switch(get_class_iterator_size(cc + i)) + { + case 1: + private_count = 1; + private_srcw[0] = PRIVATE_DATA(cc); + break; - case 2: - count = 2; - srcw[0] = PRIVATE_DATA(cc); - srcw[1] = srcw[0] + sizeof(sljit_sw); - break; + case 2: + private_count = 2; + private_srcw[0] = PRIVATE_DATA(cc); + private_srcw[1] = private_srcw[0] + sizeof(sljit_sw); + break; - default: - SLJIT_ASSERT_STOP(); - break; - } - cc += size; - break; - - default: - cc = next_opcode(common, cc); - SLJIT_ASSERT(cc != NULL); - break; - } + default: + SLJIT_UNREACHABLE(); + break; + } + cc += i; break; - case end: - SLJIT_ASSERT_STOP(); + case OP_MARK: + case OP_COMMIT_ARG: + case OP_PRUNE_ARG: + case OP_THEN_ARG: + SLJIT_ASSERT(common->mark_ptr != 0); + if (has_quit && !setmark_found) + { + kept_shared_srcw[0] = common->mark_ptr; + kept_shared_count = 1; + setmark_found = TRUE; + } + if (common->control_head_ptr != 0 && !control_head_found) + { + shared_srcw[0] = common->control_head_ptr; + shared_count = 1; + control_head_found = TRUE; + } + cc += 1 + 2 + cc[1]; + break; + + case OP_THEN: + SLJIT_ASSERT(common->control_head_ptr != 0); + if (!control_head_found) + { + shared_srcw[0] = common->control_head_ptr; + shared_count = 1; + control_head_found = TRUE; + } + cc++; + break; + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); break; } - while (count > 0) + if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global) { - count--; - if (save) - { - if (tmp1next) - { - if (!tmp1empty) - { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); - stackptr += sizeof(sljit_sw); - } - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), srcw[count]); - tmp1empty = FALSE; - tmp1next = FALSE; - } - else - { - if (!tmp2empty) - { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); - stackptr += sizeof(sljit_sw); - } - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), srcw[count]); - tmp2empty = FALSE; - tmp1next = TRUE; - } - } - else - { - if (tmp1next) - { - SLJIT_ASSERT(!tmp1empty); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), srcw[count], TMP1, 0); - tmp1empty = stackptr >= stacktop; - if (!tmp1empty) - { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr); - stackptr += sizeof(sljit_sw); - } - tmp1next = FALSE; - } - else - { - SLJIT_ASSERT(!tmp2empty); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), srcw[count], TMP2, 0); - tmp2empty = stackptr >= stacktop; - if (!tmp2empty) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), stackptr); - stackptr += sizeof(sljit_sw); - } - tmp1next = TRUE; - } - } - } - } -while (status != end); + SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global); -if (save) - { - if (tmp1next) - { - if (!tmp1empty) + for (i = 0; i < private_count; i++) { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); - stackptr += sizeof(sljit_sw); - } - if (!tmp2empty) - { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); + SLJIT_ASSERT(private_srcw[i] != 0); + + if (!from_sp) + delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, private_srcw[i]); + + if (from_sp || type == recurse_swap_global) + delayed_mem_copy_move(&status, SLJIT_SP, private_srcw[i], base_reg, stackptr); + stackptr += sizeof(sljit_sw); } } else + stackptr += sizeof(sljit_sw) * private_count; + + if (type != recurse_copy_private_to_global && type != recurse_copy_kept_shared_to_global) { - if (!tmp2empty) + SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_swap_global); + + for (i = 0; i < shared_count; i++) { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); - stackptr += sizeof(sljit_sw); - } - if (!tmp1empty) - { - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); + SLJIT_ASSERT(shared_srcw[i] != 0); + + if (!from_sp) + delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, shared_srcw[i]); + + if (from_sp || type == recurse_swap_global) + delayed_mem_copy_move(&status, SLJIT_SP, shared_srcw[i], base_reg, stackptr); + stackptr += sizeof(sljit_sw); } } + else + stackptr += sizeof(sljit_sw) * shared_count; + + if (type != recurse_copy_private_to_global && type != recurse_swap_global) + { + SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_copy_kept_shared_to_global); + + for (i = 0; i < kept_shared_count; i++) + { + SLJIT_ASSERT(kept_shared_srcw[i] != 0); + + if (!from_sp) + delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, kept_shared_srcw[i]); + + if (from_sp || type == recurse_swap_global) + delayed_mem_copy_move(&status, SLJIT_SP, kept_shared_srcw[i], base_reg, stackptr); + + stackptr += sizeof(sljit_sw); + } + } + else + stackptr += sizeof(sljit_sw) * kept_shared_count; } -SLJIT_ASSERT(cc == ccend && stackptr == stacktop && (save || (tmp1empty && tmp2empty))); + +SLJIT_ASSERT(cc == ccend && stackptr == stacktop); + +delayed_mem_copy_finish(&status); } static SLJIT_INLINE PCRE2_SPTR set_then_offsets(compiler_common *common, PCRE2_SPTR cc, sljit_u8 *current_offset) @@ -2337,7 +2649,7 @@ static SLJIT_INLINE void count_match(compiler_common *common) { DEFINE_COMPILER; -OP2(SLJIT_SUB | SLJIT_SET_E, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1); +OP2(SLJIT_SUB | SLJIT_SET_Z, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1); add_jump(compiler, &common->calllimit, JUMP(SLJIT_ZERO)); } @@ -2347,7 +2659,7 @@ static SLJIT_INLINE void allocate_stack(compiler_common *common, int size) DEFINE_COMPILER; SLJIT_ASSERT(size > 0); -OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); +OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); #ifdef DESTROY_REGISTERS OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345); OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); @@ -2355,7 +2667,7 @@ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, TMP1, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP1, 0); #endif -add_stub(common, CMP(SLJIT_GREATER, STACK_TOP, 0, STACK_LIMIT, 0)); +add_stub(common, CMP(SLJIT_LESS, STACK_TOP, 0, STACK_LIMIT, 0)); } static SLJIT_INLINE void free_stack(compiler_common *common, int size) @@ -2363,7 +2675,7 @@ static SLJIT_INLINE void free_stack(compiler_common *common, int size) DEFINE_COMPILER; SLJIT_ASSERT(size > 0); -OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); +OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_sw)); } static sljit_uw * allocate_read_only_data(compiler_common *common, sljit_uw size) @@ -2403,12 +2715,25 @@ if (length < 8) } else { - GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START); - OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1); - loop = LABEL(); - OP1(SLJIT_MOVU, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw), SLJIT_R0, 0); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); - JUMPTO(SLJIT_NOT_ZERO, loop); + if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)) == SLJIT_SUCCESS) + { + GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1); + loop = LABEL(); + sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_NOT_ZERO, loop); + } + else + { + GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START + sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1); + loop = LABEL(); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), 0, SLJIT_R0, 0); + OP2(SLJIT_ADD, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_NOT_ZERO, loop); + } } } @@ -2441,12 +2766,25 @@ if (length < 8) } else { - GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw)); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); - loop = LABEL(); - OP1(SLJIT_MOVU, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0); - OP2(SLJIT_SUB | SLJIT_SET_E, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); - JUMPTO(SLJIT_NOT_ZERO, loop); + if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)) == SLJIT_SUCCESS) + { + GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); + loop = LABEL(); + sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); + OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_NOT_ZERO, loop); + } + else + { + GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2); + loop = LABEL(); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0); + OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_NOT_ZERO, loop); + } } OP1(SLJIT_MOV, STACK_TOP, 0, ARGUMENTS, 0); @@ -2456,37 +2794,38 @@ if (common->control_head_ptr != 0) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(jit_arguments, stack)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr); -OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(struct sljit_stack, base)); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(struct sljit_stack, end)); } -static sljit_sw SLJIT_CALL do_search_mark(sljit_sw *current, PCRE2_SPTR skip_arg) +static sljit_sw SLJIT_FUNC do_search_mark(sljit_sw *current, PCRE2_SPTR skip_arg) { while (current != NULL) { - switch (current[-2]) + switch (current[1]) { case type_then_trap: break; case type_mark: - if (PRIV(strcmp)(skip_arg, (PCRE2_SPTR)current[-3]) == 0) - return current[-4]; + if (PRIV(strcmp)(skip_arg, (PCRE2_SPTR)current[2]) == 0) + return current[3]; break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } - SLJIT_ASSERT(current > (sljit_sw*)current[-1]); - current = (sljit_sw*)current[-1]; + SLJIT_ASSERT(current[0] == 0 || current < (sljit_sw*)current[0]); + current = (sljit_sw*)current[0]; } -return -1; +return 0; } static SLJIT_INLINE void copy_ovector(compiler_common *common, int topbracket) { DEFINE_COMPILER; struct sljit_label *loop; +BOOL has_pre; /* At this point we can freely use all registers. */ OP1(SLJIT_MOV, SLJIT_S2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)); @@ -2503,36 +2842,62 @@ if (common->mark_ptr != 0) OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, match_data), SLJIT_IMM, SLJIT_OFFSETOF(pcre2_match_data, ovector) - sizeof(PCRE2_SIZE)); -GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START); +has_pre = sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)) == SLJIT_SUCCESS; + +GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START - (has_pre ? sizeof(sljit_sw) : 0)); OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, begin)); loop = LABEL(); -OP2(SLJIT_SUB, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0, SLJIT_R0, 0); -OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw)); + +if (has_pre) + sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)); +else + { + OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0); + OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw)); + } + +OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, sizeof(PCRE2_SIZE)); +OP2(SLJIT_SUB, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_R0, 0); /* Copy the integer value to the output buffer */ #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_ASHR, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif + SLJIT_ASSERT(sizeof(PCRE2_SIZE) == 4 || sizeof(PCRE2_SIZE) == 8); -if (sizeof(PCRE2_SIZE) == 4) - OP1(SLJIT_MOVU_U32, SLJIT_MEM1(SLJIT_R2), sizeof(PCRE2_SIZE), SLJIT_S1, 0); -else - OP1(SLJIT_MOVU, SLJIT_MEM1(SLJIT_R2), sizeof(PCRE2_SIZE), SLJIT_S1, 0); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); +OP1(((sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV), SLJIT_MEM1(SLJIT_R2), 0, SLJIT_S1, 0); + +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, loop); /* Calculate the return value, which is the maximum ovector value. */ if (topbracket > 1) { - GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw)); - OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1); + if (sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))) == SLJIT_SUCCESS) + { + GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1); - /* OVECTOR(0) is never equal to SLJIT_S2. */ - loop = LABEL(); - OP1(SLJIT_MOVU, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))); - OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); - CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); - OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0); + /* OVECTOR(0) is never equal to SLJIT_S2. */ + loop = LABEL(); + sljit_emit_mem(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * (sljit_sw)sizeof(sljit_sw))); + OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); + CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0); + } + else + { + GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + (topbracket - 1) * 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1); + + /* OVECTOR(0) is never equal to SLJIT_S2. */ + loop = LABEL(); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), 0); + OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, 2 * (sljit_sw)sizeof(sljit_sw)); + OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1); + CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop); + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0); + } } else OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1); @@ -2543,7 +2908,7 @@ static SLJIT_INLINE void return_with_partial_match(compiler_common *common, stru DEFINE_COMPILER; sljit_s32 mov_opcode; -SLJIT_COMPILE_ASSERT(STR_END == SLJIT_S1, str_end_must_be_saved_reg2); +SLJIT_COMPILE_ASSERT(STR_END == SLJIT_S0, str_end_must_be_saved_reg0); SLJIT_ASSERT(common->start_used_ptr != 0 && common->start_ptr != 0 && (common->mode == PCRE2_JIT_PARTIAL_SOFT ? common->hit_start != 0 : common->hit_start == 0)); @@ -2553,19 +2918,19 @@ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_PARTIAL); /* Store match begin and end. */ -OP1(SLJIT_MOV, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, begin)); +OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, begin)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_R2, 0); OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, match_data)); mov_opcode = (sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV; -OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_S0, 0); +OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_S1, 0); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_ASHR, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, UCHAR_SHIFT); #endif OP1(mov_opcode, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(pcre2_match_data, ovector), SLJIT_R2, 0); -OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_S0, 0); +OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_S1, 0); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_ASHR, STR_END, 0, STR_END, 0, SLJIT_IMM, UCHAR_SHIFT); #endif @@ -3104,8 +3469,8 @@ if (common->utf) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); /* Skip low surrogate if necessary. */ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0); return; @@ -3124,6 +3489,7 @@ struct sljit_jump *jump; if (nltype == NLTYPE_ANY) { add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL)); + sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(jumpifmatch ? SLJIT_NOT_ZERO : SLJIT_ZERO)); } else if (nltype == NLTYPE_ANYCRLF) @@ -3165,7 +3531,7 @@ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); /* Searching for the first zero. */ -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); jump = JUMP(SLJIT_NOT_ZERO); /* Two byte sequence. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); @@ -3179,7 +3545,7 @@ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10000); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10000); jump = JUMP(SLJIT_NOT_ZERO); /* Three byte sequence. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); @@ -3213,15 +3579,15 @@ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); /* Searching for the first zero. */ -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x800); jump = JUMP(SLJIT_NOT_ZERO); /* Two byte sequence. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); sljit_emit_fast_return(compiler, RETURN_ADDR, 0); JUMPHERE(jump); -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x400); -OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_NOT_ZERO); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x400); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_NOT_ZERO); /* This code runs only in 8 bit mode. No need to shift the value. */ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); @@ -3244,7 +3610,7 @@ struct sljit_jump *compare; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20); jump = JUMP(SLJIT_NOT_ZERO); /* Two byte sequence. */ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); @@ -3281,12 +3647,33 @@ static void do_getucd(compiler_common *common) /* Search the UCD record for the character comes in TMP1. Returns chartype in TMP1 and UCD offset in TMP2. */ DEFINE_COMPILER; +#if PCRE2_CODE_UNIT_WIDTH == 32 +struct sljit_jump *jump; +#endif + +#if defined SLJIT_DEBUG && SLJIT_DEBUG +/* dummy_ucd_record */ +const ucd_record *record = GET_UCD(INVALID_UTF_CHAR); +SLJIT_ASSERT(record->script == ucp_Common && record->chartype == ucp_Cn && record->gbprop == ucp_gbOther); +SLJIT_ASSERT(record->caseset == 0 && record->other_case == 0); +#endif SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 8); sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); + +#if PCRE2_CODE_UNIT_WIDTH == 32 +if (!common->utf) + { + jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); + JUMPHERE(jump); + } +#endif + OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); -OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); +OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); @@ -3299,7 +3686,7 @@ sljit_emit_fast_return(compiler, RETURN_ADDR, 0); #endif /* SUPPORT_UNICODE */ -static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common, BOOL hascrorlf, sljit_u32 overall_options) +static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common) { DEFINE_COMPILER; struct sljit_label *mainloop; @@ -3311,6 +3698,8 @@ struct sljit_jump *end2 = NULL; struct sljit_jump *singlechar; #endif jump_list *newline = NULL; +sljit_u32 overall_options = common->re->overall_options; +BOOL hascrorlf = (common->re->flags & PCRE2_HASCRORLF) != 0; BOOL newlinecheck = FALSE; BOOL readuchar = FALSE; @@ -3318,7 +3707,7 @@ if (!(hascrorlf || (overall_options & PCRE2_FIRSTLINE) != 0) && (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF || common->newline > 255)) newlinecheck = TRUE; -SLJIT_ASSERT(common->forced_quit_label == NULL); +SLJIT_ASSERT(common->abort_label == NULL); if ((overall_options & PCRE2_FIRSTLINE) != 0) { @@ -3375,7 +3764,7 @@ else if ((overall_options & PCRE2_USE_OFFSET_LIMIT) != 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)); + add_jump(compiler, &common->abort, CMP(SLJIT_LESS, TMP2, 0, STR_PTR, 0)); JUMPHERE(end); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, TMP2, 0); } @@ -3388,8 +3777,8 @@ if (newlinecheck) OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif @@ -3426,8 +3815,8 @@ if (common->utf) { singlechar = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); JUMPHERE(singlechar); @@ -3445,40 +3834,42 @@ if (newlinecheck) return mainloop; } -#define MAX_N_CHARS 16 -#define MAX_DIFF_CHARS 6 -static SLJIT_INLINE void add_prefix_char(PCRE2_UCHAR chr, PCRE2_UCHAR *chars) +static SLJIT_INLINE void add_prefix_char(PCRE2_UCHAR chr, fast_forward_char_data *chars, BOOL last) { -PCRE2_UCHAR i, len; +sljit_u32 i, count = chars->count; -len = chars[0]; -if (len == 255) +if (count == 255) return; -if (len == 0) +if (count == 0) { - chars[0] = 1; - chars[1] = chr; + chars->count = 1; + chars->chars[0] = chr; + + if (last) + chars->last_count = 1; return; } -for (i = len; i > 0; i--) - if (chars[i] == chr) +for (i = 0; i < count; i++) + if (chars->chars[i] == chr) return; -if (len >= MAX_DIFF_CHARS - 1) +if (count >= MAX_DIFF_CHARS) { - chars[0] = 255; + chars->count = 255; return; } -len++; -chars[len] = chr; -chars[0] = len; +chars->chars[count] = chr; +chars->count = count + 1; + +if (last) + chars->last_count++; } -static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, PCRE2_UCHAR *chars, int max_chars, sljit_u32 *rec_count) +static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, fast_forward_char_data *chars, int max_chars, sljit_u32 *rec_count) { /* Recursive function, which scans prefix literals. */ BOOL last, any, class, caseless; @@ -3487,7 +3878,7 @@ sljit_u32 chr; /* Any unicode character. */ sljit_u8 *bytes, *bytes_end, byte; PCRE2_SPTR alternative, cc_save, oc; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 -PCRE2_UCHAR othercase[8]; +PCRE2_UCHAR othercase[4]; #elif defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16 PCRE2_UCHAR othercase[2]; #else @@ -3510,6 +3901,7 @@ while (TRUE) { case OP_CHARI: caseless = TRUE; + /* Fall through */ case OP_CHAR: last = FALSE; cc++; @@ -3541,6 +3933,7 @@ while (TRUE) case OP_MINPLUSI: case OP_POSPLUSI: caseless = TRUE; + /* Fall through */ case OP_PLUS: case OP_MINPLUS: case OP_POSPLUS: @@ -3549,6 +3942,7 @@ while (TRUE) case OP_EXACTI: caseless = TRUE; + /* Fall through */ case OP_EXACT: repeat = GET2(cc, 1); last = FALSE; @@ -3559,6 +3953,7 @@ while (TRUE) case OP_MINQUERYI: case OP_POSQUERYI: caseless = TRUE; + /* Fall through */ case OP_QUERY: case OP_MINQUERY: case OP_POSQUERY: @@ -3582,7 +3977,6 @@ while (TRUE) continue; case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_BRAPOS: case OP_CBRA: @@ -3703,12 +4097,12 @@ while (TRUE) { do { - chars[0] = 255; + chars->count = 255; consumed++; if (--max_chars == 0) return consumed; - chars += MAX_DIFF_CHARS; + chars++; } while (--repeat > 0); @@ -3752,8 +4146,8 @@ while (TRUE) do { if (bytes[31] & 0x80) - chars[0] = 255; - else if (chars[0] != 255) + chars->count = 255; + else if (chars->count != 255) { bytes_end = bytes + 32; chr = 0; @@ -3768,7 +4162,7 @@ while (TRUE) do { if ((byte & 0x1) != 0) - add_prefix_char(chr, chars); + add_prefix_char(chr, chars, TRUE); byte >>= 1; chr++; } @@ -3776,14 +4170,14 @@ while (TRUE) chr = (chr + 7) & ~7; } } - while (chars[0] != 255 && bytes < bytes_end); + while (chars->count != 255 && bytes < bytes_end); bytes = bytes_end - 32; } consumed++; if (--max_chars == 0) return consumed; - chars += MAX_DIFF_CHARS; + chars++; } while (--repeat > 0); @@ -3847,17 +4241,18 @@ while (TRUE) oc = othercase; do { - chr = *cc; - add_prefix_char(*cc, chars); - - if (caseless) - add_prefix_char(*oc, chars); - len--; consumed++; + + chr = *cc; + add_prefix_char(*cc, chars, len == 0); + + if (caseless) + add_prefix_char(*oc, chars, len == 0); + if (--max_chars == 0) return consumed; - chars += MAX_DIFF_CHARS; + chars++; cc++; oc++; } @@ -3876,7 +4271,37 @@ while (TRUE) } } -#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +static void jumpto_if_not_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg, struct sljit_label *label) +{ +#if PCRE2_CODE_UNIT_WIDTH == 8 +OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0); +CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0x80, label); +#elif PCRE2_CODE_UNIT_WIDTH == 16 +OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00); +CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00, label); +#else +#error "Unknown code width" +#endif +} +#endif + +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND) + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +static struct sljit_jump *jump_if_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg) +{ +#if PCRE2_CODE_UNIT_WIDTH == 8 +OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0); +return CMP(SLJIT_NOT_EQUAL, reg, 0, SLJIT_IMM, 0x80); +#elif PCRE2_CODE_UNIT_WIDTH == 16 +OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00); +return CMP(SLJIT_NOT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00); +#else +#error "Unknown code width" +#endif +} +#endif static sljit_s32 character_to_int32(PCRE2_UCHAR chr) { @@ -3895,39 +4320,140 @@ return value; #endif } -static SLJIT_INLINE void fast_forward_first_char2_sse2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2) +static void load_from_mem_sse2(struct sljit_compiler *compiler, sljit_s32 dst_xmm_reg, sljit_s32 src_general_reg) +{ +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +sljit_u8 instruction[5]; +#else +sljit_u8 instruction[4]; +#endif + +SLJIT_ASSERT(dst_xmm_reg < 8); + +/* MOVDQA xmm1, xmm2/m128 */ +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +if (src_general_reg < 8) + { + instruction[0] = 0x66; + instruction[1] = 0x0f; + instruction[2] = 0x6f; + instruction[3] = (dst_xmm_reg << 3) | src_general_reg; + sljit_emit_op_custom(compiler, instruction, 4); + } +else + { + instruction[0] = 0x66; + instruction[1] = 0x41; + instruction[2] = 0x0f; + instruction[3] = 0x6f; + instruction[4] = (dst_xmm_reg << 3) | (src_general_reg & 0x7); + sljit_emit_op_custom(compiler, instruction, 4); + } +#else +instruction[0] = 0x66; +instruction[1] = 0x0f; +instruction[2] = 0x6f; +instruction[3] = (dst_xmm_reg << 3) | src_general_reg; +sljit_emit_op_custom(compiler, instruction, 4); +#endif +} + +static void fast_forward_char_pair_sse2_compare(struct sljit_compiler *compiler, PCRE2_UCHAR char1, PCRE2_UCHAR char2, + sljit_u32 bit, sljit_s32 dst_ind, sljit_s32 cmp1_ind, sljit_s32 cmp2_ind, sljit_s32 tmp_ind) +{ +sljit_u8 instruction[4]; +instruction[0] = 0x66; +instruction[1] = 0x0f; + +if (char1 == char2 || bit != 0) + { + if (bit != 0) + { + /* POR xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ + instruction[2] = 0xeb; + instruction[3] = 0xc0 | (dst_ind << 3) | cmp2_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } + + /* PCMPEQB/W/D xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ + instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX; + instruction[3] = 0xc0 | (dst_ind << 3) | cmp1_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } +else + { + /* MOVDQA xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ + instruction[2] = 0x6f; + instruction[3] = 0xc0 | (tmp_ind << 3) | dst_ind; + sljit_emit_op_custom(compiler, instruction, 4); + + /* PCMPEQB/W/D xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ + instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX; + instruction[3] = 0xc0 | (dst_ind << 3) | cmp1_ind; + sljit_emit_op_custom(compiler, instruction, 4); + + instruction[3] = 0xc0 | (tmp_ind << 3) | cmp2_ind; + sljit_emit_op_custom(compiler, instruction, 4); + + /* POR xmm1, xmm2/m128 */ + /* instruction[0] = 0x66; */ + /* instruction[1] = 0x0f; */ + instruction[2] = 0xeb; + instruction[3] = 0xc0 | (dst_ind << 3) | tmp_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } +} + +static void fast_forward_first_char2_sse2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset) { DEFINE_COMPILER; struct sljit_label *start; -struct sljit_jump *quit[3]; -struct sljit_jump *nomatch; +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +struct sljit_label *restart; +#endif +struct sljit_jump *quit; +struct sljit_jump *partial_quit[2]; sljit_u8 instruction[8]; sljit_s32 tmp1_ind = sljit_get_register_index(TMP1); -sljit_s32 tmp2_ind = sljit_get_register_index(TMP2); sljit_s32 str_ptr_ind = sljit_get_register_index(STR_PTR); -BOOL load_twice = FALSE; -PCRE2_UCHAR bit; +sljit_s32 data_ind = 0; +sljit_s32 tmp_ind = 1; +sljit_s32 cmp1_ind = 2; +sljit_s32 cmp2_ind = 3; +sljit_u32 bit = 0; -bit = char1 ^ char2; -if (!is_powerof2(bit)) - bit = 0; +SLJIT_UNUSED_ARG(offset); -if ((char1 != char2) && bit == 0) - load_twice = TRUE; +if (char1 != char2) + { + bit = char1 ^ char2; + if (!is_powerof2(bit)) + bit = 0; + } -quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +partial_quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +if (common->mode == PCRE2_JIT_COMPLETE) + add_jump(compiler, &common->failed_match, partial_quit[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); +SLJIT_ASSERT(tmp1_ind < 8); /* MOVD xmm, r/m32 */ instruction[0] = 0x66; instruction[1] = 0x0f; instruction[2] = 0x6e; -instruction[3] = 0xc0 | (2 << 3) | tmp1_ind; +instruction[3] = 0xc0 | (cmp1_ind << 3) | tmp1_ind; sljit_emit_op_custom(compiler, instruction, 4); if (char1 != char2) @@ -3935,224 +4461,521 @@ 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; + instruction[3] = 0xc0 | (cmp2_ind << 3) | tmp1_ind; sljit_emit_op_custom(compiler, instruction, 4); } +OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0); + /* PSHUFD xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ instruction[2] = 0x70; -instruction[3] = 0xc0 | (2 << 3) | 2; +instruction[3] = 0xc0 | (cmp1_ind << 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; + instruction[3] = 0xc0 | (cmp2_ind << 3) | 3; 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); - } - +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +restart = LABEL(); #endif +OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf); -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); - } +load_from_mem_sse2(compiler, data_ind, str_ptr_ind); +fast_forward_char_pair_sse2_compare(compiler, char1, char2, bit, data_ind, cmp1_ind, cmp2_ind, tmp_ind); /* PMOVMSKB reg, xmm */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ instruction[2] = 0xd7; instruction[3] = 0xc0 | (tmp1_ind << 3) | 0; sljit_emit_op_custom(compiler, instruction, 4); -if (load_twice) - { - OP1(SLJIT_MOV, RETURN_ADDR, 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, RETURN_ADDR, 0); - } - -OP2(SLJIT_ASHR, TMP1, 0, TMP1, 0, TMP2, 0); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +OP2(SLJIT_LSHR, 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); +sljit_set_current_flags(compiler, SLJIT_SET_Z); -nomatch = JUMP(SLJIT_ZERO); +quit = JUMP(SLJIT_NOT_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); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); 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); + +partial_quit[1] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +if (common->mode == PCRE2_JIT_COMPLETE) + add_jump(compiler, &common->failed_match, partial_quit[1]); /* 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); - } +load_from_mem_sse2(compiler, 0, str_ptr_ind); +fast_forward_char_pair_sse2_compare(compiler, char1, char2, bit, data_ind, cmp1_ind, cmp2_ind, tmp_ind); /* PMOVMSKB reg, xmm */ +instruction[0] = 0x66; +instruction[1] = 0x0f; 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); +sljit_set_current_flags(compiler, SLJIT_SET_Z); JUMPTO(SLJIT_ZERO, start); +JUMPHERE(quit); 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); +if (common->mode != PCRE2_JIT_COMPLETE) + { + JUMPHERE(partial_quit[0]); + JUMPHERE(partial_quit[1]); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_PTR, 0, STR_END, 0); + CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0); + } +else + add_jump(compiler, &common->failed_match, 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)); + + quit = jump_if_utf_char_start(compiler, TMP1); + + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0); + JUMPTO(SLJIT_JUMP, restart); + + JUMPHERE(quit); + } +#endif } +#ifndef _WIN64 + +static SLJIT_INLINE sljit_u32 max_fast_forward_char_pair_sse2_offset(void) +{ +#if PCRE2_CODE_UNIT_WIDTH == 8 +return 15; +#elif PCRE2_CODE_UNIT_WIDTH == 16 +return 7; +#elif PCRE2_CODE_UNIT_WIDTH == 32 +return 3; +#else +#error "Unsupported unit width" +#endif +} + +static void fast_forward_char_pair_sse2(compiler_common *common, sljit_s32 offs1, + PCRE2_UCHAR char1a, PCRE2_UCHAR char1b, sljit_s32 offs2, PCRE2_UCHAR char2a, PCRE2_UCHAR char2b) +{ +DEFINE_COMPILER; +sljit_u32 bit1 = 0; +sljit_u32 bit2 = 0; +sljit_u32 diff = IN_UCHARS(offs1 - offs2); +sljit_s32 tmp1_ind = sljit_get_register_index(TMP1); +sljit_s32 tmp2_ind = sljit_get_register_index(TMP2); +sljit_s32 str_ptr_ind = sljit_get_register_index(STR_PTR); +sljit_s32 data1_ind = 0; +sljit_s32 data2_ind = 1; +sljit_s32 tmp_ind = 2; +sljit_s32 cmp1a_ind = 3; +sljit_s32 cmp1b_ind = 4; +sljit_s32 cmp2a_ind = 5; +sljit_s32 cmp2b_ind = 6; +struct sljit_label *start; +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +struct sljit_label *restart; +#endif +struct sljit_jump *jump[2]; + +sljit_u8 instruction[8]; + +SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE && offs1 > offs2); +SLJIT_ASSERT(diff <= IN_UCHARS(max_fast_forward_char_pair_sse2_offset())); +SLJIT_ASSERT(tmp1_ind < 8 && tmp2_ind == 1); + +/* Initialize. */ +if (common->match_end_ptr != 0) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); + OP1(SLJIT_MOV, TMP3, 0, STR_END, 0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offs1 + 1)); + + OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP1, 0, STR_END, 0); + CMOV(SLJIT_LESS, STR_END, TMP1, 0); + } + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1)); +add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + +/* MOVD xmm, r/m32 */ +instruction[0] = 0x66; +instruction[1] = 0x0f; +instruction[2] = 0x6e; + +if (char1a == char1b) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a)); +else + { + bit1 = char1a ^ char1b; + if (is_powerof2(bit1)) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a | bit1)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(bit1)); + } + else + { + bit1 = 0; + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(char1b)); + } + } + +instruction[3] = 0xc0 | (cmp1a_ind << 3) | tmp1_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +if (char1a != char1b) + { + instruction[3] = 0xc0 | (cmp1b_ind << 3) | tmp2_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } + +if (char2a == char2b) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a)); +else + { + bit2 = char2a ^ char2b; + if (is_powerof2(bit2)) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a | bit2)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(bit2)); + } + else + { + bit2 = 0; + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(char2b)); + } + } + +instruction[3] = 0xc0 | (cmp2a_ind << 3) | tmp1_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +if (char2a != char2b) + { + instruction[3] = 0xc0 | (cmp2b_ind << 3) | tmp2_ind; + sljit_emit_op_custom(compiler, instruction, 4); + } + +/* PSHUFD xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x70; +instruction[4] = 0; + +instruction[3] = 0xc0 | (cmp1a_ind << 3) | cmp1a_ind; +sljit_emit_op_custom(compiler, instruction, 5); + +if (char1a != char1b) + { + instruction[3] = 0xc0 | (cmp1b_ind << 3) | cmp1b_ind; + sljit_emit_op_custom(compiler, instruction, 5); + } + +instruction[3] = 0xc0 | (cmp2a_ind << 3) | cmp2a_ind; +sljit_emit_op_custom(compiler, instruction, 5); + +if (char2a != char2b) + { + instruction[3] = 0xc0 | (cmp2b_ind << 3) | cmp2b_ind; + sljit_emit_op_custom(compiler, instruction, 5); + } + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +restart = LABEL(); +#endif + +OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1 - offs2)); +OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0); +OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, ~0xf); + +load_from_mem_sse2(compiler, data1_ind, str_ptr_ind); + +jump[0] = CMP(SLJIT_EQUAL, STR_PTR, 0, TMP1, 0); + +load_from_mem_sse2(compiler, data2_ind, tmp1_ind); + +/* MOVDQA xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x6f; +instruction[3] = 0xc0 | (tmp_ind << 3) | data1_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +/* PSLLDQ xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x73; +instruction[3] = 0xc0 | (7 << 3) | tmp_ind; +instruction[4] = diff; +sljit_emit_op_custom(compiler, instruction, 5); + +/* PSRLDQ xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +/* instruction[2] = 0x73; */ +instruction[3] = 0xc0 | (3 << 3) | data2_ind; +instruction[4] = 16 - diff; +sljit_emit_op_custom(compiler, instruction, 5); + +/* POR xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xeb; +instruction[3] = 0xc0 | (data2_ind << 3) | tmp_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +jump[1] = JUMP(SLJIT_JUMP); + +JUMPHERE(jump[0]); + +/* MOVDQA xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x6f; +instruction[3] = 0xc0 | (data2_ind << 3) | data1_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +/* PSLLDQ xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x73; +instruction[3] = 0xc0 | (7 << 3) | data2_ind; +instruction[4] = diff; +sljit_emit_op_custom(compiler, instruction, 5); + +JUMPHERE(jump[1]); + +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf); + +fast_forward_char_pair_sse2_compare(compiler, char2a, char2b, bit2, data2_ind, cmp2a_ind, cmp2b_ind, tmp_ind); +fast_forward_char_pair_sse2_compare(compiler, char1a, char1b, bit1, data1_ind, cmp1a_ind, cmp1b_ind, tmp_ind); + +/* PAND xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xdb; +instruction[3] = 0xc0 | (data1_ind << 3) | data2_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +/* PMOVMSKB reg, xmm */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xd7; +instruction[3] = 0xc0 | (tmp1_ind << 3) | 0; +sljit_emit_op_custom(compiler, instruction, 4); + +/* Ignore matches before the first STR_PTR. */ +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +OP2(SLJIT_LSHR, 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); +sljit_set_current_flags(compiler, SLJIT_SET_Z); + +jump[0] = JUMP(SLJIT_NOT_ZERO); + +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + +/* Main loop. */ +instruction[0] = 0x66; +instruction[1] = 0x0f; + +start = LABEL(); + +load_from_mem_sse2(compiler, data2_ind, str_ptr_ind); + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16); +add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + +load_from_mem_sse2(compiler, data1_ind, str_ptr_ind); + +/* PSRLDQ xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x73; +instruction[3] = 0xc0 | (3 << 3) | data2_ind; +instruction[4] = 16 - diff; +sljit_emit_op_custom(compiler, instruction, 5); + +/* MOVDQA xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x6f; +instruction[3] = 0xc0 | (tmp_ind << 3) | data1_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +/* PSLLDQ xmm1, xmm2/m128, imm8 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0x73; +instruction[3] = 0xc0 | (7 << 3) | tmp_ind; +instruction[4] = diff; +sljit_emit_op_custom(compiler, instruction, 5); + +/* POR xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xeb; +instruction[3] = 0xc0 | (data2_ind << 3) | tmp_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +fast_forward_char_pair_sse2_compare(compiler, char1a, char1b, bit1, data1_ind, cmp1a_ind, cmp1b_ind, tmp_ind); +fast_forward_char_pair_sse2_compare(compiler, char2a, char2b, bit2, data2_ind, cmp2a_ind, cmp2b_ind, tmp_ind); + +/* PAND xmm1, xmm2/m128 */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xdb; +instruction[3] = 0xc0 | (data1_ind << 3) | data2_ind; +sljit_emit_op_custom(compiler, instruction, 4); + +/* PMOVMSKB reg, xmm */ +/* instruction[0] = 0x66; */ +/* instruction[1] = 0x0f; */ +instruction[2] = 0xd7; +instruction[3] = 0xc0 | (tmp1_ind << 3) | 0; +sljit_emit_op_custom(compiler, instruction, 4); + +/* 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); +sljit_set_current_flags(compiler, SLJIT_SET_Z); + +JUMPTO(SLJIT_ZERO, start); + +JUMPHERE(jump[0]); + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + +add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); + +if (common->match_end_ptr != 0) + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); + +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 +if (common->utf) + { + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offs1)); + + jump[0] = jump_if_utf_char_start(compiler, TMP1); + + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, restart); + + add_jump(compiler, &common->failed_match, JUMP(SLJIT_JUMP)); + + JUMPHERE(jump[0]); + } +#endif + +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1)); + +if (common->match_end_ptr != 0) + OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); +} + +static BOOL check_fast_forward_char_pair_sse2(compiler_common *common, fast_forward_char_data *chars, int max) +{ +sljit_s32 i, j, priority, count; +sljit_u32 priorities; +PCRE2_UCHAR a1, a2, b1, b2; + +priorities = 0; + +count = 0; +for (i = 0; i < max; i++) + { + if (chars[i].last_count > 2) + { + SLJIT_ASSERT(chars[i].last_count <= 7); + + priorities |= (1 << chars[i].last_count); + count++; + } + } + +if (count < 2) + return FALSE; + +for (priority = 7; priority > 2; priority--) + { + if ((priorities & (1 << priority)) == 0) + continue; + + for (i = max - 1; i >= 1; i--) + if (chars[i].last_count >= priority) + { + SLJIT_ASSERT(chars[i].count <= 2 && chars[i].count >= 1); + + a1 = chars[i].chars[0]; + a2 = chars[i].chars[1]; + + j = i - max_fast_forward_char_pair_sse2_offset(); + if (j < 0) + j = 0; + + while (j < i) + { + if (chars[j].last_count >= priority) + { + b1 = chars[j].chars[0]; + b2 = chars[j].chars[1]; + + if (a1 != b1 && a1 != b2 && a2 != b1 && a2 != b2) + { + fast_forward_char_pair_sse2(common, i, a1, a2, j, b1, b2); + return TRUE; + } + } + j++; + } + } + } + +return FALSE; +} + +#endif + #undef SSE2_COMPARE_TYPE_INDEX #endif @@ -4161,15 +4984,16 @@ static void fast_forward_first_char2(compiler_common *common, PCRE2_UCHAR char1, { DEFINE_COMPILER; struct sljit_label *start; -struct sljit_jump *quit; -struct sljit_jump *found; +struct sljit_jump *match; +struct sljit_jump *partial_quit; 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); +SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE || offset == 0); + +if (has_match_end) + OP1(SLJIT_MOV, TMP1, 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)); @@ -4177,76 +5001,21 @@ if (has_match_end) { OP1(SLJIT_MOV, TMP3, 0, STR_END, 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()) - { - 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); - } + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offset + 1)); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0); + CMOV(SLJIT_GREATER, STR_END, TMP1, 0); } -#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) +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND) /* SSE2 accelerated first character search. */ -if (sljit_x86_is_sse2_available()) +if (sljit_has_cpu_feature(SLJIT_HAS_SSE2)) { - fast_forward_first_char2_sse2(common, char1, char2); + fast_forward_first_char2_sse2(common, char1, char2, offset); - 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 (offset > 0) + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset)); if (has_match_end) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); @@ -4255,85 +5024,56 @@ if (sljit_x86_is_sse2_available()) #endif -quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); - start = LABEL(); + +partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +if (common->mode == PCRE2_JIT_COMPLETE) + add_jump(compiler, &common->failed_match, partial_quit); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); if (char1 == char2) - found = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1, start); 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); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1 | mask, start); } 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); + match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char2, start); + JUMPHERE(match); } } -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); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-(offset + 1))); + jumpto_if_not_utf_char_start(compiler, TMP1, start); } #endif -JUMPHERE(quit); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset + 1)); + +if (common->mode != PCRE2_JIT_COMPLETE) + JUMPHERE(partial_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]; +fast_forward_char_data chars[MAX_N_CHARS]; sljit_s32 offset; PCRE2_UCHAR mask; PCRE2_UCHAR *char_set, *char_set_end; @@ -4344,7 +5084,10 @@ BOOL in_range; sljit_u32 rec_count; for (i = 0; i < MAX_N_CHARS; i++) - chars[i * MAX_DIFF_CHARS] = 0; + { + chars[i].count = 0; + chars[i].last_count = 0; + } rec_count = 10000; max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count); @@ -4352,21 +5095,50 @@ max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count); if (max < 1) return FALSE; +/* Convert last_count to priority. */ +for (i = 0; i < max; i++) + { + SLJIT_ASSERT(chars[i].count > 0 && chars[i].last_count <= chars[i].count); + + if (chars[i].count == 1) + { + chars[i].last_count = (chars[i].last_count == 1) ? 7 : 5; + /* Simplifies algorithms later. */ + chars[i].chars[1] = chars[i].chars[0]; + } + else if (chars[i].count == 2) + { + SLJIT_ASSERT(chars[i].chars[0] != chars[i].chars[1]); + + if (is_powerof2(chars[i].chars[0] ^ chars[i].chars[1])) + chars[i].last_count = (chars[i].last_count == 2) ? 6 : 4; + else + chars[i].last_count = (chars[i].last_count == 2) ? 3 : 2; + } + else + chars[i].last_count = (chars[i].count == 255) ? 0 : 1; + } + +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) && !(defined SUPPORT_VALGRIND) && !(defined _WIN64) +if (check_fast_forward_char_pair_sse2(common, chars, max)) + return TRUE; +#endif + in_range = FALSE; /* 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 && (chars[(i - 1) * MAX_DIFF_CHARS] < 255)) + if (in_range && (i - from) > range_len && (chars[i - 1].count < 255)) { range_len = i - from; range_right = i - 1; } - if (i < max && chars[i * MAX_DIFF_CHARS] < 255) + if (i < max && chars[i].count < 255) { - SLJIT_ASSERT(chars[i * MAX_DIFF_CHARS] > 0); + SLJIT_ASSERT(chars[i].count > 0); if (!in_range) { in_range = TRUE; @@ -4386,16 +5158,17 @@ if (range_right >= 0) for (i = 0; i < range_len; i++) { - 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) + SLJIT_ASSERT(chars[range_right - i].count > 0 && chars[range_right - i].count < 255); + + char_set = chars[range_right - i].chars; + char_set_end = char_set + chars[range_right - i].count; + do { if (update_table[(*char_set) & 0xff] > IN_UCHARS(i)) update_table[(*char_set) & 0xff] = IN_UCHARS(i); char_set++; } + while (char_set < char_set_end); } } @@ -4403,54 +5176,38 @@ offset = -1; /* Scan forward. */ for (i = 0; i < max; i++) { + if (range_right == i) + continue; + if (offset == -1) { - if (chars[i * MAX_DIFF_CHARS] <= 2) + if (chars[i].last_count >= 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)) - { - mask = chars[i * MAX_DIFF_CHARS + 1] ^ chars[i * MAX_DIFF_CHARS + 2]; - if (is_powerof2(mask)) - offset = i; - } - } - } + else if (chars[offset].last_count < chars[i].last_count) + offset = i; } +SLJIT_ASSERT(offset == -1 || (chars[offset].count >= 1 && chars[offset].count <= 2)); + 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); + fast_forward_first_char2(common, chars[offset].chars[0], chars[offset].chars[1], offset); return TRUE; } -if (range_right == offset) - offset = -1; +SLJIT_ASSERT(range_right != offset); -SLJIT_ASSERT(offset == -1 || (chars[offset * MAX_DIFF_CHARS] >= 1 && chars[offset * MAX_DIFF_CHARS] <= 2)); - -max -= 1; -SLJIT_ASSERT(max > 0); if (common->match_end_ptr != 0) { 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); - OP1(SLJIT_MOV, STR_END, 0, TMP1, 0); - JUMPHERE(quit); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0); + CMOV(SLJIT_GREATER, STR_END, TMP1, 0); } else OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max)); @@ -4462,7 +5219,7 @@ 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); +add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0)); #if PCRE2_CODE_UNIT_WIDTH == 8 || (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN) OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right)); @@ -4483,20 +5240,20 @@ if (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 (chars[offset * MAX_DIFF_CHARS] == 1) - CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset * MAX_DIFF_CHARS + 1], start); + if (chars[offset].count == 1) + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0], start); else { - mask = chars[offset * MAX_DIFF_CHARS + 1] ^ chars[offset * MAX_DIFF_CHARS + 2]; + mask = chars[offset].chars[0] ^ chars[offset].chars[1]; 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); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0] | 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); + match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0]); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[1], start); JUMPHERE(match); } } @@ -4512,15 +5269,9 @@ if (common->utf && offset != 0) } 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 + + jumpto_if_not_utf_char_start(compiler, TMP1, start); + if (offset < 0) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } @@ -4529,33 +5280,20 @@ if (common->utf && offset != 0) if (offset >= 0) OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -JUMPHERE(quit); - if (common->match_end_ptr != 0) - { - if (range_right >= 0) - 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) - { - quit = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP1, 0); - OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0); - JUMPHERE(quit); - } - } else OP2(SLJIT_ADD, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max)); return TRUE; } -#undef MAX_N_CHARS - -static SLJIT_INLINE void fast_forward_first_char(compiler_common *common, PCRE2_UCHAR first_char, BOOL caseless) +static SLJIT_INLINE void fast_forward_first_char(compiler_common *common) { +PCRE2_UCHAR first_char = (PCRE2_UCHAR)(common->re->first_codeunit); PCRE2_UCHAR oc; oc = first_char; -if (caseless) +if ((common->re->flags & PCRE2_FIRSTCASELESS) != 0) { oc = TABLE_GET(first_char, common->fcc, first_char); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 @@ -4593,8 +5331,8 @@ if (common->nltype == NLTYPE_FIXED && common->newline > 255) firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(2)); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER_EQUAL); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT); #endif @@ -4638,8 +5376,8 @@ if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF) JUMPHERE(foundcr); notfoundnl = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); #if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT); #endif @@ -4654,79 +5392,75 @@ if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, TMP3, 0); } -static BOOL check_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks); +static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks); -static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common, const sljit_u8 *start_bits) +static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common) { DEFINE_COMPILER; +const sljit_u8 *start_bits = common->re->start_bitmap; struct sljit_label *start; -struct sljit_jump *quit; -struct sljit_jump *found = NULL; -jump_list *matches = NULL; +struct sljit_jump *partial_quit; #if PCRE2_CODE_UNIT_WIDTH != 8 -struct sljit_jump *jump; +struct sljit_jump *found = NULL; #endif +jump_list *matches = NULL; if (common->match_end_ptr != 0) { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); OP1(SLJIT_MOV, RETURN_ADDR, 0, STR_END, 0); - OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, STR_END, 0, TMP1, 0); + CMOV(SLJIT_GREATER, STR_END, TMP1, 0); } start = LABEL(); -quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); -OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); -#ifdef SUPPORT_UNICODE -if (common->utf) - OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); -#endif -if (!check_class_ranges(common, start_bits, (start_bits[31] & 0x80) != 0, TRUE, &matches)) +partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +if (common->mode == PCRE2_JIT_COMPLETE) + add_jump(compiler, &common->failed_match, partial_quit); + +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +if (!optimize_class(common, start_bits, (start_bits[31] & 0x80) != 0, FALSE, &matches)) { #if PCRE2_CODE_UNIT_WIDTH != 8 - jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 255); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 255); - JUMPHERE(jump); + if ((start_bits[31] & 0x80) != 0) + found = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255); + else + CMPTO(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255, start); +#elif defined SUPPORT_UNICODE + if (common->utf && is_char7_bitset(start_bits, FALSE)) + CMPTO(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 127, start); #endif OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)start_bits); - OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); - found = JUMP(SLJIT_NOT_ZERO); + if (sljit_get_register_index(TMP3) >= 0) + { + OP2(SLJIT_SHL, TMP3, 0, SLJIT_IMM, 1, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP3, 0); + } + else + { + OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + } + JUMPTO(SLJIT_ZERO, start); } +else + set_jumps(matches, start); -#ifdef SUPPORT_UNICODE -if (common->utf) - OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); -#endif -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -#ifdef SUPPORT_UNICODE -#if PCRE2_CODE_UNIT_WIDTH == 8 -if (common->utf) - { - CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0, start); - OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0); - OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); - } -#elif PCRE2_CODE_UNIT_WIDTH == 16 -if (common->utf) - { - CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800, start); - OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); - OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); - OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); - } -#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16] */ -#endif /* SUPPORT_UNICODE */ -JUMPTO(SLJIT_JUMP, start); +#if PCRE2_CODE_UNIT_WIDTH != 8 if (found != NULL) JUMPHERE(found); -if (matches != NULL) - set_jumps(matches, LABEL()); -JUMPHERE(quit); +#endif + +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +if (common->mode != PCRE2_JIT_COMPLETE) + JUMPHERE(partial_quit); if (common->match_end_ptr != 0) OP1(SLJIT_MOV, STR_END, 0, RETURN_ADDR, 0); @@ -4802,31 +5536,50 @@ struct sljit_jump *jump; struct sljit_label *mainloop; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); -OP1(SLJIT_MOV, TMP1, 0, STACK_TOP, 0); -GET_LOCAL_BASE(TMP3, 0, 0); +GET_LOCAL_BASE(TMP1, 0, 0); /* Drop frames until we reach STACK_TOP. */ mainloop = LABEL(); -OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), 0); -OP2(SLJIT_SUB | SLJIT_SET_S, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0); -jump = JUMP(SLJIT_SIG_LESS_EQUAL); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), -sizeof(sljit_sw)); +jump = CMP(SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0); -OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP3, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_sw)); -OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(TMP1), 2 * sizeof(sljit_sw)); -OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); +OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); +if (sljit_get_register_index (TMP3) < 0) + { + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(STACK_TOP), -(3 * sizeof(sljit_sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); + } +else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); + OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(3 * sizeof(sljit_sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0); + GET_LOCAL_BASE(TMP1, 0, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP3, 0); + } JUMPTO(SLJIT_JUMP, mainloop); JUMPHERE(jump); -jump = JUMP(SLJIT_SIG_LESS); -/* End of dropping frames. */ +jump = CMP(SLJIT_NOT_ZERO /* SIG_LESS */, TMP2, 0, SLJIT_IMM, 0); +/* End of reverting values. */ sljit_emit_fast_return(compiler, RETURN_ADDR, 0); JUMPHERE(jump); OP1(SLJIT_NEG, TMP2, 0, TMP2, 0); -OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP3, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_sw)); -OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); +OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0); +if (sljit_get_register_index (TMP3) < 0) + { + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); + } +else + { + OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(2 * sizeof(sljit_sw))); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP3, 0); + } JUMPTO(SLJIT_JUMP, mainloop); } @@ -4859,11 +5612,11 @@ if (common->use_ucp) jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE); add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); JUMPHERE(jump); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP2, 0); } @@ -4903,11 +5656,11 @@ if (common->use_ucp) jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE); add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); JUMPHERE(jump); } else @@ -4935,15 +5688,15 @@ else } set_jumps(skipread_list, LABEL()); -OP2(SLJIT_XOR | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); +OP2(SLJIT_XOR | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); } -static BOOL check_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) +static BOOL optimize_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) { /* May destroy TMP1. */ DEFINE_COMPILER; -int ranges[MAX_RANGE_SIZE]; +int ranges[MAX_CLASS_RANGE_SIZE]; sljit_u8 bit, cbit, all; int i, byte, length = 0; @@ -4961,7 +5714,7 @@ for (i = 0; i < 256; ) cbit = (bits[byte] >> (i & 0x7)) & 0x1; if (cbit != bit) { - if (length >= MAX_RANGE_SIZE) + if (length >= MAX_CLASS_RANGE_SIZE) return FALSE; ranges[length] = i; length++; @@ -4974,7 +5727,7 @@ for (i = 0; i < 256; ) if (((bit == 0) && nclass) || ((bit == 1) && !nclass)) { - if (length >= MAX_RANGE_SIZE) + if (length >= MAX_CLASS_RANGE_SIZE) return FALSE; ranges[length] = 256; length++; @@ -5086,11 +5839,120 @@ switch(length) return TRUE; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return FALSE; } } +static BOOL optimize_class_chars(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) +{ +/* May destroy TMP1. */ +DEFINE_COMPILER; +uint16_t char_list[MAX_CLASS_CHARS_SIZE]; +uint8_t byte; +sljit_s32 type; +int i, j, k, len, c; + +if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV)) + return FALSE; + +if (invert) + nclass = !nclass; + +len = 0; + +for (i = 0; i < 32; i++) + { + byte = bits[i]; + + if (nclass) + byte = ~byte; + + j = 0; + while (byte != 0) + { + if (byte & 0x1) + { + c = i * 8 + j; + + k = len; + + if ((c & 0x20) != 0) + { + for (k = 0; k < len; k++) + if (char_list[k] == c - 0x20) + { + char_list[k] |= 0x120; + break; + } + } + + if (k == len) + { + if (len >= MAX_CLASS_CHARS_SIZE) + return FALSE; + + char_list[len++] = (uint16_t) c; + } + } + + byte >>= 1; + j++; + } + } + +if (len == 0) return FALSE; /* Should never occur, but stops analyzers complaining. */ + +i = 0; +j = 0; + +if (char_list[0] == 0) + { + i++; + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_ZERO); + } +else + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); + +while (i < len) + { + if ((char_list[i] & 0x100) != 0) + j++; + else + { + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char_list[i]); + CMOV(SLJIT_ZERO, TMP2, TMP1, 0); + } + i++; + } + +if (j != 0) + { + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x20); + + for (i = 0; i < len; i++) + if ((char_list[i] & 0x100) != 0) + { + j--; + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char_list[i] & 0xff); + CMOV(SLJIT_ZERO, TMP2, TMP1, 0); + } + } + +type = nclass ? SLJIT_NOT_EQUAL : SLJIT_EQUAL; +add_jump(compiler, backtracks, CMP(type, TMP2, 0, SLJIT_IMM, 0)); +return TRUE; +} + +static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks) +{ +/* May destroy TMP1. */ +if (optimize_class_ranges(common, bits, nclass, invert, backtracks)) + return TRUE; +return optimize_class_chars(common, bits, nclass, invert, backtracks); +} + static void check_anynewline(compiler_common *common) { /* Check whether TMP1 contains a newline character. TMP2 destroyed. */ @@ -5099,22 +5961,22 @@ DEFINE_COMPILER; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a); -OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); -OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); +OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { #endif - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); #if PCRE2_CODE_UNIT_WIDTH == 8 } #endif #endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */ -OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); +OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); sljit_emit_fast_return(compiler, RETURN_ADDR, 0); } @@ -5125,34 +5987,34 @@ DEFINE_COMPILER; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09); -OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20); -OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0); +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20); +OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { #endif - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2000); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000); #if PCRE2_CODE_UNIT_WIDTH == 8 } #endif #endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */ -OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); +OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); sljit_emit_fast_return(compiler, RETURN_ADDR, 0); } @@ -5165,113 +6027,210 @@ DEFINE_COMPILER; sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a); -OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); -OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); -OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); +OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); +OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32 #if PCRE2_CODE_UNIT_WIDTH == 8 if (common->utf) { #endif - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); #if PCRE2_CODE_UNIT_WIDTH == 8 } #endif #endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */ -OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); +OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); sljit_emit_fast_return(compiler, RETURN_ADDR, 0); } -#define CHAR1 STR_END -#define CHAR2 STACK_TOP - static void do_casefulcmp(compiler_common *common) { DEFINE_COMPILER; struct sljit_jump *jump; struct sljit_label *label; +int char1_reg; +int char2_reg; -sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); +if (sljit_get_register_index(TMP3) < 0) + { + char1_reg = STR_END; + char2_reg = STACK_TOP; + } +else + { + char1_reg = TMP3; + char2_reg = RETURN_ADDR; + } + +sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); -OP1(SLJIT_MOV, TMP3, 0, CHAR1, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, CHAR2, 0); -OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); -OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -label = LABEL(); -OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1)); -OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); -jump = CMP(SLJIT_NOT_EQUAL, CHAR1, 0, CHAR2, 0); -OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); -JUMPTO(SLJIT_NOT_ZERO, label); +if (char1_reg == STR_END) + { + OP1(SLJIT_MOV, TMP3, 0, char1_reg, 0); + OP1(SLJIT_MOV, RETURN_ADDR, 0, char2_reg, 0); + } -JUMPHERE(jump); -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -OP1(SLJIT_MOV, CHAR1, 0, TMP3, 0); -OP1(SLJIT_MOV, CHAR2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); -sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + { + label = LABEL(); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); + JUMPTO(SLJIT_NOT_ZERO, label); + + JUMPHERE(jump); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + } +else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + { + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + + label = LABEL(); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); + JUMPTO(SLJIT_NOT_ZERO, label); + + JUMPHERE(jump); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + } +else + { + label = LABEL(); + OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0); + OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); + JUMPTO(SLJIT_NOT_ZERO, label); + + JUMPHERE(jump); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + } + +if (char1_reg == STR_END) + { + OP1(SLJIT_MOV, char1_reg, 0, TMP3, 0); + OP1(SLJIT_MOV, char2_reg, 0, RETURN_ADDR, 0); + } + +sljit_emit_fast_return(compiler, TMP1, 0); } -#define LCC_TABLE STACK_LIMIT - static void do_caselesscmp(compiler_common *common) { DEFINE_COMPILER; struct sljit_jump *jump; struct sljit_label *label; +int char1_reg = STR_END; +int char2_reg; +int lcc_table; +int opt_type = 0; -sljit_emit_fast_enter(compiler, RETURN_ADDR, 0); +if (sljit_get_register_index(TMP3) < 0) + { + char2_reg = STACK_TOP; + lcc_table = STACK_LIMIT; + } +else + { + char2_reg = RETURN_ADDR; + lcc_table = TMP3; + } + +if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + opt_type = 1; +else if (sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS) + opt_type = 2; + +sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); -OP1(SLJIT_MOV, TMP3, 0, LCC_TABLE, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, CHAR1, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, CHAR2, 0); -OP1(SLJIT_MOV, LCC_TABLE, 0, SLJIT_IMM, common->lcc); -OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); -OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, char1_reg, 0); + +if (char2_reg == STACK_TOP) + { + OP1(SLJIT_MOV, TMP3, 0, char2_reg, 0); + OP1(SLJIT_MOV, RETURN_ADDR, 0, lcc_table, 0); + } + +OP1(SLJIT_MOV, lcc_table, 0, SLJIT_IMM, common->lcc); + +if (opt_type == 1) + { + label = LABEL(); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + } +else if (opt_type == 2) + { + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + + label = LABEL(); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)); + sljit_emit_mem(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + } +else + { + label = LABEL(); + OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0); + OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); + } -label = LABEL(); -OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1)); -OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); #if PCRE2_CODE_UNIT_WIDTH != 8 -jump = CMP(SLJIT_GREATER, CHAR1, 0, SLJIT_IMM, 255); +jump = CMP(SLJIT_GREATER, char1_reg, 0, SLJIT_IMM, 255); #endif -OP1(SLJIT_MOV_U8, CHAR1, 0, SLJIT_MEM2(LCC_TABLE, CHAR1), 0); +OP1(SLJIT_MOV_U8, char1_reg, 0, SLJIT_MEM2(lcc_table, char1_reg), 0); #if PCRE2_CODE_UNIT_WIDTH != 8 JUMPHERE(jump); -jump = CMP(SLJIT_GREATER, CHAR2, 0, SLJIT_IMM, 255); +jump = CMP(SLJIT_GREATER, char2_reg, 0, SLJIT_IMM, 255); #endif -OP1(SLJIT_MOV_U8, CHAR2, 0, SLJIT_MEM2(LCC_TABLE, CHAR2), 0); +OP1(SLJIT_MOV_U8, char2_reg, 0, SLJIT_MEM2(lcc_table, char2_reg), 0); #if PCRE2_CODE_UNIT_WIDTH != 8 JUMPHERE(jump); #endif -jump = CMP(SLJIT_NOT_EQUAL, CHAR1, 0, CHAR2, 0); -OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); + +if (opt_type == 0) + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0); +OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); JUMPTO(SLJIT_NOT_ZERO, label); JUMPHERE(jump); -OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); -OP1(SLJIT_MOV, LCC_TABLE, 0, TMP3, 0); -OP1(SLJIT_MOV, CHAR1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); -OP1(SLJIT_MOV, CHAR2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); -sljit_emit_fast_return(compiler, RETURN_ADDR, 0); -} +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); -#undef LCC_TABLE -#undef CHAR1 -#undef CHAR2 +if (opt_type == 2) + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +if (char2_reg == STACK_TOP) + { + OP1(SLJIT_MOV, char2_reg, 0, TMP3, 0); + OP1(SLJIT_MOV, lcc_table, 0, RETURN_ADDR, 0); + } + +OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); +sljit_emit_fast_return(compiler, TMP1, 0); +} #if defined SUPPORT_UNICODE -static PCRE2_SPTR SLJIT_CALL do_utf_caselesscmp(PCRE2_SPTR src1, jit_arguments *args, PCRE2_SPTR end1) +static PCRE2_SPTR SLJIT_FUNC do_utf_caselesscmp(PCRE2_SPTR src1, PCRE2_SPTR src2, PCRE2_SPTR end1, PCRE2_SPTR end2) { /* This function would be ineffective to do in JIT level. */ sljit_u32 c1, c2; -PCRE2_SPTR src2 = args->startchar_ptr; -PCRE2_SPTR end2 = args->end; const ucd_record *ur; const sljit_u32 *pp; @@ -5416,7 +6375,7 @@ do #endif default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } context->ucharptr = 0; @@ -5591,7 +6550,7 @@ while (*cc != XCL_END) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } cc += 2; @@ -5609,13 +6568,13 @@ if ((cc[-1] & XCL_HASPROP) == 0) if ((cc[-1] & XCL_MAP) != 0) { jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255); - if (!check_class_ranges(common, (const sljit_u8 *)cc, (((const sljit_u8 *)cc)[31] & 0x80) != 0, TRUE, &found)) + if (!optimize_class(common, (const sljit_u8 *)cc, (((const sljit_u8 *)cc)[31] & 0x80) != 0, TRUE, &found)) { OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); add_jump(compiler, &found, JUMP(SLJIT_NOT_ZERO)); } @@ -5636,7 +6595,7 @@ else if ((cc[-1] & XCL_MAP) != 0) #ifdef SUPPORT_UNICODE charsaved = TRUE; #endif - if (!check_class_ranges(common, (const sljit_u8 *)cc, FALSE, TRUE, list)) + if (!optimize_class(common, (const sljit_u8 *)cc, FALSE, TRUE, list)) { #if PCRE2_CODE_UNIT_WIDTH == 8 jump = NULL; @@ -5648,7 +6607,7 @@ else if ((cc[-1] & XCL_MAP) != 0) OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); add_jump(compiler, list, JUMP(SLJIT_NOT_ZERO)); #if PCRE2_CODE_UNIT_WIDTH == 8 @@ -5667,8 +6626,18 @@ if (needstype || needsscript) if (needschar && !charsaved) OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); +#if PCRE2_CODE_UNIT_WIDTH == 32 + if (!common->utf) + { + jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR); + JUMPHERE(jump); + } +#endif + OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); - OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); + OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1)); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); @@ -5758,14 +6727,14 @@ while (*cc != XCL_END) if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE)) { - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); - OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, numberofcmps == 0 ? SLJIT_UNUSED : TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); + OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_EQUAL); numberofcmps++; } else if (numberofcmps > 0) { - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); numberofcmps = 0; } @@ -5784,14 +6753,14 @@ while (*cc != XCL_END) if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE)) { - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); - OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, numberofcmps == 0 ? SLJIT_UNUSED : TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); + OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); numberofcmps++; } else if (numberofcmps > 0) { - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset)); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); numberofcmps = 0; } @@ -5816,12 +6785,12 @@ while (*cc != XCL_END) break; case PT_LAMP: - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; @@ -5843,33 +6812,33 @@ while (*cc != XCL_END) case PT_SPACE: case PT_PXSPACE: SET_CHAR_OFFSET(9); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd - 0x9); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd - 0x9); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x9); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x9); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x9); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x9); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); SET_TYPE_OFFSET(ucp_Zl); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_WORD: - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset)); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset)); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); /* Fall through. */ case PT_ALNUM: SET_TYPE_OFFSET(ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); - OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, (*cc == PT_ALNUM) ? SLJIT_UNUSED : TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); SET_TYPE_OFFSET(ucp_Nd); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; @@ -5891,8 +6860,8 @@ while (*cc != XCL_END) OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset); OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]); } - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[1]); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[1]); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); other_cases += 2; } else if (is_powerof2(other_cases[2] ^ other_cases[1])) @@ -5904,63 +6873,63 @@ while (*cc != XCL_END) OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset); OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]); } - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[2]); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, other_cases[2]); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset)); - OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_E : 0), TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset)); + OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL); other_cases += 3; } else { - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset)); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset)); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); } while (*other_cases != NOTACHAR) { - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset)); - OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_E : 0), TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset)); + OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL); } jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_UCNC: - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset)); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset)); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset)); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset)); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset)); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset)); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); SET_CHAR_OFFSET(0xa0); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset)); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset)); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL); SET_CHAR_OFFSET(0); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xe000 - 0); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_GREATER_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xe000 - 0); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_GREATER_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; case PT_PXGRAPH: /* C and Z groups are the farthest two groups. */ SET_TYPE_OFFSET(ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER); jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll); /* In case of ucp_Cf, we overwrite the result. */ SET_CHAR_OFFSET(0x2066); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); JUMPHERE(jump); jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0); @@ -5969,21 +6938,21 @@ while (*cc != XCL_END) case PT_PXPRINT: /* C and Z groups are the farthest two groups. */ SET_TYPE_OFFSET(ucp_Ll); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_GREATER); + OP2(SLJIT_SUB | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll); - OP_FLAGS(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_NOT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll); + OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_NOT_EQUAL); jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll); /* In case of ucp_Cf, we overwrite the result. */ SET_CHAR_OFFSET(0x2066); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066); - OP_FLAGS(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066); + OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL); JUMPHERE(jump); jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0); @@ -5991,21 +6960,21 @@ while (*cc != XCL_END) case PT_PXPUNCT: SET_TYPE_OFFSET(ucp_Sc); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL); SET_CHAR_OFFSET(0); - 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); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x7f); + OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_LESS_EQUAL); SET_TYPE_OFFSET(ucp_Pc); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL); jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp); break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } cc += 2; @@ -6051,6 +7020,7 @@ switch(type) case OP_NOT_WORD_BOUNDARY: case OP_WORD_BOUNDARY: add_jump(compiler, &common->wordboundary, JUMP(SLJIT_FAST_CALL)); + sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(type == OP_NOT_WORD_BOUNDARY ? SLJIT_NOT_ZERO : SLJIT_ZERO)); return cc; @@ -6066,10 +7036,10 @@ switch(type) else { jump[1] = CMP(SLJIT_EQUAL, TMP2, 0, STR_END, 0); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); - OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_UNUSED, 0, SLJIT_LESS); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff); - OP_FLAGS(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_NOT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_LESS, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); + OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff); + OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_NOT_EQUAL); add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL)); check_partial(common, TRUE); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); @@ -6091,9 +7061,9 @@ switch(type) OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); - OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_GREATER, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); jump[2] = JUMP(SLJIT_GREATER); - add_jump(compiler, backtracks, JUMP(SLJIT_LESS)); + add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL) /* LESS */); /* Equal. */ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); jump[3] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL); @@ -6112,6 +7082,7 @@ switch(type) read_char_range(common, common->nlmin, common->nlmax, TRUE); add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0)); add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL)); + sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(SLJIT_ZERO)); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); } @@ -6129,8 +7100,8 @@ switch(type) case OP_DOLL: OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); - OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); - add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); + OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); + add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32)); if (!common->endonly) compile_simple_assertion_matchingpath(common, OP_EODN, cc, backtracks); @@ -6144,8 +7115,8 @@ switch(type) case OP_DOLLM: jump[1] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); - OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); - add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); + OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL); + add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32)); check_partial(common, FALSE); jump[0] = JUMP(SLJIT_JUMP); JUMPHERE(jump[1]); @@ -6182,16 +7153,16 @@ switch(type) OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0)); - OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); - add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); + OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); + add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32)); return cc; case OP_CIRCM: OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0); - OP2(SLJIT_AND32 | SLJIT_SET_E, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); - add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO)); + OP2(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_UNUSED, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL); + add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO32)); jump[0] = JUMP(SLJIT_JUMP); JUMPHERE(jump[1]); @@ -6229,7 +7200,7 @@ switch(type) label = LABEL(); add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP3, 0)); skip_char_back(common); - OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } else @@ -6242,10 +7213,128 @@ switch(type) check_start_used_ptr(common); return cc + LINK_SIZE; } -SLJIT_ASSERT_STOP(); +SLJIT_UNREACHABLE(); return cc; } +#ifdef SUPPORT_UNICODE + +#if PCRE2_CODE_UNIT_WIDTH != 32 + +static PCRE2_SPTR SLJIT_FUNC do_extuni_utf(jit_arguments *args, PCRE2_SPTR cc) +{ +PCRE2_SPTR start_subject = args->begin; +PCRE2_SPTR end_subject = args->end; +int lgb, rgb, len, ricount; +PCRE2_SPTR prevcc, bptr; +uint32_t c; + +prevcc = cc; +GETCHARINC(c, cc); +lgb = UCD_GRAPHBREAK(c); + +while (cc < end_subject) + { + len = 1; + GETCHARLEN(c, cc, len); + rgb = UCD_GRAPHBREAK(c); + + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + + /* Not breaking between Regional Indicators is allowed only if there + are an even number of preceding RIs. */ + + if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) + { + ricount = 0; + bptr = prevcc; + + /* bptr is pointing to the left-hand character */ + while (bptr > start_subject) + { + bptr--; + BACKCHAR(bptr); + GETCHAR(c, bptr); + + if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break; + + ricount++; + } + + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this + allows any number of them before a following Extended_Pictographic. */ + + if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) || + lgb != ucp_gbExtended_Pictographic) + lgb = rgb; + + prevcc = cc; + cc += len; + } + +return cc; +} + +#endif + +static PCRE2_SPTR SLJIT_FUNC do_extuni_no_utf(jit_arguments *args, PCRE2_SPTR cc) +{ +PCRE2_SPTR start_subject = args->begin; +PCRE2_SPTR end_subject = args->end; +int lgb, rgb, ricount; +PCRE2_SPTR bptr; +uint32_t c; + +GETCHARINC(c, cc); +lgb = UCD_GRAPHBREAK(c); + +while (cc < end_subject) + { + c = *cc; + rgb = UCD_GRAPHBREAK(c); + + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + + /* Not breaking between Regional Indicators is allowed only if there + are an even number of preceding RIs. */ + + if (lgb == ucp_gbRegionalIndicator && rgb == ucp_gbRegionalIndicator) + { + ricount = 0; + bptr = cc - 1; + + /* bptr is pointing to the left-hand character */ + while (bptr > start_subject) + { + bptr--; + c = *bptr; + + if (UCD_GRAPHBREAK(c) != ucp_gbRegionalIndicator) break; + + ricount++; + } + + if ((ricount & 1) != 0) break; /* Grapheme break required */ + } + + /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this + allows any number of them before a following Extended_Pictographic. */ + + if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) || + lgb != ucp_gbExtended_Pictographic) + lgb = rgb; + + cc++; + } + +return cc; +} + +#endif + static PCRE2_SPTR compile_char1_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks, BOOL check_str_ptr) { DEFINE_COMPILER; @@ -6255,7 +7344,6 @@ compare_context context; struct sljit_jump *jump[3]; jump_list *end_list; #ifdef SUPPORT_UNICODE -struct sljit_label *label; PCRE2_UCHAR propdata[5]; #endif /* SUPPORT_UNICODE */ @@ -6273,7 +7361,7 @@ switch(type) #endif read_char8_type(common, type == OP_NOT_DIGIT); /* Flip the starting bit in the negative case. */ - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit); add_jump(compiler, backtracks, JUMP(type == OP_DIGIT ? SLJIT_ZERO : SLJIT_NOT_ZERO)); return cc; @@ -6287,7 +7375,7 @@ switch(type) else #endif read_char8_type(common, type == OP_NOT_WHITESPACE); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space); add_jump(compiler, backtracks, JUMP(type == OP_WHITESPACE ? SLJIT_ZERO : SLJIT_NOT_ZERO)); return cc; @@ -6301,7 +7389,7 @@ switch(type) else #endif read_char8_type(common, type == OP_NOT_WORDCHAR); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word); add_jump(compiler, backtracks, JUMP(type == OP_WORDCHAR ? SLJIT_ZERO : SLJIT_NOT_ZERO)); return cc; @@ -6343,8 +7431,8 @@ switch(type) #elif PCRE2_CODE_UNIT_WIDTH == 16 jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800); OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); - OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_UNUSED, 0, SLJIT_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL); OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); #endif @@ -6404,6 +7492,7 @@ switch(type) detect_partial_match(common, backtracks); read_char_range(common, 0x9, 0x3000, type == OP_NOT_HSPACE); add_jump(compiler, &common->hspace, JUMP(SLJIT_FAST_CALL)); + sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(type == OP_NOT_HSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO)); return cc; @@ -6413,6 +7502,7 @@ switch(type) detect_partial_match(common, backtracks); read_char_range(common, 0xa, 0x2029, type == OP_NOT_VSPACE); add_jump(compiler, &common->vspace, JUMP(SLJIT_FAST_CALL)); + sljit_set_current_flags(compiler, SLJIT_SET_Z); add_jump(compiler, backtracks, JUMP(type == OP_NOT_VSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO)); return cc; @@ -6420,35 +7510,22 @@ switch(type) case OP_EXTUNI: if (check_str_ptr) detect_partial_match(common, backtracks); - read_char(common); - add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, gbprop)); - /* Optimize register allocation: use a real register. */ - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0); - OP1(SLJIT_MOV_U8, STACK_TOP, 0, SLJIT_MEM2(TMP1, TMP2), 3); - label = LABEL(); - jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); - OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); - read_char(common); - add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, gbprop)); - OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM2(TMP1, TMP2), 3); + SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); + OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0); - OP2(SLJIT_SHL, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2); - OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(STACK_TOP), (sljit_sw)PRIV(ucp_gbtable)); - OP1(SLJIT_MOV, STACK_TOP, 0, TMP2, 0); - OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); - JUMPTO(SLJIT_NOT_ZERO, label); +#if PCRE2_CODE_UNIT_WIDTH != 32 + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, + common->utf ? SLJIT_FUNC_OFFSET(do_extuni_utf) : SLJIT_FUNC_OFFSET(do_extuni_no_utf)); +#else + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_extuni_no_utf)); +#endif - OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); - JUMPHERE(jump[0]); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0); if (common->mode == PCRE2_JIT_PARTIAL_HARD) { - jump[0] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0); + jump[0] = CMP(SLJIT_LESS, SLJIT_RETURN_REG, 0, STR_END, 0); /* Since we successfully read a char above, partial matching must occure. */ check_partial(common, TRUE); JUMPHERE(jump[0]); @@ -6582,7 +7659,7 @@ switch(type) read_char_range(common, 0, 255, type == OP_NCLASS); #endif - if (check_class_ranges(common, (const sljit_u8 *)cc, type == OP_NCLASS, FALSE, backtracks)) + if (optimize_class(common, (const sljit_u8 *)cc, type == OP_NCLASS, FALSE, backtracks)) return cc + 32 / sizeof(PCRE2_UCHAR); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 @@ -6609,7 +7686,7 @@ switch(type) OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc); OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); add_jump(compiler, backtracks, JUMP(SLJIT_ZERO)); #if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8 @@ -6626,7 +7703,7 @@ switch(type) return cc + GET(cc, 0) - 1; #endif } -SLJIT_ASSERT_STOP(); +SLJIT_UNREACHABLE(); return cc; } @@ -6781,40 +7858,42 @@ else #if defined SUPPORT_UNICODE if (common->utf && *cc == OP_REFI) { - SLJIT_ASSERT(TMP1 == SLJIT_R0 && STACK_TOP == SLJIT_R1 && TMP2 == SLJIT_R2); + SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); if (ref) - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1)); else - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); if (withchecks) - jump = CMP(SLJIT_EQUAL, TMP1, 0, TMP2, 0); + jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_R2, 0); + /* No free saved registers so save data on stack. */ + + OP1(SLJIT_MOV, SLJIT_R3, 0, STR_END, 0); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW) | SLJIT_ARG3(SW) | SLJIT_ARG4(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_utf_caselesscmp)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0); - /* Needed to save important temporary registers. */ - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0); - OP1(SLJIT_MOV, SLJIT_R1, 0, ARGUMENTS, 0); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), STR_PTR, 0); - sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_utf_caselesscmp)); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); if (common->mode == PCRE2_JIT_COMPLETE) add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1)); else { - add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0)); - nopartial = CMP(SLJIT_NOT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_LESS, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1); + + add_jump(compiler, backtracks, JUMP(SLJIT_LESS)); + + nopartial = JUMP(SLJIT_NOT_EQUAL); + OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0); check_partial(common, FALSE); add_jump(compiler, backtracks, JUMP(SLJIT_JUMP)); JUMPHERE(nopartial); } - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0); } else #endif /* SUPPORT_UNICODE */ { if (ref) - OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0); else - OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0); if (withchecks) jump = JUMP(SLJIT_ZERO); @@ -6905,7 +7984,7 @@ switch(type) cc += 1 + IMM2_SIZE + 1 + 2 * IMM2_SIZE; break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -6919,7 +7998,7 @@ if (!minimize) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); /* Temporary release of STR_PTR. */ - OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); /* Handles both invalid and empty cases. Since the minimum repeat, is zero the invalid case is basically the same as an empty case. */ if (ref) @@ -6932,7 +8011,7 @@ if (!minimize) zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw)); } /* Restore if not zero length. */ - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); } else { @@ -7096,8 +8175,10 @@ if (entry == NULL) if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return NULL; entry->next = NULL; - entry->entry = NULL; - entry->calls = NULL; + entry->entry_label = NULL; + entry->backtrack_label = NULL; + entry->entry_calls = NULL; + entry->backtrack_calls = NULL; entry->start = start; if (prev != NULL) @@ -7106,71 +8187,74 @@ if (entry == NULL) common->entries = entry; } -if (common->has_set_som && common->mark_ptr != 0) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); - allocate_stack(common, 2); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr); - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); - } -else if (common->has_set_som || common->mark_ptr != 0) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->has_set_som ? (int)(OVECTOR(0)) : common->mark_ptr); - allocate_stack(common, 1); - OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); - } +BACKTRACK_AS(recurse_backtrack)->entry = entry; -if (entry->entry == NULL) - add_jump(compiler, &entry->calls, JUMP(SLJIT_FAST_CALL)); +if (entry->entry_label == NULL) + add_jump(compiler, &entry->entry_calls, JUMP(SLJIT_FAST_CALL)); else - JUMPTO(SLJIT_FAST_CALL, entry->entry); + JUMPTO(SLJIT_FAST_CALL, entry->entry_label); /* Leave if the match is failed. */ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0)); +BACKTRACK_AS(recurse_backtrack)->matchingpath = LABEL(); return cc + 1 + LINK_SIZE; } -static int SLJIT_CALL do_callout(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector) +static sljit_s32 SLJIT_FUNC do_callout(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector) { -PCRE2_SPTR begin = arguments->begin; -PCRE2_SIZE *ovector = arguments->match_data->ovector; -sljit_u32 oveccount = arguments->oveccount; -sljit_u32 i; +PCRE2_SPTR begin; +PCRE2_SIZE *ovector; +sljit_u32 oveccount, capture_top; if (arguments->callout == NULL) return 0; -callout_block->version = 1; +SLJIT_COMPILE_ASSERT(sizeof (PCRE2_SIZE) <= sizeof (sljit_sw), pcre2_size_must_be_lower_than_sljit_sw_size); + +begin = arguments->begin; +ovector = (PCRE2_SIZE*)(callout_block + 1); +oveccount = callout_block->capture_top; + +SLJIT_ASSERT(oveccount >= 1); + +callout_block->version = 2; +callout_block->callout_flags = 0; /* Offsets in subject. */ callout_block->subject_length = arguments->end - arguments->begin; -callout_block->start_match = (PCRE2_SPTR)callout_block->subject - arguments->begin; -callout_block->current_position = (PCRE2_SPTR)callout_block->offset_vector - arguments->begin; +callout_block->start_match = jit_ovector[0] - begin; +callout_block->current_position = (PCRE2_SPTR)callout_block->offset_vector - begin; callout_block->subject = begin; /* Convert and copy the JIT offset vector to the ovector array. */ -callout_block->capture_top = 0; +callout_block->capture_top = 1; callout_block->offset_vector = ovector; -for (i = 2; i < oveccount; i += 2) - { - ovector[i] = jit_ovector[i] - begin; - ovector[i + 1] = jit_ovector[i + 1] - begin; - if (jit_ovector[i] >= begin) - callout_block->capture_top = i; - } -callout_block->capture_top = (callout_block->capture_top >> 1) + 1; ovector[0] = PCRE2_UNSET; ovector[1] = PCRE2_UNSET; +ovector += 2; +jit_ovector += 2; +capture_top = 1; + +/* Convert pointers to sizes. */ +while (--oveccount != 0) + { + capture_top++; + + ovector[0] = (PCRE2_SIZE)(jit_ovector[0] - begin); + ovector[1] = (PCRE2_SIZE)(jit_ovector[1] - begin); + + if (ovector[0] != PCRE2_UNSET) + callout_block->capture_top = capture_top; + + ovector += 2; + jit_ovector += 2; + } + return (arguments->callout)(callout_block, arguments->callout_data); } -/* Aligning to 8 byte. */ -#define CALLOUT_ARG_SIZE \ - (((int)sizeof(pcre2_callout_block) + 7) & ~7) - #define CALLOUT_ARG_OFFSET(arg) \ - (-CALLOUT_ARG_SIZE + SLJIT_OFFSETOF(pcre2_callout_block, arg)) + SLJIT_OFFSETOF(pcre2_callout_block, arg) static SLJIT_INLINE PCRE2_SPTR compile_callout_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent) { @@ -7182,10 +8266,13 @@ unsigned int callout_length = (*cc == OP_CALLOUT) sljit_sw value1; sljit_sw value2; sljit_sw value3; +sljit_uw callout_arg_size = (common->re->top_bracket + 1) * 2 * sizeof(sljit_sw); PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); -allocate_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw)); +callout_arg_size = (sizeof(pcre2_callout_block) + callout_arg_size + sizeof(sljit_sw) - 1) / sizeof(sljit_sw); + +allocate_stack(common, callout_arg_size); SLJIT_ASSERT(common->capture_last_ptr != 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr); @@ -7193,11 +8280,10 @@ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); value1 = (*cc == OP_CALLOUT) ? cc[1 + 2 * LINK_SIZE] : 0; OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_number), SLJIT_IMM, value1); OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_last), TMP2, 0); +OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_top), SLJIT_IMM, common->re->top_bracket + 1); /* These pointer sized fields temporarly stores internal variables. */ -OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(offset_vector), STR_PTR, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(subject), TMP2, 0); if (common->mark_ptr != 0) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr)); @@ -7223,22 +8309,24 @@ OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_length) OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_offset), SLJIT_IMM, value3); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(mark), (common->mark_ptr != 0) ? TMP2 : SLJIT_IMM, 0); +SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); + /* Needed to save important temporary registers. */ -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0); -OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_TOP, 0, SLJIT_IMM, CALLOUT_ARG_SIZE); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0); +/* SLJIT_R0 = arguments */ +OP1(SLJIT_MOV, SLJIT_R1, 0, STACK_TOP, 0); GET_LOCAL_BASE(SLJIT_R2, 0, OVECTOR_START); -sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_callout)); -OP1(SLJIT_MOV_S32, SLJIT_RETURN_REG, 0, SLJIT_RETURN_REG, 0); -OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); -free_stack(common, CALLOUT_ARG_SIZE / sizeof(sljit_sw)); +sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(S32) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW) | SLJIT_ARG3(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_callout)); +OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); +free_stack(common, callout_arg_size); /* Check return value. */ -OP2(SLJIT_SUB | SLJIT_SET_S, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); -add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_SIG_GREATER)); -if (common->forced_quit_label == NULL) - add_jump(compiler, &common->forced_quit, JUMP(SLJIT_SIG_LESS)); +OP2(SLJIT_SUB32 | SLJIT_SET_Z | SLJIT_SET_SIG_GREATER, SLJIT_UNUSED, 0, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); +add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_SIG_GREATER32)); +if (common->abort_label == NULL) + add_jump(compiler, &common->abort, JUMP(SLJIT_NOT_EQUAL32) /* SIG_LESS */); else - JUMPTO(SLJIT_SIG_LESS, common->forced_quit_label); + JUMPTO(SLJIT_NOT_EQUAL32 /* SIG_LESS */, common->abort_label); return cc + callout_length; } @@ -7280,6 +8368,7 @@ static PCRE2_SPTR compile_assert_matchingpath(compiler_common *common, PCRE2_SPT DEFINE_COMPILER; int framesize; int extrasize; +BOOL local_quit_available = FALSE; BOOL needs_control_head; int private_data_ptr; backtrack_common altbacktrack; @@ -7290,13 +8379,13 @@ jump_list *tmp = NULL; jump_list **target = (conditional) ? &backtrack->condfailed : &backtrack->common.topbacktracks; jump_list **found; /* Saving previous accept variables. */ -BOOL save_local_exit = common->local_exit; -BOOL save_positive_assert = common->positive_assert; +BOOL save_local_quit_available = common->local_quit_available; +BOOL save_in_positive_assertion = common->in_positive_assertion; then_trap_backtrack *save_then_trap = common->then_trap; struct sljit_label *save_quit_label = common->quit_label; struct sljit_label *save_accept_label = common->accept_label; jump_list *save_quit = common->quit; -jump_list *save_positive_assert_quit = common->positive_assert_quit; +jump_list *save_positive_assertion_quit = common->positive_assertion_quit; jump_list *save_accept = common->accept; struct sljit_jump *jump; struct sljit_jump *brajump = NULL; @@ -7363,7 +8452,7 @@ else allocate_stack(common, framesize + extrasize); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw)); + OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0); if (needs_control_head) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); @@ -7378,21 +8467,21 @@ else else OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); - init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize, FALSE); + init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize); } memset(&altbacktrack, 0, sizeof(backtrack_common)); -if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) +if (conditional || (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT)) { - /* Negative assert is stronger than positive assert. */ - common->local_exit = TRUE; + /* Control verbs cannot escape from these asserts. */ + local_quit_available = TRUE; + common->local_quit_available = TRUE; common->quit_label = NULL; common->quit = NULL; - common->positive_assert = FALSE; } -else - common->positive_assert = TRUE; -common->positive_assert_quit = NULL; + +common->in_positive_assertion = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK); +common->positive_assertion_quit = NULL; while (1) { @@ -7408,16 +8497,16 @@ while (1) compile_matchingpath(common, ccbegin + 1 + LINK_SIZE, cc, &altbacktrack); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) { - if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + if (local_quit_available) { - common->local_exit = save_local_exit; + common->local_quit_available = save_local_quit_available; common->quit_label = save_quit_label; common->quit = save_quit; } - common->positive_assert = save_positive_assert; + common->in_positive_assertion = save_in_positive_assertion; common->then_trap = save_then_trap; common->accept_label = save_accept_label; - common->positive_assert_quit = save_positive_assert_quit; + common->positive_assertion_quit = save_positive_assertion_quit; common->accept = save_accept; return NULL; } @@ -7434,23 +8523,24 @@ while (1) free_stack(common, extrasize); if (needs_control_head) - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1)); } else { if ((opcode != OP_ASSERT_NOT && opcode != OP_ASSERTBACK_NOT) || conditional) { /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); if (needs_control_head) - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1)); } else { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); if (needs_control_head) - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), (framesize + 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 2)); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw)); } } @@ -7460,25 +8550,25 @@ while (1) if (conditional) { if (extrasize > 0) - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? sizeof(sljit_sw) : 0); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? STACK(-2) : STACK(-1)); } else if (bra == OP_BRAZERO) { if (framesize < 0) - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize)); else { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw)); - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (framesize + extrasize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - extrasize)); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0); } - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } else if (framesize >= 0) { /* For OP_BRA and OP_BRAMINZERO. */ - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1)); } } add_jump(compiler, found, JUMP(SLJIT_JUMP)); @@ -7486,16 +8576,16 @@ while (1) compile_backtrackingpath(common, altbacktrack.top); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) { - if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + if (local_quit_available) { - common->local_exit = save_local_exit; + common->local_quit_available = save_local_quit_available; common->quit_label = save_quit_label; common->quit = save_quit; } - common->positive_assert = save_positive_assert; + common->in_positive_assertion = save_in_positive_assertion; common->then_trap = save_then_trap; common->accept_label = save_accept_label; - common->positive_assert_quit = save_positive_assert_quit; + common->positive_assertion_quit = save_positive_assertion_quit; common->accept = save_accept; return NULL; } @@ -7508,26 +8598,26 @@ while (1) cc += GET(cc, 1); } -if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) +if (local_quit_available) { - SLJIT_ASSERT(common->positive_assert_quit == NULL); + SLJIT_ASSERT(common->positive_assertion_quit == NULL); /* Makes the check less complicated below. */ - common->positive_assert_quit = common->quit; + common->positive_assertion_quit = common->quit; } /* None of them matched. */ -if (common->positive_assert_quit != NULL) +if (common->positive_assertion_quit != NULL) { jump = JUMP(SLJIT_JUMP); - set_jumps(common->positive_assert_quit, LABEL()); + set_jumps(common->positive_assertion_quit, LABEL()); SLJIT_ASSERT(framesize != no_stack); if (framesize < 0) - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw)); else { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (extrasize + 1) * sizeof(sljit_sw)); } JUMPHERE(jump); } @@ -7576,18 +8666,18 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) { /* We know that STR_PTR was stored on the top of the stack. */ if (extrasize > 0) - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize)); /* Keep the STR_PTR on the top of the stack. */ if (bra == OP_BRAZERO) { - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); if (extrasize == 2) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); } else if (bra == OP_BRAMINZERO) { - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); } } @@ -7596,13 +8686,13 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) if (bra == OP_BRA) { /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (extrasize - 2) * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize + 1)); } else { /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */ - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw)); if (extrasize == 2) { OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); @@ -7630,7 +8720,9 @@ if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0); } set_jumps(backtrack->common.topbacktracks, LABEL()); } @@ -7683,16 +8775,16 @@ else } } -if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) +if (local_quit_available) { - common->local_exit = save_local_exit; + common->local_quit_available = save_local_quit_available; common->quit_label = save_quit_label; common->quit = save_quit; } -common->positive_assert = save_positive_assert; +common->in_positive_assertion = save_in_positive_assertion; common->then_trap = save_then_trap; common->accept_label = save_accept_label; -common->positive_assert_quit = save_positive_assert_quit; +common->positive_assertion_quit = save_positive_assertion_quit; common->accept = save_accept; return cc + 1 + LINK_SIZE; } @@ -7717,23 +8809,23 @@ if (framesize < 0) } if (needs_control_head) - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? sizeof(sljit_sw) : 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? STACK(-2) : STACK(-1)); /* TMP2 which is set here used by OP_KETRMAX below. */ if (ket == OP_KETRMAX) - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(-1)); else if (ket == OP_KETRMIN) { /* Move the STR_PTR to the private_data_ptr. */ - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1)); } } else { stacksize = (ket != OP_KET || has_alternatives) ? 2 : 1; - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw)); if (needs_control_head) - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-1)); if (ket == OP_KETRMAX) { @@ -7820,7 +8912,6 @@ return stacksize; (|) OP_*BRA | OP_ALT ... M A (?()|) OP_*COND | OP_ALT M A (?>|) OP_ONCE | OP_ALT ... [stack trace] M A - (?>|) OP_ONCE_NC | OP_ALT ... [stack trace] M A Or nothing, if trace is unnecessary */ @@ -7888,8 +8979,6 @@ if (SLJIT_UNLIKELY(opcode == OP_COND || opcode == OP_SCOND)) if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN)) opcode = OP_SCOND; -if (SLJIT_UNLIKELY(opcode == OP_ONCE_NC)) - opcode = OP_ONCE; if (opcode == OP_CBRA || opcode == OP_SCBRA) { @@ -7966,7 +9055,7 @@ if (bra == OP_BRAMINZERO) { /* Except when the whole stack frame must be saved. */ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (BACKTRACK_AS(bracket_backtrack)->u.framesize + 1) * sizeof(sljit_sw)); + braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-BACKTRACK_AS(bracket_backtrack)->u.framesize - 2)); } JUMPHERE(skip); } @@ -8039,7 +9128,7 @@ if (opcode == OP_ONCE) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame) - OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw)); + OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0); } else if (ket == OP_KETRMAX || has_alternatives) @@ -8057,7 +9146,7 @@ if (opcode == OP_ONCE) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); + OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); stacksize = needs_control_head ? 1 : 0; if (ket != OP_KET || has_alternatives) @@ -8072,7 +9161,7 @@ if (opcode == OP_ONCE) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0); } - init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1, FALSE); + init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1); } } else if (opcode == OP_CBRA || opcode == OP_SCBRA) @@ -8129,13 +9218,13 @@ if (opcode == OP_COND || opcode == OP_SCOND) slot = common->name_table + GET2(matchingpath, 1) * common->name_entry_size; OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)); - OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0); slot += common->name_entry_size; i--; while (i-- > 0) { OP2(SLJIT_SUB, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0); - OP2(SLJIT_OR | SLJIT_SET_E, TMP2, 0, TMP2, 0, STR_PTR, 0); + OP2(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, TMP2, 0, STR_PTR, 0); slot += common->name_entry_size; } OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); @@ -8288,7 +9377,7 @@ if (ket == OP_KETRMAX) { if (has_alternatives) BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL(); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, rmax_label); /* Drop STR_PTR for greedy plus quantifier. */ if (opcode != OP_ONCE) @@ -8318,7 +9407,7 @@ if (ket == OP_KETRMAX) if (repeat_type == OP_EXACT) { count_match(common); - OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, rmax_label); } else if (repeat_type == OP_UPTO) @@ -8346,6 +9435,7 @@ if (bra == OP_BRAMINZERO) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (BACKTRACK_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw)); } else if (ket == OP_KETRMIN && opcode != OP_ONCE) free_stack(common, 1); @@ -8418,7 +9508,7 @@ switch(opcode) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -8496,7 +9586,7 @@ else OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); if (needs_control_head) OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); - OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, -STACK(stacksize - 1)); + OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); stack = 0; if (!zero) @@ -8515,7 +9605,7 @@ else stack++; } OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP1, 0); - init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize, FALSE); + init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize); stack -= 1 + (offset == 0); } @@ -8568,7 +9658,7 @@ while (*cc != OP_KETRPOS) { if (offset != 0) { - OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw)); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0); @@ -8579,10 +9669,10 @@ while (*cc != OP_KETRPOS) else { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - OP2(SLJIT_ADD, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw)); if (opcode == OP_SBRAPOS) - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw)); - OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw), STR_PTR, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2)); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(-framesize - 2), STR_PTR, 0); } /* Even if the match is empty, we need to reset the control head. */ @@ -8628,7 +9718,7 @@ while (*cc != OP_KETRPOS) else { OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2)); } } @@ -8645,7 +9735,7 @@ if (!zero) if (framesize < 0) add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0)); else /* TMP2 is set to [private_data_ptr] above. */ - add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), (stacksize - 1) * sizeof(sljit_sw), SLJIT_IMM, 0)); + add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), STACK(-stacksize), SLJIT_IMM, 0)); } /* None of them matched. */ @@ -8868,7 +9958,7 @@ if (exact > 1) OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact); label = LABEL(); compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE); - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } else @@ -8876,7 +9966,7 @@ if (exact > 1) OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact); label = LABEL(); compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE); - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } } @@ -8906,7 +9996,7 @@ switch(opcode) if (opcode == OP_UPTO) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0); - OP2(SLJIT_SUB | SLJIT_SET_E, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); jump = JUMP(SLJIT_ZERO); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0); } @@ -8968,7 +10058,7 @@ switch(opcode) label = LABEL(); if (opcode == OP_UPTO) { - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_ZERO)); } compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE); @@ -8988,7 +10078,7 @@ switch(opcode) OP1(SLJIT_MOV, base, offset1, STR_PTR, 0); if (opcode == OP_UPTO) { - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); add_jump(compiler, &no_match, JUMP(SLJIT_ZERO)); } @@ -9015,7 +10105,7 @@ switch(opcode) if (opcode == OP_UPTO) { - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } else @@ -9044,7 +10134,7 @@ switch(opcode) if (opcode == OP_UPTO) { - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); } else @@ -9070,7 +10160,7 @@ switch(opcode) compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE); if (opcode == OP_UPTO) { - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); } @@ -9157,7 +10247,7 @@ switch(opcode) label = LABEL(); compile_char1_matchingpath(common, type, cc, &no_match, TRUE); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0); - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); set_jumps(no_match, LABEL()); OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1); @@ -9168,7 +10258,7 @@ switch(opcode) label = LABEL(); detect_partial_match(common, &no_match); compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE); - OP2(SLJIT_SUB | SLJIT_SET_E, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1); JUMPTO(SLJIT_NOT_ZERO, label); OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); set_jumps(no_char1_match, LABEL()); @@ -9186,7 +10276,7 @@ switch(opcode) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -9207,6 +10297,9 @@ if (*cc == OP_FAIL) return cc + 1; } +if (*cc == OP_ACCEPT && common->currententry == NULL && (common->re->overall_options & PCRE2_ENDANCHORED) != 0) + add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0)); + if (*cc == OP_ASSERT_ACCEPT || common->currententry != NULL || !common->might_be_empty) { /* No need to check notempty conditions. */ @@ -9223,9 +10316,9 @@ else CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), common->accept_label); OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options)); -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY); add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_NOT_ZERO)); -OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART); +OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART); if (common->accept_label == NULL) add_jump(compiler, &common->accept, JUMP(SLJIT_ZERO)); else @@ -9265,7 +10358,8 @@ backtrack_common *backtrack; PCRE2_UCHAR opcode = *cc; PCRE2_SPTR ccend = cc + 1; -if (opcode == OP_PRUNE_ARG || opcode == OP_SKIP_ARG || opcode == OP_THEN_ARG) +if (opcode == OP_COMMIT_ARG || opcode == OP_PRUNE_ARG || + opcode == OP_SKIP_ARG || opcode == OP_THEN_ARG) ccend += 2 + cc[1]; PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL); @@ -9277,7 +10371,7 @@ if (opcode == OP_SKIP) return ccend; } -if (opcode == OP_PRUNE_ARG || opcode == OP_THEN_ARG) +if (opcode == OP_COMMIT_ARG || opcode == OP_PRUNE_ARG || opcode == OP_THEN_ARG) { OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2)); @@ -9309,7 +10403,7 @@ size = 3 + (size < 0 ? 0 : size); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); allocate_stack(common, size); if (size > 3) - OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw)); + OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw)); else OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 1), SLJIT_IMM, BACKTRACK_AS(then_trap_backtrack)->start); @@ -9318,7 +10412,7 @@ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 3), TMP2, 0); size = BACKTRACK_AS(then_trap_backtrack)->framesize; if (size >= 0) - init_frame(common, cc, ccend, size - 1, 0, FALSE); + init_frame(common, cc, ccend, size - 1, 0); } static void compile_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, backtrack_common *parent) @@ -9540,7 +10634,6 @@ while (cc < ccend) break; case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_CBRA: case OP_COND: @@ -9597,6 +10690,7 @@ while (cc < ccend) case OP_THEN: case OP_THEN_ARG: case OP_COMMIT: + case OP_COMMIT_ARG: cc = compile_control_verb_matchingpath(common, cc, parent); break; @@ -9615,7 +10709,7 @@ while (cc < ccend) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return; } if (cc == NULL) @@ -9723,7 +10817,7 @@ switch(opcode) case OP_MINUPTO: OP1(SLJIT_MOV, TMP1, 0, base, offset1); OP1(SLJIT_MOV, STR_PTR, 0, base, offset0); - OP2(SLJIT_SUB | SLJIT_SET_E, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); add_jump(compiler, &jumplist, JUMP(SLJIT_ZERO)); OP1(SLJIT_MOV, base, offset1, TMP1, 0); @@ -9769,7 +10863,7 @@ switch(opcode) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -9804,27 +10898,21 @@ free_stack(common, ref ? 2 : 3); static SLJIT_INLINE void compile_recurse_backtrackingpath(compiler_common *common, struct backtrack_common *current) { DEFINE_COMPILER; +recurse_entry *entry; -if (CURRENT_AS(recurse_backtrack)->inlined_pattern) +if (!CURRENT_AS(recurse_backtrack)->inlined_pattern) + { + entry = CURRENT_AS(recurse_backtrack)->entry; + if (entry->backtrack_label == NULL) + add_jump(compiler, &entry->backtrack_calls, JUMP(SLJIT_FAST_CALL)); + else + JUMPTO(SLJIT_FAST_CALL, entry->backtrack_label); + CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, CURRENT_AS(recurse_backtrack)->matchingpath); + } +else compile_backtrackingpath(common, current->top); -set_jumps(current->topbacktracks, LABEL()); -if (CURRENT_AS(recurse_backtrack)->inlined_pattern) - return; -if (common->has_set_som && common->mark_ptr != 0) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); - free_stack(common, 2); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), TMP2, 0); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP1, 0); - } -else if (common->has_set_som || common->mark_ptr != 0) - { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); - free_stack(common, 1); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->has_set_som ? (int)(OVECTOR(0)) : common->mark_ptr, TMP2, 0); - } +set_jumps(current->topbacktracks, LABEL()); } static void compile_assert_backtrackingpath(compiler_common *common, struct backtrack_common *current) @@ -9877,7 +10965,9 @@ if (*cc == OP_ASSERT || *cc == OP_ASSERTBACK) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(assert_backtrack)->framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(assert_backtrack)->framesize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, TMP1, 0); set_jumps(current->topbacktracks, LABEL()); } @@ -9887,7 +10977,7 @@ else if (bra == OP_BRAZERO) { /* We know there is enough place on the stack. */ - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw)); OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); JUMPTO(SLJIT_JUMP, CURRENT_AS(assert_backtrack)->matchingpath); JUMPHERE(brajump); @@ -9947,8 +11037,6 @@ if (opcode == OP_CBRA || opcode == OP_SCBRA) offset = (GET2(ccbegin, 1 + LINK_SIZE)) << 1; if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN)) opcode = OP_SCOND; -if (SLJIT_UNLIKELY(opcode == OP_ONCE_NC)) - opcode = OP_ONCE; alt_max = has_alternatives ? no_alternatives(ccbegin) : 0; @@ -10000,7 +11088,7 @@ else if (ket == OP_KETRMIN) else { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); - CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (CURRENT_AS(bracket_backtrack)->u.framesize + 1) * sizeof(sljit_sw), CURRENT_AS(bracket_backtrack)->recursive_matchingpath); + CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 2), CURRENT_AS(bracket_backtrack)->recursive_matchingpath); } /* Drop STR_PTR for non-greedy plus quantifier. */ if (opcode != OP_ONCE) @@ -10054,6 +11142,7 @@ if (SLJIT_UNLIKELY(opcode == OP_ONCE)) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw)); } once = JUMP(SLJIT_JUMP); } @@ -10106,7 +11195,9 @@ if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0); } cond = JUMP(SLJIT_JUMP); set_jumps(CURRENT_AS(bracket_backtrack)->u.assert->condfailed, LABEL()); @@ -10247,7 +11338,9 @@ if (has_alternatives) { OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0); } JUMPHERE(cond); } @@ -10302,7 +11395,7 @@ else if (opcode == OP_ONCE) JUMPHERE(once); /* Restore previous private_data_ptr */ if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0) - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracket_backtrack)->u.framesize * sizeof(sljit_sw)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 1)); else if (ket == OP_KETRMIN) { OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); @@ -10383,6 +11476,7 @@ if (CURRENT_AS(bracketpos_backtrack)->framesize < 0) OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr); add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); +OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracketpos_backtrack)->framesize - 1) * sizeof(sljit_sw)); if (current->topbacktracks) { @@ -10392,7 +11486,7 @@ if (current->topbacktracks) free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize); JUMPHERE(jump); } -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracketpos_backtrack)->framesize * sizeof(sljit_sw)); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracketpos_backtrack)->framesize - 1)); } static SLJIT_INLINE void compile_braminzero_backtrackingpath(compiler_common *common, struct backtrack_common *current) @@ -10438,22 +11532,23 @@ if (opcode == OP_THEN || opcode == OP_THEN_ARG) jump = JUMP(SLJIT_JUMP); loop = LABEL(); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), -(int)sizeof(sljit_sw)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); JUMPHERE(jump); - CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), -(int)(2 * sizeof(sljit_sw)), TMP1, 0, loop); - CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), -(int)(3 * sizeof(sljit_sw)), TMP2, 0, loop); + CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0, loop); + CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0, loop); add_jump(compiler, &common->then_trap->quit, JUMP(SLJIT_JUMP)); return; } - else if (common->positive_assert) + else if (!common->local_quit_available && common->in_positive_assertion) { - add_jump(compiler, &common->positive_assert_quit, JUMP(SLJIT_JUMP)); + add_jump(compiler, &common->positive_assertion_quit, JUMP(SLJIT_JUMP)); return; } } -if (common->local_exit) +if (common->local_quit_available) { + /* Abort match with a fail. */ if (common->quit_label == NULL) add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); else @@ -10463,15 +11558,13 @@ if (common->local_exit) if (opcode == OP_SKIP_ARG) { - SLJIT_ASSERT(common->control_head_ptr != 0); + SLJIT_ASSERT(common->control_head_ptr != 0 && TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STACK_TOP, 0); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, (sljit_sw)(current->cc + 2)); - sljit_emit_ijump(compiler, SLJIT_CALL2, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_search_mark)); - OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); + OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)(current->cc + 2)); + sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(do_search_mark)); - OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0); - add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, -1)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_R0, 0); + add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, SLJIT_R0, 0, SLJIT_IMM, 0)); return; } @@ -10504,7 +11597,10 @@ jump = JUMP(SLJIT_JUMP); set_jumps(CURRENT_AS(then_trap_backtrack)->quit, LABEL()); /* STACK_TOP is set by THEN. */ if (CURRENT_AS(then_trap_backtrack)->framesize >= 0) + { add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(then_trap_backtrack)->framesize - 1) * sizeof(sljit_sw)); + } OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); free_stack(common, 3); @@ -10621,7 +11717,6 @@ while (current) break; case OP_ONCE: - case OP_ONCE_NC: case OP_BRA: case OP_CBRA: case OP_COND: @@ -10670,7 +11765,8 @@ while (current) break; case OP_COMMIT: - if (!common->local_exit) + case OP_COMMIT_ARG: + if (!common->local_quit_available) OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); if (common->quit_label == NULL) add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); @@ -10692,7 +11788,7 @@ while (current) break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } current = current->prev; @@ -10707,38 +11803,52 @@ 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) - (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); -int alternativesize; -BOOL needs_frame; +BOOL has_quit; +BOOL has_accept; +int private_data_size = get_recurse_data_length(common, ccbegin, ccend, &needs_control_head, &has_quit, &has_accept); +int alt_count, alt_max, local_size; backtrack_common altbacktrack; -struct sljit_jump *jump; +jump_list *match = NULL; +sljit_uw *next_update_addr = NULL; +struct sljit_jump *alt1 = NULL; +struct sljit_jump *alt2 = NULL; +struct sljit_jump *accept_exit = NULL; +struct sljit_label *quit; /* Recurse captures then. */ common->then_trap = NULL; SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA || *cc == OP_CBRAPOS || *cc == OP_SCBRA || *cc == OP_SCBRAPOS); -needs_frame = framesize >= 0; -if (!needs_frame) - framesize = 0; -alternativesize = *(cc + GET(cc, 1)) == OP_ALT ? 1 : 0; -SLJIT_ASSERT(common->currententry->entry == NULL && common->recursive_head_ptr != 0); -common->currententry->entry = LABEL(); -set_jumps(common->currententry->calls, common->currententry->entry); +alt_max = no_alternatives(cc); +alt_count = 0; + +/* Matching path. */ +SLJIT_ASSERT(common->currententry->entry_label == NULL && common->recursive_head_ptr != 0); +common->currententry->entry_label = LABEL(); +set_jumps(common->currententry->entry_calls, common->currententry->entry_label); 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); + +local_size = (alt_max > 1) ? 2 : 1; + +/* (Reversed) stack layout: + [private data][return address][optional: str ptr] ... [optional: alternative index][recursive_head_ptr] */ + +allocate_stack(common, private_data_size + local_size); +/* Save return address. */ +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP2, 0); + +copy_recurse_data(common, ccbegin, ccend, recurse_copy_from_global, local_size, private_data_size + local_size, has_quit); + +/* This variable is saved and restored all time when we enter or exit from a recursive context. */ +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, STACK_TOP, 0); + if (needs_control_head) OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0); -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, STACK_TOP, 0); -if (needs_frame) - init_frame(common, cc, NULL, framesize + alternativesize - 1, alternativesize, TRUE); -if (alternativesize > 0) +if (alt_max > 1) OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); memset(&altbacktrack, 0, sizeof(backtrack_common)); @@ -10760,7 +11870,75 @@ while (1) if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return; - add_jump(compiler, &common->accept, JUMP(SLJIT_JUMP)); + allocate_stack(common, (alt_max > 1 || has_accept) ? 2 : 1); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); + + if (alt_max > 1 || has_accept) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_count); + + add_jump(compiler, &match, JUMP(SLJIT_JUMP)); + + if (alt_count == 0) + { + /* Backtracking path entry. */ + SLJIT_ASSERT(common->currententry->backtrack_label == NULL); + common->currententry->backtrack_label = LABEL(); + set_jumps(common->currententry->backtrack_calls, common->currententry->backtrack_label); + + sljit_emit_fast_enter(compiler, TMP1, 0); + + if (has_accept) + accept_exit = CMP(SLJIT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_max * sizeof (sljit_sw)); + + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + /* Save return address. */ + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(local_size - 1), TMP1, 0); + + copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, has_quit); + + if (alt_max > 1) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + free_stack(common, 2); + + if (alt_max > 4) + { + /* Table jump if alt_max is greater than 4. */ + next_update_addr = allocate_read_only_data(common, alt_max * sizeof(sljit_uw)); + if (SLJIT_UNLIKELY(next_update_addr == NULL)) + return; + sljit_emit_ijump(compiler, SLJIT_JUMP, SLJIT_MEM1(TMP1), (sljit_sw)next_update_addr); + add_label_addr(common, next_update_addr++); + } + else + { + if (alt_max == 4) + alt2 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_uw)); + alt1 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, sizeof(sljit_uw)); + } + } + else + free_stack(common, has_accept ? 2 : 1); + } + else if (alt_max > 4) + add_label_addr(common, next_update_addr++); + else + { + if (alt_count != 2 * sizeof(sljit_uw)) + { + JUMPHERE(alt1); + if (alt_max == 3 && alt_count == sizeof(sljit_uw)) + alt2 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_uw)); + } + else + { + JUMPHERE(alt2); + if (alt_max == 4) + alt1 = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_uw)); + } + } + + alt_count += sizeof(sljit_uw); compile_backtrackingpath(common, altbacktrack.top); if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) @@ -10774,55 +11952,65 @@ while (1) cc += GET(cc, 1); } -/* None of them matched. */ -OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0); -jump = JUMP(SLJIT_JUMP); +/* No alternative is matched. */ + +quit = LABEL(); + +copy_recurse_data(common, ccbegin, ccend, recurse_copy_private_to_global, local_size, private_data_size + local_size, has_quit); + +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1)); +free_stack(common, private_data_size + local_size); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); +sljit_emit_fast_return(compiler, TMP2, 0); if (common->quit != NULL) { + SLJIT_ASSERT(has_quit); + set_jumps(common->quit, LABEL()); OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); - if (needs_frame) - { - OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); - add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); - } - OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0); - common->quit = NULL; - add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP)); + copy_recurse_data(common, ccbegin, ccend, recurse_copy_shared_to_global, local_size, private_data_size + local_size, has_quit); + JUMPTO(SLJIT_JUMP, quit); } -set_jumps(common->accept, LABEL()); -OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); -if (needs_frame) +if (has_accept) { - OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); - add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); - OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_sw)); - } -OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 1); + JUMPHERE(accept_exit); + free_stack(common, 2); -JUMPHERE(jump); -if (common->quit != NULL) - set_jumps(common->quit, LABEL()); -copy_private_data(common, ccbegin, ccend, FALSE, private_data_size + framesize + alternativesize, framesize + alternativesize, needs_control_head); -free_stack(common, private_data_size + framesize + alternativesize); -if (needs_control_head) - { - OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), 2 * sizeof(sljit_sw)); - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_sw)); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, TMP1, 0); - OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP2, 0); + /* Save return address. */ + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP1, 0); + + copy_recurse_data(common, ccbegin, ccend, recurse_copy_kept_shared_to_global, local_size, private_data_size + local_size, has_quit); + + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1)); + free_stack(common, private_data_size + local_size); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); + sljit_emit_fast_return(compiler, TMP2, 0); } -else + +if (common->accept != NULL) { - OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_sw)); - OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); - OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, TMP2, 0); + SLJIT_ASSERT(has_accept); + + set_jumps(common->accept, LABEL()); + + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr); + OP1(SLJIT_MOV, TMP2, 0, STACK_TOP, 0); + + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_count); } -sljit_emit_fast_return(compiler, SLJIT_MEM1(STACK_TOP), 0); + +set_jumps(match, LABEL()); + +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + +copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, has_quit); + +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), STACK(local_size - 1)); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1); +sljit_emit_fast_return(compiler, TMP2, 0); } #undef COMPILE_BACKTRACKINGPATH @@ -10854,11 +12042,13 @@ struct sljit_jump *jump; struct sljit_jump *minlength_check_failed = NULL; struct sljit_jump *reqbyte_notfound = NULL; struct sljit_jump *empty_match = NULL; +struct sljit_jump *end_anchor_failed = NULL; SLJIT_ASSERT(tables); memset(&rootbacktrack, 0, sizeof(backtrack_common)); memset(common, 0, sizeof(compiler_common)); +common->re = re; common->name_table = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)); rootbacktrack.cc = common->name_table + re->name_count * re->name_entry_size; @@ -11045,7 +12235,7 @@ if (!compiler) common->compiler = compiler; /* Main pcre_jit_exec entry. */ -sljit_emit_enter(compiler, 0, 1, 5, 5, 0, 0, private_data_size); +sljit_emit_enter(compiler, 0, SLJIT_ARG1(SW), 5, 5, 0, 0, private_data_size); /* Register init. */ reset_ovector(common, (re->top_bracket + 1) * 2); @@ -11058,8 +12248,8 @@ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)) OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, end)); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); OP1(SLJIT_MOV_U32, 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)); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, end)); +OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, start)); OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH, TMP1, 0); @@ -11076,7 +12266,7 @@ 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); + mainloop_label = mainloop_entry(common); continue_match_label = LABEL(); /* Forward search if possible. */ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) @@ -11084,11 +12274,11 @@ if ((re->overall_options & PCRE2_ANCHORED) == 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); + fast_forward_first_char(common); else if ((re->flags & PCRE2_STARTLINE) != 0) fast_forward_newline(common); else if ((re->flags & PCRE2_FIRSTMAPSET) != 0) - fast_forward_start_bits(common, re->start_bitmap); + fast_forward_start_bits(common); } } else @@ -11135,6 +12325,9 @@ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) return PCRE2_ERROR_NOMEMORY; } +if ((re->overall_options & PCRE2_ENDANCHORED) != 0) + end_anchor_failed = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0); + if (common->might_be_empty) { empty_match = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)); @@ -11147,15 +12340,26 @@ if (common->accept != NULL) /* This means we have a match. Update the ovector. */ copy_ovector(common, re->top_bracket + 1); -common->quit_label = common->forced_quit_label = LABEL(); +common->quit_label = common->abort_label = LABEL(); if (common->quit != NULL) set_jumps(common->quit, common->quit_label); -if (common->forced_quit != NULL) - set_jumps(common->forced_quit, common->forced_quit_label); +if (common->abort != NULL) + set_jumps(common->abort, common->abort_label); if (minlength_check_failed != NULL) - SET_LABEL(minlength_check_failed, common->forced_quit_label); + SET_LABEL(minlength_check_failed, common->abort_label); sljit_emit_return(compiler, SLJIT_MOV, SLJIT_RETURN_REG, 0); +if (common->failed_match != NULL) + { + SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE); + set_jumps(common->failed_match, LABEL()); + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH); + JUMPTO(SLJIT_JUMP, common->abort_label); + } + +if ((re->overall_options & PCRE2_ENDANCHORED) != 0) + JUMPHERE(end_anchor_failed); + if (mode != PCRE2_JIT_COMPLETE) { common->partialmatchlabel = LABEL(); @@ -11236,9 +12440,9 @@ if (common->might_be_empty) JUMPHERE(empty_match); OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options)); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY); JUMPTO(SLJIT_NOT_ZERO, empty_match_backtrack_label); - OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART); + OP2(SLJIT_AND | SLJIT_SET_Z, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART); JUMPTO(SLJIT_ZERO, empty_match_found_label); OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, empty_match_found_label); @@ -11249,7 +12453,7 @@ 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; +common->local_quit_available = TRUE; quit_label = common->quit_label; while (common->currententry != NULL) { @@ -11266,7 +12470,7 @@ while (common->currententry != NULL) flush_stubs(common); common->currententry = common->currententry->next; } -common->local_exit = FALSE; +common->local_quit_available = FALSE; common->quit_label = quit_label; /* Allocating stack, returns with PCRE_ERROR_JIT_STACKLIMIT if fails. */ @@ -11274,20 +12478,23 @@ common->quit_label = quit_label; set_jumps(common->stackalloc, LABEL()); /* RETURN_ADDR is not a saved register. */ sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); -OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP2, 0); -OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); -OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); -OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top), STACK_TOP, 0); -OP2(SLJIT_ADD, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit), SLJIT_IMM, STACK_GROWTH_RATE); -sljit_emit_ijump(compiler, SLJIT_CALL2, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_stack_resize)); -jump = CMP(SLJIT_NOT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); -OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); -OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); -OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top)); -OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit)); -OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); -sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0); +SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1); + +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, STR_PTR, 0); +OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0); +OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_LIMIT, 0, SLJIT_IMM, STACK_GROWTH_RATE); +OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, stack)); +OP1(SLJIT_MOV, STACK_LIMIT, 0, TMP2, 0); + +sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_RET(SW) | SLJIT_ARG1(SW) | SLJIT_ARG2(SW), SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_stack_resize)); + +jump = CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); +OP1(SLJIT_MOV, TMP2, 0, STACK_LIMIT, 0); +OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_RETURN_REG, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0); +OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1); +sljit_emit_fast_return(compiler, TMP1, 0); /* Allocation failed. */ JUMPHERE(jump); diff --git a/pcre2-10.22/src/pcre2_jit_match.c b/pcre2-10.32/src/pcre2_jit_match.c similarity index 96% rename from pcre2-10.22/src/pcre2_jit_match.c rename to pcre2-10.32/src/pcre2_jit_match.c index a323971ff..5a66545ba 100644 --- a/pcre2-10.22/src/pcre2_jit_match.c +++ b/pcre2-10.32/src/pcre2_jit_match.c @@ -49,10 +49,10 @@ static SLJIT_NOINLINE int jit_machine_stack_exec(jit_arguments *arguments, jit_f sljit_u8 local_space[MACHINE_STACK_SIZE]; struct sljit_stack local_stack; -local_stack.top = (sljit_sw)&local_space; -local_stack.base = local_stack.top; -local_stack.limit = local_stack.base + MACHINE_STACK_SIZE; -local_stack.max_limit = local_stack.limit; +local_stack.min_start = local_space; +local_stack.start = local_space; +local_stack.end = local_space + MACHINE_STACK_SIZE; +local_stack.top = local_space + MACHINE_STACK_SIZE; arguments->stack = &local_stack; return executable_func(arguments); } @@ -118,7 +118,7 @@ if ((options & PCRE2_PARTIAL_HARD) != 0) else if ((options & PCRE2_PARTIAL_SOFT) != 0) index = 1; -if (functions->executable_funcs[index] == NULL) +if (functions == NULL || functions->executable_funcs[index] == NULL) return PCRE2_ERROR_JIT_BADOPTION; /* Sanity checks should be handled by pcre_exec. */ diff --git a/pcre2-10.22/src/pcre2_jit_misc.c b/pcre2-10.32/src/pcre2_jit_misc.c similarity index 100% rename from pcre2-10.22/src/pcre2_jit_misc.c rename to pcre2-10.32/src/pcre2_jit_misc.c diff --git a/pcre2-10.22/src/pcre2_jit_test.c b/pcre2-10.32/src/pcre2_jit_test.c similarity index 97% rename from pcre2-10.22/src/pcre2_jit_test.c rename to pcre2-10.32/src/pcre2_jit_test.c index 705ba181e..a28e9a0b1 100644 --- a/pcre2-10.22/src/pcre2_jit_test.c +++ b/pcre2-10.32/src/pcre2_jit_test.c @@ -179,10 +179,12 @@ static struct regression_test_case regression_test_cases[] = { { PCRE2_CASELESS, 0, 0, 0, "\xff#a", "\xff#\xff\xfe##\xff#A" }, { PCRE2_CASELESS, 0, 0, 0, "\xfe", "\xff\xfc#\xfe\xfe" }, { PCRE2_CASELESS, 0, 0, 0, "a1", "Aa1" }, +#ifndef NEVER_BACKSLASH_C { M, A, 0, 0, "\\Ca", "cda" }, { CM, A, 0, 0, "\\Ca", "CDA" }, { M, A, 0, 0 | F_NOMATCH, "\\Cx", "cda" }, { CM, A, 0, 0 | F_NOMATCH, "\\Cx", "CDA" }, +#endif { CMUP, A, 0, 0, "\xf0\x90\x90\x80\xf0\x90\x90\xa8", "\xf0\x90\x90\xa8\xf0\x90\x90\x80" }, { 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" }, @@ -258,6 +260,8 @@ static struct regression_test_case regression_test_cases[] = { { 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" }, + { U, A, 0, 0, "\xe1\x81\x80|\xe2\x82\x80|\xe4\x84\x80", "\xdf\xbf\xc2\x80\xe4\x84\x80" }, + { U, A, 0, 0, "(?:\xe1\x81\x80|\xe2\x82\x80|\xe4\x84\x80)#", "\xdf\xbf\xc2\x80#\xe4\x84\x80#" }, /* Greedy and non-greedy ? operators. */ { MU, A, 0, 0, "(?:a)?a", "laab" }, @@ -707,7 +711,7 @@ static struct regression_test_case regression_test_cases[] = { { MU, A, 0, 0, "(?1)(((a(*ACCEPT)))b)", "axaa" }, { MU, A, 0, 0, "(?1)(?(DEFINE) (((ac(*ACCEPT)))b) )", "akaac" }, { MU, A, 0, 0, "(a+)b(?1)b\\1", "abaaabaaaaa" }, - { MU, A, 0, 0 | F_NOMATCH, "(?(DEFINE)(aa|a))(?1)ab", "aab" }, + { MU, A, 0, 0, "(?(DEFINE)(aa|a))(?1)ab", "aab" }, { MU, A, 0, 0, "(?(DEFINE)(a\\Kb))(?1)+ababc", "abababxabababc" }, { MU, A, 0, 0, "(a\\Kb)(?1)+ababc", "abababxababababc" }, { MU, A, 0, 0 | F_NOMATCH, "(a\\Kb)(?1)+ababc", "abababxababababxc" }, @@ -724,6 +728,8 @@ static struct regression_test_case regression_test_cases[] = { { MU, A, 0, 0, "((?:(?(R)a|(?1))){3})", "XaaaaaaaaaX" }, { MU, A, 0, 0, "((?(R)a|(?1)){1,3})aaaaaa", "aaaaaaaaXaaaaaaaaa" }, { MU, A, 0, 0, "((?(R)a|(?1)){1,3}?)M", "aaaM" }, + { MU, A, 0, 0, "((.)(?:.|\\2(?1))){0}#(?1)#", "#aabbccdde# #aabbccddee#" }, + { MU, A, 0, 0, "((.)(?:\\2|\\2{4}b)){0}#(?:(?1))+#", "#aaaab# #aaaaab#" }, /* 16 bit specific tests. */ { CM, A, 0, 0 | F_FORCECONV, "\xc3\xa1", "\xc3\x81\xc3\xa1" }, @@ -842,13 +848,23 @@ static struct regression_test_case regression_test_cases[] = { { MU, A, 0, 0 | F_NOMATCH, "(?(?=a)a(*THEN)b|ad)", "ad" }, { MU, A, 0, 0, "(?!(?(?=a)ab|b(*THEN)d))bn|bnn", "bnn" }, + /* Recurse and control verbs. */ + { MU, A, 0, 0, "(a(*ACCEPT)b){0}a(?1)b", "aacaabb" }, + { MU, A, 0, 0, "((a)\\2(*ACCEPT)b){0}a(?1)b", "aaacaaabb" }, + { MU, A, 0, 0, "((ab|a(*ACCEPT)x)+|ababababax){0}_(?1)_", "_ababababax_ _ababababa_" }, + { MU, A, 0, 0, "((.)(?:A(*ACCEPT)|(?1)\\2)){0}_(?1)_", "_bcdaAdcb_bcdaAdcb_" }, + { MU, A, 0, 0, "((*MARK:m)(?:a|a(*COMMIT)b|aa)){0}_(?1)_", "_ab_" }, + { MU, A, 0, 0, "((*MARK:m)(?:a|a(*COMMIT)b|aa)){0}_(?1)_|(_aa_)", "_aa_" }, + { MU, A, 0, 0, "(a(*COMMIT)(?:b|bb)|c(*ACCEPT)d|dd){0}_(?1)+_", "_ax_ _cd_ _abbb_ _abcd_ _abbcdd_" }, + { MU, A, 0, 0, "((.)(?:.|(*COMMIT)\\2{3}(*ACCEPT).*|.*)){0}_(?1){0,4}_", "_aaaabbbbccccddd_ _aaaabbbbccccdddd_" }, + /* Deep recursion. */ { MU, A, 0, 0, "((((?:(?:(?:\\w)+)?)*|(?>\\w)+?)+|(?>\\w)?\?)*)?\\s", "aaaaa+ " }, { MU, A, 0, 0, "(?:((?:(?:(?:\\w*?)+)??|(?>\\w)?|\\w*+)*)+)+?\\s", "aa+ " }, { MU, A, 0, 0, "((a?)+)+b", "aaaaaaaaaaaa b" }, /* Deep recursion: Stack limit reached. */ - { M, A, 0, 0 | F_NOMATCH, "a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaa" }, + { M, A, 0, 0 | F_NOMATCH, "a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaa" }, { M, A, 0, 0 | F_NOMATCH, "(?:a+)+b", "aaaaaaaaaaaaaaaaaaaaaaaa b" }, { M, A, 0, 0 | F_NOMATCH, "(?:a+?)+?b", "aaaaaaaaaaaaaaaaaaaaaaaa b" }, { M, A, 0, 0 | F_NOMATCH, "(?:a*)*b", "aaaaaaaaaaaaaaaaaaaaaaaa b" }, @@ -1309,14 +1325,15 @@ static int regression_tests(void) } else { ovector8_1 = pcre2_get_ovector_pointer_8(mdata8_1); ovector8_2 = pcre2_get_ovector_pointer_8(mdata8_2); - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector8_1[i] = -2; - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector8_2[i] = -2; } if (re8) { + (void)pcre2_set_match_limit_8(mcontext8, 10000000); return_value8[1] = pcre2_match_8(re8, (PCRE2_SPTR8)current->input, strlen(current->input), - current->start_offset & OFFSET_MASK, current->match_options, mdata8_2, NULL); + current->start_offset & OFFSET_MASK, current->match_options, mdata8_2, mcontext8); if (pcre2_jit_compile_8(re8, jit_compile_mode)) { printf("\n8 bit: JIT compiler does not support \"%s\"\n", current->pattern); @@ -1348,9 +1365,9 @@ static int regression_tests(void) } else { ovector16_1 = pcre2_get_ovector_pointer_16(mdata16_1); ovector16_2 = pcre2_get_ovector_pointer_16(mdata16_2); - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector16_1[i] = -2; - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector16_2[i] = -2; } if (re16) { @@ -1359,8 +1376,9 @@ static int regression_tests(void) else length16 = copy_char8_to_char16((PCRE2_SPTR8)current->input, regtest_buf16, REGTEST_MAX_LENGTH16); + (void)pcre2_set_match_limit_16(mcontext16, 10000000); return_value16[1] = pcre2_match_16(re16, regtest_buf16, length16, - current->start_offset & OFFSET_MASK, current->match_options, mdata16_2, NULL); + current->start_offset & OFFSET_MASK, current->match_options, mdata16_2, mcontext16); if (pcre2_jit_compile_16(re16, jit_compile_mode)) { printf("\n16 bit: JIT compiler does not support \"%s\"\n", current->pattern); @@ -1392,9 +1410,9 @@ static int regression_tests(void) } else { ovector32_1 = pcre2_get_ovector_pointer_32(mdata32_1); ovector32_2 = pcre2_get_ovector_pointer_32(mdata32_2); - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector32_1[i] = -2; - for (i = 0; i < OVECTOR_SIZE * 3; ++i) + for (i = 0; i < OVECTOR_SIZE * 2; ++i) ovector32_2[i] = -2; } if (re32) { @@ -1403,8 +1421,9 @@ static int regression_tests(void) else length32 = copy_char8_to_char32((PCRE2_SPTR8)current->input, regtest_buf32, REGTEST_MAX_LENGTH32); + (void)pcre2_set_match_limit_32(mcontext32, 10000000); return_value32[1] = pcre2_match_32(re32, regtest_buf32, length32, - current->start_offset & OFFSET_MASK, current->match_options, mdata32_2, NULL); + current->start_offset & OFFSET_MASK, current->match_options, mdata32_2, mcontext32); if (pcre2_jit_compile_32(re32, jit_compile_mode)) { printf("\n32 bit: JIT compiler does not support \"%s\"\n", current->pattern); diff --git a/pcre2-10.22/src/pcre2_maketables.c b/pcre2-10.32/src/pcre2_maketables.c similarity index 93% rename from pcre2-10.22/src/pcre2_maketables.c rename to pcre2-10.32/src/pcre2_maketables.c index 2c7ae84d8..537edba8c 100644 --- a/pcre2-10.22/src/pcre2_maketables.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -141,13 +141,6 @@ for (i = 0; i < 256; i++) if (isdigit(i)) x += ctype_digit; if (isxdigit(i)) x += ctype_xdigit; if (isalnum(i) || i == '_') x += ctype_word; - - /* Note: strchr includes the terminating zero in the characters it considers. - In this instance, that is ok because we want binary zero to be flagged as a - meta-character, which in this sense is any character that terminates a run - of data characters. */ - - if (strchr("\\*+?{^.$|()[", i) != 0) x += ctype_meta; *p++ = x; } diff --git a/pcre2-10.32/src/pcre2_match.c b/pcre2-10.32/src/pcre2_match.c new file mode 100644 index 000000000..8741e1432 --- /dev/null +++ b/pcre2-10.32/src/pcre2_match.c @@ -0,0 +1,6860 @@ +/************************************************* +* 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) 2015-2018 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 + +/* These defines enable debugging code */ + +/* #define DEBUG_FRAMES_DISPLAY */ +/* #define DEBUG_SHOW_OPS */ +/* #define DEBUG_SHOW_RMATCH */ + +#ifdef DEBUG_FRAME_DISPLAY +#include +#endif + +/* These defines identify the name of the block containing "static" +information, and fields within it. */ + +#define NLBLOCK mb /* Block containing newline information */ +#define PSSTART start_subject /* Field containing processed string start */ +#define PSEND end_subject /* Field containing processed string end */ + +#include "pcre2_internal.h" + +#define RECURSE_UNSET 0xffffffffu /* Bigger than max group number */ + +/* Masks for identifying the public options that are permitted at match time. */ + +#define PUBLIC_MATCH_OPTIONS \ + (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \ + PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \ + PCRE2_PARTIAL_SOFT|PCRE2_NO_JIT) + +#define PUBLIC_JIT_MATCH_OPTIONS \ + (PCRE2_NO_UTF_CHECK|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY|\ + PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_SOFT|PCRE2_PARTIAL_HARD) + +/* Non-error returns from and within the match() function. Error returns are +externally defined PCRE2_ERROR_xxx codes, which are all negative. */ + +#define MATCH_MATCH 1 +#define MATCH_NOMATCH 0 + +/* Special internal returns used in the match() function. Make them +sufficiently negative to avoid the external error codes. */ + +#define MATCH_ACCEPT (-999) +#define MATCH_KETRPOS (-998) +/* The next 5 must be kept together and in sequence so that a test that checks +for any one of them can use a range. */ +#define MATCH_COMMIT (-997) +#define MATCH_PRUNE (-996) +#define MATCH_SKIP (-995) +#define MATCH_SKIP_ARG (-994) +#define MATCH_THEN (-993) +#define MATCH_BACKTRACK_MAX MATCH_THEN +#define MATCH_BACKTRACK_MIN MATCH_COMMIT + +/* Group frame type values. Zero means the frame is not a group frame. The +lower 16 bits are used for data (e.g. the capture number). Group frames are +used for most groups so that information about the start is easily available at +the end without having to scan back through intermediate frames (backtrack +points). */ + +#define GF_CAPTURE 0x00010000u +#define GF_NOCAPTURE 0x00020000u +#define GF_CONDASSERT 0x00030000u +#define GF_RECURSE 0x00040000u + +/* Masks for the identity and data parts of the group frame type. */ + +#define GF_IDMASK(a) ((a) & 0xffff0000u) +#define GF_DATAMASK(a) ((a) & 0x0000ffffu) + +/* Repetition types */ + +enum { REPTYPE_MIN, REPTYPE_MAX, REPTYPE_POS }; + +/* Min and max values for the common repeats; a maximum of UINT32_MAX => +infinity. */ + +static const uint32_t rep_min[] = { + 0, 0, /* * and *? */ + 1, 1, /* + and +? */ + 0, 0, /* ? and ?? */ + 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */ + 0, 1, 0 }; /* OP_CRPOS{STAR, PLUS, QUERY} */ + +static const uint32_t rep_max[] = { + UINT32_MAX, UINT32_MAX, /* * and *? */ + UINT32_MAX, UINT32_MAX, /* + and +? */ + 1, 1, /* ? and ?? */ + 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */ + UINT32_MAX, UINT32_MAX, 1 }; /* OP_CRPOS{STAR, PLUS, QUERY} */ + +/* Repetition types - must include OP_CRPOSRANGE (not needed above) */ + +static const uint32_t rep_typ[] = { + REPTYPE_MAX, REPTYPE_MIN, /* * and *? */ + REPTYPE_MAX, REPTYPE_MIN, /* + and +? */ + REPTYPE_MAX, REPTYPE_MIN, /* ? and ?? */ + REPTYPE_MAX, REPTYPE_MIN, /* OP_CRRANGE and OP_CRMINRANGE */ + REPTYPE_POS, REPTYPE_POS, /* OP_CRPOSSTAR, OP_CRPOSPLUS */ + REPTYPE_POS, REPTYPE_POS }; /* OP_CRPOSQUERY, OP_CRPOSRANGE */ + +/* Numbers for RMATCH calls at backtracking points. When these lists are +changed, the code at RETURN_SWITCH below must be updated in sync. */ + +enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10, + RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20, + RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30, + RM31, RM32, RM33, RM34, RM35, RM36 }; + +#ifdef SUPPORT_WIDE_CHARS +enum { RM100=100, RM101 }; +#endif + +#ifdef SUPPORT_UNICODE +enum { RM200=200, RM201, RM202, RM203, RM204, RM205, RM206, RM207, + RM208, RM209, RM210, RM211, RM212, RM213, RM214, RM215, + RM216, RM217, RM218, RM219, RM220, RM221, RM222 }; +#endif + +/* Define short names for general fields in the current backtrack frame, which +is always pointed to by the F variable. Occasional references to fields in +other frames are written out explicitly. There are also some fields in the +current frame whose names start with "temp" that are used for short-term, +localised backtracking memory. These are #defined with Lxxx names at the point +of use and undefined afterwards. */ + +#define Fback_frame F->back_frame +#define Fcapture_last F->capture_last +#define Fcurrent_recurse F->current_recurse +#define Fecode F->ecode +#define Feptr F->eptr +#define Fgroup_frame_type F->group_frame_type +#define Flast_group_offset F->last_group_offset +#define Flength F->length +#define Fmark F->mark +#define Frdepth F->rdepth +#define Fstart_match F->start_match +#define Foffset_top F->offset_top +#define Foccu F->occu +#define Fop F->op +#define Fovector F->ovector +#define Freturn_id F->return_id + + +#ifdef DEBUG_FRAMES_DISPLAY +/************************************************* +* Display current frames and contents * +*************************************************/ + +/* This debugging function displays the current set of frames and their +contents. It is not called automatically from anywhere, the intention being +that calls can be inserted where necessary when debugging frame-related +problems. + +Arguments: + f the file to write to + F the current top frame + P a previous frame of interest + frame_size the frame size + mb points to the match block + s identification text + +Returns: nothing +*/ + +static void +display_frames(FILE *f, heapframe *F, heapframe *P, PCRE2_SIZE frame_size, + match_block *mb, const char *s, ...) +{ +uint32_t i; +heapframe *Q; +va_list ap; +va_start(ap, s); + +fprintf(f, "FRAMES "); +vfprintf(f, s, ap); +va_end(ap); + +if (P != NULL) fprintf(f, " P=%lu", + ((char *)P - (char *)(mb->match_frames))/frame_size); +fprintf(f, "\n"); + +for (i = 0, Q = mb->match_frames; + Q <= F; + i++, Q = (heapframe *)((char *)Q + frame_size)) + { + fprintf(f, "Frame %d type=%x subj=%lu code=%d back=%lu id=%d", + i, Q->group_frame_type, Q->eptr - mb->start_subject, *(Q->ecode), + Q->back_frame, Q->return_id); + + if (Q->last_group_offset == PCRE2_UNSET) + fprintf(f, " lgoffset=unset\n"); + else + fprintf(f, " lgoffset=%lu\n", Q->last_group_offset/frame_size); + } +} + +#endif + + + +/************************************************* +* Process a callout * +*************************************************/ + +/* This function is called for all callouts, whether "standalone" or at the +start of a conditional group. Feptr will be pointing to either OP_CALLOUT or +OP_CALLOUT_STR. A callout block is allocated in pcre2_match() and initialized +with fixed values. + +Arguments: + F points to the current backtracking frame + mb points to the match block + lengthptr where to return the length of the callout item + +Returns: the return from the callout + or 0 if no callout function exists +*/ + +static int +do_callout(heapframe *F, match_block *mb, PCRE2_SIZE *lengthptr) +{ +int rc; +PCRE2_SIZE save0, save1; +PCRE2_SIZE *callout_ovector; +pcre2_callout_block *cb; + +*lengthptr = (*Fecode == OP_CALLOUT)? + PRIV(OP_lengths)[OP_CALLOUT] : GET(Fecode, 1 + 2*LINK_SIZE); + +if (mb->callout == NULL) return 0; /* No callout function provided */ + +/* The original matching code (pre 10.30) worked directly with the ovector +passed by the user, and this was passed to callouts. Now that the working +ovector is in the backtracking frame, it no longer needs to reserve space for +the overall match offsets (which would waste space in the frame). For backward +compatibility, however, we pass capture_top and offset_vector to the callout as +if for the extended ovector, and we ensure that the first two slots are unset +by preserving and restoring their current contents. Picky compilers complain if +references such as Fovector[-2] are use directly, so we set up a separate +pointer. */ + +callout_ovector = (PCRE2_SIZE *)(Fovector) - 2; + +/* The cb->version, cb->subject, cb->subject_length, and cb->start_match fields +are set externally. The first 3 never change; the last is updated for each +bumpalong. */ + +cb = mb->cb; +cb->capture_top = (uint32_t)Foffset_top/2 + 1; +cb->capture_last = Fcapture_last; +cb->offset_vector = callout_ovector; +cb->mark = mb->nomatch_mark; +cb->current_position = (PCRE2_SIZE)(Feptr - mb->start_subject); +cb->pattern_position = GET(Fecode, 1); +cb->next_item_length = GET(Fecode, 1 + LINK_SIZE); + +if (*Fecode == OP_CALLOUT) /* Numerical callout */ + { + cb->callout_number = Fecode[1 + 2*LINK_SIZE]; + cb->callout_string_offset = 0; + cb->callout_string = NULL; + cb->callout_string_length = 0; + } +else /* String callout */ + { + cb->callout_number = 0; + cb->callout_string_offset = GET(Fecode, 1 + 3*LINK_SIZE); + cb->callout_string = Fecode + (1 + 4*LINK_SIZE) + 1; + cb->callout_string_length = + *lengthptr - (1 + 4*LINK_SIZE) - 2; + } + +save0 = callout_ovector[0]; +save1 = callout_ovector[1]; +callout_ovector[0] = callout_ovector[1] = PCRE2_UNSET; +rc = mb->callout(cb, mb->callout_data); +callout_ovector[0] = save0; +callout_ovector[1] = save1; +cb->callout_flags = 0; +return rc; +} + + + +/************************************************* +* Match a back-reference * +*************************************************/ + +/* This function is called only when it is known that the offset lies within +the offsets that have so far been used in the match. Note that in caseless +UTF-8 mode, the number of subject bytes matched may be different to the number +of reference bytes. (In theory this could also happen in UTF-16 mode, but it +seems unlikely.) + +Arguments: + offset index into the offset vector + caseless TRUE if caseless + F the current backtracking frame pointer + mb points to match block + lengthptr pointer for returning the length matched + +Returns: = 0 sucessful match; number of code units matched is set + < 0 no match + > 0 partial match +*/ + +static int +match_ref(PCRE2_SIZE offset, BOOL caseless, heapframe *F, match_block *mb, + PCRE2_SIZE *lengthptr) +{ +PCRE2_SPTR p; +PCRE2_SIZE length; +PCRE2_SPTR eptr; +PCRE2_SPTR eptr_start; + +/* Deal with an unset group. The default is no match, but there is an option to +match an empty string. */ + +if (offset >= Foffset_top || Fovector[offset] == PCRE2_UNSET) + { + if ((mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0) + { + *lengthptr = 0; + return 0; /* Match */ + } + else return -1; /* No match */ + } + +/* Separate the caseless and UTF cases for speed. */ + +eptr = eptr_start = Feptr; +p = mb->start_subject + Fovector[offset]; +length = Fovector[offset+1] - Fovector[offset]; + +if (caseless) + { +#if defined SUPPORT_UNICODE + if ((mb->poptions & PCRE2_UTF) != 0) + { + /* Match characters up to the end of the reference. NOTE: the number of + code units matched may differ, because in UTF-8 there are some characters + whose upper and lower case codes have different numbers of bytes. For + example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65 (3 + bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a + sequence of two of the latter. It is important, therefore, to check the + length along the reference, not along the subject (earlier code did this + wrong). */ + + PCRE2_SPTR endptr = p + length; + while (p < endptr) + { + uint32_t c, d; + const ucd_record *ur; + if (eptr >= mb->end_subject) return 1; /* Partial match */ + GETCHARINC(c, eptr); + GETCHARINC(d, p); + ur = GET_UCD(d); + if (c != d && c != (uint32_t)((int)d + ur->other_case)) + { + const uint32_t *pp = PRIV(ucd_caseless_sets) + ur->caseset; + for (;;) + { + if (c < *pp) return -1; /* No match */ + if (c == *pp++) break; + } + } + } + } + else +#endif + + /* Not in UTF mode */ + + { + for (; length > 0; length--) + { + uint32_t cc, cp; + if (eptr >= mb->end_subject) return 1; /* Partial match */ + cc = UCHAR21TEST(eptr); + cp = UCHAR21TEST(p); + if (TABLE_GET(cp, mb->lcc, cp) != TABLE_GET(cc, mb->lcc, cc)) + return -1; /* No match */ + p++; + eptr++; + } + } + } + +/* In the caseful case, we can just compare the code units, whether or not we +are in UTF mode. When partial matching, we have to do this unit-by-unit. */ + +else + { + if (mb->partial != 0) + { + for (; length > 0; length--) + { + if (eptr >= mb->end_subject) return 1; /* Partial match */ + if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1; /* No match */ + } + } + + /* Not partial matching */ + + else + { + if ((PCRE2_SIZE)(mb->end_subject - eptr) < length) return 1; /* Partial */ + if (memcmp(p, eptr, CU2BYTES(length)) != 0) return -1; /* No match */ + eptr += length; + } + } + +*lengthptr = eptr - eptr_start; +return 0; /* Match */ +} + + + +/****************************************************************************** +******************************************************************************* + "Recursion" in the match() function + +The original match() function was highly recursive, but this proved to be the +source of a number of problems over the years, mostly because of the relatively +small system stacks that are commonly found. As new features were added to +patterns, various kludges were invented to reduce the amount of stack used, +making the code hard to understand in places. + +A version did exist that used individual frames on the heap instead of calling +match() recursively, but this ran substantially slower. The current version is +a refactoring that uses a vector of frames to remember backtracking points. +This runs no slower, and possibly even a bit faster than the original recursive +implementation. An initial vector of size START_FRAMES_SIZE (enough for maybe +50 frames) is allocated on the system stack. If this is not big enough, the +heap is used for a larger vector. + +******************************************************************************* +******************************************************************************/ + + + + +/************************************************* +* Macros for the match() function * +*************************************************/ + +/* These macros pack up tests that are used for partial matching several times +in the code. We set the "hit end" flag if the pointer is at the end of the +subject and also past the earliest inspected character (i.e. something has been +matched, even if not part of the actual matched string). For hard partial +matching, we then return immediately. The second one is used when we already +know we are past the end of the subject. */ + +#define CHECK_PARTIAL()\ + if (mb->partial != 0 && Feptr >= mb->end_subject && \ + Feptr > mb->start_used_ptr) \ + { \ + mb->hitend = TRUE; \ + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; \ + } + +#define SCHECK_PARTIAL()\ + if (mb->partial != 0 && Feptr > mb->start_used_ptr) \ + { \ + mb->hitend = TRUE; \ + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; \ + } + +/* These macros are used to implement backtracking. They simulate a recursive +call to the match() function by means of a local vector of frames which +remember the backtracking points. */ + +#define RMATCH(ra,rb)\ + {\ + start_ecode = ra;\ + Freturn_id = rb;\ + goto MATCH_RECURSE;\ + L_##rb:;\ + } + +#define RRETURN(ra)\ + {\ + rrc = ra;\ + goto RETURN_SWITCH;\ + } + + + +/************************************************* +* Match from current position * +*************************************************/ + +/* This function is called to run one match attempt at a single starting point +in the subject. + +Performance note: It might be tempting to extract commonly used fields from the +mb structure (e.g. end_subject) into individual variables to improve +performance. Tests using gcc on a SPARC disproved this; in the first case, it +made performance worse. + +Arguments: + start_eptr starting character in subject + start_ecode starting position in compiled code + ovector pointer to the final output vector + oveccount number of pairs in ovector + top_bracket number of capturing parentheses in the pattern + frame_size size of each backtracking frame + mb pointer to "static" variables block + +Returns: MATCH_MATCH if matched ) these values are >= 0 + MATCH_NOMATCH if failed to match ) + negative MATCH_xxx value for PRUNE, SKIP, etc + negative PCRE2_ERROR_xxx value if aborted by an error condition + (e.g. stopped by repeated call or depth limit) +*/ + +static int +match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, PCRE2_SIZE *ovector, + uint16_t oveccount, uint16_t top_bracket, PCRE2_SIZE frame_size, + match_block *mb) +{ +/* Frame-handling variables */ + +heapframe *F; /* Current frame pointer */ +heapframe *N = NULL; /* Temporary frame pointers */ +heapframe *P = NULL; +heapframe *assert_accept_frame; /* For passing back the frame with captures */ +PCRE2_SIZE frame_copy_size; /* Amount to copy when creating a new frame */ + +/* Local variables that do not need to be preserved over calls to RRMATCH(). */ + +PCRE2_SPTR bracode; /* Temp pointer to start of group */ +PCRE2_SIZE offset; /* Used for group offsets */ +PCRE2_SIZE length; /* Used for various length calculations */ + +int rrc; /* Return from functions & backtracking "recursions" */ +#ifdef SUPPORT_UNICODE +int proptype; /* Type of character property */ +#endif + +uint32_t i; /* Used for local loops */ +uint32_t fc; /* Character values */ +uint32_t number; /* Used for group and other numbers */ +uint32_t reptype = 0; /* Type of repetition (0 to avoid compiler warning) */ +uint32_t group_frame_type; /* Specifies type for new group frames */ + +BOOL condition; /* Used in conditional groups */ +BOOL cur_is_word; /* Used in "word" tests */ +BOOL prev_is_word; /* Used in "word" tests */ + +/* UTF flag */ + +#ifdef SUPPORT_UNICODE +BOOL utf = (mb->poptions & PCRE2_UTF) != 0; +#else +BOOL utf = FALSE; +#endif + +/* This is the length of the last part of a backtracking frame that must be +copied when a new frame is created. */ + +frame_copy_size = frame_size - offsetof(heapframe, eptr); + +/* Set up the first current frame at the start of the vector, and initialize +fields that are not reset for new frames. */ + +F = mb->match_frames; +Frdepth = 0; /* "Recursion" depth */ +Fcapture_last = 0; /* Number of most recent capture */ +Fcurrent_recurse = RECURSE_UNSET; /* Not pattern recursing. */ +Fstart_match = Feptr = start_eptr; /* Current data pointer and start match */ +Fmark = NULL; /* Most recent mark */ +Foffset_top = 0; /* End of captures within the frame */ +Flast_group_offset = PCRE2_UNSET; /* Saved frame of most recent group */ +group_frame_type = 0; /* Not a start of group frame */ +goto NEW_FRAME; /* Start processing with this frame */ + +/* Come back here when we want to create a new frame for remembering a +backtracking point. */ + +MATCH_RECURSE: + +/* Set up a new backtracking frame. If the vector is full, get a new one +on the heap, doubling the size, but constrained by the heap limit. */ + +N = (heapframe *)((char *)F + frame_size); +if (N >= mb->match_frames_top) + { + PCRE2_SIZE newsize = mb->frame_vector_size * 2; + heapframe *new; + + if ((newsize / 1024) > mb->heap_limit) + { + PCRE2_SIZE maxsize = ((mb->heap_limit * 1024)/frame_size) * frame_size; + if (mb->frame_vector_size >= maxsize) return PCRE2_ERROR_HEAPLIMIT; + newsize = maxsize; + } + + new = mb->memctl.malloc(newsize, mb->memctl.memory_data); + if (new == NULL) return PCRE2_ERROR_NOMEMORY; + memcpy(new, mb->match_frames, mb->frame_vector_size); + + F = (heapframe *)((char *)new + ((char *)F - (char *)mb->match_frames)); + N = (heapframe *)((char *)F + frame_size); + + if (mb->match_frames != mb->stack_frames) + mb->memctl.free(mb->match_frames, mb->memctl.memory_data); + mb->match_frames = new; + mb->match_frames_top = (heapframe *)((char *)mb->match_frames + newsize); + mb->frame_vector_size = newsize; + } + +#ifdef DEBUG_SHOW_RMATCH +fprintf(stderr, "++ RMATCH %2d frame=%d", Freturn_id, Frdepth + 1); +if (group_frame_type != 0) + { + fprintf(stderr, " type=%x ", group_frame_type); + switch (GF_IDMASK(group_frame_type)) + { + case GF_CAPTURE: + fprintf(stderr, "capture=%d", GF_DATAMASK(group_frame_type)); + break; + + case GF_NOCAPTURE: + fprintf(stderr, "nocapture op=%d", GF_DATAMASK(group_frame_type)); + break; + + case GF_CONDASSERT: + fprintf(stderr, "condassert op=%d", GF_DATAMASK(group_frame_type)); + break; + + case GF_RECURSE: + fprintf(stderr, "recurse=%d", GF_DATAMASK(group_frame_type)); + break; + + default: + fprintf(stderr, "*** unknown ***"); + break; + } + } +fprintf(stderr, "\n"); +#endif + +/* Copy those fields that must be copied into the new frame, increase the +"recursion" depth (i.e. the new frame's index) and then make the new frame +current. */ + +memcpy((char *)N + offsetof(heapframe, eptr), + (char *)F + offsetof(heapframe, eptr), + frame_copy_size); + +N->rdepth = Frdepth + 1; +F = N; + +/* Carry on processing with a new frame. */ + +NEW_FRAME: +Fgroup_frame_type = group_frame_type; +Fecode = start_ecode; /* Starting code pointer */ +Fback_frame = frame_size; /* Default is go back one frame */ + +/* If this is a special type of group frame, remember its offset for quick +access at the end of the group. If this is a recursion, set a new current +recursion value. */ + +if (group_frame_type != 0) + { + Flast_group_offset = (char *)F - (char *)mb->match_frames; + if (GF_IDMASK(group_frame_type) == GF_RECURSE) + Fcurrent_recurse = GF_DATAMASK(group_frame_type); + group_frame_type = 0; + } + + +/* ========================================================================= */ +/* This is the main processing loop. First check that we haven't recorded too +many backtracks (search tree is too large), or that we haven't exceeded the +recursive depth limit (used too many backtracking frames). If not, process the +opcodes. */ + +if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT; +if (Frdepth >= mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT; + +for (;;) + { +#ifdef DEBUG_SHOW_OPS +fprintf(stderr, "++ op=%d\n", *Fecode); +#endif + + Fop = (uint8_t)(*Fecode); /* Cast needed for 16-bit and 32-bit modes */ + switch(Fop) + { + /* ===================================================================== */ + /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, to close + any currently open capturing brackets. Unlike reaching the end of a group, + where we know the starting frame is at the top of the chained frames, in + this case we have to search back for the relevant frame in case other types + of group that use chained frames have intervened. Multiple OP_CLOSEs always + come innermost first, which matches the chain order. We can ignore this in + a recursion, because captures are not passed out of recursions. */ + + case OP_CLOSE: + if (Fcurrent_recurse == RECURSE_UNSET) + { + number = GET2(Fecode, 1); + offset = Flast_group_offset; + for(;;) + { + if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL; + N = (heapframe *)((char *)mb->match_frames + offset); + P = (heapframe *)((char *)N - frame_size); + if (N->group_frame_type == (GF_CAPTURE | number)) break; + offset = P->last_group_offset; + } + offset = (number << 1) - 2; + Fcapture_last = number; + Fovector[offset] = P->eptr - mb->start_subject; + Fovector[offset+1] = Feptr - mb->start_subject; + if (offset >= Foffset_top) Foffset_top = offset + 2; + } + Fecode += PRIV(OP_lengths)[*Fecode]; + break; + + + /* ===================================================================== */ + /* Real or forced end of the pattern, assertion, or recursion. In an + assertion ACCEPT, update the last used pointer and remember the current + frame so that the captures and mark can be fished out of it. */ + + case OP_ASSERT_ACCEPT: + if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; + assert_accept_frame = F; + RRETURN(MATCH_ACCEPT); + + /* If recursing, we have to find the most recent recursion. */ + + case OP_ACCEPT: + case OP_END: + + /* Handle end of a recursion. */ + + if (Fcurrent_recurse != RECURSE_UNSET) + { + offset = Flast_group_offset; + for(;;) + { + if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL; + N = (heapframe *)((char *)mb->match_frames + offset); + P = (heapframe *)((char *)N - frame_size); + if (GF_IDMASK(N->group_frame_type) == GF_RECURSE) break; + offset = P->last_group_offset; + } + + /* N is now the frame of the recursion; the previous frame is at the + OP_RECURSE position. Go back there, copying the current subject position + and mark, and move on past the OP_RECURSE. */ + + P->eptr = Feptr; + P->mark = Fmark; + F = P; + Fecode += 1 + LINK_SIZE; + continue; + } + + /* Not a recursion. Fail for an empty string match if either PCRE2_NOTEMPTY + is set, or if PCRE2_NOTEMPTY_ATSTART is set and we have matched at the + start of the subject. In both cases, backtracking will then try other + alternatives, if any. */ + + if (Feptr == Fstart_match && + ((mb->moptions & PCRE2_NOTEMPTY) != 0 || + ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) != 0 && + Fstart_match == mb->start_subject + mb->start_offset))) + RRETURN(MATCH_NOMATCH); + + /* Also fail if PCRE2_ENDANCHORED is set and the end of the match is not + the end of the subject. After (*ACCEPT) we fail the entire match (at this + position) but backtrack on reaching the end of the pattern. */ + + if (Feptr < mb->end_subject && + ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0) + { + if (Fop == OP_END) RRETURN(MATCH_NOMATCH); + return MATCH_NOMATCH; + } + + /* We have a successful match of the whole pattern. Record the result and + then do a direct return from the function. If there is space in the offset + vector, set any pairs that follow the highest-numbered captured string but + are less than the number of capturing groups in the pattern to PCRE2_UNSET. + It is documented that this happens. "Gaps" are set to PCRE2_UNSET + dynamically. It is only those at the end that need setting here. */ + + mb->end_match_ptr = Feptr; /* Record where we ended */ + mb->end_offset_top = Foffset_top; /* and how many extracts were taken */ + mb->mark = Fmark; /* and the last success mark */ + if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; + + ovector[0] = Fstart_match - mb->start_subject; + ovector[1] = Feptr - mb->start_subject; + + /* Set i to the smaller of the sizes of the external and frame ovectors. */ + + i = 2 * ((top_bracket + 1 > oveccount)? oveccount : top_bracket + 1); + memcpy(ovector + 2, Fovector, (i - 2) * sizeof(PCRE2_SIZE)); + while (--i >= Foffset_top + 2) ovector[i] = PCRE2_UNSET; + return MATCH_MATCH; /* Note: NOT RRETURN */ + + + /*===================================================================== */ + /* Match any single character type except newline; have to take care with + CRLF newlines and partial matching. */ + + case OP_ANY: + if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); + if (mb->partial != 0 && + Feptr == mb->end_subject - 1 && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + UCHAR21TEST(Feptr) == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + /* Fall through */ + + /* Match any single character whatsoever. */ + + case OP_ALLANY: + if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */ + { /* not be updated before SCHECK_PARTIAL. */ + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + Feptr++; +#ifdef SUPPORT_UNICODE + if (utf) ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); +#endif + Fecode++; + break; + + + /* ===================================================================== */ + /* Match a single code unit, even in UTF 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 (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */ + { /* not be updated before SCHECK_PARTIAL. */ + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + Feptr++; + Fecode++; + break; + + + /* ===================================================================== */ + /* Match a single character, casefully */ + + case OP_CHAR: +#ifdef SUPPORT_UNICODE + if (utf) + { + Flength = 1; + Fecode++; + GETCHARLEN(fc, Fecode, Flength); + if (Flength > (PCRE2_SIZE)(mb->end_subject - Feptr)) + { + CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */ + RRETURN(MATCH_NOMATCH); + } + for (; Flength > 0; Flength--) + { + if (*Fecode++ != UCHAR21INC(Feptr)) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + if (mb->end_subject - Feptr < 1) + { + SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */ + RRETURN(MATCH_NOMATCH); + } + if (Fecode[1] != *Feptr++) RRETURN(MATCH_NOMATCH); + Fecode += 2; + } + break; + + + /* ===================================================================== */ + /* Match a single character, caselessly. If we are at the end of the + subject, give up immediately. We get here only when the pattern character + has at most one other case. Characters with more than two cases are coded + as OP_PROP with the pseudo-property PT_CLIST. */ + + case OP_CHARI: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + +#ifdef SUPPORT_UNICODE + if (utf) + { + Flength = 1; + Fecode++; + GETCHARLEN(fc, Fecode, Flength); + + /* If the pattern character's value is < 128, we know that its other case + (if any) is also < 128 (and therefore only one code unit long in all + code-unit widths), so we can use the fast lookup table. We checked above + that there is at least one character left in the subject. */ + + if (fc < 128) + { + uint32_t cc = UCHAR21(Feptr); + if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH); + Fecode++; + Feptr++; + } + + /* Otherwise we must pick up the subject character and use Unicode + property support to test its other case. Note that we cannot use the + value of "Flength" to check for sufficient bytes left, because the other + case of the character may have more or fewer code units. */ + + else + { + uint32_t dc; + GETCHARINC(dc, Feptr); + Fecode += Flength; + if (dc != fc && dc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH); + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* Not UTF mode; use the table for characters < 256. */ + { + if (TABLE_GET(Fecode[1], mb->lcc, Fecode[1]) + != TABLE_GET(*Feptr, mb->lcc, *Feptr)) RRETURN(MATCH_NOMATCH); + Feptr++; + Fecode += 2; + } + break; + + + /* ===================================================================== */ + /* Match not a single character. */ + + case OP_NOT: + case OP_NOTI: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t ch; + Fecode++; + GETCHARINC(ch, Fecode); + GETCHARINC(fc, Feptr); + if (ch == fc) + { + RRETURN(MATCH_NOMATCH); /* Caseful match */ + } + else if (Fop == OP_NOTI) /* If caseless */ + { + if (ch > 127) + ch = UCD_OTHERCASE(ch); + else + ch = TABLE_GET(ch, mb->fcc, ch); + if (ch == fc) RRETURN(MATCH_NOMATCH); + } + } + else +#endif /* SUPPORT_UNICODE */ + { + uint32_t ch = Fecode[1]; + fc = *Feptr++; + if (ch == fc || (Fop == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == fc)) + RRETURN(MATCH_NOMATCH); + Fecode += 2; + } + break; + + + /* ===================================================================== */ + /* Match a single character repeatedly. */ + +#define Loclength F->temp_size +#define Lstart_eptr F->temp_sptr[0] +#define Lcharptr F->temp_sptr[1] +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] +#define Lc F->temp_32[2] +#define Loc F->temp_32[3] + + case OP_EXACT: + case OP_EXACTI: + Lmin = Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATCHAR; + + case OP_POSUPTO: + case OP_POSUPTOI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATCHAR; + + case OP_UPTO: + case OP_UPTOI: + reptype = REPTYPE_MAX; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATCHAR; + + case OP_MINUPTO: + case OP_MINUPTOI: + reptype = REPTYPE_MIN; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATCHAR; + + case OP_POSSTAR: + case OP_POSSTARI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATCHAR; + + case OP_POSPLUS: + case OP_POSPLUSI: + reptype = REPTYPE_POS; + Lmin = 1; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATCHAR; + + case OP_POSQUERY: + case OP_POSQUERYI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = 1; + Fecode++; + goto REPEATCHAR; + + case OP_STAR: + case OP_STARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_PLUS: + case OP_PLUSI: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_QUERY: + case OP_QUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + fc = *Fecode++ - ((Fop < OP_STARI)? OP_STAR : OP_STARI); + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; + + /* Common code for all repeated single-character matches. We first check + for the minimum number of characters. If the minimum equals the maximum, we + are done. Otherwise, if minimizing, check the rest of the pattern for a + match; if there isn't one, advance up to the maximum, one character at a + time. + + If maximizing, advance up to the maximum number of matching characters, + until Feptr is past the end of the maximum run. If possessive, we are + then done (no backing up). Otherwise, match at this position; anything + other than no match is immediately returned. For nomatch, back up one + character, unless we are matching \R and the last thing matched was + \r\n, in which case, back up two code units until we reach the first + optional character position. + + The various UTF/non-UTF and caseful/caseless cases are handled separately, + for speed. */ + + REPEATCHAR: +#ifdef SUPPORT_UNICODE + if (utf) + { + Flength = 1; + Lcharptr = Fecode; + GETCHARLEN(fc, Fecode, Flength); + Fecode += Flength; + + /* Handle multi-code-unit character matching, caseful and caseless. */ + + if (Flength > 1) + { + uint32_t othercase; + + if (Fop >= OP_STARI && /* Caseless */ + (othercase = UCD_OTHERCASE(fc)) != fc) + Loclength = PRIV(ord2utf)(othercase, Foccu); + else Loclength = 0; + + for (i = 1; i <= Lmin; i++) + { + if (Feptr <= mb->end_subject - Flength && + memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength; + else if (Loclength > 0 && + Feptr <= mb->end_subject - Loclength && + memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0) + Feptr += Loclength; + else + { + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + } + + if (Lmin == Lmax) continue; + + if (reptype == REPTYPE_MIN) + { + for (;;) + { + RMATCH(Fecode, RM202); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr <= mb->end_subject - Flength && + memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength; + else if (Loclength > 0 && + Feptr <= mb->end_subject - Loclength && + memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0) + Feptr += Loclength; + else + { + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } + + else /* Maximize */ + { + Lstart_eptr = Feptr; + for (i = Lmin; i < Lmax; i++) + { + if (Feptr <= mb->end_subject - Flength && + memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) + Feptr += Flength; + else if (Loclength > 0 && + Feptr <= mb->end_subject - Loclength && + memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0) + Feptr += Loclength; + else + { + CHECK_PARTIAL(); + break; + } + } + + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ + + if (reptype != REPTYPE_POS) for(;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM203); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + BACKCHAR(Feptr); + } + } + break; /* End of repeated wide character handling */ + } + + /* Length of UTF character is 1. Put it into the preserved variable and + fall through to the non-UTF code. */ + + Lc = fc; + } + else +#endif /* SUPPORT_UNICODE */ + + /* When not in UTF mode, load a single-code-unit character. Then proceed as + above. */ + + Lc = *Fecode++; + + /* Caseless comparison */ + + if (Fop >= OP_STARI) + { +#if PCRE2_CODE_UNIT_WIDTH == 8 + /* Lc must be < 128 in UTF-8 mode. */ + Loc = mb->fcc[Lc]; +#else /* 16-bit & 32-bit */ +#ifdef SUPPORT_UNICODE + if (utf && Lc > 127) Loc = UCD_OTHERCASE(Lc); + else +#endif /* SUPPORT_UNICODE */ + Loc = TABLE_GET(Lc, mb->fcc, Lc); +#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */ + + for (i = 1; i <= Lmin; i++) + { + uint32_t cc; /* Faster than PCRE2_UCHAR */ + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21TEST(Feptr); + if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH); + Feptr++; + } + if (Lmin == Lmax) continue; + + if (reptype == REPTYPE_MIN) + { + for (;;) + { + uint32_t cc; /* Faster than PCRE2_UCHAR */ + RMATCH(Fecode, RM25); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21TEST(Feptr); + if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH); + Feptr++; + } + /* Control never gets here */ + } + + else /* Maximize */ + { + Lstart_eptr = Feptr; + for (i = Lmin; i < Lmax; i++) + { + uint32_t cc; /* Faster than PCRE2_UCHAR */ + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + cc = UCHAR21TEST(Feptr); + if (Lc != cc && Loc != cc) break; + Feptr++; + } + if (reptype != REPTYPE_POS) for (;;) + { + if (Feptr == Lstart_eptr) break; + RMATCH(Fecode, RM26); + Feptr--; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } + } + } + + /* Caseful comparisons (includes all multi-byte characters) */ + + else + { + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH); + } + + if (Lmin == Lmax) continue; + + if (reptype == REPTYPE_MIN) + { + for (;;) + { + RMATCH(Fecode, RM27); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + else /* Maximize */ + { + Lstart_eptr = Feptr; + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + + if (Lc != UCHAR21TEST(Feptr)) break; + Feptr++; + } + + if (reptype != REPTYPE_POS) for (;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM28); + Feptr--; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } + } + } + break; + +#undef Loclength +#undef Lstart_eptr +#undef Lcharptr +#undef Lmin +#undef Lmax +#undef Lc +#undef Loc + + + /* ===================================================================== */ + /* Match a negated single one-byte character repeatedly. This is almost a + repeat of the code for a repeated single character, but I haven't found a + nice way of commoning these up that doesn't require a test of the + positive/negative option for each character match. Maybe that wouldn't add + very much to the time taken, but character matching *is* what this is all + about... */ + +#define Lstart_eptr F->temp_sptr[0] +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] +#define Lc F->temp_32[2] +#define Loc F->temp_32[3] + + case OP_NOTEXACT: + case OP_NOTEXACTI: + Lmin = Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; + + case OP_NOTUPTO: + case OP_NOTUPTOI: + Lmin = 0; + Lmax = GET2(Fecode, 1); + reptype = REPTYPE_MAX; + Fecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; + + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + Lmin = 0; + Lmax = GET2(Fecode, 1); + reptype = REPTYPE_MIN; + Fecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; + + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATNOTCHAR; + + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + reptype = REPTYPE_POS; + Lmin = 1; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATNOTCHAR; + + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = 1; + Fecode++; + goto REPEATNOTCHAR; + + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; + + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + fc = *Fecode++ - ((Fop >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR); + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; + + /* Common code for all repeated single-character non-matches. */ + + REPEATNOTCHAR: + GETCHARINCTEST(Lc, Fecode); + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If Lmin = Lmax, we are done. + Otherwise, if minimizing, keep trying the rest of the expression and + advancing one matching character if failing, up to the maximum. + Alternatively, if maximizing, find the maximum number of characters and + work backwards. */ + + if (Fop >= OP_NOTSTARI) /* Caseless */ + { +#ifdef SUPPORT_UNICODE + if (utf && Lc > 127) + Loc = UCD_OTHERCASE(Lc); + else +#endif /* SUPPORT_UNICODE */ + + Loc = TABLE_GET(Lc, mb->fcc, Lc); /* Other case from table */ + +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t d; + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, Feptr); + if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* Not UTF mode */ + { + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH); + Feptr++; + } + } + + if (Lmin == Lmax) continue; /* Finished for exact count */ + + if (reptype == REPTYPE_MIN) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t d; + for (;;) + { + RMATCH(Fecode, RM204); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, Feptr); + if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif /*SUPPORT_UNICODE */ + + /* Not UTF mode */ + { + for (;;) + { + RMATCH(Fecode, RM29); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH); + Feptr++; + } + } + /* Control never gets here */ + } + + /* Maximize case */ + + else + { + Lstart_eptr = Feptr; + +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t d; + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(d, Feptr, len); + if (Lc == d || Loc == d) break; + Feptr += len; + } + + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ + + if (reptype != REPTYPE_POS) for(;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM205); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + BACKCHAR(Feptr); + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* Not UTF mode */ + { + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (Lc == *Feptr || Loc == *Feptr) break; + Feptr++; + } + if (reptype != REPTYPE_POS) for (;;) + { + if (Feptr == Lstart_eptr) break; + RMATCH(Fecode, RM30); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + } + } + } + } + + /* Caseful comparisons */ + + else + { +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t d; + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, Feptr); + if (Lc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH); + } + } + + if (Lmin == Lmax) continue; + + if (reptype == REPTYPE_MIN) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t d; + for (;;) + { + RMATCH(Fecode, RM206); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, Feptr); + if (Lc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (;;) + { + RMATCH(Fecode, RM31); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } + + /* Maximize case */ + + else + { + Lstart_eptr = Feptr; + +#ifdef SUPPORT_UNICODE + if (utf) + { + uint32_t d; + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(d, Feptr, len); + if (Lc == d) break; + Feptr += len; + } + + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ + + if (reptype != REPTYPE_POS) for(;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM207); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + BACKCHAR(Feptr); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (Lc == *Feptr) break; + Feptr++; + } + if (reptype != REPTYPE_POS) for (;;) + { + if (Feptr == Lstart_eptr) break; + RMATCH(Fecode, RM32); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + } + } + } + } + break; + +#undef Lstart_eptr +#undef Lmin +#undef Lmax +#undef Lc +#undef Loc + + + /* ===================================================================== */ + /* Match a bit-mapped character class, possibly repeatedly. These opcodes + are used when all the characters in the class have values in the range + 0-255, and either the matching is caseful, or the characters are in the + range 0-127 when UTF processing is enabled. The only difference between + OP_CLASS and OP_NCLASS occurs when a data character outside the range is + encountered. */ + +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] +#define Lstart_eptr F->temp_sptr[0] +#define Lbyte_map_address F->temp_sptr[1] +#define Lbyte_map ((unsigned char *)Lbyte_map_address) + + case OP_NCLASS: + case OP_CLASS: + { + Lbyte_map_address = Fecode + 1; /* Save for matching */ + Fecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */ + + /* Look past the end of the item to see if there is repeat information + following. Then obey similar code to character type repeats. */ + + switch (*Fecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + case OP_CRPOSSTAR: + case OP_CRPOSPLUS: + case OP_CRPOSQUERY: + fc = *Fecode++ - OP_CRSTAR; + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + case OP_CRPOSRANGE: + Lmin = GET2(Fecode, 1); + Lmax = GET2(Fecode, 1 + IMM2_SIZE); + if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */ + reptype = rep_typ[*Fecode - OP_CRSTAR]; + Fecode += 1 + 2 * IMM2_SIZE; + break; + + default: /* No repeat follows */ + Lmin = Lmax = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + +#ifdef SUPPORT_UNICODE + if (utf) + { + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + if (fc > 255) + { + if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + fc = *Feptr++; +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (fc > 255) + { + if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else +#endif + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + + /* If Lmax == Lmin we are done. Continue with main loop. */ + + if (Lmin == Lmax) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (reptype == REPTYPE_MIN) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + for (;;) + { + RMATCH(Fecode, RM200); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + if (fc > 255) + { + if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (;;) + { + RMATCH(Fecode, RM23); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + fc = *Feptr++; +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (fc > 255) + { + if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else +#endif + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + Lstart_eptr = Feptr; + +#ifdef SUPPORT_UNICODE + if (utf) + { + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc > 255) + { + if (Fop == OP_CLASS) break; + } + else + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) break; + Feptr += len; + } + + if (reptype == REPTYPE_POS) continue; /* No backtracking */ + + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ + + for (;;) + { + RMATCH(Fecode, RM201); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Feptr-- <= Lstart_eptr) break; /* Tried at original position */ + BACKCHAR(Feptr); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + fc = *Feptr; +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (fc > 255) + { + if (Fop == OP_CLASS) break; + } + else +#endif + if ((Lbyte_map[fc/8] & (1 << (fc&7))) == 0) break; + Feptr++; + } + + if (reptype == REPTYPE_POS) continue; /* No backtracking */ + + while (Feptr >= Lstart_eptr) + { + RMATCH(Fecode, RM24); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + } + } + + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + +#undef Lbyte_map_address +#undef Lbyte_map +#undef Lstart_eptr +#undef Lmin +#undef Lmax + + + /* ===================================================================== */ + /* Match an extended character class. In the 8-bit library, this opcode is + encountered only when UTF-8 mode mode is supported. In the 16-bit and + 32-bit libraries, codepoints greater than 255 may be encountered even when + UTF is not supported. */ + +#define Lstart_eptr F->temp_sptr[0] +#define Lxclass_data F->temp_sptr[1] +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] + +#ifdef SUPPORT_WIDE_CHARS + case OP_XCLASS: + { + Lxclass_data = Fecode + 1 + LINK_SIZE; /* Save for matching */ + Fecode += GET(Fecode, 1); /* Advance past the item */ + + switch (*Fecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + case OP_CRPOSSTAR: + case OP_CRPOSPLUS: + case OP_CRPOSQUERY: + fc = *Fecode++ - OP_CRSTAR; + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + case OP_CRPOSRANGE: + Lmin = GET2(Fecode, 1); + Lmax = GET2(Fecode, 1 + IMM2_SIZE); + if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */ + reptype = rep_typ[*Fecode - OP_CRSTAR]; + Fecode += 1 + 2 * IMM2_SIZE; + break; + + default: /* No repeat follows */ + Lmin = Lmax = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH); + } + + /* If Lmax == Lmin we can just continue with the main loop. */ + + if (Lmin == Lmax) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (reptype == REPTYPE_MIN) + { + for (;;) + { + RMATCH(Fecode, RM100); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + Lstart_eptr = Feptr; + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } +#ifdef SUPPORT_UNICODE + GETCHARLENTEST(fc, Feptr, len); +#else + fc = *Feptr; +#endif + if (!PRIV(xclass)(fc, Lxclass_data, utf)) break; + Feptr += len; + } + + if (reptype == REPTYPE_POS) continue; /* No backtracking */ + + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ + + for(;;) + { + RMATCH(Fecode, RM101); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Feptr-- <= Lstart_eptr) break; /* Tried at original position */ +#ifdef SUPPORT_UNICODE + if (utf) BACKCHAR(Feptr); +#endif + } + RRETURN(MATCH_NOMATCH); + } + + /* Control never gets here */ + } +#endif /* SUPPORT_WIDE_CHARS: end of XCLASS */ + +#undef Lstart_eptr +#undef Lxclass_data +#undef Lmin +#undef Lmax + + + /* ===================================================================== */ + /* Match various character types when PCRE2_UCP is not set. These opcodes + are not generated when PCRE2_UCP is set - instead appropriate property + tests are compiled. */ + + case OP_NOT_DIGIT: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + case OP_DIGIT: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + case OP_NOT_WHITESPACE: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + case OP_WHITESPACE: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + case OP_NOT_WORDCHAR: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + case OP_WORDCHAR: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + case OP_ANYNL: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); + + case CHAR_CR: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + } + else if (UCHAR21TEST(Feptr) == CHAR_LF) Feptr++; + break; + + case CHAR_LF: + break; + + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC + case 0x2028: + case 0x2029: +#endif /* Not EBCDIC */ + if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); + break; + } + Fecode++; + break; + + case OP_NOT_HSPACE: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + switch(fc) + { + HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */ + default: break; + } + Fecode++; + break; + + case OP_HSPACE: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + switch(fc) + { + HSPACE_CASES: break; /* Byte and multibyte cases */ + default: RRETURN(MATCH_NOMATCH); + } + Fecode++; + break; + + case OP_NOT_VSPACE: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + switch(fc) + { + VSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; + } + Fecode++; + break; + + case OP_VSPACE: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + switch(fc) + { + VSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); + } + Fecode++; + break; + + +#ifdef SUPPORT_UNICODE + + /* ===================================================================== */ + /* Check the next character by Unicode property. We will get here only + if the support is in the binary; otherwise a compile-time error occurs. */ + + case OP_PROP: + case OP_NOTPROP: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + { + const uint32_t *cp; + const ucd_record *prop = GET_UCD(fc); + + switch(Fecode[1]) + { + case PT_ANY: + if (Fop == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + + case PT_LAMP: + if ((prop->chartype == ucp_Lu || + prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt) == (Fop == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_GC: + if ((Fecode[2] != PRIV(ucp_gentype)[prop->chartype]) == (Fop == OP_PROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_PC: + if ((Fecode[2] != prop->chartype) == (Fop == OP_PROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_SC: + if ((Fecode[2] != prop->script) == (Fop == OP_PROP)) + RRETURN(MATCH_NOMATCH); + break; + + /* These are specials */ + + case PT_ALNUM: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (Fop == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + /* Perl space used to exclude VT, but from Perl 5.18 it is included, + which means that Perl space and POSIX space are now identical. PCRE + was changed at release 8.34. */ + + case PT_SPACE: /* Perl space */ + case PT_PXSPACE: /* POSIX space */ + switch(fc) + { + HSPACE_CASES: + VSPACE_CASES: + if (Fop == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + + default: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == + (Fop == OP_NOTPROP)) RRETURN(MATCH_NOMATCH); + break; + } + break; + + case PT_WORD: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + fc == CHAR_UNDERSCORE) == (Fop == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_CLIST: + cp = PRIV(ucd_caseless_sets) + Fecode[2]; + for (;;) + { + if (fc < *cp) + { if (Fop == OP_PROP) { RRETURN(MATCH_NOMATCH); } else break; } + if (fc == *cp++) + { if (Fop == OP_PROP) break; else { RRETURN(MATCH_NOMATCH); } } + } + break; + + case PT_UCNC: + if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || + fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || + fc >= 0xe000) == (Fop == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + /* This should never occur */ + + default: + return PCRE2_ERROR_INTERNAL; + } + + Fecode += 3; + } + break; + + + /* ===================================================================== */ + /* Match an extended Unicode sequence. We will get here only if the support + is in the binary; otherwise a compile-time error occurs. */ + + case OP_EXTUNI: + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + else + { + GETCHARINCTEST(fc, Feptr); + Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf, + NULL); + } + CHECK_PARTIAL(); + Fecode++; + break; + +#endif /* SUPPORT_UNICODE */ + + + /* ===================================================================== */ + /* Match a single character type repeatedly. Note that the property type + does not need to be in a stack frame as it is not used within an RMATCH() + loop. */ + +#define Lstart_eptr F->temp_sptr[0] +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] +#define Lctype F->temp_32[2] +#define Lpropvalue F->temp_32[3] + + case OP_TYPEEXACT: + Lmin = Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATTYPE; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + Lmin = 0; + Lmax = GET2(Fecode, 1); + reptype = (*Fecode == OP_TYPEMINUPTO)? REPTYPE_MIN : REPTYPE_MAX; + Fecode += 1 + IMM2_SIZE; + goto REPEATTYPE; + + case OP_TYPEPOSSTAR: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATTYPE; + + case OP_TYPEPOSPLUS: + reptype = REPTYPE_POS; + Lmin = 1; + Lmax = UINT32_MAX; + Fecode++; + goto REPEATTYPE; + + case OP_TYPEPOSQUERY: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = 1; + Fecode++; + goto REPEATTYPE; + + case OP_TYPEPOSUPTO: + reptype = REPTYPE_POS; + Lmin = 0; + Lmax = GET2(Fecode, 1); + Fecode += 1 + IMM2_SIZE; + goto REPEATTYPE; + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + fc = *Fecode++ - OP_TYPESTAR; + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; + + /* Common code for all repeated character type matches. */ + + REPEATTYPE: + Lctype = *Fecode++; /* Code for the character type */ + +#ifdef SUPPORT_UNICODE + if (Lctype == OP_PROP || Lctype == OP_NOTPROP) + { + proptype = *Fecode++; + Lpropvalue = *Fecode++; + } + else proptype = -1; +#endif + + /* First, ensure the minimum number of matches are present. Use inline + code for maximizing the speed, and do the type test once at the start + (i.e. keep it out of the loop). The code for UTF mode is separated out for + tidiness, except for Unicode property tests. */ + + if (Lmin > 0) + { +#ifdef SUPPORT_UNICODE + if (proptype >= 0) /* Property tests in all modes */ + { + switch(proptype) + { + case PT_ANY: + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + } + break; + + case PT_LAMP: + for (i = 1; i <= Lmin; i++) + { + int chartype; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + chartype = UCD_CHARTYPE(fc); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_GC: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_PC: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_SC: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_ALNUM: + for (i = 1; i <= Lmin; i++) + { + int category; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; + + /* Perl space used to exclude VT, but from Perl 5.18 it is included, + which means that Perl space and POSIX space are now identical. PCRE + was changed at release 8.34. */ + + case PT_SPACE: /* Perl space */ + case PT_PXSPACE: /* POSIX space */ + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + switch(fc) + { + HSPACE_CASES: + VSPACE_CASES: + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + + default: + if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + } + } + break; + + case PT_WORD: + for (i = 1; i <= Lmin; i++) + { + int category; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N || + fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_CLIST: + for (i = 1; i <= Lmin; i++) + { + const uint32_t *cp; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + cp = PRIV(ucd_caseless_sets) + Lpropvalue; + for (;;) + { + if (fc < *cp) + { + if (Lctype == OP_NOTPROP) break; + RRETURN(MATCH_NOMATCH); + } + if (fc == *cp++) + { + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + } + } + } + break; + + case PT_UCNC: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || + fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || + fc >= 0xe000) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + break; + + /* This should not occur */ + + default: + return PCRE2_ERROR_INTERNAL; + } + } + + /* Match extended Unicode sequences. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ + + else if (Lctype == OP_EXTUNI) + { + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + else + { + GETCHARINCTEST(fc, Feptr); + Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, + mb->end_subject, utf, NULL); + } + CHECK_PARTIAL(); + } + } + else +#endif /* SUPPORT_UNICODE */ + +/* Handle all other cases in UTF mode */ + +#ifdef SUPPORT_UNICODE + if (utf) switch(Lctype) + { + case OP_ANY: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); + if (mb->partial != 0 && + Feptr + 1 >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + UCHAR21(Feptr) == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); + } + break; + + case OP_ALLANY: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); + } + break; + + case OP_ANYBYTE: + if (Feptr > mb->end_subject - Lmin) RRETURN(MATCH_NOMATCH); + Feptr += Lmin; + break; + + case OP_ANYNL: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); + + case CHAR_CR: + if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++; + break; + + case CHAR_LF: + break; + + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC + case 0x2028: + case 0x2029: +#endif /* Not EBCDIC */ + if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); + break; + } + } + break; + + case OP_NOT_HSPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + switch(fc) + { + HSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; + } + } + break; + + case OP_HSPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + switch(fc) + { + HSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_NOT_VSPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + switch(fc) + { + VSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; + } + } + break; + + case OP_VSPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + switch(fc) + { + VSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(fc, Feptr); + if (fc < 128 && (mb->ctypes[fc] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_DIGIT: + for (i = 1; i <= Lmin; i++) + { + uint32_t cc; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21(Feptr); + if (cc >= 128 || (mb->ctypes[cc] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + /* No need to skip more code units - we know it has only one. */ + } + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= Lmin; i++) + { + uint32_t cc; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21(Feptr); + if (cc < 128 && (mb->ctypes[cc] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); + } + break; + + case OP_WHITESPACE: + for (i = 1; i <= Lmin; i++) + { + uint32_t cc; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21(Feptr); + if (cc >= 128 || (mb->ctypes[cc] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + /* No need to skip more code units - we know it has only one. */ + } + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= Lmin; i++) + { + uint32_t cc; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21(Feptr); + if (cc < 128 && (mb->ctypes[cc] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); + } + break; + + case OP_WORDCHAR: + for (i = 1; i <= Lmin; i++) + { + uint32_t cc; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + cc = UCHAR21(Feptr); + if (cc >= 128 || (mb->ctypes[cc] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + /* No need to skip more code units - we know it has only one. */ + } + break; + + default: + return PCRE2_ERROR_INTERNAL; + } /* End switch(Lctype) */ + + else +#endif /* SUPPORT_UNICODE */ + + /* Code for the non-UTF case for minimum matching of operators other + than OP_PROP and OP_NOTPROP. */ + + switch(Lctype) + { + case OP_ANY: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); + if (mb->partial != 0 && + Feptr + 1 >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + *Feptr == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + Feptr++; + } + break; + + case OP_ALLANY: + if (Feptr > mb->end_subject - Lmin) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + Feptr += Lmin; + break; + + /* This OP_ANYBYTE case will never be reached because \C gets turned + into OP_ALLANY in non-UTF mode. Cut out the code so that coverage + reports don't complain about it's never being used. */ + +/* case OP_ANYBYTE: +* if (Feptr > mb->end_subject - Lmin) +* { +* SCHECK_PARTIAL(); +* RRETURN(MATCH_NOMATCH); +* } +* Feptr += Lmin; +* break; +*/ + case OP_ANYNL: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*Feptr++) + { + default: RRETURN(MATCH_NOMATCH); + + case CHAR_CR: + if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++; + break; + + case CHAR_LF: + break; + + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#if PCRE2_CODE_UNIT_WIDTH != 8 + case 0x2028: + case 0x2029: +#endif + if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH); + break; + } + } + break; + + case OP_NOT_HSPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*Feptr++) + { + default: break; + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif + RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_HSPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*Feptr++) + { + default: RRETURN(MATCH_NOMATCH); + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif + break; + } + } + break; + + case OP_NOT_VSPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*Feptr++) + { + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + RRETURN(MATCH_NOMATCH); + default: break; + } + } + break; + + case OP_VSPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*Feptr++) + { + default: RRETURN(MATCH_NOMATCH); + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + break; + } + } + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + } + break; + + case OP_DIGIT: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + } + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + } + break; + + case OP_WHITESPACE: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + } + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + } + break; + + case OP_WORDCHAR: + for (i = 1; i <= Lmin; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + Feptr++; + } + break; + + default: + return PCRE2_ERROR_INTERNAL; + } + } + + /* If Lmin = Lmax we are done. Continue with the main loop. */ + + if (Lmin == Lmax) continue; + + /* If minimizing, we have to test the rest of the pattern before each + subsequent match. */ + + if (reptype == REPTYPE_MIN) + { +#ifdef SUPPORT_UNICODE + if (proptype >= 0) + { + switch(proptype) + { + case PT_ANY: + for (;;) + { + RMATCH(Fecode, RM208); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_LAMP: + for (;;) + { + int chartype; + RMATCH(Fecode, RM209); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + chartype = UCD_CHARTYPE(fc); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_GC: + for (;;) + { + RMATCH(Fecode, RM210); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_PC: + for (;;) + { + RMATCH(Fecode, RM211); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_SC: + for (;;) + { + RMATCH(Fecode, RM212); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_ALNUM: + for (;;) + { + int category; + RMATCH(Fecode, RM213); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N) == + (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + /* Perl space used to exclude VT, but from Perl 5.18 it is included, + which means that Perl space and POSIX space are now identical. PCRE + was changed at release 8.34. */ + + case PT_SPACE: /* Perl space */ + case PT_PXSPACE: /* POSIX space */ + for (;;) + { + RMATCH(Fecode, RM214); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + switch(fc) + { + HSPACE_CASES: + VSPACE_CASES: + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + + default: + if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + } + } + /* Control never gets here */ + + case PT_WORD: + for (;;) + { + int category; + RMATCH(Fecode, RM215); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || + category == ucp_N || + fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_CLIST: + for (;;) + { + const uint32_t *cp; + RMATCH(Fecode, RM216); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + cp = PRIV(ucd_caseless_sets) + Lpropvalue; + for (;;) + { + if (fc < *cp) + { + if (Lctype == OP_NOTPROP) break; + RRETURN(MATCH_NOMATCH); + } + if (fc == *cp++) + { + if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + } + } + } + /* Control never gets here */ + + case PT_UCNC: + for (;;) + { + RMATCH(Fecode, RM217); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(fc, Feptr); + if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || + fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || + fc >= 0xe000) == (Lctype == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + /* This should never occur */ + default: + return PCRE2_ERROR_INTERNAL; + } + } + + /* Match extended Unicode sequences. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ + + else if (Lctype == OP_EXTUNI) + { + for (;;) + { + RMATCH(Fecode, RM218); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + else + { + GETCHARINCTEST(fc, Feptr); + Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, + utf, NULL); + } + CHECK_PARTIAL(); + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* UTF mode for non-property testing character types. */ + +#ifdef SUPPORT_UNICODE + if (utf) + { + for (;;) + { + RMATCH(Fecode, RM219); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH); + GETCHARINC(fc, Feptr); + switch(Lctype) + { + case OP_ANY: /* This is the non-NL case */ + if (mb->partial != 0 && /* Take care with CRLF partial */ + Feptr >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + fc == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + break; + + case OP_ALLANY: + case OP_ANYBYTE: + break; + + case OP_ANYNL: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); + + case CHAR_CR: + if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++; + break; + + case CHAR_LF: + break; + + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#ifndef EBCDIC + case 0x2028: + case 0x2029: +#endif /* Not EBCDIC */ + if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) + RRETURN(MATCH_NOMATCH); + break; + } + break; + + case OP_NOT_HSPACE: + switch(fc) + { + HSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; + } + break; + + case OP_HSPACE: + switch(fc) + { + HSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); + } + break; + + case OP_NOT_VSPACE: + switch(fc) + { + VSPACE_CASES: RRETURN(MATCH_NOMATCH); + default: break; + } + break; + + case OP_VSPACE: + switch(fc) + { + VSPACE_CASES: break; + default: RRETURN(MATCH_NOMATCH); + } + break; + + case OP_NOT_DIGIT: + if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_DIGIT: + if (fc >= 256 || (mb->ctypes[fc] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WHITESPACE: + if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_WHITESPACE: + if (fc >= 256 || (mb->ctypes[fc] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WORDCHAR: + if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_WORDCHAR: + if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + break; + + default: + return PCRE2_ERROR_INTERNAL; + } + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* Not UTF mode */ + { + for (;;) + { + RMATCH(Fecode, RM33); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) + RRETURN(MATCH_NOMATCH); + fc = *Feptr++; + switch(Lctype) + { + case OP_ANY: /* This is the non-NL case */ + if (mb->partial != 0 && /* Take care with CRLF partial */ + Feptr >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + fc == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + break; + + case OP_ALLANY: + case OP_ANYBYTE: + break; + + case OP_ANYNL: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); + + case CHAR_CR: + if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++; + break; + + case CHAR_LF: + break; + + case CHAR_VT: + case CHAR_FF: + case CHAR_NEL: +#if PCRE2_CODE_UNIT_WIDTH != 8 + case 0x2028: + case 0x2029: +#endif + if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) + RRETURN(MATCH_NOMATCH); + break; + } + break; + + case OP_NOT_HSPACE: + switch(fc) + { + default: break; + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_HSPACE: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif + break; + } + break; + + case OP_NOT_VSPACE: + switch(fc) + { + default: break; + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_VSPACE: + switch(fc) + { + default: RRETURN(MATCH_NOMATCH); + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + break; + } + break; + + case OP_NOT_DIGIT: + if (MAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_DIGIT: + if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WHITESPACE: + if (MAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_WHITESPACE: + if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WORDCHAR: + if (MAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_WORDCHAR: + if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + break; + + default: + return PCRE2_ERROR_INTERNAL; + } + } + } + /* Control never gets here */ + } + + /* If maximizing, it is worth using inline code for speed, doing the type + test once at the start (i.e. keep it out of the loop). */ + + else + { + Lstart_eptr = Feptr; /* Remember where we started */ + +#ifdef SUPPORT_UNICODE + if (proptype >= 0) + { + switch(proptype) + { + case PT_ANY: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(fc, Feptr, len); + if (Lctype == OP_NOTPROP) break; + Feptr+= len; + } + break; + + case PT_LAMP: + for (i = Lmin; i < Lmax; i++) + { + int chartype; + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(fc, Feptr, len); + chartype = UCD_CHARTYPE(fc); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == (Lctype == OP_NOTPROP)) + break; + Feptr+= len; + } + break; + + case PT_GC: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(fc, Feptr, len); + if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + break; + Feptr+= len; + } + break; + + case PT_PC: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(fc, Feptr, len); + if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + break; + Feptr+= len; + } + break; + + case PT_SC: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(fc, Feptr, len); + if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP)) + break; + Feptr+= len; + } + break; + + case PT_ALNUM: + for (i = Lmin; i < Lmax; i++) + { + int category; + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(fc, Feptr, len); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N) == + (Lctype == OP_NOTPROP)) + break; + Feptr+= len; + } + break; + + /* Perl space used to exclude VT, but from Perl 5.18 it is included, + which means that Perl space and POSIX space are now identical. PCRE + was changed at release 8.34. */ + + case PT_SPACE: /* Perl space */ + case PT_PXSPACE: /* POSIX space */ + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(fc, Feptr, len); + switch(fc) + { + HSPACE_CASES: + VSPACE_CASES: + if (Lctype == OP_NOTPROP) goto ENDLOOP99; /* Break the loop */ + break; + + default: + if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP)) + goto ENDLOOP99; /* Break the loop */ + break; + } + Feptr+= len; + } + ENDLOOP99: + break; + + case PT_WORD: + for (i = Lmin; i < Lmax; i++) + { + int category; + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(fc, Feptr, len); + category = UCD_CATEGORY(fc); + if ((category == ucp_L || category == ucp_N || + fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP)) + break; + Feptr+= len; + } + break; + + case PT_CLIST: + for (i = Lmin; i < Lmax; i++) + { + const uint32_t *cp; + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(fc, Feptr, len); + cp = PRIV(ucd_caseless_sets) + Lpropvalue; + for (;;) + { + if (fc < *cp) + { if (Lctype == OP_NOTPROP) break; else goto GOT_MAX; } + if (fc == *cp++) + { if (Lctype == OP_NOTPROP) goto GOT_MAX; else break; } + } + Feptr += len; + } + GOT_MAX: + break; + + case PT_UCNC: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(fc, Feptr, len); + if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT || + fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) || + fc >= 0xe000) == (Lctype == OP_NOTPROP)) + break; + Feptr += len; + } + break; + + default: + return PCRE2_ERROR_INTERNAL; + } + + /* Feptr is now past the end of the maximum run */ + + if (reptype == REPTYPE_POS) continue; /* No backtracking */ + + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't + go too far. */ + + for(;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM222); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + if (utf) BACKCHAR(Feptr); + } + } + + /* Match extended Unicode grapheme clusters. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ + + else if (Lctype == OP_EXTUNI) + { + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + else + { + GETCHARINCTEST(fc, Feptr); + Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, + utf, NULL); + } + CHECK_PARTIAL(); + } + + /* Feptr is now past the end of the maximum run */ + + if (reptype == REPTYPE_POS) continue; /* No backtracking */ + + /* We use <= Lstart_eptr rather than == Lstart_eptr to detect the start + of the run while backtracking because the use of \C in UTF mode can + cause BACKCHAR to move back past Lstart_eptr. This is just palliative; + the use of \C in UTF mode is fraught with danger. */ + + for(;;) + { + int lgb, rgb; + PCRE2_SPTR fptr; + + if (Feptr <= Lstart_eptr) break; /* At start of char run */ + RMATCH(Fecode, RM220); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + + /* Backtracking over an extended grapheme cluster involves inspecting + the previous two characters (if present) to see if a break is + permitted between them. */ + + Feptr--; + if (!utf) fc = *Feptr; else + { + BACKCHAR(Feptr); + GETCHAR(fc, Feptr); + } + rgb = UCD_GRAPHBREAK(fc); + + for (;;) + { + if (Feptr <= Lstart_eptr) break; /* At start of char run */ + fptr = Feptr - 1; + if (!utf) fc = *fptr; else + { + BACKCHAR(fptr); + GETCHAR(fc, fptr); + } + lgb = UCD_GRAPHBREAK(fc); + if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0) break; + Feptr = fptr; + rgb = lgb; + } + } + } + + else +#endif /* SUPPORT_UNICODE */ + +#ifdef SUPPORT_UNICODE + if (utf) + { + switch(Lctype) + { + case OP_ANY: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (IS_NEWLINE(Feptr)) break; + if (mb->partial != 0 && /* Take care with CRLF partial */ + Feptr + 1 >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + UCHAR21(Feptr) == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); + } + break; + + case OP_ALLANY: + if (Lmax < UINT32_MAX) + { + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + Feptr++; + ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++); + } + } + else + { + Feptr = mb->end_subject; /* Unlimited UTF-8 repeat */ + SCHECK_PARTIAL(); + } + break; + + /* The "byte" (i.e. "code unit") case is the same as non-UTF */ + + case OP_ANYBYTE: + fc = Lmax - Lmin; + if (fc > (uint32_t)(mb->end_subject - Feptr)) + { + Feptr = mb->end_subject; + SCHECK_PARTIAL(); + } + else Feptr += fc; + break; + + case OP_ANYNL: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc == CHAR_CR) + { + if (++Feptr >= mb->end_subject) break; + if (UCHAR21(Feptr) == CHAR_LF) Feptr++; + } + else + { + if (fc != CHAR_LF && + (mb->bsr_convention == PCRE2_BSR_ANYCRLF || + (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL +#ifndef EBCDIC + && fc != 0x2028 && fc != 0x2029 +#endif /* Not EBCDIC */ + ))) + break; + Feptr += len; + } + } + break; + + case OP_NOT_HSPACE: + case OP_HSPACE: + for (i = Lmin; i < Lmax; i++) + { + BOOL gotspace; + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + switch(fc) + { + HSPACE_CASES: gotspace = TRUE; break; + default: gotspace = FALSE; break; + } + if (gotspace == (Lctype == OP_NOT_HSPACE)) break; + Feptr += len; + } + break; + + case OP_NOT_VSPACE: + case OP_VSPACE: + for (i = Lmin; i < Lmax; i++) + { + BOOL gotspace; + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + switch(fc) + { + VSPACE_CASES: gotspace = TRUE; break; + default: gotspace = FALSE; break; + } + if (gotspace == (Lctype == OP_NOT_VSPACE)) break; + Feptr += len; + } + break; + + case OP_NOT_DIGIT: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) break; + Feptr+= len; + } + break; + + case OP_DIGIT: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc >= 256 ||(mb->ctypes[fc] & ctype_digit) == 0) break; + Feptr+= len; + } + break; + + case OP_NOT_WHITESPACE: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) break; + Feptr+= len; + } + break; + + case OP_WHITESPACE: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc >= 256 ||(mb->ctypes[fc] & ctype_space) == 0) break; + Feptr+= len; + } + break; + + case OP_NOT_WORDCHAR: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) break; + Feptr+= len; + } + break; + + case OP_WORDCHAR: + for (i = Lmin; i < Lmax; i++) + { + int len = 1; + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(fc, Feptr, len); + if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) break; + Feptr+= len; + } + break; + + default: + return PCRE2_ERROR_INTERNAL; + } + + if (reptype == REPTYPE_POS) continue; /* No backtracking */ + + /* After \C in UTF mode, Lstart_eptr might be in the middle of a + Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go + too far. */ + + for(;;) + { + if (Feptr <= Lstart_eptr) break; + RMATCH(Fecode, RM221); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + BACKCHAR(Feptr); + if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && + UCHAR21(Feptr) == CHAR_NL && UCHAR21(Feptr - 1) == CHAR_CR) + Feptr--; + } + } + else +#endif /* SUPPORT_UNICODE */ + + /* Not UTF mode */ + { + switch(Lctype) + { + case OP_ANY: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (IS_NEWLINE(Feptr)) break; + if (mb->partial != 0 && /* Take care with CRLF partial */ + Feptr + 1 >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + *Feptr == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + Feptr++; + } + break; + + case OP_ALLANY: + case OP_ANYBYTE: + fc = Lmax - Lmin; + if (fc > (uint32_t)(mb->end_subject - Feptr)) + { + Feptr = mb->end_subject; + SCHECK_PARTIAL(); + } + else Feptr += fc; + break; + + case OP_ANYNL: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + fc = *Feptr; + if (fc == CHAR_CR) + { + if (++Feptr >= mb->end_subject) break; + if (*Feptr == CHAR_LF) Feptr++; + } + else + { + if (fc != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF || + (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL +#if PCRE2_CODE_UNIT_WIDTH != 8 + && fc != 0x2028 && fc != 0x2029 +#endif + ))) break; + Feptr++; + } + } + break; + + case OP_NOT_HSPACE: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + switch(*Feptr) + { + default: Feptr++; break; + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif + goto ENDLOOP00; + } + } + ENDLOOP00: + break; + + case OP_HSPACE: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + switch(*Feptr) + { + default: goto ENDLOOP01; + HSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + HSPACE_MULTIBYTE_CASES: +#endif + Feptr++; break; + } + } + ENDLOOP01: + break; + + case OP_NOT_VSPACE: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + switch(*Feptr) + { + default: Feptr++; break; + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + goto ENDLOOP02; + } + } + ENDLOOP02: + break; + + case OP_VSPACE: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + switch(*Feptr) + { + default: goto ENDLOOP03; + VSPACE_BYTE_CASES: +#if PCRE2_CODE_UNIT_WIDTH != 8 + VSPACE_MULTIBYTE_CASES: +#endif + Feptr++; break; + } + } + ENDLOOP03: + break; + + case OP_NOT_DIGIT: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0) + break; + Feptr++; + } + break; + + case OP_DIGIT: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0) + break; + Feptr++; + } + break; + + case OP_NOT_WHITESPACE: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0) + break; + Feptr++; + } + break; + + case OP_WHITESPACE: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0) + break; + Feptr++; + } + break; + + case OP_NOT_WORDCHAR: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0) + break; + Feptr++; + } + break; + + case OP_WORDCHAR: + for (i = Lmin; i < Lmax; i++) + { + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0) + break; + Feptr++; + } + break; + + default: + return PCRE2_ERROR_INTERNAL; + } + + if (reptype == REPTYPE_POS) continue; /* No backtracking */ + + for (;;) + { + if (Feptr == Lstart_eptr) break; + RMATCH(Fecode, RM34); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr--; + if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && *Feptr == CHAR_LF && + Feptr[-1] == CHAR_CR) Feptr--; + } + } + } + break; /* End of repeat character type processing */ + +#undef Lstart_eptr +#undef Lmin +#undef Lmax +#undef Lctype +#undef Lpropvalue + + + /* ===================================================================== */ + /* Match a back reference, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. The OP_REF and + OP_REFI opcodes are used for a reference to a numbered group or to a + non-duplicated named group. For a duplicated named group, OP_DNREF and + OP_DNREFI are used. In this case we must scan the list of groups to which + the name refers, and use the first one that is set. */ + +#define Lmin F->temp_32[0] +#define Lmax F->temp_32[1] +#define Lcaseless F->temp_32[2] +#define Lstart F->temp_sptr[0] +#define Loffset F->temp_size + + case OP_DNREF: + case OP_DNREFI: + Lcaseless = (Fop == OP_DNREFI); + { + int count = GET2(Fecode, 1+IMM2_SIZE); + PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size; + Fecode += 1 + 2*IMM2_SIZE; + + while (count-- > 0) + { + Loffset = (GET2(slot, 0) << 1) - 2; + if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET) break; + slot += mb->name_entry_size; + } + } + goto REF_REPEAT; + + case OP_REF: + case OP_REFI: + Lcaseless = (Fop == OP_REFI); + Loffset = (GET2(Fecode, 1) << 1) - 2; + Fecode += 1 + IMM2_SIZE; + + /* Set up for repetition, or handle the non-repeated case. The maximum and + minimum must be in the heap frame, but as they are short-term values, we + use temporary fields. */ + + REF_REPEAT: + switch (*Fecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + fc = *Fecode++ - OP_CRSTAR; + Lmin = rep_min[fc]; + Lmax = rep_max[fc]; + reptype = rep_typ[fc]; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + Lmin = GET2(Fecode, 1); + Lmax = GET2(Fecode, 1 + IMM2_SIZE); + reptype = rep_typ[*Fecode - OP_CRSTAR]; + if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */ + Fecode += 1 + 2 * IMM2_SIZE; + break; + + default: /* No repeat follows */ + { + rrc = match_ref(Loffset, Lcaseless, F, mb, &length); + if (rrc != 0) + { + if (rrc > 0) Feptr = mb->end_subject; /* Partial match */ + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + } + Feptr += length; + continue; /* With the main loop */ + } + + /* Handle repeated back references. If a set group has length zero, just + continue with the main loop, because it matches however many times. For an + unset reference, if the minimum is zero, we can also just continue. We can + also continue if PCRE2_MATCH_UNSET_BACKREF is set, because this makes unset + group behave as a zero-length group. For any other unset cases, carrying + on will result in NOMATCH. */ + + if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET) + { + if (Fovector[Loffset] == Fovector[Loffset + 1]) continue; + } + else /* Group is not set */ + { + if (Lmin == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0) + continue; + } + + /* First, ensure the minimum number of matches are present. */ + + for (i = 1; i <= Lmin; i++) + { + PCRE2_SIZE slength; + rrc = match_ref(Loffset, Lcaseless, F, mb, &slength); + if (rrc != 0) + { + if (rrc > 0) Feptr = mb->end_subject; /* Partial match */ + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + Feptr += slength; + } + + /* If min = max, we are done. They are not both allowed to be zero. */ + + if (Lmin == Lmax) continue; + + /* If minimizing, keep trying and advancing the pointer. */ + + if (reptype == REPTYPE_MIN) + { + for (;;) + { + PCRE2_SIZE slength; + RMATCH(Fecode, RM20); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH); + rrc = match_ref(Loffset, Lcaseless, F, mb, &slength); + if (rrc != 0) + { + if (rrc > 0) Feptr = mb->end_subject; /* Partial match */ + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + Feptr += slength; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest string and work backwards, as long as + the matched lengths for each iteration are the same. */ + + else + { + BOOL samelengths = TRUE; + Lstart = Feptr; /* Starting position */ + Flength = Fovector[Loffset+1] - Fovector[Loffset]; + + for (i = Lmin; i < Lmax; i++) + { + PCRE2_SIZE slength; + rrc = match_ref(Loffset, Lcaseless, F, mb, &slength); + if (rrc != 0) + { + /* Can't use CHECK_PARTIAL because we don't want to update Feptr in + the soft partial matching case. */ + + if (rrc > 0 && mb->partial != 0 && + mb->end_subject > mb->start_used_ptr) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + break; + } + + if (slength != Flength) samelengths = FALSE; + Feptr += slength; + } + + /* If the length matched for each repetition is the same as the length of + the captured group, we can easily work backwards. This is the normal + case. However, in caseless UTF-8 mode there are pairs of case-equivalent + characters whose lengths (in terms of code units) differ. However, this + is very rare, so we handle it by re-matching fewer and fewer times. */ + + if (samelengths) + { + while (Feptr >= Lstart) + { + RMATCH(Fecode, RM21); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Feptr -= Flength; + } + } + + /* The rare case of non-matching lengths. Re-scan the repetition for each + iteration. We know that match_ref() will succeed every time. */ + + else + { + Lmax = i; + for (;;) + { + RMATCH(Fecode, RM22); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (Feptr == Lstart) break; /* Failed after minimal repetition */ + Feptr = Lstart; + Lmax--; + for (i = Lmin; i < Lmax; i++) + { + PCRE2_SIZE slength; + (void)match_ref(Loffset, Lcaseless, F, mb, &slength); + Feptr += slength; + } + } + } + + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + +#undef Lcaseless +#undef Lmin +#undef Lmax +#undef Lstart +#undef Loffset + + + +/* ========================================================================= */ +/* Opcodes for the start of various parenthesized items */ +/* ========================================================================= */ + + /* In all cases, if the result of RMATCH() is MATCH_THEN, check whether the + (*THEN) is within the current branch by comparing the address of OP_THEN + that is passed back with the end of the branch. If (*THEN) is within the + current branch, and the branch is one of two or more alternatives (it + either starts or ends with OP_ALT), we have reached the limit of THEN's + action, so convert the return code to NOMATCH, which will cause normal + backtracking to happen from now on. Otherwise, THEN is passed back to an + outer alternative. This implements Perl's treatment of parenthesized + groups, where a group not containing | does not affect the current + alternative, that is, (X) is NOT the same as (X|(*F)). */ + + + /* ===================================================================== */ + /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a non-possessive + bracket group, indicating that it may occur zero times. It may repeat + infinitely, or not at all - i.e. it could be ()* or ()? or even (){0} in + the pattern. Brackets with fixed upper repeat limits are compiled as a + number of copies, with the optional ones preceded by BRAZERO or BRAMINZERO. + Possessive groups with possible zero repeats are preceded by BRAPOSZERO. */ + +#define Lnext_ecode F->temp_sptr[0] + + case OP_BRAZERO: + Lnext_ecode = Fecode + 1; + RMATCH(Lnext_ecode, RM9); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT); + Fecode = Lnext_ecode + 1 + LINK_SIZE; + break; + + case OP_BRAMINZERO: + Lnext_ecode = Fecode + 1; + do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT); + RMATCH(Lnext_ecode + 1 + LINK_SIZE, RM10); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Fecode++; + break; + +#undef Lnext_ecode + + case OP_SKIPZERO: + Fecode++; + do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT); + Fecode += 1 + LINK_SIZE; + break; + + + /* ===================================================================== */ + /* Handle possessive brackets with an unlimited repeat. The end of these + brackets will always be OP_KETRPOS, which returns MATCH_KETRPOS without + going further in the pattern. */ + +#define Lframe_type F->temp_32[0] +#define Lmatched_once F->temp_32[1] +#define Lzero_allowed F->temp_32[2] +#define Lstart_eptr F->temp_sptr[0] +#define Lstart_group F->temp_sptr[1] + + case OP_BRAPOSZERO: + Lzero_allowed = TRUE; /* Zero repeat is allowed */ + Fecode += 1; + if (*Fecode == OP_CBRAPOS || *Fecode == OP_SCBRAPOS) + goto POSSESSIVE_CAPTURE; + goto POSSESSIVE_NON_CAPTURE; + + case OP_BRAPOS: + case OP_SBRAPOS: + Lzero_allowed = FALSE; /* Zero repeat not allowed */ + + POSSESSIVE_NON_CAPTURE: + Lframe_type = GF_NOCAPTURE; /* Remembered frame type */ + goto POSSESSIVE_GROUP; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + Lzero_allowed = FALSE; /* Zero repeat not allowed */ + + POSSESSIVE_CAPTURE: + number = GET2(Fecode, 1+LINK_SIZE); + Lframe_type = GF_CAPTURE | number; /* Remembered frame type */ + + POSSESSIVE_GROUP: + Lmatched_once = FALSE; /* Never matched */ + Lstart_group = Fecode; /* Start of this group */ + + for (;;) + { + Lstart_eptr = Feptr; /* Position at group start */ + group_frame_type = Lframe_type; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM8); + if (rrc == MATCH_KETRPOS) + { + Lmatched_once = TRUE; /* Matched at least once */ + if (Feptr == Lstart_eptr) /* Empty match; skip to end */ + { + do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); + break; + } + + Fecode = Lstart_group; + continue; + } + + /* See comment above about handling THEN. */ + + if (rrc == MATCH_THEN) + { + PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1); + if (mb->verb_ecode_ptr < next_ecode && + (*Fecode == OP_ALT || *next_ecode == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Fecode += GET(Fecode, 1); + if (*Fecode != OP_ALT) break; + } + + /* Success if matched something or zero repeat allowed */ + + if (Lmatched_once || Lzero_allowed) + { + Fecode += 1 + LINK_SIZE; + break; + } + + RRETURN(MATCH_NOMATCH); + +#undef Lmatched_once +#undef Lzero_allowed +#undef Lframe_type +#undef Lstart_eptr +#undef Lstart_group + + + /* ===================================================================== */ + /* Handle non-capturing brackets that cannot match an empty string. When we + get to the final alternative within the brackets, as long as there are no + THEN's in the pattern, we can optimize by not recording a new backtracking + point. (Ideally we should test for a THEN within this group, but we don't + have that information.) Don't do this if we are at the very top level, + however, because that would make handling assertions and once-only brackets + messier when there is nothing to go back to. */ + +#define Lframe_type F->temp_32[0] /* Set for all that use GROUPLOOP */ +#define Lnext_branch F->temp_sptr[0] /* Used only in OP_BRA handling */ + + case OP_BRA: + if (mb->hasthen || Frdepth == 0) + { + Lframe_type = 0; + goto GROUPLOOP; + } + + for (;;) + { + Lnext_branch = Fecode + GET(Fecode, 1); + if (*Lnext_branch != OP_ALT) break; + + /* This is never the final branch. We do not need to test for MATCH_THEN + here because this code is not used when there is a THEN in the pattern. */ + + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM1); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Fecode = Lnext_branch; + } + + /* Hit the start of the final branch. Continue at this level. */ + + Fecode += PRIV(OP_lengths)[*Fecode]; + break; + +#undef Lnext_branch + + + /* ===================================================================== */ + /* Handle a capturing bracket, other than those that are possessive with an + unlimited repeat. */ + + case OP_CBRA: + case OP_SCBRA: + Lframe_type = GF_CAPTURE | GET2(Fecode, 1+LINK_SIZE); + goto GROUPLOOP; + + + /* ===================================================================== */ + /* Atomic groups and non-capturing brackets that can match an empty string + must record a backtracking point and also set up a chained frame. */ + + case OP_ONCE: + case OP_SBRA: + Lframe_type = GF_NOCAPTURE | Fop; + + GROUPLOOP: + for (;;) + { + group_frame_type = Lframe_type; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM2); + if (rrc == MATCH_THEN) + { + PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1); + if (mb->verb_ecode_ptr < next_ecode && + (*Fecode == OP_ALT || *next_ecode == OP_ALT)) + rrc = MATCH_NOMATCH; + } + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Fecode += GET(Fecode, 1); + if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH); + } + /* Control never reaches here. */ + +#undef Lframe_type + + + /* ===================================================================== */ + /* Recursion either matches the current regex, or some subexpression. The + offset data is the offset to the starting bracket from the start of the + whole pattern. (This is so that it works from duplicated subpatterns.) */ + +#define Lframe_type F->temp_32[0] +#define Lstart_branch F->temp_sptr[0] + + case OP_RECURSE: + bracode = mb->start_code + GET(Fecode, 1); + number = (bracode == mb->start_code)? 0 : GET2(bracode, 1 + LINK_SIZE); + + /* If we are already in a recursion, check for repeating the same one + without advancing the subject pointer. This should catch convoluted mutual + recursions. (Some simple cases are caught at compile time.) */ + + if (Fcurrent_recurse != RECURSE_UNSET) + { + offset = Flast_group_offset; + while (offset != PCRE2_UNSET) + { + N = (heapframe *)((char *)mb->match_frames + offset); + P = (heapframe *)((char *)N - frame_size); + if (N->group_frame_type == (GF_RECURSE | number)) + { + if (Feptr == P->eptr) return PCRE2_ERROR_RECURSELOOP; + break; + } + offset = P->last_group_offset; + } + } + + /* Now run the recursion, branch by branch. */ + + Lstart_branch = bracode; + Lframe_type = GF_RECURSE | number; + + for (;;) + { + PCRE2_SPTR next_ecode; + + group_frame_type = Lframe_type; + RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM11); + next_ecode = Lstart_branch + GET(Lstart_branch,1); + + /* Handle backtracking verbs, which are defined in a range that can + easily be tested for. PCRE does not allow THEN, SKIP, PRUNE or COMMIT to + escape beyond a recursion; they cause a NOMATCH for the entire recursion. + + When one of these verbs triggers, the current recursion group number is + recorded. If it matches the recursion we are processing, the verb + happened within the recursion and we must deal with it. Otherwise it must + have happened after the recursion completed, and so has to be passed + back. See comment above about handling THEN. */ + + if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX && + mb->verb_current_recurse == (Lframe_type ^ GF_RECURSE)) + { + if (rrc == MATCH_THEN && mb->verb_ecode_ptr < next_ecode && + (*Lstart_branch == OP_ALT || *next_ecode == OP_ALT)) + rrc = MATCH_NOMATCH; + else RRETURN(MATCH_NOMATCH); + } + + /* Note that carrying on after (*ACCEPT) in a recursion is handled in the + OP_ACCEPT code. Nothing needs to be done here. */ + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Lstart_branch = next_ecode; + if (*Lstart_branch != OP_ALT) RRETURN(MATCH_NOMATCH); + } + /* Control never reaches here. */ + +#undef Lframe_type +#undef Lstart_branch + + + /* ===================================================================== */ + /* Positive assertions are like other groups except that PCRE doesn't allow + the effect of (*THEN) to escape beyond an assertion; it is therefore + treated as NOMATCH. (*ACCEPT) is treated as successful assertion, with its + captures and mark retained. Any other return is an error. */ + +#define Lframe_type F->temp_32[0] + + case OP_ASSERT: + case OP_ASSERTBACK: + Lframe_type = GF_NOCAPTURE | Fop; + for (;;) + { + group_frame_type = Lframe_type; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM3); + if (rrc == MATCH_ACCEPT) + { + memcpy(Fovector, + (char *)assert_accept_frame + offsetof(heapframe, ovector), + assert_accept_frame->offset_top * sizeof(PCRE2_SIZE)); + Foffset_top = assert_accept_frame->offset_top; + Fmark = assert_accept_frame->mark; + break; + } + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + Fecode += GET(Fecode, 1); + if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH); + } + + do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); + Fecode += 1 + LINK_SIZE; + break; + +#undef Lframe_type + + + /* ===================================================================== */ + /* Handle negative assertions. Loop for each non-matching branch as for + positive assertions. */ + +#define Lframe_type F->temp_32[0] + + case OP_ASSERT_NOT: + case OP_ASSERTBACK_NOT: + Lframe_type = GF_NOCAPTURE | Fop; + + for (;;) + { + group_frame_type = Lframe_type; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM4); + switch(rrc) + { + case MATCH_ACCEPT: /* Assertion matched, therefore it fails. */ + case MATCH_MATCH: + RRETURN (MATCH_NOMATCH); + + case MATCH_NOMATCH: /* Branch failed, try next if present. */ + case MATCH_THEN: + Fecode += GET(Fecode, 1); + if (*Fecode != OP_ALT) goto ASSERT_NOT_FAILED; + break; + + case MATCH_COMMIT: /* Assertion forced to fail, therefore continue. */ + case MATCH_SKIP: + case MATCH_PRUNE: + do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); + goto ASSERT_NOT_FAILED; + + default: /* Pass back any other return */ + RRETURN(rrc); + } + } + + /* None of the branches have matched or there was a backtrack to (*COMMIT), + (*SKIP), (*PRUNE), or (*THEN) in the last branch. This is success for a + negative assertion, so carry on. */ + + ASSERT_NOT_FAILED: + Fecode += 1 + LINK_SIZE; + break; + +#undef Lframe_type + + + /* ===================================================================== */ + /* The callout item calls an external function, if one is provided, passing + details of the match so far. This is mainly for debugging, though the + function is able to force a failure. */ + + case OP_CALLOUT: + case OP_CALLOUT_STR: + rrc = do_callout(F, mb, &length); + if (rrc > 0) RRETURN(MATCH_NOMATCH); + if (rrc < 0) RRETURN(rrc); + Fecode += length; + break; + + + /* ===================================================================== */ + /* Conditional group: compilation checked that there are no more than two + branches. If the condition is false, skipping the first branch takes us + past the end of the item if there is only one branch, but that's exactly + what we want. */ + + case OP_COND: + case OP_SCOND: + + /* The variable Flength will be added to Fecode when the condition is + false, to get to the second branch. Setting it to the offset to the ALT or + KET, then incrementing Fecode achieves this effect. However, if the second + branch is non-existent, we must point to the KET so that the end of the + group is correctly processed. We now have Fecode pointing to the condition + or callout. */ + + Flength = GET(Fecode, 1); /* Offset to the second branch */ + if (Fecode[Flength] != OP_ALT) Flength -= 1 + LINK_SIZE; + Fecode += 1 + LINK_SIZE; /* From this opcode */ + + /* Because of the way auto-callout works during compile, a callout item is + inserted between OP_COND and an assertion condition. Such a callout can + also be inserted manually. */ + + if (*Fecode == OP_CALLOUT || *Fecode == OP_CALLOUT_STR) + { + rrc = do_callout(F, mb, &length); + if (rrc > 0) RRETURN(MATCH_NOMATCH); + if (rrc < 0) RRETURN(rrc); + + /* Advance Fecode past the callout, so it now points to the condition. We + must adjust Flength so that the value of Fecode+Flength is unchanged. */ + + Fecode += length; + Flength -= length; + } + + /* Test the various possible conditions */ + + condition = FALSE; + switch(*Fecode) + { + case OP_RREF: /* Group recursion test */ + if (Fcurrent_recurse != RECURSE_UNSET) + { + number = GET2(Fecode, 1); + condition = (number == RREF_ANY || number == Fcurrent_recurse); + } + break; + + case OP_DNRREF: /* Duplicate named group recursion test */ + if (Fcurrent_recurse != RECURSE_UNSET) + { + int count = GET2(Fecode, 1 + IMM2_SIZE); + PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size; + while (count-- > 0) + { + number = GET2(slot, 0); + condition = number == Fcurrent_recurse; + if (condition) break; + slot += mb->name_entry_size; + } + } + break; + + case OP_CREF: /* Numbered group used test */ + offset = (GET2(Fecode, 1) << 1) - 2; /* Doubled ref number */ + condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET; + break; + + case OP_DNCREF: /* Duplicate named group used test */ + { + int count = GET2(Fecode, 1 + IMM2_SIZE); + PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size; + while (count-- > 0) + { + offset = (GET2(slot, 0) << 1) - 2; + condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET; + if (condition) break; + slot += mb->name_entry_size; + } + } + break; + + case OP_FALSE: + case OP_FAIL: /* The assertion (?!) becomes OP_FAIL */ + break; + + case OP_TRUE: + condition = TRUE; + break; + + /* The condition is an assertion. Run code similar to the assertion code + above. */ + +#define Lpositive F->temp_32[0] +#define Lstart_branch F->temp_sptr[0] + + default: + Lpositive = (*Fecode == OP_ASSERT || *Fecode == OP_ASSERTBACK); + Lstart_branch = Fecode; + + for (;;) + { + group_frame_type = GF_CONDASSERT | *Fecode; + RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM5); + + switch(rrc) + { + case MATCH_ACCEPT: /* Save captures */ + memcpy(Fovector, + (char *)assert_accept_frame + offsetof(heapframe, ovector), + assert_accept_frame->offset_top * sizeof(PCRE2_SIZE)); + Foffset_top = assert_accept_frame->offset_top; + + /* Fall through */ + /* In the case of a match, the captures have already been put into + the current frame. */ + + case MATCH_MATCH: + condition = Lpositive; /* TRUE for positive assertion */ + break; + + /* PCRE doesn't allow the effect of (*THEN) to escape beyond an + assertion; it is therefore always treated as NOMATCH. */ + + case MATCH_NOMATCH: + case MATCH_THEN: + Lstart_branch += GET(Lstart_branch, 1); + if (*Lstart_branch == OP_ALT) continue; /* Try next branch */ + condition = !Lpositive; /* TRUE for negative assertion */ + break; + + /* These force no match without checking other branches. */ + + case MATCH_COMMIT: + case MATCH_SKIP: + case MATCH_PRUNE: + condition = !Lpositive; + break; + + default: + RRETURN(rrc); + } + break; /* Out of the branch loop */ + } + + /* If the condition is true, find the end of the assertion so that + advancing past it gets us to the start of the first branch. */ + + if (condition) + { + do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT); + } + break; /* End of assertion condition */ + } + +#undef Lpositive +#undef Lstart_branch + + /* Choose branch according to the condition. */ + + Fecode += condition? PRIV(OP_lengths)[*Fecode] : Flength; + + /* If the opcode is OP_SCOND it means we are at a repeated conditional + group that might match an empty string. We must therefore descend a level + so that the start is remembered for checking. For OP_COND we can just + continue at this level. */ + + if (Fop == OP_SCOND) + { + group_frame_type = GF_NOCAPTURE | Fop; + RMATCH(Fecode, RM35); + RRETURN(rrc); + } + break; + + + +/* ========================================================================= */ +/* End of start of parenthesis opcodes */ +/* ========================================================================= */ + + + /* ===================================================================== */ + /* Move the subject pointer back. This occurs only at the start of each + branch of a lookbehind assertion. If we are too close to the start to move + back, fail. When working with UTF-8 we move back a number of characters, + not bytes. */ + + case OP_REVERSE: + number = GET(Fecode, 1); +#ifdef SUPPORT_UNICODE + if (utf) + { + while (number-- > 0) + { + if (Feptr <= mb->start_subject) RRETURN(MATCH_NOMATCH); + Feptr--; + BACKCHAR(Feptr); + } + } + else +#endif + + /* No UTF-8 support, or not in UTF-8 mode: count is byte count */ + + { + if ((ptrdiff_t)number > Feptr - mb->start_subject) RRETURN(MATCH_NOMATCH); + Feptr -= number; + } + + /* Save the earliest consulted character, then skip to next opcode */ + + if (Feptr < mb->start_used_ptr) mb->start_used_ptr = Feptr; + Fecode += 1 + LINK_SIZE; + break; + + + /* ===================================================================== */ + /* An alternation is the end of a branch; scan along to find the end of the + bracketed group. */ + + case OP_ALT: + do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT); + break; + + + /* ===================================================================== */ + /* The end of a parenthesized group. For all but OP_BRA and OP_COND, the + starting frame was added to the chained frames in order to remember the + starting subject position for the group. */ + + case OP_KET: + case OP_KETRMIN: + case OP_KETRMAX: + case OP_KETRPOS: + + bracode = Fecode - GET(Fecode, 1); + + /* Point N to the frame at the start of the most recent group. + Remember the subject pointer at the start of the group. */ + + if (*bracode != OP_BRA && *bracode != OP_COND) + { + N = (heapframe *)((char *)mb->match_frames + Flast_group_offset); + P = (heapframe *)((char *)N - frame_size); + Flast_group_offset = P->last_group_offset; + +#ifdef DEBUG_SHOW_RMATCH + fprintf(stderr, "++ KET for frame=%d type=%x prev char offset=%lu\n", + N->rdepth, N->group_frame_type, + (char *)P->eptr - (char *)mb->start_subject); +#endif + + /* If we are at the end of an assertion that is a condition, return a + match, discarding any intermediate backtracking points. Copy back the + captures into the frame before N so that they are set on return. Doing + this for all assertions, both positive and negative, seems to match what + Perl does. */ + + if (GF_IDMASK(N->group_frame_type) == GF_CONDASSERT) + { + memcpy((char *)P + offsetof(heapframe, ovector), Fovector, + Foffset_top * sizeof(PCRE2_SIZE)); + P->offset_top = Foffset_top; + Fback_frame = (char *)F - (char *)P; + RRETURN(MATCH_MATCH); + } + } + else P = NULL; /* Indicates starting frame not recorded */ + + /* The group was not a conditional assertion. */ + + switch (*bracode) + { + case OP_BRA: /* No need to do anything for these */ + case OP_COND: + case OP_SCOND: + break; + + /* Positive assertions are like OP_ONCE, except that in addition the + subject pointer must be put back to where it was at the start of the + assertion. */ + + case OP_ASSERT: + case OP_ASSERTBACK: + if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr; + Feptr = P->eptr; + /* Fall through */ + + /* For an atomic group, discard internal backtracking points. We must + also ensure that any remaining branches within the top-level of the group + are not tried. Do this by adjusting the code pointer within the backtrack + frame so that it points to the final branch. */ + + case OP_ONCE: + Fback_frame = ((char *)F - (char *)P); + for (;;) + { + uint32_t y = GET(P->ecode,1); + if ((P->ecode)[y] != OP_ALT) break; + P->ecode += y; + } + break; + + /* A matching negative assertion returns MATCH, which is turned into + NOMATCH at the assertion level. */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK_NOT: + RRETURN(MATCH_MATCH); + + /* Whole-pattern recursion is coded as a recurse into group 0, so it + won't be picked up here. Instead, we catch it when the OP_END is reached. + Other recursion is handled here. */ + + case OP_CBRA: + case OP_CBRAPOS: + case OP_SCBRA: + case OP_SCBRAPOS: + number = GET2(bracode, 1+LINK_SIZE); + + /* Handle a recursively called group. We reinstate the previous set of + captures and then carry on after the recursion call. */ + + if (Fcurrent_recurse == number) + { + P = (heapframe *)((char *)N - frame_size); + memcpy((char *)F + offsetof(heapframe, ovector), P->ovector, + P->offset_top * sizeof(PCRE2_SIZE)); + Foffset_top = P->offset_top; + Fcapture_last = P->capture_last; + Fcurrent_recurse = P->current_recurse; + Fecode = P->ecode + 1 + LINK_SIZE; + continue; /* With next opcode */ + } + + /* Deal with actual capturing. */ + + offset = (number << 1) - 2; + Fcapture_last = number; + Fovector[offset] = P->eptr - mb->start_subject; + Fovector[offset+1] = Feptr - mb->start_subject; + if (offset >= Foffset_top) Foffset_top = offset + 2; + break; + } /* End actions relating to the starting opcode */ + + /* OP_KETRPOS is a possessive repeating ket. Remember the current position, + and return the MATCH_KETRPOS. This makes it possible to do the repeats one + at a time from the outer level. This must precede the empty string test - + in this case that test is done at the outer level. */ + + if (*Fecode == OP_KETRPOS) + { + memcpy((char *)P + offsetof(heapframe, eptr), + (char *)F + offsetof(heapframe, eptr), + frame_copy_size); + RRETURN(MATCH_KETRPOS); + } + + /* Handle the different kinds of closing brackets. A non-repeating ket + needs no special action, just continuing at this level. This also happens + for the repeating kets if the group matched no characters, in order to + forcibly break infinite loops. Otherwise, the repeating kets try the rest + of the pattern or restart from the preceding bracket, in the appropriate + order. */ + + if (Fop != OP_KET && (P == NULL || Feptr != P->eptr)) + { + if (Fop == OP_KETRMIN) + { + RMATCH(Fecode + 1 + LINK_SIZE, RM6); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + Fecode -= GET(Fecode, 1); + break; /* End of ket processing */ + } + + /* Repeat the maximum number of times (KETRMAX) */ + + RMATCH(bracode, RM7); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } + + /* Carry on at this level for a non-repeating ket, or after matching an + empty string, or after repeating for a maximum number of times. */ + + Fecode += 1 + LINK_SIZE; + break; + + + /* ===================================================================== */ + /* Start and end of line assertions, not multiline mode. */ + + case OP_CIRC: /* Start of line, unless PCRE2_NOTBOL is set. */ + if (Feptr != mb->start_subject || (mb->moptions & PCRE2_NOTBOL) != 0) + RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + case OP_SOD: /* Unconditional start of subject */ + if (Feptr != mb->start_subject) RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + /* When PCRE2_NOTEOL is unset, assert before the subject end, or a + terminating newline unless PCRE2_DOLLAR_ENDONLY is set. */ + + case OP_DOLL: + if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH); + if ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0) goto ASSERT_NL_OR_EOS; + + /* Fall through */ + /* Unconditional end of subject assertion (\z) */ + + case OP_EOD: + if (Feptr < mb->end_subject) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + Fecode++; + break; + + /* End of subject or ending \n assertion (\Z) */ + + case OP_EODN: + ASSERT_NL_OR_EOS: + if (Feptr < mb->end_subject && + (!IS_NEWLINE(Feptr) || Feptr != mb->end_subject - mb->nllen)) + { + if (mb->partial != 0 && + Feptr + 1 >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + UCHAR21TEST(Feptr) == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + RRETURN(MATCH_NOMATCH); + } + + /* Either at end of string or \n before end. */ + + SCHECK_PARTIAL(); + Fecode++; + break; + + + /* ===================================================================== */ + /* Start and end of line assertions, multiline mode. */ + + /* Start of subject unless notbol, or after any newline except for one at + the very end, unless PCRE2_ALT_CIRCUMFLEX is set. */ + + case OP_CIRCM: + if ((mb->moptions & PCRE2_NOTBOL) != 0 && Feptr == mb->start_subject) + RRETURN(MATCH_NOMATCH); + if (Feptr != mb->start_subject && + ((Feptr == mb->end_subject && + (mb->poptions & PCRE2_ALT_CIRCUMFLEX) == 0) || + !WAS_NEWLINE(Feptr))) + RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + /* Assert before any newline, or before end of subject unless noteol is + set. */ + + case OP_DOLLM: + if (Feptr < mb->end_subject) + { + if (!IS_NEWLINE(Feptr)) + { + if (mb->partial != 0 && + Feptr + 1 >= mb->end_subject && + NLBLOCK->nltype == NLTYPE_FIXED && + NLBLOCK->nllen == 2 && + UCHAR21TEST(Feptr) == NLBLOCK->nl[0]) + { + mb->hitend = TRUE; + if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; + } + RRETURN(MATCH_NOMATCH); + } + } + else + { + if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + } + Fecode++; + break; + + + /* ===================================================================== */ + /* Start of match assertion */ + + case OP_SOM: + if (Feptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH); + Fecode++; + break; + + + /* ===================================================================== */ + /* Reset the start of match point */ + + case OP_SET_SOM: + Fstart_match = Feptr; + Fecode++; + break; + + + /* ===================================================================== */ + /* Word boundary assertions. Find out if the previous and current + characters are "word" characters. It takes a bit more work in UTF mode. + Characters > 255 are assumed to be "non-word" characters when PCRE2_UCP is + not set. When it is set, use Unicode properties if available, even when not + in UTF mode. Remember the earliest and latest consulted characters. */ + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + if (Feptr == mb->start_subject) prev_is_word = FALSE; else + { + PCRE2_SPTR lastptr = Feptr - 1; +#ifdef SUPPORT_UNICODE + if (utf) + { + BACKCHAR(lastptr); + GETCHAR(fc, lastptr); + } + else +#endif /* SUPPORT_UNICODE */ + fc = *lastptr; + if (lastptr < mb->start_used_ptr) mb->start_used_ptr = lastptr; +#ifdef SUPPORT_UNICODE + if ((mb->poptions & PCRE2_UCP) != 0) + { + if (fc == '_') prev_is_word = TRUE; else + { + int cat = UCD_CATEGORY(fc); + prev_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif /* SUPPORT_UNICODE */ + prev_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0; + } + + /* Get status of next character */ + + if (Feptr >= mb->end_subject) + { + SCHECK_PARTIAL(); + cur_is_word = FALSE; + } + else + { + PCRE2_SPTR nextptr = Feptr + 1; +#ifdef SUPPORT_UNICODE + if (utf) + { + FORWARDCHARTEST(nextptr, mb->end_subject); + GETCHAR(fc, Feptr); + } + else +#endif /* SUPPORT_UNICODE */ + fc = *Feptr; + if (nextptr > mb->last_used_ptr) mb->last_used_ptr = nextptr; +#ifdef SUPPORT_UNICODE + if ((mb->poptions & PCRE2_UCP) != 0) + { + if (fc == '_') cur_is_word = TRUE; else + { + int cat = UCD_CATEGORY(fc); + cur_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif /* SUPPORT_UNICODE */ + cur_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0; + } + + /* Now see if the situation is what we want */ + + if ((*Fecode++ == OP_WORD_BOUNDARY)? + cur_is_word == prev_is_word : cur_is_word != prev_is_word) + RRETURN(MATCH_NOMATCH); + break; + + + /* ===================================================================== */ + /* Backtracking (*VERB)s, with and without arguments. Note that if the + pattern is successfully matched, we do not come back from RMATCH. */ + + case OP_MARK: + Fmark = mb->nomatch_mark = Fecode + 2; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM12); + + /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an + argument, and we must check whether that argument matches this MARK's + argument. It is passed back in mb->verb_skip_ptr. If it does match, we + return MATCH_SKIP with mb->verb_skip_ptr now pointing to the subject + position that corresponds to this mark. Otherwise, pass back the return + code unaltered. */ + + if (rrc == MATCH_SKIP_ARG && + PRIV(strcmp)(Fecode + 2, mb->verb_skip_ptr) == 0) + { + mb->verb_skip_ptr = Feptr; /* Pass back current position */ + RRETURN(MATCH_SKIP); + } + RRETURN(rrc); + + case OP_FAIL: + RRETURN(MATCH_NOMATCH); + + /* Record the current recursing group number in mb->verb_current_recurse + when a backtracking return such as MATCH_COMMIT is given. This enables the + recurse processing to catch verbs from within the recursion. */ + + case OP_COMMIT: + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM13); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_COMMIT); + + case OP_COMMIT_ARG: + Fmark = mb->nomatch_mark = Fecode + 2; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM36); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_COMMIT); + + case OP_PRUNE: + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM14); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_PRUNE); + + case OP_PRUNE_ARG: + Fmark = mb->nomatch_mark = Fecode + 2; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM15); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_PRUNE); + + case OP_SKIP: + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM16); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_skip_ptr = Feptr; /* Pass back current position */ + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_SKIP); + + /* Note that, for Perl compatibility, SKIP with an argument does NOT set + nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was + not a matching mark, we have to re-run the match, ignoring the SKIP_ARG + that failed and any that precede it (either they also failed, or were not + triggered). To do this, we maintain a count of executed SKIP_ARGs. If a + SKIP_ARG gets to top level, the match is re-run with mb->ignore_skip_arg + set to the count of the one that failed. */ + + case OP_SKIP_ARG: + mb->skip_arg_count++; + if (mb->skip_arg_count <= mb->ignore_skip_arg) + { + Fecode += PRIV(OP_lengths)[*Fecode] + Fecode[1]; + break; + } + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM17); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + + /* Pass back the current skip name and return the special MATCH_SKIP_ARG + return code. This will either be caught by a matching MARK, or get to the + top, where it causes a rematch with mb->ignore_skip_arg set to the value of + mb->skip_arg_count. */ + + mb->verb_skip_ptr = Fecode + 2; + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_SKIP_ARG); + + /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that + the branch in which it occurs can be determined. */ + + case OP_THEN: + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM18); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_ecode_ptr = Fecode; + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_THEN); + + case OP_THEN_ARG: + Fmark = mb->nomatch_mark = Fecode + 2; + RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM19); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + mb->verb_ecode_ptr = Fecode; + mb->verb_current_recurse = Fcurrent_recurse; + RRETURN(MATCH_THEN); + + + /* ===================================================================== */ + /* There's been some horrible disaster. Arrival here can only mean there is + something seriously wrong in the code above or the OP_xxx definitions. */ + + default: + return PCRE2_ERROR_INTERNAL; + } + + /* Do not insert any code in here without much thought; it is assumed + that "continue" in the code above comes out to here to repeat the main + loop. */ + + } /* End of main loop */ +/* Control never reaches here */ + + +/* ========================================================================= */ +/* The RRETURN() macro jumps here. The number that is saved in Freturn_id +indicates which label we actually want to return to. The value in Frdepth is +the index number of the frame in the vector. The return value has been placed +in rrc. */ + +#define LBL(val) case val: goto L_RM##val; + +RETURN_SWITCH: +if (Frdepth == 0) return rrc; /* Exit from the top level */ +F = (heapframe *)((char *)F - Fback_frame); /* Backtrack */ +mb->cb->callout_flags |= PCRE2_CALLOUT_BACKTRACK; /* Note for callouts */ + +#ifdef DEBUG_SHOW_RMATCH +fprintf(stderr, "++ RETURN %d to %d\n", rrc, Freturn_id); +#endif + +switch (Freturn_id) + { + LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8) + LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(16) + LBL(17) LBL(18) LBL(19) LBL(20) LBL(21) LBL(22) LBL(23) LBL(24) + LBL(25) LBL(26) LBL(27) LBL(28) LBL(29) LBL(30) LBL(31) LBL(32) + LBL(33) LBL(34) LBL(35) LBL(36) + +#ifdef SUPPORT_WIDE_CHARS + LBL(100) LBL(101) +#endif + +#ifdef SUPPORT_UNICODE + LBL(200) LBL(201) LBL(202) LBL(203) LBL(204) LBL(205) LBL(206) + LBL(207) LBL(208) LBL(209) LBL(210) LBL(211) LBL(212) LBL(213) + LBL(214) LBL(215) LBL(216) LBL(217) LBL(218) LBL(219) LBL(220) + LBL(221) LBL(222) +#endif + + default: + return PCRE2_ERROR_INTERNAL; + } +#undef LBL +} + + +/************************************************* +* Match a Regular Expression * +*************************************************/ + +/* This function applies a compiled pattern to a subject string and picks out +portions of the string if it matches. Two elements in the vector are set for +each substring: the offsets to the start and end of the substring. + +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 + mcontext points a PCRE2 context + +Returns: > 0 => success; value is the number of ovector pairs filled + = 0 => success, but ovector is not big enough + -1 => failed to match (PCRE2_ERROR_NOMATCH) + -2 => partial match (PCRE2_ERROR_PARTIAL) + < -2 => some kind of unexpected problem +*/ + +PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION +pcre2_match(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) +{ +int rc; +const uint8_t *start_bits = NULL; + +const pcre2_real_code *re = (const pcre2_real_code *)code; + +BOOL anchored; +BOOL firstline; +BOOL has_first_cu = FALSE; +BOOL has_req_cu = FALSE; +BOOL startline; +BOOL utf; + +PCRE2_UCHAR first_cu = 0; +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; +PCRE2_SPTR start_partial = NULL; +PCRE2_SPTR match_partial = NULL; + +PCRE2_SIZE frame_size; + +/* We need to have mb as a pointer to a match block, because the IS_NEWLINE +macro is used below, and it expects NLBLOCK to be defined as a pointer. */ + +pcre2_callout_block cb; +match_block actual_match_block; +match_block *mb = &actual_match_block; + +/* Allocate an initial vector of backtracking frames on the stack. If this +proves to be too small, it is replaced by a larger one on the heap. To get a +vector of the size required that is aligned for pointers, allocate it as a +vector of pointers. */ + +PCRE2_SPTR stack_frames_vector[START_FRAMES_SIZE/sizeof(PCRE2_SPTR)]; +mb->stack_frames = (heapframe *)stack_frames_vector; + +/* A length equal to PCRE2_ZERO_TERMINATED implies a zero-terminated +subject string. */ + +if (length == PCRE2_ZERO_TERMINATED) length = PRIV(strlen)(subject); +end_subject = subject + length; + +/* Plausibility checks */ + +if ((options & ~PUBLIC_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION; +if (code == NULL || subject == NULL || match_data == NULL) + return PCRE2_ERROR_NULL; +if (start_offset > length) return PCRE2_ERROR_BADOFFSET; + +/* Check that the first field in the block is the magic number. */ + +if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC; + +/* Check the code unit width. */ + +if ((re->flags & PCRE2_MODE_MASK) != PCRE2_CODE_UNIT_WIDTH/8) + return PCRE2_ERROR_BADMODE; + +/* PCRE2_NOTEMPTY and PCRE2_NOTEMPTY_ATSTART are match-time flags in the +options variable for this function. Users of PCRE2 who are not calling the +function directly would like to have a way of setting these flags, in the same +way that they can set pcre2_compile() flags like PCRE2_NO_AUTOPOSSESS with +constructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and +(*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which we now +transfer to the options for this function. The bits are guaranteed to be +adjacent, but do not have the same values. This bit of Boolean trickery assumes +that the match-time bits are not more significant than the flag bits. If by +accident this is not the case, a compile-time division by zero error will +occur. */ + +#define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET) +#define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART) +options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1))); +#undef FF +#undef OO + +/* These two settings are used in the code for checking a UTF string that +follows immediately afterwards. Other values in the mb block are used only +during interpretive processing, not when the JIT support is in use, so they are +set up later. */ + +utf = (re->overall_options & PCRE2_UTF) != 0; +mb->partial = ((options & PCRE2_PARTIAL_HARD) != 0)? 2 : + ((options & PCRE2_PARTIAL_SOFT) != 0)? 1 : 0; + +/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same +time. */ + +if (mb->partial != 0 && + ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0) + return PCRE2_ERROR_BADOPTION; + +/* 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. 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) + { + PCRE2_SPTR check_subject = start_match; /* start_match includes offset */ + + if (start_offset > 0) + { +#if PCRE2_CODE_UNIT_WIDTH != 32 + 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 != NULL && 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 +an unsupported option is set or if JIT returns BADOPTION (which means that the +selected normal or partial matching mode was not compiled). */ + +#ifdef SUPPORT_JIT +if (re->executable_jit != NULL && (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0) + { + rc = pcre2_jit_match(code, subject, length, start_offset, options, + match_data, mcontext); + if (rc != PCRE2_ERROR_JIT_BADOPTION) return rc; + } +#endif + +/* Carry on with non-JIT matching. A NULL match context means "use a default +context", but we take the memory control functions from the pattern. */ + +if (mcontext == NULL) + { + mcontext = (pcre2_match_context *)(&PRIV(default_match_context)); + mb->memctl = re->memctl; + } +else mb->memctl = mcontext->memctl; + +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; + +/* Initialize and set up the fixed fields in the callout block, with a pointer +in the match block. */ + +mb->cb = &cb; +cb.version = 2; +cb.subject = subject; +cb.subject_length = (PCRE2_SIZE)(end_subject - subject); +cb.callout_flags = 0; + +/* Fill in the remaining fields in the match block. */ + +mb->callout = mcontext->callout; +mb->callout_data = mcontext->callout_data; + +mb->start_subject = subject; +mb->start_offset = start_offset; +mb->end_subject = end_subject; +mb->hasthen = (re->flags & PCRE2_HASTHEN) != 0; + +mb->moptions = options; /* Match options */ +mb->poptions = re->overall_options; /* Pattern options */ + +mb->ignore_skip_arg = 0; +mb->mark = mb->nomatch_mark = NULL; /* In case never set */ +mb->hitend = FALSE; + +/* The name table is needed for finding all the numbers associated with a +given name, for condition testing. The code follows the name table. */ + +mb->name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)); +mb->name_count = re->name_count; +mb->name_entry_size = re->name_entry_size; +mb->start_code = mb->name_table + re->name_count * re->name_entry_size; + +/* Process the \R and newline settings. */ + +mb->bsr_convention = re->bsr_convention; +mb->nltype = NLTYPE_FIXED; +switch(re->newline_convention) + { + case PCRE2_NEWLINE_CR: + mb->nllen = 1; + mb->nl[0] = CHAR_CR; + break; + + case PCRE2_NEWLINE_LF: + mb->nllen = 1; + mb->nl[0] = CHAR_NL; + break; + + case PCRE2_NEWLINE_NUL: + mb->nllen = 1; + mb->nl[0] = CHAR_NUL; + break; + + case PCRE2_NEWLINE_CRLF: + mb->nllen = 2; + mb->nl[0] = CHAR_CR; + mb->nl[1] = CHAR_NL; + break; + + case PCRE2_NEWLINE_ANY: + mb->nltype = NLTYPE_ANY; + break; + + case PCRE2_NEWLINE_ANYCRLF: + mb->nltype = NLTYPE_ANYCRLF; + break; + + default: return PCRE2_ERROR_INTERNAL; + } + +/* The backtracking frames have fixed data at the front, and a PCRE2_SIZE +vector at the end, whose size depends on the number of capturing parentheses in +the pattern. It is not used at all if there are no capturing parentheses. + + frame_size is the total size of each frame + mb->frame_vector_size is the total usable size of the vector (rounded down + to a whole number of frames) + +The last of these is changed within the match() function if the frame vector +has to be expanded. We therefore put it into the match block so that it is +correct when calling match() more than once for non-anchored patterns. */ + +frame_size = offsetof(heapframe, ovector) + + re->top_bracket * 2 * sizeof(PCRE2_SIZE); + +/* Limits set in the pattern override the match context only if they are +smaller. */ + +mb->heap_limit = (mcontext->heap_limit < re->limit_heap)? + mcontext->heap_limit : re->limit_heap; + +mb->match_limit = (mcontext->match_limit < re->limit_match)? + mcontext->match_limit : re->limit_match; + +mb->match_limit_depth = (mcontext->depth_limit < re->limit_depth)? + mcontext->depth_limit : re->limit_depth; + +/* If a pattern has very many capturing parentheses, the frame size may be very +large. Ensure that there are at least 10 available frames by getting an initial +vector on the heap if necessary, except when the heap limit prevents this. Get +fewer if possible. (The heap limit is in kibibytes.) */ + +if (frame_size <= START_FRAMES_SIZE/10) + { + mb->match_frames = mb->stack_frames; /* Initial frame vector on the stack */ + mb->frame_vector_size = ((START_FRAMES_SIZE/frame_size) * frame_size); + } +else + { + mb->frame_vector_size = frame_size * 10; + if ((mb->frame_vector_size / 1024) > mb->heap_limit) + { + if (frame_size > mb->heap_limit * 1024) return PCRE2_ERROR_HEAPLIMIT; + mb->frame_vector_size = ((mb->heap_limit * 1024)/frame_size) * frame_size; + } + mb->match_frames = mb->memctl.malloc(mb->frame_vector_size, + mb->memctl.memory_data); + if (mb->match_frames == NULL) return PCRE2_ERROR_NOMEMORY; + } + +mb->match_frames_top = + (heapframe *)((char *)mb->match_frames + mb->frame_vector_size); + +/* Write to the ovector within the first frame to mark every capture unset and +to avoid uninitialized memory read errors when it is copied to a new frame. */ + +memset((char *)(mb->match_frames) + offsetof(heapframe, ovector), 0xff, + re->top_bracket * 2 * sizeof(PCRE2_SIZE)); + +/* Pointers to the individual character tables */ + +mb->lcc = re->tables + lcc_offset; +mb->fcc = re->tables + fcc_offset; +mb->ctypes = re->tables + ctypes_offset; + +/* Set up the first code unit to match, if available. If there's no first code +unit there may be a bitmap of possible first characters. */ + +if ((re->flags & PCRE2_FIRSTSET) != 0) + { + has_first_cu = TRUE; + first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit); + if ((re->flags & PCRE2_FIRSTCASELESS) != 0) + { + first_cu2 = TABLE_GET(first_cu, mb->fcc, first_cu); +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 + if (utf && first_cu > 127) first_cu2 = UCD_OTHERCASE(first_cu); +#endif + } + } +else + if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0) + start_bits = re->start_bitmap; + +/* There may also be a "last known required character" set. */ + +if ((re->flags & PCRE2_LASTSET) != 0) + { + has_req_cu = TRUE; + req_cu = req_cu2 = (PCRE2_UCHAR)(re->last_codeunit); + if ((re->flags & PCRE2_LASTCASELESS) != 0) + { + req_cu2 = TABLE_GET(req_cu, mb->fcc, req_cu); +#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 8 + if (utf && req_cu > 127) req_cu2 = UCD_OTHERCASE(req_cu); +#endif + } + } + + +/* ==========================================================================*/ + +/* Loop for handling unanchored repeated matching attempts; for anchored regexs +the loop runs just once. */ + +for(;;) + { + PCRE2_SPTR new_start_match; + + /* ----------------- Start of match optimizations ---------------- */ + + /* There are some optimizations that avoid running the match if a known + starting point is not found, or if a known later code unit is not present. + However, there is an option (settable at compile time) that disables these, + for testing and for ensuring that all callouts do actually occur. */ + + if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0) + { + /* If firstline is TRUE, the start of the match is constrained to the first + line of a multiline string. That is, the match must be before or at the + first newline following the start of matching. Temporarily adjust + end_subject so that we stop the scans for a first code unit at a newline. + If the match fails at the newline, later code breaks the loop. */ + + if (firstline) + { + PCRE2_SPTR t = start_match; +#ifdef SUPPORT_UNICODE + if (utf) + { + while (t < end_subject && !IS_NEWLINE(t)) + { + t++; + ACROSSCHAR(t < end_subject, t, t++); + } + } + else +#endif + while (t < end_subject && !IS_NEWLINE(t)) t++; + end_subject = t; + } + + /* Anchored: check the first code unit if one is recorded. This may seem + pointless but it can help in detecting a no match case without scanning for + the required code unit. */ + + if (anchored) + { + if (has_first_cu || start_bits != NULL) + { + BOOL ok = start_match < end_subject; + if (ok) + { + PCRE2_UCHAR c = UCHAR21TEST(start_match); + ok = has_first_cu && (c == first_cu || c == first_cu2); + if (!ok && start_bits != NULL) + { +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (c > 255) c = 255; +#endif + ok = (start_bits[c/8] & (1 << (c&7))) != 0; + } + } + if (!ok) + { + rc = MATCH_NOMATCH; + break; + } + } + } + + /* Not anchored. Advance to a unique first code unit if there is one. In + 8-bit mode, the use of memchr() gives a big speed up, even though we have + to call it twice in caseless mode, in order to find the earliest occurrence + of the character in either of its cases. */ + + else + { + if (has_first_cu) + { + if (first_cu != first_cu2) /* Caseless */ + { +#if PCRE2_CODE_UNIT_WIDTH != 8 + PCRE2_UCHAR smc; + while (start_match < end_subject && + (smc = UCHAR21TEST(start_match)) != first_cu && + smc != first_cu2) + start_match++; +#else /* 8-bit code units */ + PCRE2_SPTR pp1 = + memchr(start_match, first_cu, end_subject-start_match); + PCRE2_SPTR pp2 = + memchr(start_match, first_cu2, end_subject-start_match); + if (pp1 == NULL) + start_match = (pp2 == NULL)? end_subject : pp2; + else + start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2; +#endif + } + + /* The caseful case */ + + 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 + } + + /* If we can't find the required code unit, having reached the true end + of the subject, break the bumpalong loop, to force a match failure, + except when doing partial matching, when we let the next cycle run at + the end of the subject. To see why, consider the pattern /(?<=abc)def/, + which partially matches "abc", even though the string does not contain + the starting character "d". If we have not reached the true end of the + subject (PCRE2_FIRSTLINE caused end_subject to be temporarily modified) + we also let the cycle run, because the matching string is legitimately + allowed to start with the first code unit of a newline. */ + + if (!mb->partial && start_match >= mb->end_subject) + { + rc = MATCH_NOMATCH; + break; + } + } + + /* If there's no first code unit, advance to just after a linebreak for a + multiline match if required. */ + + else if (startline) + { + if (start_match > mb->start_subject + start_offset) + { +#ifdef SUPPORT_UNICODE + if (utf) + { + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + { + start_match++; + ACROSSCHAR(start_match < end_subject, start_match, start_match++); + } + } + else +#endif + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + start_match++; + + /* If we have just passed a CR and the newline option is ANY or + ANYCRLF, and we are now at a LF, advance the match position by one + more code unit. */ + + if (start_match[-1] == CHAR_CR && + (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) && + start_match < end_subject && + UCHAR21TEST(start_match) == CHAR_NL) + start_match++; + } + } + + /* If there's no first code unit or a requirement for a multiline line + start, advance to a non-unique first code unit if any have been + identified. The bitmap contains only 256 bits. When code units are 16 or + 32 bits wide, all code units greater than 254 set the 255 bit. */ + + else if (start_bits != NULL) + { + while (start_match < end_subject) + { + uint32_t c = UCHAR21TEST(start_match); +#if PCRE2_CODE_UNIT_WIDTH != 8 + if (c > 255) c = 255; +#endif + if ((start_bits[c/8] & (1 << (c&7))) != 0) break; + start_match++; + } + + /* See comment above in first_cu checking about the next few lines. */ + + if (!mb->partial && start_match >= mb->end_subject) + { + rc = MATCH_NOMATCH; + break; + } + } + } /* End first code unit handling */ + + /* Restore fudged end_subject */ + + end_subject = mb->end_subject; + + /* The following two optimizations must be disabled for partial matching. */ + + if (!mb->partial) + { + /* The minimum matching length is a lower bound; no string of that length + may actually match the pattern. Although the value is, strictly, in + characters, we treat it as code units to avoid spending too much time in + this optimization. */ + + if (end_subject - start_match < re->minlength) + { + rc = MATCH_NOMATCH; + break; + } + + /* If req_cu is set, we know that that code unit must appear in the + subject for the (non-partial) match to succeed. If the first code unit is + set, req_cu must be later in the subject; otherwise the test starts at + the match point. This optimization can save a huge amount of backtracking + in patterns with nested unlimited repeats that aren't going to match. + Writing separate code for caseful/caseless versions makes it go faster, + as does using an autoincrement and backing off on a match. As in the case + of the first code unit, using memchr() in the 8-bit library gives a big + speed up. Unlike the first_cu check above, we do not need to call + memchr() twice in the caseless case because we only need to check for the + presence of the character in either case, not find the first occurrence. + + HOWEVER: when the subject string is very, very long, searching to its end + can take a long time, and give bad performance on quite ordinary + patterns. This showed up when somebody was matching something like + /^\d+C/ on a 32-megabyte string... so we don't do this when the string is + sufficiently long. */ + + if (has_req_cu && end_subject - start_match < REQ_CU_MAX) + { + PCRE2_SPTR p = start_match + (has_first_cu? 1:0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it last time round the bumpalong loop. */ + + if (p > req_cu_ptr) + { + if (p < end_subject) + { + if (req_cu != req_cu2) /* Caseless */ + { +#if PCRE2_CODE_UNIT_WIDTH != 8 + do + { + uint32_t pp = UCHAR21INCTEST(p); + if (pp == req_cu || pp == req_cu2) { p--; break; } + } + while (p < end_subject); + +#else /* 8-bit code units */ + PCRE2_SPTR pp = p; + p = memchr(pp, req_cu, end_subject - pp); + if (p == NULL) + { + p = memchr(pp, req_cu2, end_subject - pp); + if (p == NULL) p = end_subject; + } +#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */ + } + + /* The caseful case */ + + else + { +#if PCRE2_CODE_UNIT_WIDTH != 8 + do + { + if (UCHAR21INCTEST(p) == req_cu) { p--; break; } + } + while (p < end_subject); + +#else /* 8-bit code units */ + p = memchr(p, req_cu, end_subject - p); + if (p == NULL) p = end_subject; +#endif + } + } + + /* If we can't find the required code unit, break the bumpalong loop, + forcing a match failure. */ + + if (p >= end_subject) + { + rc = MATCH_NOMATCH; + break; + } + + /* If we have found the required code unit, save the point where we + found it, so that we don't search again next time round the bumpalong + loop if the start hasn't yet passed this code unit. */ + + req_cu_ptr = p; + } + } + } + } + + /* ------------ 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. */ + + cb.start_match = (PCRE2_SIZE)(start_match - subject); + cb.callout_flags |= PCRE2_CALLOUT_STARTMATCH; + + mb->start_used_ptr = start_match; + mb->last_used_ptr = start_match; + mb->match_call_count = 0; + mb->end_offset_top = 0; + mb->skip_arg_count = 0; + + rc = match(start_match, mb->start_code, match_data->ovector, + match_data->oveccount, re->top_bracket, frame_size, mb); + + if (mb->hitend && start_partial == NULL) + { + start_partial = mb->start_used_ptr; + match_partial = start_match; + } + + switch(rc) + { + /* If MATCH_SKIP_ARG reaches this level it means that a MARK that matched + the SKIP's arg was not found. In this circumstance, Perl ignores the SKIP + entirely. The only way we can do that is to re-do the match at the same + point, with a flag to force SKIP with an argument to be ignored. Just + treating this case as NOMATCH does not work because it does not check other + alternatives in patterns such as A(*SKIP:A)B|AC when the subject is AC. */ + + case MATCH_SKIP_ARG: + new_start_match = start_match; + mb->ignore_skip_arg = mb->skip_arg_count; + break; + + /* SKIP passes back the next starting point explicitly, but if it is no + greater than the match we have just done, treat it as NOMATCH. */ + + case MATCH_SKIP: + if (mb->verb_skip_ptr > start_match) + { + new_start_match = mb->verb_skip_ptr; + break; + } + /* Fall through */ + + /* NOMATCH and PRUNE advance by one character. THEN at this level acts + exactly like PRUNE. Unset ignore SKIP-with-argument. */ + + case MATCH_NOMATCH: + case MATCH_PRUNE: + case MATCH_THEN: + mb->ignore_skip_arg = 0; + new_start_match = start_match + 1; +#ifdef SUPPORT_UNICODE + if (utf) + ACROSSCHAR(new_start_match < end_subject, new_start_match, + new_start_match++); +#endif + break; + + /* COMMIT disables the bumpalong, but otherwise behaves as NOMATCH. */ + + case MATCH_COMMIT: + rc = MATCH_NOMATCH; + goto ENDLOOP; + + /* Any other return is either a match, or some kind of error. */ + + default: + goto ENDLOOP; + } + + /* Control reaches here for the various types of "no match at this point" + result. Reset the code to MATCH_NOMATCH for subsequent checking. */ + + rc = MATCH_NOMATCH; + + /* If PCRE2_FIRSTLINE is set, the match must happen before or at the first + newline in the subject (though it may continue over the newline). Therefore, + if we have just failed to match, starting at a newline, do not continue. */ + + if (firstline && IS_NEWLINE(start_match)) break; + + /* Advance to new matching position */ + + start_match = new_start_match; + + /* Break the loop if the pattern is anchored or if we have passed the end of + the subject. */ + + if (anchored || start_match > end_subject) break; + + /* If we have just passed a CR and we are now at a LF, and the pattern does + not contain any explicit matches for \r or \n, and the newline option is CRLF + or ANY or ANYCRLF, advance the match position by one more code unit. In + normal matching start_match will aways be greater than the first position at + this stage, but a failed *SKIP can cause a return at the same point, which is + why the first test exists. */ + + if (start_match > subject + start_offset && + start_match[-1] == CHAR_CR && + start_match < end_subject && + *start_match == CHAR_NL && + (re->flags & PCRE2_HASCRORLF) == 0 && + (mb->nltype == NLTYPE_ANY || + mb->nltype == NLTYPE_ANYCRLF || + mb->nllen == 2)) + start_match++; + + mb->mark = NULL; /* Reset for start of next match attempt */ + } /* End of for(;;) "bumpalong" loop */ + +/* ==========================================================================*/ + +/* When we reach here, one of the following stopping conditions is true: + +(1) The match succeeded, either completely, or partially; + +(2) The pattern is anchored or the match was failed after (*COMMIT); + +(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 + the subject. + +(5) Some kind of error occurred. + +*/ + +ENDLOOP: + +/* Release an enlarged frame vector that is on the heap. */ + +if (mb->match_frames != mb->stack_frames) + mb->memctl.free(mb->match_frames, mb->memctl.memory_data); + +/* Fill in fields that are always returned in the match data. */ + +match_data->code = re; +match_data->subject = subject; +match_data->mark = mb->mark; +match_data->matchedby = PCRE2_MATCHEDBY_INTERPRETER; + +/* Handle a fully successful match. Set the return code to the number of +captured strings, or 0 if there were too many to fit into the ovector, and then +set the remaining returned values before returning. */ + +if (rc == MATCH_MATCH) + { + match_data->rc = ((int)mb->end_offset_top >= 2 * match_data->oveccount)? + 0 : (int)mb->end_offset_top/2 + 1; + match_data->startchar = start_match - subject; + match_data->leftchar = mb->start_used_ptr - subject; + match_data->rightchar = ((mb->last_used_ptr > mb->end_match_ptr)? + mb->last_used_ptr : mb->end_match_ptr) - subject; + return match_data->rc; + } + +/* Control gets here if there has been a partial match, an error, or if the +overall match attempt has failed at all permitted starting positions. Any mark +data is in the nomatch_mark field. */ + +match_data->mark = mb->nomatch_mark; + +/* For anything other than nomatch or partial match, just return the code. */ + +if (rc != MATCH_NOMATCH && rc != PCRE2_ERROR_PARTIAL) match_data->rc = rc; + +/* Handle a partial match. */ + +else if (match_partial != NULL) + { + match_data->ovector[0] = match_partial - subject; + match_data->ovector[1] = end_subject - subject; + match_data->startchar = match_partial - subject; + match_data->leftchar = start_partial - subject; + match_data->rightchar = end_subject - subject; + match_data->rc = PCRE2_ERROR_PARTIAL; + } + +/* Else this is the classic nomatch case. */ + +else match_data->rc = PCRE2_ERROR_NOMATCH; + +return match_data->rc; +} + +/* End of pcre2_match.c */ diff --git a/pcre2-10.22/src/pcre2_match_data.c b/pcre2-10.32/src/pcre2_match_data.c similarity index 95% rename from pcre2-10.22/src/pcre2_match_data.c rename to pcre2-10.32/src/pcre2_match_data.c index 85ac99834..b297f326b 100644 --- a/pcre2-10.22/src/pcre2_match_data.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2017 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -51,7 +51,7 @@ POSSIBILITY OF SUCH DAMAGE. * Create a match data block given ovector size * *************************************************/ -/* A minimum of 1 is imposed on the number of ovector triplets. */ +/* A minimum of 1 is imposed on the number of ovector pairs. */ PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION pcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext) @@ -59,7 +59,7 @@ pcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext) pcre2_match_data *yield; if (oveccount < 1) oveccount = 1; yield = PRIV(memctl_malloc)( - sizeof(pcre2_match_data) + 3*oveccount*sizeof(PCRE2_SIZE), + offsetof(pcre2_match_data, ovector) + 2*oveccount*sizeof(PCRE2_SIZE), (pcre2_memctl *)gcontext); if (yield == NULL) return NULL; yield->oveccount = oveccount; diff --git a/pcre2-10.22/src/pcre2_newline.c b/pcre2-10.32/src/pcre2_newline.c similarity index 100% rename from pcre2-10.22/src/pcre2_newline.c rename to pcre2-10.32/src/pcre2_newline.c diff --git a/pcre2-10.22/src/pcre2_ord2utf.c b/pcre2-10.32/src/pcre2_ord2utf.c similarity index 99% rename from pcre2-10.22/src/pcre2_ord2utf.c rename to pcre2-10.32/src/pcre2_ord2utf.c index 75252b763..140373099 100644 --- a/pcre2-10.22/src/pcre2_ord2utf.c +++ b/pcre2-10.32/src/pcre2_ord2utf.c @@ -83,7 +83,7 @@ PRIV(ord2utf)(uint32_t cvalue, PCRE2_UCHAR *buffer) /* Convert to UTF-8 */ #if PCRE2_CODE_UNIT_WIDTH == 8 -register int i, j; +int i, j; for (i = 0; i < PRIV(utf8_table1_size); i++) if ((int)cvalue <= PRIV(utf8_table1)[i]) break; buffer += i; diff --git a/pcre2-10.22/src/pcre2_pattern_info.c b/pcre2-10.32/src/pcre2_pattern_info.c similarity index 93% rename from pcre2-10.22/src/pcre2_pattern_info.c rename to pcre2-10.32/src/pcre2_pattern_info.c index 5b32a905b..a29f5eff6 100644 --- a/pcre2-10.22/src/pcre2_pattern_info.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -75,10 +75,13 @@ if (where == NULL) /* Requests field length */ case PCRE2_INFO_BACKREFMAX: case PCRE2_INFO_BSR: case PCRE2_INFO_CAPTURECOUNT: + case PCRE2_INFO_DEPTHLIMIT: + case PCRE2_INFO_EXTRAOPTIONS: case PCRE2_INFO_FIRSTCODETYPE: case PCRE2_INFO_FIRSTCODEUNIT: case PCRE2_INFO_HASBACKSLASHC: case PCRE2_INFO_HASCRORLF: + case PCRE2_INFO_HEAPLIMIT: case PCRE2_INFO_JCHANGED: case PCRE2_INFO_LASTCODETYPE: case PCRE2_INFO_LASTCODEUNIT: @@ -89,7 +92,6 @@ if (where == NULL) /* Requests field length */ case PCRE2_INFO_NAMEENTRYSIZE: case PCRE2_INFO_NAMECOUNT: case PCRE2_INFO_NEWLINE: - case PCRE2_INFO_RECURSIONLIMIT: return sizeof(uint32_t); case PCRE2_INFO_FIRSTBITMAP: @@ -97,6 +99,7 @@ if (where == NULL) /* Requests field length */ case PCRE2_INFO_JITSIZE: case PCRE2_INFO_SIZE: + case PCRE2_INFO_FRAMESIZE: return sizeof(size_t); case PCRE2_INFO_NAMETABLE: @@ -137,6 +140,15 @@ switch(what) *((uint32_t *)where) = re->top_bracket; break; + case PCRE2_INFO_DEPTHLIMIT: + *((uint32_t *)where) = re->limit_depth; + if (re->limit_depth == UINT32_MAX) return PCRE2_ERROR_UNSET; + break; + + case PCRE2_INFO_EXTRAOPTIONS: + *((uint32_t *)where) = re->extra_options; + break; + case PCRE2_INFO_FIRSTCODETYPE: *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)? 1 : ((re->flags & PCRE2_STARTLINE) != 0)? 2 : 0; @@ -152,6 +164,11 @@ switch(what) &(re->start_bitmap[0]) : NULL; break; + case PCRE2_INFO_FRAMESIZE: + *((size_t *)where) = offsetof(heapframe, ovector) + + re->top_bracket * 2 * sizeof(PCRE2_SIZE); + break; + case PCRE2_INFO_HASBACKSLASHC: *((uint32_t *)where) = (re->flags & PCRE2_HASBKC) != 0; break; @@ -160,6 +177,11 @@ switch(what) *((uint32_t *)where) = (re->flags & PCRE2_HASCRORLF) != 0; break; + case PCRE2_INFO_HEAPLIMIT: + *((uint32_t *)where) = re->limit_heap; + if (re->limit_heap == UINT32_MAX) return PCRE2_ERROR_UNSET; + break; + case PCRE2_INFO_JCHANGED: *((uint32_t *)where) = (re->flags & PCRE2_JCHANGED) != 0; break; @@ -215,11 +237,6 @@ switch(what) *((uint32_t *)where) = re->newline_convention; break; - case PCRE2_INFO_RECURSIONLIMIT: - *((uint32_t *)where) = re->limit_recursion; - if (re->limit_recursion == UINT32_MAX) return PCRE2_ERROR_UNSET; - break; - case PCRE2_INFO_SIZE: *((size_t *)where) = re->blocksize; break; @@ -255,11 +272,15 @@ pcre2_real_code *re = (pcre2_real_code *)code; pcre2_callout_enumerate_block cb; PCRE2_SPTR cc; #ifdef SUPPORT_UNICODE -BOOL utf = (re->overall_options & PCRE2_UTF) != 0; +BOOL utf; #endif if (re == NULL) return PCRE2_ERROR_NULL; +#ifdef SUPPORT_UNICODE +utf = (re->overall_options & PCRE2_UTF) != 0; +#endif + /* Check that the first field in the block is the magic number. If it is not, return with PCRE2_ERROR_BADMAGIC. */ @@ -369,6 +390,7 @@ while (TRUE) #endif case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: diff --git a/pcre2-10.22/src/pcre2_printint.c b/pcre2-10.32/src/pcre2_printint.c similarity index 99% rename from pcre2-10.22/src/pcre2_printint.c rename to pcre2-10.32/src/pcre2_printint.c index 2d30926a7..bd10c6b1d 100644 --- a/pcre2-10.22/src/pcre2_printint.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -206,7 +206,7 @@ print_custring(FILE *f, PCRE2_SPTR ptr) { while (*ptr != '\0') { - register uint32_t c = *ptr++; + uint32_t c = *ptr++; if (PRINTABLE(c)) fprintf(f, "%c", c); else fprintf(f, "\\x{%x}", c); } } @@ -216,7 +216,7 @@ print_custring_bylen(FILE *f, PCRE2_SPTR ptr, PCRE2_UCHAR len) { for (; len > 0; len--) { - register uint32_t c = *ptr++; + uint32_t c = *ptr++; if (PRINTABLE(c)) fprintf(f, "%c", c); else fprintf(f, "\\x{%x}", c); } } @@ -340,7 +340,7 @@ for(;;) case OP_TABLE_LENGTH + ((sizeof(OP_names)/sizeof(const char *) == OP_TABLE_LENGTH) && (sizeof(OP_lengths) == OP_TABLE_LENGTH)): - break; + return; /* ========================================================================== */ case OP_END: @@ -393,7 +393,6 @@ for(;;) case OP_ASSERTBACK: case OP_ASSERTBACK_NOT: case OP_ONCE: - case OP_ONCE_NC: case OP_COND: case OP_SCOND: case OP_REVERSE: @@ -800,6 +799,7 @@ for(;;) break; case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: diff --git a/pcre2-10.22/src/pcre2_serialize.c b/pcre2-10.32/src/pcre2_serialize.c similarity index 88% rename from pcre2-10.22/src/pcre2_serialize.c rename to pcre2-10.32/src/pcre2_serialize.c index 0af26d8fc..cec1a035d 100644 --- a/pcre2-10.22/src/pcre2_serialize.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -127,7 +127,25 @@ dst_bytes += tables_length; for (i = 0; i < number_of_codes; i++) { re = (const pcre2_real_code *)(codes[i]); - memcpy(dst_bytes, (char *)re, re->blocksize); + (void)memcpy(dst_bytes, (char *)re, re->blocksize); + + /* Certain fields in the compiled code block are re-set during + deserialization. In order to ensure that the serialized data stream is always + the same for the same pattern, set them to zero here. We can't assume the + copy of the pattern is correctly aligned for accessing the fields as part of + a structure. Note the use of sizeof(void *) in the second of these, to + specify the size of a pointer. If sizeof(uint8_t *) is used (tables is a + pointer to uint8_t), gcc gives a warning because the first argument is also a + pointer to uint8_t. Casting the first argument to (void *) can stop this, but + it didn't stop Coverity giving the same complaint. */ + + (void)memset(dst_bytes + offsetof(pcre2_real_code, memctl), 0, + sizeof(pcre2_memctl)); + (void)memset(dst_bytes + offsetof(pcre2_real_code, tables), 0, + sizeof(void *)); + (void)memset(dst_bytes + offsetof(pcre2_real_code, executable_jit), 0, + sizeof(void *)); + dst_bytes += re->blocksize; } @@ -214,7 +232,10 @@ for (i = 0; i < number_of_codes; i++) if (dst_re->magic_number != MAGIC_NUMBER || dst_re->name_entry_size > MAX_NAME_SIZE + IMM2_SIZE + 1 || dst_re->name_count > MAX_NAME_COUNT) + { + memctl->free(dst_re, memctl->memory_data); return PCRE2_ERROR_BADSERIALIZEDDATA; + } /* At the moment only one table is supported. */ diff --git a/pcre2-10.22/src/pcre2_string_utils.c b/pcre2-10.32/src/pcre2_string_utils.c similarity index 84% rename from pcre2-10.22/src/pcre2_string_utils.c rename to pcre2-10.32/src/pcre2_string_utils.c index 2a1f28262..d6be01acf 100644 --- a/pcre2-10.22/src/pcre2_string_utils.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -50,6 +50,42 @@ functions work only on 8-bit data. */ #include "pcre2_internal.h" +/************************************************* +* Emulated memmove() for systems without it * +*************************************************/ + +/* This function can make use of bcopy() if it is available. Otherwise do it by +steam, as there some non-Unix environments that lack both memmove() and +bcopy(). */ + +#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE) +void * +PRIV(memmove)(void *d, const void *s, size_t n) +{ +#ifdef HAVE_BCOPY +bcopy(s, d, n); +return d; +#else +size_t i; +unsigned char *dest = (unsigned char *)d; +const unsigned char *src = (const unsigned char *)s; +if (dest > src) + { + dest += n; + src += n; + for (i = 0; i < n; ++i) *(--dest) = *(--src); + return (void *)dest; + } +else + { + for (i = 0; i < n; ++i) *dest++ = *src++; + return (void *)(dest - n); + } +#endif /* not HAVE_BCOPY */ +} +#endif /* not VPCOMPAT && not HAVE_MEMMOVE */ + + /************************************************* * Compare two zero-terminated PCRE2 strings * *************************************************/ diff --git a/pcre2-10.22/src/pcre2_study.c b/pcre2-10.32/src/pcre2_study.c similarity index 90% rename from pcre2-10.22/src/pcre2_study.c rename to pcre2-10.32/src/pcre2_study.c index db0826674..acbf98b41 100644 --- a/pcre2-10.22/src/pcre2_study.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -46,9 +46,11 @@ collecting data (e.g. minimum matching length). */ #include "config.h" #endif - #include "pcre2_internal.h" +/* The maximum remembered capturing brackets minimum. */ + +#define MAX_CACHE_BACKREF 128 /* Set a bit in the starting code unit bit map. */ @@ -71,6 +73,12 @@ 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. +Backreference minimum lengths are cached to speed up multiple references. This +function is called only when the highest back reference in the pattern is less +than or equal to MAX_CACHE_BACKREF, which is one less than the size of the +caching vector. The zeroth element contains the number of the highest set +value. + Arguments: re compiled pattern block code pointer to start of group (the bracket) @@ -78,6 +86,7 @@ integer overflow for really crazy patterns cannot happen. utf UTF flag recurses chain of recurse_check to catch mutual recursion countptr pointer to call count (to catch over complexity) + backref_cache vector for caching back references. Returns: the minimum length -1 \C in UTF-8 mode @@ -90,7 +99,8 @@ Returns: the minimum length static int find_minlength(const pcre2_real_code *re, PCRE2_SPTR code, - PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses, int *countptr) + PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses, int *countptr, + int *backref_cache) { int length = -1; int prev_cap_recno = -1; @@ -101,8 +111,8 @@ 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; +int branchlength = 0; +PCRE2_UCHAR *cc = (PCRE2_UCHAR *)code + 1 + LINK_SIZE; /* If this is a "could be empty" group, its minimum length is 0. */ @@ -124,7 +134,7 @@ for (;;) { int d, min, recno; PCRE2_UCHAR *cs, *ce; - register PCRE2_UCHAR op = *cc; + PCRE2_UCHAR op = *cc; if (branchlength >= UINT16_MAX) return UINT16_MAX; @@ -146,12 +156,12 @@ for (;;) } goto PROCESS_NON_CAPTURE; - /* There's a special case of OP_ONCE, when it is wrapped round an + case OP_BRA: + /* There's a special case of OP_BRA, when it is wrapped round a repeated 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; @@ -160,13 +170,13 @@ for (;;) } /* Fall through */ - case OP_ONCE_NC: - case OP_BRA: + case OP_ONCE: case OP_SBRA: case OP_BRAPOS: case OP_SBRAPOS: PROCESS_NON_CAPTURE: - d = find_minlength(re, cc, startcode, utf, recurses, countptr); + d = find_minlength(re, cc, startcode, utf, recurses, countptr, + backref_cache); if (d < 0) return d; branchlength += d; do cc += GET(cc, 1); while (*cc == OP_ALT); @@ -182,11 +192,12 @@ for (;;) case OP_SCBRA: case OP_CBRAPOS: case OP_SCBRAPOS: - recno = dupcapused? prev_cap_recno - 1 : (int)GET2(cc, 1+LINK_SIZE); - if (recno != prev_cap_recno) + recno = (int)GET2(cc, 1+LINK_SIZE); + if (dupcapused || recno != prev_cap_recno) { prev_cap_recno = recno; - prev_cap_d = find_minlength(re, cc, startcode, utf, recurses, countptr); + prev_cap_d = find_minlength(re, cc, startcode, utf, recurses, countptr, + backref_cache); if (prev_cap_d < 0) return prev_cap_d; } branchlength += prev_cap_d; @@ -456,38 +467,52 @@ for (;;) d = INT_MAX; - /* Scan all groups with the same name */ + /* Scan all groups with the same name; find the shortest. */ while (count-- > 0) { - ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, GET2(slot, 0)); - if (cs == NULL) return -2; - do ce += GET(ce, 1); while (*ce == OP_ALT); - if (cc > cs && cc < ce) /* Simple recursion */ - { - d = 0; - had_recurse = TRUE; - break; - } + int dd, i; + recno = GET2(slot, 0); + + if (recno <= backref_cache[0] && backref_cache[recno] >= 0) + dd = backref_cache[recno]; else { - recurse_check *r = recurses; - for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; - if (r != NULL) /* Mutual recursion */ + ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, recno); + if (cs == NULL) return -2; + do ce += GET(ce, 1); while (*ce == OP_ALT); + if (cc > cs && cc < ce) /* Simple recursion */ { - d = 0; + dd = 0; had_recurse = TRUE; - break; } else { - int dd; - this_recurse.prev = recurses; - this_recurse.group = cs; - dd = find_minlength(re, cs, startcode, utf, &this_recurse, countptr); - if (dd < d) d = dd; + recurse_check *r = recurses; + for (r = recurses; r != NULL; r = r->prev) + if (r->group == cs) break; + if (r != NULL) /* Mutual recursion */ + { + dd = 0; + had_recurse = TRUE; + } + else + { + this_recurse.prev = recurses; + this_recurse.group = cs; + dd = find_minlength(re, cs, startcode, utf, &this_recurse, + countptr, backref_cache); + if (dd < 0) return dd; + } } + + backref_cache[recno] = dd; + for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1; + backref_cache[0] = recno; } + + if (dd < d) d = dd; + if (d <= 0) break; /* No point looking at any more */ slot += re->name_entry_size; } } @@ -501,34 +526,48 @@ for (;;) case OP_REF: case OP_REFI: if (dupcapused) return -1; - if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0) + recno = GET2(cc, 1); + if (recno <= backref_cache[0] && backref_cache[recno] >= 0) + d = backref_cache[recno]; + else { - ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, GET2(cc, 1)); - if (cs == NULL) return -2; - do ce += GET(ce, 1); while (*ce == OP_ALT); - if (cc > cs && cc < ce) /* Simple recursion */ + int i; + if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0) { - d = 0; - had_recurse = TRUE; - } - else - { - recurse_check *r = recurses; - for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; - if (r != NULL) /* Mutual recursion */ + ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, recno); + if (cs == NULL) return -2; + do ce += GET(ce, 1); while (*ce == OP_ALT); + if (cc > cs && cc < ce) /* Simple recursion */ { d = 0; had_recurse = TRUE; } else { - this_recurse.prev = recurses; - this_recurse.group = cs; - d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr); + recurse_check *r = recurses; + for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break; + if (r != NULL) /* Mutual recursion */ + { + d = 0; + had_recurse = TRUE; + } + else + { + this_recurse.prev = recurses; + this_recurse.group = cs; + d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr, + backref_cache); + if (d < 0) return d; + } } } + else d = 0; + + backref_cache[recno] = d; + for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1; + backref_cache[0] = recno; } - else d = 0; + cc += 1 + IMM2_SIZE; /* Handle repeated back references */ @@ -601,7 +640,7 @@ for (;;) this_recurse.prev = recurses; this_recurse.group = cs; prev_recurse_d = find_minlength(re, cs, startcode, utf, &this_recurse, - countptr); + countptr, backref_cache); if (prev_recurse_d < 0) return prev_recurse_d; prev_recurse_recno = recno; branchlength += prev_recurse_d; @@ -668,6 +707,7 @@ for (;;) /* Skip these, but we need to add in the name length. */ case OP_MARK: + case OP_COMMIT_ARG: case OP_PRUNE_ARG: case OP_SKIP_ARG: case OP_THEN_ARG: @@ -747,6 +787,7 @@ if (utf) if (caseless) { +#ifdef SUPPORT_UNICODE if (utf) { #if PCRE2_CODE_UNIT_WIDTH == 8 @@ -759,10 +800,12 @@ if (caseless) if (c > 0xff) SET_BIT(0xff); else SET_BIT(c); #endif } + else +#endif /* SUPPORT_UNICODE */ /* Not UTF */ - else if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]); + if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]); } return p; @@ -792,7 +835,7 @@ Returns: nothing static void set_type_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit) { -register uint32_t c; +uint32_t c; for (c = 0; c < table_limit; c++) re->start_bitmap[c] |= re->tables[c+cbits_offset+cbit_type]; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 @@ -833,7 +876,7 @@ Returns: nothing static void set_nottype_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit) { -register uint32_t c; +uint32_t c; for (c = 0; c < table_limit; c++) re->start_bitmap[c] |= ~(re->tables[c+cbits_offset+cbit_type]); #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 @@ -873,7 +916,7 @@ Returns: SSB_FAIL => Failed to find any starting code units static int set_start_bits(pcre2_real_code *re, PCRE2_SPTR code, BOOL utf) { -register uint32_t c; +uint32_t c; int yield = SSB_DONE; #if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 @@ -911,10 +954,10 @@ do case OP_ALLANY: case OP_ANY: case OP_ANYBYTE: - case OP_CIRC: case OP_CIRCM: case OP_CLOSE: case OP_COMMIT: + case OP_COMMIT_ARG: case OP_COND: case OP_CREF: case OP_FALSE: @@ -979,6 +1022,13 @@ do case OP_THEN_ARG: return SSB_FAIL; + /* OP_CIRC happens only at the start of an anchored branch (multiline ^ + uses OP_CIRCM). Skip over it. */ + + case OP_CIRC: + tcode += PRIV(OP_lengths)[OP_CIRC]; + break; + /* A "real" property test implies no starting bits, but the fake property PT_CLIST identifies a list of characters. These lists are short, as they are used for characters with more than one "other case", so there is no @@ -1025,7 +1075,6 @@ do case OP_CBRAPOS: case OP_SCBRAPOS: case OP_ONCE: - case OP_ONCE_NC: case OP_ASSERT: rc = set_start_bits(re, tcode, utf); if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc; @@ -1227,7 +1276,7 @@ do break; /* Single character types set the bits and stop. Note that if PCRE2_UCP - is set, we do not see these op codes because \d etc are converted to + is set, we do not see these opcodes because \d etc are converted to properties. Therefore, these apply in the case when only characters less than 256 are recognized to match the types. */ @@ -1407,6 +1456,10 @@ do classmap = ((tcode[1 + LINK_SIZE] & XCL_MAP) == 0)? NULL : (uint8_t *)(tcode + 1 + LINK_SIZE + 1); #endif + /* It seems that the fall through comment must be outside the #ifdef if + it is to avoid the gcc compiler warning. */ + + /* Fall through */ /* Enter here for a negative non-XCLASS. In the 8-bit library, if we are in UTF mode, any byte with a value >= 0xc4 is a potentially valid starter @@ -1534,24 +1587,31 @@ BOOL utf = (re->overall_options & PCRE2_UTF) != 0; code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) + re->name_entry_size * re->name_count; -/* For an anchored pattern, or an unanchored pattern that has a first code -unit, or a multiline pattern that matches only at "line start", there is no -point in seeking a list of starting code units. */ +/* For a pattern that has a first code unit, or a multiline pattern that +matches only at "line start", there is no point in seeking a list of starting +code units. */ -if ((re->overall_options & PCRE2_ANCHORED) == 0 && - (re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0) +if ((re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0) { int rc = set_start_bits(re, code, utf); if (rc == SSB_UNKNOWN) return 1; if (rc == SSB_DONE) re->flags |= PCRE2_FIRSTMAPSET; } -/* Find the minimum length of subject string. If it can match an empty string, -the minimum length is already known. */ +/* Find the minimum length of subject string. If the pattern can match an empty +string, the minimum length is already known. If there are more back references +than the size of the vector we are going to cache them in, do nothing. A +pattern that complicated will probably take a long time to analyze and may in +any case turn out to be too complicated. Note that back reference minima are +held as 16-bit numbers. */ -if ((re->flags & PCRE2_MATCH_EMPTY) == 0) +if ((re->flags & PCRE2_MATCH_EMPTY) == 0 && + re->top_backref <= MAX_CACHE_BACKREF) { - switch(min = find_minlength(re, code, code, utf, NULL, &count)) + int backref_cache[MAX_CACHE_BACKREF+1]; + backref_cache[0] = 0; /* Highest one that is set */ + min = find_minlength(re, code, code, utf, NULL, &count, backref_cache); + switch(min) { case -1: /* \C in UTF mode or (*ACCEPT) or over-complex regex */ break; /* Leave minlength unchanged (will be zero) */ diff --git a/pcre2-10.22/src/pcre2_substitute.c b/pcre2-10.32/src/pcre2_substitute.c similarity index 91% rename from pcre2-10.22/src/pcre2_substitute.c rename to pcre2-10.32/src/pcre2_substitute.c index 0bf781efc..ab8d10908 100644 --- a/pcre2-10.22/src/pcre2_substitute.c +++ b/pcre2-10.32/src/pcre2_substitute.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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -114,7 +114,7 @@ for (; ptr < ptrend; ptr++) else if (*ptr == CHAR_BACKSLASH) { int erc; - int errorcode = 0; + int errorcode; uint32_t ch; if (ptr < ptrend - 1) switch (ptr[1]) @@ -127,8 +127,10 @@ for (; ptr < ptrend; ptr++) continue; } + ptr += 1; /* Must point after \ */ erc = PRIV(check_escape)(&ptr, ptrend, &ch, &errorcode, code->overall_options, FALSE, NULL); + ptr -= 1; /* Back to last code unit of escape */ if (errorcode != 0) { rc = errorcode; @@ -236,10 +238,12 @@ PCRE2_SPTR repend; PCRE2_SIZE extra_needed = 0; PCRE2_SIZE buff_offset, buff_length, lengthleft, fraglength; PCRE2_SIZE *ovector; +PCRE2_SIZE ovecsave[3]; buff_offset = 0; lengthleft = buff_length = *blength; *blength = PCRE2_UNSET; +ovecsave[0] = ovecsave[1] = ovecsave[2] = PCRE2_UNSET; /* Partial matching is not valid. */ @@ -287,6 +291,12 @@ options &= ~SUBSTITUTE_OPTIONS; /* Copy up to the start offset */ +if (start_offset > length) + { + match_data->leftchar = 0; + rc = PCRE2_ERROR_BADOFFSET; + goto EXIT; + } CHECKMEMCPY(subject, start_offset); /* Loop for global substituting. */ @@ -353,13 +363,33 @@ do } /* Handle a successful match. Matches that use \K to end before they start - are not supported. */ - - if (ovector[1] < ovector[0]) + or start before the current point in the subject are not supported. */ + + if (ovector[1] < ovector[0] || ovector[0] < start_offset) { rc = PCRE2_ERROR_BADSUBSPATTERN; goto EXIT; } + + /* Check for the same match as previous. This is legitimate after matching an + empty string that starts after the initial match offset. We have tried again + at the match point in case the pattern is one like /(?<=\G.)/ which can never + match at its starting point, so running the match achieves the bumpalong. If + we do get the same (null) match at the original match point, it isn't such a + pattern, so we now do the empty string magic. In all other cases, a repeat + match should never occur. */ + + if (ovecsave[0] == ovector[0] && ovecsave[1] == ovector[1]) + { + if (ovector[0] == ovector[1] && ovecsave[2] != start_offset) + { + goptions = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; + ovecsave[2] = start_offset; + continue; /* Back to the top of the loop */ + } + rc = PCRE2_ERROR_INTERNAL_DUPMATCH; + goto EXIT; + } /* Count substitutions with a paranoid check for integer overflow; surely no real call to this function would ever hit this! */ @@ -698,7 +728,7 @@ do else if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 && *ptr == CHAR_BACKSLASH) { - int errorcode = 0; + int errorcode; if (ptr < repend - 1) switch (ptr[1]) { @@ -728,10 +758,10 @@ do break; } + ptr++; /* Point after \ */ rc = PRIV(check_escape)(&ptr, repend, &ch, &errorcode, code->overall_options, FALSE, NULL); if (errorcode != 0) goto BADESCAPE; - ptr++; switch(rc) { @@ -791,13 +821,18 @@ do } /* 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 : + /* The replacement has been copied to the output. Save the details of this + match. See above for how this data is used. If we matched an empty string, do + the magic for global matches. Finally, update the start offset to point to + the rest of the subject string. */ + + ovecsave[0] = ovector[0]; + ovecsave[1] = ovector[1]; + ovecsave[2] = start_offset; + + goptions = (ovector[0] != ovector[1] || ovector[0] > start_offset)? 0 : PCRE2_ANCHORED|PCRE2_NOTEMPTY_ATSTART; + start_offset = ovector[1]; } while ((suboptions & PCRE2_SUBSTITUTE_GLOBAL) != 0); /* Repeat "do" loop */ /* Copy the rest of the subject. */ diff --git a/pcre2-10.22/src/pcre2_substring.c b/pcre2-10.32/src/pcre2_substring.c similarity index 98% rename from pcre2-10.22/src/pcre2_substring.c rename to pcre2-10.32/src/pcre2_substring.c index f6d7c3972..ddf5774e1 100644 --- a/pcre2-10.22/src/pcre2_substring.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -414,7 +414,12 @@ else for (i = 0; i < count2; i += 2) { size = (ovector[i+1] > ovector[i])? (ovector[i+1] - ovector[i]) : 0; - memcpy(sp, match_data->subject + ovector[i], CU2BYTES(size)); + + /* Size == 0 includes the case when the capture is unset. Avoid adding + PCRE2_UNSET to match_data->subject because it overflows, even though with + zero size calling memcpy() is harmless. */ + + if (size != 0) memcpy(sp, match_data->subject + ovector[i], CU2BYTES(size)); *listp++ = sp; if (lensp != NULL) *lensp++ = size; sp += size; diff --git a/pcre2-10.22/src/pcre2_tables.c b/pcre2-10.32/src/pcre2_tables.c similarity index 69% rename from pcre2-10.22/src/pcre2_tables.c rename to pcre2-10.32/src/pcre2_tables.c index b945ed7a7..83d6f9de5 100644 --- a/pcre2-10.22/src/pcre2_tables.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -39,7 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. */ /* This module contains some fixed tables that are used by more than one of the -PCRE code modules. The tables are also #included by the pcre2test program, +PCRE2 code modules. The tables are also #included by the pcre2test program, which uses macros to change their names from _pcre2_xxx to xxxx, thereby avoiding name clashes with the library. In this case, PCRE2_PCRE2TEST is defined. */ @@ -137,9 +137,10 @@ const uint32_t PRIV(ucp_gentype)[] = { /* This table encodes the rules for finding the end of an extended grapheme cluster. Every code point has a grapheme break property which is one of the -ucp_gbXX values defined in pcre2_ucp.h. The 2-dimensional table is indexed by -the properties of two adjacent code points. The left property selects a word -from the table, and the right property selects a bit from that word like this: +ucp_gbXX values defined in pcre2_ucp.h. These changed between Unicode versions +10 and 11. The 2-dimensional table is indexed by the properties of two adjacent +code points. The left property selects a word from the table, and the right +property selects a bit from that word like this: PRIV(ucp_gbtable)[left-property] & (1 << right-property) @@ -148,7 +149,7 @@ two code points. The breaking rules are as follows: 1. Break at the start and end of text (pretty obviously). -2. Do not break between a CR and LF; otherwise, break before and after +2. Do not break between a CR and LF; otherwise, break before and after controls. 3. Do not break Hangul syllable sequences, the rules for which are: @@ -157,44 +158,54 @@ two code points. The breaking rules are as follows: LV or V may be followed by V or T LVT or T may be followed by T -4. Do not break before extending characters. +4. Do not break before extending characters or zero-width-joiner (ZWJ). -The next two rules are only for extended grapheme clusters (but that's what we +The following rules are only for extended grapheme clusters (but that's what we are implementing). 5. Do not break before SpacingMarks. 6. Do not break after Prepend characters. -7. Otherwise, break everywhere. +7. Do not break within emoji modifier sequences or emoji zwj sequences. That + is, do not break between characters with the Extended_Pictographic property. + Extend and ZWJ characters are allowed between the characters; this cannot be + represented in this table, the code has to deal with it. + +8. Do not break within emoji flag sequences. That is, do not break between + regional indicator (RI) symbols if there are an odd number of RI characters + before the break point. This table encodes "join RI characters"; the code + has to deal with checking for previous adjoining RIs. + +9. Otherwise, break everywhere. */ +#define ESZ (1< 0; p++) { - register uint32_t ab, d; + uint32_t ab, d; c = *p; length--; @@ -142,20 +142,20 @@ for (p = string; length > 0; p++) if (c < 0xc0) /* Isolated 10xx xxxx byte */ { - *erroroffset = (int)(p - string); + *erroroffset = (PCRE2_SIZE)(p - string); return PCRE2_ERROR_UTF8_ERR20; } if (c >= 0xfe) /* Invalid 0xfe or 0xff bytes */ { - *erroroffset = (int)(p - string); + *erroroffset = (PCRE2_SIZE)(p - string); return PCRE2_ERROR_UTF8_ERR21; } ab = PRIV(utf8_table4)[c & 0x3f]; /* Number of additional bytes (1-5) */ if (length < ab) /* Missing bytes */ { - *erroroffset = (int)(p - string); + *erroroffset = (PCRE2_SIZE)(p - string); switch(ab - length) { case 1: return PCRE2_ERROR_UTF8_ERR1; diff --git a/pcre2-10.22/src/pcre2_xclass.c b/pcre2-10.32/src/pcre2_xclass.c similarity index 100% rename from pcre2-10.22/src/pcre2_xclass.c rename to pcre2-10.32/src/pcre2_xclass.c diff --git a/pcre2-10.22/src/pcre2demo.c b/pcre2-10.32/src/pcre2demo.c similarity index 88% rename from pcre2-10.22/src/pcre2demo.c rename to pcre2-10.32/src/pcre2demo.c index 8ae49f100..5d9b3217b 100644 --- a/pcre2-10.22/src/pcre2demo.c +++ b/pcre2-10.32/src/pcre2demo.c @@ -211,6 +211,21 @@ pcre2_match_data_create_from_pattern() above. */ if (rc == 0) printf("ovector was not big enough for all the captured substrings\n"); +/* We must guard against patterns such as /(?=.\K)/ that use \K in an assertion +to set the start of a match later than its end. In this demonstration program, +we just detect this case and give up. */ + +if (ovector[0] > ovector[1]) + { + printf("\\K was used in an assertion to set the match start after its end.\n" + "From end to start the match was: %.*s\n", (int)(ovector[0] - ovector[1]), + (char *)(subject + ovector[1])); + printf("Run abandoned\n"); + pcre2_match_data_free(match_data); + pcre2_code_free(re); + return 1; + } + /* Show substrings stored in the output vector by number. Obviously, in a real application you might want to do things other than print them. */ @@ -338,6 +353,29 @@ for (;;) options = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; } + /* If the previous match was not an empty string, there is one tricky case to + consider. If a pattern contains \K within a lookbehind assertion at the + start, the end of the matched string can be at the offset where the match + started. Without special action, this leads to a loop that keeps on matching + the same substring. We must detect this case and arrange to move the start on + by one character. The pcre2_get_startchar() function returns the starting + offset that was passed to pcre2_match(). */ + + else + { + PCRE2_SIZE startchar = pcre2_get_startchar(match_data); + if (start_offset <= startchar) + { + if (startchar >= subject_length) break; /* Reached end of subject. */ + start_offset = startchar + 1; /* Advance by one character. */ + if (utf8) /* If UTF-8, it may be more */ + { /* than one code unit. */ + for (; start_offset < subject_length; start_offset++) + if ((subject[start_offset] & 0xc0) != 0x80) break; + } + } + } + /* Run the next matching operation */ rc = pcre2_match( @@ -402,6 +440,21 @@ for (;;) if (rc == 0) printf("ovector was not big enough for all the captured substrings\n"); + /* We must guard against patterns such as /(?=.\K)/ that use \K in an + assertion to set the start of a match later than its end. In this + demonstration program, we just detect this case and give up. */ + + if (ovector[0] > ovector[1]) + { + printf("\\K was used in an assertion to set the match start after its end.\n" + "From end to start the match was: %.*s\n", (int)(ovector[0] - ovector[1]), + (char *)(subject + ovector[1])); + printf("Run abandoned\n"); + pcre2_match_data_free(match_data); + pcre2_code_free(re); + return 1; + } + /* As before, show substrings stored in the output vector by number, and then also any named substrings. */ diff --git a/pcre2-10.22/src/pcre2grep.c b/pcre2-10.32/src/pcre2grep.c similarity index 72% rename from pcre2-10.22/src/pcre2grep.c rename to pcre2-10.32/src/pcre2grep.c index 49747c0c3..d5f34c81f 100644 --- a/pcre2-10.22/src/pcre2grep.c +++ b/pcre2-10.32/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-2016 University of Cambridge + Copyright (c) 1997-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -58,14 +58,28 @@ POSSIBILITY OF SUCH DAMAGE. #include #include -#if defined(_WIN32) || defined(WIN32) +#if (defined _WIN32 || (defined HAVE_WINDOWS_H && HAVE_WINDOWS_H)) \ + && !defined WIN32 && !defined(__CYGWIN__) +#define WIN32 +#endif + +/* Some cmake's define it still */ +#if defined(__CYGWIN__) && defined(WIN32) +#undef WIN32 +#endif + +#ifdef WIN32 #include /* For _setmode() */ #include /* For _O_BINARY */ #endif #ifdef SUPPORT_PCRE2GREP_CALLOUT +#ifdef WIN32 +#include +#else #include #endif +#endif #ifdef HAVE_UNISTD_H #include @@ -82,6 +96,14 @@ POSSIBILITY OF SUCH DAMAGE. #define PCRE2_CODE_UNIT_WIDTH 8 #include "pcre2.h" +/* Older versions of MSVC lack snprintf(). This define allows for +warning/error-free compilation and testing with MSVC compilers back to at least +MSVC 10/2010. Except for VC6 (which is missing some fundamentals and fails). */ + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define snprintf _snprintf +#endif + #define FALSE 0 #define TRUE 1 @@ -95,7 +117,8 @@ typedef int BOOL; #define MAXPATLEN 8192 #endif -#define PATBUFSIZE (MAXPATLEN + 10) /* Allows for prefix+suffix */ +#define FNBUFSIZ 2048 +#define ERRBUFSIZ 256 /* Values for the "filenames" variable, which specifies options for file name output. The order is important; it is assumed that a file name is wanted for @@ -128,17 +151,20 @@ Unfortunately, casting to (void) does not suppress the warning. To get round this, we use a macro that compiles a fudge. Oddly, this does not also seem to apply to fprintf(). */ -#define FWRITE(a,b,c,d) if (fwrite(a,b,c,d)) {} +#define FWRITE_IGNORE(a,b,c,d) if (fwrite(a,b,c,d)) {} /* Under Windows, we have to set stdout to be binary, so that it does not convert \r\n at the ends of output lines to \r\r\n. However, that means that any messages written to stdout must have \r\n as their line terminator. This is -handled by using STDOUT_NL as the newline string. */ +handled by using STDOUT_NL as the newline string. We also use a normal double +quote for the example, as single quotes aren't usually available. */ -#if defined(_WIN32) || defined(WIN32) +#ifdef WIN32 #define STDOUT_NL "\r\n" +#define QUOT "\"" #else #define STDOUT_NL "\n" +#define QUOT "'" #endif @@ -158,29 +184,36 @@ static const char *jfriedl_prefix = ""; static const char *jfriedl_postfix = ""; #endif -static char *colour_string = (char *)"1;31"; -static char *colour_option = NULL; -static char *dee_option = NULL; -static char *DEE_option = NULL; -static char *locale = NULL; +static const char *colour_string = "1;31"; +static const char *colour_option = NULL; +static const char *dee_option = NULL; +static const char *DEE_option = NULL; +static const char *locale = NULL; +static const char *newline_arg = NULL; +static const char *om_separator = NULL; +static const char *stdin_name = "(standard input)"; +static const char *output_text = NULL; + static char *main_buffer = NULL; -static char *newline_arg = NULL; -static char *om_separator = (char *)""; -static char *stdin_name = (char *)"(standard input)"; static int after_context = 0; static int before_context = 0; static int binary_files = BIN_BINARY; static int both_context = 0; static int bufthird = PCRE2GREP_BUFSIZE; +static int max_bufthird = PCRE2GREP_MAX_BUFSIZE; static int bufsize = 3*PCRE2GREP_BUFSIZE; static int endlinetype; -#if defined HAVE_WINDOWS_H && HAVE_WINDOWS_H +static unsigned long int total_count = 0; +static unsigned long int counts_printed = 0; + +#ifdef WIN32 static int dee_action = dee_SKIP; #else static int dee_action = dee_READ; #endif + static int DEE_action = DEE_READ; static int error_count = 0; static int filenames = FN_DEFAULT; @@ -194,9 +227,10 @@ static BOOL use_jit = FALSE; static const uint8_t *character_tables = NULL; static uint32_t pcre2_options = 0; -static uint32_t process_options = 0; +static uint32_t extra_options = 0; +static PCRE2_SIZE heap_limit = PCRE2_UNSET; static uint32_t match_limit = 0; -static uint32_t recursion_limit = 0; +static uint32_t depth_limit = 0; static pcre2_compile_context *compile_context; static pcre2_match_context *match_context; @@ -205,6 +239,9 @@ static PCRE2_SIZE *offsets; static BOOL count_only = FALSE; static BOOL do_colour = FALSE; +#ifdef WIN32 +static BOOL do_ansi = FALSE; +#endif static BOOL file_offsets = FALSE; static BOOL hyphenpending = FALSE; static BOOL invert = FALSE; @@ -215,7 +252,7 @@ static BOOL number = FALSE; static BOOL omit_zero_count = FALSE; static BOOL resource_error = FALSE; static BOOL quiet = FALSE; -static BOOL show_only_matching = FALSE; +static BOOL show_total_count = FALSE; static BOOL silent = FALSE; static BOOL utf = FALSE; @@ -228,6 +265,7 @@ typedef struct omstr { static omstr *only_matching = NULL; static omstr *only_matching_last = NULL; +static int only_matching_count; /* Structure for holding the two variables that describe a number chain. */ @@ -273,6 +311,7 @@ also for include/exclude patterns. */ typedef struct patstr { struct patstr *next; char *string; + PCRE2_SIZE length; pcre2_code *compiled; } patstr; @@ -309,7 +348,7 @@ static const char *incexname[4] = { "--include", "--exclude", /* Structure for options and list of them */ -enum { OP_NODATA, OP_STRING, OP_OP_STRING, OP_NUMBER, OP_U32NUMBER, +enum { OP_NODATA, OP_STRING, OP_OP_STRING, OP_NUMBER, OP_U32NUMBER, OP_SIZE, OP_OP_NUMBER, OP_OP_NUMBERS, OP_PATLIST, OP_FILELIST, OP_BINFILES }; typedef struct option_item { @@ -335,15 +374,17 @@ used to identify them. */ #define N_LOFFSETS (-10) #define N_FOFFSETS (-11) #define N_LBUFFER (-12) -#define N_M_LIMIT (-13) -#define N_M_LIMIT_REC (-14) -#define N_BUFSIZE (-15) -#define N_NOJIT (-16) -#define N_FILE_LIST (-17) -#define N_BINARY_FILES (-18) -#define N_EXCLUDE_FROM (-19) -#define N_INCLUDE_FROM (-20) -#define N_OM_SEPARATOR (-21) +#define N_H_LIMIT (-13) +#define N_M_LIMIT (-14) +#define N_M_LIMIT_DEP (-15) +#define N_BUFSIZE (-16) +#define N_NOJIT (-17) +#define N_FILE_LIST (-18) +#define N_BINARY_FILES (-19) +#define N_EXCLUDE_FROM (-20) +#define N_INCLUDE_FROM (-21) +#define N_OM_SEPARATOR (-22) +#define N_MAX_BUFSIZE (-23) static option_item optionlist[] = { { OP_NODATA, N_NULL, NULL, "", "terminate options" }, @@ -352,7 +393,8 @@ static option_item optionlist[] = { { OP_NODATA, 'a', NULL, "text", "treat binary files as text" }, { OP_NUMBER, 'B', &before_context, "before-context=number", "set number of prior context lines" }, { OP_BINFILES, N_BINARY_FILES, NULL, "binary-files=word", "set treatment of binary files" }, - { OP_NUMBER, N_BUFSIZE,&bufthird, "buffer-size=number", "set processing buffer size parameter" }, + { OP_NUMBER, N_BUFSIZE,&bufthird, "buffer-size=number", "set processing buffer starting size" }, + { OP_NUMBER, N_MAX_BUFSIZE,&max_bufthird, "max-buffer-size=number", "set processing buffer maximum size" }, { OP_OP_STRING, N_COLOUR, &colour_option, "color=option", "matched text color option" }, { OP_OP_STRING, N_COLOUR, &colour_option, "colour=option", "matched text colour option" }, { OP_NUMBER, 'C', &both_context, "context=number", "set number of context lines, before & after" }, @@ -368,22 +410,25 @@ static option_item optionlist[] = { { OP_NODATA, 'h', NULL, "no-filename", "suppress the prefixing filename on output" }, { OP_NODATA, 'I', NULL, "", "treat binary files as not matching (ignore)" }, { OP_NODATA, 'i', NULL, "ignore-case", "ignore case distinctions" }, -#ifdef SUPPORT_PCRE2GREP_JIT - { OP_NODATA, N_NOJIT, NULL, "no-jit", "do not use just-in-time compiler optimization" }, -#else - { OP_NODATA, N_NOJIT, NULL, "no-jit", "ignored: this pcre2grep does not support JIT" }, -#endif { OP_NODATA, 'l', NULL, "files-with-matches", "print only FILE names containing matches" }, { OP_NODATA, 'L', NULL, "files-without-match","print only FILE names not containing matches" }, { OP_STRING, N_LABEL, &stdin_name, "label=name", "set name for standard input" }, { OP_NODATA, N_LBUFFER, NULL, "line-buffered", "use line buffering" }, { OP_NODATA, N_LOFFSETS, NULL, "line-offsets", "output line numbers and offsets, not text" }, { OP_STRING, N_LOCALE, &locale, "locale=locale", "use the named locale" }, - { OP_U32NUMBER, N_M_LIMIT, &match_limit, "match-limit=number", "set PCRE match limit option" }, - { OP_U32NUMBER, N_M_LIMIT_REC, &recursion_limit, "recursion-limit=number", "set PCRE match recursion limit option" }, + { OP_SIZE, N_H_LIMIT, &heap_limit, "heap-limit=number", "set PCRE2 heap limit option (kibibytes)" }, + { OP_U32NUMBER, N_M_LIMIT, &match_limit, "match-limit=number", "set PCRE2 match limit option" }, + { OP_U32NUMBER, N_M_LIMIT_DEP, &depth_limit, "depth-limit=number", "set PCRE2 depth limit option" }, + { OP_U32NUMBER, N_M_LIMIT_DEP, &depth_limit, "recursion-limit=number", "obsolete synonym for depth-limit" }, { OP_NODATA, 'M', NULL, "multiline", "run in multiline mode" }, - { OP_STRING, 'N', &newline_arg, "newline=type", "set newline type (CR, LF, CRLF, ANYCRLF or ANY)" }, + { OP_STRING, 'N', &newline_arg, "newline=type", "set newline type (CR, LF, CRLF, ANYCRLF, ANY, or NUL)" }, { OP_NODATA, 'n', NULL, "line-number", "print line number with output lines" }, +#ifdef SUPPORT_PCRE2GREP_JIT + { OP_NODATA, N_NOJIT, NULL, "no-jit", "do not use just-in-time compiler optimization" }, +#else + { OP_NODATA, N_NOJIT, NULL, "no-jit", "ignored: this pcre2grep does not support JIT" }, +#endif + { OP_STRING, 'O', &output_text, "output=text", "show only this text (possibly expanded)" }, { OP_OP_NUMBERS, 'o', &only_matching_data, "only-matching=n", "show only the part of the line that matched" }, { OP_STRING, N_OM_SEPARATOR, &om_separator, "om-separator=text", "set separator for multiple -o output" }, { OP_NODATA, 'q', NULL, "quiet", "suppress output, just set return code" }, @@ -398,6 +443,7 @@ static option_item optionlist[] = { { OP_OP_NUMBER, 'S', &S_arg, "jeffS", "replace matched (sub)string with X" }, #endif { OP_NODATA, 's', NULL, "no-messages", "suppress error messages" }, + { OP_NODATA, 't', NULL, "total-count", "print total count of matching lines" }, { OP_NODATA, 'u', NULL, "utf", "use UTF mode" }, { OP_NODATA, 'V', NULL, "version", "print version information and exit" }, { OP_NODATA, 'v', NULL, "invert-match", "select non-matching lines" }, @@ -410,20 +456,7 @@ static option_item optionlist[] = { of PCRE2_NEWLINE_xx in pcre2.h. */ static const char *newlines[] = { - "DEFAULT", "CR", "LF", "CRLF", "ANY", "ANYCRLF" }; - -/* Tables for prefixing and suffixing patterns, according to the -w, -x, and -F -options. These set the 1, 2, and 4 bits in process_options, respectively. Note -that the combination of -w and -x has the same effect as -x on its own, so we -can treat them as the same. Note that the MAXPATLEN macro assumes the longest -prefix+suffix is 10 characters; if anything longer is added, it must be -adjusted. */ - -static const char *prefix[] = { - "", "\\b", "^(?:", "^(?:", "\\Q", "\\b\\Q", "^(?:\\Q", "^(?:\\Q" }; - -static const char *suffix[] = { - "", "\\b", ")$", ")$", "\\E", "\\E\\b", "\\E)$", "\\E)$" }; + "DEFAULT", "CR", "LF", "CRLF", "ANY", "ANYCRLF", "NUL" }; /* UTF-8 tables - used only when the newline setting is "any". */ @@ -436,6 +469,43 @@ const char utf8_table4[] = { 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; +#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE) +/************************************************* +* Emulated memmove() for systems without it * +*************************************************/ + +/* This function can make use of bcopy() if it is available. Otherwise do it by +steam, as there are some non-Unix environments that lack both memmove() and +bcopy(). */ + +static void * +emulated_memmove(void *d, const void *s, size_t n) +{ +#ifdef HAVE_BCOPY +bcopy(s, d, n); +return d; +#else +size_t i; +unsigned char *dest = (unsigned char *)d; +const unsigned char *src = (const unsigned char *)s; +if (dest > src) + { + dest += n; + src += n; + for (i = 0; i < n; ++i) *(--dest) = *(--src); + return (void *)dest; + } +else + { + for (i = 0; i < n; ++i) *dest++ = *src++; + return (void *)(dest - n); + } +#endif /* not HAVE_BCOPY */ +} +#undef memmove +#define memmove(d,s,n) emulated_memmove(d,s,n) +#endif /* not VPCOMPAT && not HAVE_MEMMOVE */ + /************************************************* * Case-independent string compare * @@ -455,6 +525,34 @@ return 0; } +/************************************************* +* Parse GREP_COLORS * +*************************************************/ + +/* Extract ms or mt from GREP_COLORS. + +Argument: the string, possibly NULL +Returns: the value of ms or mt, or NULL if neither present +*/ + +static char * +parse_grep_colors(const char *gc) +{ +static char seq[16]; +char *col; +uint32_t len; +if (gc == NULL) return NULL; +col = strstr(gc, "ms="); +if (col == NULL) col = strstr(gc, "mt="); +if (col == NULL) return NULL; +len = 0; +col += 3; +while (*col != ':' && *col != 0 && len < sizeof(seq)-1) + seq[len++] = *col++; +seq[len] = 0; +return seq; +} + /************************************************* * Exit from the program * @@ -469,11 +567,27 @@ Returns: does not return static void pcre2grep_exit(int rc) { +/* VMS does exit codes differently: both exit(1) and exit(0) return with a +status of 1, which is not helpful. To help with this problem, define a symbol +(akin to an environment variable) called "PCRE2GREP_RC" and put the exit code +therein. */ + +#ifdef __VMS +#include descrip +#include lib$routines + char val_buf[4]; + $DESCRIPTOR(sym_nam, "PCRE2GREP_RC"); + $DESCRIPTOR(sym_val, val_buf); + sprintf(val_buf, "%d", rc); + sym_val.dsc$w_length = strlen(val_buf); + lib$set_symbol(&sym_nam, &sym_val); +#endif + if (resource_error) { - fprintf(stderr, "pcre2grep: Error %d, %d or %d means that a resource limit " - "was exceeded.\n", PCRE2_ERROR_JIT_STACKLIMIT, PCRE2_ERROR_MATCHLIMIT, - PCRE2_ERROR_RECURSIONLIMIT); + fprintf(stderr, "pcre2grep: Error %d, %d, %d or %d means that a resource " + "limit was exceeded.\n", PCRE2_ERROR_JIT_STACKLIMIT, PCRE2_ERROR_MATCHLIMIT, + PCRE2_ERROR_DEPTHLIMIT, PCRE2_ERROR_HEAPLIMIT); fprintf(stderr, "pcre2grep: Check your regex for nested unlimited loops.\n"); } exit(rc); @@ -489,13 +603,14 @@ exit(rc); Arguments: s pattern string to add + patlen length of pattern after if not NULL points to item to insert after Returns: new pattern block or NULL on error */ static patstr * -add_pattern(char *s, patstr *after) +add_pattern(char *s, PCRE2_SIZE patlen, patstr *after) { patstr *p = (patstr *)malloc(sizeof(patstr)); if (p == NULL) @@ -503,7 +618,7 @@ if (p == NULL) fprintf(stderr, "pcre2grep: malloc failed\n"); pcre2grep_exit(2); } -if (strlen(s) > MAXPATLEN) +if (patlen > MAXPATLEN) { fprintf(stderr, "pcre2grep: pattern is too long (limit is %d bytes)\n", MAXPATLEN); @@ -512,6 +627,7 @@ if (strlen(s) > MAXPATLEN) } p->next = NULL; p->string = s; +p->length = patlen; p->compiled = NULL; if (after != NULL) @@ -571,9 +687,83 @@ while (fn != NULL) * OS-specific functions * *************************************************/ -/* These functions are defined so that they can be made system specific. -At present there are versions for Unix-style environments, Windows, native -z/OS, and "no support". */ +/* These definitions are needed in all Windows environments, even those where +Unix-style directory scanning can be used (see below). */ + +#ifdef WIN32 + +#ifndef STRICT +# define STRICT +#endif +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#include + +#define iswild(name) (strpbrk(name, "*?") != NULL) + +/* Convert ANSI BGR format to RGB used by Windows */ +#define BGR_RGB(x) ((x & 1 ? 4 : 0) | (x & 2) | (x & 4 ? 1 : 0)) + +static HANDLE hstdout; +static CONSOLE_SCREEN_BUFFER_INFO csbi; +static WORD match_colour; + +static WORD +decode_ANSI_colour(const char *cs) +{ +WORD result = csbi.wAttributes; +while (*cs) + { + if (isdigit(*cs)) + { + int code = atoi(cs); + if (code == 1) result |= 0x08; + else if (code == 4) result |= 0x8000; + else if (code == 5) result |= 0x80; + else if (code >= 30 && code <= 37) result = (result & 0xF8) | BGR_RGB(code - 30); + else if (code == 39) result = (result & 0xF0) | (csbi.wAttributes & 0x0F); + else if (code >= 40 && code <= 47) result = (result & 0x8F) | (BGR_RGB(code - 40) << 4); + else if (code == 49) result = (result & 0x0F) | (csbi.wAttributes & 0xF0); + /* aixterm high intensity colour codes */ + else if (code >= 90 && code <= 97) result = (result & 0xF0) | BGR_RGB(code - 90) | 0x08; + else if (code >= 100 && code <= 107) result = (result & 0x0F) | (BGR_RGB(code - 100) << 4) | 0x80; + + while (isdigit(*cs)) cs++; + } + if (*cs) cs++; + } +return result; +} + + +static void +init_colour_output() +{ +if (do_colour) + { + hstdout = GetStdHandle(STD_OUTPUT_HANDLE); + /* This fails when redirected to con; try again if so. */ + if (!GetConsoleScreenBufferInfo(hstdout, &csbi) && !do_ansi) + { + HANDLE hcon = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + GetConsoleScreenBufferInfo(hcon, &csbi); + CloseHandle(hcon); + } + match_colour = decode_ANSI_colour(colour_string); + /* No valid colour found - turn off colouring */ + if (!match_colour) do_colour = FALSE; + } +} + +#endif /* WIN32 */ + + +/* The following sets of functions are defined so that they can be made system +specific. At present there are versions for Unix-style environments, Windows, +native z/OS, and "no support". */ /************* Directory scanning Unix-style and z/OS ***********/ @@ -677,6 +867,18 @@ return isatty(fileno(f)); } #endif + +/************* Print optionally coloured match Unix-style and z/OS **********/ + +static void +print_match(const void *buf, int length) +{ +if (length == 0) return; +if (do_colour) fprintf(stdout, "%c[%sm", 0x1b, colour_string); +FWRITE_IGNORE(buf, 1, length, stdout); +if (do_colour) fprintf(stdout, "%c[0m", 0x1b); +} + /* End of Unix-style or native z/OS environment functions. */ @@ -686,19 +888,9 @@ return isatty(fileno(f)); Lionel Fourquaux. David Burgess added a patch to define INVALID_FILE_ATTRIBUTES when it did not exist. David Byron added a patch that moved the #include of to before the INVALID_FILE_ATTRIBUTES definition rather than after. -The double test below stops gcc 4.4.4 grumbling that HAVE_WINDOWS_H is -undefined when it is indeed undefined. */ +*/ -#elif defined HAVE_WINDOWS_H && HAVE_WINDOWS_H - -#ifndef STRICT -# define STRICT -#endif -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif - -#include +#elif defined WIN32 #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF @@ -738,7 +930,10 @@ if ((pattern == NULL) || (dir == NULL)) pcre2grep_exit(2); } memcpy(pattern, filename, len); -memcpy(&(pattern[len]), "\\*", 3); +if (iswild(filename)) + pattern[len] = 0; +else + memcpy(&(pattern[len]), "\\*", 3); dir->handle = FindFirstFile(pattern, &(dir->data)); if (dir->handle != INVALID_HANDLE_VALUE) { @@ -796,18 +991,36 @@ return !isdirectory(filename); /************* Test for a terminal in Windows **********/ -/* I don't know how to do this; assume never */ - static BOOL is_stdout_tty(void) { -return FALSE; +return _isatty(_fileno(stdout)); } static BOOL is_file_tty(FILE *f) { -return FALSE; +return _isatty(_fileno(f)); +} + + +/************* Print optionally coloured match in Windows **********/ + +static void +print_match(const void *buf, int length) +{ +if (length == 0) return; +if (do_colour) + { + if (do_ansi) fprintf(stdout, "%c[%sm", 0x1b, colour_string); + else SetConsoleTextAttribute(hstdout, match_colour); + } +FWRITE_IGNORE(buf, 1, length, stdout); +if (do_colour) + { + if (do_ansi) fprintf(stdout, "%c[0m", 0x1b); + else SetConsoleTextAttribute(hstdout, csbi.wAttributes); + } } /* End of Windows functions */ @@ -849,6 +1062,16 @@ is_file_tty(FILE *f) return FALSE; } + +/************* Print optionally coloured match when we can't do it **********/ + +static void +print_match(const void *buf, int length) +{ +if (length == 0) return; +FWRITE_IGNORE(buf, 1, length, stdout); +} + #endif /* End of system-specific functions */ @@ -889,7 +1112,7 @@ for (op = optionlist; op->one_char != 0; op++) if (op->one_char > 0) fprintf(stderr, "%c", op->one_char); } fprintf(stderr, "] [long options] [pattern] [files]\n"); -fprintf(stderr, "Type `pcre2grep --help' for more information and the long " +fprintf(stderr, "Type \"pcre2grep --help\" for more information and the long " "options.\n"); return rc; } @@ -931,7 +1154,7 @@ printf("Other files and the standard input are read as plain files." STDOUT_NL S printf("All files are read as plain files, without any interpretation." STDOUT_NL STDOUT_NL); #endif -printf("Example: pcre2grep -i 'hello.*world' menu.h main.c" STDOUT_NL STDOUT_NL); +printf("Example: pcre2grep -i " QUOT "hello.*world" QUOT " menu.h main.c" STDOUT_NL STDOUT_NL); printf("Options:" STDOUT_NL); for (op = optionlist; op->one_char != 0; op++) @@ -952,8 +1175,9 @@ for (op = optionlist; op->one_char != 0; op++) printf("%.*s%s" STDOUT_NL, n, " ", op->help_text); } -printf(STDOUT_NL "Numbers may be followed by K or M, e.g. --buffer-size=100K." STDOUT_NL); +printf(STDOUT_NL "Numbers may be followed by K or M, e.g. --max-buffer-size=100K." STDOUT_NL); printf("The default value for --buffer-size is %d." STDOUT_NL, PCRE2GREP_BUFSIZE); +printf("The default value for --max-buffer-size is %d." STDOUT_NL, PCRE2GREP_MAX_BUFSIZE); printf("When reading patterns or file names from a file, trailing white" STDOUT_NL); printf("space is removed and blank lines are ignored." STDOUT_NL); printf("The maximum size of any pattern is %d bytes." STDOUT_NL, MAXPATLEN); @@ -1100,12 +1324,14 @@ return om; * Read one line of input * *************************************************/ -/* Normally, input is read using fread() into a large buffer, so many lines may -be read at once. However, doing this for tty input means that no output appears -until a lot of input has been typed. Instead, tty input is handled line by -line. We cannot use fgets() for this, because it does not stop at a binary -zero, and therefore there is no way of telling how many characters it has read, -because there may be binary zeros embedded in the data. +/* Normally, input that is to be scanned is read using fread() (or gzread, or +BZ2_read) into a large buffer, so many lines may be read at once. However, +doing this for tty input means that no output appears until a lot of input has +been typed. Instead, tty input is handled line by line. We cannot use fgets() +for this, because it does not stop at a binary zero, and therefore there is no +way of telling how many characters it has read, because there may be binary +zeros embedded in the data. This function is also used for reading patterns +from files (the -f option). Arguments: buffer the buffer to read into @@ -1115,7 +1341,7 @@ because there may be binary zeros embedded in the data. Returns: the number of characters read, zero at end of file */ -static unsigned int +static PCRE2_SIZE read_one_line(char *buffer, int length, FILE *f) { int c; @@ -1172,6 +1398,16 @@ switch(endlinetype) *lenptr = 0; return endptr; + case PCRE2_NEWLINE_NUL: + while (p < endptr && *p != '\0') p++; + if (p < endptr) + { + *lenptr = 1; + return p + 1; + } + *lenptr = 0; + return endptr; + case PCRE2_NEWLINE_CRLF: for (;;) { @@ -1193,7 +1429,7 @@ switch(endlinetype) while (p < endptr) { int extra = 0; - register int c = *((unsigned char *)p); + int c = *((unsigned char *)p); if (utf && c >= 0xc0) { @@ -1237,7 +1473,7 @@ switch(endlinetype) while (p < endptr) { int extra = 0; - register int c = *((unsigned char *)p); + int c = *((unsigned char *)p); if (utf && c >= 0xc0) { @@ -1323,6 +1559,11 @@ switch(endlinetype) while (p > startptr && p[-1] != '\n') p--; return p; + case PCRE2_NEWLINE_NUL: + p--; + while (p > startptr && p[-1] != '\0') p--; + return p; + case PCRE2_NEWLINE_CRLF: for (;;) { @@ -1339,7 +1580,7 @@ switch(endlinetype) while (p > startptr) { - register unsigned int c; + unsigned int c; char *pp = p - 1; if (utf) @@ -1378,7 +1619,7 @@ switch(endlinetype) case '\v': /* VT */ case '\f': /* FF */ case '\r': /* CR */ -#ifndef EBCDIE +#ifndef EBCDIC case 0x85: /* Unicode NEL */ case 0x2028: /* Unicode LS */ case 0x2029: /* Unicode PS */ @@ -1398,8 +1639,6 @@ switch(endlinetype) - - /************************************************* * Print the previous "after" lines * *************************************************/ @@ -1418,23 +1657,24 @@ Returns: nothing */ static void -do_after_lines(int lastmatchnumber, char *lastmatchrestart, char *endptr, - char *printname) +do_after_lines(unsigned long int lastmatchnumber, char *lastmatchrestart, + char *endptr, const char *printname) { if (after_context > 0 && lastmatchnumber > 0) { int count = 0; - while (lastmatchrestart < endptr && count++ < after_context) + while (lastmatchrestart < endptr && count < after_context) { int ellength; - char *pp = lastmatchrestart; + char *pp = end_of_line(lastmatchrestart, endptr, &ellength); + if (ellength == 0 && pp == main_buffer + bufsize) break; if (printname != NULL) fprintf(stdout, "%s-", printname); - if (number) fprintf(stdout, "%d-", lastmatchnumber++); - pp = end_of_line(pp, endptr, &ellength); - FWRITE(lastmatchrestart, 1, pp - lastmatchrestart, stdout); + if (number) fprintf(stdout, "%lu-", lastmatchnumber++); + FWRITE_IGNORE(lastmatchrestart, 1, pp - lastmatchrestart, stdout); lastmatchrestart = pp; + count++; } - hyphenpending = TRUE; + if (count > 0) hyphenpending = TRUE; } } @@ -1461,11 +1701,11 @@ Returns: TRUE if there was a match */ static BOOL -match_patterns(char *matchptr, size_t length, unsigned int options, - size_t startoffset, int *mrc) +match_patterns(char *matchptr, PCRE2_SIZE length, unsigned int options, + PCRE2_SIZE startoffset, int *mrc) { int i; -size_t slen = length; +PCRE2_SIZE slen = length; patstr *p = patterns; const char *msg = "this text:\n\n"; @@ -1483,10 +1723,10 @@ for (i = 1; p != NULL; p = p->next, i++) fprintf(stderr, "pcre2grep: pcre2_match() gave error %d while matching ", *mrc); if (patterns->next != NULL) fprintf(stderr, "pattern number %d to ", i); fprintf(stderr, "%s", msg); - FWRITE(matchptr, 1, slen, stderr); /* In case binary zero included */ + FWRITE_IGNORE(matchptr, 1, slen, stderr); /* In case binary zero included */ fprintf(stderr, "\n\n"); - if (*mrc == PCRE2_ERROR_MATCHLIMIT || *mrc == PCRE2_ERROR_RECURSIONLIMIT || - *mrc == PCRE2_ERROR_JIT_STACKLIMIT) + if (*mrc == PCRE2_ERROR_MATCHLIMIT || *mrc == PCRE2_ERROR_DEPTHLIMIT || + *mrc == PCRE2_ERROR_HEAPLIMIT || *mrc == PCRE2_ERROR_JIT_STACKLIMIT) resource_error = TRUE; if (error_count++ > 20) { @@ -1500,6 +1740,277 @@ return FALSE; /* No match, no errors */ } +/************************************************* +* Check output text for errors * +*************************************************/ + +static BOOL +syntax_check_output_text(PCRE2_SPTR string, BOOL callout) +{ +PCRE2_SPTR begin = string; +for (; *string != 0; string++) + { + if (*string == '$') + { + PCRE2_SIZE capture_id = 0; + BOOL brace = FALSE; + + string++; + + /* Syntax error: a character must be present after $. */ + if (*string == 0) + { + if (!callout) + fprintf(stderr, "pcre2grep: Error in output text at offset %d: %s\n", + (int)(string - begin), "no character after $"); + return FALSE; + } + + if (*string == '{') + { + /* Must be a decimal number in braces, e.g: {5} or {38} */ + string++; + + brace = TRUE; + } + + if ((*string >= '1' && *string <= '9') || (!callout && *string == '0')) + { + do + { + /* Maximum capture id is 65535. */ + if (capture_id <= 65535) + capture_id = capture_id * 10 + (*string - '0'); + + string++; + } + while (*string >= '0' && *string <= '9'); + + if (brace) + { + /* Syntax error: closing brace is missing. */ + if (*string != '}') + { + if (!callout) + fprintf(stderr, "pcre2grep: Error in output text at offset %d: %s\n", + (int)(string - begin), "missing closing brace"); + return FALSE; + } + } + else + { + /* To negate the effect of the for. */ + string--; + } + } + else if (brace) + { + /* Syntax error: a decimal number required. */ + if (!callout) + fprintf(stderr, "pcre2grep: Error in output text at offset %d: %s\n", + (int)(string - begin), "decimal number expected"); + return FALSE; + } + else if (*string == 'o') + { + string++; + + if (*string < '0' || *string > '7') + { + /* Syntax error: an octal number required. */ + if (!callout) + fprintf(stderr, "pcre2grep: Error in output text at offset %d: %s\n", + (int)(string - begin), "octal number expected"); + return FALSE; + } + } + else if (*string == 'x') + { + string++; + + if (!isxdigit((unsigned char)*string)) + { + /* Syntax error: a hexdecimal number required. */ + if (!callout) + fprintf(stderr, "pcre2grep: Error in output text at offset %d: %s\n", + (int)(string - begin), "hexadecimal number expected"); + return FALSE; + } + } + } + } + + return TRUE; +} + + +/************************************************* +* Display output text * +*************************************************/ + +/* Display the output text, which is assumed to have already been syntax +checked. Output may contain escape sequences started by the dollar sign. The +escape sequences are substituted as follows: + + $ or ${} is replaced by the captured substring of the given + decimal number; zero will substitute the whole match. If the number is + greater than the number of capturing substrings, or if the capture is unset, + the replacement is empty. + + $a is replaced by bell. + $b is replaced by backspace. + $e is replaced by escape. + $f is replaced by form feed. + $n is replaced by newline. + $r is replaced by carriage return. + $t is replaced by tab. + $v is replaced by vertical tab. + + $o is replaced by the character represented by the given octal + number; up to three digits are processed. + + $x is replaced by the character represented by the given hexadecimal + number; up to two digits are processed. + + Any other character is substituted by itself. E.g: $$ is replaced by a single + dollar. + +Arguments: + string: the output text + callout: TRUE for the builtin callout, FALSE for --output + subject the start of the subject + ovector: capture offsets + capture_top: number of captures + +Returns: TRUE if something was output, other than newline + FALSE if nothing was output, or newline was last output +*/ + +static BOOL +display_output_text(PCRE2_SPTR string, BOOL callout, PCRE2_SPTR subject, + PCRE2_SIZE *ovector, PCRE2_SIZE capture_top) +{ +BOOL printed = FALSE; + +for (; *string != 0; string++) + { + int ch = EOF; + if (*string == '$') + { + PCRE2_SIZE capture_id = 0; + BOOL brace = FALSE; + + string++; + + if (*string == '{') + { + /* Must be a decimal number in braces, e.g: {5} or {38} */ + string++; + + brace = TRUE; + } + + if ((*string >= '1' && *string <= '9') || (!callout && *string == '0')) + { + do + { + /* Maximum capture id is 65535. */ + if (capture_id <= 65535) + capture_id = capture_id * 10 + (*string - '0'); + + string++; + } + while (*string >= '0' && *string <= '9'); + + if (!brace) + { + /* To negate the effect of the for. */ + string--; + } + + if (capture_id < capture_top) + { + PCRE2_SIZE capturesize; + capture_id *= 2; + + capturesize = ovector[capture_id + 1] - ovector[capture_id]; + if (capturesize > 0) + { + print_match(subject + ovector[capture_id], capturesize); + printed = TRUE; + } + } + } + else if (*string == 'a') ch = '\a'; + else if (*string == 'b') ch = '\b'; +#ifndef EBCDIC + else if (*string == 'e') ch = '\033'; +#else + else if (*string == 'e') ch = '\047'; +#endif + else if (*string == 'f') ch = '\f'; + else if (*string == 'r') ch = '\r'; + else if (*string == 't') ch = '\t'; + else if (*string == 'v') ch = '\v'; + else if (*string == 'n') + { + fprintf(stdout, STDOUT_NL); + printed = FALSE; + } + else if (*string == 'o') + { + string++; + + ch = *string - '0'; + if (string[1] >= '0' && string[1] <= '7') + { + string++; + ch = ch * 8 + (*string - '0'); + } + if (string[1] >= '0' && string[1] <= '7') + { + string++; + ch = ch * 8 + (*string - '0'); + } + } + else if (*string == 'x') + { + string++; + + if (*string >= '0' && *string <= '9') + ch = *string - '0'; + else + ch = (*string | 0x20) - 'a' + 10; + if (isxdigit((unsigned char)string[1])) + { + string++; + ch *= 16; + if (*string >= '0' && *string <= '9') + ch += *string - '0'; + else + ch += (*string | 0x20) - 'a' + 10; + } + } + else + { + ch = *string; + } + } + else + { + ch = *string; + } + if (ch != EOF) + { + fprintf(stdout, "%c", ch); + printed = TRUE; + } + } + +return printed; +} + + #ifdef SUPPORT_PCRE2GREP_CALLOUT /************************************************* @@ -1513,7 +2024,7 @@ executable name, and the following substrings specify the arguments: program_name|param1|param2|... -Any substirng (including the program name) can contain escape sequences +Any substring (including the program name) can contain escape sequences started by the dollar character. The escape sequences are substituted as follows: @@ -1525,6 +2036,10 @@ started by the dollar character. The escape sequences are substituted as Any other character is substituted by itself. E.g: $$ is replaced by a single dollar or $| replaced by a pipe character. +Alternatively, if string starts with pipe, the remainder is taken as an output +string, same as --output. In this case, --om-separator is used to separate each +callout, defaulting to newline. + Example: echo -e "abcde\n12345" | pcre2grep \ @@ -1557,7 +2072,9 @@ char *args; char *argsptr; char **argsvector; char **argsvectorptr; +#ifndef WIN32 pid_t pid; +#endif int result = 0; (void)unused; /* Avoid compiler warning */ @@ -1565,6 +2082,16 @@ int result = 0; /* Only callout with strings are supported. */ if (string == NULL || length == 0) return 0; +/* If there's no command, output the remainder directly. */ + +if (*string == '|') + { + string++; + if (!syntax_check_output_text(string, TRUE)) return 0; + (void)display_output_text(string, TRUE, subject, ovector, capture_top); + return 0; + } + /* Checking syntax and compute the number of string fragments. Callout strings are ignored in case of a syntax error. */ @@ -1606,7 +2133,7 @@ while (length > 0) } else if (*string == '{') { - /* Must be a decimal number in parenthesis, e.g: (5) or (38) */ + /* Must be a decimal number in braces, e.g: {5} or {38} */ string++; length--; @@ -1628,7 +2155,7 @@ while (length > 0) } while (*string >= '0' && *string <= '9'); - /* Syntax error: close paren is missing. */ + /* Syntax error: closing brace is missing. */ if (*string != '}') return 0; } @@ -1745,6 +2272,9 @@ while (length > 0) *argsptr++ = '\0'; *argsvectorptr = NULL; +#ifdef WIN32 +result = _spawnvp(_P_WAIT, argsvector[0], (const char * const *)argsvector); +#else pid = fork(); if (pid == 0) @@ -1755,6 +2285,7 @@ if (pid == 0) } else if (pid > 0) (void)waitpid(pid, &result, 0); +#endif free(args); free(argsvector); @@ -1769,6 +2300,35 @@ return result != 0; +/************************************************* +* Read a portion of the file into buffer * +*************************************************/ + +static int +fill_buffer(void *handle, int frtype, char *buffer, int length, + BOOL input_line_buffered) +{ +(void)frtype; /* Avoid warning when not used */ + +#ifdef SUPPORT_LIBZ +if (frtype == FR_LIBZ) + return gzread((gzFile)handle, buffer, length); +else +#endif + +#ifdef SUPPORT_LIBBZ2 +if (frtype == FR_LIBBZ2) + return BZ2_bzread((BZFILE *)handle, buffer, length); +else +#endif + +return (input_line_buffered ? + read_one_line(buffer, length, (FILE *)handle) : + fread(buffer, 1, length, (FILE *)handle)); +} + + + /************************************************* * Grep an individual file * *************************************************/ @@ -1797,75 +2357,52 @@ Returns: 0 if there was at least one match */ static int -pcre2grep(void *handle, int frtype, char *filename, char *printname) +pcre2grep(void *handle, int frtype, const char *filename, const char *printname) { int rc = 1; -int linenumber = 1; -int lastmatchnumber = 0; -int count = 0; int filepos = 0; -char *lastmatchrestart = NULL; +unsigned long int linenumber = 1; +unsigned long int lastmatchnumber = 0; +unsigned long int count = 0; +char *lastmatchrestart = main_buffer; char *ptr = main_buffer; char *endptr; -size_t bufflength; +PCRE2_SIZE bufflength; BOOL binary = FALSE; BOOL endhyphenpending = FALSE; BOOL input_line_buffered = line_buffered; FILE *in = NULL; /* Ensure initialized */ -#ifdef SUPPORT_LIBZ -gzFile ingz = NULL; -#endif - -#ifdef SUPPORT_LIBBZ2 -BZFILE *inbz2 = NULL; -#endif - - /* Do the first read into the start of the buffer and set up the pointer to end of what we have. In the case of libz, a non-zipped .gz file will be read as a plain file. However, if a .bz2 file isn't actually bzipped, the first read will fail. */ -(void)frtype; - -#ifdef SUPPORT_LIBZ -if (frtype == FR_LIBZ) - { - ingz = (gzFile)handle; - bufflength = gzread (ingz, main_buffer, bufsize); - } -else -#endif - -#ifdef SUPPORT_LIBBZ2 -if (frtype == FR_LIBBZ2) - { - inbz2 = (BZFILE *)handle; - bufflength = BZ2_bzread(inbz2, main_buffer, bufsize); - if ((int)bufflength < 0) return 2; /* Gotcha: bufflength is size_t; */ - } /* without the cast it is unsigned. */ -else -#endif - +if (frtype != FR_LIBZ && frtype != FR_LIBBZ2) { in = (FILE *)handle; if (is_file_tty(in)) input_line_buffered = TRUE; - bufflength = input_line_buffered? - read_one_line(main_buffer, bufsize, in) : - fread(main_buffer, 1, bufsize, in); } +else input_line_buffered = FALSE; + +bufflength = fill_buffer(handle, frtype, main_buffer, bufsize, + input_line_buffered); + +#ifdef SUPPORT_LIBBZ2 +if (frtype == FR_LIBBZ2 && (int)bufflength < 0) return 2; /* Gotcha: bufflength is PCRE2_SIZE; */ +#endif endptr = main_buffer + bufflength; /* Unless binary-files=text, see if we have a binary file. This uses the same rule as GNU grep, namely, a search for a binary zero byte near the start of the -file. */ +file. However, when the newline convention is binary zero, we can't do this. */ if (binary_files != BIN_TEXT) { - binary = - memchr(main_buffer, 0, (bufflength > 1024)? 1024 : bufflength) != NULL; + if (endlinetype != PCRE2_NEWLINE_NUL) + binary = memchr(main_buffer, 0, (bufflength > 1024)? 1024 : bufflength) + != NULL; if (binary && binary_files == BIN_NOMATCH) return 1; } @@ -1880,10 +2417,9 @@ while (ptr < endptr) int mrc = 0; unsigned int options = 0; BOOL match; - char *matchptr = ptr; char *t = ptr; - size_t length, linelength; - size_t startoffset = 0; + PCRE2_SIZE length, linelength; + PCRE2_SIZE startoffset = 0; /* At this point, ptr is at the start of a line. We need to find the length of the subject string to pass to pcre2_match(). In multiline mode, it is the @@ -1895,22 +2431,65 @@ while (ptr < endptr) t = end_of_line(t, endptr, &endlinelength); linelength = t - ptr - endlinelength; - length = multiline? (size_t)(endptr - ptr) : linelength; + length = multiline? (PCRE2_SIZE)(endptr - ptr) : linelength; /* Check to see if the line we are looking at extends right to the very end of the buffer without a line terminator. This means the line is too long to - handle. */ + handle at the current buffer size. Until the buffer reaches its maximum size, + try doubling it and reading more data. */ if (endlinelength == 0 && t == main_buffer + bufsize) { - fprintf(stderr, "pcre2grep: line %d%s%s is too long for the internal buffer\n" - "pcre2grep: the buffer size is %d\n" - "pcre2grep: use the --buffer-size option to change it\n", - linenumber, - (filename == NULL)? "" : " of file ", - (filename == NULL)? "" : filename, - bufthird); - return 2; + if (bufthird < max_bufthird) + { + char *new_buffer; + int new_bufthird = 2*bufthird; + + if (new_bufthird > max_bufthird) new_bufthird = max_bufthird; + new_buffer = (char *)malloc(3*new_bufthird); + + if (new_buffer == NULL) + { + fprintf(stderr, + "pcre2grep: line %lu%s%s is too long for the internal buffer\n" + "pcre2grep: not enough memory to increase the buffer size to %d\n", + linenumber, + (filename == NULL)? "" : " of file ", + (filename == NULL)? "" : filename, + new_bufthird); + return 2; + } + + /* Copy the data and adjust pointers to the new buffer location. */ + + memcpy(new_buffer, main_buffer, bufsize); + bufthird = new_bufthird; + bufsize = 3*bufthird; + ptr = new_buffer + (ptr - main_buffer); + lastmatchrestart = new_buffer + (lastmatchrestart - main_buffer); + free(main_buffer); + main_buffer = new_buffer; + + /* Read more data into the buffer and then try to find the line ending + again. */ + + bufflength += fill_buffer(handle, frtype, main_buffer + bufflength, + bufsize - bufflength, input_line_buffered); + endptr = main_buffer + bufflength; + continue; + } + else + { + fprintf(stderr, + "pcre2grep: line %lu%s%s is too long for the internal buffer\n" + "pcre2grep: the maximum buffer size is %d\n" + "pcre2grep: use the --max-buffer-size option to change it\n", + linenumber, + (filename == NULL)? "" : " of file ", + (filename == NULL)? "" : filename, + bufthird); + return 2; + } } /* Extra processing for Jeffrey Friedl's debugging. */ @@ -1963,8 +2542,8 @@ while (ptr < endptr) } #endif - /* We come back here after a match when show_only_matching is set, in order - to find any further matches in the same line. This applies to + /* We come back here after a match when only_matching_count is non-zero, in + order to find any further matches in the same line. This applies to --only-matching, --file-offsets, and --line-offsets. */ ONLY_MATCHING_RESTART: @@ -1975,10 +2554,13 @@ while (ptr < endptr) match, set PCRE2_NOTEMPTY to disable any further matches of null strings in this line. */ - match = match_patterns(matchptr, length, options, startoffset, &mrc); + match = match_patterns(ptr, length, options, startoffset, &mrc); options = PCRE2_NOTEMPTY; - /* If it's a match or a not-match (as required), do what's wanted. */ + /* If it's a match or a not-match (as required), do what's wanted. NOTE: Use + only FWRITE_IGNORE() - which is just a packaged fwrite() that ignores its + return code - to output data lines, so that binary zeroes are treated as just + another data character. */ if (match != invert) { @@ -1994,7 +2576,7 @@ while (ptr < endptr) /* Just count if just counting is wanted. */ - else if (count_only) count++; + else if (count_only || show_total_count) 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 @@ -2018,34 +2600,44 @@ while (ptr < endptr) /* 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 - the matching substring (all three set show_only_matching). None of these - mutually exclusive options prints any context. Afterwards, adjust the start - and then jump back to look for further matches in the same line. If we are - in invert mode, however, nothing is printed and we do not restart - this - could still be useful because the return code is set. */ + the matching substring (all three set only_matching_count non-zero). None + of these mutually exclusive options prints any context. Afterwards, adjust + the start and then jump back to look for further matches in the same line. + If we are in invert mode, however, nothing is printed and we do not restart + - this could still be useful because the return code is set. */ - else if (show_only_matching) + else if (only_matching_count != 0) { if (!invert) { - size_t oldstartoffset; + PCRE2_SIZE oldstartoffset; if (printname != NULL) fprintf(stdout, "%s:", printname); - if (number) fprintf(stdout, "%d:", linenumber); + if (number) fprintf(stdout, "%lu:", linenumber); /* Handle --line-offsets */ if (line_offsets) - fprintf(stdout, "%d,%d" STDOUT_NL, (int)(matchptr + offsets[0] - ptr), + fprintf(stdout, "%d,%d" STDOUT_NL, (int)(ptr + offsets[0] - ptr), (int)(offsets[1] - offsets[0])); /* Handle --file-offsets */ else if (file_offsets) fprintf(stdout, "%d,%d" STDOUT_NL, - (int)(filepos + matchptr + offsets[0] - ptr), + (int)(filepos + ptr + offsets[0] - ptr), (int)(offsets[1] - offsets[0])); + /* Handle --output (which has already been syntax checked) */ + + else if (output_text != NULL) + { + if (display_output_text((PCRE2_SPTR)output_text, FALSE, + (PCRE2_SPTR)ptr, offsets, mrc) || printname != NULL || + number) + fprintf(stdout, STDOUT_NL); + } + /* Handle --only-matching, which may occur many times */ else @@ -2061,10 +2653,9 @@ while (ptr < endptr) int plen = offsets[2*n + 1] - offsets[2*n]; if (plen > 0) { - if (printed) fprintf(stdout, "%s", om_separator); - if (do_colour) fprintf(stdout, "%c[%sm", 0x1b, colour_string); - FWRITE(matchptr + offsets[n*2], 1, plen, stdout); - if (do_colour) fprintf(stdout, "%c[00m", 0x1b); + if (printed && om_separator != NULL) + fprintf(stdout, "%s", om_separator); + print_match(ptr + offsets[n*2], plen); printed = TRUE; } } @@ -2080,11 +2671,6 @@ while (ptr < endptr) if (line_buffered) fflush(stdout); rc = 0; /* Had some success */ - /* If the current match ended past the end of the line (only possible - in multiline mode), we are done with this line. */ - - if (offsets[1] > linelength) goto END_ONE_MATCH; - /* If the pattern contained a lookbehind that included \K, it is possible that the end of the match might be at or before the actual starting offset we have just used. In this case, start one character @@ -2096,9 +2682,24 @@ while (ptr < endptr) { if (startoffset >= length) goto END_ONE_MATCH; /* Were at end */ startoffset = oldstartoffset + 1; - if (utf) - while ((matchptr[startoffset] & 0xc0) == 0x80) startoffset++; + if (utf) while ((ptr[startoffset] & 0xc0) == 0x80) startoffset++; } + + /* If the current match ended past the end of the line (only possible + in multiline mode), we must move on to the line in which it did end + before searching for more matches. */ + + while (startoffset > linelength) + { + ptr += linelength + endlinelength; + filepos += (int)(linelength + endlinelength); + linenumber++; + startoffset -= (int)(linelength + endlinelength); + t = end_of_line(ptr, endptr, &endlinelength); + linelength = t - ptr - endlinelength; + length = (PCRE2_SIZE)(endptr - ptr); + } + goto ONLY_MATCHING_RESTART; } } @@ -2132,9 +2733,9 @@ while (ptr < endptr) { char *pp = lastmatchrestart; if (printname != NULL) fprintf(stdout, "%s-", printname); - if (number) fprintf(stdout, "%d-", lastmatchnumber++); + if (number) fprintf(stdout, "%lu-", lastmatchnumber++); pp = end_of_line(pp, endptr, &ellength); - FWRITE(lastmatchrestart, 1, pp - lastmatchrestart, stdout); + FWRITE_IGNORE(lastmatchrestart, 1, pp - lastmatchrestart, stdout); lastmatchrestart = pp; } if (lastmatchrestart != ptr) hyphenpending = TRUE; @@ -2172,9 +2773,9 @@ while (ptr < endptr) int ellength; char *pp = p; if (printname != NULL) fprintf(stdout, "%s-", printname); - if (number) fprintf(stdout, "%d-", linenumber - linecount--); + if (number) fprintf(stdout, "%lu-", linenumber - linecount--); pp = end_of_line(pp, endptr, &ellength); - FWRITE(p, 1, pp - p, stdout); + FWRITE_IGNORE(p, 1, pp - p, stdout); p = pp; } } @@ -2186,28 +2787,7 @@ while (ptr < endptr) endhyphenpending = TRUE; if (printname != NULL) fprintf(stdout, "%s:", printname); - if (number) fprintf(stdout, "%d:", linenumber); - - /* In multiline mode, we want to print to the end of the line in which - the end of the matched string is found, so we adjust linelength and the - line number appropriately, but only when there actually was a match - (invert not set). Because the PCRE2_FIRSTLINE option is set, the start of - the match will always be before the first newline sequence. */ - - if (multiline & !invert) - { - char *endmatch = ptr + offsets[1]; - t = ptr; - while (t <= endmatch) - { - t = end_of_line(t, endptr, &endlinelength); - if (t < endmatch) linenumber++; else break; - } - linelength = t - ptr - endlinelength; - } - - /*** NOTE: Use only fwrite() to output the data line, so that binary - zeroes are treated as just another data character. */ + if (number) fprintf(stdout, "%lu:", linenumber); /* This extra option, for Jeffrey Friedl's debugging requirements, replaces the matched string, or a specific captured string if it exists, @@ -2218,46 +2798,109 @@ while (ptr < endptr) { int first = S_arg * 2; int last = first + 1; - FWRITE(ptr, 1, offsets[first], stdout); + FWRITE_IGNORE(ptr, 1, offsets[first], stdout); fprintf(stdout, "X"); - FWRITE(ptr + offsets[last], 1, linelength - offsets[last], stdout); + FWRITE_IGNORE(ptr + offsets[last], 1, linelength - offsets[last], stdout); } else #endif - /* We have to split the line(s) up if colouring, and search for further - matches, but not of course if the line is a non-match. */ + /* In multiline mode, or if colouring, we have to split the line(s) up + and search for further matches, but not of course if the line is a + non-match. In multiline mode this is necessary in case there is another + match that spans the end of the current line. When colouring we want to + colour all matches. */ - if (do_colour && !invert) + if ((multiline || do_colour) && !invert) { int plength; - FWRITE(ptr, 1, offsets[0], stdout); - fprintf(stdout, "%c[%sm", 0x1b, colour_string); - FWRITE(ptr + offsets[0], 1, offsets[1] - offsets[0], stdout); - fprintf(stdout, "%c[00m", 0x1b); + PCRE2_SIZE endprevious; + + /* The use of \K may make the end offset earlier than the start. In + this situation, swap them round. */ + + if (offsets[0] > offsets[1]) + { + PCRE2_SIZE temp = offsets[0]; + offsets[0] = offsets[1]; + offsets[1] = temp; + } + + FWRITE_IGNORE(ptr, 1, offsets[0], stdout); + print_match(ptr + offsets[0], offsets[1] - offsets[0]); + for (;;) { - startoffset = offsets[1]; - if (startoffset >= linelength + endlinelength || - !match_patterns(matchptr, length, options, startoffset, &mrc)) - break; - FWRITE(matchptr + startoffset, 1, offsets[0] - startoffset, stdout); - fprintf(stdout, "%c[%sm", 0x1b, colour_string); - FWRITE(matchptr + offsets[0], 1, offsets[1] - offsets[0], stdout); - fprintf(stdout, "%c[00m", 0x1b); + PCRE2_SIZE oldstartoffset = pcre2_get_startchar(match_data); + + endprevious = offsets[1]; + startoffset = endprevious; /* Advance after previous match. */ + + /* If the pattern contained a lookbehind that included \K, it is + possible that the end of the match might be at or before the actual + starting offset we have just used. In this case, start one character + further on. */ + + if (startoffset <= oldstartoffset) + { + startoffset = oldstartoffset + 1; + if (utf) while ((ptr[startoffset] & 0xc0) == 0x80) startoffset++; + } + + /* If the current match ended past the end of the line (only possible + in multiline mode), we must move on to the line in which it did end + before searching for more matches. Because the PCRE2_FIRSTLINE option + is set, the start of the match will always be before the first + newline sequence. */ + + while (startoffset > linelength + endlinelength) + { + ptr += linelength + endlinelength; + filepos += (int)(linelength + endlinelength); + linenumber++; + startoffset -= (int)(linelength + endlinelength); + endprevious -= (int)(linelength + endlinelength); + t = end_of_line(ptr, endptr, &endlinelength); + linelength = t - ptr - endlinelength; + length = (PCRE2_SIZE)(endptr - ptr); + } + + /* If startoffset is at the exact end of the line it means this + complete line was the final part of the match, so there is nothing + more to do. */ + + if (startoffset == linelength + endlinelength) break; + + /* Otherwise, run a match from within the final line, and if found, + loop for any that may follow. */ + + if (!match_patterns(ptr, length, options, startoffset, &mrc)) break; + + /* The use of \K may make the end offset earlier than the start. In + this situation, swap them round. */ + + if (offsets[0] > offsets[1]) + { + PCRE2_SIZE temp = offsets[0]; + offsets[0] = offsets[1]; + offsets[1] = temp; + } + + FWRITE_IGNORE(ptr + endprevious, 1, offsets[0] - endprevious, stdout); + print_match(ptr + offsets[0], offsets[1] - offsets[0]); } /* In multiline mode, we may have already printed the complete line and its line-ending characters (if they matched the pattern), so there may be no more to print. */ - plength = (int)((linelength + endlinelength) - startoffset); - if (plength > 0) FWRITE(ptr + startoffset, 1, plength, stdout); + plength = (int)((linelength + endlinelength) - endprevious); + if (plength > 0) FWRITE_IGNORE(ptr + endprevious, 1, plength, stdout); } - /* Not colouring; no need to search for further matches */ + /* Not colouring or multiline; no need to search for further matches. */ - else FWRITE(ptr, 1, linelength + endlinelength, stdout); + else FWRITE_IGNORE(ptr, 1, linelength + endlinelength, stdout); } /* End of doing what has to be done for a match. If --line-buffered was @@ -2302,7 +2945,7 @@ while (ptr < endptr) /* If input is line buffered, and the buffer is not yet full, read another line and add it into the buffer. */ - if (input_line_buffered && bufflength < (size_t)bufsize) + if (input_line_buffered && bufflength < (PCRE2_SIZE)bufsize) { int add = read_one_line(ptr, bufsize - (int)(ptr - main_buffer), in); bufflength += add; @@ -2314,39 +2957,23 @@ while (ptr < endptr) 1/3 and refill it. Before we do this, if some unprinted "after" lines are about to be lost, print them. */ - if (bufflength >= (size_t)bufsize && ptr > main_buffer + 2*bufthird) + if (bufflength >= (PCRE2_SIZE)bufsize && ptr > main_buffer + 2*bufthird) { if (after_context > 0 && lastmatchnumber > 0 && lastmatchrestart < main_buffer + bufthird) { do_after_lines(lastmatchnumber, lastmatchrestart, endptr, printname); - lastmatchnumber = 0; + lastmatchnumber = 0; /* Indicates no after lines pending */ } /* Now do the shuffle */ - memmove(main_buffer, main_buffer + bufthird, 2*bufthird); + (void)memmove(main_buffer, main_buffer + bufthird, 2*bufthird); ptr -= bufthird; -#ifdef SUPPORT_LIBZ - if (frtype == FR_LIBZ) - bufflength = 2*bufthird + - gzread (ingz, main_buffer + 2*bufthird, bufthird); - else -#endif - -#ifdef SUPPORT_LIBBZ2 - if (frtype == FR_LIBBZ2) - bufflength = 2*bufthird + - BZ2_bzread(inbz2, main_buffer + 2*bufthird, bufthird); - else -#endif - - bufflength = 2*bufthird + - (input_line_buffered? - read_one_line(main_buffer + 2*bufthird, bufthird, in) : - fread(main_buffer + 2*bufthird, 1, bufthird, in)); + bufflength = 2*bufthird + fill_buffer(handle, frtype, + main_buffer + 2*bufthird, bufthird, input_line_buffered); endptr = main_buffer + bufflength; /* Adjust any last match point */ @@ -2358,7 +2985,7 @@ while (ptr < endptr) /* End of file; print final "after" lines if wanted; do_after_lines sets hyphenpending if it prints something. */ -if (!show_only_matching && !count_only) +if (only_matching_count == 0 && !(count_only|show_total_count)) { do_after_lines(lastmatchnumber, lastmatchrestart, endptr, printname); hyphenpending |= endhyphenpending; @@ -2381,10 +3008,12 @@ if (count_only && !quiet) { if (printname != NULL && filenames != FN_NONE) fprintf(stdout, "%s:", printname); - fprintf(stdout, "%d" STDOUT_NL, count); + fprintf(stdout, "%lu" STDOUT_NL, count); + counts_printed++; } } +total_count += count; /* Can be set without count_only */ return rc; } @@ -2503,7 +3132,7 @@ if (isdirectory(pathname)) if (dee_action == dee_RECURSE) { - char buffer[1024]; + char buffer[FNBUFSIZ]; char *nextfile; directory_type *dir = opendirectory(pathname); @@ -2518,7 +3147,14 @@ if (isdirectory(pathname)) while ((nextfile = readdirectory(dir)) != NULL) { int frc; - sprintf(buffer, "%.512s%c%.128s", pathname, FILESEP, nextfile); + int fnlength = strlen(pathname) + strlen(nextfile) + 2; + if (fnlength > FNBUFSIZ) + { + fprintf(stderr, "pcre2grep: recursive filename is too long\n"); + rc = 2; + break; + } + sprintf(buffer, "%s%c%s", pathname, FILESEP, nextfile); frc = grep_or_recurse(buffer, dir_recurse, FALSE); if (frc > 1) rc = frc; else if (frc == 0 && rc == 1) rc = 0; @@ -2529,6 +3165,36 @@ if (isdirectory(pathname)) } } +#ifdef WIN32 +if (iswild(pathname)) + { + char buffer[1024]; + char *nextfile; + char *name; + directory_type *dir = opendirectory(pathname); + + if (dir == NULL) + return 0; + + for (nextfile = name = pathname; *nextfile != 0; nextfile++) + if (*nextfile == '/' || *nextfile == '\\') + name = nextfile + 1; + *name = 0; + + while ((nextfile = readdirectory(dir)) != NULL) + { + int frc; + sprintf(buffer, "%.512s%.128s", pathname, nextfile); + frc = grep_or_recurse(buffer, dir_recurse, FALSE); + if (frc > 1) rc = frc; + else if (frc == 0 && rc == 1) rc = 0; + } + + closedirectory(dir); + return rc; + } +#endif + #if defined NATIVE_ZOS } #endif @@ -2669,13 +3335,13 @@ handle_option(int letter, int options) switch(letter) { case N_FOFFSETS: file_offsets = TRUE; break; - case N_HELP: help(); pcre2grep_exit(0); + case N_HELP: help(); pcre2grep_exit(0); break; /* Stops compiler warning */ case N_LBUFFER: line_buffered = TRUE; break; case N_LOFFSETS: line_offsets = number = TRUE; break; case N_NOJIT: use_jit = FALSE; break; case 'a': binary_files = BIN_TEXT; break; case 'c': count_only = TRUE; break; - case 'F': process_options |= PO_FIXED_STRINGS; break; + case 'F': options |= PCRE2_LITERAL; break; case 'H': filenames = FN_FORCE; break; case 'I': binary_files = BIN_NOMATCH; break; case 'h': filenames = FN_NONE; break; @@ -2693,10 +3359,11 @@ switch(letter) case 'q': quiet = TRUE; break; case 'r': dee_action = dee_RECURSE; break; case 's': silent = TRUE; break; + case 't': show_total_count = TRUE; break; case 'u': options |= PCRE2_UTF; utf = TRUE; break; case 'v': invert = TRUE; break; - case 'w': process_options |= PO_WORD_MATCH; break; - case 'x': process_options |= PO_LINE_MATCH; break; + case 'w': extra_options |= PCRE2_EXTRA_MATCH_WORD; break; + case 'x': extra_options |= PCRE2_EXTRA_MATCH_LINE; break; case 'V': { @@ -2717,7 +3384,6 @@ return options; - /************************************************* * Construct printed ordinal * *************************************************/ @@ -2731,6 +3397,8 @@ static char buffer[14]; char *p = buffer; sprintf(p, "%d", n); while (*p != 0) p++; +n %= 100; +if (n >= 11 && n <= 13) n = 0; switch (n%10) { case 1: strcpy(p, "st"); break; @@ -2758,7 +3426,6 @@ pattern chain. Arguments: p points to the pattern block options the PCRE options - popts the processing options fromfile TRUE if the pattern was read from a file fromtext file name or identifying text (e.g. "include") count 0 if this is the only command line pattern, or @@ -2769,18 +3436,19 @@ Returns: TRUE on success, FALSE after an error */ static BOOL -compile_pattern(patstr *p, int options, int popts, int fromfile, - const char *fromtext, int count) +compile_pattern(patstr *p, int options, int fromfile, const char *fromtext, + int count) { -unsigned char buffer[PATBUFSIZE]; -PCRE2_SIZE erroffset; -char *ps = p->string; -unsigned int patlen = strlen(ps); +char *ps; int errcode; +PCRE2_SIZE patlen, erroffset; +PCRE2_UCHAR errmessbuffer[ERRBUFSIZ]; if (p->compiled != NULL) return TRUE; +ps = p->string; +patlen = p->length; -if ((popts & PO_FIXED_STRINGS) != 0) +if ((options & PCRE2_LITERAL) != 0) { int ellength; char *eop = ps + patlen; @@ -2788,50 +3456,44 @@ if ((popts & PO_FIXED_STRINGS) != 0) if (ellength != 0) { - if (add_pattern(pe, p) == NULL) return FALSE; - patlen = (int)(pe - ps - ellength); + patlen = pe - ps - ellength; + if (add_pattern(pe, p->length-patlen-ellength, p) == NULL) return FALSE; } } -sprintf((char *)buffer, "%s%.*s%s", prefix[popts], patlen, ps, suffix[popts]); -p->compiled = pcre2_compile(buffer, PCRE2_ZERO_TERMINATED, options, &errcode, +p->compiled = pcre2_compile((PCRE2_SPTR)ps, patlen, options, &errcode, &erroffset, compile_context); -/* Handle successful compile */ +/* Handle successful compile. Try JIT-compiling if supported and enabled. We +ignore any JIT compiler errors, relying falling back to interpreting if +anything goes wrong with JIT. */ if (p->compiled != NULL) { #ifdef SUPPORT_PCRE2GREP_JIT - if (use_jit) - { - errcode = pcre2_jit_compile(p->compiled, PCRE2_JIT_COMPLETE); - if (errcode == 0) return TRUE; - erroffset = PCRE2_SIZE_MAX; /* Will get reduced to patlen below */ - } - else + if (use_jit) (void)pcre2_jit_compile(p->compiled, PCRE2_JIT_COMPLETE); #endif return TRUE; } -/* Handle compile and JIT compile errors */ +/* Handle compile errors */ -erroffset -= (int)strlen(prefix[popts]); if (erroffset > patlen) erroffset = patlen; -pcre2_get_error_message(errcode, buffer, PATBUFSIZE); +pcre2_get_error_message(errcode, errmessbuffer, sizeof(errmessbuffer)); if (fromfile) { fprintf(stderr, "pcre2grep: Error in regex in line %d of %s " - "at offset %d: %s\n", count, fromtext, (int)erroffset, buffer); + "at offset %d: %s\n", count, fromtext, (int)erroffset, errmessbuffer); } else { if (count == 0) fprintf(stderr, "pcre2grep: Error in %s regex at offset %d: %s\n", - fromtext, (int)erroffset, buffer); + fromtext, (int)erroffset, errmessbuffer); else fprintf(stderr, "pcre2grep: Error in %s %s regex at offset %d: %s\n", - ordin(count), fromtext, (int)erroffset, buffer); + ordin(count), fromtext, (int)erroffset, errmessbuffer); } return FALSE; @@ -2849,18 +3511,18 @@ return FALSE; name the name of the file; "-" is stdin patptr pointer to the pattern chain anchor patlastptr pointer to the last pattern pointer - popts the process options to pass to pattern_compile() Returns: TRUE if all went well */ static BOOL -read_pattern_file(char *name, patstr **patptr, patstr **patlastptr, int popts) +read_pattern_file(char *name, patstr **patptr, patstr **patlastptr) { int linenumber = 0; +PCRE2_SIZE patlen; FILE *f; -char *filename; -char buffer[PATBUFSIZE]; +const char *filename; +char buffer[MAXPATLEN+20]; if (strcmp(name, "-") == 0) { @@ -2878,20 +3540,18 @@ else filename = name; } -while (fgets(buffer, PATBUFSIZE, f) != NULL) +while ((patlen = read_one_line(buffer, sizeof(buffer), f)) > 0) { - char *s = buffer + (int)strlen(buffer); - while (s > buffer && isspace((unsigned char)(s[-1]))) s--; - *s = 0; + while (patlen > 0 && isspace((unsigned char)(buffer[patlen-1]))) patlen--; linenumber++; - if (buffer[0] == 0) continue; /* Skip blank lines */ + if (patlen == 0) continue; /* Skip blank lines */ /* Note: this call to add_pattern() puts a pointer to the local variable "buffer" into the pattern chain. However, that pointer is used only when compiling the pattern, which happens immediately below, so we flatten it afterwards, as a precaution against any later code trying to use it. */ - *patlastptr = add_pattern(buffer, *patlastptr); + *patlastptr = add_pattern(buffer, patlen, *patlastptr); if (*patlastptr == NULL) { if (f != stdin) fclose(f); @@ -2901,12 +3561,13 @@ while (fgets(buffer, PATBUFSIZE, f) != NULL) /* This loop is needed because compiling a "pattern" when -F is set may add on additional literal patterns if the original contains a newline. In the - common case, it never will, because fgets() stops at a newline. However, - the -N option can be used to give pcre2grep a different newline setting. */ + common case, it never will, because read_one_line() stops at a newline. + However, the -N option can be used to give pcre2grep a different newline + setting. */ for(;;) { - if (!compile_pattern(*patlastptr, pcre2_options, popts, TRUE, filename, + if (!compile_pattern(*patlastptr, pcre2_options, TRUE, filename, linenumber)) { if (f != stdin) fclose(f); @@ -2950,8 +3611,8 @@ change from ....\r\n to ....\r\r\n, which is not right. We therefore ensure that stdout is a binary stream. Note that this means all other output to stdout must use STDOUT_NL to terminate lines. */ -#if defined(_WIN32) || defined(WIN32) -_setmode( _fileno(stdout), _O_BINARY); +#ifdef WIN32 +_setmode(_fileno(stdout), _O_BINARY); #endif /* Set up a default compile and match contexts and a match data block. */ @@ -3047,14 +3708,23 @@ for (i = 1; i < argc; i++) { char buff1[24]; char buff2[24]; + int ret; int baselen = (int)(opbra - op->long_name); int fulllen = (int)(strchr(op->long_name, ')') - op->long_name + 1); int arglen = (argequals == NULL || equals == NULL)? (int)strlen(arg) : (int)(argequals - arg); - sprintf(buff1, "%.*s", baselen, op->long_name); - sprintf(buff2, "%s%.*s", buff1, fulllen - baselen - 2, opbra + 1); + if ((ret = snprintf(buff1, sizeof(buff1), "%.*s", baselen, op->long_name), + ret < 0 || ret > (int)sizeof(buff1)) || + (ret = snprintf(buff2, sizeof(buff2), "%s%.*s", buff1, + fulllen - baselen - 2, opbra + 1), + ret < 0 || ret > (int)sizeof(buff2))) + { + fprintf(stderr, "pcre2grep: Buffer overflow when parsing %s option\n", + op->long_name); + pcre2grep_exit(2); + } if (strncmp(arg, buff1, arglen) == 0 || strncmp(arg, buff2, arglen) == 0) @@ -3174,7 +3844,7 @@ for (i = 1; i < argc; i++) switch (op->one_char) { case N_COLOUR: - colour_option = (char *)"auto"; + colour_option = "auto"; break; case 'o': @@ -3221,7 +3891,8 @@ for (i = 1; i < argc; i++) else if (op->type == OP_PATLIST) { patdatastr *pd = (patdatastr *)op->dataptr; - *(pd->lastptr) = add_pattern(option_data, *(pd->lastptr)); + *(pd->lastptr) = add_pattern(option_data, (PCRE2_SIZE)strlen(option_data), + *(pd->lastptr)); if (*(pd->lastptr) == NULL) goto EXIT2; if (*(pd->anchor) == NULL) *(pd->anchor) = *(pd->lastptr); } @@ -3268,7 +3939,7 @@ for (i = 1; i < argc; i++) /* Otherwise, deal with a single string or numeric data value. */ else if (op->type != OP_NUMBER && op->type != OP_U32NUMBER && - op->type != OP_OP_NUMBER) + op->type != OP_OP_NUMBER && op->type != OP_SIZE) { *((char **)op->dataptr) = option_data; } @@ -3276,6 +3947,7 @@ for (i = 1; i < argc; i++) { unsigned long int n = decode_number(option_data, op, longop); if (op->type == OP_U32NUMBER) *((uint32_t *)op->dataptr) = n; + else if (op->type == OP_SIZE) *((PCRE2_SIZE *)op->dataptr) = n; else *((int *)op->dataptr) = n; } } @@ -3289,25 +3961,31 @@ if (both_context > 0) if (before_context == 0) before_context = both_context; } -/* Only one of --only-matching, --file-offsets, or --line-offsets is permitted. -However, all three set show_only_matching because they display, each in their -own way, only the data that has matched. */ +/* Only one of --only-matching, --output, --file-offsets, or --line-offsets is +permitted. They display, each in their own way, only the data that has matched. +*/ -if ((only_matching != NULL && (file_offsets || line_offsets)) || - (file_offsets && line_offsets)) +only_matching_count = (only_matching != NULL) + (output_text != NULL) + + file_offsets + line_offsets; + +if (only_matching_count > 1) { - fprintf(stderr, "pcre2grep: Cannot mix --only-matching, --file-offsets " - "and/or --line-offsets\n"); + fprintf(stderr, "pcre2grep: Cannot mix --only-matching, --output, " + "--file-offsets and/or --line-offsets\n"); pcre2grep_exit(usage(2)); } +/* Check the text supplied to --output for errors. */ + +if (output_text != NULL && + !syntax_check_output_text((PCRE2_SPTR)output_text, FALSE)) + goto EXIT2; + /* Put limits into the match data block. */ +if (heap_limit != PCRE2_UNSET) pcre2_set_heap_limit(match_context, heap_limit); if (match_limit > 0) pcre2_set_match_limit(match_context, match_limit); -if (recursion_limit > 0) pcre2_set_recursion_limit(match_context, recursion_limit); - -if (only_matching != NULL || file_offsets || line_offsets) - show_only_matching = TRUE; +if (depth_limit > 0) pcre2_set_depth_limit(match_context, depth_limit); /* If a locale has not been provided as an option, see if the LC_CTYPE or LC_ALL environment variable is set, and if so, use it. */ @@ -3315,7 +3993,7 @@ LC_ALL environment variable is set, and if so, use it. */ if (locale == NULL) { locale = getenv("LC_ALL"); - locale_from = "LCC_ALL"; + locale_from = "LC_ALL"; } if (locale == NULL) @@ -3343,7 +4021,11 @@ if (locale != NULL) if (colour_option != NULL && strcmp(colour_option, "never") != 0) { - if (strcmp(colour_option, "always") == 0) do_colour = TRUE; + if (strcmp(colour_option, "always") == 0) +#ifdef WIN32 + do_ansi = !is_stdout_tty(), +#endif + do_colour = TRUE; else if (strcmp(colour_option, "auto") == 0) do_colour = is_stdout_tty(); else { @@ -3355,7 +4037,17 @@ if (colour_option != NULL && strcmp(colour_option, "never") != 0) { char *cs = getenv("PCRE2GREP_COLOUR"); if (cs == NULL) cs = getenv("PCRE2GREP_COLOR"); - if (cs != NULL) colour_string = cs; + if (cs == NULL) cs = getenv("PCREGREP_COLOUR"); + if (cs == NULL) cs = getenv("PCREGREP_COLOR"); + if (cs == NULL) cs = parse_grep_colors(getenv("GREP_COLORS")); + if (cs == NULL) cs = getenv("GREP_COLOR"); + if (cs != NULL) + { + if (strspn(cs, ";0123456789") == strlen(cs)) colour_string = cs; + } +#ifdef WIN32 + init_colour_output(); +#endif } } @@ -3410,6 +4102,10 @@ if (DEE_option != NULL) } } +/* Set the extra options */ + +(void)pcre2_set_compile_extra_options(compile_context, extra_options); + /* Check the values for Jeffrey Friedl's debugging options. */ #ifdef JFRIEDL_DEBUG @@ -3425,8 +4121,24 @@ if (jfriedl_XT != 0 || jfriedl_XR != 0) } #endif +/* If use_jit is set, check whether JIT is available. If not, do not try +to use JIT. */ + +if (use_jit) + { + uint32_t answer; + (void)pcre2_config(PCRE2_CONFIG_JIT, &answer); + if (!answer) use_jit = FALSE; + } + /* Get memory for the main buffer. */ +if (bufthird <= 0) + { + fprintf(stderr, "pcre2grep: --buffer-size must be greater than zero\n"); + goto EXIT2; + } + bufsize = 3*bufthird; main_buffer = (char *)malloc(bufsize); @@ -3442,7 +4154,9 @@ the first argument is the one and only pattern, and it must exist. */ if (patterns == NULL && pattern_files == NULL) { if (i >= argc) return usage(2); - patterns = patterns_last = add_pattern(argv[i++], NULL); + patterns = patterns_last = add_pattern(argv[i], (PCRE2_SIZE)strlen(argv[i]), + NULL); + i++; if (patterns == NULL) goto EXIT2; } @@ -3454,7 +4168,7 @@ chain, so we must not access the next pointer till after the compile. */ for (j = 1, cp = patterns; cp != NULL; j++, cp = cp->next) { - if (!compile_pattern(cp, pcre2_options, process_options, FALSE, "command-line", + if (!compile_pattern(cp, pcre2_options, FALSE, "command-line", (j == 1 && patterns->next == NULL)? 0 : j)) goto EXIT2; } @@ -3463,35 +4177,35 @@ for (j = 1, cp = patterns; cp != NULL; j++, cp = cp->next) for (fn = pattern_files; fn != NULL; fn = fn->next) { - if (!read_pattern_file(fn->name, &patterns, &patterns_last, process_options)) - goto EXIT2; + if (!read_pattern_file(fn->name, &patterns, &patterns_last)) goto EXIT2; } /* Unless JIT has been explicitly disabled, arrange a stack for it to use. */ #ifdef SUPPORT_PCRE2GREP_JIT if (use_jit) + { jit_stack = pcre2_jit_stack_create(32*1024, 1024*1024, NULL); + if (jit_stack != NULL ) + pcre2_jit_stack_assign(match_context, NULL, jit_stack); + } #endif -for (j = 1, cp = patterns; cp != NULL; j++, cp = cp->next) - { -#ifdef SUPPORT_PCRE2GREP_JIT - if (jit_stack != NULL && cp->compiled != NULL) - pcre2_jit_stack_assign(match_context, NULL, jit_stack); -#endif - } +/* -F, -w, and -x do not apply to include or exclude patterns, so we must +adjust the options. */ + +pcre2_options &= ~PCRE2_LITERAL; +(void)pcre2_set_compile_extra_options(compile_context, 0); /* If there are include or exclude patterns read from the command line, compile -them. -F, -w, and -x do not apply, so the third argument of compile_pattern is -0. */ +them. */ for (j = 0; j < 4; j++) { int k; for (k = 1, cp = *(incexlist[j]); cp != NULL; k++, cp = cp->next) { - if (!compile_pattern(cp, pcre2_options, 0, FALSE, incexname[j], + if (!compile_pattern(cp, pcre2_options, FALSE, incexname[j], (k == 1 && cp->next == NULL)? 0 : k)) goto EXIT2; } @@ -3501,13 +4215,13 @@ for (j = 0; j < 4; j++) for (fn = include_from; fn != NULL; fn = fn->next) { - if (!read_pattern_file(fn->name, &include_patterns, &include_patterns_last, 0)) + if (!read_pattern_file(fn->name, &include_patterns, &include_patterns_last)) goto EXIT2; } for (fn = exclude_from; fn != NULL; fn = fn->next) { - if (!read_pattern_file(fn->name, &exclude_patterns, &exclude_patterns_last, 0)) + if (!read_pattern_file(fn->name, &exclude_patterns, &exclude_patterns_last)) goto EXIT2; } @@ -3526,7 +4240,7 @@ read them line by line and search the given files. */ for (fn = file_lists; fn != NULL; fn = fn->next) { - char buffer[PATBUFSIZE]; + char buffer[FNBUFSIZ]; FILE *fl; if (strcmp(fn->name, "-") == 0) fl = stdin; else { @@ -3538,7 +4252,7 @@ for (fn = file_lists; fn != NULL; fn = fn->next) goto EXIT2; } } - while (fgets(buffer, PATBUFSIZE, fl) != NULL) + while (fgets(buffer, sizeof(buffer), fl) != NULL) { int frc; char *end = buffer + (int)strlen(buffer); @@ -3568,6 +4282,24 @@ for (; i < argc; i++) else if (frc == 0 && rc == 1) rc = 0; } +#ifdef SUPPORT_PCRE2GREP_CALLOUT +/* If separating builtin echo callouts by implicit newline, add one more for +the final item. */ + +if (om_separator != NULL && strcmp(om_separator, STDOUT_NL) == 0) + fprintf(stdout, STDOUT_NL); +#endif + +/* Show the total number of matches if requested, but not if only one file's +count was printed. */ + +if (show_total_count && counts_printed != 1 && filenames != FN_NOMATCH_ONLY) + { + if (counts_printed != 0 && filenames >= FN_DEFAULT) + fprintf(stdout, "TOTAL:"); + fprintf(stdout, "%lu" STDOUT_NL, total_count); + } + EXIT: #ifdef SUPPORT_PCRE2GREP_JIT if (jit_stack != NULL) pcre2_jit_stack_free(jit_stack); diff --git a/pcre2-10.22/src/pcre2posix.c b/pcre2-10.32/src/pcre2posix.c similarity index 93% rename from pcre2-10.22/src/pcre2posix.c rename to pcre2-10.32/src/pcre2posix.c index e7cf9c294..7b9f47742 100644 --- a/pcre2-10.22/src/pcre2posix.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + New API code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -93,7 +93,7 @@ information; I know nothing about MSVC myself). For example, something like void __cdecl function(....) -might be needed. In order so make this easy, all the exported functions have +might be needed. In order to make this easy, all the exported functions have PCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not set, we ensure here that it has no effect. */ @@ -142,6 +142,7 @@ static const int eint2[] = { 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 */ + 92, REG_INVARG, /* invalid option bits with PCRE2_LITERAL */ }; /* Table of texts corresponding to POSIX error codes */ @@ -231,20 +232,25 @@ PCRE2POSIX_EXP_DEFN int PCRE2_CALL_CONVENTION regcomp(regex_t *preg, const char *pattern, int cflags) { PCRE2_SIZE erroffset; +PCRE2_SIZE patlen; int errorcode; int options = 0; int re_nsub = 0; +patlen = ((cflags & REG_PEND) != 0)? (PCRE2_SIZE)(preg->re_endp - pattern) : + PCRE2_ZERO_TERMINATED; + if ((cflags & REG_ICASE) != 0) options |= PCRE2_CASELESS; if ((cflags & REG_NEWLINE) != 0) options |= PCRE2_MULTILINE; if ((cflags & REG_DOTALL) != 0) options |= PCRE2_DOTALL; +if ((cflags & REG_NOSPEC) != 0) options |= PCRE2_LITERAL; if ((cflags & REG_UTF) != 0) options |= PCRE2_UTF; if ((cflags & REG_UCP) != 0) options |= PCRE2_UCP; if ((cflags & REG_UNGREEDY) != 0) options |= PCRE2_UNGREEDY; preg->re_cflags = cflags; -preg->re_pcre2_code = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, - options, &errorcode, &erroffset, NULL); +preg->re_pcre2_code = pcre2_compile((PCRE2_SPTR)pattern, patlen, options, + &errorcode, &erroffset, NULL); preg->re_erroffset = erroffset; if (preg->re_pcre2_code == NULL) @@ -259,7 +265,7 @@ if (preg->re_pcre2_code == NULL) if (errorcode < (int)(sizeof(eint1)/sizeof(const int))) return eint1[errorcode]; - for (i = 0; i < sizeof(eint2)/(2*sizeof(const int)); i += 2) + for (i = 0; i < sizeof(eint2)/sizeof(const int); i += 2) if (errorcode == eint2[i]) return eint2[i+1]; return REG_BADPAT; } @@ -286,8 +292,7 @@ return 0; /* A suitable match_data block, large enough to hold all possible captures, was obtained when the pattern was compiled, to save having to allocate and free it -for each match. If REG_NOSUB was specified at compile time, the -PCRE_NO_AUTO_CAPTURE flag will be set. When this is the case, the nmatch and +for each match. If REG_NOSUB was specified at compile time, the nmatch and pmatch arguments are ignored, and the only result is yes/no/error. */ PCRE2POSIX_EXP_DEFN int PCRE2_CALL_CONVENTION @@ -339,8 +344,10 @@ if (rc >= 0) if ((size_t)rc > nmatch) rc = (int)nmatch; for (i = 0; i < (size_t)rc; i++) { - pmatch[i].rm_so = ovector[i*2]; - pmatch[i].rm_eo = ovector[i*2+1]; + pmatch[i].rm_so = (ovector[i*2] == PCRE2_UNSET)? -1 : + (int)(ovector[i*2] + so); + pmatch[i].rm_eo = (ovector[i*2+1] == PCRE2_UNSET)? -1 : + (int)(ovector[i*2+1] + so); } for (; i < nmatch; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; return 0; diff --git a/pcre2-10.22/src/pcre2posix.h b/pcre2-10.32/src/pcre2posix.h similarity index 93% rename from pcre2-10.22/src/pcre2posix.h rename to pcre2-10.32/src/pcre2posix.h index 5f2906a4f..4ae1d3c2a 100644 --- a/pcre2-10.22/src/pcre2posix.h +++ b/pcre2-10.32/src/pcre2posix.h @@ -56,12 +56,14 @@ extern "C" { #define REG_NOTBOL 0x0004 /* Maps to PCRE2_NOTBOL */ #define REG_NOTEOL 0x0008 /* Maps to PCRE2_NOTEOL */ #define REG_DOTALL 0x0010 /* NOT defined by POSIX; maps to PCRE2_DOTALL */ -#define REG_NOSUB 0x0020 /* Maps to PCRE2_NO_AUTO_CAPTURE */ +#define REG_NOSUB 0x0020 /* Do not report what was matched */ #define REG_UTF 0x0040 /* NOT defined by POSIX; maps to PCRE2_UTF */ #define REG_STARTEND 0x0080 /* BSD feature: pass subject string by so,eo */ #define REG_NOTEMPTY 0x0100 /* NOT defined by POSIX; maps to PCRE2_NOTEMPTY */ #define REG_UNGREEDY 0x0200 /* NOT defined by POSIX; maps to PCRE2_UNGREEDY */ #define REG_UCP 0x0400 /* NOT defined by POSIX; maps to PCRE2_UCP */ +#define REG_PEND 0x0800 /* GNU feature: pass end pattern by re_endp */ +#define REG_NOSPEC 0x1000 /* Maps to PCRE2_LITERAL */ /* This is not used by PCRE2, but by defining it we make it easier to slot PCRE2 into existing programs that make POSIX calls. */ @@ -91,11 +93,13 @@ enum { }; -/* The structure representing a compiled regular expression. */ +/* The structure representing a compiled regular expression. It is also used +for passing the pattern end pointer when REG_PEND is set. */ typedef struct { void *re_pcre2_code; void *re_match_data; + const char *re_endp; size_t re_nsub; size_t re_erroffset; int re_cflags; diff --git a/pcre2-10.22/src/pcre2test.c b/pcre2-10.32/src/pcre2test.c similarity index 79% rename from pcre2-10.22/src/pcre2test.c rename to pcre2-10.32/src/pcre2test.c index a8dffa372..8cfb8e919 100644 --- a/pcre2-10.22/src/pcre2test.c +++ b/pcre2-10.32/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) 2016 University of Cambridge + Rewritten code Copyright (c) 2016-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -78,6 +78,10 @@ from www.cbttape.org. */ #include #endif +/* Debugging code enabler */ + +/* #define DEBUG_SHOW_MALLOC_ADDRESSES */ + /* Both libreadline and libedit are optionally supported. The user-supplied original patch uses readline/readline.h for libedit, but in at least one system it is installed as editline/readline.h, so the configuration code now looks for @@ -158,6 +162,18 @@ patterns. */ void vms_setsymbol( char *, char *, int ); #endif +/* VC and older compilers don't support %td or %zu. */ + +#if defined(_MSC_VER) || !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L +#define PTR_FORM "lu" +#define SIZ_FORM "lu" +#define SIZ_CAST (unsigned long int) +#else +#define PTR_FORM "td" +#define SIZ_FORM "zu" +#define SIZ_CAST +#endif + /* ------------------End of system-specific definitions -------------------- */ /* Glueing macros that are used in several places below. */ @@ -175,15 +191,18 @@ void vms_setsymbol( char *, char *, int ); #endif #endif -#define CFAIL_UNSET UINT32_MAX /* Unset value for cfail fields */ -#define DFA_WS_DIMENSION 1000 /* Size of DFA workspace */ -#define DEFAULT_OVECCOUNT 15 /* Default ovector count */ -#define JUNK_OFFSET 0xdeadbeef /* For initializing ovector */ -#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 100 /* Field for reading 8-bit replacement */ -#define VERSION_SIZE 64 /* Size of buffer for the version strings */ +#define CFORE_UNSET UINT32_MAX /* Unset value for startend/cfail/cerror fields */ +#define CONVERT_UNSET UINT32_MAX /* Unset value for convert_type field */ +#define DFA_WS_DIMENSION 1000 /* Size of DFA workspace */ +#define DEFAULT_OVECCOUNT 15 /* Default ovector count */ +#define JUNK_OFFSET 0xdeadbeef /* For initializing ovector */ +#define LOCALESIZE 32 /* Size of locale name */ +#define LOOPREPEAT 500000 /* Default loop count for timing */ +#define MALLOCLISTSIZE 20 /* For remembering mallocs */ +#define PARENS_NEST_DEFAULT 220 /* Default parentheses nest limit */ +#define PATSTACKSIZE 20 /* Pattern stack for save/restore testing */ +#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. */ @@ -211,7 +230,7 @@ systems that differ in their output from isprint() even in the "C" locale. */ #define PRINTABLE(c) ((c) >= 32 && (c) < 127) #endif -#define PRINTOK(c) ((locale_tables != NULL)? isprint(c) : PRINTABLE(c)) +#define PRINTOK(c) ((use_tables != NULL && c < 256)? isprint(c) : PRINTABLE(c)) /* We have to include some of the library source files because we need to use some of the macros, internal structure definitions, and other internal @@ -312,7 +331,7 @@ extern int valid_utf(PCRE2_SPTR8, PCRE2_SIZE, PCRE2_SIZE *); /* If we have 8-bit support, default to it; if there is also 16-or 32-bit support, it can be selected by a command-line option. If there is no 8-bit -support, there must be 16- or 32-bit support, so default to one of them. The +support, there must be 16-bit or 32-bit support, so default to one of them. The config function, JIT stack, contexts, and version string are the same in all modes, so use the form of the first that is available. */ @@ -323,8 +342,8 @@ modes, so use the form of the first that is available. */ #define PCRE2_JIT_STACK pcre2_jit_stack_8 #define PCRE2_REAL_GENERAL_CONTEXT pcre2_real_general_context_8 #define PCRE2_REAL_COMPILE_CONTEXT pcre2_real_compile_context_8 +#define PCRE2_REAL_CONVERT_CONTEXT pcre2_real_convert_context_8 #define PCRE2_REAL_MATCH_CONTEXT pcre2_real_match_context_8 -#define VERSION_TYPE PCRE2_UCHAR8 #elif defined SUPPORT_PCRE2_16 #define DEFAULT_TEST_MODE PCRE16_MODE @@ -333,6 +352,7 @@ modes, so use the form of the first that is available. */ #define PCRE2_JIT_STACK pcre2_jit_stack_16 #define PCRE2_REAL_GENERAL_CONTEXT pcre2_real_general_context_16 #define PCRE2_REAL_COMPILE_CONTEXT pcre2_real_compile_context_16 +#define PCRE2_REAL_CONVERT_CONTEXT pcre2_real_convert_context_16 #define PCRE2_REAL_MATCH_CONTEXT pcre2_real_match_context_16 #elif defined SUPPORT_PCRE2_32 @@ -342,6 +362,7 @@ modes, so use the form of the first that is available. */ #define PCRE2_JIT_STACK pcre2_jit_stack_32 #define PCRE2_REAL_GENERAL_CONTEXT pcre2_real_general_context_32 #define PCRE2_REAL_COMPILE_CONTEXT pcre2_real_compile_context_32 +#define PCRE2_REAL_CONVERT_CONTEXT pcre2_real_convert_context_32 #define PCRE2_REAL_MATCH_CONTEXT pcre2_real_match_context_32 #endif @@ -366,7 +387,7 @@ static cmdstruct cmdlist[] = { { "save", CMD_SAVE }, { "subject", CMD_SUBJECT }}; -#define cmdlistcount sizeof(cmdlist)/sizeof(cmdstruct) +#define cmdlistcount (sizeof(cmdlist)/sizeof(cmdstruct)) /* ------------- Structures and tables for handling modifiers -------------- */ @@ -374,7 +395,24 @@ static cmdstruct cmdlist[] = { of PCRE2_NEWLINE_xx in pcre2.h. */ static const char *newlines[] = { - "DEFAULT", "CR", "LF", "CRLF", "ANY", "ANYCRLF" }; + "DEFAULT", "CR", "LF", "CRLF", "ANY", "ANYCRLF", "NUL" }; + +/* Structure and table for handling pattern conversion types. */ + +typedef struct convertstruct { + const char *name; + uint32_t option; +} convertstruct; + +static convertstruct convertlist[] = { + { "glob", PCRE2_CONVERT_GLOB }, + { "glob_no_starstar", PCRE2_CONVERT_GLOB_NO_STARSTAR }, + { "glob_no_wild_separator", PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR }, + { "posix_basic", PCRE2_CONVERT_POSIX_BASIC }, + { "posix_extended", PCRE2_CONVERT_POSIX_EXTENDED }, + { "unset", CONVERT_UNSET }}; + +#define convertlistcount (sizeof(convertlist)/sizeof(convertstruct)) /* Modifier types and applicability */ @@ -387,6 +425,8 @@ enum { MOD_CTC, /* Applies to a compile context */ MOD_PDP, /* As MOD_PD, OK for Perl test */ MOD_PND, /* As MOD_PD, but not for a default pattern */ MOD_PNDP, /* As MOD_PND, OK for Perl test */ + MOD_CHR, /* Is a single character */ + MOD_CON, /* Is a "convert" type/options list */ MOD_CTL, /* Is a control bit */ MOD_BSR, /* Is a BSR value */ MOD_IN2, /* Is one or two unsigned integers */ @@ -415,33 +455,26 @@ so many of them that they are split into two fields. */ #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_POSIX_NOSUB 0x00800000u -#define CTL_PUSH 0x01000000u -#define CTL_PUSHCOPY 0x02000000u -#define CTL_STARTCHAR 0x04000000u -#define CTL_ZERO_TERMINATE 0x08000000u -/* Spare 0x10000000u */ -/* Spare 0x20000000u */ -#define CTL_NL_SET 0x40000000u /* Informational */ -#define CTL_BSR_SET 0x80000000u /* 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 +#define CTL_FRAMESIZE 0x00001000u +#define CTL_FULLBINCODE 0x00002000u +#define CTL_GETALL 0x00004000u +#define CTL_GLOBAL 0x00008000u +#define CTL_HEXPAT 0x00010000u /* Same word as USE_LENGTH */ +#define CTL_INFO 0x00020000u +#define CTL_JITFAST 0x00040000u +#define CTL_JITVERIFY 0x00080000u +#define CTL_MARK 0x00100000u +#define CTL_MEMORY 0x00200000u +#define CTL_NULLCONTEXT 0x00400000u +#define CTL_POSIX 0x00800000u +#define CTL_POSIX_NOSUB 0x01000000u +#define CTL_PUSH 0x02000000u /* These three must be */ +#define CTL_PUSHCOPY 0x04000000u /* all in the same */ +#define CTL_PUSHTABLESCOPY 0x08000000u /* word. */ +#define CTL_STARTCHAR 0x10000000u +#define CTL_USE_LENGTH 0x20000000u /* Same word as HEXPAT */ +#define CTL_UTF8_INPUT 0x40000000u +#define CTL_ZERO_TERMINATE 0x80000000u /* Combinations */ @@ -449,8 +482,23 @@ so many of them that they are split into two fields. */ #define CTL_ANYINFO (CTL_DEBUG|CTL_BINCODE|CTL_CALLOUT_INFO) #define CTL_ANYGLOB (CTL_ALTGLOBAL|CTL_GLOBAL) -/* These are all the controls that may be set either on a pattern or on a -data line. */ +/* 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 +#define CTL2_SUBJECT_LITERAL 0x00000010u +#define CTL2_CALLOUT_NO_WHERE 0x00000020u +#define CTL2_CALLOUT_EXTRA 0x00000040u + +#define CTL2_NL_SET 0x40000000u /* Informational */ +#define CTL2_BSR_SET 0x80000000u /* Informational */ + +/* These are the matching controls that may be set either on a pattern or on a +data line. They are copied from the pattern controls as initial settings for +data line controls. Note that CTL_MEMORY is not included here, because it does +different things in the two cases. */ #define CTL_ALLPD (CTL_AFTERTEXT|\ CTL_ALLAFTERTEXT|\ @@ -459,8 +507,8 @@ data line. */ CTL_ALTGLOBAL|\ CTL_GLOBAL|\ CTL_MARK|\ - CTL_MEMORY|\ - CTL_STARTCHAR) + CTL_STARTCHAR|\ + CTL_UTF8_INPUT) #define CTL2_ALLPD (CTL2_SUBSTITUTE_EXTENDED|\ CTL2_SUBSTITUTE_OVERFLOW_LENGTH|\ @@ -476,10 +524,15 @@ 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 */ + uint32_t jitstack; /* 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 convert_type; + uint32_t convert_length; + uint32_t convert_glob_escape; + uint32_t convert_glob_separator; uint32_t regerror_buffsize; uint8_t locale[LOCALESIZE]; } patctl; @@ -491,12 +544,14 @@ 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 */ + uint32_t jitstack; /* Must be in same position as patctl */ uint8_t replacement[REPLACE_MODSIZE]; /* So must this */ + uint32_t startend[2]; + uint32_t cerror[2]; uint32_t cfail[2]; int32_t callout_data; int32_t copy_numbers[MAXCPYGET]; int32_t get_numbers[MAXCPYGET]; - uint32_t jitstack; uint32_t oveccount; uint32_t offset; uint8_t copy_names[LENCPYGET]; @@ -535,6 +590,7 @@ static modstruct modlist[] = { { "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) }, + { "allow_surrogate_escapes", MOD_CTC, MOD_OPT, PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES, CO(extra_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) }, @@ -542,40 +598,56 @@ static modstruct modlist[] = { { "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) }, + { "bad_escape_is_literal", MOD_CTC, MOD_OPT, PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL, CO(extra_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_error", MOD_DAT, MOD_IN2, 0, DO(cerror) }, + { "callout_extra", MOD_DAT, MOD_CTL, CTL2_CALLOUT_EXTRA, DO(control2) }, { "callout_fail", MOD_DAT, MOD_IN2, 0, DO(cfail) }, { "callout_info", MOD_PAT, MOD_CTL, CTL_CALLOUT_INFO, PO(control) }, + { "callout_no_where", MOD_DAT, MOD_CTL, CTL2_CALLOUT_NO_WHERE, DO(control2) }, { "callout_none", MOD_DAT, MOD_CTL, CTL_CALLOUT_NONE, DO(control) }, { "caseless", MOD_PATP, MOD_OPT, PCRE2_CASELESS, PO(options) }, + { "convert", MOD_PAT, MOD_CON, 0, PO(convert_type) }, + { "convert_glob_escape", MOD_PAT, MOD_CHR, 0, PO(convert_glob_escape) }, + { "convert_glob_separator", MOD_PAT, MOD_CHR, 0, PO(convert_glob_separator) }, + { "convert_length", MOD_PAT, MOD_INT, 0, PO(convert_length) }, { "copy", MOD_DAT, MOD_NN, DO(copy_numbers), DO(copy_names) }, { "debug", MOD_PAT, MOD_CTL, CTL_DEBUG, PO(control) }, + { "depth_limit", MOD_CTM, MOD_INT, 0, MO(depth_limit) }, { "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) }, + { "endanchored", MOD_PD, MOD_OPT, PCRE2_ENDANCHORED, PD(options) }, { "expand", MOD_PAT, MOD_CTL, CTL_EXPAND, PO(control) }, { "extended", MOD_PATP, MOD_OPT, PCRE2_EXTENDED, PO(options) }, + { "extended_more", MOD_PATP, MOD_OPT, PCRE2_EXTENDED_MORE, PO(options) }, { "find_limits", MOD_DAT, MOD_CTL, CTL_FINDLIMITS, DO(control) }, { "firstline", MOD_PAT, MOD_OPT, PCRE2_FIRSTLINE, PO(options) }, + { "framesize", MOD_PAT, MOD_CTL, CTL_FRAMESIZE, PO(control) }, { "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) }, + { "heap_limit", MOD_CTM, MOD_INT, 0, MO(heap_limit) }, { "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) }, + { "jitstack", MOD_PNDP, MOD_INT, 0, PO(jitstack) }, { "jitverify", MOD_PAT, MOD_CTL, CTL_JITVERIFY, PO(control) }, + { "literal", MOD_PAT, MOD_OPT, PCRE2_LITERAL, PO(options) }, { "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_line", MOD_CTC, MOD_OPT, PCRE2_EXTRA_MATCH_LINE, CO(extra_options) }, { "match_unset_backref", MOD_PAT, MOD_OPT, PCRE2_MATCH_UNSET_BACKREF, PO(options) }, + { "match_word", MOD_CTC, MOD_OPT, PCRE2_EXTRA_MATCH_WORD, CO(extra_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) }, @@ -603,15 +675,18 @@ static modstruct modlist[] = { { "ph", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_HARD, DO(options) }, { "posix", MOD_PAT, MOD_CTL, CTL_POSIX, PO(control) }, { "posix_nosub", MOD_PAT, MOD_CTL, CTL_POSIX|CTL_POSIX_NOSUB, PO(control) }, + { "posix_startend", MOD_DAT, MOD_IN2, 0, DO(startend) }, { "ps", MOD_DAT, MOD_OPT, PCRE2_PARTIAL_SOFT, DO(options) }, { "push", MOD_PAT, MOD_CTL, CTL_PUSH, PO(control) }, - { "pushcopy", MOD_PAT, MOD_CTL, CTL_PUSHCOPY, PO(control) }, - { "recursion_limit", MOD_CTM, MOD_INT, 0, MO(recursion_limit) }, + { "pushcopy", MOD_PAT, MOD_CTL, CTL_PUSHCOPY, PO(control) }, + { "pushtablescopy", MOD_PAT, MOD_CTL, CTL_PUSHTABLESCOPY, PO(control) }, + { "recursion_limit", MOD_CTM, MOD_INT, 0, MO(depth_limit) }, /* Obsolete synonym */ { "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) }, + { "subject_literal", MOD_PATP, MOD_CTL, CTL2_SUBJECT_LITERAL, PO(control2) }, { "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) }, @@ -619,8 +694,10 @@ static modstruct modlist[] = { { "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_length", MOD_PAT, MOD_CTL, CTL_USE_LENGTH, PO(control) }, { "use_offset_limit", MOD_PAT, MOD_OPT, PCRE2_USE_OFFSET_LIMIT, PO(options) }, { "utf", MOD_PATP, MOD_OPT, PCRE2_UTF, PO(options) }, + { "utf8_input", MOD_PAT, MOD_CTL, CTL_UTF8_INPUT, PO(control) }, { "zero_terminate", MOD_DAT, MOD_CTL, CTL_ZERO_TERMINATE, DO(control) } }; @@ -629,11 +706,14 @@ static modstruct modlist[] = { /* Controls and options that are supported for use with the POSIX interface. */ #define POSIX_SUPPORTED_COMPILE_OPTIONS ( \ - PCRE2_CASELESS|PCRE2_DOTALL|PCRE2_MULTILINE|PCRE2_UCP|PCRE2_UTF| \ - PCRE2_UNGREEDY) + PCRE2_CASELESS|PCRE2_DOTALL|PCRE2_LITERAL|PCRE2_MULTILINE|PCRE2_UCP| \ + PCRE2_UTF|PCRE2_UNGREEDY) + +#define POSIX_SUPPORTED_COMPILE_EXTRA_OPTIONS (0) #define POSIX_SUPPORTED_COMPILE_CONTROLS ( \ - CTL_AFTERTEXT|CTL_ALLAFTERTEXT|CTL_EXPAND|CTL_POSIX|CTL_POSIX_NOSUB) + CTL_AFTERTEXT|CTL_ALLAFTERTEXT|CTL_EXPAND|CTL_HEXPAT|CTL_POSIX| \ + CTL_POSIX_NOSUB|CTL_USE_LENGTH) #define POSIX_SUPPORTED_COMPILE_CONTROLS2 (0) @@ -647,9 +727,10 @@ static modstruct modlist[] = { #define PUSH_SUPPORTED_COMPILE_CONTROLS ( \ CTL_BINCODE|CTL_CALLOUT_INFO|CTL_FULLBINCODE|CTL_HEXPAT|CTL_INFO| \ - CTL_JITVERIFY|CTL_MEMORY|CTL_PUSH|CTL_PUSHCOPY|CTL_BSR_SET|CTL_NL_SET) + CTL_JITVERIFY|CTL_MEMORY|CTL_FRAMESIZE|CTL_PUSH|CTL_PUSHCOPY| \ + CTL_PUSHTABLESCOPY|CTL_USE_LENGTH) -#define PUSH_SUPPORTED_COMPILE_CONTROLS2 (0) +#define PUSH_SUPPORTED_COMPILE_CONTROLS2 (CTL2_BSR_SET|CTL2_NL_SET) /* Controls that apply only at compile time with 'push'. */ @@ -659,20 +740,24 @@ static modstruct modlist[] = { /* Controls that are forbidden with #pop or #popcopy. */ #define NOTPOP_CONTROLS (CTL_HEXPAT|CTL_POSIX|CTL_POSIX_NOSUB|CTL_PUSH| \ - CTL_PUSHCOPY) + CTL_PUSHCOPY|CTL_PUSHTABLESCOPY|CTL_USE_LENGTH) /* Pattern controls that are mutually exclusive. At present these are all in the first control word. Note that CTL_POSIX_NOSUB is always accompanied by CTL_POSIX, so it doesn't need its own entries. */ static uint32_t exclusive_pat_controls[] = { - CTL_POSIX | CTL_HEXPAT, - CTL_POSIX | CTL_PUSH, - CTL_POSIX | CTL_PUSHCOPY, - CTL_EXPAND | CTL_HEXPAT }; + CTL_POSIX | CTL_PUSH, + CTL_POSIX | CTL_PUSHCOPY, + CTL_POSIX | CTL_PUSHTABLESCOPY, + CTL_PUSH | CTL_PUSHCOPY, + CTL_PUSH | CTL_PUSHTABLESCOPY, + CTL_PUSHCOPY | CTL_PUSHTABLESCOPY, + 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 }; @@ -690,13 +775,14 @@ typedef struct c1modstruct { } c1modstruct; static c1modstruct c1modlist[] = { - { "bincode", 'B', -1 }, - { "info", 'I', -1 }, - { "global", 'g', -1 }, - { "caseless", 'i', -1 }, - { "multiline", 'm', -1 }, - { "dotall", 's', -1 }, - { "extended", 'x', -1 } + { "bincode", 'B', -1 }, + { "info", 'I', -1 }, + { "global", 'g', -1 }, + { "caseless", 'i', -1 }, + { "multiline", 'm', -1 }, + { "no_auto_capture", 'n', -1 }, + { "dotall", 's', -1 }, + { "extended", 'x', -1 } }; #define C1MODLISTCOUNT sizeof(c1modlist)/sizeof(c1modstruct) @@ -817,12 +903,17 @@ static datctl dat_datctl; static void *patstack[PATSTACKSIZE]; static int patstacknext = 0; +static void *malloclist[MALLOCLISTSIZE]; +static PCRE2_SIZE malloclistlength[MALLOCLISTSIZE]; +static uint32_t malloclistptr = 0; + #ifdef SUPPORT_PCRE2_8 -static regex_t preg = { NULL, NULL, 0, 0, 0 }; +static regex_t preg = { NULL, NULL, 0, 0, 0, 0 }; #endif static int *dfa_workspace = NULL; static const uint8_t *locale_tables = NULL; +static const uint8_t *use_tables = NULL; static uint8_t locale_name[32]; /* We need buffers for building 16/32-bit strings; 8-bit strings don't need @@ -848,6 +939,7 @@ static uint8_t *dbuffer = NULL; static pcre2_code_8 *compiled_code8; static pcre2_general_context_8 *general_context8, *general_context_copy8; static pcre2_compile_context_8 *pat_context8, *default_pat_context8; +static pcre2_convert_context_8 *con_context8, *default_con_context8; static pcre2_match_context_8 *dat_context8, *default_dat_context8; static pcre2_match_data_8 *match_data8; #endif @@ -856,6 +948,7 @@ static pcre2_match_data_8 *match_data8; static pcre2_code_16 *compiled_code16; static pcre2_general_context_16 *general_context16, *general_context_copy16; static pcre2_compile_context_16 *pat_context16, *default_pat_context16; +static pcre2_convert_context_16 *con_context16, *default_con_context16; static pcre2_match_context_16 *dat_context16, *default_dat_context16; static pcre2_match_data_16 *match_data16; static PCRE2_SIZE pbuffer16_size = 0; /* Set only when needed */ @@ -866,6 +959,7 @@ static uint16_t *pbuffer16 = NULL; static pcre2_code_32 *compiled_code32; static pcre2_general_context_32 *general_context32, *general_context_copy32; static pcre2_compile_context_32 *pat_context32, *default_pat_context32; +static pcre2_convert_context_32 *con_context32, *default_con_context32; static pcre2_match_context_32 *dat_context32, *default_dat_context32; static pcre2_match_data_32 *match_data32; static PCRE2_SIZE pbuffer32_size = 0; /* Set only when needed */ @@ -906,6 +1000,21 @@ are supported. */ (test_mode == PCRE16_MODE)? (uint32_t)(((PCRE2_SPTR16)(a))[b]) : \ (uint32_t)(((PCRE2_SPTR32)(a))[b])) +#define CONCTXCPY(a,b) \ + if (test_mode == PCRE8_MODE) \ + memcpy(G(a,8),G(b,8),sizeof(pcre2_convert_context_8)); \ + else if (test_mode == PCRE16_MODE) \ + memcpy(G(a,16),G(b,16),sizeof(pcre2_convert_context_16)); \ + else memcpy(G(a,32),G(b,32),sizeof(pcre2_convert_context_32)) + +#define CONVERT_COPY(a,b,c) \ + if (test_mode == PCRE8_MODE) \ + memcpy(G(a,8),(char *)b,c); \ + else if (test_mode == PCRE16_MODE) \ + memcpy(G(a,16),(char *)b,(c)*2); \ + else if (test_mode == PCRE32_MODE) \ + memcpy(G(a,32),(char *)b,(c)*4) + #define DATCTXCPY(a,b) \ if (test_mode == PCRE8_MODE) \ memcpy(G(a,8),G(b,8),sizeof(pcre2_match_context_8)); \ @@ -966,6 +1075,14 @@ are supported. */ else \ a = (void *)pcre2_code_copy_32(G(b,32)) +#define PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(a,b) \ + if (test_mode == PCRE8_MODE) \ + a = (void *)pcre2_code_copy_with_tables_8(G(b,8)); \ + else if (test_mode == PCRE16_MODE) \ + a = (void *)pcre2_code_copy_with_tables_16(G(b,16)); \ + else \ + a = (void *)pcre2_code_copy_with_tables_32(G(b,32)) + #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); \ @@ -974,6 +1091,11 @@ are supported. */ else \ G(a,32) = pcre2_compile_32(G(b,32),c,d,e,f,g) +#define PCRE2_CONVERTED_PATTERN_FREE(a) \ + if (test_mode == PCRE8_MODE) pcre2_converted_pattern_free_8((PCRE2_UCHAR8 *)a); \ + else if (test_mode == PCRE16_MODE) pcre2_converted_pattern_free_16((PCRE2_UCHAR16 *)a); \ + else pcre2_converted_pattern_free_32((PCRE2_UCHAR32 *)a) + #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),h,i,j); \ @@ -986,9 +1108,9 @@ are supported. */ if (test_mode == PCRE8_MODE) \ r = pcre2_get_error_message_8(a,G(b,8),G(G(b,8),_size)); \ else if (test_mode == PCRE16_MODE) \ - r = pcre2_get_error_message_16(a,G(b,16),G(G(b,16),_size)); \ + r = pcre2_get_error_message_16(a,G(b,16),G(G(b,16),_size/2)); \ else \ - r = pcre2_get_error_message_32(a,G(b,32),G(G(b,32),_size)) + r = pcre2_get_error_message_32(a,G(b,32),G(G(b,32),_size/4)) #define PCRE2_GET_OVECTOR_COUNT(a,b) \ if (test_mode == PCRE8_MODE) \ @@ -1085,6 +1207,14 @@ are supported. */ else \ pcre2_match_data_free_32(G(a,32)) +#define PCRE2_PATTERN_CONVERT(a,b,c,d,e,f,g) \ + if (test_mode == PCRE8_MODE) \ + a = pcre2_pattern_convert_8(G(b,8),c,d,(PCRE2_UCHAR8 **)e,f,G(g,8)); \ + else if (test_mode == PCRE16_MODE) \ + a = pcre2_pattern_convert_16(G(b,16),c,d,(PCRE2_UCHAR16 **)e,f,G(g,16)); \ + else \ + a = pcre2_pattern_convert_32(G(b,32),c,d,(PCRE2_UCHAR32 **)e,f,G(g,32)) + #define PCRE2_PATTERN_INFO(a,b,c,d) \ if (test_mode == PCRE8_MODE) \ a = pcre2_pattern_info_8(G(b,8),c,d); \ @@ -1157,6 +1287,38 @@ are supported. */ else \ pcre2_set_compile_recursion_guard_32(G(a,32),b,c) +#define PCRE2_SET_DEPTH_LIMIT(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_depth_limit_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_depth_limit_16(G(a,16),b); \ + else \ + pcre2_set_depth_limit_32(G(a,32),b) + +#define PCRE2_SET_GLOB_SEPARATOR(r,a,b) \ + if (test_mode == PCRE8_MODE) \ + r = pcre2_set_glob_separator_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + r = pcre2_set_glob_separator_16(G(a,16),b); \ + else \ + r = pcre2_set_glob_separator_32(G(a,32),b) + +#define PCRE2_SET_GLOB_ESCAPE(r,a,b) \ + if (test_mode == PCRE8_MODE) \ + r = pcre2_set_glob_escape_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + r = pcre2_set_glob_escape_16(G(a,16),b); \ + else \ + r = pcre2_set_glob_escape_32(G(a,32),b) + +#define PCRE2_SET_HEAP_LIMIT(a,b) \ + if (test_mode == PCRE8_MODE) \ + pcre2_set_heap_limit_8(G(a,8),b); \ + else if (test_mode == PCRE16_MODE) \ + pcre2_set_heap_limit_16(G(a,16),b); \ + else \ + pcre2_set_heap_limit_32(G(a,32),b) + #define PCRE2_SET_MATCH_LIMIT(a,b) \ if (test_mode == PCRE8_MODE) \ pcre2_set_match_limit_8(G(a,8),b); \ @@ -1189,14 +1351,6 @@ are supported. */ else \ pcre2_set_parens_nest_limit_32(G(a,32),b) -#define PCRE2_SET_RECURSION_LIMIT(a,b) \ - if (test_mode == PCRE8_MODE) \ - pcre2_set_recursion_limit_8(G(a,8),b); \ - else if (test_mode == PCRE16_MODE) \ - pcre2_set_recursion_limit_16(G(a,16),b); \ - else \ - pcre2_set_recursion_limit_32(G(a,32),b) - #define PCRE2_SUBSTITUTE(a,b,c,d,e,f,g,h,i,j,k,l) \ if (test_mode == PCRE8_MODE) \ a = pcre2_substitute_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),G(h,8), \ @@ -1339,7 +1493,6 @@ are supported. */ (test_mode == PCRE32_MODE && G(x,32)->f r (y))) - /* ----- Two out of three modes are supported ----- */ #else @@ -1369,6 +1522,9 @@ the three different cases. */ /* ----- Common macros for two-mode cases ----- */ +#define BYTEONE (BITONE/8) +#define BYTETWO (BITTWO/8) + #define CASTFLD(t,a,b) \ ((test_mode == G(G(PCRE,BITONE),_MODE))? (t)(G(a,BITONE)->b) : \ (t)(G(a,BITTWO)->b)) @@ -1382,6 +1538,17 @@ the three different cases. */ (uint32_t)(((G(PCRE2_SPTR,BITONE))(a))[b]) : \ (uint32_t)(((G(PCRE2_SPTR,BITTWO))(a))[b])) +#define CONCTXCPY(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + memcpy(G(a,BITONE),G(b,BITONE),sizeof(G(pcre2_convert_context_,BITONE))); \ + else \ + memcpy(G(a,BITTWO),G(b,BITTWO),sizeof(G(pcre2_convert_context_,BITTWO))) + +#define CONVERT_COPY(a,b,c) \ + (test_mode == G(G(PCRE,BITONE),_MODE))? \ + memcpy(G(a,BITONE),(char *)b,(c)*BYTEONE) : \ + memcpy(G(a,BITTWO),(char *)b,(c)*BYTETWO) + #define DATCTXCPY(a,b) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ memcpy(G(a,BITONE),G(b,BITONE),sizeof(G(pcre2_match_context_,BITONE))); \ @@ -1429,12 +1596,24 @@ the three different cases. */ else \ a = (void *)G(pcre2_code_copy_,BITTWO)(G(b,BITTWO)) +#define PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = (void *)G(pcre2_code_copy_with_tables_,BITONE)(G(b,BITONE)); \ + else \ + a = (void *)G(pcre2_code_copy_with_tables_,BITTWO)(G(b,BITTWO)) + #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); \ else \ G(a,BITTWO) = G(pcre2_compile_,BITTWO)(G(b,BITTWO),c,d,e,f,g) +#define PCRE2_CONVERTED_PATTERN_FREE(a) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_converted_pattern_free_,BITONE)((G(PCRE2_UCHAR,BITONE) *)a); \ + else \ + G(pcre2_converted_pattern_free_,BITTWO)((G(PCRE2_UCHAR,BITTWO) *)a) + #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, \ @@ -1445,9 +1624,9 @@ the three different cases. */ #define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ - r = G(pcre2_get_error_message_,BITONE)(a,G(b,BITONE),G(G(b,BITONE),_size)); \ + r = G(pcre2_get_error_message_,BITONE)(a,G(b,BITONE),G(G(b,BITONE),_size/BYTEONE)); \ else \ - r = G(pcre2_get_error_message_,BITTWO)(a,G(b,BITTWO),G(G(b,BITTWO),_size)) + r = G(pcre2_get_error_message_,BITTWO)(a,G(b,BITTWO),G(G(b,BITTWO),_size/BYTETWO)) #define PCRE2_GET_OVECTOR_COUNT(a,b) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ @@ -1531,6 +1710,12 @@ the three different cases. */ else \ G(pcre2_match_data_free_,BITTWO)(G(a,BITTWO)) +#define PCRE2_PATTERN_CONVERT(a,b,c,d,e,f,g) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + a = G(pcre2_pattern_convert_,BITONE)(G(b,BITONE),c,d,(G(PCRE2_UCHAR,BITONE) **)e,f,G(g,BITONE)); \ + else \ + a = G(pcre2_pattern_convert_,BITTWO)(G(b,BITTWO),c,d,(G(PCRE2_UCHAR,BITTWO) **)e,f,G(g,BITTWO)) + #define PCRE2_PATTERN_INFO(a,b,c,d) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ a = G(pcre2_pattern_info_,BITONE)(G(b,BITONE),c,d); \ @@ -1587,6 +1772,30 @@ the three different cases. */ else \ G(pcre2_set_compile_recursion_guard_,BITTWO)(G(a,BITTWO),b,c) +#define PCRE2_SET_DEPTH_LIMIT(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_depth_limit_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_depth_limit_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_GLOB_ESCAPE(r,a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + r = G(pcre2_set_glob_escape_,BITONE)(G(a,BITONE),b); \ + else \ + r = G(pcre2_set_glob_escape_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_GLOB_SEPARATOR(r,a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + r = G(pcre2_set_glob_separator_,BITONE)(G(a,BITONE),b); \ + else \ + r = G(pcre2_set_glob_separator_,BITTWO)(G(a,BITTWO),b) + +#define PCRE2_SET_HEAP_LIMIT(a,b) \ + if (test_mode == G(G(PCRE,BITONE),_MODE)) \ + G(pcre2_set_heap_limit_,BITONE)(G(a,BITONE),b); \ + else \ + G(pcre2_set_heap_limit_,BITTWO)(G(a,BITTWO),b) + #define PCRE2_SET_MATCH_LIMIT(a,b) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ G(pcre2_set_match_limit_,BITONE)(G(a,BITONE),b); \ @@ -1611,12 +1820,6 @@ the three different cases. */ else \ G(pcre2_set_parens_nest_limit_,BITTWO)(G(a,BITTWO),b) -#define PCRE2_SET_RECURSION_LIMIT(a,b) \ - if (test_mode == G(G(PCRE,BITONE),_MODE)) \ - G(pcre2_set_recursion_limit_,BITONE)(G(a,BITONE),b); \ - else \ - G(pcre2_set_recursion_limit_,BITTWO)(G(a,BITTWO),b) - #define PCRE2_SUBSTITUTE(a,b,c,d,e,f,g,h,i,j,k,l) \ if (test_mode == G(G(PCRE,BITONE),_MODE)) \ a = G(pcre2_substitute_,BITONE)(G(b,BITONE),(G(PCRE2_SPTR,BITONE))c,d,e,f, \ @@ -1754,6 +1957,8 @@ the three different cases. */ #define CASTFLD(t,a,b) (t)(G(a,8)->b) #define CASTVAR(t,x) (t)G(x,8) #define CODE_UNIT(a,b) (uint32_t)(((PCRE2_SPTR8)(a))[b]) +#define CONCTXCPY(a,b) memcpy(G(a,8),G(b,8),sizeof(pcre2_convert_context_8)) +#define CONVERT_COPY(a,b,c) memcpy(G(a,8),(char *)b, c) #define DATCTXCPY(a,b) memcpy(G(a,8),G(b,8),sizeof(pcre2_match_context_8)) #define FLD(a,b) G(a,8)->b #define PATCTXCPY(a,b) memcpy(G(a,8),G(b,8),sizeof(pcre2_compile_context_8)) @@ -1766,8 +1971,11 @@ the three different cases. */ (int (*)(struct pcre2_callout_enumerate_block_8 *, void *))b,c) #define PCRE2_CODE_COPY_FROM_VOID(a,b) G(a,8) = pcre2_code_copy_8(b) #define PCRE2_CODE_COPY_TO_VOID(a,b) a = (void *)pcre2_code_copy_8(G(b,8)) +#define PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(a,b) a = (void *)pcre2_code_copy_with_tables_8(G(b,8)) #define PCRE2_COMPILE(a,b,c,d,e,f,g) \ G(a,8) = pcre2_compile_8(G(b,8),c,d,e,f,g) +#define PCRE2_CONVERTED_PATTERN_FREE(a) \ + pcre2_converted_pattern_free_8((PCRE2_UCHAR8 *)a) #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),h,i,j) #define PCRE2_GET_ERROR_MESSAGE(r,a,b) \ @@ -1790,6 +1998,7 @@ the three different cases. */ #define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ G(a,8) = pcre2_match_data_create_from_pattern_8(G(b,8),c) #define PCRE2_MATCH_DATA_FREE(a) pcre2_match_data_free_8(G(a,8)) +#define PCRE2_PATTERN_CONVERT(a,b,c,d,e,f,g) a = pcre2_pattern_convert_8(G(b,8),c,d,(PCRE2_UCHAR8 **)e,f,G(g,8)) #define PCRE2_PATTERN_INFO(a,b,c,d) a = pcre2_pattern_info_8(G(b,8),c,d) #define PCRE2_PRINTINT(a) pcre2_printint_8(compiled_code8,outfile,a) #define PCRE2_SERIALIZE_DECODE(r,a,b,c,d) \ @@ -1804,11 +2013,14 @@ the three different cases. */ #define PCRE2_SET_CHARACTER_TABLES(a,b) pcre2_set_character_tables_8(G(a,8),b) #define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ pcre2_set_compile_recursion_guard_8(G(a,8),b,c) +#define PCRE2_SET_DEPTH_LIMIT(a,b) pcre2_set_depth_limit_8(G(a,8),b) +#define PCRE2_SET_GLOB_ESCAPE(r,a,b) r = pcre2_set_glob_escape_8(G(a,8),b) +#define PCRE2_SET_GLOB_SEPARATOR(r,a,b) r = pcre2_set_glob_separator_8(G(a,8),b) +#define PCRE2_SET_HEAP_LIMIT(a,b) pcre2_set_heap_limit_8(G(a,8),b) #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) \ a = pcre2_substitute_8(G(b,8),(PCRE2_SPTR8)c,d,e,f,G(g,8),G(h,8), \ (PCRE2_SPTR8)i,j,(PCRE2_UCHAR8 *)k,l) @@ -1849,6 +2061,8 @@ the three different cases. */ #define CASTFLD(t,a,b) (t)(G(a,16)->b) #define CASTVAR(t,x) (t)G(x,16) #define CODE_UNIT(a,b) (uint32_t)(((PCRE2_SPTR16)(a))[b]) +#define CONCTXCPY(a,b) memcpy(G(a,16),G(b,16),sizeof(pcre2_convert_context_16)) +#define CONVERT_COPY(a,b,c) memcpy(G(a,16),(char *)b, (c)*2) #define DATCTXCPY(a,b) memcpy(G(a,16),G(b,16),sizeof(pcre2_match_context_16)) #define FLD(a,b) G(a,16)->b #define PATCTXCPY(a,b) memcpy(G(a,16),G(b,16),sizeof(pcre2_compile_context_16)) @@ -1861,12 +2075,15 @@ the three different cases. */ (int (*)(struct pcre2_callout_enumerate_block_16 *, void *))b,c) #define PCRE2_CODE_COPY_FROM_VOID(a,b) G(a,16) = pcre2_code_copy_16(b) #define PCRE2_CODE_COPY_TO_VOID(a,b) a = (void *)pcre2_code_copy_16(G(b,16)) +#define PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(a,b) a = (void *)pcre2_code_copy_with_tables_16(G(b,16)) #define PCRE2_COMPILE(a,b,c,d,e,f,g) \ G(a,16) = pcre2_compile_16(G(b,16),c,d,e,f,g) +#define PCRE2_CONVERTED_PATTERN_FREE(a) \ + pcre2_converted_pattern_free_16((PCRE2_UCHAR16 *)a) #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),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)) + r = pcre2_get_error_message_16(a,G(b,16),G(G(b,16),_size/2)) #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(r,a,b) r = pcre2_jit_compile_16(G(a,16),b) @@ -1885,6 +2102,7 @@ the three different cases. */ #define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ G(a,16) = pcre2_match_data_create_from_pattern_16(G(b,16),c) #define PCRE2_MATCH_DATA_FREE(a) pcre2_match_data_free_16(G(a,16)) +#define PCRE2_PATTERN_CONVERT(a,b,c,d,e,f,g) a = pcre2_pattern_convert_16(G(b,16),c,d,(PCRE2_UCHAR16 **)e,f,G(g,16)) #define PCRE2_PATTERN_INFO(a,b,c,d) a = pcre2_pattern_info_16(G(b,16),c,d) #define PCRE2_PRINTINT(a) pcre2_printint_16(compiled_code16,outfile,a) #define PCRE2_SERIALIZE_DECODE(r,a,b,c,d) \ @@ -1899,11 +2117,14 @@ the three different cases. */ #define PCRE2_SET_CHARACTER_TABLES(a,b) pcre2_set_character_tables_16(G(a,16),b) #define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ pcre2_set_compile_recursion_guard_16(G(a,16),b,c) +#define PCRE2_SET_DEPTH_LIMIT(a,b) pcre2_set_depth_limit_16(G(a,16),b) +#define PCRE2_SET_GLOB_ESCAPE(r,a,b) r = pcre2_set_glob_escape_16(G(a,16),b) +#define PCRE2_SET_GLOB_SEPARATOR(r,a,b) r = pcre2_set_glob_separator_16(G(a,16),b) +#define PCRE2_SET_HEAP_LIMIT(a,b) pcre2_set_heap_limit_16(G(a,16),b) #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) \ a = pcre2_substitute_16(G(b,16),(PCRE2_SPTR16)c,d,e,f,G(g,16),G(h,16), \ (PCRE2_SPTR16)i,j,(PCRE2_UCHAR16 *)k,l) @@ -1944,6 +2165,8 @@ the three different cases. */ #define CASTFLD(t,a,b) (t)(G(a,32)->b) #define CASTVAR(t,x) (t)G(x,32) #define CODE_UNIT(a,b) (uint32_t)(((PCRE2_SPTR32)(a))[b]) +#define CONCTXCPY(a,b) memcpy(G(a,32),G(b,32),sizeof(pcre2_convert_context_32)) +#define CONVERT_COPY(a,b,c) memcpy(G(a,32),(char *)b, (c)*4) #define DATCTXCPY(a,b) memcpy(G(a,32),G(b,32),sizeof(pcre2_match_context_32)) #define FLD(a,b) G(a,32)->b #define PATCTXCPY(a,b) memcpy(G(a,32),G(b,32),sizeof(pcre2_compile_context_32)) @@ -1956,12 +2179,15 @@ the three different cases. */ (int (*)(struct pcre2_callout_enumerate_block_32 *, void *))b,c) #define PCRE2_CODE_COPY_FROM_VOID(a,b) G(a,32) = pcre2_code_copy_32(b) #define PCRE2_CODE_COPY_TO_VOID(a,b) a = (void *)pcre2_code_copy_32(G(b,32)) +#define PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(a,b) a = (void *)pcre2_code_copy_with_tables_32(G(b,32)) #define PCRE2_COMPILE(a,b,c,d,e,f,g) \ G(a,32) = pcre2_compile_32(G(b,32),c,d,e,f,g) +#define PCRE2_CONVERTED_PATTERN_FREE(a) \ + pcre2_converted_pattern_free_32((PCRE2_UCHAR32 *)a) #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),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)) + r = pcre2_get_error_message_32(a,G(b,32),G(G(b,32),_size/4)) #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(r,a,b) r = pcre2_jit_compile_32(G(a,32),b) @@ -1980,6 +2206,7 @@ the three different cases. */ #define PCRE2_MATCH_DATA_CREATE_FROM_PATTERN(a,b,c) \ G(a,32) = pcre2_match_data_create_from_pattern_32(G(b,32),c) #define PCRE2_MATCH_DATA_FREE(a) pcre2_match_data_free_32(G(a,32)) +#define PCRE2_PATTERN_CONVERT(a,b,c,d,e,f,g) a = pcre2_pattern_convert_32(G(b,32),c,d,(PCRE2_UCHAR32 **)e,f,G(g,32)) #define PCRE2_PATTERN_INFO(a,b,c,d) a = pcre2_pattern_info_32(G(b,32),c,d) #define PCRE2_PRINTINT(a) pcre2_printint_32(compiled_code32,outfile,a) #define PCRE2_SERIALIZE_DECODE(r,a,b,c,d) \ @@ -1994,11 +2221,14 @@ the three different cases. */ #define PCRE2_SET_CHARACTER_TABLES(a,b) pcre2_set_character_tables_32(G(a,32),b) #define PCRE2_SET_COMPILE_RECURSION_GUARD(a,b,c) \ pcre2_set_compile_recursion_guard_32(G(a,32),b,c) +#define PCRE2_SET_DEPTH_LIMIT(a,b) pcre2_set_depth_limit_32(G(a,32),b) +#define PCRE2_SET_GLOB_ESCAPE(r,a,b) r = pcre2_set_glob_escape_32(G(a,32),b) +#define PCRE2_SET_GLOB_SEPARATOR(r,a,b) r = pcre2_set_glob_separator_32(G(a,32),b) +#define PCRE2_SET_HEAP_LIMIT(a,b) pcre2_set_heap_limit_32(G(a,32),b) #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) \ a = pcre2_substitute_32(G(b,32),(PCRE2_SPTR32)c,d,e,f,G(g,32),G(h,32), \ (PCRE2_SPTR32)i,j,(PCRE2_UCHAR32 *)k,l) @@ -2366,18 +2596,99 @@ static const uint8_t tables2[] = { +#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE) +/************************************************* +* Emulated memmove() for systems without it * +*************************************************/ + +/* This function can make use of bcopy() if it is available. Otherwise do it by +steam, as there are some non-Unix environments that lack both memmove() and +bcopy(). */ + +static void * +emulated_memmove(void *d, const void *s, size_t n) +{ +#ifdef HAVE_BCOPY +bcopy(s, d, n); +return d; +#else +size_t i; +unsigned char *dest = (unsigned char *)d; +const unsigned char *src = (const unsigned char *)s; +if (dest > src) + { + dest += n; + src += n; + for (i = 0; i < n; ++i) *(--dest) = *(--src); + return (void *)dest; + } +else + { + for (i = 0; i < n; ++i) *dest++ = *src++; + return (void *)(dest - n); + } +#endif /* not HAVE_BCOPY */ +} +#undef memmove +#define memmove(d,s,n) emulated_memmove(d,s,n) +#endif /* not VPCOMPAT && not HAVE_MEMMOVE */ + + + +#ifndef HAVE_STRERROR +/************************************************* +* Provide strerror() for non-ANSI libraries * +*************************************************/ + +/* Some old-fashioned systems (e.g. SunOS4) didn't have strerror() in their +libraries. They may no longer be around, but just in case, we can try to +provide the same facility by this simple alternative function. */ + +extern int sys_nerr; +extern char *sys_errlist[]; + +char * +strerror(int n) +{ +if (n < 0 || n >= sys_nerr) return "unknown error number"; +return sys_errlist[n]; +} +#endif /* HAVE_STRERROR */ + + + /************************************************* * Local memory functions * *************************************************/ /* Alternative memory functions, to test functionality. */ -static void *my_malloc(size_t size, void *data) +static void *my_malloc(PCRE2_SIZE size, void *data) { void *block = malloc(size); (void)data; if (show_memory) - fprintf(outfile, "malloc %3d %p\n", (int)size, block); + { + if (block == NULL) + { + fprintf(outfile, "** malloc() failed for %" SIZ_FORM "\n", SIZ_CAST size); + } + else + { + fprintf(outfile, "malloc %5" SIZ_FORM, SIZ_CAST size); +#ifdef DEBUG_SHOW_MALLOC_ADDRESSES + fprintf(outfile, " %p", block); /* Not portable */ +#endif + if (malloclistptr < MALLOCLISTSIZE) + { + malloclist[malloclistptr] = block; + malloclistlength[malloclistptr++] = size; + } + else + fprintf(outfile, " (not remembered)"); + fprintf(outfile, "\n"); + } + } return block; } @@ -2385,30 +2696,35 @@ static void my_free(void *block, void *data) { (void)data; if (show_memory) - fprintf(outfile, "free %p\n", block); + { + uint32_t i, j; + BOOL found = FALSE; + + fprintf(outfile, "free"); + for (i = 0; i < malloclistptr; i++) + { + if (block == malloclist[i]) + { + fprintf(outfile, " %5" SIZ_FORM, SIZ_CAST malloclistlength[i]); + malloclistptr--; + for (j = i; j < malloclistptr; j++) + { + malloclist[j] = malloclist[j+1]; + malloclistlength[j] = malloclistlength[j+1]; + } + found = TRUE; + break; + } + } + if (!found) fprintf(outfile, " unremembered block"); +#ifdef DEBUG_SHOW_MALLOC_ADDRESSES + fprintf(outfile, " %p", block); /* Not portable */ +#endif + fprintf(outfile, "\n"); + } free(block); } -/* For recursion malloc/free, to test stacking calls */ - -#ifdef HEAP_MATCH_RECURSE -static void *my_stack_malloc(size_t size, void *data) -{ -void *block = malloc(size); -(void)data; -if (show_memory) - fprintf(outfile, "stack_malloc %3d %p\n", (int)size, block); -return block; -} - -static void my_stack_free(void *block, void *data) -{ -(void)data; -if (show_memory) - fprintf(outfile, "stack_free %p\n", block); -free(block); -} -#endif /* HEAP_MATCH_RECURSE */ /************************************************* @@ -2525,6 +2841,8 @@ static int pchar(uint32_t c, BOOL utf, FILE *f) { int n = 0; +char tempbuffer[16]; + if (PRINTOK(c)) { if (f != NULL) fprintf(f, "%c", c); @@ -2546,6 +2864,8 @@ if (c < 0x100) } if (f != NULL) n = fprintf(f, "\\x{%02x}", c); + else n = sprintf(tempbuffer, "\\x{%02x}", c); + return n >= 0 ? n : 0; } @@ -2657,13 +2977,14 @@ return yield; *************************************************/ /* Must handle UTF-32 strings in utf mode. Yields number of characters printed. -For printing *MARK strings, a negative length is given.If handed a NULL file, +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 = p[-1]; while (length-- > 0) { @@ -2695,7 +3016,7 @@ Returns: number of characters placed in the buffer static int ord2utf8(uint32_t cvalue, uint8_t *utf8bytes) { -register int i, j; +int i, j; if (cvalue > 0x7fffffffu) return -1; for (i = 0; i < utf8_table1_size; i++) @@ -2715,16 +3036,22 @@ return i + 1; #ifdef SUPPORT_PCRE2_16 /************************************************* -* Convert pattern to 16-bit * +* Convert string to 16-bit * *************************************************/ -/* In UTF mode the input is always interpreted as a string of UTF-8 bytes. If -all the input bytes are ASCII, the space needed for a 16-bit string is exactly -double the 8-bit size. Otherwise, the size needed for a 16-bit string is no -more than double, because up to 0xffff uses no more than 3 bytes in UTF-8 but -possibly 4 in UTF-16. Higher values use 4 bytes in UTF-8 and up to 4 bytes in -UTF-16. The result is always left in pbuffer16. Impose a minimum size to save -repeated re-sizing. +/* In UTF mode the input is always interpreted as a string of UTF-8 bytes using +the original UTF-8 definition of RFC 2279, which allows for up to 6 bytes, and +code values from 0 to 0x7fffffff. However, values greater than the later UTF +limit of 0x10ffff cause an error. In non-UTF mode the input is interpreted as +UTF-8 if the utf8_input modifier is set, but an error is generated for values +greater than 0xffff. + +If all the input bytes are ASCII, the space needed for a 16-bit string is +exactly double the 8-bit size. Otherwise, the size needed for a 16-bit string +is no more than double, because up to 0xffff uses no more than 3 bytes in UTF-8 +but possibly 4 in UTF-16. Higher values use 4 bytes in UTF-8 and up to 4 bytes +in UTF-16. The result is always left in pbuffer16. Impose a minimum size to +save repeated re-sizing. Note that this function does not object to surrogate values. This is deliberate; it makes it possible to construct UTF-16 strings that are invalid, @@ -2732,7 +3059,7 @@ for the purpose of testing that they are correctly faulted. Arguments: p points to a byte string - utf non-zero if converting to UTF-16 + utf true in UTF mode lenptr points to number of bytes in the string (excluding trailing zero) Returns: 0 on success, with the length updated to the number of 16-bit @@ -2752,18 +3079,18 @@ if (pbuffer16_size < 2*len + 2) { if (pbuffer16 != NULL) free(pbuffer16); pbuffer16_size = 2*len + 2; - if (pbuffer16_size < 256) pbuffer16_size = 256; + if (pbuffer16_size < 4096) pbuffer16_size = 4096; pbuffer16 = (uint16_t *)malloc(pbuffer16_size); if (pbuffer16 == NULL) { - fprintf(stderr, "pcre2test: malloc(%lu) failed for pbuffer16\n", - (unsigned long int)pbuffer16_size); + fprintf(stderr, "pcre2test: malloc(%" SIZ_FORM ") failed for pbuffer16\n", + SIZ_CAST pbuffer16_size); exit(1); } } pp = pbuffer16; -if (!utf) +if (!utf && (pat_patctl.control & CTL_UTF8_INPUT) == 0) { for (; len > 0; len--) *pp++ = *p++; } @@ -2772,12 +3099,12 @@ else while (len > 0) uint32_t c; int chlen = utf82ord(p, &c); if (chlen <= 0) return -1; + if (!utf && c > 0xffff) return -3; if (c > 0x10ffff) return -2; p += chlen; len -= chlen; if (c < 0x10000) *pp++ = c; else { - if (!utf) return -3; c -= 0x10000; *pp++ = 0xD800 | (c >> 10); *pp++ = 0xDC00 | (c & 0x3ff); @@ -2794,15 +3121,25 @@ return 0; #ifdef SUPPORT_PCRE2_32 /************************************************* -* Convert pattern to 32-bit * +* Convert string to 32-bit * *************************************************/ -/* In UTF mode the input is always interpreted as a string of UTF-8 bytes. If -all the input bytes are ASCII, the space needed for a 32-bit string is exactly -four times the 8-bit size. Otherwise, the size needed for a 32-bit string is no -more than four times, because the number of characters must be less than the -number of bytes. The result is always left in pbuffer32. Impose a minimum size -to save repeated re-sizing. +/* In UTF mode the input is always interpreted as a string of UTF-8 bytes using +the original UTF-8 definition of RFC 2279, which allows for up to 6 bytes, and +code values from 0 to 0x7fffffff. However, values greater than the later UTF +limit of 0x10ffff cause an error. + +In non-UTF mode the input is interpreted as UTF-8 if the utf8_input modifier +is set, and no limit is imposed. There is special interpretation of the 0xff +byte (which is illegal in UTF-8) in this case: it causes the top bit of the +next character to be set. This provides a way of generating 32-bit characters +greater than 0x7fffffff. + +If all the input bytes are ASCII, the space needed for a 32-bit string is +exactly four times the 8-bit size. Otherwise, the size needed for a 32-bit +string is no more than four times, because the number of characters must be +less than the number of bytes. The result is always left in pbuffer32. Impose a +minimum size to save repeated re-sizing. Note that this function does not object to surrogate values. This is deliberate; it makes it possible to construct UTF-32 strings that are invalid, @@ -2810,7 +3147,7 @@ for the purpose of testing that they are correctly faulted. Arguments: p points to a byte string - utf true if UTF-8 (to be converted to UTF-32) + utf true in UTF mode lenptr points to number of bytes in the string (excluding trailing zero) Returns: 0 on success, with the length updated to the number of 32-bit @@ -2829,30 +3166,40 @@ if (pbuffer32_size < 4*len + 4) { if (pbuffer32 != NULL) free(pbuffer32); pbuffer32_size = 4*len + 4; - if (pbuffer32_size < 256) pbuffer32_size = 256; + if (pbuffer32_size < 8192) pbuffer32_size = 8192; pbuffer32 = (uint32_t *)malloc(pbuffer32_size); if (pbuffer32 == NULL) { - fprintf(stderr, "pcre2test: malloc(%lu) failed for pbuffer32\n", - (unsigned long int)pbuffer32_size); + fprintf(stderr, "pcre2test: malloc(%" SIZ_FORM ") failed for pbuffer32\n", + SIZ_CAST pbuffer32_size); exit(1); } } pp = pbuffer32; -if (!utf) + +if (!utf && (pat_patctl.control & CTL_UTF8_INPUT) == 0) { for (; len > 0; len--) *pp++ = *p++; } + else while (len > 0) { + int chlen; uint32_t c; - int chlen = utf82ord(p, &c); + uint32_t topbit = 0; + if (!utf && *p == 0xff && len > 1) + { + topbit = 0x80000000u; + p++; + len--; + } + chlen = utf82ord(p, &c); if (chlen <= 0) return -1; if (utf && c > 0x10ffff) return -2; p += chlen; len -= chlen; - *pp++ = c; + *pp++ = c | topbit; } *pp = 0; @@ -3072,7 +3419,7 @@ strncmpic(const uint8_t *s, const uint8_t *t, int n) while (n--) { int c = tolower(*s++) - tolower(*t++); - if (c) return c; + if (c != 0) return c; } return 0; } @@ -3183,7 +3530,8 @@ switch (m->which) case MOD_PND: /* Ditto, but not default pattern */ case MOD_PNDP: /* Ditto, allowed for Perl test */ if (dctl != NULL) field = dctl; - else if (pctl != NULL && (m->which == MOD_PD || ctx != CTX_DEFPAT)) + else if (pctl != NULL && (m->which == MOD_PD || m->which == MOD_PDP || + ctx != CTX_DEFPAT)) field = pctl; break; } @@ -3318,7 +3666,17 @@ for (;;) field = check_modifier(modlist + index, ctx, pctl, dctl, *p); if (field == NULL) return FALSE; - *((uint32_t *)field) |= modlist[index].value; + + /* /x is a special case; a second appearance changes PCRE2_EXTENDED to + PCRE2_EXTENDED_MORE. */ + + if (cc == 'x' && (*((uint32_t *)field) & PCRE2_EXTENDED) != 0) + { + *((uint32_t *)field) &= ~PCRE2_EXTENDED; + *((uint32_t *)field) |= PCRE2_EXTENDED_MORE; + } + else + *((uint32_t *)field) |= modlist[index].value; } continue; /* With tne next (fullname) modifier */ @@ -3377,8 +3735,8 @@ for (;;) #else *((uint16_t *)field) = PCRE2_BSR_UNICODE; #endif - if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control &= ~CTL_BSR_SET; - else dctl->control &= ~CTL_BSR_SET; + if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control2 &= ~CTL2_BSR_SET; + else dctl->control2 &= ~CTL2_BSR_SET; } else { @@ -3387,12 +3745,38 @@ for (;;) else if (len == 7 && strncmpic(pp, (const uint8_t *)"unicode", 7) == 0) *((uint16_t *)field) = PCRE2_BSR_UNICODE; else goto INVALID_VALUE; - if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control |= CTL_BSR_SET; - else dctl->control |= CTL_BSR_SET; + if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control2 |= CTL2_BSR_SET; + else dctl->control2 |= CTL2_BSR_SET; } pp = ep; break; + case MOD_CHR: /* A single character */ + *((uint32_t *)field) = *pp++; + break; + + case MOD_CON: /* A convert type/options list */ + for (;; pp++) + { + uint8_t *colon = (uint8_t *)strchr((const char *)pp, ':'); + len = ((colon != NULL && colon < ep)? colon:ep) - pp; + for (i = 0; i < convertlistcount; i++) + { + if (strncmpic(pp, (const uint8_t *)convertlist[i].name, len) == 0) + { + if (*((uint32_t *)field) == CONVERT_UNSET) + *((uint32_t *)field) = convertlist[i].option; + else + *((uint32_t *)field) |= convertlist[i].option; + break; + } + } + if (i >= convertlistcount) goto INVALID_VALUE; + pp += len; + if (*pp != ':') break; + } + break; + case MOD_IN2: /* One or two unsigned integers */ if (!isdigit(*pp)) goto INVALID_VALUE; uli = strtoul((const char *)pp, &endptr, 10); @@ -3455,14 +3839,14 @@ for (;;) if (i == 0) { *((uint16_t *)field) = NEWLINE_DEFAULT; - if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control &= ~CTL_NL_SET; - else dctl->control &= ~CTL_NL_SET; + if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control2 &= ~CTL2_NL_SET; + else dctl->control2 &= ~CTL2_NL_SET; } else { *((uint16_t *)field) = i; - if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control |= CTL_NL_SET; - else dctl->control |= CTL_NL_SET; + if (ctx == CTX_PAT || ctx == CTX_DEFPAT) pctl->control2 |= CTL2_NL_SET; + else dctl->control2 |= CTL2_NL_SET; } pp = ep; break; @@ -3498,10 +3882,16 @@ for (;;) char *nn = (char *)field; if (len > 0) /* Add new name */ { - while (*nn != 0) nn += strlen(nn) + 1; - if (nn + len + 1 - (char *)field > LENCPYGET) + if (len > MAX_NAME_SIZE) { - fprintf(outfile, "** Too many named '%s' modifiers\n", m->name); + fprintf(outfile, "** Group name in '%s' is too long\n", m->name); + return FALSE; + } + while (*nn != 0) nn += strlen(nn) + 1; + if (nn + len + 2 - (char *)field > LENCPYGET) + { + fprintf(outfile, "** Too many characters in named '%s' modifiers\n", + m->name); return FALSE; } memcpy(nn, pp, len); @@ -3572,6 +3962,7 @@ static int pattern_info(int what, void *where, BOOL unsetok) { int rc; +PCRE2_PATTERN_INFO(rc, compiled_code, what, NULL); /* Exercise the code */ PCRE2_PATTERN_INFO(rc, compiled_code, what, where); if (rc >= 0) return 0; if (rc != PCRE2_ERROR_UNSET || !unsetok) @@ -3627,7 +4018,7 @@ Returns: nothing static void 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%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%s%s%s%s%s%s%s%s", before, ((controls & CTL_AFTERTEXT) != 0)? " aftertext" : "", ((controls & CTL_ALLAFTERTEXT) != 0)? " allaftertext" : "", @@ -3635,13 +4026,16 @@ 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 ((controls & CTL_ALLUSEDTEXT) != 0)? " allusedtext" : "", ((controls & CTL_ALTGLOBAL) != 0)? " altglobal" : "", ((controls & CTL_BINCODE) != 0)? " bincode" : "", - ((controls & CTL_BSR_SET) != 0)? " bsr" : "", + ((controls2 & CTL2_BSR_SET) != 0)? " bsr" : "", ((controls & CTL_CALLOUT_CAPTURE) != 0)? " callout_capture" : "", + ((controls2 & CTL2_CALLOUT_EXTRA) != 0)? " callout_extra" : "", ((controls & CTL_CALLOUT_INFO) != 0)? " callout_info" : "", ((controls & CTL_CALLOUT_NONE) != 0)? " callout_none" : "", + ((controls2 & CTL2_CALLOUT_NO_WHERE) != 0)? " callout_no_where" : "", ((controls & CTL_DFA) != 0)? " dfa" : "", ((controls & CTL_EXPAND) != 0)? " expand" : "", ((controls & CTL_FINDLIMITS) != 0)? " find_limits" : "", + ((controls & CTL_FRAMESIZE) != 0)? " framesize" : "", ((controls & CTL_FULLBINCODE) != 0)? " fullbincode" : "", ((controls & CTL_GETALL) != 0)? " getall" : "", ((controls & CTL_GLOBAL) != 0)? " global" : "", @@ -3651,17 +4045,20 @@ 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 ((controls & CTL_JITVERIFY) != 0)? " jitverify" : "", ((controls & CTL_MARK) != 0)? " mark" : "", ((controls & CTL_MEMORY) != 0)? " memory" : "", - ((controls & CTL_NL_SET) != 0)? " newline" : "", + ((controls2 & CTL2_NL_SET) != 0)? " newline" : "", ((controls & CTL_NULLCONTEXT) != 0)? " null_context" : "", ((controls & CTL_POSIX) != 0)? " posix" : "", ((controls & CTL_POSIX_NOSUB) != 0)? " posix_nosub" : "", ((controls & CTL_PUSH) != 0)? " push" : "", ((controls & CTL_PUSHCOPY) != 0)? " pushcopy" : "", + ((controls & CTL_PUSHTABLESCOPY) != 0)? " pushtablescopy" : "", ((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_USE_LENGTH) != 0)? " use_length" : "", + ((controls & CTL_UTF8_INPUT) != 0)? " utf8_input" : "", ((controls & CTL_ZERO_TERMINATE) != 0)? " zero_terminate" : ""); } @@ -3685,7 +4082,7 @@ 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%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%s%s%s", before, ((options & PCRE2_ALT_BSUX) != 0)? " alt_bsux" : "", ((options & PCRE2_ALT_CIRCUMFLEX) != 0)? " alt_circumflex" : "", @@ -3697,8 +4094,11 @@ 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" ((options & PCRE2_DOLLAR_ENDONLY) != 0)? " dollar_endonly" : "", ((options & PCRE2_DOTALL) != 0)? " dotall" : "", ((options & PCRE2_DUPNAMES) != 0)? " dupnames" : "", + ((options & PCRE2_ENDANCHORED) != 0)? " endanchored" : "", ((options & PCRE2_EXTENDED) != 0)? " extended" : "", + ((options & PCRE2_EXTENDED_MORE) != 0)? " extended_more" : "", ((options & PCRE2_FIRSTLINE) != 0)? " firstline" : "", + ((options & PCRE2_LITERAL) != 0)? " literal" : "", ((options & PCRE2_MATCH_UNSET_BACKREF) != 0)? " match_unset_backref" : "", ((options & PCRE2_MULTILINE) != 0)? " multiline" : "", ((options & PCRE2_NEVER_BACKSLASH_C) != 0)? " never_backslash_c" : "", @@ -3717,6 +4117,35 @@ 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" } +/************************************************* +* Show compile extra options * +*************************************************/ + +/* Called from show_pattern_info() and for unsupported POSIX options. + +Arguments: + options an options word + before text to print before + after text to print after + +Returns: nothing +*/ + +static void +show_compile_extra_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", + before, + ((options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) != 0)? " allow_surrogate_escapes" : "", + ((options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) != 0)? " bad_escape_is_literal" : "", + ((options & PCRE2_EXTRA_MATCH_WORD) != 0)? " match_word" : "", + ((options & PCRE2_EXTRA_MATCH_LINE) != 0)? " match_line" : "", + after); +} + + #ifdef SUPPORT_PCRE2_8 /************************************************* @@ -3728,10 +4157,11 @@ 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" static void show_match_options(uint32_t options) { -fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s", +fprintf(outfile, "%s%s%s%s%s%s%s%s%s%s%s", ((options & PCRE2_ANCHORED) != 0)? " anchored" : "", ((options & PCRE2_DFA_RESTART) != 0)? " dfa_restart" : "", ((options & PCRE2_DFA_SHORTEST) != 0)? " dfa_shortest" : "", + ((options & PCRE2_ENDANCHORED) != 0)? " endanchored" : "", ((options & PCRE2_NO_UTF_CHECK) != 0)? " no_utf_check" : "", ((options & PCRE2_NOTBOL) != 0)? " notbol" : "", ((options & PCRE2_NOTEMPTY) != 0)? " notempty" : "", @@ -3759,13 +4189,13 @@ warning we must initialize cblock_size. */ cblock_size = 0; #ifdef SUPPORT_PCRE2_8 -if (test_mode == 8) cblock_size = sizeof(pcre2_real_code_8); +if (test_mode == PCRE8_MODE) cblock_size = sizeof(pcre2_real_code_8); #endif #ifdef SUPPORT_PCRE2_16 -if (test_mode == 16) cblock_size = sizeof(pcre2_real_code_16); +if (test_mode == PCRE16_MODE) cblock_size = sizeof(pcre2_real_code_16); #endif #ifdef SUPPORT_PCRE2_32 -if (test_mode == 32) cblock_size = sizeof(pcre2_real_code_32); +if (test_mode == PCRE32_MODE) cblock_size = sizeof(pcre2_real_code_32); #endif (void)pattern_info(PCRE2_INFO_SIZE, &size, FALSE); @@ -3782,6 +4212,44 @@ if (pat_patctl.jit != 0) +/************************************************* +* Show frame size info for a pattern * +*************************************************/ + +static void +show_framesize(void) +{ +size_t frame_size; +(void)pattern_info(PCRE2_INFO_FRAMESIZE, &frame_size, FALSE); +fprintf(outfile, "Frame size for pcre2_match(): %d\n", (int)frame_size); +} + + + +/************************************************* +* Get and output an error message * +*************************************************/ + +static BOOL +print_error_message(int errorcode, const char *before, const char *after) +{ +int len; +PCRE2_GET_ERROR_MESSAGE(len, errorcode, pbuffer); +if (len < 0) + { + fprintf(outfile, "\n** pcre2test internal error: cannot interpret error " + "number\n** Unexpected return (%d) from pcre2_get_error_message()\n", len); + } +else + { + fprintf(outfile, "%s", before); + PCHARSV(CASTVAR(void *, pbuffer), 0, len, FALSE, outfile); + fprintf(outfile, "%s", after); + } +return len >= 0; +} + + /************************************************* * Callback function for callout enumeration * *************************************************/ @@ -3849,7 +4317,7 @@ Returns: PR_OK continue processing next line static int show_pattern_info(void) { -uint32_t compile_options, overall_options; +uint32_t compile_options, overall_options, extra_options; if ((pat_patctl.control & (CTL_BINCODE|CTL_FULLBINCODE)) != 0) { @@ -3859,16 +4327,36 @@ if ((pat_patctl.control & (CTL_BINCODE|CTL_FULLBINCODE)) != 0) if ((pat_patctl.control & CTL_INFO) != 0) { + int rc; void *nametable; uint8_t *start_bits; - BOOL match_limit_set, recursion_limit_set; + BOOL heap_limit_set, match_limit_set, depth_limit_set; uint32_t backrefmax, bsr_convention, capture_count, first_ctype, first_cunit, hasbackslashc, hascrorlf, jchanged, last_ctype, last_cunit, match_empty, - match_limit, minlength, nameentrysize, namecount, newline_convention, - recursion_limit; + depth_limit, heap_limit, match_limit, minlength, nameentrysize, namecount, + newline_convention; + + /* Exercise the error route. */ + + PCRE2_PATTERN_INFO(rc, compiled_code, 999, NULL); + (void)rc; /* These info requests may return PCRE2_ERROR_UNSET. */ + switch(pattern_info(PCRE2_INFO_HEAPLIMIT, &heap_limit, TRUE)) + { + case 0: + heap_limit_set = TRUE; + break; + + case PCRE2_ERROR_UNSET: + heap_limit_set = FALSE; + break; + + default: + return PR_ABEND; + } + switch(pattern_info(PCRE2_INFO_MATCHLIMIT, &match_limit, TRUE)) { case 0: @@ -3883,14 +4371,14 @@ if ((pat_patctl.control & CTL_INFO) != 0) return PR_ABEND; } - switch(pattern_info(PCRE2_INFO_RECURSIONLIMIT, &recursion_limit, TRUE)) + switch(pattern_info(PCRE2_INFO_DEPTHLIMIT, &depth_limit, TRUE)) { case 0: - recursion_limit_set = TRUE; + depth_limit_set = TRUE; break; case PCRE2_ERROR_UNSET: - recursion_limit_set = FALSE; + depth_limit_set = FALSE; break; default: @@ -3927,11 +4415,14 @@ if ((pat_patctl.control & CTL_INFO) != 0) if (maxlookbehind > 0) fprintf(outfile, "Max lookbehind = %d\n", maxlookbehind); + if (heap_limit_set) + fprintf(outfile, "Heap limit = %u\n", heap_limit); + if (match_limit_set) fprintf(outfile, "Match limit = %u\n", match_limit); - if (recursion_limit_set) - fprintf(outfile, "Recursion limit = %u\n", recursion_limit); + if (depth_limit_set) + fprintf(outfile, "Depth limit = %u\n", depth_limit); if (namecount > 0) { @@ -3966,6 +4457,7 @@ if ((pat_patctl.control & CTL_INFO) != 0) pattern_info(PCRE2_INFO_ARGOPTIONS, &compile_options, FALSE); pattern_info(PCRE2_INFO_ALLOPTIONS, &overall_options, FALSE); + pattern_info(PCRE2_INFO_EXTRAOPTIONS, &extra_options, FALSE); /* Remove UTF/UCP if they were there only because of forbid_utf. This saves cluttering up the verification output of non-UTF test files. */ @@ -3993,9 +4485,12 @@ if ((pat_patctl.control & CTL_INFO) != 0) } } + if (extra_options != 0) + show_compile_extra_options(extra_options, "Extra options:", "\n"); + if (jchanged) fprintf(outfile, "Duplicate name status changes\n"); - if ((pat_patctl.control & CTL_BSR_SET) != 0 || + if ((pat_patctl.control2 & CTL2_BSR_SET) != 0 || (FLD(compiled_code, flags) & PCRE2_BSR_SET) != 0) fprintf(outfile, "\\R matches %s\n", (bsr_convention == PCRE2_BSR_UNICODE)? "any Unicode newline" : "CR, LF, or CRLF"); @@ -4024,6 +4519,10 @@ if ((pat_patctl.control & CTL_INFO) != 0) fprintf(outfile, "Forced newline is any Unicode newline\n"); break; + case PCRE2_NEWLINE_NUL: + fprintf(outfile, "Forced newline is NUL\n"); + break; + default: break; } @@ -4100,15 +4599,9 @@ if ((pat_patctl.control & CTL_INFO) != 0) else { #ifdef SUPPORT_JIT - 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, ")"); - } + if (jitrc != 0 && !print_error_message(jitrc, " (", ")")) + return PR_ABEND; fprintf(outfile, "\n"); #else fprintf(outfile, "JIT support is not available in this version of PCRE2\n"); @@ -4123,14 +4616,9 @@ if ((pat_patctl.control & CTL_CALLOUT_INFO) != 0) PCRE2_CALLOUT_ENUMERATE(errorcode, callout_callback, 0); if (errorcode != 0) { - int len; fprintf(outfile, "Callout enumerate failed: error %d: ", errorcode); - if (errorcode < 0) - { - PCRE2_GET_ERROR_MESSAGE(len, errorcode, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, len, FALSE, outfile); - } - fprintf(outfile, "\n"); + if (errorcode < 0 && !print_error_message(errorcode, "", "\n")) + return PR_ABEND; return PR_SKIP; } } @@ -4150,16 +4638,14 @@ return PR_OK; rc the error code msg an initial message for what failed -Returns: nothing +Returns: FALSE if print_error_message() fails */ -static void +static BOOL serial_error(int rc, const char *msg) { fprintf(outfile, "%s failed: error %d: ", msg, rc); -PCRE2_GET_ERROR_MESSAGE(rc, rc, pbuffer); -PCHARSV(CASTVAR(void *, pbuffer), 0, rc, FALSE, outfile); -fprintf(outfile, "\n"); +return print_error_message(rc, "", "\n"); } @@ -4197,7 +4683,7 @@ if (endf == filename) *fptr = fopen((const char *)filename, mode); if (*fptr == NULL) { - fprintf(outfile, "** Failed to open '%s'\n", filename); + fprintf(outfile, "** Failed to open '%s': %s\n", filename, strerror(errno)); return PR_ABEND; } @@ -4227,17 +4713,12 @@ process_command(void) FILE *f; PCRE2_SIZE serial_size; size_t i; -int rc, cmd, cmdlen; +int rc, cmd, cmdlen, yield; uint16_t first_listed_newline; const char *cmdname; uint8_t *argptr, *serial; -if (restrict_for_perl_test) - { - fprintf(outfile, "** #-commands are not allowed after #perltest\n"); - return PR_ABEND; - } - +yield = PR_OK; cmd = CMD_UNKNOWN; cmdlen = 0; @@ -4255,6 +4736,12 @@ for (i = 0; i < cmdlistcount; i++) argptr = buffer + cmdlen + 1; +if (restrict_for_perl_test && cmd != CMD_PATTERN && cmd != CMD_SUBJECT) + { + fprintf(outfile, "** #%s is not allowed after #perltest\n", cmdname); + return PR_ABEND; + } + switch(cmd) { case CMD_UNKNOWN: @@ -4337,6 +4824,7 @@ switch(cmd) PCRE2_JIT_COMPILE(jitrc, compiled_code, pat_patctl.jit); } if ((pat_patctl.control & CTL_MEMORY) != 0) show_memory_info(); + if ((pat_patctl.control & CTL_FRAMESIZE) != 0) show_framesize(); if ((pat_patctl.control & CTL_ANYINFO) != 0) { rc = show_pattern_info(); @@ -4360,7 +4848,8 @@ switch(cmd) general_context); if (rc < 0) { - serial_error(rc, "Serialization"); + fclose(f); + if (!serial_error(rc, "Serialization")) return PR_ABEND; break; } @@ -4374,6 +4863,7 @@ switch(cmd) if (fwrite(serial, 1, serial_size, f) != serial_size) { fprintf(outfile, "** Wrong return from fwrite()\n"); + fclose(f); return PR_ABEND; } @@ -4399,40 +4889,52 @@ switch(cmd) serial = malloc(serial_size); if (serial == NULL) { - fprintf(outfile, "** Failed to get memory (size %lu) for #load\n", - (unsigned long int)serial_size); + fprintf(outfile, "** Failed to get memory (size %" SIZ_FORM ") for #load\n", + SIZ_CAST serial_size); + fclose(f); return PR_ABEND; } - if (fread(serial, 1, serial_size, f) != serial_size) - { - fprintf(outfile, "** Wrong return from fread()\n"); - return PR_ABEND; - } + i = fread(serial, 1, serial_size, f); fclose(f); - PCRE2_SERIALIZE_GET_NUMBER_OF_CODES(rc, serial); - if (rc < 0) serial_error(rc, "Get number of codes"); else + if (i != serial_size) { - if (rc + patstacknext > PATSTACKSIZE) + fprintf(outfile, "** Wrong return from fread()\n"); + yield = PR_ABEND; + } + else + { + PCRE2_SERIALIZE_GET_NUMBER_OF_CODES(rc, serial); + if (rc < 0) { - fprintf(outfile, "** Not enough space on pattern stack for %d pattern%s\n", - rc, (rc == 1)? "" : "s"); - rc = PATSTACKSIZE - patstacknext; - fprintf(outfile, "** Decoding %d pattern%s\n", rc, - (rc == 1)? "" : "s"); + if (!serial_error(rc, "Get number of codes")) yield = PR_ABEND; } - PCRE2_SERIALIZE_DECODE(rc, patstack + patstacknext, rc, serial, - general_context); - if (rc < 0) serial_error(rc, "Deserialization"); + else + { + if (rc + patstacknext > PATSTACKSIZE) + { + fprintf(outfile, "** Not enough space on pattern stack for %d pattern%s\n", + rc, (rc == 1)? "" : "s"); + rc = PATSTACKSIZE - patstacknext; + fprintf(outfile, "** Decoding %d pattern%s\n", rc, + (rc == 1)? "" : "s"); + } + PCRE2_SERIALIZE_DECODE(rc, patstack + patstacknext, rc, serial, + general_context); + if (rc < 0) + { + if (!serial_error(rc, "Deserialization")) yield = PR_ABEND; + } else patstacknext += rc; + } } free(serial); break; } -return PR_OK; +return yield; } @@ -4459,11 +4961,12 @@ 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; +uint32_t use_forbid_utf = forbid_utf; PCRE2_SIZE patlen; +PCRE2_SIZE valgrind_access_length; PCRE2_SIZE erroroffset; /* Initialize the context and pattern/data controls for this test from the @@ -4507,8 +5010,34 @@ 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. */ +/* The utf8_input modifier is not allowed in 8-bit mode, and is mutually +exclusive with the utf modifier. */ + +if ((pat_patctl.control & CTL_UTF8_INPUT) != 0) + { + if (test_mode == PCRE8_MODE) + { + fprintf(outfile, "** The utf8_input modifier is not allowed in 8-bit mode\n"); + return PR_SKIP; + } + if (utf) + { + fprintf(outfile, "** The utf and utf8_input modifiers are mutually exclusive\n"); + return PR_SKIP; + } + } + +/* The convert and posix modifiers are mutually exclusive. */ + +if (pat_patctl.convert_type != CONVERT_UNSET && + (pat_patctl.control & CTL_POSIX) != 0) + { + fprintf(outfile, "** The convert and posix modifiers are mutually exclusive\n"); + return PR_SKIP; + } + +/* Check for mutually exclusive control modifiers. At present, these are all in +the first control word. */ for (k = 0; k < sizeof(exclusive_pat_controls)/sizeof(uint32_t); k++) { @@ -4548,12 +5077,14 @@ if ((pat_patctl.control & CTL_HEXPAT) != 0) if (c == '\'' || c == '"') { + uint8_t *pq = pp; for (;; pp++) { d = *pp; if (d == 0) { - fprintf(outfile, "** Missing closing quote in hex pattern\n"); + fprintf(outfile, "** Missing closing quote in hex pattern: " + "opening quote is at offset %" PTR_FORM ".\n", pq - buffer - 2); return PR_SKIP; } if (d == c) break; @@ -4567,8 +5098,8 @@ if ((pat_patctl.control & CTL_HEXPAT) != 0) { if (!isxdigit(c)) { - fprintf(outfile, "** Unexpected non-hex-digit '%c' in hex pattern: " - "quote missing?\n", c); + fprintf(outfile, "** Unexpected non-hex-digit '%c' at offset %" + PTR_FORM " in hex pattern: quote missing?\n", c, pp - buffer - 2); return PR_SKIP; } if (*pp == 0) @@ -4579,8 +5110,8 @@ if ((pat_patctl.control & CTL_HEXPAT) != 0) d = *pp; if (!isxdigit(d)) { - fprintf(outfile, "** Unexpected non-hex-digit '%c' in hex pattern: " - "quote missing?\n", d); + fprintf(outfile, "** Unexpected non-hex-digit '%c' at offset %" + PTR_FORM " in hex pattern: quote missing?\n", d, pp - buffer - 1); return PR_SKIP; } c = toupper(c); @@ -4738,7 +5269,7 @@ if ((pat_patctl.control & CTL_POSIX) != 0) const char *msg = "** Ignored with POSIX interface:"; #endif - if (test_mode != 8) + if (test_mode != PCRE8_MODE) { fprintf(outfile, "** The POSIX interface is available only in 8-bit mode\n"); return PR_SKIP; @@ -4760,6 +5291,16 @@ if ((pat_patctl.control & CTL_POSIX) != 0) pat_patctl.options & ~POSIX_SUPPORTED_COMPILE_OPTIONS, msg, ""); msg = ""; } + + if ((FLD(pat_context, extra_options) & + ~POSIX_SUPPORTED_COMPILE_EXTRA_OPTIONS) != 0) + { + show_compile_extra_options( + FLD(pat_context, extra_options) & ~POSIX_SUPPORTED_COMPILE_EXTRA_OPTIONS, + msg, ""); + msg = ""; + } + if ((pat_patctl.control & ~POSIX_SUPPORTED_COMPILE_CONTROLS) != 0 || (pat_patctl.control2 & ~POSIX_SUPPORTED_COMPILE_CONTROLS2) != 0) { @@ -4769,6 +5310,10 @@ if ((pat_patctl.control & CTL_POSIX) != 0) } if (local_newline_default != 0) prmsg(&msg, "#newline_default"); + if (FLD(pat_context, max_pattern_length) != PCRE2_UNSET) + prmsg(&msg, "max_pattern_length"); + if (FLD(pat_context, parens_nest_limit) != PARENS_NEST_DEFAULT) + prmsg(&msg, "parens_nest_limit"); if (msg[0] == 0) fprintf(outfile, "\n"); @@ -4778,10 +5323,17 @@ if ((pat_patctl.control & CTL_POSIX) != 0) if ((pat_patctl.control & CTL_POSIX_NOSUB) != 0) cflags |= REG_NOSUB; if ((pat_patctl.options & PCRE2_UCP) != 0) cflags |= REG_UCP; if ((pat_patctl.options & PCRE2_CASELESS) != 0) cflags |= REG_ICASE; + if ((pat_patctl.options & PCRE2_LITERAL) != 0) cflags |= REG_NOSPEC; if ((pat_patctl.options & PCRE2_MULTILINE) != 0) cflags |= REG_NEWLINE; if ((pat_patctl.options & PCRE2_DOTALL) != 0) cflags |= REG_DOTALL; if ((pat_patctl.options & PCRE2_UNGREEDY) != 0) cflags |= REG_UNGREEDY; + if ((pat_patctl.control & (CTL_HEXPAT|CTL_USE_LENGTH)) != 0) + { + preg.re_endp = (char *)pbuffer8 + patlen; + cflags |= REG_PEND; + } + rc = regcomp(&preg, (char *)pbuffer8, cflags); /* Compiling failed */ @@ -4844,7 +5396,7 @@ if ((pat_patctl.control & CTL_POSIX) != 0) /* Handle compiling via the native interface. Controls that act later are ignored with "push". Replacements are locked out. */ -if ((pat_patctl.control & (CTL_PUSH|CTL_PUSHCOPY)) != 0) +if ((pat_patctl.control & (CTL_PUSH|CTL_PUSHCOPY|CTL_PUSHTABLESCOPY)) != 0) { if (pat_patctl.replacement[0] != 0) { @@ -4902,38 +5454,165 @@ switch(errorcode) break; } -/* The pattern is now in pbuffer[8|16|32], with the length in patlen. By -default, however, we pass a zero-terminated pattern. The length is passed only -if we had a hex pattern. */ +/* The pattern is now in pbuffer[8|16|32], with the length in code units in +patlen. If it is to be converted, copy the result back afterwards so that it +ends up back in the usual place. */ -if ((pat_patctl.control & CTL_HEXPAT) == 0) patlen = PCRE2_ZERO_TERMINATED; +if (pat_patctl.convert_type != CONVERT_UNSET) + { + int rc; + int convert_return = PR_OK; + uint32_t convert_options = pat_patctl.convert_type; + void *converted_pattern; + PCRE2_SIZE converted_length; + + if (pat_patctl.convert_length != 0) + { + converted_length = pat_patctl.convert_length; + converted_pattern = malloc(converted_length * code_unit_size); + if (converted_pattern == NULL) + { + fprintf(outfile, "** Failed: malloc failed for converted pattern\n"); + return PR_SKIP; + } + } + else converted_pattern = NULL; /* Let the library allocate */ + + if (utf) convert_options |= PCRE2_CONVERT_UTF; + if ((pat_patctl.options & PCRE2_NO_UTF_CHECK) != 0) + convert_options |= PCRE2_CONVERT_NO_UTF_CHECK; + + CONCTXCPY(con_context, default_con_context); + + if (pat_patctl.convert_glob_escape != 0) + { + uint32_t escape = (pat_patctl.convert_glob_escape == '0')? 0 : + pat_patctl.convert_glob_escape; + PCRE2_SET_GLOB_ESCAPE(rc, con_context, escape); + if (rc != 0) + { + fprintf(outfile, "** Invalid glob escape '%c'\n", + pat_patctl.convert_glob_escape); + convert_return = PR_SKIP; + goto CONVERT_FINISH; + } + } + + if (pat_patctl.convert_glob_separator != 0) + { + PCRE2_SET_GLOB_SEPARATOR(rc, con_context, pat_patctl.convert_glob_separator); + if (rc != 0) + { + fprintf(outfile, "** Invalid glob separator '%c'\n", + pat_patctl.convert_glob_separator); + convert_return = PR_SKIP; + goto CONVERT_FINISH; + } + } + + PCRE2_PATTERN_CONVERT(rc, pbuffer, patlen, convert_options, + &converted_pattern, &converted_length, con_context); + + if (rc != 0) + { + fprintf(outfile, "** Pattern conversion error at offset %" SIZ_FORM ": ", + SIZ_CAST converted_length); + convert_return = print_error_message(rc, "", "\n")? PR_SKIP:PR_ABEND; + } + + /* Output the converted pattern, then copy it. */ + + else + { + PCHARSV(converted_pattern, 0, converted_length, utf, outfile); + fprintf(outfile, "\n"); + patlen = converted_length; + CONVERT_COPY(pbuffer, converted_pattern, converted_length + 1); + } + + /* Free the converted pattern. */ + + CONVERT_FINISH: + if (pat_patctl.convert_length != 0) + free(converted_pattern); + else + PCRE2_CONVERTED_PATTERN_FREE(converted_pattern); + + /* Return if conversion was unsuccessful. */ + + if (convert_return != PR_OK) return convert_return; + } + +/* By default we pass a zero-terminated pattern, but a length is passed if +"use_length" was specified or this is a hex pattern (which might contain binary +zeros). When valgrind is supported, arrange for the unused part of the buffer +to be marked as no access. */ + +valgrind_access_length = patlen; +if ((pat_patctl.control & (CTL_HEXPAT|CTL_USE_LENGTH)) == 0) + { + patlen = PCRE2_ZERO_TERMINATED; + valgrind_access_length += 1; /* For the terminating zero */ + } + +#ifdef SUPPORT_VALGRIND +#ifdef SUPPORT_PCRE2_8 +if (test_mode == PCRE8_MODE && pbuffer8 != NULL) + { + VALGRIND_MAKE_MEM_NOACCESS(pbuffer8 + valgrind_access_length, + pbuffer8_size - valgrind_access_length); + } +#endif +#ifdef SUPPORT_PCRE2_16 +if (test_mode == PCRE16_MODE && pbuffer16 != NULL) + { + VALGRIND_MAKE_MEM_NOACCESS(pbuffer16 + valgrind_access_length, + pbuffer16_size - valgrind_access_length*sizeof(uint16_t)); + } +#endif +#ifdef SUPPORT_PCRE2_32 +if (test_mode == PCRE32_MODE && pbuffer32 != NULL) + { + VALGRIND_MAKE_MEM_NOACCESS(pbuffer32 + valgrind_access_length, + pbuffer32_size - valgrind_access_length*sizeof(uint32_t)); + } +#endif +#else /* Valgrind not supported */ +(void)valgrind_access_length; /* Avoid compiler warning */ +#endif /* 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) +if ((pat_patctl.control2 & CTL2_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. */ +/* The null_context 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); +/* If PCRE2_LITERAL is set, set use_forbid_utf zero because PCRE2_NEVER_UTF +and PCRE2_NEVER_UCP are invalid with it. */ + +if ((pat_patctl.options & PCRE2_LITERAL) != 0) use_forbid_utf = 0; + /* Compile many times when timing. */ if (timeit > 0) { - register int i; + int i; clock_t time_taken = 0; for (i = 0; i < timeit; i++) { clock_t start_time = clock(); PCRE2_COMPILE(compiled_code, pbuffer, patlen, - pat_patctl.options|forbid_utf, &errorcode, &erroroffset, use_pat_context); + pat_patctl.options|use_forbid_utf, &errorcode, &erroroffset, + use_pat_context); time_taken += clock() - start_time; if (TEST(compiled_code, !=, NULL)) { SUB1(pcre2_code_free, compiled_code); } @@ -4946,20 +5625,76 @@ if (timeit > 0) /* A final compile that is used "for real". */ -PCRE2_COMPILE(compiled_code, pbuffer, patlen, pat_patctl.options|forbid_utf, +PCRE2_COMPILE(compiled_code, pbuffer, patlen, pat_patctl.options|use_forbid_utf, &errorcode, &erroroffset, use_pat_context); +/* Call the JIT compiler if requested. When timing, we must free and recompile +the pattern each time because that is the only way to free the JIT compiled +code. We know that compilation will always succeed. */ + +if (TEST(compiled_code, !=, NULL) && pat_patctl.jit != 0) + { + if (timeit > 0) + { + int i; + clock_t time_taken = 0; + for (i = 0; i < timeit; i++) + { + clock_t start_time; + SUB1(pcre2_code_free, compiled_code); + PCRE2_COMPILE(compiled_code, pbuffer, patlen, + pat_patctl.options|use_forbid_utf, &errorcode, &erroroffset, + use_pat_context); + start_time = clock(); + PCRE2_JIT_COMPILE(jitrc,compiled_code, pat_patctl.jit); + time_taken += clock() - start_time; + } + total_jit_compile_time += time_taken; + fprintf(outfile, "JIT compile %.4f milliseconds\n", + (((double)time_taken * 1000.0) / (double)timeit) / + (double)CLOCKS_PER_SEC); + } + else + { + PCRE2_JIT_COMPILE(jitrc, compiled_code, pat_patctl.jit); + } + } + +/* If valgrind is supported, mark the pbuffer as accessible again. The 16-bit +and 32-bit buffers can be marked completely undefined, but we must leave the +pattern in the 8-bit buffer defined because it may be read from a callout +during matching. */ + +#ifdef SUPPORT_VALGRIND +#ifdef SUPPORT_PCRE2_8 +if (test_mode == PCRE8_MODE) + { + VALGRIND_MAKE_MEM_UNDEFINED(pbuffer8 + valgrind_access_length, + pbuffer8_size - valgrind_access_length); + } +#endif +#ifdef SUPPORT_PCRE2_16 +if (test_mode == PCRE16_MODE) + { + VALGRIND_MAKE_MEM_UNDEFINED(pbuffer16, pbuffer16_size); + } +#endif +#ifdef SUPPORT_PCRE2_32 +if (test_mode == PCRE32_MODE) + { + VALGRIND_MAKE_MEM_UNDEFINED(pbuffer32, pbuffer32_size); + } +#endif +#endif + /* Compilation failed; go back for another re, skipping to blank line if non-interactive. */ if (TEST(compiled_code, ==, NULL)) { - int len; fprintf(outfile, "Failed: error %d at offset %d: ", errorcode, (int)erroroffset); - PCRE2_GET_ERROR_MESSAGE(len, errorcode, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, len, FALSE, outfile); - fprintf(outfile, "\n"); + if (!print_error_message(errorcode, "", "\n")) return PR_ABEND; return PR_SKIP; } @@ -4982,42 +5717,10 @@ if (forbid_utf != 0) if (pattern_info(PCRE2_INFO_MAXLOOKBEHIND, &maxlookbehind, FALSE) != 0) return PR_ABEND; -/* Call the JIT compiler if requested. When timing, we must free and recompile -the pattern each time because that is the only way to free the JIT compiled -code. We know that compilation will always succeed. */ - -if (pat_patctl.jit != 0) - { - if (timeit > 0) - { - register int i; - clock_t time_taken = 0; - for (i = 0; i < timeit; i++) - { - clock_t start_time; - SUB1(pcre2_code_free, compiled_code); - PCRE2_COMPILE(compiled_code, pbuffer, patlen, - pat_patctl.options|forbid_utf, &errorcode, &erroroffset, - use_pat_context); - start_time = clock(); - PCRE2_JIT_COMPILE(jitrc,compiled_code, pat_patctl.jit); - time_taken += clock() - start_time; - } - total_jit_compile_time += time_taken; - fprintf(outfile, "JIT compile %.4f milliseconds\n", - (((double)time_taken * 1000.0) / (double)timeit) / - (double)CLOCKS_PER_SEC); - } - else - { - 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) +if ((pat_patctl.control2 & CTL2_NL_SET) != 0) { SETFLD(compiled_code, flags, FLD(compiled_code, flags) | PCRE2_NL_SET); } @@ -5025,6 +5728,7 @@ if ((pat_patctl.control & CTL_NL_SET) != 0) /* Output code size and other information if requested. */ if ((pat_patctl.control & CTL_MEMORY) != 0) show_memory_info(); +if ((pat_patctl.control & CTL_FRAMESIZE) != 0) show_framesize(); if ((pat_patctl.control & CTL_ANYINFO) != 0) { int rc = show_pattern_info(); @@ -5045,17 +5749,25 @@ if ((pat_patctl.control & CTL_PUSH) != 0) SET(compiled_code, NULL); } -/* The "pushcopy" control is similar, but pushes a copy of the pattern. This -tests the pcre2_code_copy() function. */ +/* The "pushcopy" and "pushtablescopy" controls are similar, but push a +copy of the pattern, the latter with a copy of its character tables. This tests +the pcre2_code_copy() and pcre2_code_copy_with_tables() functions. */ -if ((pat_patctl.control & CTL_PUSHCOPY) != 0) +if ((pat_patctl.control & (CTL_PUSHCOPY|CTL_PUSHTABLESCOPY)) != 0) { if (patstacknext >= PATSTACKSIZE) { fprintf(outfile, "** Too many pushed patterns (max %d)\n", PATSTACKSIZE); return PR_ABEND; } - PCRE2_CODE_COPY_TO_VOID(patstack[patstacknext++], compiled_code); + if ((pat_patctl.control & CTL_PUSHCOPY) != 0) + { + PCRE2_CODE_COPY_TO_VOID(patstack[patstacknext++], compiled_code); + } + else + { + PCRE2_CODE_COPY_WITH_TABLES_TO_VOID(patstack[patstacknext++], + compiled_code); } } return PR_OK; @@ -5064,11 +5776,23 @@ return PR_OK; /************************************************* -* Check match or recursion limit * +* Check heap, match or depth limit * *************************************************/ +/* This is used for DFA, normal, and JIT fast matching. For DFA matching it +should only be called with the third argument set to PCRE2_ERROR_DEPTHLIMIT. + +Arguments: + pp the subject string + ulen length of subject or PCRE2_ZERO_TERMINATED + errnumber defines which limit to test + msg string to include in final message + +Returns: the return from the final match function call +*/ + static int -check_match_limit(uint8_t *pp, size_t ulen, int errnumber, const char *msg) +check_match_limit(uint8_t *pp, PCRE2_SIZE ulen, int errnumber, const char *msg) { int capcount; uint32_t min = 0; @@ -5076,28 +5800,58 @@ uint32_t mid = 64; uint32_t max = UINT32_MAX; PCRE2_SET_MATCH_LIMIT(dat_context, max); -PCRE2_SET_RECURSION_LIMIT(dat_context, max); +PCRE2_SET_DEPTH_LIMIT(dat_context, max); +PCRE2_SET_HEAP_LIMIT(dat_context, max); for (;;) { - if (errnumber == PCRE2_ERROR_MATCHLIMIT) + uint32_t stack_start = 0; + + if (errnumber == PCRE2_ERROR_HEAPLIMIT) + { + PCRE2_SET_HEAP_LIMIT(dat_context, mid); + } + else if (errnumber == PCRE2_ERROR_MATCHLIMIT) { PCRE2_SET_MATCH_LIMIT(dat_context, mid); } else { - PCRE2_SET_RECURSION_LIMIT(dat_context, mid); + PCRE2_SET_DEPTH_LIMIT(dat_context, mid); } - if ((pat_patctl.control & CTL_JITFAST) != 0) + if ((dat_datctl.control & CTL_DFA) != 0) + { + stack_start = DFA_START_RWS_SIZE/1024; + if (dfa_workspace == NULL) + dfa_workspace = (int *)malloc(DFA_WS_DIMENSION*sizeof(int)); + if (dfa_matched++ == 0) + dfa_workspace[0] = -1; /* To catch bad restart */ + PCRE2_DFA_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, + dat_datctl.options, match_data, + PTR(dat_context), dfa_workspace, DFA_WS_DIMENSION); + } + + else if ((pat_patctl.control & CTL_JITFAST) != 0) PCRE2_JIT_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, dat_datctl.options, match_data, PTR(dat_context)); + else + { + stack_start = START_FRAMES_SIZE/1024; PCRE2_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, dat_datctl.options, match_data, PTR(dat_context)); + } if (capcount == errnumber) { + if ((mid & 0x80000000u) != 0) + { + fprintf(outfile, "Can't find minimum %s limit: check pattern for " + "restriction\n", msg); + break; + } + min = mid; mid = (mid == max - 1)? max : (max != UINT32_MAX)? (min + max)/2 : mid*2; } @@ -5105,13 +5859,24 @@ for (;;) capcount == PCRE2_ERROR_NOMATCH || capcount == PCRE2_ERROR_PARTIAL) { + /* If we've not hit the error with a heap limit less than the size of the + initial stack frame vector (for pcre2_match()) or the initial stack + workspace vector (for pcre2_dfa_match()), the heap is not being used, so + the minimum limit is zero; there's no need to go on. The other limits are + always greater than zero. */ + + if (errnumber == PCRE2_ERROR_HEAPLIMIT && mid < stack_start) + { + fprintf(outfile, "Minimum %s limit = 0\n", msg); + break; + } if (mid == min + 1) { fprintf(outfile, "Minimum %s limit = %d\n", msg, mid); break; } max = mid; - mid = (min + mid)/2; + mid = (min + max)/2; } else break; /* Some other error */ } @@ -5126,11 +5891,11 @@ return capcount; *************************************************/ /* Called from a PCRE2 library as a result of the (?C) item. We print out where -we are in the match. Yield zero unless more callouts than the fail count, or -the callout data is not zero. The only differences in the callout block for -different code unit widths are that the pointers to the subject, the most -recent MARK, and a callout argument string point to strings of the appropriate -width. Casts can be used to deal with this. +we are in the match (unless suppressed). Yield zero unless more callouts than +the fail count, or the callout data is not zero. The only differences in the +callout block for different code unit widths are that the pointers to the +subject, the most recent MARK, and a callout argument string point to strings +of the appropriate width. Casts can be used to deal with this. Argument: a pointer to a callout block Return: @@ -5139,16 +5904,43 @@ Argument: a pointer to a callout block static int callout_function(pcre2_callout_block_8 *cb, void *callout_data_ptr) { +FILE *f, *fdefault; uint32_t i, pre_start, post_start, subject_length; PCRE2_SIZE current_position; BOOL utf = (FLD(compiled_code, overall_options) & PCRE2_UTF) != 0; BOOL callout_capture = (dat_datctl.control & CTL_CALLOUT_CAPTURE) != 0; +BOOL callout_where = (dat_datctl.control2 & CTL2_CALLOUT_NO_WHERE) == 0; -/* This FILE is used for echoing the subject. This is done only once in simple -cases. */ +/* The FILE f is used for echoing the subject string if it is non-NULL. This +happens only once in simple cases, but we want to repeat after any additional +output caused by CALLOUT_EXTRA. */ -FILE *f = (first_callout || callout_capture || cb->callout_string != NULL)? - outfile : NULL; +fdefault = (!first_callout && !callout_capture && cb->callout_string == NULL)? + NULL : outfile; + +if ((dat_datctl.control2 & CTL2_CALLOUT_EXTRA) != 0) + { + f = outfile; + switch (cb->callout_flags) + { + case PCRE2_CALLOUT_BACKTRACK: + fprintf(f, "Backtrack\n"); + break; + + case PCRE2_CALLOUT_STARTMATCH|PCRE2_CALLOUT_BACKTRACK: + fprintf(f, "Backtrack\nNo other matching paths\n"); + /* Fall through */ + + case PCRE2_CALLOUT_STARTMATCH: + fprintf(f, "New match attempt\n"); + break; + + default: + f = fdefault; + break; + } + } +else f = fdefault; /* For a callout with a string argument, show the string first because there isn't a tidy way to fit it in the rest of the data. */ @@ -5156,8 +5948,8 @@ isn't a tidy way to fit it in the rest of the data. */ if (cb->callout_string != NULL) { uint32_t delimiter = CODE_UNIT(cb->callout_string, -1); - fprintf(outfile, "Callout (%lu): %c", - (unsigned long int)cb->callout_string_offset, delimiter); + fprintf(outfile, "Callout (%" SIZ_FORM "): %c", + SIZ_CAST cb->callout_string_offset, delimiter); PCHARSV(cb->callout_string, 0, cb->callout_string_length, utf, outfile); for (i = 0; callout_start_delims[i] != 0; i++) @@ -5177,7 +5969,7 @@ if (callout_capture) if (cb->callout_string == NULL) fprintf(outfile, "Callout %d:", cb->callout_number); fprintf(outfile, " last capture = %d\n", cb->capture_last); - for (i = 0; i < cb->capture_top * 2; i += 2) + for (i = 2; i < cb->capture_top * 2; i += 2) { fprintf(outfile, "%2d: ", i/2); if (cb->offset_vector[i] == PCRE2_UNSET) @@ -5191,75 +5983,84 @@ if (callout_capture) } } -/* Re-print the subject in canonical form (with escapes for non-printing -characters), the first time, or if giving full details. On subsequent calls in -the same match, we use PCHARS() just to find the printed lengths of the -substrings. */ +/* Unless suppressed, re-print the subject in canonical form (with escapes for +non-printing characters), the first time, or if giving full details. On +subsequent calls in the same match, we use PCHARS() just to find the printed +lengths of the substrings. */ -if (f != NULL) fprintf(f, "--->"); - -/* The subject before the match start. */ - -PCHARS(pre_start, cb->subject, 0, cb->start_match, utf, f); - -/* If a lookbehind is involved, the current position may be earlier than the -match start. If so, use the match start instead. */ - -current_position = (cb->current_position >= cb->start_match)? - cb->current_position : cb->start_match; - -/* The subject between the match start and the current position. */ - -PCHARS(post_start, cb->subject, cb->start_match, - current_position - cb->start_match, utf, f); - -/* Print from the current position to the end. */ - -PCHARSV(cb->subject, current_position, cb->subject_length - current_position, - utf, f); - -/* Calculate the total subject printed length (no print). */ - -PCHARS(subject_length, cb->subject, 0, cb->subject_length, utf, NULL); - -if (f != NULL) fprintf(f, "\n"); - -/* For automatic callouts, show the pattern offset. Otherwise, for a numerical -callout whose number has not already been shown with captured strings, show the -number here. A callout with a string argument has been displayed above. */ - -if (cb->callout_number == 255) +if (callout_where) { - fprintf(outfile, "%+3d ", (int)cb->pattern_position); - if (cb->pattern_position > 99) fprintf(outfile, "\n "); - } -else - { - if (callout_capture || cb->callout_string != NULL) fprintf(outfile, " "); - else fprintf(outfile, "%3d ", cb->callout_number); - } + if (f != NULL) fprintf(f, "--->"); -/* Now show position indicators */ + /* The subject before the match start. */ -for (i = 0; i < pre_start; i++) fprintf(outfile, " "); -fprintf(outfile, "^"); + PCHARS(pre_start, cb->subject, 0, cb->start_match, utf, f); -if (post_start > 0) - { - for (i = 0; i < post_start - 1; i++) fprintf(outfile, " "); + /* If a lookbehind is involved, the current position may be earlier than the + match start. If so, use the match start instead. */ + + current_position = (cb->current_position >= cb->start_match)? + cb->current_position : cb->start_match; + + /* The subject between the match start and the current position. */ + + PCHARS(post_start, cb->subject, cb->start_match, + current_position - cb->start_match, utf, f); + + /* Print from the current position to the end. */ + + PCHARSV(cb->subject, current_position, cb->subject_length - current_position, + utf, f); + + /* Calculate the total subject printed length (no print). */ + + PCHARS(subject_length, cb->subject, 0, cb->subject_length, utf, NULL); + + if (f != NULL) fprintf(f, "\n"); + + /* For automatic callouts, show the pattern offset. Otherwise, for a + numerical callout whose number has not already been shown with captured + strings, show the number here. A callout with a string argument has been + displayed above. */ + + if (cb->callout_number == 255) + { + fprintf(outfile, "%+3d ", (int)cb->pattern_position); + if (cb->pattern_position > 99) fprintf(outfile, "\n "); + } + else + { + if (callout_capture || cb->callout_string != NULL) fprintf(outfile, " "); + else fprintf(outfile, "%3d ", cb->callout_number); + } + + /* Now show position indicators */ + + for (i = 0; i < pre_start; i++) fprintf(outfile, " "); fprintf(outfile, "^"); + + if (post_start > 0) + { + for (i = 0; i < post_start - 1; i++) fprintf(outfile, " "); + fprintf(outfile, "^"); + } + + for (i = 0; i < subject_length - pre_start - post_start + 4; i++) + fprintf(outfile, " "); + + if (cb->next_item_length != 0) + fprintf(outfile, "%.*s", (int)(cb->next_item_length), + pbuffer8 + cb->pattern_position); + else + fprintf(outfile, "End of pattern"); + + fprintf(outfile, "\n"); } -for (i = 0; i < subject_length - pre_start - post_start + 4; i++) - fprintf(outfile, " "); - -fprintf(outfile, "%.*s", - (int)((cb->next_item_length == 0)? 1 : cb->next_item_length), - pbuffer8 + cb->pattern_position); - -fprintf(outfile, "\n"); first_callout = FALSE; +/* Show any mark info */ + if (cb->mark != last_callout_mark) { if (cb->mark == NULL) @@ -5273,6 +6074,8 @@ if (cb->mark != last_callout_mark) last_callout_mark = cb->mark; } +/* Show callout data */ + if (callout_data_ptr != NULL) { int callout_data = *((int32_t *)callout_data_ptr); @@ -5283,8 +6086,19 @@ if (callout_data_ptr != NULL) } } -return (cb->callout_number != dat_datctl.cfail[0])? 0 : - (++callout_count >= dat_datctl.cfail[1])? 1 : 0; +/* Keep count and give the appropriate return code */ + +callout_count++; + +if (cb->callout_number == dat_datctl.cerror[0] && + callout_count >= dat_datctl.cerror[1]) + return PCRE2_ERROR_CALLOUT; + +if (cb->callout_number == dat_datctl.cfail[0] && + callout_count >= dat_datctl.cfail[1]) + return 1; + +return 0; } @@ -5300,10 +6114,10 @@ tests for substring extraction. utf TRUE for utf capcount return from pcre2_match() -Returns: nothing +Returns: FALSE if print_error_message() fails */ -static void +static BOOL copy_and_get(BOOL utf, int capcount) { int i; @@ -5322,9 +6136,7 @@ for (i = 0; i < MAXCPYGET && dat_datctl.copy_numbers[i] >= 0; i++) if (rc < 0) { fprintf(outfile, "Copy substring %d failed (%d): ", n, rc); - PCRE2_GET_ERROR_MESSAGE(rc, rc, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, rc, FALSE, outfile); - fprintf(outfile, "\n"); + if (!print_error_message(rc, "", "\n")) return FALSE; } else { @@ -5332,18 +6144,16 @@ for (i = 0; i < MAXCPYGET && dat_datctl.copy_numbers[i] >= 0; i++) if (rc < 0) { fprintf(outfile, "Get substring %d length failed (%d): ", n, rc); - PCRE2_GET_ERROR_MESSAGE(rc, rc, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, rc, FALSE, outfile); - fprintf(outfile, "\n"); + if (!print_error_message(rc, "", "\n")) return FALSE; } else if (length2 != length) { - fprintf(outfile, "Mismatched substring lengths: %lu %lu\n", - (unsigned long int)length, (unsigned long int)length2); + fprintf(outfile, "Mismatched substring lengths: %" + SIZ_FORM " %" SIZ_FORM "\n", SIZ_CAST length, SIZ_CAST length2); } fprintf(outfile, "%2dC ", n); PCHARSV(copybuffer, 0, length, utf, outfile); - fprintf(outfile, " (%lu)\n", (unsigned long)length); + fprintf(outfile, " (%" SIZ_FORM ")\n", SIZ_CAST length); } } @@ -5381,9 +6191,7 @@ for (;;) if (rc < 0) { fprintf(outfile, "Copy substring '%s' failed (%d): ", nptr, rc); - PCRE2_GET_ERROR_MESSAGE(rc, rc, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, rc, FALSE, outfile); - fprintf(outfile, "\n"); + if (!print_error_message(rc, "", "\n")) return FALSE; } else { @@ -5391,18 +6199,16 @@ for (;;) if (rc < 0) { fprintf(outfile, "Get substring '%s' length failed (%d): ", nptr, rc); - PCRE2_GET_ERROR_MESSAGE(rc, rc, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, rc, FALSE, outfile); - fprintf(outfile, "\n"); + if (!print_error_message(rc, "", "\n")) return FALSE; } else if (length2 != length) { - fprintf(outfile, "Mismatched substring lengths: %lu %lu\n", - (unsigned long int)length, (unsigned long int)length2); + fprintf(outfile, "Mismatched substring lengths: %" + SIZ_FORM " %" SIZ_FORM "\n", SIZ_CAST length, SIZ_CAST length2); } fprintf(outfile, " C "); PCHARSV(copybuffer, 0, length, utf, outfile); - fprintf(outfile, " (%lu) %s", (unsigned long)length, nptr); + fprintf(outfile, " (%" SIZ_FORM ") %s", SIZ_CAST length, nptr); if (groupnumber >= 0) fprintf(outfile, " (group %d)\n", groupnumber); else fprintf(outfile, " (non-unique)\n"); } @@ -5421,15 +6227,13 @@ for (i = 0; i < MAXCPYGET && dat_datctl.get_numbers[i] >= 0; i++) if (rc < 0) { fprintf(outfile, "Get substring %d failed (%d): ", n, rc); - PCRE2_GET_ERROR_MESSAGE(rc, rc, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, rc, FALSE, outfile); - fprintf(outfile, "\n"); + if (!print_error_message(rc, "", "\n")) return FALSE; } else { fprintf(outfile, "%2dG ", n); PCHARSV(gotbuffer, 0, length, utf, outfile); - fprintf(outfile, " (%lu)\n", (unsigned long)length); + fprintf(outfile, " (%" SIZ_FORM ")\n", SIZ_CAST length); PCRE2_SUBSTRING_FREE(gotbuffer); } } @@ -5467,15 +6271,13 @@ for (;;) if (rc < 0) { fprintf(outfile, "Get substring '%s' failed (%d): ", nptr, rc); - PCRE2_GET_ERROR_MESSAGE(rc, rc, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, rc, FALSE, outfile); - fprintf(outfile, "\n"); + if (!print_error_message(rc, "", "\n")) return FALSE; } else { fprintf(outfile, " G "); PCHARSV(gotbuffer, 0, length, utf, outfile); - fprintf(outfile, " (%lu) %s", (unsigned long)length, nptr); + fprintf(outfile, " (%" SIZ_FORM ") %s", SIZ_CAST length, nptr); if (groupnumber >= 0) fprintf(outfile, " (group %d)\n", groupnumber); else fprintf(outfile, " (non-unique)\n"); PCRE2_SUBSTRING_FREE(gotbuffer); @@ -5494,9 +6296,7 @@ if ((dat_datctl.control & CTL_GETALL) != 0) if (rc < 0) { fprintf(outfile, "get substring list failed (%d): ", rc); - PCRE2_GET_ERROR_MESSAGE(rc, rc, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, rc, FALSE, outfile); - fprintf(outfile, "\n"); + if (!print_error_message(rc, "", "\n")) return FALSE; } else { @@ -5511,6 +6311,8 @@ if ((dat_datctl.control & CTL_GETALL) != 0) PCRE2_SUBSTRING_LIST_FREE(stringlist); } } + +return TRUE; } @@ -5531,7 +6333,7 @@ Returns: PR_OK continue processing next line static int process_data(void) { -PCRE2_SIZE len, ulen; +PCRE2_SIZE len, ulen, arg_ulen; uint32_t gmatched; uint32_t c, k; uint32_t g_notempty = 0; @@ -5539,6 +6341,8 @@ uint8_t *p, *pp, *start_rep; size_t needlen; void *use_dat_context; BOOL utf; +BOOL subject_literal; +PCRE2_SIZE ovecsave[3]; #ifdef SUPPORT_PCRE2_8 uint8_t *q8 = NULL; @@ -5550,6 +6354,8 @@ uint16_t *q16 = NULL; uint32_t *q32 = NULL; #endif +subject_literal = (pat_patctl.control2 & CTL2_SUBJECT_LITERAL) != 0; + /* Copy the default context and data control blocks to the active ones. Then copy from the pattern the controls that can be set in either the pattern or the data. This allows them to be overridden in the data line. We do not do this for @@ -5561,6 +6367,7 @@ 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); +if (dat_datctl.jitstack == 0) dat_datctl.jitstack = pat_patctl.jitstack; /* Initialize for scanning the data line. */ @@ -5622,7 +6429,9 @@ if (dbuffer == NULL || needlen >= dbuffer_size) SETCASTPTR(q, dbuffer); /* Sets q8, q16, or q32, as appropriate. */ /* Scan the data line, interpreting data escapes, and put the result into a -buffer of the appropriate width. In UTF mode, input can be UTF-8. */ +buffer of the appropriate width. In UTF mode, input is always UTF-8; otherwise, +in 16- and 32-bit modes, it can be forced to UTF-8 by the utf8_input modifier. +*/ while ((c = *p++) != 0) { @@ -5691,11 +6500,20 @@ while ((c = *p++) != 0) continue; } - /* Handle a non-escaped character */ + /* Handle a non-escaped character. In non-UTF 32-bit mode with utf8_input + set, do the fudge for setting the top bit. */ - if (c != '\\') + if (c != '\\' || subject_literal) { - if (utf && HASUTF8EXTRALEN(c)) { GETUTF8INC(c, p); } + uint32_t topbit = 0; + if (test_mode == PCRE32_MODE && c == 0xff && *p != 0) + { + topbit = 0x80000000; + c = *p++; + } + if ((utf || (pat_patctl.control & CTL_UTF8_INPUT) != 0) && + HASUTF8EXTRALEN(c)) { GETUTF8INC(c, p); } + c |= topbit; } /* Handle backslash escapes */ @@ -5883,6 +6701,7 @@ while ((c = *p++) != 0) SET(*q, 0); len = CASTVAR(uint8_t *, q) - dbuffer; /* Length in bytes */ ulen = len/code_unit_size; /* Length in code units */ +arg_ulen = ulen; /* Value to use in match arg */ /* If the string was terminated by \= we must now interpret modifiers. */ @@ -5911,11 +6730,15 @@ if (pat_patctl.replacement[0] != 0 && } /* 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. */ +ulen containing the code unit length, with a copy in arg_ulen for use in match +function arguments (this gets changed to PCRE2_ZERO_TERMINATED when the +zero_terminate modifier is present). + +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. */ c = code_unit_size * (((pat_patctl.control & CTL_POSIX) + (dat_datctl.control & CTL_ZERO_TERMINATE) != 0)? 1:0); @@ -5936,13 +6759,16 @@ if ((pat_patctl.control & CTL_POSIX) != 0) regmatch_t *pmatch = NULL; const char *msg = "** Ignored with POSIX interface:"; - if (dat_datctl.cfail[0] != CFAIL_UNSET || dat_datctl.cfail[1] != CFAIL_UNSET) + if (dat_datctl.cerror[0] != CFORE_UNSET || dat_datctl.cerror[1] != CFORE_UNSET) + prmsg(&msg, "callout_error"); + if (dat_datctl.cfail[0] != CFORE_UNSET || dat_datctl.cfail[1] != CFORE_UNSET) prmsg(&msg, "callout_fail"); if (dat_datctl.copy_numbers[0] >= 0 || dat_datctl.copy_names[0] != 0) prmsg(&msg, "copy"); if (dat_datctl.get_numbers[0] >= 0 || dat_datctl.get_names[0] != 0) prmsg(&msg, "get"); if (dat_datctl.jitstack != 0) prmsg(&msg, "jitstack"); + if (dat_datctl.offset != 0) prmsg(&msg, "offset"); if ((dat_datctl.options & ~POSIX_SUPPORTED_MATCH_OPTIONS) != 0) { @@ -5961,13 +6787,29 @@ if ((pat_patctl.control & CTL_POSIX) != 0) if (msg[0] == 0) fprintf(outfile, "\n"); if (dat_datctl.oveccount > 0) + { pmatch = (regmatch_t *)malloc(sizeof(regmatch_t) * dat_datctl.oveccount); + if (pmatch == NULL) + { + fprintf(outfile, "** Failed to get memory for recording matching " + "information (size set = %du)\n", dat_datctl.oveccount); + return PR_OK; + } + } + + if (dat_datctl.startend[0] != CFORE_UNSET) + { + pmatch[0].rm_so = dat_datctl.startend[0]; + pmatch[0].rm_eo = (dat_datctl.startend[1] != 0)? + dat_datctl.startend[1] : len; + eflags |= REG_STARTEND; + } + if ((dat_datctl.options & PCRE2_NOTBOL) != 0) eflags |= REG_NOTBOL; if ((dat_datctl.options & PCRE2_NOTEOL) != 0) eflags |= REG_NOTEOL; if ((dat_datctl.options & PCRE2_NOTEMPTY) != 0) eflags |= REG_NOTEMPTY; - rc = regexec(&preg, (const char *)pp + dat_datctl.offset, - dat_datctl.oveccount, pmatch, eflags); + rc = regexec(&preg, (const char *)pp, dat_datctl.oveccount, pmatch, eflags); if (rc != 0) { (void)regerror(rc, &preg, (char *)pbuffer8, pbuffer8_size); @@ -5979,23 +6821,36 @@ if ((pat_patctl.control & CTL_POSIX) != 0) fprintf(outfile, "Matched without capture\n"); else { - size_t i; + size_t i, j; + size_t last_printed = (size_t)dat_datctl.oveccount; for (i = 0; i < (size_t)dat_datctl.oveccount; i++) { if (pmatch[i].rm_so >= 0) { + PCRE2_SIZE start = pmatch[i].rm_so; + PCRE2_SIZE end = pmatch[i].rm_eo; + for (j = last_printed + 1; j < i; j++) + fprintf(outfile, "%2d: \n", (int)j); + last_printed = i; + if (start > end) + { + start = pmatch[i].rm_eo; + end = pmatch[i].rm_so; + fprintf(outfile, "Start of matched string is beyond its end - " + "displaying from end to start.\n"); + } fprintf(outfile, "%2d: ", (int)i); - PCHARSV(pp, pmatch[i].rm_so, - pmatch[i].rm_eo - pmatch[i].rm_so, utf, outfile); + PCHARSV(pp, start, end - start, 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(pp, pmatch[i].rm_eo, len - pmatch[i].rm_eo, - utf, outfile); - fprintf(outfile, "\n"); - } + /* Note: don't use the start/end variables here because we want to + show the text from what is reported as the end. */ + PCHARSV(pp, pmatch[i].rm_eo, len - pmatch[i].rm_eo, utf, outfile); + fprintf(outfile, "\n"); } } } } @@ -6007,11 +6862,8 @@ if ((pat_patctl.control & CTL_POSIX) != 0) /* Handle matching via the native interface. Check for consistency of modifiers. */ -if ((dat_datctl.control & (CTL_DFA|CTL_FINDLIMITS)) == (CTL_DFA|CTL_FINDLIMITS)) - { - fprintf(outfile, "** Finding match limits is not relevant for DFA matching: ignored\n"); - dat_datctl.control &= ~CTL_FINDLIMITS; - } +if (dat_datctl.startend[0] != CFORE_UNSET) + fprintf(outfile, "** \\=posix_startend ignored for non-POSIX matching\n"); /* ALLUSEDTEXT is not supported with JIT, but JIT is not used with DFA matching, even if the JIT compiler was used. */ @@ -6026,7 +6878,7 @@ if ((dat_datctl.control & (CTL_ALLUSEDTEXT|CTL_DFA)) == CTL_ALLUSEDTEXT && /* Handle passing the subject as zero-terminated. */ if ((dat_datctl.control & CTL_ZERO_TERMINATE) != 0) - ulen = PCRE2_ZERO_TERMINATED; + arg_ulen = PCRE2_ZERO_TERMINATED; /* The nullcontext modifier is used to test calling pcre2_[jit_]match() with a NULL context. */ @@ -6034,10 +6886,16 @@ NULL context. */ use_dat_context = ((dat_datctl.control & CTL_NULLCONTEXT) != 0)? NULL : PTR(dat_context); -/* Enable display of malloc/free if wanted. */ +/* Enable display of malloc/free if wanted. We can do this only if either the +pattern or the subject is processed with a context. */ show_memory = (dat_datctl.control & CTL_MEMORY) != 0; +if (show_memory && + (pat_patctl.control & dat_datctl.control & CTL_NULLCONTEXT) != 0) + fprintf(outfile, "** \\=memory requires either a pattern or a subject " + "context: ignored\n"); + /* Create and assign a JIT stack if requested. */ if (dat_datctl.jitstack != 0) @@ -6089,6 +6947,14 @@ else PCRE2_MATCH_DATA_CREATE(match_data, max_oveccount, NULL); } +if (CASTVAR(void *, match_data) == NULL) + { + fprintf(outfile, "** Failed to get memory for recording matching " + "information (size requested: %d)\n", dat_datctl.oveccount); + max_oveccount = 0; + return PR_OK; + } + /* Replacement processing is ignored for DFA matching. */ if (dat_datctl.replacement[0] != 0 && (dat_datctl.control & CTL_DFA) != 0) @@ -6124,6 +6990,9 @@ if (dat_datctl.replacement[0] != 0) if (timeitm) fprintf(outfile, "** Timing is not supported with replace: ignored\n"); + if ((dat_datctl.control & CTL_ALTGLOBAL) != 0) + fprintf(outfile, "** Altglobal is not supported with replace: ignored\n"); + xoptions = (((dat_datctl.control & CTL_GLOBAL) == 0)? 0 : PCRE2_SUBSTITUTE_GLOBAL) | (((dat_datctl.control2 & CTL2_SUBSTITUTE_EXTENDED) == 0)? 0 : @@ -6153,8 +7022,8 @@ if (dat_datctl.replacement[0] != 0) } if (n > nsize) { - fprintf(outfile, "Replacement buffer setting (%lu) is too large " - "(max %lu)\n", (unsigned long int)n, (unsigned long int)nsize); + fprintf(outfile, "Replacement buffer setting (%" SIZ_FORM ") is too " + "large (max %" SIZ_FORM ")\n", SIZ_CAST n, SIZ_CAST nsize); return PR_OK; } nsize = n; @@ -6220,19 +7089,17 @@ if (dat_datctl.replacement[0] != 0) rlen = PCRE2_ZERO_TERMINATED; else rlen = (CASTVAR(uint8_t *, r) - rbuffer)/code_unit_size; - PCRE2_SUBSTITUTE(rc, compiled_code, pp, ulen, dat_datctl.offset, + PCRE2_SUBSTITUTE(rc, compiled_code, pp, arg_ulen, dat_datctl.offset, dat_datctl.options|xoptions, match_data, dat_context, rbuffer, rlen, nbuffer, &nsize); if (rc < 0) { - 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 (!print_error_message(rc, "", "")) return PR_ABEND; if (rc == PCRE2_ERROR_NOMEMORY && (xoptions & PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) != 0) fprintf(outfile, ": %ld code units are needed", (long int)nsize); @@ -6244,35 +7111,24 @@ if (dat_datctl.replacement[0] != 0) } fprintf(outfile, "\n"); + show_memory = FALSE; + return PR_OK; } /* End of substitution handling */ /* When a replacement string is not provided, run a loop for global matching -with one of the basic matching functions. */ +with one of the basic matching functions. For altglobal (or first time round +the loop), set an "unset" value for the previous match info. */ -else for (gmatched = 0;; gmatched++) +ovecsave[0] = ovecsave[1] = ovecsave[2] = PCRE2_UNSET; + +for (gmatched = 0;; gmatched++) { PCRE2_SIZE j; int capcount; PCRE2_SIZE *ovector; - PCRE2_SIZE ovecsave[2]; ovector = FLD(match_data, ovector); - /* After the first time round a global loop, for a normal global (/g) - iteration, save the current ovector[0,1] so that we can check that they do - change each time. Otherwise a matching bug that returns the same string - causes an infinite loop. It has happened! */ - - if (gmatched > 0 && (dat_datctl.control & CTL_GLOBAL) != 0) - { - ovecsave[0] = ovector[0]; - ovecsave[1] = ovector[1]; - } - - /* For altglobal (or first time round the loop), set an "unset" value. */ - - else ovecsave[0] = ovecsave[1] = PCRE2_UNSET; - /* Fill the ovector with junk to detect elements that do not get set when they should be. */ @@ -6287,7 +7143,7 @@ else for (gmatched = 0;; gmatched++) if (timeitm > 0) { - register int i; + int i; clock_t start_time, time_taken; if ((dat_datctl.control & CTL_DFA) != 0) @@ -6302,7 +7158,7 @@ else for (gmatched = 0;; gmatched++) start_time = clock(); for (i = 0; i < timeitm; i++) { - PCRE2_DFA_MATCH(capcount, compiled_code, pp, ulen, + PCRE2_DFA_MATCH(capcount, compiled_code, pp, arg_ulen, dat_datctl.offset, dat_datctl.options | g_notempty, match_data, use_dat_context, dfa_workspace, DFA_WS_DIMENSION); } @@ -6313,7 +7169,7 @@ else for (gmatched = 0;; gmatched++) start_time = clock(); for (i = 0; i < timeitm; i++) { - PCRE2_JIT_MATCH(capcount, compiled_code, pp, ulen, + PCRE2_JIT_MATCH(capcount, compiled_code, pp, arg_ulen, dat_datctl.offset, dat_datctl.options | g_notempty, match_data, use_dat_context); } @@ -6324,7 +7180,7 @@ else for (gmatched = 0;; gmatched++) start_time = clock(); for (i = 0; i < timeitm; i++) { - PCRE2_MATCH(capcount, compiled_code, pp, ulen, + PCRE2_MATCH(capcount, compiled_code, pp, arg_ulen, dat_datctl.offset, dat_datctl.options | g_notempty, match_data, use_dat_context); } @@ -6335,19 +7191,40 @@ else for (gmatched = 0;; gmatched++) (double)CLOCKS_PER_SEC); } - /* Find the match and recursion limits if requested. The recursion limit - is not relevant for JIT. */ + /* Find the heap, match and depth limits if requested. The depth and heap + limits are not relevant for JIT. The return from check_match_limit() is the + return from the final call to pcre2_match() or pcre2_dfa_match(). */ if ((dat_datctl.control & CTL_FINDLIMITS) != 0) { - capcount = check_match_limit(pp, ulen, PCRE2_ERROR_MATCHLIMIT, "match"); - if (FLD(compiled_code, executable_jit) == NULL) - (void)check_match_limit(pp, ulen, PCRE2_ERROR_RECURSIONLIMIT, - "recursion"); + capcount = 0; /* This stops compiler warnings */ + + if (FLD(compiled_code, executable_jit) == NULL || + (dat_datctl.options & PCRE2_NO_JIT) != 0) + { + (void)check_match_limit(pp, arg_ulen, PCRE2_ERROR_HEAPLIMIT, "heap"); + } + + capcount = check_match_limit(pp, arg_ulen, PCRE2_ERROR_MATCHLIMIT, + "match"); + + if (FLD(compiled_code, executable_jit) == NULL || + (dat_datctl.options & PCRE2_NO_JIT) != 0 || + (dat_datctl.control & CTL_DFA) != 0) + { + capcount = check_match_limit(pp, arg_ulen, PCRE2_ERROR_DEPTHLIMIT, + "depth"); + } + + if (capcount == 0) + { + fprintf(outfile, "Matched, but offsets vector is too small to show all matches\n"); + capcount = dat_datctl.oveccount; + } } /* Otherwise just run a single match, setting up a callout if required (the - default). */ + default). There is a copy of the pattern in pbuffer8 for use by callouts. */ else { @@ -6372,7 +7249,7 @@ else for (gmatched = 0;; gmatched++) dfa_workspace = (int *)malloc(DFA_WS_DIMENSION*sizeof(int)); if (dfa_matched++ == 0) dfa_workspace[0] = -1; /* To catch bad restart */ - PCRE2_DFA_MATCH(capcount, compiled_code, pp, ulen, + PCRE2_DFA_MATCH(capcount, compiled_code, pp, arg_ulen, dat_datctl.offset, dat_datctl.options | g_notempty, match_data, use_dat_context, dfa_workspace, DFA_WS_DIMENSION); if (capcount == 0) @@ -6384,10 +7261,10 @@ else for (gmatched = 0;; gmatched++) else { if ((pat_patctl.control & CTL_JITFAST) != 0) - PCRE2_JIT_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, + PCRE2_JIT_MATCH(capcount, compiled_code, pp, arg_ulen, dat_datctl.offset, dat_datctl.options | g_notempty, match_data, use_dat_context); else - PCRE2_MATCH(capcount, compiled_code, pp, ulen, dat_datctl.offset, + PCRE2_MATCH(capcount, compiled_code, pp, arg_ulen, dat_datctl.offset, dat_datctl.options | g_notempty, match_data, use_dat_context); if (capcount == 0) { @@ -6422,12 +7299,23 @@ else for (gmatched = 0;; gmatched++) } /* If this is not the first time round a global loop, check that the - returned string has changed. If not, there is a bug somewhere and we must - break the loop because it will go on for ever. We know that there are - always at least two elements in the ovector. */ + returned string has changed. If it has not, check for an empty string match + at different starting offset from the previous match. This is a failed test + retry for null-matching patterns that don't match at their starting offset, + for example /(?<=\G.)/. A repeated match at the same point is not such a + pattern, and must be discarded, and we then proceed to seek a non-null + match at the current point. For any other repeated match, there is a bug + somewhere and we must break the loop because it will go on for ever. We + know that there are always at least two elements in the ovector. */ if (gmatched > 0 && ovecsave[0] == ovector[0] && ovecsave[1] == ovector[1]) { + if (ovector[0] == ovector[1] && ovecsave[2] != dat_datctl.offset) + { + g_notempty = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; + ovecsave[2] = dat_datctl.offset; + continue; /* Back to the top of the loop */ + } fprintf(outfile, "** PCRE2 error: global repeat returned the same string as previous\n"); fprintf(outfile, "** Global loop abandoned\n"); @@ -6588,7 +7476,7 @@ else for (gmatched = 0;; gmatched++) /* Process copy/get strings */ - copy_and_get(utf, capcount); + if (!copy_and_get(utf, capcount)) return PR_ABEND; } /* End of handling a successful match */ @@ -6631,7 +7519,7 @@ else for (gmatched = 0;; gmatched++) /* Process copy/get strings */ - copy_and_get(utf, 1); + if (!copy_and_get(utf, 1)) return PR_ABEND; break; /* Out of the /g loop */ } /* End of handling partial match */ @@ -6687,8 +7575,6 @@ else for (gmatched = 0;; gmatched++) else { - int mlen; - switch(capcount) { case PCRE2_ERROR_NOMATCH: @@ -6713,14 +7599,13 @@ else for (gmatched = 0;; gmatched++) default: fprintf(outfile, "Failed: error %d: ", capcount); - PCRE2_GET_ERROR_MESSAGE(mlen, capcount, pbuffer); - PCHARSV(CASTVAR(void *, pbuffer), 0, mlen, FALSE, outfile); + if (!print_error_message(capcount, "", "")) return PR_ABEND; if (capcount <= PCRE2_ERROR_UTF8_ERR1 && capcount >= PCRE2_ERROR_UTF32_ERR2) { PCRE2_SIZE startchar; PCRE2_GET_STARTCHAR(startchar, match_data); - fprintf(outfile, " at offset %lu", (unsigned long int)startchar); + fprintf(outfile, " at offset %" SIZ_FORM, SIZ_CAST startchar); } fprintf(outfile, "\n"); break; @@ -6738,6 +7623,7 @@ else for (gmatched = 0;; gmatched++) if ((dat_datctl.control & CTL_ANYGLOB) == 0) break; else { + PCRE2_SIZE match_offset = FLD(match_data, ovector)[0]; PCRE2_SIZE end_offset = FLD(match_data, ovector)[1]; /* We must now set up for the next iteration of a global search. If we have @@ -6745,12 +7631,19 @@ else for (gmatched = 0;; gmatched++) subject. If so, the loop is over. Otherwise, mimic what Perl's /g option does. Set PCRE2_NOTEMPTY_ATSTART and PCRE2_ANCHORED and try the match again at the same point. If this fails it will be picked up above, where a fake - match is set up so that at this point we advance to the next character. */ + match is set up so that at this point we advance to the next character. - if (FLD(match_data, ovector)[0] == end_offset) + However, in order to cope with patterns that never match at their starting + offset (e.g. /(?<=\G.)/) we don't do this when the match offset is greater + than the starting offset. This means there will be a retry with the + starting offset at the match offset. If this returns the same match again, + it is picked up above and ignored, and the special action is then taken. */ + + if (match_offset == end_offset) { - if (end_offset == ulen) break; /* End of subject */ - g_notempty = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; + if (end_offset == ulen) break; /* End of subject */ + if (match_offset <= dat_datctl.offset) + g_notempty = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED; } /* However, even after matching a non-empty string, there is still one @@ -6788,10 +7681,19 @@ else for (gmatched = 0;; gmatched++) } } - /* For /g (global), update the start offset, leaving the rest alone. */ + /* For a normal global (/g) iteration, save the current ovector[0,1] and + the starting offset so that we can check that they do change each time. + Otherwise a matching bug that returns the same string causes an infinite + loop. It has happened! Then update the start offset, leaving other + parameters alone. */ if ((dat_datctl.control & CTL_GLOBAL) != 0) + { + ovecsave[0] = ovector[0]; + ovecsave[1] = ovector[1]; + ovecsave[2] = dat_datctl.offset; dat_datctl.offset = end_offset; + } /* For altglobal, just update the pointer and length. */ @@ -6800,6 +7702,7 @@ else for (gmatched = 0;; gmatched++) pp += end_offset * code_unit_size; len -= end_offset * code_unit_size; ulen -= end_offset; + if (arg_ulen != PCRE2_ZERO_TERMINATED) arg_ulen -= end_offset; } } } /* End of global loop */ @@ -6901,7 +7804,9 @@ printf(" -16 use the 16-bit library\n"); #ifdef SUPPORT_PCRE2_32 printf(" -32 use the 32-bit library\n"); #endif -printf(" -b set default pattern control 'fullbincode'\n"); +printf(" -ac set default pattern modifier PCRE2_AUTO_CALLOUT\n"); +printf(" -AC as -ac, but also set subject 'callout_extra' modifier\n"); +printf(" -b set default pattern modifier '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"); @@ -6911,21 +7816,23 @@ printf(" ebcdic compiled for EBCDIC character code [0,1]\n"); printf(" ebcdic-nl NL code if compiled for EBCDIC\n"); printf(" jit just-in-time compiler supported [0, 1]\n"); printf(" linksize internal link size [2, 3, 4]\n"); -printf(" newline newline type [CR, LF, CRLF, ANYCRLF, ANY]\n"); +printf(" newline newline type [CR, LF, CRLF, ANYCRLF, ANY, NUL]\n"); printf(" pcre2-8 8 bit library support enabled [0, 1]\n"); printf(" pcre2-16 16 bit library support enabled [0, 1]\n"); printf(" pcre2-32 32 bit library support enabled [0, 1]\n"); printf(" unicode Unicode and UTF support enabled [0, 1]\n"); -printf(" -d set default pattern control 'debug'\n"); -printf(" -dfa set default subject control 'dfa'\n"); +printf(" -d set default pattern modifier 'debug'\n"); +printf(" -dfa set default subject modifier 'dfa'\n"); printf(" -error show messages for error numbers, then exit\n"); printf(" -help show usage information\n"); -printf(" -i set default pattern control 'info'\n"); -printf(" -jit set default pattern control 'jit'\n"); +printf(" -i set default pattern modifier 'info'\n"); +printf(" -jit set default pattern modifier 'jit'\n"); +printf(" -jitverify set default pattern modifier 'jitverify'\n"); +printf(" -LM list pattern and subject modifiers, then exit\n"); printf(" -q quiet: do not output PCRE2 version number at start\n"); -printf(" -pattern set default pattern control fields\n"); -printf(" -subject set default subject control fields\n"); -printf(" -S set stack size to megabytes\n"); +printf(" -pattern set default pattern modifier fields\n"); +printf(" -subject set default subject modifier fields\n"); +printf(" -S set stack size to mebibytes\n"); printf(" -t [] time compilation and execution, repeating times\n"); printf(" -tm [] time execution (matching) only, repeating times\n"); printf(" -T same as -t, but show total times at the end\n"); @@ -6952,26 +7859,25 @@ static int c_option(const char *arg) { uint32_t optval; +unsigned int i = COPTLISTCOUNT; int yield = 0; -if (arg != NULL) +if (arg != NULL && arg[0] != CHAR_MINUS) { - unsigned int i; - for (i = 0; i < COPTLISTCOUNT; i++) if (strcmp(arg, coptlist[i].name) == 0) break; if (i >= COPTLISTCOUNT) { fprintf(stderr, "** Unknown -C option '%s'\n", arg); - return -1; + return 0; } switch (coptlist[i].type) { case CONF_BSR: (void)PCRE2_CONFIG(coptlist[i].value, &optval); - printf("%s\n", optval? "ANYCRLF" : "ANY"); + printf("%s\n", (optval == PCRE2_BSR_ANYCRLF)? "ANYCRLF" : "ANY"); break; case CONF_FIX: @@ -7002,7 +7908,7 @@ if (arg != NULL) { char ucname[16]; strcpy(ucname, coptlist[i].name); - for (i = 0; ucname[i] != 0; i++) ucname[i] = toupper[ucname[i]; + for (i = 0; ucname[i] != 0; i++) ucname[i] = toupper[ucname[i]]; vms_setsymbol(ucname, 0, optval); } #endif @@ -7022,14 +7928,13 @@ printf(" EBCDIC code page %s or similar\n", pcrz_cpversion()); #endif #endif -#ifdef SUPPORT_PCRE2_8 -printf(" 8-bit support\n"); -#endif -#ifdef SUPPORT_PCRE2_16 -printf(" 16-bit support\n"); -#endif -#ifdef SUPPORT_PCRE2_32 -printf(" 32-bit support\n"); +(void)PCRE2_CONFIG(PCRE2_CONFIG_COMPILED_WIDTHS, &optval); +if (optval & 1) printf(" 8-bit support\n"); +if (optval & 2) printf(" 16-bit support\n"); +if (optval & 4) printf(" 32-bit support\n"); + +#ifdef SUPPORT_VALGRIND +printf(" Valgrind support\n"); #endif (void)PCRE2_CONFIG(PCRE2_CONFIG_UNICODE, &optval); @@ -7056,30 +7961,134 @@ else (void)PCRE2_CONFIG(PCRE2_CONFIG_NEWLINE, &optval); print_newline_config(optval, FALSE); (void)PCRE2_CONFIG(PCRE2_CONFIG_BSR, &optval); -printf(" \\R matches %s\n", optval? "CR, LF, or CRLF only" : +printf(" \\R matches %s\n", + (optval == PCRE2_BSR_ANYCRLF)? "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_NEVER_BACKSLASH_C, &optval); +printf(" \\C is %ssupported\n", optval? "not ":""); (void)PCRE2_CONFIG(PCRE2_CONFIG_LINKSIZE, &optval); printf(" Internal link size = %d\n", optval); (void)PCRE2_CONFIG(PCRE2_CONFIG_PARENSLIMIT, &optval); printf(" Parentheses nest limit = %d\n", optval); +(void)PCRE2_CONFIG(PCRE2_CONFIG_HEAPLIMIT, &optval); +printf(" Default heap limit = %d\n", optval); (void)PCRE2_CONFIG(PCRE2_CONFIG_MATCHLIMIT, &optval); printf(" Default match limit = %d\n", optval); -(void)PCRE2_CONFIG(PCRE2_CONFIG_RECURSIONLIMIT, &optval); -printf(" Default recursion depth limit = %d\n", optval); -(void)PCRE2_CONFIG(PCRE2_CONFIG_STACKRECURSE, &optval); -printf(" Match recursion uses %s", optval? "stack" : "heap"); - -printf("\n"); +(void)PCRE2_CONFIG(PCRE2_CONFIG_DEPTHLIMIT, &optval); +printf(" Default depth limit = %d\n", optval); return 0; } +/************************************************* +* Display one modifier * +*************************************************/ + +static void +display_one_modifier(modstruct *m, BOOL for_pattern) +{ +uint32_t c = (!for_pattern && (m->which == MOD_PND || m->which == MOD_PNDP))? + '*' : ' '; +printf("%c%s", c, m->name); +} + + + +/************************************************* +* Display pattern or subject modifiers * +*************************************************/ + +/* In order to print in two columns, first scan without printing to get a list +of the modifiers that are required. + +Arguments: + for_pattern TRUE for pattern modifiers, FALSE for subject modifiers + title string to be used in title + +Returns: nothing +*/ + +static void +display_selected_modifiers(BOOL for_pattern, const char *title) +{ +uint32_t i, j; +uint32_t n = 0; +uint32_t list[MODLISTCOUNT]; + +for (i = 0; i < MODLISTCOUNT; i++) + { + BOOL is_pattern = TRUE; + modstruct *m = modlist + i; + + switch (m->which) + { + case MOD_CTC: /* Compile context */ + case MOD_PAT: /* Pattern */ + case MOD_PATP: /* Pattern, OK for Perl-compatible test */ + break; + + /* The MOD_PND and MOD_PNDP modifiers are precisely those that affect + subjects, but can be given with a pattern. We list them as subject + modifiers, but marked with an asterisk.*/ + + case MOD_CTM: /* Match context */ + case MOD_DAT: /* Subject line */ + case MOD_PND: /* As PD, but not default pattern */ + case MOD_PNDP: /* As PND, OK for Perl-compatible test */ + is_pattern = FALSE; + break; + + default: printf("** Unknown type for modifier '%s'\n", m->name); + /* Fall through */ + case MOD_PD: /* Pattern or subject */ + case MOD_PDP: /* As PD, OK for Perl-compatible test */ + is_pattern = for_pattern; + break; + } + + if (for_pattern == is_pattern) list[n++] = i; + } + +/* Now print from the list in two columns. */ + +printf("-------------- %s MODIFIERS --------------\n", title); + +for (i = 0, j = (n+1)/2; i < (n+1)/2; i++, j++) + { + modstruct *m = modlist + list[i]; + display_one_modifier(m, for_pattern); + if (j < n) + { + uint32_t k = 27 - strlen(m->name); + while (k-- > 0) printf(" "); + display_one_modifier(modlist + list[j], for_pattern); + } + printf("\n"); + } +} + + + +/************************************************* +* Display the list of modifiers * +*************************************************/ + +static void +display_modifiers(void) +{ +printf( + "An asterisk on a subject modifier means that it may be given on a pattern\n" + "line, in order to apply to all subjects matched by that pattern. Modifiers\n" + "that are listed for both patterns and subjects have different effects in\n" + "each case.\n\n"); +display_selected_modifiers(TRUE, "PATTERN"); +printf("\n"); +display_selected_modifiers(FALSE, "SUBJECT"); +} + + + /************************************************* * Main Program * *************************************************/ @@ -7087,9 +8096,9 @@ return 0; int main(int argc, char **argv) { +uint32_t temp; uint32_t yield = 0; uint32_t op = 1; -uint32_t stack_size; BOOL notdone = TRUE; BOOL quiet = FALSE; BOOL showtotaltimes = FALSE; @@ -7132,6 +8141,20 @@ if (PCRE2_CONFIG(PCRE2_CONFIG_VERSION, NULL) != return 1; } +/* Check that bad options are diagnosed. */ + +if (PCRE2_CONFIG(999, NULL) != PCRE2_ERROR_BADOPTION || + PCRE2_CONFIG(999, &temp) != PCRE2_ERROR_BADOPTION) + { + fprintf(stderr, "** Error in pcre2_config(): bad option not diagnosed\n"); + return 1; + } + +/* This configuration option is now obsolete, but running a quick check ensures +that its code is covered. */ + +(void)PCRE2_CONFIG(PCRE2_CONFIG_STACKRECURSE, &temp); + /* Get buffers from malloc() so that valgrind will check their misuse when debugging. They grow automatically when very long lines are read. The 16- and 32-bit buffers (pbuffer16, pbuffer32) are obtained only if needed. */ @@ -7151,12 +8174,17 @@ _setmode( _fileno( stdout ), _O_BINARY ); /* Initialization that does not depend on the running mode. */ locale_name[0] = 0; + memset(&def_patctl, 0, sizeof(patctl)); +def_patctl.convert_type = CONVERT_UNSET; + memset(&def_datctl, 0, sizeof(datctl)); def_datctl.oveccount = DEFAULT_OVECCOUNT; def_datctl.copy_numbers[0] = -1; def_datctl.get_numbers[0] = -1; -def_datctl.cfail[0] = def_datctl.cfail[1] = CFAIL_UNSET; +def_datctl.startend[0] = def_datctl.startend[1] = CFORE_UNSET; +def_datctl.cerror[0] = def_datctl.cerror[1] = CFORE_UNSET; +def_datctl.cfail[0] = def_datctl.cfail[1] = CFORE_UNSET; /* Scan command line options. */ @@ -7166,6 +8194,14 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) char *arg = argv[op]; unsigned long uli; + /* List modifiers and exit. */ + + if (strcmp(arg, "-LM") == 0) + { + display_modifiers(); + goto EXIT; + } + /* Display and/or set return code for configuration options. */ if (strcmp(arg, "-C") == 0) @@ -7174,32 +8210,45 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) goto EXIT; } - /* Select operating mode */ + /* Select operating mode. Ensure that pcre2_config() is called in 16-bit + and 32-bit modes because that won't happen naturally when 8-bit is also + configured. Also call some other functions that are not otherwise used. This + means that a coverage report won't claim there are uncalled functions. */ if (strcmp(arg, "-8") == 0) { #ifdef SUPPORT_PCRE2_8 test_mode = PCRE8_MODE; + (void)pcre2_set_bsr_8(pat_context8, 999); + (void)pcre2_set_newline_8(pat_context8, 999); #else fprintf(stderr, "** This version of PCRE2 was built without 8-bit support\n"); exit(1); #endif } + else if (strcmp(arg, "-16") == 0) { #ifdef SUPPORT_PCRE2_16 test_mode = PCRE16_MODE; + (void)pcre2_config_16(PCRE2_CONFIG_VERSION, NULL); + (void)pcre2_set_bsr_16(pat_context16, 999); + (void)pcre2_set_newline_16(pat_context16, 999); #else fprintf(stderr, "** This version of PCRE2 was built without 16-bit support\n"); exit(1); #endif } + else if (strcmp(arg, "-32") == 0) { #ifdef SUPPORT_PCRE2_32 test_mode = PCRE32_MODE; + (void)pcre2_config_32(PCRE2_CONFIG_VERSION, NULL); + (void)pcre2_set_bsr_32(pat_context32, 999); + (void)pcre2_set_newline_32(pat_context32, 999); #else fprintf(stderr, "** This version of PCRE2 was built without 32-bit support\n"); @@ -7221,6 +8270,7 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) exit(1); #else int rc; + uint32_t stack_size; struct rlimit rlim; if (U32OVERFLOW(uli)) { @@ -7233,15 +8283,15 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) if (rlim.rlim_cur > rlim.rlim_max) { fprintf(stderr, - "pcre2test: requested stack size %luM is greater than hard limit %lu\n", - (unsigned long int)stack_size, - (unsigned long int)(rlim.rlim_max)); + "pcre2test: requested stack size %luMiB is greater than hard limit " + "%luMiB\n", (unsigned long int)stack_size, + (unsigned long int)(rlim.rlim_max)); exit(1); } rc = setrlimit(RLIMIT_STACK, &rlim); if (rc != 0) { - fprintf(stderr, "pcre2test: setting stack size %luM failed: %s\n", + fprintf(stderr, "pcre2test: setting stack size %luMiB failed: %s\n", (unsigned long int)stack_size, strerror(errno)); exit(1); } @@ -7252,16 +8302,23 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) /* Set some common pattern and subject controls */ - else if (strcmp(arg, "-dfa") == 0) def_datctl.control |= CTL_DFA; - else if (strcmp(arg, "-b") == 0) def_patctl.control |= CTL_FULLBINCODE; - else if (strcmp(arg, "-d") == 0) def_patctl.control |= CTL_DEBUG; - else if (strcmp(arg, "-i") == 0) def_patctl.control |= CTL_INFO; - else if (strcmp(arg, "-jit") == 0) + else if (strcmp(arg, "-AC") == 0) { + def_patctl.options |= PCRE2_AUTO_CALLOUT; + def_datctl.control2 |= CTL2_CALLOUT_EXTRA; + } + else if (strcmp(arg, "-ac") == 0) def_patctl.options |= PCRE2_AUTO_CALLOUT; + else if (strcmp(arg, "-b") == 0) def_patctl.control |= CTL_FULLBINCODE; + else if (strcmp(arg, "-d") == 0) def_patctl.control |= CTL_DEBUG; + else if (strcmp(arg, "-dfa") == 0) def_datctl.control |= CTL_DFA; + else if (strcmp(arg, "-i") == 0) def_patctl.control |= CTL_INFO; + else if (strcmp(arg, "-jit") == 0 || strcmp(arg, "-jitverify") == 0) + { + if (arg[4] != 0) def_patctl.control |= CTL_JITVERIFY; def_patctl.jit = 7; /* full & partial */ #ifndef SUPPORT_JIT fprintf(stderr, "** Warning: JIT support is not available: " - "-jit calls functions that do nothing.\n"); + "-jit[verify] calls functions that do nothing.\n"); #endif } @@ -7356,7 +8413,8 @@ if (arg_error != NULL) int errcode; char *endptr; -/* Ensure the relevant non-8-bit buffer is available. */ +/* Ensure the relevant non-8-bit buffer is available. Ensure that it is at +least 128 code units, because it is used for retrieving error messages. */ #ifdef SUPPORT_PCRE2_16 if (test_mode == PCRE16_MODE) @@ -7365,8 +8423,8 @@ if (arg_error != NULL) pbuffer16 = (uint16_t *)malloc(pbuffer16_size); if (pbuffer16 == NULL) { - fprintf(stderr, "pcre2test: malloc(%lu) failed for pbuffer16\n", - (unsigned long int)pbuffer16_size); + fprintf(stderr, "pcre2test: malloc(%" SIZ_FORM ") failed for pbuffer16\n", + SIZ_CAST pbuffer16_size); yield = 1; goto EXIT; } @@ -7376,12 +8434,12 @@ if (arg_error != NULL) #ifdef SUPPORT_PCRE2_32 if (test_mode == PCRE32_MODE) { - pbuffer32_size = 256; + pbuffer32_size = 512; pbuffer32 = (uint32_t *)malloc(pbuffer32_size); if (pbuffer32 == NULL) { - fprintf(stderr, "pcre2test: malloc(%lu) failed for pbuffer32\n", - (unsigned long int)pbuffer32_size); + fprintf(stderr, "pcre2test: malloc(%" SIZ_FORM ") failed for pbuffer32\n", + SIZ_CAST pbuffer32_size); yield = 1; goto EXIT; } @@ -7430,9 +8488,8 @@ if (arg_error != NULL) } /* End of -error handling */ /* Initialize things that cannot be done until we know which test mode we are -running in. When HEAP_MATCH_RECURSE is undefined, calling pcre2_set_recursion_ -memory_management() is a no-op, but we call it in order to exercise it. Also -exercise the general context copying function, which is not otherwise used. */ +running in. Exercise the general context copying function, which is not +otherwise used. */ code_unit_size = test_mode/8; max_oveccount = DEFAULT_OVECCOUNT; @@ -7446,19 +8503,18 @@ max_oveccount = DEFAULT_OVECCOUNT; G(pat_context,BITS) = G(pcre2_compile_context_copy_,BITS)(G(default_pat_context,BITS)); \ G(default_dat_context,BITS) = G(pcre2_match_context_create_,BITS)(G(general_context,BITS)); \ G(dat_context,BITS) = G(pcre2_match_context_copy_,BITS)(G(default_dat_context,BITS)); \ + G(default_con_context,BITS) = G(pcre2_convert_context_create_,BITS)(G(general_context,BITS)); \ + G(con_context,BITS) = G(pcre2_convert_context_copy_,BITS)(G(default_con_context,BITS)); \ G(match_data,BITS) = G(pcre2_match_data_create_,BITS)(max_oveccount, G(general_context,BITS)) -#ifdef HEAP_MATCH_RECURSE -#define SETRECURSEMEMMAN \ - (void)G(pcre2_set_recursion_memory_management_,BITS) \ - (G(default_dat_context,BITS), \ - &my_stack_malloc, &my_stack_free, NULL) -#else -#define SETRECURSEMEMMAN \ - (void)G(pcre2_set_recursion_memory_management_,BITS)(NULL, NULL, NULL, NULL) -#endif +#define CONTEXTTESTS \ + (void)G(pcre2_set_compile_extra_options_,BITS)(G(pat_context,BITS), 0); \ + (void)G(pcre2_set_max_pattern_length_,BITS)(G(pat_context,BITS), 0); \ + (void)G(pcre2_set_offset_limit_,BITS)(G(dat_context,BITS), 0); \ + (void)G(pcre2_set_recursion_memory_management_,BITS)(G(dat_context,BITS), my_malloc, my_free, NULL) -/* Call the appropriate functions for the current mode. */ +/* Call the appropriate functions for the current mode, and exercise some +functions that are not otherwise called. */ #ifdef SUPPORT_PCRE2_8 #undef BITS @@ -7466,7 +8522,7 @@ max_oveccount = DEFAULT_OVECCOUNT; if (test_mode == PCRE8_MODE) { CREATECONTEXTS; - SETRECURSEMEMMAN; + CONTEXTTESTS; } #endif @@ -7476,7 +8532,7 @@ if (test_mode == PCRE8_MODE) if (test_mode == PCRE16_MODE) { CREATECONTEXTS; - SETRECURSEMEMMAN; + CONTEXTTESTS; } #endif @@ -7486,14 +8542,14 @@ if (test_mode == PCRE16_MODE) if (test_mode == PCRE32_MODE) { CREATECONTEXTS; - SETRECURSEMEMMAN; + CONTEXTTESTS; } #endif /* Set a default parentheses nest limit that is large enough to run the standard tests (this also exercises the function). */ -PCRE2_SET_PARENS_NEST_LIMIT(default_pat_context, 220); +PCRE2_SET_PARENS_NEST_LIMIT(default_pat_context, PARENS_NEST_DEFAULT); /* Handle command line modifier settings, sending any error messages to stderr. We need to know the mode before modifying the context, and it is tidier @@ -7519,18 +8575,22 @@ if (argc > 1 && strcmp(argv[op], "-") != 0) infile = fopen(argv[op], INPUT_MODE); if (infile == NULL) { - printf("** Failed to open '%s'\n", argv[op]); + printf("** Failed to open '%s': %s\n", argv[op], strerror(errno)); yield = 1; goto EXIT; } } +#if defined(SUPPORT_LIBREADLINE) || defined(SUPPORT_LIBEDIT) +if (INTERACTIVE(infile)) using_history(); +#endif + if (argc > 2) { outfile = fopen(argv[op+1], OUTPUT_MODE); if (outfile == NULL) { - printf("** Failed to open '%s'\n", argv[op+1]); + printf("** Failed to open '%s': %s\n", argv[op+1], strerror(errno)); yield = 1; goto EXIT; } @@ -7563,8 +8623,7 @@ while (notdone) p = buffer; /* If we have a pattern set up for testing, or we are skipping after a - compile failure, a blank line terminates this test; otherwise process the - line as a data line. */ + compile failure, a blank line terminates this test. */ if (expectdata || skipping) { @@ -7587,14 +8646,21 @@ while (notdone) skipping = FALSE; setlocale(LC_CTYPE, "C"); } + + /* Otherwise, if we are not skipping, and the line is not a data comment + line starting with "\=", process a data line. */ + 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 either comments or special commands. Blank lines are ignored. Otherwise, the line must start with a valid delimiter. It is then processed as a pattern - line. */ + line. A copy of the pattern is left in pbuffer8 for use by callouts. Under + valgrind, make the unused part of the buffer undefined, to catch overruns. */ else if (*p == '#') { @@ -7655,6 +8721,10 @@ if (showtotaltimes) EXIT: +#if defined(SUPPORT_LIBREADLINE) || defined(SUPPORT_LIBEDIT) +if (infile != NULL && INTERACTIVE(infile)) clear_history(); +#endif + if (infile != NULL && infile != stdin) fclose(infile); if (outfile != NULL && outfile != stdout) fclose(outfile); @@ -7684,7 +8754,9 @@ if (jit_stack != NULL) G(pcre2_compile_context_free_,BITS)(G(pat_context,BITS)); \ G(pcre2_compile_context_free_,BITS)(G(default_pat_context,BITS)); \ G(pcre2_match_context_free_,BITS)(G(dat_context,BITS)); \ - G(pcre2_match_context_free_,BITS)(G(default_dat_context,BITS)) + G(pcre2_match_context_free_,BITS)(G(default_dat_context,BITS)); \ + G(pcre2_convert_context_free_,BITS)(G(default_con_context,BITS)); \ + G(pcre2_convert_context_free_,BITS)(G(con_context,BITS)); #ifdef SUPPORT_PCRE2_8 #undef BITS diff --git a/pcre2-10.22/src/sljit/sljitConfig.h b/pcre2-10.32/src/sljit/sljitConfig.h similarity index 86% rename from pcre2-10.22/src/sljit/sljitConfig.h rename to pcre2-10.32/src/sljit/sljitConfig.h index a548c37ab..d54b5e6f5 100644 --- a/pcre2-10.22/src/sljit/sljitConfig.h +++ b/pcre2-10.32/src/sljit/sljitConfig.h @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -90,16 +90,28 @@ /* Executable code allocation: If SLJIT_EXECUTABLE_ALLOCATOR is not defined, the application should - define both SLJIT_MALLOC_EXEC and SLJIT_FREE_EXEC. */ + define SLJIT_MALLOC_EXEC, SLJIT_FREE_EXEC, and SLJIT_EXEC_OFFSET. */ #ifndef SLJIT_EXECUTABLE_ALLOCATOR /* Enabled by default. */ #define SLJIT_EXECUTABLE_ALLOCATOR 1 + +/* When SLJIT_PROT_EXECUTABLE_ALLOCATOR is enabled SLJIT uses + an allocator which does not set writable and executable + permission flags at the same time. The trade-of is increased + memory consumption and disabled dynamic code modifications. */ +#ifndef SLJIT_PROT_EXECUTABLE_ALLOCATOR +/* Disabled by default. */ +#define SLJIT_PROT_EXECUTABLE_ALLOCATOR 0 +#endif + #endif /* Force cdecl calling convention even if a better calling convention (e.g. fastcall) is supported by the C compiler. - If this option is enabled, C functions without - SLJIT_CALL can also be called from JIT code. */ + If this option is disabled (this is the default), functions + called from JIT should be defined with SLJIT_FUNC attribute. + Standard C functions can still be called by using the + SLJIT_CALL_CDECL jump type. */ #ifndef SLJIT_USE_CDECL_CALLING_CONVENTION /* Disabled by default */ #define SLJIT_USE_CDECL_CALLING_CONVENTION 0 diff --git a/pcre2-10.22/src/sljit/sljitConfigInternal.h b/pcre2-10.32/src/sljit/sljitConfigInternal.h similarity index 89% rename from pcre2-10.22/src/sljit/sljitConfigInternal.h rename to pcre2-10.32/src/sljit/sljitConfigInternal.h index 566c36806..f5703e8e7 100644 --- a/pcre2-10.22/src/sljit/sljitConfigInternal.h +++ b/pcre2-10.32/src/sljit/sljitConfigInternal.h @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -60,11 +60,13 @@ a single precision floating point array by index SLJIT_F64_SHIFT : the shift required to apply when accessing a double precision floating point array by index + SLJIT_PREF_SHIFT_REG : x86 systems prefers ecx for shifting by register + the scratch register index of ecx is stored in this variable SLJIT_LOCALS_OFFSET : local space starting offset (SLJIT_SP + SLJIT_LOCALS_OFFSET) SLJIT_RETURN_ADDRESS_OFFSET : a return instruction always adds this offset to the return address Other macros: - SLJIT_CALL : C calling convention define for both calling JIT form C and C callbacks for JIT + SLJIT_FUNC : calling convention attribute for both calling JIT from C and C calling back from JIT SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (compiler independent helper) */ @@ -145,17 +147,23 @@ #define SLJIT_CONFIG_UNSUPPORTED 1 #endif -#else /* !_WIN32 */ +#else /* _WIN32 */ #if defined(_M_X64) || defined(__x86_64__) #define SLJIT_CONFIG_X86_64 1 +#elif (defined(_M_ARM) && _M_ARM >= 7 && defined(_M_ARMT)) || defined(__thumb2__) +#define SLJIT_CONFIG_ARM_THUMB2 1 +#elif (defined(_M_ARM) && _M_ARM >= 7) +#define SLJIT_CONFIG_ARM_V7 1 #elif defined(_ARM_) #define SLJIT_CONFIG_ARM_V5 1 +#elif defined(_M_ARM64) || defined(__aarch64__) +#define SLJIT_CONFIG_ARM_64 1 #else #define SLJIT_CONFIG_X86_32 1 #endif -#endif /* !WIN32 */ +#endif /* !_WIN32 */ #endif /* SLJIT_CONFIG_AUTO */ #if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) @@ -187,14 +195,6 @@ /* External function definitions. */ /**********************************/ -#if !(defined SLJIT_STD_MACROS_DEFINED && SLJIT_STD_MACROS_DEFINED) - -/* These libraries are needed for the macros below. */ -#include -#include - -#endif /* SLJIT_STD_MACROS_DEFINED */ - /* General macros: Note: SLJIT is designed to be independent from them as possible. @@ -304,6 +304,13 @@ #define SLJIT_CACHE_FLUSH(from, to) \ sys_icache_invalidate((char*)(from), (char*)(to) - (char*)(from)) +#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) + +/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */ +#define SLJIT_CACHE_FLUSH(from, to) \ + ppc_cache_flush((from), (to)) +#define SLJIT_CACHE_FLUSH_OWN_IMPL 1 + #elif (defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) #define SLJIT_CACHE_FLUSH(from, to) \ @@ -316,13 +323,6 @@ #define SLJIT_CACHE_FLUSH(from, to) \ cacheflush((long)(from), (long)(to), 0) -#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) - -/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */ -#define SLJIT_CACHE_FLUSH(from, to) \ - ppc_cache_flush((from), (to)) -#define SLJIT_CACHE_FLUSH_OWN_IMPL 1 - #elif (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) /* The __clear_cache() implementation of GCC is a dummy function on Sparc. */ @@ -330,6 +330,11 @@ sparc_cache_flush((from), (to)) #define SLJIT_CACHE_FLUSH_OWN_IMPL 1 +#elif defined _WIN32 + +#define SLJIT_CACHE_FLUSH(from, to) \ + FlushInstructionCache(GetCurrentProcess(), (char*)(from), (char*)(to) - (char*)(from)) + #else /* Calls __ARM_NR_cacheflush on ARM-Linux. */ @@ -377,12 +382,18 @@ typedef int sljit_sw; #define SLJIT_64BIT_ARCHITECTURE 1 #define SLJIT_WORD_SHIFT 3 #ifdef _WIN32 +#ifdef __GNUC__ +/* These types do not require windows.h */ +typedef unsigned long long sljit_uw; +typedef long long sljit_sw; +#else typedef unsigned __int64 sljit_uw; typedef __int64 sljit_sw; -#else +#endif +#else /* !_WIN32 */ typedef unsigned long int sljit_uw; typedef long int sljit_sw; -#endif +#endif /* _WIN32 */ #endif typedef sljit_uw sljit_p; @@ -401,7 +412,9 @@ typedef double sljit_f64; #ifndef SLJIT_W /* Defining long constants. */ -#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) +#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) +#define SLJIT_W(w) (w##l) +#elif (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) #define SLJIT_W(w) (w##ll) #else #define SLJIT_W(w) (w) @@ -477,44 +490,44 @@ typedef double sljit_f64; /* Calling convention of functions generated by SLJIT or called from the generated code. */ /*****************************************************************************************/ -#ifndef SLJIT_CALL +#ifndef SLJIT_FUNC #if (defined SLJIT_USE_CDECL_CALLING_CONVENTION && SLJIT_USE_CDECL_CALLING_CONVENTION) /* Force cdecl. */ -#define SLJIT_CALL +#define SLJIT_FUNC #elif (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) #if defined(__GNUC__) && !defined(__APPLE__) -#define SLJIT_CALL __attribute__ ((fastcall)) +#define SLJIT_FUNC __attribute__ ((fastcall)) #define SLJIT_X86_32_FASTCALL 1 #elif defined(_MSC_VER) -#define SLJIT_CALL __fastcall +#define SLJIT_FUNC __fastcall #define SLJIT_X86_32_FASTCALL 1 #elif defined(__BORLANDC__) -#define SLJIT_CALL __msfastcall +#define SLJIT_FUNC __msfastcall #define SLJIT_X86_32_FASTCALL 1 #else /* Unknown compiler. */ /* The cdecl attribute is the default. */ -#define SLJIT_CALL +#define SLJIT_FUNC #endif #else /* Non x86-32 architectures. */ -#define SLJIT_CALL +#define SLJIT_FUNC #endif /* SLJIT_CONFIG_X86_32 */ -#endif /* !SLJIT_CALL */ +#endif /* !SLJIT_FUNC */ #ifndef SLJIT_INDIRECT_CALL #if ((defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) && (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN)) \ @@ -545,6 +558,14 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr); SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void); #define SLJIT_MALLOC_EXEC(size) sljit_malloc_exec(size) #define SLJIT_FREE_EXEC(ptr) sljit_free_exec(ptr) + +#if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR) +SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr); +#define SLJIT_EXEC_OFFSET(ptr) sljit_exec_offset(ptr) +#else +#define SLJIT_EXEC_OFFSET(ptr) 0 +#endif + #endif /**********************************************/ @@ -553,48 +574,44 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void); #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) -#define SLJIT_NUMBER_OF_REGISTERS 10 -#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7 -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) -#define SLJIT_LOCALS_OFFSET_BASE ((2 + 4) * sizeof(sljit_sw)) -#else -/* Maximum 3 arguments are passed on the stack, +1 for double alignment. */ -#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1 + 4) * sizeof(sljit_sw)) -#endif /* SLJIT_X86_32_FASTCALL */ +#define SLJIT_NUMBER_OF_REGISTERS 12 +#define SLJIT_NUMBER_OF_SAVED_REGISTERS 9 +#define SLJIT_LOCALS_OFFSET_BASE (compiler->locals_offset) +#define SLJIT_PREF_SHIFT_REG SLJIT_R2 #elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +#define SLJIT_NUMBER_OF_REGISTERS 13 #ifndef _WIN64 -#define SLJIT_NUMBER_OF_REGISTERS 12 #define SLJIT_NUMBER_OF_SAVED_REGISTERS 6 -#define SLJIT_LOCALS_OFFSET_BASE (sizeof(sljit_sw)) -#else -#define SLJIT_NUMBER_OF_REGISTERS 12 +#define SLJIT_LOCALS_OFFSET_BASE 0 +#else /* _WIN64 */ #define SLJIT_NUMBER_OF_SAVED_REGISTERS 8 -#define SLJIT_LOCALS_OFFSET_BASE ((4 + 2) * sizeof(sljit_sw)) -#endif /* _WIN64 */ +#define SLJIT_LOCALS_OFFSET_BASE (compiler->locals_offset) +#endif /* !_WIN64 */ +#define SLJIT_PREF_SHIFT_REG SLJIT_R3 #elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) -#define SLJIT_NUMBER_OF_REGISTERS 11 +#define SLJIT_NUMBER_OF_REGISTERS 12 #define SLJIT_NUMBER_OF_SAVED_REGISTERS 8 #define SLJIT_LOCALS_OFFSET_BASE 0 #elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) -#define SLJIT_NUMBER_OF_REGISTERS 11 -#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7 +#define SLJIT_NUMBER_OF_REGISTERS 12 +#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8 #define SLJIT_LOCALS_OFFSET_BASE 0 #elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) -#define SLJIT_NUMBER_OF_REGISTERS 25 +#define SLJIT_NUMBER_OF_REGISTERS 26 #define SLJIT_NUMBER_OF_SAVED_REGISTERS 10 -#define SLJIT_LOCALS_OFFSET_BASE (2 * sizeof(sljit_sw)) +#define SLJIT_LOCALS_OFFSET_BASE 0 #elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) -#define SLJIT_NUMBER_OF_REGISTERS 22 +#define SLJIT_NUMBER_OF_REGISTERS 23 #define SLJIT_NUMBER_OF_SAVED_REGISTERS 17 #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) || (defined _AIX) #define SLJIT_LOCALS_OFFSET_BASE ((6 + 8) * sizeof(sljit_sw)) @@ -607,7 +624,7 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void); #elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) -#define SLJIT_NUMBER_OF_REGISTERS 17 +#define SLJIT_NUMBER_OF_REGISTERS 21 #define SLJIT_NUMBER_OF_SAVED_REGISTERS 8 #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) #define SLJIT_LOCALS_OFFSET_BASE (4 * sizeof(sljit_sw)) @@ -620,8 +637,9 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void); #define SLJIT_NUMBER_OF_REGISTERS 18 #define SLJIT_NUMBER_OF_SAVED_REGISTERS 14 #if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) -/* Add +1 for double alignment. */ -#define SLJIT_LOCALS_OFFSET_BASE ((23 + 1) * sizeof(sljit_sw)) +/* saved registers (16), return struct pointer (1), space for 6 argument words (1), + 4th double arg (2), double alignment (1). */ +#define SLJIT_LOCALS_OFFSET_BASE ((16 + 1 + 6 + 2 + 1) * sizeof(sljit_sw)) #endif #elif (defined SLJIT_CONFIG_TILEGX && SLJIT_CONFIG_TILEGX) @@ -663,7 +681,7 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void); #if (defined SLJIT_DEBUG && SLJIT_DEBUG) -#if !defined(SLJIT_ASSERT) || !defined(SLJIT_ASSERT_STOP) +#if !defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE) /* SLJIT_HALT_PROCESS must halt the process. */ #ifndef SLJIT_HALT_PROCESS @@ -675,7 +693,7 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void); #include -#endif /* !SLJIT_ASSERT || !SLJIT_ASSERT_STOP */ +#endif /* !SLJIT_ASSERT || !SLJIT_UNREACHABLE */ /* Feel free to redefine these two macros. */ #ifndef SLJIT_ASSERT @@ -690,34 +708,33 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void); #endif /* !SLJIT_ASSERT */ -#ifndef SLJIT_ASSERT_STOP +#ifndef SLJIT_UNREACHABLE -#define SLJIT_ASSERT_STOP() \ +#define SLJIT_UNREACHABLE() \ do { \ printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \ SLJIT_HALT_PROCESS(); \ } while (0) -#endif /* !SLJIT_ASSERT_STOP */ +#endif /* !SLJIT_UNREACHABLE */ #else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */ /* Forcing empty, but valid statements. */ #undef SLJIT_ASSERT -#undef SLJIT_ASSERT_STOP +#undef SLJIT_UNREACHABLE #define SLJIT_ASSERT(x) \ do { } while (0) -#define SLJIT_ASSERT_STOP() \ +#define SLJIT_UNREACHABLE() \ do { } while (0) #endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */ #ifndef SLJIT_COMPILE_ASSERT -/* Should be improved eventually. */ #define SLJIT_COMPILE_ASSERT(x, description) \ - SLJIT_ASSERT(x) + switch(0) { case 0: case ((x) ? 1 : 0): break; } #endif /* !SLJIT_COMPILE_ASSERT */ diff --git a/pcre2-10.22/src/sljit/sljitExecAllocator.c b/pcre2-10.32/src/sljit/sljitExecAllocator.c similarity index 95% rename from pcre2-10.22/src/sljit/sljitExecAllocator.c rename to pcre2-10.32/src/sljit/sljitExecAllocator.c index 54f05f5dd..7c1857861 100644 --- a/pcre2-10.22/src/sljit/sljitExecAllocator.c +++ b/pcre2-10.32/src/sljit/sljitExecAllocator.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -86,7 +86,7 @@ static SLJIT_INLINE void* alloc_chunk(sljit_uw size) return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); } -static SLJIT_INLINE void free_chunk(void* chunk, sljit_uw size) +static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size) { SLJIT_UNUSED_ARG(size); VirtualFree(chunk, 0, MEM_RELEASE); @@ -96,10 +96,17 @@ static SLJIT_INLINE void free_chunk(void* chunk, sljit_uw size) static SLJIT_INLINE void* alloc_chunk(sljit_uw size) { - void* retval; + void *retval; #ifdef MAP_ANON - retval = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0); + + int flags = MAP_PRIVATE | MAP_ANON; + +#ifdef MAP_JIT + flags |= MAP_JIT; +#endif + + retval = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, flags, -1, 0); #else if (dev_zero < 0) { if (open_dev_zero()) @@ -111,7 +118,7 @@ static SLJIT_INLINE void* alloc_chunk(sljit_uw size) return (retval != MAP_FAILED) ? retval : NULL; } -static SLJIT_INLINE void free_chunk(void* chunk, sljit_uw size) +static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size) { munmap(chunk, size); } @@ -180,8 +187,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size) sljit_uw chunk_size; allocator_grab_lock(); - if (size < sizeof(struct free_block)) - size = sizeof(struct free_block); + if (size < (64 - sizeof(struct block_header))) + size = (64 - sizeof(struct block_header)); size = ALIGN_SIZE(size); free_block = free_blocks; diff --git a/pcre2-10.22/src/sljit/sljitLir.c b/pcre2-10.32/src/sljit/sljitLir.c similarity index 64% rename from pcre2-10.22/src/sljit/sljitLir.c rename to pcre2-10.32/src/sljit/sljitLir.c index ec1781e4c..5bdddc10c 100644 --- a/pcre2-10.22/src/sljit/sljitLir.c +++ b/pcre2-10.32/src/sljit/sljitLir.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -26,6 +26,21 @@ #include "sljitLir.h" +#ifdef _WIN32 + +/* For SLJIT_CACHE_FLUSH, which can expand to FlushInstructionCache. */ +#include + +#endif /* _WIN32 */ + +#if !(defined SLJIT_STD_MACROS_DEFINED && SLJIT_STD_MACROS_DEFINED) + +/* These libraries are needed for the macros below. */ +#include +#include + +#endif /* SLJIT_STD_MACROS_DEFINED */ + #define CHECK_ERROR() \ do { \ if (SLJIT_UNLIKELY(compiler->error)) \ @@ -76,17 +91,26 @@ #if !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) -#define GET_OPCODE(op) \ - ((op) & ~(SLJIT_I32_OP | SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS)) +#define VARIABLE_FLAG_SHIFT (10) +#define VARIABLE_FLAG_MASK (0x3f << VARIABLE_FLAG_SHIFT) +#define GET_FLAG_TYPE(op) ((op) >> VARIABLE_FLAG_SHIFT) -#define GET_FLAGS(op) \ - ((op) & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C)) +#define GET_OPCODE(op) \ + ((op) & ~(SLJIT_I32_OP | SLJIT_SET_Z | VARIABLE_FLAG_MASK)) + +#define HAS_FLAGS(op) \ + ((op) & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)) #define GET_ALL_FLAGS(op) \ - ((op) & (SLJIT_I32_OP | SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS)) + ((op) & (SLJIT_I32_OP | SLJIT_SET_Z | VARIABLE_FLAG_MASK)) +#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) #define TYPE_CAST_NEEDED(op) \ - (((op) >= SLJIT_MOV_U8 && (op) <= SLJIT_MOV_S16) || ((op) >= SLJIT_MOVU_U8 && (op) <= SLJIT_MOVU_S16)) + ((op) >= SLJIT_MOV_U8 && (op) <= SLJIT_MOV_S32) +#else +#define TYPE_CAST_NEEDED(op) \ + ((op) >= SLJIT_MOV_U8 && (op) <= SLJIT_MOV_S16) +#endif #define BUF_SIZE 4096 @@ -106,16 +130,19 @@ /* When reg can be unused. */ #define SLOW_IS_REG(reg) ((reg) > 0 && (reg) <= REG_MASK) +/* Mask for argument types. */ +#define SLJIT_DEF_MASK ((1 << SLJIT_DEF_SHIFT) - 1) + /* Jump flags. */ #define JUMP_LABEL 0x1 #define JUMP_ADDR 0x2 /* SLJIT_REWRITABLE_JUMP is 0x1000. */ #if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) -# define PATCH_MB 0x4 -# define PATCH_MW 0x8 +# define PATCH_MB 0x4 +# define PATCH_MW 0x8 #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) -# define PATCH_MD 0x10 +# define PATCH_MD 0x10 #endif #endif @@ -242,9 +269,21 @@ #if !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) #if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) + +#if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR) +#include "sljitProtExecAllocator.c" +#else #include "sljitExecAllocator.c" #endif +#endif + +#if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR) +#define SLJIT_ADD_EXEC_OFFSET(ptr, exec_offset) ((sljit_u8 *)(ptr) + (exec_offset)) +#else +#define SLJIT_ADD_EXEC_OFFSET(ptr, exec_offset) ((sljit_u8 *)(ptr)) +#endif + /* Argument checking features. */ #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) @@ -318,7 +357,7 @@ /* Public functions */ /* --------------------------------------------------------------------- */ -#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) #define SLJIT_NEEDS_COMPILER_INIT 1 static sljit_s32 compiler_initialized = 0; /* A thread safe initialization. */ @@ -345,6 +384,8 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allo int_op_and_single_op_must_be_the_same); SLJIT_COMPILE_ASSERT(SLJIT_REWRITABLE_JUMP != SLJIT_F32_OP, rewritable_jump_and_single_op_must_not_be_the_same); + SLJIT_COMPILE_ASSERT(!(SLJIT_EQUAL & 0x1) && !(SLJIT_LESS & 0x1) && !(SLJIT_EQUAL_F64 & 0x1) && !(SLJIT_JUMP & 0x1), + conditional_flags_must_be_even_numbers); /* Only the non-zero members must be set. */ compiler->error = SLJIT_SUCCESS; @@ -479,6 +520,18 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw } } +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, sljit_s32 current_flags) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(current_flags); + +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + if ((current_flags & ~(VARIABLE_FLAG_MASK | SLJIT_I32_OP | SLJIT_SET_Z)) == 0) { + compiler->last_flags = GET_FLAG_TYPE(current_flags) | (current_flags & (SLJIT_I32_OP | SLJIT_SET_Z)); + } +#endif +} + /* --------------------------------------------------------------------- */ /* Private functions */ /* --------------------------------------------------------------------- */ @@ -553,6 +606,19 @@ static SLJIT_INLINE void reverse_buf(struct sljit_compiler *compiler) compiler->buf = prev; } +static SLJIT_INLINE sljit_s32 get_arg_count(sljit_s32 arg_types) +{ + sljit_s32 arg_count = 0; + + arg_types >>= SLJIT_DEF_SHIFT; + while (arg_types) { + arg_count++; + arg_types >>= SLJIT_DEF_SHIFT; + } + + return arg_count; +} + static SLJIT_INLINE void set_emit_enter(struct sljit_compiler *compiler, sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) @@ -624,149 +690,108 @@ static SLJIT_INLINE void set_const(struct sljit_const *const_, struct sljit_comp (((exp) & SLJIT_MEM) && (((exp) & REG_MASK) == reg || OFFS_REG(exp) == reg)) #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) -#define FUNCTION_CHECK_OP() \ - CHECK_ARGUMENT(!GET_FLAGS(op) || !(op & SLJIT_KEEP_FLAGS)); \ - switch (GET_OPCODE(op)) { \ - case SLJIT_NOT: \ - case SLJIT_CLZ: \ - case SLJIT_AND: \ - case SLJIT_OR: \ - case SLJIT_XOR: \ - case SLJIT_SHL: \ - case SLJIT_LSHR: \ - case SLJIT_ASHR: \ - CHECK_ARGUMENT(!(op & (SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C))); \ - break; \ - case SLJIT_NEG: \ - CHECK_ARGUMENT(!(op & (SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_C))); \ - break; \ - case SLJIT_MUL: \ - CHECK_ARGUMENT(!(op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_C))); \ - break; \ - case SLJIT_ADD: \ - CHECK_ARGUMENT(!(op & (SLJIT_SET_U | SLJIT_SET_S))); \ - break; \ - case SLJIT_SUB: \ - break; \ - case SLJIT_ADDC: \ - case SLJIT_SUBC: \ - CHECK_ARGUMENT(!(op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O))); \ - break; \ - case SLJIT_BREAKPOINT: \ - case SLJIT_NOP: \ - case SLJIT_LMUL_UW: \ - case SLJIT_LMUL_SW: \ - case SLJIT_MOV: \ - case SLJIT_MOV_U32: \ - case SLJIT_MOV_P: \ - case SLJIT_MOVU: \ - case SLJIT_MOVU_U32: \ - case SLJIT_MOVU_P: \ - /* Nothing allowed */ \ - CHECK_ARGUMENT(!(op & (SLJIT_I32_OP | SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))); \ - break; \ - default: \ - /* Only SLJIT_I32_OP or SLJIT_F32_OP is allowed. */ \ - CHECK_ARGUMENT(!(op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))); \ - break; \ - } - -#define FUNCTION_CHECK_FOP() \ - CHECK_ARGUMENT(!GET_FLAGS(op) || !(op & SLJIT_KEEP_FLAGS)); \ - switch (GET_OPCODE(op)) { \ - case SLJIT_CMP_F64: \ - CHECK_ARGUMENT(!(op & (SLJIT_SET_U | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))); \ - CHECK_ARGUMENT((op & (SLJIT_SET_E | SLJIT_SET_S))); \ - break; \ - default: \ - /* Only SLJIT_I32_OP or SLJIT_F32_OP is allowed. */ \ - CHECK_ARGUMENT(!(op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))); \ - break; \ - } #define FUNCTION_CHECK_IS_REG(r) \ - (((r) >= SLJIT_R0 && (r) < (SLJIT_R0 + compiler->scratches)) || \ - ((r) > (SLJIT_S0 - compiler->saveds) && (r) <= SLJIT_S0)) + (((r) >= SLJIT_R0 && (r) < (SLJIT_R0 + compiler->scratches)) \ + || ((r) > (SLJIT_S0 - compiler->saveds) && (r) <= SLJIT_S0)) -#define FUNCTION_CHECK_IS_REG_OR_UNUSED(r) \ - ((r) == SLJIT_UNUSED || \ - ((r) >= SLJIT_R0 && (r) < (SLJIT_R0 + compiler->scratches)) || \ - ((r) > (SLJIT_S0 - compiler->saveds) && (r) <= SLJIT_S0)) +#define FUNCTION_CHECK_IS_FREG(fr) \ + (((fr) >= SLJIT_FR0 && (fr) < (SLJIT_FR0 + compiler->fscratches)) \ + || ((fr) > (SLJIT_FS0 - compiler->fsaveds) && (fr) <= SLJIT_FS0)) #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) -#define CHECK_NOT_VIRTUAL_REGISTER(p) \ - CHECK_ARGUMENT((p) < SLJIT_R3 || (p) > SLJIT_R6); +#define CHECK_IF_VIRTUAL_REGISTER(p) ((p) <= SLJIT_S3 && (p) >= SLJIT_S8) #else -#define CHECK_NOT_VIRTUAL_REGISTER(p) +#define CHECK_IF_VIRTUAL_REGISTER(p) 0 #endif -#define FUNCTION_CHECK_SRC(p, i) \ - CHECK_ARGUMENT(compiler->scratches != -1 && compiler->saveds != -1); \ - if (FUNCTION_CHECK_IS_REG(p)) \ - CHECK_ARGUMENT((i) == 0); \ - else if ((p) == SLJIT_IMM) \ - ; \ - else if ((p) == (SLJIT_MEM1(SLJIT_SP))) \ - CHECK_ARGUMENT((i) >= 0 && (i) < compiler->logical_local_size); \ - else { \ - CHECK_ARGUMENT((p) & SLJIT_MEM); \ - CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG_OR_UNUSED((p) & REG_MASK)); \ - CHECK_NOT_VIRTUAL_REGISTER((p) & REG_MASK); \ - if ((p) & OFFS_REG_MASK) { \ - CHECK_ARGUMENT(((p) & REG_MASK) != SLJIT_UNUSED); \ - CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(OFFS_REG(p))); \ - CHECK_NOT_VIRTUAL_REGISTER(OFFS_REG(p)); \ - CHECK_ARGUMENT(!((i) & ~0x3)); \ - } \ - CHECK_ARGUMENT(!((p) & ~(SLJIT_MEM | SLJIT_IMM | REG_MASK | OFFS_REG_MASK))); \ +static sljit_s32 function_check_src_mem(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i) +{ + if (compiler->scratches == -1 || compiler->saveds == -1) + return 0; + + if (!(p & SLJIT_MEM)) + return 0; + + if (!((p & REG_MASK) == SLJIT_UNUSED || FUNCTION_CHECK_IS_REG(p & REG_MASK))) + return 0; + + if (CHECK_IF_VIRTUAL_REGISTER(p & REG_MASK)) + return 0; + + if (p & OFFS_REG_MASK) { + if ((p & REG_MASK) == SLJIT_UNUSED) + return 0; + + if (!(FUNCTION_CHECK_IS_REG(OFFS_REG(p)))) + return 0; + + if (CHECK_IF_VIRTUAL_REGISTER(OFFS_REG(p))) + return 0; + + if ((i & ~0x3) != 0) + return 0; } -#define FUNCTION_CHECK_DST(p, i) \ - CHECK_ARGUMENT(compiler->scratches != -1 && compiler->saveds != -1); \ - if (FUNCTION_CHECK_IS_REG_OR_UNUSED(p)) \ - CHECK_ARGUMENT((i) == 0); \ - else if ((p) == (SLJIT_MEM1(SLJIT_SP))) \ - CHECK_ARGUMENT((i) >= 0 && (i) < compiler->logical_local_size); \ - else { \ - CHECK_ARGUMENT((p) & SLJIT_MEM); \ - CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG_OR_UNUSED((p) & REG_MASK)); \ - CHECK_NOT_VIRTUAL_REGISTER((p) & REG_MASK); \ - if ((p) & OFFS_REG_MASK) { \ - CHECK_ARGUMENT(((p) & REG_MASK) != SLJIT_UNUSED); \ - CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(OFFS_REG(p))); \ - CHECK_NOT_VIRTUAL_REGISTER(OFFS_REG(p)); \ - CHECK_ARGUMENT(!((i) & ~0x3)); \ - } \ - CHECK_ARGUMENT(!((p) & ~(SLJIT_MEM | SLJIT_IMM | REG_MASK | OFFS_REG_MASK))); \ - } + return (p & ~(SLJIT_MEM | REG_MASK | OFFS_REG_MASK)) == 0; +} + +#define FUNCTION_CHECK_SRC_MEM(p, i) \ + CHECK_ARGUMENT(function_check_src_mem(compiler, p, i)); + +static sljit_s32 function_check_src(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i) +{ + if (compiler->scratches == -1 || compiler->saveds == -1) + return 0; + + if (FUNCTION_CHECK_IS_REG(p)) + return (i == 0); + + if (p == SLJIT_IMM) + return 1; + + if (p == SLJIT_MEM1(SLJIT_SP)) + return (i >= 0 && i < compiler->logical_local_size); + + return function_check_src_mem(compiler, p, i); +} + +#define FUNCTION_CHECK_SRC(p, i) \ + CHECK_ARGUMENT(function_check_src(compiler, p, i)); + +static sljit_s32 function_check_dst(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i, sljit_s32 unused) +{ + if (compiler->scratches == -1 || compiler->saveds == -1) + return 0; + + if (FUNCTION_CHECK_IS_REG(p) || ((unused) && (p) == SLJIT_UNUSED)) + return (i == 0); + + if (p == SLJIT_MEM1(SLJIT_SP)) + return (i >= 0 && i < compiler->logical_local_size); + + return function_check_src_mem(compiler, p, i); +} + +#define FUNCTION_CHECK_DST(p, i, unused) \ + CHECK_ARGUMENT(function_check_dst(compiler, p, i, unused)); + +static sljit_s32 function_fcheck(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i) +{ + if (compiler->scratches == -1 || compiler->saveds == -1) + return 0; + + if (FUNCTION_CHECK_IS_FREG(p)) + return (i == 0); + + if (p == SLJIT_MEM1(SLJIT_SP)) + return (i >= 0 && i < compiler->logical_local_size); + + return function_check_src_mem(compiler, p, i); +} #define FUNCTION_FCHECK(p, i) \ - CHECK_ARGUMENT(compiler->fscratches != -1 && compiler->fsaveds != -1); \ - if (((p) >= SLJIT_FR0 && (p) < (SLJIT_FR0 + compiler->fscratches)) || \ - ((p) > (SLJIT_FS0 - compiler->fsaveds) && (p) <= SLJIT_FS0)) \ - CHECK_ARGUMENT(i == 0); \ - else if ((p) == (SLJIT_MEM1(SLJIT_SP))) \ - CHECK_ARGUMENT((i) >= 0 && (i) < compiler->logical_local_size); \ - else { \ - CHECK_ARGUMENT((p) & SLJIT_MEM); \ - CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG_OR_UNUSED((p) & REG_MASK)); \ - CHECK_NOT_VIRTUAL_REGISTER((p) & REG_MASK); \ - if ((p) & OFFS_REG_MASK) { \ - CHECK_ARGUMENT(((p) & REG_MASK) != SLJIT_UNUSED); \ - CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(OFFS_REG(p))); \ - CHECK_NOT_VIRTUAL_REGISTER(OFFS_REG(p)); \ - CHECK_ARGUMENT(((p) & OFFS_REG_MASK) != TO_OFFS_REG(SLJIT_SP) && !(i & ~0x3)); \ - } \ - CHECK_ARGUMENT(!((p) & ~(SLJIT_MEM | SLJIT_IMM | REG_MASK | OFFS_REG_MASK))); \ - } - -#define FUNCTION_CHECK_OP1() \ - if (GET_OPCODE(op) >= SLJIT_MOVU && GET_OPCODE(op) <= SLJIT_MOVU_P) { \ - CHECK_ARGUMENT(!(src & SLJIT_MEM) || (src & REG_MASK) != SLJIT_SP); \ - CHECK_ARGUMENT(!(dst & SLJIT_MEM) || (dst & REG_MASK) != SLJIT_SP); \ - if ((src & SLJIT_MEM) && (src & REG_MASK)) \ - CHECK_ARGUMENT((dst & REG_MASK) != (src & REG_MASK) && OFFS_REG(dst) != (src & REG_MASK)); \ - } + CHECK_ARGUMENT(function_fcheck(compiler, p, i)); #endif /* SLJIT_ARGUMENT_CHECKS */ @@ -787,62 +812,72 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *comp # define SLJIT_PRINT_D "" #endif -#define sljit_verbose_reg(compiler, r) \ - do { \ - if ((r) < (SLJIT_R0 + compiler->scratches)) \ - fprintf(compiler->verbose, "r%d", (r) - SLJIT_R0); \ - else \ - fprintf(compiler->verbose, "s%d", SLJIT_NUMBER_OF_REGISTERS - (r)); \ - } while (0) +static void sljit_verbose_reg(struct sljit_compiler *compiler, sljit_s32 r) +{ + if (r < (SLJIT_R0 + compiler->scratches)) + fprintf(compiler->verbose, "r%d", r - SLJIT_R0); + else if (r != SLJIT_SP) + fprintf(compiler->verbose, "s%d", SLJIT_NUMBER_OF_REGISTERS - r); + else + fprintf(compiler->verbose, "sp"); +} -#define sljit_verbose_param(compiler, p, i) \ - if ((p) & SLJIT_IMM) \ - fprintf(compiler->verbose, "#%" SLJIT_PRINT_D "d", (i)); \ - else if ((p) & SLJIT_MEM) { \ - if ((p) & REG_MASK) { \ - fputc('[', compiler->verbose); \ - sljit_verbose_reg(compiler, (p) & REG_MASK); \ - if ((p) & OFFS_REG_MASK) { \ - fprintf(compiler->verbose, " + "); \ - sljit_verbose_reg(compiler, OFFS_REG(p)); \ - if (i) \ - fprintf(compiler->verbose, " * %d", 1 << (i)); \ - } \ - else if (i) \ - fprintf(compiler->verbose, " + %" SLJIT_PRINT_D "d", (i)); \ - fputc(']', compiler->verbose); \ - } \ - else \ - fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i)); \ - } else if (p) \ - sljit_verbose_reg(compiler, p); \ - else \ +static void sljit_verbose_freg(struct sljit_compiler *compiler, sljit_s32 r) +{ + if (r < (SLJIT_FR0 + compiler->fscratches)) + fprintf(compiler->verbose, "fr%d", r - SLJIT_FR0); + else + fprintf(compiler->verbose, "fs%d", SLJIT_NUMBER_OF_FLOAT_REGISTERS - r); +} + +static void sljit_verbose_param(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i) +{ + if ((p) & SLJIT_IMM) + fprintf(compiler->verbose, "#%" SLJIT_PRINT_D "d", (i)); + else if ((p) & SLJIT_MEM) { + if ((p) & REG_MASK) { + fputc('[', compiler->verbose); + sljit_verbose_reg(compiler, (p) & REG_MASK); + if ((p) & OFFS_REG_MASK) { + fprintf(compiler->verbose, " + "); + sljit_verbose_reg(compiler, OFFS_REG(p)); + if (i) + fprintf(compiler->verbose, " * %d", 1 << (i)); + } + else if (i) + fprintf(compiler->verbose, " + %" SLJIT_PRINT_D "d", (i)); + fputc(']', compiler->verbose); + } + else + fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i)); + } else if (p) + sljit_verbose_reg(compiler, p); + else fprintf(compiler->verbose, "unused"); +} -#define sljit_verbose_fparam(compiler, p, i) \ - if ((p) & SLJIT_MEM) { \ - if ((p) & REG_MASK) { \ - fputc('[', compiler->verbose); \ - sljit_verbose_reg(compiler, (p) & REG_MASK); \ - if ((p) & OFFS_REG_MASK) { \ - fprintf(compiler->verbose, " + "); \ - sljit_verbose_reg(compiler, OFFS_REG(p)); \ - if (i) \ - fprintf(compiler->verbose, "%d", 1 << (i)); \ - } \ - else if (i) \ - fprintf(compiler->verbose, "%" SLJIT_PRINT_D "d", (i)); \ - fputc(']', compiler->verbose); \ - } \ - else \ - fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i)); \ - } \ - else { \ - if ((p) < (SLJIT_FR0 + compiler->fscratches)) \ - fprintf(compiler->verbose, "fr%d", (p) - SLJIT_FR0); \ - else \ - fprintf(compiler->verbose, "fs%d", SLJIT_NUMBER_OF_FLOAT_REGISTERS - (p)); \ +static void sljit_verbose_fparam(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i) +{ + if ((p) & SLJIT_MEM) { + if ((p) & REG_MASK) { + fputc('[', compiler->verbose); + sljit_verbose_reg(compiler, (p) & REG_MASK); + if ((p) & OFFS_REG_MASK) { + fprintf(compiler->verbose, " + "); + sljit_verbose_reg(compiler, OFFS_REG(p)); + if (i) + fprintf(compiler->verbose, "%d", 1 << (i)); + } + else if (i) + fprintf(compiler->verbose, " + %" SLJIT_PRINT_D "d", (i)); + fputc(']', compiler->verbose); + } + else + fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i)); } + else + sljit_verbose_freg(compiler, p); +} static const char* op0_names[] = { (char*)"breakpoint", (char*)"nop", (char*)"lmul.uw", (char*)"lmul.sw", @@ -885,12 +920,17 @@ static char* jump_names[] = { (char*)"sig_greater", (char*)"sig_less_equal", (char*)"overflow", (char*)"not_overflow", (char*)"mul_overflow", (char*)"mul_not_overflow", + (char*)"carry", (char*)"", (char*)"equal", (char*)"not_equal", (char*)"less", (char*)"greater_equal", (char*)"greater", (char*)"less_equal", (char*)"unordered", (char*)"ordered", (char*)"jump", (char*)"fast_call", - (char*)"call0", (char*)"call1", (char*)"call2", (char*)"call3" + (char*)"call", (char*)"call.cdecl" +}; + +static char* call_arg_names[] = { + (char*)"void", (char*)"sw", (char*)"uw", (char*)"s32", (char*)"u32", (char*)"f32", (char*)"f64" }; #endif /* SLJIT_VERBOSE */ @@ -923,56 +963,104 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_generate_code(struct sljit_com } static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + sljit_s32 types, arg_count, curr_type; +#endif + SLJIT_UNUSED_ARG(compiler); #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(!(options & ~SLJIT_DOUBLE_ALIGNMENT)); - CHECK_ARGUMENT(args >= 0 && args <= 3); + CHECK_ARGUMENT(!(options & ~SLJIT_F64_ALIGNMENT)); CHECK_ARGUMENT(scratches >= 0 && scratches <= SLJIT_NUMBER_OF_REGISTERS); CHECK_ARGUMENT(saveds >= 0 && saveds <= SLJIT_NUMBER_OF_REGISTERS); CHECK_ARGUMENT(scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS); - CHECK_ARGUMENT(args <= saveds); CHECK_ARGUMENT(fscratches >= 0 && fscratches <= SLJIT_NUMBER_OF_FLOAT_REGISTERS); CHECK_ARGUMENT(fsaveds >= 0 && fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS); CHECK_ARGUMENT(fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS); CHECK_ARGUMENT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE); + CHECK_ARGUMENT((arg_types & SLJIT_DEF_MASK) == 0); + + types = (arg_types >> SLJIT_DEF_SHIFT); + arg_count = 0; + while (types != 0 && arg_count < 3) { + curr_type = (types & SLJIT_DEF_MASK); + CHECK_ARGUMENT(curr_type == SLJIT_ARG_TYPE_SW || curr_type == SLJIT_ARG_TYPE_UW); + arg_count++; + types >>= SLJIT_DEF_SHIFT; + } + CHECK_ARGUMENT(arg_count <= saveds && types == 0); + + compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) - if (SLJIT_UNLIKELY(!!compiler->verbose)) - fprintf(compiler->verbose, " enter options:none args:%d scratches:%d saveds:%d fscratches:%d fsaveds:%d local_size:%d\n", - args, scratches, saveds, fscratches, fsaveds, local_size); + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " enter options:%s args[", (options & SLJIT_F64_ALIGNMENT) ? "f64_align" : ""); + + arg_types >>= SLJIT_DEF_SHIFT; + while (arg_types) { + fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_DEF_MASK]); + arg_types >>= SLJIT_DEF_SHIFT; + if (arg_types) + fprintf(compiler->verbose, ","); + } + + fprintf(compiler->verbose, "] scratches:%d saveds:%d fscratches:%d fsaveds:%d local_size:%d\n", + scratches, saveds, fscratches, fsaveds, local_size); + } #endif CHECK_RETURN_OK; } static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { - if (SLJIT_UNLIKELY(compiler->skip_checks)) { - compiler->skip_checks = 0; - CHECK_RETURN_OK; - } +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + sljit_s32 types, arg_count, curr_type; +#endif + + SLJIT_UNUSED_ARG(compiler); #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(!(options & ~SLJIT_DOUBLE_ALIGNMENT)); - CHECK_ARGUMENT(args >= 0 && args <= 3); + CHECK_ARGUMENT(!(options & ~SLJIT_F64_ALIGNMENT)); CHECK_ARGUMENT(scratches >= 0 && scratches <= SLJIT_NUMBER_OF_REGISTERS); CHECK_ARGUMENT(saveds >= 0 && saveds <= SLJIT_NUMBER_OF_REGISTERS); CHECK_ARGUMENT(scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS); - CHECK_ARGUMENT(args <= saveds); CHECK_ARGUMENT(fscratches >= 0 && fscratches <= SLJIT_NUMBER_OF_FLOAT_REGISTERS); CHECK_ARGUMENT(fsaveds >= 0 && fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS); CHECK_ARGUMENT(fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS); CHECK_ARGUMENT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE); + + types = (arg_types >> SLJIT_DEF_SHIFT); + arg_count = 0; + while (types != 0 && arg_count < 3) { + curr_type = (types & SLJIT_DEF_MASK); + CHECK_ARGUMENT(curr_type == SLJIT_ARG_TYPE_SW || curr_type == SLJIT_ARG_TYPE_UW); + arg_count++; + types >>= SLJIT_DEF_SHIFT; + } + CHECK_ARGUMENT(arg_count <= saveds && types == 0); + + compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) - if (SLJIT_UNLIKELY(!!compiler->verbose)) - fprintf(compiler->verbose, " set_context options:none args:%d scratches:%d saveds:%d fscratches:%d fsaveds:%d local_size:%d\n", - args, scratches, saveds, fscratches, fsaveds, local_size); + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " set_context options:%s args[", (options & SLJIT_F64_ALIGNMENT) ? "f64_align" : ""); + + arg_types >>= SLJIT_DEF_SHIFT; + while (arg_types) { + fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_DEF_MASK]); + arg_types >>= SLJIT_DEF_SHIFT; + if (arg_types) + fprintf(compiler->verbose, ","); + } + + fprintf(compiler->verbose, "] scratches:%d saveds:%d fscratches:%d fsaveds:%d local_size:%d\n", + scratches, saveds, fscratches, fsaveds, local_size); + } #endif CHECK_RETURN_OK; } @@ -987,6 +1075,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return(struct sljit_compi } else CHECK_ARGUMENT(src == 0 && srcw == 0); + compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { @@ -1005,7 +1094,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return(struct sljit_compi static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - FUNCTION_CHECK_DST(dst, dstw); + FUNCTION_CHECK_DST(dst, dstw, 0); + compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { @@ -1021,6 +1111,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fast_return(struct sljit_ { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) FUNCTION_CHECK_SRC(src, srcw); + CHECK_ARGUMENT(src != SLJIT_IMM); + compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { @@ -1038,6 +1130,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op0(struct sljit_compiler CHECK_ARGUMENT((op >= SLJIT_BREAKPOINT && op <= SLJIT_LMUL_SW) || ((op & ~SLJIT_I32_OP) >= SLJIT_DIVMOD_UW && (op & ~SLJIT_I32_OP) <= SLJIT_DIV_SW)); CHECK_ARGUMENT(op < SLJIT_LMUL_UW || compiler->scratches >= 2); + if (op >= SLJIT_LMUL_UW) + compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) @@ -1063,23 +1157,48 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op1(struct sljit_compiler #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_CLZ); - FUNCTION_CHECK_OP(); + + switch (GET_OPCODE(op)) { + case SLJIT_NOT: + /* Only SLJIT_I32_OP and SLJIT_SET_Z are allowed. */ + CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)); + break; + case SLJIT_NEG: + CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK) + || GET_FLAG_TYPE(op) == SLJIT_OVERFLOW); + break; + case SLJIT_MOV: + case SLJIT_MOV_U32: + case SLJIT_MOV_P: + /* Nothing allowed */ + CHECK_ARGUMENT(!(op & (SLJIT_I32_OP | SLJIT_SET_Z | VARIABLE_FLAG_MASK))); + break; + default: + /* Only SLJIT_I32_OP is allowed. */ + CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK))); + break; + } + + FUNCTION_CHECK_DST(dst, dstw, 1); FUNCTION_CHECK_SRC(src, srcw); - FUNCTION_CHECK_DST(dst, dstw); - FUNCTION_CHECK_OP1(); + + if (GET_OPCODE(op) >= SLJIT_NOT) { + CHECK_ARGUMENT(src != SLJIT_IMM); + compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_I32_OP | SLJIT_SET_Z)); + } #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { - if (GET_OPCODE(op) <= SLJIT_MOVU_P) + if (GET_OPCODE(op) <= SLJIT_MOV_P) { - fprintf(compiler->verbose, " mov%s%s%s ", (GET_OPCODE(op) >= SLJIT_MOVU) ? "u" : "", - !(op & SLJIT_I32_OP) ? "" : "32", (op != SLJIT_MOV32 && op != SLJIT_MOVU32) ? op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE] : ""); + fprintf(compiler->verbose, " mov%s%s ", !(op & SLJIT_I32_OP) ? "" : "32", + (op != SLJIT_MOV32) ? op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE] : ""); } else { - fprintf(compiler->verbose, " %s%s%s%s%s%s%s%s ", op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE], !(op & SLJIT_I32_OP) ? "" : "32", - !(op & SLJIT_SET_E) ? "" : ".e", !(op & SLJIT_SET_U) ? "" : ".u", !(op & SLJIT_SET_S) ? "" : ".s", - !(op & SLJIT_SET_O) ? "" : ".o", !(op & SLJIT_SET_C) ? "" : ".c", !(op & SLJIT_KEEP_FLAGS) ? "" : ".k"); + fprintf(compiler->verbose, " %s%s%s%s%s ", op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE], !(op & SLJIT_I32_OP) ? "" : "32", + !(op & SLJIT_SET_Z) ? "" : ".z", !(op & VARIABLE_FLAG_MASK) ? "" : ".", + !(op & VARIABLE_FLAG_MASK) ? "" : jump_names[GET_FLAG_TYPE(op)]); } sljit_verbose_param(compiler, dst, dstw); @@ -1103,16 +1222,53 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op2(struct sljit_compiler #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_ADD && GET_OPCODE(op) <= SLJIT_ASHR); - FUNCTION_CHECK_OP(); + + switch (GET_OPCODE(op)) { + case SLJIT_AND: + case SLJIT_OR: + case SLJIT_XOR: + case SLJIT_SHL: + case SLJIT_LSHR: + case SLJIT_ASHR: + CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)); + break; + case SLJIT_MUL: + CHECK_ARGUMENT(!(op & SLJIT_SET_Z)); + CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK) + || GET_FLAG_TYPE(op) == SLJIT_MUL_OVERFLOW); + break; + case SLJIT_ADD: + CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK) + || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY) + || GET_FLAG_TYPE(op) == SLJIT_OVERFLOW); + break; + case SLJIT_SUB: + CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK) + || (GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_OVERFLOW) + || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)); + break; + case SLJIT_ADDC: + case SLJIT_SUBC: + CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK) + || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)); + CHECK_ARGUMENT((compiler->last_flags & 0xff) == GET_FLAG_TYPE(SLJIT_SET_CARRY)); + CHECK_ARGUMENT((op & SLJIT_I32_OP) == (compiler->last_flags & SLJIT_I32_OP)); + break; + default: + SLJIT_UNREACHABLE(); + break; + } + + FUNCTION_CHECK_DST(dst, dstw, 1); FUNCTION_CHECK_SRC(src1, src1w); FUNCTION_CHECK_SRC(src2, src2w); - FUNCTION_CHECK_DST(dst, dstw); + compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_I32_OP | SLJIT_SET_Z)); #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { - fprintf(compiler->verbose, " %s%s%s%s%s%s%s%s ", op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], !(op & SLJIT_I32_OP) ? "" : "32", - !(op & SLJIT_SET_E) ? "" : ".e", !(op & SLJIT_SET_U) ? "" : ".u", !(op & SLJIT_SET_S) ? "" : ".s", - !(op & SLJIT_SET_O) ? "" : ".o", !(op & SLJIT_SET_C) ? "" : ".c", !(op & SLJIT_KEEP_FLAGS) ? "" : ".k"); + fprintf(compiler->verbose, " %s%s%s%s%s ", op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], !(op & SLJIT_I32_OP) ? "" : "32", + !(op & SLJIT_SET_Z) ? "" : ".z", !(op & VARIABLE_FLAG_MASK) ? "" : ".", + !(op & VARIABLE_FLAG_MASK) ? "" : jump_names[GET_FLAG_TYPE(op)]); sljit_verbose_param(compiler, dst, dstw); fprintf(compiler->verbose, ", "); sljit_verbose_param(compiler, src1, src1w); @@ -1153,6 +1309,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_custom(struct sljit_co #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) CHECK_ARGUMENT(instruction); + #if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) CHECK_ARGUMENT(size > 0 && size < 16); #elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) @@ -1162,6 +1319,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_custom(struct sljit_co CHECK_ARGUMENT(size == 4 && (((sljit_sw)instruction) & 0x3) == 0); #endif + compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { @@ -1184,9 +1342,9 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1(struct sljit_compile } #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(sljit_is_fpu_available()); + CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU)); CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_MOV_F64 && GET_OPCODE(op) <= SLJIT_ABS_F64); - FUNCTION_CHECK_FOP(); + CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK))); FUNCTION_FCHECK(src, srcw); FUNCTION_FCHECK(dst, dstw); #endif @@ -1212,22 +1370,31 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_cmp(struct sljit_com sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_I32_OP | SLJIT_SET_Z)); +#endif + if (SLJIT_UNLIKELY(compiler->skip_checks)) { compiler->skip_checks = 0; CHECK_RETURN_OK; } #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(sljit_is_fpu_available()); + CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU)); CHECK_ARGUMENT(GET_OPCODE(op) == SLJIT_CMP_F64); - FUNCTION_CHECK_FOP(); + CHECK_ARGUMENT(!(op & SLJIT_SET_Z)); + CHECK_ARGUMENT((op & VARIABLE_FLAG_MASK) + || (GET_FLAG_TYPE(op) >= SLJIT_EQUAL_F64 && GET_FLAG_TYPE(op) <= SLJIT_ORDERED_F64)); FUNCTION_FCHECK(src1, src1w); FUNCTION_FCHECK(src2, src2w); #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { - fprintf(compiler->verbose, " %s%s%s%s ", fop1_names[SLJIT_CMP_F64 - SLJIT_FOP1_BASE], (op & SLJIT_F32_OP) ? ".f32" : ".f64", - (op & SLJIT_SET_E) ? ".e" : "", (op & SLJIT_SET_S) ? ".s" : ""); + fprintf(compiler->verbose, " %s%s", fop1_names[SLJIT_CMP_F64 - SLJIT_FOP1_BASE], (op & SLJIT_F32_OP) ? ".f32" : ".f64"); + if (op & VARIABLE_FLAG_MASK) { + fprintf(compiler->verbose, ".%s_f", jump_names[GET_FLAG_TYPE(op)]); + } + fprintf(compiler->verbose, " "); sljit_verbose_fparam(compiler, src1, src1w); fprintf(compiler->verbose, ", "); sljit_verbose_fparam(compiler, src2, src2w); @@ -1247,11 +1414,11 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_conv_sw_from_f64(str } #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(sljit_is_fpu_available()); + CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU)); CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_CONV_SW_FROM_F64 && GET_OPCODE(op) <= SLJIT_CONV_S32_FROM_F64); - FUNCTION_CHECK_FOP(); + CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK))); FUNCTION_FCHECK(src, srcw); - FUNCTION_CHECK_DST(dst, dstw); + FUNCTION_CHECK_DST(dst, dstw, 0); #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { @@ -1277,9 +1444,9 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_conv_f64_from_sw(str } #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(sljit_is_fpu_available()); + CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU)); CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_CONV_F64_FROM_SW && GET_OPCODE(op) <= SLJIT_CONV_F64_FROM_S32); - FUNCTION_CHECK_FOP(); + CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK))); FUNCTION_CHECK_SRC(src, srcw); FUNCTION_FCHECK(dst, dstw); #endif @@ -1303,9 +1470,9 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop2(struct sljit_compile sljit_s32 src2, sljit_sw src2w) { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(sljit_is_fpu_available()); + CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU)); CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_ADD_F64 && GET_OPCODE(op) <= SLJIT_DIV_F64); - FUNCTION_CHECK_FOP(); + CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK))); FUNCTION_FCHECK(src1, src1w); FUNCTION_FCHECK(src2, src2w); FUNCTION_FCHECK(dst, dstw); @@ -1328,6 +1495,15 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_label(struct sljit_compil { SLJIT_UNUSED_ARG(compiler); + if (SLJIT_UNLIKELY(compiler->skip_checks)) { + compiler->skip_checks = 0; + CHECK_RETURN_OK; + } + +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->last_flags = 0; +#endif + #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) fprintf(compiler->verbose, "label:\n"); @@ -1344,9 +1520,19 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_jump(struct sljit_compile #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_I32_OP))); - CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_CALL3); + CHECK_ARGUMENT((type & 0xff) != GET_FLAG_TYPE(SLJIT_SET_CARRY) && (type & 0xff) != (GET_FLAG_TYPE(SLJIT_SET_CARRY) + 1)); + CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_FAST_CALL); CHECK_ARGUMENT((type & 0xff) < SLJIT_JUMP || !(type & SLJIT_I32_OP)); - CHECK_ARGUMENT((type & 0xff) <= SLJIT_CALL0 || ((type & 0xff) - SLJIT_CALL0) <= compiler->scratches); + + if ((type & 0xff) < SLJIT_JUMP) { + if ((type & 0xff) <= SLJIT_NOT_ZERO) + CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z); + else + CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff) + || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW) + || ((type & 0xff) == SLJIT_MUL_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_MUL_OVERFLOW)); + CHECK_ARGUMENT((type & SLJIT_I32_OP) == (compiler->last_flags & SLJIT_I32_OP)); + } #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) @@ -1356,6 +1542,63 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_jump(struct sljit_compile CHECK_RETURN_OK; } +static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + sljit_s32 i, types, curr_type, scratches, fscratches; + + CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP))); + CHECK_ARGUMENT((type & 0xff) == SLJIT_CALL || (type & 0xff) == SLJIT_CALL_CDECL); + + types = arg_types; + scratches = 0; + fscratches = 0; + for (i = 0; i < 5; i++) { + curr_type = (types & SLJIT_DEF_MASK); + CHECK_ARGUMENT(curr_type <= SLJIT_ARG_TYPE_F64); + if (i > 0) { + if (curr_type == 0) { + break; + } + if (curr_type >= SLJIT_ARG_TYPE_F32) + fscratches++; + else + scratches++; + } else { + if (curr_type >= SLJIT_ARG_TYPE_F32) { + CHECK_ARGUMENT(compiler->fscratches > 0); + } else if (curr_type >= SLJIT_ARG_TYPE_SW) { + CHECK_ARGUMENT(compiler->scratches > 0); + } + } + types >>= SLJIT_DEF_SHIFT; + } + CHECK_ARGUMENT(compiler->scratches >= scratches); + CHECK_ARGUMENT(compiler->fscratches >= fscratches); + CHECK_ARGUMENT(types == 0); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " %s%s ret[%s", jump_names[type & 0xff], + !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", call_arg_names[arg_types & SLJIT_DEF_MASK]); + + arg_types >>= SLJIT_DEF_SHIFT; + if (arg_types) { + fprintf(compiler->verbose, "], args["); + do { + fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_DEF_MASK]); + arg_types >>= SLJIT_DEF_SHIFT; + if (arg_types) + fprintf(compiler->verbose, ","); + } while (arg_types); + } + fprintf(compiler->verbose, "]\n"); + } +#endif + CHECK_RETURN_OK; +} + static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) @@ -1365,6 +1608,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmp(struct sljit_compiler CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_SIG_LESS_EQUAL); FUNCTION_CHECK_SRC(src1, src1w); FUNCTION_CHECK_SRC(src2, src2w); + compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { @@ -1384,11 +1628,12 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fcmp(struct sljit_compile sljit_s32 src2, sljit_sw src2w) { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(sljit_is_fpu_available()); + CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU)); CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_F32_OP))); CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL_F64 && (type & 0xff) <= SLJIT_ORDERED_F64); FUNCTION_FCHECK(src1, src1w); FUNCTION_FCHECK(src2, src2w); + compiler->last_flags = 0; #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { @@ -1403,7 +1648,8 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fcmp(struct sljit_compile CHECK_RETURN_OK; } -static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) +static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 src, sljit_sw srcw) { if (SLJIT_UNLIKELY(compiler->skip_checks)) { compiler->skip_checks = 0; @@ -1411,8 +1657,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_ijump(struct sljit_compil } #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(type >= SLJIT_JUMP && type <= SLJIT_CALL3); - CHECK_ARGUMENT(type <= SLJIT_CALL0 || (type - SLJIT_CALL0) <= compiler->scratches); + CHECK_ARGUMENT(type >= SLJIT_JUMP && type <= SLJIT_FAST_CALL); FUNCTION_CHECK_SRC(src, srcw); #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) @@ -1425,48 +1670,212 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_ijump(struct sljit_compil CHECK_RETURN_OK; } +static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + sljit_s32 i, types, curr_type, scratches, fscratches; + + CHECK_ARGUMENT(type == SLJIT_CALL || type == SLJIT_CALL_CDECL); + FUNCTION_CHECK_SRC(src, srcw); + + types = arg_types; + scratches = 0; + fscratches = 0; + for (i = 0; i < 5; i++) { + curr_type = (types & SLJIT_DEF_MASK); + CHECK_ARGUMENT(curr_type <= SLJIT_ARG_TYPE_F64); + if (i > 0) { + if (curr_type == 0) { + break; + } + if (curr_type >= SLJIT_ARG_TYPE_F32) + fscratches++; + else + scratches++; + } else { + if (curr_type >= SLJIT_ARG_TYPE_F32) { + CHECK_ARGUMENT(compiler->fscratches > 0); + } else if (curr_type >= SLJIT_ARG_TYPE_SW) { + CHECK_ARGUMENT(compiler->scratches > 0); + } + } + types >>= SLJIT_DEF_SHIFT; + } + CHECK_ARGUMENT(compiler->scratches >= scratches); + CHECK_ARGUMENT(compiler->fscratches >= fscratches); + CHECK_ARGUMENT(types == 0); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " i%s%s ret[%s", jump_names[type & 0xff], + !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", call_arg_names[arg_types & SLJIT_DEF_MASK]); + + arg_types >>= SLJIT_DEF_SHIFT; + if (arg_types) { + fprintf(compiler->verbose, "], args["); + do { + fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_DEF_MASK]); + arg_types >>= SLJIT_DEF_SHIFT; + if (arg_types) + fprintf(compiler->verbose, ","); + } while (arg_types); + } + fprintf(compiler->verbose, "], "); + sljit_verbose_param(compiler, src, srcw); + fprintf(compiler->verbose, "\n"); + } +#endif + CHECK_RETURN_OK; +} + static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw, sljit_s32 type) { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_I32_OP))); CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_ORDERED_F64); - CHECK_ARGUMENT(op == SLJIT_MOV || GET_OPCODE(op) == SLJIT_MOV_U32 || GET_OPCODE(op) == SLJIT_MOV_S32 + CHECK_ARGUMENT((type & 0xff) != GET_FLAG_TYPE(SLJIT_SET_CARRY) && (type & 0xff) != (GET_FLAG_TYPE(SLJIT_SET_CARRY) + 1)); + CHECK_ARGUMENT(op == SLJIT_MOV || op == SLJIT_MOV32 || (GET_OPCODE(op) >= SLJIT_AND && GET_OPCODE(op) <= SLJIT_XOR)); - CHECK_ARGUMENT((op & (SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O | SLJIT_SET_C)) == 0); - CHECK_ARGUMENT((op & (SLJIT_SET_E | SLJIT_KEEP_FLAGS)) != (SLJIT_SET_E | SLJIT_KEEP_FLAGS)); - if (GET_OPCODE(op) < SLJIT_ADD) { - CHECK_ARGUMENT(src == SLJIT_UNUSED && srcw == 0); - } else { - CHECK_ARGUMENT(src == dst && srcw == dstw); - } - FUNCTION_CHECK_DST(dst, dstw); + CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)); + + if ((type & 0xff) <= SLJIT_NOT_ZERO) + CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z); + else + CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff) + || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW) + || ((type & 0xff) == SLJIT_MUL_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_MUL_OVERFLOW)); + + FUNCTION_CHECK_DST(dst, dstw, 0); + + if (GET_OPCODE(op) >= SLJIT_ADD) + compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_I32_OP | SLJIT_SET_Z)); #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { - fprintf(compiler->verbose, " flags %s%s%s%s, ", - !(op & SLJIT_SET_E) ? "" : ".e", !(op & SLJIT_KEEP_FLAGS) ? "" : ".k", + fprintf(compiler->verbose, " flags%s %s%s, ", + !(op & SLJIT_SET_Z) ? "" : ".z", GET_OPCODE(op) < SLJIT_OP2_BASE ? "mov" : op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], GET_OPCODE(op) < SLJIT_OP2_BASE ? op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE] : ((op & SLJIT_I32_OP) ? "32" : "")); sljit_verbose_param(compiler, dst, dstw); - if (src != SLJIT_UNUSED) { - fprintf(compiler->verbose, ", "); - sljit_verbose_param(compiler, src, srcw); - } fprintf(compiler->verbose, ", %s%s\n", jump_names[type & 0xff], JUMP_POSTFIX(type)); } #endif CHECK_RETURN_OK; } +static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_I32_OP))); + CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_ORDERED_F64); + + CHECK_ARGUMENT(compiler->scratches != -1 && compiler->saveds != -1); + CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(dst_reg & ~SLJIT_I32_OP)); + if (src != SLJIT_IMM) { + CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(src)); + CHECK_ARGUMENT(srcw == 0); + } + + if ((type & 0xff) <= SLJIT_NOT_ZERO) + CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z); + else + CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff) + || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW) + || ((type & 0xff) == SLJIT_MUL_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_MUL_OVERFLOW)); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " cmov%s %s%s, ", + !(dst_reg & SLJIT_I32_OP) ? "" : "32", + jump_names[type & 0xff], JUMP_POSTFIX(type)); + sljit_verbose_reg(compiler, dst_reg & ~SLJIT_I32_OP); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(compiler, src, srcw); + fprintf(compiler->verbose, "\n"); + } +#endif + CHECK_RETURN_OK; +} + +static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + CHECK_ARGUMENT((type & 0xff) >= SLJIT_MOV && (type & 0xff) <= SLJIT_MOV_P); + CHECK_ARGUMENT(!(type & SLJIT_I32_OP) || ((type & 0xff) != SLJIT_MOV && (type & 0xff) != SLJIT_MOV_U32 && (type & 0xff) != SLJIT_MOV_P)); + CHECK_ARGUMENT((type & SLJIT_MEM_PRE) || (type & SLJIT_MEM_POST)); + CHECK_ARGUMENT((type & (SLJIT_MEM_PRE | SLJIT_MEM_POST)) != (SLJIT_MEM_PRE | SLJIT_MEM_POST)); + CHECK_ARGUMENT((type & ~(0xff | SLJIT_I32_OP | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_PRE | SLJIT_MEM_POST)) == 0); + + FUNCTION_CHECK_SRC_MEM(mem, memw); + CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(reg)); + + CHECK_ARGUMENT((mem & REG_MASK) != SLJIT_UNUSED && (mem & REG_MASK) != reg); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (!(type & SLJIT_MEM_SUPP) && SLJIT_UNLIKELY(!!compiler->verbose)) { + if (sljit_emit_mem(compiler, type | SLJIT_MEM_SUPP, reg, mem, memw) == SLJIT_ERR_UNSUPPORTED) + fprintf(compiler->verbose, " //"); + + fprintf(compiler->verbose, " mem%s.%s%s%s ", + !(type & SLJIT_I32_OP) ? "" : "32", + (type & SLJIT_MEM_STORE) ? "st" : "ld", + op1_names[(type & 0xff) - SLJIT_OP1_BASE], + (type & SLJIT_MEM_PRE) ? ".pre" : ".post"); + sljit_verbose_reg(compiler, reg); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(compiler, mem, memw); + fprintf(compiler->verbose, "\n"); + } +#endif + CHECK_RETURN_OK; +} + +static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw) +{ +#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV_F64); + CHECK_ARGUMENT((type & SLJIT_MEM_PRE) || (type & SLJIT_MEM_POST)); + CHECK_ARGUMENT((type & (SLJIT_MEM_PRE | SLJIT_MEM_POST)) != (SLJIT_MEM_PRE | SLJIT_MEM_POST)); + CHECK_ARGUMENT((type & ~(0xff | SLJIT_I32_OP | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_PRE | SLJIT_MEM_POST)) == 0); + + FUNCTION_CHECK_SRC_MEM(mem, memw); + CHECK_ARGUMENT(FUNCTION_CHECK_IS_FREG(freg)); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (!(type & SLJIT_MEM_SUPP) && SLJIT_UNLIKELY(!!compiler->verbose)) { + if (sljit_emit_fmem(compiler, type | SLJIT_MEM_SUPP, freg, mem, memw) == SLJIT_ERR_UNSUPPORTED) + fprintf(compiler->verbose, " //"); + + fprintf(compiler->verbose, " fmem.%s%s%s ", + (type & SLJIT_MEM_STORE) ? "st" : "ld", + !(type & SLJIT_I32_OP) ? ".f64" : ".f32", + (type & SLJIT_MEM_PRE) ? ".pre" : ".post"); + sljit_verbose_freg(compiler, freg); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(compiler, mem, memw); + fprintf(compiler->verbose, "\n"); + } +#endif + CHECK_RETURN_OK; +} + static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset) { + /* Any offset is allowed. */ SLJIT_UNUSED_ARG(offset); #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - FUNCTION_CHECK_DST(dst, dstw); + FUNCTION_CHECK_DST(dst, dstw, 0); #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { @@ -1483,7 +1892,7 @@ static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_const(struct sljit_compil SLJIT_UNUSED_ARG(init_value); #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - FUNCTION_CHECK_DST(dst, dstw); + FUNCTION_CHECK_DST(dst, dstw, 0); #endif #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) if (SLJIT_UNLIKELY(!!compiler->verbose)) { @@ -1544,6 +1953,44 @@ static SLJIT_INLINE sljit_s32 emit_mov_before_return(struct sljit_compiler *comp return sljit_emit_op1(compiler, op, SLJIT_RETURN_REG, 0, src, srcw); } +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \ + || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \ + || (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) \ + || ((defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) && !(defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1)) + +static SLJIT_INLINE sljit_s32 sljit_emit_cmov_generic(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ + struct sljit_label *label; + struct sljit_jump *jump; + sljit_s32 op = (dst_reg & SLJIT_I32_OP) ? SLJIT_MOV32 : SLJIT_MOV; + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + jump = sljit_emit_jump(compiler, type ^ 0x1); + FAIL_IF(!jump); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + FAIL_IF(sljit_emit_op1(compiler, op, dst_reg & ~SLJIT_I32_OP, 0, src, srcw)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + label = sljit_emit_label(compiler); + FAIL_IF(!label); + sljit_set_label(jump, label); + return SLJIT_SUCCESS; +} + +#endif + /* CPU description section */ #if (defined SLJIT_32BIT_ARCHITECTURE && SLJIT_32BIT_ARCHITECTURE) @@ -1645,6 +2092,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler condition = SLJIT_SIG_GREATER_EQUAL; break; } + type = condition | (type & (SLJIT_I32_OP | SLJIT_REWRITABLE_JUMP)); tmp_src = src1; src1 = src2; @@ -1655,11 +2103,9 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler } if (condition <= SLJIT_NOT_ZERO) - flags = SLJIT_SET_E; - else if (condition <= SLJIT_LESS_EQUAL) - flags = SLJIT_SET_U; + flags = SLJIT_SET_Z; else - flags = SLJIT_SET_S; + flags = condition << VARIABLE_FLAG_SHIFT; #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) @@ -1671,39 +2117,76 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) compiler->skip_checks = 1; #endif - return sljit_emit_jump(compiler, condition | (type & SLJIT_REWRITABLE_JUMP)); + return sljit_emit_jump(compiler, condition | (type & (SLJIT_REWRITABLE_JUMP | SLJIT_I32_OP))); } +#endif + SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { - sljit_s32 flags, condition; - CHECK_ERROR_PTR(); CHECK_PTR(check_sljit_emit_fcmp(compiler, type, src1, src1w, src2, src2w)); - condition = type & 0xff; - flags = (condition <= SLJIT_NOT_EQUAL_F64) ? SLJIT_SET_E : SLJIT_SET_S; - if (type & SLJIT_F32_OP) - flags |= SLJIT_F32_OP; +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + sljit_emit_fop1(compiler, SLJIT_CMP_F64 | ((type & 0xff) << VARIABLE_FLAG_SHIFT) | (type & SLJIT_I32_OP), src1, src1w, src2, src2w); #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) compiler->skip_checks = 1; #endif - sljit_emit_fop1(compiler, SLJIT_CMP_F64 | flags, src1, src1w, src2, src2w); + return sljit_emit_jump(compiler, type); +} -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - return sljit_emit_jump(compiler, condition | (type & SLJIT_REWRITABLE_JUMP)); +#if !(defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) \ + && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \ + && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(reg); + SLJIT_UNUSED_ARG(mem); + SLJIT_UNUSED_ARG(memw); + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + + return SLJIT_ERR_UNSUPPORTED; } #endif -#if !(defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) +#if !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \ + && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(freg); + SLJIT_UNUSED_ARG(mem); + SLJIT_UNUSED_ARG(memw); + + CHECK_ERROR(); + CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw)); + + return SLJIT_ERR_UNSUPPORTED; +} + +#endif + +#if !(defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \ + && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset) { @@ -1716,7 +2199,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *c compiler->skip_checks = 1; #endif if (offset != 0) - return sljit_emit_op2(compiler, SLJIT_ADD | SLJIT_KEEP_FLAGS, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset); + return sljit_emit_op2(compiler, SLJIT_ADD, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset); return sljit_emit_op1(compiler, SLJIT_MOV, dst, dstw, SLJIT_SP, 0); } @@ -1731,23 +2214,30 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) return "unsupported"; } -SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void) +SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allocator_data) { - SLJIT_ASSERT_STOP(); + SLJIT_UNUSED_ARG(allocator_data); + SLJIT_UNREACHABLE(); return NULL; } SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler) { SLJIT_UNUSED_ARG(compiler); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_compiler_memory_error(struct sljit_compiler *compiler) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNREACHABLE(); } SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, sljit_s32 size) { SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(size); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return NULL; } @@ -1756,52 +2246,59 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *comp { SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(verbose); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } #endif SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) { SLJIT_UNUSED_ARG(compiler); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return NULL; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) +{ + SLJIT_UNUSED_ARG(feature_type); + SLJIT_UNREACHABLE(); + return 0; +} + SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code) { SLJIT_UNUSED_ARG(code); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(options); - SLJIT_UNUSED_ARG(args); + SLJIT_UNUSED_ARG(arg_types); SLJIT_UNUSED_ARG(scratches); SLJIT_UNUSED_ARG(saveds); SLJIT_UNUSED_ARG(fscratches); SLJIT_UNUSED_ARG(fsaveds); SLJIT_UNUSED_ARG(local_size); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(options); - SLJIT_UNUSED_ARG(args); + SLJIT_UNUSED_ARG(arg_types); SLJIT_UNUSED_ARG(scratches); SLJIT_UNUSED_ARG(saveds); SLJIT_UNUSED_ARG(fscratches); SLJIT_UNUSED_ARG(fsaveds); SLJIT_UNUSED_ARG(local_size); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } @@ -1811,7 +2308,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp SLJIT_UNUSED_ARG(op); SLJIT_UNUSED_ARG(src); SLJIT_UNUSED_ARG(srcw); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } @@ -1820,7 +2317,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(dst); SLJIT_UNUSED_ARG(dstw); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } @@ -1829,7 +2326,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(src); SLJIT_UNUSED_ARG(srcw); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } @@ -1837,7 +2334,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile { SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(op); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } @@ -1851,7 +2348,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile SLJIT_UNUSED_ARG(dstw); SLJIT_UNUSED_ARG(src); SLJIT_UNUSED_ARG(srcw); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } @@ -1868,13 +2365,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile SLJIT_UNUSED_ARG(src1w); SLJIT_UNUSED_ARG(src2); SLJIT_UNUSED_ARG(src2w); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) { - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return reg; } @@ -1884,14 +2381,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(instruction); SLJIT_UNUSED_ARG(size); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, sljit_s32 current_flags) { - SLJIT_ASSERT_STOP(); - return 0; + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(current_flags); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op, @@ -1904,7 +2401,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil SLJIT_UNUSED_ARG(dstw); SLJIT_UNUSED_ARG(src); SLJIT_UNUSED_ARG(srcw); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } @@ -1921,14 +2418,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil SLJIT_UNUSED_ARG(src1w); SLJIT_UNUSED_ARG(src2); SLJIT_UNUSED_ARG(src2w); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler) { SLJIT_UNUSED_ARG(compiler); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return NULL; } @@ -1936,7 +2433,17 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile { SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(type); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); + return NULL; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(arg_types); + SLJIT_UNREACHABLE(); return NULL; } @@ -1950,7 +2457,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler SLJIT_UNUSED_ARG(src1w); SLJIT_UNUSED_ARG(src2); SLJIT_UNUSED_ARG(src2w); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return NULL; } @@ -1964,7 +2471,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compile SLJIT_UNUSED_ARG(src1w); SLJIT_UNUSED_ARG(src2); SLJIT_UNUSED_ARG(src2w); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return NULL; } @@ -1972,14 +2479,14 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sl { SLJIT_UNUSED_ARG(jump); SLJIT_UNUSED_ARG(label); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target) { SLJIT_UNUSED_ARG(jump); SLJIT_UNUSED_ARG(target); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) @@ -1988,23 +2495,68 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi SLJIT_UNUSED_ARG(type); SLJIT_UNUSED_ARG(src); SLJIT_UNUSED_ARG(srcw); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(arg_types); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw, sljit_s32 type) { SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(op); SLJIT_UNUSED_ARG(dst); SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(type); + SLJIT_UNREACHABLE(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(dst_reg); SLJIT_UNUSED_ARG(src); SLJIT_UNUSED_ARG(srcw); + SLJIT_UNREACHABLE(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw) +{ + SLJIT_UNUSED_ARG(compiler); SLJIT_UNUSED_ARG(type); - SLJIT_ASSERT_STOP(); + SLJIT_UNUSED_ARG(reg); + SLJIT_UNUSED_ARG(mem); + SLJIT_UNUSED_ARG(memw); + SLJIT_UNREACHABLE(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(freg); + SLJIT_UNUSED_ARG(mem); + SLJIT_UNUSED_ARG(memw); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } @@ -2014,7 +2566,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *c SLJIT_UNUSED_ARG(dst); SLJIT_UNUSED_ARG(dstw); SLJIT_UNUSED_ARG(offset); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_ERR_UNSUPPORTED; } @@ -2024,22 +2576,24 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi SLJIT_UNUSED_ARG(dst); SLJIT_UNUSED_ARG(dstw); SLJIT_UNUSED_ARG(initval); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return NULL; } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) { SLJIT_UNUSED_ARG(addr); - SLJIT_UNUSED_ARG(new_addr); - SLJIT_ASSERT_STOP(); + SLJIT_UNUSED_ARG(new_target); + SLJIT_UNUSED_ARG(executable_offset); + SLJIT_UNREACHABLE(); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) { SLJIT_UNUSED_ARG(addr); SLJIT_UNUSED_ARG(new_constant); - SLJIT_ASSERT_STOP(); + SLJIT_UNUSED_ARG(executable_offset); + SLJIT_UNREACHABLE(); } #endif diff --git a/pcre2-10.22/src/sljit/sljitLir.h b/pcre2-10.32/src/sljit/sljitLir.h similarity index 60% rename from pcre2-10.22/src/sljit/sljitLir.h rename to pcre2-10.32/src/sljit/sljitLir.h index df69b8656..e71890cf7 100644 --- a/pcre2-10.22/src/sljit/sljitLir.h +++ b/pcre2-10.32/src/sljit/sljitLir.h @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -99,6 +99,8 @@ of sljitConfigInternal.h */ #define SLJIT_ERR_UNSUPPORTED 4 /* An ivalid argument is passed to any SLJIT function. */ #define SLJIT_ERR_BAD_ARGUMENT 5 +/* Dynamic code modification is not enabled. */ +#define SLJIT_ERR_DYN_CODE_MOD 6 /* --------------------------------------------------------------------- */ /* Registers */ @@ -118,8 +120,8 @@ of sljitConfigInternal.h */ If an architecture provides two scratch and three saved registers, its scratch and saved register sets are the following: - R0 | [S4] | R0 and S4 represent the same physical register - R1 | [S3] | R1 and S3 represent the same physical register + R0 | | R0 is always a scratch register + R1 | | R1 is always a scratch register [R2] | S2 | R2 and S2 represent the same physical register [R3] | S1 | R3 and S1 represent the same physical register [R4] | S0 | R4 and S0 represent the same physical register @@ -127,38 +129,35 @@ of sljitConfigInternal.h */ Note: SLJIT_NUMBER_OF_SCRATCH_REGISTERS would be 2 and SLJIT_NUMBER_OF_SAVED_REGISTERS would be 3 for this architecture. - Note: On all supported architectures SLJIT_NUMBER_OF_REGISTERS >= 10 - and SLJIT_NUMBER_OF_SAVED_REGISTERS >= 5. However, 4 registers + Note: On all supported architectures SLJIT_NUMBER_OF_REGISTERS >= 12 + and SLJIT_NUMBER_OF_SAVED_REGISTERS >= 6. However, 6 registers are virtual on x86-32. See below. - The purpose of this definition is convenience. Although a register - is either scratch register or saved register, SLJIT allows accessing - them from the other set. For example, four registers can be used as - scratch registers and the fifth one as saved register on the architecture - above. Of course the last two scratch registers (R2 and R3) from this - four will be saved on the stack, because they are defined as saved - registers in the application binary interface. Still R2 and R3 can be - used for referencing to these registers instead of S2 and S1, which - makes easier to write platform independent code. Scratch registers - can be saved registers in a similar way, but these extra saved - registers will not be preserved across function calls! Hence the - application must save them on those platforms, where the number of - saved registers is too low. This can be done by copy them onto - the stack and restore them after a function call. + The purpose of this definition is convenience: saved registers can + be used as extra scratch registers. For example four registers can + be specified as scratch registers and the fifth one as saved register + on the CPU above and any user code which requires four scratch + registers can run unmodified. The SLJIT compiler automatically saves + the content of the two extra scratch register on the stack. Scratch + registers can also be preserved by saving their value on the stack + but this needs to be done manually. Note: To emphasize that registers assigned to R2-R4 are saved - registers, they are enclosed by square brackets. S3-S4 - are marked in a similar way. + registers, they are enclosed by square brackets. Note: sljit_emit_enter and sljit_set_context defines whether a register is S or R register. E.g: when 3 scratches and 1 saved is mapped by sljit_emit_enter, the allowed register set will be: R0-R2 and S0. Although S2 is mapped to the same position as R2, it does not - available in the current configuration. Furthermore the R3 (S1) - register does not available as well. + available in the current configuration. Furthermore the S1 register + is not available at all. */ -/* When SLJIT_UNUSED is specified as destination, the result is discarded. */ +/* When SLJIT_UNUSED is specified as the destination of sljit_emit_op1 + or sljit_emit_op2 operations the result is discarded. If no status + flags are set, no instructions are emitted for these operations. Data + prefetch is a special exception, see SLJIT_MOV operation. Other SLJIT + operations do not support SLJIT_UNUSED as a destination operand. */ #define SLJIT_UNUSED 0 /* Scratch registers. */ @@ -214,14 +213,6 @@ of sljitConfigInternal.h */ #define SLJIT_RETURN_REG SLJIT_R0 -/* x86 prefers specific registers for special purposes. In case of shift - by register it supports only SLJIT_R2 for shift argument - (which is the src2 argument of sljit_emit_op2). If another register is - used, sljit must exchange data between registers which cause a minor - slowdown. Other architectures has no such limitation. */ - -#define SLJIT_PREF_SHIFT_REG SLJIT_R2 - /* --------------------------------------------------------------------- */ /* Floating point registers */ /* --------------------------------------------------------------------- */ @@ -258,6 +249,79 @@ of sljitConfigInternal.h */ /* Float registers >= SLJIT_FIRST_SAVED_FLOAT_REG are saved registers. */ #define SLJIT_FIRST_SAVED_FLOAT_REG (SLJIT_FS0 - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS + 1) +/* --------------------------------------------------------------------- */ +/* Argument type definitions */ +/* --------------------------------------------------------------------- */ + +/* Argument type definitions. + Used by SLJIT_[DEF_]ARGx and SLJIT_[DEF]_RET macros. */ + +#define SLJIT_ARG_TYPE_VOID 0 +#define SLJIT_ARG_TYPE_SW 1 +#define SLJIT_ARG_TYPE_UW 2 +#define SLJIT_ARG_TYPE_S32 3 +#define SLJIT_ARG_TYPE_U32 4 +#define SLJIT_ARG_TYPE_F32 5 +#define SLJIT_ARG_TYPE_F64 6 + +/* The following argument type definitions are used by sljit_emit_enter, + sljit_set_context, sljit_emit_call, and sljit_emit_icall functions. + The following return type definitions are used by sljit_emit_call + and sljit_emit_icall functions. + + When a function is called, the first integer argument must be placed + in SLJIT_R0, the second in SLJIT_R1, and so on. Similarly the first + floating point argument must be placed in SLJIT_FR0, the second in + SLJIT_FR1, and so on. + + Example function definition: + sljit_f32 SLJIT_FUNC example_c_callback(sljit_sw arg_a, + sljit_f64 arg_b, sljit_u32 arg_c, sljit_f32 arg_d); + + Argument type definition: + SLJIT_DEF_RET(SLJIT_ARG_TYPE_F32) + | SLJIT_DEF_ARG1(SLJIT_ARG_TYPE_SW) | SLJIT_DEF_ARG2(SLJIT_ARG_TYPE_F64) + | SLJIT_DEF_ARG3(SLJIT_ARG_TYPE_U32) | SLJIT_DEF_ARG2(SLJIT_ARG_TYPE_F32) + + Short form of argument type definition: + SLJIT_RET(F32) | SLJIT_ARG1(SW) | SLJIT_ARG2(F64) + | SLJIT_ARG3(S32) | SLJIT_ARG4(F32) + + Argument passing: + arg_a must be placed in SLJIT_R0 + arg_c must be placed in SLJIT_R1 + arg_b must be placed in SLJIT_FR0 + arg_d must be placed in SLJIT_FR1 + +Note: + The SLJIT_ARG_TYPE_VOID type is only supported by + SLJIT_DEF_RET, and SLJIT_ARG_TYPE_VOID is also the + default value when SLJIT_DEF_RET is not specified. */ +#define SLJIT_DEF_SHIFT 4 +#define SLJIT_DEF_RET(type) (type) +#define SLJIT_DEF_ARG1(type) ((type) << SLJIT_DEF_SHIFT) +#define SLJIT_DEF_ARG2(type) ((type) << (2 * SLJIT_DEF_SHIFT)) +#define SLJIT_DEF_ARG3(type) ((type) << (3 * SLJIT_DEF_SHIFT)) +#define SLJIT_DEF_ARG4(type) ((type) << (4 * SLJIT_DEF_SHIFT)) + +/* Short form of the macros above. + + For example the following definition: + SLJIT_DEF_RET(SLJIT_ARG_TYPE_SW) | SLJIT_DEF_ARG1(SLJIT_ARG_TYPE_F32) + + can be shortened to: + SLJIT_RET(SW) | SLJIT_ARG1(F32) + +Note: + The VOID type is only supported by SLJIT_RET, and + VOID is also the default value when SLJIT_RET is + not specified. */ +#define SLJIT_RET(type) SLJIT_DEF_RET(SLJIT_ARG_TYPE_ ## type) +#define SLJIT_ARG1(type) SLJIT_DEF_ARG1(SLJIT_ARG_TYPE_ ## type) +#define SLJIT_ARG2(type) SLJIT_DEF_ARG2(SLJIT_ARG_TYPE_ ## type) +#define SLJIT_ARG3(type) SLJIT_DEF_ARG3(SLJIT_ARG_TYPE_ ## type) +#define SLJIT_ARG4(type) SLJIT_DEF_ARG4(SLJIT_ARG_TYPE_ ## type) + /* --------------------------------------------------------------------- */ /* Main structures and functions */ /* --------------------------------------------------------------------- */ @@ -323,19 +387,23 @@ struct sljit_compiler { sljit_s32 local_size; /* Code size. */ sljit_uw size; - /* For statistical purposes. */ + /* Relative offset of the executable mapping from the writable mapping. */ + sljit_uw executable_offset; + /* Executable size for statistical purposes. */ sljit_uw executable_size; #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) sljit_s32 args; + sljit_s32 locals_offset; + sljit_s32 saveds_offset; + sljit_s32 stack_tmp_size; #endif #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) sljit_s32 mode32; +#ifdef _WIN64 + sljit_s32 locals_offset; #endif - -#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) - sljit_s32 flags_saved; #endif #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) @@ -352,24 +420,10 @@ struct sljit_compiler { #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) /* Temporary fields. */ sljit_uw shift_imm; - sljit_s32 cache_arg; - sljit_sw cache_argw; -#endif - -#if (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) - sljit_s32 cache_arg; - sljit_sw cache_argw; -#endif - -#if (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) - sljit_s32 cache_arg; - sljit_sw cache_argw; #endif #if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) sljit_sw imm; - sljit_s32 cache_arg; - sljit_sw cache_argw; #endif #if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) @@ -395,6 +449,9 @@ struct sljit_compiler { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ || (defined SLJIT_DEBUG && SLJIT_DEBUG) + /* Flags specified by the last arithmetic instruction. + It contains the type of the variable flag. */ + sljit_s32 last_flags; /* Local size passed to the functions. */ sljit_s32 logical_local_size; #endif @@ -402,6 +459,7 @@ struct sljit_compiler { #if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \ || (defined SLJIT_DEBUG && SLJIT_DEBUG) \ || (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + /* Trust arguments when the API function is called. */ sljit_s32 skip_checks; #endif }; @@ -455,44 +513,89 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compile SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose); #endif +/* + Create executable code from the sljit instruction stream. This is the final step + of the code generation so no more instructions can be added after this call. +*/ + SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler); + +/* Free executable code. */ + SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code); /* - After the machine code generation is finished we can retrieve the allocated - executable memory size, although this area may not be fully filled with - instructions depending on some optimizations. This function is useful only - for statistical purposes. + When the protected executable allocator is used the JIT code is mapped + twice. The first mapping has read/write and the second mapping has read/exec + permissions. This function returns with the relative offset of the executable + mapping using the writable mapping as the base after the machine code is + successfully generated. The returned value is always 0 for the normal executable + allocator, since it uses only one mapping with read/write/exec permissions. + Dynamic code modifications requires this value. + + Before a successful code generation, this function returns with 0. +*/ +static SLJIT_INLINE sljit_sw sljit_get_executable_offset(struct sljit_compiler *compiler) { return compiler->executable_offset; } + +/* + The executable memory consumption of the generated code can be retrieved by + this function. The returned value can be used for statistical purposes. Before a successful code generation, this function returns with 0. */ static SLJIT_INLINE sljit_uw sljit_get_generated_code_size(struct sljit_compiler *compiler) { return compiler->executable_size; } +/* Returns with non-zero if the feature or limitation type passed as its + argument is present on the current CPU. + + Some features (e.g. floating point operations) require hardware (CPU) + support while others (e.g. move with update) are emulated if not available. + However even if a feature is emulated, specialized code paths can be faster + than the emulation. Some limitations are emulated as well so their general + case is supported but it has extra performance costs. */ + +/* [Not emulated] Floating-point support is available. */ +#define SLJIT_HAS_FPU 0 +/* [Limitation] Some registers are virtual registers. */ +#define SLJIT_HAS_VIRTUAL_REGISTERS 1 +/* [Emulated] Count leading zero is supported. */ +#define SLJIT_HAS_CLZ 2 +/* [Emulated] Conditional move is supported. */ +#define SLJIT_HAS_CMOV 3 + +#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) +/* [Not emulated] SSE2 support is available on x86. */ +#define SLJIT_HAS_SSE2 100 +#endif + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type); + /* Instruction generation. Returns with any error code. If there is no error, they return with SLJIT_SUCCESS. */ /* - The executable code is a function call from the viewpoint of the C + The executable code is a function from the viewpoint of the C language. The function calls must obey to the ABI (Application Binary Interface) of the platform, which specify the purpose of - all machine registers and stack handling among other things. The + machine registers and stack handling among other things. The sljit_emit_enter function emits the necessary instructions for setting up a new context for the executable code and moves function arguments to the saved registers. Furthermore the options argument can be used to pass configuration options to the compiler. The available options are listed before sljit_emit_enter. - The number of sljit_sw arguments passed to the generated function - are specified in the "args" parameter. The number of arguments must - be less than or equal to 3. The first argument goes to SLJIT_S0, - the second goes to SLJIT_S1 and so on. The register set used by - the function must be declared as well. The number of scratch and - saved registers used by the function must be passed to sljit_emit_enter. - Only R registers between R0 and "scratches" argument can be used - later. E.g. if "scratches" is set to 2, the register set will be - limited to R0 and R1. The S registers and the floating point + The function argument list is the combination of SLJIT_ARGx + (SLJIT_DEF_ARG1) macros. Currently maximum 3 SW / UW + (SLJIT_ARG_TYPE_SW / LJIT_ARG_TYPE_UW) arguments are supported. + The first argument goes to SLJIT_S0, the second goes to SLJIT_S1 + and so on. The register set used by the function must be declared + as well. The number of scratch and saved registers used by the + function must be passed to sljit_emit_enter. Only R registers + between R0 and "scratches" argument can be used later. E.g. if + "scratches" is set to 2, the scratch register set will be limited + to SLJIT_R0 and SLJIT_R1. The S registers and the floating point registers ("fscratches" and "fsaveds") are specified in a similar - way. The sljit_emit_enter is also capable of allocating a stack + manner. The sljit_emit_enter is also capable of allocating a stack space for local variables. The "local_size" argument contains the size in bytes of this local area and its staring address is stored in SLJIT_SP. The memory area between SLJIT_SP (inclusive) and @@ -512,14 +615,14 @@ static SLJIT_INLINE sljit_uw sljit_get_generated_code_size(struct sljit_compiler */ /* The absolute address returned by sljit_get_local_base with -offset 0 is aligned to sljit_d. Otherwise it is aligned to sljit_uw. */ -#define SLJIT_DOUBLE_ALIGNMENT 0x00000001 +offset 0 is aligned to sljit_f64. Otherwise it is aligned to sljit_sw. */ +#define SLJIT_F64_ALIGNMENT 0x00000001 /* The local_size must be >= 0 and <= SLJIT_MAX_LOCAL_SIZE. */ #define SLJIT_MAX_LOCAL_SIZE 65536 SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size); /* The machine code has a context (which contains the local stack space size, @@ -533,7 +636,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi the previous context. */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size); /* Return from machine code. The op argument can be SLJIT_UNUSED which means the @@ -545,26 +648,31 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw); -/* Fast calling mechanism for utility functions (see SLJIT_FAST_CALL). All registers and - even the stack frame is passed to the callee. The return address is preserved in - dst/dstw by sljit_emit_fast_enter (the type of the value stored by this function - is sljit_p), and sljit_emit_fast_return can use this as a return value later. */ +/* Generating entry and exit points for fast call functions (see SLJIT_FAST_CALL). + Both sljit_emit_fast_enter and sljit_emit_fast_return functions preserve the + values of all registers and stack frame. The return address is stored in the + dst argument of sljit_emit_fast_enter, and this return address can be passed + to sljit_emit_fast_return to continue the execution after the fast call. -/* Note: only for sljit specific, non ABI compilant calls. Fast, since only a few machine - instructions are needed. Excellent for small uility functions, where saving registers - and setting up a new stack frame would cost too much performance. However, it is still - possible to return to the address of the caller (or anywhere else). */ + Fast calls are cheap operations (usually only a single call instruction is + emitted) but they do not preserve any registers. However the callee function + can freely use / update any registers and stack values which can be + efficiently exploited by various optimizations. Registers can be saved + manually by the callee function if needed. -/* Note: flags are not changed (unlike sljit_emit_enter / sljit_emit_return). */ + Although returning to different address by sljit_emit_fast_return is possible, + this address usually cannot be predicted by the return address predictor of + modern CPUs which may reduce performance. Furthermore using sljit_emit_ijump + to return is also inefficient since return address prediction is usually + triggered by a specific form of ijump. -/* Note: although sljit_emit_fast_return could be replaced by an ijump, it is not suggested, - since many architectures do clever branch prediction on call / return instruction pairs. */ + Flags: - (does not modify flags). */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw); SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw); /* - Source and destination values for arithmetical instructions + Source and destination operands for arithmetical instructions imm - a simple immediate value (cannot be used as a destination) reg - any of the registers (immediate argument must be 0) [imm] - absolute immediate memory address @@ -605,6 +713,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler arm-t2: [reg+imm], -255 <= imm <= 4095 [reg+(reg<addr; } static SLJIT_INLINE sljit_uw sljit_get_const_addr(struct sljit_const *const_) { return const_->addr; } -/* Only the address is required to rewrite the code. */ -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr); -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant); +/* Only the address and executable offset are required to perform dynamic + code modifications. See sljit_get_executable_offset function. */ +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset); +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset); /* --------------------------------------------------------------------- */ /* Miscellaneous utility functions */ /* --------------------------------------------------------------------- */ #define SLJIT_MAJOR_VERSION 0 -#define SLJIT_MINOR_VERSION 93 +#define SLJIT_MINOR_VERSION 94 /* Get the human readable name of the platform. Can be useful on platforms like ARM, where ARM and Thumb2 functions can be mixed, and @@ -1107,53 +1347,58 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void); #if (defined SLJIT_UTIL_GLOBAL_LOCK && SLJIT_UTIL_GLOBAL_LOCK) /* This global lock is useful to compile common functions. */ -SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void); -SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void); +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_grab_lock(void); +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_release_lock(void); #endif #if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) -/* The sljit_stack is a utiliy feature of sljit, which allocates a - writable memory region between base (inclusive) and limit (exclusive). - Both base and limit is a pointer, and base is always <= than limit. - This feature uses the "address space reserve" feature - of modern operating systems. Basically we don't need to allocate a - huge memory block in one step for the worst case, we can start with - a smaller chunk and extend it later. Since the address space is - reserved, the data never copied to other regions, thus it is safe - to store pointers here. */ +/* The sljit_stack structure and its manipulation functions provides + an implementation for a top-down stack. The stack top is stored + in the end field of the sljit_stack structure and the stack goes + down to the min_start field, so the memory region reserved for + this stack is between min_start (inclusive) and end (exclusive) + fields. However the application can only use the region between + start (inclusive) and end (exclusive) fields. The sljit_stack_resize + function can be used to extend this region up to min_start. -/* Note: The base field is aligned to PAGE_SIZE bytes (usually 4k or more). - Note: stack growing should not happen in small steps: 4k, 16k or even - bigger growth is better. - Note: this structure may not be supported by all operating systems. - Some kind of fallback mechanism is suggested when SLJIT_UTIL_STACK - is not defined. */ + This feature uses the "address space reserve" feature of modern + operating systems. Instead of allocating a large memory block + applications can allocate a small memory region and extend it + later without moving the content of the memory area. Therefore + after a successful resize by sljit_stack_resize all pointers into + this region are still valid. + + Note: + this structure may not be supported by all operating systems. + end and max_limit fields are aligned to PAGE_SIZE bytes (usually + 4 Kbyte or more). + stack should grow in larger steps, e.g. 4Kbyte, 16Kbyte or more. */ struct sljit_stack { /* User data, anything can be stored here. - Starting with the same value as base. */ - sljit_uw top; - /* These members are read only. */ - sljit_uw base; - sljit_uw limit; - sljit_uw max_limit; + Initialized to the same value as the end field. */ + sljit_u8 *top; +/* These members are read only. */ + /* End address of the stack */ + sljit_u8 *end; + /* Current start address of the stack. */ + sljit_u8 *start; + /* Lowest start address of the stack. */ + sljit_u8 *min_start; }; -/* Returns NULL if unsuccessful. - Note: limit and max_limit contains the size for stack allocation. - Note: the top field is initialized to base. +/* Allocates a new stack. Returns NULL if unsuccessful. Note: see sljit_create_compiler for the explanation of allocator_data. */ -SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_CALL sljit_allocate_stack(sljit_uw limit, sljit_uw max_limit, void *allocator_data); -SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_free_stack(struct sljit_stack *stack, void *allocator_data); +SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data); +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data); -/* Can be used to increase (allocate) or decrease (free) the memory area. - Returns with a non-zero value if unsuccessful. If new_limit is greater than - max_limit, it will fail. It is very easy to implement a stack data structure, - since the growth ratio can be added to the current limit, and sljit_stack_resize - will do all the necessary checks. The fields of the stack are not changed if - sljit_stack_resize fails. */ -SLJIT_API_FUNC_ATTRIBUTE sljit_sw SLJIT_CALL sljit_stack_resize(struct sljit_stack *stack, sljit_uw new_limit); +/* Can be used to increase (extend) or decrease (shrink) the stack + memory area. Returns with new_start if successful and NULL otherwise. + It always fails if new_start is less than min_start or greater or equal + than end fields. The fields of the stack are not changed if the returned + value is NULL (the current memory content is never lost). */ +SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start); #endif /* (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) */ @@ -1182,6 +1427,15 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct #endif /* !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) */ +#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) +/* Free unused executable memory. The allocator keeps some free memory + around to reduce the number of OS executable memory allocations. + This improves performance since these calls are costly. However + it is sometimes desired to free all unused memory regions, e.g. + before the application terminates. */ +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void); +#endif + /* --------------------------------------------------------------------- */ /* CPU specific functions */ /* --------------------------------------------------------------------- */ @@ -1214,32 +1468,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, void *instruction, sljit_s32 size); -#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) +/* Define the currently available CPU status flags. It is usually used after an + sljit_emit_op_custom call to define which flags are set. */ -/* Returns with non-zero if sse2 is available. */ - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_x86_is_sse2_available(void); - -/* Returns with non-zero if cmov instruction is available. */ - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 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_I32_OP to perform 32 bit arithmetic - Flags: I - (never set any flags) - */ - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_x86_emit_cmov(struct sljit_compiler *compiler, - sljit_s32 type, - sljit_s32 dst_reg, - sljit_s32 src, sljit_sw srcw); - -#endif +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, + sljit_s32 current_flags); #endif /* _SLJIT_LIR_H_ */ diff --git a/pcre2-10.22/src/sljit/sljitNativeARM_32.c b/pcre2-10.32/src/sljit/sljitNativeARM_32.c similarity index 61% rename from pcre2-10.22/src/sljit/sljitNativeARM_32.c rename to pcre2-10.32/src/sljit/sljitNativeARM_32.c index b92808f52..6d61eed9a 100644 --- a/pcre2-10.22/src/sljit/sljitNativeARM_32.c +++ b/pcre2-10.32/src/sljit/sljitNativeARM_32.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -24,12 +24,18 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifdef __SOFTFP__ +#define ARM_ABI_INFO " ABI:softfp" +#else +#define ARM_ABI_INFO " ABI:hardfp" +#endif + SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) { #if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) - return "ARMv7" SLJIT_CPUINFO; + return "ARMv7" SLJIT_CPUINFO ARM_ABI_INFO; #elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) - return "ARMv5" SLJIT_CPUINFO; + return "ARMv5" SLJIT_CPUINFO ARM_ABI_INFO; #else #error "Internal error: Unknown ARM architecture" #endif @@ -38,11 +44,10 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) /* Last register + 1. */ #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 TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 5) +#define TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 4) -#define TMP_FREG1 (0) -#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2) /* In ARM instruction words. Cache lines are usually 32 byte aligned. */ @@ -55,8 +60,12 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) (((max_diff) / (sljit_s32)sizeof(sljit_uw)) - (CONST_POOL_ALIGNMENT - 1)) /* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */ -static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { - 0, 0, 1, 2, 11, 10, 9, 8, 7, 6, 5, 4, 13, 3, 12, 14, 15 +static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = { + 0, 0, 1, 2, 3, 11, 10, 9, 8, 7, 6, 5, 4, 13, 12, 14, 15 +}; + +static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { + 0, 0, 1, 2, 3, 4, 5, 6, 7 }; #define RM(rm) (reg_map[rm]) @@ -73,31 +82,31 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { #define CONDITIONAL 0xe0000000 #define PUSH_POOL 0xff000000 -/* DP - Data Processing instruction (use with EMIT_DATA_PROCESS_INS). */ -#define ADC_DP 0x5 -#define ADD_DP 0x4 -#define AND_DP 0x0 +#define ADC 0xe0a00000 +#define ADD 0xe0800000 +#define AND 0xe0000000 #define B 0xea000000 -#define BIC_DP 0xe +#define BIC 0xe1c00000 #define BL 0xeb000000 #define BLX 0xe12fff30 #define BX 0xe12fff10 #define CLZ 0xe16f0f10 -#define CMP_DP 0xa +#define CMN 0xe1600000 +#define CMP 0xe1400000 #define BKPT 0xe1200070 -#define EOR_DP 0x1 -#define MOV_DP 0xd +#define EOR 0xe0200000 +#define MOV 0xe1a00000 #define MUL 0xe0000090 -#define MVN_DP 0xf +#define MVN 0xe1e00000 #define NOP 0xe1a00000 -#define ORR_DP 0xc +#define ORR 0xe1800000 #define PUSH 0xe92d0000 #define POP 0xe8bd0000 -#define RSB_DP 0x3 -#define RSC_DP 0x7 -#define SBC_DP 0x6 +#define RSB 0xe0600000 +#define RSC 0xe0e00000 +#define SBC 0xe0c00000 #define SMULL 0xe0c00090 -#define SUB_DP 0x2 +#define SUB 0xe0400000 #define UMULL 0xe0800090 #define VABS_F32 0xeeb00ac0 #define VADD_F32 0xee300a00 @@ -108,6 +117,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { #define VDIV_F32 0xee800a00 #define VMOV_F32 0xeeb00a40 #define VMOV 0xee000a10 +#define VMOV2 0xec400a10 #define VMRS 0xeef1fa10 #define VMUL_F32 0xee200a00 #define VNEG_F32 0xeeb10a40 @@ -260,6 +270,8 @@ static SLJIT_INLINE sljit_s32 emit_blx(struct sljit_compiler *compiler) { /* Must follow tightly the previous instruction (to be able to convert it to bl instruction). */ SLJIT_ASSERT(compiler->cpool_diff == CONST_POOL_EMPTY || compiler->size - compiler->cpool_diff < MAX_DIFFERENCE(4092)); + SLJIT_ASSERT(reg_map[TMP_REG1] != 14); + return push_inst(compiler, BLX | RM(TMP_REG1)); } @@ -389,7 +401,7 @@ static SLJIT_INLINE sljit_s32 emit_imm(struct sljit_compiler *compiler, sljit_s3 #endif -static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_uw *code_ptr, sljit_uw *code) +static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_uw *code_ptr, sljit_uw *code, sljit_sw executable_offset) { sljit_sw diff; @@ -401,7 +413,7 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_uw code_ptr--; if (jump->flags & JUMP_ADDR) - diff = ((sljit_sw)jump->u.target - (sljit_sw)(code_ptr + 2)); + diff = ((sljit_sw)jump->u.target - (sljit_sw)(code_ptr + 2) - executable_offset); else { SLJIT_ASSERT(jump->flags & JUMP_LABEL); diff = ((sljit_sw)(code + jump->u.label->size) - (sljit_sw)(code_ptr + 2)); @@ -426,7 +438,7 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_uw } #else if (jump->flags & JUMP_ADDR) - diff = ((sljit_sw)jump->u.target - (sljit_sw)code_ptr); + diff = ((sljit_sw)jump->u.target - (sljit_sw)code_ptr - executable_offset); else { SLJIT_ASSERT(jump->flags & JUMP_LABEL); diff = ((sljit_sw)(code + jump->u.label->size) - (sljit_sw)code_ptr); @@ -446,26 +458,28 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_uw return 0; } -static SLJIT_INLINE void inline_set_jump_addr(sljit_uw addr, sljit_uw new_addr, sljit_s32 flush) +static SLJIT_INLINE void inline_set_jump_addr(sljit_uw jump_ptr, sljit_sw executable_offset, sljit_uw new_addr, sljit_s32 flush_cache) { #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) - sljit_uw *ptr = (sljit_uw*)addr; - sljit_uw *inst = (sljit_uw*)ptr[0]; + sljit_uw *ptr = (sljit_uw *)jump_ptr; + sljit_uw *inst = (sljit_uw *)ptr[0]; sljit_uw mov_pc = ptr[1]; sljit_s32 bl = (mov_pc & 0x0000f000) != RD(TMP_PC); - sljit_sw diff = (sljit_sw)(((sljit_sw)new_addr - (sljit_sw)(inst + 2)) >> 2); + sljit_sw diff = (sljit_sw)(((sljit_sw)new_addr - (sljit_sw)(inst + 2) - executable_offset) >> 2); if (diff <= 0x7fffff && diff >= -0x800000) { /* Turn to branch. */ if (!bl) { inst[0] = (mov_pc & COND_MASK) | (B - CONDITIONAL) | (diff & 0xffffff); - if (flush) { + if (flush_cache) { + inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 1); } } else { inst[0] = (mov_pc & COND_MASK) | (BL - CONDITIONAL) | (diff & 0xffffff); inst[1] = NOP; - if (flush) { + if (flush_cache) { + inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 2); } } @@ -479,12 +493,14 @@ static SLJIT_INLINE void inline_set_jump_addr(sljit_uw addr, sljit_uw new_addr, if (*inst != mov_pc) { inst[0] = mov_pc; if (!bl) { - if (flush) { + if (flush_cache) { + inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 1); } } else { inst[1] = BLX | RM(TMP_REG1); - if (flush) { + if (flush_cache) { + inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 2); } } @@ -492,11 +508,12 @@ static SLJIT_INLINE void inline_set_jump_addr(sljit_uw addr, sljit_uw new_addr, *ptr = new_addr; } #else - sljit_uw *inst = (sljit_uw*)addr; + sljit_uw *inst = (sljit_uw*)jump_ptr; SLJIT_ASSERT((inst[0] & 0xfff00000) == MOVW && (inst[1] & 0xfff00000) == MOVT); inst[0] = MOVW | (inst[0] & 0xf000) | ((new_addr << 4) & 0xf0000) | (new_addr & 0xfff); inst[1] = MOVT | (inst[1] & 0xf000) | ((new_addr >> 12) & 0xf0000) | ((new_addr >> 16) & 0xfff); - if (flush) { + if (flush_cache) { + inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 2); } #endif @@ -504,7 +521,7 @@ static SLJIT_INLINE void inline_set_jump_addr(sljit_uw addr, sljit_uw new_addr, static sljit_uw get_imm(sljit_uw imm); -static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_sw new_constant, sljit_s32 flush) +static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_sw executable_offset, sljit_sw new_constant, sljit_s32 flush_cache) { #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) sljit_uw *ptr = (sljit_uw*)addr; @@ -515,7 +532,8 @@ static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_sw new_constant, src2 = get_imm(new_constant); if (src2) { *inst = 0xe3a00000 | (ldr_literal & 0xf000) | src2; - if (flush) { + if (flush_cache) { + inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 1); } return; @@ -524,7 +542,8 @@ static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_sw new_constant, src2 = get_imm(~new_constant); if (src2) { *inst = 0xe3e00000 | (ldr_literal & 0xf000) | src2; - if (flush) { + if (flush_cache) { + inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 1); } return; @@ -537,7 +556,8 @@ static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_sw new_constant, if (*inst != ldr_literal) { *inst = ldr_literal; - if (flush) { + if (flush_cache) { + inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 1); } } @@ -547,7 +567,8 @@ static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_sw new_constant, SLJIT_ASSERT((inst[0] & 0xfff00000) == MOVW && (inst[1] & 0xfff00000) == MOVT); inst[0] = MOVW | (inst[0] & 0xf000) | ((new_constant << 4) & 0xf0000) | (new_constant & 0xfff); inst[1] = MOVT | (inst[1] & 0xf000) | ((new_constant >> 12) & 0xf0000) | ((new_constant >> 16) & 0xfff); - if (flush) { + if (flush_cache) { + inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 2); } #endif @@ -562,6 +583,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil sljit_uw *buf_end; sljit_uw size; sljit_uw word_count; + sljit_sw executable_offset; + sljit_sw jump_addr; #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) sljit_uw cpool_size; sljit_uw cpool_skip_alignment; @@ -602,14 +625,14 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil code_ptr = code; word_count = 0; + executable_offset = SLJIT_EXEC_OFFSET(code); label = compiler->labels; jump = compiler->jumps; const_ = compiler->consts; if (label && label->size == 0) { - label->addr = (sljit_uw)code; - label->size = 0; + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code, executable_offset); label = label->next; } @@ -636,7 +659,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil cpool_size = 0; if (label && label->size == word_count) { /* Points after the current instruction. */ - label->addr = (sljit_uw)code_ptr; + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); label->size = code_ptr - code; label = label->next; } @@ -652,19 +675,19 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil SLJIT_ASSERT(!const_ || const_->addr >= word_count); if (jump && jump->addr == word_count) { #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) - if (detect_jump_type(jump, code_ptr, code)) + if (detect_jump_type(jump, code_ptr, code, executable_offset)) code_ptr--; jump->addr = (sljit_uw)code_ptr; #else jump->addr = (sljit_uw)(code_ptr - 2); - if (detect_jump_type(jump, code_ptr, code)) + if (detect_jump_type(jump, code_ptr, code, executable_offset)) code_ptr -= 2; #endif jump = jump->next; } if (label && label->size == word_count) { /* code_ptr can be affected above. */ - label->addr = (sljit_uw)(code_ptr + 1); + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr + 1, executable_offset); label->size = (code_ptr + 1) - code; label = label->next; } @@ -729,17 +752,18 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil jump = compiler->jumps; while (jump) { - buf_ptr = (sljit_uw*)jump->addr; + buf_ptr = (sljit_uw *)jump->addr; if (jump->flags & PATCH_B) { + jump_addr = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(buf_ptr + 2, executable_offset); if (!(jump->flags & JUMP_ADDR)) { SLJIT_ASSERT(jump->flags & JUMP_LABEL); - SLJIT_ASSERT(((sljit_sw)jump->u.label->addr - (sljit_sw)(buf_ptr + 2)) <= 0x01ffffff && ((sljit_sw)jump->u.label->addr - (sljit_sw)(buf_ptr + 2)) >= -0x02000000); - *buf_ptr |= (((sljit_sw)jump->u.label->addr - (sljit_sw)(buf_ptr + 2)) >> 2) & 0x00ffffff; + SLJIT_ASSERT(((sljit_sw)jump->u.label->addr - jump_addr) <= 0x01ffffff && ((sljit_sw)jump->u.label->addr - jump_addr) >= -0x02000000); + *buf_ptr |= (((sljit_sw)jump->u.label->addr - jump_addr) >> 2) & 0x00ffffff; } else { - SLJIT_ASSERT(((sljit_sw)jump->u.target - (sljit_sw)(buf_ptr + 2)) <= 0x01ffffff && ((sljit_sw)jump->u.target - (sljit_sw)(buf_ptr + 2)) >= -0x02000000); - *buf_ptr |= (((sljit_sw)jump->u.target - (sljit_sw)(buf_ptr + 2)) >> 2) & 0x00ffffff; + SLJIT_ASSERT(((sljit_sw)jump->u.target - jump_addr) <= 0x01ffffff && ((sljit_sw)jump->u.target - jump_addr) >= -0x02000000); + *buf_ptr |= (((sljit_sw)jump->u.target - jump_addr) >> 2) & 0x00ffffff; } } else if (jump->flags & SLJIT_REWRITABLE_JUMP) { @@ -747,10 +771,10 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil jump->addr = (sljit_uw)code_ptr; code_ptr[0] = (sljit_uw)buf_ptr; code_ptr[1] = *buf_ptr; - inline_set_jump_addr((sljit_uw)code_ptr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0); + inline_set_jump_addr((sljit_uw)code_ptr, executable_offset, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0); code_ptr += 2; #else - inline_set_jump_addr((sljit_uw)buf_ptr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0); + inline_set_jump_addr((sljit_uw)buf_ptr, executable_offset, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0); #endif } else { @@ -763,7 +787,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil buf_ptr += 1; *buf_ptr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; #else - inline_set_jump_addr((sljit_uw)buf_ptr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0); + inline_set_jump_addr((sljit_uw)buf_ptr, executable_offset, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0); #endif } jump = jump->next; @@ -782,7 +806,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil else buf_ptr += 1; /* Set the value again (can be a simple constant). */ - inline_set_const((sljit_uw)code_ptr, *buf_ptr, 0); + inline_set_const((sljit_uw)code_ptr, executable_offset, *buf_ptr, 0); code_ptr += 2; const_ = const_->next; @@ -792,33 +816,87 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil SLJIT_ASSERT(code_ptr - code <= (sljit_s32)size); compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_offset = executable_offset; compiler->executable_size = (code_ptr - code) * sizeof(sljit_uw); + + code = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset); + code_ptr = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); + SLJIT_CACHE_FLUSH(code, code_ptr); return code; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) +{ + switch (feature_type) { + case SLJIT_HAS_FPU: +#ifdef SLJIT_IS_FPU_AVAILABLE + return SLJIT_IS_FPU_AVAILABLE; +#else + /* Available by default. */ + return 1; +#endif + + case SLJIT_HAS_CLZ: + case SLJIT_HAS_CMOV: + return 1; + + default: + return 0; + } +} + /* --------------------------------------------------------------------- */ /* Entry, exit */ /* --------------------------------------------------------------------- */ -/* emit_op inp_flags. - WRITE_BACK must be the first, since it is a flag. */ -#define WRITE_BACK 0x01 -#define ALLOW_IMM 0x02 -#define ALLOW_INV_IMM 0x04 -#define ALLOW_ANY_IMM (ALLOW_IMM | ALLOW_INV_IMM) -#define ARG_TEST 0x08 - /* Creates an index in data_transfer_insts array. */ -#define WORD_DATA 0x00 -#define BYTE_DATA 0x10 -#define HALF_DATA 0x20 -#define SIGNED_DATA 0x40 -#define LOAD_DATA 0x80 +#define WORD_SIZE 0x00 +#define BYTE_SIZE 0x01 +#define HALF_SIZE 0x02 +#define PRELOAD 0x03 +#define SIGNED 0x04 +#define LOAD_DATA 0x08 -/* Condition: AL. */ -#define EMIT_DATA_PROCESS_INS(opcode, set_flags, dst, src1, src2) \ - (0xe0000000 | ((opcode) << 21) | (set_flags) | RD(dst) | RN(src1) | (src2)) +/* Flag bits for emit_op. */ +#define ALLOW_IMM 0x10 +#define ALLOW_INV_IMM 0x20 +#define ALLOW_ANY_IMM (ALLOW_IMM | ALLOW_INV_IMM) + +/* s/l - store/load (1 bit) + u/s - signed/unsigned (1 bit) + w/b/h/N - word/byte/half/NOT allowed (2 bit) + Storing signed and unsigned values are the same operations. */ + +static const sljit_uw data_transfer_insts[16] = { +/* s u w */ 0xe5000000 /* str */, +/* s u b */ 0xe5400000 /* strb */, +/* s u h */ 0xe10000b0 /* strh */, +/* s u N */ 0x00000000 /* not allowed */, +/* s s w */ 0xe5000000 /* str */, +/* s s b */ 0xe5400000 /* strb */, +/* s s h */ 0xe10000b0 /* strh */, +/* s s N */ 0x00000000 /* not allowed */, + +/* l u w */ 0xe5100000 /* ldr */, +/* l u b */ 0xe5500000 /* ldrb */, +/* l u h */ 0xe11000b0 /* ldrh */, +/* l u p */ 0xf5500000 /* preload */, +/* l s w */ 0xe5100000 /* ldr */, +/* l s b */ 0xe11000d0 /* ldrsb */, +/* l s h */ 0xe11000f0 /* ldrsh */, +/* l s N */ 0x00000000 /* not allowed */, +}; + +#define EMIT_DATA_TRANSFER(type, add, target_reg, base_reg, arg) \ + (data_transfer_insts[(type) & 0xf] | ((add) << 23) | RD(target_reg) | RN(base_reg) | (arg)) + +/* Normal ldr/str instruction. + Type2: ldrsb, ldrh, ldrsh */ +#define IS_TYPE1_TRANSFER(type) \ + (data_transfer_insts[(type) & 0xf] & 0x04000000) +#define TYPE2_TRANSFER_IMM(imm) \ + (((imm) & 0xf) | (((imm) & 0xf0) << 4) | (1 << 22)) static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 inp_flags, sljit_s32 dst, sljit_sw dstw, @@ -826,15 +904,15 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 sljit_s32 src2, sljit_sw src2w); SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { - sljit_s32 size, i, tmp; + sljit_s32 args, size, i, tmp; sljit_uw push; CHECK_ERROR(); - 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); + CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); /* Push saved registers, temporary registers stmdb sp!, {..., lr} */ @@ -856,25 +934,27 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi if (local_size > 0) FAIL_IF(emit_op(compiler, SLJIT_SUB, ALLOW_IMM, SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size)); + args = get_arg_count(arg_types); + if (args >= 1) - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_S0, SLJIT_UNUSED, RM(SLJIT_R0)))); + FAIL_IF(push_inst(compiler, MOV | RD(SLJIT_S0) | RM(SLJIT_R0))); if (args >= 2) - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_S1, SLJIT_UNUSED, RM(SLJIT_R1)))); + FAIL_IF(push_inst(compiler, MOV | RD(SLJIT_S1) | RM(SLJIT_R1))); if (args >= 3) - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_S2, SLJIT_UNUSED, RM(SLJIT_R2)))); + FAIL_IF(push_inst(compiler, MOV | RD(SLJIT_S2) | RM(SLJIT_R2))); return SLJIT_SUCCESS; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { sljit_s32 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); + CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); compiler->local_size = ((size + local_size + 7) & ~7) - size; @@ -912,52 +992,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp /* Operators */ /* --------------------------------------------------------------------- */ -/* s/l - store/load (1 bit) - u/s - signed/unsigned (1 bit) - w/b/h/N - word/byte/half/NOT allowed (2 bit) - It contans 16 items, but not all are different. */ - -static sljit_sw data_transfer_insts[16] = { -/* s u w */ 0xe5000000 /* str */, -/* s u b */ 0xe5400000 /* strb */, -/* s u h */ 0xe10000b0 /* strh */, -/* s u N */ 0x00000000 /* not allowed */, -/* s s w */ 0xe5000000 /* str */, -/* s s b */ 0xe5400000 /* strb */, -/* s s h */ 0xe10000b0 /* strh */, -/* s s N */ 0x00000000 /* not allowed */, - -/* l u w */ 0xe5100000 /* ldr */, -/* l u b */ 0xe5500000 /* ldrb */, -/* l u h */ 0xe11000b0 /* ldrh */, -/* l u N */ 0x00000000 /* not allowed */, -/* l s w */ 0xe5100000 /* ldr */, -/* l s b */ 0xe11000d0 /* ldrsb */, -/* l s h */ 0xe11000f0 /* ldrsh */, -/* l s N */ 0x00000000 /* not allowed */, -}; - -#define EMIT_DATA_TRANSFER(type, add, wb, target, base1, base2) \ - (data_transfer_insts[(type) >> 4] | ((add) << 23) | ((wb) << 21) | (reg_map[target] << 12) | (reg_map[base1] << 16) | (base2)) -/* Normal ldr/str instruction. - Type2: ldrsb, ldrh, ldrsh */ -#define IS_TYPE1_TRANSFER(type) \ - (data_transfer_insts[(type) >> 4] & 0x04000000) -#define TYPE2_TRANSFER_IMM(imm) \ - (((imm) & 0xf) | (((imm) & 0xf0) << 4) | (1 << 22)) - /* flags: */ /* Arguments are swapped. */ #define ARGS_SWAPPED 0x01 /* Inverted immediate. */ #define INV_IMM 0x02 /* Source and destination is register. */ -#define REG_DEST 0x04 -#define REG_SOURCE 0x08 - /* One instruction is enough. */ -#define FAST_DEST 0x10 - /* Multiple instructions are required. */ -#define SLOW_DEST 0x20 +#define MOVE_REG_CONV 0x04 + /* Unused return value. */ +#define UNUSED_RETURN 0x08 /* SET_FLAGS must be (1 << 20) as it is also the value of S bit (can be used for optimization). */ #define SET_FLAGS (1 << 20) /* dst: reg @@ -966,157 +1009,127 @@ static sljit_sw data_transfer_insts[16] = { SRC2_IMM must be (1 << 25) as it is also the value of I bit (can be used for optimization). */ #define SRC2_IMM (1 << 25) -#define EMIT_DATA_PROCESS_INS_AND_RETURN(opcode) \ - return push_inst(compiler, EMIT_DATA_PROCESS_INS(opcode, flags & SET_FLAGS, dst, src1, (src2 & SRC2_IMM) ? src2 : RM(src2))) - -#define EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(opcode, dst, src1, src2) \ - return push_inst(compiler, EMIT_DATA_PROCESS_INS(opcode, flags & SET_FLAGS, dst, src1, src2)) - #define EMIT_SHIFT_INS_AND_RETURN(opcode) \ SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM)); \ if (compiler->shift_imm != 0x20) { \ SLJIT_ASSERT(src1 == TMP_REG1); \ SLJIT_ASSERT(!(flags & ARGS_SWAPPED)); \ + \ if (compiler->shift_imm != 0) \ - return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, (compiler->shift_imm << 7) | (opcode << 5) | reg_map[src2])); \ - return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, reg_map[src2])); \ + return push_inst(compiler, MOV | (flags & SET_FLAGS) | \ + RD(dst) | (compiler->shift_imm << 7) | (opcode << 5) | RM(src2)); \ + return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst) | RM(src2)); \ } \ - return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, (reg_map[(flags & ARGS_SWAPPED) ? src1 : src2] << 8) | (opcode << 5) | 0x10 | ((flags & ARGS_SWAPPED) ? reg_map[src2] : reg_map[src1]))); + return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst) | \ + (reg_map[(flags & ARGS_SWAPPED) ? src1 : src2] << 8) | (opcode << 5) | 0x10 | RM((flags & ARGS_SWAPPED) ? src2 : src1)); static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, sljit_s32 dst, sljit_s32 src1, sljit_s32 src2) { - sljit_sw mul_inst; - switch (GET_OPCODE(op)) { case SLJIT_MOV: SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED)); if (dst != src2) { if (src2 & SRC2_IMM) { - if (flags & INV_IMM) - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2); - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2); + return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2); } - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, reg_map[src2]); + return push_inst(compiler, MOV | RD(dst) | RM(src2)); } return SLJIT_SUCCESS; case SLJIT_MOV_U8: case SLJIT_MOV_S8: SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED)); - if ((flags & (REG_DEST | REG_SOURCE)) == (REG_DEST | REG_SOURCE)) { + if (flags & MOVE_REG_CONV) { #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) if (op == SLJIT_MOV_U8) - return push_inst(compiler, EMIT_DATA_PROCESS_INS(AND_DP, 0, dst, src2, SRC2_IMM | 0xff)); - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (24 << 7) | reg_map[src2]))); - return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (24 << 7) | (op == SLJIT_MOV_U8 ? 0x20 : 0x40) | reg_map[dst])); + return push_inst(compiler, AND | RD(dst) | RN(src2) | SRC2_IMM | 0xff); + FAIL_IF(push_inst(compiler, MOV | RD(dst) | (24 << 7) | RM(src2))); + return push_inst(compiler, MOV | RD(dst) | (24 << 7) | (op == SLJIT_MOV_U8 ? 0x20 : 0x40) | RM(dst)); #else return push_inst(compiler, (op == SLJIT_MOV_U8 ? UXTB : SXTB) | RD(dst) | RM(src2)); #endif } else if (dst != src2) { SLJIT_ASSERT(src2 & SRC2_IMM); - if (flags & INV_IMM) - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2); - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2); + return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2); } return SLJIT_SUCCESS; case SLJIT_MOV_U16: case SLJIT_MOV_S16: SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED)); - if ((flags & (REG_DEST | REG_SOURCE)) == (REG_DEST | REG_SOURCE)) { + if (flags & MOVE_REG_CONV) { #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (16 << 7) | reg_map[src2]))); - return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (16 << 7) | (op == SLJIT_MOV_U16 ? 0x20 : 0x40) | reg_map[dst])); + FAIL_IF(push_inst(compiler, MOV | RD(dst) | (16 << 7) | RM(src2))); + return push_inst(compiler, MOV | RD(dst) | (16 << 7) | (op == SLJIT_MOV_U16 ? 0x20 : 0x40) | RM(dst)); #else return push_inst(compiler, (op == SLJIT_MOV_U16 ? UXTH : SXTH) | RD(dst) | RM(src2)); #endif } else if (dst != src2) { SLJIT_ASSERT(src2 & SRC2_IMM); - if (flags & INV_IMM) - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2); - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2); + return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2); } return SLJIT_SUCCESS; case SLJIT_NOT: if (src2 & SRC2_IMM) { - if (flags & INV_IMM) - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2); - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2); + return push_inst(compiler, ((flags & INV_IMM) ? MOV : MVN) | (flags & SET_FLAGS) | RD(dst) | src2); } - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, RM(src2)); + return push_inst(compiler, MVN | (flags & SET_FLAGS) | RD(dst) | RM(src2)); case SLJIT_CLZ: SLJIT_ASSERT(!(flags & INV_IMM)); SLJIT_ASSERT(!(src2 & SRC2_IMM)); FAIL_IF(push_inst(compiler, CLZ | RD(dst) | RM(src2))); - if (flags & SET_FLAGS) - EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(CMP_DP, SLJIT_UNUSED, dst, SRC2_IMM); return SLJIT_SUCCESS; case SLJIT_ADD: SLJIT_ASSERT(!(flags & INV_IMM)); - EMIT_DATA_PROCESS_INS_AND_RETURN(ADD_DP); + if ((flags & (UNUSED_RETURN | SET_FLAGS)) == (UNUSED_RETURN | SET_FLAGS) && !(flags & ARGS_SWAPPED)) + return push_inst(compiler, CMN | SET_FLAGS | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2))); + return push_inst(compiler, ADD | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2))); case SLJIT_ADDC: SLJIT_ASSERT(!(flags & INV_IMM)); - EMIT_DATA_PROCESS_INS_AND_RETURN(ADC_DP); + return push_inst(compiler, ADC | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2))); case SLJIT_SUB: SLJIT_ASSERT(!(flags & INV_IMM)); - if (!(flags & ARGS_SWAPPED)) - EMIT_DATA_PROCESS_INS_AND_RETURN(SUB_DP); - EMIT_DATA_PROCESS_INS_AND_RETURN(RSB_DP); + if ((flags & (UNUSED_RETURN | SET_FLAGS)) == (UNUSED_RETURN | SET_FLAGS) && !(flags & ARGS_SWAPPED)) + return push_inst(compiler, CMP | SET_FLAGS | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2))); + return push_inst(compiler, (!(flags & ARGS_SWAPPED) ? SUB : RSB) | (flags & SET_FLAGS) + | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2))); case SLJIT_SUBC: SLJIT_ASSERT(!(flags & INV_IMM)); - if (!(flags & ARGS_SWAPPED)) - EMIT_DATA_PROCESS_INS_AND_RETURN(SBC_DP); - EMIT_DATA_PROCESS_INS_AND_RETURN(RSC_DP); + return push_inst(compiler, (!(flags & ARGS_SWAPPED) ? SBC : RSC) | (flags & SET_FLAGS) + | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2))); case SLJIT_MUL: SLJIT_ASSERT(!(flags & INV_IMM)); SLJIT_ASSERT(!(src2 & SRC2_IMM)); - if (SLJIT_UNLIKELY(op & SLJIT_SET_O)) - mul_inst = SMULL | (reg_map[TMP_REG3] << 16) | (reg_map[dst] << 12); - else - mul_inst = MUL | (reg_map[dst] << 16); - if (dst != src2) - FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src1] << 8) | reg_map[src2])); - else if (dst != src1) - FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src2] << 8) | reg_map[src1])); - else { - /* Rm and Rd must not be the same register. */ - SLJIT_ASSERT(dst != TMP_REG1); - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG1, SLJIT_UNUSED, reg_map[src2]))); - FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src2] << 8) | reg_map[TMP_REG1])); - } + if (!HAS_FLAGS(op)) + return push_inst(compiler, MUL | (reg_map[dst] << 16) | (reg_map[src2] << 8) | reg_map[src1]); - if (!(op & SLJIT_SET_O)) - return SLJIT_SUCCESS; + FAIL_IF(push_inst(compiler, SMULL | (reg_map[TMP_REG1] << 16) | (reg_map[dst] << 12) | (reg_map[src2] << 8) | reg_map[src1])); - /* We need to use TMP_REG3. */ - compiler->cache_arg = 0; - compiler->cache_argw = 0; - /* cmp TMP_REG2, dst asr #31. */ - return push_inst(compiler, EMIT_DATA_PROCESS_INS(CMP_DP, SET_FLAGS, SLJIT_UNUSED, TMP_REG3, RM(dst) | 0xfc0)); + /* cmp TMP_REG1, dst asr #31. */ + return push_inst(compiler, CMP | SET_FLAGS | RN(TMP_REG1) | RM(dst) | 0xfc0); case SLJIT_AND: - if (!(flags & INV_IMM)) - EMIT_DATA_PROCESS_INS_AND_RETURN(AND_DP); - EMIT_DATA_PROCESS_INS_AND_RETURN(BIC_DP); + return push_inst(compiler, (!(flags & INV_IMM) ? AND : BIC) | (flags & SET_FLAGS) + | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2))); case SLJIT_OR: SLJIT_ASSERT(!(flags & INV_IMM)); - EMIT_DATA_PROCESS_INS_AND_RETURN(ORR_DP); + return push_inst(compiler, ORR | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2))); case SLJIT_XOR: SLJIT_ASSERT(!(flags & INV_IMM)); - EMIT_DATA_PROCESS_INS_AND_RETURN(EOR_DP); + return push_inst(compiler, EOR | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2))); case SLJIT_SHL: EMIT_SHIFT_INS_AND_RETURN(0); @@ -1127,12 +1140,11 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl case SLJIT_ASHR: EMIT_SHIFT_INS_AND_RETURN(2); } - SLJIT_ASSERT_STOP(); + + SLJIT_UNREACHABLE(); return SLJIT_SUCCESS; } -#undef EMIT_DATA_PROCESS_INS_AND_RETURN -#undef EMIT_FULL_DATA_PROCESS_INS_AND_RETURN #undef EMIT_SHIFT_INS_AND_RETURN /* Tests whether the immediate can be stored in the 12 bit imm field. @@ -1280,8 +1292,8 @@ static sljit_s32 generate_int(struct sljit_compiler *compiler, sljit_s32 reg, sl return 0; } - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(positive ? MOV_DP : MVN_DP, 0, reg, SLJIT_UNUSED, imm1))); - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(positive ? ORR_DP : BIC_DP, 0, reg, reg, imm2))); + FAIL_IF(push_inst(compiler, (positive ? MOV : MVN) | RD(reg) | imm1)); + FAIL_IF(push_inst(compiler, (positive ? ORR : BIC) | RD(reg) | RN(reg) | imm2)); return 1; } #endif @@ -1298,11 +1310,11 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, /* Create imm by 1 inst. */ tmp = get_imm(imm); if (tmp) - return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, reg, SLJIT_UNUSED, tmp)); + return push_inst(compiler, MOV | RD(reg) | tmp); tmp = get_imm(~imm); if (tmp) - return push_inst(compiler, EMIT_DATA_PROCESS_INS(MVN_DP, 0, reg, SLJIT_UNUSED, tmp)); + return push_inst(compiler, MVN | RD(reg) | tmp); #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) /* Create imm by 2 inst. */ @@ -1310,293 +1322,109 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, FAIL_IF(generate_int(compiler, reg, ~imm, 0)); /* Load integer. */ - return push_inst_with_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, reg, TMP_PC, 0), imm); + return push_inst_with_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, reg, TMP_PC, 0), imm); #else - return emit_imm(compiler, reg, imm); + FAIL_IF(push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | (imm & 0xfff))); + if (imm <= 0xffff) + return SLJIT_SUCCESS; + return push_inst(compiler, MOVT | RD(reg) | ((imm >> 12) & 0xf0000) | ((imm >> 16) & 0xfff)); #endif } -/* Helper function. Dst should be reg + value, using at most 1 instruction, flags does not set. */ -static sljit_s32 emit_set_delta(struct sljit_compiler *compiler, sljit_s32 dst, sljit_s32 reg, sljit_sw value) +static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, + sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg) { - if (value >= 0) { - value = get_imm(value); - if (value) - return push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, dst, reg, value)); - } - else { - value = get_imm(-value); - if (value) - return push_inst(compiler, EMIT_DATA_PROCESS_INS(SUB_DP, 0, dst, reg, value)); - } - return SLJIT_ERR_UNSUPPORTED; -} + sljit_uw imm, offset_reg; + sljit_uw is_type1_transfer = IS_TYPE1_TRANSFER(flags); -/* Can perform an operation using at most 1 instruction. */ -static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) -{ - sljit_uw imm; - - if (arg & SLJIT_IMM) { - imm = get_imm(argw); - if (imm) { - if (inp_flags & ARG_TEST) - return 1; - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, reg, SLJIT_UNUSED, imm))); - return -1; - } - imm = get_imm(~argw); - if (imm) { - if (inp_flags & ARG_TEST) - return 1; - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MVN_DP, 0, reg, SLJIT_UNUSED, imm))); - return -1; - } - return 0; - } - - SLJIT_ASSERT(arg & SLJIT_MEM); - - /* Fast loads/stores. */ - if (!(arg & REG_MASK)) - return 0; - - if (arg & OFFS_REG_MASK) { - if ((argw & 0x3) != 0 && !IS_TYPE1_TRANSFER(inp_flags)) - return 0; - - if (inp_flags & ARG_TEST) - return 1; - FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, - RM(OFFS_REG(arg)) | (IS_TYPE1_TRANSFER(inp_flags) ? SRC2_IMM : 0) | ((argw & 0x3) << 7)))); - return -1; - } - - if (IS_TYPE1_TRANSFER(inp_flags)) { - if (argw >= 0 && argw <= 0xfff) { - if (inp_flags & ARG_TEST) - return 1; - FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, argw))); - return -1; - } - if (argw < 0 && argw >= -0xfff) { - if (inp_flags & ARG_TEST) - return 1; - FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 0, inp_flags & WRITE_BACK, reg, arg & REG_MASK, -argw))); - return -1; - } - } - else { - if (argw >= 0 && argw <= 0xff) { - if (inp_flags & ARG_TEST) - return 1; - FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, TYPE2_TRANSFER_IMM(argw)))); - return -1; - } - if (argw < 0 && argw >= -0xff) { - if (inp_flags & ARG_TEST) - return 1; - argw = -argw; - FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 0, inp_flags & WRITE_BACK, reg, arg & REG_MASK, TYPE2_TRANSFER_IMM(argw)))); - return -1; - } - } - - return 0; -} - -/* See getput_arg below. - Note: can_cache is called only for binary operators. Those - operators always uses word arguments without write back. */ -static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) -{ - /* Immediate caching is not supported as it would be an operation on constant arguments. */ - if (arg & SLJIT_IMM) - return 0; - - /* Always a simple operation. */ - if (arg & OFFS_REG_MASK) - return 0; - - if (!(arg & REG_MASK)) { - /* Immediate access. */ - if ((next_arg & SLJIT_MEM) && ((sljit_uw)argw - (sljit_uw)next_argw <= 0xfff || (sljit_uw)next_argw - (sljit_uw)argw <= 0xfff)) - return 1; - return 0; - } - - if (argw <= 0xfffff && argw >= -0xfffff) - return 0; - - if (argw == next_argw && (next_arg & SLJIT_MEM)) - return 1; - - if (arg == next_arg && ((sljit_uw)argw - (sljit_uw)next_argw <= 0xfff || (sljit_uw)next_argw - (sljit_uw)argw <= 0xfff)) - return 1; - - return 0; -} - -#define GETPUT_ARG_DATA_TRANSFER(add, wb, target, base, imm) \ - if (max_delta & 0xf00) \ - FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, add, wb, target, base, imm))); \ - else \ - FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, add, wb, target, base, TYPE2_TRANSFER_IMM(imm)))); - -#define TEST_WRITE_BACK() \ - if (inp_flags & WRITE_BACK) { \ - tmp_r = arg & REG_MASK; \ - if (reg == tmp_r) { \ - /* This can only happen for stores */ \ - /* since ldr reg, [reg, ...]! has no meaning */ \ - SLJIT_ASSERT(!(inp_flags & LOAD_DATA)); \ - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(reg)))); \ - reg = TMP_REG3; \ - } \ - } - -/* Emit the necessary instructions. See can_cache above. */ -static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) -{ - sljit_s32 tmp_r; - sljit_sw max_delta; - sljit_sw sign; - sljit_uw imm; - - if (arg & SLJIT_IMM) { - SLJIT_ASSERT(inp_flags & LOAD_DATA); - return load_immediate(compiler, reg, argw); - } - - SLJIT_ASSERT(arg & SLJIT_MEM); - - tmp_r = (inp_flags & LOAD_DATA) ? reg : TMP_REG3; - max_delta = IS_TYPE1_TRANSFER(inp_flags) ? 0xfff : 0xff; + SLJIT_ASSERT (arg & SLJIT_MEM); + SLJIT_ASSERT((arg & REG_MASK) != tmp_reg); if ((arg & REG_MASK) == SLJIT_UNUSED) { - /* Write back is not used. */ - imm = (sljit_uw)(argw - compiler->cache_argw); - if ((compiler->cache_arg & SLJIT_IMM) && (imm <= (sljit_uw)max_delta || imm >= (sljit_uw)-max_delta)) { - if (imm <= (sljit_uw)max_delta) { - sign = 1; - argw = argw - compiler->cache_argw; - } - else { - sign = 0; - argw = compiler->cache_argw - argw; - } - - GETPUT_ARG_DATA_TRANSFER(sign, 0, reg, TMP_REG3, argw); - return SLJIT_SUCCESS; + if (is_type1_transfer) { + FAIL_IF(load_immediate(compiler, tmp_reg, argw & ~0xfff)); + argw &= 0xfff; + } + else { + FAIL_IF(load_immediate(compiler, tmp_reg, argw & ~0xff)); + argw &= 0xff; } - /* With write back, we can create some sophisticated loads, but - it is hard to decide whether we should convert downward (0s) or upward (1s). */ - imm = (sljit_uw)(argw - next_argw); - if ((next_arg & SLJIT_MEM) && (imm <= (sljit_uw)max_delta || imm >= (sljit_uw)-max_delta)) { - SLJIT_ASSERT(inp_flags & LOAD_DATA); - - compiler->cache_arg = SLJIT_IMM; - compiler->cache_argw = argw; - tmp_r = TMP_REG3; - } - - FAIL_IF(load_immediate(compiler, tmp_r, argw)); - GETPUT_ARG_DATA_TRANSFER(1, 0, reg, tmp_r, 0); - return SLJIT_SUCCESS; + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, tmp_reg, + is_type1_transfer ? argw : TYPE2_TRANSFER_IMM(argw))); } if (arg & OFFS_REG_MASK) { - SLJIT_ASSERT((argw & 0x3) && !(max_delta & 0xf00)); - if (inp_flags & WRITE_BACK) - tmp_r = arg & REG_MASK; - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, tmp_r, arg & REG_MASK, RM(OFFS_REG(arg)) | ((argw & 0x3) << 7)))); - return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, 0, reg, tmp_r, TYPE2_TRANSFER_IMM(0))); + offset_reg = OFFS_REG(arg); + arg &= REG_MASK; + argw &= 0x3; + + if (argw != 0 && !is_type1_transfer) { + FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | RM(offset_reg) | (argw << 7))); + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, tmp_reg, TYPE2_TRANSFER_IMM(0))); + } + + /* Bit 25: RM is offset. */ + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, + RM(offset_reg) | (is_type1_transfer ? (1 << 25) : 0) | (argw << 7))); } - imm = (sljit_uw)(argw - compiler->cache_argw); - if (compiler->cache_arg == arg && imm <= (sljit_uw)max_delta) { - SLJIT_ASSERT(!(inp_flags & WRITE_BACK)); - GETPUT_ARG_DATA_TRANSFER(1, 0, reg, TMP_REG3, imm); - return SLJIT_SUCCESS; + arg &= REG_MASK; + + if (is_type1_transfer) { + if (argw > 0xfff) { + imm = get_imm(argw & ~0xfff); + if (imm) { + FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | imm)); + argw = argw & 0xfff; + arg = tmp_reg; + } + } + else if (argw < -0xfff) { + imm = get_imm(-argw & ~0xfff); + if (imm) { + FAIL_IF(push_inst(compiler, SUB | RD(tmp_reg) | RN(arg) | imm)); + argw = -(-argw & 0xfff); + arg = tmp_reg; + } + } + + if (argw >= 0 && argw <= 0xfff) + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, argw)); + + if (argw < 0 && argw >= -0xfff) + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 0, reg, arg, -argw)); } - if (compiler->cache_arg == arg && imm >= (sljit_uw)-max_delta) { - SLJIT_ASSERT(!(inp_flags & WRITE_BACK)); - imm = (sljit_uw)-(sljit_sw)imm; - GETPUT_ARG_DATA_TRANSFER(0, 0, reg, TMP_REG3, imm); - return SLJIT_SUCCESS; + else { + if (argw > 0xff) { + imm = get_imm(argw & ~0xff); + if (imm) { + FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | imm)); + argw = argw & 0xff; + arg = tmp_reg; + } + } + else if (argw < -0xff) { + imm = get_imm(-argw & ~0xff); + if (imm) { + FAIL_IF(push_inst(compiler, SUB | RD(tmp_reg) | RN(arg) | imm)); + argw = -(-argw & 0xff); + arg = tmp_reg; + } + } + + if (argw >= 0 && argw <= 0xff) + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, TYPE2_TRANSFER_IMM(argw))); + + if (argw < 0 && argw >= -0xff) { + argw = -argw; + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 0, reg, arg, TYPE2_TRANSFER_IMM(argw))); + } } - imm = get_imm(argw & ~max_delta); - if (imm) { - TEST_WRITE_BACK(); - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, tmp_r, arg & REG_MASK, imm))); - GETPUT_ARG_DATA_TRANSFER(1, inp_flags & WRITE_BACK, reg, tmp_r, argw & max_delta); - return SLJIT_SUCCESS; - } - - imm = get_imm(-argw & ~max_delta); - if (imm) { - argw = -argw; - TEST_WRITE_BACK(); - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(SUB_DP, 0, tmp_r, arg & REG_MASK, imm))); - GETPUT_ARG_DATA_TRANSFER(0, inp_flags & WRITE_BACK, reg, tmp_r, argw & max_delta); - return SLJIT_SUCCESS; - } - - if ((compiler->cache_arg & SLJIT_IMM) && compiler->cache_argw == argw) { - TEST_WRITE_BACK(); - return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, RM(TMP_REG3) | (max_delta & 0xf00 ? SRC2_IMM : 0))); - } - - if (argw == next_argw && (next_arg & SLJIT_MEM)) { - SLJIT_ASSERT(inp_flags & LOAD_DATA); - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); - - compiler->cache_arg = SLJIT_IMM; - compiler->cache_argw = argw; - - TEST_WRITE_BACK(); - return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, RM(TMP_REG3) | (max_delta & 0xf00 ? SRC2_IMM : 0))); - } - - imm = (sljit_uw)(argw - next_argw); - if (arg == next_arg && !(inp_flags & WRITE_BACK) && (imm <= (sljit_uw)max_delta || imm >= (sljit_uw)-max_delta)) { - SLJIT_ASSERT(inp_flags & LOAD_DATA); - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG3, TMP_REG3, reg_map[arg & REG_MASK]))); - - compiler->cache_arg = arg; - compiler->cache_argw = argw; - - GETPUT_ARG_DATA_TRANSFER(1, 0, reg, TMP_REG3, 0); - return SLJIT_SUCCESS; - } - - if ((arg & REG_MASK) == tmp_r) { - compiler->cache_arg = SLJIT_IMM; - compiler->cache_argw = argw; - tmp_r = TMP_REG3; - } - - FAIL_IF(load_immediate(compiler, tmp_r, argw)); - return push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & REG_MASK, reg_map[tmp_r] | (max_delta & 0xf00 ? SRC2_IMM : 0))); -} - -static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) -{ - if (getput_arg_fast(compiler, flags, reg, arg, argw)) - return compiler->error; - compiler->cache_arg = 0; - compiler->cache_argw = 0; - return getput_arg(compiler, flags, reg, arg, argw, 0, 0); -} - -static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w) -{ - if (getput_arg_fast(compiler, flags, reg, arg1, arg1w)) - return compiler->error; - return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w); + FAIL_IF(load_immediate(compiler, tmp_reg, argw)); + return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, + RM(tmp_reg) | (is_type1_transfer ? (1 << 25) : 0))); } static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 inp_flags, @@ -1604,68 +1432,66 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { - /* arg1 goes to TMP_REG1 or src reg - arg2 goes to TMP_REG2, imm or src reg - TMP_REG3 can be used for caching - result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */ + /* src1 is reg or TMP_REG1 + src2 is reg, TMP_REG2, or imm + result goes to TMP_REG2, so put result can use TMP_REG1. */ /* We prefers register and simple consts. */ - sljit_s32 dst_r; - sljit_s32 src1_r; - sljit_s32 src2_r = 0; - sljit_s32 sugg_src2_r = TMP_REG2; - sljit_s32 flags = GET_FLAGS(op) ? SET_FLAGS : 0; - - compiler->cache_arg = 0; - compiler->cache_argw = 0; + sljit_s32 dst_reg; + sljit_s32 src1_reg; + sljit_s32 src2_reg; + sljit_s32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0; /* Destination check. */ - if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) { - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32 && !(src2 & SLJIT_MEM)) - return SLJIT_SUCCESS; - dst_r = TMP_REG2; - } - else if (FAST_IS_REG(dst)) { - dst_r = dst; - flags |= REG_DEST; - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) - sugg_src2_r = dst_r; - } - else { - SLJIT_ASSERT(dst & SLJIT_MEM); - if (getput_arg_fast(compiler, inp_flags | ARG_TEST, TMP_REG2, dst, dstw)) { - flags |= FAST_DEST; - dst_r = TMP_REG2; - } - else { - flags |= SLOW_DEST; - dst_r = 0; - } - } + if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) + flags |= UNUSED_RETURN; - /* Source 1. */ - if (FAST_IS_REG(src1)) - src1_r = src1; - else if (FAST_IS_REG(src2)) { - flags |= ARGS_SWAPPED; - src1_r = src2; - src2 = src1; - src2w = src1w; - } - else do { /* do { } while(0) is used because of breaks. */ - src1_r = 0; - if ((inp_flags & ALLOW_ANY_IMM) && (src1 & SLJIT_IMM)) { - /* The second check will generate a hit. */ - src2_r = get_imm(src1w); - if (src2_r) { + SLJIT_ASSERT(!(inp_flags & ALLOW_INV_IMM) || (inp_flags & ALLOW_IMM)); + + src2_reg = 0; + + do { + if (!(inp_flags & ALLOW_IMM)) + break; + + if (src2 & SLJIT_IMM) { + src2_reg = get_imm(src2w); + if (src2_reg) + break; + if (inp_flags & ALLOW_INV_IMM) { + src2_reg = get_imm(~src2w); + if (src2_reg) { + flags |= INV_IMM; + break; + } + } + if (GET_OPCODE(op) == SLJIT_ADD) { + src2_reg = get_imm(-src2w); + if (src2_reg) { + op = SLJIT_SUB | GET_ALL_FLAGS(op); + break; + } + } + if (GET_OPCODE(op) == SLJIT_SUB) { + src2_reg = get_imm(-src2w); + if (src2_reg) { + op = SLJIT_ADD | GET_ALL_FLAGS(op); + break; + } + } + } + + if (src1 & SLJIT_IMM) { + src2_reg = get_imm(src1w); + if (src2_reg) { flags |= ARGS_SWAPPED; src1 = src2; src1w = src2w; break; } if (inp_flags & ALLOW_INV_IMM) { - src2_r = get_imm(~src1w); - if (src2_r) { + src2_reg = get_imm(~src1w); + if (src2_reg) { flags |= ARGS_SWAPPED | INV_IMM; src1 = src2; src1w = src2w; @@ -1673,9 +1499,9 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 } } if (GET_OPCODE(op) == SLJIT_ADD) { - src2_r = get_imm(-src1w); - if (src2_r) { - /* Note: ARGS_SWAPPED is intentionally not applied! */ + src2_reg = get_imm(-src1w); + if (src2_reg) { + /* Note: add is commutative operation. */ src1 = src2; src1w = src2w; op = SLJIT_SUB | GET_ALL_FLAGS(op); @@ -1683,110 +1509,54 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 } } } + } while(0); - if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w)) { - FAIL_IF(compiler->error); - src1_r = TMP_REG1; + /* Source 1. */ + if (FAST_IS_REG(src1)) + src1_reg = src1; + else if (src1 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1)); + src1_reg = TMP_REG1; + } + else { + FAIL_IF(load_immediate(compiler, TMP_REG1, src1w)); + src1_reg = TMP_REG1; + } + + /* Destination. */ + dst_reg = SLOW_IS_REG(dst) ? dst : TMP_REG2; + + if (op <= SLJIT_MOV_P) { + if (dst & SLJIT_MEM) { + if (inp_flags & BYTE_SIZE) + inp_flags &= ~SIGNED; + + if (FAST_IS_REG(src2)) + return emit_op_mem(compiler, inp_flags, src2, dst, dstw, TMP_REG2); } - } while (0); + + if (FAST_IS_REG(src2) && dst_reg != TMP_REG2) + flags |= MOVE_REG_CONV; + } /* Source 2. */ - if (src2_r == 0) { - if (FAST_IS_REG(src2)) { - src2_r = src2; - flags |= REG_SOURCE; - if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) - dst_r = src2_r; - } - else do { /* do { } while(0) is used because of breaks. */ - if ((inp_flags & ALLOW_ANY_IMM) && (src2 & SLJIT_IMM)) { - src2_r = get_imm(src2w); - if (src2_r) - break; - if (inp_flags & ALLOW_INV_IMM) { - src2_r = get_imm(~src2w); - if (src2_r) { - flags |= INV_IMM; - break; - } - } - if (GET_OPCODE(op) == SLJIT_ADD) { - src2_r = get_imm(-src2w); - if (src2_r) { - op = SLJIT_SUB | GET_ALL_FLAGS(op); - flags &= ~ARGS_SWAPPED; - break; - } - } - if (GET_OPCODE(op) == SLJIT_SUB && !(flags & ARGS_SWAPPED)) { - src2_r = get_imm(-src2w); - if (src2_r) { - op = SLJIT_ADD | GET_ALL_FLAGS(op); - flags &= ~ARGS_SWAPPED; - break; - } - } - } + if (src2_reg == 0) { + src2_reg = (op <= SLJIT_MOV_P) ? dst_reg : TMP_REG2; - /* src2_r is 0. */ - if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w)) { - FAIL_IF(compiler->error); - src2_r = sugg_src2_r; - } - } while (0); - } - - /* src1_r, src2_r and dst_r can be zero (=unprocessed) or non-zero. - If they are zero, they must not be registers. */ - if (src1_r == 0 && src2_r == 0 && dst_r == 0) { - if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { - SLJIT_ASSERT(!(flags & ARGS_SWAPPED)); - flags |= ARGS_SWAPPED; - FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src2, src2w, src1, src1w)); - FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src1, src1w, dst, dstw)); - } - else { - FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); - FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw)); - } - src1_r = TMP_REG1; - src2_r = TMP_REG2; - } - else if (src1_r == 0 && src2_r == 0) { - FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); - src1_r = TMP_REG1; - } - else if (src1_r == 0 && dst_r == 0) { - FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); - src1_r = TMP_REG1; - } - else if (src2_r == 0 && dst_r == 0) { - FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw)); - src2_r = sugg_src2_r; - } - - if (dst_r == 0) - dst_r = TMP_REG2; - - if (src1_r == 0) { - FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, 0, 0)); - src1_r = TMP_REG1; - } - - if (src2_r == 0) { - FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, 0, 0)); - src2_r = sugg_src2_r; - } - - FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r)); - - if (flags & (FAST_DEST | SLOW_DEST)) { - if (flags & FAST_DEST) - FAIL_IF(getput_arg_fast(compiler, inp_flags, dst_r, dst, dstw)); + if (FAST_IS_REG(src2)) + src2_reg = src2; + else if (src2 & SLJIT_MEM) + FAIL_IF(emit_op_mem(compiler, inp_flags | LOAD_DATA, src2_reg, src2, src2w, TMP_REG2)); else - FAIL_IF(getput_arg(compiler, inp_flags, dst_r, dst, dstw, 0, 0)); + FAIL_IF(load_immediate(compiler, src2_reg, src2w)); } - return SLJIT_SUCCESS; + + FAIL_IF(emit_single_op(compiler, op, flags, dst_reg, src1_reg, src2_reg)); + + if (!(dst & SLJIT_MEM)) + return SLJIT_SUCCESS; + + return emit_op_mem(compiler, inp_flags, dst_reg, dst, dstw, TMP_REG1); } #ifdef __cplusplus @@ -1806,6 +1576,9 @@ extern int __aeabi_idivmod(int numerator, int denominator); SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op) { + sljit_sw saved_reg_list[3]; + sljit_sw saved_reg_count; + CHECK_ERROR(); CHECK(check_sljit_emit_op0(compiler, op)); @@ -1819,33 +1592,38 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile break; case SLJIT_LMUL_UW: case SLJIT_LMUL_SW: -#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) return push_inst(compiler, (op == SLJIT_LMUL_UW ? UMULL : SMULL) | (reg_map[SLJIT_R1] << 16) | (reg_map[SLJIT_R0] << 12) | (reg_map[SLJIT_R0] << 8) | reg_map[SLJIT_R1]); -#else - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG1, SLJIT_UNUSED, RM(SLJIT_R1)))); - return push_inst(compiler, (op == SLJIT_LMUL_UW ? UMULL : SMULL) - | (reg_map[SLJIT_R1] << 16) - | (reg_map[SLJIT_R0] << 12) - | (reg_map[SLJIT_R0] << 8) - | reg_map[TMP_REG1]); -#endif case SLJIT_DIVMOD_UW: case SLJIT_DIVMOD_SW: case SLJIT_DIV_UW: case SLJIT_DIV_SW: SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments); - SLJIT_COMPILE_ASSERT(reg_map[2] == 1 && reg_map[3] == 2, bad_register_mapping); + SLJIT_ASSERT(reg_map[2] == 1 && reg_map[3] == 2 && reg_map[4] == 3); - if ((op >= SLJIT_DIV_UW) && (compiler->scratches >= 3)) { - FAIL_IF(push_inst(compiler, 0xe52d2008 /* str r2, [sp, #-8]! */)); - FAIL_IF(push_inst(compiler, 0xe58d1004 /* str r1, [sp, #4] */)); + saved_reg_count = 0; + if (compiler->scratches >= 4) + saved_reg_list[saved_reg_count++] = 3; + if (compiler->scratches >= 3) + saved_reg_list[saved_reg_count++] = 2; + if (op >= SLJIT_DIV_UW) + saved_reg_list[saved_reg_count++] = 1; + + if (saved_reg_count > 0) { + FAIL_IF(push_inst(compiler, 0xe52d0000 | (saved_reg_count >= 3 ? 16 : 8) + | (saved_reg_list[0] << 12) /* str rX, [sp, #-8/-16]! */)); + if (saved_reg_count >= 2) { + SLJIT_ASSERT(saved_reg_list[1] < 8); + FAIL_IF(push_inst(compiler, 0xe58d0004 | (saved_reg_list[1] << 12) /* str rX, [sp, #4] */)); + } + if (saved_reg_count >= 3) { + SLJIT_ASSERT(saved_reg_list[2] < 8); + FAIL_IF(push_inst(compiler, 0xe58d0008 | (saved_reg_list[2] << 12) /* str rX, [sp, #8] */)); + } } - else if ((op >= SLJIT_DIV_UW) || (compiler->scratches >= 3)) - FAIL_IF(push_inst(compiler, 0xe52d0008 | (op >= SLJIT_DIV_UW ? 0x1000 : 0x2000) /* str r1/r2, [sp, #-8]! */)); #if defined(__GNUC__) FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM, @@ -1854,12 +1632,18 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile #error "Software divmod functions are needed" #endif - if ((op >= SLJIT_DIV_UW) && (compiler->scratches >= 3)) { - FAIL_IF(push_inst(compiler, 0xe59d1004 /* ldr r1, [sp, #4] */)); - FAIL_IF(push_inst(compiler, 0xe49d2008 /* ldr r2, [sp], #8 */)); + if (saved_reg_count > 0) { + if (saved_reg_count >= 3) { + SLJIT_ASSERT(saved_reg_list[2] < 8); + FAIL_IF(push_inst(compiler, 0xe59d0008 | (saved_reg_list[2] << 12) /* ldr rX, [sp, #8] */)); + } + if (saved_reg_count >= 2) { + SLJIT_ASSERT(saved_reg_list[1] < 8); + FAIL_IF(push_inst(compiler, 0xe59d0004 | (saved_reg_list[1] << 12) /* ldr rX, [sp, #4] */)); + } + return push_inst(compiler, 0xe49d0000 | (saved_reg_count >= 3 ? 16 : 8) + | (saved_reg_list[0] << 12) /* ldr rX, [sp], #8/16 */); } - else if ((op >= SLJIT_DIV_UW) || (compiler->scratches >= 3)) - return push_inst(compiler, 0xe49d0008 | (op >= SLJIT_DIV_UW ? 0x1000 : 0x2000) /* ldr r1/r2, [sp], #8 */); return SLJIT_SUCCESS; } @@ -1875,6 +1659,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(dst, dstw); ADJUST_LOCAL_OFFSET(src, srcw); + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) { +#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) + if (op <= SLJIT_MOV_P && (src & SLJIT_MEM)) + return emit_op_mem(compiler, PRELOAD | LOAD_DATA, TMP_PC, src, srcw, TMP_REG1); +#endif + return SLJIT_SUCCESS; + } + switch (GET_OPCODE(op)) { case SLJIT_MOV: case SLJIT_MOV_U32: @@ -1883,34 +1675,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw); case SLJIT_MOV_U8: - return emit_op(compiler, SLJIT_MOV_U8, ALLOW_ANY_IMM | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw); + return emit_op(compiler, SLJIT_MOV_U8, ALLOW_ANY_IMM | BYTE_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw); case SLJIT_MOV_S8: - return emit_op(compiler, SLJIT_MOV_S8, ALLOW_ANY_IMM | SIGNED_DATA | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw); + return emit_op(compiler, SLJIT_MOV_S8, ALLOW_ANY_IMM | SIGNED | BYTE_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw); case SLJIT_MOV_U16: - return emit_op(compiler, SLJIT_MOV_U16, ALLOW_ANY_IMM | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw); + return emit_op(compiler, SLJIT_MOV_U16, ALLOW_ANY_IMM | HALF_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw); case SLJIT_MOV_S16: - return emit_op(compiler, SLJIT_MOV_S16, ALLOW_ANY_IMM | SIGNED_DATA | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw); - - case SLJIT_MOVU: - case SLJIT_MOVU_U32: - case SLJIT_MOVU_S32: - case SLJIT_MOVU_P: - return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); - - case SLJIT_MOVU_U8: - return emit_op(compiler, SLJIT_MOV_U8, ALLOW_ANY_IMM | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw); - - case SLJIT_MOVU_S8: - return emit_op(compiler, SLJIT_MOV_S8, ALLOW_ANY_IMM | SIGNED_DATA | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw); - - case SLJIT_MOVU_U16: - return emit_op(compiler, SLJIT_MOV_U16, ALLOW_ANY_IMM | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw); - - case SLJIT_MOVU_S16: - return emit_op(compiler, SLJIT_MOV_S16, ALLOW_ANY_IMM | SIGNED_DATA | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw); + return emit_op(compiler, SLJIT_MOV_S16, ALLOW_ANY_IMM | SIGNED | HALF_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw); case SLJIT_NOT: return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw); @@ -1940,6 +1714,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) + return SLJIT_SUCCESS; + switch (GET_OPCODE(op)) { case SLJIT_ADD: case SLJIT_ADDC: @@ -1980,7 +1757,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) { CHECK_REG_INDEX(check_sljit_get_float_register_index(reg)); - return reg << 1; + return (freg_map[reg] << 1); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, @@ -1996,118 +1773,63 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c /* Floating point operators */ /* --------------------------------------------------------------------- */ -#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) - -/* 0 - no fpu - 1 - vfp */ -static sljit_s32 arm_fpu_type = -1; - -static void init_compiler(void) -{ - if (arm_fpu_type != -1) - return; - - /* TODO: Only the OS can help to determine the correct fpu type. */ - arm_fpu_type = 1; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void) -{ -#ifdef SLJIT_IS_FPU_AVAILABLE - return SLJIT_IS_FPU_AVAILABLE; -#else - if (arm_fpu_type == -1) - init_compiler(); - return arm_fpu_type; -#endif -} - -#else - -#define arm_fpu_type 1 - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void) -{ - /* Always available. */ - return 1; -} - -#endif #define FPU_LOAD (1 << 20) #define EMIT_FPU_DATA_TRANSFER(inst, add, base, freg, offs) \ - ((inst) | ((add) << 23) | (reg_map[base] << 16) | (freg << 12) | (offs)) + ((inst) | ((add) << 23) | (reg_map[base] << 16) | (freg_map[freg] << 12) | (offs)) #define EMIT_FPU_OPERATION(opcode, mode, dst, src1, src2) \ - ((opcode) | (mode) | ((dst) << 12) | (src1) | ((src2) << 16)) + ((opcode) | (mode) | (freg_map[dst] << 12) | freg_map[src1] | (freg_map[src2] << 16)) static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) { - sljit_sw tmp; sljit_uw imm; sljit_sw inst = VSTR_F32 | (flags & (SLJIT_F32_OP | FPU_LOAD)); + SLJIT_ASSERT(arg & SLJIT_MEM); + arg &= ~SLJIT_MEM; if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG1, arg & REG_MASK, RM(OFFS_REG(arg)) | ((argw & 0x3) << 7)))); - arg = SLJIT_MEM | TMP_REG1; + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | ((argw & 0x3) << 7))); + arg = TMP_REG2; argw = 0; } /* Fast loads and stores. */ - if ((arg & REG_MASK)) { + if (arg) { if (!(argw & ~0x3fc)) return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, arg & REG_MASK, reg, argw >> 2)); if (!(-argw & ~0x3fc)) return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, arg & REG_MASK, reg, (-argw) >> 2)); - } - if (compiler->cache_arg == arg) { - tmp = argw - compiler->cache_argw; - if (!(tmp & ~0x3fc)) - return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG3, reg, tmp >> 2)); - if (!(-tmp & ~0x3fc)) - return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, TMP_REG3, reg, -tmp >> 2)); - if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, tmp) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - compiler->cache_argw = argw; - return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG3, reg, 0)); - } - } - - if (arg & REG_MASK) { - if (emit_set_delta(compiler, TMP_REG1, arg & REG_MASK, argw) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG1, reg, 0)); - } imm = get_imm(argw & ~0x3fc); if (imm) { - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG1, arg & REG_MASK, imm))); - return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG1, reg, (argw & 0x3fc) >> 2)); + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | imm)); + return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG2, reg, (argw & 0x3fc) >> 2)); } imm = get_imm(-argw & ~0x3fc); if (imm) { argw = -argw; - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(SUB_DP, 0, TMP_REG1, arg & REG_MASK, imm))); - return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, TMP_REG1, reg, (argw & 0x3fc) >> 2)); + FAIL_IF(push_inst(compiler, SUB | RD(TMP_REG2) | RN(arg & REG_MASK) | imm)); + return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, TMP_REG2, reg, (argw & 0x3fc) >> 2)); } } - compiler->cache_arg = arg; - compiler->cache_argw = argw; - if (arg & REG_MASK) { - FAIL_IF(load_immediate(compiler, TMP_REG1, argw)); - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG3, arg & REG_MASK, reg_map[TMP_REG1]))); + if (arg) { + FAIL_IF(load_immediate(compiler, TMP_REG2, argw)); + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | RM(TMP_REG2))); } else - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + FAIL_IF(load_immediate(compiler, TMP_REG2, argw)); - return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG3, reg, 0)); + return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG2, reg, 0)); } static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) { + op ^= SLJIT_F32_OP; + if (src & SLJIT_MEM) { FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, TMP_FREG1, src, srcw)); src = TMP_FREG1; @@ -2115,11 +1837,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_S32_F32, op & SLJIT_F32_OP, TMP_FREG1, src, 0))); - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; - if (FAST_IS_REG(dst)) - return push_inst(compiler, VMOV | (1 << 20) | RD(dst) | (TMP_FREG1 << 16)); + return push_inst(compiler, VMOV | (1 << 20) | RD(dst) | (freg_map[TMP_FREG1] << 16)); /* Store the integer value from a VFP register. */ return emit_fop_mem(compiler, 0, TMP_FREG1, dst, dstw); @@ -2131,15 +1850,17 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp { sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; + op ^= SLJIT_F32_OP; + if (FAST_IS_REG(src)) - FAIL_IF(push_inst(compiler, VMOV | RD(src) | (TMP_FREG1 << 16))); + FAIL_IF(push_inst(compiler, VMOV | RD(src) | (freg_map[TMP_FREG1] << 16))); else if (src & SLJIT_MEM) { /* Load the integer value into a VFP register. */ FAIL_IF(emit_fop_mem(compiler, FPU_LOAD, TMP_FREG1, src, srcw)); } else { FAIL_IF(load_immediate(compiler, TMP_REG1, srcw)); - FAIL_IF(push_inst(compiler, VMOV | RD(TMP_REG1) | (TMP_FREG1 << 16))); + FAIL_IF(push_inst(compiler, VMOV | RD(TMP_REG1) | (freg_map[TMP_FREG1] << 16))); } FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_F32_S32, op & SLJIT_F32_OP, dst_r, TMP_FREG1, 0))); @@ -2153,6 +1874,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { + op ^= SLJIT_F32_OP; + if (src1 & SLJIT_MEM) { FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, TMP_FREG1, src1, src1w)); src1 = TMP_FREG1; @@ -2174,16 +1897,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil sljit_s32 dst_r; CHECK_ERROR(); - compiler->cache_arg = 0; - compiler->cache_argw = 0; - if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32) - op ^= SLJIT_F32_OP; SLJIT_COMPILE_ASSERT((SLJIT_F32_OP == 0x100), float_transfer_bit_error); SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw); dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; + if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32) + op ^= SLJIT_F32_OP; + if (src & SLJIT_MEM) { FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, dst_r, src, srcw)); src = dst_r; @@ -2228,8 +1950,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); - compiler->cache_arg = 0; - compiler->cache_argw = 0; op ^= SLJIT_F32_OP; dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; @@ -2270,7 +1990,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil #undef FPU_LOAD #undef EMIT_FPU_DATA_TRANSFER -#undef EMIT_FPU_OPERATION /* --------------------------------------------------------------------- */ /* Other instructions */ @@ -2282,21 +2001,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); ADJUST_LOCAL_OFFSET(dst, dstw); - /* For UNUSED dst. Uncommon, but possible. */ - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; + SLJIT_ASSERT(reg_map[TMP_REG2] == 14); if (FAST_IS_REG(dst)) - return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, RM(TMP_REG3))); + return push_inst(compiler, MOV | RD(dst) | RM(TMP_REG2)); /* Memory. */ - if (getput_arg_fast(compiler, WORD_DATA, TMP_REG3, dst, dstw)) - return compiler->error; - /* TMP_REG3 is used for caching. */ - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG2, SLJIT_UNUSED, RM(TMP_REG3)))); - compiler->cache_arg = 0; - compiler->cache_argw = 0; - return getput_arg(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0); + return emit_op_mem(compiler, WORD_SIZE, TMP_REG2, dst, dstw, TMP_REG1); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw) @@ -2305,21 +2016,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler CHECK(check_sljit_emit_fast_return(compiler, src, srcw)); ADJUST_LOCAL_OFFSET(src, srcw); + SLJIT_ASSERT(reg_map[TMP_REG2] == 14); + if (FAST_IS_REG(src)) - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(src)))); - else if (src & SLJIT_MEM) { - if (getput_arg_fast(compiler, WORD_DATA | LOAD_DATA, TMP_REG3, src, srcw)) - FAIL_IF(compiler->error); - else { - compiler->cache_arg = 0; - compiler->cache_argw = 0; - FAIL_IF(getput_arg(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw, 0, 0)); - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(TMP_REG2)))); - } - } - else if (src & SLJIT_IMM) - FAIL_IF(load_immediate(compiler, TMP_REG3, srcw)); - return push_inst(compiler, BLX | RM(TMP_REG3)); + FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(src))); + else + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, src, srcw, TMP_REG1)); + + return push_inst(compiler, BX | RM(TMP_REG2)); } /* --------------------------------------------------------------------- */ @@ -2376,7 +2080,7 @@ static sljit_uw get_cc(sljit_s32 type) return 0x70000000; default: - SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL3); + SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_CDECL); return 0xe0000000; } } @@ -2409,11 +2113,12 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); type &= 0xff; - /* In ARM, we don't need to touch the arguments. */ + SLJIT_ASSERT(reg_map[TMP_REG1] != 14); + #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) if (type >= SLJIT_FAST_CALL) PTR_FAIL_IF(prepare_blx(compiler)); - PTR_FAIL_IF(push_inst_with_unique_literal(compiler, ((EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, + PTR_FAIL_IF(push_inst_with_unique_literal(compiler, ((EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0)) & ~COND_MASK) | get_cc(type), 0)); if (jump->flags & SLJIT_REWRITABLE_JUMP) { @@ -2438,6 +2143,241 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile return jump; } +#ifdef __SOFTFP__ + +static sljit_s32 softfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src) +{ + sljit_s32 stack_offset = 0; + sljit_s32 arg_count = 0; + sljit_s32 word_arg_offset = 0; + sljit_s32 float_arg_count = 0; + sljit_s32 types = 0; + sljit_s32 src_offset = 4 * sizeof(sljit_sw); + sljit_u8 offsets[4]; + + if (src && FAST_IS_REG(*src)) + src_offset = reg_map[*src] * sizeof(sljit_sw); + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK); + + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + offsets[arg_count] = (sljit_u8)stack_offset; + stack_offset += sizeof(sljit_f32); + arg_count++; + float_arg_count++; + break; + case SLJIT_ARG_TYPE_F64: + if (stack_offset & 0x7) + stack_offset += sizeof(sljit_sw); + offsets[arg_count] = (sljit_u8)stack_offset; + stack_offset += sizeof(sljit_f64); + arg_count++; + float_arg_count++; + break; + default: + offsets[arg_count] = (sljit_u8)stack_offset; + stack_offset += sizeof(sljit_sw); + arg_count++; + word_arg_offset += sizeof(sljit_sw); + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + if (stack_offset > 16) + FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | (((stack_offset - 16) + 0x7) & ~0x7))); + + /* Process arguments in reversed direction. */ + while (types) { + switch (types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + arg_count--; + float_arg_count--; + stack_offset = offsets[arg_count]; + + if (stack_offset < 16) { + if (src_offset == stack_offset) { + FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2))); + *src = TMP_REG1; + } + FAIL_IF(push_inst(compiler, VMOV | 0x100000 | (float_arg_count << 16) | (stack_offset << 10))); + } else + FAIL_IF(push_inst(compiler, VSTR_F32 | 0x800000 | RN(SLJIT_SP) | (float_arg_count << 12) | ((stack_offset - 16) >> 2))); + break; + case SLJIT_ARG_TYPE_F64: + arg_count--; + float_arg_count--; + stack_offset = offsets[arg_count]; + + SLJIT_ASSERT((stack_offset & 0x7) == 0); + + if (stack_offset < 16) { + if (src_offset == stack_offset || src_offset == stack_offset + sizeof(sljit_sw)) { + FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2))); + *src = TMP_REG1; + } + FAIL_IF(push_inst(compiler, VMOV2 | 0x100000 | (stack_offset << 10) | ((stack_offset + sizeof(sljit_sw)) << 14) | float_arg_count)); + } else + FAIL_IF(push_inst(compiler, VSTR_F32 | 0x800100 | RN(SLJIT_SP) | (float_arg_count << 12) | ((stack_offset - 16) >> 2))); + break; + default: + arg_count--; + word_arg_offset -= sizeof(sljit_sw); + stack_offset = offsets[arg_count]; + + SLJIT_ASSERT(stack_offset >= word_arg_offset); + + if (stack_offset != word_arg_offset) { + if (stack_offset < 16) { + if (src_offset == stack_offset) { + FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2))); + *src = TMP_REG1; + } + else if (src_offset == word_arg_offset) { + *src = 1 + (stack_offset >> 2); + src_offset = stack_offset; + } + FAIL_IF(push_inst(compiler, MOV | (stack_offset << 10) | (word_arg_offset >> 2))); + } else + FAIL_IF(push_inst(compiler, data_transfer_insts[WORD_SIZE] | 0x800000 | RN(SLJIT_SP) | (word_arg_offset << 10) | (stack_offset - 16))); + } + break; + } + + types >>= SLJIT_DEF_SHIFT; + } + + return SLJIT_SUCCESS; +} + +static sljit_s32 softfloat_post_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types) +{ + sljit_s32 stack_size = 0; + + if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F32) + FAIL_IF(push_inst(compiler, VMOV | (0 << 16) | (0 << 12))); + if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F64) + FAIL_IF(push_inst(compiler, VMOV2 | (1 << 16) | (0 << 12) | 0)); + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + stack_size += sizeof(sljit_f32); + break; + case SLJIT_ARG_TYPE_F64: + if (stack_size & 0x7) + stack_size += sizeof(sljit_sw); + stack_size += sizeof(sljit_f64); + break; + default: + stack_size += sizeof(sljit_sw); + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + if (stack_size <= 16) + return SLJIT_SUCCESS; + + return push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | (((stack_size - 16) + 0x7) & ~0x7)); +} + +#else /* !__SOFTFP__ */ + +static sljit_s32 hardfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types) +{ + sljit_u32 remap = 0; + sljit_u32 offset = 0; + sljit_u32 new_offset, mask; + + /* Remove return value. */ + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F32) { + new_offset = 0; + mask = 1; + + while (remap & mask) { + new_offset++; + mask <<= 1; + } + remap |= mask; + + if (offset != new_offset) + FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32, + 0, (new_offset >> 1) + 1, (offset >> 1) + 1, 0) | ((new_offset & 0x1) ? 0x400000 : 0))); + + offset += 2; + } + else if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F64) { + new_offset = 0; + mask = 3; + + while (remap & mask) { + new_offset += 2; + mask <<= 2; + } + remap |= mask; + + if (offset != new_offset) + FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32, SLJIT_F32_OP, (new_offset >> 1) + 1, (offset >> 1) + 1, 0))); + + offset += 2; + } + arg_types >>= SLJIT_DEF_SHIFT; + } + + return SLJIT_SUCCESS; +} + +#endif /* __SOFTFP__ */ + +#undef EMIT_FPU_OPERATION + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ +#ifdef __SOFTFP__ + struct sljit_jump *jump; +#endif + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); + +#ifdef __SOFTFP__ + PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + jump = sljit_emit_jump(compiler, type); + PTR_FAIL_IF(jump == NULL); + + PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types)); + return jump; +#else /* !__SOFTFP__ */ + PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_jump(compiler, type); +#endif /* __SOFTFP__ */ +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) { struct sljit_jump *jump; @@ -2446,16 +2386,20 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); ADJUST_LOCAL_OFFSET(src, srcw); - /* In ARM, we don't need to touch the arguments. */ + SLJIT_ASSERT(reg_map[TMP_REG1] != 14); + if (!(src & SLJIT_IMM)) { - if (FAST_IS_REG(src)) + if (FAST_IS_REG(src)) { + SLJIT_ASSERT(reg_map[src] != 14); return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(src)); + } SLJIT_ASSERT(src & SLJIT_MEM); - FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw)); - return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG2)); + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1)); + return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1)); } + /* These jumps are converted to jump/call instructions when possible. */ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); FAIL_IF(!jump); set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0)); @@ -2464,7 +2408,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) if (type >= SLJIT_FAST_CALL) FAIL_IF(prepare_blx(compiler)); - FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0), 0)); + FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0), 0)); if (type >= SLJIT_FAST_CALL) FAIL_IF(emit_blx(compiler)); #else @@ -2475,57 +2419,221 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi return SLJIT_SUCCESS; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + +#ifdef __SOFTFP__ + if (src & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1)); + src = TMP_REG1; + } + + FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); + + return softfloat_post_call_with_args(compiler, arg_types); +#else /* !__SOFTFP__ */ + FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_ijump(compiler, type, src, srcw); +#endif /* __SOFTFP__ */ +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw, sljit_s32 type) { - sljit_s32 dst_r, flags = GET_ALL_FLAGS(op); + sljit_s32 dst_reg, flags = GET_ALL_FLAGS(op); sljit_uw cc, ins; CHECK_ERROR(); - CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type)); + CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); ADJUST_LOCAL_OFFSET(dst, dstw); - ADJUST_LOCAL_OFFSET(src, srcw); - - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; op = GET_OPCODE(op); cc = get_cc(type & 0xff); - dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; + dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG1; if (op < SLJIT_ADD) { - FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst_r, SLJIT_UNUSED, SRC2_IMM | 0))); - FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst_r, SLJIT_UNUSED, SRC2_IMM | 1) & ~COND_MASK) | cc)); - return (dst_r == TMP_REG2) ? emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw) : SLJIT_SUCCESS; + FAIL_IF(push_inst(compiler, MOV | RD(dst_reg) | SRC2_IMM | 0)); + FAIL_IF(push_inst(compiler, ((MOV | RD(dst_reg) | SRC2_IMM | 1) & ~COND_MASK) | cc)); + if (dst & SLJIT_MEM) + return emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2); + return SLJIT_SUCCESS; } - ins = (op == SLJIT_AND ? AND_DP : (op == SLJIT_OR ? ORR_DP : EOR_DP)); - if ((op == SLJIT_OR || op == SLJIT_XOR) && FAST_IS_REG(dst) && dst == src) { - FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(ins, 0, dst, dst, SRC2_IMM | 1) & ~COND_MASK) | cc)); - /* The condition must always be set, even if the ORR/EOR is not executed above. */ - return (flags & SLJIT_SET_E) ? push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, SET_FLAGS, TMP_REG1, SLJIT_UNUSED, RM(dst))) : SLJIT_SUCCESS; - } + ins = (op == SLJIT_AND ? AND : (op == SLJIT_OR ? ORR : EOR)); - compiler->cache_arg = 0; - compiler->cache_argw = 0; - if (src & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw)); - src = TMP_REG1; - srcw = 0; - } else if (src & SLJIT_IMM) { + if (dst & SLJIT_MEM) + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, dst, dstw, TMP_REG2)); + + FAIL_IF(push_inst(compiler, ((ins | RD(dst_reg) | RN(dst_reg) | SRC2_IMM | 1) & ~COND_MASK) | cc)); + + if (op == SLJIT_AND) + FAIL_IF(push_inst(compiler, ((ins | RD(dst_reg) | RN(dst_reg) | SRC2_IMM | 0) & ~COND_MASK) | (cc ^ 0x10000000))); + + if (dst & SLJIT_MEM) + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2)); + + if (flags & SLJIT_SET_Z) + return push_inst(compiler, MOV | SET_FLAGS | RD(TMP_REG2) | RM(dst_reg)); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ + sljit_uw cc, tmp; + + CHECK_ERROR(); + CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); + + dst_reg &= ~SLJIT_I32_OP; + + cc = get_cc(type & 0xff); + + if (SLJIT_UNLIKELY(src & SLJIT_IMM)) { + tmp = get_imm(srcw); + if (tmp) + return push_inst(compiler, ((MOV | RD(dst_reg) | tmp) & ~COND_MASK) | cc); + + tmp = get_imm(~srcw); + if (tmp) + return push_inst(compiler, ((MVN | RD(dst_reg) | tmp) & ~COND_MASK) | cc); + +#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) + tmp = (sljit_uw) srcw; + FAIL_IF(push_inst(compiler, (MOVW & ~COND_MASK) | cc | RD(dst_reg) | ((tmp << 4) & 0xf0000) | (tmp & 0xfff))); + if (tmp <= 0xffff) + return SLJIT_SUCCESS; + return push_inst(compiler, (MOVT & ~COND_MASK) | cc | RD(dst_reg) | ((tmp >> 12) & 0xf0000) | ((tmp >> 16) & 0xfff)); +#else FAIL_IF(load_immediate(compiler, TMP_REG1, srcw)); src = TMP_REG1; - srcw = 0; +#endif } - FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(ins, 0, dst_r, src, SRC2_IMM | 1) & ~COND_MASK) | cc)); - FAIL_IF(push_inst(compiler, (EMIT_DATA_PROCESS_INS(ins, 0, dst_r, src, SRC2_IMM | 0) & ~COND_MASK) | (cc ^ 0x10000000))); - if (dst_r == TMP_REG2) - FAIL_IF(emit_op_mem2(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0)); + return push_inst(compiler, ((MOV | RD(dst_reg) | RM(src)) & ~COND_MASK) | cc); +} - return (flags & SLJIT_SET_E) ? push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, SET_FLAGS, TMP_REG1, SLJIT_UNUSED, RM(dst_r))) : SLJIT_SUCCESS; +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_s32 flags; + sljit_uw is_type1_transfer, inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + + is_type1_transfer = 1; + + switch (type & 0xff) { + case SLJIT_MOV: + case SLJIT_MOV_U32: + case SLJIT_MOV_S32: + case SLJIT_MOV_P: + flags = WORD_SIZE; + break; + case SLJIT_MOV_U8: + flags = BYTE_SIZE; + break; + case SLJIT_MOV_S8: + if (!(type & SLJIT_MEM_STORE)) + is_type1_transfer = 0; + flags = BYTE_SIZE | SIGNED; + break; + case SLJIT_MOV_U16: + is_type1_transfer = 0; + flags = HALF_SIZE; + break; + case SLJIT_MOV_S16: + is_type1_transfer = 0; + flags = HALF_SIZE | SIGNED; + break; + default: + SLJIT_UNREACHABLE(); + flags = WORD_SIZE; + break; + } + + if (!(type & SLJIT_MEM_STORE)) + flags |= LOAD_DATA; + + SLJIT_ASSERT(is_type1_transfer == !!IS_TYPE1_TRANSFER(flags)); + + if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) { + if (!is_type1_transfer && memw != 0) + return SLJIT_ERR_UNSUPPORTED; + } + else { + if (is_type1_transfer) { + if (memw > 4095 && memw < -4095) + return SLJIT_ERR_UNSUPPORTED; + } + else { + if (memw > 255 && memw < -255) + return SLJIT_ERR_UNSUPPORTED; + } + } + + if (type & SLJIT_MEM_SUPP) + return SLJIT_SUCCESS; + + if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) { + memw &= 0x3; + + inst = EMIT_DATA_TRANSFER(flags, 1, reg, mem & REG_MASK, RM(OFFS_REG(mem)) | (memw << 7)); + + if (is_type1_transfer) + inst |= (1 << 25); + + if (type & SLJIT_MEM_PRE) + inst |= (1 << 21); + else + inst ^= (1 << 24); + + return push_inst(compiler, inst); + } + + inst = EMIT_DATA_TRANSFER(flags, 0, reg, mem & REG_MASK, 0); + + if (type & SLJIT_MEM_PRE) + inst |= (1 << 21); + else + inst ^= (1 << 24); + + if (is_type1_transfer) { + if (memw >= 0) + inst |= (1 << 23); + else + memw = -memw; + + return push_inst(compiler, inst | memw); + } + + if (memw >= 0) + inst |= (1 << 23); + else + memw = -memw; + + return push_inst(compiler, inst | TYPE2_TRANSFER_IMM(memw)); } SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) @@ -2543,7 +2651,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi reg = SLOW_IS_REG(dst) ? dst : TMP_REG2; #if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) - PTR_FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, reg, TMP_PC, 0), init_value)); + PTR_FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, reg, TMP_PC, 0), init_value)); compiler->patches++; #else PTR_FAIL_IF(emit_imm(compiler, reg, init_value)); @@ -2551,16 +2659,16 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi set_const(const_, compiler); if (dst & SLJIT_MEM) - PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw)); + PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, dst, dstw, TMP_REG1)); return const_; } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) { - inline_set_jump_addr(addr, new_addr, 1); + inline_set_jump_addr(addr, executable_offset, new_target, 1); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) { - inline_set_const(addr, new_constant, 1); + inline_set_const(addr, executable_offset, new_constant, 1); } diff --git a/pcre2-10.22/src/sljit/sljitNativeARM_64.c b/pcre2-10.32/src/sljit/sljitNativeARM_64.c similarity index 65% rename from pcre2-10.22/src/sljit/sljitNativeARM_64.c rename to pcre2-10.32/src/sljit/sljitNativeARM_64.c index d9958512c..27af74148 100644 --- a/pcre2-10.22/src/sljit/sljitNativeARM_64.c +++ b/pcre2-10.32/src/sljit/sljitNativeARM_64.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -36,15 +36,19 @@ typedef sljit_u32 sljit_ins; #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 TMP_LR (SLJIT_NUMBER_OF_REGISTERS + 5) -#define TMP_SP (SLJIT_NUMBER_OF_REGISTERS + 6) +#define TMP_LR (SLJIT_NUMBER_OF_REGISTERS + 4) +#define TMP_FP (SLJIT_NUMBER_OF_REGISTERS + 5) -#define TMP_FREG1 (0) -#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2) +/* r18 - platform register, currently not used */ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = { - 31, 0, 1, 2, 3, 4, 5, 6, 7, 12, 13, 14, 15, 16, 17, 8, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 29, 9, 10, 11, 30, 31 + 31, 0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 8, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 31, 9, 10, 30, 29 +}; + +static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { + 0, 0, 1, 2, 3, 4, 5, 6, 7 }; #define W_OP (1 << 31) @@ -53,10 +57,10 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = { #define RN(rn) (reg_map[rn] << 5) #define RT2(rt2) (reg_map[rt2] << 10) #define RM(rm) (reg_map[rm] << 16) -#define VD(vd) (vd) -#define VT(vt) (vt) -#define VN(vn) ((vn) << 5) -#define VM(vm) ((vm) << 16) +#define VD(vd) (freg_map[vd]) +#define VT(vt) (freg_map[vt]) +#define VN(vn) (freg_map[vn] << 5) +#define VM(vm) (freg_map[vm] << 16) /* --------------------------------------------------------------------- */ /* Instrucion forms */ @@ -64,6 +68,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = { #define ADC 0x9a000000 #define ADD 0x8b000000 +#define ADDE 0x8b200000 #define ADDI 0x91000000 #define AND 0x8a000000 #define ANDI 0x92000000 @@ -76,6 +81,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = { #define BRK 0xd4200000 #define CBZ 0xb4000000 #define CLZ 0xdac01000 +#define CSEL 0x9a800000 #define CSINC 0x9a800400 #define EOR 0xca000000 #define EORI 0xd2000000 @@ -91,7 +97,8 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = { #define FSUB 0x1e603800 #define LDRI 0xf9400000 #define LDP 0xa9400000 -#define LDP_PST 0xa8c00000 +#define LDP_PRE 0xa9c00000 +#define LDR_PRE 0xf8400c00 #define LSLV 0x9ac02000 #define LSRV 0x9ac02400 #define MADD 0x9b000000 @@ -111,10 +118,13 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = { #define SMULH 0x9b403c00 #define STP 0xa9000000 #define STP_PRE 0xa9800000 +#define STRB 0x38206800 +#define STRBI 0x39000000 #define STRI 0xf9000000 #define STR_FI 0x3d000000 #define STR_FR 0x3c206800 #define STUR_FI 0x3c000000 +#define STURBI 0x38000000 #define SUB 0xcb000000 #define SUBI 0xd1000000 #define SUBS 0xeb000000 @@ -151,7 +161,7 @@ static SLJIT_INLINE void modify_imm64_const(sljit_ins* inst, sljit_uw new_imm) inst[3] = MOVK | dst | ((new_imm >> 48) << 5) | (3 << 21); } -static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code) +static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code, sljit_sw executable_offset) { sljit_sw diff; sljit_uw target_addr; @@ -165,9 +175,10 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_in target_addr = jump->u.target; else { SLJIT_ASSERT(jump->flags & JUMP_LABEL); - target_addr = (sljit_uw)(code + jump->u.label->size); + target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset; } - diff = (sljit_sw)target_addr - (sljit_sw)(code_ptr + 4); + + diff = (sljit_sw)target_addr - (sljit_sw)(code_ptr + 4) - executable_offset; if (jump->flags & IS_COND) { diff += sizeof(sljit_ins); @@ -191,6 +202,7 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_in code_ptr[-2] = code_ptr[0]; return 2; } + if (target_addr <= 0xffffffffffffl) { if (jump->flags & IS_COND) code_ptr[-5] -= (1 << 5); @@ -211,6 +223,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil sljit_ins *buf_ptr; sljit_ins *buf_end; sljit_uw word_count; + sljit_sw executable_offset; sljit_uw addr; sljit_s32 dst; @@ -228,6 +241,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil code_ptr = code; word_count = 0; + executable_offset = SLJIT_EXEC_OFFSET(code); + label = compiler->labels; jump = compiler->jumps; const_ = compiler->consts; @@ -242,13 +257,13 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil SLJIT_ASSERT(!jump || jump->addr >= word_count); SLJIT_ASSERT(!const_ || const_->addr >= word_count); if (label && label->size == word_count) { - label->addr = (sljit_uw)code_ptr; + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); label->size = code_ptr - code; label = label->next; } if (jump && jump->addr == word_count) { jump->addr = (sljit_uw)(code_ptr - 4); - code_ptr -= detect_jump_type(jump, code_ptr, code); + code_ptr -= detect_jump_type(jump, code_ptr, code, executable_offset); jump = jump->next; } if (const_ && const_->addr == word_count) { @@ -263,7 +278,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } while (buf); if (label && label->size == word_count) { - label->addr = (sljit_uw)code_ptr; + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); label->size = code_ptr - code; label = label->next; } @@ -277,9 +292,10 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil while (jump) { do { addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; - buf_ptr = (sljit_ins*)jump->addr; + buf_ptr = (sljit_ins *)jump->addr; + if (jump->flags & PATCH_B) { - addr = (sljit_sw)(addr - jump->addr) >> 2; + addr = (sljit_sw)(addr - (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset)) >> 2; SLJIT_ASSERT((sljit_sw)addr <= 0x1ffffff && (sljit_sw)addr >= -0x2000000); buf_ptr[0] = ((jump->flags & IS_BL) ? BL : B) | (addr & 0x3ffffff); if (jump->flags & IS_COND) @@ -287,7 +303,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil break; } if (jump->flags & PATCH_COND) { - addr = (sljit_sw)(addr - jump->addr) >> 2; + addr = (sljit_sw)(addr - (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset)) >> 2; SLJIT_ASSERT((sljit_sw)addr <= 0x3ffff && (sljit_sw)addr >= -0x40000); buf_ptr[0] = (buf_ptr[0] & ~0xffffe0) | ((addr & 0x7ffff) << 5); break; @@ -308,11 +324,36 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_offset = executable_offset; compiler->executable_size = (code_ptr - code) * sizeof(sljit_ins); + + code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset); + code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); + SLJIT_CACHE_FLUSH(code, code_ptr); return code; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) +{ + switch (feature_type) { + case SLJIT_HAS_FPU: +#ifdef SLJIT_IS_FPU_AVAILABLE + return SLJIT_IS_FPU_AVAILABLE; +#else + /* Available by default. */ + return 1; +#endif + + case SLJIT_HAS_CLZ: + case SLJIT_HAS_CMOV: + return 1; + + default: + return 0; + } +} + /* --------------------------------------------------------------------- */ /* Core code generator functions. */ /* --------------------------------------------------------------------- */ @@ -362,12 +403,14 @@ static sljit_ins logical_imm(sljit_sw imm, sljit_s32 len) SLJIT_ASSERT((len == 32 && imm != 0 && imm != -1) || (len == 16 && (sljit_s32)imm != 0 && (sljit_s32)imm != -1)); + uimm = (sljit_uw)imm; while (1) { if (len <= 0) { - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return 0; } + mask = ((sljit_uw)1 << len) - 1; if ((uimm & mask) != ((uimm >> len) & mask)) break; @@ -416,39 +459,42 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst, sljit_s32 i, zeros, ones, first; sljit_ins bitmask; + /* Handling simple immediates first. */ if (imm <= 0xffff) return push_inst(compiler, MOVZ | RD(dst) | (imm << 5)); - if (simm >= -0x10000 && simm < 0) + if (simm < 0 && simm >= -0x10000) return push_inst(compiler, MOVN | RD(dst) | ((~imm & 0xffff) << 5)); if (imm <= 0xffffffffl) { + if ((imm & 0xffff) == 0) + return push_inst(compiler, MOVZ | RD(dst) | ((imm >> 16) << 5) | (1 << 21)); if ((imm & 0xffff0000l) == 0xffff0000) return push_inst(compiler, (MOVN ^ W_OP) | RD(dst) | ((~imm & 0xffff) << 5)); if ((imm & 0xffff) == 0xffff) return push_inst(compiler, (MOVN ^ W_OP) | RD(dst) | ((~imm & 0xffff0000l) >> (16 - 5)) | (1 << 21)); + bitmask = logical_imm(simm, 16); if (bitmask != 0) return push_inst(compiler, (ORRI ^ W_OP) | RD(dst) | RN(TMP_ZERO) | bitmask); - } - else { - bitmask = logical_imm(simm, 32); - if (bitmask != 0) - return push_inst(compiler, ORRI | RD(dst) | RN(TMP_ZERO) | bitmask); - } - if (imm <= 0xffffffffl) { FAIL_IF(push_inst(compiler, MOVZ | RD(dst) | ((imm & 0xffff) << 5))); return push_inst(compiler, MOVK | RD(dst) | ((imm & 0xffff0000l) >> (16 - 5)) | (1 << 21)); } - if (simm >= -0x100000000l && simm < 0) { + bitmask = logical_imm(simm, 32); + if (bitmask != 0) + return push_inst(compiler, ORRI | RD(dst) | RN(TMP_ZERO) | bitmask); + + if (simm < 0 && simm >= -0x100000000l) { + if ((imm & 0xffff) == 0xffff) + return push_inst(compiler, MOVN | RD(dst) | ((~imm & 0xffff0000l) >> (16 - 5)) | (1 << 21)); + FAIL_IF(push_inst(compiler, MOVN | RD(dst) | ((~imm & 0xffff) << 5))); return push_inst(compiler, MOVK | RD(dst) | ((imm & 0xffff0000l) >> (16 - 5)) | (1 << 21)); } - /* A large amount of number can be constructed from ORR and MOVx, - but computing them is costly. We don't */ + /* A large amount of number can be constructed from ORR and MOVx, but computing them is costly. */ zeros = 0; ones = 0; @@ -501,9 +547,6 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst, #define INT_OP 0x0040000 #define SET_FLAGS 0x0080000 #define UNUSED_RETURN 0x0100000 -#define SLOW_DEST 0x0200000 -#define SLOW_SRC1 0x0400000 -#define SLOW_SRC2 0x0800000 #define CHECK_FLAGS(flag_bits) \ if (flags & SET_FLAGS) { \ @@ -635,7 +678,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s } goto set_flags; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } @@ -661,40 +704,32 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s switch (op) { case SLJIT_MOV: case SLJIT_MOV_P: - case SLJIT_MOVU: - case SLJIT_MOVU_P: SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); if (dst == arg2) return SLJIT_SUCCESS; return push_inst(compiler, ORR | RD(dst) | RN(TMP_ZERO) | RM(arg2)); case SLJIT_MOV_U8: - case SLJIT_MOVU_U8: SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); return push_inst(compiler, (UBFM ^ (1 << 31)) | RD(dst) | RN(arg2) | (7 << 10)); case SLJIT_MOV_S8: - case SLJIT_MOVU_S8: SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); if (!(flags & INT_OP)) inv_bits |= 1 << 22; return push_inst(compiler, (SBFM ^ inv_bits) | RD(dst) | RN(arg2) | (7 << 10)); case SLJIT_MOV_U16: - case SLJIT_MOVU_U16: SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); return push_inst(compiler, (UBFM ^ (1 << 31)) | RD(dst) | RN(arg2) | (15 << 10)); case SLJIT_MOV_S16: - case SLJIT_MOVU_S16: SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); if (!(flags & INT_OP)) inv_bits |= 1 << 22; return push_inst(compiler, (SBFM ^ inv_bits) | RD(dst) | RN(arg2) | (15 << 10)); case SLJIT_MOV_U32: - case SLJIT_MOVU_U32: SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); if ((flags & INT_OP) && dst == arg2) return SLJIT_SUCCESS; return push_inst(compiler, (ORR ^ (1 << 31)) | RD(dst) | RN(TMP_ZERO) | RM(arg2)); case SLJIT_MOV_S32: - case SLJIT_MOVU_S32: SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); if ((flags & INT_OP) && dst == arg2) return SLJIT_SUCCESS; @@ -702,7 +737,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s case SLJIT_NOT: SLJIT_ASSERT(arg1 == TMP_REG1); FAIL_IF(push_inst(compiler, (ORN ^ inv_bits) | RD(dst) | RN(TMP_ZERO) | RM(arg2))); - goto set_flags; + break; /* Set flags. */ case SLJIT_NEG: SLJIT_ASSERT(arg1 == TMP_REG1); if (flags & SET_FLAGS) @@ -710,8 +745,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s return push_inst(compiler, (SUB ^ inv_bits) | RD(dst) | RN(TMP_ZERO) | RM(arg2)); case SLJIT_CLZ: SLJIT_ASSERT(arg1 == TMP_REG1); - FAIL_IF(push_inst(compiler, (CLZ ^ inv_bits) | RD(dst) | RN(arg2))); - goto set_flags; + return push_inst(compiler, (CLZ ^ inv_bits) | RD(dst) | RN(arg2)); case SLJIT_ADD: CHECK_FLAGS(1 << 29); return push_inst(compiler, (ADD ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)); @@ -740,320 +774,91 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s return push_inst(compiler, (AND ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)); case SLJIT_OR: FAIL_IF(push_inst(compiler, (ORR ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2))); - goto set_flags; + break; /* Set flags. */ case SLJIT_XOR: FAIL_IF(push_inst(compiler, (EOR ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2))); - goto set_flags; + break; /* Set flags. */ case SLJIT_SHL: FAIL_IF(push_inst(compiler, (LSLV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2))); - goto set_flags; + break; /* Set flags. */ case SLJIT_LSHR: FAIL_IF(push_inst(compiler, (LSRV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2))); - goto set_flags; + break; /* Set flags. */ case SLJIT_ASHR: FAIL_IF(push_inst(compiler, (ASRV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2))); - goto set_flags; + break; /* Set flags. */ + default: + SLJIT_UNREACHABLE(); + return SLJIT_SUCCESS; } - SLJIT_ASSERT_STOP(); - return SLJIT_SUCCESS; - set_flags: if (flags & SET_FLAGS) return push_inst(compiler, (SUBS ^ inv_bits) | RD(TMP_ZERO) | RN(dst) | RM(TMP_ZERO)); return SLJIT_SUCCESS; } -#define STORE 0x01 -#define SIGNED 0x02 +#define STORE 0x10 +#define SIGNED 0x20 -#define UPDATE 0x04 -#define ARG_TEST 0x08 +#define BYTE_SIZE 0x0 +#define HALF_SIZE 0x1 +#define INT_SIZE 0x2 +#define WORD_SIZE 0x3 -#define BYTE_SIZE 0x000 -#define HALF_SIZE 0x100 -#define INT_SIZE 0x200 -#define WORD_SIZE 0x300 +#define MEM_SIZE_SHIFT(flags) ((flags) & 0x3) -#define MEM_SIZE_SHIFT(flags) ((flags) >> 8) - -static const sljit_ins sljit_mem_imm[4] = { -/* u l */ 0x39400000 /* ldrb [reg,imm] */, -/* u s */ 0x39000000 /* strb [reg,imm] */, -/* s l */ 0x39800000 /* ldrsb [reg,imm] */, -/* s s */ 0x39000000 /* strb [reg,imm] */, -}; - -static const sljit_ins sljit_mem_simm[4] = { -/* u l */ 0x38400000 /* ldurb [reg,imm] */, -/* u s */ 0x38000000 /* sturb [reg,imm] */, -/* s l */ 0x38800000 /* ldursb [reg,imm] */, -/* s s */ 0x38000000 /* sturb [reg,imm] */, -}; - -static const sljit_ins sljit_mem_pre_simm[4] = { -/* u l */ 0x38400c00 /* ldrb [reg,imm]! */, -/* u s */ 0x38000c00 /* strb [reg,imm]! */, -/* s l */ 0x38800c00 /* ldrsb [reg,imm]! */, -/* s s */ 0x38000c00 /* strb [reg,imm]! */, -}; - -static const sljit_ins sljit_mem_reg[4] = { -/* u l */ 0x38606800 /* ldrb [reg,reg] */, -/* u s */ 0x38206800 /* strb [reg,reg] */, -/* s l */ 0x38a06800 /* ldrsb [reg,reg] */, -/* s s */ 0x38206800 /* strb [reg,reg] */, -}; - -/* Helper function. Dst should be reg + value, using at most 1 instruction, flags does not set. */ -static sljit_s32 emit_set_delta(struct sljit_compiler *compiler, sljit_s32 dst, sljit_s32 reg, sljit_sw value) -{ - if (value >= 0) { - if (value <= 0xfff) - return push_inst(compiler, ADDI | RD(dst) | RN(reg) | (value << 10)); - if (value <= 0xffffff && !(value & 0xfff)) - return push_inst(compiler, ADDI | (1 << 22) | RD(dst) | RN(reg) | (value >> 2)); - } - else { - value = -value; - if (value <= 0xfff) - return push_inst(compiler, SUBI | RD(dst) | RN(reg) | (value << 10)); - if (value <= 0xffffff && !(value & 0xfff)) - return push_inst(compiler, SUBI | (1 << 22) | RD(dst) | RN(reg) | (value >> 2)); - } - return SLJIT_ERR_UNSUPPORTED; -} - -/* Can perform an operation using at most 1 instruction. */ -static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) +static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, + sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg) { sljit_u32 shift = MEM_SIZE_SHIFT(flags); + sljit_u32 type = (shift << 30); + + if (!(flags & STORE)) + type |= (flags & SIGNED) ? 0x00800000 : 0x00400000; SLJIT_ASSERT(arg & SLJIT_MEM); - if (SLJIT_UNLIKELY(flags & UPDATE)) { - if ((arg & REG_MASK) && !(arg & OFFS_REG_MASK) && argw <= 255 && argw >= -256) { - if (SLJIT_UNLIKELY(flags & ARG_TEST)) - return 1; - - arg &= REG_MASK; - argw &= 0x1ff; - FAIL_IF(push_inst(compiler, sljit_mem_pre_simm[flags & 0x3] - | (shift << 30) | RT(reg) | RN(arg) | (argw << 12))); - return -1; - } - return 0; - } - if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { argw &= 0x3; - if (argw && argw != shift) - return 0; - if (SLJIT_UNLIKELY(flags & ARG_TEST)) - return 1; + if (argw == 0 || argw == shift) + return push_inst(compiler, STRB | type | RT(reg) + | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw ? (1 << 12) : 0)); - FAIL_IF(push_inst(compiler, sljit_mem_reg[flags & 0x3] | (shift << 30) | RT(reg) - | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw ? (1 << 12) : 0))); - return -1; + FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw << 10))); + return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg)); } arg &= REG_MASK; - if (argw >= 0 && (argw >> shift) <= 0xfff && (argw & ((1 << shift) - 1)) == 0) { - if (SLJIT_UNLIKELY(flags & ARG_TEST)) - return 1; - FAIL_IF(push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) - | RT(reg) | RN(arg) | (argw << (10 - shift)))); - return -1; + if (arg == SLJIT_UNUSED) { + FAIL_IF(load_immediate(compiler, tmp_reg, argw & ~(0xfff << shift))); + + argw = (argw >> shift) & 0xfff; + + return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | (argw << 10)); } - if (argw > 255 || argw < -256) - return 0; - - if (SLJIT_UNLIKELY(flags & ARG_TEST)) - return 1; - - FAIL_IF(push_inst(compiler, sljit_mem_simm[flags & 0x3] | (shift << 30) - | RT(reg) | RN(arg) | ((argw & 0x1ff) << 12))); - return -1; -} - -/* see getput_arg below. - Note: can_cache is called only for binary operators. Those - operators always uses word arguments without write back. */ -static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) -{ - sljit_sw diff; - if ((arg & OFFS_REG_MASK) || !(next_arg & SLJIT_MEM)) - return 0; - - if (!(arg & REG_MASK)) { - diff = argw - next_argw; - if (diff <= 0xfff && diff >= -0xfff) - return 1; - return 0; - } - - if (argw == next_argw) - return 1; - - diff = argw - next_argw; - if (arg == next_arg && diff <= 0xfff && diff >= -0xfff) - return 1; - - return 0; -} - -/* Emit the necessary instructions. See can_cache above. */ -static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, - sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) -{ - sljit_u32 shift = MEM_SIZE_SHIFT(flags); - sljit_s32 tmp_r, other_r; - sljit_sw diff; - - SLJIT_ASSERT(arg & SLJIT_MEM); - if (!(next_arg & SLJIT_MEM)) { - next_arg = 0; - next_argw = 0; - } - - tmp_r = (flags & STORE) ? TMP_REG3 : reg; - - if (SLJIT_UNLIKELY((flags & UPDATE) && (arg & REG_MASK))) { - /* Update only applies if a base register exists. */ - other_r = OFFS_REG(arg); - if (!other_r) { - other_r = arg & REG_MASK; - if (other_r != reg && argw >= 0 && argw <= 0xffffff) { - if ((argw & 0xfff) != 0) - FAIL_IF(push_inst(compiler, ADDI | RD(other_r) | RN(other_r) | ((argw & 0xfff) << 10))); - if (argw >> 12) - FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(other_r) | RN(other_r) | ((argw >> 12) << 10))); - return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(other_r)); - } - else if (other_r != reg && argw < 0 && argw >= -0xffffff) { - argw = -argw; - if ((argw & 0xfff) != 0) - FAIL_IF(push_inst(compiler, SUBI | RD(other_r) | RN(other_r) | ((argw & 0xfff) << 10))); - if (argw >> 12) - FAIL_IF(push_inst(compiler, SUBI | (1 << 22) | RD(other_r) | RN(other_r) | ((argw >> 12) << 10))); - return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(other_r)); - } - - if (compiler->cache_arg == SLJIT_MEM) { - if (argw == compiler->cache_argw) { - other_r = TMP_REG3; - argw = 0; - } - else if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - compiler->cache_argw = argw; - other_r = TMP_REG3; - argw = 0; - } - } - - if (argw) { - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); - compiler->cache_arg = SLJIT_MEM; - compiler->cache_argw = argw; - other_r = TMP_REG3; - argw = 0; - } + if (argw >= 0 && (argw & ((1 << shift) - 1)) == 0) { + if ((argw >> shift) <= 0xfff) { + return push_inst(compiler, STRBI | type | RT(reg) | RN(arg) | (argw << (10 - shift))); } - /* No caching here. */ - arg &= REG_MASK; - argw &= 0x3; - if (!argw || argw == shift) { - FAIL_IF(push_inst(compiler, sljit_mem_reg[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg) | RM(other_r) | (argw ? (1 << 12) : 0))); - return push_inst(compiler, ADD | RD(arg) | RN(arg) | RM(other_r) | (argw << 10)); - } - if (arg != reg) { - FAIL_IF(push_inst(compiler, ADD | RD(arg) | RN(arg) | RM(other_r) | (argw << 10))); - return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg)); - } - FAIL_IF(push_inst(compiler, ADD | RD(TMP_LR) | RN(arg) | RM(other_r) | (argw << 10))); - FAIL_IF(push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(TMP_LR))); - return push_inst(compiler, ORR | RD(arg) | RN(TMP_ZERO) | RM(TMP_LR)); - } + if (argw <= 0xffffff) { + FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_reg) | RN(arg) | ((argw >> 12) << 10))); - if (arg & OFFS_REG_MASK) { - other_r = OFFS_REG(arg); - arg &= REG_MASK; - FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RN(arg) | RM(other_r) | ((argw & 0x3) << 10))); - return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(tmp_r)); - } - - if (compiler->cache_arg == arg) { - diff = argw - compiler->cache_argw; - if (diff <= 255 && diff >= -256) - return push_inst(compiler, sljit_mem_simm[flags & 0x3] | (shift << 30) - | RT(reg) | RN(TMP_REG3) | ((diff & 0x1ff) << 12)); - if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, diff) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg)); + argw = ((argw & 0xfff) >> shift); + return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | (argw << 10)); } } - if (argw >= 0 && argw <= 0xffffff && (argw & ((1 << shift) - 1)) == 0) { - FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_r) | RN(arg & REG_MASK) | ((argw >> 12) << 10))); - return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) - | RT(reg) | RN(tmp_r) | ((argw & 0xfff) << (10 - shift))); - } + if (argw <= 255 && argw >= -256) + return push_inst(compiler, STURBI | type | RT(reg) | RN(arg) | ((argw & 0x1ff) << 12)); - diff = argw - next_argw; - next_arg = (arg & REG_MASK) && (arg == next_arg) && diff <= 0xfff && diff >= -0xfff && diff != 0; - arg &= REG_MASK; + FAIL_IF(load_immediate(compiler, tmp_reg, argw)); - if (arg && compiler->cache_arg == SLJIT_MEM) { - if (compiler->cache_argw == argw) - return push_inst(compiler, sljit_mem_reg[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg) | RM(TMP_REG3)); - if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - compiler->cache_argw = argw; - return push_inst(compiler, sljit_mem_reg[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg) | RM(TMP_REG3)); - } - } - - compiler->cache_argw = argw; - if (next_arg && emit_set_delta(compiler, TMP_REG3, arg, argw) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - compiler->cache_arg = SLJIT_MEM | arg; - arg = 0; - } - else { - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); - compiler->cache_arg = SLJIT_MEM; - - if (next_arg) { - FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RN(TMP_REG3) | RM(arg))); - compiler->cache_arg = SLJIT_MEM | arg; - arg = 0; - } - } - - if (arg) - return push_inst(compiler, sljit_mem_reg[flags & 0x3] | (shift << 30) | RT(reg) | RN(arg) | RM(TMP_REG3)); - return push_inst(compiler, sljit_mem_imm[flags & 0x3] | (shift << 30) | RT(reg) | RN(TMP_REG3)); -} - -static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) -{ - if (getput_arg_fast(compiler, flags, reg, arg, argw)) - return compiler->error; - compiler->cache_arg = 0; - compiler->cache_argw = 0; - return getput_arg(compiler, flags, reg, arg, argw, 0, 0); -} - -static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w) -{ - if (getput_arg_fast(compiler, flags, reg, arg1, arg1w)) - return compiler->error; - return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w); + return push_inst(compiler, STRB | type | RT(reg) | RN(arg) | RM(tmp_reg)); } /* --------------------------------------------------------------------- */ @@ -1061,82 +866,62 @@ static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, slji /* --------------------------------------------------------------------- */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { - sljit_s32 i, tmp, offs, prev, saved_regs_size; + sljit_s32 args, i, tmp, offs, prev, saved_regs_size; CHECK_ERROR(); - 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); + CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); + + saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 2); + if (saved_regs_size & 0x8) + saved_regs_size += sizeof(sljit_sw); - saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 0); - local_size += saved_regs_size + SLJIT_LOCALS_OFFSET; local_size = (local_size + 15) & ~0xf; - compiler->local_size = local_size; + compiler->local_size = local_size + saved_regs_size; - if (local_size <= (63 * sizeof(sljit_sw))) { - FAIL_IF(push_inst(compiler, STP_PRE | 29 | RT2(TMP_LR) - | RN(TMP_SP) | ((-(local_size >> 3) & 0x7f) << 15))); - FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(TMP_SP) | (0 << 10))); - offs = (local_size - saved_regs_size) << (15 - 3); - } else { - offs = 0 << 15; - if (saved_regs_size & 0x8) { - offs = 1 << 15; - saved_regs_size += sizeof(sljit_sw); - } - local_size -= saved_regs_size + SLJIT_LOCALS_OFFSET; - if (saved_regs_size > 0) - FAIL_IF(push_inst(compiler, SUBI | RD(TMP_SP) | RN(TMP_SP) | (saved_regs_size << 10))); - } + FAIL_IF(push_inst(compiler, STP_PRE | RT(TMP_FP) | RT2(TMP_LR) + | RN(SLJIT_SP) | ((-(saved_regs_size >> 3) & 0x7f) << 15))); + +#ifdef _WIN32 + if (local_size >= 4096) + FAIL_IF(push_inst(compiler, SUBI | RD(TMP_REG1) | RN(SLJIT_SP) | (1 << 10) | (1 << 22))); + else if (local_size > 256) + FAIL_IF(push_inst(compiler, SUBI | RD(TMP_REG1) | RN(SLJIT_SP) | (local_size << 10))); +#endif tmp = saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - saveds) : SLJIT_FIRST_SAVED_REG; prev = -1; + offs = 2 << 15; for (i = SLJIT_S0; i >= tmp; i--) { if (prev == -1) { - if (!(offs & (1 << 15))) { - prev = i; - continue; - } - FAIL_IF(push_inst(compiler, STRI | RT(i) | RN(TMP_SP) | (offs >> 5))); - offs += 1 << 15; + prev = i; continue; } - FAIL_IF(push_inst(compiler, STP | RT(prev) | RT2(i) | RN(TMP_SP) | offs)); + FAIL_IF(push_inst(compiler, STP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs)); offs += 2 << 15; prev = -1; } for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) { if (prev == -1) { - if (!(offs & (1 << 15))) { - prev = i; - continue; - } - FAIL_IF(push_inst(compiler, STRI | RT(i) | RN(TMP_SP) | (offs >> 5))); - offs += 1 << 15; + prev = i; continue; } - FAIL_IF(push_inst(compiler, STP | RT(prev) | RT2(i) | RN(TMP_SP) | offs)); + FAIL_IF(push_inst(compiler, STP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs)); offs += 2 << 15; prev = -1; } - SLJIT_ASSERT(prev == -1); + if (prev != -1) + FAIL_IF(push_inst(compiler, STRI | RT(prev) | RN(SLJIT_SP) | (offs >> 5))); - if (compiler->local_size > (63 * sizeof(sljit_sw))) { - /* The local_size is already adjusted by the saved registers. */ - if (local_size > 0xfff) { - FAIL_IF(push_inst(compiler, SUBI | RD(TMP_SP) | RN(TMP_SP) | ((local_size >> 12) << 10) | (1 << 22))); - local_size &= 0xfff; - } - if (local_size) - FAIL_IF(push_inst(compiler, SUBI | RD(TMP_SP) | RN(TMP_SP) | (local_size << 10))); - FAIL_IF(push_inst(compiler, STP_PRE | 29 | RT2(TMP_LR) - | RN(TMP_SP) | ((-(16 >> 3) & 0x7f) << 15))); - FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(TMP_SP) | (0 << 10))); - } + + FAIL_IF(push_inst(compiler, ADDI | RD(TMP_FP) | RN(SLJIT_SP) | (0 << 10))); + + args = get_arg_count(arg_types); if (args >= 1) FAIL_IF(push_inst(compiler, ORR | RD(SLJIT_S0) | RN(TMP_ZERO) | RM(SLJIT_R0))); @@ -1145,20 +930,82 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi if (args >= 3) FAIL_IF(push_inst(compiler, ORR | RD(SLJIT_S2) | RN(TMP_ZERO) | RM(SLJIT_R2))); +#ifdef _WIN32 + if (local_size >= 4096) { + if (local_size < 4 * 4096) { + /* No need for a loop. */ + if (local_size >= 2 * 4096) { + FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(TMP_REG1))); + FAIL_IF(push_inst(compiler, SUBI | RD(TMP_REG1) | RN(TMP_REG1) | (1 << 10) | (1 << 22))); + local_size -= 4096; + } + + if (local_size >= 2 * 4096) { + FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(TMP_REG1))); + FAIL_IF(push_inst(compiler, SUBI | RD(TMP_REG1) | RN(TMP_REG1) | (1 << 10) | (1 << 22))); + local_size -= 4096; + } + + FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(TMP_REG1))); + local_size -= 4096; + } + else { + FAIL_IF(push_inst(compiler, MOVZ | RD(TMP_REG2) | (((local_size >> 12) - 1) << 5))); + FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(TMP_REG1))); + FAIL_IF(push_inst(compiler, SUBI | RD(TMP_REG1) | RN(TMP_REG1) | (1 << 10) | (1 << 22))); + FAIL_IF(push_inst(compiler, SUBI | (1 << 29) | RD(TMP_REG2) | RN(TMP_REG2) | (1 << 10))); + FAIL_IF(push_inst(compiler, B_CC | ((((sljit_ins) -3) & 0x7ffff) << 5) | 0x1 /* not-equal */)); + FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(TMP_REG1))); + + local_size &= 0xfff; + } + + if (local_size > 256) { + FAIL_IF(push_inst(compiler, SUBI | RD(TMP_REG1) | RN(TMP_REG1) | (local_size << 10))); + FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(TMP_REG1))); + } + else if (local_size > 0) + FAIL_IF(push_inst(compiler, LDR_PRE | RT(TMP_ZERO) | RN(TMP_REG1) | ((-local_size & 0x1ff) << 12))); + + FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(TMP_REG1) | (0 << 10))); + } + else if (local_size > 256) { + FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(TMP_REG1))); + FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(TMP_REG1) | (0 << 10))); + } + else if (local_size > 0) + FAIL_IF(push_inst(compiler, LDR_PRE | RT(TMP_ZERO) | RN(SLJIT_SP) | ((-local_size & 0x1ff) << 12))); + +#else /* !_WIN32 */ + + /* The local_size does not include saved registers size. */ + if (local_size > 0xfff) { + FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | ((local_size >> 12) << 10) | (1 << 22))); + local_size &= 0xfff; + } + if (local_size != 0) + FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (local_size << 10))); + +#endif /* _WIN32 */ + return SLJIT_SUCCESS; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 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); + sljit_s32 saved_regs_size; - local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 0) + SLJIT_LOCALS_OFFSET; - local_size = (local_size + 15) & ~0xf; - compiler->local_size = local_size; + CHECK_ERROR(); + CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); + + saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 2); + if (saved_regs_size & 0x8) + saved_regs_size += sizeof(sljit_sw); + + compiler->local_size = saved_regs_size + ((local_size + 15) & ~0xf); return SLJIT_SUCCESS; } @@ -1172,71 +1019,59 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); - local_size = compiler->local_size; + saved_regs_size = GET_SAVED_REGISTERS_SIZE(compiler->scratches, compiler->saveds, 2); + if (saved_regs_size & 0x8) + saved_regs_size += sizeof(sljit_sw); - saved_regs_size = GET_SAVED_REGISTERS_SIZE(compiler->scratches, compiler->saveds, 0); - if (local_size <= (63 * sizeof(sljit_sw))) - offs = (local_size - saved_regs_size) << (15 - 3); + local_size = compiler->local_size - saved_regs_size; + + /* Load LR as early as possible. */ + if (local_size == 0) + FAIL_IF(push_inst(compiler, LDP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP))); + else if (local_size < 63 * sizeof(sljit_sw)) { + FAIL_IF(push_inst(compiler, LDP_PRE | RT(TMP_FP) | RT2(TMP_LR) + | RN(SLJIT_SP) | (local_size << (15 - 3)))); + } else { - FAIL_IF(push_inst(compiler, LDP_PST | 29 | RT2(TMP_LR) - | RN(TMP_SP) | (((16 >> 3) & 0x7f) << 15))); - offs = 0 << 15; - if (saved_regs_size & 0x8) { - offs = 1 << 15; - saved_regs_size += sizeof(sljit_sw); - } - local_size -= saved_regs_size + SLJIT_LOCALS_OFFSET; if (local_size > 0xfff) { - FAIL_IF(push_inst(compiler, ADDI | RD(TMP_SP) | RN(TMP_SP) | ((local_size >> 12) << 10) | (1 << 22))); + FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(SLJIT_SP) | ((local_size >> 12) << 10) | (1 << 22))); local_size &= 0xfff; } if (local_size) - FAIL_IF(push_inst(compiler, ADDI | RD(TMP_SP) | RN(TMP_SP) | (local_size << 10))); + FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(SLJIT_SP) | (local_size << 10))); + + FAIL_IF(push_inst(compiler, LDP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP))); } tmp = compiler->saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - compiler->saveds) : SLJIT_FIRST_SAVED_REG; prev = -1; + offs = 2 << 15; for (i = SLJIT_S0; i >= tmp; i--) { if (prev == -1) { - if (!(offs & (1 << 15))) { - prev = i; - continue; - } - FAIL_IF(push_inst(compiler, LDRI | RT(i) | RN(TMP_SP) | (offs >> 5))); - offs += 1 << 15; + prev = i; continue; } - FAIL_IF(push_inst(compiler, LDP | RT(prev) | RT2(i) | RN(TMP_SP) | offs)); + FAIL_IF(push_inst(compiler, LDP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs)); offs += 2 << 15; prev = -1; } for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) { if (prev == -1) { - if (!(offs & (1 << 15))) { - prev = i; - continue; - } - FAIL_IF(push_inst(compiler, LDRI | RT(i) | RN(TMP_SP) | (offs >> 5))); - offs += 1 << 15; + prev = i; continue; } - FAIL_IF(push_inst(compiler, LDP | RT(prev) | RT2(i) | RN(TMP_SP) | offs)); + FAIL_IF(push_inst(compiler, LDP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs)); offs += 2 << 15; prev = -1; } - SLJIT_ASSERT(prev == -1); + if (prev != -1) + FAIL_IF(push_inst(compiler, LDRI | RT(prev) | RN(SLJIT_SP) | (offs >> 5))); - if (compiler->local_size <= (63 * sizeof(sljit_sw))) { - FAIL_IF(push_inst(compiler, LDP_PST | 29 | RT2(TMP_LR) - | RN(TMP_SP) | (((local_size >> 3) & 0x7f) << 15))); - } else if (saved_regs_size > 0) { - FAIL_IF(push_inst(compiler, ADDI | RD(TMP_SP) | RN(TMP_SP) | (saved_regs_size << 10))); - } - - FAIL_IF(push_inst(compiler, RET | RN(TMP_LR))); - return SLJIT_SUCCESS; + /* These two can be executed in parallel. */ + FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(SLJIT_SP) | (saved_regs_size << 10))); + return push_inst(compiler, RET | RN(TMP_LR)); } /* --------------------------------------------------------------------- */ @@ -1287,112 +1122,87 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(dst, dstw); ADJUST_LOCAL_OFFSET(src, srcw); - compiler->cache_arg = 0; - compiler->cache_argw = 0; + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) { + if (op <= SLJIT_MOV_P && (src & SLJIT_MEM)) { + SLJIT_ASSERT(reg_map[1] == 0 && reg_map[3] == 2 && reg_map[5] == 4); + + if (op >= SLJIT_MOV_U8 && op <= SLJIT_MOV_S8) + dst = 5; + else if (op >= SLJIT_MOV_U16 && op <= SLJIT_MOV_S16) + dst = 3; + else + dst = 1; + + /* Signed word sized load is the prefetch instruction. */ + return emit_op_mem(compiler, WORD_SIZE | SIGNED, dst, src, srcw, TMP_REG1); + } + return SLJIT_SUCCESS; + } dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1; op = GET_OPCODE(op); - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_P) { + if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) { + /* Both operands are registers. */ + if (dst_r != TMP_REG1 && FAST_IS_REG(src)) + return emit_op_imm(compiler, op | ((op_flags & SLJIT_I32_OP) ? INT_OP : 0), dst_r, TMP_REG1, src); + switch (op) { case SLJIT_MOV: case SLJIT_MOV_P: - flags = WORD_SIZE; + mem_flags = WORD_SIZE; break; case SLJIT_MOV_U8: - flags = BYTE_SIZE; + mem_flags = BYTE_SIZE; if (src & SLJIT_IMM) srcw = (sljit_u8)srcw; break; case SLJIT_MOV_S8: - flags = BYTE_SIZE | SIGNED; + mem_flags = BYTE_SIZE | SIGNED; if (src & SLJIT_IMM) srcw = (sljit_s8)srcw; break; case SLJIT_MOV_U16: - flags = HALF_SIZE; + mem_flags = HALF_SIZE; if (src & SLJIT_IMM) srcw = (sljit_u16)srcw; break; case SLJIT_MOV_S16: - flags = HALF_SIZE | SIGNED; + mem_flags = HALF_SIZE | SIGNED; if (src & SLJIT_IMM) srcw = (sljit_s16)srcw; break; case SLJIT_MOV_U32: - flags = INT_SIZE; + mem_flags = INT_SIZE; if (src & SLJIT_IMM) srcw = (sljit_u32)srcw; break; case SLJIT_MOV_S32: - flags = INT_SIZE | SIGNED; - if (src & SLJIT_IMM) - srcw = (sljit_s32)srcw; - break; - case SLJIT_MOVU: - case SLJIT_MOVU_P: - flags = WORD_SIZE | UPDATE; - break; - case SLJIT_MOVU_U8: - flags = BYTE_SIZE | UPDATE; - if (src & SLJIT_IMM) - srcw = (sljit_u8)srcw; - break; - case SLJIT_MOVU_S8: - flags = BYTE_SIZE | SIGNED | UPDATE; - if (src & SLJIT_IMM) - srcw = (sljit_s8)srcw; - break; - case SLJIT_MOVU_U16: - flags = HALF_SIZE | UPDATE; - if (src & SLJIT_IMM) - srcw = (sljit_u16)srcw; - break; - case SLJIT_MOVU_S16: - flags = HALF_SIZE | SIGNED | UPDATE; - if (src & SLJIT_IMM) - srcw = (sljit_s16)srcw; - break; - case SLJIT_MOVU_U32: - flags = INT_SIZE | UPDATE; - if (src & SLJIT_IMM) - srcw = (sljit_u32)srcw; - break; - case SLJIT_MOVU_S32: - flags = INT_SIZE | SIGNED | UPDATE; + mem_flags = INT_SIZE | SIGNED; if (src & SLJIT_IMM) srcw = (sljit_s32)srcw; break; default: - SLJIT_ASSERT_STOP(); - flags = 0; + SLJIT_UNREACHABLE(); + mem_flags = 0; break; } if (src & SLJIT_IMM) FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG1, srcw)); - else if (src & SLJIT_MEM) { - if (getput_arg_fast(compiler, flags, dst_r, src, srcw)) - FAIL_IF(compiler->error); - else - FAIL_IF(getput_arg(compiler, flags, dst_r, src, srcw, dst, dstw)); - } else { - if (dst_r != TMP_REG1) - return emit_op_imm(compiler, op | ((op_flags & SLJIT_I32_OP) ? INT_OP : 0), dst_r, TMP_REG1, src); + else if (!(src & SLJIT_MEM)) dst_r = src; - } + else + FAIL_IF(emit_op_mem(compiler, mem_flags, dst_r, src, srcw, TMP_REG1)); - if (dst & SLJIT_MEM) { - if (getput_arg_fast(compiler, flags | STORE, dst_r, dst, dstw)) - return compiler->error; - else - return getput_arg(compiler, flags | STORE, dst_r, dst, dstw, 0, 0); - } + if (dst & SLJIT_MEM) + return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2); return SLJIT_SUCCESS; } - flags = GET_FLAGS(op_flags) ? SET_FLAGS : 0; + flags = HAS_FLAGS(op_flags) ? SET_FLAGS : 0; mem_flags = WORD_SIZE; + if (op_flags & SLJIT_I32_OP) { flags |= INT_OP; mem_flags = INT_SIZE; @@ -1402,28 +1212,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile flags |= UNUSED_RETURN; if (src & SLJIT_MEM) { - if (getput_arg_fast(compiler, mem_flags, TMP_REG2, src, srcw)) - FAIL_IF(compiler->error); - else - FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG2, src, srcw, dst, dstw)); + FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG2, src, srcw, TMP_REG2)); src = TMP_REG2; } - if (src & SLJIT_IMM) { - flags |= ARG2_IMM; - if (op_flags & SLJIT_I32_OP) - srcw = (sljit_s32)srcw; - } else - srcw = src; + emit_op_imm(compiler, flags | op, dst_r, TMP_REG1, src); - emit_op_imm(compiler, flags | op, dst_r, TMP_REG1, srcw); - - if (dst & SLJIT_MEM) { - if (getput_arg_fast(compiler, mem_flags | STORE, dst_r, dst, dstw)) - return compiler->error; - else - return getput_arg(compiler, mem_flags | STORE, dst_r, dst, dstw, 0, 0); - } + if (SLJIT_UNLIKELY(dst & SLJIT_MEM)) + return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2); return SLJIT_SUCCESS; } @@ -1440,12 +1236,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); - compiler->cache_arg = 0; - compiler->cache_argw = 0; + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) + return SLJIT_SUCCESS; dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1; - flags = GET_FLAGS(op) ? SET_FLAGS : 0; + flags = HAS_FLAGS(op) ? SET_FLAGS : 0; mem_flags = WORD_SIZE; + if (op & SLJIT_I32_OP) { flags |= INT_OP; mem_flags = INT_SIZE; @@ -1454,46 +1251,21 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile if (dst == SLJIT_UNUSED) flags |= UNUSED_RETURN; - if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, mem_flags | STORE | ARG_TEST, TMP_REG1, dst, dstw)) - flags |= SLOW_DEST; - if (src1 & SLJIT_MEM) { - if (getput_arg_fast(compiler, mem_flags, TMP_REG1, src1, src1w)) - FAIL_IF(compiler->error); - else - flags |= SLOW_SRC1; - } - if (src2 & SLJIT_MEM) { - if (getput_arg_fast(compiler, mem_flags, TMP_REG2, src2, src2w)) - FAIL_IF(compiler->error); - else - flags |= SLOW_SRC2; - } - - if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { - if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { - FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG2, src2, src2w, src1, src1w)); - FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG1, src1, src1w, dst, dstw)); - } - else { - FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG1, src1, src1w, src2, src2w)); - FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG2, src2, src2w, dst, dstw)); - } - } - else if (flags & SLOW_SRC1) - FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG1, src1, src1w, dst, dstw)); - else if (flags & SLOW_SRC2) - FAIL_IF(getput_arg(compiler, mem_flags, TMP_REG2, src2, src2w, dst, dstw)); - - if (src1 & SLJIT_MEM) + FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG1, src1, src1w, TMP_REG1)); src1 = TMP_REG1; - if (src2 & SLJIT_MEM) + } + + if (src2 & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG2, src2, src2w, TMP_REG2)); src2 = TMP_REG2; + } if (src1 & SLJIT_IMM) flags |= ARG1_IMM; else src1w = src1; + if (src2 & SLJIT_IMM) flags |= ARG2_IMM; else @@ -1501,14 +1273,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src1w, src2w); - if (dst & SLJIT_MEM) { - if (!(flags & SLOW_DEST)) { - getput_arg_fast(compiler, mem_flags | STORE, dst_r, dst, dstw); - return compiler->error; - } - return getput_arg(compiler, mem_flags | STORE, TMP_REG1, dst, dstw, 0, 0); - } - + if (dst & SLJIT_MEM) + return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2); return SLJIT_SUCCESS; } @@ -1521,7 +1287,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) { CHECK_REG_INDEX(check_sljit_get_float_register_index(reg)); - return reg; + return freg_map[reg]; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, @@ -1537,74 +1303,60 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c /* Floating point operators */ /* --------------------------------------------------------------------- */ -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void) -{ -#ifdef SLJIT_IS_FPU_AVAILABLE - return SLJIT_IS_FPU_AVAILABLE; -#else - /* Available by default. */ - return 1; -#endif -} - static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) { sljit_u32 shift = MEM_SIZE_SHIFT(flags); - sljit_ins ins_bits = (shift << 30); - sljit_s32 other_r; - sljit_sw diff; + sljit_ins type = (shift << 30); SLJIT_ASSERT(arg & SLJIT_MEM); if (!(flags & STORE)) - ins_bits |= 1 << 22; + type |= 0x00400000; if (arg & OFFS_REG_MASK) { argw &= 3; - if (!argw || argw == shift) - return push_inst(compiler, STR_FR | ins_bits | VT(reg) + if (argw == 0 || argw == shift) + return push_inst(compiler, STR_FR | type | VT(reg) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw ? (1 << 12) : 0)); - other_r = OFFS_REG(arg); - arg &= REG_MASK; - FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg) | RM(other_r) | (argw << 10))); - arg = TMP_REG1; - argw = 0; + + FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw << 10))); + return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1)); } arg &= REG_MASK; - if (arg && argw >= 0 && ((argw >> shift) <= 0xfff) && (argw & ((1 << shift) - 1)) == 0) - return push_inst(compiler, STR_FI | ins_bits | VT(reg) | RN(arg) | (argw << (10 - shift))); - if (arg && argw <= 255 && argw >= -256) - return push_inst(compiler, STUR_FI | ins_bits | VT(reg) | RN(arg) | ((argw & 0x1ff) << 12)); + if (arg == SLJIT_UNUSED) { + FAIL_IF(load_immediate(compiler, TMP_REG1, argw & ~(0xfff << shift))); - /* Slow cases */ - if (compiler->cache_arg == SLJIT_MEM && argw != compiler->cache_argw) { - diff = argw - compiler->cache_argw; - if (!arg && diff <= 255 && diff >= -256) - return push_inst(compiler, STUR_FI | ins_bits | VT(reg) | RN(TMP_REG3) | ((diff & 0x1ff) << 12)); - if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - compiler->cache_argw = argw; + argw = (argw >> shift) & 0xfff; + + return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1) | (argw << 10)); + } + + if (argw >= 0 && (argw & ((1 << shift) - 1)) == 0) { + if ((argw >> shift) <= 0xfff) + return push_inst(compiler, STR_FI | type | VT(reg) | RN(arg) | (argw << (10 - shift))); + + if (argw <= 0xffffff) { + FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(TMP_REG1) | RN(arg) | ((argw >> 12) << 10))); + + argw = ((argw & 0xfff) >> shift); + return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1) | (argw << 10)); } } - if (compiler->cache_arg != SLJIT_MEM || argw != compiler->cache_argw) { - compiler->cache_arg = SLJIT_MEM; - compiler->cache_argw = argw; - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); - } + if (argw <= 255 && argw >= -256) + return push_inst(compiler, STUR_FI | type | VT(reg) | RN(arg) | ((argw & 0x1ff) << 12)); - if (arg & REG_MASK) - return push_inst(compiler, STR_FR | ins_bits | VT(reg) | RN(arg) | RM(TMP_REG3)); - return push_inst(compiler, STR_FI | ins_bits | VT(reg) | RN(TMP_REG3)); + FAIL_IF(load_immediate(compiler, TMP_REG1, argw)); + return push_inst(compiler, STR_FR | type | VT(reg) | RN(arg) | RM(TMP_REG1)); } static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) { - sljit_s32 dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1; + sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; sljit_ins inv_bits = (op & SLJIT_F32_OP) ? (1 << 22) : 0; if (GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64) @@ -1617,8 +1369,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp FAIL_IF(push_inst(compiler, (FCVTZS ^ inv_bits) | RD(dst_r) | VN(src))); - if (dst_r == TMP_REG1 && dst != SLJIT_UNUSED) - return emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64) ? INT_SIZE : WORD_SIZE) | STORE, TMP_REG1, dst, dstw); + if (dst & SLJIT_MEM) + return emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64) ? INT_SIZE : WORD_SIZE) | STORE, TMP_REG1, dst, dstw, TMP_REG2); return SLJIT_SUCCESS; } @@ -1633,7 +1385,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp inv_bits |= (1 << 31); if (src & SLJIT_MEM) { - emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) ? INT_SIZE : WORD_SIZE), TMP_REG1, src, srcw); + emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) ? INT_SIZE : WORD_SIZE), TMP_REG1, src, srcw, TMP_REG1); src = TMP_REG1; } else if (src & SLJIT_IMM) { #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) @@ -1679,17 +1431,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil sljit_ins inv_bits; CHECK_ERROR(); - compiler->cache_arg = 0; - compiler->cache_argw = 0; - SLJIT_COMPILE_ASSERT((INT_SIZE ^ 0x100) == WORD_SIZE, must_be_one_bit_difference); + SLJIT_COMPILE_ASSERT((INT_SIZE ^ 0x1) == WORD_SIZE, must_be_one_bit_difference); SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw); inv_bits = (op & SLJIT_F32_OP) ? (1 << 22) : 0; dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; if (src & SLJIT_MEM) { - emit_fop_mem(compiler, (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) ? (mem_flags ^ 0x100) : mem_flags, dst_r, src, srcw); + emit_fop_mem(compiler, (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) ? (mem_flags ^ 0x1) : mem_flags, dst_r, src, srcw); src = dst_r; } @@ -1732,9 +1482,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); - compiler->cache_arg = 0; - compiler->cache_argw = 0; - dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; if (src1 & SLJIT_MEM) { emit_fop_mem(compiler, mem_flags, TMP_FREG1, src1, src1w); @@ -1775,15 +1522,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); ADJUST_LOCAL_OFFSET(dst, dstw); - /* For UNUSED dst. Uncommon, but possible. */ - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; - if (FAST_IS_REG(dst)) return push_inst(compiler, ORR | RD(dst) | RN(TMP_ZERO) | RM(TMP_LR)); /* Memory. */ - return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_LR, dst, dstw); + return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_LR, dst, dstw, TMP_REG1); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw) @@ -1794,10 +1537,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler if (FAST_IS_REG(src)) FAIL_IF(push_inst(compiler, ORR | RD(TMP_LR) | RN(TMP_ZERO) | RM(src))); - else if (src & SLJIT_MEM) - FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_LR, src, srcw)); - else if (src & SLJIT_IMM) - FAIL_IF(load_immediate(compiler, TMP_LR, srcw)); + else + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_LR, src, srcw, TMP_REG1)); return push_inst(compiler, RET | RN(TMP_LR)); } @@ -1856,7 +1597,7 @@ static sljit_uw get_cc(sljit_s32 type) return 0x6; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return 0xe; } } @@ -1903,6 +1644,20 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile return jump; } +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_jump(compiler, type); +} + static SLJIT_INLINE struct sljit_jump* emit_cmp_to0(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) { @@ -1918,13 +1673,14 @@ static SLJIT_INLINE struct sljit_jump* emit_cmp_to0(struct sljit_compiler *compi jump->flags |= IS_CBZ | IS_COND; if (src & SLJIT_MEM) { - PTR_FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG1, src, srcw)); + PTR_FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1)); src = TMP_REG1; } else if (src & SLJIT_IMM) { PTR_FAIL_IF(load_immediate(compiler, TMP_REG1, srcw)); src = TMP_REG1; } + SLJIT_ASSERT(FAST_IS_REG(src)); if ((type & 0xff) == SLJIT_EQUAL) @@ -1945,15 +1701,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); ADJUST_LOCAL_OFFSET(src, srcw); - /* In ARM, we don't need to touch the arguments. */ if (!(src & SLJIT_IMM)) { if (src & SLJIT_MEM) { - FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw)); + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1)); src = TMP_REG1; } return push_inst(compiler, ((type >= SLJIT_FAST_CALL) ? BLR : BR) | RN(src)); } + /* These jumps are converted to jump/call instructions when possible. */ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); FAIL_IF(!jump); set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0)); @@ -1964,54 +1720,210 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi return push_inst(compiler, ((type >= SLJIT_FAST_CALL) ? BLR : BR) | RN(TMP_REG1)); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_ijump(compiler, type, src, srcw); +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw, sljit_s32 type) { - sljit_s32 dst_r, flags, mem_flags; + sljit_s32 dst_r, src_r, flags, mem_flags; sljit_ins cc; CHECK_ERROR(); - CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type)); + CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); ADJUST_LOCAL_OFFSET(dst, dstw); - ADJUST_LOCAL_OFFSET(src, srcw); - - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; cc = get_cc(type & 0xff); dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; if (GET_OPCODE(op) < SLJIT_ADD) { FAIL_IF(push_inst(compiler, CSINC | (cc << 12) | RD(dst_r) | RN(TMP_ZERO) | RM(TMP_ZERO))); - if (dst_r != TMP_REG1) - return SLJIT_SUCCESS; - return emit_op_mem(compiler, (GET_OPCODE(op) == SLJIT_MOV ? WORD_SIZE : INT_SIZE) | STORE, TMP_REG1, dst, dstw); + + if (dst_r == TMP_REG1) { + mem_flags = (GET_OPCODE(op) == SLJIT_MOV ? WORD_SIZE : INT_SIZE) | STORE; + return emit_op_mem(compiler, mem_flags, TMP_REG1, dst, dstw, TMP_REG2); + } + + return SLJIT_SUCCESS; } - compiler->cache_arg = 0; - compiler->cache_argw = 0; - flags = GET_FLAGS(op) ? SET_FLAGS : 0; + flags = HAS_FLAGS(op) ? SET_FLAGS : 0; mem_flags = WORD_SIZE; + if (op & SLJIT_I32_OP) { flags |= INT_OP; mem_flags = INT_SIZE; } - if (src & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, mem_flags, TMP_REG1, src, srcw, dst, dstw)); - src = TMP_REG1; - srcw = 0; - } else if (src & SLJIT_IMM) - flags |= ARG1_IMM; + src_r = dst; + + if (dst & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG1, dst, dstw, TMP_REG1)); + src_r = TMP_REG1; + } FAIL_IF(push_inst(compiler, CSINC | (cc << 12) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(TMP_ZERO))); - emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src, TMP_REG2); + emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src_r, TMP_REG2); - if (dst_r != TMP_REG1) + if (dst & SLJIT_MEM) + return emit_op_mem(compiler, mem_flags | STORE, TMP_REG1, dst, dstw, TMP_REG2); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ + sljit_ins inv_bits = (dst_reg & SLJIT_I32_OP) ? (1 << 31) : 0; + sljit_ins cc; + + CHECK_ERROR(); + CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); + + if (SLJIT_UNLIKELY(src & SLJIT_IMM)) { + if (dst_reg & SLJIT_I32_OP) + srcw = (sljit_s32)srcw; + FAIL_IF(load_immediate(compiler, TMP_REG1, srcw)); + src = TMP_REG1; + srcw = 0; + } + + cc = get_cc(type & 0xff); + dst_reg &= ~SLJIT_I32_OP; + + return push_inst(compiler, (CSEL ^ inv_bits) | (cc << 12) | RD(dst_reg) | RN(dst_reg) | RM(src)); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_u32 sign = 0, inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + + if ((mem & OFFS_REG_MASK) || (memw > 255 && memw < -256)) + return SLJIT_ERR_UNSUPPORTED; + + if (type & SLJIT_MEM_SUPP) return SLJIT_SUCCESS; - return emit_op_mem2(compiler, mem_flags | STORE, TMP_REG1, dst, dstw, 0, 0); + + switch (type & 0xff) { + case SLJIT_MOV: + case SLJIT_MOV_P: + inst = STURBI | (MEM_SIZE_SHIFT(WORD_SIZE) << 30) | 0x400; + break; + case SLJIT_MOV_S8: + sign = 1; + case SLJIT_MOV_U8: + inst = STURBI | (MEM_SIZE_SHIFT(BYTE_SIZE) << 30) | 0x400; + break; + case SLJIT_MOV_S16: + sign = 1; + case SLJIT_MOV_U16: + inst = STURBI | (MEM_SIZE_SHIFT(HALF_SIZE) << 30) | 0x400; + break; + case SLJIT_MOV_S32: + sign = 1; + case SLJIT_MOV_U32: + inst = STURBI | (MEM_SIZE_SHIFT(INT_SIZE) << 30) | 0x400; + break; + default: + SLJIT_UNREACHABLE(); + inst = STURBI | (MEM_SIZE_SHIFT(WORD_SIZE) << 30) | 0x400; + break; + } + + if (!(type & SLJIT_MEM_STORE)) + inst |= sign ? 0x00800000 : 0x00400000; + + if (type & SLJIT_MEM_PRE) + inst |= 0x800; + + return push_inst(compiler, inst | RT(reg) | RN(mem & REG_MASK) | ((memw & 0x1ff) << 12)); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_u32 inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw)); + + if ((mem & OFFS_REG_MASK) || (memw > 255 && memw < -256)) + return SLJIT_ERR_UNSUPPORTED; + + if (type & SLJIT_MEM_SUPP) + return SLJIT_SUCCESS; + + inst = STUR_FI | 0x80000400; + + if (!(type & SLJIT_F32_OP)) + inst |= 0x40000000; + + if (!(type & SLJIT_MEM_STORE)) + inst |= 0x00400000; + + if (type & SLJIT_MEM_PRE) + inst |= 0x800; + + return push_inst(compiler, inst | VT(freg) | RN(mem & REG_MASK) | ((memw & 0x1ff) << 12)); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset) +{ + sljit_s32 dst_reg; + sljit_ins ins; + + CHECK_ERROR(); + CHECK(check_sljit_get_local_base(compiler, dst, dstw, offset)); + + SLJIT_ASSERT (SLJIT_LOCALS_OFFSET_BASE == 0); + + dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG1; + + if (offset <= 0xffffff && offset >= -0xffffff) { + ins = ADDI; + if (offset < 0) { + offset = -offset; + ins = SUBI; + } + + if (offset <= 0xfff) + FAIL_IF(push_inst(compiler, ins | RD(dst_reg) | RN(SLJIT_SP) | (offset << 10))); + else { + FAIL_IF(push_inst(compiler, ins | RD(dst_reg) | RN(SLJIT_SP) | ((offset & 0xfff000) >> (12 - 10)) | (1 << 22))); + + offset &= 0xfff; + if (offset != 0) + FAIL_IF(push_inst(compiler, ins | RD(dst_reg) | RN(dst_reg) | (offset << 10))); + } + } + else { + FAIL_IF(load_immediate (compiler, dst_reg, offset)); + /* Add extended register form. */ + FAIL_IF(push_inst(compiler, ADDE | (0x3 << 13) | RD(dst_reg) | RN(SLJIT_SP) | RM(dst_reg))); + } + + if (SLJIT_UNLIKELY(dst & SLJIT_MEM)) + return emit_op_mem(compiler, WORD_SIZE | STORE, dst_reg, dst, dstw, TMP_REG1); + return SLJIT_SUCCESS; } SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) @@ -2027,24 +1939,26 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi PTR_FAIL_IF(!const_); set_const(const_, compiler); - dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1; + dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; PTR_FAIL_IF(emit_imm64_const(compiler, dst_r, init_value)); if (dst & SLJIT_MEM) - PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw)); + PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2)); return const_; } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) { sljit_ins* inst = (sljit_ins*)addr; - modify_imm64_const(inst, new_addr); + modify_imm64_const(inst, new_target); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 4); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) { sljit_ins* inst = (sljit_ins*)addr; modify_imm64_const(inst, new_constant); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 4); } diff --git a/pcre2-10.22/src/sljit/sljitNativeARM_T2_32.c b/pcre2-10.32/src/sljit/sljitNativeARM_T2_32.c similarity index 64% rename from pcre2-10.22/src/sljit/sljitNativeARM_T2_32.c rename to pcre2-10.32/src/sljit/sljitNativeARM_T2_32.c index 1ed44a813..d7024b6d7 100644 --- a/pcre2-10.22/src/sljit/sljitNativeARM_T2_32.c +++ b/pcre2-10.32/src/sljit/sljitNativeARM_T2_32.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -26,7 +26,11 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) { - return "ARM-Thumb2" SLJIT_CPUINFO; +#ifdef __SOFTFP__ + return "ARM-Thumb2" SLJIT_CPUINFO " ABI:softfp"; +#else + return "ARM-Thumb2" SLJIT_CPUINFO " ABI:hardfp"; +#endif } /* Length of an instruction word. */ @@ -35,15 +39,18 @@ typedef sljit_u32 sljit_ins; /* Last register + 1. */ #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 TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 5) +#define TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 4) -#define TMP_FREG1 (0) -#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2) /* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */ -static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { - 0, 0, 1, 2, 12, 11, 10, 9, 8, 7, 6, 5, 13, 3, 4, 14, 15 +static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = { + 0, 0, 1, 2, 3, 11, 10, 9, 8, 7, 6, 5, 4, 13, 12, 14, 15 +}; + +static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { + 0, 0, 1, 2, 3, 4, 5, 6, 7 }; #define COPY_BITS(src, from, to, bits) \ @@ -70,9 +77,9 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { #define RN4(rn) (reg_map[rn] << 16) #define RM4(rm) (reg_map[rm]) #define RT4(rt) (reg_map[rt] << 12) -#define DD4(dd) ((dd) << 12) -#define DN4(dn) ((dn) << 16) -#define DM4(dm) (dm) +#define DD4(dd) (freg_map[dd] << 12) +#define DN4(dn) (freg_map[dn] << 16) +#define DM4(dm) (freg_map[dm]) #define IMM5(imm) \ (COPY_BITS(imm, 2, 12, 3) | ((imm & 0x3) << 6)) #define IMM12(imm) \ @@ -103,17 +110,23 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { #define ASRSI 0x1000 #define ASR_W 0xfa40f000 #define ASR_WI 0xea4f0020 +#define BCC 0xd000 #define BICI 0xf0200000 #define BKPT 0xbe00 #define BLX 0x4780 #define BX 0x4700 #define CLZ 0xfab0f080 +#define CMNI_W 0xf1100f00 +#define CMP 0x4280 #define CMPI 0x2800 +#define CMPI_W 0xf1b00f00 +#define CMP_X 0x4500 #define CMP_W 0xebb00f00 #define EORI 0xf0800000 #define EORS 0x4040 #define EOR_W 0xea800000 #define IT 0xbf00 +#define LDRI 0xf8500800 #define LSLS 0x4080 #define LSLSI 0x0000 #define LSL_W 0xfa00f000 @@ -147,6 +160,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { #define SBCI 0xf1600000 #define SBCS 0x4180 #define SBC_W 0xeb600000 +#define SDIV 0xfb90f0f0 #define SMULL 0xfb800000 #define STR_SP 0x9000 #define SUBS 0x1a00 @@ -161,6 +175,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { #define SXTH 0xb200 #define SXTH_W 0xfa0ff080 #define TST 0x4200 +#define UDIV 0xfbb0f0f0 #define UMULL 0xfba00000 #define UXTB 0xb2c0 #define UXTB_W 0xfa5ff080 @@ -175,6 +190,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { #define VDIV_F32 0xee800a00 #define VMOV_F32 0xeeb00a40 #define VMOV 0xee000a10 +#define VMOV2 0xec400a10 #define VMRS 0xeef1fa10 #define VMUL_F32 0xee200a00 #define VNEG_F32 0xeeb10a40 @@ -205,10 +221,10 @@ static sljit_s32 push_inst32(struct sljit_compiler *compiler, sljit_ins inst) static SLJIT_INLINE sljit_s32 emit_imm32_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_uw imm) { - FAIL_IF(push_inst32(compiler, MOVW | RD4(dst) | - COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff))); - return push_inst32(compiler, MOVT | RD4(dst) | - COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16)); + FAIL_IF(push_inst32(compiler, MOVW | RD4(dst) + | COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff))); + return push_inst32(compiler, MOVT | RD4(dst) + | COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16)); } static SLJIT_INLINE void modify_imm32_const(sljit_u16 *inst, sljit_uw new_imm) @@ -221,7 +237,7 @@ static SLJIT_INLINE void modify_imm32_const(sljit_u16 *inst, sljit_uw new_imm) inst[3] = dst | COPY_BITS(new_imm, 8 + 16, 12, 3) | ((new_imm & 0xff0000) >> 16); } -static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_u16 *code_ptr, sljit_u16 *code) +static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_u16 *code_ptr, sljit_u16 *code, sljit_sw executable_offset) { sljit_sw diff; @@ -232,7 +248,7 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_u1 /* Branch to ARM code is not optimized yet. */ if (!(jump->u.target & 0x1)) return 0; - diff = ((sljit_sw)jump->u.target - (sljit_sw)(code_ptr + 2)) >> 1; + diff = ((sljit_sw)jump->u.target - (sljit_sw)(code_ptr + 2) - executable_offset) >> 1; } else { SLJIT_ASSERT(jump->flags & JUMP_LABEL); @@ -276,7 +292,7 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_u1 return 0; } -static SLJIT_INLINE void set_jump_instruction(struct sljit_jump *jump) +static SLJIT_INLINE void set_jump_instruction(struct sljit_jump *jump, sljit_sw executable_offset) { sljit_s32 type = (jump->flags >> 4) & 0xf; sljit_sw diff; @@ -290,10 +306,12 @@ static SLJIT_INLINE void set_jump_instruction(struct sljit_jump *jump) if (jump->flags & JUMP_ADDR) { SLJIT_ASSERT(jump->u.target & 0x1); - diff = ((sljit_sw)jump->u.target - (sljit_sw)(jump->addr + 4)) >> 1; + diff = ((sljit_sw)jump->u.target - (sljit_sw)(jump->addr + sizeof(sljit_u32)) - executable_offset) >> 1; + } + else { + SLJIT_ASSERT(jump->u.label->addr & 0x1); + diff = ((sljit_sw)(jump->u.label->addr) - (sljit_sw)(jump->addr + sizeof(sljit_u32)) - executable_offset) >> 1; } - else - diff = ((sljit_sw)(jump->u.label->addr) - (sljit_sw)(jump->addr + 4)) >> 1; jump_inst = (sljit_u16*)jump->addr; switch (type) { @@ -325,8 +343,8 @@ static SLJIT_INLINE void set_jump_instruction(struct sljit_jump *jump) /* Really complex instruction form for branches. */ s = (diff >> 23) & 0x1; - j1 = (~(diff >> 21) ^ s) & 0x1; - j2 = (~(diff >> 22) ^ s) & 0x1; + j1 = (~(diff >> 22) ^ s) & 0x1; + j2 = (~(diff >> 21) ^ s) & 0x1; jump_inst[0] = 0xf000 | (s << 10) | COPY_BITS(diff, 11, 0, 10); jump_inst[1] = (j1 << 13) | (j2 << 11) | (diff & 0x7ff); @@ -336,7 +354,7 @@ static SLJIT_INLINE void set_jump_instruction(struct sljit_jump *jump) else if (type == 6) /* Encoding T1 of 'BL' instruction */ jump_inst[1] |= 0xd000; else - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) @@ -347,6 +365,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil sljit_u16 *buf_ptr; sljit_u16 *buf_end; sljit_uw half_count; + sljit_sw executable_offset; struct sljit_label *label; struct sljit_jump *jump; @@ -362,6 +381,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil code_ptr = code; half_count = 0; + executable_offset = SLJIT_EXEC_OFFSET(code); + label = compiler->labels; jump = compiler->jumps; const_ = compiler->consts; @@ -376,13 +397,13 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil SLJIT_ASSERT(!jump || jump->addr >= half_count); SLJIT_ASSERT(!const_ || const_->addr >= half_count); if (label && label->size == half_count) { - label->addr = ((sljit_uw)code_ptr) | 0x1; + label->addr = ((sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset)) | 0x1; label->size = code_ptr - code; label = label->next; } if (jump && jump->addr == half_count) { jump->addr = (sljit_uw)code_ptr - ((jump->flags & IS_COND) ? 10 : 8); - code_ptr -= detect_jump_type(jump, code_ptr, code); + code_ptr -= detect_jump_type(jump, code_ptr, code, executable_offset); jump = jump->next; } if (const_ && const_->addr == half_count) { @@ -397,7 +418,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } while (buf); if (label && label->size == half_count) { - label->addr = ((sljit_uw)code_ptr) | 0x1; + label->addr = ((sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset)) | 0x1; label->size = code_ptr - code; label = label->next; } @@ -409,17 +430,42 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil jump = compiler->jumps; while (jump) { - set_jump_instruction(jump); + set_jump_instruction(jump, executable_offset); jump = jump->next; } compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_offset = executable_offset; compiler->executable_size = (code_ptr - code) * sizeof(sljit_u16); + + code = (sljit_u16 *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset); + code_ptr = (sljit_u16 *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); + SLJIT_CACHE_FLUSH(code, code_ptr); /* Set thumb mode flag. */ return (void*)((sljit_uw)code | 0x1); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) +{ + switch (feature_type) { + case SLJIT_HAS_FPU: +#ifdef SLJIT_IS_FPU_AVAILABLE + return SLJIT_IS_FPU_AVAILABLE; +#else + /* Available by default. */ + return 1; +#endif + + case SLJIT_HAS_CLZ: + case SLJIT_HAS_CMOV: + return 1; + + default: + return 0; + } +} + /* --------------------------------------------------------------------- */ /* Core code generator functions. */ /* --------------------------------------------------------------------- */ @@ -478,6 +524,8 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst, { sljit_uw tmp; + /* MOVS cannot be used since it destroy flags. */ + if (imm >= 0x10000) { tmp = get_imm(imm); if (tmp != INVALID_IMM) @@ -488,36 +536,32 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst, } /* set low 16 bits, set hi 16 bits to 0. */ - FAIL_IF(push_inst32(compiler, MOVW | RD4(dst) | - COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff))); + FAIL_IF(push_inst32(compiler, MOVW | RD4(dst) + | COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff))); /* set hi 16 bit if needed. */ if (imm >= 0x10000) - return push_inst32(compiler, MOVT | RD4(dst) | - COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16)); + return push_inst32(compiler, MOVT | RD4(dst) + | COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16)); return SLJIT_SUCCESS; } #define ARG1_IMM 0x0010000 #define ARG2_IMM 0x0020000 -#define KEEP_FLAGS 0x0040000 /* SET_FLAGS must be 0x100000 as it is also the value of S bit (can be used for optimization). */ #define SET_FLAGS 0x0100000 #define UNUSED_RETURN 0x0200000 -#define SLOW_DEST 0x0400000 -#define SLOW_SRC1 0x0800000 -#define SLOW_SRC2 0x1000000 static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 dst, sljit_uw arg1, sljit_uw arg2) { /* dst must be register, TMP_REG1 - arg1 must be register, TMP_REG1, imm - arg2 must be register, TMP_REG2, imm */ + arg1 must be register, imm + arg2 must be register, imm */ sljit_s32 reg; sljit_uw imm, nimm; if (SLJIT_UNLIKELY((flags & (ARG1_IMM | ARG2_IMM)) == (ARG1_IMM | ARG2_IMM))) { - /* Both are immediates. */ + /* Both are immediates, no temporaries are used. */ flags &= ~ARG1_IMM; FAIL_IF(load_immediate(compiler, TMP_REG1, arg1)); arg1 = TMP_REG1; @@ -533,7 +577,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s /* No form with immediate operand. */ break; case SLJIT_MOV: - SLJIT_ASSERT(!(flags & SET_FLAGS) && (flags & ARG2_IMM) && arg1 == TMP_REG1); + SLJIT_ASSERT(!(flags & SET_FLAGS) && (flags & ARG2_IMM) && arg1 == TMP_REG2); return load_immediate(compiler, dst, imm); case SLJIT_NOT: if (!(flags & SET_FLAGS)) @@ -543,7 +587,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s break; case SLJIT_ADD: nimm = -imm; - if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(reg, dst)) { + if (IS_2_LO_REGS(reg, dst)) { if (imm <= 0x7) return push_inst16(compiler, ADDSI3 | IMM3(imm) | RD3(dst) | RN3(reg)); if (nimm <= 0x7) @@ -561,9 +605,12 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s if (nimm <= 0xfff) return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(nimm)); } - imm = get_imm(imm); - if (imm != INVALID_IMM) - return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + nimm = get_imm(imm); + if (nimm != INVALID_IMM) + return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm); + nimm = get_imm(-imm); + if (nimm != INVALID_IMM) + return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm); break; case SLJIT_ADDC: imm = get_imm(imm); @@ -571,16 +618,27 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s return push_inst32(compiler, ADCI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); break; case SLJIT_SUB: + /* SUB operation can be replaced by ADD because of the negative carry flag. */ if (flags & ARG1_IMM) { - if (!(flags & KEEP_FLAGS) && imm == 0 && IS_2_LO_REGS(reg, dst)) + if (imm == 0 && IS_2_LO_REGS(reg, dst)) return push_inst16(compiler, RSBSI | RD3(dst) | RN3(reg)); imm = get_imm(imm); if (imm != INVALID_IMM) return push_inst32(compiler, RSB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); break; } + if (flags & UNUSED_RETURN) { + if (imm <= 0xff && reg_map[reg] <= 7) + return push_inst16(compiler, CMPI | IMM8(imm) | RDN3(reg)); + nimm = get_imm(imm); + if (nimm != INVALID_IMM) + return push_inst32(compiler, CMPI_W | RN4(reg) | nimm); + nimm = get_imm(-imm); + if (nimm != INVALID_IMM) + return push_inst32(compiler, CMNI_W | RN4(reg) | nimm); + } nimm = -imm; - if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(reg, dst)) { + if (IS_2_LO_REGS(reg, dst)) { if (imm <= 0x7) return push_inst16(compiler, SUBSI3 | IMM3(imm) | RD3(dst) | RN3(reg)); if (nimm <= 0x7) @@ -591,8 +649,6 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s if (nimm <= 0xff) return push_inst16(compiler, ADDSI8 | IMM8(nimm) | RDN3(dst)); } - if (imm <= 0xff && (flags & UNUSED_RETURN)) - return push_inst16(compiler, CMPI | IMM8(imm) | RDN3(reg)); } if (!(flags & SET_FLAGS)) { if (imm <= 0xfff) @@ -600,9 +656,12 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s if (nimm <= 0xfff) return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(nimm)); } - imm = get_imm(imm); - if (imm != INVALID_IMM) - return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + nimm = get_imm(imm); + if (nimm != INVALID_IMM) + return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm); + nimm = get_imm(-imm); + if (nimm != INVALID_IMM) + return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | nimm); break; case SLJIT_SUBC: if (flags & ARG1_IMM) @@ -647,31 +706,35 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s } switch (flags & 0xffff) { case SLJIT_SHL: - if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, reg)) + if (IS_2_LO_REGS(dst, reg)) return push_inst16(compiler, LSLSI | RD3(dst) | RN3(reg) | (imm << 6)); return push_inst32(compiler, LSL_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm)); case SLJIT_LSHR: - if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, reg)) + if (IS_2_LO_REGS(dst, reg)) return push_inst16(compiler, LSRSI | RD3(dst) | RN3(reg) | (imm << 6)); return push_inst32(compiler, LSR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm)); default: /* SLJIT_ASHR */ - if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, reg)) + if (IS_2_LO_REGS(dst, reg)) return push_inst16(compiler, ASRSI | RD3(dst) | RN3(reg) | (imm << 6)); return push_inst32(compiler, ASR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm)); } default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } if (flags & ARG2_IMM) { - FAIL_IF(load_immediate(compiler, TMP_REG2, arg2)); - arg2 = TMP_REG2; + imm = arg2; + arg2 = (arg1 == TMP_REG1) ? TMP_REG2 : TMP_REG1; + FAIL_IF(load_immediate(compiler, arg2, imm)); } else { - FAIL_IF(load_immediate(compiler, TMP_REG1, arg1)); - arg1 = TMP_REG1; + imm = arg1; + arg1 = (arg2 == TMP_REG1) ? TMP_REG2 : TMP_REG1; + FAIL_IF(load_immediate(compiler, arg1, imm)); } + + SLJIT_ASSERT(arg1 != arg2); } /* Both arguments are registers. */ @@ -680,108 +743,98 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s case SLJIT_MOV_U32: case SLJIT_MOV_S32: case SLJIT_MOV_P: - case SLJIT_MOVU: - case SLJIT_MOVU_U32: - case SLJIT_MOVU_S32: - case SLJIT_MOVU_P: - SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); + SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2); if (dst == arg2) return SLJIT_SUCCESS; return push_inst16(compiler, MOV | SET_REGS44(dst, arg2)); case SLJIT_MOV_U8: - case SLJIT_MOVU_U8: - SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); + SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2); if (IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, UXTB | RD3(dst) | RN3(arg2)); return push_inst32(compiler, UXTB_W | RD4(dst) | RM4(arg2)); case SLJIT_MOV_S8: - case SLJIT_MOVU_S8: - SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); + SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2); if (IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, SXTB | RD3(dst) | RN3(arg2)); return push_inst32(compiler, SXTB_W | RD4(dst) | RM4(arg2)); case SLJIT_MOV_U16: - case SLJIT_MOVU_U16: - SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); + SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2); if (IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, UXTH | RD3(dst) | RN3(arg2)); return push_inst32(compiler, UXTH_W | RD4(dst) | RM4(arg2)); case SLJIT_MOV_S16: - case SLJIT_MOVU_S16: - SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); + SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2); if (IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, SXTH | RD3(dst) | RN3(arg2)); return push_inst32(compiler, SXTH_W | RD4(dst) | RM4(arg2)); case SLJIT_NOT: - SLJIT_ASSERT(arg1 == TMP_REG1); - if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + SLJIT_ASSERT(arg1 == TMP_REG2); + if (IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, MVNS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, MVN_W | (flags & SET_FLAGS) | RD4(dst) | RM4(arg2)); case SLJIT_CLZ: - SLJIT_ASSERT(arg1 == TMP_REG1); + SLJIT_ASSERT(arg1 == TMP_REG2); FAIL_IF(push_inst32(compiler, CLZ | RN4(arg2) | RD4(dst) | RM4(arg2))); - if (flags & SET_FLAGS) { - if (reg_map[dst] <= 7) - return push_inst16(compiler, CMPI | RDN3(dst)); - return push_inst32(compiler, ADD_WI | SET_FLAGS | RN4(dst) | RD4(dst)); - } return SLJIT_SUCCESS; case SLJIT_ADD: - if (!(flags & KEEP_FLAGS) && IS_3_LO_REGS(dst, arg1, arg2)) + if (IS_3_LO_REGS(dst, arg1, arg2)) return push_inst16(compiler, ADDS | RD3(dst) | RN3(arg1) | RM3(arg2)); if (dst == arg1 && !(flags & SET_FLAGS)) return push_inst16(compiler, ADD | SET_REGS44(dst, arg2)); return push_inst32(compiler, ADD_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); case SLJIT_ADDC: - if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + if (dst == arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, ADCS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, ADC_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); case SLJIT_SUB: - if (!(flags & KEEP_FLAGS) && IS_3_LO_REGS(dst, arg1, arg2)) + if (flags & UNUSED_RETURN) { + if (IS_2_LO_REGS(arg1, arg2)) + return push_inst16(compiler, CMP | RD3(arg1) | RN3(arg2)); + return push_inst16(compiler, CMP_X | SET_REGS44(arg1, arg2)); + } + if (IS_3_LO_REGS(dst, arg1, arg2)) return push_inst16(compiler, SUBS | RD3(dst) | RN3(arg1) | RM3(arg2)); return push_inst32(compiler, SUB_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); case SLJIT_SUBC: - if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + if (dst == arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, SBCS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, SBC_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); case SLJIT_MUL: if (!(flags & SET_FLAGS)) return push_inst32(compiler, MUL | RD4(dst) | RN4(arg1) | RM4(arg2)); - SLJIT_ASSERT(reg_map[TMP_REG2] <= 7 && dst != TMP_REG2); + SLJIT_ASSERT(dst != TMP_REG2); FAIL_IF(push_inst32(compiler, SMULL | RT4(dst) | RD4(TMP_REG2) | RN4(arg1) | RM4(arg2))); /* cmp TMP_REG2, dst asr #31. */ return push_inst32(compiler, CMP_W | RN4(TMP_REG2) | 0x70e0 | RM4(dst)); case SLJIT_AND: - if (!(flags & KEEP_FLAGS)) { - if (dst == arg1 && IS_2_LO_REGS(dst, arg2)) - return push_inst16(compiler, ANDS | RD3(dst) | RN3(arg2)); - if ((flags & UNUSED_RETURN) && IS_2_LO_REGS(arg1, arg2)) - return push_inst16(compiler, TST | RD3(arg1) | RN3(arg2)); - } + if (dst == arg1 && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, ANDS | RD3(dst) | RN3(arg2)); + if ((flags & UNUSED_RETURN) && IS_2_LO_REGS(arg1, arg2)) + return push_inst16(compiler, TST | RD3(arg1) | RN3(arg2)); return push_inst32(compiler, AND_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); case SLJIT_OR: - if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + if (dst == arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, ORRS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, ORR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); case SLJIT_XOR: - if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + if (dst == arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, EORS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, EOR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); case SLJIT_SHL: - if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + if (dst == arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, LSLS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, LSL_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); case SLJIT_LSHR: - if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + if (dst == arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, LSRS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, LSR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); case SLJIT_ASHR: - if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + if (dst == arg1 && IS_2_LO_REGS(dst, arg2)) return push_inst16(compiler, ASRS | RD3(dst) | RN3(arg2)); return push_inst32(compiler, ASR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); } - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_SUCCESS; } @@ -791,9 +844,7 @@ static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, s #define WORD_SIZE 0x00 #define BYTE_SIZE 0x04 #define HALF_SIZE 0x08 - -#define UPDATE 0x10 -#define ARG_TEST 0x20 +#define PRELOAD 0x0c #define IS_WORD_SIZE(flags) (!(flags & (BYTE_SIZE | HALF_SIZE))) #define OFFSET_CHECK(imm, shift) (!(argw & ~(imm << shift))) @@ -849,7 +900,7 @@ static const sljit_ins sljit_mem16_imm5[12] = { #define MEM_IMM8 0xc00 #define MEM_IMM12 0x800000 -static const sljit_ins sljit_mem32[12] = { +static const sljit_ins sljit_mem32[13] = { /* w u l */ 0xf8500000 /* ldr.w */, /* w u s */ 0xf8400000 /* str.w */, /* w s l */ 0xf8500000 /* ldr.w */, @@ -864,6 +915,8 @@ static const sljit_ins sljit_mem32[12] = { /* h u s */ 0xf8200000 /* strsh.w */, /* h s l */ 0xf9300000 /* ldrsh.w */, /* h s s */ 0xf8200000 /* strsh.w */, + +/* p u l */ 0xf8100000 /* pld */, }; /* Helper function. Dst should be reg + value, using at most 1 instruction, flags does not set. */ @@ -887,240 +940,92 @@ static sljit_s32 emit_set_delta(struct sljit_compiler *compiler, sljit_s32 dst, return SLJIT_ERR_UNSUPPORTED; } -/* Can perform an operation using at most 1 instruction. */ -static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) +static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, + sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg) { - sljit_s32 other_r, shift; + sljit_s32 other_r; + sljit_uw tmp; SLJIT_ASSERT(arg & SLJIT_MEM); + SLJIT_ASSERT((arg & REG_MASK) != tmp_reg); + arg &= ~SLJIT_MEM; - if (SLJIT_UNLIKELY(flags & UPDATE)) { - if ((arg & REG_MASK) && !(arg & OFFS_REG_MASK) && argw <= 0xff && argw >= -0xff) { - if (SLJIT_UNLIKELY(flags & ARG_TEST)) - return 1; - - flags &= ~UPDATE; - arg &= 0xf; - if (argw >= 0) - argw |= 0x200; - else { - argw = -argw; - } - - SLJIT_ASSERT(argw >= 0 && (argw & 0xff) <= 0xff); - FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(arg) | 0x100 | argw)); - return -1; + if (SLJIT_UNLIKELY(!(arg & REG_MASK))) { + tmp = get_imm(argw & ~0xfff); + if (tmp != INVALID_IMM) { + FAIL_IF(push_inst32(compiler, MOV_WI | RD4(tmp_reg) | tmp)); + return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(tmp_reg) | (argw & 0xfff)); } - return 0; + + FAIL_IF(load_immediate(compiler, tmp_reg, argw)); + if (IS_2_LO_REGS(reg, tmp_reg) && sljit_mem16_imm5[flags]) + return push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(tmp_reg)); + return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(tmp_reg)); } if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { - if (SLJIT_UNLIKELY(flags & ARG_TEST)) - return 1; - argw &= 0x3; other_r = OFFS_REG(arg); arg &= 0xf; if (!argw && IS_3_LO_REGS(reg, arg, other_r)) - FAIL_IF(push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(other_r))); - else - FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(other_r) | (argw << 4))); - return -1; + return push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(other_r)); + return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(other_r) | (argw << 4)); } - if (!(arg & REG_MASK) || argw > 0xfff || argw < -0xff) - return 0; + if (argw > 0xfff) { + tmp = get_imm(argw & ~0xfff); + if (tmp != INVALID_IMM) { + push_inst32(compiler, ADD_WI | RD4(tmp_reg) | RN4(arg) | tmp); + arg = tmp_reg; + argw = argw & 0xfff; + } + } + else if (argw < -0xff) { + tmp = get_imm(-argw & ~0xff); + if (tmp != INVALID_IMM) { + push_inst32(compiler, SUB_WI | RD4(tmp_reg) | RN4(arg) | tmp); + arg = tmp_reg; + argw = -(-argw & 0xff); + } + } - if (SLJIT_UNLIKELY(flags & ARG_TEST)) - return 1; - - arg &= 0xf; if (IS_2_LO_REGS(reg, arg) && sljit_mem16_imm5[flags]) { - shift = 3; + tmp = 3; if (IS_WORD_SIZE(flags)) { if (OFFSET_CHECK(0x1f, 2)) - shift = 2; + tmp = 2; } else if (flags & BYTE_SIZE) { if (OFFSET_CHECK(0x1f, 0)) - shift = 0; + tmp = 0; } else { SLJIT_ASSERT(flags & HALF_SIZE); if (OFFSET_CHECK(0x1f, 1)) - shift = 1; + tmp = 1; } - if (shift != 3) { - FAIL_IF(push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(arg) | (argw << (6 - shift)))); - return -1; - } + if (tmp < 3) + return push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(arg) | (argw << (6 - tmp))); + } + else if (SLJIT_UNLIKELY(arg == SLJIT_SP) && IS_WORD_SIZE(flags) && OFFSET_CHECK(0xff, 2) && reg_map[reg] <= 7) { + /* SP based immediate. */ + return push_inst16(compiler, STR_SP | ((flags & STORE) ? 0 : 0x800) | RDN3(reg) | (argw >> 2)); } - /* SP based immediate. */ - if (SLJIT_UNLIKELY(arg == SLJIT_SP) && OFFSET_CHECK(0xff, 2) && IS_WORD_SIZE(flags) && reg_map[reg] <= 7) { - FAIL_IF(push_inst16(compiler, STR_SP | ((flags & STORE) ? 0 : 0x800) | RDN3(reg) | (argw >> 2))); - return -1; - } + if (argw >= 0 && argw <= 0xfff) + return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(arg) | argw); + else if (argw < 0 && argw >= -0xff) + return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(arg) | -argw); - if (argw >= 0) - FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(arg) | argw)); - else - FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(arg) | -argw)); - return -1; -} + SLJIT_ASSERT(arg != tmp_reg); -/* see getput_arg below. - Note: can_cache is called only for binary operators. Those - operators always uses word arguments without write back. */ -static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) -{ - sljit_sw diff; - if ((arg & OFFS_REG_MASK) || !(next_arg & SLJIT_MEM)) - return 0; - - if (!(arg & REG_MASK)) { - diff = argw - next_argw; - if (diff <= 0xfff && diff >= -0xfff) - return 1; - return 0; - } - - if (argw == next_argw) - return 1; - - diff = argw - next_argw; - if (arg == next_arg && diff <= 0xfff && diff >= -0xfff) - return 1; - - return 0; -} - -/* Emit the necessary instructions. See can_cache above. */ -static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, - sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) -{ - sljit_s32 tmp_r, other_r; - sljit_sw diff; - - SLJIT_ASSERT(arg & SLJIT_MEM); - if (!(next_arg & SLJIT_MEM)) { - next_arg = 0; - next_argw = 0; - } - - tmp_r = (flags & STORE) ? TMP_REG3 : reg; - - if (SLJIT_UNLIKELY((flags & UPDATE) && (arg & REG_MASK))) { - /* Update only applies if a base register exists. */ - /* There is no caching here. */ - other_r = OFFS_REG(arg); - arg &= 0xf; - flags &= ~UPDATE; - - if (!other_r) { - if (!(argw & ~0xfff)) { - FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(arg) | argw)); - return push_inst32(compiler, ADDWI | RD4(arg) | RN4(arg) | IMM12(argw)); - } - - if (compiler->cache_arg == SLJIT_MEM) { - if (argw == compiler->cache_argw) { - other_r = TMP_REG3; - argw = 0; - } - else if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - compiler->cache_argw = argw; - other_r = TMP_REG3; - argw = 0; - } - } - - if (argw) { - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); - compiler->cache_arg = SLJIT_MEM; - compiler->cache_argw = argw; - other_r = TMP_REG3; - argw = 0; - } - } - - argw &= 0x3; - if (!argw && IS_3_LO_REGS(reg, arg, other_r)) { - FAIL_IF(push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(other_r))); - return push_inst16(compiler, ADD | SET_REGS44(arg, other_r)); - } - FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(other_r) | (argw << 4))); - return push_inst32(compiler, ADD_W | RD4(arg) | RN4(arg) | RM4(other_r) | (argw << 6)); - } - flags &= ~UPDATE; - - SLJIT_ASSERT(!(arg & OFFS_REG_MASK)); - - if (compiler->cache_arg == arg) { - diff = argw - compiler->cache_argw; - if (!(diff & ~0xfff)) - return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(TMP_REG3) | diff); - if (!((compiler->cache_argw - argw) & ~0xff)) - return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(TMP_REG3) | (compiler->cache_argw - argw)); - if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, diff) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(TMP_REG3) | 0); - } - } - - next_arg = (arg & REG_MASK) && (arg == next_arg) && (argw != next_argw); - arg &= 0xf; - if (arg && compiler->cache_arg == SLJIT_MEM) { - if (compiler->cache_argw == argw) - return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(TMP_REG3)); - if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - compiler->cache_argw = argw; - return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(TMP_REG3)); - } - } - - compiler->cache_argw = argw; - if (next_arg && emit_set_delta(compiler, TMP_REG3, arg, argw) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - compiler->cache_arg = SLJIT_MEM | arg; - arg = 0; - } - else { - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); - compiler->cache_arg = SLJIT_MEM; - - diff = argw - next_argw; - if (next_arg && diff <= 0xfff && diff >= -0xfff) { - FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG3, arg))); - compiler->cache_arg = SLJIT_MEM | arg; - arg = 0; - } - } - - if (arg) - return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(TMP_REG3)); - return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(TMP_REG3) | 0); -} - -static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) -{ - if (getput_arg_fast(compiler, flags, reg, arg, argw)) - return compiler->error; - compiler->cache_arg = 0; - compiler->cache_argw = 0; - return getput_arg(compiler, flags, reg, arg, argw, 0, 0); -} - -static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w) -{ - if (getput_arg_fast(compiler, flags, reg, arg1, arg1w)) - return compiler->error; - return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w); + FAIL_IF(load_immediate(compiler, tmp_reg, argw)); + if (IS_3_LO_REGS(reg, arg, tmp_reg)) + return push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(tmp_reg)); + return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(tmp_reg)); } /* --------------------------------------------------------------------- */ @@ -1128,17 +1033,18 @@ static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, slji /* --------------------------------------------------------------------- */ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { - sljit_s32 size, i, tmp; - sljit_ins push; + sljit_s32 args, size, i, tmp; + sljit_ins push = 0; +#ifdef _WIN32 + sljit_uw imm; +#endif CHECK_ERROR(); - 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); - - push = (1 << 4); + CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); tmp = saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - saveds) : SLJIT_FIRST_SAVED_REG; for (i = SLJIT_S0; i >= tmp; i--) @@ -1152,15 +1058,30 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi : push_inst16(compiler, PUSH | (1 << 8) | push)); /* Stack must be aligned to 8 bytes: (LR, R4) */ - size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 2); + size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); local_size = ((size + local_size + 7) & ~7) - size; compiler->local_size = local_size; + +#ifdef _WIN32 + if (local_size >= 256) { + if (local_size > 4096) + imm = get_imm(4096); + else + imm = get_imm(local_size & ~0xff); + + SLJIT_ASSERT(imm != INVALID_IMM); + FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(SLJIT_SP) | imm)); + } +#else if (local_size > 0) { if (local_size <= (127 << 2)) FAIL_IF(push_inst16(compiler, SUB_SP | (local_size >> 2))); else FAIL_IF(emit_op_imm(compiler, SLJIT_SUB | ARG2_IMM, SLJIT_SP, SLJIT_SP, local_size)); } +#endif + + args = get_arg_count(arg_types); if (args >= 1) FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_S0, SLJIT_R0))); @@ -1169,20 +1090,75 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi if (args >= 3) FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_S2, SLJIT_R2))); +#ifdef _WIN32 + if (local_size >= 256) { + if (local_size > 4096) { + imm = get_imm(4096); + SLJIT_ASSERT(imm != INVALID_IMM); + + if (local_size < 4 * 4096) { + if (local_size > 2 * 4096) { + FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG2) | RN4(TMP_REG1))); + FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(TMP_REG1) | imm)); + local_size -= 4096; + } + + if (local_size > 2 * 4096) { + FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG2) | RN4(TMP_REG1))); + FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(TMP_REG1) | imm)); + local_size -= 4096; + } + + FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG2) | RN4(TMP_REG1))); + local_size -= 4096; + + SLJIT_ASSERT(local_size > 0); + } + else { + FAIL_IF(load_immediate(compiler, SLJIT_R3, (local_size >> 12) - 1)); + FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG2) | RN4(TMP_REG1))); + FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(TMP_REG1) | imm)); + SLJIT_ASSERT(reg_map[SLJIT_R3] < 7); + FAIL_IF(push_inst16(compiler, SUBSI8 | RDN3(SLJIT_R3) | 1)); + FAIL_IF(push_inst16(compiler, BCC | (0x1 << 8) /* not-equal */ | (-7 & 0xff))); + + local_size &= 0xfff; + + if (local_size != 0) + FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG2) | RN4(TMP_REG1))); + } + + if (local_size >= 256) { + imm = get_imm(local_size & ~0xff); + SLJIT_ASSERT(imm != INVALID_IMM); + + FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(TMP_REG1) | imm)); + } + } + + local_size &= 0xff; + FAIL_IF(push_inst32(compiler, LDRI | 0x400 | (local_size > 0 ? 0x100 : 0) | RT4(TMP_REG2) | RN4(TMP_REG1) | local_size)); + + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_SP, TMP_REG1))); + } + else if (local_size > 0) + FAIL_IF(push_inst32(compiler, LDRI | 0x500 | RT4(TMP_REG1) | RN4(SLJIT_SP) | local_size)); +#endif + return SLJIT_SUCCESS; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { sljit_s32 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); + CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 2); + size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); compiler->local_size = ((size + local_size + 7) & ~7) - size; return SLJIT_SUCCESS; } @@ -1190,7 +1166,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *comp SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) { sljit_s32 i, tmp; - sljit_ins pop; + sljit_ins pop = 0; CHECK_ERROR(); CHECK(check_sljit_emit_return(compiler, op, src, srcw)); @@ -1204,8 +1180,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp FAIL_IF(emit_op_imm(compiler, SLJIT_ADD | ARG2_IMM, SLJIT_SP, SLJIT_SP, compiler->local_size)); } - pop = (1 << 4); - tmp = compiler->saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - compiler->saveds) : SLJIT_FIRST_SAVED_REG; for (i = SLJIT_S0; i >= tmp; i--) pop |= 1 << reg_map[i]; @@ -1222,11 +1196,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp /* Operators */ /* --------------------------------------------------------------------- */ +#if !(defined __ARM_FEATURE_IDIV) && !(defined __ARM_ARCH_EXT_IDIV__) + #ifdef __cplusplus extern "C" { #endif -#if defined(__GNUC__) +#ifdef _WIN32 +extern unsigned long long __rt_udiv(unsigned int denominator, unsigned int numerator); +extern long long __rt_sdiv(int denominator, int numerator); +#elif defined(__GNUC__) extern unsigned int __aeabi_uidivmod(unsigned int numerator, int unsigned denominator); extern int __aeabi_idivmod(int numerator, int denominator); #else @@ -1237,10 +1216,14 @@ extern int __aeabi_idivmod(int numerator, int denominator); } #endif +#endif /* !__ARM_FEATURE_IDIV && !__ARM_ARCH_EXT_IDIV__ */ + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op) { +#if !(defined __ARM_FEATURE_IDIV) && !(defined __ARM_ARCH_EXT_IDIV__) sljit_sw saved_reg_list[3]; sljit_sw saved_reg_count; +#endif CHECK_ERROR(); CHECK(check_sljit_emit_op0(compiler, op)); @@ -1258,16 +1241,27 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile | (reg_map[SLJIT_R0] << 12) | (reg_map[SLJIT_R0] << 16) | reg_map[SLJIT_R1]); +#if (defined __ARM_FEATURE_IDIV) || (defined __ARM_ARCH_EXT_IDIV__) + case SLJIT_DIVMOD_UW: + case SLJIT_DIVMOD_SW: + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, SLJIT_R0))); + FAIL_IF(push_inst32(compiler, (op == SLJIT_DIVMOD_UW ? UDIV : SDIV) | RD4(SLJIT_R0) | RN4(SLJIT_R0) | RM4(SLJIT_R1))); + FAIL_IF(push_inst32(compiler, MUL | RD4(SLJIT_R1) | RN4(SLJIT_R0) | RM4(SLJIT_R1))); + return push_inst32(compiler, SUB_W | RD4(SLJIT_R1) | RN4(TMP_REG1) | RM4(SLJIT_R1)); + case SLJIT_DIV_UW: + case SLJIT_DIV_SW: + return push_inst32(compiler, (op == SLJIT_DIV_UW ? UDIV : SDIV) | RD4(SLJIT_R0) | RN4(SLJIT_R0) | RM4(SLJIT_R1)); +#else /* !__ARM_FEATURE_IDIV && !__ARM_ARCH_EXT_IDIV__ */ case SLJIT_DIVMOD_UW: case SLJIT_DIVMOD_SW: case SLJIT_DIV_UW: case SLJIT_DIV_SW: SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments); - SLJIT_COMPILE_ASSERT(reg_map[2] == 1 && reg_map[3] == 2 && reg_map[4] == 12, bad_register_mapping); + SLJIT_ASSERT(reg_map[2] == 1 && reg_map[3] == 2 && reg_map[4] == 3); saved_reg_count = 0; if (compiler->scratches >= 4) - saved_reg_list[saved_reg_count++] = 12; + saved_reg_list[saved_reg_count++] = 3; if (compiler->scratches >= 3) saved_reg_list[saved_reg_count++] = 2; if (op >= SLJIT_DIV_UW) @@ -1286,7 +1280,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile } } -#if defined(__GNUC__) +#ifdef _WIN32 + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, SLJIT_R0))); + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_R0, SLJIT_R1))); + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_R1, TMP_REG1))); + FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM, + ((op | 0x2) == SLJIT_DIV_UW ? SLJIT_FUNC_OFFSET(__rt_udiv) : SLJIT_FUNC_OFFSET(__rt_sdiv)))); +#elif defined(__GNUC__) FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM, ((op | 0x2) == SLJIT_DIV_UW ? SLJIT_FUNC_OFFSET(__aeabi_uidivmod) : SLJIT_FUNC_OFFSET(__aeabi_idivmod)))); #else @@ -1306,6 +1306,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile | (saved_reg_list[0] << 12) /* ldr rX, [sp], #8/16 */); } return SLJIT_SUCCESS; +#endif /* __ARM_FEATURE_IDIV || __ARM_ARCH_EXT_IDIV__ */ } return SLJIT_SUCCESS; @@ -1323,13 +1324,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(dst, dstw); ADJUST_LOCAL_OFFSET(src, srcw); - compiler->cache_arg = 0; - compiler->cache_argw = 0; + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) { + /* Since TMP_PC has index 15, IS_2_LO_REGS and IS_3_LO_REGS checks always fail. */ + if (op <= SLJIT_MOV_P && (src & SLJIT_MEM)) + return emit_op_mem(compiler, PRELOAD, TMP_PC, src, srcw, TMP_REG1); + return SLJIT_SUCCESS; + } dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1; op = GET_OPCODE(op); - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_P) { + if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) { switch (op) { case SLJIT_MOV: case SLJIT_MOV_U32: @@ -1357,58 +1362,26 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile if (src & SLJIT_IMM) srcw = (sljit_s16)srcw; break; - case SLJIT_MOVU: - case SLJIT_MOVU_U32: - case SLJIT_MOVU_S32: - case SLJIT_MOVU_P: - flags = WORD_SIZE | UPDATE; - break; - case SLJIT_MOVU_U8: - flags = BYTE_SIZE | UPDATE; - if (src & SLJIT_IMM) - srcw = (sljit_u8)srcw; - break; - case SLJIT_MOVU_S8: - flags = BYTE_SIZE | SIGNED | UPDATE; - if (src & SLJIT_IMM) - srcw = (sljit_s8)srcw; - break; - case SLJIT_MOVU_U16: - flags = HALF_SIZE | UPDATE; - if (src & SLJIT_IMM) - srcw = (sljit_u16)srcw; - break; - case SLJIT_MOVU_S16: - flags = HALF_SIZE | SIGNED | UPDATE; - if (src & SLJIT_IMM) - srcw = (sljit_s16)srcw; - break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); flags = 0; break; } if (src & SLJIT_IMM) - FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG1, srcw)); + FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG2, srcw)); else if (src & SLJIT_MEM) { - if (getput_arg_fast(compiler, flags, dst_r, src, srcw)) - FAIL_IF(compiler->error); - else - FAIL_IF(getput_arg(compiler, flags, dst_r, src, srcw, dst, dstw)); + FAIL_IF(emit_op_mem(compiler, flags, dst_r, src, srcw, TMP_REG1)); } else { if (dst_r != TMP_REG1) - return emit_op_imm(compiler, op, dst_r, TMP_REG1, src); + return emit_op_imm(compiler, op, dst_r, TMP_REG2, src); dst_r = src; } - if (dst & SLJIT_MEM) { - if (getput_arg_fast(compiler, flags | STORE, dst_r, dst, dstw)) - return compiler->error; - else - return getput_arg(compiler, flags | STORE, dst_r, dst, dstw, 0, 0); - } - return SLJIT_SUCCESS; + if (!(dst & SLJIT_MEM)) + return SLJIT_SUCCESS; + + return emit_op_mem(compiler, flags | STORE, dst_r, dst, dstw, TMP_REG2); } if (op == SLJIT_NEG) { @@ -1419,28 +1392,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile return sljit_emit_op2(compiler, SLJIT_SUB | op_flags, dst, dstw, SLJIT_IMM, 0, src, srcw); } - flags = (GET_FLAGS(op_flags) ? SET_FLAGS : 0) | ((op_flags & SLJIT_KEEP_FLAGS) ? KEEP_FLAGS : 0); + flags = HAS_FLAGS(op_flags) ? SET_FLAGS : 0; + if (src & SLJIT_MEM) { - if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG2, src, srcw)) - FAIL_IF(compiler->error); - else - FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src, srcw, dst, dstw)); - src = TMP_REG2; + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1)); + src = TMP_REG1; } - if (src & SLJIT_IMM) - flags |= ARG2_IMM; - else - srcw = src; + emit_op_imm(compiler, flags | op, dst_r, TMP_REG2, src); - emit_op_imm(compiler, flags | op, dst_r, TMP_REG1, srcw); - - if (dst & SLJIT_MEM) { - if (getput_arg_fast(compiler, flags | STORE, dst_r, dst, dstw)) - return compiler->error; - else - return getput_arg(compiler, flags | STORE, dst_r, dst, dstw, 0, 0); - } + if (SLJIT_UNLIKELY(dst & SLJIT_MEM)) + return emit_op_mem(compiler, flags | STORE, dst_r, dst, dstw, TMP_REG2); return SLJIT_SUCCESS; } @@ -1449,7 +1411,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { - sljit_s32 dst_r, flags; + sljit_s32 dst_reg, flags, src2_reg; CHECK_ERROR(); CHECK(check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w)); @@ -1457,70 +1419,39 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); - compiler->cache_arg = 0; - compiler->cache_argw = 0; + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) + return SLJIT_SUCCESS; - dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1; - flags = (GET_FLAGS(op) ? SET_FLAGS : 0) | ((op & SLJIT_KEEP_FLAGS) ? KEEP_FLAGS : 0); - - if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, WORD_SIZE | STORE | ARG_TEST, TMP_REG1, dst, dstw)) - flags |= SLOW_DEST; - - if (src1 & SLJIT_MEM) { - if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG1, src1, src1w)) - FAIL_IF(compiler->error); - else - flags |= SLOW_SRC1; - } - if (src2 & SLJIT_MEM) { - if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG2, src2, src2w)) - FAIL_IF(compiler->error); - else - flags |= SLOW_SRC2; - } - - if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { - if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { - FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src2, src2w, src1, src1w)); - FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG1, src1, src1w, dst, dstw)); - } - else { - FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG1, src1, src1w, src2, src2w)); - FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src2, src2w, dst, dstw)); - } - } - else if (flags & SLOW_SRC1) - FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG1, src1, src1w, dst, dstw)); - else if (flags & SLOW_SRC2) - FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src2, src2w, dst, dstw)); - - if (src1 & SLJIT_MEM) - src1 = TMP_REG1; - if (src2 & SLJIT_MEM) - src2 = TMP_REG2; + dst_reg = SLOW_IS_REG(dst) ? dst : TMP_REG1; + flags = HAS_FLAGS(op) ? SET_FLAGS : 0; if (src1 & SLJIT_IMM) flags |= ARG1_IMM; + else if (src1 & SLJIT_MEM) { + emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src1, src1w, TMP_REG1); + src1w = TMP_REG1; + } else src1w = src1; + if (src2 & SLJIT_IMM) flags |= ARG2_IMM; + else if (src2 & SLJIT_MEM) { + src2_reg = (!(flags & ARG1_IMM) && (src1w == TMP_REG1)) ? TMP_REG2 : TMP_REG1; + emit_op_mem(compiler, WORD_SIZE, src2_reg, src2, src2w, src2_reg); + src2w = src2_reg; + } else src2w = src2; if (dst == SLJIT_UNUSED) flags |= UNUSED_RETURN; - emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src1w, src2w); + emit_op_imm(compiler, flags | GET_OPCODE(op), dst_reg, src1w, src2w); - if (dst & SLJIT_MEM) { - if (!(flags & SLOW_DEST)) { - getput_arg_fast(compiler, WORD_SIZE | STORE, dst_r, dst, dstw); - return compiler->error; - } - return getput_arg(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, 0, 0); - } - return SLJIT_SUCCESS; + if (!(dst & SLJIT_MEM)) + return SLJIT_SUCCESS; + return emit_op_mem(compiler, WORD_SIZE | STORE, dst_reg, dst, dstw, TMP_REG2); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) @@ -1532,7 +1463,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) { CHECK_REG_INDEX(check_sljit_get_float_register_index(reg)); - return reg << 1; + return (freg_map[reg] << 1); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, @@ -1550,21 +1481,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c /* Floating point operators */ /* --------------------------------------------------------------------- */ -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void) -{ -#ifdef SLJIT_IS_FPU_AVAILABLE - return SLJIT_IS_FPU_AVAILABLE; -#else - /* Available by default. */ - return 1; -#endif -} - #define FPU_LOAD (1 << 20) static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) { - sljit_sw tmp; sljit_uw imm; sljit_sw inst = VSTR_F32 | (flags & (SLJIT_F32_OP | FPU_LOAD)); @@ -1572,8 +1492,8 @@ static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, /* Fast loads and stores. */ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { - FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG2) | RN4(arg & REG_MASK) | RM4(OFFS_REG(arg)) | ((argw & 0x3) << 6))); - arg = SLJIT_MEM | TMP_REG2; + FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(arg & REG_MASK) | RM4(OFFS_REG(arg)) | ((argw & 0x3) << 6))); + arg = SLJIT_MEM | TMP_REG1; argw = 0; } @@ -1584,21 +1504,6 @@ static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, return push_inst32(compiler, inst | RN4(arg & REG_MASK) | DD4(reg) | (-argw >> 2)); } - /* Slow cases */ - SLJIT_ASSERT(!(arg & OFFS_REG_MASK)); - if (compiler->cache_arg == arg) { - tmp = argw - compiler->cache_argw; - if (!(tmp & ~0x3fc)) - return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG3) | DD4(reg) | (tmp >> 2)); - if (!(-tmp & ~0x3fc)) - return push_inst32(compiler, inst | RN4(TMP_REG3) | DD4(reg) | (-tmp >> 2)); - if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, tmp) != SLJIT_ERR_UNSUPPORTED) { - FAIL_IF(compiler->error); - compiler->cache_argw = argw; - return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG3) | DD4(reg)); - } - } - if (arg & REG_MASK) { if (emit_set_delta(compiler, TMP_REG1, arg & REG_MASK, argw) != SLJIT_ERR_UNSUPPORTED) { FAIL_IF(compiler->error); @@ -1617,19 +1522,18 @@ static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, } } - compiler->cache_arg = arg; - compiler->cache_argw = argw; - - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + FAIL_IF(load_immediate(compiler, TMP_REG1, argw)); if (arg & REG_MASK) - FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG3, (arg & REG_MASK)))); - return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG3) | DD4(reg)); + FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, (arg & REG_MASK)))); + return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG1) | DD4(reg)); } static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) { + op ^= SLJIT_F32_OP; + if (src & SLJIT_MEM) { FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, TMP_FREG1, src, srcw)); src = TMP_FREG1; @@ -1637,9 +1541,6 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp FAIL_IF(push_inst32(compiler, VCVT_S32_F32 | (op & SLJIT_F32_OP) | DD4(TMP_FREG1) | DM4(src))); - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; - if (FAST_IS_REG(dst)) return push_inst32(compiler, VMOV | (1 << 20) | RT4(dst) | DN4(TMP_FREG1)); @@ -1653,6 +1554,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp { sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; + op ^= SLJIT_F32_OP; + if (FAST_IS_REG(src)) FAIL_IF(push_inst32(compiler, VMOV | RT4(src) | DN4(TMP_FREG1))); else if (src & SLJIT_MEM) { @@ -1675,6 +1578,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { + op ^= SLJIT_F32_OP; + if (src1 & SLJIT_MEM) { emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, TMP_FREG1, src1, src1w); src1 = TMP_FREG1; @@ -1696,16 +1601,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil sljit_s32 dst_r; CHECK_ERROR(); - compiler->cache_arg = 0; - compiler->cache_argw = 0; - if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32) - op ^= SLJIT_F32_OP; SLJIT_COMPILE_ASSERT((SLJIT_F32_OP == 0x100), float_transfer_bit_error); SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw); dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; + if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32) + op ^= SLJIT_F32_OP; + if (src & SLJIT_MEM) { emit_fop_mem(compiler, (op & SLJIT_F32_OP) | FPU_LOAD, dst_r, src, srcw); src = dst_r; @@ -1750,8 +1654,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); - compiler->cache_arg = 0; - compiler->cache_argw = 0; op ^= SLJIT_F32_OP; dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; @@ -1796,21 +1698,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); ADJUST_LOCAL_OFFSET(dst, dstw); - /* For UNUSED dst. Uncommon, but possible. */ - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; + SLJIT_ASSERT(reg_map[TMP_REG2] == 14); if (FAST_IS_REG(dst)) - return push_inst16(compiler, MOV | SET_REGS44(dst, TMP_REG3)); + return push_inst16(compiler, MOV | SET_REGS44(dst, TMP_REG2)); /* Memory. */ - if (getput_arg_fast(compiler, WORD_SIZE | STORE, TMP_REG3, dst, dstw)) - return compiler->error; - /* TMP_REG3 is used for caching. */ - FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG2, TMP_REG3))); - compiler->cache_arg = 0; - compiler->cache_argw = 0; - return getput_arg(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw, 0, 0); + return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw, TMP_REG1); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw) @@ -1819,21 +1713,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler CHECK(check_sljit_emit_fast_return(compiler, src, srcw)); ADJUST_LOCAL_OFFSET(src, srcw); + SLJIT_ASSERT(reg_map[TMP_REG2] == 14); + if (FAST_IS_REG(src)) - FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG3, src))); - else if (src & SLJIT_MEM) { - if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG3, src, srcw)) - FAIL_IF(compiler->error); - else { - compiler->cache_arg = 0; - compiler->cache_argw = 0; - FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src, srcw, 0, 0)); - FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG3, TMP_REG2))); - } - } - else if (src & SLJIT_IMM) - FAIL_IF(load_immediate(compiler, TMP_REG3, srcw)); - return push_inst16(compiler, BLX | RN3(TMP_REG3)); + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG2, src))); + else + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, src, srcw, TMP_REG2)); + + return push_inst16(compiler, BX | RN3(TMP_REG2)); } /* --------------------------------------------------------------------- */ @@ -1890,7 +1777,7 @@ static sljit_uw get_cc(sljit_s32 type) return 0x7; default: /* SLJIT_JUMP */ - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return 0xe; } } @@ -1924,7 +1811,6 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); type &= 0xff; - /* In ARM, we don't need to touch the arguments. */ PTR_FAIL_IF(emit_imm32_const(compiler, TMP_REG1, 0)); if (type < SLJIT_JUMP) { jump->flags |= IS_COND; @@ -1944,6 +1830,241 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile return jump; } +#ifdef __SOFTFP__ + +static sljit_s32 softfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src) +{ + sljit_s32 stack_offset = 0; + sljit_s32 arg_count = 0; + sljit_s32 word_arg_offset = 0; + sljit_s32 float_arg_count = 0; + sljit_s32 types = 0; + sljit_s32 src_offset = 4 * sizeof(sljit_sw); + sljit_u8 offsets[4]; + + if (src && FAST_IS_REG(*src)) + src_offset = reg_map[*src] * sizeof(sljit_sw); + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK); + + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + offsets[arg_count] = (sljit_u8)stack_offset; + stack_offset += sizeof(sljit_f32); + arg_count++; + float_arg_count++; + break; + case SLJIT_ARG_TYPE_F64: + if (stack_offset & 0x7) + stack_offset += sizeof(sljit_sw); + offsets[arg_count] = (sljit_u8)stack_offset; + stack_offset += sizeof(sljit_f64); + arg_count++; + float_arg_count++; + break; + default: + offsets[arg_count] = (sljit_u8)stack_offset; + stack_offset += sizeof(sljit_sw); + arg_count++; + word_arg_offset += sizeof(sljit_sw); + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + if (stack_offset > 16) + FAIL_IF(push_inst16(compiler, SUB_SP | (((stack_offset - 16) + 0x7) & ~0x7) >> 2)); + + SLJIT_ASSERT(reg_map[TMP_REG1] == 12); + + /* Process arguments in reversed direction. */ + while (types) { + switch (types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + arg_count--; + float_arg_count--; + stack_offset = offsets[arg_count]; + + if (stack_offset < 16) { + if (src_offset == stack_offset) { + FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7))); + *src = TMP_REG1; + } + FAIL_IF(push_inst32(compiler, VMOV | 0x100000 | (float_arg_count << 16) | (stack_offset << 10))); + } else + FAIL_IF(push_inst32(compiler, VSTR_F32 | 0x800000 | RN4(SLJIT_SP) | (float_arg_count << 12) | ((stack_offset - 16) >> 2))); + break; + case SLJIT_ARG_TYPE_F64: + arg_count--; + float_arg_count--; + stack_offset = offsets[arg_count]; + + SLJIT_ASSERT((stack_offset & 0x7) == 0); + + if (stack_offset < 16) { + if (src_offset == stack_offset || src_offset == stack_offset + sizeof(sljit_sw)) { + FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7))); + *src = TMP_REG1; + } + FAIL_IF(push_inst32(compiler, VMOV2 | 0x100000 | (stack_offset << 10) | ((stack_offset + sizeof(sljit_sw)) << 14) | float_arg_count)); + } else + FAIL_IF(push_inst32(compiler, VSTR_F32 | 0x800100 | RN4(SLJIT_SP) | (float_arg_count << 12) | ((stack_offset - 16) >> 2))); + break; + default: + arg_count--; + word_arg_offset -= sizeof(sljit_sw); + stack_offset = offsets[arg_count]; + + SLJIT_ASSERT(stack_offset >= word_arg_offset); + + if (stack_offset != word_arg_offset) { + if (stack_offset < 16) { + if (src_offset == stack_offset) { + FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7))); + *src = TMP_REG1; + } + else if (src_offset == word_arg_offset) { + *src = 1 + (stack_offset >> 2); + src_offset = stack_offset; + } + FAIL_IF(push_inst16(compiler, MOV | (stack_offset >> 2) | (word_arg_offset << 1))); + } else + FAIL_IF(push_inst16(compiler, STR_SP | (word_arg_offset << 6) | ((stack_offset - 16) >> 2))); + } + break; + } + + types >>= SLJIT_DEF_SHIFT; + } + + return SLJIT_SUCCESS; +} + +static sljit_s32 softfloat_post_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types) +{ + sljit_s32 stack_size = 0; + + if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F32) + FAIL_IF(push_inst32(compiler, VMOV | (0 << 16) | (0 << 12))); + if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F64) + FAIL_IF(push_inst32(compiler, VMOV2 | (1 << 16) | (0 << 12) | 0)); + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + stack_size += sizeof(sljit_f32); + break; + case SLJIT_ARG_TYPE_F64: + if (stack_size & 0x7) + stack_size += sizeof(sljit_sw); + stack_size += sizeof(sljit_f64); + break; + default: + stack_size += sizeof(sljit_sw); + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + if (stack_size <= 16) + return SLJIT_SUCCESS; + + return push_inst16(compiler, ADD_SP | ((((stack_size - 16) + 0x7) & ~0x7) >> 2)); +} + +#else + +static sljit_s32 hardfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types) +{ + sljit_u32 remap = 0; + sljit_u32 offset = 0; + sljit_u32 new_offset, mask; + + /* Remove return value. */ + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F32) { + new_offset = 0; + mask = 1; + + while (remap & mask) { + new_offset++; + mask <<= 1; + } + remap |= mask; + + if (offset != new_offset) + FAIL_IF(push_inst32(compiler, VMOV_F32 | DD4((new_offset >> 1) + 1) + | ((new_offset & 0x1) ? 0x400000 : 0) | DM4((offset >> 1) + 1))); + + offset += 2; + } + else if ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F64) { + new_offset = 0; + mask = 3; + + while (remap & mask) { + new_offset += 2; + mask <<= 2; + } + remap |= mask; + + if (offset != new_offset) + FAIL_IF(push_inst32(compiler, VMOV_F32 | SLJIT_F32_OP | DD4((new_offset >> 1) + 1) | DM4((offset >> 1) + 1))); + + offset += 2; + } + arg_types >>= SLJIT_DEF_SHIFT; + } + + return SLJIT_SUCCESS; +} + +#endif + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ +#ifdef __SOFTFP__ + struct sljit_jump *jump; +#endif + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); + +#ifdef __SOFTFP__ + PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + jump = sljit_emit_jump(compiler, type); + PTR_FAIL_IF(jump == NULL); + + PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types)); + return jump; +#else + PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_jump(compiler, type); +#endif +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) { struct sljit_jump *jump; @@ -1952,16 +2073,20 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); ADJUST_LOCAL_OFFSET(src, srcw); - /* In ARM, we don't need to touch the arguments. */ - if (!(src & SLJIT_IMM)) { - if (FAST_IS_REG(src)) - return push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(src)); + SLJIT_ASSERT(reg_map[TMP_REG1] != 14); - FAIL_IF(emit_op_mem(compiler, WORD_SIZE, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, src, srcw)); + if (!(src & SLJIT_IMM)) { + if (FAST_IS_REG(src)) { + SLJIT_ASSERT(reg_map[src] != 14); + return push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(src)); + } + + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, src, srcw, TMP_REG1)); if (type >= SLJIT_FAST_CALL) return push_inst16(compiler, BLX | RN3(TMP_REG1)); } + /* These jumps are converted to jump/call instructions when possible. */ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); FAIL_IF(!jump); set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0)); @@ -1972,25 +2097,55 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi return push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(TMP_REG1)); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + +#ifdef __SOFTFP__ + if (src & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1)); + src = TMP_REG1; + } + + FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); + + return softfloat_post_call_with_args(compiler, arg_types); +#else /* !__SOFTFP__ */ + FAIL_IF(hardfloat_call_with_args(compiler, arg_types)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_ijump(compiler, type, src, srcw); +#endif /* __SOFTFP__ */ +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw, sljit_s32 type) { sljit_s32 dst_r, flags = GET_ALL_FLAGS(op); - sljit_ins cc, ins; + sljit_ins cc; CHECK_ERROR(); - CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type)); + CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); ADJUST_LOCAL_OFFSET(dst, dstw); - ADJUST_LOCAL_OFFSET(src, srcw); - - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; op = GET_OPCODE(op); cc = get_cc(type & 0xff); - dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; + dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; if (op < SLJIT_ADD) { FAIL_IF(push_inst16(compiler, IT | (cc << 4) | (((cc & 0x1) ^ 0x1) << 3) | 0x4)); @@ -1998,60 +2153,141 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co FAIL_IF(push_inst32(compiler, MOV_WI | RD4(dst_r) | 1)); FAIL_IF(push_inst32(compiler, MOV_WI | RD4(dst_r) | 0)); } else { + /* The movsi (immediate) instruction does not set flags in IT block. */ FAIL_IF(push_inst16(compiler, MOVSI | RDN3(dst_r) | 1)); FAIL_IF(push_inst16(compiler, MOVSI | RDN3(dst_r) | 0)); } - if (dst_r != TMP_REG2) + if (!(dst & SLJIT_MEM)) return SLJIT_SUCCESS; - return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw); + return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, TMP_REG2); } - ins = (op == SLJIT_AND ? ANDI : (op == SLJIT_OR ? ORRI : EORI)); - if ((op == SLJIT_OR || op == SLJIT_XOR) && FAST_IS_REG(dst) && dst == src) { - /* Does not change the other bits. */ - FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8)); - FAIL_IF(push_inst32(compiler, ins | RN4(src) | RD4(dst) | 1)); - if (flags & SLJIT_SET_E) { - /* The condition must always be set, even if the ORRI/EORI is not executed above. */ - if (reg_map[dst] <= 7) - return push_inst16(compiler, MOVS | RD3(TMP_REG1) | RN3(dst)); - return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(TMP_REG1) | RM4(dst)); - } - return SLJIT_SUCCESS; - } + if (dst & SLJIT_MEM) + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2)); - compiler->cache_arg = 0; - compiler->cache_argw = 0; - if (src & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, WORD_SIZE, TMP_REG2, src, srcw, dst, dstw)); - src = TMP_REG2; - srcw = 0; - } else if (src & SLJIT_IMM) { - FAIL_IF(load_immediate(compiler, TMP_REG2, srcw)); - src = TMP_REG2; - srcw = 0; - } - - if (op == SLJIT_AND || src != dst_r) { + if (op == SLJIT_AND) { FAIL_IF(push_inst16(compiler, IT | (cc << 4) | (((cc & 0x1) ^ 0x1) << 3) | 0x4)); - FAIL_IF(push_inst32(compiler, ins | RN4(src) | RD4(dst_r) | 1)); - FAIL_IF(push_inst32(compiler, ins | RN4(src) | RD4(dst_r) | 0)); + FAIL_IF(push_inst32(compiler, ANDI | RN4(dst_r) | RD4(dst_r) | 1)); + FAIL_IF(push_inst32(compiler, ANDI | RN4(dst_r) | RD4(dst_r) | 0)); } else { FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8)); - FAIL_IF(push_inst32(compiler, ins | RN4(src) | RD4(dst_r) | 1)); + FAIL_IF(push_inst32(compiler, ((op == SLJIT_OR) ? ORRI : EORI) | RN4(dst_r) | RD4(dst_r) | 1)); } - if (dst_r == TMP_REG2) - FAIL_IF(emit_op_mem2(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw, 0, 0)); + if (dst & SLJIT_MEM) + FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, TMP_REG2)); - if (flags & SLJIT_SET_E) { - /* The condition must always be set, even if the ORR/EORI is not executed above. */ - if (reg_map[dst_r] <= 7) - return push_inst16(compiler, MOVS | RD3(TMP_REG1) | RN3(dst_r)); - return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(TMP_REG1) | RM4(dst_r)); + if (!(flags & SLJIT_SET_Z)) + return SLJIT_SUCCESS; + + /* The condition must always be set, even if the ORR/EORI is not executed above. */ + return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(TMP_REG1) | RM4(dst_r)); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ + sljit_uw cc, tmp; + + CHECK_ERROR(); + CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); + + dst_reg &= ~SLJIT_I32_OP; + + cc = get_cc(type & 0xff); + + if (!(src & SLJIT_IMM)) { + FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8)); + return push_inst16(compiler, MOV | SET_REGS44(dst_reg, src)); } - return SLJIT_SUCCESS; + + tmp = (sljit_uw) srcw; + + if (tmp < 0x10000) { + /* set low 16 bits, set hi 16 bits to 0. */ + FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8)); + return push_inst32(compiler, MOVW | RD4(dst_reg) + | COPY_BITS(tmp, 12, 16, 4) | COPY_BITS(tmp, 11, 26, 1) | COPY_BITS(tmp, 8, 12, 3) | (tmp & 0xff)); + } + + tmp = get_imm(srcw); + if (tmp != INVALID_IMM) { + FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8)); + return push_inst32(compiler, MOV_WI | RD4(dst_reg) | tmp); + } + + tmp = get_imm(~srcw); + if (tmp != INVALID_IMM) { + FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8)); + return push_inst32(compiler, MVN_WI | RD4(dst_reg) | tmp); + } + + FAIL_IF(push_inst16(compiler, IT | (cc << 4) | ((cc & 0x1) << 3) | 0x4)); + + tmp = (sljit_uw) srcw; + FAIL_IF(push_inst32(compiler, MOVW | RD4(dst_reg) + | COPY_BITS(tmp, 12, 16, 4) | COPY_BITS(tmp, 11, 26, 1) | COPY_BITS(tmp, 8, 12, 3) | (tmp & 0xff))); + return push_inst32(compiler, MOVT | RD4(dst_reg) + | COPY_BITS(tmp, 12 + 16, 16, 4) | COPY_BITS(tmp, 11 + 16, 26, 1) | COPY_BITS(tmp, 8 + 16, 12, 3) | ((tmp & 0xff0000) >> 16)); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_s32 flags; + sljit_ins inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + + if ((mem & OFFS_REG_MASK) || (memw > 255 && memw < -255)) + return SLJIT_ERR_UNSUPPORTED; + + if (type & SLJIT_MEM_SUPP) + return SLJIT_SUCCESS; + + switch (type & 0xff) { + case SLJIT_MOV: + case SLJIT_MOV_U32: + case SLJIT_MOV_S32: + case SLJIT_MOV_P: + flags = WORD_SIZE; + break; + case SLJIT_MOV_U8: + flags = BYTE_SIZE; + break; + case SLJIT_MOV_S8: + flags = BYTE_SIZE | SIGNED; + break; + case SLJIT_MOV_U16: + flags = HALF_SIZE; + break; + case SLJIT_MOV_S16: + flags = HALF_SIZE | SIGNED; + break; + default: + SLJIT_UNREACHABLE(); + flags = WORD_SIZE; + break; + } + + if (type & SLJIT_MEM_STORE) + flags |= STORE; + + inst = sljit_mem32[flags] | 0x900; + + if (type & SLJIT_MEM_PRE) + inst |= 0x400; + + if (memw >= 0) + inst |= 0x200; + else + memw = -memw; + + return push_inst32(compiler, inst | RT4(reg) | RN4(mem & REG_MASK) | memw); } SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) @@ -2067,24 +2303,26 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi PTR_FAIL_IF(!const_); set_const(const_, compiler); - dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1; + dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; PTR_FAIL_IF(emit_imm32_const(compiler, dst_r, init_value)); if (dst & SLJIT_MEM) - PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw)); + PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2)); return const_; } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) { sljit_u16 *inst = (sljit_u16*)addr; - modify_imm32_const(inst, new_addr); + modify_imm32_const(inst, new_target); + inst = (sljit_u16 *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 4); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) { sljit_u16 *inst = (sljit_u16*)addr; modify_imm32_const(inst, new_constant); + inst = (sljit_u16 *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 4); } diff --git a/pcre2-10.32/src/sljit/sljitNativeMIPS_32.c b/pcre2-10.32/src/sljit/sljitNativeMIPS_32.c new file mode 100644 index 000000000..094c9923b --- /dev/null +++ b/pcre2-10.32/src/sljit/sljitNativeMIPS_32.c @@ -0,0 +1,666 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) 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 HOLDER(S) 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. + */ + +/* mips 32-bit arch dependent functions. */ + +static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_ar, sljit_sw imm) +{ + if (!(imm & ~0xffff)) + return push_inst(compiler, ORI | SA(0) | TA(dst_ar) | IMM(imm), dst_ar); + + if (imm < 0 && imm >= SIMM_MIN) + return push_inst(compiler, ADDIU | SA(0) | TA(dst_ar) | IMM(imm), dst_ar); + + FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(imm >> 16), dst_ar)); + return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS; +} + +#define EMIT_LOGICAL(op_imm, op_norm) \ + if (flags & SRC2_IMM) { \ + if (op & SLJIT_SET_Z) \ + FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \ + if (!(flags & UNUSED_DEST)) \ + FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \ + } \ + else { \ + if (op & SLJIT_SET_Z) \ + FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ + if (!(flags & UNUSED_DEST)) \ + FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | D(dst), DR(dst))); \ + } + +#define EMIT_SHIFT(op_imm, op_v) \ + if (flags & SRC2_IMM) { \ + if (op & SLJIT_SET_Z) \ + FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); \ + if (!(flags & UNUSED_DEST)) \ + FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | SH_IMM(src2), DR(dst))); \ + } \ + else { \ + if (op & SLJIT_SET_Z) \ + FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ + if (!(flags & UNUSED_DEST)) \ + FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | D(dst), DR(dst))); \ + } + +static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags, + sljit_s32 dst, sljit_s32 src1, sljit_sw src2) +{ + sljit_s32 is_overflow, is_carry, is_handled; + + switch (GET_OPCODE(op)) { + case SLJIT_MOV: + case SLJIT_MOV_U32: + case SLJIT_MOV_S32: + case SLJIT_MOV_P: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if (dst != src2) + return push_inst(compiler, ADDU | S(src2) | TA(0) | D(dst), DR(dst)); + return SLJIT_SUCCESS; + + case SLJIT_MOV_U8: + case SLJIT_MOV_S8: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + if (op == SLJIT_MOV_S8) { +#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) + return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst)); +#else + FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(24), DR(dst))); + return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(24), DR(dst)); +#endif + } + return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst)); + } + else { + SLJIT_ASSERT(dst == src2); + } + return SLJIT_SUCCESS; + + case SLJIT_MOV_U16: + case SLJIT_MOV_S16: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + if (op == SLJIT_MOV_S16) { +#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) + return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst)); +#else + FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(16), DR(dst))); + return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(16), DR(dst)); +#endif + } + return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst)); + } + else { + SLJIT_ASSERT(dst == src2); + } + return SLJIT_SUCCESS; + + case SLJIT_NOT: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + if (!(flags & UNUSED_DEST)) + FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst))); + return SLJIT_SUCCESS; + + case SLJIT_CLZ: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); +#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, CLZ | S(src2) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); + if (!(flags & UNUSED_DEST)) + FAIL_IF(push_inst(compiler, CLZ | S(src2) | T(dst) | D(dst), DR(dst))); +#else + if (SLJIT_UNLIKELY(flags & UNUSED_DEST)) { + FAIL_IF(push_inst(compiler, SRL | T(src2) | DA(EQUAL_FLAG) | SH_IMM(31), EQUAL_FLAG)); + return push_inst(compiler, XORI | SA(EQUAL_FLAG) | TA(EQUAL_FLAG) | IMM(1), EQUAL_FLAG); + } + /* Nearly all instructions are unmovable in the following sequence. */ + FAIL_IF(push_inst(compiler, ADDU | S(src2) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); + /* Check zero. */ + FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG1) | TA(0) | IMM(5), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, ORI | SA(0) | T(dst) | IMM(32), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(dst) | IMM(-1), DR(dst))); + /* Loop for searching the highest bit. */ + FAIL_IF(push_inst(compiler, ADDIU | S(dst) | T(dst) | IMM(1), DR(dst))); + FAIL_IF(push_inst(compiler, BGEZ | S(TMP_REG1) | IMM(-2), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, SLL | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), UNMOVABLE_INS)); +#endif + return SLJIT_SUCCESS; + + case SLJIT_ADD: + is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_overflow) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else + FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + } + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); + + if (is_overflow || is_carry) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + else { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); + } + } + /* dst may be the same as src1 or src2. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(src2), DR(dst))); + } + else { + if (is_overflow) + FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); + /* dst may be the same as src1 or src2. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | D(dst), DR(dst))); + } + + /* a + b >= a | b (otherwise, the carry should be set to 1). */ + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); + if (!is_overflow) + return SLJIT_SUCCESS; + FAIL_IF(push_inst(compiler, SLL | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); + FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); + return push_inst(compiler, SRL | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG); + + case SLJIT_ADDC: + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_carry) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); + else { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, OR | S(src1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); + } + } + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(src2), DR(dst))); + } else { + if (is_carry) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + /* dst may be the same as src1 or src2. */ + FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | D(dst), DR(dst))); + } + if (is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); + + FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst))); + if (!is_carry) + return SLJIT_SUCCESS; + + /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */ + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); + /* Set carry flag. */ + return push_inst(compiler, OR | SA(OTHER_FLAG) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); + + case SLJIT_SUB: + if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + is_handled = 0; + + if (flags & SRC2_IMM) { + if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + is_handled = 1; + } + else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLTI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + is_handled = 1; + } + } + + if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) { + is_handled = 1; + + if (flags & SRC2_IMM) { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); + } + else if (GET_FLAG_TYPE(op) == SLJIT_GREATER || GET_FLAG_TYPE(op) == SLJIT_LESS_EQUAL) + { + FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); + } + else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); + } + else if (GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER || GET_FLAG_TYPE(op) == SLJIT_SIG_LESS_EQUAL) + { + FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); + } + } + + if (is_handled) { + if (flags & SRC2_IMM) { + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); + if (!(flags & UNUSED_DEST)) + return push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst)); + } + else { + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + if (!(flags & UNUSED_DEST)) + return push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst)); + } + return SLJIT_SUCCESS; + } + + is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_overflow) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else + FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + } + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); + + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + /* dst may be the same as src1 or src2. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst))); + } + else { + if (is_overflow) + FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); + /* dst may be the same as src1 or src2. */ + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) + FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst))); + } + + if (!is_overflow) + return SLJIT_SUCCESS; + FAIL_IF(push_inst(compiler, SLL | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); + FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); + return push_inst(compiler, SRL | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG); + + case SLJIT_SUBC: + if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_carry) + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); + /* dst may be the same as src1 or src2. */ + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst))); + } + else { + if (is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + /* dst may be the same as src1 or src2. */ + FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst))); + } + + if (is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | D(TMP_REG1), DR(TMP_REG1))); + + FAIL_IF(push_inst(compiler, SUBU | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst))); + return (is_carry) ? push_inst(compiler, OR | SA(EQUAL_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG) : SLJIT_SUCCESS; + + case SLJIT_MUL: + SLJIT_ASSERT(!(flags & SRC2_IMM)); + + if (GET_FLAG_TYPE(op) != SLJIT_MUL_OVERFLOW) { +#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) + return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst)); +#else + FAIL_IF(push_inst(compiler, MULT | S(src1) | T(src2), MOVABLE_INS)); + return push_inst(compiler, MFLO | D(dst), DR(dst)); +#endif + } + FAIL_IF(push_inst(compiler, MULT | S(src1) | T(src2), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, MFHI | DA(EQUAL_FLAG), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst))); + FAIL_IF(push_inst(compiler, SRA | T(dst) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG)); + return push_inst(compiler, SUBU | SA(EQUAL_FLAG) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); + + case SLJIT_AND: + EMIT_LOGICAL(ANDI, AND); + return SLJIT_SUCCESS; + + case SLJIT_OR: + EMIT_LOGICAL(ORI, OR); + return SLJIT_SUCCESS; + + case SLJIT_XOR: + EMIT_LOGICAL(XORI, XOR); + return SLJIT_SUCCESS; + + case SLJIT_SHL: + EMIT_SHIFT(SLL, SLLV); + return SLJIT_SUCCESS; + + case SLJIT_LSHR: + EMIT_SHIFT(SRL, SRLV); + return SLJIT_SUCCESS; + + case SLJIT_ASHR: + EMIT_SHIFT(SRA, SRAV); + return SLJIT_SUCCESS; + } + + SLJIT_UNREACHABLE(); + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value) +{ + FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 16), DR(dst))); + return push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value), DR(dst)); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) +{ + sljit_ins *inst = (sljit_ins *)addr; + + inst[0] = (inst[0] & 0xffff0000) | ((new_target >> 16) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | (new_target & 0xffff); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); + SLJIT_CACHE_FLUSH(inst, inst + 2); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) +{ + sljit_ins *inst = (sljit_ins *)addr; + + inst[0] = (inst[0] & 0xffff0000) | ((new_constant >> 16) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | (new_constant & 0xffff); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); + SLJIT_CACHE_FLUSH(inst, inst + 2); +} + +static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_ins *ins_ptr) +{ + sljit_s32 stack_offset = 0; + sljit_s32 arg_count = 0; + sljit_s32 float_arg_count = 0; + sljit_s32 word_arg_count = 0; + sljit_s32 types = 0; + sljit_s32 arg_count_save, types_save; + sljit_ins prev_ins = NOP; + sljit_ins ins = NOP; + sljit_u8 offsets[4]; + + SLJIT_ASSERT(reg_map[TMP_REG1] == 4 && freg_map[TMP_FREG1] == 12); + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK); + + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + offsets[arg_count] = (sljit_u8)stack_offset; + + if (word_arg_count == 0 && arg_count <= 1) + offsets[arg_count] = 254 + arg_count; + + stack_offset += sizeof(sljit_f32); + arg_count++; + float_arg_count++; + break; + case SLJIT_ARG_TYPE_F64: + if (stack_offset & 0x7) + stack_offset += sizeof(sljit_sw); + offsets[arg_count] = (sljit_u8)stack_offset; + + if (word_arg_count == 0 && arg_count <= 1) + offsets[arg_count] = 254 + arg_count; + + stack_offset += sizeof(sljit_f64); + arg_count++; + float_arg_count++; + break; + default: + offsets[arg_count] = (sljit_u8)stack_offset; + stack_offset += sizeof(sljit_sw); + arg_count++; + word_arg_count++; + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + /* Stack is aligned to 16 bytes, max two doubles can be placed on the stack. */ + if (stack_offset > 16) + FAIL_IF(push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(-16), DR(SLJIT_SP))); + + types_save = types; + arg_count_save = arg_count; + + while (types) { + switch (types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + arg_count--; + if (offsets[arg_count] < 254) + ins = SWC1 | S(SLJIT_SP) | FT(float_arg_count) | IMM(offsets[arg_count]); + float_arg_count--; + break; + case SLJIT_ARG_TYPE_F64: + arg_count--; + if (offsets[arg_count] < 254) + ins = SDC1 | S(SLJIT_SP) | FT(float_arg_count) | IMM(offsets[arg_count]); + float_arg_count--; + break; + default: + if (offsets[arg_count - 1] >= 16) + ins = SW | S(SLJIT_SP) | T(word_arg_count) | IMM(offsets[arg_count - 1]); + else if (arg_count != word_arg_count) + ins = ADDU | S(word_arg_count) | TA(0) | DA(4 + (offsets[arg_count - 1] >> 2)); + else if (arg_count == 1) + ins = ADDU | S(SLJIT_R0) | TA(0) | DA(4); + + arg_count--; + word_arg_count--; + break; + } + + if (ins != NOP) { + if (prev_ins != NOP) + FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS)); + prev_ins = ins; + ins = NOP; + } + + types >>= SLJIT_DEF_SHIFT; + } + + types = types_save; + arg_count = arg_count_save; + + while (types) { + switch (types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + arg_count--; + if (offsets[arg_count] == 254) + ins = MOV_S | FMT_S | FS(SLJIT_FR0) | FD(TMP_FREG1); + else if (offsets[arg_count] < 16) + ins = LW | S(SLJIT_SP) | TA(4 + (offsets[arg_count] >> 2)) | IMM(offsets[arg_count]); + break; + case SLJIT_ARG_TYPE_F64: + arg_count--; + if (offsets[arg_count] == 254) + ins = MOV_S | FMT_D | FS(SLJIT_FR0) | FD(TMP_FREG1); + else if (offsets[arg_count] < 16) { + if (prev_ins != NOP) + FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS)); + prev_ins = LW | S(SLJIT_SP) | TA(4 + (offsets[arg_count] >> 2)) | IMM(offsets[arg_count]); + ins = LW | S(SLJIT_SP) | TA(5 + (offsets[arg_count] >> 2)) | IMM(offsets[arg_count] + sizeof(sljit_sw)); + } + break; + default: + arg_count--; + break; + } + + if (ins != NOP) { + if (prev_ins != NOP) + FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS)); + prev_ins = ins; + ins = NOP; + } + + types >>= SLJIT_DEF_SHIFT; + } + + *ins_ptr = prev_ins; + + return SLJIT_SUCCESS; +} + +static sljit_s32 post_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types) +{ + sljit_s32 stack_offset = 0; + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + stack_offset += sizeof(sljit_f32); + break; + case SLJIT_ARG_TYPE_F64: + if (stack_offset & 0x7) + stack_offset += sizeof(sljit_sw); + stack_offset += sizeof(sljit_f64); + break; + default: + stack_offset += sizeof(sljit_sw); + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + /* Stack is aligned to 16 bytes, max two doubles can be placed on the stack. */ + if (stack_offset > 16) + return push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(16), DR(SLJIT_SP)); + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ + struct sljit_jump *jump; + sljit_ins ins; + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF(!jump); + set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); + type &= 0xff; + + PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins)); + + SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); + + PTR_FAIL_IF(emit_const(compiler, PIC_ADDR_REG, 0)); + + jump->flags |= IS_JAL | IS_CALL; + PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); + jump->addr = compiler->size; + PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS)); + + PTR_FAIL_IF(post_call_with_args(compiler, arg_types)); + + return jump; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + sljit_ins ins; + + CHECK_ERROR(); + CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + + SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); + + if (src & SLJIT_IMM) + FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw)); + else if (FAST_IS_REG(src)) + FAIL_IF(push_inst(compiler, ADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG))); + else if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw)); + } + + FAIL_IF(call_with_args(compiler, arg_types, &ins)); + + /* Register input. */ + FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS)); + return post_call_with_args(compiler, arg_types); +} diff --git a/pcre2-10.22/src/sljit/sljitNativeMIPS_64.c b/pcre2-10.32/src/sljit/sljitNativeMIPS_64.c similarity index 54% rename from pcre2-10.22/src/sljit/sljitNativeMIPS_64.c rename to pcre2-10.32/src/sljit/sljitNativeMIPS_64.c index c7ee8c9c2..f841aef5d 100644 --- a/pcre2-10.22/src/sljit/sljitNativeMIPS_64.c +++ b/pcre2-10.32/src/sljit/sljitNativeMIPS_64.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -123,15 +123,15 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_a #define EMIT_LOGICAL(op_imm, op_norm) \ if (flags & SRC2_IMM) { \ - if (op & SLJIT_SET_E) \ + if (op & SLJIT_SET_Z) \ FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \ - if (CHECK_FLAGS(SLJIT_SET_E)) \ + if (!(flags & UNUSED_DEST)) \ FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \ } \ else { \ - if (op & SLJIT_SET_E) \ + if (op & SLJIT_SET_Z) \ FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ - if (CHECK_FLAGS(SLJIT_SET_E)) \ + if (!(flags & UNUSED_DEST)) \ FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | D(dst), DR(dst))); \ } @@ -144,16 +144,16 @@ static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_a } \ else \ ins = (op & SLJIT_I32_OP) ? op_imm : op_dimm; \ - if (op & SLJIT_SET_E) \ + if (op & SLJIT_SET_Z) \ FAIL_IF(push_inst(compiler, ins | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); \ - if (CHECK_FLAGS(SLJIT_SET_E)) \ + if (!(flags & UNUSED_DEST)) \ FAIL_IF(push_inst(compiler, ins | T(src1) | D(dst) | SH_IMM(src2), DR(dst))); \ } \ else { \ ins = (op & SLJIT_I32_OP) ? op_v : op_dv; \ - if (op & SLJIT_SET_E) \ + if (op & SLJIT_SET_Z) \ FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ - if (CHECK_FLAGS(SLJIT_SET_E)) \ + if (!(flags & UNUSED_DEST)) \ FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | D(dst), DR(dst))); \ } @@ -161,6 +161,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl sljit_s32 dst, sljit_s32 src1, sljit_sw src2) { sljit_ins ins; + sljit_s32 is_overflow, is_carry, is_handled; switch (GET_OPCODE(op)) { case SLJIT_MOV: @@ -180,8 +181,9 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl } return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst)); } - else if (dst != src2) - SLJIT_ASSERT_STOP(); + else { + SLJIT_ASSERT(dst == src2); + } return SLJIT_SUCCESS; case SLJIT_MOV_U16: @@ -194,8 +196,9 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl } return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst)); } - else if (dst != src2) - SLJIT_ASSERT_STOP(); + else { + SLJIT_ASSERT(dst == src2); + } return SLJIT_SUCCESS; case SLJIT_MOV_U32: @@ -209,18 +212,18 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl case SLJIT_NOT: SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - if (op & SLJIT_SET_E) + if (op & SLJIT_SET_Z) FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (CHECK_FLAGS(SLJIT_SET_E)) + if (!(flags & UNUSED_DEST)) FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst))); return SLJIT_SUCCESS; case SLJIT_CLZ: SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); #if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) - if (op & SLJIT_SET_E) + if (op & SLJIT_SET_Z) FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (CHECK_FLAGS(SLJIT_SET_E)) + if (!(flags & UNUSED_DEST)) FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | T(dst) | D(dst), DR(dst))); #else if (SLJIT_UNLIKELY(flags & UNUSED_DEST)) { @@ -237,130 +240,192 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(dst) | T(dst) | IMM(1), DR(dst))); FAIL_IF(push_inst(compiler, BGEZ | S(TMP_REG1) | IMM(-2), UNMOVABLE_INS)); FAIL_IF(push_inst(compiler, SELECT_OP(DSLL, SLL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), UNMOVABLE_INS)); - if (op & SLJIT_SET_E) - return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG); #endif return SLJIT_SUCCESS; case SLJIT_ADD: + is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + if (flags & SRC2_IMM) { - if (op & SLJIT_SET_O) { + if (is_overflow) { if (src2 >= 0) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); else - FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); + FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); } - if (op & SLJIT_SET_E) + else if (op & SLJIT_SET_Z) FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); - if (op & (SLJIT_SET_C | SLJIT_SET_O)) { + + if (is_overflow || is_carry) { if (src2 >= 0) - FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG)); + FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); else { - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG)); - FAIL_IF(push_inst(compiler, OR | S(src1) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG)); + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); } } /* dst may be the same as src1 or src2. */ - if (CHECK_FLAGS(SLJIT_SET_E)) + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst))); } else { - if (op & SLJIT_SET_O) - FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - if (op & SLJIT_SET_E) + if (is_overflow) + FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else if (op & SLJIT_SET_Z) FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (op & (SLJIT_SET_C | SLJIT_SET_O)) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG)); + + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); /* dst may be the same as src1 or src2. */ - if (CHECK_FLAGS(SLJIT_SET_E)) + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst))); } /* a + b >= a | b (otherwise, the carry should be set to 1). */ - if (op & (SLJIT_SET_C | SLJIT_SET_O)) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG)); - if (!(op & SLJIT_SET_O)) + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); + if (!is_overflow) return SLJIT_SUCCESS; - FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(ULESS_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - return push_inst(compiler, SELECT_OP(DSRL32, SLL) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG); + FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); + FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); + return push_inst(compiler, SELECT_OP(DSRL32, SRL) | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG); case SLJIT_ADDC: + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + if (flags & SRC2_IMM) { - if (op & SLJIT_SET_C) { + if (is_carry) { if (src2 >= 0) - FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG)); + FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); else { - FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG)); - FAIL_IF(push_inst(compiler, OR | S(src1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, OR | S(src1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); } } FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst))); } else { - if (op & SLJIT_SET_C) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); + if (is_carry) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); /* dst may be the same as src1 or src2. */ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst))); } - if (op & SLJIT_SET_C) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); + if (is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst))); - if (!(op & SLJIT_SET_C)) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst))); + if (!is_carry) return SLJIT_SUCCESS; - /* Set ULESS_FLAG (dst == 0) && (ULESS_FLAG == 1). */ - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG)); + /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */ + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); /* Set carry flag. */ - return push_inst(compiler, OR | SA(ULESS_FLAG) | TA(OVERFLOW_FLAG) | DA(ULESS_FLAG), ULESS_FLAG); + return push_inst(compiler, OR | SA(OTHER_FLAG) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); case SLJIT_SUB: - if ((flags & SRC2_IMM) && ((op & (SLJIT_SET_U | SLJIT_SET_S)) || src2 == SIMM_MIN)) { + if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); src2 = TMP_REG2; flags &= ~SRC2_IMM; } + is_handled = 0; + if (flags & SRC2_IMM) { - if (op & SLJIT_SET_O) { - if (src2 >= 0) - FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - else - FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); + if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + is_handled = 1; } - if (op & SLJIT_SET_E) + else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLTI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); + is_handled = 1; + } + } + + if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) { + is_handled = 1; + + if (flags & SRC2_IMM) { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); + } + else if (GET_FLAG_TYPE(op) == SLJIT_GREATER || GET_FLAG_TYPE(op) == SLJIT_LESS_EQUAL) + { + FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); + } + else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) { + FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); + } + else if (GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER || GET_FLAG_TYPE(op) == SLJIT_SIG_LESS_EQUAL) + { + FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG)); + } + } + + if (is_handled) { + if (flags & SRC2_IMM) { + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); + if (!(flags & UNUSED_DEST)) + return push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)); + } + else { + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + if (!(flags & UNUSED_DEST)) + return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)); + } + return SLJIT_SUCCESS; + } + + is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW; + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + + if (flags & SRC2_IMM) { + if (is_overflow) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else + FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); + } + else if (op & SLJIT_SET_Z) FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); - if (op & (SLJIT_SET_C | SLJIT_SET_O)) - FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG)); + + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG)); /* dst may be the same as src1 or src2. */ - if (CHECK_FLAGS(SLJIT_SET_E)) + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst))); } else { - if (op & SLJIT_SET_O) - FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - if (op & SLJIT_SET_E) + if (is_overflow) + FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + else if (op & SLJIT_SET_Z) FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); - if (op & (SLJIT_SET_U | SLJIT_SET_C | SLJIT_SET_O)) - FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG)); - if (op & SLJIT_SET_U) - FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(UGREATER_FLAG), UGREATER_FLAG)); - if (op & SLJIT_SET_S) { - FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(LESS_FLAG), LESS_FLAG)); - FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(GREATER_FLAG), GREATER_FLAG)); - } + + if (is_overflow || is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG)); /* dst may be the same as src1 or src2. */ - if (CHECK_FLAGS(SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_C)) + if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK)) FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst))); } - if (!(op & SLJIT_SET_O)) + if (!is_overflow) return SLJIT_SUCCESS; - FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(ULESS_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); - return push_inst(compiler, SELECT_OP(DSRL32, SRL) | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG); + FAIL_IF(push_inst(compiler, SELECT_OP(DSLL32, SLL) | TA(OTHER_FLAG) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1))); + FAIL_IF(push_inst(compiler, XOR | S(TMP_REG1) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG)); + if (op & SLJIT_SET_Z) + FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG)); + return push_inst(compiler, SELECT_OP(DSRL32, SRL) | TA(OTHER_FLAG) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG); case SLJIT_SUBC: if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { @@ -369,28 +434,31 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl flags &= ~SRC2_IMM; } + is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY); + if (flags & SRC2_IMM) { - if (op & SLJIT_SET_C) - FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OVERFLOW_FLAG) | IMM(src2), OVERFLOW_FLAG)); + if (is_carry) + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); /* dst may be the same as src1 or src2. */ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst))); } else { - if (op & SLJIT_SET_C) - FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); + if (is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); /* dst may be the same as src1 or src2. */ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst))); } - if (op & SLJIT_SET_C) - FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(LESS_FLAG), LESS_FLAG)); + if (is_carry) + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | D(TMP_REG1), DR(TMP_REG1))); - FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst))); - return (op & SLJIT_SET_C) ? push_inst(compiler, OR | SA(OVERFLOW_FLAG) | TA(LESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG) : SLJIT_SUCCESS; + FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst))); + return (is_carry) ? push_inst(compiler, OR | SA(EQUAL_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG) : SLJIT_SUCCESS; case SLJIT_MUL: SLJIT_ASSERT(!(flags & SRC2_IMM)); - if (!(op & SLJIT_SET_O)) { + + if (GET_FLAG_TYPE(op) != SLJIT_MUL_OVERFLOW) { #if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) if (op & SLJIT_I32_OP) return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst)); @@ -402,10 +470,10 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl #endif } FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS)); - FAIL_IF(push_inst(compiler, MFHI | DA(ULESS_FLAG), ULESS_FLAG)); + FAIL_IF(push_inst(compiler, MFHI | DA(EQUAL_FLAG), EQUAL_FLAG)); FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst))); - FAIL_IF(push_inst(compiler, SELECT_OP(DSRA32, SRA) | T(dst) | DA(UGREATER_FLAG) | SH_IMM(31), UGREATER_FLAG)); - return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(ULESS_FLAG) | TA(UGREATER_FLAG) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG); + FAIL_IF(push_inst(compiler, SELECT_OP(DSRA32, SRA) | T(dst) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG)); + return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(EQUAL_FLAG) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG); case SLJIT_AND: EMIT_LOGICAL(ANDI, AND); @@ -432,7 +500,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl return SLJIT_SUCCESS; } - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_SUCCESS; } @@ -446,24 +514,155 @@ static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_ return push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value), DR(dst)); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) { - sljit_ins *inst = (sljit_ins*)addr; + sljit_ins *inst = (sljit_ins *)addr; - inst[0] = (inst[0] & 0xffff0000) | ((new_addr >> 48) & 0xffff); - inst[1] = (inst[1] & 0xffff0000) | ((new_addr >> 32) & 0xffff); - inst[3] = (inst[3] & 0xffff0000) | ((new_addr >> 16) & 0xffff); - inst[5] = (inst[5] & 0xffff0000) | (new_addr & 0xffff); + inst[0] = (inst[0] & 0xffff0000) | ((new_target >> 48) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | ((new_target >> 32) & 0xffff); + inst[3] = (inst[3] & 0xffff0000) | ((new_target >> 16) & 0xffff); + inst[5] = (inst[5] & 0xffff0000) | (new_target & 0xffff); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 6); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) { - sljit_ins *inst = (sljit_ins*)addr; + sljit_ins *inst = (sljit_ins *)addr; inst[0] = (inst[0] & 0xffff0000) | ((new_constant >> 48) & 0xffff); inst[1] = (inst[1] & 0xffff0000) | ((new_constant >> 32) & 0xffff); inst[3] = (inst[3] & 0xffff0000) | ((new_constant >> 16) & 0xffff); inst[5] = (inst[5] & 0xffff0000) | (new_constant & 0xffff); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 6); } + +static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_ins *ins_ptr) +{ + sljit_s32 arg_count = 0; + sljit_s32 word_arg_count = 0; + sljit_s32 float_arg_count = 0; + sljit_s32 types = 0; + sljit_ins prev_ins = NOP; + sljit_ins ins = NOP; + + SLJIT_ASSERT(reg_map[TMP_REG1] == 4 && freg_map[TMP_FREG1] == 12); + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK); + + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + case SLJIT_ARG_TYPE_F64: + arg_count++; + float_arg_count++; + break; + default: + arg_count++; + word_arg_count++; + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + while (types) { + switch (types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + if (arg_count != float_arg_count) + ins = MOV_S | FMT_S | FS(float_arg_count) | FD(arg_count); + else if (arg_count == 1) + ins = MOV_S | FMT_S | FS(SLJIT_FR0) | FD(TMP_FREG1); + arg_count--; + float_arg_count--; + break; + case SLJIT_ARG_TYPE_F64: + if (arg_count != float_arg_count) + ins = MOV_S | FMT_D | FS(float_arg_count) | FD(arg_count); + else if (arg_count == 1) + ins = MOV_S | FMT_D | FS(SLJIT_FR0) | FD(TMP_FREG1); + arg_count--; + float_arg_count--; + break; + default: + if (arg_count != word_arg_count) + ins = DADDU | S(word_arg_count) | TA(0) | D(arg_count); + else if (arg_count == 1) + ins = DADDU | S(SLJIT_R0) | TA(0) | DA(4); + arg_count--; + word_arg_count--; + break; + } + + if (ins != NOP) { + if (prev_ins != NOP) + FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS)); + prev_ins = ins; + ins = NOP; + } + + types >>= SLJIT_DEF_SHIFT; + } + + *ins_ptr = prev_ins; + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ + struct sljit_jump *jump; + sljit_ins ins; + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF(!jump); + set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); + type &= 0xff; + + PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins)); + + SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); + + PTR_FAIL_IF(emit_const(compiler, PIC_ADDR_REG, 0)); + + jump->flags |= IS_JAL | IS_CALL; + PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); + jump->addr = compiler->size; + PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS)); + + return jump; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + sljit_ins ins; + + CHECK_ERROR(); + CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + + SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); + + if (src & SLJIT_IMM) + FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw)); + else if (FAST_IS_REG(src)) + FAIL_IF(push_inst(compiler, DADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG))); + else if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw)); + } + + FAIL_IF(call_with_args(compiler, arg_types, &ins)); + + /* Register input. */ + FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); + return push_inst(compiler, ins, UNMOVABLE_INS); +} diff --git a/pcre2-10.22/src/sljit/sljitNativeMIPS_common.c b/pcre2-10.32/src/sljit/sljitNativeMIPS_common.c similarity index 75% rename from pcre2-10.22/src/sljit/sljitNativeMIPS_common.c rename to pcre2-10.32/src/sljit/sljitNativeMIPS_common.c index c2c251b1f..894e21304 100644 --- a/pcre2-10.22/src/sljit/sljitNativeMIPS_common.c +++ b/pcre2-10.32/src/sljit/sljitNativeMIPS_common.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -57,21 +57,30 @@ typedef sljit_u32 sljit_ins; #define RETURN_ADDR_REG 31 /* Flags are kept in volatile registers. */ -#define EQUAL_FLAG 12 -/* And carry flag as well. */ -#define ULESS_FLAG 13 -#define UGREATER_FLAG 14 -#define LESS_FLAG 15 -#define GREATER_FLAG 31 -#define OVERFLOW_FLAG 1 +#define EQUAL_FLAG 3 +#define OTHER_FLAG 1 -#define TMP_FREG1 (0) -#define TMP_FREG2 ((SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) << 1) +#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2) static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = { - 0, 2, 5, 6, 7, 8, 9, 10, 11, 24, 23, 22, 21, 20, 19, 18, 17, 16, 29, 3, 25, 4 + 0, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 24, 23, 22, 21, 20, 19, 18, 17, 16, 29, 4, 25, 31 }; +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + +static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { + 0, 0, 14, 2, 4, 6, 8, 12, 10 +}; + +#else + +static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { + 0, 0, 13, 14, 15, 16, 17, 12, 18 +}; + +#endif + /* --------------------------------------------------------------------- */ /* Instrucion forms */ /* --------------------------------------------------------------------- */ @@ -79,21 +88,23 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = { #define S(s) (reg_map[s] << 21) #define T(t) (reg_map[t] << 16) #define D(d) (reg_map[d] << 11) +#define FT(t) (freg_map[t] << 16) +#define FS(s) (freg_map[s] << 11) +#define FD(d) (freg_map[d] << 6) /* Absolute registers. */ #define SA(s) ((s) << 21) #define TA(t) ((t) << 16) #define DA(d) ((d) << 11) -#define FT(t) ((t) << 16) -#define FS(s) ((s) << 11) -#define FD(d) ((d) << 6) #define IMM(imm) ((imm) & 0xffff) #define SH_IMM(imm) ((imm) << 6) #define DR(dr) (reg_map[dr]) +#define FR(dr) (freg_map[dr]) #define HI(opcode) ((opcode) << 26) #define LO(opcode) (opcode) /* S = (16 << 21) D = (17 << 21) */ #define FMT_S (16 << 21) +#define FMT_D (17 << 21) #define ABS_S (HI(17) | FMT_S | LO(5)) #define ADD_S (HI(17) | FMT_S | LO(0)) @@ -158,6 +169,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = { #define OR (HI(0) | LO(37)) #define ORI (HI(13)) #define SD (HI(63)) +#define SDC1 (HI(61)) #define SLT (HI(0) | LO(42)) #define SLTI (HI(10)) #define SLTIU (HI(11)) @@ -171,6 +183,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = { #define SUB_S (HI(17) | FMT_S | LO(1)) #define SUBU (HI(0) | LO(35)) #define SW (HI(43)) +#define SWC1 (HI(57)) #define TRUNC_W_S (HI(17) | FMT_S | LO(13)) #define XOR (HI(0) | LO(38)) #define XORI (HI(14)) @@ -178,7 +191,13 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = { #if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) #define CLZ (HI(28) | LO(32)) #define DCLZ (HI(28) | LO(36)) +#define MOVF (HI(0) | (0 << 16) | LO(1)) +#define MOVN (HI(0) | LO(11)) +#define MOVT (HI(0) | (1 << 16) | LO(1)) +#define MOVZ (HI(0) | LO(10)) #define MUL (HI(28) | LO(2)) +#define PREF (HI(51)) +#define PREFX (HI(19) | LO(15)) #define SEB (HI(31) | (16 << 6) | LO(32)) #define SEH (HI(31) | (24 << 6) | LO(32)) #endif @@ -218,7 +237,7 @@ static SLJIT_INLINE sljit_ins invert_branch(sljit_s32 flags) return (flags & IS_BIT26_COND) ? (1 << 26) : (1 << 16); } -static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code) +static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code, sljit_sw executable_offset) { sljit_sw diff; sljit_uw target_addr; @@ -237,9 +256,10 @@ static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_i target_addr = jump->u.target; else { SLJIT_ASSERT(jump->flags & JUMP_LABEL); - target_addr = (sljit_uw)(code + jump->u.label->size); + target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset; } - inst = (sljit_ins*)jump->addr; + + inst = (sljit_ins *)jump->addr; if (jump->flags & IS_COND) inst--; @@ -250,7 +270,7 @@ static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_i /* B instructions. */ if (jump->flags & IS_MOVABLE) { - diff = ((sljit_sw)target_addr - (sljit_sw)(inst)) >> 2; + diff = ((sljit_sw)target_addr - (sljit_sw)inst - executable_offset) >> 2; if (diff <= SIMM_MAX && diff >= SIMM_MIN) { jump->flags |= PATCH_B; @@ -268,7 +288,7 @@ static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_i } } else { - diff = ((sljit_sw)target_addr - (sljit_sw)(inst + 1)) >> 2; + diff = ((sljit_sw)target_addr - (sljit_sw)(inst + 1) - executable_offset) >> 2; if (diff <= SIMM_MAX && diff >= SIMM_MIN) { jump->flags |= PATCH_B; @@ -364,6 +384,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil sljit_ins *buf_ptr; sljit_ins *buf_end; sljit_uw word_count; + sljit_sw executable_offset; sljit_uw addr; struct sljit_label *label; @@ -380,9 +401,12 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil code_ptr = code; word_count = 0; + executable_offset = SLJIT_EXEC_OFFSET(code); + label = compiler->labels; jump = compiler->jumps; const_ = compiler->consts; + do { buf_ptr = (sljit_ins*)buf->memory; buf_end = buf_ptr + (buf->used_size >> 2); @@ -393,8 +417,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil SLJIT_ASSERT(!const_ || const_->addr >= word_count); /* These structures are ordered by their address. */ if (label && label->size == word_count) { - /* Just recording the address. */ - label->addr = (sljit_uw)code_ptr; + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); label->size = code_ptr - code; label = label->next; } @@ -404,7 +427,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil #else jump->addr = (sljit_uw)(code_ptr - 7); #endif - code_ptr = detect_jump_type(jump, code_ptr, code); + code_ptr = detect_jump_type(jump, code_ptr, code, executable_offset); jump = jump->next; } if (const_ && const_->addr == word_count) { @@ -434,16 +457,16 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil while (jump) { do { addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; - buf_ptr = (sljit_ins*)jump->addr; + buf_ptr = (sljit_ins *)jump->addr; if (jump->flags & PATCH_B) { - addr = (sljit_sw)(addr - (jump->addr + sizeof(sljit_ins))) >> 2; + addr = (sljit_sw)(addr - ((sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset) + sizeof(sljit_ins))) >> 2; SLJIT_ASSERT((sljit_sw)addr <= SIMM_MAX && (sljit_sw)addr >= SIMM_MIN); buf_ptr[0] = (buf_ptr[0] & 0xffff0000) | (addr & 0xffff); break; } if (jump->flags & PATCH_J) { - SLJIT_ASSERT((addr & ~0xfffffff) == ((jump->addr + sizeof(sljit_ins)) & ~0xfffffff)); + SLJIT_ASSERT((addr & ~0xfffffff) == (((sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset) + sizeof(sljit_ins)) & ~0xfffffff)); buf_ptr[0] |= (addr >> 2) & 0x03ffffff; break; } @@ -476,7 +499,12 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_offset = executable_offset; compiler->executable_size = (code_ptr - code) * sizeof(sljit_ins); + + code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset); + code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); + #ifndef __GNUC__ SLJIT_CACHE_FLUSH(code, code_ptr); #else @@ -486,6 +514,32 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil return code; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) +{ + sljit_sw fir = 0; + + switch (feature_type) { + case SLJIT_HAS_FPU: +#ifdef SLJIT_IS_FPU_AVAILABLE + return SLJIT_IS_FPU_AVAILABLE; +#elif defined(__GNUC__) + asm ("cfc1 %0, $0" : "=r"(fir)); + return (fir >> 22) & 0x1; +#else +#error "FIR check is not implemented for this architecture" +#endif + +#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) + case SLJIT_HAS_CLZ: + case SLJIT_HAS_CMOV: + return 1; +#endif + + default: + return fir; + } +} + /* --------------------------------------------------------------------- */ /* Entry, exit */ /* --------------------------------------------------------------------- */ @@ -504,25 +558,20 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil #define MEM_MASK 0x1f -#define WRITE_BACK 0x00020 -#define ARG_TEST 0x00040 -#define ALT_KEEP_CACHE 0x00080 -#define CUMULATIVE_OP 0x00100 -#define LOGICAL_OP 0x00200 -#define IMM_OP 0x00400 -#define SRC2_IMM 0x00800 +#define ARG_TEST 0x00020 +#define ALT_KEEP_CACHE 0x00040 +#define CUMULATIVE_OP 0x00080 +#define LOGICAL_OP 0x00100 +#define IMM_OP 0x00200 +#define SRC2_IMM 0x00400 -#define UNUSED_DEST 0x01000 -#define REG_DEST 0x02000 -#define REG1_SOURCE 0x04000 -#define REG2_SOURCE 0x08000 -#define SLOW_SRC1 0x10000 -#define SLOW_SRC2 0x20000 -#define SLOW_DEST 0x40000 - -/* Only these flags are set. UNUSED_DEST is not set when no flags should be set. */ -#define CHECK_FLAGS(list) \ - (!(flags & UNUSED_DEST) || (op & GET_FLAGS(~(list)))) +#define UNUSED_DEST 0x00800 +#define REG_DEST 0x01000 +#define REG1_SOURCE 0x02000 +#define REG2_SOURCE 0x04000 +#define SLOW_SRC1 0x08000 +#define SLOW_SRC2 0x10000 +#define SLOW_DEST 0x20000 #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) #define STACK_STORE SW @@ -532,6 +581,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil #define STACK_LOAD LD #endif +static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw); + #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) #include "sljitNativeMIPS_32.c" #else @@ -539,15 +590,15 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil #endif SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { sljit_ins base; - sljit_s32 i, tmp, offs; + sljit_s32 args, i, tmp, offs; CHECK_ERROR(); - 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); + CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1) + SLJIT_LOCALS_OFFSET; #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) @@ -561,16 +612,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi /* Frequent case. */ FAIL_IF(push_inst(compiler, ADDIU_W | S(SLJIT_SP) | T(SLJIT_SP) | IMM(-local_size), DR(SLJIT_SP))); base = S(SLJIT_SP); + offs = local_size - (sljit_sw)sizeof(sljit_sw); } else { - FAIL_IF(load_immediate(compiler, DR(TMP_REG1), local_size)); + FAIL_IF(load_immediate(compiler, DR(OTHER_FLAG), local_size)); FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_SP) | TA(0) | D(TMP_REG2), DR(TMP_REG2))); - FAIL_IF(push_inst(compiler, SUBU_W | S(SLJIT_SP) | T(TMP_REG1) | D(SLJIT_SP), DR(SLJIT_SP))); + FAIL_IF(push_inst(compiler, SUBU_W | S(SLJIT_SP) | T(OTHER_FLAG) | D(SLJIT_SP), DR(SLJIT_SP))); base = S(TMP_REG2); local_size = 0; + offs = -(sljit_sw)sizeof(sljit_sw); } - offs = local_size - (sljit_sw)(sizeof(sljit_sw)); FAIL_IF(push_inst(compiler, STACK_STORE | base | TA(RETURN_ADDR_REG) | IMM(offs), MOVABLE_INS)); tmp = saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - saveds) : SLJIT_FIRST_SAVED_REG; @@ -584,6 +636,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi FAIL_IF(push_inst(compiler, STACK_STORE | base | T(i) | IMM(offs), MOVABLE_INS)); } + args = get_arg_count(arg_types); + if (args >= 1) FAIL_IF(push_inst(compiler, ADDU_W | SA(4) | TA(0) | D(SLJIT_S0), DR(SLJIT_S0))); if (args >= 2) @@ -595,12 +649,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 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); + CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1) + SLJIT_LOCALS_OFFSET; #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) @@ -702,7 +756,7 @@ static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flag { SLJIT_ASSERT(arg & SLJIT_MEM); - if ((!(flags & WRITE_BACK) || !(arg & REG_MASK)) && !(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) { + if (!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) { /* Works for both absoulte and relative addresses. */ if (SLJIT_UNLIKELY(flags & ARG_TEST)) return 1; @@ -752,7 +806,8 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) { tmp_ar = reg_ar; delay_slot = reg_ar; - } else { + } + else { tmp_ar = DR(TMP_REG1); delay_slot = MOVABLE_INS; } @@ -760,33 +815,21 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { argw &= 0x3; - if ((flags & WRITE_BACK) && reg_ar == DR(base)) { - SLJIT_ASSERT(!(flags & LOAD_DATA) && DR(TMP_REG1) != reg_ar); - FAIL_IF(push_inst(compiler, ADDU_W | SA(reg_ar) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); - reg_ar = DR(TMP_REG1); - } /* Using the cache. */ if (argw == compiler->cache_argw) { - if (!(flags & WRITE_BACK)) { - if (arg == compiler->cache_arg) + if (arg == compiler->cache_arg) + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot); + + if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) { + if (arg == next_arg && argw == (next_argw & 0x3)) { + compiler->cache_arg = arg; + compiler->cache_argw = argw; + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(TMP_REG3), DR(TMP_REG3))); return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot); - if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) { - if (arg == next_arg && argw == (next_argw & 0x3)) { - compiler->cache_arg = arg; - compiler->cache_argw = argw; - FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(TMP_REG3), DR(TMP_REG3))); - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot); - } - FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | DA(tmp_ar), tmp_ar)); - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot); - } - } - else { - if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) { - FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(base), DR(base))); - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar), delay_slot); } + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | DA(tmp_ar), tmp_ar)); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot); } } @@ -796,55 +839,15 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | D(TMP_REG3) | SH_IMM(argw), DR(TMP_REG3))); } - if (!(flags & WRITE_BACK)) { - if (arg == next_arg && argw == (next_argw & 0x3)) { - compiler->cache_arg = arg; - compiler->cache_argw = argw; - FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | D(TMP_REG3), DR(TMP_REG3))); - tmp_ar = DR(TMP_REG3); - } - else - FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | DA(tmp_ar), tmp_ar)); - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot); + if (arg == next_arg && argw == (next_argw & 0x3)) { + compiler->cache_arg = arg; + compiler->cache_argw = argw; + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | D(TMP_REG3), DR(TMP_REG3))); + tmp_ar = DR(TMP_REG3); } - FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | D(base), DR(base))); - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar), delay_slot); - } - - if (SLJIT_UNLIKELY(flags & WRITE_BACK) && base) { - /* Update only applies if a base register exists. */ - if (reg_ar == DR(base)) { - SLJIT_ASSERT(!(flags & LOAD_DATA) && DR(TMP_REG1) != reg_ar); - if (argw <= SIMM_MAX && argw >= SIMM_MIN) { - FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar) | IMM(argw), MOVABLE_INS)); - if (argw) - return push_inst(compiler, ADDIU_W | S(base) | T(base) | IMM(argw), DR(base)); - return SLJIT_SUCCESS; - } - FAIL_IF(push_inst(compiler, ADDU_W | SA(reg_ar) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); - reg_ar = DR(TMP_REG1); - } - - if (argw <= SIMM_MAX && argw >= SIMM_MIN) { - if (argw) - FAIL_IF(push_inst(compiler, ADDIU_W | S(base) | T(base) | IMM(argw), DR(base))); - } - else { - if (compiler->cache_arg == SLJIT_MEM && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) { - if (argw != compiler->cache_argw) { - FAIL_IF(push_inst(compiler, ADDIU_W | S(TMP_REG3) | T(TMP_REG3) | IMM(argw - compiler->cache_argw), DR(TMP_REG3))); - compiler->cache_argw = argw; - } - FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(base), DR(base))); - } - else { - compiler->cache_arg = SLJIT_MEM; - compiler->cache_argw = argw; - FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw)); - FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(base), DR(base))); - } - } - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar), delay_slot); + else + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | DA(tmp_ar), tmp_ar)); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot); } if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) { @@ -880,11 +883,39 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw) { + sljit_s32 tmp_ar, base, delay_slot; + if (getput_arg_fast(compiler, flags, reg_ar, arg, argw)) return compiler->error; - compiler->cache_arg = 0; - compiler->cache_argw = 0; - return getput_arg(compiler, flags, reg_ar, arg, argw, 0, 0); + + if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) { + tmp_ar = reg_ar; + delay_slot = reg_ar; + } + else { + tmp_ar = DR(TMP_REG1); + delay_slot = MOVABLE_INS; + } + base = arg & REG_MASK; + + if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { + argw &= 0x3; + + if (SLJIT_UNLIKELY(argw)) { + FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | DA(tmp_ar) | SH_IMM(argw), tmp_ar)); + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | TA(tmp_ar) | DA(tmp_ar), tmp_ar)); + } + else + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(OFFS_REG(arg)) | DA(tmp_ar), tmp_ar)); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot); + } + + FAIL_IF(load_immediate(compiler, tmp_ar, argw)); + + if (base != 0) + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | TA(tmp_ar) | DA(tmp_ar), tmp_ar)); + + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot); } static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w) @@ -914,15 +945,13 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 } if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) { - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32 && !(src2 & SLJIT_MEM)) - return SLJIT_SUCCESS; - if (GET_FLAGS(op)) - flags |= UNUSED_DEST; + SLJIT_ASSERT(HAS_FLAGS(op)); + flags |= UNUSED_DEST; } else if (FAST_IS_REG(dst)) { dst_r = dst; flags |= REG_DEST; - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) + if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) sugg_src2_r = dst_r; } else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, DR(TMP_REG1), dst, dstw)) @@ -976,7 +1005,7 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 if (FAST_IS_REG(src2)) { src2_r = src2; flags |= REG2_SOURCE; - if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) + if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOV_P) dst_r = src2_r; } else if (src2 & SLJIT_IMM) { @@ -987,7 +1016,7 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 } else { src2_r = 0; - if ((op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) && (dst & SLJIT_MEM)) + if ((op >= SLJIT_MOV && op <= SLJIT_MOV_P) && (dst & SLJIT_MEM)) dst_r = 0; } } @@ -1079,6 +1108,29 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile return SLJIT_SUCCESS; } +#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) +static sljit_s32 emit_prefetch(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ + if (!(src & OFFS_REG_MASK)) { + if (srcw <= SIMM_MAX && srcw >= SIMM_MIN) + return push_inst(compiler, PREF | S(src & REG_MASK) | IMM(srcw), MOVABLE_INS); + + FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw)); + return push_inst(compiler, PREFX | S(src & REG_MASK) | T(TMP_REG1), MOVABLE_INS); + } + + srcw &= 0x3; + + if (SLJIT_UNLIKELY(srcw != 0)) { + FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(src)) | D(TMP_REG1) | SH_IMM(srcw), DR(TMP_REG1))); + return push_inst(compiler, PREFX | S(src & REG_MASK) | T(TMP_REG1), MOVABLE_INS); + } + + return push_inst(compiler, PREFX | S(src & REG_MASK) | T(OFFS_REG(src)), MOVABLE_INS); +} +#endif + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) @@ -1094,12 +1146,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(dst, dstw); ADJUST_LOCAL_OFFSET(src, srcw); -#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) - if ((op & SLJIT_I32_OP) && GET_OPCODE(op) >= SLJIT_NOT) { - flags |= INT_DATA | SIGNED_DATA; - if (src & SLJIT_IMM) - srcw = (sljit_s32)srcw; + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) { +#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) + if (op <= SLJIT_MOV_P && (src & SLJIT_MEM)) + return emit_prefetch(compiler, src, srcw); +#endif + return SLJIT_SUCCESS; } + +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) + if ((op & SLJIT_I32_OP) && GET_OPCODE(op) >= SLJIT_NOT) + flags |= INT_DATA | SIGNED_DATA; #endif switch (GET_OPCODE(op)) { @@ -1133,36 +1190,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile case SLJIT_MOV_S16: return emit_op(compiler, SLJIT_MOV_S16, HALF_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw); - case SLJIT_MOVU: - case SLJIT_MOVU_P: - return emit_op(compiler, SLJIT_MOV, WORD_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); - - case SLJIT_MOVU_U32: -#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) - return emit_op(compiler, SLJIT_MOV_U32, INT_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); -#else - return emit_op(compiler, SLJIT_MOV_U32, INT_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u32)srcw : srcw); -#endif - - case SLJIT_MOVU_S32: -#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) - return emit_op(compiler, SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); -#else - return emit_op(compiler, SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s32)srcw : srcw); -#endif - - case SLJIT_MOVU_U8: - return emit_op(compiler, SLJIT_MOV_U8, BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw); - - case SLJIT_MOVU_S8: - return emit_op(compiler, SLJIT_MOV_S8, BYTE_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw); - - case SLJIT_MOVU_U16: - return emit_op(compiler, SLJIT_MOV_U16, HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw); - - case SLJIT_MOVU_S16: - return emit_op(compiler, SLJIT_MOV_S16, HALF_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw); - case SLJIT_NOT: return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw); @@ -1173,6 +1200,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw); } + SLJIT_UNREACHABLE(); return SLJIT_SUCCESS; #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) @@ -1197,6 +1225,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) + return SLJIT_SUCCESS; + #if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) if (op & SLJIT_I32_OP) { flags |= INT_DATA | SIGNED_DATA; @@ -1241,6 +1272,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w); } + SLJIT_UNREACHABLE(); return SLJIT_SUCCESS; #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) @@ -1257,7 +1289,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) { CHECK_REG_INDEX(check_sljit_get_float_register_index(reg)); - return reg << 1; + return FR(reg); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, @@ -1273,19 +1305,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c /* Floating point operators */ /* --------------------------------------------------------------------- */ -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void) -{ -#ifdef SLJIT_IS_FPU_AVAILABLE - return SLJIT_IS_FPU_AVAILABLE; -#elif defined(__GNUC__) - sljit_sw fir; - asm ("cfc1 %0, $0" : "=r"(fir)); - return (fir >> 22) & 0x1; -#else -#error "FIR check is not implemented for this architecture" -#endif -} - #define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_F32_OP) >> 7)) #define FMT(op) (((op & SLJIT_F32_OP) ^ SLJIT_F32_OP) << (21 - 8)) @@ -1300,22 +1319,17 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp #endif if (src & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw)); + FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src, srcw, dst, dstw)); src = TMP_FREG1; } - else - src <<= 1; FAIL_IF(push_inst(compiler, (TRUNC_W_S ^ (flags >> 19)) | FMT(op) | FS(src) | FD(TMP_FREG1), MOVABLE_INS)); - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; - if (FAST_IS_REG(dst)) return push_inst(compiler, MFC1 | flags | T(dst) | FS(TMP_FREG1), MOVABLE_INS); /* Store the integer value from a VFP register. */ - return emit_op_mem2(compiler, flags ? DOUBLE_DATA : SINGLE_DATA, TMP_FREG1, dst, dstw, 0, 0); + return emit_op_mem2(compiler, flags ? DOUBLE_DATA : SINGLE_DATA, FR(TMP_FREG1), dst, dstw, 0, 0); #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) # undef is_long @@ -1332,13 +1346,13 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp sljit_s32 flags = (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW) << 21; #endif - sljit_s32 dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG1; + sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; if (FAST_IS_REG(src)) FAIL_IF(push_inst(compiler, MTC1 | flags | T(src) | FS(TMP_FREG1), MOVABLE_INS)); else if (src & SLJIT_MEM) { /* Load the integer value into a VFP register. */ - FAIL_IF(emit_op_mem2(compiler, ((flags) ? DOUBLE_DATA : SINGLE_DATA) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw)); + FAIL_IF(emit_op_mem2(compiler, ((flags) ? DOUBLE_DATA : SINGLE_DATA) | LOAD_DATA, FR(TMP_FREG1), src, srcw, dst, dstw)); } else { #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) @@ -1352,7 +1366,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp FAIL_IF(push_inst(compiler, CVT_S_S | flags | (4 << 21) | (((op & SLJIT_F32_OP) ^ SLJIT_F32_OP) >> 8) | FS(TMP_FREG1) | FD(dst_r), MOVABLE_INS)); if (dst & SLJIT_MEM) - return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0); + return emit_op_mem2(compiler, FLOAT_DATA(op), FR(TMP_FREG1), dst, dstw, 0, 0); return SLJIT_SUCCESS; #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) @@ -1364,39 +1378,38 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { + sljit_ins inst; + if (src1 & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); + FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, src2, src2w)); src1 = TMP_FREG1; } - else - src1 <<= 1; if (src2 & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0)); + FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, 0, 0)); src2 = TMP_FREG2; } - else - src2 <<= 1; - /* src2 and src1 are swapped. */ - if (op & SLJIT_SET_E) { - FAIL_IF(push_inst(compiler, C_UEQ_S | FMT(op) | FT(src2) | FS(src1), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, CFC1 | TA(EQUAL_FLAG) | DA(FCSR_REG), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, SRL | TA(EQUAL_FLAG) | DA(EQUAL_FLAG) | SH_IMM(23), EQUAL_FLAG)); - FAIL_IF(push_inst(compiler, ANDI | SA(EQUAL_FLAG) | TA(EQUAL_FLAG) | IMM(1), EQUAL_FLAG)); + switch (GET_FLAG_TYPE(op)) { + case SLJIT_EQUAL_F64: + case SLJIT_NOT_EQUAL_F64: + inst = C_UEQ_S; + break; + case SLJIT_LESS_F64: + case SLJIT_GREATER_EQUAL_F64: + inst = C_ULT_S; + break; + case SLJIT_GREATER_F64: + case SLJIT_LESS_EQUAL_F64: + inst = C_ULE_S; + break; + default: + SLJIT_ASSERT(GET_FLAG_TYPE(op) == SLJIT_UNORDERED_F64 || GET_FLAG_TYPE(op) == SLJIT_ORDERED_F64); + inst = C_UN_S; + break; } - if (op & SLJIT_SET_S) { - /* Mixing the instructions for the two checks. */ - FAIL_IF(push_inst(compiler, C_ULT_S | FMT(op) | FT(src2) | FS(src1), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, CFC1 | TA(ULESS_FLAG) | DA(FCSR_REG), ULESS_FLAG)); - FAIL_IF(push_inst(compiler, C_ULT_S | FMT(op) | FT(src1) | FS(src2), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, SRL | TA(ULESS_FLAG) | DA(ULESS_FLAG) | SH_IMM(23), ULESS_FLAG)); - FAIL_IF(push_inst(compiler, ANDI | SA(ULESS_FLAG) | TA(ULESS_FLAG) | IMM(1), ULESS_FLAG)); - FAIL_IF(push_inst(compiler, CFC1 | TA(UGREATER_FLAG) | DA(FCSR_REG), UGREATER_FLAG)); - FAIL_IF(push_inst(compiler, SRL | TA(UGREATER_FLAG) | DA(UGREATER_FLAG) | SH_IMM(23), UGREATER_FLAG)); - FAIL_IF(push_inst(compiler, ANDI | SA(UGREATER_FLAG) | TA(UGREATER_FLAG) | IMM(1), UGREATER_FLAG)); - } - return push_inst(compiler, C_UN_S | FMT(op) | FT(src2) | FS(src1), FCSR_FCC); + + return push_inst(compiler, inst | FMT(op) | FT(src2) | FS(src1), UNMOVABLE_INS); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op, @@ -1415,14 +1428,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) op ^= SLJIT_F32_OP; - dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG1; + dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; if (src & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw)); + FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(dst_r), src, srcw, dst, dstw)); src = dst_r; } - else - src <<= 1; switch (GET_OPCODE(op)) { case SLJIT_MOV_F64: @@ -1446,7 +1457,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil } if (dst & SLJIT_MEM) - return emit_op_mem2(compiler, FLOAT_DATA(op), dst_r, dst, dstw, 0, 0); + return emit_op_mem2(compiler, FLOAT_DATA(op), FR(dst_r), dst, dstw, 0, 0); return SLJIT_SUCCESS; } @@ -1466,42 +1477,38 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil compiler->cache_arg = 0; compiler->cache_argw = 0; - dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG2; + dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2; if (src1 & SLJIT_MEM) { - if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) { + if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w)) { FAIL_IF(compiler->error); src1 = TMP_FREG1; } else flags |= SLOW_SRC1; } - else - src1 <<= 1; if (src2 & SLJIT_MEM) { - if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) { + if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w)) { FAIL_IF(compiler->error); src2 = TMP_FREG2; } else flags |= SLOW_SRC2; } - else - src2 <<= 1; if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, src1, src1w)); - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw)); + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, src1, src1w)); + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, dst, dstw)); } else { - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw)); + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, src2, src2w)); + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, dst, dstw)); } } else if (flags & SLOW_SRC1) - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw)); + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, dst, dstw)); else if (flags & SLOW_SRC2) - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw)); + FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, dst, dstw)); if (flags & SLOW_SRC1) src1 = TMP_FREG1; @@ -1527,7 +1534,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil } if (dst_r == TMP_FREG2) - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, 0, 0)); + FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), FR(TMP_FREG2), dst, dstw, 0, 0)); return SLJIT_SUCCESS; } @@ -1542,10 +1549,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); ADJUST_LOCAL_OFFSET(dst, dstw); - /* For UNUSED dst. Uncommon, but possible. */ - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; - if (FAST_IS_REG(dst)) return push_inst(compiler, ADDU_W | SA(RETURN_ADDR_REG) | TA(0) | D(dst), DR(dst)); @@ -1561,10 +1564,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler if (FAST_IS_REG(src)) FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | DA(RETURN_ADDR_REG), RETURN_ADDR_REG)); - else if (src & SLJIT_MEM) + else FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG, src, srcw)); - else if (src & SLJIT_IMM) - FAIL_IF(load_immediate(compiler, RETURN_ADDR_REG, srcw)); FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS)); return push_inst(compiler, NOP, UNMOVABLE_INS); @@ -1634,55 +1635,39 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile switch (type) { case SLJIT_EQUAL: - case SLJIT_NOT_EQUAL_F64: BR_NZ(EQUAL_FLAG); break; case SLJIT_NOT_EQUAL: - case SLJIT_EQUAL_F64: BR_Z(EQUAL_FLAG); break; case SLJIT_LESS: - case SLJIT_LESS_F64: - BR_Z(ULESS_FLAG); - break; - case SLJIT_GREATER_EQUAL: - case SLJIT_GREATER_EQUAL_F64: - BR_NZ(ULESS_FLAG); - break; case SLJIT_GREATER: - case SLJIT_GREATER_F64: - BR_Z(UGREATER_FLAG); - break; - case SLJIT_LESS_EQUAL: - case SLJIT_LESS_EQUAL_F64: - BR_NZ(UGREATER_FLAG); - break; case SLJIT_SIG_LESS: - BR_Z(LESS_FLAG); - break; - case SLJIT_SIG_GREATER_EQUAL: - BR_NZ(LESS_FLAG); - break; case SLJIT_SIG_GREATER: - BR_Z(GREATER_FLAG); - break; - case SLJIT_SIG_LESS_EQUAL: - BR_NZ(GREATER_FLAG); - break; case SLJIT_OVERFLOW: case SLJIT_MUL_OVERFLOW: - BR_Z(OVERFLOW_FLAG); + BR_Z(OTHER_FLAG); break; + case SLJIT_GREATER_EQUAL: + case SLJIT_LESS_EQUAL: + case SLJIT_SIG_GREATER_EQUAL: + case SLJIT_SIG_LESS_EQUAL: case SLJIT_NOT_OVERFLOW: case SLJIT_MUL_NOT_OVERFLOW: - BR_NZ(OVERFLOW_FLAG); - break; - case SLJIT_UNORDERED_F64: - BR_F(); + BR_NZ(OTHER_FLAG); break; + case SLJIT_NOT_EQUAL_F64: + case SLJIT_GREATER_EQUAL_F64: + case SLJIT_GREATER_F64: case SLJIT_ORDERED_F64: BR_T(); break; + case SLJIT_EQUAL_F64: + case SLJIT_LESS_F64: + case SLJIT_LESS_EQUAL_F64: + case SLJIT_UNORDERED_F64: + BR_F(); + break; default: /* Not conditional branch. */ inst = 0; @@ -1697,19 +1682,16 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile PTR_FAIL_IF(push_inst(compiler, inst, UNMOVABLE_INS)); PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0)); - if (type <= SLJIT_JUMP) { + + if (type <= SLJIT_JUMP) PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS)); - jump->addr = compiler->size; - PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); - } else { - SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); - /* Cannot be optimized out if type is >= CALL0. */ - jump->flags |= IS_JAL | (type >= SLJIT_CALL0 ? IS_CALL : 0); + else { + jump->flags |= IS_JAL; PTR_FAIL_IF(push_inst(compiler, JALR | S(TMP_REG2) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); - jump->addr = compiler->size; - /* A NOP if type < CALL1. */ - PTR_FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_R0) | TA(0) | DA(4), UNMOVABLE_INS)); } + + jump->addr = compiler->size; + PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); return jump; } @@ -1854,86 +1836,6 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler #undef RESOLVE_IMM1 #undef RESOLVE_IMM2 -SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type, - sljit_s32 src1, sljit_sw src1w, - sljit_s32 src2, sljit_sw src2w) -{ - struct sljit_jump *jump; - sljit_ins inst; - sljit_s32 if_true; - - CHECK_ERROR_PTR(); - CHECK_PTR(check_sljit_emit_fcmp(compiler, type, src1, src1w, src2, src2w)); - - compiler->cache_arg = 0; - compiler->cache_argw = 0; - - if (src1 & SLJIT_MEM) { - PTR_FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(type) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); - src1 = TMP_FREG1; - } - else - src1 <<= 1; - - if (src2 & SLJIT_MEM) { - PTR_FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(type) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0)); - src2 = TMP_FREG2; - } - else - src2 <<= 1; - - jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); - PTR_FAIL_IF(!jump); - set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); - jump->flags |= IS_BIT16_COND; - - switch (type & 0xff) { - case SLJIT_EQUAL_F64: - inst = C_UEQ_S; - if_true = 1; - break; - case SLJIT_NOT_EQUAL_F64: - inst = C_UEQ_S; - if_true = 0; - break; - case SLJIT_LESS_F64: - inst = C_ULT_S; - if_true = 1; - break; - case SLJIT_GREATER_EQUAL_F64: - inst = C_ULT_S; - if_true = 0; - break; - case SLJIT_GREATER_F64: - inst = C_ULE_S; - if_true = 0; - break; - case SLJIT_LESS_EQUAL_F64: - inst = C_ULE_S; - if_true = 1; - break; - case SLJIT_UNORDERED_F64: - inst = C_UN_S; - if_true = 1; - break; - default: /* Make compilers happy. */ - SLJIT_ASSERT_STOP(); - case SLJIT_ORDERED_F64: - inst = C_UN_S; - if_true = 0; - break; - } - - PTR_FAIL_IF(push_inst(compiler, inst | FMT(type) | FT(src2) | FS(src1), UNMOVABLE_INS)); - /* Intentionally the other opcode. */ - PTR_FAIL_IF(push_inst(compiler, (if_true ? BC1F : BC1T) | JUMP_LENGTH, UNMOVABLE_INS)); - PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0)); - PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS)); - jump->addr = compiler->size; - PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); - return jump; -} - #undef JUMP_LENGTH #undef BR_Z #undef BR_NZ @@ -1945,41 +1847,12 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compile SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) { - sljit_s32 src_r = TMP_REG2; struct sljit_jump *jump = NULL; CHECK_ERROR(); CHECK(check_sljit_emit_ijump(compiler, type, src, srcw)); ADJUST_LOCAL_OFFSET(src, srcw); - if (FAST_IS_REG(src)) { - if (DR(src) != 4) - src_r = src; - else - FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | D(TMP_REG2), DR(TMP_REG2))); - } - - if (type >= SLJIT_CALL0) { - SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); - if (src & (SLJIT_IMM | SLJIT_MEM)) { - if (src & SLJIT_IMM) - FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw)); - else { - SLJIT_ASSERT(src_r == TMP_REG2 && (src & SLJIT_MEM)); - FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); - } - FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); - /* We need an extra instruction in any case. */ - return push_inst(compiler, ADDU_W | S(SLJIT_R0) | TA(0) | DA(4), UNMOVABLE_INS); - } - - /* Register input. */ - if (type >= SLJIT_CALL1) - FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_R0) | TA(0) | DA(4), 4)); - FAIL_IF(push_inst(compiler, JALR | S(src_r) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); - return push_inst(compiler, ADDU_W | S(src_r) | TA(0) | D(PIC_ADDR_REG), UNMOVABLE_INS); - } - if (src & SLJIT_IMM) { jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); FAIL_IF(!jump); @@ -1990,11 +1863,14 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi jump->flags |= IS_MOVABLE; FAIL_IF(emit_const(compiler, TMP_REG2, 0)); + src = TMP_REG2; + } + else if (src & SLJIT_MEM) { + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(TMP_REG2), src, srcw)); + src = TMP_REG2; } - else if (src & SLJIT_MEM) - FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); - FAIL_IF(push_inst(compiler, JR | S(src_r), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, JR | S(src), UNMOVABLE_INS)); if (jump) jump->addr = compiler->size; FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); @@ -2003,115 +1879,160 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw, sljit_s32 type) { - sljit_s32 sugg_dst_ar, dst_ar; - sljit_s32 flags = GET_ALL_FLAGS(op); + sljit_s32 src_ar, dst_ar; + sljit_s32 saved_op = op; #if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) -# define mem_type WORD_DATA + sljit_s32 mem_type = WORD_DATA; #else sljit_s32 mem_type = (op & SLJIT_I32_OP) ? (INT_DATA | SIGNED_DATA) : WORD_DATA; #endif CHECK_ERROR(); - CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type)); + CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); ADJUST_LOCAL_OFFSET(dst, dstw); - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; - op = GET_OPCODE(op); #if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) - if (op == SLJIT_MOV_S32 || op == SLJIT_MOV_U32) + if (op == SLJIT_MOV_S32) mem_type = INT_DATA | SIGNED_DATA; #endif - sugg_dst_ar = DR((op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2); + dst_ar = DR((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, mem_type | LOAD_DATA, DR(TMP_REG1), src, srcw, dst, dstw)); - src = TMP_REG1; - srcw = 0; - } + + if (op >= SLJIT_ADD && (dst & SLJIT_MEM)) + FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, DR(TMP_REG1), dst, dstw, dst, dstw)); switch (type & 0xff) { case SLJIT_EQUAL: case SLJIT_NOT_EQUAL: - FAIL_IF(push_inst(compiler, SLTIU | SA(EQUAL_FLAG) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar)); - dst_ar = sugg_dst_ar; - break; - case SLJIT_LESS: - case SLJIT_GREATER_EQUAL: - case SLJIT_LESS_F64: - case SLJIT_GREATER_EQUAL_F64: - dst_ar = ULESS_FLAG; - break; - case SLJIT_GREATER: - case SLJIT_LESS_EQUAL: - case SLJIT_GREATER_F64: - case SLJIT_LESS_EQUAL_F64: - dst_ar = UGREATER_FLAG; - break; - case SLJIT_SIG_LESS: - case SLJIT_SIG_GREATER_EQUAL: - dst_ar = LESS_FLAG; - break; - case SLJIT_SIG_GREATER: - case SLJIT_SIG_LESS_EQUAL: - dst_ar = GREATER_FLAG; - break; - case SLJIT_OVERFLOW: - case SLJIT_NOT_OVERFLOW: - dst_ar = OVERFLOW_FLAG; + FAIL_IF(push_inst(compiler, SLTIU | SA(EQUAL_FLAG) | TA(dst_ar) | IMM(1), dst_ar)); + src_ar = dst_ar; break; case SLJIT_MUL_OVERFLOW: case SLJIT_MUL_NOT_OVERFLOW: - FAIL_IF(push_inst(compiler, SLTIU | SA(OVERFLOW_FLAG) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar)); - dst_ar = sugg_dst_ar; + FAIL_IF(push_inst(compiler, SLTIU | SA(OTHER_FLAG) | TA(dst_ar) | IMM(1), dst_ar)); + src_ar = dst_ar; type ^= 0x1; /* Flip type bit for the XORI below. */ break; + case SLJIT_GREATER_F64: + case SLJIT_LESS_EQUAL_F64: + type ^= 0x1; /* Flip type bit for the XORI below. */ case SLJIT_EQUAL_F64: case SLJIT_NOT_EQUAL_F64: - dst_ar = EQUAL_FLAG; - break; - + case SLJIT_LESS_F64: + case SLJIT_GREATER_EQUAL_F64: case SLJIT_UNORDERED_F64: case SLJIT_ORDERED_F64: - FAIL_IF(push_inst(compiler, CFC1 | TA(sugg_dst_ar) | DA(FCSR_REG), sugg_dst_ar)); - FAIL_IF(push_inst(compiler, SRL | TA(sugg_dst_ar) | DA(sugg_dst_ar) | SH_IMM(23), sugg_dst_ar)); - FAIL_IF(push_inst(compiler, ANDI | SA(sugg_dst_ar) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar)); - dst_ar = sugg_dst_ar; + FAIL_IF(push_inst(compiler, CFC1 | TA(dst_ar) | DA(FCSR_REG), dst_ar)); + FAIL_IF(push_inst(compiler, SRL | TA(dst_ar) | DA(dst_ar) | SH_IMM(23), dst_ar)); + FAIL_IF(push_inst(compiler, ANDI | SA(dst_ar) | TA(dst_ar) | IMM(1), dst_ar)); + src_ar = dst_ar; break; default: - SLJIT_ASSERT_STOP(); - dst_ar = sugg_dst_ar; + src_ar = OTHER_FLAG; break; } if (type & 0x1) { - FAIL_IF(push_inst(compiler, XORI | SA(dst_ar) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar)); - dst_ar = sugg_dst_ar; + FAIL_IF(push_inst(compiler, XORI | SA(src_ar) | TA(dst_ar) | IMM(1), dst_ar)); + src_ar = dst_ar; } - if (op >= SLJIT_ADD) { - if (DR(TMP_REG2) != dst_ar) - FAIL_IF(push_inst(compiler, ADDU_W | SA(dst_ar) | TA(0) | D(TMP_REG2), DR(TMP_REG2))); - 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 (op < SLJIT_ADD) { + if (dst & SLJIT_MEM) + return emit_op_mem(compiler, mem_type, src_ar, dst, dstw); + + if (src_ar != dst_ar) + return push_inst(compiler, ADDU_W | SA(src_ar) | TA(0) | DA(dst_ar), dst_ar); + return SLJIT_SUCCESS; } + /* OTHER_FLAG cannot be specified as src2 argument at the moment. */ + if (DR(TMP_REG2) != src_ar) + FAIL_IF(push_inst(compiler, ADDU_W | SA(src_ar) | TA(0) | D(TMP_REG2), DR(TMP_REG2))); + + mem_type |= CUMULATIVE_OP | LOGICAL_OP | IMM_OP | ALT_KEEP_CACHE; + if (dst & SLJIT_MEM) - return emit_op_mem(compiler, mem_type, dst_ar, dst, dstw); + return emit_op(compiler, saved_op, mem_type, dst, dstw, TMP_REG1, 0, TMP_REG2, 0); + return emit_op(compiler, saved_op, mem_type, dst, dstw, dst, dstw, TMP_REG2, 0); +} - if (sugg_dst_ar != dst_ar) - return push_inst(compiler, ADDU_W | SA(dst_ar) | TA(0) | DA(sugg_dst_ar), sugg_dst_ar); - return SLJIT_SUCCESS; +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ +#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) + sljit_ins ins; +#endif -#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) -# undef mem_type + CHECK_ERROR(); + CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); + +#if (defined SLJIT_MIPS_R1 && SLJIT_MIPS_R1) + + if (SLJIT_UNLIKELY(src & SLJIT_IMM)) { +#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) + if (dst_reg & SLJIT_I32_OP) + srcw = (sljit_s32)srcw; +#endif + FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw)); + src = TMP_REG1; + srcw = 0; + } + + dst_reg &= ~SLJIT_I32_OP; + + switch (type & 0xff) { + case SLJIT_EQUAL: + ins = MOVZ | TA(EQUAL_FLAG); + break; + case SLJIT_NOT_EQUAL: + ins = MOVN | TA(EQUAL_FLAG); + break; + case SLJIT_LESS: + case SLJIT_GREATER: + case SLJIT_SIG_LESS: + case SLJIT_SIG_GREATER: + case SLJIT_OVERFLOW: + case SLJIT_MUL_OVERFLOW: + ins = MOVN | TA(OTHER_FLAG); + break; + case SLJIT_GREATER_EQUAL: + case SLJIT_LESS_EQUAL: + case SLJIT_SIG_GREATER_EQUAL: + case SLJIT_SIG_LESS_EQUAL: + case SLJIT_NOT_OVERFLOW: + case SLJIT_MUL_NOT_OVERFLOW: + ins = MOVZ | TA(OTHER_FLAG); + break; + case SLJIT_EQUAL_F64: + case SLJIT_LESS_F64: + case SLJIT_LESS_EQUAL_F64: + case SLJIT_UNORDERED_F64: + ins = MOVT; + break; + case SLJIT_NOT_EQUAL_F64: + case SLJIT_GREATER_EQUAL_F64: + case SLJIT_GREATER_F64: + case SLJIT_ORDERED_F64: + ins = MOVF; + break; + default: + ins = MOVZ | TA(OTHER_FLAG); + SLJIT_UNREACHABLE(); + break; + } + + return push_inst(compiler, ins | S(src) | D(dst_reg), DR(dst_reg)); + +#else + return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw); #endif } @@ -2128,7 +2049,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi PTR_FAIL_IF(!const_); set_const(const_, compiler); - reg = SLOW_IS_REG(dst) ? dst : TMP_REG2; + reg = FAST_IS_REG(dst) ? dst : TMP_REG2; PTR_FAIL_IF(emit_const(compiler, reg, init_value)); diff --git a/pcre2-10.22/src/sljit/sljitNativePPC_32.c b/pcre2-10.32/src/sljit/sljitNativePPC_32.c similarity index 73% rename from pcre2-10.22/src/sljit/sljitNativePPC_32.c rename to pcre2-10.32/src/sljit/sljitNativePPC_32.c index 0f23cf86d..fc185f784 100644 --- a/pcre2-10.22/src/sljit/sljitNativePPC_32.c +++ b/pcre2-10.32/src/sljit/sljitNativePPC_32.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -88,77 +88,86 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl case SLJIT_NEG: SLJIT_ASSERT(src1 == TMP_REG1); - return push_inst(compiler, NEG | OERC(flags) | D(dst) | A(src2)); + /* Setting XER SO is not enough, CR SO is also needed. */ + return push_inst(compiler, NEG | OE((flags & ALT_FORM1) ? ALT_SET_FLAGS : 0) | RC(flags) | D(dst) | A(src2)); case SLJIT_CLZ: SLJIT_ASSERT(src1 == TMP_REG1); - return push_inst(compiler, CNTLZW | RC(flags) | S(src2) | A(dst)); + return push_inst(compiler, CNTLZW | S(src2) | A(dst)); case SLJIT_ADD: if (flags & ALT_FORM1) { - /* Flags does not set: BIN_IMM_EXTS unnecessary. */ - SLJIT_ASSERT(src2 == TMP_REG2); - return push_inst(compiler, ADDI | D(dst) | A(src1) | compiler->imm); + /* Setting XER SO is not enough, CR SO is also needed. */ + return push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)); } + if (flags & ALT_FORM2) { /* Flags does not set: BIN_IMM_EXTS unnecessary. */ SLJIT_ASSERT(src2 == TMP_REG2); - return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm); + + if (flags & ALT_FORM3) + return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm); + + if (flags & ALT_FORM4) { + FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1)))); + src1 = dst; + } + + return push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff)); } if (flags & ALT_FORM3) { SLJIT_ASSERT(src2 == TMP_REG2); return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm); } - if (flags & ALT_FORM4) { - /* Flags does not set: BIN_IMM_EXTS unnecessary. */ - FAIL_IF(push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff))); - return push_inst(compiler, ADDIS | D(dst) | A(dst) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1))); - } if (!(flags & ALT_SET_FLAGS)) return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2)); - return push_inst(compiler, ADDC | OERC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)); + if (flags & ALT_FORM4) + return push_inst(compiler, ADDC | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)); + return push_inst(compiler, ADD | RC(flags) | D(dst) | A(src1) | B(src2)); case SLJIT_ADDC: - if (flags & ALT_FORM1) { - FAIL_IF(push_inst(compiler, MFXER | D(0))); - FAIL_IF(push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2))); - return push_inst(compiler, MTXER | S(0)); - } return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2)); case SLJIT_SUB: if (flags & ALT_FORM1) { + if (flags & ALT_FORM2) { + FAIL_IF(push_inst(compiler, CMPLI | CRD(0) | A(src1) | compiler->imm)); + if (!(flags & ALT_FORM3)) + return SLJIT_SUCCESS; + return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff)); + } + FAIL_IF(push_inst(compiler, CMPL | CRD(0) | A(src1) | B(src2))); + if (!(flags & ALT_FORM3)) + return SLJIT_SUCCESS; + return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1)); + } + + if (flags & ALT_FORM2) { + /* Setting XER SO is not enough, CR SO is also needed. */ + return push_inst(compiler, SUBF | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)); + } + + if (flags & ALT_FORM3) { /* Flags does not set: BIN_IMM_EXTS unnecessary. */ SLJIT_ASSERT(src2 == TMP_REG2); return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm); } - if (flags & (ALT_FORM2 | ALT_FORM3)) { - SLJIT_ASSERT(src2 == TMP_REG2); - if (flags & ALT_FORM2) - FAIL_IF(push_inst(compiler, CMPI | CRD(0) | A(src1) | compiler->imm)); - if (flags & ALT_FORM3) - return push_inst(compiler, CMPLI | CRD(4) | A(src1) | compiler->imm); - return SLJIT_SUCCESS; - } - if (flags & (ALT_FORM4 | ALT_FORM5)) { - if (flags & ALT_FORM4) - FAIL_IF(push_inst(compiler, CMPL | CRD(4) | A(src1) | B(src2))); - if (flags & ALT_FORM5) - FAIL_IF(push_inst(compiler, CMP | CRD(0) | A(src1) | B(src2))); - return SLJIT_SUCCESS; + + if (flags & ALT_FORM4) { + if (flags & ALT_FORM5) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, CMPI | CRD(0) | A(src1) | compiler->imm); + } + return push_inst(compiler, CMP | CRD(0) | A(src1) | B(src2)); } + if (!(flags & ALT_SET_FLAGS)) return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1)); - if (flags & ALT_FORM6) - FAIL_IF(push_inst(compiler, CMPL | CRD(4) | A(src1) | B(src2))); - return push_inst(compiler, SUBFC | OERC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)); + if (flags & ALT_FORM5) + return push_inst(compiler, SUBFC | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)); + return push_inst(compiler, SUBF | RC(flags) | D(dst) | A(src2) | B(src1)); case SLJIT_SUBC: - if (flags & ALT_FORM1) { - FAIL_IF(push_inst(compiler, MFXER | D(0))); - FAIL_IF(push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1))); - return push_inst(compiler, MTXER | S(0)); - } return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1)); case SLJIT_MUL: @@ -166,7 +175,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl SLJIT_ASSERT(src2 == TMP_REG2); return push_inst(compiler, MULLI | D(dst) | A(src1) | compiler->imm); } - return push_inst(compiler, MULLW | OERC(flags) | D(dst) | A(src2) | B(src1)); + return push_inst(compiler, MULLW | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1)); case SLJIT_AND: if (flags & ALT_FORM1) { @@ -228,19 +237,15 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl return push_inst(compiler, SRW | RC(flags) | S(src1) | A(dst) | B(src2)); case SLJIT_ASHR: - if (flags & ALT_FORM3) - FAIL_IF(push_inst(compiler, MFXER | D(0))); if (flags & ALT_FORM1) { SLJIT_ASSERT(src2 == TMP_REG2); compiler->imm &= 0x1f; - FAIL_IF(push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11))); + return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11)); } - else - FAIL_IF(push_inst(compiler, SRAW | RC(flags) | S(src1) | A(dst) | B(src2))); - return (flags & ALT_FORM3) ? push_inst(compiler, MTXER | S(0)) : SLJIT_SUCCESS; + return push_inst(compiler, SRAW | RC(flags) | S(src1) | A(dst) | B(src2)); } - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_SUCCESS; } @@ -250,20 +255,22 @@ static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_ return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value)); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) { - sljit_ins *inst = (sljit_ins*)addr; + sljit_ins *inst = (sljit_ins *)addr; - inst[0] = (inst[0] & 0xffff0000) | ((new_addr >> 16) & 0xffff); - inst[1] = (inst[1] & 0xffff0000) | (new_addr & 0xffff); + inst[0] = (inst[0] & 0xffff0000) | ((new_target >> 16) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | (new_target & 0xffff); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 2); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) { - sljit_ins *inst = (sljit_ins*)addr; + sljit_ins *inst = (sljit_ins *)addr; inst[0] = (inst[0] & 0xffff0000) | ((new_constant >> 16) & 0xffff); inst[1] = (inst[1] & 0xffff0000) | (new_constant & 0xffff); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 2); } diff --git a/pcre2-10.22/src/sljit/sljitNativePPC_64.c b/pcre2-10.32/src/sljit/sljitNativePPC_64.c similarity index 68% rename from pcre2-10.22/src/sljit/sljitNativePPC_64.c rename to pcre2-10.32/src/sljit/sljitNativePPC_64.c index 8e3223f72..706b2ba20 100644 --- a/pcre2-10.22/src/sljit/sljitNativePPC_64.c +++ b/pcre2-10.32/src/sljit/sljitNativePPC_64.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -204,84 +204,118 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl case SLJIT_NEG: SLJIT_ASSERT(src1 == TMP_REG1); + + if ((flags & (ALT_FORM1 | ALT_SIGN_EXT)) == (ALT_FORM1 | ALT_SIGN_EXT)) { + FAIL_IF(push_inst(compiler, RLDI(TMP_REG2, src2, 32, 31, 1))); + FAIL_IF(push_inst(compiler, NEG | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(TMP_REG2))); + return push_inst(compiler, RLDI(dst, dst, 32, 32, 0)); + } + UN_EXTS(); - return push_inst(compiler, NEG | OERC(flags) | D(dst) | A(src2)); + /* Setting XER SO is not enough, CR SO is also needed. */ + return push_inst(compiler, NEG | OE((flags & ALT_FORM1) ? ALT_SET_FLAGS : 0) | RC(flags) | D(dst) | A(src2)); case SLJIT_CLZ: SLJIT_ASSERT(src1 == TMP_REG1); if (flags & ALT_FORM1) - return push_inst(compiler, CNTLZW | RC(flags) | S(src2) | A(dst)); - return push_inst(compiler, CNTLZD | RC(flags) | S(src2) | A(dst)); + return push_inst(compiler, CNTLZW | S(src2) | A(dst)); + return push_inst(compiler, CNTLZD | S(src2) | A(dst)); case SLJIT_ADD: if (flags & ALT_FORM1) { - /* Flags does not set: BIN_IMM_EXTS unnecessary. */ - SLJIT_ASSERT(src2 == TMP_REG2); - return push_inst(compiler, ADDI | D(dst) | A(src1) | compiler->imm); + if (flags & ALT_SIGN_EXT) { + FAIL_IF(push_inst(compiler, RLDI(TMP_REG1, src1, 32, 31, 1))); + src1 = TMP_REG1; + FAIL_IF(push_inst(compiler, RLDI(TMP_REG2, src2, 32, 31, 1))); + src2 = TMP_REG2; + } + /* Setting XER SO is not enough, CR SO is also needed. */ + FAIL_IF(push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2))); + if (flags & ALT_SIGN_EXT) + return push_inst(compiler, RLDI(dst, dst, 32, 32, 0)); + return SLJIT_SUCCESS; } + if (flags & ALT_FORM2) { /* Flags does not set: BIN_IMM_EXTS unnecessary. */ SLJIT_ASSERT(src2 == TMP_REG2); - return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm); + + if (flags & ALT_FORM3) + return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm); + + if (flags & ALT_FORM4) { + FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1)))); + src1 = dst; + } + + return push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff)); } if (flags & ALT_FORM3) { SLJIT_ASSERT(src2 == TMP_REG2); BIN_IMM_EXTS(); return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm); } - if (flags & ALT_FORM4) { - /* Flags does not set: BIN_IMM_EXTS unnecessary. */ - FAIL_IF(push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff))); - return push_inst(compiler, ADDIS | D(dst) | A(dst) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1))); - } if (!(flags & ALT_SET_FLAGS)) return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2)); BIN_EXTS(); - return push_inst(compiler, ADDC | OERC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)); + if (flags & ALT_FORM4) + return push_inst(compiler, ADDC | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)); + return push_inst(compiler, ADD | RC(flags) | D(dst) | A(src1) | B(src2)); case SLJIT_ADDC: - if (flags & ALT_FORM1) { - FAIL_IF(push_inst(compiler, MFXER | D(0))); - FAIL_IF(push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2))); - return push_inst(compiler, MTXER | S(0)); - } BIN_EXTS(); return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2)); case SLJIT_SUB: if (flags & ALT_FORM1) { + if (flags & ALT_FORM2) { + FAIL_IF(push_inst(compiler, CMPLI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm)); + if (!(flags & ALT_FORM3)) + return SLJIT_SUCCESS; + return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff)); + } + FAIL_IF(push_inst(compiler, CMPL | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2))); + if (!(flags & ALT_FORM3)) + return SLJIT_SUCCESS; + return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1)); + } + + if (flags & ALT_FORM2) { + if (flags & ALT_SIGN_EXT) { + FAIL_IF(push_inst(compiler, RLDI(TMP_REG1, src1, 32, 31, 1))); + src1 = TMP_REG1; + FAIL_IF(push_inst(compiler, RLDI(TMP_REG2, src2, 32, 31, 1))); + src2 = TMP_REG2; + } + /* Setting XER SO is not enough, CR SO is also needed. */ + FAIL_IF(push_inst(compiler, SUBF | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1))); + if (flags & ALT_SIGN_EXT) + return push_inst(compiler, RLDI(dst, dst, 32, 32, 0)); + return SLJIT_SUCCESS; + } + + if (flags & ALT_FORM3) { /* Flags does not set: BIN_IMM_EXTS unnecessary. */ SLJIT_ASSERT(src2 == TMP_REG2); return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm); } - if (flags & (ALT_FORM2 | ALT_FORM3)) { - SLJIT_ASSERT(src2 == TMP_REG2); - if (flags & ALT_FORM2) - FAIL_IF(push_inst(compiler, CMPI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm)); - if (flags & ALT_FORM3) - return push_inst(compiler, CMPLI | CRD(4 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm); - return SLJIT_SUCCESS; - } - if (flags & (ALT_FORM4 | ALT_FORM5)) { - if (flags & ALT_FORM4) - FAIL_IF(push_inst(compiler, CMPL | CRD(4 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2))); - if (flags & ALT_FORM5) - return push_inst(compiler, CMP | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)); - return SLJIT_SUCCESS; + + if (flags & ALT_FORM4) { + if (flags & ALT_FORM5) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, CMPI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm); + } + return push_inst(compiler, CMP | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)); } + if (!(flags & ALT_SET_FLAGS)) return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1)); BIN_EXTS(); - if (flags & ALT_FORM6) - FAIL_IF(push_inst(compiler, CMPL | CRD(4 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2))); - return push_inst(compiler, SUBFC | OERC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)); + if (flags & ALT_FORM5) + return push_inst(compiler, SUBFC | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)); + return push_inst(compiler, SUBF | RC(flags) | D(dst) | A(src2) | B(src1)); case SLJIT_SUBC: - if (flags & ALT_FORM1) { - FAIL_IF(push_inst(compiler, MFXER | D(0))); - FAIL_IF(push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1))); - return push_inst(compiler, MTXER | S(0)); - } BIN_EXTS(); return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1)); @@ -292,8 +326,8 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl } BIN_EXTS(); if (flags & ALT_FORM2) - return push_inst(compiler, MULLW | OERC(flags) | D(dst) | A(src2) | B(src1)); - return push_inst(compiler, MULLD | OERC(flags) | D(dst) | A(src2) | B(src1)); + return push_inst(compiler, MULLW | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1)); + return push_inst(compiler, MULLD | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1)); case SLJIT_AND: if (flags & ALT_FORM1) { @@ -345,10 +379,8 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl compiler->imm &= 0x1f; return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11) | ((31 - compiler->imm) << 1)); } - else { - compiler->imm &= 0x3f; - return push_inst(compiler, RLDI(dst, src1, compiler->imm, 63 - compiler->imm, 1) | RC(flags)); - } + compiler->imm &= 0x3f; + return push_inst(compiler, RLDI(dst, src1, compiler->imm, 63 - compiler->imm, 1) | RC(flags)); } return push_inst(compiler, ((flags & ALT_FORM2) ? SLW : SLD) | RC(flags) | S(src1) | A(dst) | B(src2)); @@ -359,33 +391,80 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl compiler->imm &= 0x1f; return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (((32 - compiler->imm) & 0x1f) << 11) | (compiler->imm << 6) | (31 << 1)); } - else { - compiler->imm &= 0x3f; - return push_inst(compiler, RLDI(dst, src1, 64 - compiler->imm, compiler->imm, 0) | RC(flags)); - } + compiler->imm &= 0x3f; + return push_inst(compiler, RLDI(dst, src1, 64 - compiler->imm, compiler->imm, 0) | RC(flags)); } return push_inst(compiler, ((flags & ALT_FORM2) ? SRW : SRD) | RC(flags) | S(src1) | A(dst) | B(src2)); case SLJIT_ASHR: - if (flags & ALT_FORM3) - FAIL_IF(push_inst(compiler, MFXER | D(0))); if (flags & ALT_FORM1) { SLJIT_ASSERT(src2 == TMP_REG2); if (flags & ALT_FORM2) { compiler->imm &= 0x1f; - FAIL_IF(push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11))); - } - else { - compiler->imm &= 0x3f; - FAIL_IF(push_inst(compiler, SRADI | RC(flags) | S(src1) | A(dst) | ((compiler->imm & 0x1f) << 11) | ((compiler->imm & 0x20) >> 4))); + return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11)); } + compiler->imm &= 0x3f; + return push_inst(compiler, SRADI | RC(flags) | S(src1) | A(dst) | ((compiler->imm & 0x1f) << 11) | ((compiler->imm & 0x20) >> 4)); } - else - FAIL_IF(push_inst(compiler, ((flags & ALT_FORM2) ? SRAW : SRAD) | RC(flags) | S(src1) | A(dst) | B(src2))); - return (flags & ALT_FORM3) ? push_inst(compiler, MTXER | S(0)) : SLJIT_SUCCESS; + return push_inst(compiler, ((flags & ALT_FORM2) ? SRAW : SRAD) | RC(flags) | S(src1) | A(dst) | B(src2)); + } + + SLJIT_UNREACHABLE(); + return SLJIT_SUCCESS; +} + +static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src) +{ + sljit_s32 arg_count = 0; + sljit_s32 word_arg_count = 0; + sljit_s32 types = 0; + sljit_s32 reg = 0; + + if (src) + reg = *src & REG_MASK; + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK); + + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + case SLJIT_ARG_TYPE_F64: + arg_count++; + break; + default: + arg_count++; + word_arg_count++; + + if (arg_count != word_arg_count && arg_count == reg) { + FAIL_IF(push_inst(compiler, OR | S(reg) | A(TMP_CALL_REG) | B(reg))); + *src = TMP_CALL_REG; + } + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + while (types) { + switch (types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + case SLJIT_ARG_TYPE_F64: + arg_count--; + break; + default: + if (arg_count != word_arg_count) + FAIL_IF(push_inst(compiler, OR | S(word_arg_count) | A(arg_count) | B(word_arg_count))); + + arg_count--; + word_arg_count--; + break; + } + + types >>= SLJIT_DEF_SHIFT; } - SLJIT_ASSERT_STOP(); return SLJIT_SUCCESS; } @@ -398,18 +477,19 @@ static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_ return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value)); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) { sljit_ins *inst = (sljit_ins*)addr; - inst[0] = (inst[0] & 0xffff0000) | ((new_addr >> 48) & 0xffff); - inst[1] = (inst[1] & 0xffff0000) | ((new_addr >> 32) & 0xffff); - inst[3] = (inst[3] & 0xffff0000) | ((new_addr >> 16) & 0xffff); - inst[4] = (inst[4] & 0xffff0000) | (new_addr & 0xffff); + inst[0] = (inst[0] & 0xffff0000) | ((new_target >> 48) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | ((new_target >> 32) & 0xffff); + inst[3] = (inst[3] & 0xffff0000) | ((new_target >> 16) & 0xffff); + inst[4] = (inst[4] & 0xffff0000) | (new_target & 0xffff); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 5); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) { sljit_ins *inst = (sljit_ins*)addr; @@ -417,5 +497,6 @@ SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_consta inst[1] = (inst[1] & 0xffff0000) | ((new_constant >> 32) & 0xffff); inst[3] = (inst[3] & 0xffff0000) | ((new_constant >> 16) & 0xffff); inst[4] = (inst[4] & 0xffff0000) | (new_constant & 0xffff); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 5); } diff --git a/pcre2-10.22/src/sljit/sljitNativePPC_common.c b/pcre2-10.32/src/sljit/sljitNativePPC_common.c similarity index 63% rename from pcre2-10.22/src/sljit/sljitNativePPC_common.c rename to pcre2-10.32/src/sljit/sljitNativePPC_common.c index a3647327b..5ef4ac96c 100644 --- a/pcre2-10.22/src/sljit/sljitNativePPC_common.c +++ b/pcre2-10.32/src/sljit/sljitNativePPC_common.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -93,20 +93,23 @@ static void ppc_cache_flush(sljit_ins *from, sljit_ins *to) #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 TMP_ZERO (SLJIT_NUMBER_OF_REGISTERS + 5) +#define TMP_ZERO (SLJIT_NUMBER_OF_REGISTERS + 4) #if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL) -#define TMP_CALL_REG (SLJIT_NUMBER_OF_REGISTERS + 6) +#define TMP_CALL_REG (SLJIT_NUMBER_OF_REGISTERS + 5) #else #define TMP_CALL_REG TMP_REG2 #endif -#define TMP_FREG1 (0) -#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2) static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = { - 0, 3, 4, 5, 6, 7, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 1, 8, 9, 10, 31, 12 + 0, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 1, 9, 10, 31, 12 +}; + +static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { + 0, 1, 2, 3, 4, 5, 6, 0, 7 }; /* --------------------------------------------------------------------- */ @@ -117,19 +120,19 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = { #define A(a) (reg_map[a] << 16) #define B(b) (reg_map[b] << 11) #define C(c) (reg_map[c] << 6) -#define FD(fd) ((fd) << 21) -#define FS(fs) ((fs) << 21) -#define FA(fa) ((fa) << 16) -#define FB(fb) ((fb) << 11) -#define FC(fc) ((fc) << 6) +#define FD(fd) (freg_map[fd] << 21) +#define FS(fs) (freg_map[fs] << 21) +#define FA(fa) (freg_map[fa] << 16) +#define FB(fb) (freg_map[fb] << 11) +#define FC(fc) (freg_map[fc] << 6) #define IMM(imm) ((imm) & 0xffff) #define CRD(d) ((d) << 21) /* Instruction bit sections. OE and Rc flag (see ALT_SET_FLAGS). */ -#define OERC(flags) (((flags & ALT_SET_FLAGS) >> 10) | (flags & ALT_SET_FLAGS)) +#define OE(flags) ((flags) & ALT_SET_FLAGS) /* Rc flag (see ALT_SET_FLAGS). */ -#define RC(flags) ((flags & ALT_SET_FLAGS) >> 10) +#define RC(flags) (((flags) & ALT_SET_FLAGS) >> 10) #define HI(opcode) ((opcode) << 26) #define LO(opcode) ((opcode) << 1) @@ -154,6 +157,7 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = { #define CMPL (HI(31) | LO(32)) #define CMPLI (HI(10)) #define CROR (HI(19) | LO(449)) +#define DCBT (HI(31) | LO(278)) #define DIVD (HI(31) | LO(489)) #define DIVDU (HI(31) | LO(457)) #define DIVW (HI(31) | LO(491)) @@ -249,7 +253,7 @@ static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins) return SLJIT_SUCCESS; } -static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code) +static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code, sljit_sw executable_offset) { sljit_sw diff; sljit_uw target_addr; @@ -267,7 +271,7 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_in target_addr = jump->u.target; else { SLJIT_ASSERT(jump->flags & JUMP_LABEL); - target_addr = (sljit_uw)(code + jump->u.label->size); + target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset; } #if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL) && (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) @@ -275,7 +279,7 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_in goto keep_address; #endif - diff = ((sljit_sw)target_addr - (sljit_sw)(code_ptr)) & ~0x3l; + diff = ((sljit_sw)target_addr - (sljit_sw)(code_ptr) - executable_offset) & ~0x3l; extra_jump_flags = 0; if (jump->flags & IS_COND) { @@ -296,6 +300,7 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_in jump->flags |= PATCH_B | extra_jump_flags; return 1; } + if (target_addr <= 0x03ffffff) { jump->flags |= PATCH_B | PATCH_ABS_B | extra_jump_flags; return 1; @@ -309,6 +314,7 @@ static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_in jump->flags |= PATCH_ABS32; return 1; } + if (target_addr <= 0x7fffffffffffl) { jump->flags |= PATCH_ABS48; return 1; @@ -326,6 +332,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil sljit_ins *buf_ptr; sljit_ins *buf_end; sljit_uw word_count; + sljit_sw executable_offset; sljit_uw addr; struct sljit_label *label; @@ -349,9 +356,12 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil code_ptr = code; word_count = 0; + executable_offset = SLJIT_EXEC_OFFSET(code); + label = compiler->labels; jump = compiler->jumps; const_ = compiler->consts; + do { buf_ptr = (sljit_ins*)buf->memory; buf_end = buf_ptr + (buf->used_size >> 2); @@ -363,7 +373,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil /* These structures are ordered by their address. */ if (label && label->size == word_count) { /* Just recording the address. */ - label->addr = (sljit_uw)code_ptr; + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); label->size = code_ptr - code; label = label->next; } @@ -373,7 +383,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil #else jump->addr = (sljit_uw)(code_ptr - 6); #endif - if (detect_jump_type(jump, code_ptr, code)) { + if (detect_jump_type(jump, code_ptr, code, executable_offset)) { #if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) code_ptr[-3] = code_ptr[0]; code_ptr -= 3; @@ -420,7 +430,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } while (buf); if (label && label->size == word_count) { - label->addr = (sljit_uw)code_ptr; + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); label->size = code_ptr - code; label = label->next; } @@ -438,11 +448,12 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil while (jump) { do { addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; - buf_ptr = (sljit_ins*)jump->addr; + buf_ptr = (sljit_ins *)jump->addr; + if (jump->flags & PATCH_B) { if (jump->flags & IS_COND) { if (!(jump->flags & PATCH_ABS_B)) { - addr = addr - jump->addr; + addr -= (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset); SLJIT_ASSERT((sljit_sw)addr <= 0x7fff && (sljit_sw)addr >= -0x8000); *buf_ptr = BCx | (addr & 0xfffc) | ((*buf_ptr) & 0x03ff0001); } @@ -453,7 +464,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } else { if (!(jump->flags & PATCH_ABS_B)) { - addr = addr - jump->addr; + addr -= (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset); SLJIT_ASSERT((sljit_sw)addr <= 0x01ffffff && (sljit_sw)addr >= -0x02000000); *buf_ptr = Bx | (addr & 0x03fffffc) | ((*buf_ptr) & 0x1); } @@ -464,6 +475,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } break; } + /* Set the fields of immediate loads. */ #if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) buf_ptr[0] = (buf_ptr[0] & 0xffff0000) | ((addr >> 16) & 0xffff); @@ -492,24 +504,49 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_offset = executable_offset; compiler->executable_size = (code_ptr - code) * sizeof(sljit_ins); - SLJIT_CACHE_FLUSH(code, code_ptr); + + code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset); #if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) if (((sljit_sw)code_ptr) & 0x4) code_ptr++; - sljit_set_function_context(NULL, (struct sljit_function_context*)code_ptr, (sljit_sw)code, (void*)sljit_generate_code); - return code_ptr; -#else - sljit_set_function_context(NULL, (struct sljit_function_context*)code_ptr, (sljit_sw)code, (void*)sljit_generate_code); - return code_ptr; #endif + sljit_set_function_context(NULL, (struct sljit_function_context*)code_ptr, (sljit_sw)code, (void*)sljit_generate_code); +#endif + + code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); + + SLJIT_CACHE_FLUSH(code, code_ptr); + +#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) + return code_ptr; #else return code; #endif } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) +{ + switch (feature_type) { + case SLJIT_HAS_FPU: +#ifdef SLJIT_IS_FPU_AVAILABLE + return SLJIT_IS_FPU_AVAILABLE; +#else + /* Available by default. */ + return 1; +#endif + + case SLJIT_HAS_CLZ: + return 1; + + default: + return 0; + } +} + /* --------------------------------------------------------------------- */ /* Entry, exit */ /* --------------------------------------------------------------------- */ @@ -519,47 +556,40 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil /* Creates an index in data_transfer_insts array. */ #define LOAD_DATA 0x01 #define INDEXED 0x02 -#define WRITE_BACK 0x04 +#define SIGNED_DATA 0x04 + #define WORD_DATA 0x00 #define BYTE_DATA 0x08 #define HALF_DATA 0x10 #define INT_DATA 0x18 -#define SIGNED_DATA 0x20 /* Separates integer and floating point registers */ -#define GPR_REG 0x3f -#define DOUBLE_DATA 0x40 +#define GPR_REG 0x1f +#define DOUBLE_DATA 0x20 #define MEM_MASK 0x7f /* Other inp_flags. */ -#define ARG_TEST 0x000100 /* Integer opertion and set flags -> requires exts on 64 bit systems. */ -#define ALT_SIGN_EXT 0x000200 +#define ALT_SIGN_EXT 0x000100 /* This flag affects the RC() and OERC() macros. */ #define ALT_SET_FLAGS 0x000400 -#define ALT_KEEP_CACHE 0x000800 -#define ALT_FORM1 0x010000 -#define ALT_FORM2 0x020000 -#define ALT_FORM3 0x040000 -#define ALT_FORM4 0x080000 -#define ALT_FORM5 0x100000 -#define ALT_FORM6 0x200000 +#define ALT_FORM1 0x001000 +#define ALT_FORM2 0x002000 +#define ALT_FORM3 0x004000 +#define ALT_FORM4 0x008000 +#define ALT_FORM5 0x010000 /* Source and destination is register. */ #define REG_DEST 0x000001 #define REG1_SOURCE 0x000002 #define REG2_SOURCE 0x000004 -/* getput_arg_fast returned true. */ -#define FAST_DEST 0x000008 -/* Multiple instructions are required. */ -#define SLOW_DEST 0x000010 /* -ALT_SIGN_EXT 0x000200 -ALT_SET_FLAGS 0x000400 -ALT_FORM1 0x010000 +ALT_SIGN_EXT 0x000100 +ALT_SET_FLAGS 0x000200 +ALT_FORM1 0x001000 ... -ALT_FORM6 0x200000 */ +ALT_FORM5 0x010000 */ #if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) #include "sljitNativePPC_32.c" @@ -576,14 +606,14 @@ ALT_FORM6 0x200000 */ #endif SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { - sljit_s32 i, tmp, offs; + sljit_s32 args, i, tmp, offs; CHECK_ERROR(); - 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); + CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); FAIL_IF(push_inst(compiler, MFLR | D(0))); offs = -(sljit_s32)(sizeof(sljit_sw)); @@ -609,6 +639,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi #endif FAIL_IF(push_inst(compiler, ADDI | D(TMP_ZERO) | A(0) | 0)); + + args = get_arg_count(arg_types); + if (args >= 1) FAIL_IF(push_inst(compiler, OR | S(SLJIT_R0) | A(SLJIT_S0) | B(SLJIT_R0))); if (args >= 2) @@ -640,12 +673,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 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); + CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1) + SLJIT_LOCALS_OFFSET; compiler->local_size = (local_size + 15) & ~0xf; @@ -704,17 +737,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp /* Operators */ /* --------------------------------------------------------------------- */ -/* i/x - immediate/indexed form - n/w - no write-back / write-back (1 bit) - s/l - store/load (1 bit) +/* s/l - store/load (1 bit) + i/x - immediate/indexed form u/s - signed/unsigned (1 bit) w/b/h/i - word/byte/half/int allowed (2 bit) - It contans 32 items, but not all are different. */ + + Some opcodes are repeated (e.g. store signed / unsigned byte is the same instruction). */ /* 64 bit only: [reg+imm] must be aligned to 4 bytes. */ +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) #define INT_ALIGNED 0x10000 -/* 64-bit only: there is no lwau instruction. */ -#define UPDATE_REQ 0x20000 +#endif #if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) #define ARCH_32_64(a, b) a @@ -723,401 +756,217 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp #else #define ARCH_32_64(a, b) b #define INST_CODE_AND_DST(inst, flags, reg) \ - (((inst) & ~(INT_ALIGNED | UPDATE_REQ)) | (((flags) & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg))) + (((inst) & ~INT_ALIGNED) | (((flags) & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg))) #endif -static const sljit_ins data_transfer_insts[64 + 8] = { +static const sljit_ins data_transfer_insts[64 + 16] = { -/* -------- Unsigned -------- */ +/* -------- Integer -------- */ /* Word. */ -/* u w n i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */), -/* u w n i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */), -/* u w n x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */), -/* u w n x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */), +/* w u i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */), +/* w u i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */), +/* w u x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */), +/* w u x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */), -/* u w w i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */), -/* u w w i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */), -/* u w w x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */), -/* u w w x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */), +/* w s i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */), +/* w s i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */), +/* w s x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */), +/* w s x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */), /* Byte. */ -/* u b n i s */ HI(38) /* stb */, -/* u b n i l */ HI(34) /* lbz */, -/* u b n x s */ HI(31) | LO(215) /* stbx */, -/* u b n x l */ HI(31) | LO(87) /* lbzx */, +/* b u i s */ HI(38) /* stb */, +/* b u i l */ HI(34) /* lbz */, +/* b u x s */ HI(31) | LO(215) /* stbx */, +/* b u x l */ HI(31) | LO(87) /* lbzx */, -/* u b w i s */ HI(39) /* stbu */, -/* u b w i l */ HI(35) /* lbzu */, -/* u b w x s */ HI(31) | LO(247) /* stbux */, -/* u b w x l */ HI(31) | LO(119) /* lbzux */, +/* b s i s */ HI(38) /* stb */, +/* b s i l */ HI(34) /* lbz */ /* EXTS_REQ */, +/* b s x s */ HI(31) | LO(215) /* stbx */, +/* b s x l */ HI(31) | LO(87) /* lbzx */ /* EXTS_REQ */, /* Half. */ -/* u h n i s */ HI(44) /* sth */, -/* u h n i l */ HI(40) /* lhz */, -/* u h n x s */ HI(31) | LO(407) /* sthx */, -/* u h n x l */ HI(31) | LO(279) /* lhzx */, +/* h u i s */ HI(44) /* sth */, +/* h u i l */ HI(40) /* lhz */, +/* h u x s */ HI(31) | LO(407) /* sthx */, +/* h u x l */ HI(31) | LO(279) /* lhzx */, -/* u h w i s */ HI(45) /* sthu */, -/* u h w i l */ HI(41) /* lhzu */, -/* u h w x s */ HI(31) | LO(439) /* sthux */, -/* u h w x l */ HI(31) | LO(311) /* lhzux */, +/* h s i s */ HI(44) /* sth */, +/* h s i l */ HI(42) /* lha */, +/* h s x s */ HI(31) | LO(407) /* sthx */, +/* h s x l */ HI(31) | LO(343) /* lhax */, /* Int. */ -/* u i n i s */ HI(36) /* stw */, -/* u i n i l */ HI(32) /* lwz */, -/* u i n x s */ HI(31) | LO(151) /* stwx */, -/* u i n x l */ HI(31) | LO(23) /* lwzx */, +/* i u i s */ HI(36) /* stw */, +/* i u i l */ HI(32) /* lwz */, +/* i u x s */ HI(31) | LO(151) /* stwx */, +/* i u x l */ HI(31) | LO(23) /* lwzx */, -/* u i w i s */ HI(37) /* stwu */, -/* u i w i l */ HI(33) /* lwzu */, -/* u i w x s */ HI(31) | LO(183) /* stwux */, -/* u i w x l */ HI(31) | LO(55) /* lwzux */, +/* i s i s */ HI(36) /* stw */, +/* i s i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x2 /* lwa */), +/* i s x s */ HI(31) | LO(151) /* stwx */, +/* i s x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(341) /* lwax */), -/* -------- Signed -------- */ +/* -------- Floating point -------- */ + +/* d i s */ HI(54) /* stfd */, +/* d i l */ HI(50) /* lfd */, +/* d x s */ HI(31) | LO(727) /* stfdx */, +/* d x l */ HI(31) | LO(599) /* lfdx */, + +/* s i s */ HI(52) /* stfs */, +/* s i l */ HI(48) /* lfs */, +/* s x s */ HI(31) | LO(663) /* stfsx */, +/* s x l */ HI(31) | LO(535) /* lfsx */, +}; + +static const sljit_ins updated_data_transfer_insts[64] = { + +/* -------- Integer -------- */ /* Word. */ -/* s w n i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */), -/* s w n i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */), -/* s w n x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */), -/* s w n x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */), +/* w u i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */), +/* w u i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */), +/* w u x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */), +/* w u x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */), -/* s w w i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */), -/* s w w i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */), -/* s w w x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */), -/* s w w x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */), +/* w s i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */), +/* w s i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */), +/* w s x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */), +/* w s x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */), /* Byte. */ -/* s b n i s */ HI(38) /* stb */, -/* s b n i l */ HI(34) /* lbz */ /* EXTS_REQ */, -/* s b n x s */ HI(31) | LO(215) /* stbx */, -/* s b n x l */ HI(31) | LO(87) /* lbzx */ /* EXTS_REQ */, +/* b u i s */ HI(39) /* stbu */, +/* b u i l */ HI(35) /* lbzu */, +/* b u x s */ HI(31) | LO(247) /* stbux */, +/* b u x l */ HI(31) | LO(119) /* lbzux */, -/* s b w i s */ HI(39) /* stbu */, -/* s b w i l */ HI(35) /* lbzu */ /* EXTS_REQ */, -/* s b w x s */ HI(31) | LO(247) /* stbux */, -/* s b w x l */ HI(31) | LO(119) /* lbzux */ /* EXTS_REQ */, +/* b s i s */ HI(39) /* stbu */, +/* b s i l */ 0 /* no such instruction */, +/* b s x s */ HI(31) | LO(247) /* stbux */, +/* b s x l */ 0 /* no such instruction */, /* Half. */ -/* s h n i s */ HI(44) /* sth */, -/* s h n i l */ HI(42) /* lha */, -/* s h n x s */ HI(31) | LO(407) /* sthx */, -/* s h n x l */ HI(31) | LO(343) /* lhax */, +/* h u i s */ HI(45) /* sthu */, +/* h u i l */ HI(41) /* lhzu */, +/* h u x s */ HI(31) | LO(439) /* sthux */, +/* h u x l */ HI(31) | LO(311) /* lhzux */, -/* s h w i s */ HI(45) /* sthu */, -/* s h w i l */ HI(43) /* lhau */, -/* s h w x s */ HI(31) | LO(439) /* sthux */, -/* s h w x l */ HI(31) | LO(375) /* lhaux */, +/* h s i s */ HI(45) /* sthu */, +/* h s i l */ HI(43) /* lhau */, +/* h s x s */ HI(31) | LO(439) /* sthux */, +/* h s x l */ HI(31) | LO(375) /* lhaux */, /* Int. */ -/* s i n i s */ HI(36) /* stw */, -/* s i n i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x2 /* lwa */), -/* s i n x s */ HI(31) | LO(151) /* stwx */, -/* s i n x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(341) /* lwax */), +/* i u i s */ HI(37) /* stwu */, +/* i u i l */ HI(33) /* lwzu */, +/* i u x s */ HI(31) | LO(183) /* stwux */, +/* i u x l */ HI(31) | LO(55) /* lwzux */, -/* s i w i s */ HI(37) /* stwu */, -/* s i w i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | UPDATE_REQ | 0x2 /* lwa */), -/* s i w x s */ HI(31) | LO(183) /* stwux */, -/* s i w x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(373) /* lwaux */), +/* i s i s */ HI(37) /* stwu */, +/* i s i l */ ARCH_32_64(HI(33) /* lwzu */, 0 /* no such instruction */), +/* i s x s */ HI(31) | LO(183) /* stwux */, +/* i s x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(373) /* lwaux */), -/* -------- Double -------- */ +/* -------- Floating point -------- */ -/* d n i s */ HI(54) /* stfd */, -/* d n i l */ HI(50) /* lfd */, -/* d n x s */ HI(31) | LO(727) /* stfdx */, -/* d n x l */ HI(31) | LO(599) /* lfdx */, - -/* s n i s */ HI(52) /* stfs */, -/* s n i l */ HI(48) /* lfs */, -/* s n x s */ HI(31) | LO(663) /* stfsx */, -/* s n x l */ HI(31) | LO(535) /* lfsx */, +/* d i s */ HI(55) /* stfdu */, +/* d i l */ HI(51) /* lfdu */, +/* d x s */ HI(31) | LO(759) /* stfdux */, +/* d x l */ HI(31) | LO(631) /* lfdux */, +/* s i s */ HI(53) /* stfsu */, +/* s i l */ HI(49) /* lfsu */, +/* s x s */ HI(31) | LO(695) /* stfsux */, +/* s x l */ HI(31) | LO(567) /* lfsux */, }; #undef ARCH_32_64 /* Simple cases, (no caching is required). */ -static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) +static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg, + sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg) { sljit_ins inst; + sljit_s32 offs_reg; + sljit_sw high_short; /* Should work when (arg & REG_MASK) == 0. */ - SLJIT_COMPILE_ASSERT(A(0) == 0, a0_must_be_0); + SLJIT_ASSERT(A(0) == 0); SLJIT_ASSERT(arg & SLJIT_MEM); - if (arg & OFFS_REG_MASK) { - if (argw & 0x3) - return 0; - if (inp_flags & ARG_TEST) - return 1; - - inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; - SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ))); - FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(OFFS_REG(arg)))); - return -1; - } - - if (SLJIT_UNLIKELY(!(arg & REG_MASK))) - inp_flags &= ~WRITE_BACK; - -#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - inst = data_transfer_insts[inp_flags & MEM_MASK]; - SLJIT_ASSERT((arg & REG_MASK) || !(inst & UPDATE_REQ)); - - if (argw > SIMM_MAX || argw < SIMM_MIN || ((inst & INT_ALIGNED) && (argw & 0x3)) || (inst & UPDATE_REQ)) - return 0; - if (inp_flags & ARG_TEST) - return 1; -#endif - -#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) - if (argw > SIMM_MAX || argw < SIMM_MIN) - return 0; - if (inp_flags & ARG_TEST) - return 1; - - inst = data_transfer_insts[inp_flags & MEM_MASK]; - SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ))); -#endif - - FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | IMM(argw))); - return -1; -} - -/* See getput_arg below. - Note: can_cache is called only for binary operators. Those operator always - uses word arguments without write back. */ -static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) -{ - sljit_sw high_short, next_high_short; -#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - sljit_sw diff; -#endif - - SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM)); - - if (arg & OFFS_REG_MASK) - return ((arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK) && (argw & 0x3) == (next_argw & 0x3)); - - if (next_arg & OFFS_REG_MASK) - return 0; - -#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) - high_short = (argw + ((argw & 0x8000) << 1)) & ~0xffff; - next_high_short = (next_argw + ((next_argw & 0x8000) << 1)) & ~0xffff; - return high_short == next_high_short; -#else - if (argw <= 0x7fffffffl && argw >= -0x80000000l) { - high_short = (argw + ((argw & 0x8000) << 1)) & ~0xffff; - next_high_short = (next_argw + ((next_argw & 0x8000) << 1)) & ~0xffff; - if (high_short == next_high_short) - return 1; - } - - diff = argw - next_argw; - if (!(arg & REG_MASK)) - return diff <= SIMM_MAX && diff >= SIMM_MIN; - - if (arg == next_arg && diff <= SIMM_MAX && diff >= SIMM_MIN) - return 1; - - return 0; -#endif -} - -#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) -#define ADJUST_CACHED_IMM(imm) \ - if ((inst & INT_ALIGNED) && (imm & 0x3)) { \ - /* Adjust cached value. Fortunately this is really a rare case */ \ - compiler->cache_argw += imm & 0x3; \ - FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG3) | A(TMP_REG3) | (imm & 0x3))); \ - imm &= ~0x3; \ - } -#endif - -/* Emit the necessary instructions. See can_cache above. */ -static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw) -{ - sljit_s32 tmp_r; - sljit_ins inst; - sljit_sw high_short, next_high_short; -#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - sljit_sw diff; -#endif - - SLJIT_ASSERT(arg & SLJIT_MEM); - - tmp_r = ((inp_flags & LOAD_DATA) && ((inp_flags) & MEM_MASK) <= GPR_REG) ? reg : TMP_REG1; - /* Special case for "mov reg, [reg, ... ]". */ - if ((arg & REG_MASK) == tmp_r) - tmp_r = TMP_REG1; - if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { argw &= 0x3; - /* Otherwise getput_arg_fast would capture it. */ - SLJIT_ASSERT(argw); + offs_reg = OFFS_REG(arg); - if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg && argw == compiler->cache_argw) - tmp_r = TMP_REG3; - else { - if ((arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK) && argw == (next_argw & 0x3)) { - compiler->cache_arg = SLJIT_MEM | (arg & OFFS_REG_MASK); - compiler->cache_argw = argw; - tmp_r = TMP_REG3; - } + if (argw != 0) { #if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) - FAIL_IF(push_inst(compiler, RLWINM | S(OFFS_REG(arg)) | A(tmp_r) | (argw << 11) | ((31 - argw) << 1))); + FAIL_IF(push_inst(compiler, RLWINM | S(OFFS_REG(arg)) | A(tmp_reg) | (argw << 11) | ((31 - argw) << 1))); #else - FAIL_IF(push_inst(compiler, RLDI(tmp_r, OFFS_REG(arg), argw, 63 - argw, 1))); + FAIL_IF(push_inst(compiler, RLDI(tmp_reg, OFFS_REG(arg), argw, 63 - argw, 1))); #endif + offs_reg = tmp_reg; } + inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; - SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ))); - return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(tmp_r)); - } - - if (SLJIT_UNLIKELY(!(arg & REG_MASK))) - inp_flags &= ~WRITE_BACK; - - inst = data_transfer_insts[inp_flags & MEM_MASK]; - SLJIT_ASSERT((arg & REG_MASK) || !(inst & UPDATE_REQ)); #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - if (argw <= 0x7fff7fffl && argw >= -0x80000000l - && (!(inst & INT_ALIGNED) || !(argw & 0x3)) && !(inst & UPDATE_REQ)) { + SLJIT_ASSERT(!(inst & INT_ALIGNED)); +#endif + + return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(offs_reg)); + } + + inst = data_transfer_insts[inp_flags & MEM_MASK]; + arg &= REG_MASK; + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if ((inst & INT_ALIGNED) && (argw & 0x3) != 0) { + FAIL_IF(load_immediate(compiler, tmp_reg, argw)); + + inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; + return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | B(tmp_reg)); + } +#endif + + if (argw <= SIMM_MAX && argw >= SIMM_MIN) + return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | IMM(argw)); + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if (argw <= 0x7fff7fffl && argw >= -0x80000000l) { #endif - arg &= REG_MASK; high_short = (sljit_s32)(argw + ((argw & 0x8000) << 1)) & ~0xffff; - /* The getput_arg_fast should handle this otherwise. */ + #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) SLJIT_ASSERT(high_short && high_short <= 0x7fffffffl && high_short >= -0x80000000l); #else - SLJIT_ASSERT(high_short && !(inst & (INT_ALIGNED | UPDATE_REQ))); + SLJIT_ASSERT(high_short); #endif - if (inp_flags & WRITE_BACK) { - if (arg == reg) { - FAIL_IF(push_inst(compiler, OR | S(reg) | A(tmp_r) | B(reg))); - reg = tmp_r; - } - tmp_r = arg; - FAIL_IF(push_inst(compiler, ADDIS | D(arg) | A(arg) | IMM(high_short >> 16))); - } - else if (compiler->cache_arg != (SLJIT_MEM | arg) || high_short != compiler->cache_argw) { - if ((next_arg & SLJIT_MEM) && !(next_arg & OFFS_REG_MASK)) { - next_high_short = (sljit_s32)(next_argw + ((next_argw & 0x8000) << 1)) & ~0xffff; - if (high_short == next_high_short) { - compiler->cache_arg = SLJIT_MEM | arg; - compiler->cache_argw = high_short; - tmp_r = TMP_REG3; - } - } - FAIL_IF(push_inst(compiler, ADDIS | D(tmp_r) | A(arg & REG_MASK) | IMM(high_short >> 16))); - } - else - tmp_r = TMP_REG3; - - return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(tmp_r) | IMM(argw)); + FAIL_IF(push_inst(compiler, ADDIS | D(tmp_reg) | A(arg) | IMM(high_short >> 16))); + return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(tmp_reg) | IMM(argw)); #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) } - /* Everything else is PPC-64 only. */ - if (SLJIT_UNLIKELY(!(arg & REG_MASK))) { - diff = argw - compiler->cache_argw; - if ((compiler->cache_arg & SLJIT_IMM) && diff <= SIMM_MAX && diff >= SIMM_MIN) { - ADJUST_CACHED_IMM(diff); - return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(TMP_REG3) | IMM(diff)); - } + /* The rest is PPC-64 only. */ - diff = argw - next_argw; - if ((next_arg & SLJIT_MEM) && diff <= SIMM_MAX && diff >= SIMM_MIN) { - SLJIT_ASSERT(inp_flags & LOAD_DATA); + FAIL_IF(load_immediate(compiler, tmp_reg, argw)); - compiler->cache_arg = SLJIT_IMM; - compiler->cache_argw = argw; - tmp_r = TMP_REG3; - } - - FAIL_IF(load_immediate(compiler, tmp_r, argw)); - return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(tmp_r)); - } - - diff = argw - compiler->cache_argw; - if (compiler->cache_arg == arg && diff <= SIMM_MAX && diff >= SIMM_MIN) { - SLJIT_ASSERT(!(inp_flags & WRITE_BACK) && !(inst & UPDATE_REQ)); - ADJUST_CACHED_IMM(diff); - return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(TMP_REG3) | IMM(diff)); - } - - if ((compiler->cache_arg & SLJIT_IMM) && diff <= SIMM_MAX && diff >= SIMM_MIN) { - inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; - SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ))); - if (compiler->cache_argw != argw) { - FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG3) | A(TMP_REG3) | IMM(diff))); - compiler->cache_argw = argw; - } - return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(TMP_REG3)); - } - - if (argw == next_argw && (next_arg & SLJIT_MEM)) { - SLJIT_ASSERT(inp_flags & LOAD_DATA); - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); - - compiler->cache_arg = SLJIT_IMM; - compiler->cache_argw = argw; - - inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; - SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ))); - return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(TMP_REG3)); - } - - diff = argw - next_argw; - if (arg == next_arg && !(inp_flags & WRITE_BACK) && diff <= SIMM_MAX && diff >= SIMM_MIN) { - SLJIT_ASSERT(inp_flags & LOAD_DATA); - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); - FAIL_IF(push_inst(compiler, ADD | D(TMP_REG3) | A(TMP_REG3) | B(arg & REG_MASK))); - - compiler->cache_arg = arg; - compiler->cache_argw = argw; - - return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(TMP_REG3)); - } - - if ((next_arg & SLJIT_MEM) && !(next_arg & OFFS_REG_MASK) && diff <= SIMM_MAX && diff >= SIMM_MIN) { - SLJIT_ASSERT(inp_flags & LOAD_DATA); - FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); - - compiler->cache_arg = SLJIT_IMM; - compiler->cache_argw = argw; - tmp_r = TMP_REG3; - } - else - FAIL_IF(load_immediate(compiler, tmp_r, argw)); - - /* Get the indexed version instead of the normal one. */ inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; - SLJIT_ASSERT(!(inst & (INT_ALIGNED | UPDATE_REQ))); - return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(tmp_r)); + return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | B(tmp_reg)); #endif } -static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w) -{ - if (getput_arg_fast(compiler, flags, reg, arg1, arg1w)) - return compiler->error; - return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w); -} - static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 input_flags, sljit_s32 dst, sljit_sw dstw, sljit_s32 src1, sljit_sw src1w, @@ -1125,42 +974,21 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 { /* arg1 goes to TMP_REG1 or src reg arg2 goes to TMP_REG2, imm or src reg - TMP_REG3 can be used for caching - result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */ - sljit_s32 dst_r; + result goes to TMP_REG2, so put result can use TMP_REG1. */ + sljit_s32 dst_r = TMP_REG2; sljit_s32 src1_r; sljit_s32 src2_r; sljit_s32 sugg_src2_r = TMP_REG2; - sljit_s32 flags = input_flags & (ALT_FORM1 | ALT_FORM2 | ALT_FORM3 | ALT_FORM4 | ALT_FORM5 | ALT_FORM6 | ALT_SIGN_EXT | ALT_SET_FLAGS); - - if (!(input_flags & ALT_KEEP_CACHE)) { - compiler->cache_arg = 0; - compiler->cache_argw = 0; - } + sljit_s32 flags = input_flags & (ALT_FORM1 | ALT_FORM2 | ALT_FORM3 | ALT_FORM4 | ALT_FORM5 | ALT_SIGN_EXT | ALT_SET_FLAGS); /* Destination check. */ - if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) { - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32 && !(src2 & SLJIT_MEM)) - return SLJIT_SUCCESS; - dst_r = TMP_REG2; - } - else if (FAST_IS_REG(dst)) { + if (SLOW_IS_REG(dst)) { dst_r = dst; flags |= REG_DEST; - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) + + if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) sugg_src2_r = dst_r; } - else { - SLJIT_ASSERT(dst & SLJIT_MEM); - if (getput_arg_fast(compiler, input_flags | ARG_TEST, TMP_REG2, dst, dstw)) { - flags |= FAST_DEST; - dst_r = TMP_REG2; - } - else { - flags |= SLOW_DEST; - dst_r = 0; - } - } /* Source 1. */ if (FAST_IS_REG(src1)) { @@ -1171,80 +999,34 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 FAIL_IF(load_immediate(compiler, TMP_REG1, src1w)); src1_r = TMP_REG1; } - else if (getput_arg_fast(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w)) { - FAIL_IF(compiler->error); + else { + FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1)); src1_r = TMP_REG1; } - else - src1_r = 0; /* Source 2. */ if (FAST_IS_REG(src2)) { src2_r = src2; flags |= REG2_SOURCE; - if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) + + if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOV_P) dst_r = src2_r; } else if (src2 & SLJIT_IMM) { FAIL_IF(load_immediate(compiler, sugg_src2_r, src2w)); src2_r = sugg_src2_r; } - else if (getput_arg_fast(compiler, input_flags | LOAD_DATA, sugg_src2_r, src2, src2w)) { - FAIL_IF(compiler->error); - src2_r = sugg_src2_r; - } - else - src2_r = 0; - - /* src1_r, src2_r and dst_r can be zero (=unprocessed). - All arguments are complex addressing modes, and it is a binary operator. */ - if (src1_r == 0 && src2_r == 0 && dst_r == 0) { - if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { - FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG2, src2, src2w, src1, src1w)); - FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); - } - else { - FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); - FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw)); - } - src1_r = TMP_REG1; - src2_r = TMP_REG2; - } - else if (src1_r == 0 && src2_r == 0) { - FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); - src1_r = TMP_REG1; - } - else if (src1_r == 0 && dst_r == 0) { - FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); - src1_r = TMP_REG1; - } - else if (src2_r == 0 && dst_r == 0) { - FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw)); - src2_r = sugg_src2_r; - } - - if (dst_r == 0) - dst_r = TMP_REG2; - - if (src1_r == 0) { - FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, 0, 0)); - src1_r = TMP_REG1; - } - - if (src2_r == 0) { - FAIL_IF(getput_arg(compiler, input_flags | LOAD_DATA, sugg_src2_r, src2, src2w, 0, 0)); + else { + FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, sugg_src2_r, src2, src2w, TMP_REG2)); src2_r = sugg_src2_r; } FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r)); - if (flags & (FAST_DEST | SLOW_DEST)) { - if (flags & FAST_DEST) - FAIL_IF(getput_arg_fast(compiler, input_flags, dst_r, dst, dstw)); - else - FAIL_IF(getput_arg(compiler, input_flags, dst_r, dst, dstw, 0, 0)); - } - return SLJIT_SUCCESS; + if (!(dst & SLJIT_MEM)) + return SLJIT_SUCCESS; + + return emit_op_mem(compiler, input_flags, dst_r, dst, dstw, TMP_REG1); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op) @@ -1294,6 +1076,31 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile return SLJIT_SUCCESS; } +static sljit_s32 emit_prefetch(struct sljit_compiler *compiler, + sljit_s32 src, sljit_sw srcw) +{ + if (!(src & OFFS_REG_MASK)) { + if (srcw == 0 && (src & REG_MASK) != SLJIT_UNUSED) + return push_inst(compiler, DCBT | A(0) | B(src & REG_MASK)); + + FAIL_IF(load_immediate(compiler, TMP_REG1, srcw)); + /* Works with SLJIT_MEM0() case as well. */ + return push_inst(compiler, DCBT | A(src & REG_MASK) | B(TMP_REG1)); + } + + srcw &= 0x3; + + if (srcw == 0) + return push_inst(compiler, DCBT | A(src & REG_MASK) | B(OFFS_REG(src))); + +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + FAIL_IF(push_inst(compiler, RLWINM | S(OFFS_REG(src)) | A(TMP_REG1) | (srcw << 11) | ((31 - srcw) << 1))); +#else + FAIL_IF(push_inst(compiler, RLDI(TMP_REG1, OFFS_REG(src), srcw, 63 - srcw, 1))); +#endif + return push_inst(compiler, DCBT | A(src & REG_MASK) | B(TMP_REG1)); +} + #define EMIT_MOV(type, type_flags, type_cast) \ emit_op(compiler, (src & SLJIT_IMM) ? SLJIT_MOV : type, flags | (type_flags), dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? type_cast srcw : srcw) @@ -1301,7 +1108,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) { - sljit_s32 flags = GET_FLAGS(op) ? ALT_SET_FLAGS : 0; + sljit_s32 flags = HAS_FLAGS(op) ? ALT_SET_FLAGS : 0; sljit_s32 op_flags = GET_ALL_FLAGS(op); CHECK_ERROR(); @@ -1309,39 +1116,45 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(dst, dstw); ADJUST_LOCAL_OFFSET(src, srcw); + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) { + if (op <= SLJIT_MOV_P && (src & SLJIT_MEM)) + return emit_prefetch(compiler, src, srcw); + + return SLJIT_SUCCESS; + } + op = GET_OPCODE(op); if ((src & SLJIT_IMM) && srcw == 0) src = TMP_ZERO; - if (op_flags & SLJIT_SET_O) + if (GET_FLAG_TYPE(op_flags) == SLJIT_OVERFLOW) FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO))); + if (op < SLJIT_NOT && FAST_IS_REG(src) && src == dst) { + if (!TYPE_CAST_NEEDED(op)) + return SLJIT_SUCCESS; + } + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) if (op_flags & SLJIT_I32_OP) { if (op < SLJIT_NOT) { - if (FAST_IS_REG(src) && src == dst) { - if (!TYPE_CAST_NEEDED(op)) - return SLJIT_SUCCESS; + if (src & SLJIT_MEM) { + if (op == SLJIT_MOV_S32) + op = SLJIT_MOV_U32; + } + else if (src & SLJIT_IMM) { + if (op == SLJIT_MOV_U32) + op = SLJIT_MOV_S32; } -#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - if (op == SLJIT_MOV_S32 && (src & SLJIT_MEM)) - op = SLJIT_MOV_U32; - if (op == SLJIT_MOVU_S32 && (src & SLJIT_MEM)) - op = SLJIT_MOVU_U32; - if (op == SLJIT_MOV_U32 && (src & SLJIT_IMM)) - op = SLJIT_MOV_S32; - if (op == SLJIT_MOVU_U32 && (src & SLJIT_IMM)) - op = SLJIT_MOVU_S32; -#endif } -#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) else { /* Most operations expect sign extended arguments. */ flags |= INT_DATA | SIGNED_DATA; - if (src & SLJIT_IMM) - srcw = (sljit_s32)srcw; + if (HAS_FLAGS(op_flags)) + flags |= ALT_SIGN_EXT; } -#endif } +#endif switch (op) { case SLJIT_MOV: @@ -1372,39 +1185,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile case SLJIT_MOV_S16: return EMIT_MOV(SLJIT_MOV_S16, HALF_DATA | SIGNED_DATA, (sljit_s16)); - case SLJIT_MOVU: - case SLJIT_MOVU_P: -#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) - case SLJIT_MOVU_U32: - case SLJIT_MOVU_S32: -#endif - return emit_op(compiler, SLJIT_MOV, flags | WORD_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); - -#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - case SLJIT_MOVU_U32: - return EMIT_MOV(SLJIT_MOV_U32, INT_DATA | WRITE_BACK, (sljit_u32)); - - case SLJIT_MOVU_S32: - return EMIT_MOV(SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | WRITE_BACK, (sljit_s32)); -#endif - - case SLJIT_MOVU_U8: - return EMIT_MOV(SLJIT_MOV_U8, BYTE_DATA | WRITE_BACK, (sljit_u8)); - - case SLJIT_MOVU_S8: - return EMIT_MOV(SLJIT_MOV_S8, BYTE_DATA | SIGNED_DATA | WRITE_BACK, (sljit_s8)); - - case SLJIT_MOVU_U16: - return EMIT_MOV(SLJIT_MOV_U16, HALF_DATA | WRITE_BACK, (sljit_u16)); - - case SLJIT_MOVU_S16: - return EMIT_MOV(SLJIT_MOV_S16, HALF_DATA | SIGNED_DATA | WRITE_BACK, (sljit_s16)); - case SLJIT_NOT: return emit_op(compiler, SLJIT_NOT, flags, dst, dstw, TMP_REG1, 0, src, srcw); case SLJIT_NEG: - return emit_op(compiler, SLJIT_NEG, flags, dst, dstw, TMP_REG1, 0, src, srcw); + return emit_op(compiler, SLJIT_NEG, flags | (GET_FLAG_TYPE(op_flags) ? ALT_FORM1 : 0), dst, dstw, TMP_REG1, 0, src, srcw); case SLJIT_CLZ: #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) @@ -1457,7 +1242,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { - sljit_s32 flags = GET_FLAGS(op) ? ALT_SET_FLAGS : 0; + sljit_s32 flags = HAS_FLAGS(op) ? ALT_SET_FLAGS : 0; CHECK_ERROR(); CHECK(check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w)); @@ -1465,6 +1250,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) + return SLJIT_SUCCESS; + if ((src1 & SLJIT_IMM) && src1w == 0) src1 = TMP_ZERO; if ((src2 & SLJIT_IMM) && src2w == 0) @@ -1478,45 +1266,46 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile src1w = (sljit_s32)(src1w); if (src2 & SLJIT_IMM) src2w = (sljit_s32)(src2w); - if (GET_FLAGS(op)) + if (HAS_FLAGS(op)) flags |= ALT_SIGN_EXT; } #endif - if (op & SLJIT_SET_O) + if (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW) FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO))); - if (src2 == TMP_REG2) - flags |= ALT_KEEP_CACHE; switch (GET_OPCODE(op)) { case SLJIT_ADD: - if (!GET_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) { + if (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW) + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM1, dst, dstw, src1, src1w, src2, src2w); + + if (!HAS_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) { if (TEST_SL_IMM(src2, src2w)) { compiler->imm = src2w & 0xffff; - return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0); + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0); } if (TEST_SL_IMM(src1, src1w)) { compiler->imm = src1w & 0xffff; - return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0); + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0); } if (TEST_SH_IMM(src2, src2w)) { compiler->imm = (src2w >> 16) & 0xffff; - return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0); + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); } if (TEST_SH_IMM(src1, src1w)) { compiler->imm = (src1w >> 16) & 0xffff; - return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0); + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0); } /* Range between -1 and -32768 is covered above. */ if (TEST_ADD_IMM(src2, src2w)) { compiler->imm = src2w & 0xffffffff; - return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0); + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0); } if (TEST_ADD_IMM(src1, src1w)) { compiler->imm = src1w & 0xffffffff; - return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4, dst, dstw, src2, src2w, TMP_REG2, 0); + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src2, src2w, TMP_REG2, 0); } } - if (!(GET_FLAGS(op) & (SLJIT_SET_E | SLJIT_SET_O))) { + if (HAS_FLAGS(op)) { if (TEST_SL_IMM(src2, src2w)) { compiler->imm = src2w & 0xffff; return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); @@ -1526,75 +1315,75 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0); } } - return emit_op(compiler, SLJIT_ADD, flags, dst, dstw, src1, src1w, src2, src2w); + return emit_op(compiler, SLJIT_ADD, flags | ((GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)) ? ALT_FORM4 : 0), dst, dstw, src1, src1w, src2, src2w); case SLJIT_ADDC: - return emit_op(compiler, SLJIT_ADDC, flags | (!(op & SLJIT_KEEP_FLAGS) ? 0 : ALT_FORM1), dst, dstw, src1, src1w, src2, src2w); + return emit_op(compiler, SLJIT_ADDC, flags, dst, dstw, src1, src1w, src2, src2w); case SLJIT_SUB: - if (!GET_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) { + if (GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_LESS_EQUAL) { + if (dst == SLJIT_UNUSED) { + if (TEST_UL_IMM(src2, src2w)) { + compiler->imm = src2w & 0xffff; + return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0); + } + return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1, dst, dstw, src1, src1w, src2, src2w); + } + + if ((src2 & SLJIT_IMM) && src2w >= 0 && src2w <= (SIMM_MAX + 1)) { + compiler->imm = src2w; + return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); + } + return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM3, dst, dstw, src1, src1w, src2, src2w); + } + + if (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW) + return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2, dst, dstw, src1, src1w, src2, src2w); + + if (!HAS_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) { if (TEST_SL_IMM(src2, -src2w)) { compiler->imm = (-src2w) & 0xffff; - return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0); + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0); } if (TEST_SL_IMM(src1, src1w)) { compiler->imm = src1w & 0xffff; - return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0); + return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0); } if (TEST_SH_IMM(src2, -src2w)) { compiler->imm = ((-src2w) >> 16) & 0xffff; - return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0); + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); } /* Range between -1 and -32768 is covered above. */ if (TEST_ADD_IMM(src2, -src2w)) { compiler->imm = -src2w & 0xffffffff; - return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0); + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0); } } - if (dst == SLJIT_UNUSED && (op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S)) && !(op & (SLJIT_SET_O | SLJIT_SET_C))) { - if (!(op & SLJIT_SET_U)) { - /* We know ALT_SIGN_EXT is set if it is an SLJIT_I32_OP on 64 bit systems. */ - if (TEST_SL_IMM(src2, src2w)) { - compiler->imm = src2w & 0xffff; - return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0); - } - if (GET_FLAGS(op) == SLJIT_SET_E && TEST_SL_IMM(src1, src1w)) { - compiler->imm = src1w & 0xffff; - return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0); - } + + if (dst == SLJIT_UNUSED && GET_FLAG_TYPE(op) != GET_FLAG_TYPE(SLJIT_SET_CARRY)) { + if (TEST_SL_IMM(src2, src2w)) { + compiler->imm = src2w & 0xffff; + return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM4 | ALT_FORM5, dst, dstw, src1, src1w, TMP_REG2, 0); } - if (!(op & (SLJIT_SET_E | SLJIT_SET_S))) { - /* We know ALT_SIGN_EXT is set if it is an SLJIT_I32_OP on 64 bit systems. */ - if (TEST_UL_IMM(src2, src2w)) { - compiler->imm = src2w & 0xffff; - return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); - } - return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM4, dst, dstw, src1, src1w, src2, src2w); - } - if ((src2 & SLJIT_IMM) && src2w >= 0 && src2w <= 0x7fff) { - compiler->imm = src2w; - return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); - } - return emit_op(compiler, SLJIT_SUB, flags | ((op & SLJIT_SET_U) ? ALT_FORM4 : 0) | ((op & (SLJIT_SET_E | SLJIT_SET_S)) ? ALT_FORM5 : 0), dst, dstw, src1, src1w, src2, src2w); + return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM4, dst, dstw, src1, src1w, src2, src2w); } - if (!(op & (SLJIT_SET_E | SLJIT_SET_U | SLJIT_SET_S | SLJIT_SET_O))) { - if (TEST_SL_IMM(src2, -src2w)) { - compiler->imm = (-src2w) & 0xffff; - return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); - } + + if (TEST_SL_IMM(src2, -src2w)) { + compiler->imm = (-src2w) & 0xffff; + return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); } /* We know ALT_SIGN_EXT is set if it is an SLJIT_I32_OP on 64 bit systems. */ - return emit_op(compiler, SLJIT_SUB, flags | (!(op & SLJIT_SET_U) ? 0 : ALT_FORM6), dst, dstw, src1, src1w, src2, src2w); + return emit_op(compiler, SLJIT_SUB, flags | ((GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)) ? ALT_FORM5 : 0), dst, dstw, src1, src1w, src2, src2w); case SLJIT_SUBC: - return emit_op(compiler, SLJIT_SUBC, flags | (!(op & SLJIT_KEEP_FLAGS) ? 0 : ALT_FORM1), dst, dstw, src1, src1w, src2, src2w); + return emit_op(compiler, SLJIT_SUBC, flags, dst, dstw, src1, src1w, src2, src2w); case SLJIT_MUL: #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) if (op & SLJIT_I32_OP) flags |= ALT_FORM2; #endif - if (!GET_FLAGS(op)) { + if (!HAS_FLAGS(op)) { if (TEST_SL_IMM(src2, src2w)) { compiler->imm = src2w & 0xffff; return emit_op(compiler, SLJIT_MUL, flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0); @@ -1604,13 +1393,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile return emit_op(compiler, SLJIT_MUL, flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0); } } + else + FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO))); return emit_op(compiler, SLJIT_MUL, flags, dst, dstw, src1, src1w, src2, src2w); case SLJIT_AND: case SLJIT_OR: case SLJIT_XOR: /* Commutative unsigned operations. */ - if (!GET_FLAGS(op) || GET_OPCODE(op) == SLJIT_AND) { + if (!HAS_FLAGS(op) || GET_OPCODE(op) == SLJIT_AND) { if (TEST_UL_IMM(src2, src2w)) { compiler->imm = src2w; return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0); @@ -1628,7 +1419,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0); } } - if (!GET_FLAGS(op) && GET_OPCODE(op) != SLJIT_AND) { + if (GET_OPCODE(op) != SLJIT_AND && GET_OPCODE(op) != SLJIT_AND) { + /* Unlike or and xor, and resets unwanted bits as well. */ if (TEST_UI_IMM(src2, src2w)) { compiler->imm = src2w; return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); @@ -1640,12 +1432,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile } return emit_op(compiler, GET_OPCODE(op), flags, dst, dstw, src1, src1w, src2, src2w); - case SLJIT_ASHR: - if (op & SLJIT_KEEP_FLAGS) - flags |= ALT_FORM3; - /* Fall through. */ case SLJIT_SHL: case SLJIT_LSHR: + case SLJIT_ASHR: #if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) if (op & SLJIT_I32_OP) flags |= ALT_FORM2; @@ -1669,7 +1458,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) { CHECK_REG_INDEX(check_sljit_get_float_register_index(reg)); - return reg; + return freg_map[reg]; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, @@ -1685,16 +1474,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c /* Floating point operators */ /* --------------------------------------------------------------------- */ -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void) -{ -#ifdef SLJIT_IS_FPU_AVAILABLE - return SLJIT_IS_FPU_AVAILABLE; -#else - /* Available by default. */ - return 1; -#endif -} - #define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_F32_OP) >> 6)) #define SELECT_FOP(op, single, double) ((op & SLJIT_F32_OP) ? single : double) @@ -1719,7 +1498,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp { if (src & SLJIT_MEM) { /* We can ignore the temporary data store on the stack from caching point of view. */ - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw)); + FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, TMP_REG1)); src = TMP_FREG1; } @@ -1727,28 +1506,21 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp op = GET_OPCODE(op); FAIL_IF(push_inst(compiler, (op == SLJIT_CONV_S32_FROM_F64 ? FCTIWZ : FCTIDZ) | FD(TMP_FREG1) | FB(src))); - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; - if (op == SLJIT_CONV_SW_FROM_F64) { if (FAST_IS_REG(dst)) { - FAIL_IF(emit_op_mem2(compiler, DOUBLE_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, 0, 0)); - return emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, 0, 0); + FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1)); + return emit_op_mem(compiler, WORD_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1); } - return emit_op_mem2(compiler, DOUBLE_DATA, TMP_FREG1, dst, dstw, 0, 0); + return emit_op_mem(compiler, DOUBLE_DATA, TMP_FREG1, dst, dstw, TMP_REG1); } - #else FAIL_IF(push_inst(compiler, FCTIWZ | FD(TMP_FREG1) | FB(src))); - - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; #endif if (FAST_IS_REG(dst)) { FAIL_IF(load_immediate(compiler, TMP_REG1, FLOAT_TMP_MEM_OFFSET)); FAIL_IF(push_inst(compiler, STFIWX | FS(TMP_FREG1) | A(SLJIT_SP) | B(TMP_REG1))); - return emit_op_mem2(compiler, INT_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, 0, 0); + return emit_op_mem(compiler, INT_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1); } SLJIT_ASSERT(dst & SLJIT_MEM); @@ -1799,21 +1571,21 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp if (FAST_IS_REG(src)) FAIL_IF(push_inst(compiler, EXTSW | S(src) | A(TMP_REG1))); else - FAIL_IF(emit_op_mem2(compiler, INT_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET)); + FAIL_IF(emit_op_mem(compiler, INT_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1)); src = TMP_REG1; } if (FAST_IS_REG(src)) { - FAIL_IF(emit_op_mem2(compiler, WORD_DATA, src, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET)); - FAIL_IF(emit_op_mem2(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, dst, dstw)); + FAIL_IF(emit_op_mem(compiler, WORD_DATA, src, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1)); + FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1)); } else - FAIL_IF(emit_op_mem2(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw)); + FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, TMP_REG1)); FAIL_IF(push_inst(compiler, FCFID | FD(dst_r) | FB(TMP_FREG1))); if (dst & SLJIT_MEM) - return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0); + return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1); if (op & SLJIT_F32_OP) return push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r)); return SLJIT_SUCCESS; @@ -1829,7 +1601,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp invert_sign = 0; } else if (!FAST_IS_REG(src)) { - FAIL_IF(emit_op_mem2(compiler, WORD_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW)); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1)); src = TMP_REG1; } @@ -1841,17 +1613,17 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG2) | A(0) | 0x4330)); if (invert_sign) FAIL_IF(push_inst(compiler, XORIS | S(src) | A(TMP_REG1) | 0x8000)); - FAIL_IF(emit_op_mem2(compiler, WORD_DATA, TMP_REG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_HI, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET)); - FAIL_IF(emit_op_mem2(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_HI)); + FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_HI, TMP_REG1)); + FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, TMP_REG2)); FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG1) | A(0) | 0x8000)); - FAIL_IF(emit_op_mem2(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW)); - FAIL_IF(emit_op_mem2(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET)); - FAIL_IF(emit_op_mem2(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW)); + FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1)); + FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, TMP_REG2)); + FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1)); FAIL_IF(push_inst(compiler, FSUB | FD(dst_r) | FA(TMP_FREG1) | FB(TMP_FREG2))); if (dst & SLJIT_MEM) - return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0); + return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1); if (op & SLJIT_F32_OP) return push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r)); return SLJIT_SUCCESS; @@ -1864,12 +1636,12 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile sljit_s32 src2, sljit_sw src2w) { if (src1 & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); + FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, TMP_REG1)); src1 = TMP_FREG1; } if (src2 & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0)); + FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, TMP_REG2)); src2 = TMP_FREG2; } @@ -1883,8 +1655,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil sljit_s32 dst_r; CHECK_ERROR(); - compiler->cache_arg = 0; - compiler->cache_argw = 0; SLJIT_COMPILE_ASSERT((SLJIT_F32_OP == 0x100) && !(DOUBLE_DATA & 0x4), float_transfer_bit_error); SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw); @@ -1895,7 +1665,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; if (src & SLJIT_MEM) { - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw)); + FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, TMP_REG1)); src = dst_r; } @@ -1924,7 +1694,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil } if (dst & SLJIT_MEM) - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), dst_r, dst, dstw, 0, 0)); + FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op), dst_r, dst, dstw, TMP_REG1)); return SLJIT_SUCCESS; } @@ -1933,7 +1703,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { - sljit_s32 dst_r, flags = 0; + sljit_s32 dst_r; CHECK_ERROR(); CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w)); @@ -1941,46 +1711,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); - compiler->cache_arg = 0; - compiler->cache_argw = 0; - dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2; if (src1 & SLJIT_MEM) { - if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) { - FAIL_IF(compiler->error); - src1 = TMP_FREG1; - } else - flags |= ALT_FORM1; + FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, TMP_REG1)); + src1 = TMP_FREG1; } if (src2 & SLJIT_MEM) { - if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) { - FAIL_IF(compiler->error); - src2 = TMP_FREG2; - } else - flags |= ALT_FORM2; - } - - if ((flags & (ALT_FORM1 | ALT_FORM2)) == (ALT_FORM1 | ALT_FORM2)) { - if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, src1, src1w)); - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw)); - } - else { - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw)); - } - } - else if (flags & ALT_FORM1) - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw)); - else if (flags & ALT_FORM2) - FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw)); - - if (flags & ALT_FORM1) - src1 = TMP_FREG1; - if (flags & ALT_FORM2) + FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, TMP_REG2)); src2 = TMP_FREG2; + } switch (GET_OPCODE(op)) { case SLJIT_ADD_F64: @@ -2000,13 +1741,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil break; } - if (dst_r == TMP_FREG2) - FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, 0, 0)); + if (dst & SLJIT_MEM) + FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, TMP_REG1)); return SLJIT_SUCCESS; } -#undef FLOAT_DATA #undef SELECT_FOP /* --------------------------------------------------------------------- */ @@ -2019,10 +1759,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); ADJUST_LOCAL_OFFSET(dst, dstw); - /* For UNUSED dst. Uncommon, but possible. */ - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; - if (FAST_IS_REG(dst)) return push_inst(compiler, MFLR | D(dst)); @@ -2040,12 +1776,10 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler if (FAST_IS_REG(src)) FAIL_IF(push_inst(compiler, MTLR | S(src))); else { - if (src & SLJIT_MEM) - FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); - else if (src & SLJIT_IMM) - FAIL_IF(load_immediate(compiler, TMP_REG2, srcw)); + FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); FAIL_IF(push_inst(compiler, MTLR | S(TMP_REG2))); } + return push_inst(compiler, BLR); } @@ -2079,33 +1813,33 @@ static sljit_ins get_bo_bi_flags(sljit_s32 type) return (4 << 21) | (2 << 16); case SLJIT_LESS: - case SLJIT_LESS_F64: - return (12 << 21) | ((4 + 0) << 16); - - case SLJIT_GREATER_EQUAL: - case SLJIT_GREATER_EQUAL_F64: - return (4 << 21) | ((4 + 0) << 16); - - case SLJIT_GREATER: - case SLJIT_GREATER_F64: - return (12 << 21) | ((4 + 1) << 16); - - case SLJIT_LESS_EQUAL: - case SLJIT_LESS_EQUAL_F64: - return (4 << 21) | ((4 + 1) << 16); - case SLJIT_SIG_LESS: return (12 << 21) | (0 << 16); + case SLJIT_GREATER_EQUAL: case SLJIT_SIG_GREATER_EQUAL: return (4 << 21) | (0 << 16); + case SLJIT_GREATER: case SLJIT_SIG_GREATER: return (12 << 21) | (1 << 16); + case SLJIT_LESS_EQUAL: case SLJIT_SIG_LESS_EQUAL: return (4 << 21) | (1 << 16); + case SLJIT_LESS_F64: + return (12 << 21) | ((4 + 0) << 16); + + case SLJIT_GREATER_EQUAL_F64: + return (4 << 21) | ((4 + 0) << 16); + + case SLJIT_GREATER_F64: + return (12 << 21) | ((4 + 1) << 16); + + case SLJIT_LESS_EQUAL_F64: + return (4 << 21) | ((4 + 1) << 16); + case SLJIT_OVERFLOW: case SLJIT_MUL_OVERFLOW: return (12 << 21) | (3 << 16); @@ -2127,7 +1861,7 @@ static sljit_ins get_bo_bi_flags(sljit_s32 type) return (4 << 21) | ((4 + 3) << 16); default: - SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL3); + SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_CDECL); return (20 << 21); } } @@ -2153,7 +1887,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile if (type < SLJIT_JUMP) jump->flags |= IS_COND; #if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL) - if (type >= SLJIT_CALL0) + if (type >= SLJIT_CALL) jump->flags |= IS_CALL; #endif @@ -2164,6 +1898,24 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile return jump; } +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL)); +#endif + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_jump(compiler, type); +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) { struct sljit_jump *jump = NULL; @@ -2175,7 +1927,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi if (FAST_IS_REG(src)) { #if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL) - if (type >= SLJIT_CALL0) { + if (type >= SLJIT_CALL) { FAIL_IF(push_inst(compiler, OR | S(src) | A(TMP_CALL_REG) | B(src))); src_r = TMP_CALL_REG; } @@ -2185,12 +1937,13 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi src_r = src; #endif } else if (src & SLJIT_IMM) { + /* These jumps are converted to jump/call instructions when possible. */ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); FAIL_IF(!jump); set_jump(jump, compiler, JUMP_ADDR); jump->u.target = srcw; #if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL) - if (type >= SLJIT_CALL0) + if (type >= SLJIT_CALL) jump->flags |= IS_CALL; #endif FAIL_IF(emit_const(compiler, TMP_CALL_REG, 0)); @@ -2207,153 +1960,302 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi return push_inst(compiler, BCCTR | (20 << 21) | (type >= SLJIT_FAST_CALL ? 1 : 0)); } -/* Get a bit from CR, all other bits are zeroed. */ -#define GET_CR_BIT(bit, dst) \ - FAIL_IF(push_inst(compiler, MFCR | D(dst))); \ - FAIL_IF(push_inst(compiler, RLWINM | S(dst) | A(dst) | ((1 + (bit)) << 11) | (31 << 6) | (31 << 1))); +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); -#define INVERT_BIT(dst) \ - FAIL_IF(push_inst(compiler, XORI | S(dst) | A(dst) | 0x1)); +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_CALL_REG, 0, TMP_REG1, 0, src, srcw)); + src = TMP_CALL_REG; + } + + FAIL_IF(call_with_args(compiler, arg_types, &src)); +#endif + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_ijump(compiler, type, src, srcw); +} SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw, sljit_s32 type) { - sljit_s32 reg, input_flags; - sljit_s32 flags = GET_ALL_FLAGS(op); - sljit_sw original_dstw = dstw; + sljit_s32 reg, input_flags, cr_bit, invert; + sljit_s32 saved_op = op; + sljit_sw saved_dstw = dstw; CHECK_ERROR(); - CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type)); + CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); ADJUST_LOCAL_OFFSET(dst, dstw); - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + input_flags = (op & SLJIT_I32_OP) ? INT_DATA : WORD_DATA; +#else + input_flags = WORD_DATA; +#endif op = GET_OPCODE(op); reg = (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); -#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - input_flags = (flags & SLJIT_I32_OP) ? INT_DATA : WORD_DATA; -#else - input_flags = WORD_DATA; -#endif - FAIL_IF(emit_op_mem2(compiler, input_flags | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw)); - src = TMP_REG1; - srcw = 0; - } + if (op >= SLJIT_ADD && (dst & SLJIT_MEM)) + FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, TMP_REG1, dst, dstw, TMP_REG1)); + + invert = 0; + cr_bit = 0; switch (type & 0xff) { - case SLJIT_EQUAL: - GET_CR_BIT(2, reg); - break; - - case SLJIT_NOT_EQUAL: - GET_CR_BIT(2, reg); - INVERT_BIT(reg); - break; - case SLJIT_LESS: - case SLJIT_LESS_F64: - GET_CR_BIT(4 + 0, reg); + case SLJIT_SIG_LESS: break; case SLJIT_GREATER_EQUAL: - case SLJIT_GREATER_EQUAL_F64: - GET_CR_BIT(4 + 0, reg); - INVERT_BIT(reg); + case SLJIT_SIG_GREATER_EQUAL: + invert = 1; break; case SLJIT_GREATER: - case SLJIT_GREATER_F64: - GET_CR_BIT(4 + 1, reg); + case SLJIT_SIG_GREATER: + cr_bit = 1; break; case SLJIT_LESS_EQUAL: - case SLJIT_LESS_EQUAL_F64: - GET_CR_BIT(4 + 1, reg); - INVERT_BIT(reg); - break; - - case SLJIT_SIG_LESS: - GET_CR_BIT(0, reg); - break; - - case SLJIT_SIG_GREATER_EQUAL: - GET_CR_BIT(0, reg); - INVERT_BIT(reg); - break; - - case SLJIT_SIG_GREATER: - GET_CR_BIT(1, reg); - break; - case SLJIT_SIG_LESS_EQUAL: - GET_CR_BIT(1, reg); - INVERT_BIT(reg); + cr_bit = 1; + invert = 1; + break; + + case SLJIT_EQUAL: + cr_bit = 2; + break; + + case SLJIT_NOT_EQUAL: + cr_bit = 2; + invert = 1; break; case SLJIT_OVERFLOW: case SLJIT_MUL_OVERFLOW: - GET_CR_BIT(3, reg); + cr_bit = 3; break; case SLJIT_NOT_OVERFLOW: case SLJIT_MUL_NOT_OVERFLOW: - GET_CR_BIT(3, reg); - INVERT_BIT(reg); + cr_bit = 3; + invert = 1; + break; + + case SLJIT_LESS_F64: + cr_bit = 4 + 0; + break; + + case SLJIT_GREATER_EQUAL_F64: + cr_bit = 4 + 0; + invert = 1; + break; + + case SLJIT_GREATER_F64: + cr_bit = 4 + 1; + break; + + case SLJIT_LESS_EQUAL_F64: + cr_bit = 4 + 1; + invert = 1; break; case SLJIT_EQUAL_F64: - GET_CR_BIT(4 + 2, reg); + cr_bit = 4 + 2; break; case SLJIT_NOT_EQUAL_F64: - GET_CR_BIT(4 + 2, reg); - INVERT_BIT(reg); + cr_bit = 4 + 2; + invert = 1; break; case SLJIT_UNORDERED_F64: - GET_CR_BIT(4 + 3, reg); + cr_bit = 4 + 3; break; case SLJIT_ORDERED_F64: - GET_CR_BIT(4 + 3, reg); - INVERT_BIT(reg); + cr_bit = 4 + 3; + invert = 1; break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); break; } + FAIL_IF(push_inst(compiler, MFCR | D(reg))); + FAIL_IF(push_inst(compiler, RLWINM | S(reg) | A(reg) | ((1 + (cr_bit)) << 11) | (31 << 6) | (31 << 1))); + + if (invert) + FAIL_IF(push_inst(compiler, XORI | S(reg) | A(reg) | 0x1)); + if (op < SLJIT_ADD) { -#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) - if (op == SLJIT_MOV) - input_flags = WORD_DATA; - else { - op = SLJIT_MOV_U32; - input_flags = INT_DATA; - } -#else - op = SLJIT_MOV; - input_flags = WORD_DATA; -#endif - if (reg != TMP_REG2) + if (!(dst & SLJIT_MEM)) return SLJIT_SUCCESS; - return emit_op(compiler, op, input_flags, dst, dstw, TMP_REG1, 0, TMP_REG2, 0); + return emit_op_mem(compiler, input_flags, reg, dst, dstw, TMP_REG1); } #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) compiler->skip_checks = 1; #endif - return sljit_emit_op2(compiler, op | flags, dst, original_dstw, src, srcw, TMP_REG2, 0); + if (dst & SLJIT_MEM) + return sljit_emit_op2(compiler, saved_op, dst, saved_dstw, TMP_REG1, 0, TMP_REG2, 0); + return sljit_emit_op2(compiler, saved_op, dst, 0, dst, 0, TMP_REG2, 0); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); + + return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 reg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_s32 mem_flags; + sljit_ins inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw)); + + if (type & SLJIT_MEM_POST) + return SLJIT_ERR_UNSUPPORTED; + + switch (type & 0xff) { + case SLJIT_MOV: + case SLJIT_MOV_P: +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + case SLJIT_MOV_U32: + case SLJIT_MOV_S32: +#endif + mem_flags = WORD_DATA; + break; + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + case SLJIT_MOV_U32: + mem_flags = INT_DATA; + break; + + case SLJIT_MOV_S32: + mem_flags = INT_DATA; + + if (!(type & SLJIT_MEM_STORE) && !(type & SLJIT_I32_OP)) { + if (mem & OFFS_REG_MASK) + mem_flags |= SIGNED_DATA; + else + return SLJIT_ERR_UNSUPPORTED; + } + break; +#endif + + case SLJIT_MOV_U8: + case SLJIT_MOV_S8: + mem_flags = BYTE_DATA; + break; + + case SLJIT_MOV_U16: + mem_flags = HALF_DATA; + break; + + case SLJIT_MOV_S16: + mem_flags = HALF_DATA | SIGNED_DATA; + break; + + default: + SLJIT_UNREACHABLE(); + mem_flags = WORD_DATA; + break; + } + + if (!(type & SLJIT_MEM_STORE)) + mem_flags |= LOAD_DATA; + + if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) { + if (memw != 0) + return SLJIT_ERR_UNSUPPORTED; + + if (type & SLJIT_MEM_SUPP) + return SLJIT_SUCCESS; + + inst = updated_data_transfer_insts[mem_flags | INDEXED]; + FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, 0, reg) | A(mem & REG_MASK) | B(OFFS_REG(mem)))); + } + else { + if (memw > SIMM_MAX || memw < SIMM_MIN) + return SLJIT_ERR_UNSUPPORTED; + + inst = updated_data_transfer_insts[mem_flags]; + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if ((inst & INT_ALIGNED) && (memw & 0x3) != 0) + return SLJIT_ERR_UNSUPPORTED; +#endif + + if (type & SLJIT_MEM_SUPP) + return SLJIT_SUCCESS; + + FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, 0, reg) | A(mem & REG_MASK) | IMM(memw))); + } + + if ((mem_flags & LOAD_DATA) && (type & 0xff) == SLJIT_MOV_S8) + return push_inst(compiler, EXTSB | S(reg) | A(reg)); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 freg, + sljit_s32 mem, sljit_sw memw) +{ + sljit_s32 mem_flags; + sljit_ins inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw)); + + if (type & SLJIT_MEM_POST) + return SLJIT_ERR_UNSUPPORTED; + + if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) { + if (memw != 0) + return SLJIT_ERR_UNSUPPORTED; + } + else { + if (memw > SIMM_MAX || memw < SIMM_MIN) + return SLJIT_ERR_UNSUPPORTED; + } + + if (type & SLJIT_MEM_SUPP) + return SLJIT_SUCCESS; + + mem_flags = FLOAT_DATA(type); + + if (!(type & SLJIT_MEM_STORE)) + mem_flags |= LOAD_DATA; + + if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) { + inst = updated_data_transfer_insts[mem_flags | INDEXED]; + return push_inst(compiler, INST_CODE_AND_DST(inst, DOUBLE_DATA, freg) | A(mem & REG_MASK) | B(OFFS_REG(mem))); + } + + inst = updated_data_transfer_insts[mem_flags]; + return push_inst(compiler, INST_CODE_AND_DST(inst, DOUBLE_DATA, freg) | A(mem & REG_MASK) | IMM(memw)); } SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) @@ -2369,7 +2271,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi PTR_FAIL_IF(!const_); set_const(const_, compiler); - reg = SLOW_IS_REG(dst) ? dst : TMP_REG2; + reg = FAST_IS_REG(dst) ? dst : TMP_REG2; PTR_FAIL_IF(emit_const(compiler, reg, init_value)); diff --git a/pcre2-10.22/src/sljit/sljitNativeSPARC_32.c b/pcre2-10.32/src/sljit/sljitNativeSPARC_32.c similarity index 62% rename from pcre2-10.22/src/sljit/sljitNativeSPARC_32.c rename to pcre2-10.32/src/sljit/sljitNativeSPARC_32.c index 7e589a17c..0671b130c 100644 --- a/pcre2-10.22/src/sljit/sljitNativeSPARC_32.c +++ b/pcre2-10.32/src/sljit/sljitNativeSPARC_32.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -60,7 +60,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl return push_inst(compiler, SRA | D(dst) | S1(dst) | IMM(24), DR(dst)); } else if (dst != src2) - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_SUCCESS; case SLJIT_MOV_U16: @@ -71,7 +71,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl return push_inst(compiler, (op == SLJIT_MOV_S16 ? SRA : SRL) | D(dst) | S1(dst) | IMM(16), DR(dst)); } else if (dst != src2) - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_SUCCESS; case SLJIT_NOT: @@ -80,18 +80,17 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl case SLJIT_CLZ: SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); - /* sparc 32 does not support SLJIT_KEEP_FLAGS. Not sure I can fix this. */ FAIL_IF(push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(src2) | S2(0), SET_FLAGS)); FAIL_IF(push_inst(compiler, OR | D(TMP_REG1) | S1(0) | S2(src2), DR(TMP_REG1))); FAIL_IF(push_inst(compiler, BICC | DA(0x1) | (7 & DISP_MASK), UNMOVABLE_INS)); - FAIL_IF(push_inst(compiler, OR | (flags & SET_FLAGS) | D(dst) | S1(0) | IMM(32), UNMOVABLE_INS | (flags & SET_FLAGS))); + FAIL_IF(push_inst(compiler, OR | D(dst) | S1(0) | IMM(32), UNMOVABLE_INS)); FAIL_IF(push_inst(compiler, OR | D(dst) | S1(0) | IMM(-1), DR(dst))); /* Loop. */ FAIL_IF(push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(TMP_REG1) | S2(0), SET_FLAGS)); FAIL_IF(push_inst(compiler, SLL | D(TMP_REG1) | S1(TMP_REG1) | IMM(1), DR(TMP_REG1))); FAIL_IF(push_inst(compiler, BICC | DA(0xe) | (-2 & DISP_MASK), UNMOVABLE_INS)); - return push_inst(compiler, ADD | (flags & SET_FLAGS) | D(dst) | S1(dst) | IMM(1), UNMOVABLE_INS | (flags & SET_FLAGS)); + return push_inst(compiler, ADD | D(dst) | S1(dst) | IMM(1), UNMOVABLE_INS); case SLJIT_ADD: return push_inst(compiler, ADD | (flags & SET_FLAGS) | D(dst) | S1(src1) | ARG2(flags, src2), DR(dst) | (flags & SET_FLAGS)); @@ -135,7 +134,126 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl return !(flags & SET_FLAGS) ? SLJIT_SUCCESS : push_inst(compiler, SUB | SET_FLAGS | D(0) | S1(dst) | S2(0), SET_FLAGS); } - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); + return SLJIT_SUCCESS; +} + +static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src) +{ + sljit_s32 reg_index = 8; + sljit_s32 word_reg_index = 8; + sljit_s32 float_arg_index = 1; + sljit_s32 double_arg_count = 0; + sljit_s32 float_offset = (16 + 6) * sizeof(sljit_sw); + sljit_s32 types = 0; + sljit_s32 reg = 0; + sljit_s32 move_to_tmp2 = 0; + + if (src) + reg = reg_map[*src & REG_MASK]; + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK); + + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + float_arg_index++; + if (reg_index == reg) + move_to_tmp2 = 1; + reg_index++; + break; + case SLJIT_ARG_TYPE_F64: + float_arg_index++; + double_arg_count++; + if (reg_index == reg || reg_index + 1 == reg) + move_to_tmp2 = 1; + reg_index += 2; + break; + default: + if (reg_index != word_reg_index && reg_index < 14 && reg_index == reg) + move_to_tmp2 = 1; + reg_index++; + word_reg_index++; + break; + } + + if (move_to_tmp2) { + move_to_tmp2 = 0; + if (reg < 14) + FAIL_IF(push_inst(compiler, OR | D(TMP_REG1) | S1(0) | S2A(reg), DR(TMP_REG1))); + *src = TMP_REG1; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + arg_types = types; + + while (arg_types) { + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + float_arg_index--; + FAIL_IF(push_inst(compiler, STF | FD(float_arg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS)); + float_offset -= sizeof(sljit_f64); + break; + case SLJIT_ARG_TYPE_F64: + float_arg_index--; + if (float_arg_index == 4 && double_arg_count == 4) { + FAIL_IF(push_inst(compiler, STF | FD(float_arg_index) | S1(SLJIT_SP) | IMM((16 + 7) * sizeof(sljit_sw)), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, STF | FD(float_arg_index) | (1 << 25) | S1(SLJIT_SP) | IMM((16 + 8) * sizeof(sljit_sw)), MOVABLE_INS)); + } + else + FAIL_IF(push_inst(compiler, STDF | FD(float_arg_index) | S1(SLJIT_SP) | IMM(float_offset), MOVABLE_INS)); + float_offset -= sizeof(sljit_f64); + break; + default: + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + float_offset = (16 + 6) * sizeof(sljit_sw); + + while (types) { + switch (types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + reg_index--; + if (reg_index < 14) + FAIL_IF(push_inst(compiler, LDUW | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), reg_index)); + float_offset -= sizeof(sljit_f64); + break; + case SLJIT_ARG_TYPE_F64: + reg_index -= 2; + if (reg_index < 14) { + if ((reg_index & 0x1) != 0) { + FAIL_IF(push_inst(compiler, LDUW | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), reg_index)); + if (reg_index < 13) + FAIL_IF(push_inst(compiler, LDUW | DA(reg_index + 1) | S1(SLJIT_SP) | IMM(float_offset + sizeof(sljit_sw)), reg_index + 1)); + } + else + FAIL_IF(push_inst(compiler, LDD | DA(reg_index) | S1(SLJIT_SP) | IMM(float_offset), reg_index)); + } + float_offset -= sizeof(sljit_f64); + break; + default: + reg_index--; + word_reg_index--; + + if (reg_index != word_reg_index) { + if (reg_index < 14) + FAIL_IF(push_inst(compiler, OR | DA(reg_index) | S1(0) | S2A(word_reg_index), reg_index)); + else + FAIL_IF(push_inst(compiler, STW | DA(word_reg_index) | S1(SLJIT_SP) | IMM(92), word_reg_index)); + } + break; + } + + types >>= SLJIT_DEF_SHIFT; + } + return SLJIT_SUCCESS; } @@ -145,20 +263,22 @@ static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_ return push_inst(compiler, OR | D(dst) | S1(dst) | IMM_ARG | (init_value & 0x3ff), DR(dst)); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) { - sljit_ins *inst = (sljit_ins*)addr; + sljit_ins *inst = (sljit_ins *)addr; - inst[0] = (inst[0] & 0xffc00000) | ((new_addr >> 10) & 0x3fffff); - inst[1] = (inst[1] & 0xfffffc00) | (new_addr & 0x3ff); + inst[0] = (inst[0] & 0xffc00000) | ((new_target >> 10) & 0x3fffff); + inst[1] = (inst[1] & 0xfffffc00) | (new_target & 0x3ff); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 2); } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) { - sljit_ins *inst = (sljit_ins*)addr; + sljit_ins *inst = (sljit_ins *)addr; inst[0] = (inst[0] & 0xffc00000) | ((new_constant >> 10) & 0x3fffff); inst[1] = (inst[1] & 0xfffffc00) | (new_constant & 0x3ff); + inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset); SLJIT_CACHE_FLUSH(inst, inst + 2); } diff --git a/pcre2-10.22/src/sljit/sljitNativeSPARC_common.c b/pcre2-10.32/src/sljit/sljitNativeSPARC_common.c similarity index 82% rename from pcre2-10.22/src/sljit/sljitNativeSPARC_common.c rename to pcre2-10.32/src/sljit/sljitNativeSPARC_common.c index f3a33a109..669ecd815 100644 --- a/pcre2-10.22/src/sljit/sljitNativeSPARC_common.c +++ b/pcre2-10.32/src/sljit/sljitNativeSPARC_common.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -90,13 +90,19 @@ static void sparc_cache_flush(sljit_ins *from, sljit_ins *to) #define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2) #define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3) #define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4) +/* This register is modified by calls, which affects the instruction + in the delay slot if it is used as a source register. */ #define TMP_LINK (SLJIT_NUMBER_OF_REGISTERS + 5) -#define TMP_FREG1 (0) -#define TMP_FREG2 ((SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) << 1) +#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1) +#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2) static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { - 0, 8, 9, 10, 13, 29, 28, 27, 23, 22, 21, 20, 19, 18, 17, 16, 26, 25, 24, 14, 1, 11, 12, 15 + 0, 8, 9, 10, 11, 29, 28, 27, 23, 22, 21, 20, 19, 18, 17, 16, 26, 25, 24, 14, 1, 12, 13, 15 +}; + +static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = { + 0, 0, 2, 4, 6, 8, 10, 12, 14 }; /* --------------------------------------------------------------------- */ @@ -104,10 +110,15 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { /* --------------------------------------------------------------------- */ #define D(d) (reg_map[d] << 25) +#define FD(d) (freg_map[d] << 25) +#define FDN(d) ((freg_map[d] | 0x1) << 25) #define DA(d) ((d) << 25) #define S1(s1) (reg_map[s1] << 14) -#define S2(s2) (reg_map[s2]) +#define FS1(s1) (freg_map[s1] << 14) #define S1A(s1) ((s1) << 14) +#define S2(s2) (reg_map[s2]) +#define FS2(s2) (freg_map[s2]) +#define FS2N(s2) (freg_map[s2] | 0x1) #define S2A(s2) (s2) #define IMM_ARG 0x2000 #define DOP(op) ((op) << 5) @@ -144,6 +155,8 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { #define FSUBD (OPC1(0x2) | OPC3(0x34) | DOP(0x46)) #define FSUBS (OPC1(0x2) | OPC3(0x34) | DOP(0x45)) #define JMPL (OPC1(0x2) | OPC3(0x38)) +#define LDD (OPC1(0x3) | OPC3(0x03)) +#define LDUW (OPC1(0x3) | OPC3(0x00)) #define NOP (OPC1(0x0) | OPC2(0x04)) #define OR (OPC1(0x2) | OPC3(0x02)) #define ORN (OPC1(0x2) | OPC3(0x06)) @@ -157,6 +170,9 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 6] = { #define SRAX (OPC1(0x2) | OPC3(0x27) | (1 << 12)) #define SRL (OPC1(0x2) | OPC3(0x26)) #define SRLX (OPC1(0x2) | OPC3(0x26) | (1 << 12)) +#define STDF (OPC1(0x3) | OPC3(0x27)) +#define STF (OPC1(0x3) | OPC3(0x24)) +#define STW (OPC1(0x3) | OPC3(0x04)) #define SUB (OPC1(0x2) | OPC3(0x04)) #define SUBC (OPC1(0x2) | OPC3(0x0c)) #define TA (OPC1(0x2) | OPC3(0x3a) | (8 << 25)) @@ -199,7 +215,7 @@ static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins, sljit return SLJIT_SUCCESS; } -static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code) +static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code, sljit_sw executable_offset) { sljit_sw diff; sljit_uw target_addr; @@ -213,7 +229,7 @@ static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_i target_addr = jump->u.target; else { SLJIT_ASSERT(jump->flags & JUMP_LABEL); - target_addr = (sljit_uw)(code + jump->u.label->size); + target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset; } inst = (sljit_ins*)jump->addr; @@ -239,8 +255,9 @@ static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_i if (jump->flags & IS_COND) inst--; + diff = ((sljit_sw)target_addr - (sljit_sw)(inst - 1) - executable_offset) >> 2; + if (jump->flags & IS_MOVABLE) { - diff = ((sljit_sw)target_addr - (sljit_sw)(inst - 1)) >> 2; if (diff <= MAX_DISP && diff >= MIN_DISP) { jump->flags |= PATCH_B; inst--; @@ -257,7 +274,8 @@ static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_i } } - diff = ((sljit_sw)target_addr - (sljit_sw)(inst)) >> 2; + diff += sizeof(sljit_ins); + if (diff <= MAX_DISP && diff >= MIN_DISP) { jump->flags |= PATCH_B; if (jump->flags & IS_COND) @@ -280,6 +298,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil sljit_ins *buf_ptr; sljit_ins *buf_end; sljit_uw word_count; + sljit_sw executable_offset; sljit_uw addr; struct sljit_label *label; @@ -296,9 +315,12 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil code_ptr = code; word_count = 0; + executable_offset = SLJIT_EXEC_OFFSET(code); + label = compiler->labels; jump = compiler->jumps; const_ = compiler->consts; + do { buf_ptr = (sljit_ins*)buf->memory; buf_end = buf_ptr + (buf->used_size >> 2); @@ -310,7 +332,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil /* These structures are ordered by their address. */ if (label && label->size == word_count) { /* Just recording the address. */ - label->addr = (sljit_uw)code_ptr; + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); label->size = code_ptr - code; label = label->next; } @@ -320,7 +342,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil #else jump->addr = (sljit_uw)(code_ptr - 6); #endif - code_ptr = detect_jump_type(jump, code_ptr, code); + code_ptr = detect_jump_type(jump, code_ptr, code, executable_offset); jump = jump->next; } if (const_ && const_->addr == word_count) { @@ -336,7 +358,7 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil } while (buf); if (label && label->size == word_count) { - label->addr = (sljit_uw)code_ptr; + label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); label->size = code_ptr - code; label = label->next; } @@ -350,16 +372,16 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil while (jump) { do { addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; - buf_ptr = (sljit_ins*)jump->addr; + buf_ptr = (sljit_ins *)jump->addr; if (jump->flags & PATCH_CALL) { - addr = (sljit_sw)(addr - jump->addr) >> 2; + addr = (sljit_sw)(addr - (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset)) >> 2; SLJIT_ASSERT((sljit_sw)addr <= 0x1fffffff && (sljit_sw)addr >= -0x20000000); buf_ptr[0] = CALL | (addr & 0x3fffffff); break; } if (jump->flags & PATCH_B) { - addr = (sljit_sw)(addr - jump->addr) >> 2; + addr = (sljit_sw)(addr - (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset)) >> 2; SLJIT_ASSERT((sljit_sw)addr <= MAX_DISP && (sljit_sw)addr >= MIN_DISP); buf_ptr[0] = (buf_ptr[0] & ~DISP_MASK) | (addr & DISP_MASK); break; @@ -378,11 +400,37 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_offset = executable_offset; compiler->executable_size = (code_ptr - code) * sizeof(sljit_ins); + + code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset); + code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset); + SLJIT_CACHE_FLUSH(code, code_ptr); return code; } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) +{ + switch (feature_type) { + case SLJIT_HAS_FPU: +#ifdef SLJIT_IS_FPU_AVAILABLE + return SLJIT_IS_FPU_AVAILABLE; +#else + /* Available by default. */ + return 1; +#endif + +#if (defined SLJIT_CONFIG_SPARC_64 && SLJIT_CONFIG_SPARC_64) + case SLJIT_HAS_CMOV: + return 1; +#endif + + default: + return 0; + } +} + /* --------------------------------------------------------------------- */ /* Entry, exit */ /* --------------------------------------------------------------------- */ @@ -401,18 +449,17 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil #define MEM_MASK 0x1f -#define WRITE_BACK 0x00020 -#define ARG_TEST 0x00040 -#define ALT_KEEP_CACHE 0x00080 -#define CUMULATIVE_OP 0x00100 -#define IMM_OP 0x00200 -#define SRC2_IMM 0x00400 +#define ARG_TEST 0x00020 +#define ALT_KEEP_CACHE 0x00040 +#define CUMULATIVE_OP 0x00080 +#define IMM_OP 0x00100 +#define SRC2_IMM 0x00200 -#define REG_DEST 0x00800 -#define REG2_SOURCE 0x01000 -#define SLOW_SRC1 0x02000 -#define SLOW_SRC2 0x04000 -#define SLOW_DEST 0x08000 +#define REG_DEST 0x00400 +#define REG2_SOURCE 0x00800 +#define SLOW_SRC1 0x01000 +#define SLOW_SRC2 0x02000 +#define SLOW_DEST 0x04000 /* SET_FLAGS (0x10 << 19) also belong here! */ @@ -423,12 +470,12 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil #endif SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { CHECK_ERROR(); - 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); + CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); local_size = (local_size + SLJIT_LOCALS_OFFSET + 7) & ~0x7; compiler->local_size = local_size; @@ -447,12 +494,12 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 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); + CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 7) & ~0x7; return SLJIT_SUCCESS; @@ -514,18 +561,16 @@ static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flag { SLJIT_ASSERT(arg & SLJIT_MEM); - if (!(flags & WRITE_BACK) || !(arg & REG_MASK)) { - if ((!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) - || ((arg & OFFS_REG_MASK) && (argw & 0x3) == 0)) { - /* Works for both absoulte and relative addresses (immediate case). */ - if (SLJIT_UNLIKELY(flags & ARG_TEST)) - return 1; - FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] - | ((flags & MEM_MASK) <= GPR_REG ? D(reg) : DA(reg)) - | S1(arg & REG_MASK) | ((arg & OFFS_REG_MASK) ? S2(OFFS_REG(arg)) : IMM(argw)), - ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? DR(reg) : MOVABLE_INS)); - return -1; - } + if ((!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) + || ((arg & OFFS_REG_MASK) && (argw & 0x3) == 0)) { + /* Works for both absoulte and relative addresses (immediate case). */ + if (SLJIT_UNLIKELY(flags & ARG_TEST)) + return 1; + FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] + | ((flags & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg)) + | S1(arg & REG_MASK) | ((arg & OFFS_REG_MASK) ? S2(OFFS_REG(arg)) : IMM(argw)), + ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? DR(reg) : MOVABLE_INS)); + return -1; } return 0; } @@ -567,7 +612,6 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl base = arg & REG_MASK; if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) { argw &= 0x3; - SLJIT_ASSERT(argw != 0); /* Using the cache. */ if (((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) && (argw == compiler->cache_argw)) @@ -607,14 +651,11 @@ static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sl } } - dest = ((flags & MEM_MASK) <= GPR_REG ? D(reg) : DA(reg)); + dest = ((flags & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg)); delay_slot = ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? DR(reg) : MOVABLE_INS; if (!base) return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(arg2) | IMM(0), delay_slot); - if (!(flags & WRITE_BACK)) - return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(base) | S2(arg2), delay_slot); - FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(base) | S2(arg2), delay_slot)); - return push_inst(compiler, ADD | D(base) | S1(base) | S2(arg2), DR(base)); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | dest | S1(base) | S2(arg2), delay_slot); } static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw) @@ -652,18 +693,16 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 compiler->cache_argw = 0; } - if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) { - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32 && !(src2 & SLJIT_MEM)) - return SLJIT_SUCCESS; + if (dst != SLJIT_UNUSED) { + if (FAST_IS_REG(dst)) { + dst_r = dst; + flags |= REG_DEST; + if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) + sugg_src2_r = dst_r; + } + else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, TMP_REG1, dst, dstw)) + flags |= SLOW_DEST; } - else if (FAST_IS_REG(dst)) { - dst_r = dst; - flags |= REG_DEST; - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) - sugg_src2_r = dst_r; - } - else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, TMP_REG1, dst, dstw)) - flags |= SLOW_DEST; if (flags & IMM_OP) { if ((src2 & SLJIT_IMM) && src2w) { @@ -709,7 +748,7 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 if (FAST_IS_REG(src2)) { src2_r = src2; flags |= REG2_SOURCE; - if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) + if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOV_P) dst_r = src2_r; } else if (src2 & SLJIT_IMM) { @@ -720,7 +759,7 @@ static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s3 } else { src2_r = 0; - if ((op >= SLJIT_MOV && op <= SLJIT_MOVU_S32) && (dst & SLJIT_MEM)) + if ((op >= SLJIT_MOV && op <= SLJIT_MOV_P) && (dst & SLJIT_MEM)) dst_r = 0; } } @@ -812,13 +851,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) { - sljit_s32 flags = GET_FLAGS(op) ? SET_FLAGS : 0; + sljit_s32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0; CHECK_ERROR(); CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw)); ADJUST_LOCAL_OFFSET(dst, dstw); ADJUST_LOCAL_OFFSET(src, srcw); + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) + return SLJIT_SUCCESS; + op = GET_OPCODE(op); switch (op) { case SLJIT_MOV: @@ -843,28 +885,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile case SLJIT_MOV_S16: return emit_op(compiler, SLJIT_MOV_S16, flags | HALF_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw); - case SLJIT_MOVU: - case SLJIT_MOVU_P: - return emit_op(compiler, SLJIT_MOV, flags | WORD_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); - - case SLJIT_MOVU_U32: - return emit_op(compiler, SLJIT_MOV_U32, flags | INT_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); - - case SLJIT_MOVU_S32: - return emit_op(compiler, SLJIT_MOV_S32, flags | INT_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); - - case SLJIT_MOVU_U8: - return emit_op(compiler, SLJIT_MOV_U8, flags | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw); - - case SLJIT_MOVU_S8: - return emit_op(compiler, SLJIT_MOV_S8, flags | BYTE_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw); - - case SLJIT_MOVU_U16: - return emit_op(compiler, SLJIT_MOV_U16, flags | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw); - - case SLJIT_MOVU_S16: - return emit_op(compiler, SLJIT_MOV_S16, flags | HALF_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw); - case SLJIT_NOT: case SLJIT_CLZ: return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw); @@ -881,7 +901,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { - sljit_s32 flags = GET_FLAGS(op) ? SET_FLAGS : 0; + sljit_s32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0; CHECK_ERROR(); CHECK(check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w)); @@ -889,6 +909,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(src1, src1w); ADJUST_LOCAL_OFFSET(src2, src2w); + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) + return SLJIT_SUCCESS; + op = GET_OPCODE(op); switch (op) { case SLJIT_ADD: @@ -910,7 +933,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile if (src2 & SLJIT_IMM) src2w &= 0x1f; #else - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); #endif return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w); } @@ -927,7 +950,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) { CHECK_REG_INDEX(check_sljit_get_float_register_index(reg)); - return reg << 1; + return freg_map[reg]; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, @@ -943,16 +966,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c /* Floating point operators */ /* --------------------------------------------------------------------- */ -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void) -{ -#ifdef SLJIT_IS_FPU_AVAILABLE - return SLJIT_IS_FPU_AVAILABLE; -#else - /* Available by default. */ - return 1; -#endif -} - #define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_F32_OP) >> 7)) #define SELECT_FOP(op, single, double) ((op & SLJIT_F32_OP) ? single : double) #define FLOAT_TMP_MEM_OFFSET (22 * sizeof(sljit_sw)) @@ -965,13 +978,8 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw)); src = TMP_FREG1; } - else - src <<= 1; - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOI, FDTOI) | DA(TMP_FREG1) | S2A(src), MOVABLE_INS)); - - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; + FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOI, FDTOI) | FD(TMP_FREG1) | FS2(src), MOVABLE_INS)); if (FAST_IS_REG(dst)) { FAIL_IF(emit_op_mem2(compiler, SINGLE_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET)); @@ -986,7 +994,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) { - sljit_s32 dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG1; + sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; if (src & SLJIT_IMM) { #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) @@ -1005,7 +1013,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_comp } FAIL_IF(emit_op_mem2(compiler, SINGLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw)); - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FITOS, FITOD) | DA(dst_r) | S2A(TMP_FREG1), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, SELECT_FOP(op, FITOS, FITOD) | FD(dst_r) | FS2(TMP_FREG1), MOVABLE_INS)); if (dst & SLJIT_MEM) return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0); @@ -1020,17 +1028,13 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w)); src1 = TMP_FREG1; } - else - src1 <<= 1; if (src2 & SLJIT_MEM) { FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0)); src2 = TMP_FREG2; } - else - src2 <<= 1; - return push_inst(compiler, SELECT_FOP(op, FCMPS, FCMPD) | S1A(src1) | S2A(src2), FCC_IS_SET | MOVABLE_INS); + return push_inst(compiler, SELECT_FOP(op, FCMPS, FCMPD) | FS1(src1) | FS2(src2), FCC_IS_SET | MOVABLE_INS); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op, @@ -1049,39 +1053,37 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) op ^= SLJIT_F32_OP; - dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG1; + dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1; if (src & SLJIT_MEM) { FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw)); src = dst_r; } - else - src <<= 1; switch (GET_OPCODE(op)) { case SLJIT_MOV_F64: if (src != dst_r) { if (dst_r != TMP_FREG1) { - FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r) | S2A(src), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, FMOVS | FD(dst_r) | FS2(src), MOVABLE_INS)); if (!(op & SLJIT_F32_OP)) - FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r | 1) | S2A(src | 1), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, FMOVS | FDN(dst_r) | FS2N(src), MOVABLE_INS)); } else dst_r = src; } break; case SLJIT_NEG_F64: - FAIL_IF(push_inst(compiler, FNEGS | DA(dst_r) | S2A(src), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, FNEGS | FD(dst_r) | FS2(src), MOVABLE_INS)); if (dst_r != src && !(op & SLJIT_F32_OP)) - FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r | 1) | S2A(src | 1), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, FMOVS | FDN(dst_r) | FS2N(src), MOVABLE_INS)); break; case SLJIT_ABS_F64: - FAIL_IF(push_inst(compiler, FABSS | DA(dst_r) | S2A(src), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, FABSS | FD(dst_r) | FS2(src), MOVABLE_INS)); if (dst_r != src && !(op & SLJIT_F32_OP)) - FAIL_IF(push_inst(compiler, FMOVS | DA(dst_r | 1) | S2A(src | 1), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, FMOVS | FDN(dst_r) | FS2N(src), MOVABLE_INS)); break; case SLJIT_CONV_F64_FROM_F32: - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOD, FDTOS) | DA(dst_r) | S2A(src), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSTOD, FDTOS) | FD(dst_r) | FS2(src), MOVABLE_INS)); op ^= SLJIT_F32_OP; break; } @@ -1107,7 +1109,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil compiler->cache_arg = 0; compiler->cache_argw = 0; - dst_r = FAST_IS_REG(dst) ? (dst << 1) : TMP_FREG2; + dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2; if (src1 & SLJIT_MEM) { if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) { @@ -1116,8 +1118,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil } else flags |= SLOW_SRC1; } - else - src1 <<= 1; if (src2 & SLJIT_MEM) { if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) { @@ -1126,8 +1126,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil } else flags |= SLOW_SRC2; } - else - src2 <<= 1; if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { @@ -1151,19 +1149,19 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compil switch (GET_OPCODE(op)) { case SLJIT_ADD_F64: - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FADDS, FADDD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, SELECT_FOP(op, FADDS, FADDD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS)); break; case SLJIT_SUB_F64: - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSUBS, FSUBD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSUBS, FSUBD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS)); break; case SLJIT_MUL_F64: - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FMULS, FMULD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, SELECT_FOP(op, FMULS, FMULD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS)); break; case SLJIT_DIV_F64: - FAIL_IF(push_inst(compiler, SELECT_FOP(op, FDIVS, FDIVD) | DA(dst_r) | S1A(src1) | S2A(src2), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, SELECT_FOP(op, FDIVS, FDIVD) | FD(dst_r) | FS1(src1) | FS2(src2), MOVABLE_INS)); break; } @@ -1186,10 +1184,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler * CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); ADJUST_LOCAL_OFFSET(dst, dstw); - /* For UNUSED dst. Uncommon, but possible. */ - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; - if (FAST_IS_REG(dst)) return push_inst(compiler, OR | D(dst) | S1(0) | S2(TMP_LINK), DR(dst)); @@ -1205,10 +1199,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler if (FAST_IS_REG(src)) FAIL_IF(push_inst(compiler, OR | D(TMP_LINK) | S1(0) | S2(src), DR(TMP_LINK))); - else if (src & SLJIT_MEM) + else FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_LINK, src, srcw)); - else if (src & SLJIT_IMM) - FAIL_IF(load_immediate(compiler, TMP_LINK, srcw)); FAIL_IF(push_inst(compiler, JMPL | D(0) | S1(TMP_LINK) | IMM(8), UNMOVABLE_INS)); return push_inst(compiler, NOP, UNMOVABLE_INS); @@ -1285,7 +1277,7 @@ static sljit_ins get_cc(sljit_s32 type) return DA(0xf); default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return DA(0x8); } } @@ -1321,21 +1313,38 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile #else #error "Implementation required" #endif - } else { + } + else { if ((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS) jump->flags |= IS_MOVABLE; if (type >= SLJIT_FAST_CALL) jump->flags |= IS_CALL; } - PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0)); - PTR_FAIL_IF(push_inst(compiler, JMPL | D(type >= SLJIT_FAST_CALL ? TMP_LINK : 0) | S1(TMP_REG2) | IMM(0), UNMOVABLE_INS)); + PTR_FAIL_IF(emit_const(compiler, TMP_REG1, 0)); + PTR_FAIL_IF(push_inst(compiler, JMPL | D(type >= SLJIT_FAST_CALL ? TMP_LINK : 0) | S1(TMP_REG1) | IMM(0), UNMOVABLE_INS)); jump->addr = compiler->size; PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); return jump; } +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); + + PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_jump(compiler, type); +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw) { struct sljit_jump *jump = NULL; @@ -1352,17 +1361,18 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi FAIL_IF(!jump); set_jump(jump, compiler, JUMP_ADDR); jump->u.target = srcw; + if ((compiler->delay_slot & DST_INS_MASK) != UNMOVABLE_INS) jump->flags |= IS_MOVABLE; if (type >= SLJIT_FAST_CALL) jump->flags |= IS_CALL; - FAIL_IF(emit_const(compiler, TMP_REG2, 0)); - src_r = TMP_REG2; + FAIL_IF(emit_const(compiler, TMP_REG1, 0)); + src_r = TMP_REG1; } else { - FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw)); - src_r = TMP_REG2; + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw)); + src_r = TMP_REG1; } FAIL_IF(push_inst(compiler, JMPL | D(type >= SLJIT_FAST_CALL ? TMP_LINK : 0) | S1(src_r) | IMM(0), UNMOVABLE_INS)); @@ -1371,32 +1381,48 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi return push_inst(compiler, NOP, UNMOVABLE_INS); } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw)); + src = TMP_REG1; + } + + FAIL_IF(call_with_args(compiler, arg_types, &src)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_ijump(compiler, type, src, srcw); +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw, sljit_s32 type) { - sljit_s32 reg, flags = (GET_FLAGS(op) ? SET_FLAGS : 0); + sljit_s32 reg, flags = HAS_FLAGS(op) ? SET_FLAGS : 0; CHECK_ERROR(); - CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type)); + CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); ADJUST_LOCAL_OFFSET(dst, dstw); - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; - #if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) op = GET_OPCODE(op); reg = (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, src, srcw, dst, dstw)); - src = TMP_REG1; - srcw = 0; - } + + if (op >= SLJIT_ADD && (dst & SLJIT_MEM)) + FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, dst, dstw, dst, dstw)); type &= 0xff; if (type < SLJIT_EQUAL_F64) @@ -1407,10 +1433,31 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co FAIL_IF(push_inst(compiler, OR | D(reg) | S1(0) | IMM(1), UNMOVABLE_INS)); FAIL_IF(push_inst(compiler, OR | D(reg) | S1(0) | IMM(0), UNMOVABLE_INS)); - if (op >= SLJIT_ADD) - return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP | ALT_KEEP_CACHE, dst, dstw, src, srcw, TMP_REG2, 0); + if (op >= SLJIT_ADD) { + flags |= CUMULATIVE_OP | IMM_OP | ALT_KEEP_CACHE; + if (dst & SLJIT_MEM) + return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, TMP_REG2, 0); + return emit_op(compiler, op, flags, dst, 0, dst, 0, TMP_REG2, 0); + } - return (reg == TMP_REG2) ? emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw) : SLJIT_SUCCESS; + if (!(dst & SLJIT_MEM)) + return SLJIT_SUCCESS; + + return emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw); +#else +#error "Implementation required" +#endif +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); + +#if (defined SLJIT_CONFIG_SPARC_32 && SLJIT_CONFIG_SPARC_32) + return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);; #else #error "Implementation required" #endif @@ -1429,7 +1476,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi PTR_FAIL_IF(!const_); set_const(const_, compiler); - reg = SLOW_IS_REG(dst) ? dst : TMP_REG2; + reg = FAST_IS_REG(dst) ? dst : TMP_REG2; PTR_FAIL_IF(emit_const(compiler, reg, init_value)); diff --git a/pcre2-10.22/src/sljit/sljitNativeTILEGX-encoder.c b/pcre2-10.32/src/sljit/sljitNativeTILEGX-encoder.c similarity index 99% rename from pcre2-10.22/src/sljit/sljitNativeTILEGX-encoder.c rename to pcre2-10.32/src/sljit/sljitNativeTILEGX-encoder.c index 719632908..dd82ebae6 100644 --- a/pcre2-10.22/src/sljit/sljitNativeTILEGX-encoder.c +++ b/pcre2-10.32/src/sljit/sljitNativeTILEGX-encoder.c @@ -2,7 +2,7 @@ * Stack-less Just-In-Time compiler * * Copyright 2013-2013 Tilera Corporation(jiwang@tilera.com). All rights reserved. - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: diff --git a/pcre2-10.22/src/sljit/sljitNativeTILEGX_64.c b/pcre2-10.32/src/sljit/sljitNativeTILEGX_64.c similarity index 99% rename from pcre2-10.22/src/sljit/sljitNativeTILEGX_64.c rename to pcre2-10.32/src/sljit/sljitNativeTILEGX_64.c index 462a8b9cd..003f43a79 100644 --- a/pcre2-10.22/src/sljit/sljitNativeTILEGX_64.c +++ b/pcre2-10.32/src/sljit/sljitNativeTILEGX_64.c @@ -2,7 +2,7 @@ * Stack-less Just-In-Time compiler * * Copyright 2013-2013 Tilera Corporation(jiwang@tilera.com). All rights reserved. - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -687,7 +687,7 @@ static sljit_s32 update_buffer(struct sljit_compiler *compiler) inst_buf[0] = inst1; inst_buf_index = 1; } else - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); #ifdef TILEGX_JIT_DEBUG return push_inst_nodebug(compiler, bits); @@ -727,10 +727,10 @@ static sljit_s32 update_buffer(struct sljit_compiler *compiler) return push_inst(compiler, bits); #endif } else - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } static sljit_s32 flush_buffer(struct sljit_compiler *compiler) @@ -814,7 +814,7 @@ static sljit_s32 push_3_buffer(struct sljit_compiler *compiler, tilegx_mnemonic break; default: printf("unrecoginzed opc: %s\n", opcode->name); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } inst_buf_index++; @@ -859,7 +859,7 @@ static sljit_s32 push_2_buffer(struct sljit_compiler *compiler, tilegx_mnemonic break; default: printf("unrecoginzed opc: %s\n", opcode->name); - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } inst_buf_index++; @@ -1952,7 +1952,7 @@ static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sl return SLJIT_SUCCESS; } - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); return SLJIT_SUCCESS; } @@ -2092,9 +2092,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co 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_S32 || op == SLJIT_MOV_U32) mem_type = INT_DATA | SIGNED_DATA; @@ -2143,7 +2140,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co break; default: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); dst_ar = sugg_dst_ar; break; } @@ -2186,7 +2183,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile case SLJIT_DIVMOD_SW: case SLJIT_DIV_UW: case SLJIT_DIV_SW: - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } return SLJIT_SUCCESS; @@ -2487,19 +2484,14 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump * sljit_emit_jump(struct sljit_compil return jump; } -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void) -{ - return 0; -} - SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) { - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { - SLJIT_ASSERT_STOP(); + SLJIT_UNREACHABLE(); } SLJIT_API_FUNC_ATTRIBUTE struct sljit_const * sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value) @@ -2526,13 +2518,13 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const * sljit_emit_const(struct sljit_comp return const_; } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target) { sljit_ins *inst = (sljit_ins *)addr; - inst[0] = (inst[0] & ~(0xFFFFL << 43)) | (((new_addr >> 32) & 0xffff) << 43); - inst[1] = (inst[1] & ~(0xFFFFL << 43)) | (((new_addr >> 16) & 0xffff) << 43); - inst[2] = (inst[2] & ~(0xFFFFL << 43)) | ((new_addr & 0xffff) << 43); + inst[0] = (inst[0] & ~(0xFFFFL << 43)) | (((new_target >> 32) & 0xffff) << 43); + inst[1] = (inst[1] & ~(0xFFFFL << 43)) | (((new_target >> 16) & 0xffff) << 43); + inst[2] = (inst[2] & ~(0xFFFFL << 43)) | ((new_target & 0xffff) << 43); SLJIT_CACHE_FLUSH(inst, inst + 3); } diff --git a/pcre2-10.32/src/sljit/sljitNativeX86_32.c b/pcre2-10.32/src/sljit/sljitNativeX86_32.c new file mode 100644 index 000000000..074e64b9f --- /dev/null +++ b/pcre2-10.32/src/sljit/sljitNativeX86_32.c @@ -0,0 +1,894 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) 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 HOLDER(S) 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. + */ + +/* x86 32-bit arch dependent functions. */ + +static sljit_s32 emit_do_imm(struct sljit_compiler *compiler, sljit_u8 opcode, sljit_sw imm) +{ + sljit_u8 *inst; + + inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + sizeof(sljit_sw)); + FAIL_IF(!inst); + INC_SIZE(1 + sizeof(sljit_sw)); + *inst++ = opcode; + sljit_unaligned_store_sw(inst, imm); + return SLJIT_SUCCESS; +} + +static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_s32 type, sljit_sw executable_offset) +{ + if (type == SLJIT_JUMP) { + *code_ptr++ = JMP_i32; + jump->addr++; + } + else if (type >= SLJIT_FAST_CALL) { + *code_ptr++ = CALL_i32; + jump->addr++; + } + else { + *code_ptr++ = GROUP_0F; + *code_ptr++ = get_jump_code(type); + jump->addr += 2; + } + + if (jump->flags & JUMP_LABEL) + jump->flags |= PATCH_MW; + else + sljit_unaligned_store_sw(code_ptr, jump->u.target - (jump->addr + 4) - (sljit_uw)executable_offset); + code_ptr += 4; + + return code_ptr; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) +{ + sljit_s32 args, size; + sljit_u8 *inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); + + args = get_arg_count(arg_types); + compiler->args = args; + + /* [esp+0] for saving temporaries and function calls. */ + compiler->stack_tmp_size = 2 * sizeof(sljit_sw); + +#if !(defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (scratches > 3) + compiler->stack_tmp_size = 3 * sizeof(sljit_sw); +#endif + + compiler->saveds_offset = compiler->stack_tmp_size; + if (scratches > 3) + compiler->saveds_offset += ((scratches > (3 + 6)) ? 6 : (scratches - 3)) * sizeof(sljit_sw); + + compiler->locals_offset = compiler->saveds_offset; + + if (saveds > 3) + compiler->locals_offset += (saveds - 3) * sizeof(sljit_sw); + + if (options & SLJIT_F64_ALIGNMENT) + compiler->locals_offset = (compiler->locals_offset + sizeof(sljit_f64) - 1) & ~(sizeof(sljit_f64) - 1); + + size = 1 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3); +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + size += (args > 0 ? (args * 2) : 0) + (args > 2 ? 2 : 0); +#else + size += (args > 0 ? (2 + args * 3) : 0); +#endif + inst = (sljit_u8*)ensure_buf(compiler, 1 + size); + FAIL_IF(!inst); + + INC_SIZE(size); + PUSH_REG(reg_map[TMP_REG1]); +#if !(defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (args > 0) { + *inst++ = MOV_r_rm; + *inst++ = MOD_REG | (reg_map[TMP_REG1] << 3) | 0x4 /* esp */; + } +#endif + if (saveds > 2 || scratches > 9) + PUSH_REG(reg_map[SLJIT_S2]); + if (saveds > 1 || scratches > 10) + PUSH_REG(reg_map[SLJIT_S1]); + if (saveds > 0 || scratches > 11) + PUSH_REG(reg_map[SLJIT_S0]); + +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (args > 0) { + inst[0] = MOV_r_rm; + inst[1] = MOD_REG | (reg_map[SLJIT_S0] << 3) | reg_map[SLJIT_R2]; + inst += 2; + } + if (args > 1) { + inst[0] = MOV_r_rm; + inst[1] = MOD_REG | (reg_map[SLJIT_S1] << 3) | reg_map[SLJIT_R1]; + inst += 2; + } + if (args > 2) { + inst[0] = MOV_r_rm; + inst[1] = MOD_DISP8 | (reg_map[SLJIT_S2] << 3) | 0x4 /* esp */; + inst[2] = 0x24; + inst[3] = sizeof(sljit_sw) * (3 + 2); /* saveds >= 3 as well. */ + } +#else + if (args > 0) { + inst[0] = MOV_r_rm; + inst[1] = MOD_DISP8 | (reg_map[SLJIT_S0] << 3) | reg_map[TMP_REG1]; + inst[2] = sizeof(sljit_sw) * 2; + inst += 3; + } + if (args > 1) { + inst[0] = MOV_r_rm; + inst[1] = MOD_DISP8 | (reg_map[SLJIT_S1] << 3) | reg_map[TMP_REG1]; + inst[2] = sizeof(sljit_sw) * 3; + inst += 3; + } + if (args > 2) { + inst[0] = MOV_r_rm; + inst[1] = MOD_DISP8 | (reg_map[SLJIT_S2] << 3) | reg_map[TMP_REG1]; + inst[2] = sizeof(sljit_sw) * 4; + } +#endif + + SLJIT_ASSERT(SLJIT_LOCALS_OFFSET > 0); + +#if defined(__APPLE__) + /* Ignore pushed registers and SLJIT_LOCALS_OFFSET when computing the aligned local size. */ + saveds = (2 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3)) * sizeof(sljit_uw); + local_size = ((SLJIT_LOCALS_OFFSET + saveds + local_size + 15) & ~15) - saveds; +#else + if (options & SLJIT_F64_ALIGNMENT) + local_size = SLJIT_LOCALS_OFFSET + ((local_size + sizeof(sljit_f64) - 1) & ~(sizeof(sljit_f64) - 1)); + else + local_size = SLJIT_LOCALS_OFFSET + ((local_size + sizeof(sljit_sw) - 1) & ~(sizeof(sljit_sw) - 1)); +#endif + + compiler->local_size = local_size; + +#ifdef _WIN32 + if (local_size > 0) { + if (local_size <= 4 * 4096) { + if (local_size > 4096) + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096); + if (local_size > 2 * 4096) + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 2); + if (local_size > 3 * 4096) + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 3); + } + else { + EMIT_MOV(compiler, SLJIT_R0, 0, SLJIT_SP, 0); + EMIT_MOV(compiler, SLJIT_R1, 0, SLJIT_IMM, (local_size - 1) >> 12); + + SLJIT_ASSERT (reg_map[SLJIT_R0] == 0); + + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_R0), -4096); + FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), + SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, 4096)); + FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), + SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1)); + + inst = (sljit_u8*)ensure_buf(compiler, 1 + 2); + FAIL_IF(!inst); + + INC_SIZE(2); + inst[0] = JNE_i8; + inst[1] = (sljit_s8) -16; + } + + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -local_size); + } +#endif + + SLJIT_ASSERT(local_size > 0); + +#if !defined(__APPLE__) + if (options & SLJIT_F64_ALIGNMENT) { + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_SP, 0); + + /* Some space might allocated during sljit_grow_stack() above on WIN32. */ + FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), + SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size + sizeof(sljit_sw))); + +#if defined _WIN32 && !(defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (compiler->local_size > 1024) + FAIL_IF(emit_cum_binary(compiler, BINARY_OPCODE(ADD), + TMP_REG1, 0, TMP_REG1, 0, SLJIT_IMM, sizeof(sljit_sw))); +#endif + + inst = (sljit_u8*)ensure_buf(compiler, 1 + 6); + FAIL_IF(!inst); + + INC_SIZE(6); + inst[0] = GROUP_BINARY_81; + inst[1] = MOD_REG | AND | reg_map[SLJIT_SP]; + sljit_unaligned_store_sw(inst + 2, ~(sizeof(sljit_f64) - 1)); + + /* The real local size must be used. */ + return emit_mov(compiler, SLJIT_MEM1(SLJIT_SP), compiler->local_size, TMP_REG1, 0); + } +#endif + return emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), + SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) +{ + CHECK_ERROR(); + CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); + + compiler->args = get_arg_count(arg_types); + + /* [esp+0] for saving temporaries and function calls. */ + compiler->stack_tmp_size = 2 * sizeof(sljit_sw); + +#if !(defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (scratches > 3) + compiler->stack_tmp_size = 3 * sizeof(sljit_sw); +#endif + + compiler->saveds_offset = compiler->stack_tmp_size; + if (scratches > 3) + compiler->saveds_offset += ((scratches > (3 + 6)) ? 6 : (scratches - 3)) * sizeof(sljit_sw); + + compiler->locals_offset = compiler->saveds_offset; + + if (saveds > 3) + compiler->locals_offset += (saveds - 3) * sizeof(sljit_sw); + + if (options & SLJIT_F64_ALIGNMENT) + compiler->locals_offset = (compiler->locals_offset + sizeof(sljit_f64) - 1) & ~(sizeof(sljit_f64) - 1); + +#if defined(__APPLE__) + saveds = (2 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3)) * sizeof(sljit_uw); + compiler->local_size = ((SLJIT_LOCALS_OFFSET + saveds + local_size + 15) & ~15) - saveds; +#else + if (options & SLJIT_F64_ALIGNMENT) + compiler->local_size = SLJIT_LOCALS_OFFSET + ((local_size + sizeof(sljit_f64) - 1) & ~(sizeof(sljit_f64) - 1)); + else + compiler->local_size = SLJIT_LOCALS_OFFSET + ((local_size + sizeof(sljit_sw) - 1) & ~(sizeof(sljit_sw) - 1)); +#endif + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw) +{ + sljit_s32 size; + sljit_u8 *inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_return(compiler, op, src, srcw)); + SLJIT_ASSERT(compiler->args >= 0); + + FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); + + SLJIT_ASSERT(compiler->local_size > 0); + +#if !defined(__APPLE__) + if (compiler->options & SLJIT_F64_ALIGNMENT) + EMIT_MOV(compiler, SLJIT_SP, 0, SLJIT_MEM1(SLJIT_SP), compiler->local_size) + else + FAIL_IF(emit_cum_binary(compiler, BINARY_OPCODE(ADD), + SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, compiler->local_size)); +#else + FAIL_IF(emit_cum_binary(compiler, BINARY_OPCODE(ADD), + SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, compiler->local_size)); +#endif + + size = 2 + (compiler->scratches > 7 ? (compiler->scratches - 7) : 0) + + (compiler->saveds <= 3 ? compiler->saveds : 3); +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (compiler->args > 2) + size += 2; +#else + if (compiler->args > 0) + size += 2; +#endif + inst = (sljit_u8*)ensure_buf(compiler, 1 + size); + FAIL_IF(!inst); + + INC_SIZE(size); + + if (compiler->saveds > 0 || compiler->scratches > 11) + POP_REG(reg_map[SLJIT_S0]); + if (compiler->saveds > 1 || compiler->scratches > 10) + POP_REG(reg_map[SLJIT_S1]); + if (compiler->saveds > 2 || compiler->scratches > 9) + POP_REG(reg_map[SLJIT_S2]); + POP_REG(reg_map[TMP_REG1]); +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (compiler->args > 2) + RET_I16(sizeof(sljit_sw)); + else + RET(); +#else + RET(); +#endif + + return SLJIT_SUCCESS; +} + +/* --------------------------------------------------------------------- */ +/* Operators */ +/* --------------------------------------------------------------------- */ + +/* Size contains the flags as well. */ +static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32 size, + /* The register or immediate operand. */ + sljit_s32 a, sljit_sw imma, + /* The general operand (not immediate). */ + sljit_s32 b, sljit_sw immb) +{ + sljit_u8 *inst; + sljit_u8 *buf_ptr; + sljit_s32 flags = size & ~0xf; + sljit_s32 inst_size; + + /* Both cannot be switched on. */ + SLJIT_ASSERT((flags & (EX86_BIN_INS | EX86_SHIFT_INS)) != (EX86_BIN_INS | EX86_SHIFT_INS)); + /* Size flags not allowed for typed instructions. */ + SLJIT_ASSERT(!(flags & (EX86_BIN_INS | EX86_SHIFT_INS)) || (flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) == 0); + /* Both size flags cannot be switched on. */ + SLJIT_ASSERT((flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) != (EX86_BYTE_ARG | EX86_HALF_ARG)); + /* SSE2 and immediate is not possible. */ + SLJIT_ASSERT(!(a & SLJIT_IMM) || !(flags & EX86_SSE2)); + SLJIT_ASSERT((flags & (EX86_PREF_F2 | EX86_PREF_F3)) != (EX86_PREF_F2 | EX86_PREF_F3) + && (flags & (EX86_PREF_F2 | EX86_PREF_66)) != (EX86_PREF_F2 | EX86_PREF_66) + && (flags & (EX86_PREF_F3 | EX86_PREF_66)) != (EX86_PREF_F3 | EX86_PREF_66)); + + size &= 0xf; + inst_size = size; + + if (flags & (EX86_PREF_F2 | EX86_PREF_F3)) + inst_size++; + if (flags & EX86_PREF_66) + inst_size++; + + /* Calculate size of b. */ + inst_size += 1; /* mod r/m byte. */ + if (b & SLJIT_MEM) { + if ((b & REG_MASK) == SLJIT_UNUSED) + inst_size += sizeof(sljit_sw); + else if (immb != 0 && !(b & OFFS_REG_MASK)) { + /* Immediate operand. */ + if (immb <= 127 && immb >= -128) + inst_size += sizeof(sljit_s8); + else + inst_size += sizeof(sljit_sw); + } + + if ((b & REG_MASK) == SLJIT_SP && !(b & OFFS_REG_MASK)) + b |= TO_OFFS_REG(SLJIT_SP); + + if ((b & OFFS_REG_MASK) != SLJIT_UNUSED) + inst_size += 1; /* SIB byte. */ + } + + /* Calculate size of a. */ + if (a & SLJIT_IMM) { + if (flags & EX86_BIN_INS) { + if (imma <= 127 && imma >= -128) { + inst_size += 1; + flags |= EX86_BYTE_ARG; + } else + inst_size += 4; + } + else if (flags & EX86_SHIFT_INS) { + imma &= 0x1f; + if (imma != 1) { + inst_size ++; + flags |= EX86_BYTE_ARG; + } + } else if (flags & EX86_BYTE_ARG) + inst_size++; + else if (flags & EX86_HALF_ARG) + inst_size += sizeof(short); + else + inst_size += sizeof(sljit_sw); + } + else + SLJIT_ASSERT(!(flags & EX86_SHIFT_INS) || a == SLJIT_PREF_SHIFT_REG); + + inst = (sljit_u8*)ensure_buf(compiler, 1 + inst_size); + PTR_FAIL_IF(!inst); + + /* Encoding the byte. */ + INC_SIZE(inst_size); + if (flags & EX86_PREF_F2) + *inst++ = 0xf2; + if (flags & EX86_PREF_F3) + *inst++ = 0xf3; + if (flags & EX86_PREF_66) + *inst++ = 0x66; + + buf_ptr = inst + size; + + /* Encode mod/rm byte. */ + if (!(flags & EX86_SHIFT_INS)) { + if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM)) + *inst = (flags & EX86_BYTE_ARG) ? GROUP_BINARY_83 : GROUP_BINARY_81; + + if (a & SLJIT_IMM) + *buf_ptr = 0; + else if (!(flags & EX86_SSE2_OP1)) + *buf_ptr = reg_map[a] << 3; + else + *buf_ptr = a << 3; + } + else { + if (a & SLJIT_IMM) { + if (imma == 1) + *inst = GROUP_SHIFT_1; + else + *inst = GROUP_SHIFT_N; + } else + *inst = GROUP_SHIFT_CL; + *buf_ptr = 0; + } + + if (!(b & SLJIT_MEM)) + *buf_ptr++ |= MOD_REG + ((!(flags & EX86_SSE2_OP2)) ? reg_map[b] : b); + else if ((b & REG_MASK) != SLJIT_UNUSED) { + if ((b & OFFS_REG_MASK) == SLJIT_UNUSED || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP)) { + if (immb != 0) { + if (immb <= 127 && immb >= -128) + *buf_ptr |= 0x40; + else + *buf_ptr |= 0x80; + } + + if ((b & OFFS_REG_MASK) == SLJIT_UNUSED) + *buf_ptr++ |= reg_map[b & REG_MASK]; + else { + *buf_ptr++ |= 0x04; + *buf_ptr++ = reg_map[b & REG_MASK] | (reg_map[OFFS_REG(b)] << 3); + } + + if (immb != 0) { + if (immb <= 127 && immb >= -128) + *buf_ptr++ = immb; /* 8 bit displacement. */ + else { + sljit_unaligned_store_sw(buf_ptr, immb); /* 32 bit displacement. */ + buf_ptr += sizeof(sljit_sw); + } + } + } + else { + *buf_ptr++ |= 0x04; + *buf_ptr++ = reg_map[b & REG_MASK] | (reg_map[OFFS_REG(b)] << 3) | (immb << 6); + } + } + else { + *buf_ptr++ |= 0x05; + sljit_unaligned_store_sw(buf_ptr, immb); /* 32 bit displacement. */ + buf_ptr += sizeof(sljit_sw); + } + + if (a & SLJIT_IMM) { + if (flags & EX86_BYTE_ARG) + *buf_ptr = imma; + else if (flags & EX86_HALF_ARG) + sljit_unaligned_store_s16(buf_ptr, imma); + else if (!(flags & EX86_SHIFT_INS)) + sljit_unaligned_store_sw(buf_ptr, imma); + } + + return !(flags & EX86_SHIFT_INS) ? inst : (inst + 1); +} + +/* --------------------------------------------------------------------- */ +/* Call / return instructions */ +/* --------------------------------------------------------------------- */ + +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + +static sljit_s32 c_fast_call_get_stack_size(sljit_s32 arg_types, sljit_s32 *word_arg_count_ptr) +{ + sljit_s32 stack_size = 0; + sljit_s32 word_arg_count = 0; + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + stack_size += sizeof(sljit_f32); + break; + case SLJIT_ARG_TYPE_F64: + stack_size += sizeof(sljit_f64); + break; + default: + word_arg_count++; + if (word_arg_count > 2) + stack_size += sizeof(sljit_sw); + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + if (word_arg_count_ptr) + *word_arg_count_ptr = word_arg_count; + + return stack_size; +} + +static sljit_s32 c_fast_call_with_args(struct sljit_compiler *compiler, + sljit_s32 arg_types, sljit_s32 stack_size, sljit_s32 word_arg_count, sljit_s32 swap_args) +{ + sljit_u8 *inst; + sljit_s32 float_arg_count; + + if (stack_size == sizeof(sljit_sw) && word_arg_count == 3) { + inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!inst); + INC_SIZE(1); + PUSH_REG(reg_map[SLJIT_R2]); + } + else if (stack_size > 0) { + if (word_arg_count >= 4) + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), compiler->saveds_offset - sizeof(sljit_sw)); + + FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), + SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, stack_size)); + + stack_size = 0; + arg_types >>= SLJIT_DEF_SHIFT; + word_arg_count = 0; + float_arg_count = 0; + while (arg_types) { + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + float_arg_count++; + FAIL_IF(emit_sse2_store(compiler, 1, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count)); + stack_size += sizeof(sljit_f32); + break; + case SLJIT_ARG_TYPE_F64: + float_arg_count++; + FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count)); + stack_size += sizeof(sljit_f64); + break; + default: + word_arg_count++; + if (word_arg_count == 3) { + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size, SLJIT_R2, 0); + stack_size += sizeof(sljit_sw); + } + else if (word_arg_count == 4) { + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size, TMP_REG1, 0); + stack_size += sizeof(sljit_sw); + } + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + } + + if (word_arg_count > 0) { + if (swap_args) { + inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!inst); + INC_SIZE(1); + + *inst++ = XCHG_EAX_r | reg_map[SLJIT_R2]; + } + else { + inst = (sljit_u8*)ensure_buf(compiler, 1 + 2); + FAIL_IF(!inst); + INC_SIZE(2); + + *inst++ = MOV_r_rm; + *inst++ = MOD_REG | (reg_map[SLJIT_R2] << 3) | reg_map[SLJIT_R0]; + } + } + + return SLJIT_SUCCESS; +} + +#endif + +static sljit_s32 cdecl_call_get_stack_size(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *word_arg_count_ptr) +{ + sljit_s32 stack_size = 0; + sljit_s32 word_arg_count = 0; + + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + stack_size += sizeof(sljit_f32); + break; + case SLJIT_ARG_TYPE_F64: + stack_size += sizeof(sljit_f64); + break; + default: + word_arg_count++; + stack_size += sizeof(sljit_sw); + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + if (word_arg_count_ptr) + *word_arg_count_ptr = word_arg_count; + + if (stack_size <= compiler->stack_tmp_size) + return 0; + +#if defined(__APPLE__) + return ((stack_size - compiler->stack_tmp_size + 15) & ~15); +#else + return stack_size - compiler->stack_tmp_size; +#endif +} + +static sljit_s32 cdecl_call_with_args(struct sljit_compiler *compiler, + sljit_s32 arg_types, sljit_s32 stack_size, sljit_s32 word_arg_count) +{ + sljit_s32 float_arg_count = 0; + + if (word_arg_count >= 4) + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), compiler->saveds_offset - sizeof(sljit_sw)); + + if (stack_size > 0) + FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), + SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, stack_size)); + + stack_size = 0; + word_arg_count = 0; + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + float_arg_count++; + FAIL_IF(emit_sse2_store(compiler, 1, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count)); + stack_size += sizeof(sljit_f32); + break; + case SLJIT_ARG_TYPE_F64: + float_arg_count++; + FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), stack_size, float_arg_count)); + stack_size += sizeof(sljit_f64); + break; + default: + word_arg_count++; + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), stack_size, (word_arg_count >= 4) ? TMP_REG1 : word_arg_count, 0); + stack_size += sizeof(sljit_sw); + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; + } + + return SLJIT_SUCCESS; +} + +static sljit_s32 post_call_with_args(struct sljit_compiler *compiler, + sljit_s32 arg_types, sljit_s32 stack_size) +{ + sljit_u8 *inst; + sljit_s32 single; + + if (stack_size > 0) + FAIL_IF(emit_cum_binary(compiler, BINARY_OPCODE(ADD), + SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, stack_size)); + + if ((arg_types & SLJIT_DEF_MASK) < SLJIT_ARG_TYPE_F32) + return SLJIT_SUCCESS; + + single = ((arg_types & SLJIT_DEF_MASK) == SLJIT_ARG_TYPE_F32); + + inst = (sljit_u8*)ensure_buf(compiler, 1 + 3); + FAIL_IF(!inst); + INC_SIZE(3); + inst[0] = single ? FSTPS : FSTPD; + inst[1] = (0x03 << 3) | 0x04; + inst[2] = (0x04 << 3) | reg_map[SLJIT_SP]; + + return emit_sse2_load(compiler, single, SLJIT_FR0, SLJIT_MEM1(SLJIT_SP), 0); +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ + struct sljit_jump *jump; + sljit_s32 stack_size = 0; + sljit_s32 word_arg_count; + + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); + +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if ((type & 0xff) == SLJIT_CALL) { + stack_size = c_fast_call_get_stack_size(arg_types, &word_arg_count); + PTR_FAIL_IF(c_fast_call_with_args(compiler, arg_types, stack_size, word_arg_count, 0)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + jump = sljit_emit_jump(compiler, type); + PTR_FAIL_IF(jump == NULL); + + PTR_FAIL_IF(post_call_with_args(compiler, arg_types, 0)); + return jump; + } +#endif + + stack_size = cdecl_call_get_stack_size(compiler, arg_types, &word_arg_count); + PTR_FAIL_IF(cdecl_call_with_args(compiler, arg_types, stack_size, word_arg_count)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + jump = sljit_emit_jump(compiler, type); + PTR_FAIL_IF(jump == NULL); + + PTR_FAIL_IF(post_call_with_args(compiler, arg_types, stack_size)); + return jump; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + sljit_s32 stack_size = 0; + sljit_s32 word_arg_count; +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + sljit_s32 swap_args; +#endif + + CHECK_ERROR(); + CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + SLJIT_ASSERT(reg_map[SLJIT_R0] == 0 && reg_map[SLJIT_R2] == 1 && SLJIT_R0 == 1 && SLJIT_R2 == 3); + + if ((type & 0xff) == SLJIT_CALL) { + stack_size = c_fast_call_get_stack_size(arg_types, &word_arg_count); + swap_args = 0; + + if (word_arg_count > 0) { + if ((src & REG_MASK) == SLJIT_R2 || OFFS_REG(src) == SLJIT_R2) { + swap_args = 1; + if (((src & REG_MASK) | 0x2) == SLJIT_R2) + src ^= 0x2; + if ((OFFS_REG(src) | 0x2) == SLJIT_R2) + src ^= TO_OFFS_REG(0x2); + } + } + + FAIL_IF(c_fast_call_with_args(compiler, arg_types, stack_size, word_arg_count, swap_args)); + + compiler->saveds_offset += stack_size; + compiler->locals_offset += stack_size; + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); + + compiler->saveds_offset -= stack_size; + compiler->locals_offset -= stack_size; + + return post_call_with_args(compiler, arg_types, 0); + } +#endif + + stack_size = cdecl_call_get_stack_size(compiler, arg_types, &word_arg_count); + FAIL_IF(cdecl_call_with_args(compiler, arg_types, stack_size, word_arg_count)); + + compiler->saveds_offset += stack_size; + compiler->locals_offset += stack_size; + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw)); + + compiler->saveds_offset -= stack_size; + compiler->locals_offset -= stack_size; + + return post_call_with_args(compiler, arg_types, stack_size); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) +{ + sljit_u8 *inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw)); + ADJUST_LOCAL_OFFSET(dst, dstw); + + CHECK_EXTRA_REGS(dst, dstw, (void)0); + + /* For UNUSED dst. Uncommon, but possible. */ + if (dst == SLJIT_UNUSED) + dst = TMP_REG1; + + if (FAST_IS_REG(dst)) { + /* Unused dest is possible here. */ + inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!inst); + + INC_SIZE(1); + POP_REG(reg_map[dst]); + return SLJIT_SUCCESS; + } + + /* Memory. */ + inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw); + FAIL_IF(!inst); + *inst++ = POP_rm; + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw) +{ + sljit_u8 *inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_fast_return(compiler, src, srcw)); + ADJUST_LOCAL_OFFSET(src, srcw); + + CHECK_EXTRA_REGS(src, srcw, (void)0); + + if (FAST_IS_REG(src)) { + inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 1); + FAIL_IF(!inst); + + INC_SIZE(1 + 1); + PUSH_REG(reg_map[src]); + } + else { + inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw); + FAIL_IF(!inst); + *inst++ = GROUP_FF; + *inst |= PUSH_rm; + + inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!inst); + INC_SIZE(1); + } + + RET(); + return SLJIT_SUCCESS; +} diff --git a/pcre2-10.22/src/sljit/sljitNativeX86_64.c b/pcre2-10.32/src/sljit/sljitNativeX86_64.c similarity index 64% rename from pcre2-10.22/src/sljit/sljitNativeX86_64.c rename to pcre2-10.32/src/sljit/sljitNativeX86_64.c index e88ddedcd..850656561 100644 --- a/pcre2-10.22/src/sljit/sljitNativeX86_64.c +++ b/pcre2-10.32/src/sljit/sljitNativeX86_64.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -41,64 +41,57 @@ static sljit_s32 emit_load_imm64(struct sljit_compiler *compiler, sljit_s32 reg, static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_s32 type) { + int short_addr = !(jump->flags & SLJIT_REWRITABLE_JUMP) && !(jump->flags & JUMP_LABEL) && (jump->u.target <= 0xffffffff); + + /* The relative jump below specialized for this case. */ + SLJIT_ASSERT(reg_map[TMP_REG2] >= 8); + if (type < SLJIT_JUMP) { /* Invert type. */ *code_ptr++ = get_jump_code(type ^ 0x1) - 0x10; - *code_ptr++ = 10 + 3; + *code_ptr++ = short_addr ? (6 + 3) : (10 + 3); } - SLJIT_COMPILE_ASSERT(reg_map[TMP_REG3] == 9, tmp3_is_9_first); - *code_ptr++ = REX_W | REX_B; - *code_ptr++ = MOV_r_i32 + 1; + *code_ptr++ = short_addr ? REX_B : (REX_W | REX_B); + *code_ptr++ = MOV_r_i32 | reg_lmap[TMP_REG2]; jump->addr = (sljit_uw)code_ptr; if (jump->flags & JUMP_LABEL) jump->flags |= PATCH_MD; + else if (short_addr) + sljit_unaligned_store_s32(code_ptr, (sljit_s32)jump->u.target); else sljit_unaligned_store_sw(code_ptr, jump->u.target); - code_ptr += sizeof(sljit_sw); + code_ptr += short_addr ? sizeof(sljit_s32) : sizeof(sljit_sw); + *code_ptr++ = REX_B; *code_ptr++ = GROUP_FF; - *code_ptr++ = (type >= SLJIT_FAST_CALL) ? (MOD_REG | CALL_rm | 1) : (MOD_REG | JMP_rm | 1); - - return code_ptr; -} - -static sljit_u8* generate_fixed_jump(sljit_u8 *code_ptr, sljit_sw addr, sljit_s32 type) -{ - sljit_sw delta = addr - ((sljit_sw)code_ptr + 1 + sizeof(sljit_s32)); - - if (delta <= HALFWORD_MAX && delta >= HALFWORD_MIN) { - *code_ptr++ = (type == 2) ? CALL_i32 : JMP_i32; - sljit_unaligned_store_sw(code_ptr, delta); - } - else { - SLJIT_COMPILE_ASSERT(reg_map[TMP_REG3] == 9, tmp3_is_9_second); - *code_ptr++ = REX_W | REX_B; - *code_ptr++ = MOV_r_i32 + 1; - sljit_unaligned_store_sw(code_ptr, addr); - code_ptr += sizeof(sljit_sw); - *code_ptr++ = REX_B; - *code_ptr++ = GROUP_FF; - *code_ptr++ = (type == 2) ? (MOD_REG | CALL_rm | 1) : (MOD_REG | JMP_rm | 1); - } + *code_ptr++ = MOD_REG | (type >= SLJIT_FAST_CALL ? CALL_rm : JMP_rm) | reg_lmap[TMP_REG2]; return code_ptr; } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { - sljit_s32 i, tmp, size, saved_register_size; + sljit_s32 args, i, tmp, size, saved_register_size; sljit_u8 *inst; CHECK_ERROR(); - 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); + CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); - compiler->flags_saved = 0; + compiler->mode32 = 0; + +#ifdef _WIN64 + /* Two/four register slots for parameters plus space for xmm6 register if needed. */ + if (fscratches >= 6 || fsaveds >= 1) + compiler->locals_offset = 6 * sizeof(sljit_sw); + else + compiler->locals_offset = ((scratches > 2) ? 4 : 2) * sizeof(sljit_sw); +#endif /* Including the return address saved by the call instruction. */ saved_register_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); @@ -124,6 +117,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi PUSH_REG(reg_lmap[i]); } + args = get_arg_count(arg_types); + if (args > 0) { size = args * 3; inst = (sljit_u8*)ensure_buf(compiler, 1 + size); @@ -133,35 +128,39 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi #ifndef _WIN64 if (args > 0) { - *inst++ = REX_W; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_map[SLJIT_S0] << 3) | 0x7 /* rdi */; + inst[0] = REX_W; + inst[1] = MOV_r_rm; + inst[2] = MOD_REG | (reg_map[SLJIT_S0] << 3) | 0x7 /* rdi */; + inst += 3; } if (args > 1) { - *inst++ = REX_W | REX_R; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_lmap[SLJIT_S1] << 3) | 0x6 /* rsi */; + inst[0] = REX_W | REX_R; + inst[1] = MOV_r_rm; + inst[2] = MOD_REG | (reg_lmap[SLJIT_S1] << 3) | 0x6 /* rsi */; + inst += 3; } if (args > 2) { - *inst++ = REX_W | REX_R; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_lmap[SLJIT_S2] << 3) | 0x2 /* rdx */; + inst[0] = REX_W | REX_R; + inst[1] = MOV_r_rm; + inst[2] = MOD_REG | (reg_lmap[SLJIT_S2] << 3) | 0x2 /* rdx */; } #else if (args > 0) { - *inst++ = REX_W; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_map[SLJIT_S0] << 3) | 0x1 /* rcx */; + inst[0] = REX_W; + inst[1] = MOV_r_rm; + inst[2] = MOD_REG | (reg_map[SLJIT_S0] << 3) | 0x1 /* rcx */; + inst += 3; } if (args > 1) { - *inst++ = REX_W; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_map[SLJIT_S1] << 3) | 0x2 /* rdx */; + inst[0] = REX_W; + inst[1] = MOV_r_rm; + inst[2] = MOD_REG | (reg_map[SLJIT_S1] << 3) | 0x2 /* rdx */; + inst += 3; } if (args > 2) { - *inst++ = REX_W | REX_B; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_map[SLJIT_S2] << 3) | 0x0 /* r8 */; + inst[0] = REX_W | REX_B; + inst[1] = MOV_r_rm; + inst[2] = MOD_REG | (reg_map[SLJIT_S2] << 3) | 0x0 /* r8 */; } #endif } @@ -170,57 +169,42 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi compiler->local_size = local_size; #ifdef _WIN64 - if (local_size > 1024) { - /* Allocate stack for the callback, which grows the stack. */ - inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 + (3 + sizeof(sljit_s32))); - FAIL_IF(!inst); - INC_SIZE(4 + (3 + sizeof(sljit_s32))); - *inst++ = REX_W; - *inst++ = GROUP_BINARY_83; - *inst++ = MOD_REG | SUB | 4; - /* Allocated size for registers must be divisible by 8. */ - SLJIT_ASSERT(!(saved_register_size & 0x7)); - /* Aligned to 16 byte. */ - if (saved_register_size & 0x8) { - *inst++ = 5 * sizeof(sljit_sw); - local_size -= 5 * sizeof(sljit_sw); - } else { - *inst++ = 4 * sizeof(sljit_sw); - local_size -= 4 * sizeof(sljit_sw); + if (local_size > 0) { + if (local_size <= 4 * 4096) { + if (local_size > 4096) + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096); + if (local_size > 2 * 4096) + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 2); + if (local_size > 3 * 4096) + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 3); } - /* Second instruction */ - SLJIT_COMPILE_ASSERT(reg_map[SLJIT_R0] < 8, temporary_reg1_is_loreg); - *inst++ = REX_W; - *inst++ = MOV_rm_i32; - *inst++ = MOD_REG | reg_lmap[SLJIT_R0]; - sljit_unaligned_store_s32(inst, local_size); -#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ - || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - compiler->skip_checks = 1; -#endif - FAIL_IF(sljit_emit_ijump(compiler, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_grow_stack))); + else { + EMIT_MOV(compiler, SLJIT_R0, 0, SLJIT_SP, 0); + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, (local_size - 1) >> 12); + + SLJIT_ASSERT (reg_map[SLJIT_R0] == 0); + + EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_MEM1(SLJIT_R0), -4096); + FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), + SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, 4096)); + FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), + TMP_REG1, 0, TMP_REG1, 0, SLJIT_IMM, 1)); + + inst = (sljit_u8*)ensure_buf(compiler, 1 + 2); + FAIL_IF(!inst); + + INC_SIZE(2); + inst[0] = JNE_i8; + inst[1] = (sljit_s8) -19; + } + + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -local_size); } #endif - SLJIT_ASSERT(local_size > 0); - if (local_size <= 127) { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 4); - FAIL_IF(!inst); - INC_SIZE(4); - *inst++ = REX_W; - *inst++ = GROUP_BINARY_83; - *inst++ = MOD_REG | SUB | 4; - *inst++ = local_size; - } - else { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 7); - FAIL_IF(!inst); - INC_SIZE(7); - *inst++ = REX_W; - *inst++ = GROUP_BINARY_81; - *inst++ = MOD_REG | SUB | 4; - sljit_unaligned_store_s32(inst, local_size); - inst += sizeof(sljit_s32); + if (local_size > 0) { + FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), + SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size)); } #ifdef _WIN64 @@ -238,14 +222,22 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compi } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler, - sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds, + sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size) { sljit_s32 saved_register_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); + CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size)); + set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size); + +#ifdef _WIN64 + /* Two/four register slots for parameters plus space for xmm6 register if needed. */ + if (fscratches >= 6 || fsaveds >= 1) + compiler->locals_offset = 6 * sizeof(sljit_sw); + else + compiler->locals_offset = ((scratches > 2) ? 4 : 2) * sizeof(sljit_sw); +#endif /* Including the return address saved by the call instruction. */ saved_register_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds, 1); @@ -261,7 +253,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp CHECK_ERROR(); CHECK(check_sljit_emit_return(compiler, op, src, srcw)); - compiler->flags_saved = 0; FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); #ifdef _WIN64 @@ -275,24 +266,25 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *comp } #endif - SLJIT_ASSERT(compiler->local_size > 0); - if (compiler->local_size <= 127) { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 4); - FAIL_IF(!inst); - INC_SIZE(4); - *inst++ = REX_W; - *inst++ = GROUP_BINARY_83; - *inst++ = MOD_REG | ADD | 4; - *inst = compiler->local_size; - } - else { - inst = (sljit_u8*)ensure_buf(compiler, 1 + 7); - FAIL_IF(!inst); - INC_SIZE(7); - *inst++ = REX_W; - *inst++ = GROUP_BINARY_81; - *inst++ = MOD_REG | ADD | 4; - sljit_unaligned_store_s32(inst, compiler->local_size); + if (compiler->local_size > 0) { + if (compiler->local_size <= 127) { + inst = (sljit_u8*)ensure_buf(compiler, 1 + 4); + FAIL_IF(!inst); + INC_SIZE(4); + *inst++ = REX_W; + *inst++ = GROUP_BINARY_83; + *inst++ = MOD_REG | ADD | 4; + *inst = compiler->local_size; + } + else { + inst = (sljit_u8*)ensure_buf(compiler, 1 + 7); + FAIL_IF(!inst); + INC_SIZE(7); + *inst++ = REX_W; + *inst++ = GROUP_BINARY_81; + *inst++ = MOD_REG | ADD | 4; + sljit_unaligned_store_s32(inst, compiler->local_size); + } } tmp = compiler->scratches; @@ -387,13 +379,12 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32 if (b & SLJIT_MEM) { if (!(b & OFFS_REG_MASK)) { if (NOT_HALFWORD(immb)) { - if (emit_load_imm64(compiler, TMP_REG3, immb)) - return NULL; + PTR_FAIL_IF(emit_load_imm64(compiler, TMP_REG2, immb)); immb = 0; if (b & REG_MASK) - b |= TO_OFFS_REG(TMP_REG3); + b |= TO_OFFS_REG(TMP_REG2); else - b |= TMP_REG3; + b |= TMP_REG2; } else if (reg_lmap[b & REG_MASK] == 4) b |= TO_OFFS_REG(SLJIT_SP); @@ -422,7 +413,11 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32 } } } - else if (!(flags & EX86_SSE2_OP2) && reg_map[b] >= 8) + else if (!(flags & EX86_SSE2_OP2)) { + if (reg_map[b] >= 8) + rex |= REX_B; + } + else if (freg_map[b] >= 8) rex |= REX_B; if (a & SLJIT_IMM) { @@ -449,7 +444,11 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32 else { SLJIT_ASSERT(!(flags & EX86_SHIFT_INS) || a == SLJIT_PREF_SHIFT_REG); /* reg_map[SLJIT_PREF_SHIFT_REG] is less than 8. */ - if (!(flags & EX86_SSE2_OP1) && reg_map[a] >= 8) + if (!(flags & EX86_SSE2_OP1)) { + if (reg_map[a] >= 8) + rex |= REX_R; + } + else if (freg_map[a] >= 8) rex |= REX_R; } @@ -476,12 +475,12 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32 if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM)) *inst = (flags & EX86_BYTE_ARG) ? GROUP_BINARY_83 : GROUP_BINARY_81; - if ((a & SLJIT_IMM) || (a == 0)) + if (a & SLJIT_IMM) *buf_ptr = 0; else if (!(flags & EX86_SSE2_OP1)) *buf_ptr = reg_lmap[a] << 3; else - *buf_ptr = a << 3; + *buf_ptr = freg_lmap[a] << 3; } else { if (a & SLJIT_IMM) { @@ -495,7 +494,7 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32 } if (!(b & SLJIT_MEM)) - *buf_ptr++ |= MOD_REG + ((!(flags & EX86_SSE2_OP2)) ? reg_lmap[b] : b); + *buf_ptr++ |= MOD_REG + ((!(flags & EX86_SSE2_OP2)) ? reg_lmap[b] : freg_lmap[b]); else if ((b & REG_MASK) != SLJIT_UNUSED) { if ((b & OFFS_REG_MASK) == SLJIT_UNUSED || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP)) { if (immb != 0 || reg_lmap[b & REG_MASK] == 5) { @@ -553,42 +552,161 @@ static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_s32 /* Call / return instructions */ /* --------------------------------------------------------------------- */ -static SLJIT_INLINE sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 type) -{ - sljit_u8 *inst; - #ifndef _WIN64 - SLJIT_COMPILE_ASSERT(reg_map[SLJIT_R1] == 6 && reg_map[SLJIT_R0] < 8 && reg_map[SLJIT_R2] < 8, args_registers); - inst = (sljit_u8*)ensure_buf(compiler, 1 + ((type < SLJIT_CALL3) ? 3 : 6)); - FAIL_IF(!inst); - INC_SIZE((type < SLJIT_CALL3) ? 3 : 6); - if (type >= SLJIT_CALL3) { - *inst++ = REX_W; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (0x2 /* rdx */ << 3) | reg_lmap[SLJIT_R2]; +static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src_ptr, sljit_sw srcw) +{ + sljit_s32 src = src_ptr ? (*src_ptr) : 0; + sljit_s32 word_arg_count = 0; + + SLJIT_ASSERT(reg_map[SLJIT_R1] == 6 && reg_map[SLJIT_R3] == 1 && reg_map[TMP_REG1] == 2); + + compiler->mode32 = 0; + + /* Remove return value. */ + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + if ((arg_types & SLJIT_DEF_MASK) < SLJIT_ARG_TYPE_F32) + word_arg_count++; + arg_types >>= SLJIT_DEF_SHIFT; } - *inst++ = REX_W; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (0x7 /* rdi */ << 3) | reg_lmap[SLJIT_R0]; + + if (word_arg_count == 0) + return SLJIT_SUCCESS; + + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + EMIT_MOV(compiler, TMP_REG2, 0, src, srcw); + *src_ptr = TMP_REG2; + } + else if (src == SLJIT_R2 && word_arg_count >= SLJIT_R2) + *src_ptr = TMP_REG1; + + if (word_arg_count >= 3) + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_R2, 0); + return emit_mov(compiler, SLJIT_R2, 0, SLJIT_R0, 0); +} + #else - SLJIT_COMPILE_ASSERT(reg_map[SLJIT_R1] == 2 && reg_map[SLJIT_R0] < 8 && reg_map[SLJIT_R2] < 8, args_registers); - inst = (sljit_u8*)ensure_buf(compiler, 1 + ((type < SLJIT_CALL3) ? 3 : 6)); - FAIL_IF(!inst); - INC_SIZE((type < SLJIT_CALL3) ? 3 : 6); - if (type >= SLJIT_CALL3) { - *inst++ = REX_W | REX_R; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (0x0 /* r8 */ << 3) | reg_lmap[SLJIT_R2]; +static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src_ptr, sljit_sw srcw) +{ + sljit_s32 src = src_ptr ? (*src_ptr) : 0; + sljit_s32 arg_count = 0; + sljit_s32 word_arg_count = 0; + sljit_s32 float_arg_count = 0; + sljit_s32 types = 0; + sljit_s32 data_trandfer = 0; + static sljit_u8 word_arg_regs[5] = { 0, SLJIT_R3, SLJIT_R1, SLJIT_R2, TMP_REG1 }; + + SLJIT_ASSERT(reg_map[SLJIT_R3] == 1 && reg_map[SLJIT_R1] == 2 && reg_map[SLJIT_R2] == 8 && reg_map[TMP_REG1] == 9); + + compiler->mode32 = 0; + arg_types >>= SLJIT_DEF_SHIFT; + + while (arg_types) { + types = (types << SLJIT_DEF_SHIFT) | (arg_types & SLJIT_DEF_MASK); + + switch (arg_types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + case SLJIT_ARG_TYPE_F64: + arg_count++; + float_arg_count++; + + if (arg_count != float_arg_count) + data_trandfer = 1; + break; + default: + arg_count++; + word_arg_count++; + + if (arg_count != word_arg_count || arg_count != word_arg_regs[arg_count]) { + data_trandfer = 1; + + if (src == word_arg_regs[arg_count]) { + EMIT_MOV(compiler, TMP_REG2, 0, src, 0); + *src_ptr = TMP_REG2; + } + } + break; + } + + arg_types >>= SLJIT_DEF_SHIFT; } - *inst++ = REX_W; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (0x1 /* rcx */ << 3) | reg_lmap[SLJIT_R0]; -#endif + + if (!data_trandfer) + return SLJIT_SUCCESS; + + if (src & SLJIT_MEM) { + ADJUST_LOCAL_OFFSET(src, srcw); + EMIT_MOV(compiler, TMP_REG2, 0, src, srcw); + *src_ptr = TMP_REG2; + } + + while (types) { + switch (types & SLJIT_DEF_MASK) { + case SLJIT_ARG_TYPE_F32: + if (arg_count != float_arg_count) + FAIL_IF(emit_sse2_load(compiler, 1, arg_count, float_arg_count, 0)); + arg_count--; + float_arg_count--; + break; + case SLJIT_ARG_TYPE_F64: + if (arg_count != float_arg_count) + FAIL_IF(emit_sse2_load(compiler, 0, arg_count, float_arg_count, 0)); + arg_count--; + float_arg_count--; + break; + default: + if (arg_count != word_arg_count || arg_count != word_arg_regs[arg_count]) + EMIT_MOV(compiler, word_arg_regs[arg_count], 0, word_arg_count, 0); + arg_count--; + word_arg_count--; + break; + } + + types >>= SLJIT_DEF_SHIFT; + } + return SLJIT_SUCCESS; } +#endif + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types) +{ + CHECK_ERROR_PTR(); + CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types)); + + PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL, 0)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_jump(compiler, type); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 arg_types, + sljit_s32 src, sljit_sw srcw) +{ + CHECK_ERROR(); + CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw)); + + FAIL_IF(call_with_args(compiler, arg_types, &src, srcw)); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ + || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) + compiler->skip_checks = 1; +#endif + + return sljit_emit_ijump(compiler, type, src, srcw); +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw) { sljit_u8 *inst; @@ -634,11 +752,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler CHECK(check_sljit_emit_fast_return(compiler, src, srcw)); ADJUST_LOCAL_OFFSET(src, srcw); - if ((src & SLJIT_IMM) && NOT_HALFWORD(srcw)) { - FAIL_IF(emit_load_imm64(compiler, TMP_REG1, srcw)); - src = TMP_REG1; - } - if (FAST_IS_REG(src)) { if (reg_map[src] < 8) { inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 1); @@ -656,7 +769,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler PUSH_REG(reg_lmap[src]); } } - else if (src & SLJIT_MEM) { + else { /* REX_W is not necessary (src is not immediate). */ compiler->mode32 = 1; inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw); @@ -668,23 +781,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_return(struct sljit_compiler FAIL_IF(!inst); INC_SIZE(1); } - else { - SLJIT_ASSERT(IS_HALFWORD(srcw)); - /* SLJIT_IMM. */ - inst = (sljit_u8*)ensure_buf(compiler, 1 + 5 + 1); - FAIL_IF(!inst); - - INC_SIZE(5 + 1); - *inst++ = PUSH_i32; - sljit_unaligned_store_s32(inst, srcw); - inst += sizeof(sljit_s32); - } RET(); return SLJIT_SUCCESS; } - /* --------------------------------------------------------------------- */ /* Extend input */ /* --------------------------------------------------------------------- */ diff --git a/pcre2-10.22/src/sljit/sljitNativeX86_common.c b/pcre2-10.32/src/sljit/sljitNativeX86_common.c similarity index 80% rename from pcre2-10.22/src/sljit/sljitNativeX86_common.c rename to pcre2-10.32/src/sljit/sljitNativeX86_common.c index aa5ba089d..6f02ee3e8 100644 --- a/pcre2-10.22/src/sljit/sljitNativeX86_common.c +++ b/pcre2-10.32/src/sljit/sljitNativeX86_common.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -26,7 +26,11 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) { +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + return "x86" SLJIT_CPUINFO " ABI:fastcall"; +#else return "x86" SLJIT_CPUINFO; +#endif } /* @@ -35,7 +39,7 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) 1 - ECX 2 - EDX 3 - EBX - 4 - none + 4 - ESP 5 - EBP 6 - ESI 7 - EDI @@ -47,7 +51,7 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) 1 - RCX 2 - RDX 3 - RBX - 4 - none + 4 - RSP 5 - RBP 6 - RSI 7 - RDI @@ -67,12 +71,15 @@ SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void) #define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2) static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 3] = { - 0, 0, 2, 1, 0, 0, 0, 0, 7, 6, 3, 4, 5 + 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 7, 6, 3, 4, 5 }; #define CHECK_EXTRA_REGS(p, w, do) \ - if (p >= SLJIT_R3 && p <= SLJIT_R6) { \ - w = SLJIT_LOCALS_OFFSET + ((p) - (SLJIT_R3 + 4)) * sizeof(sljit_sw); \ + if (p >= SLJIT_R3 && p <= SLJIT_S3) { \ + if (p <= compiler->scratches) \ + w = compiler->saveds_offset - ((p) - SLJIT_R2) * (sljit_sw)sizeof(sljit_sw); \ + else \ + w = compiler->locals_offset + ((p) - SLJIT_S2) * (sljit_sw)sizeof(sljit_sw); \ p = SLJIT_MEM1(SLJIT_SP); \ do; \ } @@ -82,31 +89,39 @@ static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 3] = { /* Last register + 1. */ #define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2) #define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3) -#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4) /* Note: r12 & 0x7 == 0b100, which decoded as SIB byte present Note: avoid to use r12 and r13 for memory addessing - therefore r12 is better for SAVED_EREG than SAVED_REG. */ + therefore r12 is better to be a higher saved register. */ #ifndef _WIN64 -/* 1st passed in rdi, 2nd argument passed in rsi, 3rd in rdx. */ -static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = { - 0, 0, 6, 1, 8, 11, 10, 12, 5, 13, 14, 15, 3, 4, 2, 7, 9 +/* Args: rdi(=7), rsi(=6), rdx(=2), rcx(=1), r8, r9. Scratches: rax(=0), r10, r11 */ +static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 4] = { + 0, 0, 6, 7, 1, 8, 11, 10, 12, 5, 13, 14, 15, 3, 4, 2, 9 }; /* low-map. reg_map & 0x7. */ -static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 5] = { - 0, 0, 6, 1, 0, 3, 2, 4, 5, 5, 6, 7, 3, 4, 2, 7, 1 +static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 4] = { + 0, 0, 6, 7, 1, 0, 3, 2, 4, 5, 5, 6, 7, 3, 4, 2, 1 }; #else -/* 1st passed in rcx, 2nd argument passed in rdx, 3rd in r8. */ -static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = { - 0, 0, 2, 1, 11, 12, 5, 13, 14, 15, 7, 6, 3, 4, 10, 8, 9 +/* Args: rcx(=1), rdx(=2), r8, r9. Scratches: rax(=0), r10, r11 */ +static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 4] = { + 0, 0, 2, 8, 1, 11, 12, 5, 13, 14, 15, 7, 6, 3, 4, 9, 10 }; /* low-map. reg_map & 0x7. */ -static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 5] = { - 0, 0, 2, 1, 3, 4, 5, 5, 6, 7, 7, 6, 3, 4, 2, 0, 1 +static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 4] = { + 0, 0, 2, 0, 1, 3, 4, 5, 5, 6, 7, 7, 6, 3, 4, 1, 2 }; #endif +/* Args: xmm0-xmm3 */ +static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = { + 4, 0, 1, 2, 3, 5, 6 +}; +/* low-map. freg_map & 0x7. */ +static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = { + 4, 0, 1, 2, 3, 5, 6 +}; + #define REX_W 0x48 #define REX_R 0x44 #define REX_X 0x42 @@ -166,7 +181,7 @@ static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 5] = { #define CALL_i32 0xe8 #define CALL_rm (/* GROUP_FF */ 2 << 3) #define CDQ 0x99 -#define CMOVNE_r_rm (/* GROUP_0F */ 0x45) +#define CMOVE_r_rm (/* GROUP_0F */ 0x44) #define CMP (/* BINARY */ 7 << 3) #define CMP_EAX_i32 0x3d #define CMP_r_rm 0x3b @@ -176,6 +191,8 @@ static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 5] = { #define CVTTSD2SI_r_xm 0x2c #define DIV (/* GROUP_F7 */ 6 << 3) #define DIVSD_x_xm 0x5e +#define FSTPS 0xd9 +#define FSTPD 0xdd #define INT3 0xcc #define IDIV (/* GROUP_F7 */ 7 << 3) #define IMUL (/* GROUP_F7 */ 5 << 3) @@ -214,6 +231,7 @@ static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 5] = { #define POP_r 0x58 #define POP_rm 0x8f #define POPF 0x9d +#define PREFETCH 0x18 #define PUSH_i32 0x68 #define PUSH_r 0x50 #define PUSH_rm (/* GROUP_FF */ 6 << 3) @@ -409,13 +427,13 @@ static sljit_u8 get_jump_code(sljit_s32 type) return 0; } +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) +static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_s32 type, sljit_sw executable_offset); +#else static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_s32 type); - -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) -static sljit_u8* generate_fixed_jump(sljit_u8 *code_ptr, sljit_sw addr, sljit_s32 type); #endif -static sljit_u8* generate_near_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_u8 *code, sljit_s32 type) +static sljit_u8* generate_near_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_u8 *code, sljit_s32 type, sljit_sw executable_offset) { sljit_s32 short_jump; sljit_uw label_addr; @@ -423,7 +441,8 @@ static sljit_u8* generate_near_jump_code(struct sljit_jump *jump, sljit_u8 *code if (jump->flags & JUMP_LABEL) label_addr = (sljit_uw)(code + jump->u.label->size); else - label_addr = jump->u.target; + label_addr = jump->u.target - executable_offset; + short_jump = (sljit_sw)(label_addr - (jump->addr + 2)) >= -128 && (sljit_sw)(label_addr - (jump->addr + 2)) <= 127; #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) @@ -458,11 +477,7 @@ static sljit_u8* generate_near_jump_code(struct sljit_jump *jump, sljit_u8 *code code_ptr += sizeof(sljit_s8); } else { jump->flags |= PATCH_MW; -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - code_ptr += sizeof(sljit_sw); -#else code_ptr += sizeof(sljit_s32); -#endif } return code_ptr; @@ -476,6 +491,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil sljit_u8 *buf_ptr; sljit_u8 *buf_end; sljit_u8 len; + sljit_sw executable_offset; + sljit_sw jump_addr; struct sljit_label *label; struct sljit_jump *jump; @@ -494,6 +511,8 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil label = compiler->labels; jump = compiler->jumps; const_ = compiler->consts; + executable_offset = SLJIT_EXEC_OFFSET(code); + do { buf_ptr = buf->memory; buf_end = buf_ptr + buf->used_size; @@ -506,35 +525,28 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil buf_ptr += len; } else { - if (*buf_ptr >= 4) { + if (*buf_ptr >= 2) { jump->addr = (sljit_uw)code_ptr; if (!(jump->flags & SLJIT_REWRITABLE_JUMP)) - code_ptr = generate_near_jump_code(jump, code_ptr, code, *buf_ptr - 4); - else - code_ptr = generate_far_jump_code(jump, code_ptr, *buf_ptr - 4); + code_ptr = generate_near_jump_code(jump, code_ptr, code, *buf_ptr - 2, executable_offset); + else { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + code_ptr = generate_far_jump_code(jump, code_ptr, *buf_ptr - 2, executable_offset); +#else + code_ptr = generate_far_jump_code(jump, code_ptr, *buf_ptr - 2); +#endif + } jump = jump->next; } else if (*buf_ptr == 0) { - label->addr = (sljit_uw)code_ptr; + label->addr = ((sljit_uw)code_ptr) + executable_offset; label->size = code_ptr - code; label = label->next; } - else if (*buf_ptr == 1) { + else { /* *buf_ptr is 1 */ const_->addr = ((sljit_uw)code_ptr) - sizeof(sljit_sw); const_ = const_->next; } - else { -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - *code_ptr++ = (*buf_ptr == 2) ? CALL_i32 : JMP_i32; - buf_ptr++; - sljit_unaligned_store_sw(code_ptr, *(sljit_sw*)buf_ptr - ((sljit_sw)code_ptr + sizeof(sljit_sw))); - code_ptr += sizeof(sljit_sw); - buf_ptr += sizeof(sljit_sw) - 1; -#else - code_ptr = generate_fixed_jump(code_ptr, *(sljit_sw*)(buf_ptr + 1), *buf_ptr); - buf_ptr += sizeof(sljit_sw); -#endif - } buf_ptr++; } } while (buf_ptr < buf_end); @@ -548,24 +560,26 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil jump = compiler->jumps; while (jump) { + jump_addr = jump->addr + executable_offset; + if (jump->flags & PATCH_MB) { - SLJIT_ASSERT((sljit_sw)(jump->u.label->addr - (jump->addr + sizeof(sljit_s8))) >= -128 && (sljit_sw)(jump->u.label->addr - (jump->addr + sizeof(sljit_s8))) <= 127); - *(sljit_u8*)jump->addr = (sljit_u8)(jump->u.label->addr - (jump->addr + sizeof(sljit_s8))); + SLJIT_ASSERT((sljit_sw)(jump->u.label->addr - (jump_addr + sizeof(sljit_s8))) >= -128 && (sljit_sw)(jump->u.label->addr - (jump_addr + sizeof(sljit_s8))) <= 127); + *(sljit_u8*)jump->addr = (sljit_u8)(jump->u.label->addr - (jump_addr + sizeof(sljit_s8))); } else if (jump->flags & PATCH_MW) { if (jump->flags & JUMP_LABEL) { #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)(jump->u.label->addr - (jump->addr + sizeof(sljit_sw)))); + sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)(jump->u.label->addr - (jump_addr + sizeof(sljit_sw)))); #else - SLJIT_ASSERT((sljit_sw)(jump->u.label->addr - (jump->addr + sizeof(sljit_s32))) >= HALFWORD_MIN && (sljit_sw)(jump->u.label->addr - (jump->addr + sizeof(sljit_s32))) <= HALFWORD_MAX); - sljit_unaligned_store_s32((void*)jump->addr, (sljit_s32)(jump->u.label->addr - (jump->addr + sizeof(sljit_s32)))); + SLJIT_ASSERT((sljit_sw)(jump->u.label->addr - (jump_addr + sizeof(sljit_s32))) >= HALFWORD_MIN && (sljit_sw)(jump->u.label->addr - (jump_addr + sizeof(sljit_s32))) <= HALFWORD_MAX); + sljit_unaligned_store_s32((void*)jump->addr, (sljit_s32)(jump->u.label->addr - (jump_addr + sizeof(sljit_s32)))); #endif } else { #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)(jump->u.target - (jump->addr + sizeof(sljit_sw)))); + sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)(jump->u.target - (jump_addr + sizeof(sljit_sw)))); #else - SLJIT_ASSERT((sljit_sw)(jump->u.target - (jump->addr + sizeof(sljit_s32))) >= HALFWORD_MIN && (sljit_sw)(jump->u.target - (jump->addr + sizeof(sljit_s32))) <= HALFWORD_MAX); - sljit_unaligned_store_s32((void*)jump->addr, (sljit_s32)(jump->u.target - (jump->addr + sizeof(sljit_s32)))); + SLJIT_ASSERT((sljit_sw)(jump->u.target - (jump_addr + sizeof(sljit_s32))) >= HALFWORD_MIN && (sljit_sw)(jump->u.target - (jump_addr + sizeof(sljit_s32))) <= HALFWORD_MAX); + sljit_unaligned_store_s32((void*)jump->addr, (sljit_s32)(jump->u.target - (jump_addr + sizeof(sljit_s32)))); #endif } } @@ -577,25 +591,67 @@ SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compil jump = jump->next; } - /* Maybe we waste some space because of short jumps. */ + /* Some space may be wasted because of short jumps. */ SLJIT_ASSERT(code_ptr <= code + compiler->size); compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_offset = executable_offset; compiler->executable_size = code_ptr - code; - return (void*)code; + return (void*)(code + executable_offset); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type) +{ + switch (feature_type) { + case SLJIT_HAS_FPU: +#ifdef SLJIT_IS_FPU_AVAILABLE + return SLJIT_IS_FPU_AVAILABLE; +#elif (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2) + if (cpu_has_sse2 == -1) + get_cpu_features(); + return cpu_has_sse2; +#else /* SLJIT_DETECT_SSE2 */ + return 1; +#endif /* SLJIT_DETECT_SSE2 */ + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + case SLJIT_HAS_VIRTUAL_REGISTERS: + return 1; +#endif + + case SLJIT_HAS_CLZ: + case SLJIT_HAS_CMOV: + if (cpu_has_cmov == -1) + get_cpu_features(); + return cpu_has_cmov; + + case SLJIT_HAS_SSE2: +#if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2) + if (cpu_has_sse2 == -1) + get_cpu_features(); + return cpu_has_sse2; +#else + return 1; +#endif + + default: + return 0; + } } /* --------------------------------------------------------------------- */ /* Operators */ /* --------------------------------------------------------------------- */ +#define BINARY_OPCODE(opcode) (((opcode ## _EAX_i32) << 24) | ((opcode ## _r_rm) << 16) | ((opcode ## _rm_r) << 8) | (opcode)) + static sljit_s32 emit_cum_binary(struct sljit_compiler *compiler, - sljit_u8 op_rm, sljit_u8 op_mr, sljit_u8 op_imm, sljit_u8 op_eax_imm, + sljit_u32 op_types, sljit_s32 dst, sljit_sw dstw, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w); static sljit_s32 emit_non_cum_binary(struct sljit_compiler *compiler, - sljit_u8 op_rm, sljit_u8 op_mr, sljit_u8 op_imm, sljit_u8 op_eax_imm, + sljit_u32 op_types, sljit_s32 dst, sljit_sw dstw, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w); @@ -604,69 +660,14 @@ static sljit_s32 emit_mov(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw); -static SLJIT_INLINE sljit_s32 emit_save_flags(struct sljit_compiler *compiler) -{ - sljit_u8 *inst; +#define EMIT_MOV(compiler, dst, dstw, src, srcw) \ + FAIL_IF(emit_mov(compiler, dst, dstw, src, srcw)); -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - inst = (sljit_u8*)ensure_buf(compiler, 1 + 5); - FAIL_IF(!inst); - INC_SIZE(5); -#else - inst = (sljit_u8*)ensure_buf(compiler, 1 + 6); - FAIL_IF(!inst); - INC_SIZE(6); - *inst++ = REX_W; -#endif - *inst++ = LEA_r_m; /* lea esp/rsp, [esp/rsp + sizeof(sljit_sw)] */ - *inst++ = 0x64; - *inst++ = 0x24; - *inst++ = (sljit_u8)sizeof(sljit_sw); - *inst++ = PUSHF; - compiler->flags_saved = 1; - return SLJIT_SUCCESS; -} +static SLJIT_INLINE sljit_s32 emit_sse2_store(struct sljit_compiler *compiler, + sljit_s32 single, sljit_s32 dst, sljit_sw dstw, sljit_s32 src); -static SLJIT_INLINE sljit_s32 emit_restore_flags(struct sljit_compiler *compiler, sljit_s32 keep_flags) -{ - sljit_u8 *inst; - -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - inst = (sljit_u8*)ensure_buf(compiler, 1 + 5); - FAIL_IF(!inst); - INC_SIZE(5); - *inst++ = POPF; -#else - inst = (sljit_u8*)ensure_buf(compiler, 1 + 6); - FAIL_IF(!inst); - INC_SIZE(6); - *inst++ = POPF; - *inst++ = REX_W; -#endif - *inst++ = LEA_r_m; /* lea esp/rsp, [esp/rsp - sizeof(sljit_sw)] */ - *inst++ = 0x64; - *inst++ = 0x24; - *inst++ = (sljit_u8)(-(sljit_s8)sizeof(sljit_sw)); - compiler->flags_saved = keep_flags; - return SLJIT_SUCCESS; -} - -#ifdef _WIN32 -#include - -static void SLJIT_CALL sljit_grow_stack(sljit_sw local_size) -{ - /* Workaround for calling the internal _chkstk() function on Windows. - This function touches all 4k pages belongs to the requested stack space, - which size is passed in local_size. This is necessary on Windows where - the stack can only grow in 4k steps. However, this function just burn - CPU cycles if the stack is large enough. However, you don't know it in - advance, so it must always be called. I think this is a bad design in - general even if it has some reasons. */ - *(volatile sljit_s32*)alloca(local_size) = 0; -} - -#endif +static SLJIT_INLINE sljit_s32 emit_sse2_load(struct sljit_compiler *compiler, + sljit_s32 single, sljit_s32 dst, sljit_s32 src, sljit_sw srcw); #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) #include "sljitNativeX86_32.c" @@ -680,15 +681,8 @@ static sljit_s32 emit_mov(struct sljit_compiler *compiler, { sljit_u8* inst; - if (dst == SLJIT_UNUSED) { - /* No destination, doesn't need to setup flags. */ - if (src & SLJIT_MEM) { - inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src, srcw); - FAIL_IF(!inst); - *inst = MOV_r_rm; - } - return SLJIT_SUCCESS; - } + SLJIT_ASSERT(dst != SLJIT_UNUSED); + if (FAST_IS_REG(src)) { inst = emit_x86_instruction(compiler, 1, src, 0, dst, dstw); FAIL_IF(!inst); @@ -710,8 +704,10 @@ static sljit_s32 emit_mov(struct sljit_compiler *compiler, } #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) if (!compiler->mode32 && NOT_HALFWORD(srcw)) { - FAIL_IF(emit_load_imm64(compiler, TMP_REG2, srcw)); - inst = emit_x86_instruction(compiler, 1, TMP_REG2, 0, dst, dstw); + /* Immediate to memory move. Only SLJIT_MOV operation copies + an immediate directly into memory so TMP_REG1 can be used. */ + FAIL_IF(emit_load_imm64(compiler, TMP_REG1, srcw)); + inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw); FAIL_IF(!inst); *inst = MOV_rm_r; return SLJIT_SUCCESS; @@ -729,7 +725,8 @@ static sljit_s32 emit_mov(struct sljit_compiler *compiler, return SLJIT_SUCCESS; } - /* Memory to memory move. Requires two instruction. */ + /* Memory to memory move. Only SLJIT_MOV operation copies + data from memory to memory so TMP_REG1 can be used. */ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src, srcw); FAIL_IF(!inst); *inst = MOV_r_rm; @@ -739,9 +736,6 @@ static sljit_s32 emit_mov(struct sljit_compiler *compiler, return SLJIT_SUCCESS; } -#define EMIT_MOV(compiler, dst, dstw, src, srcw) \ - FAIL_IF(emit_mov(compiler, dst, dstw, src, srcw)); - SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op) { sljit_u8 *inst; @@ -771,20 +765,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compile case SLJIT_DIVMOD_SW: case SLJIT_DIV_UW: case SLJIT_DIV_SW: - compiler->flags_saved = 0; #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) #ifdef _WIN64 - SLJIT_COMPILE_ASSERT( + SLJIT_ASSERT( reg_map[SLJIT_R0] == 0 && reg_map[SLJIT_R1] == 2 - && reg_map[TMP_REG1] > 7, - invalid_register_assignment_for_div_mul); + && reg_map[TMP_REG1] > 7); #else - SLJIT_COMPILE_ASSERT( + SLJIT_ASSERT( reg_map[SLJIT_R0] == 0 && reg_map[SLJIT_R1] < 7 - && reg_map[TMP_REG1] == 2, - invalid_register_assignment_for_div_mul); + && reg_map[TMP_REG1] == 2); #endif compiler->mode32 = op & SLJIT_I32_OP; #endif @@ -908,9 +899,6 @@ static sljit_s32 emit_mov_byte(struct sljit_compiler *compiler, sljit_s32 sign, compiler->mode32 = 0; #endif - if (dst == SLJIT_UNUSED && !(src & SLJIT_MEM)) - return SLJIT_SUCCESS; /* Empty instruction. */ - if (src & SLJIT_IMM) { if (FAST_IS_REG(dst)) { #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) @@ -1039,6 +1027,30 @@ static sljit_s32 emit_mov_byte(struct sljit_compiler *compiler, sljit_s32 sign, return SLJIT_SUCCESS; } +static sljit_s32 emit_prefetch(struct sljit_compiler *compiler, sljit_s32 op, + sljit_s32 src, sljit_sw srcw) +{ + sljit_u8* inst; + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 1; +#endif + + inst = emit_x86_instruction(compiler, 2, 0, 0, src, srcw); + FAIL_IF(!inst); + *inst++ = GROUP_0F; + *inst++ = PREFETCH; + + if (op >= SLJIT_MOV_U8 && op <= SLJIT_MOV_S8) + *inst |= (3 << 3); + else if (op >= SLJIT_MOV_U16 && op <= SLJIT_MOV_S16) + *inst |= (2 << 3); + else + *inst |= (1 << 3); + + return SLJIT_SUCCESS; +} + static sljit_s32 emit_mov_half(struct sljit_compiler *compiler, sljit_s32 sign, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) @@ -1050,9 +1062,6 @@ static sljit_s32 emit_mov_half(struct sljit_compiler *compiler, sljit_s32 sign, compiler->mode32 = 0; #endif - if (dst == SLJIT_UNUSED && !(src & SLJIT_MEM)) - return SLJIT_SUCCESS; /* Empty instruction. */ - if (src & SLJIT_IMM) { if (FAST_IS_REG(dst)) { #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) @@ -1096,14 +1105,6 @@ static sljit_s32 emit_unary(struct sljit_compiler *compiler, sljit_u8 opcode, { sljit_u8* inst; - if (dst == SLJIT_UNUSED) { - EMIT_MOV(compiler, TMP_REG1, 0, src, srcw); - inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0); - FAIL_IF(!inst); - *inst++ = GROUP_F7; - *inst |= opcode; - return SLJIT_SUCCESS; - } if (dst == src && dstw == srcw) { /* Same input and output */ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw); @@ -1112,14 +1113,19 @@ static sljit_s32 emit_unary(struct sljit_compiler *compiler, sljit_u8 opcode, *inst |= opcode; return SLJIT_SUCCESS; } + + if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) + dst = TMP_REG1; + if (FAST_IS_REG(dst)) { EMIT_MOV(compiler, dst, 0, src, srcw); - inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw); + inst = emit_x86_instruction(compiler, 1, 0, 0, dst, 0); FAIL_IF(!inst); *inst++ = GROUP_F7; *inst |= opcode; return SLJIT_SUCCESS; } + EMIT_MOV(compiler, TMP_REG1, 0, src, srcw); inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0); FAIL_IF(!inst); @@ -1135,20 +1141,12 @@ static sljit_s32 emit_not_with_flags(struct sljit_compiler *compiler, { sljit_u8* inst; - if (dst == SLJIT_UNUSED) { - EMIT_MOV(compiler, TMP_REG1, 0, src, srcw); - inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0); - FAIL_IF(!inst); - *inst++ = GROUP_F7; - *inst |= NOT_rm; - inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, TMP_REG1, 0); - FAIL_IF(!inst); - *inst = OR_r_rm; - return SLJIT_SUCCESS; - } + if (dst == SLJIT_UNUSED) + dst = TMP_REG1; + if (FAST_IS_REG(dst)) { EMIT_MOV(compiler, dst, 0, src, srcw); - inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw); + inst = emit_x86_instruction(compiler, 1, 0, 0, dst, 0); FAIL_IF(!inst); *inst++ = GROUP_F7; *inst |= NOT_rm; @@ -1157,6 +1155,7 @@ static sljit_s32 emit_not_with_flags(struct sljit_compiler *compiler, *inst = OR_r_rm; return SLJIT_SUCCESS; } + EMIT_MOV(compiler, TMP_REG1, 0, src, srcw); inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0); FAIL_IF(!inst); @@ -1169,6 +1168,10 @@ static sljit_s32 emit_not_with_flags(struct sljit_compiler *compiler, return SLJIT_SUCCESS; } +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) +static const sljit_sw emit_clz_arg = 32 + 31; +#endif + static sljit_s32 emit_clz(struct sljit_compiler *compiler, sljit_s32 op_flags, sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) @@ -1177,104 +1180,54 @@ static sljit_s32 emit_clz(struct sljit_compiler *compiler, sljit_s32 op_flags, sljit_s32 dst_r; SLJIT_UNUSED_ARG(op_flags); - if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) { - /* Just set the zero flag. */ - EMIT_MOV(compiler, TMP_REG1, 0, src, srcw); - inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0); - FAIL_IF(!inst); - *inst++ = GROUP_F7; - *inst |= NOT_rm; -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, 31, TMP_REG1, 0); -#else - inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, !(op_flags & SLJIT_I32_OP) ? 63 : 31, TMP_REG1, 0); -#endif - FAIL_IF(!inst); - *inst |= SHR; - return SLJIT_SUCCESS; - } - if (SLJIT_UNLIKELY(src & SLJIT_IMM)) { - EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, srcw); - src = TMP_REG1; - srcw = 0; - } + if (cpu_has_cmov == -1) + get_cpu_features(); - inst = emit_x86_instruction(compiler, 2, TMP_REG1, 0, src, srcw); + dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; + + inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw); FAIL_IF(!inst); *inst++ = GROUP_0F; *inst = BSR_r_rm; #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - if (FAST_IS_REG(dst)) - dst_r = dst; - else { - /* Find an unused temporary register. */ - if ((dst & REG_MASK) != SLJIT_R0 && (dst & OFFS_REG_MASK) != TO_OFFS_REG(SLJIT_R0)) - dst_r = SLJIT_R0; - else if ((dst & REG_MASK) != SLJIT_R1 && (dst & OFFS_REG_MASK) != TO_OFFS_REG(SLJIT_R1)) - dst_r = SLJIT_R1; - else - dst_r = SLJIT_R2; - EMIT_MOV(compiler, dst, dstw, dst_r, 0); - } - EMIT_MOV(compiler, dst_r, 0, SLJIT_IMM, 32 + 31); -#else - dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2; - compiler->mode32 = 0; - EMIT_MOV(compiler, dst_r, 0, SLJIT_IMM, !(op_flags & SLJIT_I32_OP) ? 64 + 63 : 32 + 31); - compiler->mode32 = op_flags & SLJIT_I32_OP; -#endif - - if (cpu_has_cmov == -1) - get_cpu_features(); - if (cpu_has_cmov) { - inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG1, 0); + if (dst_r != TMP_REG1) { + EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, 32 + 31); + inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG1, 0); + } + else + inst = emit_x86_instruction(compiler, 2, dst_r, 0, SLJIT_MEM0(), (sljit_sw)&emit_clz_arg); + FAIL_IF(!inst); *inst++ = GROUP_0F; - *inst = CMOVNE_r_rm; - } else { -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - inst = (sljit_u8*)ensure_buf(compiler, 1 + 4); - FAIL_IF(!inst); - INC_SIZE(4); - - *inst++ = JE_i8; - *inst++ = 2; - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_map[dst_r] << 3) | reg_map[TMP_REG1]; -#else - inst = (sljit_u8*)ensure_buf(compiler, 1 + 5); - FAIL_IF(!inst); - INC_SIZE(5); - - *inst++ = JE_i8; - *inst++ = 3; - *inst++ = REX_W | (reg_map[dst_r] >= 8 ? REX_R : 0) | (reg_map[TMP_REG1] >= 8 ? REX_B : 0); - *inst++ = MOV_r_rm; - *inst++ = MOD_REG | (reg_lmap[dst_r] << 3) | reg_lmap[TMP_REG1]; -#endif + *inst = CMOVE_r_rm; } + else + FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, 32 + 31)); -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 31, dst_r, 0); #else + if (cpu_has_cmov) { + EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, !(op_flags & SLJIT_I32_OP) ? (64 + 63) : (32 + 31)); + + inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0); + FAIL_IF(!inst); + *inst++ = GROUP_0F; + *inst = CMOVE_r_rm; + } + else + FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, !(op_flags & SLJIT_I32_OP) ? (64 + 63) : (32 + 31))); + inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, !(op_flags & SLJIT_I32_OP) ? 63 : 31, dst_r, 0); #endif + FAIL_IF(!inst); *(inst + 1) |= XOR; -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - if (dst & SLJIT_MEM) { - inst = emit_x86_instruction(compiler, 1, dst_r, 0, dst, dstw); - FAIL_IF(!inst); - *inst = XCHG_r_rm; - } -#else if (dst & SLJIT_MEM) - EMIT_MOV(compiler, dst, dstw, TMP_REG2, 0); -#endif + EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0); return SLJIT_SUCCESS; } @@ -1282,14 +1235,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) { - sljit_u8* inst; - sljit_s32 update = 0; sljit_s32 op_flags = GET_ALL_FLAGS(op); #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) sljit_s32 dst_is_ereg = 0; - sljit_s32 src_is_ereg = 0; -#else -# define src_is_ereg 0 #endif CHECK_ERROR(); @@ -1298,38 +1246,40 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile ADJUST_LOCAL_OFFSET(src, srcw); CHECK_EXTRA_REGS(dst, dstw, dst_is_ereg = 1); - CHECK_EXTRA_REGS(src, srcw, src_is_ereg = 1); + CHECK_EXTRA_REGS(src, srcw, (void)0); #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) compiler->mode32 = op_flags & SLJIT_I32_OP; #endif + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) { + if (op <= SLJIT_MOV_P && (src & SLJIT_MEM)) + return emit_prefetch(compiler, op, src, srcw); + return SLJIT_SUCCESS; + } + op = GET_OPCODE(op); - if (op >= SLJIT_MOV && op <= SLJIT_MOVU_P) { + + if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) { #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) compiler->mode32 = 0; #endif - if (op_flags & SLJIT_I32_OP) { - if (FAST_IS_REG(src) && src == dst) { - if (!TYPE_CAST_NEEDED(op)) - return SLJIT_SUCCESS; - } -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - if (op == SLJIT_MOV_S32 && (src & SLJIT_MEM)) - op = SLJIT_MOV_U32; - if (op == SLJIT_MOVU_S32 && (src & SLJIT_MEM)) - op = SLJIT_MOVU_U32; - if (op == SLJIT_MOV_U32 && (src & SLJIT_IMM)) - op = SLJIT_MOV_S32; - if (op == SLJIT_MOVU_U32 && (src & SLJIT_IMM)) - op = SLJIT_MOVU_S32; -#endif + if (FAST_IS_REG(src) && src == dst) { + if (!TYPE_CAST_NEEDED(op)) + return SLJIT_SUCCESS; } - SLJIT_COMPILE_ASSERT(SLJIT_MOV + 8 == SLJIT_MOVU, movu_offset); - if (op >= SLJIT_MOVU) { - update = 1; - op -= 8; + if (op_flags & SLJIT_I32_OP) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if (src & SLJIT_MEM) { + if (op == SLJIT_MOV_S32) + op = SLJIT_MOV_U32; + } + else if (src & SLJIT_IMM) { + if (op == SLJIT_MOV_U32) + op = SLJIT_MOV_S32; + } +#endif } if (src & SLJIT_IMM) { @@ -1361,14 +1311,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile #endif } - if (SLJIT_UNLIKELY(update) && (src & SLJIT_MEM) && !src_is_ereg && (src & REG_MASK) && (srcw != 0 || (src & OFFS_REG_MASK) != 0)) { - inst = emit_x86_instruction(compiler, 1, src & REG_MASK, 0, src, srcw); - FAIL_IF(!inst); - *inst = LEA_r_m; - src &= SLJIT_MEM | 0xf; - srcw = 0; - } - #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) if (SLJIT_UNLIKELY(dst_is_ereg) && (!(op == SLJIT_MOV || op == SLJIT_MOV_U32 || op == SLJIT_MOV_S32 || op == SLJIT_MOV_P) || (src & SLJIT_MEM))) { SLJIT_ASSERT(dst == SLJIT_MEM1(SLJIT_SP)); @@ -1411,40 +1353,23 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile if (SLJIT_UNLIKELY(dst_is_ereg) && dst == TMP_REG1) return emit_mov(compiler, SLJIT_MEM1(SLJIT_SP), dstw, TMP_REG1, 0); #endif - - if (SLJIT_UNLIKELY(update) && (dst & SLJIT_MEM) && (dst & REG_MASK) && (dstw != 0 || (dst & OFFS_REG_MASK) != 0)) { - inst = emit_x86_instruction(compiler, 1, dst & REG_MASK, 0, dst, dstw); - FAIL_IF(!inst); - *inst = LEA_r_m; - } return SLJIT_SUCCESS; } - if (SLJIT_UNLIKELY(GET_FLAGS(op_flags))) - compiler->flags_saved = 0; - switch (op) { case SLJIT_NOT: - if (SLJIT_UNLIKELY(op_flags & SLJIT_SET_E)) + if (SLJIT_UNLIKELY(op_flags & SLJIT_SET_Z)) return emit_not_with_flags(compiler, dst, dstw, src, srcw); return emit_unary(compiler, NOT_rm, dst, dstw, src, srcw); case SLJIT_NEG: - if (SLJIT_UNLIKELY(op_flags & SLJIT_KEEP_FLAGS) && !compiler->flags_saved) - FAIL_IF(emit_save_flags(compiler)); return emit_unary(compiler, NEG_rm, dst, dstw, src, srcw); case SLJIT_CLZ: - if (SLJIT_UNLIKELY(op_flags & SLJIT_KEEP_FLAGS) && !compiler->flags_saved) - FAIL_IF(emit_save_flags(compiler)); return emit_clz(compiler, op_flags, dst, dstw, src, srcw); } return SLJIT_SUCCESS; - -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) -# undef src_is_ereg -#endif } #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) @@ -1456,8 +1381,8 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile *(inst + 1) |= (op_imm); \ } \ else { \ - FAIL_IF(emit_load_imm64(compiler, TMP_REG2, immw)); \ - inst = emit_x86_instruction(compiler, 1, TMP_REG2, 0, arg, argw); \ + FAIL_IF(emit_load_imm64(compiler, (arg == TMP_REG1) ? TMP_REG2 : TMP_REG1, immw)); \ + inst = emit_x86_instruction(compiler, 1, (arg == TMP_REG1) ? TMP_REG2 : TMP_REG1, 0, arg, argw); \ FAIL_IF(!inst); \ *inst = (op_mr); \ } @@ -1478,12 +1403,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compile #endif static sljit_s32 emit_cum_binary(struct sljit_compiler *compiler, - sljit_u8 op_rm, sljit_u8 op_mr, sljit_u8 op_imm, sljit_u8 op_eax_imm, + sljit_u32 op_types, sljit_s32 dst, sljit_sw dstw, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { sljit_u8* inst; + sljit_u8 op_eax_imm = (op_types >> 24); + sljit_u8 op_rm = (op_types >> 16) & 0xff; + sljit_u8 op_mr = (op_types >> 8) & 0xff; + sljit_u8 op_imm = op_types & 0xff; if (dst == SLJIT_UNUSED) { EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w); @@ -1594,12 +1523,16 @@ static sljit_s32 emit_cum_binary(struct sljit_compiler *compiler, } static sljit_s32 emit_non_cum_binary(struct sljit_compiler *compiler, - sljit_u8 op_rm, sljit_u8 op_mr, sljit_u8 op_imm, sljit_u8 op_eax_imm, + sljit_u32 op_types, sljit_s32 dst, sljit_sw dstw, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { sljit_u8* inst; + sljit_u8 op_eax_imm = (op_types >> 24); + sljit_u8 op_rm = (op_types >> 16) & 0xff; + sljit_u8 op_mr = (op_types >> 8) & 0xff; + sljit_u8 op_imm = op_types & 0xff; if (dst == SLJIT_UNUSED) { EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w); @@ -1683,7 +1616,7 @@ static sljit_s32 emit_mul(struct sljit_compiler *compiler, sljit_u8* inst; sljit_s32 dst_r; - dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; + dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1; /* Register destination. */ if (dst_r == src1 && !(src2 & SLJIT_IMM)) { @@ -1735,9 +1668,9 @@ static sljit_s32 emit_mul(struct sljit_compiler *compiler, sljit_unaligned_store_s32(inst, (sljit_s32)src1w); } else { - EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, src1w); if (dst_r != src2) EMIT_MOV(compiler, dst_r, 0, src2, src2w); + FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src1w)); inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0); FAIL_IF(!inst); *inst++ = GROUP_0F; @@ -1778,9 +1711,9 @@ static sljit_s32 emit_mul(struct sljit_compiler *compiler, sljit_unaligned_store_s32(inst, (sljit_s32)src2w); } else { - EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, src2w); if (dst_r != src1) EMIT_MOV(compiler, dst_r, 0, src1, src1w); + FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src2w)); inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0); FAIL_IF(!inst); *inst++ = GROUP_0F; @@ -1799,13 +1732,13 @@ static sljit_s32 emit_mul(struct sljit_compiler *compiler, *inst = IMUL_r_rm; } - if (dst_r == TMP_REG1) + if (dst & SLJIT_MEM) EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0); return SLJIT_SUCCESS; } -static sljit_s32 emit_lea_binary(struct sljit_compiler *compiler, sljit_s32 keep_flags, +static sljit_s32 emit_lea_binary(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) @@ -1814,12 +1747,10 @@ static sljit_s32 emit_lea_binary(struct sljit_compiler *compiler, sljit_s32 keep sljit_s32 dst_r, done = 0; /* These cases better be left to handled by normal way. */ - if (!keep_flags) { - if (dst == src1 && dstw == src1w) - return SLJIT_ERR_UNSUPPORTED; - if (dst == src2 && dstw == src2w) - return SLJIT_ERR_UNSUPPORTED; - } + if (dst == src1 && dstw == src1w) + return SLJIT_ERR_UNSUPPORTED; + if (dst == src2 && dstw == src2w) + return SLJIT_ERR_UNSUPPORTED; dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; @@ -1931,7 +1862,7 @@ static sljit_s32 emit_test_binary(struct sljit_compiler *compiler, } #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - if (src2 == SLJIT_R0 && (src2 & SLJIT_IMM) && (src1w > 127 || src1w < -128) && (compiler->mode32 || IS_HALFWORD(src1w))) { + if (src2 == SLJIT_R0 && (src1 & SLJIT_IMM) && (src1w > 127 || src1w < -128) && (compiler->mode32 || IS_HALFWORD(src1w))) { #else if (src2 == SLJIT_R0 && (src1 & SLJIT_IMM) && (src1w > 127 || src1w < -128)) { #endif @@ -1948,8 +1879,8 @@ static sljit_s32 emit_test_binary(struct sljit_compiler *compiler, *inst = GROUP_F7; } else { - FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src2w)); - inst = emit_x86_instruction(compiler, 1, TMP_REG2, 0, src1, src1w); + FAIL_IF(emit_load_imm64(compiler, TMP_REG1, src2w)); + inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src1, src1w); FAIL_IF(!inst); *inst = TEST_rm_r; } @@ -1977,8 +1908,8 @@ static sljit_s32 emit_test_binary(struct sljit_compiler *compiler, *inst = GROUP_F7; } else { - FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src1w)); - inst = emit_x86_instruction(compiler, 1, TMP_REG2, 0, src2, src2w); + FAIL_IF(emit_load_imm64(compiler, TMP_REG1, src1w)); + inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w); FAIL_IF(!inst); *inst = TEST_rm_r; } @@ -2079,7 +2010,7 @@ static sljit_s32 emit_shift(struct sljit_compiler *compiler, *inst |= mode; EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); } - else if (FAST_IS_REG(dst) && dst != src2 && !ADDRESSING_DEPENDS_ON(src2, dst)) { + else if (SLOW_IS_REG(dst) && dst != src2 && !ADDRESSING_DEPENDS_ON(src2, dst)) { if (src1 != dst) EMIT_MOV(compiler, dst, 0, src1, src1w); EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_PREF_SHIFT_REG, 0); @@ -2090,25 +2021,26 @@ static sljit_s32 emit_shift(struct sljit_compiler *compiler, EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); } else { - /* This case is really difficult, since ecx itself may used for - addressing, and we must ensure to work even in that case. */ + /* This case is complex since ecx itself may be used for + addressing, and this case must be supported as well. */ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w); -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_PREF_SHIFT_REG, 0); -#else - /* [esp+0] contains the flags. */ - EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), sizeof(sljit_sw), SLJIT_PREF_SHIFT_REG, 0); -#endif +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), 0, SLJIT_PREF_SHIFT_REG, 0); EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); FAIL_IF(!inst); *inst |= mode; -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG2, 0); + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, SLJIT_MEM1(SLJIT_SP), 0); #else - EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, SLJIT_MEM1(SLJIT_SP), sizeof(sljit_sw)); + EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_PREF_SHIFT_REG, 0); + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); + inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0); + FAIL_IF(!inst); + *inst |= mode; + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG2, 0); #endif - EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0); + if (dst != SLJIT_UNUSED) + return emit_mov(compiler, dst, dstw, TMP_REG1, 0); } return SLJIT_SUCCESS; @@ -2132,7 +2064,7 @@ static sljit_s32 emit_shift_with_flags(struct sljit_compiler *compiler, if (!set_flags) return emit_mov(compiler, dst, dstw, src1, src1w); /* OR dst, src, 0 */ - return emit_cum_binary(compiler, OR_r_rm, OR_rm_r, OR, OR_EAX_i32, + return emit_cum_binary(compiler, BINARY_OPCODE(OR), dst, dstw, src1, src1w, SLJIT_IMM, 0); } @@ -2142,10 +2074,10 @@ static sljit_s32 emit_shift_with_flags(struct sljit_compiler *compiler, if (!FAST_IS_REG(dst)) FAIL_IF(emit_cmp_binary(compiler, src1, src1w, SLJIT_IMM, 0)); - FAIL_IF(emit_shift(compiler,mode, dst, dstw, src1, src1w, src2, src2w)); + FAIL_IF(emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w)); if (FAST_IS_REG(dst)) - return emit_cmp_binary(compiler, dst, dstw, SLJIT_IMM, 0); + return emit_cmp_binary(compiler, (dst == SLJIT_UNUSED) ? TMP_REG1 : dst, dstw, SLJIT_IMM, 0); return SLJIT_SUCCESS; } @@ -2167,77 +2099,54 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compile compiler->mode32 = op & SLJIT_I32_OP; #endif - if (GET_OPCODE(op) >= SLJIT_MUL) { - if (SLJIT_UNLIKELY(GET_FLAGS(op))) - compiler->flags_saved = 0; - else if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved) - FAIL_IF(emit_save_flags(compiler)); - } + if (dst == SLJIT_UNUSED && !HAS_FLAGS(op)) + return SLJIT_SUCCESS; switch (GET_OPCODE(op)) { case SLJIT_ADD: - if (!GET_FLAGS(op)) { - if (emit_lea_binary(compiler, op & SLJIT_KEEP_FLAGS, dst, dstw, src1, src1w, src2, src2w) != SLJIT_ERR_UNSUPPORTED) + if (!HAS_FLAGS(op)) { + if (emit_lea_binary(compiler, dst, dstw, src1, src1w, src2, src2w) != SLJIT_ERR_UNSUPPORTED) return compiler->error; } - else - compiler->flags_saved = 0; - if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved) - FAIL_IF(emit_save_flags(compiler)); - return emit_cum_binary(compiler, ADD_r_rm, ADD_rm_r, ADD, ADD_EAX_i32, + return emit_cum_binary(compiler, BINARY_OPCODE(ADD), dst, dstw, src1, src1w, src2, src2w); case SLJIT_ADDC: - if (SLJIT_UNLIKELY(compiler->flags_saved)) /* C flag must be restored. */ - FAIL_IF(emit_restore_flags(compiler, 1)); - else if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS)) - FAIL_IF(emit_save_flags(compiler)); - if (SLJIT_UNLIKELY(GET_FLAGS(op))) - compiler->flags_saved = 0; - return emit_cum_binary(compiler, ADC_r_rm, ADC_rm_r, ADC, ADC_EAX_i32, + return emit_cum_binary(compiler, BINARY_OPCODE(ADC), dst, dstw, src1, src1w, src2, src2w); case SLJIT_SUB: - if (!GET_FLAGS(op)) { - if ((src2 & SLJIT_IMM) && emit_lea_binary(compiler, op & SLJIT_KEEP_FLAGS, dst, dstw, src1, src1w, SLJIT_IMM, -src2w) != SLJIT_ERR_UNSUPPORTED) + if (!HAS_FLAGS(op)) { + if ((src2 & SLJIT_IMM) && emit_lea_binary(compiler, dst, dstw, src1, src1w, SLJIT_IMM, -src2w) != SLJIT_ERR_UNSUPPORTED) return compiler->error; } - else - compiler->flags_saved = 0; - if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved) - FAIL_IF(emit_save_flags(compiler)); + if (dst == SLJIT_UNUSED) return emit_cmp_binary(compiler, src1, src1w, src2, src2w); - return emit_non_cum_binary(compiler, SUB_r_rm, SUB_rm_r, SUB, SUB_EAX_i32, + return emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), dst, dstw, src1, src1w, src2, src2w); case SLJIT_SUBC: - if (SLJIT_UNLIKELY(compiler->flags_saved)) /* C flag must be restored. */ - FAIL_IF(emit_restore_flags(compiler, 1)); - else if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS)) - FAIL_IF(emit_save_flags(compiler)); - if (SLJIT_UNLIKELY(GET_FLAGS(op))) - compiler->flags_saved = 0; - return emit_non_cum_binary(compiler, SBB_r_rm, SBB_rm_r, SBB, SBB_EAX_i32, + return emit_non_cum_binary(compiler, BINARY_OPCODE(SBB), dst, dstw, src1, src1w, src2, src2w); case SLJIT_MUL: return emit_mul(compiler, dst, dstw, src1, src1w, src2, src2w); case SLJIT_AND: if (dst == SLJIT_UNUSED) return emit_test_binary(compiler, src1, src1w, src2, src2w); - return emit_cum_binary(compiler, AND_r_rm, AND_rm_r, AND, AND_EAX_i32, + return emit_cum_binary(compiler, BINARY_OPCODE(AND), dst, dstw, src1, src1w, src2, src2w); case SLJIT_OR: - return emit_cum_binary(compiler, OR_r_rm, OR_rm_r, OR, OR_EAX_i32, + return emit_cum_binary(compiler, BINARY_OPCODE(OR), dst, dstw, src1, src1w, src2, src2w); case SLJIT_XOR: - return emit_cum_binary(compiler, XOR_r_rm, XOR_rm_r, XOR, XOR_EAX_i32, + return emit_cum_binary(compiler, BINARY_OPCODE(XOR), dst, dstw, src1, src1w, src2, src2w); case SLJIT_SHL: - return emit_shift_with_flags(compiler, SHL, GET_FLAGS(op), + return emit_shift_with_flags(compiler, SHL, HAS_FLAGS(op), dst, dstw, src1, src1w, src2, src2w); case SLJIT_LSHR: - return emit_shift_with_flags(compiler, SHR, GET_FLAGS(op), + return emit_shift_with_flags(compiler, SHR, HAS_FLAGS(op), dst, dstw, src1, src1w, src2, src2w); case SLJIT_ASHR: - return emit_shift_with_flags(compiler, SAR, GET_FLAGS(op), + return emit_shift_with_flags(compiler, SAR, HAS_FLAGS(op), dst, dstw, src1, src1w, src2, src2w); } @@ -2248,7 +2157,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) { CHECK_REG_INDEX(check_sljit_get_register_index(reg)); #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - if (reg >= SLJIT_R3 && reg <= SLJIT_R6) + if (reg >= SLJIT_R3 && reg <= SLJIT_R8) return -1; #endif return reg_map[reg]; @@ -2257,7 +2166,11 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg) SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg) { CHECK_REG_INDEX(check_sljit_get_float_register_index(reg)); +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) return reg; +#else + return freg_map[reg]; +#endif } SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler, @@ -2279,36 +2192,25 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *c /* Floating point operators */ /* --------------------------------------------------------------------- */ -/* Alignment + 2 * 16 bytes. */ -static sljit_s32 sse2_data[3 + (4 + 4) * 2]; +/* Alignment(3) + 4 * 16 bytes. */ +static sljit_s32 sse2_data[3 + (4 * 4)]; static sljit_s32 *sse2_buffer; static void init_compiler(void) { + /* Align to 16 bytes. */ sse2_buffer = (sljit_s32*)(((sljit_uw)sse2_data + 15) & ~0xf); - /* Single precision constants. */ + + /* Single precision constants (each constant is 16 byte long). */ sse2_buffer[0] = 0x80000000; sse2_buffer[4] = 0x7fffffff; - /* Double precision constants. */ + /* Double precision constants (each constant is 16 byte long). */ sse2_buffer[8] = 0; sse2_buffer[9] = 0x80000000; sse2_buffer[12] = 0xffffffff; sse2_buffer[13] = 0x7fffffff; } -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_is_fpu_available(void) -{ -#ifdef SLJIT_IS_FPU_AVAILABLE - return SLJIT_IS_FPU_AVAILABLE; -#elif (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2) - if (cpu_has_sse2 == -1) - get_cpu_features(); - return cpu_has_sse2; -#else /* SLJIT_DETECT_SSE2 */ - return 1; -#endif /* SLJIT_DETECT_SSE2 */ -} - static sljit_s32 emit_sse2(struct sljit_compiler *compiler, sljit_u8 opcode, sljit_s32 single, sljit_s32 xmm1, sljit_s32 xmm2, sljit_sw xmm2w) { @@ -2349,7 +2251,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp sljit_s32 dst, sljit_sw dstw, sljit_s32 src, sljit_sw srcw) { - sljit_s32 dst_r = SLOW_IS_REG(dst) ? dst : TMP_REG1; + sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1; sljit_u8 *inst; #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) @@ -2362,7 +2264,7 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_comp *inst++ = GROUP_0F; *inst = CVTTSD2SI_r_xm; - if (dst_r == TMP_REG1 && dst != SLJIT_UNUSED) + if (dst & SLJIT_MEM) return emit_mov(compiler, dst, dstw, TMP_REG1, 0); return SLJIT_SUCCESS; } @@ -2406,11 +2308,11 @@ static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compile sljit_s32 src1, sljit_sw src1w, sljit_s32 src2, sljit_sw src2w) { - compiler->flags_saved = 0; if (!FAST_IS_REG(src1)) { FAIL_IF(emit_sse2_load(compiler, op & SLJIT_F32_OP, TMP_FREG, src1, src1w)); src1 = TMP_FREG; } + return emit_sse2_logic(compiler, UCOMISD_x_xm, !(op & SLJIT_F32_OP), src1, src2, src2w); } @@ -2455,7 +2357,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compil return SLJIT_SUCCESS; } - if (SLOW_IS_REG(dst)) { + if (FAST_IS_REG(dst)) { dst_r = dst; if (dst != src) FAIL_IF(emit_sse2_load(compiler, op & SLJIT_F32_OP, dst_r, src, srcw)); @@ -2553,11 +2455,6 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compi CHECK_ERROR_PTR(); CHECK_PTR(check_sljit_emit_label(compiler)); - /* We should restore the flags before the label, - since other taken jumps has their own flags as well. */ - if (SLJIT_UNLIKELY(compiler->flags_saved)) - PTR_FAIL_IF(emit_restore_flags(compiler, 0)); - if (compiler->last_label && compiler->last_label->size == compiler->size) return compiler->last_label; @@ -2582,20 +2479,11 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile CHECK_ERROR_PTR(); CHECK_PTR(check_sljit_emit_jump(compiler, type)); - if (SLJIT_UNLIKELY(compiler->flags_saved)) { - if ((type & 0xff) <= SLJIT_JUMP) - PTR_FAIL_IF(emit_restore_flags(compiler, 0)); - compiler->flags_saved = 0; - } - jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); PTR_FAIL_IF_NULL(jump); set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); type &= 0xff; - if (type >= SLJIT_CALL1) - PTR_FAIL_IF(call_with_args(compiler, type)); - /* Worst case size. */ #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) compiler->size += (type >= SLJIT_JUMP) ? 5 : 6; @@ -2607,7 +2495,7 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compile PTR_FAIL_IF_NULL(inst); *inst++ = 0; - *inst++ = type + 4; + *inst++ = type + 2; return jump; } @@ -2622,32 +2510,6 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi CHECK_EXTRA_REGS(src, srcw, (void)0); - if (SLJIT_UNLIKELY(compiler->flags_saved)) { - if (type <= SLJIT_JUMP) - FAIL_IF(emit_restore_flags(compiler, 0)); - compiler->flags_saved = 0; - } - - if (type >= SLJIT_CALL1) { -#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) -#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) - if (src == SLJIT_R2) { - EMIT_MOV(compiler, TMP_REG1, 0, src, 0); - src = TMP_REG1; - } - if (src == SLJIT_MEM1(SLJIT_SP) && type >= SLJIT_CALL3) - srcw += sizeof(sljit_sw); -#endif -#endif -#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && defined(_WIN64) - if (src == SLJIT_R2) { - EMIT_MOV(compiler, TMP_REG1, 0, src, 0); - src = TMP_REG1; - } -#endif - FAIL_IF(call_with_args(compiler, type)); - } - if (src == SLJIT_IMM) { jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); FAIL_IF_NULL(jump); @@ -2665,7 +2527,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi FAIL_IF_NULL(inst); *inst++ = 0; - *inst++ = type + 4; + *inst++ = type + 2; } else { #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) @@ -2682,37 +2544,29 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compi SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw dstw, - sljit_s32 src, sljit_sw srcw, sljit_s32 type) { sljit_u8 *inst; sljit_u8 cond_set = 0; #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) sljit_s32 reg; -#else - /* CHECK_EXTRA_REGS migh overwrite these values. */ +#endif + /* ADJUST_LOCAL_OFFSET and CHECK_EXTRA_REGS might overwrite these values. */ sljit_s32 dst_save = dst; sljit_sw dstw_save = dstw; -#endif CHECK_ERROR(); - CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, src, srcw, type)); - SLJIT_UNUSED_ARG(srcw); - - if (dst == SLJIT_UNUSED) - return SLJIT_SUCCESS; + CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type)); ADJUST_LOCAL_OFFSET(dst, dstw); CHECK_EXTRA_REGS(dst, dstw, (void)0); - if (SLJIT_UNLIKELY(compiler->flags_saved)) - FAIL_IF(emit_restore_flags(compiler, op & SLJIT_KEEP_FLAGS)); type &= 0xff; /* setcc = jcc + 0x10. */ cond_set = get_jump_code(type) + 0x10; #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) - if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst) && dst == src) { + if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst)) { inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 + 3); FAIL_IF(!inst); INC_SIZE(4 + 3); @@ -2727,7 +2581,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co return SLJIT_SUCCESS; } - reg = (op == SLJIT_MOV && FAST_IS_REG(dst)) ? dst : TMP_REG1; + reg = (GET_OPCODE(op) < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG1; inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 + 4); FAIL_IF(!inst); @@ -2738,6 +2592,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co *inst++ = cond_set; *inst++ = MOD_REG | reg_lmap[reg]; *inst++ = REX_W | (reg_map[reg] <= 7 ? 0 : (REX_B | REX_R)); + /* The movzx instruction does not affect flags. */ *inst++ = GROUP_0F; *inst++ = MOVZX_r_rm8; *inst = MOD_REG | (reg_lmap[reg] << 3) | reg_lmap[reg]; @@ -2749,12 +2604,15 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co compiler->mode32 = GET_OPCODE(op) != SLJIT_MOV; return emit_mov(compiler, dst, dstw, TMP_REG1, 0); } + #if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \ || (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) compiler->skip_checks = 1; #endif - return sljit_emit_op2(compiler, op, dst, dstw, dst, dstw, TMP_REG1, 0); -#else /* SLJIT_CONFIG_X86_64 */ + return sljit_emit_op2(compiler, op, dst_save, dstw_save, dst_save, dstw_save, TMP_REG1, 0); + +#else + /* The SLJIT_CONFIG_X86_32 code path starts here. */ if (GET_OPCODE(op) < SLJIT_ADD && FAST_IS_REG(dst)) { if (reg_map[dst] <= 4) { /* Low byte is accessible. */ @@ -2808,8 +2666,9 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co return SLJIT_SUCCESS; } - if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst) && dst == src && reg_map[dst] <= 4) { - SLJIT_COMPILE_ASSERT(reg_map[SLJIT_R0] == 0, scratch_reg1_must_be_eax); + if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst) && reg_map[dst] <= 4) { + SLJIT_ASSERT(reg_map[SLJIT_R0] == 0); + if (dst != SLJIT_R0) { inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 3 + 2 + 1); FAIL_IF(!inst); @@ -2868,6 +2727,46 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *co #endif /* SLJIT_CONFIG_X86_64 */ } +SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type, + sljit_s32 dst_reg, + sljit_s32 src, sljit_sw srcw) +{ + sljit_u8* inst; + + CHECK_ERROR(); + CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw)); + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + dst_reg &= ~SLJIT_I32_OP; + + if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV) || (dst_reg >= SLJIT_R3 && dst_reg <= SLJIT_S3)) + return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw); +#else + if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV)) + return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw); +#endif + + /* ADJUST_LOCAL_OFFSET is not needed. */ + CHECK_EXTRA_REGS(src, srcw, (void)0); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = dst_reg & SLJIT_I32_OP; + dst_reg &= ~SLJIT_I32_OP; +#endif + + 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; +} + SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset) { CHECK_ERROR(); @@ -2886,16 +2785,16 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *c if (NOT_HALFWORD(offset)) { FAIL_IF(emit_load_imm64(compiler, TMP_REG1, offset)); #if (defined SLJIT_DEBUG && SLJIT_DEBUG) - SLJIT_ASSERT(emit_lea_binary(compiler, SLJIT_KEEP_FLAGS, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0) != SLJIT_ERR_UNSUPPORTED); + SLJIT_ASSERT(emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0) != SLJIT_ERR_UNSUPPORTED); return compiler->error; #else - return emit_lea_binary(compiler, SLJIT_KEEP_FLAGS, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0); + return emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0); #endif } #endif if (offset != 0) - return emit_lea_binary(compiler, SLJIT_KEEP_FLAGS, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset); + return emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset); return emit_mov(compiler, dst, dstw, SLJIT_SP, 0); } @@ -2919,14 +2818,11 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi #if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) compiler->mode32 = 0; - reg = SLOW_IS_REG(dst) ? dst : TMP_REG1; + reg = FAST_IS_REG(dst) ? dst : TMP_REG1; if (emit_load_imm64(compiler, reg, init_value)) return NULL; #else - if (dst == SLJIT_UNUSED) - dst = TMP_REG1; - if (emit_mov(compiler, dst, dstw, SLJIT_IMM, init_value)) return NULL; #endif @@ -2946,82 +2842,18 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compi return const_; } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset) { + SLJIT_UNUSED_ARG(executable_offset); #if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) - sljit_unaligned_store_sw((void*)addr, new_addr - (addr + 4)); + sljit_unaligned_store_sw((void*)addr, new_target - (addr + 4) - (sljit_uw)executable_offset); #else - sljit_unaligned_store_sw((void*)addr, (sljit_sw) new_addr); + sljit_unaligned_store_sw((void*)addr, (sljit_sw) new_target); #endif } -SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant) +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset) { + SLJIT_UNUSED_ARG(executable_offset); sljit_unaligned_store_sw((void*)addr, new_constant); } - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 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_s32 sljit_x86_is_cmov_available(void) -{ - if (cpu_has_cmov == -1) - get_cpu_features(); - return cpu_has_cmov; -} - -SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_x86_emit_cmov(struct sljit_compiler *compiler, - sljit_s32 type, - sljit_s32 dst_reg, - sljit_s32 src, sljit_sw srcw) -{ - sljit_u8* inst; - - CHECK_ERROR(); -#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) - CHECK_ARGUMENT(sljit_x86_is_cmov_available()); - CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_I32_OP))); - CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_ORDERED_F64); - CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(dst_reg & ~SLJIT_I32_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_I32_OP) ? "" : ".i", - jump_names[type & 0xff], JUMP_POSTFIX(type)); - sljit_verbose_reg(compiler, dst_reg & ~SLJIT_I32_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_I32_OP; -#endif - dst_reg &= ~SLJIT_I32_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.32/src/sljit/sljitProtExecAllocator.c b/pcre2-10.32/src/sljit/sljitProtExecAllocator.c new file mode 100644 index 000000000..8a5b2b3cf --- /dev/null +++ b/pcre2-10.32/src/sljit/sljitProtExecAllocator.c @@ -0,0 +1,421 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) 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 HOLDER(S) 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 file contains a simple executable memory allocator + + It is assumed, that executable code blocks are usually medium (or sometimes + large) memory blocks, and the allocator is not too frequently called (less + optimized than other allocators). Thus, using it as a generic allocator is + not suggested. + + How does it work: + Memory is allocated in continuous memory areas called chunks by alloc_chunk() + Chunk format: + [ block ][ block ] ... [ block ][ block terminator ] + + All blocks and the block terminator is started with block_header. The block + header contains the size of the previous and the next block. These sizes + can also contain special values. + Block size: + 0 - The block is a free_block, with a different size member. + 1 - The block is a block terminator. + n - The block is used at the moment, and the value contains its size. + Previous block size: + 0 - This is the first block of the memory chunk. + n - The size of the previous block. + + Using these size values we can go forward or backward on the block chain. + The unused blocks are stored in a chain list pointed by free_blocks. This + list is useful if we need to find a suitable memory area when the allocator + is called. + + When a block is freed, the new free block is connected to its adjacent free + blocks if possible. + + [ free block ][ used block ][ free block ] + and "used block" is freed, the three blocks are connected together: + [ one big free block ] +*/ + +/* --------------------------------------------------------------------- */ +/* System (OS) functions */ +/* --------------------------------------------------------------------- */ + +/* 64 KByte. */ +#define CHUNK_SIZE 0x10000 + +struct chunk_header { + void *executable; + int fd; +}; + +/* + alloc_chunk / free_chunk : + * allocate executable system memory chunks + * the size is always divisible by CHUNK_SIZE + allocator_grab_lock / allocator_release_lock : + * make the allocator thread safe + * can be empty if the OS (or the application) does not support threading + * only the allocator requires this lock, sljit is fully thread safe + as it only uses local variables +*/ + +#include + +#ifndef O_NOATIME +#define O_NOATIME 0 +#endif + +#ifdef __O_TMPFILE +#ifndef O_TMPFILE +#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) +#endif +#endif + +int mkostemp(char *template, int flags); +char *secure_getenv(const char *name); + +static SLJIT_INLINE int create_tempfile(void) +{ + int fd; + + char tmp_name[256]; + size_t tmp_name_len; + char *dir; + size_t len; + +#ifdef P_tmpdir + len = (P_tmpdir != NULL) ? strlen(P_tmpdir) : 0; + + if (len > 0 && len < sizeof(tmp_name)) { + strcpy(tmp_name, P_tmpdir); + tmp_name_len = len; + } + else { + strcpy(tmp_name, "/tmp"); + tmp_name_len = 4; + } +#else + strcpy(tmp_name, "/tmp"); + tmp_name_len = 4; +#endif + + dir = secure_getenv("TMPDIR"); + if (dir) { + len = strlen(dir); + if (len > 0 && len < sizeof(tmp_name)) { + strcpy(tmp_name, dir); + tmp_name_len = len; + } + } + + SLJIT_ASSERT(tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name)); + + while (tmp_name_len > 0 && tmp_name[tmp_name_len - 1] == '/') { + tmp_name_len--; + tmp_name[tmp_name_len] = '\0'; + } + +#ifdef O_TMPFILE + fd = open(tmp_name, O_TMPFILE | O_EXCL | O_RDWR | O_NOATIME | O_CLOEXEC, S_IRUSR | S_IWUSR); + if (fd != -1) + return fd; +#endif + + if (tmp_name_len + 7 >= sizeof(tmp_name)) + { + return -1; + } + + strcpy(tmp_name + tmp_name_len, "/XXXXXX"); + fd = mkostemp(tmp_name, O_CLOEXEC | O_NOATIME); + + if (fd == -1) + return fd; + + if (unlink(tmp_name)) { + close(fd); + return -1; + } + + return fd; +} + +static SLJIT_INLINE struct chunk_header* alloc_chunk(sljit_uw size) +{ + struct chunk_header *retval; + int fd; + + fd = create_tempfile(); + if (fd == -1) + return NULL; + + if (ftruncate(fd, size)) { + close(fd); + return NULL; + } + + retval = (struct chunk_header *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (retval == MAP_FAILED) { + close(fd); + return NULL; + } + + retval->executable = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0); + + if (retval->executable == MAP_FAILED) { + munmap(retval, size); + close(fd); + return NULL; + } + + retval->fd = fd; + return retval; +} + +static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size) +{ + struct chunk_header *header = ((struct chunk_header *)chunk) - 1; + + int fd = header->fd; + munmap(header->executable, size); + munmap(header, size); + close(fd); +} + +/* --------------------------------------------------------------------- */ +/* Common functions */ +/* --------------------------------------------------------------------- */ + +#define CHUNK_MASK (~(CHUNK_SIZE - 1)) + +struct block_header { + sljit_uw size; + sljit_uw prev_size; + sljit_sw executable_offset; +}; + +struct free_block { + struct block_header header; + struct free_block *next; + struct free_block *prev; + sljit_uw size; +}; + +#define AS_BLOCK_HEADER(base, offset) \ + ((struct block_header*)(((sljit_u8*)base) + offset)) +#define AS_FREE_BLOCK(base, offset) \ + ((struct free_block*)(((sljit_u8*)base) + offset)) +#define MEM_START(base) ((void*)((base) + 1)) +#define ALIGN_SIZE(size) (((size) + sizeof(struct block_header) + 7) & ~7) + +static struct free_block* free_blocks; +static sljit_uw allocated_size; +static sljit_uw total_size; + +static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size) +{ + free_block->header.size = 0; + free_block->size = size; + + free_block->next = free_blocks; + free_block->prev = NULL; + if (free_blocks) + free_blocks->prev = free_block; + free_blocks = free_block; +} + +static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block) +{ + if (free_block->next) + free_block->next->prev = free_block->prev; + + if (free_block->prev) + free_block->prev->next = free_block->next; + else { + SLJIT_ASSERT(free_blocks == free_block); + free_blocks = free_block->next; + } +} + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size) +{ + struct chunk_header *chunk_header; + struct block_header *header; + struct block_header *next_header; + struct free_block *free_block; + sljit_uw chunk_size; + sljit_sw executable_offset; + + allocator_grab_lock(); + if (size < (64 - sizeof(struct block_header))) + size = (64 - sizeof(struct block_header)); + size = ALIGN_SIZE(size); + + free_block = free_blocks; + while (free_block) { + if (free_block->size >= size) { + chunk_size = free_block->size; + if (chunk_size > size + 64) { + /* We just cut a block from the end of the free block. */ + chunk_size -= size; + free_block->size = chunk_size; + header = AS_BLOCK_HEADER(free_block, chunk_size); + header->prev_size = chunk_size; + header->executable_offset = free_block->header.executable_offset; + AS_BLOCK_HEADER(header, size)->prev_size = size; + } + else { + sljit_remove_free_block(free_block); + header = (struct block_header*)free_block; + size = chunk_size; + } + allocated_size += size; + header->size = size; + allocator_release_lock(); + return MEM_START(header); + } + free_block = free_block->next; + } + + chunk_size = sizeof(struct chunk_header) + sizeof(struct block_header); + chunk_size = (chunk_size + size + CHUNK_SIZE - 1) & CHUNK_MASK; + + chunk_header = alloc_chunk(chunk_size); + if (!chunk_header) { + allocator_release_lock(); + return NULL; + } + + executable_offset = (sljit_sw)((sljit_u8*)chunk_header->executable - (sljit_u8*)chunk_header); + + chunk_size -= sizeof(struct chunk_header) + sizeof(struct block_header); + total_size += chunk_size; + + header = (struct block_header *)(chunk_header + 1); + + header->prev_size = 0; + header->executable_offset = executable_offset; + if (chunk_size > size + 64) { + /* Cut the allocated space into a free and a used block. */ + allocated_size += size; + header->size = size; + chunk_size -= size; + + free_block = AS_FREE_BLOCK(header, size); + free_block->header.prev_size = size; + free_block->header.executable_offset = executable_offset; + sljit_insert_free_block(free_block, chunk_size); + next_header = AS_BLOCK_HEADER(free_block, chunk_size); + } + else { + /* All space belongs to this allocation. */ + allocated_size += chunk_size; + header->size = chunk_size; + next_header = AS_BLOCK_HEADER(header, chunk_size); + } + next_header->size = 1; + next_header->prev_size = chunk_size; + next_header->executable_offset = executable_offset; + allocator_release_lock(); + return MEM_START(header); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr) +{ + struct block_header *header; + struct free_block* free_block; + + allocator_grab_lock(); + header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header)); + header = AS_BLOCK_HEADER(header, -header->executable_offset); + allocated_size -= header->size; + + /* Connecting free blocks together if possible. */ + + /* If header->prev_size == 0, free_block will equal to header. + In this case, free_block->header.size will be > 0. */ + free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size); + if (SLJIT_UNLIKELY(!free_block->header.size)) { + free_block->size += header->size; + header = AS_BLOCK_HEADER(free_block, free_block->size); + header->prev_size = free_block->size; + } + else { + free_block = (struct free_block*)header; + sljit_insert_free_block(free_block, header->size); + } + + header = AS_BLOCK_HEADER(free_block, free_block->size); + if (SLJIT_UNLIKELY(!header->size)) { + free_block->size += ((struct free_block*)header)->size; + sljit_remove_free_block((struct free_block*)header); + header = AS_BLOCK_HEADER(free_block, free_block->size); + header->prev_size = free_block->size; + } + + /* The whole chunk is free. */ + if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) { + /* If this block is freed, we still have (allocated_size / 2) free space. */ + if (total_size - free_block->size > (allocated_size * 3 / 2)) { + total_size -= free_block->size; + sljit_remove_free_block(free_block); + free_chunk(free_block, free_block->size + sizeof(struct block_header)); + } + } + + allocator_release_lock(); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void) +{ + struct free_block* free_block; + struct free_block* next_free_block; + + allocator_grab_lock(); + + free_block = free_blocks; + while (free_block) { + next_free_block = free_block->next; + if (!free_block->header.prev_size && + AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) { + total_size -= free_block->size; + sljit_remove_free_block(free_block); + free_chunk(free_block, free_block->size + sizeof(struct block_header)); + } + free_block = next_free_block; + } + + SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks)); + allocator_release_lock(); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr) +{ + return ((struct block_header *)(ptr))[-1].executable_offset; +} diff --git a/pcre2-10.22/src/sljit/sljitUtils.c b/pcre2-10.32/src/sljit/sljitUtils.c similarity index 68% rename from pcre2-10.22/src/sljit/sljitUtils.c rename to pcre2-10.32/src/sljit/sljitUtils.c index ec5c32119..5c2a83893 100644 --- a/pcre2-10.22/src/sljit/sljitUtils.c +++ b/pcre2-10.32/src/sljit/sljitUtils.c @@ -1,7 +1,7 @@ /* * Stack-less Just-In-Time compiler * - * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. + * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -48,12 +48,12 @@ static SLJIT_INLINE void allocator_release_lock(void) #if (defined SLJIT_UTIL_GLOBAL_LOCK && SLJIT_UTIL_GLOBAL_LOCK) -SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void) +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_grab_lock(void) { /* Always successful. */ } -SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void) +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_release_lock(void) { /* Always successful. */ } @@ -88,7 +88,7 @@ static SLJIT_INLINE void allocator_release_lock(void) static HANDLE global_mutex = 0; -SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void) +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_grab_lock(void) { /* No idea what to do if an error occures. Static mutexes should never fail... */ if (!global_mutex) @@ -97,7 +97,7 @@ SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void) WaitForSingleObject(global_mutex, INFINITE); } -SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void) +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_release_lock(void) { ReleaseMutex(global_mutex); } @@ -130,12 +130,12 @@ static SLJIT_INLINE void allocator_release_lock(void) static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; -SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void) +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_grab_lock(void) { pthread_mutex_lock(&global_mutex); } -SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void) +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_release_lock(void) { pthread_mutex_unlock(&global_mutex); } @@ -203,19 +203,16 @@ static SLJIT_INLINE sljit_s32 open_dev_zero(void) /* Planning to make it even more clever in the future. */ static sljit_sw sljit_page_align = 0; -SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_CALL sljit_allocate_stack(sljit_uw limit, sljit_uw max_limit, void *allocator_data) +SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data) { struct sljit_stack *stack; - union { - void *ptr; - sljit_uw uw; - } base; + void *ptr; #ifdef _WIN32 SYSTEM_INFO si; #endif SLJIT_UNUSED_ARG(allocator_data); - if (limit > max_limit || limit < 1) + if (start_size > max_size || start_size < 1) return NULL; #ifdef _WIN32 @@ -233,29 +230,31 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_CALL sljit_allocate_stack(slj } #endif - /* Align limit and max_limit. */ - max_limit = (max_limit + sljit_page_align) & ~sljit_page_align; - stack = (struct sljit_stack*)SLJIT_MALLOC(sizeof(struct sljit_stack), allocator_data); if (!stack) return NULL; + /* Align max_size. */ + max_size = (max_size + sljit_page_align) & ~sljit_page_align; + #ifdef _WIN32 - base.ptr = VirtualAlloc(NULL, max_limit, MEM_RESERVE, PAGE_READWRITE); - if (!base.ptr) { + ptr = VirtualAlloc(NULL, max_size, MEM_RESERVE, PAGE_READWRITE); + if (!ptr) { SLJIT_FREE(stack, allocator_data); return NULL; } - stack->base = base.uw; - stack->limit = stack->base; - stack->max_limit = stack->base + max_limit; - if (sljit_stack_resize(stack, stack->base + limit)) { + + stack->min_start = (sljit_u8 *)ptr; + stack->end = stack->min_start + max_size; + stack->start = stack->end; + + if (sljit_stack_resize(stack, stack->end - start_size) == NULL) { sljit_free_stack(stack, allocator_data); return NULL; } #else #ifdef MAP_ANON - base.ptr = mmap(NULL, max_limit, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); #else if (dev_zero < 0) { if (open_dev_zero()) { @@ -263,73 +262,70 @@ SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_CALL sljit_allocate_stack(slj return NULL; } } - base.ptr = mmap(NULL, max_limit, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero, 0); + ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero, 0); #endif - if (base.ptr == MAP_FAILED) { + if (ptr == MAP_FAILED) { SLJIT_FREE(stack, allocator_data); return NULL; } - stack->base = base.uw; - stack->limit = stack->base + limit; - stack->max_limit = stack->base + max_limit; + stack->min_start = (sljit_u8 *)ptr; + stack->end = stack->min_start + max_size; + stack->start = stack->end - start_size; #endif - stack->top = stack->base; + stack->top = stack->end; return stack; } #undef PAGE_ALIGN -SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_free_stack(struct sljit_stack* stack, void *allocator_data) +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data) { SLJIT_UNUSED_ARG(allocator_data); #ifdef _WIN32 - VirtualFree((void*)stack->base, 0, MEM_RELEASE); + VirtualFree((void*)stack->min_start, 0, MEM_RELEASE); #else - munmap((void*)stack->base, stack->max_limit - stack->base); + munmap((void*)stack->min_start, stack->end - stack->min_start); #endif SLJIT_FREE(stack, allocator_data); } -SLJIT_API_FUNC_ATTRIBUTE sljit_sw SLJIT_CALL sljit_stack_resize(struct sljit_stack* stack, sljit_uw new_limit) +SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start) { - sljit_uw aligned_old_limit; - sljit_uw aligned_new_limit; + sljit_uw aligned_old_start; + sljit_uw aligned_new_start; + + if ((new_start < stack->min_start) || (new_start >= stack->end)) + return NULL; - if ((new_limit > stack->max_limit) || (new_limit < stack->base)) - return -1; #ifdef _WIN32 - aligned_new_limit = (new_limit + sljit_page_align) & ~sljit_page_align; - aligned_old_limit = (stack->limit + sljit_page_align) & ~sljit_page_align; - if (aligned_new_limit != aligned_old_limit) { - if (aligned_new_limit > aligned_old_limit) { - if (!VirtualAlloc((void*)aligned_old_limit, aligned_new_limit - aligned_old_limit, MEM_COMMIT, PAGE_READWRITE)) - return -1; + aligned_new_start = (sljit_uw)new_start & ~sljit_page_align; + aligned_old_start = ((sljit_uw)stack->start) & ~sljit_page_align; + if (aligned_new_start != aligned_old_start) { + if (aligned_new_start < aligned_old_start) { + if (!VirtualAlloc((void*)aligned_new_start, aligned_old_start - aligned_new_start, MEM_COMMIT, PAGE_READWRITE)) + return NULL; } else { - if (!VirtualFree((void*)aligned_new_limit, aligned_old_limit - aligned_new_limit, MEM_DECOMMIT)) - return -1; + if (!VirtualFree((void*)aligned_old_start, aligned_new_start - aligned_old_start, MEM_DECOMMIT)) + return NULL; } } - stack->limit = new_limit; - return 0; #else - if (new_limit >= stack->limit) { - stack->limit = new_limit; - return 0; - } - aligned_new_limit = (new_limit + sljit_page_align) & ~sljit_page_align; - aligned_old_limit = (stack->limit + sljit_page_align) & ~sljit_page_align; - /* If madvise is available, we release the unnecessary space. */ + if (stack->start < new_start) { + aligned_new_start = (sljit_uw)new_start & ~sljit_page_align; + aligned_old_start = ((sljit_uw)stack->start) & ~sljit_page_align; + /* If madvise is available, we release the unnecessary space. */ #if defined(MADV_DONTNEED) - if (aligned_new_limit < aligned_old_limit) - madvise((void*)aligned_new_limit, aligned_old_limit - aligned_new_limit, MADV_DONTNEED); + if (aligned_new_start > aligned_old_start) + madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, MADV_DONTNEED); #elif defined(POSIX_MADV_DONTNEED) - if (aligned_new_limit < aligned_old_limit) - posix_madvise((void*)aligned_new_limit, aligned_old_limit - aligned_new_limit, POSIX_MADV_DONTNEED); + if (aligned_new_start > aligned_old_start) + posix_madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, POSIX_MADV_DONTNEED); #endif - stack->limit = new_limit; - return 0; + } #endif + stack->start = new_start; + return new_start; } #endif /* SLJIT_UTIL_STACK */ diff --git a/pcre2-10.22/test-driver b/pcre2-10.32/test-driver similarity index 97% rename from pcre2-10.22/test-driver rename to pcre2-10.32/test-driver index 8e575b017..0218a01f6 100755 --- a/pcre2-10.22/test-driver +++ b/pcre2-10.32/test-driver @@ -1,9 +1,9 @@ #! /bin/sh # test-driver - basic testsuite driver script. -scriptversion=2013-07-13.22; # UTC +scriptversion=2016-01-11.22; # UTC -# Copyright (C) 2011-2014 Free Software Foundation, Inc. +# Copyright (C) 2011-2017 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 @@ -143,6 +143,6 @@ echo ":copy-in-global-log: $gcopy" >> $trs_file # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: From 8261eb18d20ee33a82728908596e5e0239e03a3e Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 29 Dec 2018 22:46:53 +0800 Subject: [PATCH 017/439] pcre2: add maintainer mode and disable by default --- pcre2-10.32/Makefile.in | 9 +++++---- pcre2-10.32/aclocal.m4 | 36 ++++++++++++++++++++++++++++++++++++ pcre2-10.32/configure | 39 +++++++++++++++++++++++++++++++++++++++ pcre2-10.32/configure.ac | 6 ++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/pcre2-10.32/Makefile.in b/pcre2-10.32/Makefile.in index 597b17152..356f8a9f6 100644 --- a/pcre2-10.32/Makefile.in +++ b/pcre2-10.32/Makefile.in @@ -749,6 +749,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@ @@ -1292,7 +1293,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*) \ @@ -1318,9 +1319,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): @@ -1331,7 +1332,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.32/aclocal.m4 b/pcre2-10.32/aclocal.m4 index cc10b262e..36d7ef369 100644 --- a/pcre2-10.32/aclocal.m4 +++ b/pcre2-10.32/aclocal.m4 @@ -1049,6 +1049,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-2017 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-2017 Free Software Foundation, Inc. diff --git a/pcre2-10.32/configure b/pcre2-10.32/configure index 6091d75bd..dbd963558 100755 --- a/pcre2-10.32/configure +++ b/pcre2-10.32/configure @@ -718,6 +718,9 @@ build_cpu build ac_ct_AR AR +MAINT +MAINTAINER_MODE_FALSE +MAINTAINER_MODE_TRUE EGREP GREP CPP @@ -808,6 +811,7 @@ ac_user_opts=' enable_option_checking enable_silent_rules enable_dependency_tracking +enable_maintainer_mode enable_shared enable_static with_pic @@ -1497,6 +1501,9 @@ Optional Features: do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer --enable-shared[=PKGS] build shared libraries [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] @@ -4677,6 +4684,34 @@ then fi fi +# 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 if test -n "$ac_tool_prefix"; then for ac_prog in ar lib "link -lib" @@ -15579,6 +15614,10 @@ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 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 "${WITH_PCRE2_8_TRUE}" && test -z "${WITH_PCRE2_8_FALSE}"; then as_fn_error $? "conditional \"WITH_PCRE2_8\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/pcre2-10.32/configure.ac b/pcre2-10.32/configure.ac index c43ae3806..15c84318e 100644 --- a/pcre2-10.32/configure.ac +++ b/pcre2-10.32/configure.ac @@ -57,6 +57,12 @@ then fi fi +# 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 32e6bf6f64d9103e84604d99677eaabdd806ca16 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 29 Dec 2018 23:16:09 +0800 Subject: [PATCH 018/439] debian packaging/fish.spec: drop bc dependency --- debian/control | 2 +- fish.spec.in | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/debian/control b/debian/control index 56c1a692f..8612d6192 100644 --- a/debian/control +++ b/debian/control @@ -12,7 +12,7 @@ Vcs-Browser: https://github.com/fish-shell/fish-shell Package: fish Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, fish-common (= ${source:Version}), passwd (>= 4.0.3-10), bc, gettext-base, man-db +Depends: ${shlibs:Depends}, ${misc:Depends}, fish-common (= ${source:Version}), passwd (>= 4.0.3-10), gettext-base, man-db Recommends: xsel (>=1.2.0) Description: friendly interactive shell Fish is a command-line shell for modern systems, focusing on user-friendliness, diff --git a/fish.spec.in b/fish.spec.in index 5ea3885d3..70d0b190b 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -27,7 +27,6 @@ BuildRequires: pcre2-devel BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) -Requires: bc Requires: python Requires: man From 4a3ac6e91ec415bfe536f722a71903d9a67bb10d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 30 Dec 2018 16:04:57 +0100 Subject: [PATCH 019/439] Don't wait for disowned pgids if they are special If a job is disowned that, for some reason, has a pgid that is special to waitpid, like 0 (process with pgid of the calling process), -1 (any process), or our actual pgid, that would lead to us waiting for too many processes when we later try to reap the disowned processes (to stop zombies from appearing). And that means we'd snag away the processes we actually do want to wait for, which would end with us in a waiting loop. This is tough to reproduce, the easiest I've found was fish -ic 'sleep 5 &; disown; set -g __fish_git_prompt_showupstream auto; __fish_git_prompt' in a git repo. What we do is to not allow special pgids in the disowned_pids list. That means we might leave a zombie around (though we probably wait on 0 somewhere), but that's preferable to infinitely looping. See #5426. --- src/proc.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/proc.cpp b/src/proc.cpp index 4790449c5..a6da0c122 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -354,7 +354,12 @@ typedef unsigned int process_generation_count_t; static std::vector s_disowned_pids; void add_disowned_pgid(pid_t pgid) { - s_disowned_pids.push_back(pgid * -1); + // NEVER add our own pgid, or one of the special values, + // or waiting for it will + // snag other processes away. + if (pgid != getpgrp() && (pgid > 0 || pgid < -1)) { + s_disowned_pids.push_back(pgid * -1); + } } /// A static value tracking how many SIGCHLDs we have seen, which is used in a heurstic to From 040d921fa16b0c7daafd47292b9fb81db9bbdbb8 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 30 Dec 2018 10:11:20 -0600 Subject: [PATCH 020/439] Fix check for valid disowned pgids The function `add_disowned_pgid` adds process *group* ids and not process ids. It multiplies the value by negative 1 to indicate a wait on a process group, so the original value must be positive. --- src/proc.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/proc.cpp b/src/proc.cpp index a6da0c122..f31b720c4 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -354,10 +354,11 @@ typedef unsigned int process_generation_count_t; static std::vector s_disowned_pids; void add_disowned_pgid(pid_t pgid) { - // NEVER add our own pgid, or one of the special values, - // or waiting for it will - // snag other processes away. - if (pgid != getpgrp() && (pgid > 0 || pgid < -1)) { + // NEVER add our own (or an invalid) pgid as they are not unique to only + // one job, and may result in a deadlock if we attempt the wait. + if (pgid != getpgrp() && pgid > 0) { + // waitpid(2) is signalled to wait on a process group rather than a + // process id by using the negative of its value. s_disowned_pids.push_back(pgid * -1); } } From a7998c4829a80269bcd6af941812e35c86bf49e2 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 30 Dec 2018 18:54:09 +0100 Subject: [PATCH 021/439] Don't ASSERT_IS_NOT_FORKED_CHILD so much This is hammered sooo much that it actually hurts performance. for i in (seq 100000); test 1 = 1; end is about 40% (!) slower with it. --- src/parser.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parser.cpp b/src/parser.cpp index b16c86e47..4196c3dd8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -108,7 +108,6 @@ parser_t::~parser_t() = default; static parser_t s_principal_parser; parser_t &parser_t::principal_parser() { - ASSERT_IS_NOT_FORKED_CHILD(); ASSERT_IS_MAIN_THREAD(); return s_principal_parser; } From dabd05f2e36c07a9bd9b2ac8b103e9a4fa9fba10 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 30 Dec 2018 19:24:03 +0100 Subject: [PATCH 022/439] Remove string fallback function We already have a fallback here, and upgrading from 2.3.0 to 3.X will be rare. This costs every shell on every start. See #5279. --- share/functions/string.fish | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 share/functions/string.fish diff --git a/share/functions/string.fish b/share/functions/string.fish deleted file mode 100644 index 72cb67a62..000000000 --- a/share/functions/string.fish +++ /dev/null @@ -1,26 +0,0 @@ -# XXX nostring -if not contains string (builtin -n) - function string - if not set -q __is_launched_without_string - if status --is-interactive - # We've been autoloaded after fish < 2.3.0 upgraded to >= 2.3.1 - no string builtin - set_color --bold >&2 - echo "Fish has been upgraded, and the scripts on your system are not compatible" >&2 - echo "with this prior instance of fish. You can probably run:" >&2 - set_color green >&2 - echo -e "\n exec fish" >&2 - set_color normal >&2 - echo "… to replace this process with a new one in-place." >&2 - set -g __is_launched_without_string 1 - end - end - set -p PATH $__fish_bin_dir - set string_cmd string \'$argv\' - - if fish -c 'contains string (builtin -n)' - fish -c "$string_cmd" - else - return 127 - end - end -end From cbc25d78293a933a38a9f2c3cc559e212d17a3f6 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 18 Oct 2018 17:00:29 +0200 Subject: [PATCH 023/439] [tinyexpr] Port to C++ This removes the need to run c-compilation on one file, and allows us to in future c++-ify this a bit. There's a lot of bit-fiddling here that is quite unnecessary, better error-handling would be nice... So far this removes a few more unused things (because I would have had to port them), including: - Functions with ARITY > 3 (even 3 isn't used, but just so we don't get complacent) - Variables - Most functions moved out of the header, because only te_interp is used. - The te_print function --- src/{tinyexpr.c => tinyexpr.cpp} | 271 +++++++++++++------------------ src/tinyexpr.h | 81 ++------- 2 files changed, 129 insertions(+), 223 deletions(-) rename src/{tinyexpr.c => tinyexpr.cpp} (66%) mode change 100755 => 100644 diff --git a/src/tinyexpr.c b/src/tinyexpr.cpp old mode 100755 new mode 100644 similarity index 66% rename from src/tinyexpr.c rename to src/tinyexpr.cpp index f53a5d574..279d4f21f --- a/src/tinyexpr.c +++ b/src/tinyexpr.cpp @@ -22,7 +22,7 @@ * 3. This notice may not be removed or altered from any source distribution. */ -// This version has been altered for inclusion in fish. +// This version has been altered and ported to C++ for inclusion in fish. #include "tinyexpr.h" #include #include @@ -30,48 +30,79 @@ #include #include +#include + +// TODO: It would be nice not to rely on a typedef for this, especially one that can only do functions with two args. typedef double (*te_fun2)(double, double); +typedef double (*te_fun1)(double); +typedef double (*te_fun0)(); enum { - TOK_NULL = TE_FUNCTION0+16, TOK_ERROR, TOK_END, TOK_SEP, - TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_VARIABLE, TOK_INFIX + TE_VARIABLE = 0, TE_CONSTANT, + TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3, + TE_FLAG_PURE = 32 }; +// TODO: This is crappy bit-fiddling that should not be done. +#define TYPE_MASK(TYPE) ((TYPE)&0x0000001F) +#define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0) +#define ARITY(TYPE) ( ((TYPE) & TE_FUNCTION0) ? ((TYPE) & 0x00000007) : 0 ) -enum {TE_CONSTANT = 1}; +// TODO: Is it actually used that these share a space? +enum { + TOK_NULL = TE_FUNCTION0+16, TOK_ERROR, TOK_END, TOK_SEP, + TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_INFIX +}; +typedef struct te_expr { + int type; + union {double value; const void *function;}; + // TODO: This void pointer is quite ugly. + void *parameters[1]; +} te_expr; + +// TODO: Rename since variables have been removed. +typedef struct te_variable { + const char *name; + const void *address; + int type; +} te_variable; typedef struct state { const char *start; const char *next; int type; - union {double value; const double *bound; const void *function;}; - void *context; + union {double value; const void *function;}; - const te_variable *lookup; - int lookup_len; te_error_type_t error; } state; +/* Parses the input expression and binds variables. */ +/* Returns NULL on error. */ +te_expr *te_compile(const char *expression, te_error_t *error); -#define TYPE_MASK(TYPE) ((TYPE)&0x0000001F) +/* Evaluates the expression. */ +double te_eval(const te_expr *n); -#define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0) -#define IS_FUNCTION(TYPE) (((TYPE) & TE_FUNCTION0) != 0) -#define ARITY(TYPE) ( ((TYPE) & TE_FUNCTION0) ? ((TYPE) & 0x00000007) : 0 ) -#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){__VA_ARGS__}) +/* Frees the expression. */ +/* This is safe to call on NULL pointers. */ +void te_free(te_expr *n); + +// TODO: That move there? Ouch. Replace with a proper class with a constructor. +#define NEW_EXPR(type, ...) new_expr((type), std::move((const te_expr*[]){__VA_ARGS__})) static te_expr *new_expr(const int type, const te_expr *parameters[]) { const int arity = ARITY(type); const int psize = sizeof(void*) * arity; const int size = (sizeof(te_expr) - sizeof(void*)) + psize; - te_expr *ret = malloc(size); + te_expr *ret = (te_expr *)malloc(size); + // This sets float to 0, which depends on the implementation. + // We rely on IEEE-754 floats anyway, so it's okay. memset(ret, 0, size); if (arity && parameters) { memcpy(ret->parameters, parameters, psize); } ret->type = type; - ret->bound = 0; return ret; } @@ -79,13 +110,9 @@ static te_expr *new_expr(const int type, const te_expr *parameters[]) { void te_free_parameters(te_expr *n) { if (!n) return; switch (TYPE_MASK(n->type)) { - case TE_FUNCTION7: te_free(n->parameters[6]); - case TE_FUNCTION6: te_free(n->parameters[5]); - case TE_FUNCTION5: te_free(n->parameters[4]); - case TE_FUNCTION4: te_free(n->parameters[3]); - case TE_FUNCTION3: te_free(n->parameters[2]); - case TE_FUNCTION2: te_free(n->parameters[1]); - case TE_FUNCTION1: te_free(n->parameters[0]); + case TE_FUNCTION3: te_free((te_expr *)n->parameters[2]); + case TE_FUNCTION2: te_free((te_expr *)n->parameters[1]); + case TE_FUNCTION1: te_free((te_expr *)n->parameters[0]); } } @@ -113,6 +140,7 @@ static double fac(double a) {/* simplest version of fac */ } return (double)result; } + static double ncr(double n, double r) { if (n < 0.0 || r < 0.0 || n < r) return NAN; if (n > UINT_MAX || r > UINT_MAX) return INFINITY; @@ -127,41 +155,42 @@ static double ncr(double n, double r) { } return result; } + static double npr(double n, double r) {return ncr(n, r) * fac(r);} static const te_variable functions[] = { /* must be in alphabetical order */ - {"abs", fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"acos", acos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"asin", asin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan", atan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"atan2", atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cos", cos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0}, - {"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"fac", fac, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ln", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"log", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"ncr", ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"npr", npr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE, 0}, - {"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, - {"round", round, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sinh", sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"sqrt", sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tan", tan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {"tanh", tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, - {0, 0, 0, 0} + {"abs", (const void *)(te_fun1)fabs, TE_FUNCTION1 | TE_FLAG_PURE}, + {"acos", (const void *)(te_fun1)acos, TE_FUNCTION1 | TE_FLAG_PURE}, + {"asin", (const void *)(te_fun1)asin, TE_FUNCTION1 | TE_FLAG_PURE}, + {"atan", (const void *)(te_fun1)atan, TE_FUNCTION1 | TE_FLAG_PURE}, + {"atan2", (const void *)(te_fun2)atan2, TE_FUNCTION2 | TE_FLAG_PURE}, + {"ceil", (const void *)(te_fun1)ceil, TE_FUNCTION1 | TE_FLAG_PURE}, + {"cos", (const void *)(te_fun1)cos, TE_FUNCTION1 | TE_FLAG_PURE}, + {"cosh", (const void *)(te_fun1)cosh, TE_FUNCTION1 | TE_FLAG_PURE}, + {"e", (const void *)(te_fun0)e, TE_FUNCTION0 | TE_FLAG_PURE}, + {"exp", (const void *)(te_fun1)exp, TE_FUNCTION1 | TE_FLAG_PURE}, + {"fac", (const void *)(te_fun1)fac, TE_FUNCTION1 | TE_FLAG_PURE}, + {"floor", (const void *)(te_fun1)floor, TE_FUNCTION1 | TE_FLAG_PURE}, + {"ln", (const void *)(te_fun1)log, TE_FUNCTION1 | TE_FLAG_PURE}, + {"log", (const void *)(te_fun1)log10, TE_FUNCTION1 | TE_FLAG_PURE}, + {"log10", (const void *)(te_fun1)log10, TE_FUNCTION1 | TE_FLAG_PURE}, + {"ncr", (const void *)(te_fun2)ncr, TE_FUNCTION2 | TE_FLAG_PURE}, + {"npr", (const void *)(te_fun2)npr, TE_FUNCTION2 | TE_FLAG_PURE}, + {"pi", (const void *)(te_fun1)pi, TE_FUNCTION0 | TE_FLAG_PURE}, + {"pow", (const void *)(te_fun2)pow, TE_FUNCTION2 | TE_FLAG_PURE}, + {"round", (const void *)(te_fun1)round, TE_FUNCTION1 | TE_FLAG_PURE}, + {"sin", (const void *)(te_fun1)sin, TE_FUNCTION1 | TE_FLAG_PURE}, + {"sinh", (const void *)(te_fun1)sinh, TE_FUNCTION1 | TE_FLAG_PURE}, + {"sqrt", (const void *)(te_fun1)sqrt, TE_FUNCTION1 | TE_FLAG_PURE}, + {"tan", (const void *)(te_fun1)tan, TE_FUNCTION1 | TE_FLAG_PURE}, + {"tanh", (const void *)(te_fun1)tanh, TE_FUNCTION1 | TE_FLAG_PURE}, + {0, 0, 0} }; static const te_variable *find_builtin(const char *name, int len) { - int imin = 0; - int imax = sizeof(functions) / sizeof(te_variable) - 2; + long imin = 0; + long imax = sizeof(functions) / sizeof(te_variable) - 2; /*Binary search.*/ while (imax >= imin) { @@ -180,33 +209,16 @@ static const te_variable *find_builtin(const char *name, int len) { return 0; } -static const te_variable *find_lookup(const state *s, const char *name, int len) { - int iters; - const te_variable *var; - if (!s->lookup) return 0; - - for (var = s->lookup, iters = s->lookup_len; iters; ++var, --iters) { - if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') { - return var; - } - } - return 0; -} - - - static double add(double a, double b) {return a + b;} static double sub(double a, double b) {return a - b;} static double mul(double a, double b) {return a * b;} static double divide(double a, double b) {return a / b;} static double negate(double a) {return -a;} - void next_token(state *s) { s->type = TOK_NULL; do { - if (!*s->next){ s->type = TOK_END; return; @@ -223,19 +235,11 @@ void next_token(state *s) { start = s->next; while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9') || (s->next[0] == '_')) s->next++; - const te_variable *var = find_lookup(s, start, s->next - start); - if (!var) var = find_builtin(start, s->next - start); + const te_variable *var = find_builtin(start, s->next - start); if (var) { - switch(TYPE_MASK(var->type)) - { - case TE_VARIABLE: - s->type = TOK_VARIABLE; - s->bound = var->address; - break; - + switch(TYPE_MASK(var->type)) { case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: - case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: s->type = var->type; s->function = var->address; break; @@ -246,21 +250,20 @@ void next_token(state *s) { s->type = TOK_ERROR; s->error = TE_ERROR_UNKNOWN_VARIABLE; } - } else { /* Look for an operator or special character. */ switch (s->next++[0]) { - case '+': s->type = TOK_INFIX; s->function = add; break; - case '-': s->type = TOK_INFIX; s->function = sub; break; - case '*': s->type = TOK_INFIX; s->function = mul; break; - case '/': s->type = TOK_INFIX; s->function = divide; break; - case '^': s->type = TOK_INFIX; s->function = pow; break; - case '%': s->type = TOK_INFIX; s->function = fmod; break; + case '+': s->type = TOK_INFIX; s->function = (const void *)(te_fun2) add; break; + case '-': s->type = TOK_INFIX; s->function = (const void *)(te_fun2) sub; break; + case '*': s->type = TOK_INFIX; s->function = (const void *)(te_fun2) mul; break; + case '/': s->type = TOK_INFIX; s->function = (const void *)(te_fun2) divide; break; + case '^': s->type = TOK_INFIX; s->function = (const void *)(te_fun2) pow; break; + case '%': s->type = TOK_INFIX; s->function = (const void *)(te_fun2) fmod; break; case '(': s->type = TOK_OPEN; break; case ')': s->type = TOK_CLOSE; break; case ',': s->type = TOK_SEP; break; case ' ': case '\t': case '\n': case '\r': break; - default: s->type = TOK_ERROR; s->error = TE_ERROR_MISSING_OPERATOR; break; + default: s->type = TOK_ERROR; s->error = TE_ERROR_MISSING_OPERATOR; break; } } } @@ -283,12 +286,6 @@ static te_expr *base(state *s) { next_token(s); break; - case TOK_VARIABLE: - ret = new_expr(TE_VARIABLE, 0); - ret->bound = s->bound; - next_token(s); - break; - case TE_FUNCTION0: ret = new_expr(s->type, 0); ret->function = s->function; @@ -306,8 +303,7 @@ static te_expr *base(state *s) { break; case TE_FUNCTION1: - case TE_FUNCTION2: case TE_FUNCTION3: case TE_FUNCTION4: - case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: + case TE_FUNCTION2: case TE_FUNCTION3: arity = ARITY(s->type); ret = new_expr(s->type, 0); @@ -388,7 +384,7 @@ static te_expr *power(state *s) { ret = base(s); } else { ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); - ret->function = negate; + ret->function = (const void *) negate; } return ret; @@ -398,11 +394,11 @@ static te_expr *factor(state *s) { /* = {"^" } */ te_expr *ret = power(s); - while (s->type == TOK_INFIX && (s->function == pow)) { - te_fun2 t = s->function; + while (s->type == TOK_INFIX && (s->function == (const void*)(te_fun2)pow)) { + te_fun2 t = (te_fun2) s->function; next_token(s); ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); - ret->function = t; + ret->function = (const void *) t; } return ret; @@ -413,11 +409,11 @@ static te_expr *term(state *s) { /* = {("*" | "/" | "%") } */ te_expr *ret = factor(s); - while (s->type == TOK_INFIX && (s->function == mul || s->function == divide || s->function == fmod)) { - te_fun2 t = s->function; + while (s->type == TOK_INFIX && (s->function == (const void*)(te_fun2)mul || s->function == (const void*)(te_fun2)divide || s->function == (const void*)(te_fun2)fmod)) { + te_fun2 t = (te_fun2) s->function; next_token(s); ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, factor(s)); - ret->function = t; + ret->function = (const void *) t; } return ret; @@ -429,10 +425,10 @@ static te_expr *expr(state *s) { te_expr *ret = term(s); while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { - te_fun2 t = s->function; + te_fun2 t = (te_fun2) s->function; next_token(s); ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, term(s)); - ret->function = t; + ret->function = (const void *) t; } return ret; @@ -440,7 +436,7 @@ static te_expr *expr(state *s) { #define TE_FUN(...) ((double(*)(__VA_ARGS__))n->function) -#define M(e) te_eval(n->parameters[e]) +#define M(e) te_eval((te_expr *)n->parameters[e]) double te_eval(const te_expr *n) { @@ -448,22 +444,14 @@ double te_eval(const te_expr *n) { switch(TYPE_MASK(n->type)) { case TE_CONSTANT: return n->value; - case TE_VARIABLE: return *n->bound; - - case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: - case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: - switch(ARITY(n->type)) { - case 0: return TE_FUN(void)(); - case 1: return TE_FUN(double)(M(0)); - case 2: return TE_FUN(double, double)(M(0), M(1)); - case 3: return TE_FUN(double, double, double)(M(0), M(1), M(2)); - case 4: return TE_FUN(double, double, double, double)(M(0), M(1), M(2), M(3)); - case 5: return TE_FUN(double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4)); - case 6: return TE_FUN(double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5)); - case 7: return TE_FUN(double, double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5), M(6)); - default: return NAN; - } - + case TE_FUNCTION0: + return TE_FUN(void)(); + case TE_FUNCTION1: + return TE_FUN(double)(M(0)); + case TE_FUNCTION2: + return TE_FUN(double, double)(M(0), M(1)); + case TE_FUNCTION3: + return TE_FUN(double, double, double)(M(0), M(1), M(2)); default: return NAN; } @@ -478,14 +466,14 @@ static void optimize(te_expr *n) { if (n->type == TE_VARIABLE) return; /* Only optimize out functions flagged as pure. */ + // (which is currently all of them) if (IS_PURE(n->type)) { const int arity = ARITY(n->type); - int known = 1; - int i; - for (i = 0; i < arity; ++i) { - optimize(n->parameters[i]); + bool known = true; + for (int i = 0; i < arity; ++i) { + optimize((te_expr *)n->parameters[i]); if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) { - known = 0; + known = false; } } if (known) { @@ -498,11 +486,9 @@ static void optimize(te_expr *n) { } -te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, te_error_t *error) { +te_expr *te_compile(const char *expression, te_error_t *error) { state s; s.start = s.next = expression; - s.lookup = variables; - s.lookup_len = var_count; s.error = TE_ERROR_NONE; next_token(&s); @@ -532,9 +518,8 @@ te_expr *te_compile(const char *expression, const te_variable *variables, int va } } - double te_interp(const char *expression, te_error_t *error) { - te_expr *n = te_compile(expression, 0, 0, error); + te_expr *n = te_compile(expression, error); double ret; if (n) { ret = te_eval(n); @@ -544,31 +529,3 @@ double te_interp(const char *expression, te_error_t *error) { } return ret; } - -static void pn (const te_expr *n, int depth) { - int i, arity; - printf("%*s", depth, ""); - - switch(TYPE_MASK(n->type)) { - case TE_CONSTANT: printf("%f\n", n->value); break; - case TE_VARIABLE: printf("bound %p\n", n->bound); break; - - case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: - case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: - arity = ARITY(n->type); - printf("f%d", arity); - for(i = 0; i < arity; i++) { - printf(" %p", n->parameters[i]); - } - printf("\n"); - for(i = 0; i < arity; i++) { - pn(n->parameters[i], depth + 1); - } - break; - } -} - - -void te_print(const te_expr *n) { - pn(n, 0); -} diff --git a/src/tinyexpr.h b/src/tinyexpr.h index 06d324379..193cc706b 100644 --- a/src/tinyexpr.h +++ b/src/tinyexpr.h @@ -22,80 +22,29 @@ * 3. This notice may not be removed or altered from any source distribution. */ -// This version was altered for inclusion in fish. +// This version was altered and ported to C++ for inclusion in fish. #ifndef __TINYEXPR_H__ #define __TINYEXPR_H__ +typedef enum { + TE_ERROR_NONE = 0, + TE_ERROR_UNKNOWN_VARIABLE = 1, + TE_ERROR_MISSING_CLOSING_PAREN = 2, + TE_ERROR_MISSING_OPENING_PAREN = 3, + TE_ERROR_TOO_FEW_ARGS = 4, + TE_ERROR_TOO_MANY_ARGS = 5, + TE_ERROR_MISSING_OPERATOR = 6, + TE_ERROR_UNKNOWN = 7 +} te_error_type_t; -#ifdef __cplusplus -extern "C" { -#endif - - - typedef enum { - TE_ERROR_NONE = 0, - TE_ERROR_UNKNOWN_VARIABLE = 1, - TE_ERROR_MISSING_CLOSING_PAREN = 2, - TE_ERROR_MISSING_OPENING_PAREN = 3, - TE_ERROR_TOO_FEW_ARGS = 4, - TE_ERROR_TOO_MANY_ARGS = 5, - TE_ERROR_MISSING_OPERATOR = 6, - TE_ERROR_UNKNOWN = 7 - } te_error_type_t; - - - typedef struct te_error_t { - te_error_type_t type; - int position; - } te_error_t; - -typedef struct te_expr { - int type; - union {double value; const double *bound; const void *function;}; - void *parameters[1]; -} te_expr; - - -enum { - TE_VARIABLE = 0, - - TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3, - TE_FUNCTION4, TE_FUNCTION5, TE_FUNCTION6, TE_FUNCTION7, - - TE_FLAG_PURE = 32 -}; - -typedef struct te_variable { - const char *name; - const void *address; - int type; - void *context; -} te_variable; - - +typedef struct te_error_t { + te_error_type_t type; + int position; +} te_error_t; /* Parses the input expression, evaluates it, and frees it. */ /* Returns NaN on error. */ double te_interp(const char *expression, te_error_t *error); -/* Parses the input expression and binds variables. */ -/* Returns NULL on error. */ -te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, te_error_t *error); - -/* Evaluates the expression. */ -double te_eval(const te_expr *n); - -/* Prints debugging information on the syntax tree. */ -void te_print(const te_expr *n); - -/* Frees the expression. */ -/* This is safe to call on NULL pointers. */ -void te_free(te_expr *n); - - -#ifdef __cplusplus -} -#endif - #endif /*__TINYEXPR_H__*/ From 61e7f84e29db6e4e9cf3c32fd3c59da7f581c345 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 2 Nov 2018 12:40:34 +0100 Subject: [PATCH 024/439] tinyexpr: Remove PURE flag This was unused because all functions were marked as pure. We don't have any plans to add any that aren't, and if we did we'd still have this in git. --- src/tinyexpr.cpp | 90 ++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 279d4f21f..e35d752a6 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -39,13 +39,11 @@ typedef double (*te_fun0)(); enum { TE_VARIABLE = 0, TE_CONSTANT, - TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3, - TE_FLAG_PURE = 32 + TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3 }; // TODO: This is crappy bit-fiddling that should not be done. #define TYPE_MASK(TYPE) ((TYPE)&0x0000001F) -#define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0) #define ARITY(TYPE) ( ((TYPE) & TE_FUNCTION0) ? ((TYPE) & 0x00000007) : 0 ) // TODO: Is it actually used that these share a space? @@ -160,31 +158,31 @@ static double npr(double n, double r) {return ncr(n, r) * fac(r);} static const te_variable functions[] = { /* must be in alphabetical order */ - {"abs", (const void *)(te_fun1)fabs, TE_FUNCTION1 | TE_FLAG_PURE}, - {"acos", (const void *)(te_fun1)acos, TE_FUNCTION1 | TE_FLAG_PURE}, - {"asin", (const void *)(te_fun1)asin, TE_FUNCTION1 | TE_FLAG_PURE}, - {"atan", (const void *)(te_fun1)atan, TE_FUNCTION1 | TE_FLAG_PURE}, - {"atan2", (const void *)(te_fun2)atan2, TE_FUNCTION2 | TE_FLAG_PURE}, - {"ceil", (const void *)(te_fun1)ceil, TE_FUNCTION1 | TE_FLAG_PURE}, - {"cos", (const void *)(te_fun1)cos, TE_FUNCTION1 | TE_FLAG_PURE}, - {"cosh", (const void *)(te_fun1)cosh, TE_FUNCTION1 | TE_FLAG_PURE}, - {"e", (const void *)(te_fun0)e, TE_FUNCTION0 | TE_FLAG_PURE}, - {"exp", (const void *)(te_fun1)exp, TE_FUNCTION1 | TE_FLAG_PURE}, - {"fac", (const void *)(te_fun1)fac, TE_FUNCTION1 | TE_FLAG_PURE}, - {"floor", (const void *)(te_fun1)floor, TE_FUNCTION1 | TE_FLAG_PURE}, - {"ln", (const void *)(te_fun1)log, TE_FUNCTION1 | TE_FLAG_PURE}, - {"log", (const void *)(te_fun1)log10, TE_FUNCTION1 | TE_FLAG_PURE}, - {"log10", (const void *)(te_fun1)log10, TE_FUNCTION1 | TE_FLAG_PURE}, - {"ncr", (const void *)(te_fun2)ncr, TE_FUNCTION2 | TE_FLAG_PURE}, - {"npr", (const void *)(te_fun2)npr, TE_FUNCTION2 | TE_FLAG_PURE}, - {"pi", (const void *)(te_fun1)pi, TE_FUNCTION0 | TE_FLAG_PURE}, - {"pow", (const void *)(te_fun2)pow, TE_FUNCTION2 | TE_FLAG_PURE}, - {"round", (const void *)(te_fun1)round, TE_FUNCTION1 | TE_FLAG_PURE}, - {"sin", (const void *)(te_fun1)sin, TE_FUNCTION1 | TE_FLAG_PURE}, - {"sinh", (const void *)(te_fun1)sinh, TE_FUNCTION1 | TE_FLAG_PURE}, - {"sqrt", (const void *)(te_fun1)sqrt, TE_FUNCTION1 | TE_FLAG_PURE}, - {"tan", (const void *)(te_fun1)tan, TE_FUNCTION1 | TE_FLAG_PURE}, - {"tanh", (const void *)(te_fun1)tanh, TE_FUNCTION1 | TE_FLAG_PURE}, + {"abs", (const void *)(te_fun1)fabs, TE_FUNCTION1}, + {"acos", (const void *)(te_fun1)acos, TE_FUNCTION1}, + {"asin", (const void *)(te_fun1)asin, TE_FUNCTION1}, + {"atan", (const void *)(te_fun1)atan, TE_FUNCTION1}, + {"atan2", (const void *)(te_fun2)atan2, TE_FUNCTION2}, + {"ceil", (const void *)(te_fun1)ceil, TE_FUNCTION1}, + {"cos", (const void *)(te_fun1)cos, TE_FUNCTION1}, + {"cosh", (const void *)(te_fun1)cosh, TE_FUNCTION1}, + {"e", (const void *)(te_fun0)e, TE_FUNCTION0}, + {"exp", (const void *)(te_fun1)exp, TE_FUNCTION1}, + {"fac", (const void *)(te_fun1)fac, TE_FUNCTION1}, + {"floor", (const void *)(te_fun1)floor, TE_FUNCTION1}, + {"ln", (const void *)(te_fun1)log, TE_FUNCTION1}, + {"log", (const void *)(te_fun1)log10, TE_FUNCTION1}, + {"log10", (const void *)(te_fun1)log10, TE_FUNCTION1}, + {"ncr", (const void *)(te_fun2)ncr, TE_FUNCTION2}, + {"npr", (const void *)(te_fun2)npr, TE_FUNCTION2}, + {"pi", (const void *)(te_fun1)pi, TE_FUNCTION0}, + {"pow", (const void *)(te_fun2)pow, TE_FUNCTION2}, + {"round", (const void *)(te_fun1)round, TE_FUNCTION1}, + {"sin", (const void *)(te_fun1)sin, TE_FUNCTION1}, + {"sinh", (const void *)(te_fun1)sinh, TE_FUNCTION1}, + {"sqrt", (const void *)(te_fun1)sqrt, TE_FUNCTION1}, + {"tan", (const void *)(te_fun1)tan, TE_FUNCTION1}, + {"tanh", (const void *)(te_fun1)tanh, TE_FUNCTION1}, {0, 0, 0} }; @@ -383,7 +381,7 @@ static te_expr *power(state *s) { if (sign == 1) { ret = base(s); } else { - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret = NEW_EXPR(TE_FUNCTION1, base(s)); ret->function = (const void *) negate; } @@ -397,7 +395,7 @@ static te_expr *factor(state *s) { while (s->type == TOK_INFIX && (s->function == (const void*)(te_fun2)pow)) { te_fun2 t = (te_fun2) s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); + ret = NEW_EXPR(TE_FUNCTION2, ret, power(s)); ret->function = (const void *) t; } @@ -412,7 +410,7 @@ static te_expr *term(state *s) { while (s->type == TOK_INFIX && (s->function == (const void*)(te_fun2)mul || s->function == (const void*)(te_fun2)divide || s->function == (const void*)(te_fun2)fmod)) { te_fun2 t = (te_fun2) s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, factor(s)); + ret = NEW_EXPR(TE_FUNCTION2, ret, factor(s)); ret->function = (const void *) t; } @@ -427,7 +425,7 @@ static te_expr *expr(state *s) { while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { te_fun2 t = (te_fun2) s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, term(s)); + ret = NEW_EXPR(TE_FUNCTION2, ret, term(s)); ret->function = (const void *) t; } @@ -465,24 +463,20 @@ static void optimize(te_expr *n) { if (n->type == TE_CONSTANT) return; if (n->type == TE_VARIABLE) return; - /* Only optimize out functions flagged as pure. */ - // (which is currently all of them) - if (IS_PURE(n->type)) { - const int arity = ARITY(n->type); - bool known = true; - for (int i = 0; i < arity; ++i) { - optimize((te_expr *)n->parameters[i]); - if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) { - known = false; - } - } - if (known) { - const double value = te_eval(n); - te_free_parameters(n); - n->type = TE_CONSTANT; - n->value = value; + const int arity = ARITY(n->type); + bool known = true; + for (int i = 0; i < arity; ++i) { + optimize((te_expr *)n->parameters[i]); + if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) { + known = false; } } + if (known) { + const double value = te_eval(n); + te_free_parameters(n); + n->type = TE_CONSTANT; + n->value = value; + } } From 84ca265b48c0fc2509a8299c1f631810f2205f9c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 2 Nov 2018 12:45:06 +0100 Subject: [PATCH 025/439] tinyexpr: Unfiddle the bits Mainly this removes the "TYPE_MASK" macro that just masks off the higher bits, which I don't think were ever actually used. Much of this seems like anticipation of future direction, but we're going somewhere else. --- src/tinyexpr.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index e35d752a6..d9b6887bb 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -42,9 +42,12 @@ enum { TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3 }; -// TODO: This is crappy bit-fiddling that should not be done. -#define TYPE_MASK(TYPE) ((TYPE)&0x0000001F) -#define ARITY(TYPE) ( ((TYPE) & TE_FUNCTION0) ? ((TYPE) & 0x00000007) : 0 ) +int get_arity(const int type) { + if (type == TE_FUNCTION3) return 3; + if (type == TE_FUNCTION2) return 2; + if (type == TE_FUNCTION1) return 1; + return 0; +} // TODO: Is it actually used that these share a space? enum { @@ -90,7 +93,7 @@ void te_free(te_expr *n); #define NEW_EXPR(type, ...) new_expr((type), std::move((const te_expr*[]){__VA_ARGS__})) static te_expr *new_expr(const int type, const te_expr *parameters[]) { - const int arity = ARITY(type); + const int arity = get_arity(type); const int psize = sizeof(void*) * arity; const int size = (sizeof(te_expr) - sizeof(void*)) + psize; te_expr *ret = (te_expr *)malloc(size); @@ -107,11 +110,8 @@ static te_expr *new_expr(const int type, const te_expr *parameters[]) { void te_free_parameters(te_expr *n) { if (!n) return; - switch (TYPE_MASK(n->type)) { - case TE_FUNCTION3: te_free((te_expr *)n->parameters[2]); - case TE_FUNCTION2: te_free((te_expr *)n->parameters[1]); - case TE_FUNCTION1: te_free((te_expr *)n->parameters[0]); - } + const int arity = get_arity(n->type); + if (arity > 0) te_free((te_expr *)n->parameters[arity - 1]); } @@ -236,7 +236,7 @@ void next_token(state *s) { const te_variable *var = find_builtin(start, s->next - start); if (var) { - switch(TYPE_MASK(var->type)) { + switch(var->type) { case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: s->type = var->type; s->function = var->address; @@ -277,7 +277,7 @@ static te_expr *base(state *s) { te_expr *ret; int arity; - switch (TYPE_MASK(s->type)) { + switch (s->type) { case TOK_NUMBER: ret = new_expr(TE_CONSTANT, 0); ret->value = s->value; @@ -302,7 +302,7 @@ static te_expr *base(state *s) { case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: - arity = ARITY(s->type); + arity = get_arity(s->type); ret = new_expr(s->type, 0); ret->function = s->function; @@ -440,7 +440,7 @@ static te_expr *expr(state *s) { double te_eval(const te_expr *n) { if (!n) return NAN; - switch(TYPE_MASK(n->type)) { + switch(n->type) { case TE_CONSTANT: return n->value; case TE_FUNCTION0: return TE_FUN(void)(); @@ -463,7 +463,7 @@ static void optimize(te_expr *n) { if (n->type == TE_CONSTANT) return; if (n->type == TE_VARIABLE) return; - const int arity = ARITY(n->type); + const int arity = get_arity(n->type); bool known = true; for (int i = 0; i < arity; ++i) { optimize((te_expr *)n->parameters[i]); From 26dfca67e563fa7a4b91930836f97f9a4ce5b6f3 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 2 Nov 2018 13:35:06 +0100 Subject: [PATCH 026/439] Fix cmake build Screwed up a rebase there! --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 63c060712..5ebfc73e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ SET(FISH_SRCS src/parse_execution.cpp src/parse_productions.cpp src/parse_tree.cpp src/parse_util.cpp src/parser.cpp src/parser_keywords.cpp src/path.cpp src/postfork.cpp src/proc.cpp src/reader.cpp src/sanity.cpp src/screen.cpp - src/signal.cpp src/tinyexpr.c src/tnode.cpp src/tokenizer.cpp src/utf8.cpp src/util.cpp + src/signal.cpp src/tinyexpr.cpp src/tnode.cpp src/tokenizer.cpp src/utf8.cpp src/util.cpp src/wcstringutil.cpp src/wgetopt.cpp src/wildcard.cpp src/wutil.cpp src/future_feature_flags.cpp ) From 4674784a0b3570bc07149b96184747eb2a43a612 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 2 Nov 2018 13:35:26 +0100 Subject: [PATCH 027/439] Make autotools build use tinyexpr.cpp --- Makefile.in | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Makefile.in b/Makefile.in index bfdd75e4b..01ac297da 100644 --- a/Makefile.in +++ b/Makefile.in @@ -744,10 +744,6 @@ obj/%.o: src/%.cpp | show-CXX show-CXXFLAGS show-CPPFLAGS obj @echo " CXX $(em)$@$(sgr0)" $v $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ -obj/tinyexpr.o: src/tinyexpr.c | obj - @echo " CC $(em)$@$(sqr0)" - $v $(CC) $(CFLAGS) -c $< -o $@ - # # obj directory # From 3bbec871e43069071f01d2c53692aba442fb713e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 2 Nov 2018 13:47:00 +0100 Subject: [PATCH 028/439] tinyexpr: Free all parameters again This used implicit fallthrough to free all. We still iterate back-to-front (i--) because maybe that's important? --- src/tinyexpr.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index d9b6887bb..8d955d274 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -110,8 +110,12 @@ static te_expr *new_expr(const int type, const te_expr *parameters[]) { void te_free_parameters(te_expr *n) { if (!n) return; - const int arity = get_arity(n->type); - if (arity > 0) te_free((te_expr *)n->parameters[arity - 1]); + int arity = get_arity(n->type); + // Free all parameters from the back to the front. + while (arity > 0) { + te_free((te_expr *)n->parameters[arity - 1]); + arity--; + } } From b193df8b42b172cb9fc5a25724ae38a2dae3607c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 2 Nov 2018 13:56:11 +0100 Subject: [PATCH 029/439] tinyexpr: Move enums together and stop explicit numbering We should _not_ be doing bit-fiddling with these, so there's no reason to care about the number. This also removes the unused "TE_VARIABLE" symbol. --- src/tinyexpr.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 8d955d274..1fa9ebb36 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -38,8 +38,10 @@ typedef double (*te_fun1)(double); typedef double (*te_fun0)(); enum { - TE_VARIABLE = 0, TE_CONSTANT, - TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3 + TE_CONSTANT = 0, + TE_FUNCTION0, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3, + TOK_NULL, TOK_ERROR, TOK_END, TOK_SEP, + TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_INFIX }; int get_arity(const int type) { @@ -49,12 +51,6 @@ int get_arity(const int type) { return 0; } -// TODO: Is it actually used that these share a space? -enum { - TOK_NULL = TE_FUNCTION0+16, TOK_ERROR, TOK_END, TOK_SEP, - TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_INFIX -}; - typedef struct te_expr { int type; union {double value; const void *function;}; @@ -465,7 +461,6 @@ double te_eval(const te_expr *n) { static void optimize(te_expr *n) { /* Evaluates as much as possible. */ if (n->type == TE_CONSTANT) return; - if (n->type == TE_VARIABLE) return; const int arity = get_arity(n->type); bool known = true; From c3c1ae18c6dc4155cea8bdcfb1d410ecb37e79d1 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 2 Nov 2018 14:28:59 +0100 Subject: [PATCH 030/439] tinyexpr: C++ify find_builtin --- src/tinyexpr.cpp | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 1fa9ebb36..74d98d761 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -30,6 +30,8 @@ #include #include +#include +#include #include // TODO: It would be nice not to rely on a typedef for this, especially one that can only do functions with two args. @@ -182,29 +184,18 @@ static const te_variable functions[] = { {"sinh", (const void *)(te_fun1)sinh, TE_FUNCTION1}, {"sqrt", (const void *)(te_fun1)sqrt, TE_FUNCTION1}, {"tan", (const void *)(te_fun1)tan, TE_FUNCTION1}, - {"tanh", (const void *)(te_fun1)tanh, TE_FUNCTION1}, - {0, 0, 0} + {"tanh", (const void *)(te_fun1)tanh, TE_FUNCTION1} }; static const te_variable *find_builtin(const char *name, int len) { - long imin = 0; - long imax = sizeof(functions) / sizeof(te_variable) - 2; - - /*Binary search.*/ - while (imax >= imin) { - const int i = (imin + ((imax-imin)/2)); - int c = strncmp(name, functions[i].name, len); - if (!c) c = '\0' - functions[i].name[len]; - if (c == 0) { - return functions + i; - } else if (c > 0) { - imin = i + 1; - } else { - imax = i - 1; - } - } - - return 0; + const auto end = std::end(functions); + const te_variable *found = std::lower_bound(std::begin(functions), end, name, + [len](const te_variable &lhs, const char *rhs) { + return strncmp(lhs.name, rhs, len) < 0; + }); + // We need to compare again because we might have gotten the first "larger" element. + if (found != end && strncmp(found->name, name, len) == 0) return found; + return NULL; } static double add(double a, double b) {return a + b;} From e504faeb384997620f72c0892f00f46b304566ff Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 2 Nov 2018 14:42:50 +0100 Subject: [PATCH 031/439] tinyexpr: Add Comments --- src/tinyexpr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 74d98d761..214273ae2 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -191,6 +191,7 @@ static const te_variable *find_builtin(const char *name, int len) { const auto end = std::end(functions); const te_variable *found = std::lower_bound(std::begin(functions), end, name, [len](const te_variable &lhs, const char *rhs) { + // The length is important because that's where the parens start return strncmp(lhs.name, rhs, len) < 0; }); // We need to compare again because we might have gotten the first "larger" element. @@ -242,6 +243,7 @@ void next_token(state *s) { } else { /* Look for an operator or special character. */ switch (s->next++[0]) { + // The "te_fun2" casts are necessary to pick the right overload. case '+': s->type = TOK_INFIX; s->function = (const void *)(te_fun2) add; break; case '-': s->type = TOK_INFIX; s->function = (const void *)(te_fun2) sub; break; case '*': s->type = TOK_INFIX; s->function = (const void *)(te_fun2) mul; break; From a433868363f5de7182277f6ded5cf49479149fa3 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 3 Nov 2018 19:37:36 +0100 Subject: [PATCH 032/439] tinyexpr: Make parameters te_expr* instead of void* --- src/tinyexpr.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 214273ae2..fb920d279 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -56,8 +56,7 @@ int get_arity(const int type) { typedef struct te_expr { int type; union {double value; const void *function;}; - // TODO: This void pointer is quite ugly. - void *parameters[1]; + te_expr *parameters[1]; } te_expr; // TODO: Rename since variables have been removed. @@ -92,7 +91,7 @@ void te_free(te_expr *n); static te_expr *new_expr(const int type, const te_expr *parameters[]) { const int arity = get_arity(type); - const int psize = sizeof(void*) * arity; + const int psize = sizeof(te_expr*) * arity; const int size = (sizeof(te_expr) - sizeof(void*)) + psize; te_expr *ret = (te_expr *)malloc(size); // This sets float to 0, which depends on the implementation. @@ -111,7 +110,7 @@ void te_free_parameters(te_expr *n) { int arity = get_arity(n->type); // Free all parameters from the back to the front. while (arity > 0) { - te_free((te_expr *)n->parameters[arity - 1]); + te_free(n->parameters[arity - 1]); arity--; } } @@ -427,7 +426,7 @@ static te_expr *expr(state *s) { #define TE_FUN(...) ((double(*)(__VA_ARGS__))n->function) -#define M(e) te_eval((te_expr *)n->parameters[e]) +#define M(e) te_eval(n->parameters[e]) double te_eval(const te_expr *n) { @@ -458,8 +457,8 @@ static void optimize(te_expr *n) { const int arity = get_arity(n->type); bool known = true; for (int i = 0; i < arity; ++i) { - optimize((te_expr *)n->parameters[i]); - if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) { + optimize(n->parameters[i]); + if ((n->parameters[i])->type != TE_CONSTANT) { known = false; } } From b8697e7795b5fa0a2a9bc888da07d8a47316d830 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 3 Nov 2018 19:41:51 +0100 Subject: [PATCH 033/439] tinyexpr: Rename te_variable to te_builtin Variables aren't a thing here anymore. --- src/tinyexpr.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index fb920d279..95cc71f3c 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -60,11 +60,11 @@ typedef struct te_expr { } te_expr; // TODO: Rename since variables have been removed. -typedef struct te_variable { +typedef struct te_builtin { const char *name; const void *address; int type; -} te_variable; +} te_builtin; typedef struct state { const char *start; @@ -157,7 +157,7 @@ static double ncr(double n, double r) { static double npr(double n, double r) {return ncr(n, r) * fac(r);} -static const te_variable functions[] = { +static const te_builtin functions[] = { /* must be in alphabetical order */ {"abs", (const void *)(te_fun1)fabs, TE_FUNCTION1}, {"acos", (const void *)(te_fun1)acos, TE_FUNCTION1}, @@ -186,10 +186,10 @@ static const te_variable functions[] = { {"tanh", (const void *)(te_fun1)tanh, TE_FUNCTION1} }; -static const te_variable *find_builtin(const char *name, int len) { +static const te_builtin *find_builtin(const char *name, int len) { const auto end = std::end(functions); - const te_variable *found = std::lower_bound(std::begin(functions), end, name, - [len](const te_variable &lhs, const char *rhs) { + const te_builtin *found = std::lower_bound(std::begin(functions), end, name, + [len](const te_builtin &lhs, const char *rhs) { // The length is important because that's where the parens start return strncmp(lhs.name, rhs, len) < 0; }); @@ -224,7 +224,7 @@ void next_token(state *s) { start = s->next; while ((s->next[0] >= 'a' && s->next[0] <= 'z') || (s->next[0] >= '0' && s->next[0] <= '9') || (s->next[0] == '_')) s->next++; - const te_variable *var = find_builtin(start, s->next - start); + const te_builtin *var = find_builtin(start, s->next - start); if (var) { switch(var->type) { From 2e11e6c692f4a7d1c865637d27a24cd2f79f526b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 4 Nov 2018 13:13:03 +0100 Subject: [PATCH 034/439] tinyexpr: Make te_expr a class Removes some #define weirdness. --- src/tinyexpr.cpp | 202 ++++++++++++++++++----------------------------- 1 file changed, 78 insertions(+), 124 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 95cc71f3c..a3c03ca21 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -33,6 +33,7 @@ #include #include #include +#include // TODO: It would be nice not to rely on a typedef for this, especially one that can only do functions with two args. typedef double (*te_fun2)(double, double); @@ -46,18 +47,32 @@ enum { TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_INFIX }; -int get_arity(const int type) { - if (type == TE_FUNCTION3) return 3; - if (type == TE_FUNCTION2) return 2; - if (type == TE_FUNCTION1) return 1; - return 0; -} - -typedef struct te_expr { +class te_expr { +public: int type; - union {double value; const void *function;}; - te_expr *parameters[1]; -} te_expr; + double value; + const void *function; + int arity = 0; + std::vector parameters; + te_expr(int t, const void *fun, te_expr *params) : type(t), value(0), function(fun) + { + // We fill in the arity automatically, because we need it here anyway. + switch (t) { + case TE_FUNCTION0: arity = 0; break; + case TE_FUNCTION1: arity = 1; break; + case TE_FUNCTION2: arity = 2; break; + case TE_FUNCTION3: arity = 3; break; + default: arity = 0; + } + + // We allow filling the parameters later. + if (params) { + for (int i = 0; i < arity; i++) parameters.push_back(params[i]); + } + } + + te_expr(int t, double val) : type(t), value(val), function(NULL) {} +}; // TODO: Rename since variables have been removed. typedef struct te_builtin { @@ -77,51 +92,10 @@ typedef struct state { /* Parses the input expression and binds variables. */ /* Returns NULL on error. */ -te_expr *te_compile(const char *expression, te_error_t *error); +te_expr te_compile(const char *expression, te_error_t *error); /* Evaluates the expression. */ -double te_eval(const te_expr *n); - -/* Frees the expression. */ -/* This is safe to call on NULL pointers. */ -void te_free(te_expr *n); - -// TODO: That move there? Ouch. Replace with a proper class with a constructor. -#define NEW_EXPR(type, ...) new_expr((type), std::move((const te_expr*[]){__VA_ARGS__})) - -static te_expr *new_expr(const int type, const te_expr *parameters[]) { - const int arity = get_arity(type); - const int psize = sizeof(te_expr*) * arity; - const int size = (sizeof(te_expr) - sizeof(void*)) + psize; - te_expr *ret = (te_expr *)malloc(size); - // This sets float to 0, which depends on the implementation. - // We rely on IEEE-754 floats anyway, so it's okay. - memset(ret, 0, size); - if (arity && parameters) { - memcpy(ret->parameters, parameters, psize); - } - ret->type = type; - return ret; -} - - -void te_free_parameters(te_expr *n) { - if (!n) return; - int arity = get_arity(n->type); - // Free all parameters from the back to the front. - while (arity > 0) { - te_free(n->parameters[arity - 1]); - arity--; - } -} - - -void te_free(te_expr *n) { - if (!n) return; - te_free_parameters(n); - free(n); -} - +double te_eval(const te_expr &n); static double pi() {return 3.14159265358979323846;} static double e() {return 2.71828182845904523536;} @@ -261,24 +235,21 @@ void next_token(state *s) { } -static te_expr *expr(state *s); -static te_expr *power(state *s); +static te_expr expr(state *s); +static te_expr power(state *s); -static te_expr *base(state *s) { +static te_expr base(state *s) { /* = | | {"(" ")"} | | "(" {"," } ")" | "(" ")" */ - te_expr *ret; - int arity; + te_expr ret(0, NULL, NULL); switch (s->type) { case TOK_NUMBER: - ret = new_expr(TE_CONSTANT, 0); - ret->value = s->value; + ret = te_expr(TE_CONSTANT, s->value); next_token(s); break; case TE_FUNCTION0: - ret = new_expr(s->type, 0); - ret->function = s->function; + ret = te_expr(s->type, s->function, NULL); next_token(s); if (s->type == TOK_OPEN) { next_token(s); @@ -294,27 +265,24 @@ static te_expr *base(state *s) { case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: - arity = get_arity(s->type); - - ret = new_expr(s->type, 0); - ret->function = s->function; + ret = te_expr(s->type, s->function, NULL); next_token(s); if (s->type == TOK_OPEN) { int i; - for(i = 0; i < arity; i++) { + for(i = 0; i < ret.arity; i++) { next_token(s); - ret->parameters[i] = expr(s); + ret.parameters.push_back(expr(s)); if(s->type != TOK_SEP) { break; } } - if(s->type == TOK_CLOSE && i == arity - 1) { + if(s->type == TOK_CLOSE && i == ret.arity - 1) { next_token(s); } else if (s->type != TOK_ERROR || s->error == TE_ERROR_UNKNOWN) { s->type = TOK_ERROR; - s->error = i < arity ? TE_ERROR_TOO_FEW_ARGS + s->error = i < ret.arity ? TE_ERROR_TOO_FEW_ARGS : TE_ERROR_TOO_MANY_ARGS; } } else if (s->type != TOK_ERROR @@ -343,16 +311,14 @@ static te_expr *base(state *s) { // This means we have too few things. // Instead of introducing another error, just call it // "too few args". - ret = new_expr(0, 0); + ret = te_expr(TE_CONSTANT, NAN); s->type = TOK_ERROR; s->error = TE_ERROR_TOO_FEW_ARGS; - ret->value = NAN; break; default: - ret = new_expr(0, 0); + ret = te_expr(TE_CONSTANT, NAN); s->type = TOK_ERROR; s->error = TE_ERROR_UNKNOWN; - ret->value = NAN; break; } @@ -360,7 +326,7 @@ static te_expr *base(state *s) { } -static te_expr *power(state *s) { +static te_expr power(state *s) { /* = {("-" | "+")} */ int sign = 1; while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { @@ -368,72 +334,70 @@ static te_expr *power(state *s) { next_token(s); } - te_expr *ret; - if (sign == 1) { - ret = base(s); + te_expr ret = base(s); + return ret; } else { - ret = NEW_EXPR(TE_FUNCTION1, base(s)); - ret->function = (const void *) negate; + te_expr params[1] = { base(s) }; + te_expr ret = te_expr(TE_FUNCTION1, (const void *) negate, params); + return ret; } - - return ret; } -static te_expr *factor(state *s) { +static te_expr factor(state *s) { /* = {"^" } */ - te_expr *ret = power(s); + te_expr ret = power(s); while (s->type == TOK_INFIX && (s->function == (const void*)(te_fun2)pow)) { te_fun2 t = (te_fun2) s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2, ret, power(s)); - ret->function = (const void *) t; + + te_expr param[2] = { ret, power(s) }; + ret = te_expr(TE_FUNCTION2, (const void *) t, param); } return ret; } -static te_expr *term(state *s) { +static te_expr term(state *s) { /* = {("*" | "/" | "%") } */ - te_expr *ret = factor(s); + te_expr ret = factor(s); while (s->type == TOK_INFIX && (s->function == (const void*)(te_fun2)mul || s->function == (const void*)(te_fun2)divide || s->function == (const void*)(te_fun2)fmod)) { te_fun2 t = (te_fun2) s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2, ret, factor(s)); - ret->function = (const void *) t; + te_expr params[2] = { ret, factor(s) }; + ret = te_expr(TE_FUNCTION2, (const void *) t, params); } return ret; } -static te_expr *expr(state *s) { +static te_expr expr(state *s) { /* = {("+" | "-") } */ - te_expr *ret = term(s); + te_expr ret = term(s); while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { te_fun2 t = (te_fun2) s->function; next_token(s); - ret = NEW_EXPR(TE_FUNCTION2, ret, term(s)); - ret->function = (const void *) t; + te_expr params[2] = { ret, term(s) }; + ret = te_expr(TE_FUNCTION2, (const void *) t, params); } return ret; } -#define TE_FUN(...) ((double(*)(__VA_ARGS__))n->function) -#define M(e) te_eval(n->parameters[e]) +#define TE_FUN(...) ((double(*)(__VA_ARGS__))n.function) +#define M(e) te_eval(n.parameters[e]) -double te_eval(const te_expr *n) { - if (!n) return NAN; - - switch(n->type) { - case TE_CONSTANT: return n->value; +double te_eval(const te_expr &n) { + switch(n.type) { + case TE_CONSTANT: + return n.value; case TE_FUNCTION0: return TE_FUN(void)(); case TE_FUNCTION1: @@ -444,43 +408,39 @@ double te_eval(const te_expr *n) { return TE_FUN(double, double, double)(M(0), M(1), M(2)); default: return NAN; } - } #undef TE_FUN #undef M -static void optimize(te_expr *n) { +static void optimize(te_expr &n) { /* Evaluates as much as possible. */ - if (n->type == TE_CONSTANT) return; + if (n.type == TE_CONSTANT) return; - const int arity = get_arity(n->type); bool known = true; - for (int i = 0; i < arity; ++i) { - optimize(n->parameters[i]); - if ((n->parameters[i])->type != TE_CONSTANT) { + for (int i = 0; i < n.arity; ++i) { + optimize(n.parameters[i]); + if ((n.parameters[i]).type != TE_CONSTANT) { known = false; } } if (known) { const double value = te_eval(n); - te_free_parameters(n); - n->type = TE_CONSTANT; - n->value = value; + n.type = TE_CONSTANT; + n.value = value; } } -te_expr *te_compile(const char *expression, te_error_t *error) { +te_expr te_compile(const char *expression, te_error_t *error) { state s; s.start = s.next = expression; s.error = TE_ERROR_NONE; next_token(&s); - te_expr *root = expr(&s); + te_expr root = expr(&s); if (s.type != TOK_END) { - te_free(root); if (error) { error->position = (s.next - s.start) + 1; if (s.error != TE_ERROR_NONE) { @@ -495,22 +455,16 @@ te_expr *te_compile(const char *expression, te_error_t *error) { error->type = TE_ERROR_TOO_MANY_ARGS; } } - return 0; } else { optimize(root); if (error) error->position = 0; - return root; } + return root; } double te_interp(const char *expression, te_error_t *error) { - te_expr *n = te_compile(expression, error); + te_expr n = te_compile(expression, error); double ret; - if (n) { - ret = te_eval(n); - te_free(n); - } else { - ret = NAN; - } + ret = te_eval(n); return ret; } From e33d29a5d83dfa1bdd163085ed739aef9864ebb6 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 30 Dec 2018 20:33:35 +0100 Subject: [PATCH 035/439] tinyexpr: Reserve arity parameters This somehow fixes heap-buffer-overflow? I thought this was supposed to be safe. --- src/tinyexpr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index a3c03ca21..356ed90bb 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -65,6 +65,8 @@ public: default: arity = 0; } + parameters.reserve(arity); + // We allow filling the parameters later. if (params) { for (int i = 0; i < arity; i++) parameters.push_back(params[i]); From b0072482e4146e3e0219960834331516f4fb6865 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 30 Dec 2018 22:32:07 +0100 Subject: [PATCH 036/439] Only warn on exec for background jobs If it's a foreground job, it is related to the currently running exec. This fixes exec in functions, i.e. function reload exec fish end would previously always ask about the "function reload" job. Fixes #5449. Fixes oh-my-fish/oh-my-fish#664. --- src/parse_execution.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 701d41106..09b7f7d9d 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -773,7 +773,10 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process( bool have_bg = false; const job_t *bg = nullptr; while ((bg = jobs.next())) { - if (!bg->is_completed()) { + // The assumption here is that if it is a foreground job, + // it's related to us. + // This stops us from asking if we're doing `exec` inside a function. + if (!bg->is_completed() && !bg->is_foreground()) { have_bg = true; break; } From 259cf02aacf3de83ccd9e12e434c65de5ec7ccd4 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 30 Dec 2018 19:02:38 -0600 Subject: [PATCH 037/439] Wait on individual processes in a job in reverse order This fixes #5438 by having fish block while waiting on a foreground job via its individual processes by enumerating the procs in reverse order, such that we hang waiting for the last job in the IO chain to terminate, rather than the first. --- src/proc.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/proc.cpp b/src/proc.cpp index f31b720c4..a5895c7cd 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -451,7 +451,12 @@ static bool process_mark_finished_children(bool block_on_fg) { // If the pgid is 0, we need to wait by process because that's invalid. // This happens in firejail for reasons not entirely clear to me. bool wait_by_process = !j->job_chain_is_fully_constructed() || j->pgid == 0; - process_list_t::iterator process = j->processes.begin(); + // When waiting on processes individually in a pipeline, we need to enumerate in reverse + // order so that the first process we actually wait on (i.e. ~WNOHANG) is the last process + // in the IO chain, because that's the one that controls the lifetime of the foreground job + // - as long as it is still running, we are in the background and once it exits or is + // killed, all previous jobs in the IO pipeline must necessarily terminate as well. + auto process = j->processes.rbegin(); // waitpid(2) returns 1 process each time, we need to keep calling it until we've reaped all // children of the pgrp in question or else we can't reset the dirty_state flag. In all // cases, calling waitpid(2) is faster than potentially calling select_try() on a process @@ -469,10 +474,15 @@ static bool process_mark_finished_children(bool block_on_fg) { // parent job has been fully constructed), we need to call waitpid(2) on the // individual processes of the child job instead of using a catch-all waitpid(2) // call on the job's process group. - if (process == j->processes.end()) { + if (process == j->processes.rend()) { break; } assert((*process)->pid != INVALID_PID && "Waiting by process on an invalid PID!"); + if ((options & WNOHANG) == 0) { + debug(4, "Waiting on individual process %d", (*process)->pid); + } else { + debug(4, "waitpid with WNOHANG on individual process %d", (*process)->pid); + } pid = waitpid((*process)->pid, &status, options); process++; } else { @@ -513,7 +523,7 @@ static bool process_mark_finished_children(bool block_on_fg) { } // Poll disowned processes/process groups, but do nothing with the result. Only used to avoid - // zombie processes. Entries have already be converted to negative for process groups. + // zombie processes. Entries have already been converted to negative for process groups. int status; s_disowned_pids.erase(std::remove_if(s_disowned_pids.begin(), s_disowned_pids.end(), [&status](pid_t pid) { return waitpid(pid, &status, WNOHANG) > 0; }), From 840619197e32c43a697b3479179b007aa0e3fab5 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 30 Dec 2018 19:25:50 -0600 Subject: [PATCH 038/439] Optimize ASSERT_IS_MAIN_THREAD() By using a user-land thread-local integer and lock-free (at least under x86/x64) atomics, we can implement a safe `assert_is_main_thread()` without calling into the kernel. Thread-local variables are part of C++11. This is called a lot in some performance-sensitive areas, so it is worth optimizing. --- src/common.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 8dabeb570..688ed30d2 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -37,6 +37,7 @@ #endif #include +#include #include // IWYU pragma: keep #include @@ -53,8 +54,7 @@ constexpr wint_t NOT_A_WCHAR = static_cast(WEOF); struct termios shell_modes; -// Note we foolishly assume that pthread_t is just a primitive. But it might be a struct. -static pthread_t main_thread_id = 0; +static std::atomic thread_id { 0 }; static bool thread_asserts_cfg_for_testing = false; wchar_t ellipsis_char; @@ -2162,7 +2162,11 @@ __attribute__((noinline)) void debug_thread_error(void) { } } -void set_main_thread() { main_thread_id = pthread_self(); } +void set_main_thread() { + // Just call is_main_thread() once to force increment of thread_id + // We store the result as `volatile` to guarantee the function call is not optimized away + volatile bool _x = is_main_thread(); +} void configure_thread_assertions_for_testing() { thread_asserts_cfg_for_testing = true; } @@ -2198,8 +2202,8 @@ void restore_term_foreground_process_group() { } bool is_main_thread() { - assert(main_thread_id != 0); - return main_thread_id == pthread_self(); + static thread_local int local_thread_id = thread_id++; + return local_thread_id == 0; } void assert_is_main_thread(const char *who) { From 8dddc62aeb636c302de3876607027c7b48a94ba0 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 30 Dec 2018 19:39:59 -0600 Subject: [PATCH 039/439] Optimize ASSERT_IS_NOT_FORKED_CHILD() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use `pthread_atfork()` to mark child processes as dirty when `fork()` is invoked rather than needing to call into the kernel each time `ASSERT_IS_NOT_FORKED_CHILD()` is called. This makes simple test cases that hit `ASSERT_IS_NOT_FORKED_CHILD()` 1.8x faster. ------------------------ With a7998c4829a80269bcd6af941812e35c86bf49e2 reverted but before this optimization: ``` mqudsi@ZBOOK ~/r/fish-shell> hyperfine -S build/fish 'for i in (seq 100000); test 1 = 1; end' Benchmark #1: for i in (seq 100000); test 1 = 1; end Time (mean ± σ): 717.8 ms ± 14.9 ms [User: 503.4 ms, System: 216.2 ms] Range (min … max): 692.3 ms … 740.2 ms ``` With a7998c4829a80269bcd6af941812e35c86bf49e2 reverted and with this optimization: ``` mqudsi@ZBOOK ~/r/fish-shell> hyperfine -S build/fish 'for i in (seq 100000); test 1 = 1; end' Benchmark #1: for i in (seq 100000); test 1 = 1; end Time (mean ± σ): 397.2 ms ± 22.3 ms [User: 322.1 ms, System: 79.3 ms] Range (min … max): 376.0 ms … 444.0 ms ``` Without a7998c4829a80269bcd6af941812e35c86bf49e2 reverted and with this optimization: mqudsi@ZBOOK ~/r/fish-shell> hyperfine -S build/fish 'for i in (seq 100000); test 1 = 1; end' Benchmark #1: for i in (seq 100000); test 1 = 1; end Time (mean ± σ): 423.4 ms ± 51.6 ms [User: 363.2 ms, System: 61.3 ms] Range (min … max): 378.4 ms … 541.1 ms ``` --- src/common.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 688ed30d2..527210a1f 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -54,7 +54,11 @@ constexpr wint_t NOT_A_WCHAR = static_cast(WEOF); struct termios shell_modes; +/// This allows us to determine if we're running on the main thread static std::atomic thread_id { 0 }; +/// This allows us to notice when we've forked. +static bool is_forked_proc = false; +/// This allows us to bypass the main thread checks static bool thread_asserts_cfg_for_testing = false; wchar_t ellipsis_char; @@ -2171,20 +2175,13 @@ void set_main_thread() { void configure_thread_assertions_for_testing() { thread_asserts_cfg_for_testing = true; } bool is_forked_child() { - // 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) { - debug(0, L"Uh-oh: getpid() != initial_pid: %d != %d\n", getpid(), initial_pid); - while (1) sleep(10000); - } - return is_child_of_fork; + return is_forked_proc; } void setup_fork_guards() { - // Notice when we fork by stashing our pid. This seems simpler than pthread_atfork(). - initial_pid = getpid(); + pthread_atfork(nullptr, nullptr, []() { + is_forked_proc = true; + }); } void save_term_foreground_process_group() { From b4301ff54fab5e9884bc3d89cbf791fad8116844 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 30 Dec 2018 19:41:17 -0600 Subject: [PATCH 040/439] Drop initial_pid and optimize `debug_shared()` fast case If we are running on the main thread, don't call `getpid()` unnecessarily from `debug_shared()`. --- src/common.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 527210a1f..9362c69b2 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -70,8 +70,6 @@ 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 -/// 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. /// This is set during startup and not modified after. @@ -611,11 +609,11 @@ bool should_suppress_stderr_for_tests() { } static void debug_shared(const wchar_t level, const wcstring &msg) { - pid_t current_pid = getpid(); - - if (current_pid == initial_pid) { + pid_t current_pid; + if (!is_forked_child()) { fwprintf(stderr, L"<%lc> %ls: %ls\n", (unsigned long)level, program_name, msg.c_str()); } else { + current_pid = getpid(); fwprintf(stderr, L"<%lc> %ls: %d: %ls\n", (unsigned long)level, program_name, current_pid, msg.c_str()); } From 077d656b87abf6db4d47faf6449a377ebe3ee56a Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 30 Dec 2018 20:21:36 -0600 Subject: [PATCH 041/439] Allow redeclaration of main process via `setup_fork_guards()` This is necessary for the history race condition test to succeed. (That test is permanently disabled under WSL (as it always fails) so I didn't catch this on my end.) --- src/common.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/common.cpp b/src/common.cpp index 9362c69b2..94367bdac 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -2177,6 +2177,15 @@ bool is_forked_child() { } void setup_fork_guards() { + static bool already_initialized = false; + + is_forked_proc = false; + if (already_initialized) { + // Just mark this process as main and exit + return; + } + + already_initialized = true; pthread_atfork(nullptr, nullptr, []() { is_forked_proc = true; }); From bfe08a471dbcee62d21c95fcfd11dfdf5c909c1a Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 30 Dec 2018 20:15:49 -0600 Subject: [PATCH 042/439] Remove fish_mutex_t wrapper around std::mutex @ridiculousfish had introduced this in 3a45cad12ea392bbc5fe38010d1cdf4a90ba7adb to work around an issue with Coverity Scan where it couldn't tell the mutex was correctly locked, but even with the `fish_mutex_t` hack, it still emits the same warnings, so there's no pointing in keeping it. --- src/autoload.h | 2 +- src/common.cpp | 11 +++++++--- src/common.h | 41 ++++++-------------------------------- src/complete.cpp | 4 +--- src/env.cpp | 2 +- src/env_universal_common.h | 2 +- src/history.h | 2 +- src/iothread.cpp | 6 +++--- 8 files changed, 22 insertions(+), 48 deletions(-) diff --git a/src/autoload.h b/src/autoload.h index 83614b761..ceb703c53 100644 --- a/src/autoload.h +++ b/src/autoload.h @@ -46,7 +46,7 @@ class env_vars_snapshot_t; class autoload_t : public lru_cache_t { private: /// Lock for thread safety. - fish_mutex_t lock; + std::mutex lock; /// The environment variable name. const wcstring env_var_name; /// The paths from which to autoload, or missing if none. diff --git a/src/common.cpp b/src/common.cpp index 94367bdac..e13f68738 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -78,7 +78,7 @@ static pid_t initial_fg_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 /// common_get_width()/common_get_height(). -static fish_mutex_t termsize_lock; +static std::mutex termsize_lock; static struct winsize termsize = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; static volatile bool termsize_valid = false; @@ -2234,11 +2234,16 @@ void assert_is_background_thread(const char *who) { } } -void fish_mutex_t::assert_is_locked(const char *who, const char *caller) const { - if (!is_locked_) { +void assert_is_locked(void *vmutex, const char *who, const char *caller) { + std::mutex *mutex = static_cast(vmutex); + + // Note that std::mutex.try_lock() is allowed to return false when the mutex isn't + // actually locked; fortunately we are checking the opposite so we're safe. + if (mutex->try_lock()) { debug(0, "%s is not locked when it should be in '%s'", who, caller); debug(0, "Break on debug_thread_error to debug."); debug_thread_error(); + mutex->unlock(); } } diff --git a/src/common.h b/src/common.h index 49a381f0f..889036841 100644 --- a/src/common.h +++ b/src/common.h @@ -543,39 +543,10 @@ 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__) -// fish_mutex is a wrapper around std::mutex that tracks whether it is locked, allowing for checking -// if the mutex is locked. It owns a boolean guarded by the lock that records whether the lock is -// currently locked; this is only used by assertions for correctness. -class fish_mutex_t { - std::mutex lock_{}; - bool is_locked_{false}; - - public: - constexpr fish_mutex_t() = default; - ~fish_mutex_t() = default; - - void lock() { - lock_.lock(); - is_locked_ = true; - } - - void unlock() { - is_locked_ = false; - lock_.unlock(); - } - - // assert that this lock (identified as 'who') is locked in the function 'caller'. - void assert_is_locked(const char *who, const char *caller) const; - - // return the underlying std::mutex. Note the fish_mutex_t cannot track locks to the underlying - // mutex; do not use assert_is_locked() with this. - std::mutex &get_mutex() { return lock_; } -}; - /// 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(const fish_mutex_t &m, const char *who, const char *caller); -#define ASSERT_IS_LOCKED(x) (x).assert_is_locked(#x, __FUNCTION__) +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. wcstring format_size(long long sz); @@ -706,7 +677,7 @@ class null_terminated_array_t { // null_terminated_array_t. void convert_wide_array_to_narrow(const null_terminated_array_t &arr, null_terminated_array_t *output); -typedef std::lock_guard scoped_lock; +typedef std::lock_guard scoped_lock; typedef std::lock_guard scoped_rlock; // An object wrapping a scoped lock and a value @@ -721,8 +692,8 @@ typedef std::lock_guard scoped_rlock; // template class acquired_lock { - std::unique_lock lock; - acquired_lock(fish_mutex_t &lk, DATA *v) : lock(lk), value(v) {} + std::unique_lock lock; + acquired_lock(std::mutex &lk, DATA *v) : lock(lk), value(v) {} template friend class owning_lock; @@ -752,7 +723,7 @@ class owning_lock { owning_lock(owning_lock &&) = default; owning_lock &operator=(owning_lock &&) = default; - fish_mutex_t lock; + std::mutex lock; DATA data; public: diff --git a/src/complete.cpp b/src/complete.cpp index c6f2bf432..440d5f3c9 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -1253,9 +1253,7 @@ bool completer_t::try_complete_user(const wcstring &str) { bool result = false; size_t name_len = str.length() - 1; - // We don't bother with the thread-safe `getpwent_r()` variant because this is the sole place - // where we call getpwent(). - static fish_mutex_t lock; + static std::mutex lock; scoped_lock locker(lock); setpwent(); // cppcheck-suppress getpwentCalled diff --git a/src/env.cpp b/src/env.cpp index 22e8c1097..4085b8576 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -139,7 +139,7 @@ class env_node_t { bool contains_any_of(const wcstring_list_t &vars) const; }; -static fish_mutex_t env_lock; +static std::mutex env_lock; static bool local_scope_exports(const env_node_t *n); diff --git a/src/env_universal_common.h b/src/env_universal_common.h index 6e055db68..3a8daf6c9 100644 --- a/src/env_universal_common.h +++ b/src/env_universal_common.h @@ -52,7 +52,7 @@ class env_universal_t { // fish wrote the uvars contents. bool ok_to_save{true}; - mutable fish_mutex_t lock; + mutable std::mutex lock; bool load_from_path(const wcstring &path, callback_data_list_t &callbacks); void load_from_fd(int fd, callback_data_list_t &callbacks); diff --git a/src/history.h b/src/history.h index 08e6f36c4..6419cdcc9 100644 --- a/src/history.h +++ b/src/history.h @@ -122,7 +122,7 @@ class history_t { void add(const history_item_t &item, bool pending = false); // Lock for thread safety. - fish_mutex_t lock; + std::mutex lock; // Internal function. void clear_file_state(); diff --git a/src/iothread.cpp b/src/iothread.cpp index c3fc55736..d834da8c1 100644 --- a/src/iothread.cpp +++ b/src/iothread.cpp @@ -72,9 +72,9 @@ static owning_lock s_spawn_requests; static owning_lock> s_result_queue; // "Do on main thread" support. -static fish_mutex_t s_main_thread_performer_lock; // protects the main thread requests +static std::mutex s_main_thread_performer_lock; // protects the main thread requests static std::condition_variable s_main_thread_performer_cond; // protects the main thread requests -static fish_mutex_t s_main_thread_request_q_lock; // protects the queue +static std::mutex s_main_thread_request_q_lock; // protects the queue static std::queue s_main_thread_request_queue; // Notifying pipes. @@ -332,7 +332,7 @@ void iothread_perform_on_main(void_function_t &&func) { assert_with_errno(write_loop(s_write_pipe, &wakeup_byte, sizeof wakeup_byte) != -1); // Wait on the condition, until we're done. - std::unique_lock perform_lock(s_main_thread_performer_lock.get_mutex()); + std::unique_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 From 2ebdcf82eebec230549b64dc740ea604c7772049 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 30 Dec 2018 20:46:49 -0600 Subject: [PATCH 043/439] Do not use up the ~WNOHANG waitpid call on completed processes This is the more correct fix for #5447, as regardless of which process in the job (be it the first or the last) finished first, once we have waited on a process without ~WNOHANG we don't do that for any subsequent processes in the job. It is also a waste to call into the kernel to wait for a process we already know is completed! --- src/proc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/proc.cpp b/src/proc.cpp index a5895c7cd..7f5f37b9d 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -478,6 +478,11 @@ static bool process_mark_finished_children(bool block_on_fg) { break; } assert((*process)->pid != INVALID_PID && "Waiting by process on an invalid PID!"); + if ((*process)->completed) { + // This process has already been waited on to completion + continue; + } + if ((options & WNOHANG) == 0) { debug(4, "Waiting on individual process %d", (*process)->pid); } else { From 0337588979d71de72e6d7bb96a765d87fd9f4d4a Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 30 Dec 2018 21:44:14 -0600 Subject: [PATCH 044/439] fixup! Do not use up the ~WNOHANG waitpid call on completed processes --- src/proc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/proc.cpp b/src/proc.cpp index 7f5f37b9d..4c824e090 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -480,15 +480,17 @@ static bool process_mark_finished_children(bool block_on_fg) { assert((*process)->pid != INVALID_PID && "Waiting by process on an invalid PID!"); if ((*process)->completed) { // This process has already been waited on to completion + process++; continue; } if ((options & WNOHANG) == 0) { - debug(4, "Waiting on individual process %d", (*process)->pid); + debug(4, "Waiting on individual process %d: %ls", (*process)->pid, (*process)->argv0()); } else { debug(4, "waitpid with WNOHANG on individual process %d", (*process)->pid); } pid = waitpid((*process)->pid, &status, options); + process++; } else { // A negative PID passed in to `waitpid()` means wait on any child in that process From 803619b19bdd337b6f4403301114c2c6bcee4c4e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 31 Dec 2018 00:43:26 -0600 Subject: [PATCH 045/439] Convert some old-school int booleans to bool --- src/builtin_fg.cpp | 4 ++-- src/builtin_jobs.cpp | 6 +++--- src/highlight.cpp | 8 ++++---- src/proc.cpp | 14 +++++++------- src/proc.h | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/builtin_fg.cpp b/src/builtin_fg.cpp index f8483687d..ac4af798a 100644 --- a/src/builtin_fg.cpp +++ b/src/builtin_fg.cpp @@ -53,12 +53,12 @@ int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // 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. int pid; - int found_job = 0; + bool found_job = false; pid = fish_wcstoi(argv[optind]); if (!(errno || pid < 0)) { j = job_t::from_pid(pid); - if (j) found_job = 1; + if (j) found_job = true; } if (found_job) { diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index ffddc85c5..f57ed101b 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -115,7 +115,7 @@ static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); - int found = 0; + bool found = false; int mode = JOBS_DEFAULT; int print_last = 0; @@ -210,7 +210,7 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (j && !j->is_completed() && j->is_constructed()) { builtin_jobs_print(j, mode, false, streams); - found = 1; + found = true; } else { streams.err.append_format(_(L"%ls: No suitable job: %ls\n"), cmd, argv[i]); return STATUS_CMD_ERROR; @@ -223,7 +223,7 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // Ignore unconstructed jobs, i.e. ourself. if (j->is_constructed() && !j->is_completed()) { builtin_jobs_print(j, mode, !found && !streams.out_is_redirected, streams); - found = 1; + found = true; } } } diff --git a/src/highlight.cpp b/src/highlight.cpp index c683b1161..a6612b68f 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -1231,7 +1231,7 @@ static void highlight_universal_internal(const wcstring &buffstr, wchar_t prev_q = 0; const wchar_t *const buff = buffstr.c_str(); const wchar_t *str = buff; - int match_found = 0; + bool match_found = false; while (*str) { switch (*str) { @@ -1257,7 +1257,7 @@ static void highlight_universal_internal(const wcstring &buffstr, highlight_make_background(highlight_spec_match); color.at(pos2) |= highlight_make_background(highlight_spec_match); - match_found = 1; + match_found = true; } prev_q = *str == L'\"' ? L'\'' : L'\"'; } else { @@ -1286,7 +1286,7 @@ static void highlight_universal_internal(const wcstring &buffstr, wchar_t dec_char = *(wcschr(L"()[]{}", c) + step); wchar_t inc_char = c; int level = 0; - int match_found = 0; + bool match_found = false; 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++; @@ -1295,7 +1295,7 @@ static void highlight_universal_internal(const wcstring &buffstr, long pos2 = i; color.at(pos) |= highlight_spec_match << 16; color.at(pos2) |= highlight_spec_match << 16; - match_found = 1; + match_found = true; break; } } diff --git a/src/proc.cpp b/src/proc.cpp index 4c824e090..8072a64a7 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -604,10 +604,10 @@ void proc_fire_event(const wchar_t *msg, int type, pid_t pid, int status) { event.arguments.resize(0); } -static int process_clean_after_marking(bool allow_interactive) { +static bool process_clean_after_marking(bool allow_interactive) { ASSERT_IS_MAIN_THREAD(); job_t *jnext; - int found = 0; + bool found = false; // this function may fire an event handler, we do not want to call ourselves recursively (to // avoid infinite recursion). @@ -697,7 +697,7 @@ static int process_clean_after_marking(bool allow_interactive) { if (clr_eol) tputs(clr_eol, 1, &writeb); fwprintf(stdout, L"\n"); } - found = 1; + found = false; p->status = 0; // clear status so it is not reported more than once } @@ -707,7 +707,7 @@ static int process_clean_after_marking(bool allow_interactive) { if (!j->is_foreground() && !j->get_flag(job_flag_t::NOTIFIED) && !j->get_flag(job_flag_t::SKIP_NOTIFICATION)) { format_job_info(j, JOB_ENDED); - found = 1; + found = true; } // TODO: The generic process-exit event is useless and unused. // Remove this in future. @@ -725,7 +725,7 @@ static int process_clean_after_marking(bool allow_interactive) { // Notify the user about newly stopped jobs. if (!j->get_flag(job_flag_t::SKIP_NOTIFICATION)) { format_job_info(j, JOB_STOPPED); - found = 1; + found = true; } j->set_flag(job_flag_t::NOTIFIED, true); } @@ -738,9 +738,9 @@ static int process_clean_after_marking(bool allow_interactive) { return found; } -int job_reap(bool allow_interactive) { +bool job_reap(bool allow_interactive) { ASSERT_IS_MAIN_THREAD(); - int found = 0; + bool found = false; process_mark_finished_children(false); diff --git a/src/proc.h b/src/proc.h index 99b3b2c26..2f14c89da 100644 --- a/src/proc.h +++ b/src/proc.h @@ -354,7 +354,7 @@ int proc_get_last_status(); /// 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); +bool job_reap(bool interactive); /// Signal handler for SIGCHLD. Mark any processes with relevant information. void job_handle_signal(int signal, siginfo_t *info, void *con); From a8eb02f9f59350f222295e16b8f68dc3b45d9ed5 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 31 Dec 2018 02:31:48 -0600 Subject: [PATCH 046/439] Fix wcstod_l detection under FreeBSD --- cmake/ConfigureChecks.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index 1444361e9..5182ba808 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -72,7 +72,7 @@ CHECK_CXX_SYMBOL_EXISTS(wcsdup wchar.h HAVE_WCSDUP) CHECK_CXX_SYMBOL_EXISTS(wcslcpy wchar.h HAVE_WCSLCPY) CHECK_CXX_SYMBOL_EXISTS(wcsncasecmp wchar.h HAVE_WCSNCASECMP) CHECK_CXX_SYMBOL_EXISTS(wcsndup wchar.h HAVE_WCSNDUP) -CHECK_CXX_SYMBOL_EXISTS(wcstod_l wchar.h HAVE_WCSTOD_L) +CHECK_CXX_SYMBOL_EXISTS(wcstod_l "wchar.h;xlocale.h" HAVE_WCSTOD_L) CHECK_CXX_SYMBOL_EXISTS(_sys_errs stdlib.h HAVE__SYS__ERRS) From 7078aa46428aa083053bcefb9e67d41293d6d13a Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 31 Dec 2018 10:07:52 +0100 Subject: [PATCH 047/439] cmake: Add missing HAVE_WCSTOD_L #cmakedefine Turns out we've been using the fallback everywhere. See #5453. --- config_cmake.h.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config_cmake.h.in b/config_cmake.h.in index d234acba9..47cfcc5e2 100644 --- a/config_cmake.h.in +++ b/config_cmake.h.in @@ -115,6 +115,9 @@ /* Define to 1 if you have the `wcsndup' function. */ #cmakedefine HAVE_WCSNDUP 1 +/* Define to 1 if you have the `wcstod_l' function. */ +#cmakedefine HAVE_WCSTOD_L 1 + /* Define to 1 if the winsize struct and TIOCGWINSZ macro exist */ #cmakedefine HAVE_WINSIZE 1 From a615151d916a28dde556345a59825a65d5057023 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 31 Dec 2018 10:27:17 +0100 Subject: [PATCH 048/439] Revert "tinyexpr: Make te_expr a class" Turns out this crashes on musl when doing te_expr::parameter.push_back(). For some reason. This reverts commit 2e11e6c692f4a7d1c865637d27a24cd2f79f526b. --- src/tinyexpr.cpp | 204 ++++++++++++++++++++++++++++------------------- 1 file changed, 124 insertions(+), 80 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 356ed90bb..95cc71f3c 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -33,7 +33,6 @@ #include #include #include -#include // TODO: It would be nice not to rely on a typedef for this, especially one that can only do functions with two args. typedef double (*te_fun2)(double, double); @@ -47,34 +46,18 @@ enum { TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_INFIX }; -class te_expr { -public: +int get_arity(const int type) { + if (type == TE_FUNCTION3) return 3; + if (type == TE_FUNCTION2) return 2; + if (type == TE_FUNCTION1) return 1; + return 0; +} + +typedef struct te_expr { int type; - double value; - const void *function; - int arity = 0; - std::vector parameters; - te_expr(int t, const void *fun, te_expr *params) : type(t), value(0), function(fun) - { - // We fill in the arity automatically, because we need it here anyway. - switch (t) { - case TE_FUNCTION0: arity = 0; break; - case TE_FUNCTION1: arity = 1; break; - case TE_FUNCTION2: arity = 2; break; - case TE_FUNCTION3: arity = 3; break; - default: arity = 0; - } - - parameters.reserve(arity); - - // We allow filling the parameters later. - if (params) { - for (int i = 0; i < arity; i++) parameters.push_back(params[i]); - } - } - - te_expr(int t, double val) : type(t), value(val), function(NULL) {} -}; + union {double value; const void *function;}; + te_expr *parameters[1]; +} te_expr; // TODO: Rename since variables have been removed. typedef struct te_builtin { @@ -94,10 +77,51 @@ typedef struct state { /* Parses the input expression and binds variables. */ /* Returns NULL on error. */ -te_expr te_compile(const char *expression, te_error_t *error); +te_expr *te_compile(const char *expression, te_error_t *error); /* Evaluates the expression. */ -double te_eval(const te_expr &n); +double te_eval(const te_expr *n); + +/* Frees the expression. */ +/* This is safe to call on NULL pointers. */ +void te_free(te_expr *n); + +// TODO: That move there? Ouch. Replace with a proper class with a constructor. +#define NEW_EXPR(type, ...) new_expr((type), std::move((const te_expr*[]){__VA_ARGS__})) + +static te_expr *new_expr(const int type, const te_expr *parameters[]) { + const int arity = get_arity(type); + const int psize = sizeof(te_expr*) * arity; + const int size = (sizeof(te_expr) - sizeof(void*)) + psize; + te_expr *ret = (te_expr *)malloc(size); + // This sets float to 0, which depends on the implementation. + // We rely on IEEE-754 floats anyway, so it's okay. + memset(ret, 0, size); + if (arity && parameters) { + memcpy(ret->parameters, parameters, psize); + } + ret->type = type; + return ret; +} + + +void te_free_parameters(te_expr *n) { + if (!n) return; + int arity = get_arity(n->type); + // Free all parameters from the back to the front. + while (arity > 0) { + te_free(n->parameters[arity - 1]); + arity--; + } +} + + +void te_free(te_expr *n) { + if (!n) return; + te_free_parameters(n); + free(n); +} + static double pi() {return 3.14159265358979323846;} static double e() {return 2.71828182845904523536;} @@ -237,21 +261,24 @@ void next_token(state *s) { } -static te_expr expr(state *s); -static te_expr power(state *s); +static te_expr *expr(state *s); +static te_expr *power(state *s); -static te_expr base(state *s) { +static te_expr *base(state *s) { /* = | | {"(" ")"} | | "(" {"," } ")" | "(" ")" */ - te_expr ret(0, NULL, NULL); + te_expr *ret; + int arity; switch (s->type) { case TOK_NUMBER: - ret = te_expr(TE_CONSTANT, s->value); + ret = new_expr(TE_CONSTANT, 0); + ret->value = s->value; next_token(s); break; case TE_FUNCTION0: - ret = te_expr(s->type, s->function, NULL); + ret = new_expr(s->type, 0); + ret->function = s->function; next_token(s); if (s->type == TOK_OPEN) { next_token(s); @@ -267,24 +294,27 @@ static te_expr base(state *s) { case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: - ret = te_expr(s->type, s->function, NULL); + arity = get_arity(s->type); + + ret = new_expr(s->type, 0); + ret->function = s->function; next_token(s); if (s->type == TOK_OPEN) { int i; - for(i = 0; i < ret.arity; i++) { + for(i = 0; i < arity; i++) { next_token(s); - ret.parameters.push_back(expr(s)); + ret->parameters[i] = expr(s); if(s->type != TOK_SEP) { break; } } - if(s->type == TOK_CLOSE && i == ret.arity - 1) { + if(s->type == TOK_CLOSE && i == arity - 1) { next_token(s); } else if (s->type != TOK_ERROR || s->error == TE_ERROR_UNKNOWN) { s->type = TOK_ERROR; - s->error = i < ret.arity ? TE_ERROR_TOO_FEW_ARGS + s->error = i < arity ? TE_ERROR_TOO_FEW_ARGS : TE_ERROR_TOO_MANY_ARGS; } } else if (s->type != TOK_ERROR @@ -313,14 +343,16 @@ static te_expr base(state *s) { // This means we have too few things. // Instead of introducing another error, just call it // "too few args". - ret = te_expr(TE_CONSTANT, NAN); + ret = new_expr(0, 0); s->type = TOK_ERROR; s->error = TE_ERROR_TOO_FEW_ARGS; + ret->value = NAN; break; default: - ret = te_expr(TE_CONSTANT, NAN); + ret = new_expr(0, 0); s->type = TOK_ERROR; s->error = TE_ERROR_UNKNOWN; + ret->value = NAN; break; } @@ -328,7 +360,7 @@ static te_expr base(state *s) { } -static te_expr power(state *s) { +static te_expr *power(state *s) { /* = {("-" | "+")} */ int sign = 1; while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { @@ -336,70 +368,72 @@ static te_expr power(state *s) { next_token(s); } + te_expr *ret; + if (sign == 1) { - te_expr ret = base(s); - return ret; + ret = base(s); } else { - te_expr params[1] = { base(s) }; - te_expr ret = te_expr(TE_FUNCTION1, (const void *) negate, params); - return ret; + ret = NEW_EXPR(TE_FUNCTION1, base(s)); + ret->function = (const void *) negate; } + + return ret; } -static te_expr factor(state *s) { +static te_expr *factor(state *s) { /* = {"^" } */ - te_expr ret = power(s); + te_expr *ret = power(s); while (s->type == TOK_INFIX && (s->function == (const void*)(te_fun2)pow)) { te_fun2 t = (te_fun2) s->function; next_token(s); - - te_expr param[2] = { ret, power(s) }; - ret = te_expr(TE_FUNCTION2, (const void *) t, param); + ret = NEW_EXPR(TE_FUNCTION2, ret, power(s)); + ret->function = (const void *) t; } return ret; } -static te_expr term(state *s) { +static te_expr *term(state *s) { /* = {("*" | "/" | "%") } */ - te_expr ret = factor(s); + te_expr *ret = factor(s); while (s->type == TOK_INFIX && (s->function == (const void*)(te_fun2)mul || s->function == (const void*)(te_fun2)divide || s->function == (const void*)(te_fun2)fmod)) { te_fun2 t = (te_fun2) s->function; next_token(s); - te_expr params[2] = { ret, factor(s) }; - ret = te_expr(TE_FUNCTION2, (const void *) t, params); + ret = NEW_EXPR(TE_FUNCTION2, ret, factor(s)); + ret->function = (const void *) t; } return ret; } -static te_expr expr(state *s) { +static te_expr *expr(state *s) { /* = {("+" | "-") } */ - te_expr ret = term(s); + te_expr *ret = term(s); while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { te_fun2 t = (te_fun2) s->function; next_token(s); - te_expr params[2] = { ret, term(s) }; - ret = te_expr(TE_FUNCTION2, (const void *) t, params); + ret = NEW_EXPR(TE_FUNCTION2, ret, term(s)); + ret->function = (const void *) t; } return ret; } -#define TE_FUN(...) ((double(*)(__VA_ARGS__))n.function) -#define M(e) te_eval(n.parameters[e]) +#define TE_FUN(...) ((double(*)(__VA_ARGS__))n->function) +#define M(e) te_eval(n->parameters[e]) -double te_eval(const te_expr &n) { - switch(n.type) { - case TE_CONSTANT: - return n.value; +double te_eval(const te_expr *n) { + if (!n) return NAN; + + switch(n->type) { + case TE_CONSTANT: return n->value; case TE_FUNCTION0: return TE_FUN(void)(); case TE_FUNCTION1: @@ -410,39 +444,43 @@ double te_eval(const te_expr &n) { return TE_FUN(double, double, double)(M(0), M(1), M(2)); default: return NAN; } + } #undef TE_FUN #undef M -static void optimize(te_expr &n) { +static void optimize(te_expr *n) { /* Evaluates as much as possible. */ - if (n.type == TE_CONSTANT) return; + if (n->type == TE_CONSTANT) return; + const int arity = get_arity(n->type); bool known = true; - for (int i = 0; i < n.arity; ++i) { - optimize(n.parameters[i]); - if ((n.parameters[i]).type != TE_CONSTANT) { + for (int i = 0; i < arity; ++i) { + optimize(n->parameters[i]); + if ((n->parameters[i])->type != TE_CONSTANT) { known = false; } } if (known) { const double value = te_eval(n); - n.type = TE_CONSTANT; - n.value = value; + te_free_parameters(n); + n->type = TE_CONSTANT; + n->value = value; } } -te_expr te_compile(const char *expression, te_error_t *error) { +te_expr *te_compile(const char *expression, te_error_t *error) { state s; s.start = s.next = expression; s.error = TE_ERROR_NONE; next_token(&s); - te_expr root = expr(&s); + te_expr *root = expr(&s); if (s.type != TOK_END) { + te_free(root); if (error) { error->position = (s.next - s.start) + 1; if (s.error != TE_ERROR_NONE) { @@ -457,16 +495,22 @@ te_expr te_compile(const char *expression, te_error_t *error) { error->type = TE_ERROR_TOO_MANY_ARGS; } } + return 0; } else { optimize(root); if (error) error->position = 0; + return root; } - return root; } double te_interp(const char *expression, te_error_t *error) { - te_expr n = te_compile(expression, error); + te_expr *n = te_compile(expression, error); double ret; - ret = te_eval(n); + if (n) { + ret = te_eval(n); + te_free(n); + } else { + ret = NAN; + } return ret; } From aaee5dd32d93ad9c08f8ec70515d6996ed9f98ac Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 11 Dec 2018 16:48:45 +0100 Subject: [PATCH 049/439] Rename "lines" for netbsd's benefit Netbsd's curses does a bit of a landgrab, and takes the names "lines" and "newline" and a few others for itself. --- src/builtin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/builtin.cpp b/src/builtin.cpp index 6a7dd487d..2e3306f94 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -200,11 +200,11 @@ void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t * bool is_short = false; if (is_stderr) { // Interactive mode help to screen - only print synopsis if the rest won't fit. - int screen_height, lines; + int screen_height, my_lines; screen_height = common_get_height(); - lines = count_char(str, L'\n'); - if (!shell_is_interactive() || (lines > 2 * screen_height / 3)) { + my_lines = count_char(str, L'\n'); + if (!shell_is_interactive() || (my_lines > 2 * screen_height / 3)) { wchar_t *pos; int cut = 0; int i; From a3085a3059e080b144527c56476760b80cca6f28 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 11 Dec 2018 16:48:11 +0100 Subject: [PATCH 050/439] Use varargs tparm on netbsd This needs to be defined _early_. --- configure.ac | 2 ++ src/fallback.cpp | 2 +- src/fallback.h | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 08c415739..5d83402f3 100644 --- a/configure.ac +++ b/configure.ac @@ -324,6 +324,7 @@ AC_CHECK_FUNCS( futimens clock_gettime ) AC_CHECK_FUNCS( getpwent flock ) AC_CHECK_FUNCS( dirfd ) +AC_CHECK_DECL( [__NetBSD__], AC_DEFINE([TPARM_VARARGS], 1, [ Make tparm take varargs ]) ) AC_CHECK_DECL( [mkostemp], [ AC_CHECK_FUNCS([mkostemp]) ] ) # @@ -554,6 +555,7 @@ AC_COMPILE_IFELSE( [ AC_LANG_PROGRAM( [ + #define TPARM_VARARGS 1 #if HAVE_NCURSES_H #include #elif HAVE_NCURSES_CURSES_H diff --git a/src/fallback.cpp b/src/fallback.cpp index d7e4e508e..91ed149ed 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -43,7 +43,7 @@ #include "fallback.h" // IWYU pragma: keep #include "util.h" // IWYU pragma: keep -#ifdef TPARM_SOLARIS_KLUDGE +#if defined(TPARM_SOLARIS_KLUDGE) #undef tparm char *tparm_solaris_kludge(char *str, long p1, long p2, long p3, long p4, diff --git a/src/fallback.h b/src/fallback.h index f8b7d4977..d3c624f98 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -63,7 +63,7 @@ struct winsize { #endif -#ifdef TPARM_SOLARIS_KLUDGE +#if defined(TPARM_SOLARIS_KLUDGE) /// Solaris tparm has a set fixed of paramters in its curses implementation, work around this here. #define tparm tparm_solaris_kludge char *tparm_solaris_kludge(char *str, long p1 = 0, long p2 = 0, long p3 = 0, long p4 = 0, From ee5e4cf8e2d33a53e1c6ba1b4a3d457a90e0c81b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 11 Dec 2018 16:48:30 +0100 Subject: [PATCH 051/439] Use int tputs_arg_t on netbsd --- src/fallback.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fallback.h b/src/fallback.h index d3c624f98..108e416fe 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -46,7 +46,7 @@ int fish_mkstemp_cloexec(char *); /// 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 +#if defined(NCURSES_VERSION) || defined(__NetBSD__) typedef int tputs_arg_t; #else typedef char tputs_arg_t; From e4b6007f33ba26cd7c1d041bac2d296f680d5794 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 11 Dec 2018 20:31:40 +0100 Subject: [PATCH 052/439] Check for dirfd in autoconf build This commit via https://anonhg.netbsd.org/pkgsrc/file/tip/shells/fish/patches/patch-configure.ac, credit to jklos@pkgsrc.org. --- configure.ac | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5d83402f3..ef60461c6 100644 --- a/configure.ac +++ b/configure.ac @@ -322,7 +322,24 @@ AC_CHECK_FUNCS( wcslcpy lrand48_r killpg ) AC_CHECK_FUNCS( backtrace_symbols getifaddrs ) AC_CHECK_FUNCS( futimens clock_gettime ) AC_CHECK_FUNCS( getpwent flock ) -AC_CHECK_FUNCS( dirfd ) + +AC_MSG_CHECKING([dirfd]) +AC_LINK_IFELSE([ + +#include + +DIR *dirp; + +int +main(void) +{ + return dirfd(dirp); +} +], [ AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_DIRFD], 1, [ Define to 1 if you have the `dirfd' function or macro. ]) + ], + [ AC_MSG_RESULT([no])] +) AC_CHECK_DECL( [__NetBSD__], AC_DEFINE([TPARM_VARARGS], 1, [ Make tparm take varargs ]) ) AC_CHECK_DECL( [mkostemp], [ AC_CHECK_FUNCS([mkostemp]) ] ) From 3e03625113e7995ab9d0223d52363a292e20dae8 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 11 Dec 2018 21:11:02 +0100 Subject: [PATCH 053/439] Don't try to use fstatfs on netbsd I can find a man page for it, but it doesn't seem to work. --- src/wutil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wutil.cpp b/src/wutil.cpp index f58eb529a..ceb3620c5 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -311,7 +311,7 @@ int fd_check_is_remote(int fd) { // Other FSes are assumed local. return 0; } -#elif defined(MNT_LOCAL) +#elif defined(MNT_LOCAL) && !defined(__NetBSD__) struct statfs buf {}; if (fstatfs(fd, &buf) < 0) return -1; return (buf.f_flags & MNT_LOCAL) ? 0 : 1; From a608e5d58155e600781b390a0fc15d5e045ddabc Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 11 Dec 2018 21:30:38 +0100 Subject: [PATCH 054/439] cmake: Check for TPARM_VARARGS --- cmake/ConfigureChecks.cmake | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index 5182ba808..6b4f0b99f 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -115,10 +115,24 @@ int main () { " TPARM_TAKES_VARARGS ) -SET(CMAKE_REQUIRED_LIBRARIES) IF(NOT TPARM_TAKES_VARARGS) - SET(TPARM_SOLARIS_KLUDGE 1) + CHECK_CXX_SOURCE_COMPILES(" +${TPARM_INCLUDES} +#define TPARM_VARARGS + +int main () { + tparm( \"\" ); +} +" + TPARM_TAKES_VARARGS_WITH_VARARGS + ) + IF(NOT TPARM_TAKES_VARARGS) + SET(TPARM_SOLARIS_KLUDGE 1) + ELSE() + SET(TPARM_VARARGS 1) + ENDIF() ENDIF() +SET(CMAKE_REQUIRED_LIBRARIES) CHECK_CXX_SOURCE_COMPILES(" #include From c5f9f595550ba3204031f5edec353fd79dfb9d57 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 12 Dec 2018 11:29:26 +0100 Subject: [PATCH 055/439] Always cast to non-const for tparm This is non-const on macOS, but some of the args we pass are always const on netbsd. I have no idea why you'd ever want this to modify its argument, but whatever. --- src/builtin_set_color.cpp | 6 +++--- src/output.cpp | 4 ++-- src/screen.cpp | 18 +++++++++--------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp index 78857285e..e0f98261f 100644 --- a/src/builtin_set_color.cpp +++ b/src/builtin_set_color.cpp @@ -193,7 +193,7 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) { output_set_writer(set_color_builtin_outputter); if (bold && enter_bold_mode) { - writembs_nofail(tparm(enter_bold_mode)); + writembs_nofail(tparm((char *)enter_bold_mode)); } if (underline && enter_underline_mode) { @@ -216,13 +216,13 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (bgcolor != NULL && bg.is_normal()) { write_color(rgb_color_t::black(), false /* not is_fg */); - writembs_nofail(tparm(exit_attribute_mode)); + writembs_nofail(tparm((char *)exit_attribute_mode)); } if (!fg.is_none()) { if (fg.is_normal() || fg.is_reset()) { write_color(rgb_color_t::black(), true /* is_fg */); - writembs_nofail(tparm(exit_attribute_mode)); + writembs_nofail(tparm((char *)exit_attribute_mode)); } else { if (!write_color(fg, true /* is_fg */)) { // We need to do *something* or the lack of any output messes up diff --git a/src/output.cpp b/src/output.cpp index 129ed701c..f6bf3dc6d 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -66,7 +66,7 @@ unsigned char index_for_color(rgb_color_t c) { static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) { if (term_supports_color_natively(idx)) { // Use tparm to emit color escape. - writembs(tparm(todo, idx)); + writembs(tparm((char *)todo, idx)); return true; } @@ -332,7 +332,7 @@ void set_color(rgb_color_t c, rgb_color_t c2) { // Lastly, we set bold, underline, italics, dim, and reverse modes correctly. if (is_bold && !was_bold && enter_bold_mode && strlen(enter_bold_mode) > 0 && !bg_set) { - writembs_nofail(tparm(enter_bold_mode)); + writembs_nofail(tparm((char *)enter_bold_mode)); was_bold = is_bold; } diff --git a/src/screen.cpp b/src/screen.cpp index ebc085a4f..54b65bbb9 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -222,7 +222,7 @@ static bool is_color_escape_seq(const wchar_t *code, size_t *resulting_length) { if (!esc[p]) continue; for (int k = 0; k < max_colors; k++) { - size_t esc_seq_len = try_sequence(tparm(esc[p], k), code); + size_t esc_seq_len = try_sequence(tparm((char *)esc[p], k), code); if (esc_seq_len) { *resulting_length = esc_seq_len; return true; @@ -250,7 +250,7 @@ static bool is_visual_escape_seq(const wchar_t *code, size_t *resulting_length) 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 esc_seq_len = maxi(try_sequence(tparm(esc2[p]), code), try_sequence(esc2[p], code)); + size_t esc_seq_len = maxi(try_sequence(tparm((char *)esc2[p]), code), try_sequence(esc2[p], code)); if (esc_seq_len) { *resulting_length = esc_seq_len; return true; @@ -526,7 +526,7 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) { bool use_multi = multi_str != NULL && multi_str[0] != '\0' && abs(x_steps) * strlen(str) > strlen(multi_str); if (use_multi && cur_term) { - char *multi_param = tparm(multi_str, abs(x_steps)); + char *multi_param = tparm((char *)multi_str, abs(x_steps)); writembs(multi_param); } else { for (i = 0; i < abs(x_steps); i++) { @@ -1151,7 +1151,7 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) { if (screen_width > non_space_width) { bool justgrey = true; if (cur_term && enter_dim_mode) { - std::string dim = tparm(enter_dim_mode); + std::string dim = tparm((char *)enter_dim_mode); if (!dim.empty()) { // Use dim if they have it, so the color will be based on their actual normal // color and the background of the termianl. @@ -1162,14 +1162,14 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) { if (cur_term && justgrey && set_a_foreground) { if (max_colors >= 238) { // draw the string in a particular grey - abandon_line_string.append(str2wcstring(tparm(set_a_foreground, 237))); + abandon_line_string.append(str2wcstring(tparm((char *)set_a_foreground, 237))); } else if (max_colors >= 9) { // bright black (the ninth color, looks grey) - abandon_line_string.append(str2wcstring(tparm(set_a_foreground, 8))); + abandon_line_string.append(str2wcstring(tparm((char *)set_a_foreground, 8))); } else if (max_colors >= 2 && enter_bold_mode) { // we might still get that color by setting black and going bold for bright - abandon_line_string.append(str2wcstring(tparm(enter_bold_mode))); - abandon_line_string.append(str2wcstring(tparm(set_a_foreground, 0))); + abandon_line_string.append(str2wcstring(tparm((char *)enter_bold_mode))); + abandon_line_string.append(str2wcstring(tparm((char *)set_a_foreground, 0))); } } @@ -1177,7 +1177,7 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) { if (cur_term && exit_attribute_mode) { abandon_line_string.append( - str2wcstring(tparm(exit_attribute_mode))); // normal text ANSI escape sequence + str2wcstring(tparm((char *)exit_attribute_mode))); // normal text ANSI escape sequence } int newline_glitch_width = term_has_xn ? 0 : 1; From 58ceb00781f6d9041b206e1aa9c36287d940e627 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 12 Dec 2018 11:39:37 +0100 Subject: [PATCH 056/439] Make a few methods const This helps on netbsd, because enter_standout_mode et al are const there. These methods don't alter their argument, so they should have been const to begin with. --- src/output.cpp | 4 ++-- src/output.h | 2 +- src/screen.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/output.cpp b/src/output.cpp index f6bf3dc6d..4246fd084 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -63,7 +63,7 @@ unsigned char index_for_color(rgb_color_t c) { return c.to_term256_index(); } -static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) { +static bool write_color_escape(const char *todo, unsigned char idx, bool is_fg) { if (term_supports_color_natively(idx)) { // Use tparm to emit color escape. writembs(tparm((char *)todo, idx)); @@ -550,7 +550,7 @@ rgb_color_t parse_color(const env_var_t &var, bool is_background) { } /// Write specified multibyte string. -void writembs_check(char *mbs, const char *mbs_name, bool critical, const char *file, long line) { +void writembs_check(const char *mbs, const char *mbs_name, bool critical, const char *file, long line) { if (mbs != NULL) { tputs(mbs, 1, &writeb); } else if (critical) { diff --git a/src/output.h b/src/output.h index 15c0fa4fb..8ce26b598 100644 --- a/src/output.h +++ b/src/output.h @@ -30,7 +30,7 @@ enum { void set_color(rgb_color_t c, rgb_color_t c2); -void writembs_check(char *mbs, const char *mbs_name, bool critical, const char *file, long line); +void writembs_check(const char *mbs, const char *mbs_name, bool critical, const char *file, long line); #define writembs(mbs) writembs_check((mbs), #mbs, true, __FILE__, __LINE__) #define writembs_nofail(mbs) writembs_check((mbs), #mbs, false, __FILE__, __LINE__) diff --git a/src/screen.cpp b/src/screen.cpp index 54b65bbb9..4b0df4c44 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -566,7 +566,7 @@ static void s_write_char(screen_t *s, data_buffer_t *b, wchar_t c) { } /// Send the specified string through tputs and append the output to the specified buffer. -static void s_write_mbs(data_buffer_t *b, char *s) { +static void s_write_mbs(data_buffer_t *b, const char *s) { scoped_buffer_t scoped_buffer(b); writembs(s); } From b77a909a4f6d6f27d6050c6ba91d8dfb768c55b5 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 12 Dec 2018 11:42:29 +0100 Subject: [PATCH 057/439] Make a few variables const These are then passed to tparm, but we explicitly cast the const away. --- src/screen.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/screen.cpp b/src/screen.cpp index 4b0df4c44..63e0906e7 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -214,7 +214,7 @@ static bool is_color_escape_seq(const wchar_t *code, size_t *resulting_length) { // Detect these terminfo color escapes with parameter value up to max_colors, all of which // don't move the cursor. - char *const esc[] = { + const char *const esc[] = { set_a_foreground, set_a_background, set_foreground, set_background, }; @@ -237,7 +237,7 @@ static bool is_color_escape_seq(const wchar_t *code, size_t *resulting_length) { /// displayed other than the color. static bool is_visual_escape_seq(const wchar_t *code, size_t *resulting_length) { if (!cur_term) return false; - char *const esc2[] = { + const 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, @@ -483,7 +483,7 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) { int i; int x_steps, y_steps; - char *str; + const char *str; scoped_buffer_t scoped_buffer(b); y_steps = new_y - s->actual.cursor.y; @@ -512,7 +512,7 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) { x_steps = 0; } - char *multi_str = NULL; + const char *multi_str = NULL; if (x_steps < 0) { str = cursor_left; multi_str = parm_left_cursor; From e9ad88d4b0bbe303dcce857c07812766fc7cc541 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 12 Dec 2018 13:10:34 +0100 Subject: [PATCH 058/439] Don't set the title on NetBSD's wscon We might want to check the terminfo "XT" capability here, but for now let's do the quick fix. --- src/env.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/env.cpp b/src/env.cpp index 4085b8576..148e0eb71 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -433,6 +433,9 @@ static bool does_term_support_setting_title() { if (!recognized) { if (wcscmp(term, L"linux") == 0) return false; if (wcscmp(term, L"dumb") == 0) return false; + // NetBSD + if (wcscmp(term, L"vt100") == 0) return false; + if (wcscmp(term, L"wsvt25") == 0) return false; char buf[PATH_MAX]; int retval = ttyname_r(STDIN_FILENO, buf, PATH_MAX); From 9b980f5e6e6d586bbfc48ca8b2f695389574323e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 13 Dec 2018 10:26:15 +0100 Subject: [PATCH 059/439] Use fstatvfs if ST_LOCAL is available Allows us to sometimes use mmap on NetBSD (proper capitalization is important). --- src/wutil.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wutil.cpp b/src/wutil.cpp index ceb3620c5..13001bef6 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -311,7 +311,13 @@ int fd_check_is_remote(int fd) { // Other FSes are assumed local. return 0; } -#elif defined(MNT_LOCAL) && !defined(__NetBSD__) +#elif defined(ST_LOCAL) + // ST_LOCAL is a flag to statvfs, which is itself standardized. + // In practice the only system to use this path is NetBSD. + struct statvfs buf {}; + if (fstatvfs(fd, &buf) < 0) return -1; + return (buf.f_flag & ST_LOCAL) ? 0 : 1; +#elif defined(MNT_LOCAL) struct statfs buf {}; if (fstatfs(fd, &buf) < 0) return -1; return (buf.f_flags & MNT_LOCAL) ? 0 : 1; From f871951a87e3b19568fbd4f77dcfc004eabd5e8d Mon Sep 17 00:00:00 2001 From: Curtis Jiang Date: Mon, 31 Dec 2018 23:48:03 +0800 Subject: [PATCH 060/439] fix OpenWrt and opkg support (#5454) * add OpenWrt and opkg support Signed-off-by: Curtis Jiang * fix opkg list Signed-off-by: Curtis Jiang --- share/completions/opkg.fish | 37 ++++++++++++++-------- share/functions/__fish_print_packages.fish | 2 +- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/share/completions/opkg.fish b/share/completions/opkg.fish index 9654be418..9e11bb92d 100644 --- a/share/completions/opkg.fish +++ b/share/completions/opkg.fish @@ -1,25 +1,36 @@ #completion for opkg function __fish_opkg_no_subcommand -d 'Test if opkg has yet to be given the subcommand' - for i in (commandline -opc) - if contains -- $i update upgrade install configure remove flag list list-installed list-upgradable list-changed-conffiles files search find info status download compare-versions print-architecture depends whatdepends whatdependsrec whatrecommends whatsuggests whatprovides whatconflicts whatreplaces - return 1 - end - end - return 0 + for i in (commandline -opc) + if contains -- $i update upgrade install configure remove flag list list-installed list-upgradable list-changed-conffiles files search find info status download compare-versions print-architecture depends whatdepends whatdependsrec whatrecommends whatsuggests whatprovides whatconflicts whatreplaces + return 1 + end + end + return 0 end function __fish_opkg_use_package -d 'Test if opkg command should have packages as potential completion' - for i in (commandline -opc) - if contains -- $i contains upgrade install configure remove flag files search find info status download compare-versions print-architecture depends whatdepends whatdependsrec whatrecommends whatsuggests whatprovides whatconflicts whatreplaces - return 0 - end - end - return 1 + for i in (commandline -opc) + if contains -- $i contains install search find info status download compare-versions print-architecture depends whatdepends whatdependsrec whatrecommends whatsuggests whatprovides whatconflicts whatreplaces + return 0 + end + end + return 1 +end + +function __fish_opkg_use_package_installed -d 'Test if opkg command should have installed packages as potential completion' + for i in (commandline -opc) + if contains -- $i contains upgrade configure remove flag files + return 0 + end + end + return 1 end complete -c opkg -n '__fish_opkg_use_package' -a '(__fish_print_packages)' -d 'Package' +complete -c opkg -n '__fish_opkg_use_package_installed' -a '(__fish_print_packages --installed)' -d 'Package' + complete -f -n '__fish_opkg_no_subcommand' -c opkg -a 'update' -d 'Update list of available packages' complete -f -n '__fish_opkg_no_subcommand' -c opkg -a 'upgrade' -d 'Upgrade packages' complete -f -n '__fish_opkg_no_subcommand' -c opkg -a 'install' -d 'Install package(s)' @@ -73,4 +84,4 @@ complete -c opkg -l size -d 'Print package size when listing available packages' complete -c opkg -l force-removal-of-dependent-packages -d 'Remove package and all dependencies' complete -c opkg -l autoremove -d 'Remove automatically installed packages' complete -c opkg -s t -l tmp-dir -d 'Specify tmp-dir.' -complete -c opkg -s l -l lists-dir -d 'Specify lists-dir.' \ No newline at end of file +complete -c opkg -s l -l lists-dir -d 'Specify lists-dir.' diff --git a/share/functions/__fish_print_packages.fish b/share/functions/__fish_print_packages.fish index 413c674c3..09e162460 100644 --- a/share/functions/__fish_print_packages.fish +++ b/share/functions/__fish_print_packages.fish @@ -221,7 +221,7 @@ function __fish_print_packages end if type -q -f opkg - if not set -q only_installed + if set -q only_installed opkg list-installed 2>/dev/null | sed -r 's/^([a-zA-Z0-9\-]+) - ([a-zA-Z0-9\-]+)/\1\t\2/g' return else From 131f0f2de5742df52a5ed54a8b30365b8d527308 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 31 Dec 2018 19:36:08 -0600 Subject: [PATCH 061/439] Speed up `wait.expect` test (This is being committed to a branch on the main repository so I can verify that Travis is able to run this OK.) --- tests/wait.expect | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/wait.expect b/tests/wait.expect index 72d05dfb8..354eb8329 100644 --- a/tests/wait.expect +++ b/tests/wait.expect @@ -15,33 +15,33 @@ expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } # three job ids specified set error_msg "three job ids specified: Fail" -send_line "sleep 3 &; sleep 1 &; sleep 2 &; sleep 4 &;" +send_line "sleep 0.3 &; sleep 0.1 &; sleep 0.2 &; sleep 0.4 &;" expect_prompt send_line "wait %1 %3 %4" -expect "Job 2, 'sleep 1 &' has ended" {} timeout { puts stderr $error_msg } -expect "Job 3, 'sleep 2 &' has ended" {} timeout { puts stderr $error_msg } -expect "Job 1, 'sleep 3 &' has ended" {} timeout { puts stderr $error_msg } -expect_prompt "Job 4, 'sleep 4 &' has ended" {} unmatched { puts stderr $error_msg } +expect "Job 2, 'sleep 0.1 &' has ended" {} timeout { puts stderr $error_msg } +expect "Job 3, 'sleep 0.2 &' has ended" {} timeout { puts stderr $error_msg } +expect "Job 1, 'sleep 0.3 &' has ended" {} timeout { puts stderr $error_msg } +expect_prompt "Job 4, 'sleep 0.4 &' has ended" {} unmatched { puts stderr $error_msg } send_line "jobs" expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } # specify job ids with -n option set error_msg "specify job ids with -n option: Fail" -send_line "sleep 3 &; sleep 1 &; sleep 2 &" +send_line "sleep 0.3 &; sleep 0.1 &; sleep 0.2 &" expect_prompt send_line "wait -n %1 %3" -expect "Job 2, 'sleep 1 &' has ended" {} timeout { puts stderr $error_msg } -expect_prompt "Job 3, 'sleep 2 &' has ended" {} unmatched { puts stderr $error_msg } +expect "Job 2, 'sleep 0.1 &' has ended" {} timeout { puts stderr $error_msg } +expect_prompt "Job 3, 'sleep 0.2 &' has ended" {} unmatched { puts stderr $error_msg } send_line "wait -n %1" -expect_prompt "Job 1, 'sleep 3 &' has ended" {} unmatched { puts stderr $error_msg } +expect_prompt "Job 1, 'sleep 0.3 &' has ended" {} unmatched { puts stderr $error_msg } send_line "jobs" expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } # don't wait for stopped jobs set error_msg "don't wait for stopped jobs: Fail" -send_line "sleep 3 &" +send_line "sleep 0.3 &" expect_prompt send_line "kill -STOP %1" expect_prompt @@ -71,7 +71,7 @@ expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } # wait for jobs by its process name with -n option set error_msg "wait for jobs by its process name with -n option: Fail" -send_line "for i in (seq 1 3); sleep \$i &; end" +send_line "for i in (seq 1 3); sleep 0.\$i &; end" expect_prompt send_line "wait -n sleep" expect_prompt @@ -86,11 +86,11 @@ expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } # complex case set error_msg "complex case: Fail" -send_line "for i in (seq 1 10); ls | sleep 2 | cat > /dev/null &; end" +send_line "for i in (seq 1 10); ls | sleep 0.2 | cat > /dev/null &; end" expect_prompt -send_line "sleep 3 | cat &" +send_line "sleep 0.3 | cat &" expect_prompt -send_line "sleep 1 &" +send_line "sleep 0.1 &" expect_prompt send_line "wait \$last_pid sleep" expect_prompt @@ -110,7 +110,7 @@ expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } # test with fish script set error_msg "test with fish script: Fail" -send_line "fish -c 'sleep 2 &; sleep 3 &; sleep 1 &; wait -n sleep; jobs | wc -l'" +send_line "fish -c 'sleep 0.2 &; sleep 0.3 &; sleep 0.1 &; wait -n sleep; jobs | wc -l'" expect "1" {} timeout { puts stderr $error_msg } expect_prompt From 4b8da10215c984daabeca6c6e7d6803b45a4d871 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 1 Jan 2019 13:57:32 +0100 Subject: [PATCH 062/439] completions/modinfo: Don't check uname This checks if uname exists (we already call it elsewhere without check, nobody has complained, uname is in POSIX), then calls to see if it's "Linux", and only then offers any completions. Since we don't have any other version to offer, the check is worse than useless. --- share/completions/modinfo.fish | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/share/completions/modinfo.fish b/share/completions/modinfo.fish index 765ab2846..bd59c1509 100644 --- a/share/completions/modinfo.fish +++ b/share/completions/modinfo.fish @@ -1,16 +1,12 @@ -if command -s uname > /dev/null 2>/dev/null - if test (uname) = "Linux" - complete -c modinfo -a "(__fish_print_modules)" - complete -c modinfo -l author -s a -d "Print only 'author'" - complete -c modinfo -l description -s d -d "Print only 'description'" - complete -c modinfo -l license -s l -d "Print only 'license'" - complete -c modinfo -l parameters -s p -d "Print only 'parm'" - complete -c modinfo -l filename -s n -d "Print only 'filename'" - complete -c modinfo -l null -s 0 -d "Use \\0 instead of \\n" - complete -c modinfo -l field -s F -x -d "Print only provided FIELD" -a "author description license parm depends alias intree vermagic vermagic" - complete -c modinfo -l set-version -s k -x -d "Use VERSION instead of `uname -r`" - complete -c modinfo -l basedir -s b -r -d "Use DIR as filesystem root for /lib/modules" - complete -c modinfo -l version -s V -d "Show version" - complete -c modinfo -l help -s h -d "Show help" - end -end +complete -c modinfo -a "(__fish_print_modules)" +complete -c modinfo -l author -s a -d "Print only 'author'" +complete -c modinfo -l description -s d -d "Print only 'description'" +complete -c modinfo -l license -s l -d "Print only 'license'" +complete -c modinfo -l parameters -s p -d "Print only 'parm'" +complete -c modinfo -l filename -s n -d "Print only 'filename'" +complete -c modinfo -l null -s 0 -d "Use \\0 instead of \\n" +complete -c modinfo -l field -s F -x -d "Print only provided FIELD" -a "author description license parm depends alias intree vermagic vermagic" +complete -c modinfo -l set-version -s k -x -d "Use VERSION instead of `uname -r`" +complete -c modinfo -l basedir -s b -r -d "Use DIR as filesystem root for /lib/modules" +complete -c modinfo -l version -s V -d "Show version" +complete -c modinfo -l help -s h -d "Show help" From 9dc79cd8d5600c52b0ea57f8945ea7746176ea3b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 1 Jan 2019 14:00:51 +0100 Subject: [PATCH 063/439] completions/screen: Replace eval with var-as-command [ci skip] --- share/completions/screen.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/screen.fish b/share/completions/screen.fish index 9fbb16bf1..1ef4fd2bc 100644 --- a/share/completions/screen.fish +++ b/share/completions/screen.fish @@ -1,7 +1,7 @@ function __fish_detect_screen_socket_dir -d "Detect which folder screen uses" set screen_bin screen if not set -q __fish_screen_socket_dir - set -g __fish_screen_socket_dir (eval $screen_bin -ls __fish_i_don_t_think_this_will_be_matched | string match -r "(?<=No Sockets found in ).*(?=\.)") + set -g __fish_screen_socket_dir ($screen_bin -ls __fish_i_don_t_think_this_will_be_matched | string match -r "(?<=No Sockets found in ).*(?=\.)") end end From 7aca69780c8c89310e1f557eab040c7e3db4d70b Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Tue, 1 Jan 2019 20:39:26 +0900 Subject: [PATCH 064/439] Replace deprecated options with newly introduced options for gem. Signed-off-by: Takuya Noguchi --- share/completions/gem.fish | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/share/completions/gem.fish b/share/completions/gem.fish index aaf6a39a2..bd4a82635 100644 --- a/share/completions/gem.fish +++ b/share/completions/gem.fish @@ -99,10 +99,8 @@ complete $install_opt -s l -l local -d "Restrict operations to the LOCAL domain complete $install_opt -s r -l remote -d "Restrict operations to the REMOTE domain" complete $install_opt -s b -l both -d "Allow LOCAL and REMOTE operations" complete $install_opt -s i -l install-dir -d "Gem repository directory to get installed gems" -x -complete $install_opt -s d -l rdoc -d "Generate RDoc documentation for the gem on install" -complete $install_opt -l no-rdoc -d "Don't generate RDoc documentation for the gem on install" -complete $install_opt -l ri -d "Generate RI documentation for the gem on install" -complete $install_opt -l no-ri -d "Don't generate RI documentation for the gem on install" +complete $install_opt -s N -l no-document -d "Disable documentation generation on install" +complete $install_opt -l document -a '(__fish_append , rdoc ri)' -d "Specify the documentation types you wish to generate" complete $install_opt -s f -l force -d "Force gem to install, bypassing dependency checks" complete $install_opt -l no-force -d "Don't force gem to install, bypassing dependency checks" complete $install_opt -s t -l test -d "Run unit tests prior to installation" @@ -180,10 +178,8 @@ complete $unpack_opt -s v -l version -d "Specify version of gem to unpack" -x # update set -l update_opt -c gem -n 'contains update (commandline -poc)' complete $update_opt -s i -l install-dir -d "Gem repository directory to get installed gems" -complete $update_opt -s d -l rdoc -d "Generate RDoc documentation for the gem on install" -complete $update_opt -l no-rdoc -d "Don't generate RDoc documentation for the gem on install" -complete $update_opt -l ri -d "Generate RI documentation for the gem on install" -complete $update_opt -l no-ri -d "Don't generate RI documentation for the gem on install" +complete $update_opt -s N -l no-document -d "Disable documentation generation on update" +complete $update_opt -l document -a '(__fish_append , rdoc ri)' -d "Specify the documentation types you wish to generate" complete $update_opt -s f -l force -d "Force gem to install, bypassing dependency checks" complete $update_opt -l no-force -d "Don't force gem to install, bypassing dependency checks" complete $update_opt -s t -l test -d "Run unit tests prior to installation" From 1074a59d757065e416900bd7f74a4d536944d6cd Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 17:23:26 +0100 Subject: [PATCH 065/439] tests/invocation: Set colors after $TERM builds.sr.ht doesn't set $TERM, so this failed. --- tests/invocation.sh | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/invocation.sh b/tests/invocation.sh index 2bfa902ee..5108dd283 100755 --- a/tests/invocation.sh +++ b/tests/invocation.sh @@ -80,16 +80,6 @@ fish_dir="$(dirname "${fish_exe}")" fish_leaf="$(basename "${fish_exe}")" -# Terminal colouring -term_red="$(tput setaf 1)" -term_green="$(tput setaf 2)" -term_yellow="$(tput setaf 3)" -term_blue="$(tput setaf 4)" -term_magenta="$(tput setaf 5)" -term_cyan="$(tput setaf 6)" -term_white="$(tput setaf 7)" -term_reset="$(tput sgr0)" - # Which system are we on. # fish has slightly different behaviour depending on the system it is # running on (and the libraries that it is linked with), so for special @@ -300,6 +290,17 @@ fi clean_environment +# Terminal colouring +# Only do this after setting up $TERM. +term_red="$(tput setaf 1)" +term_green="$(tput setaf 2)" +term_yellow="$(tput setaf 3)" +term_blue="$(tput setaf 4)" +term_magenta="$(tput setaf 5)" +term_cyan="$(tput setaf 6)" +term_white="$(tput setaf 7)" +term_reset="$(tput sgr0)" + say cyan "Testing shell invocation functionality" passed=0 From b7369213e226b921091a24140ef09080540e1f3c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 17:46:21 +0100 Subject: [PATCH 066/439] tests/interactive: Scope variables correctly This `set TERM`. Which, if $TERM is inherited, is already exported, but not if it isn't. This is the case on sr.ht's arch images, so we failed without a TERM variable. --- tests/interactive.fish | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/interactive.fish b/tests/interactive.fish index 230241a84..2a0bbc7ad 100644 --- a/tests/interactive.fish +++ b/tests/interactive.fish @@ -10,11 +10,11 @@ if test "$TRAVIS_OS_NAME" = osx end # This is a list of flakey tests that often succeed when rerun. -set TESTS_TO_RETRY bind.expect +set -l TESTS_TO_RETRY bind.expect # Set this var to modify behavior of the code being tests. Such as avoiding running # `fish_update_completions` when running tests. -set -x FISH_UNIT_TESTS_RUNNING 1 +set -gx FISH_UNIT_TESTS_RUNNING 1 # Change to directory containing this script cd (dirname (status -f)) @@ -22,7 +22,7 @@ cd (dirname (status -f)) # These env vars should not be inherited from the user environment because they can affect the # behavior of the tests. So either remove them or set them to a known value. # See also tests/test.fish. -set TERM xterm +set -gx TERM xterm set -e ITERM_PROFILE # Test files specified on commandline, or all *.expect files From e83c0b1a53b8ec349c240069e53533422922bfeb Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 18:11:34 +0100 Subject: [PATCH 067/439] tests/read: Set TERM=xterm explicitly --- tests/read.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/read.in b/tests/read.in index 59eddcdfd..4c8ca3475 100644 --- a/tests/read.in +++ b/tests/read.in @@ -3,6 +3,8 @@ # Test read builtin and IFS. # +# Set term again explicitly to ensure behavior. +set -gx TERM xterm logmsg Read with no vars is not an error read From e7221a21efcc290f4425771dea41d9100509ee8a Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 18:15:38 +0100 Subject: [PATCH 068/439] tests/invocation: Disable bad-switch test This isn't all that important, and it breaks on musl just because the message is different. Just skip it for now, until we figure out how to better test this. --- .../invocation/{bad-switch.invoke => bad-switch.invoke.disabled} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/invocation/{bad-switch.invoke => bad-switch.invoke.disabled} (100%) diff --git a/tests/invocation/bad-switch.invoke b/tests/invocation/bad-switch.invoke.disabled similarity index 100% rename from tests/invocation/bad-switch.invoke rename to tests/invocation/bad-switch.invoke.disabled From 54438f8dc1d2acec5451d48a3d7d38ec1affa527 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 18:24:27 +0100 Subject: [PATCH 069/439] tests/invocation: Check for tput --- tests/invocation.sh | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/tests/invocation.sh b/tests/invocation.sh index 5108dd283..44fcc7ba3 100755 --- a/tests/invocation.sh +++ b/tests/invocation.sh @@ -292,14 +292,25 @@ clean_environment # Terminal colouring # Only do this after setting up $TERM. -term_red="$(tput setaf 1)" -term_green="$(tput setaf 2)" -term_yellow="$(tput setaf 3)" -term_blue="$(tput setaf 4)" -term_magenta="$(tput setaf 5)" -term_cyan="$(tput setaf 6)" -term_white="$(tput setaf 7)" -term_reset="$(tput sgr0)" +term_red="" +term_green="" +term_yellow="" +term_blue="" +term_magenta="" +term_cyan="" +term_white="" +term_reset="" +# Some systems don't have tput. Disable coloring. +if command -v tput >/dev/null 2>&1; then + term_red="$(tput setaf 1)" + term_green="$(tput setaf 2)" + term_yellow="$(tput setaf 3)" + term_blue="$(tput setaf 4)" + term_magenta="$(tput setaf 5)" + term_cyan="$(tput setaf 6)" + term_white="$(tput setaf 7)" + term_reset="$(tput sgr0)" +fi say cyan "Testing shell invocation functionality" From ba8748877abeaeea6b6b26ebe356d531a93a0031 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 20:23:41 +0100 Subject: [PATCH 070/439] tests/functions: Don't compare diff output Turns out busybox diff (used on alpine) defaults to unified output, which we can't use because that prints filenames, and those are tempfiles made by psub. Instead, we use builtins to print the first line and compare the others. --- tests/function.in | 14 ++++++++++++-- tests/function.out | 14 ++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/tests/function.in b/tests/function.in index 5f64ed777..ef9bd537d 100644 --- a/tests/function.in +++ b/tests/function.in @@ -43,8 +43,18 @@ functions -q name3a or echo "Function name3a not found as expected" logmsg Checking that the copied functions are identical other than the name -diff (functions name1 | psub) (functions name1a | psub) -diff (functions name3 | psub) (functions name3a | psub) +# Poor man's diff because on some systems diff defaults to unified output, but that prints filenames. +# +set -l name1 (functions name1) +set -l name1a (functions name1a) +set -l name3 (functions name3) +set -l name3a (functions name3a) +echo $name1[1] +echo $name1a[1] +test "$name1[2..-1]" = "$name1a[2..-1]"; and echo "1 = 1a" +echo $name3[1] +echo $name3a[1] +test "$name3[2..-1]" = "$name3a[2..-1]"; and echo "3 = 3a" logmsg Checking reserved names function test; echo banana; end diff --git a/tests/function.out b/tests/function.out index 95a79ed2f..0e44e0182 100644 --- a/tests/function.out +++ b/tests/function.out @@ -62,14 +62,12 @@ Function name4 not found as expected #################### # Checking that the copied functions are identical other than the name -1c1 -< function name1 --argument arg1 arg2 ---- -> function name1a --argument arg1 arg2 -1c1 -< function name3 --argument arg1 arg2 ---- -> function name3a --argument arg1 arg2 +function name1 --argument arg1 arg2 +function name1a --argument arg1 arg2 +1 = 1a +function name3 --argument arg1 arg2 +function name3a --argument arg1 arg2 +3 = 3a #################### # Checking reserved names From 217486e547861d29262dc6173dcde3667a0cbc7b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 20:27:30 +0100 Subject: [PATCH 071/439] math: Use simpler format string It seems like musl's printf here fails on `%*lc`. So we use `%*ls`, which we already use in string, so it should work. --- src/builtin_math.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builtin_math.cpp b/src/builtin_math.cpp index 52ef83bae..cf51111d2 100644 --- a/src/builtin_math.cpp +++ b/src/builtin_math.cpp @@ -206,7 +206,7 @@ static int evaluate_expression(const wchar_t *cmd, parser_t &parser, io_streams_ } else { streams.err.append_format(L"%ls: Error: %ls\n", cmd, math_describe_error(error).c_str()); streams.err.append_format(L"'%ls'\n", expression.c_str()); - streams.err.append_format(L"%*lc^\n", error.position - 1, L' '); + streams.err.append_format(L"%*ls%ls\n", error.position - 1, L" ",L"^"); retval = STATUS_CMD_ERROR; } setlocale(LC_NUMERIC, saved_locale); From a84d22b9265e28c3f4dc547cdef83648073ff89a Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 20:30:39 +0100 Subject: [PATCH 072/439] tests/printf: Skip locale test on musl It does not provide a `locale`, so we can't list the locales. --- tests/printf.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/printf.in b/tests/printf.in index 4ee7e95cd..9c8b9f9a8 100644 --- a/tests/printf.in +++ b/tests/printf.in @@ -43,7 +43,9 @@ printf '%e\n' "2,34" # should fail # Try to use one of several locales that use a comma as the decimal mark # rather than the period used in english speaking locales. If we don't find # one installed we simply don't run this test. -set -l locales (locale -a) +# +# musl currently does not have a `locale` command, so we also skip it then. +set -l locales (command -sq locale; and locale -a) set -l acceptable_locales bg_BG de_DE es_ES fr_FR ru_RU set -l numeric_locale for locale in {$acceptable_locales}.{UTF-8,UTF8} From 8b5fa6f572f86f025f926fcface3c1a7a282f6e5 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 20:39:03 +0100 Subject: [PATCH 073/439] tests/test9: Guard locale Musl! --- tests/test9.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test9.in b/tests/test9.in index c38f2e58f..1a0bd79ce 100644 --- a/tests/test9.in +++ b/tests/test9.in @@ -102,7 +102,8 @@ try_unbalanced_block 'if false' "wh"'ile' false; "e"nd # BOM checking (see #1518). But only in UTF8 locales. -if string match -qi '*utf-8*' -- (locale) +# (locale guarded because of musl) +if command -sq locale; and string match -qi '*utf-8*' -- (locale) echo \uFEFF"echo bom_test" | source else echo "echo bom_test" | source From 7afd7a1985e79410d9628ec4fcc27c58a43de6a3 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 20:53:47 +0100 Subject: [PATCH 074/439] tests/histfile Remove history --save This might crash on arch on sr.ht? --- tests/histfile.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/histfile.expect b/tests/histfile.expect index 21082c811..81ca11276 100644 --- a/tests/histfile.expect +++ b/tests/histfile.expect @@ -110,8 +110,8 @@ expect_prompt # TODO: Figure out why this `history --save` is only needed in one of the # three Travis CI build environments and neither of my OS X or Ubuntu servers. -send "history --save\r" -expect_prompt +# send "history --save\r" +# expect_prompt send "grep '^$hist_line' $env_histfile\r" expect_prompt -re "\r\n$hist_line\r\n" { From ea6631641f1ce5504bffe0d9e5bf771297a756d9 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 16 Dec 2018 21:14:55 +0100 Subject: [PATCH 075/439] tests/read.expect: Skip /dev/stdin if it doesn't exist Fixes a failing test on alpine/musl. --- tests/read.expect | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/read.expect b/tests/read.expect index 6f9caac63..2467633a0 100644 --- a/tests/read.expect +++ b/tests/read.expect @@ -110,7 +110,9 @@ send "def\n" expect "abc then def\r\n" expect_prompt -send "r2l Date: Tue, 1 Jan 2019 22:09:57 +0800 Subject: [PATCH 076/439] autoconf: quiet warning by using AC_LANG_PROGRAM e4b6007f33ba26 introduced the following warning: configure.ac:327: warning: AC_LANG_CONFTEST: no AC_LANG_SOURCE call detected in body Fix by using the right autoconf macros for the job. --- configure.ac | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index ef60461c6..82f859368 100644 --- a/configure.ac +++ b/configure.ac @@ -325,17 +325,16 @@ AC_CHECK_FUNCS( getpwent flock ) AC_MSG_CHECKING([dirfd]) AC_LINK_IFELSE([ - -#include - -DIR *dirp; - -int -main(void) -{ - return dirfd(dirp); -} -], [ AC_MSG_RESULT([yes]) + AC_LANG_PROGRAM( + [ + #include + DIR *dirp; + ], [ + return dirfd(dirp); + ] + ) + ] +, [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_DIRFD], 1, [ Define to 1 if you have the `dirfd' function or macro. ]) ], [ AC_MSG_RESULT([no])] From d1913f0df0e5d05fa553b6d482f1cb71261f22ba Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 00:12:07 -0600 Subject: [PATCH 077/439] Add workaround for Cygwin process management and job control bugs We cannot wait by pgroup under Cygwin for unknown reasons. Always wait on jobs by individual processes. See code for more information. --- cmake/ConfigureChecks.cmake | 5 +++++ config_cmake.h.in | 3 +++ src/common.h | 9 +++++++++ src/proc.cpp | 24 +++++++++++++++++++++--- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index 6b4f0b99f..265a96127 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -19,6 +19,11 @@ if (CMAKE_HOST_SYSTEM_VERSION MATCHES ".*-Microsoft") SET(WSL 1) endif() +# Detect Cygwin. +if (CMAKE_SYSTEM_NAME MATCHES "CYGWIN_NT.*") + SET(CYGWIN 1) +endif() + # Set up the config.h file. SET(PACKAGE_NAME "fish") SET(PACKAGE_TARNAME "fish") diff --git a/config_cmake.h.in b/config_cmake.h.in index 47cfcc5e2..162f76de0 100644 --- a/config_cmake.h.in +++ b/config_cmake.h.in @@ -4,6 +4,9 @@ /* Define to 1 if compiled on WSL */ #cmakedefine WSL 1 +/* Define to 1 if complied under Cygwin */ +#cmakedefine CYGWIN 1 + /* Define to 1 if you have the `clock_gettime' function. */ #cmakedefine HAVE_CLOCK_GETTIME 1 diff --git a/src/common.h b/src/common.h index 889036841..dac5d9a6d 100644 --- a/src/common.h +++ b/src/common.h @@ -947,6 +947,15 @@ constexpr bool is_windows_subsystem_for_linux() { #endif } +/// Detect if we are running under Cygwin or Cgywin64 +constexpr bool is_cygwin() { +#ifdef CYGWIN + return true; +#else + return false; +#endif +} + extern "C" { __attribute__((noinline)) void debug_thread_error(void); } diff --git a/src/proc.cpp b/src/proc.cpp index 8072a64a7..69494c8dc 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -448,9 +448,27 @@ static bool process_mark_finished_children(bool block_on_fg) { options &= ~WNOHANG; } - // If the pgid is 0, we need to wait by process because that's invalid. - // This happens in firejail for reasons not entirely clear to me. - bool wait_by_process = !j->job_chain_is_fully_constructed() || j->pgid == 0; + // Child jobs (produced via execution of functions) share job ids with their not-yet- + // fully-constructed parent jobs, so we have to wait on these by individual process id + // and not by the shared pgroup. End result is the same, but it just makes more calls + // to the kernel. + bool wait_by_process = !j->job_chain_is_fully_constructed(); + + // Firejail can result in jobs with pgroup 0, in which case we cannot wait by + // job id. See discussion in #5295. + if (j->pgid == 0) { + wait_by_process = true; + } + + // Cygwin does some voodoo with regards to process management that I do not understand, but + // long story short, we cannot reap processes by their pgroup. The way child processes are + // launched under Cygwin is... weird, and outwardly they do not appear to retain information + // about their parent process when viewed in Task Manager. Waiting on processes by their + // pgroup results in never reaping any, so we just wait on them by process id instead. + if (is_cygwin()) { + wait_by_process = true; + } + // When waiting on processes individually in a pipeline, we need to enumerate in reverse // order so that the first process we actually wait on (i.e. ~WNOHANG) is the last process // in the IO chain, because that's the one that controls the lifetime of the foreground job From ef23923c8dc2dd825a5a260b99bb3b7fd5c4834b Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 00:28:25 -0600 Subject: [PATCH 078/439] Drop use of deprecated bzero(3) Use `memset(__, 0, __)` instead. Also fixes #5461 by not needing `bzero` from `strings.h` anymore. --- src/history.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/history.cpp b/src/history.cpp index a2b4bb0bc..8ee7974e8 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -270,7 +270,7 @@ class history_file_contents_t { ptr += amt; } } - bzero(ptr, remaining); + memset(ptr, 0, remaining); return true; } From 6783c63eeefaa7c96fa99ddb3cbfe3cb603bddbc Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 2 Jan 2019 21:36:55 +0800 Subject: [PATCH 079/439] fish.spec: tidy up extra CMake arguments --- fish.spec.in | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fish.spec.in b/fish.spec.in index 70d0b190b..d929ec412 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -49,11 +49,12 @@ EXTRA_CMAKE_FLAGS="-DCURSES_EXTRA_LIBRARY=tinfo" export CXXFLAGS="$CXXFLAGS -march=i686" %endif %endif -# CMake macros on OpenSUSE and Fedora define the wrong sysconfdir arguments +# CMake macros define the wrong sysconfdir arguments +EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DCMAKE_INSTALL_SYSCONFDIR=%{_sysconfdir}" %if 0%{?rhel_version} && 0%{?rhel_version} < 800 -%cmake3 -DCMAKE_INSTALL_SYSCONFDIR=%{_sysconfdir} $EXTRA_CMAKE_FLAGS +%cmake3 $EXTRA_CMAKE_FLAGS %else -%cmake -DCMAKE_INSTALL_SYSCONFDIR=%{_sysconfdir} $EXTRA_CMAKE_FLAGS +%cmake $EXTRA_CMAKE_FLAGS %endif %if 0%{?make_jobs:1} %make_jobs From 7191a42ca0f1154788a9f5bc0382f34bc3b205a2 Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 2 Jan 2019 21:38:30 +0800 Subject: [PATCH 080/439] fish.spec: turn the BUILD_SHARED_LIBS option off PCRE2 should only be built as a static library. Closes #5448. --- fish.spec.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fish.spec.in b/fish.spec.in index d929ec412..ed0640808 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -49,6 +49,9 @@ EXTRA_CMAKE_FLAGS="-DCURSES_EXTRA_LIBRARY=tinfo" export CXXFLAGS="$CXXFLAGS -march=i686" %endif %endif +# CMake macros define -DBUILD_SHARED_LIBS:BOOL=ON, which breaks the +# bundled PCRE2 static library. +EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DBUILD_SHARED_LIBS:BOOL=OFF" # CMake macros define the wrong sysconfdir arguments EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DCMAKE_INSTALL_SYSCONFDIR=%{_sysconfdir}" %if 0%{?rhel_version} && 0%{?rhel_version} < 800 From 41b333117561794e8ba827c3561378f3366a8bcf Mon Sep 17 00:00:00 2001 From: Sean Molenaar Date: Sun, 30 Dec 2018 22:51:24 +0100 Subject: [PATCH 081/439] Add support for wayland copy/paste --- CHANGELOG.md | 1 + share/functions/fish_clipboard_copy.fish | 2 ++ share/functions/fish_clipboard_paste.fish | 2 ++ 3 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 178add350..708f924fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Added completions for: - nothing yet... - Lots of improvements to completions. +- fish_clipboard_* now supports wayland by means of [wl-clipboard](https://github.com/bugaevc/wl-clipboard). --- diff --git a/share/functions/fish_clipboard_copy.fish b/share/functions/fish_clipboard_copy.fish index 85c64e3a2..0054198aa 100644 --- a/share/functions/fish_clipboard_copy.fish +++ b/share/functions/fish_clipboard_copy.fish @@ -10,5 +10,7 @@ function fish_clipboard_copy printf '%s\n' $cmdline | xsel --clipboard 2>/dev/null else if type -q xclip printf '%s\n' $cmdline | xclip -selection clipboard 2>/dev/null + else if type -q wl-copy + printf '%s\n' $cmdline | wl-copy end end diff --git a/share/functions/fish_clipboard_paste.fish b/share/functions/fish_clipboard_paste.fish index 942724160..e56ff6b16 100644 --- a/share/functions/fish_clipboard_paste.fish +++ b/share/functions/fish_clipboard_paste.fish @@ -13,6 +13,8 @@ function fish_clipboard_paste if not set data (xclip -selection clipboard -o 2>/dev/null) return 1 end + else if type -q wl-paste + set data (wl-paste) end # Also split on \r to turn it into a newline, # otherwise the output looks really confusing. From 74422e476b91643a6bea132bec78e580b6c45f2e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 14:05:06 -0600 Subject: [PATCH 082/439] Define _GNU_SOURCE for wcstod_l check --- cmake/ConfigureChecks.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index 265a96127..e4fc14039 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -33,6 +33,7 @@ INCLUDE(CheckIncludeFiles) INCLUDE(CheckStructHasMember) INCLUDE(CheckCXXSourceCompiles) INCLUDE(CheckTypeSize) +INCLUDE(CMakePushCheckState) CHECK_CXX_SYMBOL_EXISTS(backtrace_symbols execinfo.h HAVE_BACKTRACE_SYMBOLS) CHECK_CXX_SYMBOL_EXISTS(clock_gettime time.h HAVE_CLOCK_GETTIME) CHECK_CXX_SYMBOL_EXISTS(ctermid_r stdio.h HAVE_CTERMID_R) @@ -77,7 +78,12 @@ CHECK_CXX_SYMBOL_EXISTS(wcsdup wchar.h HAVE_WCSDUP) CHECK_CXX_SYMBOL_EXISTS(wcslcpy wchar.h HAVE_WCSLCPY) CHECK_CXX_SYMBOL_EXISTS(wcsncasecmp wchar.h HAVE_WCSNCASECMP) CHECK_CXX_SYMBOL_EXISTS(wcsndup wchar.h HAVE_WCSNDUP) + +CMAKE_PUSH_CHECK_STATE(RESET) +# wcstod_l is a GNU-extension, sometimes hidden behind the following define +LIST(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE=1) CHECK_CXX_SYMBOL_EXISTS(wcstod_l "wchar.h;xlocale.h" HAVE_WCSTOD_L) +CMAKE_POP_CHECK_STATE() CHECK_CXX_SYMBOL_EXISTS(_sys_errs stdlib.h HAVE__SYS__ERRS) From 7af0cad23dd2e9d4438e742f80c63a44d9cb4683 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 18:38:55 -0600 Subject: [PATCH 083/439] Fall back to CMake's pkg-config-based search for curses CMake seems to have trouble finding libraries from multiarch packages that do not have the compatibility symlink installed to the arch-independent library directory. Users must either manually supply the path to the library in question via command-line parameters or we can fall back to CMake's alternate method of finding packages based off of pkg-config rather than using the hard-coded `FindCurses` CMake module specific to the CMake version/distribution installed. --- cmake/ConfigureChecks.cmake | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index e4fc14039..a9ddf674d 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -1,5 +1,15 @@ -# Detect curses. -FIND_PACKAGE(Curses REQUIRED) +# Try using CMake's own logic to locate curses/ncurses +FIND_PACKAGE(Curses) +IF(NOT ${CURSES_FOUND}) + # CMake has trouble finding platform-specific system libraries + # installed to multiarch paths (e.g. /usr/lib/x86_64-linux-gnu) + # if not symlinked or passed in as a manual define. + MESSAGE("Falling back to pkg-config for (n)curses detection") + INCLUDE(FindPkgConfig) + PKG_SEARCH_MODULE(CURSES REQUIRED ncurses curses) + SET(CURSES_CURSES_LIBRARY ${CURSES_LIBRARIES}) + SET(CURSES_LIBRARY ${CURSES_LIBRARIES}) +ENDIF() # Get threads. set(THREADS_PREFER_PTHREAD_FLAG ON) From bc0a0b4bc84fcc4f9feeac06bc2c76993691ac34 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 17:25:33 -0600 Subject: [PATCH 084/439] Fix `wcstod_l` infinite recursion under FreeBSD This was the actual issue leading to memory corruption under FreeBSD in issue #5453, worked around by correcting the detection of `wcstod_l` so that our version of the function is not called at all. If we are 100% certain that `wcstod_l` does not exist, then then the existing code is fine. But given that our checks have failed seperately on two different platforms already (FreeBSD and Cygwin/newlib), it's a good precaution to take. --- src/fallback.cpp | 7 ++++--- src/fallback.h | 12 +++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/fallback.cpp b/src/fallback.cpp index 91ed149ed..a0db093a9 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -390,9 +390,10 @@ int flock(int fd, int op) { #endif // HAVE_FLOCK #ifndef HAVE_WCSTOD_L -// musl doesn't feature wcstod_l, -// so we just wrap wcstod. -double wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc) { +#undef wcstod_l +// For platforms without wcstod_l C extension, wrap wcstod after changing the +// thread-specific locale. +double fish_compat::wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc) { char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL)); // Yes, this is hardcoded to use the "C" locale. // That's the only thing we need, and uselocale(loc) broke in my testing. diff --git a/src/fallback.h b/src/fallback.h index 108e416fe..0a94c2b5f 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -200,5 +200,15 @@ int flock(int fd, int op); #endif #ifndef HAVE_WCSTOD_L -double wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc); +// On some platforms if this is incorrectly detected and a system-defined +// defined version of `wcstod_l` exists, calling `wcstod` from our own +// `wcstod_l` can call back into `wcstod_l` causing infinite recursion. +// e.g. FreeBSD defines `wcstod(x, y)` as `wcstod_l(x, y, __get_locale())`. +// Solution: namespace our implementation to make sure there is no symbol +// duplication. +#undef wcstod_l +namespace fish_compat { + double wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc); +} +#define wcstod_l(x, y, z) fish_compat::wcstod_l(x, y, z) #endif From 3444e1db18bcd1b9f7425a31895298c3864d294c Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 17:29:48 -0600 Subject: [PATCH 085/439] Fix unsafe locale usage in `wcstod_l` fallback Using `setlocale` is both not thread-safe and not correct, as a) The global locale is usually stored in static storage, so simultaneous calls to `setlocale` can result in corruption, and b) `setlocale` changes the locale for the entire application, not just the calling thread. This means that even if we wrapped the `wcstod_l` in a mutex to prevent the previous point, the results would still be incorrect because this would incorrectly influence the results of locale-aware functions executed in other threads while this thread is executing. The previous comment mentioned that `uselocale` hadn't worked. I'm not sure what the failing implementation looked like, but `uselocale` can be tricky. The committed implementation passes the tests for me under Linux and FreeBSD. --- src/fallback.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fallback.cpp b/src/fallback.cpp index a0db093a9..bcfc4d926 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -394,13 +394,13 @@ int flock(int fd, int op) { // For platforms without wcstod_l C extension, wrap wcstod after changing the // thread-specific locale. double fish_compat::wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc) { - char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL)); - // Yes, this is hardcoded to use the "C" locale. - // That's the only thing we need, and uselocale(loc) broke in my testing. - setlocale(LC_NUMERIC, "C"); + // Create and use a new, thread-specific locale + locale_t locale = newlocale(LC_NUMERIC, "C", nullptr); + locale_t prev_locale = uselocale(locale); double ret = wcstod(enptr, endptr); - setlocale(LC_NUMERIC, saved_locale); - free(saved_locale); + // Restore the old locale before freeing the locale we created and are still using + uselocale(prev_locale); + freelocale(locale); return ret; } #endif // defined(wcstod_l) From 380bae80bf3e3b317b4b58bb73137831daace7e2 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 19:07:53 -0600 Subject: [PATCH 086/439] Fix `wcstod_l` detection under Linux This was broken in a8eb02f9f59350f222295e16b8f68dc3b45d9ed5 when the detection was corrected for FreeBSD. This patch makes the detection work for both Linux and FreeBSD instead of one or the other (tested). --- cmake/ConfigureChecks.cmake | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index a9ddf674d..da783a6ea 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -90,9 +90,17 @@ CHECK_CXX_SYMBOL_EXISTS(wcsncasecmp wchar.h HAVE_WCSNCASECMP) CHECK_CXX_SYMBOL_EXISTS(wcsndup wchar.h HAVE_WCSNDUP) CMAKE_PUSH_CHECK_STATE(RESET) -# wcstod_l is a GNU-extension, sometimes hidden behind the following define +# `wcstod_l` is a GNU-extension, sometimes hidden behind the following define LIST(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE=1) -CHECK_CXX_SYMBOL_EXISTS(wcstod_l "wchar.h;xlocale.h" HAVE_WCSTOD_L) +# `xlocale.h` is required to find `wcstod_l` in `wchar.h` under FreeBSD, but +# it's not present under Linux. +SET(WCSTOD_L_INCLUDES "") +CHECK_INCLUDE_FILES("xlocale.h" HAVE_XLOCALE_H) +IF(HAVE_XLOCALE_H) + LIST(APPEND WCSTOD_L_INCLUDES "xlocale.h") +ENDIF() +LIST(APPEND WCSTOD_L_INCLUDES "wchar.h") +CHECK_CXX_SYMBOL_EXISTS(wcstod_l "${WCSTOD_L_INCLUDES}" HAVE_WCSTOD_L) CMAKE_POP_CHECK_STATE() CHECK_CXX_SYMBOL_EXISTS(_sys_errs stdlib.h HAVE__SYS__ERRS) From f3e87b7996e316da2ec2851b6a9565d6ed7cf893 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 2 Jan 2019 20:54:14 +0100 Subject: [PATCH 087/439] tests/psub: Don't use `grep -o` and `diff -q` These aren't available on OpenIndiana. `grep -o` is easily changed to `string`, `diff -q` imitated with `comm` and `test`. See #5472. --- tests/psub.in | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/psub.in b/tests/psub.in index 76d8787dc..5a615e540 100644 --- a/tests/psub.in +++ b/tests/psub.in @@ -30,14 +30,14 @@ else end # The --file flag is the default behavior. -if count (echo foo | psub -s .cc | grep -o '\.cc$') >/dev/null +if count (echo foo | psub -s .cc | string match -r '\.cc$') >/dev/null echo 'psub filename ends with .cc' else echo 'psub filename does not end with .cc' end # Make sure we get the same result if we explicitly ask for a temp file. -if count (echo foo | psub -f -s .cc | grep -o '\.cc$') >/dev/null +if count (echo foo | psub -f -s .cc | string match -r '\.cc$') >/dev/null echo 'psub filename ends with .cc' else echo 'psub filename does not end with .cc' @@ -50,4 +50,5 @@ else echo 'psub directory was deleted' end -diff -q (__fish_print_help psub | psub) (psub -hs banana | psub) +set -l diffs (comm -3 (__fish_print_help psub | psub) (psub -hs banana | psub)) +test -z "$diffs" From 12d7c7feb63571e1d210c65dd70b994161ab6e4f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 2 Jan 2019 14:16:39 +0100 Subject: [PATCH 088/439] Switch to readdir from readdir_r It's deprecated in glibc, and does not work properly on Solaris. Fixes #5458. --- src/wutil.cpp | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/wutil.cpp b/src/wutil.cpp index 13001bef6..5dda39b87 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -37,27 +37,27 @@ const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, static owning_lock> wgettext_map; bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name, bool *out_is_dir) { - struct dirent d; - struct dirent *result = NULL; - int retval = readdir_r(dir, &d, &result); - if (retval || !result) { + struct dirent *result = readdir(dir); + if (!result) { out_name = L""; return false; } - out_name = str2wcstring(d.d_name); - if (!out_is_dir) return true; + out_name = str2wcstring(result->d_name); + if (!out_is_dir) { + return true; + } // 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. bool check_with_stat = true; #ifdef HAVE_STRUCT_DIRENT_D_TYPE - if (d.d_type == DT_DIR) { + if (result->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) { + } else if (result->d_type == DT_LNK || result->d_type == DT_UNKNOWN) { // We want to treat symlinks to directories as directories. Use stat to resolve it. check_with_stat = true; } else { @@ -70,7 +70,7 @@ bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name, // 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); + fullpath.append(result->d_name); struct stat buf; if (stat(fullpath.c_str(), &buf) != 0) { is_dir = false; @@ -83,34 +83,24 @@ bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name, } bool wreaddir(DIR *dir, wcstring &out_name) { - // We need to use a union to ensure that the dirent struct is large enough to avoid stomping on - // the stack. Some platforms incorrectly defined the `d_name[]` member as being one element - // long when it should be at least NAME_MAX + 1. - union { - struct dirent d; - char c[offsetof(struct dirent, d_name) + NAME_MAX + 1]; - } d_u; - struct dirent *result = NULL; - - int retval = readdir_r(dir, &d_u.d, &result); - if (retval || !result) { + struct dirent *result = readdir(dir); + if (!result) { out_name = L""; return false; } - out_name = str2wcstring(d_u.d.d_name); + out_name = str2wcstring(result->d_name); return true; } bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) { - struct dirent d; struct dirent *result = NULL; while (!result) { - int retval = readdir_r(dir, &d, &result); - if (retval || !result) break; + result = readdir(dir); + if (!result) break; #if HAVE_STRUCT_DIRENT_D_TYPE - switch (d.d_type) { + switch (result->d_type) { case DT_DIR: case DT_LNK: case DT_UNKNOWN: { @@ -129,7 +119,8 @@ bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) { if (result && out_name) { *out_name = str2wcstring(result->d_name); } - return result != NULL; + if (!result) return false; + return true; } const wcstring wgetcwd() { From 6b37ff050208a9a64683a7219739b4369a76c6fb Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Thu, 3 Jan 2019 17:57:02 +0900 Subject: [PATCH 089/439] Update ppa repo to fish shell 3.x Signed-off-by: Takuya Noguchi --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9e57a074f..786a172ef 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,11 @@ Packages for Debian, Fedora, openSUSE, and Red Hat Enterprise Linux/CentOS are a Service](https://software.opensuse.org/download.html?project=shells%3Afish%3Arelease%3A2&package=fish). Packages for Ubuntu are available from the [fish -PPA](https://launchpad.net/~fish-shell/+archive/ubuntu/release-2), and can be installed using the +PPA](https://launchpad.net/~fish-shell/+archive/ubuntu/release-3), and can be installed using the following commands: ``` -sudo apt-add-repository ppa:fish-shell/release-2 +sudo apt-add-repository ppa:fish-shell/release-3 sudo apt-get update sudo apt-get install fish ``` From 72c0213d42b77b2f01223b5515669840110ea9b9 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 3 Jan 2019 11:47:32 +0100 Subject: [PATCH 090/439] docs: Document $hostname Fixes #5469. [ci skip] --- 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 a0e326e89..7f2fe7efc 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -931,6 +931,8 @@ The user can change the settings of `fish` by changing the values of certain var - `HOME`, the user's home directory. This variable can be changed by the user. +- `hostname`, the machine's hostname. + - `IFS`, the internal field separator that is used for word splitting with the read builtin. Setting this to the empty string will also disable line splitting in command substitution. This variable can be changed by the user. - `PWD`, the current working directory. From 9fb18f63221b20c14fb28fc88ac7fe1665e4c040 Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 3 Jan 2019 23:34:26 +0800 Subject: [PATCH 091/439] README: update to version-independent OBS repository [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 786a172ef..b134234de 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ fish can be installed: Packages for Debian, Fedora, openSUSE, and Red Hat Enterprise Linux/CentOS are available from the [openSUSE Build -Service](https://software.opensuse.org/download.html?project=shells%3Afish%3Arelease%3A2&package=fish). +Service](https://software.opensuse.org/download.html?project=shells%3Afish&package=fish). Packages for Ubuntu are available from the [fish PPA](https://launchpad.net/~fish-shell/+archive/ubuntu/release-3), and can be installed using the From 3a885d5e822a63700c9c9697c2e4e3b236676c2c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 3 Jan 2019 23:17:20 +0100 Subject: [PATCH 092/439] tests/cd: cd back before cleaning up Otherwise this'd run afoul of OpenIndiana's "no removing $PWD" rule. Spoilsports! See #5472. --- tests/cd.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/cd.in b/tests/cd.in index 21104a432..fa3964a22 100644 --- a/tests/cd.in +++ b/tests/cd.in @@ -1,3 +1,5 @@ +# Store pwd to later go back before cleaning up +set -l oldpwd (pwd) logmsg cd symlink non-resolution set real (mktemp -d) set link (mktemp -u) @@ -35,4 +37,7 @@ echo "ls:" complete -C'ls ../' echo "cd:" complete -C'cd ../' + +# cd back before removing the test directory again. +cd $oldpwd rm -Rf $base From 9d4e460b298a39ee2be2443af97413e7063fa8d2 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 4 Jan 2019 08:45:53 +0100 Subject: [PATCH 093/439] string: Fix crash with _GLIBCXX_ASSERTIONS This asserted because we accessed wcstring::front() when it was empty. Instead, check explicitly for it being empty before. Fixes #5479 --- src/builtin_string.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 6e810a32e..bbc7e40c0 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -622,9 +622,13 @@ class wildcard_matcher_t : public string_matcher_t { } } if (opts.entire) { - // If the pattern is empty, this becomes one ANY_STRING that matches everything. - if (wcpattern.front() != ANY_STRING) wcpattern.insert(0, 1, ANY_STRING); - if (wcpattern.back() != ANY_STRING) wcpattern.push_back(ANY_STRING); + if (!wcpattern.empty()) { + if (wcpattern.front() != ANY_STRING) wcpattern.insert(0, 1, ANY_STRING); + if (wcpattern.back() != ANY_STRING) wcpattern.push_back(ANY_STRING); + } else { + // If the pattern is empty, this becomes one ANY_STRING that matches everything. + wcpattern.push_back(ANY_STRING); + } } } From eabda835d588c99b2aa85ef1fceb0192d342b98d Mon Sep 17 00:00:00 2001 From: Benjamin Nied Date: Sun, 30 Dec 2018 08:23:33 -0800 Subject: [PATCH 094/439] fish.spec: switch to using a common rpm macro CentOS 7 does not have rhel_version as one of its macros, so trying to build results in CMake errors, since we get `cmake` instead of `cmake3`. These additional conditions allow the spec to build successfully on CentOS 7. Using %rhel should allow one set of conditionals to work across CentOS 7 and RHEL 7. This has been tested on both. --- fish.spec.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fish.spec.in b/fish.spec.in index ed0640808..1793ae48d 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -11,13 +11,13 @@ URL: https://fishshell.com/ Source0: %{name}_@VERSION@.orig.tar.gz BuildRequires: ncurses-devel gettext gcc-c++ autoconf -%if 0%{?rhel_version} && 0%{?rhel_version} && 0%{?rhel_version} < 800 +%if 0%{?rhel} < 8 BuildRequires: cmake3 %else BuildRequires: cmake %endif -%if 0%{?opensuse_bs} && 0%{?rhel_version} && 0%{?rhel_version} < 700 +%if 0%{?opensuse_bs} && 0%{?rhel} < 7 BuildRequires: gcc48 gcc48-c++ %endif @@ -40,7 +40,7 @@ is simple but incompatible with other shell languages. %setup -q -n %{name}-@VERSION@ %build -%if 0%{?opensuse_bs} && 0%{?rhel_version} && 0%{?rhel_version} < 700 +%if 0%{?opensuse_bs} && 0%{?rhel} < 7 export CC=gcc48 export CXX=g++48 EXTRA_CMAKE_FLAGS="-DCURSES_EXTRA_LIBRARY=tinfo" @@ -54,7 +54,7 @@ export CXXFLAGS="$CXXFLAGS -march=i686" EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DBUILD_SHARED_LIBS:BOOL=OFF" # CMake macros define the wrong sysconfdir arguments EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DCMAKE_INSTALL_SYSCONFDIR=%{_sysconfdir}" -%if 0%{?rhel_version} && 0%{?rhel_version} < 800 +%if 0%{?rhel} < 8 %cmake3 $EXTRA_CMAKE_FLAGS %else %cmake $EXTRA_CMAKE_FLAGS From 04f1ea0680b0deef5a567fa9c507bdce39a2117b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 4 Jan 2019 14:48:01 +0100 Subject: [PATCH 095/439] share/config: Don't split /etc/paths entries on spaces This used `read -la`, which _splits_. Instead, don't do that, each line is its own entry. Fixes #5481. [ci skip] --- share/config.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/config.fish b/share/config.fish index 5c554b213..27e6222a3 100644 --- a/share/config.fish +++ b/share/config.fish @@ -196,7 +196,7 @@ if command -sq /usr/libexec/path_helper for path_file in $argv[2] $argv[3]/* if test -f $path_file - while read -la entry + while read -l entry if not contains $entry $result set result $result $entry end From 490432c177075fad4d34a999877384a1f5ecdf3d Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Fri, 4 Jan 2019 14:04:18 -0600 Subject: [PATCH 096/439] Globally set the _GNU_SOURCE define for CMake configuration checks --- cmake/ConfigureChecks.cmake | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index da783a6ea..a4ee25186 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -1,3 +1,14 @@ +# The following defines affect the environment configuration tests are run in: +# CMAKE_REQUIRED_DEFINITIONS, CMAKE_REQUIRED_FLAGS, CMAKE_REQUIRED_LIBRARIES, +# and CMAKE_REQUIRED_INCLUDES +# `wcstod_l` is a GNU-extension, sometimes hidden behind GNU-related defines. +# This is the case for at least Cygwin and Newlib. +LIST(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE=1) +IF(APPLE) + # 10.7+ only. + LIST(APPEND CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "-Werror=unguarded-availability") +ENDIF() + # Try using CMake's own logic to locate curses/ncurses FIND_PACKAGE(Curses) IF(NOT ${CURSES_FOUND}) @@ -19,11 +30,6 @@ IF(CMAKE_VERSION VERSION_LESS 3.4.0) ENDIF() FIND_PACKAGE(Threads REQUIRED) -IF(APPLE) - # 10.7+ only. - SET(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "-Werror=unguarded-availability") -ENDIF() - # Detect WSL. Does not match against native Windows/WIN32. if (CMAKE_HOST_SYSTEM_VERSION MATCHES ".*-Microsoft") SET(WSL 1) @@ -89,19 +95,14 @@ CHECK_CXX_SYMBOL_EXISTS(wcslcpy wchar.h HAVE_WCSLCPY) CHECK_CXX_SYMBOL_EXISTS(wcsncasecmp wchar.h HAVE_WCSNCASECMP) CHECK_CXX_SYMBOL_EXISTS(wcsndup wchar.h HAVE_WCSNDUP) -CMAKE_PUSH_CHECK_STATE(RESET) -# `wcstod_l` is a GNU-extension, sometimes hidden behind the following define -LIST(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE=1) -# `xlocale.h` is required to find `wcstod_l` in `wchar.h` under FreeBSD, but -# it's not present under Linux. -SET(WCSTOD_L_INCLUDES "") +# `xlocale.h` is required to find `wcstod_l` in `wchar.h` under FreeBSD, +# but it's not present under Linux. CHECK_INCLUDE_FILES("xlocale.h" HAVE_XLOCALE_H) IF(HAVE_XLOCALE_H) LIST(APPEND WCSTOD_L_INCLUDES "xlocale.h") ENDIF() LIST(APPEND WCSTOD_L_INCLUDES "wchar.h") CHECK_CXX_SYMBOL_EXISTS(wcstod_l "${WCSTOD_L_INCLUDES}" HAVE_WCSTOD_L) -CMAKE_POP_CHECK_STATE() CHECK_CXX_SYMBOL_EXISTS(_sys_errs stdlib.h HAVE__SYS__ERRS) From 5196a421650c902c796a34fe64ee76f5b2f3d868 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Fri, 4 Jan 2019 14:30:14 -0600 Subject: [PATCH 097/439] Prevent CMake configure checks for affecting future checks Using CMAKE_PUSH_CHECK_STATE/CMAKE_POP_CHECK_STATE to prevent CMake checks from permanently altering the environment used by future checks. --- cmake/ConfigureChecks.cmake | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index a4ee25186..edceb0997 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -106,13 +106,14 @@ CHECK_CXX_SYMBOL_EXISTS(wcstod_l "${WCSTOD_L_INCLUDES}" HAVE_WCSTOD_L) CHECK_CXX_SYMBOL_EXISTS(_sys_errs stdlib.h HAVE__SYS__ERRS) +CMAKE_PUSH_CHECK_STATE() SET(CMAKE_EXTRA_INCLUDE_FILES termios.h sys/ioctl.h) CHECK_TYPE_SIZE("struct winsize" STRUCT_WINSIZE LANGUAGE CXX) CHECK_CXX_SYMBOL_EXISTS("TIOCGWINSZ" "termios.h;sys/ioctl.h" HAVE_TIOCGWINSZ) IF(STRUCT_WINSIZE GREATER -1 AND HAVE_TIOCGWINSZ EQUAL 1) SET(HAVE_WINSIZE 1) ENDIF() -SET(CMAKE_EXTRA_INCLUDE_FILES) +CMAKE_POP_CHECK_STATE() IF(EXISTS "/proc/self/stat") SET(HAVE__PROC_SELF_STAT 1) @@ -135,7 +136,8 @@ ELSEIF(HAVE_NCURSES_TERM_H) SET(TPARM_INCLUDES "${TPARM_INCLUDES}#include \n") ENDIF() -SET(CMAKE_REQUIRED_LIBRARIES ${CURSES_LIBRARY}) +CMAKE_PUSH_CHECK_STATE() +LIST(APPEND CMAKE_REQUIRED_LIBRARIES ${CURSES_LIBRARY}) CHECK_CXX_SOURCE_COMPILES(" ${TPARM_INCLUDES} @@ -162,7 +164,7 @@ int main () { SET(TPARM_VARARGS 1) ENDIF() ENDIF() -SET(CMAKE_REQUIRED_LIBRARIES) +CMAKE_POP_CHECK_STATE() CHECK_CXX_SOURCE_COMPILES(" #include From 4f196eef64176a105d6801fcb23ac9abbf5ffc1f Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Fri, 4 Jan 2019 14:46:36 -0600 Subject: [PATCH 098/439] Bypass mutually exclusive CMake checks There are some redundant CMake checks, in the sense that they are either not needed or cannot possibly match if a previous check already passed. --- cmake/ConfigureChecks.cmake | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index edceb0997..f3d82ce81 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -71,13 +71,14 @@ CHECK_CXX_SYMBOL_EXISTS(mkostemp "stdlib.h;unistd.h" HAVE_MKOSTEMP) SET(HAVE_CURSES_H ${CURSES_HAVE_CURSES_H}) SET(HAVE_NCURSES_CURSES_H ${CURSES_HAVE_NCURSES_CURSES_H}) SET(HAVE_NCURSES_H ${CURSES_HAVE_NCURSES_H}) -CHECK_INCLUDE_FILES("curses.h;term.h" HAVE_TERM_H) -CHECK_INCLUDE_FILE_CXX("ncurses/term.h" HAVE_NCURSES_TERM_H) +IF(HAVE_CURSES_H) + CHECK_INCLUDE_FILES("curses.h;term.h" HAVE_TERM_H) +ENDIF() +IF(NOT HAVE_TERM_H) + CHECK_INCLUDE_FILE_CXX("ncurses/term.h" HAVE_NCURSES_TERM_H) +ENDIF() CHECK_INCLUDE_FILE_CXX(siginfo.h HAVE_SIGINFO_H) CHECK_INCLUDE_FILE_CXX(spawn.h HAVE_SPAWN_H) -CHECK_CXX_SYMBOL_EXISTS(std::wcscasecmp wchar.h HAVE_STD__WCSCASECMP) -CHECK_CXX_SYMBOL_EXISTS(std::wcsdup wchar.h HAVE_STD__WCSDUP) -CHECK_CXX_SYMBOL_EXISTS(std::wcsncasecmp wchar.h HAVE_STD__WCSNCASECMP) CHECK_STRUCT_HAS_MEMBER("struct stat" st_ctime_nsec "sys/stat.h" HAVE_STRUCT_STAT_ST_CTIME_NSEC LANGUAGE CXX) CHECK_STRUCT_HAS_MEMBER("struct stat" st_mtimespec.tv_nsec "sys/stat.h" @@ -89,12 +90,25 @@ CHECK_INCLUDE_FILE_CXX(sys/ioctl.h HAVE_SYS_IOCTL_H) CHECK_INCLUDE_FILE_CXX(sys/select.h HAVE_SYS_SELECT_H) CHECK_INCLUDE_FILES("sys/types.h;sys/sysctl.h" HAVE_SYS_SYSCTL_H) CHECK_INCLUDE_FILE_CXX(termios.h HAVE_TERMIOS_H) # Needed for TIOCGWINSZ + CHECK_CXX_SYMBOL_EXISTS(wcscasecmp wchar.h HAVE_WCSCASECMP) CHECK_CXX_SYMBOL_EXISTS(wcsdup wchar.h HAVE_WCSDUP) CHECK_CXX_SYMBOL_EXISTS(wcslcpy wchar.h HAVE_WCSLCPY) CHECK_CXX_SYMBOL_EXISTS(wcsncasecmp wchar.h HAVE_WCSNCASECMP) CHECK_CXX_SYMBOL_EXISTS(wcsndup wchar.h HAVE_WCSNDUP) +# These are for compatibility with Solaris 10, which places the following +# in the std namespace. +IF(NOT HAVE_WCSNCASECMP) + CHECK_CXX_SYMBOL_EXISTS(std::wcscasecmp wchar.h HAVE_STD__WCSCASECMP) +ENDIF() +IF(NOT HAVE_WCSDUP) + CHECK_CXX_SYMBOL_EXISTS(std::wcsdup wchar.h HAVE_STD__WCSDUP) +ENDIF() +IF(NOT HAVE_WCSNCASECMP) + CHECK_CXX_SYMBOL_EXISTS(std::wcsncasecmp wchar.h HAVE_STD__WCSNCASECMP) +ENDIF() + # `xlocale.h` is required to find `wcstod_l` in `wchar.h` under FreeBSD, # but it's not present under Linux. CHECK_INCLUDE_FILES("xlocale.h" HAVE_XLOCALE_H) From 2c01e67a74ca48ca87e2f11f6239d54c35551e7b Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 5 Jan 2019 15:59:25 +0800 Subject: [PATCH 099/439] histfile tests: tweak expect commands to avoid crash on 32-bit platforms Rather than killing the process with close, read EOF after sending the "exit" command and wait for OS cleanup (per the expect examples). Not cleaning up with wait caused expect to crash on all 32-bit platforms including i586 and armv7l with "alloc: invalid block: 0xbf993ccb: 3d 3b". 64-bit platforms were not affected, for reasons that are not clear. --- tests/histfile.expect | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/histfile.expect b/tests/histfile.expect index 81ca11276..7ac056235 100644 --- a/tests/histfile.expect +++ b/tests/histfile.expect @@ -92,7 +92,8 @@ expect_prompt -re "\r\n$hist_line\r\n" { # ============= # Start by shutting down the previous shell. send "exit\r" -close $spawn_id +expect eof +wait # Set the fish_history env var. set ::env(fish_history) env From 4548f4f4b0fae8d3cb6869b84fc8940786d6d325 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 5 Jan 2019 17:28:37 +0800 Subject: [PATCH 100/439] fish.spec: run all tests --- fish.spec.in | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fish.spec.in b/fish.spec.in index 1793ae48d..2acffd0e9 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -25,6 +25,13 @@ BuildRequires: gcc48 gcc48-c++ BuildRequires: pcre2-devel %endif +# for tests +BuildRequires: expect +%if 0%{?fedora} +# Need the en_US.utf-8 locale at a minimum +BuildRequires: glibc-langpack-en +%endif + BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Requires: python @@ -73,6 +80,13 @@ make %{?_smp_mflags} %endif %find_lang %{name} +%check +# OpenSUSE does out-of-tree builds and defines __builddir +%if 0%{?__builddir:1} +cd %__builddir +%endif +make test + %clean rm -rf $RPM_BUILD_ROOT From b8b0c39c77bf547506b67041dda6646f92308799 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 5 Jan 2019 12:31:24 +0100 Subject: [PATCH 101/439] fish_tests: Use std::isnan Fixes the tests on Ubuntu 16.04 "xenial". --- src/fish_tests.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index abe21a2e5..e5a3d9426 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -2302,7 +2303,7 @@ static void test_wcstod() { wchar_t *wide_end = nullptr; double val1 = wcstod(a, &wide_end); double val2 = strtod(b, &narrow_end); - do_test((isnan(val1) && isnan(val2)) || fabs(val1 - val2) <= __DBL_EPSILON__); + do_test((std::isnan(val1) && std::isnan(val2)) || fabs(val1 - val2) <= __DBL_EPSILON__); do_test(wide_end - a == narrow_end - b); }; tod_test(L"", ""); From 40fa3c21f0517ede673999e6be1d49f36a4df37c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 8 Nov 2018 18:38:14 +0100 Subject: [PATCH 102/439] Switch Travis to Xenial (16.04) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 80127ddfb..1f68e79ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: cpp -dist: trusty +dist: xenial sudo: required matrix: From f4790a876781e8ec7d350802c80e90686aaef35e Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 5 Jan 2019 20:27:06 +0800 Subject: [PATCH 103/439] fish.spec: check if %rhel is defined before expanding. Work on #5446. --- fish.spec.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fish.spec.in b/fish.spec.in index 2acffd0e9..dfbf81deb 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -11,13 +11,13 @@ URL: https://fishshell.com/ Source0: %{name}_@VERSION@.orig.tar.gz BuildRequires: ncurses-devel gettext gcc-c++ autoconf -%if 0%{?rhel} < 8 +%if 0%{?rhel} && 0%{?rhel} < 8 BuildRequires: cmake3 %else BuildRequires: cmake %endif -%if 0%{?opensuse_bs} && 0%{?rhel} < 7 +%if 0%{?opensuse_bs} && 0%{?rhel} && 0%{?rhel} < 7 BuildRequires: gcc48 gcc48-c++ %endif @@ -47,7 +47,7 @@ is simple but incompatible with other shell languages. %setup -q -n %{name}-@VERSION@ %build -%if 0%{?opensuse_bs} && 0%{?rhel} < 7 +%if 0%{?opensuse_bs} && 0%{?rhel} && 0%{?rhel} < 7 export CC=gcc48 export CXX=g++48 EXTRA_CMAKE_FLAGS="-DCURSES_EXTRA_LIBRARY=tinfo" @@ -61,7 +61,7 @@ export CXXFLAGS="$CXXFLAGS -march=i686" EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DBUILD_SHARED_LIBS:BOOL=OFF" # CMake macros define the wrong sysconfdir arguments EXTRA_CMAKE_FLAGS="$EXTRA_CMAKE_FLAGS -DCMAKE_INSTALL_SYSCONFDIR=%{_sysconfdir}" -%if 0%{?rhel} < 8 +%if 0%{?rhel} && 0%{?rhel} < 8 %cmake3 $EXTRA_CMAKE_FLAGS %else %cmake $EXTRA_CMAKE_FLAGS From 896d6f41f444aea09f08818cdf2ecd9bfe0bc21d Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 5 Jan 2019 20:27:55 +0800 Subject: [PATCH 104/439] fish.spec: drop stanza for RHEL 5 CentOS / Red Hat Enterprise Linux 5 are end-of-life and builds are not made for these platforms any more. --- fish.spec.in | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fish.spec.in b/fish.spec.in index dfbf81deb..d295e7749 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -51,10 +51,6 @@ is simple but incompatible with other shell languages. export CC=gcc48 export CXX=g++48 EXTRA_CMAKE_FLAGS="-DCURSES_EXTRA_LIBRARY=tinfo" -%if 0%{?rhel} < 6 -# i686 required for atomic instructions; default is i386 -export CXXFLAGS="$CXXFLAGS -march=i686" -%endif %endif # CMake macros define -DBUILD_SHARED_LIBS:BOOL=ON, which breaks the # bundled PCRE2 static library. From d776a366faecc51832a76582bc5171aaddd985c7 Mon Sep 17 00:00:00 2001 From: "Stephen M. Coakley" Date: Thu, 3 Jan 2019 22:20:43 -0600 Subject: [PATCH 105/439] Pass final Fish exit status to fish_exit event For fish_exit to be a suitable replacement for --on-process-exit, we need to be able to provide scripts with access to the shell's final exit code. --- src/fish.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/fish.cpp b/src/fish.cpp index 49110ac1e..c213321d9 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -440,7 +440,10 @@ int main(int argc, char **argv) { // TODO: The generic process-exit event is useless and unused. // Remove this in future. proc_fire_event(L"PROCESS_EXIT", EVENT_EXIT, getpid(), exit_status); - event_fire_generic(L"fish_exit"); + + // Trigger any exit handlers. + wcstring_list_t event_args = {to_string(exit_status)}; + event_fire_generic(L"fish_exit", &event_args); restore_term_mode(); restore_term_foreground_process_group(); From e7bfd1d71ca54df726a4f1ea14bd6b0957b75752 Mon Sep 17 00:00:00 2001 From: Benjamin Nied Date: Sat, 5 Jan 2019 10:11:19 -0800 Subject: [PATCH 106/439] Remove shebangs from py scripts Starting with Fedora 30 and RHEL 8, ambiguous python shebangs will now throw errors during the RPM build process instead of just warnings, since these systems have moved to Python 3 by default, and Python 2 may not be available in the future. See [this page](https://fedoraproject.org/wiki/Changes/Make_ambiguous_python_shebangs_error) for more details. Drop these shebangs as the scripts are only ever called from fish wrappers. --- share/tools/create_manpage_completions.py | 1 - share/tools/deroff.py | 1 - share/tools/web_config/webconfig.py | 2 -- 3 files changed, 4 deletions(-) diff --git a/share/tools/create_manpage_completions.py b/share/tools/create_manpage_completions.py index f0326537e..bbee8d102 100755 --- a/share/tools/create_manpage_completions.py +++ b/share/tools/create_manpage_completions.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- # Run me like this: ./create_manpage_completions.py /usr/share/man/man{1,8}/* > man_completions.fish diff --git a/share/tools/deroff.py b/share/tools/deroff.py index 4ef4fb690..ac3d89650 100755 --- a/share/tools/deroff.py +++ b/share/tools/deroff.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ Deroff.py, ported to Python from the venerable deroff.c """ diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 58702f2fa..8fb945d7b 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from __future__ import unicode_literals from __future__ import print_function import binascii From adb97772c50b73c92163f337239a316e4a3bd267 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 6 Jan 2019 13:45:37 +0100 Subject: [PATCH 107/439] doc_src/printf: Add missing space [ci skip] --- doc_src/printf.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/printf.txt b/doc_src/printf.txt index 49fb0ad6a..26b60618c 100644 --- a/doc_src/printf.txt +++ b/doc_src/printf.txt @@ -65,6 +65,6 @@ printf '%s\\t%s\\n' flounder fish 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. \fish -printf '%s:%d' "Number of bananas in my pocket" 42 +printf '%s: %d' "Number of bananas in my pocket" 42 \endfish Will print "Number of bananas in my pocket: 42", _without_ a newline. From d4be5f08f35d94b3894042068965b0dcfa22cdae Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 7 Jan 2019 17:24:17 +0100 Subject: [PATCH 108/439] Fix nim prompt (via web_config) This had a helper function defined outside of the fish_prompt function, so `funcsave` missed it (see #736). Fixes #5490. [ci skip] --- .../tools/web_config/sample_prompts/nim.fish | 83 ++++++++++--------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/share/tools/web_config/sample_prompts/nim.fish b/share/tools/web_config/sample_prompts/nim.fish index b935f8413..93c574dd5 100644 --- a/share/tools/web_config/sample_prompts/nim.fish +++ b/share/tools/web_config/sample_prompts/nim.fish @@ -1,49 +1,50 @@ # name: Nim # author: Guilhem "Nim" Saurel − https://github.com/nim65s/dotfiles/ -# This prompt shows: -# - green lines if the last return command is OK, red otherwise -# - your user name, in red if root or yellow otherwise -# - your hostname, in cyan if ssh or blue otherwise -# - the current path (with prompt_pwd) -# - date +%X -# - the current virtual environment, if any -# - the current git status, if any, with __fish_git_prompt -# - the current battery state, if any, and if your power cable is unplugged, and if you have "acpi" -# - current background jobs, if any - -# It goes from: -# ┬─[nim@Hattori:~]─[11:39:00] -# ╰─>$ echo here - -# To: -# ┬─[nim@Hattori:~/w/dashboard]─[11:37:14]─[V:django20]─[G:master↑1|●1✚1…1]─[B:85%, 05:41:42 remaining] -# │ 2 15054 0% arrêtée sleep 100000 -# │ 1 15048 0% arrêtée sleep 100000 -# ╰─>$ echo there - -set __fish_git_prompt_showupstream auto - -function _nim_prompt_wrapper - set retc $argv[1] - set field_name $argv[2] - set field_value $argv[3] - - set_color normal - set_color $retc - echo -n '─' - set_color -o green - echo -n '[' - set_color normal - test -n $field_name - and echo -n $field_name: - set_color $retc - echo -n $field_value - set_color -o green - echo -n ']' -end function fish_prompt + # This prompt shows: + # - green lines if the last return command is OK, red otherwise + # - your user name, in red if root or yellow otherwise + # - your hostname, in cyan if ssh or blue otherwise + # - the current path (with prompt_pwd) + # - date +%X + # - the current virtual environment, if any + # - the current git status, if any, with __fish_git_prompt + # - the current battery state, if any, and if your power cable is unplugged, and if you have "acpi" + # - current background jobs, if any + + # It goes from: + # ┬─[nim@Hattori:~]─[11:39:00] + # ╰─>$ echo here + + # To: + # ┬─[nim@Hattori:~/w/dashboard]─[11:37:14]─[V:django20]─[G:master↑1|●1✚1…1]─[B:85%, 05:41:42 remaining] + # │ 2 15054 0% arrêtée sleep 100000 + # │ 1 15048 0% arrêtée sleep 100000 + # ╰─>$ echo there + + set -q __fish_git_prompt_showupstream + or set -g __fish_git_prompt_showupstream auto + + function _nim_prompt_wrapper + set retc $argv[1] + set field_name $argv[2] + set field_value $argv[3] + + set_color normal + set_color $retc + echo -n '─' + set_color -o green + echo -n '[' + set_color normal + test -n $field_name + and echo -n $field_name: + set_color $retc + echo -n $field_value + set_color -o green + echo -n ']' + end and set retc green or set retc red From 9aa8740c362540347ad2979cbe7975e9545d5bbc Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 7 Jan 2019 18:15:21 +0100 Subject: [PATCH 109/439] tests/invocation.sh: Port to sh (from bash) This makes the script worse, but it's good enough. The required changes are: - `shopt -s nullglob`, which we simply don't use (we have one glob, but that's guaranteed to match because we ship the files) - One array, which we replace with a direct use of the glob (plus it used `echo` again?) - The `function` word, which I'm still annoyed is even a thing! - Variable indirection (`color=${!color_var}` - instead we pass the value directly - which makes the script uglier!) - One array, which we replace with a function - A use of `type -t`, replaced with `command -v` - A use of `${var:begin:end}` substring expansion, replaced with trickery. - `set -o pipefail` is replaced with a function Note that checkbashisms still complains about `command -v`, because we're not using it with "-p". But we _want_ to check the current $PATH, and `command -v` is POSIX. This still uses `local`, which technically isn't in POSIX. The tests now appear to pass in: - bash - dash - zsh - mksh - busybox --- tests/invocation.sh | 93 +++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 53 deletions(-) diff --git a/tests/invocation.sh b/tests/invocation.sh index 44fcc7ba3..45e04d6ee 100755 --- a/tests/invocation.sh +++ b/tests/invocation.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh ## # Test that the invocation of the fish executable works as we hope. # @@ -57,13 +57,6 @@ # Errors will be fatal set -e -# If any command in the pipeline fails report the rc of the first fail. -set -o pipefail - -# If nothing matches a glob expansion, return nothing (not the glob -# itself) -shopt -s nullglob - # The directory this script is in (as everything is relative to here) here="$(cd "$(dirname "$0")" && pwd -P)" cd "$here" @@ -71,9 +64,6 @@ cd "$here" # The temporary directory to use temp_dir="$here/../test" -# The files we're going to execute are in the 'invocation' directory. -files_to_test=($(echo invocation/*.invoke)) - # The fish binary we are testing - for manual testing, may be overridden fish_exe="${fish_exe:-../test/root/bin/fish}" fish_dir="$(dirname "${fish_exe}")" @@ -89,7 +79,7 @@ system_name="$(uname -s)" # Check whether we have the 'colordiff' tool - if not, we'll revert to # boring regular 'diff'. -if [ "$(type -t colordiff)" != '' ] ; then +if command -v colordiff >/dev/null 2>&1; then difftool='colordiff' else difftool='diff' @@ -99,7 +89,7 @@ fi ## # Set variables to known values so that they will not affect the # execution of the test. -function clean_environment() { +clean_environment() { # Reset the terminal variables to a known type. export TERM=xterm @@ -119,27 +109,41 @@ function clean_environment() { ## # Fail completely :-( -function fail() { - say red "FAIL: $*" >&2 +fail() { + say "$term_red" "FAIL: $*" >&2 exit 1 } ## # Coloured output -function say() { - local color_name="$1" - local msg="$2" - local color_var="term_${color_name}" - local color="${!color_var}" - - echo "$color$msg$term_reset" +# +# Use like `say "$term_green" "message". +say() { + echo "$1$2$term_reset" } +run_rc() { + # Write the return code on to the end of the stderr, so that it can be + # checked like anything else. + eval "$*" || echo "RC: $?" >&2 +} + +filter() { + # In some cases we want to check only a part of the output. + # For those we filter the output through grep'd matches. + if [ -f "$1" ] ; then + # grep '-o', '-E' and '-f' are supported by the tools in modern GNU + # environments, and on OS X. + grep -oE -f "$1" + else + cat + fi +} ## # Actual testing of a .invoke file. -function test_file() { +test_file() { local file="$1" local dir="$(dirname "$file")" local base="$(basename "$file" .invoke)" @@ -150,7 +154,7 @@ function test_file() { local grep_stdout="${dir}/${base}.grep" local want_stderr="${dir}/${base}.err" local empty="${dir}/${base}.empty" - local -a filter + local filter local rc=0 local test_args_literal local test_args @@ -191,16 +195,6 @@ function test_file() { rm -f "${temp_dir}/home/fish/config.fish" fi - # In some cases we want to check only a part of the output. - # For those we filter the output through grep'd matches. - if [ -f "$grep_stdout" ] ; then - # grep '-o', '-E' and '-f' are supported by the tools in modern GNU - # environments, and on OS X. - filter=('grep' '-o' '-E' '-f' "$grep_stdout") - else - filter=('cat') - fi - echo -n "Testing file $file ${system_specific:+($system_name specific) }... " # The hoops we are jumping through here, with changing directory are @@ -210,20 +204,13 @@ function test_file() { # We disable the exit-on-error here, so that we can catch the return # code. set +e - eval "cd \"$fish_dir\" && \"./$fish_leaf\" $test_args" \ + run_rc "cd \"$fish_dir\" && \"./$fish_leaf\" $test_args" \ 2> "$test_stderr" \ < /dev/null \ - | ${filter[*]} \ + | filter "$grep_stdout" \ > "$test_stdout" - rc="$?" set -e - if [ "$rc" != '0' ] ; then - # Write the return code on to the end of the stderr, so that it can be - # checked like anything else. - echo "RC: $rc" >> "${test_stderr}" - fi - # If the wanted output files are not present, they are assumed empty. if [ ! -f "$want_stdout" ] ; then want_stdout="$empty" @@ -238,9 +225,9 @@ function test_file() { # However, fish will also have helpfully translated the home directory # into '~/' in the error report. Consequently, we need to perform a # small fix-up so that we can replace the string sanely. - xdg_config_in_home="$XDG_CONFIG_HOME" - if [ "${xdg_config_in_home:0:${#HOME}}" = "${HOME}" ] ; then - xdg_config_in_home="~/${xdg_config_in_home:${#HOME}+1}" + xdg_config_in_home="${XDG_CONFIG_HOME#$HOME}" + if [ "${#xdg_config_in_home}" -lt "${#XDG_CONFIG_HOME}" ]; then + xdg_config_in_home="~$xdg_config_in_home" fi # 'sed -i' (inplace) has different syntax on BSD and GNU versions of # the tool, so cannot be used here, hence we write to a separate file, @@ -258,20 +245,20 @@ function test_file() { if [ "$out_status" = '0' ] && \ [ "$err_status" = '0' ] ; then - say green "ok" + say "$term_green" "ok" # clean up tmp files rm -f "${test_stdout}" "${test_stderr}" "${empty}" rc=0 else - say red "fail" - say blue "$test_args_literal" | sed 's/^/ /' + say "$term_red" "fail" + say "$term_blue" "$test_args_literal" | sed 's/^/ /' if [ "$out_status" != '0' ] ; then - say yellow "Output differs for file $file. Diff follows:" + say "$term_yellow" "Output differs for file $file. Diff follows:" "$difftool" -u "${test_stdout}" "${want_stdout}" fi if [ "$err_status" != '0' ] ; then - say yellow "Error output differs for file $file. Diff follows:" + say "$term_yellow" "Error output differs for file $file. Diff follows:" "$difftool" -u "${test_stderr}" "${want_stderr}" fi rc=1 @@ -312,11 +299,11 @@ if command -v tput >/dev/null 2>&1; then term_reset="$(tput sgr0)" fi -say cyan "Testing shell invocation functionality" +say "$term_cyan" "Testing shell invocation functionality" passed=0 failed=0 -for file in ${files_to_test[*]} ; do +for file in invocation/*.invoke; do if ! test_file "$file" ; then failed=$(( failed + 1 )) else From fc5e8f9fecda8cfa1bb88ec2119ef836321485c6 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 7 Jan 2019 21:27:32 +0100 Subject: [PATCH 110/439] Revert "tests/invocation.sh: Port to sh (from bash)" This reverts commit 9aa8740c362540347ad2979cbe7975e9545d5bbc, which broke on macOS. If anyone wants to try, feel free to do so! --- tests/invocation.sh | 93 ++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/tests/invocation.sh b/tests/invocation.sh index 45e04d6ee..44fcc7ba3 100755 --- a/tests/invocation.sh +++ b/tests/invocation.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash ## # Test that the invocation of the fish executable works as we hope. # @@ -57,6 +57,13 @@ # Errors will be fatal set -e +# If any command in the pipeline fails report the rc of the first fail. +set -o pipefail + +# If nothing matches a glob expansion, return nothing (not the glob +# itself) +shopt -s nullglob + # The directory this script is in (as everything is relative to here) here="$(cd "$(dirname "$0")" && pwd -P)" cd "$here" @@ -64,6 +71,9 @@ cd "$here" # The temporary directory to use temp_dir="$here/../test" +# The files we're going to execute are in the 'invocation' directory. +files_to_test=($(echo invocation/*.invoke)) + # The fish binary we are testing - for manual testing, may be overridden fish_exe="${fish_exe:-../test/root/bin/fish}" fish_dir="$(dirname "${fish_exe}")" @@ -79,7 +89,7 @@ system_name="$(uname -s)" # Check whether we have the 'colordiff' tool - if not, we'll revert to # boring regular 'diff'. -if command -v colordiff >/dev/null 2>&1; then +if [ "$(type -t colordiff)" != '' ] ; then difftool='colordiff' else difftool='diff' @@ -89,7 +99,7 @@ fi ## # Set variables to known values so that they will not affect the # execution of the test. -clean_environment() { +function clean_environment() { # Reset the terminal variables to a known type. export TERM=xterm @@ -109,41 +119,27 @@ clean_environment() { ## # Fail completely :-( -fail() { - say "$term_red" "FAIL: $*" >&2 +function fail() { + say red "FAIL: $*" >&2 exit 1 } ## # Coloured output -# -# Use like `say "$term_green" "message". -say() { - echo "$1$2$term_reset" +function say() { + local color_name="$1" + local msg="$2" + local color_var="term_${color_name}" + local color="${!color_var}" + + echo "$color$msg$term_reset" } -run_rc() { - # Write the return code on to the end of the stderr, so that it can be - # checked like anything else. - eval "$*" || echo "RC: $?" >&2 -} - -filter() { - # In some cases we want to check only a part of the output. - # For those we filter the output through grep'd matches. - if [ -f "$1" ] ; then - # grep '-o', '-E' and '-f' are supported by the tools in modern GNU - # environments, and on OS X. - grep -oE -f "$1" - else - cat - fi -} ## # Actual testing of a .invoke file. -test_file() { +function test_file() { local file="$1" local dir="$(dirname "$file")" local base="$(basename "$file" .invoke)" @@ -154,7 +150,7 @@ test_file() { local grep_stdout="${dir}/${base}.grep" local want_stderr="${dir}/${base}.err" local empty="${dir}/${base}.empty" - local filter + local -a filter local rc=0 local test_args_literal local test_args @@ -195,6 +191,16 @@ test_file() { rm -f "${temp_dir}/home/fish/config.fish" fi + # In some cases we want to check only a part of the output. + # For those we filter the output through grep'd matches. + if [ -f "$grep_stdout" ] ; then + # grep '-o', '-E' and '-f' are supported by the tools in modern GNU + # environments, and on OS X. + filter=('grep' '-o' '-E' '-f' "$grep_stdout") + else + filter=('cat') + fi + echo -n "Testing file $file ${system_specific:+($system_name specific) }... " # The hoops we are jumping through here, with changing directory are @@ -204,13 +210,20 @@ test_file() { # We disable the exit-on-error here, so that we can catch the return # code. set +e - run_rc "cd \"$fish_dir\" && \"./$fish_leaf\" $test_args" \ + eval "cd \"$fish_dir\" && \"./$fish_leaf\" $test_args" \ 2> "$test_stderr" \ < /dev/null \ - | filter "$grep_stdout" \ + | ${filter[*]} \ > "$test_stdout" + rc="$?" set -e + if [ "$rc" != '0' ] ; then + # Write the return code on to the end of the stderr, so that it can be + # checked like anything else. + echo "RC: $rc" >> "${test_stderr}" + fi + # If the wanted output files are not present, they are assumed empty. if [ ! -f "$want_stdout" ] ; then want_stdout="$empty" @@ -225,9 +238,9 @@ test_file() { # However, fish will also have helpfully translated the home directory # into '~/' in the error report. Consequently, we need to perform a # small fix-up so that we can replace the string sanely. - xdg_config_in_home="${XDG_CONFIG_HOME#$HOME}" - if [ "${#xdg_config_in_home}" -lt "${#XDG_CONFIG_HOME}" ]; then - xdg_config_in_home="~$xdg_config_in_home" + xdg_config_in_home="$XDG_CONFIG_HOME" + if [ "${xdg_config_in_home:0:${#HOME}}" = "${HOME}" ] ; then + xdg_config_in_home="~/${xdg_config_in_home:${#HOME}+1}" fi # 'sed -i' (inplace) has different syntax on BSD and GNU versions of # the tool, so cannot be used here, hence we write to a separate file, @@ -245,20 +258,20 @@ test_file() { if [ "$out_status" = '0' ] && \ [ "$err_status" = '0' ] ; then - say "$term_green" "ok" + say green "ok" # clean up tmp files rm -f "${test_stdout}" "${test_stderr}" "${empty}" rc=0 else - say "$term_red" "fail" - say "$term_blue" "$test_args_literal" | sed 's/^/ /' + say red "fail" + say blue "$test_args_literal" | sed 's/^/ /' if [ "$out_status" != '0' ] ; then - say "$term_yellow" "Output differs for file $file. Diff follows:" + say yellow "Output differs for file $file. Diff follows:" "$difftool" -u "${test_stdout}" "${want_stdout}" fi if [ "$err_status" != '0' ] ; then - say "$term_yellow" "Error output differs for file $file. Diff follows:" + say yellow "Error output differs for file $file. Diff follows:" "$difftool" -u "${test_stderr}" "${want_stderr}" fi rc=1 @@ -299,11 +312,11 @@ if command -v tput >/dev/null 2>&1; then term_reset="$(tput sgr0)" fi -say "$term_cyan" "Testing shell invocation functionality" +say cyan "Testing shell invocation functionality" passed=0 failed=0 -for file in invocation/*.invoke; do +for file in ${files_to_test[*]} ; do if ! test_file "$file" ; then failed=$(( failed + 1 )) else From 1f897d2c4318a78976bcfb81d668f65ef2d909f0 Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 8 Jan 2019 14:50:08 +0800 Subject: [PATCH 111/439] debian packaging: recommend python3 or python2 Closes #5492. --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 8612d6192..d098a606d 100644 --- a/debian/control +++ b/debian/control @@ -22,7 +22,7 @@ Description: friendly interactive shell Package: fish-common Architecture: all Depends: ${misc:Depends} -Recommends: fish, python (>=2.6) +Recommends: fish, python3 (>= 3.3) | python (>=2.7) Suggests: xdg-utils Replaces: fish (<= 2.1.1.dfsg-2) Description: friendly interactive shell (architecture-independent files) From 827bce6c8824dd4275d2af79b3b4de32627e3295 Mon Sep 17 00:00:00 2001 From: John McKay Date: Sun, 6 Jan 2019 04:00:03 +0000 Subject: [PATCH 112/439] Use mandoc when nroff not available mandoc users do not need to install nroff to be able to format and view manual pages. If both nroff and mandoc cannot be found it will show an error. --- CHANGELOG.md | 1 + share/functions/__fish_print_help.fish | 36 ++++++++++++++++++-------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 708f924fc..de6b12fc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - nothing yet... - Lots of improvements to completions. - fish_clipboard_* now supports wayland by means of [wl-clipboard](https://github.com/bugaevc/wl-clipboard). +- mandoc can now be used to format the output from `--help` if nroff is not installed --- diff --git a/share/functions/__fish_print_help.fish b/share/functions/__fish_print_help.fish index f968a0b8d..a02ea8ae3 100644 --- a/share/functions/__fish_print_help.fish +++ b/share/functions/__fish_print_help.fish @@ -10,21 +10,35 @@ function __fish_print_help --description "Print help message for the specified f # Render help output, save output into the variable 'help' set -l help - set -l cols $COLUMNS - set -l rLL - if test -n "$cols" - set cols (math $cols - 4) # leave a bit of space on the right - set rLL -rLL=$cols[1]n + set -l format + set -l cols + if test -n "$COLUMNS" + set cols (math $COLUMNS - 4) # leave a bit of space on the right end - set -lx GROFF_TMAC_PATH $__fish_data_dir/groff - set -l mfish - if test -e $GROFF_TMAC_PATH/fish.tmac - set mfish -mfish + + # Pick which command we are using to render output or fail if none + if command -qs nroff + set format nroff -c -man -t + if test -e $__fish_data_dir/groff/fish.tmac + set -a format -M$__fish_data_dir/groff -mfish + end + if test -n "$cols" + set -a format -rLL={$cols}n + end + else if command -qs mandoc + set format mandoc -c + if test -n "$cols" + set -a format -O width=$cols + end + else + echo fish: (_ "Cannot format help; no parser found") + return 1 end + if test -e "$__fish_data_dir/man/man1/$item.1" - set help (nroff -c -man $mfish -t $rLL "$__fish_data_dir/man/man1/$item.1" 2>/dev/null) + set help ($format "$__fish_data_dir/man/man1/$item.1" 2>/dev/null) else if test -e "$__fish_data_dir/man/man1/$item.1.gz" - set help (gunzip -c "$__fish_data_dir/man/man1/$item.1.gz" 2>/dev/null | nroff -c -man $mfish -t $rLL 2>/dev/null) + set help (gunzip -c "$__fish_data_dir/man/man1/$item.1.gz" 2>/dev/null | $format 2>/dev/null) end # The original implementation trimmed off the top 5 lines and bottom 3 lines From f553cedff15d82e6b732a8aa5a01affda16357a6 Mon Sep 17 00:00:00 2001 From: John McKay Date: Sun, 6 Jan 2019 17:16:44 +0000 Subject: [PATCH 113/439] README: update to reflect mandoc support --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b134234de..9a70cfa83 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Running fish requires: The following optional features also have specific requirements: -* builtin commands that have the `--help` option or print usage messages require `nroff` and `ul` +* builtin commands that have the `--help` option or print usage messages require `ul` and either `nroff` or `mandoc` for display * automated completion generation from manual pages requires Python (2.7+ or 3.3+) and possibly the `backports.lzma` module for Python 2.7 * the `fish_config` web configuration tool requires Python (2.7+ or 3.3 +) and a web browser From 5612f47e33f13496f94eed804b0fcb18bbafe036 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 8 Jan 2019 22:44:59 +0100 Subject: [PATCH 114/439] Revert "Revert "tests/invocation.sh: Port to sh (from bash)"" The one thing I was missing: `echo -n` isn't POSIX. In practice, it appears the only shell to encounter this is macOS' crusty old bash in sh-mode. Just replace it with `touch`. This reverts commit fc5e8f9fecda8cfa1bb88ec2119ef836321485c6. --- tests/invocation.sh | 95 +++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/tests/invocation.sh b/tests/invocation.sh index 44fcc7ba3..08fe332aa 100755 --- a/tests/invocation.sh +++ b/tests/invocation.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh ## # Test that the invocation of the fish executable works as we hope. # @@ -57,13 +57,6 @@ # Errors will be fatal set -e -# If any command in the pipeline fails report the rc of the first fail. -set -o pipefail - -# If nothing matches a glob expansion, return nothing (not the glob -# itself) -shopt -s nullglob - # The directory this script is in (as everything is relative to here) here="$(cd "$(dirname "$0")" && pwd -P)" cd "$here" @@ -71,9 +64,6 @@ cd "$here" # The temporary directory to use temp_dir="$here/../test" -# The files we're going to execute are in the 'invocation' directory. -files_to_test=($(echo invocation/*.invoke)) - # The fish binary we are testing - for manual testing, may be overridden fish_exe="${fish_exe:-../test/root/bin/fish}" fish_dir="$(dirname "${fish_exe}")" @@ -89,7 +79,7 @@ system_name="$(uname -s)" # Check whether we have the 'colordiff' tool - if not, we'll revert to # boring regular 'diff'. -if [ "$(type -t colordiff)" != '' ] ; then +if command -v colordiff >/dev/null 2>&1; then difftool='colordiff' else difftool='diff' @@ -99,7 +89,7 @@ fi ## # Set variables to known values so that they will not affect the # execution of the test. -function clean_environment() { +clean_environment() { # Reset the terminal variables to a known type. export TERM=xterm @@ -119,27 +109,41 @@ function clean_environment() { ## # Fail completely :-( -function fail() { - say red "FAIL: $*" >&2 +fail() { + say "$term_red" "FAIL: $*" >&2 exit 1 } ## # Coloured output -function say() { - local color_name="$1" - local msg="$2" - local color_var="term_${color_name}" - local color="${!color_var}" - - echo "$color$msg$term_reset" +# +# Use like `say "$term_green" "message". +say() { + echo "$1$2$term_reset" } +run_rc() { + # Write the return code on to the end of the stderr, so that it can be + # checked like anything else. + eval "$*" || echo "RC: $?" >&2 +} + +filter() { + # In some cases we want to check only a part of the output. + # For those we filter the output through grep'd matches. + if [ -f "$1" ] ; then + # grep '-o', '-E' and '-f' are supported by the tools in modern GNU + # environments, and on OS X. + grep -oE -f "$1" + else + cat + fi +} ## # Actual testing of a .invoke file. -function test_file() { +test_file() { local file="$1" local dir="$(dirname "$file")" local base="$(basename "$file" .invoke)" @@ -150,7 +154,7 @@ function test_file() { local grep_stdout="${dir}/${base}.grep" local want_stderr="${dir}/${base}.err" local empty="${dir}/${base}.empty" - local -a filter + local filter local rc=0 local test_args_literal local test_args @@ -182,7 +186,7 @@ function test_file() { fi # Create an empty file so that we can compare against it if needed - echo -n > "${empty}" + touch "${empty}" # If they supplied a configuration file, we create it here if [ -f "$test_config" ] ; then @@ -191,16 +195,6 @@ function test_file() { rm -f "${temp_dir}/home/fish/config.fish" fi - # In some cases we want to check only a part of the output. - # For those we filter the output through grep'd matches. - if [ -f "$grep_stdout" ] ; then - # grep '-o', '-E' and '-f' are supported by the tools in modern GNU - # environments, and on OS X. - filter=('grep' '-o' '-E' '-f' "$grep_stdout") - else - filter=('cat') - fi - echo -n "Testing file $file ${system_specific:+($system_name specific) }... " # The hoops we are jumping through here, with changing directory are @@ -210,20 +204,13 @@ function test_file() { # We disable the exit-on-error here, so that we can catch the return # code. set +e - eval "cd \"$fish_dir\" && \"./$fish_leaf\" $test_args" \ + run_rc "cd \"$fish_dir\" && \"./$fish_leaf\" $test_args" \ 2> "$test_stderr" \ < /dev/null \ - | ${filter[*]} \ + | filter "$grep_stdout" \ > "$test_stdout" - rc="$?" set -e - if [ "$rc" != '0' ] ; then - # Write the return code on to the end of the stderr, so that it can be - # checked like anything else. - echo "RC: $rc" >> "${test_stderr}" - fi - # If the wanted output files are not present, they are assumed empty. if [ ! -f "$want_stdout" ] ; then want_stdout="$empty" @@ -238,9 +225,9 @@ function test_file() { # However, fish will also have helpfully translated the home directory # into '~/' in the error report. Consequently, we need to perform a # small fix-up so that we can replace the string sanely. - xdg_config_in_home="$XDG_CONFIG_HOME" - if [ "${xdg_config_in_home:0:${#HOME}}" = "${HOME}" ] ; then - xdg_config_in_home="~/${xdg_config_in_home:${#HOME}+1}" + xdg_config_in_home="${XDG_CONFIG_HOME#$HOME}" + if [ "${#xdg_config_in_home}" -lt "${#XDG_CONFIG_HOME}" ]; then + xdg_config_in_home="~$xdg_config_in_home" fi # 'sed -i' (inplace) has different syntax on BSD and GNU versions of # the tool, so cannot be used here, hence we write to a separate file, @@ -258,20 +245,20 @@ function test_file() { if [ "$out_status" = '0' ] && \ [ "$err_status" = '0' ] ; then - say green "ok" + say "$term_green" "ok" # clean up tmp files rm -f "${test_stdout}" "${test_stderr}" "${empty}" rc=0 else - say red "fail" - say blue "$test_args_literal" | sed 's/^/ /' + say "$term_red" "fail" + say "$term_blue" "$test_args_literal" | sed 's/^/ /' if [ "$out_status" != '0' ] ; then - say yellow "Output differs for file $file. Diff follows:" + say "$term_yellow" "Output differs for file $file. Diff follows:" "$difftool" -u "${test_stdout}" "${want_stdout}" fi if [ "$err_status" != '0' ] ; then - say yellow "Error output differs for file $file. Diff follows:" + say "$term_yellow" "Error output differs for file $file. Diff follows:" "$difftool" -u "${test_stderr}" "${want_stderr}" fi rc=1 @@ -312,11 +299,11 @@ if command -v tput >/dev/null 2>&1; then term_reset="$(tput sgr0)" fi -say cyan "Testing shell invocation functionality" +say "$term_cyan" "Testing shell invocation functionality" passed=0 failed=0 -for file in ${files_to_test[*]} ; do +for file in invocation/*.invoke; do if ! test_file "$file" ; then failed=$(( failed + 1 )) else From b4365e972acc3211b85e010480dbb0ac391834ce Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 9 Jan 2019 22:30:07 +0800 Subject: [PATCH 115/439] status completions: tidy, add current-command, fish-path and aliases --- share/completions/status.fish | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/share/completions/status.fish b/share/completions/status.fish index 1a392614c..5f14db569 100644 --- a/share/completions/status.fish +++ b/share/completions/status.fish @@ -1,5 +1,5 @@ # Note that when a completion file is sourced a new block scope is created so `set -l` works. -set -l __fish_status_all_commands is-login is-interactive is-block is-breakpoint is-command-substitution is-no-job-control is-interactive-job-control is-full-job-control current-filename current-line-number print-stack-trace job-control features test-feature +set -l __fish_status_all_commands current-command current-filename current-function current-line-number features filename fish-path function is-block is-breakpoint is-command-substitution is-full-job-control is-interactive is-interactive-job-control is-login is-no-job-control job-control line-number print-stack-trace stack-trace test-feature # These are the recognized flags. complete -c status -s h -l help -d "Display help and exit" @@ -15,9 +15,19 @@ complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_com complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-full-job-control -d "Test if all new jobs are put under job control" # The subcommands that are not "is-something" which don't change the fish state. +complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a current-command -d "Print the name of the currently running command or function" complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a current-filename -d "Print the filename of the currently running script" +complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a filename -d "Print the filename of the currently running script" +complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a current-function -d "Print the name of the current function" +complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a function -d "Print the name of the current function" complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a current-line-number -d "Print the line number of the currently running script" +complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a line-number -d "Print the line number of the currently running script" complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a print-stack-trace -d "Print a list of all function calls leading up to running the current command" +complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a stack-trace -d "Print a list of all function calls leading up to running the current command" +complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a features -d "List all feature flags" +complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a test-feature -d "Test if a feature flag is enabled" +complete -f -c status -n "__fish_seen_subcommand_from test-feature" -a '(status features)' +complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a fish-path -d "Print the path to the current instance of fish" # The job-control command changes fish state. complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a job-control -d "Set which jobs are under job control" @@ -25,6 +35,3 @@ complete -f -c status -n "__fish_seen_subcommand_from job-control" -a full -d "S complete -f -c status -n "__fish_seen_subcommand_from job-control" -a interactive -d "Set only interactive jobs under job control" complete -f -c status -n "__fish_seen_subcommand_from job-control" -a none -d "Set no jobs under job control" -complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a features -d "List all feature flags" -complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a test-feature -d "Test if a feature flag is enabled" -complete -f -c status -n "__fish_seen_subcommand_from test-feature" -a '(status features)' From 4a8db53bcf59f13d5c5fc9f7cc6c5b52e0d9ba9a Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 9 Jan 2019 22:34:43 +0800 Subject: [PATCH 116/439] status docs: add current-command Note deprecation of $_. [ci skip] --- doc_src/index.hdr.in | 4 ++-- doc_src/status.txt | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 7f2fe7efc..6acfa4a22 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -677,7 +677,7 @@ set index 2 set letters a b c d echo $letters[$index] # returns 'b' \endfish -However using variables as indices for command substitution is currently not supported, so +However using variables as indices for command substitution is currently not supported, so \fish echo (seq 5)[$index] # This won't work @@ -923,7 +923,7 @@ The user can change the settings of `fish` by changing the values of certain var `fish` also sends additional information to the user through the values of certain environment variables. The user cannot change the values of most of these variables. -- `_`, the name of the currently running command. +- `_`, the name of the currently running command (though this is deprecated, and the use of `status current-command` is preferred). - `argv`, an array of arguments to the shell or function. `argv` is only defined when inside a function call, or if fish was invoked with a list of arguments, like `fish myscript.fish foo bar`. This variable can be changed by the user. diff --git a/doc_src/status.txt b/doc_src/status.txt index b69996d57..c2f99f800 100644 --- a/doc_src/status.txt +++ b/doc_src/status.txt @@ -11,6 +11,7 @@ status is-command-substitution status is-no-job-control status is-full-job-control status is-interactive-job-control +status current-command status filename status fish-path status function @@ -43,6 +44,8 @@ The following operations (sub-commands) are available: - `is-no-job-control` returns 0 if no job control is enabled. Also `--is-no-job-control` (no short flag). +- `current-command` prints the name of the currently-running function or command, like the deprecated `_` variable. + - `filename` prints the filename of the currently running script. Also `current-filename`, `-f` or `--current-filename`. - `fish-path` prints the absolute path to the currently executing instance of fish. From a6fa237db27d25c124e7d1be82bc30360eb7086d Mon Sep 17 00:00:00 2001 From: John McKay <34872500+McKayJT@users.noreply.github.com> Date: Wed, 9 Jan 2019 23:07:09 +0000 Subject: [PATCH 117/439] print --help to stdout like other builtins (#5495) --- src/builtin_string.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index bbc7e40c0..8e7777111 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -1305,7 +1305,7 @@ int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } if (wcscmp(argv[1], L"-h") == 0 || wcscmp(argv[1], L"--help") == 0) { - builtin_print_help(parser, streams, L"string", streams.err); + builtin_print_help(parser, streams, L"string", streams.out); return STATUS_CMD_OK; } From c15a702f18d5cee8a4e54c486f90467e8729a62e Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Wed, 9 Jan 2019 15:23:55 -0800 Subject: [PATCH 118/439] Revert "Fix unsafe locale usage in `wcstod_l` fallback" This reverts commit 3444e1db18bcd1b9f7425a31895298c3864d294c. The reverted commit broke tests on the Mac. --- src/fallback.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fallback.cpp b/src/fallback.cpp index bcfc4d926..a0db093a9 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -394,13 +394,13 @@ int flock(int fd, int op) { // For platforms without wcstod_l C extension, wrap wcstod after changing the // thread-specific locale. double fish_compat::wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc) { - // Create and use a new, thread-specific locale - locale_t locale = newlocale(LC_NUMERIC, "C", nullptr); - locale_t prev_locale = uselocale(locale); + char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL)); + // Yes, this is hardcoded to use the "C" locale. + // That's the only thing we need, and uselocale(loc) broke in my testing. + setlocale(LC_NUMERIC, "C"); double ret = wcstod(enptr, endptr); - // Restore the old locale before freeing the locale we created and are still using - uselocale(prev_locale); - freelocale(locale); + setlocale(LC_NUMERIC, saved_locale); + free(saved_locale); return ret; } #endif // defined(wcstod_l) From 9743cd77d3e66f9780d828fa28645ea761329f3a Mon Sep 17 00:00:00 2001 From: Versus Date: Tue, 8 Jan 2019 19:15:45 +0300 Subject: [PATCH 119/439] yaourt: recognize *.pkg.tar as valid package extension --- share/completions/yaourt.fish | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/share/completions/yaourt.fish b/share/completions/yaourt.fish index 141623c9b..9428df99a 100644 --- a/share/completions/yaourt.fish +++ b/share/completions/yaourt.fish @@ -172,10 +172,9 @@ complete -c $progname -n "$files" -l machinereadable -d 'Show in machine readabl # 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' +# In practice, it's going to be tar, 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,.gz\})' -d 'Package file' ## Yaourt only stuff # Clean options From 89d77df6583107115062c669845a4076566868a8 Mon Sep 17 00:00:00 2001 From: Varun Arora Date: Tue, 8 Jan 2019 22:12:17 -0600 Subject: [PATCH 120/439] add missing tmux new-session flags to tmux completion script --- share/completions/tmux.fish | 41 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/share/completions/tmux.fish b/share/completions/tmux.fish index 9a5e3569e..8049d0c4f 100644 --- a/share/completions/tmux.fish +++ b/share/completions/tmux.fish @@ -1,20 +1,20 @@ function __fish_tmux_sessions -d 'available sessions' - tmux list-sessions -F "#S #{session_windows} windows created: #{session_created_string} [#{session_width}x#{session_height}]#{session_attached}" | sed 's/0$//;s/1$/ (attached)/' 2>/dev/null + tmux list-sessions -F "#S #{session_windows} windows created: #{session_created_string} [#{session_width}x#{session_height}]#{session_attached}" | sed 's/0$//;s/1$/ (attached)/' 2>/dev/null end function __fish_tmux_clients -d 'connected clients' - tmux list-clients -F "#{client_tty} #S: Created: #{client_created_string} [#{client_width}x#{client_height} #{client_termname}]" 2>/dev/null + tmux list-clients -F "#{client_tty} #S: Created: #{client_created_string} [#{client_width}x#{client_height} #{client_termname}]" 2>/dev/null end function __fish_tmux_panes -d 'window panes' - #fully qualified pane names - tmux list-panes -F '#S:#W.#P session:window.pane' 2>/dev/null + #fully qualified pane names + tmux list-panes -F '#S:#W.#P session:window.pane' 2>/dev/null - #panes by themselves - tmux list-panes -F '#P pane' 2>/dev/null + #panes by themselves + tmux list-panes -F '#P pane' 2>/dev/null - #windows by themselves - tmux list-panes -F '#W window' 2>/dev/null + #windows by themselves + tmux list-panes -F '#W window' 2>/dev/null end #don't allow dirs in the completion list... @@ -24,17 +24,17 @@ complete -c tmux -x #these do not require parameters complete -c tmux -n '__fish_use_subcommand' -s 2 -d 'Force tmux to assume the terminal supports 256 colours' complete -c tmux -n '__fish_use_subcommand' -s 8 -d 'Like -2, but indicates that the terminal supports 88 colours' -complete -c tmux -n '__fish_use_subcommand' -s l -d 'Behave as a login shell' -complete -c tmux -n '__fish_use_subcommand' -s q -d 'Set the quiet server option' -complete -c tmux -n '__fish_use_subcommand' -s u -d 'Flag explicitly informs tmux that UTF-8 is supported' -complete -c tmux -n '__fish_use_subcommand' -s v -d 'Request verbose logging' -complete -c tmux -n '__fish_use_subcommand' -s V -d 'Report the tmux version' +complete -c tmux -n '__fish_use_subcommand' -s l -d 'Behave as a login shell' +complete -c tmux -n '__fish_use_subcommand' -s q -d 'Set the quiet server option' +complete -c tmux -n '__fish_use_subcommand' -s u -d 'Flag explicitly informs tmux that UTF-8 is supported' +complete -c tmux -n '__fish_use_subcommand' -s v -d 'Request verbose logging' +complete -c tmux -n '__fish_use_subcommand' -s V -d 'Report the tmux version' #these require parameters -complete -c tmux -n '__fish_use_subcommand' -xs c -d 'Execute command using the default shell' -complete -c tmux -n '__fish_use_subcommand' -rs f -d 'Alternate config file' -complete -c tmux -n '__fish_use_subcommand' -rs L -d 'Specify the name of the server socket to use' -complete -c tmux -n '__fish_use_subcommand' -rs S -d 'Full path to server socket. If set, -L is ignored.' +complete -c tmux -n '__fish_use_subcommand' -xs c -d 'Execute command using the default shell' +complete -c tmux -n '__fish_use_subcommand' -rs f -d 'Alternate config file' +complete -c tmux -n '__fish_use_subcommand' -rs L -d 'Specify the name of the server socket to use' +complete -c tmux -n '__fish_use_subcommand' -rs S -d 'Full path to server socket. If set, -L is ignored.' ############### End: Front Flags ############### ############### Begin: Clients and Sessions ############### @@ -75,8 +75,15 @@ complete -c tmux -n '__fish_use_subcommand' -a $locks -d 'lock session' complete -c tmux -n '__fish_use_subcommand' -a $new -d 'create a new session with name session-name' complete -c tmux -n "__fish_seen_subcommand_from $new" -s d -d "don't attach to current window" +complete -c tmux -n "__fish_seen_subcommand_from $new" -s A -d "attach to existing session if session-name already exists" +complete -c tmux -n "__fish_seen_subcommand_from $new" -s D -d "if -A is specified, detach other clients attached to the session if it exists" +complete -c tmux -n "__fish_seen_subcommand_from $new" -s P -d "print information about the new session after creation" +complete -c tmux -n "__fish_seen_subcommand_from $new" -s E -d "don't apply update-environment option" +complete -c tmux -n "__fish_seen_subcommand_from $new" -xs c -d 'start-directory' +complete -c tmux -n "__fish_seen_subcommand_from $new" -xs F -d 'format' complete -c tmux -n "__fish_seen_subcommand_from $new" -xs n -d 'window-name' complete -c tmux -n "__fish_seen_subcommand_from $new" -xs s -d 'session-name' +complete -c tmux -n "__fish_seen_subcommand_from $new" -xs t -d 'group-name' complete -c tmux -n "__fish_seen_subcommand_from $new" -xs x -d 'width' complete -c tmux -n "__fish_seen_subcommand_from $new" -xs y -d 'height' From d518b01281dfab6a5c7369a763328c8a07b5c337 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 5 Jan 2019 21:52:26 +0800 Subject: [PATCH 121/439] fish_tests.cpp: mock the home directory Removes the dependency on the current user's home directory, instead overriding it to be within the current hierarchy. Fixes the tests on Debian buildd, where the home directory is deliberately unwriteable to pick up errors in builds. --- src/fish_tests.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index e5a3d9426..4270fd699 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -2677,11 +2677,10 @@ static void test_autosuggest_suggest_special() { err(L"mkdir failed"); // a path with a double quote } // This is to ensure tilde expansion is handled. See the `cd ~/test_autosuggest_suggest_specia` - // test below. I really dislike this since it mucks with a persons home directory. - // - // The question is how to modify the test so that tilde expansion can be made hermetic to this - // test. - if (system("mkdir -p ~/test_autosuggest_suggest_special/")) { + // test below. + // Fake out the home directory + env_set_one(L"HOME", ENV_LOCAL | ENV_EXPORT, L"test/test-home"); + if (system("mkdir -p test/test-home/test_autosuggest_suggest_special/")) { err(L"mkdir failed"); } if (system("mkdir -p test/autosuggest_test/start/unique2/unique3/multi4")) { @@ -2753,8 +2752,8 @@ static void test_autosuggest_suggest_special() { perform_one_completion_cd_test(L"cd ~haha", L"ha/", __LINE__); perform_one_completion_cd_test(L"cd ~hahaha/", L"path1/", __LINE__); + env_remove(L"HOME", ENV_LOCAL | ENV_EXPORT); popd(); - (void)system("rmdir ~/test_autosuggest_suggest_special/"); } static void perform_one_autosuggestion_should_ignore_test(const wcstring &command, long line) { From f5893ba47573f036afcdaf6a096c60f5641c6e9f Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 10 Jan 2019 21:37:08 +0800 Subject: [PATCH 122/439] debian packaging: turn on tests in Debian packages --- debian/control | 4 +++- debian/rules | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/debian/control b/debian/control index d098a606d..f5624cec4 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,9 @@ Section: shells Priority: extra Maintainer: ridiculous_fish Uploaders: David Adam -Build-Depends: debhelper (>= 9.0.0), libncurses5-dev, cmake3 (>= 3.2.0) | cmake (>= 3.2.0), gettext +Build-Depends: debhelper (>= 9.0.0), libncurses5-dev, cmake3 (>= 3.2.0) | cmake (>= 3.2.0), gettext, +# Test dependencies + expect, locales-all # When libpcre2-dev is available on all supported Debian versions, add a dependency on that. Standards-Version: 3.9.4 Homepage: https://fishshell.com/ diff --git a/debian/rules b/debian/rules index 7044fb86d..6c0eafbd8 100755 --- a/debian/rules +++ b/debian/rules @@ -18,6 +18,3 @@ override_dh_installdocs: # Consider transitioning https://wiki.debian.org/DebugPackage override_dh_strip: dh_strip --dbg-package=fish-dbg - -# Don't run tests; they don't work until fish is installed -override_dh_auto_test: From b402b635a9f7466616cef6e2b9cfd55a6e7068c5 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 10 Jan 2019 19:43:35 -0600 Subject: [PATCH 123/439] Drop hard requirement on explicit `-lpthread` support Closes #5512 --- cmake/ConfigureChecks.cmake | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index f3d82ce81..ef48f90ae 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -28,7 +28,12 @@ set(THREADS_PREFER_PTHREAD_FLAG ON) IF(CMAKE_VERSION VERSION_LESS 3.4.0) ENABLE_LANGUAGE(C) ENDIF() -FIND_PACKAGE(Threads REQUIRED) +# Don't set pthreads to required. Either we're on a platform where explict +# linking with -lpthread is the norm (e.g. Linux) and it'll be found, or we're +# on a platform that include pthreads by default (e.g. BSD, macOS) where this +# won't find anything, or we're on a road-much-less-traveled OS where the user +# can figure out what's wrong without a hard error here. See #5512. +FIND_PACKAGE(Threads) # Detect WSL. Does not match against native Windows/WIN32. if (CMAKE_HOST_SYSTEM_VERSION MATCHES ".*-Microsoft") From 2bb53f7253765f652b21547a9aeb001b909aa05b Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 10 Jan 2019 19:51:12 -0600 Subject: [PATCH 124/439] Fix `locale_t` under macOS 10.10 `xlocale.h` is not available on Linux, so we can't just universally include it. `HAVE_XLOCALE_H` was already being tested/set in the CMake script as a possible requirement for `wcstod_l` support, this just adds it to `config_cmake_h.in` and uses it in `wutil.h` to gate the include. --- config_cmake.h.in | 3 +++ src/wutil.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/config_cmake.h.in b/config_cmake.h.in index 162f76de0..2c6fde27d 100644 --- a/config_cmake.h.in +++ b/config_cmake.h.in @@ -156,6 +156,9 @@ /* The size of wchar_t in bits. */ #define WCHAR_T_BITS ${WCHAR_T_BITS} +/* Define if xlocale.h is required for locale_t or wide character support */ +#cmakedefine HAVE_XLOCALE_H 1 + /* Enable large inode numbers on Mac OS X 10.5. */ #ifndef _DARWIN_USE_64_BIT_INODE # define _DARWIN_USE_64_BIT_INODE 1 diff --git a/src/wutil.h b/src/wutil.h index 28a8540d1..661e82d05 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -10,6 +10,10 @@ #include #include +#ifdef HAVE_XLOCALE_H +#include +#endif + #include "common.h" #include "maybe.h" From 16a94db7020eb1bf0e1c9637923401cda9bb125e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 10 Jan 2019 19:53:33 -0600 Subject: [PATCH 125/439] Check for `-Werror=unguarded_availability` support before forcing it The compiler flag `-Werror=unguarded_availability` was hard-coded for macOS, but is not supported by GCC on macOS 10.10 (Yosemite). Test for support with CHECK_CXX_COMPILER_FLAG before forcing it. --- cmake/ConfigureChecks.cmake | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index ef48f90ae..c53be74ae 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -5,8 +5,11 @@ # This is the case for at least Cygwin and Newlib. LIST(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE=1) IF(APPLE) - # 10.7+ only. - LIST(APPEND CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "-Werror=unguarded-availability") + INCLUDE(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-Werror=unguarded-availability" REQUIRES_UNGUARDED_AVAILABILITY) + IF(REQUIRES_UNGUARDED_AVAILABILITY) + LIST(APPEND CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "-Werror=unguarded-availability") + ENDIF() ENDIF() # Try using CMake's own logic to locate curses/ncurses From e7715eecf6a81e8971091c65eb5522e9290543f5 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 10 Jan 2019 21:48:42 -0600 Subject: [PATCH 126/439] Add regression question to GitHub issue template --- .github/ISSUE_TEMPLATE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 03b2b48a7..6b42e39e1 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -11,4 +11,7 @@ Please tell us if you tried fish without third-party customizations by executing sh -c 'env HOME=$(mktemp -d) fish' Tell us how to reproduce the problem. Including an asciinema.org recording is useful for problems that involve the visual display of fish output such as its prompt. + +If you are reporting a bug in fish 3.0, please make sure to indicate whether or not this is a +regression from fish 2.7.1. --> From 895c2c4af077ac3ead37bdecc644f194dcb9be1d Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 9 Sep 2018 01:36:21 -0700 Subject: [PATCH 127/439] Minor cleanup of parser interface --- src/fish.cpp | 4 ++-- src/fish_tests.cpp | 6 +++--- src/parser.cpp | 16 ++++++---------- src/parser.h | 17 +++++++---------- src/proc.cpp | 7 +++++++ src/proc.h | 6 ++++++ 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index c213321d9..b9857e8af 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -184,9 +184,9 @@ static void source_config_in_directory(const wcstring &dir) { const wcstring cmd = L"builtin source " + escaped_pathname; parser_t &parser = parser_t::principal_parser(); - parser.set_is_within_fish_initialization(true); + set_is_within_fish_initialization(true); parser.eval(cmd, io_chain_t(), TOP); - parser.set_is_within_fish_initialization(false); + set_is_within_fish_initialization(false); } /// Parse init files. exec_path is the path of fish executable as determined by argv[0]. diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 4270fd699..c35e3c551 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -2142,7 +2142,7 @@ static void test_is_potential_path() { /// 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) { - parser_t parser; + parser_t &parser = parser_t::principal_parser(); size_t i, count = lst.size(); wchar_t **argv = new wchar_t *[count + 3]; argv[0] = (wchar_t *)(bracket ? L"[" : L"test"); @@ -2185,7 +2185,7 @@ static bool run_test_test(int expected, const wcstring &str) { static void test_test_brackets() { // Ensure [ knows it needs a ]. - parser_t parser; + parser_t &parser = parser_t::principal_parser(); io_streams_t streams(0); null_terminated_array_t args; @@ -4389,7 +4389,7 @@ static void test_pcre2_escape() { int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv); static void run_one_string_test(const wchar_t *const *argv, int expected_rc, const wchar_t *expected_out) { - parser_t parser; + parser_t &parser = parser_t::principal_parser(); io_streams_t streams(0); streams.stdin_is_directly_redirected = false; // read from argv instead of stdin int rc = builtin_string(parser, streams, const_cast(argv)); diff --git a/src/parser.cpp b/src/parser.cpp index 4196c3dd8..fd33f58fd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -100,26 +100,22 @@ 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() = default; // Out of line destructor to enable forward declaration of parse_execution_context_t parser_t::~parser_t() = default; -static parser_t s_principal_parser; +parser_t parser_t::principal; parser_t &parser_t::principal_parser() { ASSERT_IS_MAIN_THREAD(); - return s_principal_parser; -} - -void parser_t::set_is_within_fish_initialization(bool flag) { - is_within_fish_initialization = flag; + return principal; } void parser_t::skip_all_blocks() { // Tell all blocks to skip. // This may be called from a signal handler! - s_principal_parser.cancellation_requested = true; + principal.cancellation_requested = true; } // Given a new-allocated block, push it onto our block stack, acquiring ownership @@ -399,7 +395,7 @@ void parser_t::stack_trace_internal(size_t block_idx, wcstring *buff) const { 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 { append_format(*buff, _(L"\tcalled on standard input\n")); @@ -536,7 +532,7 @@ wcstring parser_t::current_line() { if (file) { append_format(prefix, _(L"%ls (line %d): "), user_presentable_path(file).c_str(), lineno); - } else if (is_within_fish_initialization) { + } else if (is_within_fish_initialization()) { append_format(prefix, L"%ls (line %d): ", _(L"Startup"), lineno); } else { append_format(prefix, L"%ls (line %d): ", _(L"Standard input"), lineno); diff --git a/src/parser.h b/src/parser.h index 5449db970..bfeea4406 100644 --- a/src/parser.h +++ b/src/parser.h @@ -162,9 +162,7 @@ class parser_t { private: /// Indication that we should skip all blocks. - volatile sig_atomic_t cancellation_requested; - /// Indicates that we are within the process of initializing fish. - bool is_within_fish_initialization; + volatile sig_atomic_t cancellation_requested = false; /// The current execution context. std::unique_ptr execution_context; /// List of called functions, used to help prevent infinite recursion. @@ -206,6 +204,12 @@ class parser_t { /// Helper for push_block() void push_block_int(block_t *b); + /// Create a parser. + parser_t(); + + /// The main parser. + static parser_t principal; + public: /// Get the "principal" parser, whatever that is. static parser_t &principal_parser(); @@ -214,9 +218,6 @@ class parser_t { /// from signal handlers! static void skip_all_blocks(); - /// Create a parser. - parser_t(); - /// Global event blocks. event_blockage_list_t global_event_blocks; @@ -270,10 +271,6 @@ class parser_t { /// 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. - void set_is_within_fish_initialization(bool flag); - /// Pushes a new block created with the given arguments /// Returns a pointer to the block. The pointer is valid /// until the call to pop_block() diff --git a/src/proc.cpp b/src/proc.cpp index 69494c8dc..6f692fb50 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #if HAVE_TERM_H #include @@ -1289,3 +1290,9 @@ void hup_background_jobs() { } } } + +static std::atomic s_is_within_fish_initialization{false}; + +void set_is_within_fish_initialization(bool flag) { s_is_within_fish_initialization.store(flag); } + +bool is_within_fish_initialization() { return s_is_within_fish_initialization.load(); } diff --git a/src/proc.h b/src/proc.h index 2f14c89da..b48e5d215 100644 --- a/src/proc.h +++ b/src/proc.h @@ -399,6 +399,12 @@ int proc_format_status(int status); /// Wait for any process finishing. pid_t proc_wait_any(); +/// Set and get whether we are in initialization. +// 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); +bool is_within_fish_initialization(); + /// Terminate all background jobs void hup_background_jobs(); From 391af6af0ccd229d3a050ccdf13ba4260dc71990 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 9 Sep 2018 02:25:51 -0700 Subject: [PATCH 128/439] Introduce class environment_t This will be used as a base class for variable snapshots and variable stacks. --- src/autoload.cpp | 2 +- src/autoload.h | 4 ++-- src/env.cpp | 11 +++++++---- src/env.h | 17 +++++++++++++++-- src/function.cpp | 2 +- src/function.h | 2 +- src/highlight.cpp | 14 +++++++------- src/highlight.h | 8 ++++---- src/path.cpp | 6 +++--- src/path.h | 6 +++--- src/proc.cpp | 2 +- src/reader.h | 4 ++-- 12 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/autoload.cpp b/src/autoload.cpp index 6d577fc46..8614731e3 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -96,7 +96,7 @@ int autoload_t::load(const wcstring &cmd, bool reload) { 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 environment_t &vars) { auto path_var = vars.get(env_var_name); if (path_var.missing_or_empty()) return false; diff --git a/src/autoload.h b/src/autoload.h index ceb703c53..249b49c34 100644 --- a/src/autoload.h +++ b/src/autoload.h @@ -40,7 +40,7 @@ struct autoload_function_t { bool is_placeholder; }; -class env_vars_snapshot_t; +class environment_t; /// Class representing a path from which we can autoload and the autoloaded contents. class autoload_t : public lru_cache_t { @@ -88,7 +88,7 @@ class autoload_t : public lru_cache_t { int unload(const wcstring &cmd); /// Check whether the given command could be loaded, but do not load it. - bool can_load(const wcstring &cmd, const env_vars_snapshot_t &vars); + bool can_load(const wcstring &cmd, const environment_t &vars); /// Invalidates all entries. Uesd when the underlying path variable changes. void invalidate(); diff --git a/src/env.cpp b/src/env.cpp index 148e0eb71..7a9b18e80 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1589,6 +1589,8 @@ void env_set_argv(const wchar_t *const *argv) { } } +environment_t::~environment_t() = default; + env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t *const *keys) { ASSERT_IS_MAIN_THREAD(); wcstring key; @@ -1596,12 +1598,13 @@ env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t *const *keys) { key.assign(keys[i]); const auto var = env_get(key); if (var) { - vars[key] = *var; + vars[key] = std::move(*var); } } } -env_vars_snapshot_t::env_vars_snapshot_t() {} +env_vars_snapshot_t::env_vars_snapshot_t() = default; +env_vars_snapshot_t::~env_vars_snapshot_t() = default; // The "current" variables are not a snapshot at all, but instead trampoline to env_get, etc. // We identify the current snapshot based on pointer values. @@ -1610,10 +1613,10 @@ const env_vars_snapshot_t &env_vars_snapshot_t::current() { return sCurrentSnaps bool env_vars_snapshot_t::is_current() const { return this == &sCurrentSnapshot; } -maybe_t env_vars_snapshot_t::get(const wcstring &key) const { +maybe_t env_vars_snapshot_t::get(const wcstring &key, env_mode_flags_t mode) const { // If we represent the current state, bounce to env_get. if (this->is_current()) { - return env_get(key); + return env_get(key, mode); } auto iter = vars.find(key); if (iter == vars.end()) return none(); diff --git a/src/env.h b/src/env.h index 30fb4ab0d..26b186c1f 100644 --- a/src/env.h +++ b/src/env.h @@ -131,6 +131,17 @@ class env_var_t { bool operator!=(const env_var_t &rhs) const { return ! (*this == rhs); } }; +/// An environment is read-only access to variable values. +class environment_t { + protected: + environment_t() = default; + + public: + virtual maybe_t get(const wcstring &key, + env_mode_flags_t mode = ENV_DEFAULT) const = 0; + virtual ~environment_t(); +}; + /// Gets the variable with the specified name, or none() if it does not exist. maybe_t env_get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT); @@ -180,7 +191,7 @@ wcstring env_get_pwd_slash(); /// Update the read_byte_limit variable. void env_set_read_limit(); -class env_vars_snapshot_t { +class env_vars_snapshot_t : public environment_t { std::map vars; bool is_current() const; @@ -191,7 +202,9 @@ class env_vars_snapshot_t { env_vars_snapshot_t(const wchar_t *const *keys); env_vars_snapshot_t(); - maybe_t get(const wcstring &key) const; + ~env_vars_snapshot_t(); + + maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override; // Returns the fake snapshot representing the live variables array. static const env_vars_snapshot_t ¤t(); diff --git a/src/function.cpp b/src/function.cpp index 30593812a..f709b08f0 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -198,7 +198,7 @@ void function_load(const wcstring &cmd) { } } -int function_exists_no_autoload(const wcstring &cmd, const env_vars_snapshot_t &vars) { +int function_exists_no_autoload(const wcstring &cmd, const environment_t &vars) { if (parser_keywords_is_reserved(cmd)) return 0; scoped_rlock locker(functions_lock); return loaded_functions.find(cmd) != loaded_functions.end() || diff --git a/src/function.h b/src/function.h index 7520e19c7..d823bdeeb 100644 --- a/src/function.h +++ b/src/function.h @@ -77,7 +77,7 @@ int function_exists(const wcstring &name); void function_load(const wcstring &name); /// Returns true if the function with the name name exists, without triggering autoload. -int function_exists_no_autoload(const wcstring &name, const env_vars_snapshot_t &vars); +int function_exists_no_autoload(const wcstring &name, const environment_t &vars); /// Returns all function names. /// diff --git a/src/highlight.cpp b/src/highlight.cpp index a6612b68f..211d7c0d7 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -333,7 +333,7 @@ static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expand bool autosuggest_validate_from_history(const history_item_t &item, const wcstring &working_directory, - const env_vars_snapshot_t &vars) { + const environment_t &vars) { ASSERT_IS_BACKGROUND_THREAD(); bool handled = false, suggestionOK = false; @@ -667,7 +667,7 @@ class highlighter_t { // Cursor position. const size_t cursor_pos; // Environment variables. Again, a reference member variable! - const env_vars_snapshot_t &vars; + const environment_t &vars; // Whether it's OK to do I/O. const bool io_ok; // Working directory. @@ -698,7 +698,7 @@ class highlighter_t { public: // Constructor - highlighter_t(const wcstring &str, size_t pos, const env_vars_snapshot_t &ev, wcstring wd, + highlighter_t(const wcstring &str, size_t pos, const environment_t &ev, wcstring wd, bool can_do_io) : buff(str), cursor_pos(pos), @@ -985,7 +985,7 @@ void highlighter_t::color_children(const parse_node_t &parent, parse_token_type_ /// 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) { + const wcstring &working_directory, const environment_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; @@ -1196,7 +1196,7 @@ const highlighter_t::color_array_t &highlighter_t::highlight() { } void highlight_shell(const wcstring &buff, std::vector &color, size_t pos, - wcstring_list_t *error, const env_vars_snapshot_t &vars) { + wcstring_list_t *error, const environment_t &vars) { UNUSED(error); // Do something sucky and get the current working directory on this background thread. This // should really be passed in. @@ -1208,7 +1208,7 @@ void highlight_shell(const wcstring &buff, std::vector &color, } void highlight_shell_no_io(const wcstring &buff, std::vector &color, size_t pos, - wcstring_list_t *error, const env_vars_snapshot_t &vars) { + wcstring_list_t *error, const environment_t &vars) { UNUSED(error); // Do something sucky and get the current working directory on this background thread. This // should really be passed in. @@ -1306,7 +1306,7 @@ static void highlight_universal_internal(const wcstring &buffstr, } void highlight_universal(const wcstring &buff, std::vector &color, size_t pos, - wcstring_list_t *error, const env_vars_snapshot_t &vars) { + wcstring_list_t *error, const environment_t &vars) { UNUSED(error); UNUSED(vars); assert(buff.size() == color.size()); diff --git a/src/highlight.h b/src/highlight.h index e4e255f27..bfac1d3f2 100644 --- a/src/highlight.h +++ b/src/highlight.h @@ -75,12 +75,12 @@ class history_item_t; /// \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); + wcstring_list_t *error, const environment_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); + size_t pos, wcstring_list_t *error, const environment_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 @@ -93,7 +93,7 @@ void highlight_shell_no_io(const wcstring &buffstr, std::vector &color, size_t pos, - wcstring_list_t *error, const env_vars_snapshot_t &vars); + wcstring_list_t *error, const environment_t &vars); /// Translate from HIGHLIGHT_* to FISH_COLOR_* according to environment variables. Defaults to /// FISH_COLOR_NORMAL. @@ -109,7 +109,7 @@ rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background); /// reference whether the suggestion is valid or not. bool autosuggest_validate_from_history(const history_item_t &item, const wcstring &working_directory, - const env_vars_snapshot_t &vars); + const environment_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). diff --git a/src/path.cpp b/src/path.cpp index 2b1b027dc..a4a8e1c3f 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -115,7 +115,7 @@ static bool path_get_path_core(const wcstring &cmd, wcstring *out_path, 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 environment_t &vars) { return path_get_path_core(cmd, out_path, vars.get(L"PATH")); } @@ -158,7 +158,7 @@ wcstring_list_t path_get_paths(const wcstring &cmd) { } bool path_get_cdpath(const wcstring &dir, wcstring *out, const wcstring &wd, - const env_vars_snapshot_t &env_vars) { + const environment_t &env_vars) { int err = ENOENT; if (dir.empty()) return false; @@ -216,7 +216,7 @@ bool path_get_cdpath(const wcstring &dir, wcstring *out, const wcstring &wd, } bool path_can_be_implicit_cd(const wcstring &path, const wcstring &wd, wcstring *out_path, - const env_vars_snapshot_t &vars) { + const environment_t &vars) { wcstring exp_path = path; expand_tilde(exp_path); diff --git a/src/path.h b/src/path.h index bb166c812..e6c2174aa 100644 --- a/src/path.h +++ b/src/path.h @@ -40,7 +40,7 @@ bool path_get_data(wcstring &path); /// false if the command can not be found else true. 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()); + const environment_t &vars = env_vars_snapshot_t::current()); /// Return all the paths that match the given command. wcstring_list_t path_get_paths(const wcstring &cmd); @@ -61,13 +61,13 @@ wcstring_list_t path_get_paths(const wcstring &cmd); /// \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 wcstring &wd, - const env_vars_snapshot_t &vars = env_vars_snapshot_t::current()); + const environment_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, const wcstring &wd, wcstring *out_path = NULL, - const env_vars_snapshot_t &vars = env_vars_snapshot_t::current()); + const environment_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. diff --git a/src/proc.cpp b/src/proc.cpp index 6f692fb50..f8794b82e 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -6,6 +6,7 @@ // IWYU pragma: no_include <__bit_reference> #include "config.h" +#include #include #include #include @@ -13,7 +14,6 @@ #include #include #include -#include #if HAVE_TERM_H #include diff --git a/src/reader.h b/src/reader.h index 2d4338b76..1ecca836c 100644 --- a/src/reader.h +++ b/src/reader.h @@ -14,8 +14,8 @@ #include "highlight.h" #include "parse_constants.h" +class environment_t; class history_t; -class env_vars_snapshot_t; class io_chain_t; /// Helper class for storing a command line. @@ -160,7 +160,7 @@ 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); + wcstring_list_t *, const environment_t &vars); /// Function type for testing if a string is valid for the reader to return. using test_function_t = parser_test_error_bits_t (*)(const wcstring &); From 8d7cae63ff1b0d76949c9fbcf6bba0a371e2eb64 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 9 Sep 2018 12:17:31 -0700 Subject: [PATCH 129/439] Introduce env_stack_t This will instance environment variable stacks. --- src/env.cpp | 142 ++++++++++++++++++++++++++++++--------------- src/env.h | 77 +++++++++++++++++++++++- src/fish_tests.cpp | 4 +- 3 files changed, 170 insertions(+), 53 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 7a9b18e80..854bd6765 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -141,8 +141,6 @@ class env_node_t { static std::mutex env_lock; -static bool local_scope_exports(const env_node_t *n); - // A class wrapping up a variable stack // Currently there is only one variable stack in fish, // but we can imagine having separate (linked) stacks @@ -163,7 +161,7 @@ struct var_stack_t { void mark_changed_exported() { has_changed_exported = true; } void update_export_array_if_necessary(); - var_stack_t() : top(new env_node_t(false)) { this->global_env = this->top.get(); } + var_stack_t() : top(new env_node_t(false)), global_env(top.get()) {} // Pushes a new node onto our stack // Optionally creates a new scope for the node @@ -180,6 +178,10 @@ struct var_stack_t { // Returns the scope used for unspecified scopes. An unspecified scope is either the topmost // shadowing scope, or the global scope if none. This implements the default behavior of `set`. env_node_t *resolve_unspecified_scope(); + + private: + bool local_scope_exports(const env_node_t *n) const; + void get_exported(const env_node_t *n, var_table_t &h) const; }; void var_stack_t::push(bool new_scope) { @@ -272,11 +274,12 @@ env_node_t *var_stack_t::resolve_unspecified_scope() { return node ? node : this->global_env; } -// Get the global variable stack -static var_stack_t &vars_stack() { - static var_stack_t global_stack; - return global_stack; -} +env_stack_t::env_stack_t() : vars_(make_unique()) {} + +// Get the variable stack +var_stack_t &env_stack_t::vars_stack() { return *vars_; } + +const var_stack_t &env_stack_t::vars_stack() const { return *vars_; } /// Universal variables global instance. Initialized in env_init. static env_universal_t *s_universal_variables = NULL; @@ -616,11 +619,11 @@ static void react_to_variable_change(const wchar_t *op, const wcstring &key) { /// Universal variable callback function. This function makes sure the proper events are triggered /// when an event occurs. -static void universal_callback(const callback_data_t &cb) { +static void universal_callback(env_stack_t *stack, const callback_data_t &cb) { const wchar_t *op = cb.is_erase() ? L"ERASE" : L"SET"; react_to_variable_change(op, cb.key); - vars_stack().mark_changed_exported(); + stack->mark_changed_exported(); event_t ev = event_t::variable_event(cb.key); ev.arguments.push_back(L"VARIABLE"); @@ -650,7 +653,7 @@ static void env_set_termsize() { } /// Update the PWD variable directory from the result of getcwd(). -void env_set_pwd_from_getcwd() { +void env_stack_t::set_pwd_from_getcwd() { wcstring cwd = wgetcwd(); if (cwd.empty()) { debug(0, @@ -662,7 +665,7 @@ void env_set_pwd_from_getcwd() { /// Allow the user to override the limit on how much data the `read` command will process. /// This is primarily for testing but could be used by users in special situations. -void env_set_read_limit() { +void env_stack_t::set_read_limit() { auto read_byte_limit_var = env_get(L"fish_read_limit"); if (!read_byte_limit_var.missing_or_empty()) { size_t limit = fish_wcstoull(read_byte_limit_var->as_string().c_str()); @@ -674,7 +677,9 @@ void env_set_read_limit() { } } -wcstring env_get_pwd_slash() { +void env_stack_t::mark_changed_exported() { vars_stack().mark_changed_exported(); } + +wcstring env_stack_t::get_pwd_slash() { // Return "/" if PWD is missing. // See https://github.com/fish-shell/fish-shell/issues/5080 auto pwd_var = env_get(L"PWD"); @@ -716,13 +721,13 @@ void misc_init() { } } -static void env_universal_callbacks(const callback_data_list_t &callbacks) { +static void env_universal_callbacks(env_stack_t *stack, const callback_data_list_t &callbacks) { for (const callback_data_t &cb : callbacks) { - universal_callback(cb); + universal_callback(stack, cb); } } -void env_universal_barrier() { +void env_stack_t::universal_barrier() { ASSERT_IS_MAIN_THREAD(); if (!uvars()) return; @@ -732,7 +737,7 @@ void env_universal_barrier() { universal_notifier_t::default_notifier().post_notification(); } - env_universal_callbacks(callbacks); + env_universal_callbacks(this, callbacks); } static void handle_fish_term_change(const wcstring &op, const wcstring &var_name) { @@ -976,7 +981,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { // initialize the PWD variable if necessary // Note we may inherit a virtual PWD that doesn't match what getcwd would return; respect that. if (env_get(L"PWD").missing_or_empty()) { - env_set_pwd_from_getcwd(); + env_stack_t::principal().set_pwd_from_getcwd(); } env_set_termsize(); // initialize the terminal size variables env_set_read_limit(); // initialize the read_byte_limit @@ -1000,7 +1005,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { s_universal_variables = new env_universal_t(L""); callback_data_list_t callbacks; s_universal_variables->initialize(callbacks); - env_universal_callbacks(callbacks); + env_universal_callbacks(&env_stack_t::principal(), callbacks); // 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 @@ -1010,7 +1015,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { /// 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_stack_t::get_node(const wcstring &key) { env_node_t *env = vars_stack().top.get(); while (env != NULL) { if (env->find_entry(key)) break; @@ -1033,7 +1038,7 @@ static int set_umask(const wcstring_list_t &list_val) { /// Set a universal variable, inheriting as applicable from the given old variable. static void env_set_internal_universal(const wcstring &key, wcstring_list_t val, - env_mode_flags_t input_var_mode) { + env_mode_flags_t input_var_mode, env_stack_t *stack) { ASSERT_IS_MAIN_THREAD(); if (!uvars()) return; env_mode_flags_t var_mode = input_var_mode; @@ -1068,7 +1073,7 @@ static void env_set_internal_universal(const wcstring &key, wcstring_list_t val, uvars()->set(key, new_var); env_universal_barrier(); if (new_var.exports() || (oldvar && oldvar->exports())) { - vars_stack().mark_changed_exported(); + stack->mark_changed_exported(); } } @@ -1088,8 +1093,7 @@ static void env_set_internal_universal(const wcstring &key, wcstring_list_t val, /// * 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. -static int env_set_internal(const wcstring &key, env_mode_flags_t input_var_mode, - wcstring_list_t val) { +int env_stack_t::set_internal(const wcstring &key, env_mode_flags_t input_var_mode, wcstring_list_t val) { ASSERT_IS_MAIN_THREAD(); env_mode_flags_t var_mode = input_var_mode; bool has_changed_old = vars_stack().has_changed_exported; @@ -1100,7 +1104,7 @@ static int env_set_internal(const wcstring &key, env_mode_flags_t input_var_mode wcstring val_canonical = val.front(); path_make_canonical(val_canonical); if (val.front() != val_canonical) { - return env_set_internal(key, var_mode, {val_canonical}); + return set_internal(key, var_mode, {val_canonical}); } } @@ -1121,12 +1125,12 @@ static int env_set_internal(const wcstring &key, env_mode_flags_t input_var_mode if (var_mode & ENV_UNIVERSAL) { if (uvars()) { - env_set_internal_universal(key, std::move(val), var_mode); + env_set_internal_universal(key, std::move(val), var_mode, this); } } else { // Determine the node. bool has_changed_new = false; - env_node_t *preexisting_node = env_get_node(key); + env_node_t *preexisting_node = get_node(key); maybe_t preexisting_flags{}; if (preexisting_node != NULL) { var_table_t::const_iterator result = preexisting_node->env.find(key); @@ -1157,7 +1161,7 @@ static int env_set_internal(const wcstring &key, env_mode_flags_t input_var_mode } if (uvars() && uvars()->get(key)) { // Modifying an existing universal variable. - env_set_internal_universal(key, std::move(val), var_mode); + env_set_internal_universal(key, std::move(val), var_mode, this); done = true; } else { // New variable with unspecified scope @@ -1229,26 +1233,26 @@ static int env_set_internal(const wcstring &key, env_mode_flags_t input_var_mode } /// Sets the variable with the specified name to the given values. -int env_set(const wcstring &key, env_mode_flags_t mode, wcstring_list_t vals) { - return env_set_internal(key, mode, std::move(vals)); +int env_stack_t::set(const wcstring &key, env_mode_flags_t mode, wcstring_list_t vals) { + return set_internal(key, mode, std::move(vals)); } /// Sets the variable with the specified name to a single value. -int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) { +int env_stack_t::set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) { wcstring_list_t vals; vals.push_back(std::move(val)); - return env_set_internal(key, mode, std::move(vals)); + return set_internal(key, mode, std::move(vals)); } /// Sets the variable with the specified name without any (i.e., zero) values. -int env_set_empty(const wcstring &key, env_mode_flags_t mode) { - return env_set_internal(key, mode, {}); +int env_stack_t::set_empty(const wcstring &key, env_mode_flags_t mode) { + return set_internal(key, mode, {}); } /// 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) { +bool env_stack_t::try_remove(env_node_t *n, const wchar_t *key, int var_mode) { if (n == NULL) { return false; } @@ -1256,7 +1260,7 @@ static bool try_remove(env_node_t *n, const wchar_t *key, int var_mode) { var_table_t::iterator result = n->env.find(key); if (result != n->env.end()) { if (result->second.exports()) { - vars_stack().mark_changed_exported(); + mark_changed_exported(); } n->env.erase(result); return true; @@ -1272,7 +1276,7 @@ static bool try_remove(env_node_t *n, const wchar_t *key, int var_mode) { return try_remove(n->next.get(), key, var_mode); } -int env_remove(const wcstring &key, int var_mode) { +int env_stack_t::remove(const wcstring &key, int var_mode) { ASSERT_IS_MAIN_THREAD(); env_node_t *first_node; int erased = 0; @@ -1345,7 +1349,7 @@ env_var_t::env_var_flags_t env_var_t::flags_for(const wchar_t *name) { return result; } -maybe_t env_get(const wcstring &key, env_mode_flags_t mode) { +maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) const { 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); @@ -1383,7 +1387,7 @@ maybe_t env_get(const wcstring &key, env_mode_flags_t mode) { if (search_local || search_global) { scoped_lock locker(env_lock); // lock around a local region - env_node_t *env = search_local ? vars_stack().top.get() : vars_stack().global_env; + const env_node_t *env = search_local ? vars_stack().top.get() : vars_stack().global_env; while (env != NULL) { if (env == vars_stack().global_env && !search_global) { @@ -1422,11 +1426,46 @@ maybe_t env_get(const wcstring &key, env_mode_flags_t mode) { return none(); } +/// Legacy versions. +maybe_t env_get(const wcstring &key, env_mode_flags_t mode) { + return env_stack_t::principal().get(key, mode); +} + +int env_set(const wcstring &key, env_mode_flags_t mode, wcstring_list_t vals) { + return env_stack_t::principal().set(key, mode, std::move(vals)); +} + +int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) { + return env_stack_t::principal().set_one(key, mode, std::move(val)); +} + +int env_set_empty(const wcstring &key, env_mode_flags_t mode) { + return env_stack_t::principal().set_empty(key, mode); +} + +int env_remove(const wcstring &key, int mode) { return env_stack_t::principal().remove(key, mode); } + +void env_push(bool new_scope) { env_stack_t::principal().push(new_scope); } + +void env_pop() { env_stack_t::principal().pop(); } + +void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } + +const char *const *env_export_arr() { return env_stack_t::principal().export_arr(); } + +void env_set_argv(const wchar_t *const *argv) { return env_stack_t::principal().set_argv(argv); } + +wcstring_list_t env_get_names(int flags) { return env_stack_t::principal().get_names(flags); } + +wcstring env_get_pwd_slash() { return env_stack_t::principal().get_pwd_slash(); } + +void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); } + /// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported /// variable. -static bool local_scope_exports(const env_node_t *n) { +bool var_stack_t::local_scope_exports(const env_node_t *n) const { assert(n != NULL); - if (n == vars_stack().global_env) return false; + if (n == global_env) return false; if (n->exportv) return true; @@ -1435,9 +1474,9 @@ static bool local_scope_exports(const env_node_t *n) { return local_scope_exports(n->next.get()); } -void env_push(bool new_scope) { vars_stack().push(new_scope); } +void env_stack_t::push(bool new_scope) { vars_stack().push(new_scope); } -void env_pop() { vars_stack().pop(); } +void env_stack_t::pop() { vars_stack().pop(); } /// 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, @@ -1453,7 +1492,7 @@ static void add_key_to_string_set(const var_table_t &envs, std::set *s } } -wcstring_list_t env_get_names(int flags) { +wcstring_list_t env_stack_t::get_names(int flags) { scoped_lock locker(env_lock); wcstring_list_t result; @@ -1499,11 +1538,11 @@ wcstring_list_t env_get_names(int flags) { } /// Get list of all exported variables. -static void get_exported(const env_node_t *n, var_table_t &h) { +void var_stack_t::get_exported(const env_node_t *n, var_table_t &h) const { if (!n) return; if (n->new_scope) { - get_exported(vars_stack().global_env, h); + get_exported(global_env, h); } else { get_exported(n->next.get(), h); } @@ -1569,14 +1608,14 @@ void var_stack_t::update_export_array_if_necessary() { has_changed_exported = false; } -const char *const *env_export_arr() { +const char *const *env_stack_t::export_arr() { ASSERT_IS_MAIN_THREAD(); ASSERT_IS_NOT_FORKED_CHILD(); vars_stack().update_export_array_if_necessary(); return vars_stack().export_array.get(); } -void env_set_argv(const wchar_t *const *argv) { +void env_stack_t::set_argv(const wchar_t *const *argv) { if (argv && *argv) { wcstring_list_t list; for (auto arg = argv; *arg; arg++) { @@ -1591,6 +1630,13 @@ void env_set_argv(const wchar_t *const *argv) { environment_t::~environment_t() = default; +env_stack_t::~env_stack_t() = default; + +env_stack_t &env_stack_t::principal() { + static env_stack_t s_principal; + return s_principal; +} + env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t *const *keys) { ASSERT_IS_MAIN_THREAD(); wcstring key; diff --git a/src/env.h b/src/env.h index 26b186c1f..73b21a9a8 100644 --- a/src/env.h +++ b/src/env.h @@ -182,15 +182,86 @@ void env_set_argv(const wchar_t *const *argv); /// Returns all variable names. wcstring_list_t env_get_names(int flags); -/// Update the PWD variable based on the result of getcwd. -void env_set_pwd_from_getcwd(); - /// Returns the PWD with a terminating slash. wcstring env_get_pwd_slash(); /// Update the read_byte_limit variable. void env_set_read_limit(); +/// A environment stack of scopes. This is the main class that tracks fish variables. +struct var_stack_t; +class env_node_t; +class env_stack_t : public environment_t { + std::unique_ptr vars_; + + int set_internal(const wcstring &key, env_mode_flags_t var_mode, wcstring_list_t val); + + bool try_remove(env_node_t *n, const wchar_t *key, int var_mode); + env_node_t *get_node(const wcstring &key); + + var_stack_t &vars_stack(); + const var_stack_t &vars_stack() const; + + env_stack_t(); + ~env_stack_t() override; + + public: + /// Gets the variable with the specified name, or none() if it does not exist. + maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override; + + /// Sets the variable with the specified name to the given values. + int set(const wcstring &key, env_mode_flags_t mode, wcstring_list_t vals); + + /// Sets the variable with the specified name to a single value. + int set_one(const wcstring &key, env_mode_flags_t mode, wcstring val); + + /// Sets the variable with the specified name to no values. + int set_empty(const wcstring &key, env_mode_flags_t mode); + + /// Update the PWD variable based on the result of getcwd. + void set_pwd_from_getcwd(); + + /// 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 remove(const wcstring &key, int mode); + + /// Push the variable stack. Used for implementing local variables for functions and for-loops. + void push(bool new_scope); + + /// Pop the variable stack. Used for implementing local variables for functions and for-loops. + void pop(); + + /// Synchronizes all universal variable changes: writes everything out, reads stuff in. + void universal_barrier(); + + /// Returns an array containing all exported variables in a format suitable for execv + const char *const *export_arr(); + + /// Returns all variable names. + wcstring_list_t get_names(int flags); + + /// Sets up argv as the given null terminated array of strings. + void set_argv(const wchar_t *const *argv); + + /// Returns the PWD with a terminating slash. + wcstring get_pwd_slash(); + + /// Update the read_byte_limit variable. + void set_read_limit(); + + /// Mark that exported variables have changed. + void mark_changed_exported(); + + // Compatibility hack; access the "environment stack" from back when there was just one. + static env_stack_t &principal(); +}; + class env_vars_snapshot_t : public environment_t { std::map vars; bool is_current() const; diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index c35e3c551..0f1a430c0 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -174,7 +174,7 @@ static bool pushd(const char *path) { return false; } - env_set_pwd_from_getcwd(); + env_stack_t::principal().set_pwd_from_getcwd(); return true; } @@ -184,7 +184,7 @@ static void popd() { err(L"chdir(\"%s\") from popd() failed: errno = %d", old_cwd.c_str(), errno); } pushed_dirs.pop_back(); - env_set_pwd_from_getcwd(); + env_stack_t::principal().set_pwd_from_getcwd(); } // The odd formulation of these macros is to avoid "multiple unary operator" warnings from oclint From bba66a3ecccdb43fe3dfa94a187011574fb70efe Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Sep 2018 01:17:37 -0700 Subject: [PATCH 130/439] Use shared_ptr instead of unique_ptr in environments This prepares for multiple environment stacks sharing the same parent. --- src/env.cpp | 119 +++++++++++++++++++++++++--------------------------- src/env.h | 5 ++- 2 files changed, 59 insertions(+), 65 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 854bd6765..b984be483 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -118,9 +118,6 @@ static void init_curses(); // Struct representing one level in the function variable stack. // Only our variable stack should create and destroy these class env_node_t { - friend struct var_stack_t; - env_node_t(bool is_new_scope) : new_scope(is_new_scope) {} - public: /// Variable table. var_table_t env; @@ -132,13 +129,17 @@ class env_node_t { /// or does it redefine any variables to not be exported? bool exportv = false; /// Pointer to next level. - std::unique_ptr next; + std::shared_ptr next; + + env_node_t(bool is_new_scope) : new_scope(is_new_scope) {} maybe_t find_entry(const wcstring &key); bool contains_any_of(const wcstring_list_t &vars) const; }; +using env_node_ref_t = std::shared_ptr; + static std::mutex env_lock; // A class wrapping up a variable stack @@ -147,11 +148,10 @@ static std::mutex env_lock; // if we introduce multiple threads of execution struct var_stack_t { // Top node on the function stack. - std::unique_ptr top = NULL; + env_node_ref_t top; - // Bottom node on the function stack - // This is an observer pointer - env_node_t *global_env = NULL; + // Bottom node on the function stack. + env_node_ref_t global_env; // Exported variable array used by execv. null_terminated_array_t export_array; @@ -161,7 +161,7 @@ struct var_stack_t { void mark_changed_exported() { has_changed_exported = true; } void update_export_array_if_necessary(); - var_stack_t() : top(new env_node_t(false)), global_env(top.get()) {} + var_stack_t() : top(globals()), global_env(globals()) {} // Pushes a new node onto our stack // Optionally creates a new scope for the node @@ -170,25 +170,32 @@ struct var_stack_t { // Pops the top node if it's not global void pop(); - // Returns the next scope to search for a given node, respecting the new_scope lag - // Returns NULL if we're done - env_node_t *next_scope_to_search(env_node_t *node); - const env_node_t *next_scope_to_search(const env_node_t *node) const; + // Returns the next scope to search for a given node, respecting the new_scope flag. + // Returns an empty pointer if we're done. + env_node_ref_t next_scope_to_search(const env_node_ref_t &node) const; // Returns the scope used for unspecified scopes. An unspecified scope is either the topmost // shadowing scope, or the global scope if none. This implements the default behavior of `set`. - env_node_t *resolve_unspecified_scope(); + env_node_ref_t resolve_unspecified_scope(); private: - bool local_scope_exports(const env_node_t *n) const; + bool local_scope_exports(const env_node_ref_t &n) const; void get_exported(const env_node_t *n, var_table_t &h) const; + + /// Returns the global variable set. + static env_node_ref_t globals(); }; +env_node_ref_t var_stack_t::globals() { + static env_node_ref_t s_globals{std::make_shared(false)}; + return s_globals; +} + void var_stack_t::push(bool new_scope) { - std::unique_ptr node(new env_node_t(new_scope)); + auto node = std::make_shared(new_scope); // Copy local-exported variables. - auto top_node = top.get(); + auto top_node = top; // Only if we introduce a new shadowing scope; i.e. not if it's just `begin; end` or // "--no-scope-shadowing". if (new_scope && top_node != this->global_env) { @@ -197,9 +204,9 @@ void var_stack_t::push(bool new_scope) { } } - node->next = std::move(this->top); - this->top = std::move(node); - if (new_scope && local_scope_exports(this->top.get())) { + node->next = this->top; + this->top = node; + if (new_scope && local_scope_exports(this->top)) { this->mark_changed_exported(); } } @@ -214,7 +221,7 @@ bool env_node_t::contains_any_of(const wcstring_list_t &vars) const { void var_stack_t::pop() { // Don't pop the top-most, global, level. - if (top.get() == this->global_env) { + if (top == this->global_env) { debug(0, _(L"Tried to pop empty environment stack.")); sanity_lose(); return; @@ -224,20 +231,14 @@ void var_stack_t::pop() { bool curses_changed = top->contains_any_of(curses_variables); if (top->new_scope) { //!OCLINT(collapsible if statements) - if (top->exportv || local_scope_exports(top->next.get())) { + if (top->exportv || local_scope_exports(top->next)) { this->mark_changed_exported(); } } - // Actually do the pop! Move the top pointer into a local variable, then replace the top pointer - // with the next pointer afterwards we should have a node with no next pointer, and our top - // should be non-null. - std::unique_ptr old_top = std::move(this->top); - this->top = std::move(old_top->next); - old_top->next.reset(); - assert(this->top && old_top && !old_top->next); - assert(this->top != NULL); - + // Actually do the pop! + env_node_ref_t old_top = this->top; + this->top = old_top->next; for (const auto &entry_pair : old_top->env) { const env_var_t &var = entry_pair.second; if (var.exports()) { @@ -250,26 +251,18 @@ void var_stack_t::pop() { if (curses_changed) init_curses(); } -const env_node_t *var_stack_t::next_scope_to_search(const env_node_t *node) const { +env_node_ref_t var_stack_t::next_scope_to_search(const env_node_ref_t &node) const { assert(node != NULL); if (node == this->global_env) { - return NULL; + return nullptr; } - return node->new_scope ? this->global_env : node->next.get(); + return node->new_scope ? this->global_env : node->next; } -env_node_t *var_stack_t::next_scope_to_search(env_node_t *node) { - assert(node != NULL); - if (node == this->global_env) { - return NULL; - } - return node->new_scope ? this->global_env : node->next.get(); -} - -env_node_t *var_stack_t::resolve_unspecified_scope() { - env_node_t *node = this->top.get(); +env_node_ref_t var_stack_t::resolve_unspecified_scope() { + env_node_ref_t node = this->top; while (node && !node->new_scope) { - node = node->next.get(); + node = node->next; } return node ? node : this->global_env; } @@ -1015,9 +1008,9 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { /// Search all visible scopes in order for the specified key. Return the first scope in which it was /// found. -env_node_t *env_stack_t::get_node(const wcstring &key) { - env_node_t *env = vars_stack().top.get(); - while (env != NULL) { +env_node_ref_t env_stack_t::get_node(const wcstring &key) { + env_node_ref_t env = vars_stack().top; + while (env) { if (env->find_entry(key)) break; env = vars_stack().next_scope_to_search(env); } @@ -1130,7 +1123,7 @@ int env_stack_t::set_internal(const wcstring &key, env_mode_flags_t input_var_mo } else { // Determine the node. bool has_changed_new = false; - env_node_t *preexisting_node = get_node(key); + env_node_ref_t preexisting_node = get_node(key); maybe_t preexisting_flags{}; if (preexisting_node != NULL) { var_table_t::const_iterator result = preexisting_node->env.find(key); @@ -1141,11 +1134,11 @@ int env_stack_t::set_internal(const wcstring &key, env_mode_flags_t input_var_mo } } - env_node_t *node = NULL; + env_node_ref_t node = nullptr; if (var_mode & ENV_GLOBAL) { node = vars_stack().global_env; } else if (var_mode & ENV_LOCAL) { - node = vars_stack().top.get(); + node = vars_stack().top; } else if (preexisting_node != NULL) { node = preexisting_node; if ((var_mode & (ENV_EXPORT | ENV_UNEXPORT)) == 0) { @@ -1252,8 +1245,8 @@ int env_stack_t::set_empty(const wcstring &key, env_mode_flags_t mode) { /// Attempt to remove/free the specified key/value pair from the specified map. /// /// \return zero if the variable was not found, non-zero otherwise -bool env_stack_t::try_remove(env_node_t *n, const wchar_t *key, int var_mode) { - if (n == NULL) { +bool env_stack_t::try_remove(env_node_ref_t n, const wchar_t *key, int var_mode) { + if (n == nullptr) { return false; } @@ -1273,19 +1266,19 @@ bool env_stack_t::try_remove(env_node_t *n, const wchar_t *key, int var_mode) { if (n->new_scope) { return try_remove(vars_stack().global_env, key, var_mode); } - return try_remove(n->next.get(), key, var_mode); + return try_remove(n->next, key, var_mode); } int env_stack_t::remove(const wcstring &key, int var_mode) { ASSERT_IS_MAIN_THREAD(); - env_node_t *first_node; + env_node_ref_t first_node{}; int erased = 0; if ((var_mode & ENV_USER) && is_read_only(key)) { return ENV_SCOPE; } - first_node = vars_stack().top.get(); + first_node = vars_stack().top; if (!(var_mode & ENV_UNIVERSAL)) { if (var_mode & ENV_GLOBAL) { @@ -1387,7 +1380,7 @@ maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) if (search_local || search_global) { scoped_lock locker(env_lock); // lock around a local region - const env_node_t *env = search_local ? vars_stack().top.get() : vars_stack().global_env; + env_node_ref_t env = search_local ? vars_stack().top : vars_stack().global_env; while (env != NULL) { if (env == vars_stack().global_env && !search_global) { @@ -1463,15 +1456,15 @@ void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); } /// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported /// variable. -bool var_stack_t::local_scope_exports(const env_node_t *n) const { - assert(n != NULL); +bool var_stack_t::local_scope_exports(const env_node_ref_t &n) const { + assert(n != nullptr); if (n == global_env) return false; if (n->exportv) return true; if (n->new_scope) return false; - return local_scope_exports(n->next.get()); + return local_scope_exports(n->next); } void env_stack_t::push(bool new_scope) { vars_stack().push(new_scope); } @@ -1501,7 +1494,7 @@ wcstring_list_t env_stack_t::get_names(int flags) { int show_global = flags & ENV_GLOBAL; int show_universal = flags & ENV_UNIVERSAL; - const env_node_t *n = vars_stack().top.get(); + env_node_ref_t n = vars_stack().top; const bool show_exported = (flags & ENV_EXPORT) || !(flags & ENV_UNEXPORT); const bool show_unexported = (flags & ENV_UNEXPORT) || !(flags & ENV_EXPORT); @@ -1517,7 +1510,7 @@ wcstring_list_t env_stack_t::get_names(int flags) { if (n->new_scope) break; else - n = n->next.get(); + n = n->next; } } @@ -1542,7 +1535,7 @@ void var_stack_t::get_exported(const env_node_t *n, var_table_t &h) const { if (!n) return; if (n->new_scope) { - get_exported(global_env, h); + get_exported(global_env.get(), h); } else { get_exported(n->next.get(), h); } diff --git a/src/env.h b/src/env.h index 73b21a9a8..7317b3e77 100644 --- a/src/env.h +++ b/src/env.h @@ -192,12 +192,13 @@ void env_set_read_limit(); struct var_stack_t; class env_node_t; class env_stack_t : public environment_t { + friend class parser_t; std::unique_ptr vars_; int set_internal(const wcstring &key, env_mode_flags_t var_mode, wcstring_list_t val); - bool try_remove(env_node_t *n, const wchar_t *key, int var_mode); - env_node_t *get_node(const wcstring &key); + bool try_remove(std::shared_ptr n, const wchar_t *key, int var_mode); + std::shared_ptr get_node(const wcstring &key); var_stack_t &vars_stack(); const var_stack_t &vars_stack() const; From a47f6859bdcf3b344c465940ae5c8fd628a02450 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Sep 2018 01:17:57 -0700 Subject: [PATCH 131/439] Equip parser_t with a variable stack Prepares to eliminate env_get and env_set by accessing variables through a parser. --- src/parser.cpp | 2 +- src/parser.h | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index fd33f58fd..ce79a0c04 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -100,7 +100,7 @@ static wcstring user_presentable_path(const wcstring &path) { return replace_home_directory_with_tilde(path); } -parser_t::parser_t() = default; +parser_t::parser_t() : variables(env_stack_t::principal()) {} // Out of line destructor to enable forward declaration of parse_execution_context_t parser_t::~parser_t() = default; diff --git a/src/parser.h b/src/parser.h index bfeea4406..deef053ac 100644 --- a/src/parser.h +++ b/src/parser.h @@ -173,7 +173,8 @@ class parser_t { std::vector> block_stack; /// The 'depth' of the fish call stack. int eval_level = -1; - + /// Set of variables for the parser. + env_stack_t &variables; #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. @@ -271,6 +272,10 @@ class parser_t { /// Get the list of jobs. job_list_t &job_list() { return my_job_list; } + /// Get the variables. + env_stack_t &vars() { return variables; } + const env_stack_t &vars() const { return variables; } + /// Pushes a new block created with the given arguments /// Returns a pointer to the block. The pointer is valid /// until the call to pop_block() From e6872b83b0344d066259525f4c384c6c0b852074 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Sep 2018 01:26:30 -0700 Subject: [PATCH 132/439] Eliminate global env_export_arr() This assumes the set of exported variables is a global property; but we want it to be a local property. --- src/env.cpp | 4 +--- src/env.h | 3 --- src/exec.cpp | 20 ++++++++++---------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index b984be483..4e82ac56c 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1444,8 +1444,6 @@ void env_pop() { env_stack_t::principal().pop(); } void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } -const char *const *env_export_arr() { return env_stack_t::principal().export_arr(); } - void env_set_argv(const wchar_t *const *argv) { return env_stack_t::principal().set_argv(argv); } wcstring_list_t env_get_names(int flags) { return env_stack_t::principal().get_names(flags); } @@ -1580,7 +1578,7 @@ void var_stack_t::update_export_array_if_necessary() { return; } - debug(4, L"env_export_arr() recalc"); + debug(4, L"export_arr() recalc"); var_table_t vals; get_exported(this->top.get(), vals); diff --git a/src/env.h b/src/env.h index 7317b3e77..148144f45 100644 --- a/src/env.h +++ b/src/env.h @@ -173,9 +173,6 @@ void env_pop(); /// 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(); - /// Sets up argv as the given null terminated array of strings. void env_set_argv(const wchar_t *const *argv); diff --git a/src/exec.cpp b/src/exec.cpp index 0bbd8721b..5b13d7b23 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -202,14 +202,14 @@ static void safe_launch_process(process_t *p, const char *actual_cmd, const char /// This function is similar to launch_process, except it is not called after a fork (i.e. it only /// calls exec) and therefore it can allocate memory. -static void launch_process_nofork(process_t *p) { +static void launch_process_nofork(env_stack_t &vars, process_t *p) { ASSERT_IS_MAIN_THREAD(); ASSERT_IS_NOT_FORKED_CHILD(); null_terminated_array_t argv_array; convert_wide_array_to_narrow(p->get_argv_array(), &argv_array); - const char *const *envv = env_export_arr(); + const char *const *envv = vars.export_arr(); char *actual_cmd = wcs2str(p->actual_cmd); // Ensure the terminal modes are what they were before we changed them. @@ -358,7 +358,7 @@ static bool can_use_posix_spawn_for_job(const std::shared_ptr &job, return result; } -void internal_exec(job_t *j, const io_chain_t &&all_ios) { +void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &all_ios) { // Do a regular launch - but without forking first... // setup_child_process makes sure signals are properly set up. @@ -380,7 +380,7 @@ void internal_exec(job_t *j, const io_chain_t &&all_ios) { env_set_one(L"SHLVL", ENV_GLOBAL | ENV_EXPORT, shlvl_str); // launch_process _never_ returns. - launch_process_nofork(j->processes.front().get()); + launch_process_nofork(vars, j->processes.front().get()); } else { j->set_flag(job_flag_t::CONSTRUCTED, true); j->processes.front()->completed = 1; @@ -648,8 +648,8 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, /// Executes an external command. /// \return true on success, false if there is an exec error. -static bool exec_external_command(const std::shared_ptr &j, process_t *p, - const io_chain_t &proc_io_chain) { +static bool exec_external_command(env_stack_t &vars, const std::shared_ptr &j, + process_t *p, const io_chain_t &proc_io_chain) { assert(p->type == EXTERNAL && "Process is not external"); // Get argv and envv before we fork. null_terminated_array_t argv_array; @@ -663,7 +663,7 @@ static bool exec_external_command(const std::shared_ptr &j, process_t *p, make_fd_blocking(STDIN_FILENO); const char *const *argv = argv_array.get(); - const char *const *envv = env_export_arr(); + const char *const *envv = vars.export_arr(); std::string actual_cmd_str = wcs2string(p->actual_cmd); const char *actual_cmd = actual_cmd_str.c_str(); @@ -919,7 +919,7 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< set_proc_had_barrier(true); env_universal_barrier(); } - env_export_arr(); + parser.vars().export_arr(); } // Set up fds that will be used in the pipe. @@ -975,7 +975,7 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< } case EXTERNAL: { - if (!exec_external_command(j, p, process_net_io_chain)) { + if (!exec_external_command(parser.vars(), j, p, process_net_io_chain)) { return false; } break; @@ -1028,7 +1028,7 @@ bool exec_job(parser_t &parser, shared_ptr j) { } if (j->processes.front()->type == INTERNAL_EXEC) { - internal_exec(j.get(), std::move(all_ios)); + internal_exec(parser.vars(), j.get(), all_ios); DIE("this should be unreachable"); } From e6b13c6bac331cfab6df1e46638f1512b123c4cf Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Sep 2018 09:58:02 -0700 Subject: [PATCH 133/439] Begin to thread environments explicitly through completions --- src/builtin_complete.cpp | 2 +- src/builtin_set.cpp | 7 ++-- src/complete.cpp | 34 +++++++++---------- src/complete.h | 4 ++- src/env.cpp | 31 +++++++++-------- src/env.h | 19 ++++++----- src/fish_tests.cpp | 72 +++++++++++++++++++++------------------- src/reader.cpp | 11 +++--- src/reader.h | 8 ++--- 9 files changed, 95 insertions(+), 93 deletions(-) diff --git a/src/builtin_complete.cpp b/src/builtin_complete.cpp index d40a4e303..2a01fcd26 100644 --- a/src/builtin_complete.cpp +++ b/src/builtin_complete.cpp @@ -318,7 +318,7 @@ int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv) { recursion_level++; std::vector comp; - complete(do_complete_param, &comp, COMPLETION_REQUEST_DEFAULT); + complete(do_complete_param, &comp, COMPLETION_REQUEST_DEFAULT, parser.vars()); for (size_t i = 0; i < comp.size(); i++) { const completion_t &next = comp.at(i); diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index e78fc4ee7..6ab298a0b 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -22,12 +22,11 @@ #include "expand.h" #include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "parser.h" #include "proc.h" #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep -class parser_t; - struct set_cmd_opts_t { bool print_help = false; bool show = false; @@ -475,7 +474,7 @@ static int builtin_set_list(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, UNUSED(parser); bool names_only = opts.list; - wcstring_list_t names = env_get_names(compute_scope(opts)); + wcstring_list_t names = parser.vars().get_names(compute_scope(opts)); sort(names.begin(), names.end()); for (size_t i = 0; i < names.size(); i++) { @@ -592,7 +591,7 @@ static int builtin_set_show(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, UNUSED(opts); if (argc == 0) { // show all vars - wcstring_list_t names = env_get_names(ENV_USER); + wcstring_list_t names = parser.vars().get_names(ENV_USER); sort(names.begin(), names.end()); for (auto it : names) { show_scope(it.c_str(), ENV_LOCAL, streams); diff --git a/src/complete.cpp b/src/complete.cpp index 440d5f3c9..fbe453264 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -81,11 +81,11 @@ void complete_set_variable_names(const wcstring_list_t *names) { s_override_variable_names = names; } -static inline wcstring_list_t complete_get_variable_names() { +static inline wcstring_list_t complete_get_variable_names(const environment_t &vars) { if (s_override_variable_names != NULL) { return *s_override_variable_names; } - return env_get_names(0); + return vars.get_names(0); } /// Struct describing a completion option entry. @@ -301,8 +301,16 @@ void completions_sort_and_prioritize(std::vector *comps, /// Class representing an attempt to compute completions. class completer_t { + /// Environment inside which we are completing. + const environment_t &vars; + + /// The command to complete. const wcstring cmd; + + /// Flags associated with the completion request. const completion_request_flags_t flags; + + /// The output cmopletions. std::vector completions; /// Table of completions conditions that have already been tested and the corresponding test @@ -372,7 +380,8 @@ class completer_t { void mark_completions_duplicating_arguments(const wcstring &prefix, const arg_list_t &args); public: - completer_t(wcstring c, completion_request_flags_t f) : cmd(std::move(c)), flags(f) {} + completer_t(const environment_t &vars, wcstring c, completion_request_flags_t f) + : vars(vars), cmd(std::move(c)), flags(f) {} void perform(); @@ -886,22 +895,11 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt, } }; - // This was originally written as a static variable protected by a mutex that is updated only if - // `scmd.size() == 1` to prevent too many lookups, but it turns out that this is mainly only - // called when the user explicitly presses after a command, so the overhead of the - // additional env lookup should be negligible. - env_vars_snapshot_t completion_snapshot; - // debug(0, L"\nThinking about looking up completions for %ls\n", cmd.c_str()); bool head_exists = builtin_exists(cmd); // Only reload environment variables if builtin_exists returned false, as an optimization if (head_exists == false) { - run_on_main_thread([&completion_snapshot]() { - completion_snapshot = std::move( - env_vars_snapshot_t((wchar_t const *const[]){L"fish_function_path", nullptr})); - }); - - head_exists = function_exists_no_autoload(cmd, completion_snapshot); + head_exists = function_exists_no_autoload(cmd.c_str(), vars); // While it may seem like first testing `path_get_path` before resorting to an env lookup // may be faster, path_get_path can potentially do a lot of FS/IO access, so env.get() + // function_exists() should still be faster. @@ -1127,7 +1125,7 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) { size_t varlen = str.length() - start_offset; bool res = false; - const wcstring_list_t names = complete_get_variable_names(); + const wcstring_list_t names = complete_get_variable_names(vars); for (size_t i = 0; i < names.size(); i++) { const wcstring &env_name = names.at(i); @@ -1578,14 +1576,14 @@ void completer_t::perform() { } void complete(const wcstring &cmd_with_subcmds, std::vector *out_comps, - completion_request_flags_t flags) { + completion_request_flags_t flags, const environment_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); assert(cmdsubst_begin != NULL && cmdsubst_end != NULL && cmdsubst_end >= cmdsubst_begin); wcstring cmd = wcstring(cmdsubst_begin, cmdsubst_end - cmdsubst_begin); - completer_t completer(std::move(cmd), flags); + completer_t completer(vars, std::move(cmd), flags); completer.perform(); *out_comps = completer.acquire_completions(); } diff --git a/src/complete.h b/src/complete.h index 2349d1ff1..0abd68208 100644 --- a/src/complete.h +++ b/src/complete.h @@ -30,6 +30,8 @@ /// Character that separates the completion and description on programmable completions. #define PROG_COMPLETE_SEP L'\t' +class environment_t; + enum { /// Do not insert space afterwards if this is the only completion. (The default is to try insert /// a space). @@ -172,7 +174,7 @@ void complete_remove_all(const wcstring &cmd, bool cmd_is_path); /// Find all completions of the command cmd, insert them into out. void complete(const wcstring &cmd, std::vector *out_comps, - completion_request_flags_t flags); + completion_request_flags_t flags, const environment_t &vars); /// Return a list of all current completions. wcstring complete_print(); diff --git a/src/env.cpp b/src/env.cpp index 4e82ac56c..50341b890 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1446,8 +1446,6 @@ void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } void env_set_argv(const wchar_t *const *argv) { return env_stack_t::principal().set_argv(argv); } -wcstring_list_t env_get_names(int flags) { return env_stack_t::principal().get_names(flags); } - wcstring env_get_pwd_slash() { return env_stack_t::principal().get_pwd_slash(); } void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); } @@ -1483,7 +1481,7 @@ static void add_key_to_string_set(const var_table_t &envs, std::set *s } } -wcstring_list_t env_stack_t::get_names(int flags) { +wcstring_list_t env_stack_t::get_names(int flags) const { scoped_lock locker(env_lock); wcstring_list_t result; @@ -1620,7 +1618,6 @@ void env_stack_t::set_argv(const wchar_t *const *argv) { } environment_t::~environment_t() = default; - env_stack_t::~env_stack_t() = default; env_stack_t &env_stack_t::principal() { @@ -1628,27 +1625,28 @@ env_stack_t &env_stack_t::principal() { return s_principal; } -env_vars_snapshot_t::env_vars_snapshot_t(const wchar_t *const *keys) { +env_vars_snapshot_t::env_vars_snapshot_t(const environment_t &source, const wchar_t *const *keys) { ASSERT_IS_MAIN_THREAD(); wcstring key; for (size_t i = 0; keys[i]; i++) { key.assign(keys[i]); - const auto var = env_get(key); + const auto var = source.get(key); if (var) { vars[key] = std::move(*var); } } + names = source.get_names(0); } -env_vars_snapshot_t::env_vars_snapshot_t() = default; env_vars_snapshot_t::~env_vars_snapshot_t() = default; // The "current" variables are not a snapshot at all, but instead trampoline to env_get, 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; } +// This is an ugly thing that has to go away. +const env_vars_snapshot_t env_vars_snapshot_t::s_current; +const env_vars_snapshot_t &env_vars_snapshot_t::current() { return s_current; } -bool env_vars_snapshot_t::is_current() const { return this == &sCurrentSnapshot; } +bool env_vars_snapshot_t::is_current() const { return this == &s_current; } maybe_t env_vars_snapshot_t::get(const wcstring &key, env_mode_flags_t mode) const { // If we represent the current state, bounce to env_get. @@ -1660,6 +1658,13 @@ maybe_t env_vars_snapshot_t::get(const wcstring &key, env_mode_flags_ return iter->second; } +wcstring_list_t env_vars_snapshot_t::get_names(int flags) const { return names; } + +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", + L"fish_function_path", NULL}; #if defined(__APPLE__) || defined(__CYGWIN__) static int check_runtime_path(const char *path) { @@ -1722,9 +1727,3 @@ wcstring env_get_runtime_path() { } return result; } - - -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}; diff --git a/src/env.h b/src/env.h index 148144f45..379175880 100644 --- a/src/env.h +++ b/src/env.h @@ -139,6 +139,7 @@ class environment_t { public: virtual maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const = 0; + virtual wcstring_list_t get_names(int flags) const = 0; virtual ~environment_t(); }; @@ -176,9 +177,6 @@ void env_universal_barrier(); /// Sets up argv as the given null terminated array of strings. void env_set_argv(const wchar_t *const *argv); -/// Returns all variable names. -wcstring_list_t env_get_names(int flags); - /// Returns the PWD with a terminating slash. wcstring env_get_pwd_slash(); @@ -242,7 +240,7 @@ class env_stack_t : public environment_t { const char *const *export_arr(); /// Returns all variable names. - wcstring_list_t get_names(int flags); + wcstring_list_t get_names(int flags) const override; /// Sets up argv as the given null terminated array of strings. void set_argv(const wchar_t *const *argv); @@ -262,19 +260,22 @@ class env_stack_t : public environment_t { class env_vars_snapshot_t : public environment_t { std::map vars; + wcstring_list_t names; bool is_current() const; + static const env_vars_snapshot_t s_current; + public: + env_vars_snapshot_t() = default; env_vars_snapshot_t(const env_vars_snapshot_t &) = default; env_vars_snapshot_t &operator=(const env_vars_snapshot_t &) = default; - - env_vars_snapshot_t(const wchar_t *const *keys); - env_vars_snapshot_t(); - - ~env_vars_snapshot_t(); + env_vars_snapshot_t(const environment_t &source, const wchar_t *const *keys); + ~env_vars_snapshot_t() override; maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override; + wcstring_list_t get_names(int flags) const override; + // Returns the fake snapshot representing the live variables array. static const env_vars_snapshot_t ¤t(); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 0f1a430c0..82497060b 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -2338,8 +2338,9 @@ static void test_complete() { const wcstring_list_t names(name_strs, name_strs + count); std::vector completions; complete_set_variable_names(&names); + env_vars_snapshot_t vars; - complete(L"$", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"$", &completions, COMPLETION_REQUEST_DEFAULT, vars); completions_sort_and_prioritize(&completions); do_test(completions.size() == 6); do_test(completions.at(0).completion == L"Bar1"); @@ -2350,7 +2351,7 @@ static void test_complete() { do_test(completions.at(5).completion == L"Foo3"); completions.clear(); - complete(L"$F", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"$F", &completions, COMPLETION_REQUEST_DEFAULT, vars); completions_sort_and_prioritize(&completions); do_test(completions.size() == 3); do_test(completions.at(0).completion == L"oo1"); @@ -2358,12 +2359,13 @@ static void test_complete() { do_test(completions.at(2).completion == L"oo3"); completions.clear(); - complete(L"$1", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"$1", &completions, COMPLETION_REQUEST_DEFAULT, vars); completions_sort_and_prioritize(&completions); do_test(completions.empty()); completions.clear(); - complete(L"$1", &completions, COMPLETION_REQUEST_DEFAULT | COMPLETION_REQUEST_FUZZY_MATCH); + 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"); @@ -2375,24 +2377,25 @@ static void test_complete() { if (system("chmod 700 'test/complete_test/testfile'")) err(L"chmod failed"); completions.clear(); - complete(L"echo (test/complete_test/testfil", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"echo (test/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 (ls test/complete_test/testfil", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"echo (ls test/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 test/complete_test/testfil", &completions, - COMPLETION_REQUEST_DEFAULT); + COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"e"); // Completing after spaces - see #2447 completions.clear(); - complete(L"echo (ls test/complete_test/has\\ ", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"echo (ls test/complete_test/has\\ ", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"space"); @@ -2405,104 +2408,104 @@ static void test_complete() { // Complete a function name. completions.clear(); - complete(L"echo (scuttlebut", &completions, COMPLETION_REQUEST_DEFAULT); + 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. completions.clear(); - complete(L"echo (command scuttlebut", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"echo (command scuttlebut", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 0); // Not with the builtin prefix. completions.clear(); - complete(L"echo (builtin scuttlebut", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"echo (builtin scuttlebut", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 0); // Not after a redirection. completions.clear(); - complete(L"echo hi > scuttlebut", &completions, COMPLETION_REQUEST_DEFAULT); + 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); completions.clear(); - complete(L"foobarbaz ", &completions, COMPLETION_REQUEST_DEFAULT); + 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). completions.clear(); - complete(L"echo '$Foo", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"echo '$Foo", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.empty()); completions.clear(); - complete(L"echo \\$Foo", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"echo \\$Foo", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.empty()); // File completions. completions.clear(); - complete(L"cat test/complete_test/te", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"cat test/complete_test/te", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"stfile"); completions.clear(); - complete(L"echo sup > test/complete_test/te", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"echo sup > test/complete_test/te", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"stfile"); completions.clear(); - complete(L"echo sup > test/complete_test/te", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"echo sup > test/complete_test/te", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"stfile"); if (!pushd("test/complete_test")) return; - complete(L"cat te", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"cat te", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"stfile"); do_test(!(completions.at(0).flags & COMPLETE_REPLACES_TOKEN)); do_test(!(completions.at(0).flags & COMPLETE_DUPLICATES_ARGUMENT)); completions.clear(); - complete(L"cat testfile te", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"cat testfile te", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"stfile"); do_test(completions.at(0).flags & COMPLETE_DUPLICATES_ARGUMENT); completions.clear(); - complete(L"cat testfile TE", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"cat testfile TE", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"testfile"); do_test(completions.at(0).flags & COMPLETE_REPLACES_TOKEN); do_test(completions.at(0).flags & COMPLETE_DUPLICATES_ARGUMENT); completions.clear(); - complete(L"something --abc=te", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"something --abc=te", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"stfile"); completions.clear(); - complete(L"something -abc=te", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"something -abc=te", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"stfile"); completions.clear(); - complete(L"something abc=te", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"something abc=te", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"stfile"); completions.clear(); - complete(L"something abc=stfile", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"something abc=stfile", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.size() == 0); completions.clear(); - complete(L"something abc=stfile", &completions, COMPLETION_REQUEST_FUZZY_MATCH); + complete(L"something abc=stfile", &completions, COMPLETION_REQUEST_FUZZY_MATCH, vars); do_test(completions.size() == 1); do_test(completions.at(0).completion == L"abc=testfile"); // Zero escapes can cause problems. See issue #1631. completions.clear(); - complete(L"cat foo\\0", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"cat foo\\0", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.empty()); completions.clear(); - complete(L"cat foo\\0bar", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"cat foo\\0bar", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.empty()); completions.clear(); - complete(L"cat \\0", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"cat \\0", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.empty()); completions.clear(); - complete(L"cat te\\0", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"cat te\\0", &completions, COMPLETION_REQUEST_DEFAULT, vars); do_test(completions.empty()); popd(); @@ -2510,11 +2513,12 @@ static void test_complete() { complete_set_variable_names(NULL); // Test abbreviations. + auto &pvars = parser_t::principal_parser().vars(); function_data_t fd; fd.name = L"testabbrsonetwothreefour"; function_add(fd, parser_t::principal_parser()); int ret = env_set_one(L"_fish_abbr_testabbrsonetwothreezero", ENV_LOCAL, L"expansion"); - complete(L"testabbrsonetwothree", &completions, COMPLETION_REQUEST_DEFAULT); + complete(L"testabbrsonetwothree", &completions, COMPLETION_REQUEST_DEFAULT, pvars); do_test(ret == 0); do_test(completions.size() == 2); do_test(completions.at(0).completion == L"four"); @@ -2594,7 +2598,7 @@ static void test_completion_insertions() { static void perform_one_autosuggestion_cd_test(const wcstring &command, const wcstring &expected, long line) { std::vector comps; - complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION); + complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, env_vars_snapshot_t{}); bool expects_error = (expected == L""); @@ -2630,7 +2634,7 @@ static void perform_one_autosuggestion_cd_test(const wcstring &command, const wc static void perform_one_completion_cd_test(const wcstring &command, const wcstring &expected, long line) { std::vector comps; - complete(command, &comps, COMPLETION_REQUEST_DEFAULT); + complete(command, &comps, COMPLETION_REQUEST_DEFAULT, env_vars_snapshot_t{}); bool expects_error = (expected == L""); @@ -2758,7 +2762,7 @@ static void test_autosuggest_suggest_special() { static void perform_one_autosuggestion_should_ignore_test(const wcstring &command, long line) { completion_list_t comps; - complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION); + complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, env_vars_snapshot_t{}); do_test(comps.empty()); if (!comps.empty()) { const wcstring &suggestion = comps.front().completion; diff --git a/src/reader.cpp b/src/reader.cpp index 70b6c4a3d..394f5d406 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1279,7 +1279,8 @@ static std::function get_autosuggestion_performer const wcstring &search_string, size_t cursor_pos, history_t *history) { const unsigned int generation_count = read_generation_count(); const wcstring working_directory(env_get_pwd_slash()); - env_vars_snapshot_t vars(env_vars_snapshot_t::highlighting_keys); + env_vars_snapshot_t vars(parser_t::principal_parser().vars(), + env_vars_snapshot_t::highlighting_keys); // TODO: suspicious use of 'history' here // This is safe because histories are immortal, but perhaps // this should use shared_ptr @@ -1329,7 +1330,7 @@ static std::function get_autosuggestion_performer // Try normal completions. completion_request_flags_t complete_flags = COMPLETION_REQUEST_AUTOSUGGESTION; std::vector completions; - complete(search_string, &completions, complete_flags); + complete(search_string, &completions, complete_flags, vars); completions_sort_and_prioritize(&completions, complete_flags); if (!completions.empty()) { const completion_t &comp = completions.at(0); @@ -2201,7 +2202,8 @@ static void highlight_complete(highlight_result_t result) { static std::function get_highlight_performer(const wcstring &text, long match_highlight_pos, bool no_io) { - env_vars_snapshot_t vars(env_vars_snapshot_t::highlighting_keys); + env_vars_snapshot_t vars(parser_t::principal_parser().vars(), + env_vars_snapshot_t::highlighting_keys); unsigned int generation_count = read_generation_count(); highlight_function_t highlight_func = no_io ? highlight_shell_no_io : current_data()->highlight_func; @@ -2683,7 +2685,8 @@ const wchar_t *reader_readline(int nchars) { complete_flags_t complete_flags = COMPLETION_REQUEST_DEFAULT | COMPLETION_REQUEST_DESCRIPTIONS | COMPLETION_REQUEST_FUZZY_MATCH; - data->complete_func(buffcpy, &comp, complete_flags); + data->complete_func(buffcpy, &comp, complete_flags, + parser_t::principal_parser().vars()); // Munge our completions. completions_sort_and_prioritize(&comp); diff --git a/src/reader.h b/src/reader.h index 1ecca836c..3854ae4f7 100644 --- a/src/reader.h +++ b/src/reader.h @@ -149,13 +149,9 @@ void reader_push(const wcstring &name); /// 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. +/// Specify function to use for finding possible tab completions. typedef void (*complete_function_t)(const wcstring &, std::vector *, - completion_request_flags_t); + completion_request_flags_t, const environment_t &); void reader_set_complete_function(complete_function_t); /// The type of a highlight function. From 94adb53b1f9154369e85fc783e15daad866301d5 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Sep 2018 18:36:04 -0700 Subject: [PATCH 134/439] Eliminate complete_set_variable_names --- src/complete.cpp | 20 ++------------------ src/complete.h | 2 -- src/fish_tests.cpp | 19 ++++++++++++------- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/complete.cpp b/src/complete.cpp index fbe453264..36afd5441 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -74,20 +74,6 @@ static const wcstring &C_(const wcstring &s) { return s; } static void complete_load(const wcstring &name, bool reload); -/// Testing apparatus. -const wcstring_list_t *s_override_variable_names = NULL; - -void complete_set_variable_names(const wcstring_list_t *names) { - s_override_variable_names = names; -} - -static inline wcstring_list_t complete_get_variable_names(const environment_t &vars) { - if (s_override_variable_names != NULL) { - return *s_override_variable_names; - } - return vars.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 @@ -1125,10 +1111,8 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) { size_t varlen = str.length() - start_offset; bool res = false; - const wcstring_list_t names = complete_get_variable_names(vars); - for (size_t i = 0; i < names.size(); i++) { - const wcstring &env_name = names.at(i); - + const wcstring_list_t names = vars.get_names(0); + for (const wcstring &env_name : vars.get_names(0)) { string_fuzzy_match_t match = string_fuzzy_match_string(var, env_name, this->max_fuzzy_match_type()); if (match.type == fuzzy_match_none) { diff --git a/src/complete.h b/src/complete.h index 0abd68208..d73ae1d8c 100644 --- a/src/complete.h +++ b/src/complete.h @@ -196,8 +196,6 @@ void append_completion(std::vector *completions, wcstring comp, wcstring desc = wcstring(), int flags = 0, string_fuzzy_match_t match = string_fuzzy_match_t(fuzzy_match_exact)); -/// 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 like another command. bool complete_add_wrapper(const wcstring &command, const wcstring &wrap_target); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 82497060b..24decdb44 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -2333,13 +2333,19 @@ static void test_colors() { static void test_complete() { say(L"Testing complete"); - const wchar_t *name_strs[] = {L"Foo1", L"Foo2", L"Foo3", L"Bar1", L"Bar2", L"Bar3"}; - size_t count = sizeof name_strs / sizeof *name_strs; - const wcstring_list_t names(name_strs, name_strs + count); - std::vector completions; - complete_set_variable_names(&names); - env_vars_snapshot_t vars; + struct test_complete_vars_t : environment_t { + wcstring_list_t get_names(int flags) const override { + return {L"Foo1", L"Foo2", L"Foo3", L"Bar1", L"Bar2", L"Bar3"}; + } + maybe_t get(const wcstring &key, + env_mode_flags_t mode = ENV_DEFAULT) const override { + return {}; + } + }; + test_complete_vars_t vars; + + completion_list_t completions; complete(L"$", &completions, COMPLETION_REQUEST_DEFAULT, vars); completions_sort_and_prioritize(&completions); do_test(completions.size() == 6); @@ -2510,7 +2516,6 @@ static void test_complete() { popd(); completions.clear(); - complete_set_variable_names(NULL); // Test abbreviations. auto &pvars = parser_t::principal_parser().vars(); From 5055621e02d17296d05495ebb3e74520a53b24cb Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Sep 2018 18:59:57 -0700 Subject: [PATCH 135/439] Eliminate env_push and env_pop --- src/builtin_argparse.cpp | 37 ++++++++++++++++++++----------------- src/env.cpp | 16 ++++++---------- src/env.h | 6 ------ src/fish_tests.cpp | 5 +++-- src/parser.cpp | 4 ++-- 5 files changed, 31 insertions(+), 37 deletions(-) diff --git a/src/builtin_argparse.cpp b/src/builtin_argparse.cpp index ae15cfc6e..4fe148032 100644 --- a/src/builtin_argparse.cpp +++ b/src/builtin_argparse.cpp @@ -22,6 +22,7 @@ #include "exec.h" #include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "parser.h" #include "wgetopt.h" // IWYU pragma: keep #include "wutil.h" // IWYU pragma: keep @@ -433,14 +434,14 @@ static void populate_option_strings(const argparse_cmd_opts_t &opts, wcstring *s long_options->push_back({NULL, 0, NULL, 0}); } -static int validate_arg(const argparse_cmd_opts_t &opts, option_spec_t *opt_spec, bool is_long_flag, - const wchar_t *woptarg, io_streams_t &streams) { +static int validate_arg(parser_t &parser, const argparse_cmd_opts_t &opts, option_spec_t *opt_spec, + bool is_long_flag, const wchar_t *woptarg, io_streams_t &streams) { // Obviously if there is no arg validation command we assume the arg is okay. if (opt_spec->validation_command.empty()) return STATUS_CMD_OK; wcstring_list_t cmd_output; - env_push(true); + parser.vars().push(true); env_set_one(L"_argparse_cmd", ENV_LOCAL, opts.name); if (is_long_flag) { env_set_one(var_name_prefix + L"name", ENV_LOCAL, opt_spec->long_flag); @@ -455,7 +456,7 @@ static int validate_arg(const argparse_cmd_opts_t &opts, option_spec_t *opt_spec streams.err.append(output); streams.err.push_back(L'\n'); } - env_pop(); + parser.vars().pop(); return retval; } @@ -473,13 +474,14 @@ static bool is_implicit_int(const argparse_cmd_opts_t &opts, const wchar_t *val) } // Store this value under the implicit int option. -static int validate_and_store_implicit_int(const argparse_cmd_opts_t &opts, const wchar_t *val, - wgetopter_t &w, int long_idx, io_streams_t &streams) { +static int validate_and_store_implicit_int(parser_t &parser, const argparse_cmd_opts_t &opts, + const wchar_t *val, wgetopter_t &w, int long_idx, + io_streams_t &streams) { // See if this option passes the validation checks. auto found = opts.options.find(opts.implicit_int_flag); assert(found != opts.options.end()); const auto &opt_spec = found->second; - int retval = validate_arg(opts, opt_spec.get(), long_idx != -1, val, streams); + int retval = validate_arg(parser, opts, opt_spec.get(), long_idx != -1, val, streams); if (retval != STATUS_CMD_OK) return retval; // It's a valid integer so store it and return success. @@ -490,8 +492,8 @@ static int validate_and_store_implicit_int(const argparse_cmd_opts_t &opts, cons return STATUS_CMD_OK; } -static int handle_flag(const argparse_cmd_opts_t &opts, option_spec_t *opt_spec, int long_idx, - const wchar_t *woptarg, io_streams_t &streams) { +static int handle_flag(parser_t &parser, const argparse_cmd_opts_t &opts, option_spec_t *opt_spec, + int long_idx, const wchar_t *woptarg, io_streams_t &streams) { opt_spec->num_seen++; if (opt_spec->num_allowed == 0) { // It's a boolean flag. Save the flag we saw since it might be useful to know if the @@ -506,7 +508,7 @@ static int handle_flag(const argparse_cmd_opts_t &opts, option_spec_t *opt_spec, } if (woptarg) { - int retval = validate_arg(opts, opt_spec, long_idx != -1, woptarg, streams); + int retval = validate_arg(parser, opts, opt_spec, long_idx != -1, woptarg, streams); if (retval != STATUS_CMD_OK) return retval; } @@ -526,9 +528,9 @@ static int handle_flag(const argparse_cmd_opts_t &opts, option_spec_t *opt_spec, return STATUS_CMD_OK; } -static int argparse_parse_flags(const argparse_cmd_opts_t &opts, const wchar_t *short_options, - const woption *long_options, const wchar_t *cmd, int argc, - wchar_t **argv, int *optind, parser_t &parser, +static int argparse_parse_flags(parser_t &parser, const argparse_cmd_opts_t &opts, + const wchar_t *short_options, const woption *long_options, + const wchar_t *cmd, int argc, wchar_t **argv, int *optind, io_streams_t &streams) { int opt; int long_idx = -1; @@ -544,7 +546,8 @@ static int argparse_parse_flags(const argparse_cmd_opts_t &opts, const wchar_t * const wchar_t *arg_contents = argv[w.woptind - 1] + 1; int retval = STATUS_CMD_OK; if (is_implicit_int(opts, arg_contents)) { - retval = validate_and_store_implicit_int(opts, arg_contents, w, long_idx, streams); + retval = validate_and_store_implicit_int(parser, opts, arg_contents, w, long_idx, + streams); } else { builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]); retval = STATUS_INVALID_ARGS; @@ -558,7 +561,7 @@ static int argparse_parse_flags(const argparse_cmd_opts_t &opts, const wchar_t * auto found = opts.options.find(opt); assert(found != opts.options.end()); - int retval = handle_flag(opts, found->second.get(), long_idx, w.woptarg, streams); + int retval = handle_flag(parser, opts, found->second.get(), long_idx, w.woptarg, streams); if (retval != STATUS_CMD_OK) return retval; long_idx = -1; } @@ -590,8 +593,8 @@ static int argparse_parse_args(argparse_cmd_opts_t &opts, const wcstring_list_t auto argv = argv_container.get(); int optind; - int retval = argparse_parse_flags(opts, short_options.c_str(), long_options.data(), cmd, argc, - argv, &optind, parser, streams); + int retval = argparse_parse_flags(parser, opts, short_options.c_str(), long_options.data(), cmd, + argc, argv, &optind, streams); if (retval != STATUS_CMD_OK) return retval; retval = check_for_mutually_exclusive_flags(opts, streams); diff --git a/src/env.cpp b/src/env.cpp index 50341b890..df7282a72 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -161,7 +161,12 @@ struct var_stack_t { void mark_changed_exported() { has_changed_exported = true; } void update_export_array_if_necessary(); - var_stack_t() : top(globals()), global_env(globals()) {} + var_stack_t() : top(globals()), global_env(globals()) { + // Add a toplevel local scope on top of the global scope. This 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. + push(false); + } // Pushes a new node onto our stack // Optionally creates a new scope for the node @@ -999,11 +1004,6 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { callback_data_list_t callbacks; s_universal_variables->initialize(callbacks); env_universal_callbacks(&env_stack_t::principal(), callbacks); - - // 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 @@ -1438,10 +1438,6 @@ int env_set_empty(const wcstring &key, env_mode_flags_t mode) { int env_remove(const wcstring &key, int mode) { return env_stack_t::principal().remove(key, mode); } -void env_push(bool new_scope) { env_stack_t::principal().push(new_scope); } - -void env_pop() { env_stack_t::principal().pop(); } - void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } void env_set_argv(const wchar_t *const *argv) { return env_stack_t::principal().set_argv(argv); } diff --git a/src/env.h b/src/env.h index 379175880..22606c45c 100644 --- a/src/env.h +++ b/src/env.h @@ -165,12 +165,6 @@ int env_set_empty(const wcstring &key, env_mode_flags_t mode); /// \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. -void env_push(bool new_scope); - -/// 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. void env_universal_barrier(); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 24decdb44..6b6c8b6eb 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1755,7 +1755,8 @@ static void test_ifind_fuzzy() { static void test_abbreviations() { say(L"Testing abbreviations"); - env_push(true); + auto &vars = parser_t::principal_parser().vars(); + vars.push(true); const std::vector> abbreviations = { {L"gc", L"git checkout"}, @@ -1822,7 +1823,7 @@ static void test_abbreviations() { 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(); + vars.pop(); } /// Test path functions. diff --git a/src/parser.cpp b/src/parser.cpp index ce79a0c04..65fcffcd4 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -154,7 +154,7 @@ void parser_t::push_block_int(block_t *new_current) { } if (new_current->type() != TOP) { - env_push(type == FUNCTION_CALL); + vars().push(type == FUNCTION_CALL); new_current->wants_pop_env = true; } } @@ -172,7 +172,7 @@ void parser_t::pop_block(const block_t *expected) { std::unique_ptr old = std::move(block_stack.back()); block_stack.pop_back(); - if (old->wants_pop_env) env_pop(); + if (old->wants_pop_env) vars().pop(); // Figure out if `status is-block` should consider us to be in a block now. bool new_is_block = false; From ede66ccaacad3c9161693c36c04d437f357c7e4d Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Sep 2018 19:17:44 -0700 Subject: [PATCH 136/439] Instance env_set_argv and env_set_pwd --- src/builtin_cd.cpp | 2 +- src/builtin_source.cpp | 2 +- src/env.cpp | 23 ++++++++++------------- src/env.h | 9 ++++++--- src/exec.cpp | 2 +- src/function.cpp | 5 +++-- src/function.h | 3 ++- 7 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/builtin_cd.cpp b/src/builtin_cd.cpp index 10362f444..1ab7a2601 100644 --- a/src/builtin_cd.cpp +++ b/src/builtin_cd.cpp @@ -86,6 +86,6 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return STATUS_CMD_ERROR; } - env_set_one(L"PWD", ENV_EXPORT | ENV_GLOBAL, std::move(norm_dir)); + parser.vars().set_one(L"PWD", ENV_EXPORT | ENV_GLOBAL, std::move(norm_dir)); return STATUS_CMD_OK; } diff --git a/src/builtin_source.cpp b/src/builtin_source.cpp index 1e09b702e..8784b0603 100644 --- a/src/builtin_source.cpp +++ b/src/builtin_source.cpp @@ -78,7 +78,7 @@ int builtin_source(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // This is slightly subtle. If this is a bare `source` with no args then `argv + optind` already // points to the end of argv. Otherwise we want to skip the file name to get to the args if any. - env_set_argv(argv + optind + (argc == optind ? 0 : 1)); + parser.vars().set_argv(argv + optind + (argc == optind ? 0 : 1)); retval = reader_read(fd, streams.io_chain ? *streams.io_chain : io_chain_t()); diff --git a/src/env.cpp b/src/env.cpp index df7282a72..c147e22d7 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -642,11 +642,11 @@ static void setup_path() { /// If they don't already exist initialize the `COLUMNS` and `LINES` env vars to reasonable /// defaults. They will be updated later by the `get_current_winsize()` function if they need to be /// adjusted. -static void env_set_termsize() { - auto cols = env_get(L"COLUMNS"); +void env_stack_t::set_termsize() { + auto cols = get(L"COLUMNS"); if (cols.missing_or_empty()) env_set_one(L"COLUMNS", ENV_GLOBAL, DFLT_TERM_COL_STR); - auto rows = env_get(L"LINES"); + auto rows = get(L"LINES"); if (rows.missing_or_empty()) env_set_one(L"LINES", ENV_GLOBAL, DFLT_TERM_ROW_STR); } @@ -708,8 +708,6 @@ static void setup_user(bool force) { /// Various things we need to initialize at run-time that don't really fit any of the other init /// routines. void misc_init() { - env_set_read_limit(); - // If stdout is open on a tty ensure stdio is unbuffered. That's because those functions might // be intermixed with `write()` calls and we need to ensure the writes are not reordered. See // issue #3748. @@ -976,21 +974,22 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { } } + env_stack_t &vars = env_stack_t::principal(); // initialize the PWD variable if necessary // Note we may inherit a virtual PWD that doesn't match what getcwd would return; respect that. - if (env_get(L"PWD").missing_or_empty()) { - env_stack_t::principal().set_pwd_from_getcwd(); + if (vars.get(L"PWD").missing_or_empty()) { + vars.set_pwd_from_getcwd(); } - env_set_termsize(); // initialize the terminal size variables - env_set_read_limit(); // initialize the read_byte_limit + vars.set_termsize(); // initialize the terminal size variables + vars.set_read_limit(); // initialize the read_byte_limit // Set g_use_posix_spawn. Default to true. - auto use_posix_spawn = env_get(L"fish_use_posix_spawn"); + auto use_posix_spawn = vars.get(L"fish_use_posix_spawn"); g_use_posix_spawn = use_posix_spawn.missing_or_empty() ? true : from_string(use_posix_spawn->as_string()); // Set fish_bind_mode to "default". - env_set_one(FISH_BIND_MODE_VAR, ENV_GLOBAL, DEFAULT_BIND_MODE); + vars.set_one(FISH_BIND_MODE_VAR, ENV_GLOBAL, DEFAULT_BIND_MODE); // This is somewhat subtle. At this point we consider our environment to be sufficiently // initialized that we can react to changes to variables. Prior to doing this we expect that the @@ -1440,8 +1439,6 @@ int env_remove(const wcstring &key, int mode) { return env_stack_t::principal(). void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } -void env_set_argv(const wchar_t *const *argv) { return env_stack_t::principal().set_argv(argv); } - wcstring env_get_pwd_slash() { return env_stack_t::principal().get_pwd_slash(); } void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); } diff --git a/src/env.h b/src/env.h index 22606c45c..83e6434e4 100644 --- a/src/env.h +++ b/src/env.h @@ -168,9 +168,6 @@ int env_remove(const wcstring &key, int mode); /// Synchronizes all universal variable changes: writes everything out, reads stuff in. void env_universal_barrier(); -/// Sets up argv as the given null terminated array of strings. -void env_set_argv(const wchar_t *const *argv); - /// Returns the PWD with a terminating slash. wcstring env_get_pwd_slash(); @@ -236,6 +233,12 @@ class env_stack_t : public environment_t { /// Returns all variable names. wcstring_list_t get_names(int flags) const override; + /// Update the termsize variable. + void set_termsize(); + + /// Update the PWD variable directory. + bool set_pwd(); + /// Sets up argv as the given null terminated array of strings. void set_argv(const wchar_t *const *argv); diff --git a/src/exec.cpp b/src/exec.cpp index 5b13d7b23..3e7af698a 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -787,7 +787,7 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr function_block_t *fb = parser.push_block(p, func_name, props->shadow_scope); - function_prepare_environment(func_name, p->get_argv() + 1, inherit_vars); + function_prepare_environment(parser.vars(), func_name, p->get_argv() + 1, inherit_vars); parser.forbid_function(func_name); internal_exec_helper(parser, props->parsed_source, props->body_node, io_chain, j); diff --git a/src/function.cpp b/src/function.cpp index f709b08f0..c5438bf36 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -344,9 +344,10 @@ void function_invalidate_path() { function_autoloader.invalidate(); } // 1. argv // 2. named arguments // 3. inherited variables -void function_prepare_environment(const wcstring &name, const wchar_t *const *argv, +void function_prepare_environment(env_stack_t &vars, const wcstring &name, + const wchar_t *const *argv, const std::map &inherited_vars) { - env_set_argv(argv); + vars.set_argv(argv); auto props = function_get_properties(name); if (props && !props->named_arguments.empty()) { const wchar_t *const *arg = argv; diff --git a/src/function.h b/src/function.h index d823bdeeb..9ee7df6eb 100644 --- a/src/function.h +++ b/src/function.h @@ -109,7 +109,8 @@ std::map function_get_inherit_vars(const wcstring &name); bool function_copy(const wcstring &name, const wcstring &new_name); /// Prepares the environment for executing a function. -void function_prepare_environment(const wcstring &name, const wchar_t *const *argv, +void function_prepare_environment(env_stack_t &vars, const wcstring &name, + const wchar_t *const *argv, const std::map &inherited_vars); /// Observes that fish_function_path has changed. From a00de96a57886f85aa6f48aa83a454f6bac9de87 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Sep 2018 20:57:10 -0700 Subject: [PATCH 137/439] Instance env_remove --- src/builtin_set.cpp | 2 +- src/env.cpp | 2 -- src/env.h | 10 ---------- src/fish_tests.cpp | 13 +++++++------ 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index 6ab298a0b..4e5f253e8 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -652,7 +652,7 @@ static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, } if (idx_count == 0) { // unset the var - retval = env_remove(dest, scope); + retval = parser.vars().remove(dest, scope); // Temporarily swallowing ENV_NOT_FOUND errors to prevent // breaking all tests that unset variables that aren't set. if (retval != ENV_NOT_FOUND) { diff --git a/src/env.cpp b/src/env.cpp index c147e22d7..6258c845a 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1435,8 +1435,6 @@ int env_set_empty(const wcstring &key, env_mode_flags_t mode) { return env_stack_t::principal().set_empty(key, mode); } -int env_remove(const wcstring &key, int mode) { return env_stack_t::principal().remove(key, mode); } - void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } wcstring env_get_pwd_slash() { return env_stack_t::principal().get_pwd_slash(); } diff --git a/src/env.h b/src/env.h index 83e6434e4..a10880d3b 100644 --- a/src/env.h +++ b/src/env.h @@ -155,16 +155,6 @@ int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val); /// Sets the variable with the specified name to no values. int env_set_empty(const wcstring &key, env_mode_flags_t mode); -/// 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); - /// Synchronizes all universal variable changes: writes everything out, reads stuff in. void env_universal_barrier(); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 6b6c8b6eb..6d241e22c 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -2762,7 +2762,7 @@ static void test_autosuggest_suggest_special() { perform_one_completion_cd_test(L"cd ~haha", L"ha/", __LINE__); perform_one_completion_cd_test(L"cd ~hahaha/", L"path1/", __LINE__); - env_remove(L"HOME", ENV_LOCAL | ENV_EXPORT); + parser_t::principal_parser().vars().remove(L"HOME", ENV_LOCAL | ENV_EXPORT); popd(); } @@ -4284,9 +4284,10 @@ static void test_highlighting() { {L"self%not", highlight_spec_param}, }); + auto &vars = parser_t::principal_parser().vars(); // Verify variables and wildcards in commands using /bin/cat. - env_set(L"VARIABLE_IN_COMMAND", ENV_LOCAL, {L"a"}); - env_set(L"VARIABLE_IN_COMMAND2", ENV_LOCAL, {L"at"}); + vars.set(L"VARIABLE_IN_COMMAND", ENV_LOCAL, {L"a"}); + vars.set(L"VARIABLE_IN_COMMAND2", ENV_LOCAL, {L"at"}); highlight_tests.push_back( {{L"/bin/ca", highlight_spec_command, ns}, {L"*", highlight_spec_operator, ns}}); @@ -4319,7 +4320,7 @@ static void test_highlighting() { do_test(expected_colors.size() == text.size()); std::vector colors(text.size()); - highlight_shell(text, colors, 20, NULL, env_vars_snapshot_t::current()); + highlight_shell(text, colors, 20, NULL, vars); if (expected_colors.size() != colors.size()) { err(L"Color vector has wrong size! Expected %lu, actual %lu", expected_colors.size(), @@ -4338,8 +4339,8 @@ static void test_highlighting() { } } } - env_remove(L"VARIABLE_IN_COMMAND", ENV_DEFAULT); - env_remove(L"VARIABLE_IN_COMMAND2", ENV_DEFAULT); + vars.remove(L"VARIABLE_IN_COMMAND", ENV_DEFAULT); + vars.remove(L"VARIABLE_IN_COMMAND2", ENV_DEFAULT); } static void test_wcstring_tok() { From 26fc705c07c4530d38bed3180c1c8883e7d5e0c7 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Sep 2018 21:27:25 -0700 Subject: [PATCH 138/439] Instance env_set_empty --- src/builtin_read.cpp | 3 ++- src/env.cpp | 19 +++++++------------ src/env.h | 3 --- src/function.cpp | 2 +- src/parse_execution.cpp | 2 +- 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 0ce1e3be0..4494ae886 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -26,6 +26,7 @@ #include "highlight.h" #include "history.h" #include "io.h" +#include "parser.h" #include "proc.h" #include "reader.h" #include "wcstringutil.h" @@ -452,7 +453,7 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { auto vars_left = [&] () { return argv + argc - var_ptr; }; auto clear_remaining_vars = [&] () { while (vars_left()) { - env_set_empty(*var_ptr, opts.place); + parser.vars().set_empty(*var_ptr, opts.place); // env_set_one(*var_ptr, opts.place, L""); ++var_ptr; } diff --git a/src/env.cpp b/src/env.cpp index 6258c845a..4d7305a19 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -852,6 +852,7 @@ static void setup_var_dispatch_table() { void env_init(const struct config_paths_t *paths /* or NULL */) { setup_var_dispatch_table(); + env_stack_t &vars = env_stack_t::principal(); // Now the environment 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 @@ -866,7 +867,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { if (eql == wcstring::npos) { // No equal-sign found so treat it as a defined var that has no value(s). if (is_read_only(key_and_val) || is_electric(key_and_val)) continue; - env_set_empty(key_and_val, ENV_EXPORT | ENV_GLOBAL); + vars.set_empty(key_and_val, ENV_EXPORT | ENV_GLOBAL); } else { key.assign(key_and_val, 0, eql); val.assign(key_and_val, eql+1, wcstring::npos); @@ -965,16 +966,15 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { } else { // We cannot get $HOME. This triggers warnings for history and config.fish already, // so it isn't necessary to warn here as well. - env_set_empty(L"HOME", ENV_GLOBAL | ENV_EXPORT); + vars.set_empty(L"HOME", ENV_GLOBAL | ENV_EXPORT); } free(unam_narrow); } else { // If $USER is empty as well (which we tried to set above), we can't get $HOME. - env_set_empty(L"HOME", ENV_GLOBAL | ENV_EXPORT); + vars.set_empty(L"HOME", ENV_GLOBAL | ENV_EXPORT); } } - env_stack_t &vars = env_stack_t::principal(); // initialize the PWD variable if necessary // Note we may inherit a virtual PWD that doesn't match what getcwd would return; respect that. if (vars.get(L"PWD").missing_or_empty()) { @@ -1431,10 +1431,6 @@ int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) { return env_stack_t::principal().set_one(key, mode, std::move(val)); } -int env_set_empty(const wcstring &key, env_mode_flags_t mode) { - return env_stack_t::principal().set_empty(key, mode); -} - void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } wcstring env_get_pwd_slash() { return env_stack_t::principal().get_pwd_slash(); } @@ -1599,12 +1595,11 @@ void env_stack_t::set_argv(const wchar_t *const *argv) { if (argv && *argv) { wcstring_list_t list; for (auto arg = argv; *arg; arg++) { - list.push_back(*arg); + list.emplace_back(*arg); } - - env_set(L"argv", ENV_LOCAL, list); + set(L"argv", ENV_LOCAL, std::move(list)); } else { - env_set_empty(L"argv", ENV_LOCAL); + set_empty(L"argv", ENV_LOCAL); } } diff --git a/src/env.h b/src/env.h index a10880d3b..918df75f1 100644 --- a/src/env.h +++ b/src/env.h @@ -152,9 +152,6 @@ int env_set(const wcstring &key, env_mode_flags_t mode, wcstring_list_t vals); /// Sets the variable with the specified name to a single value. int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val); -/// Sets the variable with the specified name to no values. -int env_set_empty(const wcstring &key, env_mode_flags_t mode); - /// Synchronizes all universal variable changes: writes everything out, reads stuff in. void env_universal_barrier(); diff --git a/src/function.cpp b/src/function.cpp index c5438bf36..e8a424414 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -356,7 +356,7 @@ void function_prepare_environment(env_stack_t &vars, const wcstring &name, env_set_one(named_arg, ENV_LOCAL | ENV_USER, *arg); arg++; } else { - env_set_empty(named_arg, ENV_LOCAL | ENV_USER); + vars.set_empty(named_arg, ENV_LOCAL | ENV_USER); } } } diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 09b7f7d9d..a56fe4815 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -387,7 +387,7 @@ parse_execution_result_t parse_execution_context_t::run_for_statement( auto var = env_get(for_var_name, ENV_LOCAL); if (!var && !is_function_context()) var = env_get(for_var_name, ENV_DEFAULT); if (!var || var->read_only()) { - int retval = env_set_empty(for_var_name, ENV_LOCAL | ENV_USER); + int retval = parser->vars().set_empty(for_var_name, ENV_LOCAL | ENV_USER); if (retval != ENV_OK) { report_error(var_name_node, L"You cannot use read-only variable '%ls' in a for loop", for_var_name.c_str()); From 421fbdd52a16afae82765c0029c6b63102025e46 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Sep 2018 22:29:52 -0700 Subject: [PATCH 139/439] Instantize env_get_pwd_slash This requires threading environment_t through many places, such as completions and history. We introduce null_environment_t for when the environment isn't important. --- src/builtin_cd.cpp | 2 +- src/complete.cpp | 13 +++++++------ src/env.cpp | 11 ++++++++--- src/env.h | 19 +++++++++++++------ src/expand.cpp | 42 ++++++++++++++++++++++++----------------- src/expand.h | 12 ++++++++---- src/fish_tests.cpp | 24 +++++++++++++++++++---- src/highlight.cpp | 27 +++++++++++++------------- src/history.cpp | 6 +++--- src/history.h | 2 +- src/parse_execution.cpp | 20 ++++++++++++-------- src/parse_util.cpp | 5 +++-- src/parser.cpp | 4 +++- src/parser.h | 2 +- src/reader.cpp | 14 ++++++++------ 15 files changed, 127 insertions(+), 76 deletions(-) diff --git a/src/builtin_cd.cpp b/src/builtin_cd.cpp index 1ab7a2601..5a0432e63 100644 --- a/src/builtin_cd.cpp +++ b/src/builtin_cd.cpp @@ -47,7 +47,7 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { dir_in = maybe_dir_in->as_string(); } - if (!path_get_cdpath(dir_in, &dir, env_get_pwd_slash())) { + if (!path_get_cdpath(dir_in, &dir, parser.vars().get_pwd_slash())) { if (errno == ENOTDIR) { streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd, dir_in.c_str()); } else if (errno == ENOENT) { diff --git a/src/complete.cpp b/src/complete.cpp index 36afd5441..5e1ee9f9c 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -535,7 +535,7 @@ void completer_t::complete_strings(const wcstring &wc_escaped, const description const std::vector &possible_comp, complete_flags_t flags) { wcstring tmp = wc_escaped; - if (!expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags(), NULL)) + if (!expand_one(tmp, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this->expand_flags(), vars)) return; const wcstring wc = parse_util_unescape_wildcards(tmp); @@ -656,7 +656,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool expand_error_t result = expand_string(str_cmd, &this->completions, EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS | EXECUTABLES_ONLY | this->expand_flags(), - NULL); + vars, NULL); if (result != EXPAND_ERROR && this->wants_descriptions()) { this->complete_cmd_desc(str_cmd); } @@ -668,7 +668,8 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool expand_error_t ignore = // Append all matching directories expand_string(str_cmd, &this->completions, - EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), NULL); + EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), vars, + NULL); UNUSED(ignore); } @@ -740,7 +741,7 @@ void completer_t::complete_from_args(const wcstring &str, const wcstring &args, } std::vector possible_comp; - parser_t::expand_argument_list(args, eflags, &possible_comp); + parser_t::expand_argument_list(args, eflags, vars, &possible_comp); if (!is_autosuggest) { proc_pop_interactive(); @@ -1079,7 +1080,7 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file, // See #4954. 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, vars, NULL) == EXPAND_ERROR) { debug(3, L"Error while expanding string '%ls'", sep_string.c_str()); } @@ -1098,7 +1099,7 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file, // 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 (expand_string(str, &this->completions, flags, vars, NULL) == EXPAND_ERROR) { debug(3, L"Error while expanding string '%ls'", str.c_str()); } } diff --git a/src/env.cpp b/src/env.cpp index 4d7305a19..82ca99df5 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -677,7 +677,7 @@ void env_stack_t::set_read_limit() { void env_stack_t::mark_changed_exported() { vars_stack().mark_changed_exported(); } -wcstring env_stack_t::get_pwd_slash() { +wcstring environment_t::get_pwd_slash() const { // Return "/" if PWD is missing. // See https://github.com/fish-shell/fish-shell/issues/5080 auto pwd_var = env_get(L"PWD"); @@ -1433,8 +1433,6 @@ int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) { void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } -wcstring env_get_pwd_slash() { return env_stack_t::principal().get_pwd_slash(); } - void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); } /// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported @@ -1606,6 +1604,13 @@ void env_stack_t::set_argv(const wchar_t *const *argv) { environment_t::~environment_t() = default; env_stack_t::~env_stack_t() = default; +null_environment_t::null_environment_t() = default; +null_environment_t::~null_environment_t() = default; +maybe_t null_environment_t::get(const wcstring &key, env_mode_flags_t mode) const { + return none(); +} +wcstring_list_t null_environment_t::get_names(int flags) const { return {}; } + env_stack_t &env_stack_t::principal() { static env_stack_t s_principal; return s_principal; diff --git a/src/env.h b/src/env.h index 918df75f1..e7e6855e8 100644 --- a/src/env.h +++ b/src/env.h @@ -141,6 +141,19 @@ class environment_t { env_mode_flags_t mode = ENV_DEFAULT) const = 0; virtual wcstring_list_t get_names(int flags) const = 0; virtual ~environment_t(); + + /// Returns the PWD with a terminating slash. + wcstring get_pwd_slash() const; +}; + +/// The null environment contains nothing. +class null_environment_t : public environment_t { + public: + null_environment_t(); + ~null_environment_t() override; + + maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override; + wcstring_list_t get_names(int flags) const override; }; /// Gets the variable with the specified name, or none() if it does not exist. @@ -155,9 +168,6 @@ int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val); /// Synchronizes all universal variable changes: writes everything out, reads stuff in. void env_universal_barrier(); -/// Returns the PWD with a terminating slash. -wcstring env_get_pwd_slash(); - /// Update the read_byte_limit variable. void env_set_read_limit(); @@ -229,9 +239,6 @@ class env_stack_t : public environment_t { /// Sets up argv as the given null terminated array of strings. void set_argv(const wchar_t *const *argv); - /// Returns the PWD with a terminating slash. - wcstring get_pwd_slash(); - /// Update the read_byte_limit variable. void set_read_limit(); diff --git a/src/expand.cpp b/src/expand.cpp index 2f4dc27a9..4b546839e 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -883,15 +883,17 @@ static void remove_internal_separator(wcstring *str, bool conv) { } /// 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 +/// of output (by reference). We get flags, vars and errors. It may return an error; if so expansion /// halts. typedef expand_error_t (*expand_stage_t)(wcstring input, //!OCLINT(unused param) std::vector *out, //!OCLINT(unused param) expand_flags_t flags, //!OCLINT(unused param) + const environment_t &vars, //!OCLINT(unused param) parse_error_list_t *errors); //!OCLINT(unused param) static expand_error_t expand_stage_cmdsubst(wcstring input, std::vector *out, - expand_flags_t flags, parse_error_list_t *errors) { + expand_flags_t flags, const environment_t &vars, + parse_error_list_t *errors) { if (EXPAND_SKIP_CMDSUBST & flags) { wchar_t *begin, *end; if (parse_util_locate_cmdsubst(input.c_str(), &begin, &end, true) == 0) { @@ -910,7 +912,8 @@ static expand_error_t expand_stage_cmdsubst(wcstring input, std::vector *out, - expand_flags_t flags, parse_error_list_t *errors) { + expand_flags_t flags, const environment_t &vars, + parse_error_list_t *errors) { // We accept incomplete strings here, since complete uses expand_string to expand incomplete // strings from the commandline. wcstring next; @@ -933,12 +936,14 @@ static expand_error_t expand_stage_variables(wcstring input, std::vector *out, - expand_flags_t flags, parse_error_list_t *errors) { + expand_flags_t flags, const environment_t &vars, + parse_error_list_t *errors) { return expand_braces(input, flags, out, errors); } static expand_error_t expand_stage_home_and_self(wcstring input, std::vector *out, - expand_flags_t flags, parse_error_list_t *errors) { + expand_flags_t flags, const environment_t &vars, + parse_error_list_t *errors) { (void)errors; if (!(EXPAND_SKIP_HOME_DIRECTORIES & flags)) { expand_home_directory(input); @@ -948,8 +953,8 @@ static expand_error_t expand_stage_home_and_self(wcstring input, std::vector *out, expand_flags_t flags, +static expand_error_t expand_stage_wildcards(wcstring path_to_expand, std::vector *out, + expand_flags_t flags, const environment_t &vars, parse_error_list_t *errors) { UNUSED(errors); expand_error_t result = EXPAND_OK; @@ -968,7 +973,7 @@ static expand_error_t expand_stage_wildcards(wcstring path_to_expand, // // 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(); + const wcstring working_dir = vars.get_pwd_slash(); wcstring_list_t effective_working_dirs; bool for_cd = static_cast(flags & EXPAND_SPECIAL_FOR_CD); bool for_command = static_cast(flags & EXPAND_SPECIAL_FOR_COMMAND); @@ -1042,7 +1047,8 @@ static expand_error_t expand_stage_wildcards(wcstring path_to_expand, } expand_error_t expand_string(wcstring input, std::vector *out_completions, - expand_flags_t flags, parse_error_list_t *errors) { + expand_flags_t flags, const environment_t &vars, + 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, std::move(input)); @@ -1064,7 +1070,7 @@ expand_error_t expand_string(wcstring input, std::vector *out_comp for (size_t i = 0; total_result != EXPAND_ERROR && i < completions.size(); i++) { wcstring &next = completions.at(i).completion; expand_error_t this_result = - stages[stage_idx](std::move(next), &output_storage, flags, errors); + stages[stage_idx](std::move(next), &output_storage, flags, vars, 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 && @@ -1090,14 +1096,15 @@ expand_error_t expand_string(wcstring input, std::vector *out_comp 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, const environment_t &vars, + parse_error_list_t *errors) { std::vector completions; if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(string)) { return true; } - if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, errors) && + if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, vars, errors) && completions.size() == 1) { string = std::move(completions.at(0).completion); return true; @@ -1105,8 +1112,9 @@ bool expand_one(wcstring &string, expand_flags_t flags, parse_error_list_t *erro return false; } -expand_error_t expand_to_command_and_args(const wcstring &instr, wcstring *out_cmd, - wcstring_list_t *out_args, parse_error_list_t *errors) { +expand_error_t expand_to_command_and_args(const wcstring &instr, const environment_t &vars, + wcstring *out_cmd, wcstring_list_t *out_args, + parse_error_list_t *errors) { // Fast path. if (expand_is_clean(instr)) { *out_cmd = instr; @@ -1114,9 +1122,9 @@ expand_error_t expand_to_command_and_args(const wcstring &instr, wcstring *out_c } std::vector completions; - expand_error_t expand_err = - expand_string(instr, &completions, - EXPAND_SKIP_CMDSUBST | EXPAND_NO_DESCRIPTIONS | EXPAND_SKIP_JOBS, errors); + expand_error_t expand_err = expand_string( + instr, &completions, EXPAND_SKIP_CMDSUBST | EXPAND_NO_DESCRIPTIONS | EXPAND_SKIP_JOBS, vars, + errors); if (expand_err == EXPAND_OK || expand_err == EXPAND_WILDCARD_MATCH) { // The first completion is the command, any remaning are arguments. bool first = true; diff --git a/src/expand.h b/src/expand.h index f7336d405..95b3c151b 100644 --- a/src/expand.h +++ b/src/expand.h @@ -17,6 +17,7 @@ #include "parse_constants.h" class env_var_t; +class environment_t; enum { /// Flag specifying that cmdsubst expansion should be skipped. @@ -111,13 +112,15 @@ enum expand_error_t { /// \param output The list to which the result will be appended. /// \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 vars variables used during expansion. /// \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(wcstring input, std::vector *output, - expand_flags_t flags, parse_error_list_t *errors); + expand_flags_t flags, const environment_t &vars, + 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. @@ -128,7 +131,8 @@ __warn_unused expand_error_t expand_string(wcstring input, std::vector get(const wcstring &key, + env_mode_flags_t mode = ENV_DEFAULT) const override { + if (key == L"PWD") { + return env_var_t{wgetcwd(), 0}; + } + return {}; + } + + wcstring_list_t get_names(int flags) const override { return {L"PWD"}; } +}; + /// Perform parameter expansion and test if the output equals the zero-terminated parameter list /// supplied. /// @@ -1515,7 +1528,7 @@ static bool expand_test(const wchar_t *in, expand_flags_t flags, ...) { wchar_t *arg; parse_error_list_t errors; - if (expand_string(in, &output, flags, &errors) == EXPAND_ERROR) { + if (expand_string(in, &output, flags, pwd_environment_t{}, &errors) == EXPAND_ERROR) { if (errors.empty()) { err(L"Bug: Parse error reported but no error text found."); } else { @@ -2173,7 +2186,7 @@ static bool run_test_test(int expected, const wcstring &str) { // We need to tokenize the string in the same manner a normal shell would do. This is because we // need to test things like quoted strings that have leading and trailing whitespace. - parser_t::expand_argument_list(str, 0, &comps); + parser_t::expand_argument_list(str, 0, null_environment_t{}, &comps); for (completion_list_t::const_iterator it = comps.begin(), end = comps.end(); it != end; ++it) { argv.push_back(it->completion); } @@ -2341,6 +2354,9 @@ static void test_complete() { maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override { + if (key == L"PWD") { + return env_var_t{wgetcwd(), 0}; + } return {}; } }; @@ -2604,7 +2620,7 @@ static void test_completion_insertions() { static void perform_one_autosuggestion_cd_test(const wcstring &command, const wcstring &expected, long line) { std::vector comps; - complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, env_vars_snapshot_t{}); + complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, pwd_environment_t{}); bool expects_error = (expected == L""); diff --git a/src/highlight.cpp b/src/highlight.cpp index 211d7c0d7..7b8e4ddff 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -243,11 +243,11 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d // appropriately for commands. If we succeed, return true. static bool plain_statement_get_expanded_command(const wcstring &src, tnode_t stmt, - wcstring *out_cmd) { + const environment_t &vars, wcstring *out_cmd) { // Get the command. Try expanding it. If we cannot, it's an error. maybe_t cmd = command_for_plain_statement(stmt, src); if (!cmd) return false; - expand_error_t err = expand_to_command_and_args(*cmd, out_cmd, nullptr); + expand_error_t err = expand_to_command_and_args(*cmd, vars, out_cmd, nullptr); return err == EXPAND_OK || err == EXPAND_WILDCARD_MATCH; } @@ -310,8 +310,8 @@ static bool has_expand_reserved(const wcstring &str) { // Parse a command line. Return by reference the last command, and the last argument to that command // (as a string), if any. This is used by autosuggestions. -static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expanded_command, - wcstring *out_last_arg) { +static bool autosuggest_parse_command(const wcstring &buff, const environment_t &vars, + wcstring *out_expanded_command, wcstring *out_last_arg) { // Parse the buffer. parse_node_tree_t parse_tree; parse_tree_from_string(buff, @@ -321,7 +321,7 @@ static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expand // Find the last statement. auto last_statement = parse_tree.find_last_node(); if (last_statement && - plain_statement_get_expanded_command(buff, last_statement, out_expanded_command)) { + plain_statement_get_expanded_command(buff, last_statement, vars, out_expanded_command)) { // Find the last argument. If we don't get one, return an invalid node. if (auto last_arg = parse_tree.find_last_node(last_statement)) { *out_last_arg = last_arg.get_source(buff); @@ -341,11 +341,11 @@ bool autosuggest_validate_from_history(const history_item_t &item, // Parse the string. wcstring parsed_command; wcstring cd_dir; - if (!autosuggest_parse_command(item.str(), &parsed_command, &cd_dir)) return false; + if (!autosuggest_parse_command(item.str(), vars, &parsed_command, &cd_dir)) return false; if (parsed_command == L"cd" && !cd_dir.empty()) { // We can possibly handle this specially. - if (expand_one(cd_dir, EXPAND_SKIP_CMDSUBST)) { + if (expand_one(cd_dir, EXPAND_SKIP_CMDSUBST, vars)) { handled = true; bool is_help = string_prefixes_string(cd_dir, L"--help") || string_prefixes_string(cd_dir, L"-h"); @@ -823,7 +823,7 @@ bool highlighter_t::is_cd(tnode_t stmt) const { bool cmd_is_cd = false; if (this->io_ok && stmt.has_source()) { wcstring cmd_str; - if (plain_statement_get_expanded_command(this->buff, stmt, &cmd_str)) { + if (plain_statement_get_expanded_command(this->buff, stmt, vars, &cmd_str)) { cmd_is_cd = (cmd_str == L"cd"); } } @@ -840,7 +840,7 @@ void highlighter_t::color_arguments(const std::vector> &arg if (cmd_is_cd) { // Mark this as an error if it's not 'help' and not a valid cd path. wcstring param = arg.get_source(this->buff); - if (expand_one(param, EXPAND_SKIP_CMDSUBST)) { + if (expand_one(param, EXPAND_SKIP_CMDSUBST, vars)) { bool is_help = string_prefixes_string(param, L"--help") || string_prefixes_string(param, L"-h"); if (!is_help && this->io_ok && @@ -883,7 +883,7 @@ void highlighter_t::color_redirection(tnode_t redirection_node) // 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)) { + } else if (!expand_one(target, EXPAND_SKIP_CMDSUBST, vars)) { // Could not be expanded. target_is_valid = false; } else { @@ -1117,7 +1117,8 @@ const highlighter_t::color_array_t &highlighter_t::highlight() { wcstring expanded_cmd; // Check to see if the command is valid. // Try expanding it. If we cannot, it's an error. - bool expanded = plain_statement_get_expanded_command(buff, stmt, &expanded_cmd); + bool expanded = + plain_statement_get_expanded_command(buff, stmt, vars, &expanded_cmd); if (expanded && !has_expand_reserved(expanded_cmd)) { is_valid_cmd = command_is_valid(expanded_cmd, decoration, working_directory, vars); @@ -1200,7 +1201,7 @@ void highlight_shell(const wcstring &buff, std::vector &color, UNUSED(error); // 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(); + const wcstring working_directory = vars.get_pwd_slash(); // Highlight it! highlighter_t highlighter(buff, pos, vars, working_directory, true /* can do IO */); @@ -1212,7 +1213,7 @@ void highlight_shell_no_io(const wcstring &buff, std::vector & UNUSED(error); // 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(); + const wcstring working_directory = vars.get_pwd_slash(); // Highlight it! highlighter_t highlighter(buff, pos, vars, working_directory, false /* no IO allowed */); diff --git a/src/history.cpp b/src/history.cpp index 8ee7974e8..f978914ed 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -1918,7 +1918,8 @@ static bool string_could_be_path(const wcstring &potential_path) { return true; } -void history_t::add_pending_with_file_detection(const wcstring &str) { +void history_t::add_pending_with_file_detection(const wcstring &str, + const wcstring &working_dir_slash) { ASSERT_IS_MAIN_THREAD(); // Find all arguments that look like they could be file paths. @@ -1967,8 +1968,7 @@ void history_t::add_pending_with_file_detection(const wcstring &str) { // Check for which paths are valid on a background thread, // then on the main thread update our history item - const wcstring wd = env_get_pwd_slash(); - iothread_perform([=]() { return valid_paths(potential_paths, wd); }, + iothread_perform([=]() { return valid_paths(potential_paths, working_dir_slash); }, [=](path_list_t validated_paths) { this->set_valid_file_paths(validated_paths, identifier); this->enable_automatic_saving(); diff --git a/src/history.h b/src/history.h index 6419cdcc9..4b50554b6 100644 --- a/src/history.h +++ b/src/history.h @@ -229,7 +229,7 @@ class history_t { // 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); + void add_pending_with_file_detection(const wcstring &str, const wcstring &working_dir_slash); // Resolves any pending history items, so that they may be returned in history searches. void resolve_pending(); diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index a56fe4815..c0a98e5cb 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -121,13 +121,14 @@ tnode_t parse_execution_context_t::infinite_recursive_statem // are not infinite recursion. In particular that is what enables 'wrapper functions'. tnode_t statement = first_job.child<0>(); tnode_t continuation = first_job.child<1>(); + const null_environment_t nullenv{}; while (statement) { tnode_t plain_statement = statement.try_get_child() .try_get_child(); if (plain_statement) { maybe_t cmd = command_for_plain_statement(plain_statement, pstree->src); - if (cmd && expand_one(*cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, NULL) && + if (cmd && expand_one(*cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES, nullenv) && cmd == forbidden_function_name) { // This is it. infinite_recursive_statement = plain_statement; @@ -371,7 +372,7 @@ parse_execution_result_t parse_execution_context_t::run_for_statement( // in just one. tnode_t var_name_node = header.child<1>(); wcstring for_var_name = get_source(var_name_node); - if (!expand_one(for_var_name, 0, NULL)) { + if (!expand_one(for_var_name, 0, parser->vars())) { report_error(var_name_node, FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG, for_var_name.c_str()); return parse_execution_errored; } @@ -438,8 +439,8 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement( // 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, + parser->vars(), &errors); parse_error_offset_source_start(&errors, switch_value_n.source_range()->start); switch (expand_ret) { @@ -722,7 +723,8 @@ parse_execution_result_t parse_execution_context_t::expand_command( wcstring_list_t args; // Expand the string to produce completions, and report errors. - expand_error_t expand_err = expand_to_command_and_args(unexp_cmd, out_cmd, out_args, &errors); + expand_error_t expand_err = + expand_to_command_and_args(unexp_cmd, parser->vars(), out_cmd, out_args, &errors); if (expand_err == EXPAND_ERROR) { proc_set_last_status(STATUS_ILLEGAL_CMD); return report_errors(errors); @@ -813,7 +815,7 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process( // Ok, no arguments or redirections; check to see if the command is a directory. wcstring implicit_cd_path; use_implicit_cd = - path_can_be_implicit_cd(cmd, env_get_pwd_slash(), &implicit_cd_path); + path_can_be_implicit_cd(cmd, parser->vars().get_pwd_slash(), &implicit_cd_path); } } @@ -883,7 +885,8 @@ parse_execution_result_t parse_execution_context_t::expand_arguments_from_nodes( // Expand this string. parse_error_list_t errors; arg_expanded.clear(); - int expand_ret = expand_string(arg_str, &arg_expanded, EXPAND_NO_DESCRIPTIONS, &errors); + int expand_ret = + expand_string(arg_str, &arg_expanded, EXPAND_NO_DESCRIPTIONS, parser->vars(), &errors); parse_error_offset_source_start(&errors, arg_node.source_range()->start); switch (expand_ret) { case EXPAND_ERROR: { @@ -931,7 +934,8 @@ bool parse_execution_context_t::determine_io_chain(tnode_tsrc, &source_fd, &target); // 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); + bool target_expanded = + expand_one(target, no_exec ? EXPAND_SKIP_VARIABLES : 0, parser->vars()); if (!target_expanded || target.empty()) { // TODO: Improve this error message. errored = diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 83d4fa109..6d4d3f5a7 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -19,6 +19,7 @@ #include "future_feature_flags.h" #include "parse_constants.h" #include "parse_util.h" +#include "parser.h" #include "tnode.h" #include "tokenizer.h" #include "util.h" @@ -1131,8 +1132,8 @@ static bool detect_errors_in_plain_statement(const wcstring &buff_src, if (maybe_t unexp_command = command_for_plain_statement(pst, buff_src)) { wcstring command; // Check that we can expand the command. - if (expand_to_command_and_args(*unexp_command, &command, nullptr, parse_errors) == - EXPAND_ERROR) { + if (expand_to_command_and_args(*unexp_command, null_environment_t{}, &command, nullptr, + parse_errors) == EXPAND_ERROR) { errored = true; } diff --git a/src/parser.cpp b/src/parser.cpp index 65fcffcd4..d70ad32e3 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -314,6 +314,7 @@ void parser_t::emit_profiling(const char *path) const { } void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t eflags, + const environment_t &vars, std::vector *output_arg_list) { assert(output_arg_list != NULL); @@ -330,7 +331,8 @@ void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t tnode_t arg_list(&tree, &tree.at(0)); while (auto arg = arg_list.next_in_list()) { const wcstring arg_src = arg.get_source(arg_list_src); - if (expand_string(arg_src, output_arg_list, eflags, NULL) == EXPAND_ERROR) { + if (expand_string(arg_src, output_arg_list, eflags, vars, NULL /* errors */) == + EXPAND_ERROR) { break; // failed to expand a string } } diff --git a/src/parser.h b/src/parser.h index deef053ac..33f28f8f0 100644 --- a/src/parser.h +++ b/src/parser.h @@ -247,7 +247,7 @@ class parser_t { /// \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); + const environment_t &vars, std::vector *output); /// Returns a string describing the current parser position in the format 'FILENAME (line /// LINE_NUMBER): LINE'. Example: diff --git a/src/reader.cpp b/src/reader.cpp index 394f5d406..a994d9dfe 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1277,10 +1277,10 @@ struct autosuggestion_result_t { // on a background thread) to determine the autosuggestion static std::function get_autosuggestion_performer( const wcstring &search_string, size_t cursor_pos, history_t *history) { + const auto &parser_vars = parser_t::principal_parser().vars(); const unsigned int generation_count = read_generation_count(); - const wcstring working_directory(env_get_pwd_slash()); - env_vars_snapshot_t vars(parser_t::principal_parser().vars(), - env_vars_snapshot_t::highlighting_keys); + const wcstring working_directory = parser_vars.get_pwd_slash(); + env_vars_snapshot_t vars(vars, env_vars_snapshot_t::highlighting_keys); // TODO: suspicious use of 'history' here // This is safe because histories are immortal, but perhaps // this should use shared_ptr @@ -2473,6 +2473,8 @@ const wchar_t *reader_readline(int nchars) { s_reset(&data->screen, screen_reset_abandon_line); reader_repaint(); + const auto &vars = parser_t::principal_parser().vars(); + // Get the current terminal modes. These will be restored when the function returns. if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output(); // Set the new modes. @@ -2685,8 +2687,7 @@ const wchar_t *reader_readline(int nchars) { complete_flags_t complete_flags = COMPLETION_REQUEST_DEFAULT | COMPLETION_REQUEST_DESCRIPTIONS | COMPLETION_REQUEST_FUZZY_MATCH; - data->complete_func(buffcpy, &comp, complete_flags, - parser_t::principal_parser().vars()); + data->complete_func(buffcpy, &comp, complete_flags, vars); // Munge our completions. completions_sort_and_prioritize(&comp); @@ -2893,7 +2894,8 @@ const wchar_t *reader_readline(int nchars) { // space. const editable_line_t *el = &data->command_line; if (data->history != NULL && !el->empty() && el->text.at(0) != L' ') { - data->history->add_pending_with_file_detection(el->text); + data->history->add_pending_with_file_detection(el->text, + vars.get_pwd_slash()); } finished = 1; update_buff_pos(&data->command_line, data->command_line.size()); From c1dd284b3e79f5ed35ec2606b0c4c833bb7c76b6 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 14 Sep 2018 00:36:26 -0700 Subject: [PATCH 140/439] Instantize env_set Switch env_set to an instance method on environmnet_t. --- src/builtin_argparse.cpp | 26 ++++--- src/builtin_fg.cpp | 3 +- src/builtin_read.cpp | 13 ++-- src/builtin_set.cpp | 9 ++- src/common.cpp | 6 +- src/common.h | 4 +- src/env.cpp | 157 +++++++++++++++++++++++---------------- src/env.h | 15 ++-- src/exec.cpp | 4 +- src/fish.cpp | 2 +- src/fish_tests.cpp | 14 ++-- src/function.cpp | 4 +- src/history.cpp | 13 ++-- src/input.cpp | 13 ++-- src/input.h | 4 +- src/maybe.h | 8 ++ src/parse_execution.cpp | 2 +- src/path.cpp | 5 +- src/reader.cpp | 21 ++++-- 19 files changed, 193 insertions(+), 130 deletions(-) diff --git a/src/builtin_argparse.cpp b/src/builtin_argparse.cpp index 4fe148032..df85cdd32 100644 --- a/src/builtin_argparse.cpp +++ b/src/builtin_argparse.cpp @@ -441,22 +441,24 @@ static int validate_arg(parser_t &parser, const argparse_cmd_opts_t &opts, optio wcstring_list_t cmd_output; - parser.vars().push(true); - env_set_one(L"_argparse_cmd", ENV_LOCAL, opts.name); + auto &vars = parser.vars(); + + vars.push(true); + vars.set_one(L"_argparse_cmd", ENV_LOCAL, opts.name); if (is_long_flag) { - env_set_one(var_name_prefix + L"name", ENV_LOCAL, opt_spec->long_flag); + vars.set_one(var_name_prefix + L"name", ENV_LOCAL, opt_spec->long_flag); } else { - env_set_one(var_name_prefix + L"name", ENV_LOCAL, - wcstring(1, opt_spec->short_flag).c_str()); + vars.set_one(var_name_prefix + L"name", ENV_LOCAL, + wcstring(1, opt_spec->short_flag).c_str()); } - env_set_one(var_name_prefix + L"value", ENV_LOCAL, woptarg); + vars.set_one(var_name_prefix + L"value", ENV_LOCAL, woptarg); int retval = exec_subshell(opt_spec->validation_command, cmd_output, false); for (const auto &output : cmd_output) { streams.err.append(output); streams.err.push_back(L'\n'); } - parser.vars().pop(); + vars.pop(); return retval; } @@ -623,13 +625,13 @@ static int check_min_max_args_constraints(const argparse_cmd_opts_t &opts, parse } /// Put the result of parsing the supplied args into the caller environment as local vars. -static void set_argparse_result_vars(const argparse_cmd_opts_t &opts) { +static void set_argparse_result_vars(env_stack_t &vars, const argparse_cmd_opts_t &opts) { for (const auto &kv : opts.options) { const auto &opt_spec = kv.second; if (!opt_spec->num_seen) continue; if (opt_spec->short_flag_valid) { - env_set(var_name_prefix + opt_spec->short_flag, ENV_LOCAL, opt_spec->vals); + vars.set(var_name_prefix + opt_spec->short_flag, ENV_LOCAL, opt_spec->vals); } if (!opt_spec->long_flag.empty()) { // We do a simple replacement of all non alphanum chars rather than calling @@ -638,11 +640,11 @@ static void set_argparse_result_vars(const argparse_cmd_opts_t &opts) { for (size_t pos = 0; pos < long_flag.size(); pos++) { if (!iswalnum(long_flag[pos])) long_flag[pos] = L'_'; } - env_set(var_name_prefix + long_flag, ENV_LOCAL, opt_spec->vals); + vars.set(var_name_prefix + long_flag, ENV_LOCAL, opt_spec->vals); } } - env_set(L"argv", ENV_LOCAL, opts.argv); + vars.set(L"argv", ENV_LOCAL, opts.argv); } /// The argparse builtin. This is explicitly not compatible with the BSD or GNU version of this @@ -679,6 +681,6 @@ int builtin_argparse(parser_t &parser, io_streams_t &streams, wchar_t **argv) { retval = check_min_max_args_constraints(opts, parser, streams); if (retval != STATUS_CMD_OK) return retval; - set_argparse_result_vars(opts); + set_argparse_result_vars(parser.vars(), opts); return retval; } diff --git a/src/builtin_fg.cpp b/src/builtin_fg.cpp index ac4af798a..605a1301a 100644 --- a/src/builtin_fg.cpp +++ b/src/builtin_fg.cpp @@ -12,6 +12,7 @@ #include "env.h" #include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "parser.h" #include "proc.h" #include "reader.h" #include "tokenizer.h" @@ -103,7 +104,7 @@ int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { const wcstring ft = tok_first(j->command()); //For compatibility with fish 2.0's $_, now replaced with `status current-command` - if (!ft.empty()) env_set_one(L"_", ENV_EXPORT, ft); + if (!ft.empty()) parser.vars().set_one(L"_", ENV_EXPORT, ft); reader_write_title(j->command()); j->promote(); diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 4494ae886..bfe2e0089 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -415,6 +415,7 @@ static int validate_read_args(const wchar_t *cmd, read_cmd_opts_t &opts, int arg /// The read builtin. Reads from stdin and stores the values in environment variables. int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { + auto &vars = parser.vars(); wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); wcstring buff; @@ -513,13 +514,13 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (opts.array) { // Array mode: assign each char as a separate element of the sole var. - env_set(*var_ptr++, opts.place, chars); + vars.set(*var_ptr++, opts.place, chars); } else { // Not array mode: assign each char to a separate var with the remainder being assigned // to the last var. auto c = chars.begin(); for (; c != chars.end() && vars_left(); ++c) { - env_set_one(*var_ptr++, opts.place, *c); + vars.set_one(*var_ptr++, opts.place, *c); } } } else if (opts.array) { @@ -535,14 +536,14 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { loc.first != wcstring::npos; loc = wcstring_tok(buff, opts.delimiter, loc)) { tokens.emplace_back(wcstring(buff, loc.first, loc.second)); } - env_set(*var_ptr++, opts.place, tokens); + vars.set(*var_ptr++, opts.place, tokens); } else { // We're using a delimiter provided by the user so use the `string split` behavior. wcstring_list_t splits; split_about(buff.begin(), buff.end(), opts.delimiter.begin(), opts.delimiter.end(), &splits); - env_set(*var_ptr++, opts.place, splits); + vars.set(*var_ptr++, opts.place, splits); } } else { // Not array mode. Split the input into tokens and assign each to the vars in sequence. @@ -556,7 +557,7 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (loc.first != wcstring::npos) { substr = wcstring(buff, loc.first, loc.second); } - env_set_one(*var_ptr++, opts.place, substr); + vars.set_one(*var_ptr++, opts.place, substr); } } else { // We're using a delimiter provided by the user so use the `string split` behavior. @@ -567,7 +568,7 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { &splits, argc - 1); assert(splits.size() <= (size_t) vars_left()); for (const auto &split : splits) { - env_set_one(*var_ptr++, opts.place, split); + vars.set_one(*var_ptr++, opts.place, split); } } } diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index 4e5f253e8..fdd38b33b 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -344,12 +344,13 @@ static void handle_env_return(int retval, const wchar_t *cmd, const wchar_t *key /// 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 env_set_reporting_errors(const wchar_t *cmd, const wchar_t *key, int scope, - wcstring_list_t &list, io_streams_t &streams) { + const wcstring_list_t &list, io_streams_t &streams, + env_stack_t &vars) { if (is_path_variable(key) && !validate_path_warning_on_colons(cmd, key, list, streams)) { return STATUS_CMD_ERROR; } - int retval = env_set(key, scope | ENV_USER, list); + int retval = vars.set(key, scope | ENV_USER, list); handle_env_return(retval, cmd, key, streams); return retval; @@ -664,7 +665,7 @@ static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, wcstring_list_t result; dest_var->to_list(result); erase_values(result, indexes); - retval = env_set_reporting_errors(cmd, dest, scope, result, streams); + retval = env_set_reporting_errors(cmd, dest, scope, result, streams, parser.vars()); } if (retval != STATUS_CMD_OK) return retval; @@ -773,7 +774,7 @@ static int builtin_set_set(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, w } if (retval != STATUS_CMD_OK) return retval; - retval = env_set_reporting_errors(cmd, varname, scope, new_values, streams); + retval = env_set_reporting_errors(cmd, varname, scope, new_values, streams, parser.vars()); if (retval != STATUS_CMD_OK) return retval; return check_global_scope_exists(cmd, opts, varname, streams); } diff --git a/src/common.cpp b/src/common.cpp index e13f68738..193a6320d 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1758,15 +1758,17 @@ static void validate_new_termsize(struct winsize *new_termsize) { /// Export the new terminal size as env vars and to the kernel if possible. static void export_new_termsize(struct winsize *new_termsize) { + auto &vars = env_stack_t::globals(); wchar_t buf[64]; auto cols = env_get(L"COLUMNS", ENV_EXPORT); swprintf(buf, 64, L"%d", (int)new_termsize->ws_col); - env_set_one(L"COLUMNS", ENV_GLOBAL | (cols.missing_or_empty() ? ENV_DEFAULT : ENV_EXPORT), buf); + vars.set_one(L"COLUMNS", ENV_GLOBAL | (cols.missing_or_empty() ? ENV_DEFAULT : ENV_EXPORT), + buf); auto lines = env_get(L"LINES", ENV_EXPORT); swprintf(buf, 64, L"%d", (int)new_termsize->ws_row); - env_set_one(L"LINES", ENV_GLOBAL | (lines.missing_or_empty() ? ENV_DEFAULT : ENV_EXPORT), buf); + vars.set_one(L"LINES", ENV_GLOBAL | (lines.missing_or_empty() ? ENV_DEFAULT : ENV_EXPORT), buf); #ifdef HAVE_WINSIZE // Only write the new terminal size if we are in the foreground (#4477) diff --git a/src/common.h b/src/common.h index dac5d9a6d..d52204929 100644 --- a/src/common.h +++ b/src/common.h @@ -624,8 +624,8 @@ class null_terminated_array_t { CharType_t **array{NULL}; // No assignment or copying. - void operator=(null_terminated_array_t rhs); - null_terminated_array_t(const null_terminated_array_t &); + void operator=(null_terminated_array_t rhs) = delete; + null_terminated_array_t(const null_terminated_array_t &) = delete; typedef std::vector> string_list_t; diff --git a/src/env.cpp b/src/env.cpp index 82ca99df5..f3fa69416 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -95,7 +95,7 @@ bool term_has_xn = false; /// found in `TERMINFO_DIRS` we don't to call `handle_curses()` before we've imported the latter. static bool env_initialized = false; -typedef std::unordered_map +typedef std::unordered_map var_dispatch_table_t; static var_dispatch_table_t var_dispatch_table; @@ -147,6 +147,8 @@ static std::mutex env_lock; // but we can imagine having separate (linked) stacks // if we introduce multiple threads of execution struct var_stack_t { + var_stack_t(var_stack_t &&) = default; + // Top node on the function stack. env_node_ref_t top; @@ -154,11 +156,13 @@ struct var_stack_t { env_node_ref_t global_env; // Exported variable array used by execv. - null_terminated_array_t export_array; + maybe_t> export_array; /// Flag for checking if we need to regenerate the exported variable array. - bool has_changed_exported = true; - void mark_changed_exported() { has_changed_exported = true; } + void mark_changed_exported() { export_array.reset(); } + + bool has_changed_exported() const { return !export_array; } + void update_export_array_if_necessary(); var_stack_t() : top(globals()), global_env(globals()) { @@ -183,7 +187,15 @@ struct var_stack_t { // shadowing scope, or the global scope if none. This implements the default behavior of `set`. env_node_ref_t resolve_unspecified_scope(); + /// Copy this vars_stack. + var_stack_t clone() const { + return var_stack_t(*this); + } + private: + /// Copy constructor. This does not copy the export array; it just allows it to be regenerated. + var_stack_t(const var_stack_t &rhs) : top(rhs.top), global_env(rhs.global_env) {} + bool local_scope_exports(const env_node_ref_t &n) const; void get_exported(const env_node_t *n, var_table_t &h) const; @@ -273,6 +285,7 @@ env_node_ref_t var_stack_t::resolve_unspecified_scope() { } env_stack_t::env_stack_t() : vars_(make_unique()) {} +env_stack_t::env_stack_t(std::unique_ptr vars) : vars_(std::move(vars)) {} // Get the variable stack var_stack_t &env_stack_t::vars_stack() { return *vars_; } @@ -351,8 +364,8 @@ static void handle_timezone(const wchar_t *env_var_name) { /// Some env vars contain a list of paths where an empty path element is equivalent to ".". /// Unfortunately that convention causes problems for fish scripts. So this function replaces the /// empty path element with an explicit ".". See issue #3914. -static void fix_colon_delimited_var(const wcstring &var_name) { - const auto paths = env_get(var_name); +static void fix_colon_delimited_var(const wcstring &var_name, env_stack_t &vars) { + const auto paths = vars.get(var_name); if (paths.missing_or_empty()) return; // See if there's any empties. @@ -362,7 +375,7 @@ static void fix_colon_delimited_var(const wcstring &var_name) { // Copy the list and replace empties with L"." wcstring_list_t newstrs = strs; std::replace(newstrs.begin(), newstrs.end(), empty, wcstring(L".")); - int retval = env_set(var_name, ENV_DEFAULT | ENV_USER, std::move(newstrs)); + int retval = vars.set(var_name, ENV_DEFAULT | ENV_USER, std::move(newstrs)); if (retval != ENV_OK) { debug(0, L"fix_colon_delimited_var failed unexpectedly with retval %d", retval); } @@ -527,8 +540,8 @@ static bool initialize_curses_using_fallback(const char *term) { /// elements are converted to explicit "." to make the vars easier to use in fish scripts. static void init_path_vars() { // Do not replace empties in MATHPATH - see #4158. - fix_colon_delimited_var(L"PATH"); - fix_colon_delimited_var(L"CDPATH"); + fix_colon_delimited_var(L"PATH", env_stack_t::globals()); + fix_colon_delimited_var(L"CDPATH", env_stack_t::globals()); } /// Update the value of g_guessed_fish_emoji_width @@ -597,7 +610,7 @@ static void init_curses() { } /// React to modifying the given variable. -static void react_to_variable_change(const wchar_t *op, const wcstring &key) { +static void react_to_variable_change(const wchar_t *op, const wcstring &key, env_stack_t &vars) { // Don't do any of this until `env_init()` has run. We only want to do this in response to // variables set by the user; e.g., in a script like *config.fish* or interactively or as part // of loading the universal variables for the first time. Variables we import from the @@ -607,7 +620,7 @@ static void react_to_variable_change(const wchar_t *op, const wcstring &key) { auto dispatch = var_dispatch_table.find(key); if (dispatch != var_dispatch_table.end()) { - (*dispatch->second)(op, key); + (*dispatch->second)(op, key, vars); } else if (string_prefixes_string(L"_fish_abbr_", key)) { update_abbr_cache(op, key); } else if (string_prefixes_string(L"fish_color_", key)) { @@ -620,7 +633,7 @@ static void react_to_variable_change(const wchar_t *op, const wcstring &key) { static void universal_callback(env_stack_t *stack, const callback_data_t &cb) { const wchar_t *op = cb.is_erase() ? L"ERASE" : L"SET"; - react_to_variable_change(op, cb.key); + react_to_variable_change(op, cb.key, *stack); stack->mark_changed_exported(); event_t ev = event_t::variable_event(cb.key); @@ -632,10 +645,11 @@ static void universal_callback(env_stack_t *stack, const callback_data_t &cb) { /// Make sure the PATH variable contains something. static void setup_path() { - const auto path = env_get(L"PATH"); + auto &vars = env_stack_t::globals(); + const auto path = vars.get(L"PATH"); if (path.missing_or_empty()) { wcstring_list_t value({L"/usr/bin", L"/bin"}); - env_set(L"PATH", ENV_GLOBAL | ENV_EXPORT, value); + vars.set(L"PATH", ENV_GLOBAL | ENV_EXPORT, value); } } @@ -643,11 +657,12 @@ static void setup_path() { /// defaults. They will be updated later by the `get_current_winsize()` function if they need to be /// adjusted. void env_stack_t::set_termsize() { + auto &vars = env_stack_t::globals(); auto cols = get(L"COLUMNS"); - if (cols.missing_or_empty()) env_set_one(L"COLUMNS", ENV_GLOBAL, DFLT_TERM_COL_STR); + if (cols.missing_or_empty()) vars.set_one(L"COLUMNS", ENV_GLOBAL, DFLT_TERM_COL_STR); auto rows = get(L"LINES"); - if (rows.missing_or_empty()) env_set_one(L"LINES", ENV_GLOBAL, DFLT_TERM_ROW_STR); + if (rows.missing_or_empty()) vars.set_one(L"LINES", ENV_GLOBAL, DFLT_TERM_ROW_STR); } /// Update the PWD variable directory from the result of getcwd(). @@ -658,7 +673,7 @@ void env_stack_t::set_pwd_from_getcwd() { _(L"Could not determine current working directory. Is your locale set correctly?")); return; } - env_set_one(L"PWD", ENV_EXPORT | ENV_GLOBAL, std::move(cwd)); + set_one(L"PWD", ENV_EXPORT | ENV_GLOBAL, cwd); } /// Allow the user to override the limit on how much data the `read` command will process. @@ -700,7 +715,7 @@ static void setup_user(bool force) { int retval = getpwuid_r(getuid(), &userinfo, buf, sizeof(buf), &result); if (!retval && result) { const wcstring uname = str2wcstring(userinfo.pw_name); - env_set_one(L"USER", ENV_GLOBAL | ENV_EXPORT, uname); + env_stack_t::globals().set_one(L"USER", ENV_GLOBAL | ENV_EXPORT, uname); } } } @@ -736,20 +751,23 @@ void env_stack_t::universal_barrier() { env_universal_callbacks(this, callbacks); } -static void handle_fish_term_change(const wcstring &op, const wcstring &var_name) { +static void handle_fish_term_change(const wcstring &op, const wcstring &var_name, + env_stack_t &vars) { UNUSED(op); UNUSED(var_name); update_fish_color_support(); reader_react_to_color_change(); } -static void handle_escape_delay_change(const wcstring &op, const wcstring &var_name) { +static void handle_escape_delay_change(const wcstring &op, const wcstring &var_name, + env_stack_t &vars) { UNUSED(op); UNUSED(var_name); update_wait_on_escape_ms(); } -static void handle_change_emoji_width(const wcstring &op, const wcstring &var_name) { +static void handle_change_emoji_width(const wcstring &op, const wcstring &var_name, + env_stack_t &vars) { (void)op; (void)var_name; int new_width = 0; @@ -759,7 +777,8 @@ static void handle_change_emoji_width(const wcstring &op, const wcstring &var_na g_fish_emoji_width = std::max(0, new_width); } -static void handle_change_ambiguous_width(const wcstring &op, const wcstring &var_name) { +static void handle_change_ambiguous_width(const wcstring &op, const wcstring &var_name, + env_stack_t &vars) { (void)op; (void)var_name; int new_width = 1; @@ -769,53 +788,59 @@ static void handle_change_ambiguous_width(const wcstring &op, const wcstring &va g_fish_ambiguous_width = std::max(0, new_width); } -static void handle_term_size_change(const wcstring &op, const wcstring &var_name) { +static void handle_term_size_change(const wcstring &op, const wcstring &var_name, + env_stack_t &vars) { UNUSED(op); UNUSED(var_name); invalidate_termsize(true); // force fish to update its idea of the terminal size plus vars } -static void handle_read_limit_change(const wcstring &op, const wcstring &var_name) { +static void handle_read_limit_change(const wcstring &op, const wcstring &var_name, + env_stack_t &vars) { UNUSED(op); UNUSED(var_name); env_set_read_limit(); } -static void handle_fish_history_change(const wcstring &op, const wcstring &var_name) { +static void handle_fish_history_change(const wcstring &op, const wcstring &var_name, + env_stack_t &vars) { UNUSED(op); UNUSED(var_name); reader_change_history(history_session_id().c_str()); } -static void handle_function_path_change(const wcstring &op, const wcstring &var_name) { +static void handle_function_path_change(const wcstring &op, const wcstring &var_name, + env_stack_t &vars) { UNUSED(op); UNUSED(var_name); function_invalidate_path(); } -static void handle_complete_path_change(const wcstring &op, const wcstring &var_name) { +static void handle_complete_path_change(const wcstring &op, const wcstring &var_name, + env_stack_t &vars) { UNUSED(op); UNUSED(var_name); complete_invalidate_path(); } -static void handle_tz_change(const wcstring &op, const wcstring &var_name) { +static void handle_tz_change(const wcstring &op, const wcstring &var_name, env_stack_t &vars) { UNUSED(op); handle_timezone(var_name.c_str()); } -static void handle_magic_colon_var_change(const wcstring &op, const wcstring &var_name) { +static void handle_magic_colon_var_change(const wcstring &op, const wcstring &var_name, + env_stack_t &vars) { UNUSED(op); - fix_colon_delimited_var(var_name); + fix_colon_delimited_var(var_name, vars); } -static void handle_locale_change(const wcstring &op, const wcstring &var_name) { +static void handle_locale_change(const wcstring &op, const wcstring &var_name, env_stack_t &vars) { UNUSED(op); UNUSED(var_name); init_locale(); } -static void handle_curses_change(const wcstring &op, const wcstring &var_name) { +static void handle_curses_change(const wcstring &op, const wcstring &var_name, env_stack_t &vars) { UNUSED(op); UNUSED(var_name); guess_emoji_width(); @@ -852,9 +877,7 @@ static void setup_var_dispatch_table() { void env_init(const struct config_paths_t *paths /* or NULL */) { setup_var_dispatch_table(); - env_stack_t &vars = env_stack_t::principal(); - // Now the environment variable handling is set up, the next step is to insert valid data. - + env_stack_t &vars = env_stack_t::globals(); // Import environment variables. Walk backwards so that the first one out of any duplicates wins // (See issue #2784). wcstring key, val; @@ -872,25 +895,25 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { key.assign(key_and_val, 0, eql); val.assign(key_and_val, eql+1, wcstring::npos); if (is_read_only(key) || is_electric(key)) continue; - env_set(key, ENV_EXPORT | ENV_GLOBAL, {val}); + vars.set(key, ENV_EXPORT | ENV_GLOBAL, {val}); } } // Set the given paths in the environment, if we have any. if (paths != NULL) { - env_set_one(FISH_DATADIR_VAR, ENV_GLOBAL, paths->data); - env_set_one(FISH_SYSCONFDIR_VAR, ENV_GLOBAL, paths->sysconf); - env_set_one(FISH_HELPDIR_VAR, ENV_GLOBAL, paths->doc); - env_set_one(FISH_BIN_DIR, ENV_GLOBAL, paths->bin); + vars.set_one(FISH_DATADIR_VAR, ENV_GLOBAL, paths->data); + vars.set_one(FISH_SYSCONFDIR_VAR, ENV_GLOBAL, paths->sysconf); + vars.set_one(FISH_HELPDIR_VAR, ENV_GLOBAL, paths->doc); + vars.set_one(FISH_BIN_DIR, ENV_GLOBAL, paths->bin); } wcstring user_config_dir; path_get_config(user_config_dir); - env_set_one(FISH_CONFIG_DIR, ENV_GLOBAL, user_config_dir); + vars.set_one(FISH_CONFIG_DIR, ENV_GLOBAL, user_config_dir); wcstring user_data_dir; path_get_data(user_data_dir); - env_set_one(FISH_USER_DATA_DIR, ENV_GLOBAL, user_data_dir); + vars.set_one(FISH_USER_DATA_DIR, ENV_GLOBAL, user_data_dir); init_locale(); init_curses(); @@ -910,20 +933,20 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { setup_user(uid == 0); // Set up $IFS - this used to be in share/config.fish, but really breaks if it isn't done. - env_set_one(L"IFS", ENV_GLOBAL, L"\n \t"); + vars.set_one(L"IFS", ENV_GLOBAL, L"\n \t"); // Set up the version variable. wcstring version = str2wcstring(get_fish_version()); - env_set_one(L"version", ENV_GLOBAL, version); - env_set_one(L"FISH_VERSION", ENV_GLOBAL, version); + vars.set_one(L"version", ENV_GLOBAL, version); + vars.set_one(L"FISH_VERSION", ENV_GLOBAL, version); // Set the $fish_pid variable. - env_set_one(L"fish_pid", ENV_GLOBAL, to_string(getpid())); + vars.set_one(L"fish_pid", ENV_GLOBAL, to_string(getpid())); // Set the $hostname variable wcstring hostname = L"fish"; get_hostname_identifier(hostname); - env_set_one(L"hostname", ENV_GLOBAL, hostname); + vars.set_one(L"hostname", ENV_GLOBAL, hostname); // Set up SHLVL variable. Not we can't use env_get because SHLVL is read-only, and therefore was // not inherited from the environment. @@ -936,7 +959,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { nshlvl_str = to_string(shlvl_i + 1); } } - env_set_one(L"SHLVL", ENV_GLOBAL | ENV_EXPORT, nshlvl_str); + vars.set_one(L"SHLVL", ENV_GLOBAL | ENV_EXPORT, nshlvl_str); // Set up the HOME variable. // Unlike $USER, it doesn't seem that `su`s pass this along @@ -962,7 +985,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { } if (!retval && result && userinfo.pw_dir) { const wcstring dir = str2wcstring(userinfo.pw_dir); - env_set_one(L"HOME", ENV_GLOBAL | ENV_EXPORT, dir); + vars.set_one(L"HOME", ENV_GLOBAL | ENV_EXPORT, dir); } else { // We cannot get $HOME. This triggers warnings for history and config.fish already, // so it isn't necessary to warn here as well. @@ -1088,7 +1111,7 @@ static void env_set_internal_universal(const wcstring &key, wcstring_list_t val, int env_stack_t::set_internal(const wcstring &key, env_mode_flags_t input_var_mode, wcstring_list_t val) { ASSERT_IS_MAIN_THREAD(); env_mode_flags_t var_mode = input_var_mode; - bool has_changed_old = vars_stack().has_changed_exported; + bool has_changed_old = vars_stack().has_changed_exported(); int done = 0; if (val.size() == 1 && (key == L"PWD" || key == L"HOME")) { @@ -1220,7 +1243,7 @@ int env_stack_t::set_internal(const wcstring &key, env_mode_flags_t input_var_mo event_fire(&ev); // debug(1, L"env_set: return from event firing"); - react_to_variable_change(L"SET", key); + react_to_variable_change(L"SET", key, *this); return ENV_OK; } @@ -1315,7 +1338,7 @@ int env_stack_t::remove(const wcstring &key, int var_mode) { if (is_exported) vars_stack().mark_changed_exported(); } - react_to_variable_change(L"ERASE", key); + react_to_variable_change(L"ERASE", key, *this); return erased ? ENV_OK : ENV_NOT_FOUND; } @@ -1423,14 +1446,6 @@ maybe_t env_get(const wcstring &key, env_mode_flags_t mode) { return env_stack_t::principal().get(key, mode); } -int env_set(const wcstring &key, env_mode_flags_t mode, wcstring_list_t vals) { - return env_stack_t::principal().set(key, mode, std::move(vals)); -} - -int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) { - return env_stack_t::principal().set_one(key, mode, std::move(val)); -} - void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); } @@ -1557,7 +1572,7 @@ static std::vector get_export_list(const var_table_t &envs) { } void var_stack_t::update_export_array_if_necessary() { - if (!this->has_changed_exported) { + if (!this->has_changed_exported()) { return; } @@ -1578,15 +1593,15 @@ void var_stack_t::update_export_array_if_necessary() { } } - export_array.set(get_export_list(vals)); - has_changed_exported = false; + export_array.emplace(get_export_list(vals)); } const char *const *env_stack_t::export_arr() { ASSERT_IS_MAIN_THREAD(); ASSERT_IS_NOT_FORKED_CHILD(); vars_stack().update_export_array_if_necessary(); - return vars_stack().export_array.get(); + assert(vars_stack().export_array && "Should have export array"); + return vars_stack().export_array->get(); } void env_stack_t::set_argv(const wchar_t *const *argv) { @@ -1603,6 +1618,7 @@ void env_stack_t::set_argv(const wchar_t *const *argv) { environment_t::~environment_t() = default; env_stack_t::~env_stack_t() = default; +env_stack_t::env_stack_t(env_stack_t &&) = default; null_environment_t::null_environment_t() = default; null_environment_t::~null_environment_t() = default; @@ -1611,11 +1627,22 @@ maybe_t null_environment_t::get(const wcstring &key, env_mode_flags_t } wcstring_list_t null_environment_t::get_names(int flags) const { return {}; } +env_stack_t env_stack_t::make_principal() { + const env_stack_t &gl = env_stack_t::globals(); + std::unique_ptr dup_stack = make_unique(gl.vars_stack().clone()); + return env_stack_t{std::move(dup_stack)}; +} + env_stack_t &env_stack_t::principal() { - static env_stack_t s_principal; + static env_stack_t s_principal = make_principal(); return s_principal; } +env_stack_t &env_stack_t::globals() { + static env_stack_t s_global; + return s_global; +} + env_vars_snapshot_t::env_vars_snapshot_t(const environment_t &source, const wchar_t *const *keys) { ASSERT_IS_MAIN_THREAD(); wcstring key; diff --git a/src/env.h b/src/env.h index e7e6855e8..32a425f5e 100644 --- a/src/env.h +++ b/src/env.h @@ -159,12 +159,6 @@ class null_environment_t : public environment_t { /// Gets the variable with the specified name, or none() if it does not exist. maybe_t env_get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT); -/// Sets the variable with the specified name to the given values. -int env_set(const wcstring &key, env_mode_flags_t mode, wcstring_list_t vals); - -/// Sets the variable with the specified name to a single value. -int env_set_one(const wcstring &key, env_mode_flags_t mode, wcstring val); - /// Synchronizes all universal variable changes: writes everything out, reads stuff in. void env_universal_barrier(); @@ -183,12 +177,17 @@ class env_stack_t : public environment_t { bool try_remove(std::shared_ptr n, const wchar_t *key, int var_mode); std::shared_ptr get_node(const wcstring &key); + static env_stack_t make_principal(); + var_stack_t &vars_stack(); const var_stack_t &vars_stack() const; + explicit env_stack_t(std::unique_ptr vars_); env_stack_t(); ~env_stack_t() override; + env_stack_t(env_stack_t &&); + public: /// Gets the variable with the specified name, or none() if it does not exist. maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override; @@ -247,6 +246,10 @@ class env_stack_t : public environment_t { // Compatibility hack; access the "environment stack" from back when there was just one. static env_stack_t &principal(); + + // Access a variable stack that only represents globals. + // Do not push or pop from this. + static env_stack_t &globals(); }; class env_vars_snapshot_t : public environment_t { diff --git a/src/exec.cpp b/src/exec.cpp index 3e7af698a..25143e863 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -377,7 +377,7 @@ void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &all_ios) { shlvl_str = to_string(shlvl - 1); } } - env_set_one(L"SHLVL", ENV_GLOBAL | ENV_EXPORT, shlvl_str); + vars.set_one(L"SHLVL", ENV_GLOBAL | ENV_EXPORT, std::move(shlvl_str)); // launch_process _never_ returns. launch_process_nofork(vars, j->processes.front().get()); @@ -1074,7 +1074,7 @@ bool exec_job(parser_t &parser, shared_ptr j) { j->set_flag(job_flag_t::CONSTRUCTED, true); if (!j->is_foreground()) { - env_set_one(L"last_pid", ENV_GLOBAL, to_string(j->pgid)); + parser.vars().set_one(L"last_pid", ENV_GLOBAL, to_string(j->pgid)); } if (exec_error) { diff --git a/src/fish.cpp b/src/fish.cpp index b9857e8af..4cf5e268c 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -417,7 +417,7 @@ int main(int argc, char **argv) { for (char **ptr = argv + my_optind; *ptr; ptr++) { list.push_back(str2wcstring(*ptr)); } - env_set(L"argv", ENV_DEFAULT, list); + parser.vars().set(L"argv", ENV_DEFAULT, list); const wcstring rel_filename = str2wcstring(file); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 9ec724d91..ba6bd38a6 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1777,7 +1777,7 @@ static void test_abbreviations() { {L"gx", L"git checkout"}, }; for (const auto &kv : abbreviations) { - int ret = env_set_one(L"_fish_abbr_" + kv.first, ENV_LOCAL, kv.second); + int ret = vars.set_one(L"_fish_abbr_" + kv.first, ENV_LOCAL, kv.second); if (ret != 0) err(L"Unable to set abbreviation variable"); } @@ -2539,7 +2539,7 @@ static void test_complete() { function_data_t fd; fd.name = L"testabbrsonetwothreefour"; function_add(fd, parser_t::principal_parser()); - int ret = env_set_one(L"_fish_abbr_testabbrsonetwothreezero", ENV_LOCAL, L"expansion"); + int ret = pvars.set_one(L"_fish_abbr_testabbrsonetwothreezero", ENV_LOCAL, L"expansion"); complete(L"testabbrsonetwothree", &completions, COMPLETION_REQUEST_DEFAULT, pvars); do_test(ret == 0); do_test(completions.size() == 2); @@ -2692,6 +2692,7 @@ static void perform_one_completion_cd_test(const wcstring &command, const wcstri // Testing test_autosuggest_suggest_special, in particular for properly handling quotes and // backslashes. static void test_autosuggest_suggest_special() { + auto &vars = parser_t::principal_parser().vars(); if (system("mkdir -p 'test/autosuggest_test/0foobar'")) err(L"mkdir failed"); if (system("mkdir -p 'test/autosuggest_test/1foo bar'")) err(L"mkdir failed"); if (system("mkdir -p 'test/autosuggest_test/2foo bar'")) err(L"mkdir failed"); @@ -2705,7 +2706,7 @@ static void test_autosuggest_suggest_special() { // This is to ensure tilde expansion is handled. See the `cd ~/test_autosuggest_suggest_specia` // test below. // Fake out the home directory - env_set_one(L"HOME", ENV_LOCAL | ENV_EXPORT, L"test/test-home"); + parser_t::principal_parser().vars().set_one(L"HOME", ENV_LOCAL | ENV_EXPORT, L"test/test-home"); if (system("mkdir -p test/test-home/test_autosuggest_suggest_special/")) { err(L"mkdir failed"); } @@ -2740,7 +2741,7 @@ static void test_autosuggest_suggest_special() { perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/5", L"foo\"bar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/5", L"foo\"bar/", __LINE__); - env_set_one(L"AUTOSUGGEST_TEST_LOC", ENV_LOCAL, wd); + vars.set_one(L"AUTOSUGGEST_TEST_LOC", ENV_LOCAL, wd); perform_one_autosuggestion_cd_test(L"cd $AUTOSUGGEST_TEST_LOC/0", L"foobar/", __LINE__); perform_one_autosuggestion_cd_test(L"cd ~/test_autosuggest_suggest_specia", L"l/", __LINE__); @@ -4755,14 +4756,15 @@ static void test_string() { /// Helper for test_timezone_env_vars(). long return_timezone_hour(time_t tstamp, const wchar_t *timezone) { + auto &vars = parser_t::principal_parser().vars(); struct tm ltime; char ltime_str[3]; char *str_ptr; size_t n; - env_set_one(L"TZ", ENV_EXPORT, timezone); + vars.set_one(L"TZ", ENV_EXPORT, timezone); - const auto var = env_get(L"TZ", ENV_DEFAULT); + const auto var = vars.get(L"TZ", ENV_DEFAULT); (void)var; localtime_r(&tstamp, <ime); diff --git a/src/function.cpp b/src/function.cpp index e8a424414..2a8e21710 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -353,7 +353,7 @@ void function_prepare_environment(env_stack_t &vars, const wcstring &name, const wchar_t *const *arg = argv; for (const wcstring &named_arg : props->named_arguments) { if (*arg) { - env_set_one(named_arg, ENV_LOCAL | ENV_USER, *arg); + vars.set_one(named_arg, ENV_LOCAL | ENV_USER, *arg); arg++; } else { vars.set_empty(named_arg, ENV_LOCAL | ENV_USER); @@ -362,6 +362,6 @@ void function_prepare_environment(env_stack_t &vars, const wcstring &name, } for (const auto &kv : inherited_vars) { - env_set(kv.first, ENV_LOCAL | ENV_USER, kv.second.as_list()); + vars.set(kv.first, ENV_LOCAL | ENV_USER, kv.second.as_list()); } } diff --git a/src/history.cpp b/src/history.cpp index f978914ed..685e61916 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -36,6 +36,7 @@ #include "io.h" #include "iothread.h" #include "lru.h" +#include "parser.h" #include "parse_constants.h" #include "parse_util.h" #include "path.h" @@ -1992,13 +1993,15 @@ void history_t::resolve_pending() { } -static bool private_mode = false; +static std::atomic private_mode{false}; + void start_private_mode() { - private_mode = true; - env_set_one(L"fish_history", ENV_GLOBAL, L""); - env_set_one(L"fish_private_mode", ENV_GLOBAL, L"1"); + private_mode.store(true); + auto &vars = parser_t::principal_parser().vars(); + vars.set_one(L"fish_history", ENV_GLOBAL, L""); + vars.set_one(L"fish_private_mode", ENV_GLOBAL, L"1"); } bool in_private_mode() { - return private_mode; + return private_mode.load(); } diff --git a/src/input.cpp b/src/input.cpp index 9374fb253..2fe26049d 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -164,8 +164,8 @@ static bool input_function_status; static int input_function_args_index = 0; /// Return the current bind mode. -wcstring input_get_bind_mode() { - auto mode = env_get(FISH_BIND_MODE_VAR); +wcstring input_get_bind_mode(const environment_t &vars) { + auto mode = vars.get(FISH_BIND_MODE_VAR); return mode ? mode->as_string() : DEFAULT_BIND_MODE; } @@ -173,9 +173,11 @@ wcstring input_get_bind_mode() { void input_set_bind_mode(const wcstring &bm) { // Only set this if it differs to not execute variable handlers all the time. // modes may not be empty - empty is a sentinel value meaning to not change the mode + ASSERT_IS_MAIN_THREAD(); + auto &vars = parser_t::principal_parser().vars(); assert(!bm.empty()); - if (input_get_bind_mode() != bm.c_str()) { - env_set_one(FISH_BIND_MODE_VAR, ENV_GLOBAL, bm); + if (input_get_bind_mode(vars) != bm.c_str()) { + vars.set_one(FISH_BIND_MODE_VAR, ENV_GLOBAL, bm); } } @@ -417,7 +419,8 @@ void input_queue_ch(wint_t ch) { input_common_queue_ch(ch); } 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(); + const auto &vars = parser_t::principal_parser().vars(); + const wcstring bind_mode = input_get_bind_mode(vars); for (auto& m : mapping_list) { if (m.mode != bind_mode) { diff --git a/src/input.h b/src/input.h index 553a68b19..757a243ac 100644 --- a/src/input.h +++ b/src/input.h @@ -12,6 +12,8 @@ #define FISH_BIND_MODE_VAR L"fish_bind_mode" +class environment_t; + wcstring describe_char(wint_t c); /// Set to true when the input subsytem has been initialized. @@ -74,7 +76,7 @@ bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_ wcstring *out_new_mode); /// Return the current bind mode. -wcstring input_get_bind_mode(); +wcstring input_get_bind_mode(const environment_t &vars); /// Set the current bind mode. void input_set_bind_mode(const wcstring &bind_mode); diff --git a/src/maybe.h b/src/maybe.h index 9e8301ed5..3281dca02 100644 --- a/src/maybe.h +++ b/src/maybe.h @@ -53,6 +53,14 @@ class maybe_t { } } + // Construct a value in-place. + template + void emplace(Args &&... args) { + reset(); + filled = true; + new (storage) T(std::forward(args)...); + } + // Access the value. T &value() { assert(filled && "maybe_t does not have a value"); diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index c0a98e5cb..a960b8fa3 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -405,7 +405,7 @@ parse_execution_result_t parse_execution_context_t::run_for_statement( break; } - int retval = env_set_one(for_var_name, ENV_DEFAULT | ENV_USER, val); + int retval = parser->vars().set_one(for_var_name, ENV_DEFAULT | ENV_USER, val); assert(retval == ENV_OK && "for loop variable should have been successfully set"); (void)retval; diff --git a/src/path.cpp b/src/path.cpp index a4a8e1c3f..55b32dee6 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -266,10 +266,11 @@ wcstring path_apply_working_directory(const wcstring &path, const wcstring &work static void maybe_issue_path_warning(const wcstring &which_dir, const wcstring &custom_error_msg, bool using_xdg, const wcstring &xdg_var, const wcstring &path, int saved_errno) { + auto &vars = env_stack_t::globals(); wcstring warning_var_name = L"_FISH_WARNED_" + which_dir; - auto var = env_get(warning_var_name, ENV_GLOBAL | ENV_EXPORT); + auto var = vars.get(warning_var_name, ENV_GLOBAL | ENV_EXPORT); if (!var) return; - env_set_one(warning_var_name, ENV_GLOBAL | ENV_EXPORT, L"1"); + vars.set_one(warning_var_name, ENV_GLOBAL | ENV_EXPORT, L"1"); debug(0, custom_error_msg.c_str()); if (path.empty()) { diff --git a/src/reader.cpp b/src/reader.cpp index a994d9dfe..6784c4138 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -397,6 +397,11 @@ class reader_data_t { /// Expand abbreviations at the current cursor position, minus backtrack_amt. bool expand_abbreviation_as_necessary(size_t cursor_backtrack) const; + /// Return the variable set used for e.g. command duration. + env_stack_t &vars() { return parser_t::principal_parser().vars(); } + + const env_stack_t &vars() const { return parser_t::principal_parser().vars(); } + /// Constructor reader_data_t(history_t *hist) : history(hist) {} }; @@ -903,10 +908,12 @@ static void exec_prompt() { void reader_init() { DIE_ON_FAILURE(pthread_key_create(&generation_count_key, NULL)); + auto &vars = parser_t::principal_parser().vars(); + // Ensure this var is present even before an interactive command is run so that if it is used // in a function like `fish_prompt` or `fish_right_prompt` it is defined at the time the first // prompt is written. - env_set_one(ENV_CMD_DURATION, ENV_UNEXPORT, L"0"); + vars.set_one(ENV_CMD_DURATION, ENV_UNEXPORT, L"0"); // Save the initial terminal mode. tcgetattr(STDIN_FILENO, &terminal_mode_on_startup); @@ -1825,7 +1832,7 @@ static void reader_interactive_init() { invalidate_termsize(); // For compatibility with fish 2.0's $_, now replaced with `status current-command` - env_set_one(L"_", ENV_GLOBAL, L"fish"); + parser_t::principal_parser().vars().set_one(L"_", ENV_GLOBAL, L"fish"); } /// Destroy data for interactive use. @@ -1998,7 +2005,7 @@ bool reader_get_selection(size_t *start, size_t *len) { return result; } -void set_env_cmd_duration(struct timeval *after, struct timeval *before) { +void set_env_cmd_duration(struct timeval *after, struct timeval *before, env_stack_t &vars) { time_t secs = after->tv_sec - before->tv_sec; suseconds_t usecs = after->tv_usec - before->tv_usec; wchar_t buf[16]; @@ -2009,7 +2016,7 @@ void set_env_cmd_duration(struct timeval *after, struct timeval *before) { } swprintf(buf, 16, L"%d", (secs * 1000) + (usecs / 1000)); - env_set_one(ENV_CMD_DURATION, ENV_UNEXPORT, buf); + vars.set_one(ENV_CMD_DURATION, ENV_UNEXPORT, buf); } void reader_run_command(parser_t &parser, const wcstring &cmd) { @@ -2018,7 +2025,7 @@ void reader_run_command(parser_t &parser, const wcstring &cmd) { wcstring ft = tok_first(cmd); // For compatibility with fish 2.0's $_, now replaced with `status current-command` - if (!ft.empty()) env_set_one(L"_", ENV_GLOBAL, ft); + if (!ft.empty()) parser.vars().set_one(L"_", ENV_GLOBAL, ft); reader_write_title(cmd); @@ -2033,12 +2040,12 @@ void reader_run_command(parser_t &parser, const wcstring &cmd) { // update the execution duration iff a command is requested for execution // issue - #4926 - if (!ft.empty()) set_env_cmd_duration(&time_after, &time_before); + if (!ft.empty()) set_env_cmd_duration(&time_after, &time_before, parser.vars()); term_steal(); // For compatibility with fish 2.0's $_, now replaced with `status current-command` - env_set_one(L"_", ENV_GLOBAL, program_name); + parser.vars().set_one(L"_", ENV_GLOBAL, program_name); #ifdef HAVE__PROC_SELF_STAT proc_update_jiffies(); From 03b92ffe001bc8966022e2658de63e8c47270279 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 16 Sep 2018 04:05:17 -0700 Subject: [PATCH 141/439] Clean up path_get_cdpath and path_can_be_implicit_cd --- src/builtin_cd.cpp | 7 ++++--- src/highlight.cpp | 7 +++---- src/parse_execution.cpp | 4 ++-- src/path.cpp | 33 ++++++++++++++------------------- src/path.h | 25 +++++++++++-------------- 5 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/builtin_cd.cpp b/src/builtin_cd.cpp index 5a0432e63..260e31844 100644 --- a/src/builtin_cd.cpp +++ b/src/builtin_cd.cpp @@ -34,8 +34,6 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } wcstring dir_in; - wcstring dir; - if (argv[optind]) { dir_in = argv[optind]; } else { @@ -47,7 +45,9 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { dir_in = maybe_dir_in->as_string(); } - if (!path_get_cdpath(dir_in, &dir, parser.vars().get_pwd_slash())) { + wcstring pwd = parser.vars().get_pwd_slash(); + maybe_t mdir = path_get_cdpath(dir_in, pwd, parser.vars()); + if (!mdir) { if (errno == ENOTDIR) { streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd, dir_in.c_str()); } else if (errno == ENOENT) { @@ -64,6 +64,7 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return STATUS_CMD_ERROR; } + const wcstring &dir = *mdir; wcstring norm_dir = normalize_path(dir); diff --git a/src/highlight.cpp b/src/highlight.cpp index 7b8e4ddff..2b49d64da 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -350,9 +350,8 @@ bool autosuggest_validate_from_history(const history_item_t &item, bool is_help = string_prefixes_string(cd_dir, L"--help") || string_prefixes_string(cd_dir, L"-h"); if (!is_help) { - wcstring path; - bool can_cd = path_get_cdpath(cd_dir, &path, working_directory, vars); - if (can_cd && !paths_are_same_file(working_directory, path)) { + auto path = path_get_cdpath(cd_dir, working_directory, vars); + if (path && !paths_are_same_file(working_directory, *path)) { suggestionOK = true; } } @@ -1021,7 +1020,7 @@ static bool command_is_valid(const wcstring &cmd, enum parse_statement_decoratio // Implicit cd if (!is_valid && implicit_cd_ok) { - is_valid = path_can_be_implicit_cd(cmd, working_directory, NULL, vars); + is_valid = path_as_implicit_cd(cmd, working_directory, vars).has_value(); } // Return what we got. diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index a960b8fa3..f5e4d4aa2 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -813,9 +813,9 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process( if (args_from_cmd_expansion.empty() && !args.try_get_child() && !args.try_get_child()) { // Ok, no arguments or redirections; check to see if the command is a directory. - wcstring implicit_cd_path; use_implicit_cd = - path_can_be_implicit_cd(cmd, parser->vars().get_pwd_slash(), &implicit_cd_path); + path_as_implicit_cd(cmd, parser->vars().get_pwd_slash(), parser->vars()) + .has_value(); } } diff --git a/src/path.cpp b/src/path.cpp index 55b32dee6..6a7b3673f 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -157,13 +157,12 @@ wcstring_list_t path_get_paths(const wcstring &cmd) { return paths; } -bool path_get_cdpath(const wcstring &dir, wcstring *out, const wcstring &wd, - const environment_t &env_vars) { +maybe_t path_get_cdpath(const wcstring &dir, const wcstring &wd, + const environment_t &env_vars) { int err = ENOENT; - if (dir.empty()) return false; - - assert(!wd.empty() && wd.back() == L'/'); + if (dir.empty()) return none(); + assert(wd.empty() || wd.back() == L'/'); wcstring_list_t paths; if (dir.at(0) == L'/') { // Absolute path. @@ -197,36 +196,32 @@ bool path_get_cdpath(const wcstring &dir, wcstring *out, const wcstring &wd, } } - bool success = false; for (const wcstring &dir : paths) { struct stat buf; if (wstat(dir, &buf) == 0) { if (S_ISDIR(buf.st_mode)) { - success = true; - if (out) out->assign(dir); - break; - } else { - err = ENOTDIR; + return dir; } + err = ENOTDIR; } } - if (!success) errno = err; - return success; + errno = err; + return none(); } -bool path_can_be_implicit_cd(const wcstring &path, const wcstring &wd, wcstring *out_path, - const environment_t &vars) { +maybe_t path_as_implicit_cd(const wcstring &path, const wcstring &wd, + const environment_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"..") { - result = path_get_cdpath(exp_path, out_path, wd, vars); + // 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). + return path_get_cdpath(exp_path, wd, vars); } - return result; + return none(); } // If the given path looks like it's relative to the working directory, then prepend that working diff --git a/src/path.h b/src/path.h index e6c2174aa..3d9b596d0 100644 --- a/src/path.h +++ b/src/path.h @@ -46,28 +46,25 @@ bool path_get_path(const wcstring &cmd, wcstring *output_or_NULL, wcstring_list_t path_get_paths(const wcstring &cmd); /// 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. +/// directories for relative paths. /// -/// If no valid path is found, null is returned and errno is set to ENOTDIR if at least one such +/// If no valid path is found, false 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, which 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 wcstring &wd, - const environment_t &vars = env_vars_snapshot_t::current()); +/// \param wd The working directory. The working directory should have a slash appended at the end. +/// \param vars The environment variables to use (for the CDPATH variable) +/// \return the command, or none() if it could not be found. +maybe_t path_get_cdpath(const wcstring &dir, const wcstring &wd, + const environment_t &vars); -/// 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, const wcstring &wd, wcstring *out_path = NULL, - const environment_t &vars = env_vars_snapshot_t::current()); +/// Returns the path resolved as an implicit cd command, or none() if none. This requires it to +/// start with one of the allowed prefixes (., .., ~) and resolve to a directory. +maybe_t path_as_implicit_cd(const wcstring &path, const wcstring &wd, + const environment_t &vars); /// Remove double slashes and trailing slashes from a path, e.g. transform foo//bar/ into foo/bar. /// The string is modified in-place. From abcd24f716f664d45e333e4776cec8e5dfd89997 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 16 Sep 2018 12:48:50 -0700 Subject: [PATCH 142/439] Eliminate env_snapshot_t::current() These uses are better served by passing in the real environment stack, now that we have environment_t as a shared base class. --- src/builtin_command.cpp | 3 ++- src/builtin_read.cpp | 1 - src/complete.cpp | 17 +++++++++-------- src/env.cpp | 12 ------------ src/env.h | 6 ------ src/fish_indent.cpp | 2 +- src/highlight.cpp | 2 +- src/parse_execution.cpp | 2 +- src/path.h | 5 ++--- 9 files changed, 16 insertions(+), 34 deletions(-) diff --git a/src/builtin_command.cpp b/src/builtin_command.cpp index 50b985691..6a04a1262 100644 --- a/src/builtin_command.cpp +++ b/src/builtin_command.cpp @@ -10,6 +10,7 @@ #include "common.h" #include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "parser.h" #include "path.h" #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep @@ -102,7 +103,7 @@ int builtin_command(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } } else { wcstring path; - if (path_get_path(command_name, &path)) { + if (path_get_path(command_name, &path, parser.vars())) { if (!opts.quiet) streams.out.append_format(L"%ls\n", path.c_str()); ++found; } diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index bfe2e0089..1bab82707 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -455,7 +455,6 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { auto clear_remaining_vars = [&] () { while (vars_left()) { parser.vars().set_empty(*var_ptr, opts.place); - // env_set_one(*var_ptr, opts.place, L""); ++var_ptr; } }; diff --git a/src/complete.cpp b/src/complete.cpp index 5e1ee9f9c..f9a633383 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -495,18 +495,19 @@ void complete_remove_all(const wcstring &cmd, bool cmd_is_path) { } /// 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)) { +static void parse_cmd_string(const wcstring &str, wcstring *path, wcstring *cmd, + const environment_t &vars) { + if (!path_get_path(str, path, vars)) { /// Use the empty string as the 'path' for commands that can not be found. - path = L""; + *path = L""; } // Make sure the path is not included in the command. size_t last_slash = str.find_last_of(L'/'); if (last_slash != wcstring::npos) { - cmd = str.substr(last_slash + 1); + *cmd = str.substr(last_slash + 1); } else { - cmd = str; + *cmd = str; } } @@ -865,7 +866,7 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt, bool use_common = 1, use_files = 1; wcstring cmd, path; - parse_cmd_string(cmd_orig, path, cmd); + parse_cmd_string(cmd_orig, &path, &cmd, vars); // mqudsi: run_on_main_thread() already just runs `func` if we're on the main thread, // but it makes a kcall to get the current thread id to ascertain that. Perhaps even @@ -891,8 +892,8 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt, // may be faster, path_get_path can potentially do a lot of FS/IO access, so env.get() + // function_exists() should still be faster. head_exists = - head_exists || - path_get_path(cmd_orig, nullptr); // use cmd_orig here as it is potentially pathed + head_exists || path_get_path(cmd_orig, nullptr, + vars); // use cmd_orig here as it is potentially pathed } if (!head_exists) { diff --git a/src/env.cpp b/src/env.cpp index f3fa69416..4e7d9738c 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1658,19 +1658,7 @@ env_vars_snapshot_t::env_vars_snapshot_t(const environment_t &source, const wcha env_vars_snapshot_t::~env_vars_snapshot_t() = default; -// The "current" variables are not a snapshot at all, but instead trampoline to env_get, etc. -// We identify the current snapshot based on pointer values. -// This is an ugly thing that has to go away. -const env_vars_snapshot_t env_vars_snapshot_t::s_current; -const env_vars_snapshot_t &env_vars_snapshot_t::current() { return s_current; } - -bool env_vars_snapshot_t::is_current() const { return this == &s_current; } - maybe_t env_vars_snapshot_t::get(const wcstring &key, env_mode_flags_t mode) const { - // If we represent the current state, bounce to env_get. - if (this->is_current()) { - return env_get(key, mode); - } auto iter = vars.find(key); if (iter == vars.end()) return none(); return iter->second; diff --git a/src/env.h b/src/env.h index 32a425f5e..ef9a9b5ae 100644 --- a/src/env.h +++ b/src/env.h @@ -255,9 +255,6 @@ class env_stack_t : public environment_t { class env_vars_snapshot_t : public environment_t { std::map vars; wcstring_list_t names; - bool is_current() const; - - static const env_vars_snapshot_t s_current; public: env_vars_snapshot_t() = default; @@ -270,9 +267,6 @@ class env_vars_snapshot_t : public environment_t { wcstring_list_t get_names(int flags) const override; - // Returns the fake snapshot representing the live variables array. - static const env_vars_snapshot_t ¤t(); - // Vars necessary for highlighting. static const wchar_t *const highlighting_keys[]; diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 428bd417f..f985c4b19 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -529,7 +529,7 @@ int main(int argc, char *argv[]) { 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()); + env_stack_t::globals()); } std::string colored_output; diff --git a/src/highlight.cpp b/src/highlight.cpp index 2b49d64da..2d1179140 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -364,7 +364,7 @@ bool autosuggest_validate_from_history(const history_item_t &item, // Not handled specially so handle it here. bool cmd_ok = false; - if (path_get_path(parsed_command, NULL)) { + if (path_get_path(parsed_command, NULL, vars)) { cmd_ok = true; } else if (builtin_exists(parsed_command) || function_exists_no_autoload(parsed_command, vars)) { diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index f5e4d4aa2..a2d739277 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -800,7 +800,7 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process( wcstring path_to_external_command; 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); + bool has_command = path_get_path(cmd, &path_to_external_command, parser->vars()); // 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. diff --git a/src/path.h b/src/path.h index 3d9b596d0..48ac8d654 100644 --- a/src/path.h +++ b/src/path.h @@ -34,13 +34,12 @@ bool path_get_data(wcstring &path); /// Args: /// cmd - The name of the executable. /// output_or_NULL - If non-NULL, store the full path. -/// vars - The environment variables snapshot to use +/// vars - The environment variables to use /// /// Returns: /// false if the command can not be found else true. The result /// should be freed with free(). -bool path_get_path(const wcstring &cmd, wcstring *output_or_NULL, - const environment_t &vars = env_vars_snapshot_t::current()); +bool path_get_path(const wcstring &cmd, wcstring *output_or_NULL, const environment_t &vars); /// Return all the paths that match the given command. wcstring_list_t path_get_paths(const wcstring &cmd); From 3eb15109cfe24e78334d7c227732bc8963443fd6 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 17 Sep 2018 21:26:21 -0700 Subject: [PATCH 143/439] Instantize env_set in env.h and env.cpp --- src/env.cpp | 83 +++++++++++++++++++++++--------------------- src/env.h | 2 +- src/input_common.cpp | 4 +-- src/input_common.h | 2 +- 4 files changed, 48 insertions(+), 43 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 4e7d9738c..5e1d4b6e9 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -112,8 +112,8 @@ static const wcstring_list_t locale_variables({L"LANG", L"LANGUAGE", L"LC_ALL", static const wcstring_list_t curses_variables({L"TERM", L"TERMINFO", L"TERMINFO_DIRS"}); // Some forward declarations to make it easy to logically group the code. -static void init_locale(); -static void init_curses(); +static void init_locale(const environment_t &vars); +static void init_curses(const environment_t &vars); // Struct representing one level in the function variable stack. // Only our variable stack should create and destroy these @@ -264,8 +264,10 @@ void var_stack_t::pop() { } } - if (locale_changed) init_locale(); - if (curses_changed) init_curses(); + // TODO: instantize this locale and curses + const auto &vars = env_stack_t::principal(); + if (locale_changed) init_locale(vars); + if (curses_changed) init_curses(vars); } env_node_ref_t var_stack_t::next_scope_to_search(const env_node_ref_t &node) const { @@ -346,9 +348,9 @@ static mode_t get_umask() { } /// Properly sets all timezone information. -static void handle_timezone(const wchar_t *env_var_name) { +static void handle_timezone(const wchar_t *env_var_name, const environment_t &vars) { // const env_var_t var = env_get(env_var_name, ENV_EXPORT); - const auto var = env_get(env_var_name, ENV_DEFAULT); + const auto var = vars.get(env_var_name, ENV_DEFAULT); debug(2, L"handle_timezone() current timezone var: |%ls| => |%ls|", env_var_name, !var ? L"MISSING" : var->as_string().c_str()); const std::string &name = wcs2string(env_var_name); @@ -383,13 +385,13 @@ static void fix_colon_delimited_var(const wcstring &var_name, env_stack_t &vars) } /// Initialize the locale subsystem. -static void init_locale() { +static void init_locale(const environment_t &vars) { // We have to make a copy because the subsequent setlocale() call to change the locale will // invalidate the pointer from the this setlocale() call. char *old_msg_locale = strdup(setlocale(LC_MESSAGES, NULL)); for (const auto &var_name : locale_variables) { - const auto var = env_get(var_name, ENV_EXPORT); + const auto var = vars.get(var_name, ENV_EXPORT); const std::string &name = wcs2string(var_name); if (var.missing_or_empty()) { debug(5, L"locale var %s missing or empty", name.c_str()); @@ -434,8 +436,8 @@ bool term_supports_setting_title() { return can_set_term_title; } /// 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. static const wchar_t *const title_terms[] = {L"xterm", L"screen", L"tmux", L"nxterm", L"rxvt"}; -static bool does_term_support_setting_title() { - const auto term_var = env_get(L"TERM"); +static bool does_term_support_setting_title(const environment_t &vars) { + const auto term_var = vars.get(L"TERM"); if (term_var.missing_or_empty()) return false; const wcstring term_str = term_var->as_string(); @@ -460,11 +462,11 @@ static bool does_term_support_setting_title() { } /// Updates our idea of whether we support term256 and term24bit (see issue #10222). -static void update_fish_color_support() { +static void update_fish_color_support(const environment_t &vars) { // Detect or infer term256 support. If fish_term256 is set, we respect it; // otherwise infer it from the TERM variable or use terminfo. - auto fish_term256 = env_get(L"fish_term256"); - auto term_var = env_get(L"TERM"); + auto fish_term256 = vars.get(L"fish_term256"); + auto term_var = vars.get(L"TERM"); wcstring term = term_var.missing_or_empty() ? L"" : term_var->as_string(); bool support_term256 = false; // default to no support if (!fish_term256.missing_or_empty()) { @@ -476,8 +478,8 @@ static void update_fish_color_support() { debug(2, L"256 color support enabled for '256color' in TERM"); } else if (term.find(L"xterm") != wcstring::npos) { // Assume that all xterms are 256, except for OS X SnowLeopard - const auto prog_var = env_get(L"TERM_PROGRAM"); - const auto progver_var = env_get(L"TERM_PROGRAM_VERSION"); + const auto prog_var = vars.get(L"TERM_PROGRAM"); + const auto progver_var = vars.get(L"TERM_PROGRAM_VERSION"); wcstring term_program = prog_var.missing_or_empty() ? L"" : prog_var->as_string(); if (term_program == L"Apple_Terminal" && !progver_var.missing_or_empty()) { // OS X Lion is version 300+, it has 256 color support @@ -497,7 +499,7 @@ static void update_fish_color_support() { debug(2, L"256 color support not enabled (yet)"); } - auto fish_term24bit = env_get(L"fish_term24bit"); + auto fish_term24bit = vars.get(L"fish_term24bit"); bool support_term24bit; if (!fish_term24bit.missing_or_empty()) { support_term24bit = from_string(fish_term24bit->as_string()); @@ -520,7 +522,8 @@ static void update_fish_color_support() { static bool initialize_curses_using_fallback(const char *term) { // If $TERM is already set to the fallback name we're about to use there isn't any point in // seeing if the fallback name can be used. - auto term_var = env_get(L"TERM"); + auto &vars = env_stack_t::globals(); + auto term_var = vars.get(L"TERM"); if (term_var.missing_or_empty()) return false; auto term_env = wcs2string(term_var->as_string()); @@ -547,12 +550,13 @@ static void init_path_vars() { /// Update the value of g_guessed_fish_emoji_width static void guess_emoji_width() { wcstring term; - if (auto term_var = env_get(L"TERM_PROGRAM")) { + auto &vars = env_stack_t::globals(); + if (auto term_var = vars.get(L"TERM_PROGRAM")) { term = term_var->as_string(); } double version = 0; - if (auto version_var = env_get(L"TERM_PROGRAM_VERSION")) { + if (auto version_var = vars.get(L"TERM_PROGRAM_VERSION")) { std::string narrow_version = wcs2string(version_var->as_string()); version = strtod(narrow_version.c_str(), NULL); } @@ -569,10 +573,10 @@ static void guess_emoji_width() { } /// Initialize the curses subsystem. -static void init_curses() { +static void init_curses(const environment_t &vars) { for (const auto &var_name : curses_variables) { std::string name = wcs2string(var_name); - const auto var = env_get(var_name, ENV_EXPORT); + const auto var = vars.get(var_name, ENV_EXPORT); if (var.missing_or_empty()) { debug(2, L"curses var %s missing or empty", name.c_str()); unsetenv(name.c_str()); @@ -585,7 +589,7 @@ static void init_curses() { int err_ret; if (setupterm(NULL, STDOUT_FILENO, &err_ret) == ERR) { - auto term = env_get(L"TERM"); + auto term = vars.get(L"TERM"); if (is_interactive_session) { debug(1, _(L"Could not set up terminal.")); if (term.missing_or_empty()) { @@ -601,9 +605,9 @@ static void init_curses() { } } - can_set_term_title = does_term_support_setting_title(); + can_set_term_title = does_term_support_setting_title(vars); term_has_xn = tigetflag((char *)"xenl") == 1; // does terminal have the eat_newline_glitch - update_fish_color_support(); + update_fish_color_support(vars); // Invalidate the cached escape sequences since they may no longer be valid. cached_layouts.clear(); curses_initialized = true; @@ -679,7 +683,7 @@ void env_stack_t::set_pwd_from_getcwd() { /// Allow the user to override the limit on how much data the `read` command will process. /// This is primarily for testing but could be used by users in special situations. void env_stack_t::set_read_limit() { - auto read_byte_limit_var = env_get(L"fish_read_limit"); + auto read_byte_limit_var = this->get(L"fish_read_limit"); if (!read_byte_limit_var.missing_or_empty()) { size_t limit = fish_wcstoull(read_byte_limit_var->as_string().c_str()); if (errno) { @@ -708,14 +712,15 @@ wcstring environment_t::get_pwd_slash() const { /// Set up the USER variable. static void setup_user(bool force) { - if (force || env_get(L"USER").missing_or_empty()) { + auto &vars = env_stack_t::globals(); + if (force || vars.get(L"USER").missing_or_empty()) { struct passwd userinfo; struct passwd *result; char buf[8192]; int retval = getpwuid_r(getuid(), &userinfo, buf, sizeof(buf), &result); if (!retval && result) { const wcstring uname = str2wcstring(userinfo.pw_name); - env_stack_t::globals().set_one(L"USER", ENV_GLOBAL | ENV_EXPORT, uname); + vars.set_one(L"USER", ENV_GLOBAL | ENV_EXPORT, uname); } } } @@ -755,7 +760,7 @@ static void handle_fish_term_change(const wcstring &op, const wcstring &var_name env_stack_t &vars) { UNUSED(op); UNUSED(var_name); - update_fish_color_support(); + update_fish_color_support(vars); reader_react_to_color_change(); } @@ -763,7 +768,7 @@ static void handle_escape_delay_change(const wcstring &op, const wcstring &var_n env_stack_t &vars) { UNUSED(op); UNUSED(var_name); - update_wait_on_escape_ms(); + update_wait_on_escape_ms(vars); } static void handle_change_emoji_width(const wcstring &op, const wcstring &var_name, @@ -771,7 +776,7 @@ static void handle_change_emoji_width(const wcstring &op, const wcstring &var_na (void)op; (void)var_name; int new_width = 0; - if (auto width_str = env_get(L"fish_emoji_width")) { + if (auto width_str = vars.get(L"fish_emoji_width")) { new_width = fish_wcstol(width_str->as_string().c_str()); } g_fish_emoji_width = std::max(0, new_width); @@ -782,7 +787,7 @@ static void handle_change_ambiguous_width(const wcstring &op, const wcstring &va (void)op; (void)var_name; int new_width = 1; - if (auto width_str = env_get(L"fish_ambiguous_width")) { + if (auto width_str = vars.get(L"fish_ambiguous_width")) { new_width = fish_wcstol(width_str->as_string().c_str()); } g_fish_ambiguous_width = std::max(0, new_width); @@ -825,7 +830,7 @@ static void handle_complete_path_change(const wcstring &op, const wcstring &var_ static void handle_tz_change(const wcstring &op, const wcstring &var_name, env_stack_t &vars) { UNUSED(op); - handle_timezone(var_name.c_str()); + handle_timezone(var_name.c_str(), vars); } static void handle_magic_colon_var_change(const wcstring &op, const wcstring &var_name, @@ -837,14 +842,14 @@ static void handle_magic_colon_var_change(const wcstring &op, const wcstring &va static void handle_locale_change(const wcstring &op, const wcstring &var_name, env_stack_t &vars) { UNUSED(op); UNUSED(var_name); - init_locale(); + init_locale(vars); } static void handle_curses_change(const wcstring &op, const wcstring &var_name, env_stack_t &vars) { UNUSED(op); UNUSED(var_name); guess_emoji_width(); - init_curses(); + init_curses(vars); } /// Populate the dispatch table used by `react_to_variable_change()` to efficiently call the @@ -915,8 +920,8 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { path_get_data(user_data_dir); vars.set_one(FISH_USER_DATA_DIR, ENV_GLOBAL, user_data_dir); - init_locale(); - init_curses(); + init_locale(vars); + init_curses(vars); init_input(); init_path_vars(); guess_emoji_width(); @@ -966,8 +971,8 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { // if the target user is root, unless "--preserve-environment" is used. // Since that is an explicit choice, we should allow it to enable e.g. // env HOME=(mktemp -d) su --preserve-environment fish - if (env_get(L"HOME").missing_or_empty()) { - auto user_var = env_get(L"USER"); + if (vars.get(L"HOME").missing_or_empty()) { + auto user_var = vars.get(L"USER"); if (!user_var.missing_or_empty()) { char *unam_narrow = wcs2str(user_var->as_string()); struct passwd userinfo; @@ -977,7 +982,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { if (retval || !result) { // Maybe USER is set but it's bogus. Reset USER from the db and try again. setup_user(true); - user_var = env_get(L"USER"); + user_var = vars.get(L"USER"); if (!user_var.missing_or_empty()) { unam_narrow = wcs2str(user_var->as_string()); retval = getpwnam_r(unam_narrow, &userinfo, buf, sizeof(buf), &result); diff --git a/src/env.h b/src/env.h index ef9a9b5ae..f5bcacaa0 100644 --- a/src/env.h +++ b/src/env.h @@ -168,7 +168,7 @@ void env_set_read_limit(); /// A environment stack of scopes. This is the main class that tracks fish variables. struct var_stack_t; class env_node_t; -class env_stack_t : public environment_t { +class env_stack_t final : public environment_t { friend class parser_t; std::unique_ptr vars_; diff --git a/src/input_common.cpp b/src/input_common.cpp index 59e0e77d7..a9fc3bc11 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -158,8 +158,8 @@ static wint_t readb() { // 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() { - auto escape_time_ms = env_get(L"fish_escape_delay_ms"); +void update_wait_on_escape_ms(const environment_t &vars) { + auto escape_time_ms = vars.get(L"fish_escape_delay_ms"); if (escape_time_ms.missing_or_empty()) { wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; return; diff --git a/src/input_common.h b/src/input_common.h index ef4550cba..c80bf6f97 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -89,7 +89,7 @@ void input_common_init(int (*ih)()); void input_common_destroy(); /// Adjust the escape timeout. -void update_wait_on_escape_ms(); +void update_wait_on_escape_ms(const environment_t &vars); /// 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 From 50c83463f1f5334671495849cc814c685a9ac4da Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 18 Sep 2018 09:18:47 -0700 Subject: [PATCH 144/439] Switch some uses of env_get to instanced environment_t --- src/builtin_history.cpp | 3 ++- src/builtin_read.cpp | 3 ++- src/env.cpp | 4 ++-- src/expand.cpp | 2 +- src/history.cpp | 9 ++++----- src/history.h | 6 +++++- src/reader.cpp | 9 +++++---- 7 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/builtin_history.cpp b/src/builtin_history.cpp index d86311531..bd0a49fa0 100644 --- a/src/builtin_history.cpp +++ b/src/builtin_history.cpp @@ -15,6 +15,7 @@ #include "fallback.h" // IWYU pragma: keep #include "history.h" #include "io.h" +#include "parser.h" #include "reader.h" #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep @@ -218,7 +219,7 @@ int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // Use the default history if we have none (which happens if invoked non-interactively, e.g. // from webconfig.py. history_t *history = reader_get_history(); - if (!history) history = &history_t::history_with_name(history_session_id()); + if (!history) history = &history_t::history_with_name(history_session_id(parser.vars())); // If a history command hasn't already been specified via a flag check the first word. // Note that this can be simplified after we eliminate allowing subcommands as flags. diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 1bab82707..0ea09bd94 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -204,7 +204,8 @@ static int read_interactive(wcstring &buff, int nchars, bool shell, bool silent, int exit_res = STATUS_CMD_OK; const wchar_t *line; - wcstring read_history_ID = history_session_id(); + auto &vars = env_stack_t::principal(); + wcstring read_history_ID = history_session_id(vars); if (!read_history_ID.empty()) read_history_ID += L"_read"; reader_push(read_history_ID); diff --git a/src/env.cpp b/src/env.cpp index 5e1d4b6e9..07c8e3a41 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -811,7 +811,7 @@ static void handle_fish_history_change(const wcstring &op, const wcstring &var_n env_stack_t &vars) { UNUSED(op); UNUSED(var_name); - reader_change_history(history_session_id().c_str()); + reader_change_history(history_session_id(vars).c_str()); } static void handle_function_path_change(const wcstring &op, const wcstring &var_name, @@ -1391,7 +1391,7 @@ maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) history_t *history = reader_get_history(); if (!history) { - history = &history_t::history_with_name(history_session_id()); + history = &history_t::history_with_name(history_session_id(*this)); } wcstring_list_t result; if (history) history->get_history(result); diff --git a/src/expand.cpp b/src/expand.cpp index 4b546839e..c9bba8fcd 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -353,7 +353,7 @@ static bool expand_variables(wcstring instr, std::vector *out, siz // Note reader_get_history may return null, if we are running non-interactively (e.g. from // web_config). if (is_main_thread()) { - history = &history_t::history_with_name(history_session_id()); + history = &history_t::history_with_name(history_session_id(env_stack_t::principal())); } } else if (var_name != wcstring{VARIABLE_EXPAND_EMPTY}) { var = env_get(var_name); diff --git a/src/history.cpp b/src/history.cpp index 685e61916..920b4c46d 100644 --- a/src/history.cpp +++ b/src/history.cpp @@ -1694,6 +1694,8 @@ void history_t::clear() { this->clear_file_state(); } +bool history_t::is_default() const { return name == DFLT_FISH_HISTORY_SESSION_ID; } + bool history_t::is_empty() { scoped_lock locker(lock); @@ -1804,9 +1806,6 @@ static bool should_import_bash_history_line(const std::string &line) { /// commands. We can't actually parse bash syntax and the bash history file does not unambiguously /// encode multiline commands. void history_t::populate_from_bash(FILE *stream) { - // We do not import bash history if an alternative fish history file is being used. - if (history_session_id() != DFLT_FISH_HISTORY_SESSION_ID) return; - // Process the entire history file until EOF is observed. bool eof = false; while (!eof) { @@ -1867,10 +1866,10 @@ void history_collection_t::save() { void history_save_all() { histories.save(); } /// Return the prefix for the files to be used for command and read history. -wcstring history_session_id() { +wcstring history_session_id(const environment_t &vars) { wcstring result = DFLT_FISH_HISTORY_SESSION_ID; - const auto var = env_get(L"fish_history"); + const auto var = vars.get(L"fish_history"); if (var) { wcstring session_id = var->as_string(); if (session_id.empty()) { diff --git a/src/history.h b/src/history.h index 4b50554b6..033766060 100644 --- a/src/history.h +++ b/src/history.h @@ -22,6 +22,7 @@ #include "wutil.h" // IWYU pragma: keep struct io_streams_t; +class environment_t; // Fish supports multiple shells writing to history at once. Here is its strategy: // @@ -214,6 +215,9 @@ class history_t { // Returns history with the given name, creating it if necessary. static history_t &history_with_name(const wcstring &name); + /// Returns whether this is using the default name. + bool is_default() const; + // Determines whether the history is empty. Unfortunately this cannot be const, since it may // require populating the history. bool is_empty(); @@ -357,7 +361,7 @@ class history_search_t { void history_save_all(); /// Return the prefix for the files to be used for command and read history. -wcstring history_session_id(); +wcstring history_session_id(const environment_t &vars); /// Given a list of paths and a working directory, return the paths that are valid /// This does disk I/O and may only be called in a background thread diff --git a/src/reader.cpp b/src/reader.cpp index 6784c4138..26c2cab80 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -2150,8 +2150,9 @@ void reader_import_history_if_necessary() { 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()) { + // Import history from bash, etc. if our current history is still empty and is the default + // history. + if (data->history && data->history->is_empty() && data->history->is_default()) { // 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. @@ -2346,7 +2347,8 @@ uint32_t reader_run_count() { return run_count; } /// Read interactively. Read input from stdin while providing editing facilities. static int read_i() { - reader_push(history_session_id()); + parser_t &parser = parser_t::principal_parser(); + reader_push(history_session_id(parser.vars())); reader_set_complete_function(&complete); reader_set_highlight_function(&highlight_shell); reader_set_test_function(&reader_shell_test); @@ -2354,7 +2356,6 @@ static int read_i() { reader_set_expand_abbreviations(true); reader_import_history_if_necessary(); - parser_t &parser = parser_t::principal_parser(); reader_data_t *data = current_data(); data->prev_end_loop = 0; From 9f62a530777285146b69d12a8c41adf84877dd90 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 18 Sep 2018 21:03:01 -0700 Subject: [PATCH 145/439] Instantize env_get inside highlighting --- src/fish_tests.cpp | 30 ++++++++++++++++-------------- src/highlight.cpp | 24 +++++++++++++----------- src/highlight.h | 2 +- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index ba6bd38a6..23283eadd 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -2133,24 +2133,26 @@ static void test_is_potential_path() { const wcstring wd = L"test/is_potential_path_test/"; const wcstring_list_t wds({L".", wd}); - do_test(is_potential_path(L"al", wds, PATH_REQUIRE_DIR)); - do_test(is_potential_path(L"alpha/", wds, PATH_REQUIRE_DIR)); - do_test(is_potential_path(L"aard", wds, 0)); + const auto &vars = env_stack_t::principal(); + do_test(is_potential_path(L"al", wds, vars, PATH_REQUIRE_DIR)); + do_test(is_potential_path(L"alpha/", wds, vars, PATH_REQUIRE_DIR)); + do_test(is_potential_path(L"aard", wds, vars, 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, vars, PATH_REQUIRE_DIR)); + do_test(!is_potential_path(L"aard", wds, vars, PATH_REQUIRE_DIR)); + do_test(!is_potential_path(L"aarde", wds, vars, PATH_REQUIRE_DIR)); + do_test(!is_potential_path(L"aarde", wds, vars, 0)); - do_test(is_potential_path(L"test/is_potential_path_test/aardvark", wds, 0)); - do_test(is_potential_path(L"test/is_potential_path_test/al", wds, PATH_REQUIRE_DIR)); - do_test(is_potential_path(L"test/is_potential_path_test/aardv", wds, 0)); + do_test(is_potential_path(L"test/is_potential_path_test/aardvark", wds, vars, 0)); + do_test(is_potential_path(L"test/is_potential_path_test/al", wds, vars, PATH_REQUIRE_DIR)); + do_test(is_potential_path(L"test/is_potential_path_test/aardv", wds, vars, 0)); - do_test(!is_potential_path(L"test/is_potential_path_test/aardvark", wds, PATH_REQUIRE_DIR)); - do_test(!is_potential_path(L"test/is_potential_path_test/al/", wds, 0)); - do_test(!is_potential_path(L"test/is_potential_path_test/ar", wds, 0)); + do_test( + !is_potential_path(L"test/is_potential_path_test/aardvark", wds, vars, PATH_REQUIRE_DIR)); + do_test(!is_potential_path(L"test/is_potential_path_test/al/", wds, vars, 0)); + do_test(!is_potential_path(L"test/is_potential_path_test/ar", wds, vars, 0)); - do_test(is_potential_path(L"/usr", wds, PATH_REQUIRE_DIR)); + do_test(is_potential_path(L"/usr", wds, vars, PATH_REQUIRE_DIR)); } /// Test the 'test' builtin. diff --git a/src/highlight.cpp b/src/highlight.cpp index 2d1179140..758660981 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -105,7 +105,7 @@ bool fs_is_case_insensitive(const wcstring &path, int fd, /// /// 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) { + const environment_t &vars, path_flags_t flags) { ASSERT_IS_BACKGROUND_THREAD(); const bool require_dir = static_cast(flags & PATH_REQUIRE_DIR); @@ -216,7 +216,7 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l // 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) { + const environment_t &vars, path_flags_t flags) { wcstring_list_t directories; if (string_prefixes_string(L"./", path)) { @@ -224,7 +224,7 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d directories.push_back(working_directory); } else { // Get the CDPATH. - auto cdpath = env_get(L"CDPATH"); + auto cdpath = vars.get(L"CDPATH"); std::vector pathsv = cdpath.missing_or_empty() ? wcstring_list_t{L"."} : cdpath->as_list(); @@ -236,7 +236,7 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d } // Call is_potential_path with all of these directories. - return is_potential_path(path, directories, flags | PATH_REQUIRE_DIR); + return is_potential_path(path, directories, vars, flags | PATH_REQUIRE_DIR); } // Given a plain statement node in a parse tree, get the command and return it, expanded @@ -252,6 +252,8 @@ static bool plain_statement_get_expanded_command(const wcstring &src, } rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background) { + // TODO: rationalize this principal_vars. + const auto &vars = env_stack_t::principal(); 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 @@ -264,17 +266,17 @@ rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background) return rgb_color_t::normal(); } - auto var = env_get(highlight_var[idx]); + auto var = vars.get(highlight_var[idx]); // debug( 1, L"%d -> %d -> %ls", highlight, idx, val ); - if (!var) var = env_get(highlight_var[0]); + if (!var) var = vars.get(highlight_var[0]); if (var) result = parse_color(*var, treat_as_background); // Handle modifiers. if (highlight & highlight_modifier_valid_path) { - auto var2 = env_get(L"fish_color_valid_path"); + auto var2 = vars.get(L"fish_color_valid_path"); if (var2) { rgb_color_t result2 = parse_color(*var2, is_background); if (result.is_normal()) @@ -800,7 +802,7 @@ void highlighter_t::color_argument(tnode_t node) { /// 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) { + const environment_t &vars, 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 @@ -813,7 +815,7 @@ static bool node_is_potential_path(const wcstring &src, const parse_node_t &node 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); + result = is_potential_path(token, working_directory_list, vars, PATH_EXPAND_TILDE); } return result; } @@ -843,7 +845,7 @@ void highlighter_t::color_arguments(const std::vector> &arg 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)) { + !is_potential_cd_path(param, working_directory, vars, PATH_EXPAND_TILDE)) { this->color_node(arg, highlight_spec_error); } } @@ -1180,7 +1182,7 @@ const highlighter_t::color_array_t &highlighter_t::highlight() { // (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 && - node_is_potential_path(buff, node, working_directory)) { + node_is_potential_path(buff, node, vars, 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, diff --git a/src/highlight.h b/src/highlight.h index bfac1d3f2..a7aa66bcb 100644 --- a/src/highlight.h +++ b/src/highlight.h @@ -124,6 +124,6 @@ enum { }; typedef unsigned int path_flags_t; bool is_potential_path(const wcstring &const_path, const wcstring_list_t &directories, - path_flags_t flags); + const environment_t &vars, path_flags_t flags); #endif From 038f3cca6df08f96efde7713410c0c4d1597b56f Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 24 Sep 2018 01:47:38 -0400 Subject: [PATCH 146/439] Remove the abbreviation cache Read abbreviations directly from the environment. --- src/env.cpp | 2 -- src/expand.cpp | 51 +++++++++++++++++++++++----------------------- src/expand.h | 8 ++++---- src/fish_tests.cpp | 18 ++++++++-------- src/highlight.cpp | 2 +- src/reader.cpp | 5 ++--- 6 files changed, 41 insertions(+), 45 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 07c8e3a41..52054e97a 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -625,8 +625,6 @@ static void react_to_variable_change(const wchar_t *op, const wcstring &key, env auto dispatch = var_dispatch_table.find(key); if (dispatch != var_dispatch_table.end()) { (*dispatch->second)(op, key, vars); - } else if (string_prefixes_string(L"_fish_abbr_", key)) { - update_abbr_cache(op, key); } else if (string_prefixes_string(L"fish_color_", key)) { reader_react_to_color_change(); } diff --git a/src/expand.cpp b/src/expand.cpp index c9bba8fcd..d885dada3 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -1188,34 +1188,33 @@ bool fish_xdm_login_hack_hack_hack_hack(std::vector *cmds, int argc return result; } -static owning_lock> s_abbreviations; -void update_abbr_cache(const wchar_t *op, const wcstring &varname) { - wcstring abbr; - if (!unescape_string(varname.substr(wcslen(L"_fish_abbr_")), &abbr, 0, STRING_STYLE_VAR)) { - debug(1, L"Abbreviation var '%ls' is not correctly encoded, ignoring it.", varname.c_str()); - return; - } - auto abbreviations = s_abbreviations.acquire(); - abbreviations->erase(abbr); - if (wcscmp(op, L"ERASE") != 0) { - const auto expansion = env_get(varname); - if (!expansion.missing_or_empty()) { - abbreviations->emplace(abbr, expansion->as_string()); - } - } -} +maybe_t expand_abbreviation(const wcstring &src) { + if (src.empty()) return none(); -bool expand_abbreviation(const wcstring &src, wcstring *output) { - if (src.empty()) return false; - - auto abbreviations = s_abbreviations.acquire(); - auto abbr = abbreviations->find(src); - if (abbr == abbreviations->end()) return false; - if (output != NULL) output->assign(abbr->second); - return true; + const auto &vars = env_stack_t::principal(); + wcstring unesc_src; + if (!unescape_string(src, &unesc_src, STRING_STYLE_VAR)) { + return none(); + } + wcstring var_name = L"_fish_abbr_" + unesc_src; + auto var_value = vars.get(var_name); + if (var_value) { + return var_value->as_string(); + } + return none(); } std::map get_abbreviations() { - auto abbreviations = s_abbreviations.acquire(); - return *abbreviations; + // TODO: try to make this cheaper + const auto &vars = env_stack_t::principal(); + const size_t fish_abbr_len = wcslen(L"_fish_abbr_"); + auto names = vars.get_names(0); + std::map result; + for (const wcstring &name : names) { + if (string_prefixes_string(L"_fish_abbr_", name)) { + result[name.substr(fish_abbr_len)] = vars.get(name)->as_string(); + } + } + return result; } + diff --git a/src/expand.h b/src/expand.h index 95b3c151b..d64f64eed 100644 --- a/src/expand.h +++ b/src/expand.h @@ -14,6 +14,7 @@ #include #include "common.h" +#include "maybe.h" #include "parse_constants.h" class env_var_t; @@ -156,10 +157,9 @@ 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); -/// 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. -void update_abbr_cache(const wchar_t *op, const wcstring &varname); -bool expand_abbreviation(const wcstring &src, wcstring *output); +/// Abbreviation support. Expand src as an abbreviation, returning the expanded form if found, +/// none() if not. +maybe_t expand_abbreviation(const wcstring &src); /// \return a snapshot of all abbreviations as a map abbreviation->expansion. std::map get_abbreviations(); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 23283eadd..ce640296b 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1781,19 +1781,19 @@ static void test_abbreviations() { if (ret != 0) err(L"Unable to set abbreviation variable"); } - 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"")) err(L"Unexpected success with empty abbreviation"); + if (expand_abbreviation(L"nothing")) err(L"Unexpected success with missing 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(); + auto mresult = expand_abbreviation(L"gc"); + if (!mresult) err(L"Unexpected failure with gc abbreviation"); + if (*mresult != L"git checkout") err(L"Wrong abbreviation result for gc"); - if (!expand_abbreviation(L"foo", &result)) err(L"Unexpected failure with foo abbreviation"); - if (result != L"bar") err(L"Wrong abbreviation result for foo"); + mresult = expand_abbreviation(L"foo"); + if (!mresult) err(L"Unexpected failure with foo abbreviation"); + if (*mresult != L"bar") err(L"Wrong abbreviation result for foo"); bool expanded; + wcstring result; 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); diff --git a/src/highlight.cpp b/src/highlight.cpp index 758660981..94a5ae52b 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -1015,7 +1015,7 @@ static bool command_is_valid(const wcstring &cmd, enum parse_statement_decoratio 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); + if (!is_valid && abbreviation_ok) is_valid = expand_abbreviation(cmd).has_value(); // Regular commands if (!is_valid && command_ok) is_valid = path_get_path(cmd, NULL, vars); diff --git a/src/reader.cpp b/src/reader.cpp index 26c2cab80..e7737696a 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -747,14 +747,13 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso bool result = false; if (matching_cmd_node) { const wcstring token = matching_cmd_node.get_source(subcmd); - wcstring abbreviation; - if (expand_abbreviation(token, &abbreviation)) { + if (auto abbreviation = expand_abbreviation(token)) { // There was an abbreviation! Replace the token in the full command. Maintain the // relative position of the cursor. if (output != NULL) { output->assign(cmdline); source_range_t r = *matching_cmd_node.source_range(); - output->replace(subcmd_offset + r.start, r.length, abbreviation); + output->replace(subcmd_offset + r.start, r.length, *abbreviation); } result = true; } From 6f52e6bb1c81b4ac9037cd4c603ef413c662d39c Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 21 Sep 2018 21:52:47 -0700 Subject: [PATCH 147/439] Instantize contents of exec.cpp and others --- src/autoload.cpp | 4 +- src/builtin.cpp | 2 +- src/builtin_argparse.cpp | 2 +- src/complete.cpp | 8 ++- src/exec.cpp | 18 +++---- src/exec.h | 5 +- src/expand.cpp | 39 ++++++++------ src/expand.h | 5 +- src/fish_tests.cpp | 111 ++++++++++++++++++++++----------------- src/highlight.cpp | 2 +- src/parser.cpp | 4 +- src/parser.h | 3 ++ src/path.cpp | 4 +- src/reader.cpp | 17 +++--- 14 files changed, 131 insertions(+), 93 deletions(-) diff --git a/src/autoload.cpp b/src/autoload.cpp index 8614731e3..18b75aef0 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -19,6 +19,7 @@ #include "common.h" #include "env.h" #include "exec.h" +#include "parser.h" #include "wutil.h" // IWYU pragma: keep /// The time before we'll recheck an autoloaded file. @@ -256,7 +257,8 @@ 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 && !script_source.empty()) { // Do nothing on failure. - exec_subshell(script_source, false /* do not apply exit status */); + exec_subshell(script_source, parser_t::principal_parser(), + false /* do not apply exit status */); } if (really_load) { diff --git a/src/builtin.cpp b/src/builtin.cpp index 2e3306f94..8e3ddeca5 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -169,7 +169,7 @@ wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t wcstring out; const wcstring name_esc = escape_string(name, 1); wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str()); - if (exec_subshell(cmd, lst, false /* don't apply exit status */) >= 0) { + if (exec_subshell(cmd, parser, lst, false /* don't apply exit status */) >= 0) { for (size_t i = 0; i < lst.size(); i++) { out.append(lst.at(i)); out.push_back(L'\n'); diff --git a/src/builtin_argparse.cpp b/src/builtin_argparse.cpp index df85cdd32..c4b88736e 100644 --- a/src/builtin_argparse.cpp +++ b/src/builtin_argparse.cpp @@ -453,7 +453,7 @@ static int validate_arg(parser_t &parser, const argparse_cmd_opts_t &opts, optio } vars.set_one(var_name_prefix + L"value", ENV_LOCAL, woptarg); - int retval = exec_subshell(opt_spec->validation_command, cmd_output, false); + int retval = exec_subshell(opt_spec->validation_command, parser, cmd_output, false); for (const auto &output : cmd_output) { streams.err.append(output); streams.err.push_back(L'\n'); diff --git a/src/complete.cpp b/src/complete.cpp index f9a633383..88c3208c0 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -410,7 +410,9 @@ bool completer_t::condition_test(const wcstring &condition) { condition_cache_t::iterator cached_entry = condition_cache.find(condition); if (cached_entry == condition_cache.end()) { // Compute new value and reinsert it. - test_res = (0 == exec_subshell(condition, false /* don't apply exit status */)); + // TODO: rationalize this parser_t usage. + test_res = (0 == exec_subshell(condition, parser_t::principal_parser(), + false /* don't apply exit status */)); condition_cache[condition] = test_res; } else { // Use the old value. @@ -591,7 +593,9 @@ void completer_t::complete_cmd_desc(const wcstring &str) { // 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) { + // TODO: rationalize this use of principal_parser. + if (exec_subshell(lookup_cmd, parser_t::principal_parser(), list, + false /* don't apply exit status */) != -1) { std::unordered_map lookup; lookup.reserve(list.size()); diff --git a/src/exec.cpp b/src/exec.cpp index 25143e863..294f6eef7 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -369,7 +369,7 @@ void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &all_ios) { // really make sense, so I'm not trying to fix it here. if (!setup_child_process(0, all_ios)) { // Decrement SHLVL as we're removing ourselves from the shell "stack". - auto shlvl_var = env_get(L"SHLVL", ENV_GLOBAL | ENV_EXPORT); + auto shlvl_var = vars.get(L"SHLVL", ENV_GLOBAL | ENV_EXPORT); wcstring shlvl_str = L"0"; if (shlvl_var) { long shlvl = fish_wcstol(shlvl_var->as_string().c_str()); @@ -1085,14 +1085,14 @@ bool exec_job(parser_t &parser, shared_ptr j) { return true; } -static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, bool apply_exit_status, - bool is_subcmd) { +static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, wcstring_list_t *lst, + bool apply_exit_status, bool is_subcmd) { ASSERT_IS_MAIN_THREAD(); bool prev_subshell = is_subshell; const int prev_status = proc_get_last_status(); bool split_output = false; - const auto ifs = env_get(L"IFS"); + const auto ifs = parser.vars().get(L"IFS"); if (!ifs.missing_or_empty()) { split_output = true; } @@ -1163,13 +1163,13 @@ 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, - bool is_subcmd) { +int exec_subshell(const wcstring &cmd, parser_t &parser, std::vector &outputs, + bool apply_exit_status, bool is_subcmd) { ASSERT_IS_MAIN_THREAD(); - return exec_subshell_internal(cmd, &outputs, apply_exit_status, is_subcmd); + return exec_subshell_internal(cmd, parser, &outputs, apply_exit_status, is_subcmd); } -int exec_subshell(const wcstring &cmd, bool apply_exit_status, bool is_subcmd) { +int exec_subshell(const wcstring &cmd, parser_t &parser, bool apply_exit_status, bool is_subcmd) { ASSERT_IS_MAIN_THREAD(); - return exec_subshell_internal(cmd, NULL, apply_exit_status, is_subcmd); + return exec_subshell_internal(cmd, parser, NULL, apply_exit_status, is_subcmd); } diff --git a/src/exec.h b/src/exec.h index 5e04fe3e5..010de4654 100644 --- a/src/exec.h +++ b/src/exec.h @@ -23,9 +23,10 @@ bool exec_job(parser_t &parser, std::shared_ptr j); /// \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, parser_t &parser, std::vector &outputs, + bool preserve_exit_status, bool is_subcmd = false); +int exec_subshell(const wcstring &cmd, parser_t &parser, bool preserve_exit_status, bool is_subcmd = false); -int exec_subshell(const wcstring &cmd, bool preserve_exit_status, bool is_subcmd = false); /// Loops over close until the syscall was run without being interrupted. void exec_close(int fd); diff --git a/src/expand.cpp b/src/expand.cpp index d885dada3..4beb86782 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -43,6 +43,7 @@ #include "iothread.h" #include "parse_constants.h" #include "parse_util.h" +#include "parser.h" #include "path.h" #include "proc.h" #include "reader.h" @@ -292,7 +293,7 @@ static size_t parse_slice(const wchar_t *in, wchar_t **end_ptr, std::vector *out, size_t last_idx, - parse_error_list_t *errors) { + const environment_t &vars, 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. @@ -356,7 +357,7 @@ static bool expand_variables(wcstring instr, std::vector *out, siz history = &history_t::history_with_name(history_session_id(env_stack_t::principal())); } } else if (var_name != wcstring{VARIABLE_EXPAND_EMPTY}) { - var = env_get(var_name); + var = vars.get(var_name); } // Parse out any following slice. @@ -406,7 +407,7 @@ static bool expand_variables(wcstring instr, std::vector *out, siz res.push_back(VARIABLE_EXPAND_EMPTY); } res.append(instr, var_name_and_slice_stop, wcstring::npos); - return expand_variables(std::move(res), out, varexp_char_idx, errors); + return expand_variables(std::move(res), out, varexp_char_idx, vars, errors); } } @@ -463,7 +464,7 @@ static bool expand_variables(wcstring instr, std::vector *out, siz // Append all entries in var_item_list, separated by the delimiter. res.append(join_strings(var_item_list, delimit)); res.append(instr, var_name_and_slice_stop, wcstring::npos); - return expand_variables(std::move(res), out, varexp_char_idx, errors); + return expand_variables(std::move(res), out, varexp_char_idx, vars, errors); } else { // Normal cartesian-product expansion. for (const wcstring &item : var_item_list) { @@ -480,7 +481,7 @@ static bool expand_variables(wcstring instr, std::vector *out, siz } new_in.append(item); new_in.append(instr, var_name_and_slice_stop, wcstring::npos); - if (!expand_variables(std::move(new_in), out, varexp_char_idx, errors)) { + if (!expand_variables(std::move(new_in), out, varexp_char_idx, vars, errors)) { return false; } } @@ -637,7 +638,10 @@ static bool expand_cmdsubst(const wcstring &input, std::vector *ou wcstring_list_t sub_res; const wcstring subcmd(paren_begin + 1, paren_end - paren_begin - 1); - if (exec_subshell(subcmd, sub_res, true /* apply_exit_status */, true /* is_subcmd */) == -1) { + // TODO: justify this parser_t::principal_parser + auto &parser = parser_t::principal_parser(); + if (exec_subshell(subcmd, parser, sub_res, true /* apply_exit_status */, + true /* is_subcmd */) == -1) { append_cmdsub_error(errors, SOURCE_LOCATION_UNKNOWN, L"Unknown error while evaulating command substitution"); return false; @@ -742,7 +746,7 @@ static wcstring get_home_directory_name(const wcstring &input, size_t *out_tail_ } /// Attempts tilde expansion of the string specified, modifying it in place. -static void expand_home_directory(wcstring &input) { +static void expand_home_directory(wcstring &input, const environment_t &vars) { if (!input.empty() && input.at(0) == HOME_DIRECTORY) { size_t tail_idx; wcstring username = get_home_directory_name(input, &tail_idx); @@ -750,7 +754,7 @@ static void expand_home_directory(wcstring &input) { maybe_t home; if (username.empty()) { // Current users home directory. - auto home_var = env_get(L"HOME"); + auto home_var = vars.get(L"HOME"); if (home_var.missing_or_empty()) { input.clear(); return; @@ -787,16 +791,17 @@ static void expand_percent_self(wcstring &input) { } } -void expand_tilde(wcstring &input) { +void expand_tilde(wcstring &input, const environment_t &vars) { // Avoid needless COW behavior by ensuring we use const at. const wcstring &tmp = input; if (!tmp.empty() && tmp.at(0) == L'~') { input.at(0) = HOME_DIRECTORY; - expand_home_directory(input); + expand_home_directory(input, vars); } } -static void unexpand_tildes(const wcstring &input, std::vector *completions) { +static void unexpand_tildes(const wcstring &input, const environment_t &vars, + 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; @@ -818,7 +823,7 @@ static void unexpand_tildes(const wcstring &input, std::vector *co // Expand username_with_tilde. wcstring home = username_with_tilde; - expand_tilde(home); + expand_tilde(home, vars); // Now for each completion that starts with home, replace it with the username_with_tilde. for (size_t i = 0; i < completions->size(); i++) { @@ -835,12 +840,12 @@ static void unexpand_tildes(const wcstring &input, std::vector *co // 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) { +wcstring replace_home_directory_with_tilde(const wcstring &str, const environment_t &vars) { // Only absolute paths get this treatment. wcstring result = str; if (string_prefixes_string(L"/", result)) { wcstring home_directory = L"~"; - expand_tilde(home_directory); + expand_tilde(home_directory, vars); if (!string_suffixes_string(L"/", home_directory)) { home_directory.push_back(L'/'); } @@ -928,7 +933,7 @@ static expand_error_t expand_stage_variables(wcstring input, std::vector *out_comp if (total_result != EXPAND_ERROR) { // Hack to un-expand tildes (see #647). if (!(flags & EXPAND_SKIP_HOME_DIRECTORIES)) { - unexpand_tildes(input, &completions); + unexpand_tildes(input, vars, &completions); } out_completions->insert(out_completions->end(), std::make_move_iterator(completions.begin()), diff --git a/src/expand.h b/src/expand.h index d64f64eed..4cf67e71c 100644 --- a/src/expand.h +++ b/src/expand.h @@ -17,6 +17,7 @@ #include "maybe.h" #include "parse_constants.h" +class environment_t; class env_var_t; class environment_t; @@ -152,10 +153,10 @@ wcstring expand_escape_variable(const env_var_t &var); /// 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); +void expand_tilde(wcstring &input, const environment_t &vars); /// Perform the opposite of tilde expansion on the string, which is modified in place. -wcstring replace_home_directory_with_tilde(const wcstring &str); +wcstring replace_home_directory_with_tilde(const wcstring &str, const environment_t &vars); /// Abbreviation support. Expand src as an abbreviation, returning the expanded form if found, /// none() if not. diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index ce640296b..9efe2c2e7 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1502,11 +1502,17 @@ static void test_lru() { /// A crappy environment_t that only knows about PWD. struct pwd_environment_t : public environment_t { + std::map extras; + virtual maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override { if (key == L"PWD") { return env_var_t{wgetcwd(), 0}; } + auto extra = extras.find(key); + if (extra != extras.end()) { + return env_var_t(extra->second, ENV_DEFAULT); + } return {}; } @@ -2620,9 +2626,9 @@ static void test_completion_insertions() { } static void perform_one_autosuggestion_cd_test(const wcstring &command, const wcstring &expected, - long line) { + const environment_t &vars, long line) { std::vector comps; - complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, pwd_environment_t{}); + complete(command, &comps, COMPLETION_REQUEST_AUTOSUGGESTION, vars); bool expects_error = (expected == L""); @@ -2694,7 +2700,6 @@ static void perform_one_completion_cd_test(const wcstring &command, const wcstri // Testing test_autosuggest_suggest_special, in particular for properly handling quotes and // backslashes. static void test_autosuggest_suggest_special() { - auto &vars = parser_t::principal_parser().vars(); if (system("mkdir -p 'test/autosuggest_test/0foobar'")) err(L"mkdir failed"); if (system("mkdir -p 'test/autosuggest_test/1foo bar'")) err(L"mkdir failed"); if (system("mkdir -p 'test/autosuggest_test/2foo bar'")) err(L"mkdir failed"); @@ -2724,60 +2729,72 @@ static void test_autosuggest_suggest_special() { const wcstring wd = L"test/autosuggest_test"; - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/5", L"foo\"bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/5", L"foo\"bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/5", L"foo\"bar/", __LINE__); + pwd_environment_t vars{}; + vars.extras[L"HOME"] = parser_t::principal_parser().vars().get(L"HOME")->as_string(); - vars.set_one(L"AUTOSUGGEST_TEST_LOC", ENV_LOCAL, wd); - perform_one_autosuggestion_cd_test(L"cd $AUTOSUGGEST_TEST_LOC/0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd ~/test_autosuggest_suggest_specia", L"l/", __LINE__); - - perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/start/", L"unique2/unique3/", + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/1", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/1", L"foo bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/1", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/2", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/2", L"foo bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/2", L"foo bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/3", L"foo\\bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/3", L"foo\\bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/3", L"foo\\bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/4", L"foo'bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/4", L"foo'bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/4", L"foo'bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/5", L"foo\"bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"test/autosuggest_test/5", L"foo\"bar/", vars, + __LINE__); + perform_one_autosuggestion_cd_test(L"cd 'test/autosuggest_test/5", L"foo\"bar/", vars, __LINE__); + vars.extras[L"AUTOSUGGEST_TEST_LOC"] = wd; + perform_one_autosuggestion_cd_test(L"cd $AUTOSUGGEST_TEST_LOC/0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd ~/test_autosuggest_suggest_specia", L"l/", vars, + __LINE__); + + perform_one_autosuggestion_cd_test(L"cd test/autosuggest_test/start/", L"unique2/unique3/", + vars, __LINE__); + if (!pushd(wcs2string(wd).c_str())) return; - perform_one_autosuggestion_cd_test(L"cd 0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '0", L"foobar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '1", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '2", L"foo bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '3", L"foo\\bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '4", L"foo'bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd 5", L"foo\"bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd \"5", L"foo\"bar/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd '5", L"foo\"bar/", __LINE__); + perform_one_autosuggestion_cd_test(L"cd 0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '0", L"foobar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 1", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"1", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '1", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 2", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"2", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '2", L"foo bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 3", L"foo\\bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"3", L"foo\\bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '3", L"foo\\bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 4", L"foo'bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"4", L"foo'bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '4", L"foo'bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd 5", L"foo\"bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd \"5", L"foo\"bar/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd '5", L"foo\"bar/", vars, __LINE__); // A single quote should defeat tilde expansion. - perform_one_autosuggestion_cd_test(L"cd '~/test_autosuggest_suggest_specia'", L"", + perform_one_autosuggestion_cd_test(L"cd '~/test_autosuggest_suggest_specia'", L"", vars, __LINE__); // Don't crash on ~ (issue #2696). Note this is cwd dependent. if (system("mkdir -p '~hahaha/path1/path2/'")) err(L"mkdir failed"); - perform_one_autosuggestion_cd_test(L"cd ~haha", L"ha/path1/path2/", __LINE__); - perform_one_autosuggestion_cd_test(L"cd ~hahaha/", L"path1/path2/", __LINE__); + perform_one_autosuggestion_cd_test(L"cd ~haha", L"ha/path1/path2/", vars, __LINE__); + perform_one_autosuggestion_cd_test(L"cd ~hahaha/", L"path1/path2/", vars, __LINE__); perform_one_completion_cd_test(L"cd ~haha", L"ha/", __LINE__); perform_one_completion_cd_test(L"cd ~hahaha/", L"path1/", __LINE__); diff --git a/src/highlight.cpp b/src/highlight.cpp index 94a5ae52b..9b844f89f 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -114,7 +114,7 @@ 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, vars); // debug( 1, L"%ls -> %ls ->%ls", path, tilde, unescaped ); diff --git a/src/parser.cpp b/src/parser.cpp index d70ad32e3..124fdbb6b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -96,8 +96,8 @@ static const struct block_lookup_entry block_lookup[] = { {(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) { - return replace_home_directory_with_tilde(path); +wcstring parser_t::user_presentable_path(const wcstring &path) const { + return replace_home_directory_with_tilde(path, vars()); } parser_t::parser_t() : variables(env_stack_t::principal()) {} diff --git a/src/parser.h b/src/parser.h index 33f28f8f0..6edea461e 100644 --- a/src/parser.h +++ b/src/parser.h @@ -199,6 +199,9 @@ class parser_t { /// every block if it is of type FUNCTION_CALL. const wchar_t *is_function(size_t idx = 0) const; + // Given a file path, return something nicer. Currently we just "unexpand" tildes. + wcstring user_presentable_path(const wcstring &path) const; + /// Helper for stack_trace(). void stack_trace_internal(size_t block_idx, wcstring *out) const; diff --git a/src/path.cpp b/src/path.cpp index 6a7b3673f..32c4932c9 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -187,7 +187,7 @@ maybe_t path_get_cdpath(const wcstring &dir, const wcstring &wd, // TODO: if next_path starts with ./ we need to replace the . with the wd. next_path = wd; } - expand_tilde(next_path); + expand_tilde(next_path, env_vars); if (next_path.empty()) continue; wcstring whole_path = next_path; @@ -213,7 +213,7 @@ maybe_t path_get_cdpath(const wcstring &dir, const wcstring &wd, maybe_t path_as_implicit_cd(const wcstring &path, const wcstring &wd, const environment_t &vars) { wcstring exp_path = path; - expand_tilde(exp_path); + expand_tilde(exp_path, vars); 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"..") { diff --git a/src/reader.cpp b/src/reader.cpp index e7737696a..339e9d360 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -400,6 +400,9 @@ class reader_data_t { /// Return the variable set used for e.g. command duration. env_stack_t &vars() { return parser_t::principal_parser().vars(); } + /// Hackish access to the parser. TODO: rationalize this. + parser_t &parser() { return parser_t::principal_parser(); } + const env_stack_t &vars() const { return parser_t::principal_parser().vars(); } /// Constructor @@ -829,7 +832,8 @@ 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 /* ignore exit status */) != -1 && + if (exec_subshell(fish_title_command, current_data()->parser(), lst, + false /* ignore exit status */) != -1 && !lst.empty()) { fputws(L"\x1B]0;", stdout); for (size_t i = 0; i < lst.size(); i++) { @@ -867,7 +871,8 @@ static void exec_prompt() { // 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); + exec_subshell(MODE_PROMPT_FUNCTION_NAME, data->parser(), 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++) { @@ -878,7 +883,7 @@ static void exec_prompt() { if (!data->left_prompt.empty()) { wcstring_list_t prompt_list; // Ignore return status. - exec_subshell(data->left_prompt, prompt_list, apply_exit_status); + exec_subshell(data->left_prompt, data->parser(), prompt_list, apply_exit_status); 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); @@ -888,7 +893,7 @@ static void exec_prompt() { if (!data->right_prompt.empty()) { wcstring_list_t prompt_list; // Status is ignored. - exec_subshell(data->right_prompt, prompt_list, apply_exit_status); + exec_subshell(data->right_prompt, data->parser(), 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. data->right_prompt_buff += prompt_list.at(i); @@ -2155,9 +2160,9 @@ void reader_import_history_if_necessary() { // 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 auto var = env_get(L"HISTFILE"); + const auto var = data->vars().get(L"HISTFILE"); wcstring path = (var ? var->as_string() : L"~/.bash_history"); - expand_tilde(path); + expand_tilde(path, data->vars()); FILE *f = wfopen(path, "r"); if (f) { data->history->populate_from_bash(f); From 3b1709180f7fbb292229571f003fd9c38b5b1bc8 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 24 Sep 2018 22:26:46 -0400 Subject: [PATCH 148/439] Instantize env_get --- src/autoload.cpp | 7 ++++- src/builtin_cd.cpp | 2 +- src/builtin_command.cpp | 2 +- src/builtin_read.cpp | 5 ++-- src/builtin_set.cpp | 58 ++++++++++++++++++++------------------- src/builtin_set_color.cpp | 5 ++-- src/common.cpp | 19 ++++++------- src/complete.cpp | 6 ++-- src/fish.cpp | 3 +- src/function.cpp | 52 +++++++++++++++++++---------------- src/output.cpp | 2 +- src/parse_execution.cpp | 5 ++-- src/path.cpp | 15 ++++------ src/path.h | 2 +- 14 files changed, 98 insertions(+), 85 deletions(-) diff --git a/src/autoload.cpp b/src/autoload.cpp index 18b75aef0..bcd980796 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -66,8 +66,12 @@ int autoload_t::load(const wcstring &cmd, bool reload) { CHECK_BLOCK(0); ASSERT_IS_MAIN_THREAD(); + // TODO: Justify this principal_parser. + auto &parser = parser_t::principal_parser(); + auto &vars = parser.vars(); + if (!this->paths) { - auto path_var = env_get(env_var_name); + auto path_var = vars.get(env_var_name); if (path_var.missing_or_empty()) return 0; this->paths = path_var->as_list(); } @@ -257,6 +261,7 @@ bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_ // If we have a script, either built-in or a file source, then run it. if (really_load && !script_source.empty()) { // Do nothing on failure. + // TODO: rationalize this use of principal_parser, or inject the loading from outside. exec_subshell(script_source, parser_t::principal_parser(), false /* do not apply exit status */); } diff --git a/src/builtin_cd.cpp b/src/builtin_cd.cpp index 260e31844..92f7d9ce4 100644 --- a/src/builtin_cd.cpp +++ b/src/builtin_cd.cpp @@ -37,7 +37,7 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (argv[optind]) { dir_in = argv[optind]; } else { - auto maybe_dir_in = env_get(L"HOME"); + auto maybe_dir_in = parser.vars().get(L"HOME"); if (maybe_dir_in.missing_or_empty()) { streams.err.append_format(_(L"%ls: Could not find home directory\n"), cmd); return STATUS_CMD_ERROR; diff --git a/src/builtin_command.cpp b/src/builtin_command.cpp index 6a04a1262..1e038e68d 100644 --- a/src/builtin_command.cpp +++ b/src/builtin_command.cpp @@ -96,7 +96,7 @@ int builtin_command(parser_t &parser, io_streams_t &streams, wchar_t **argv) { for (int idx = optind; argv[idx]; ++idx) { const wchar_t *command_name = argv[idx]; if (opts.all_paths) { - wcstring_list_t paths = path_get_paths(command_name); + wcstring_list_t paths = path_get_paths(command_name, parser.vars()); for (auto path : paths) { if (!opts.quiet) streams.out.append_format(L"%ls\n", path.c_str()); ++found; diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 0ea09bd94..0c1ea1a00 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -204,7 +204,8 @@ static int read_interactive(wcstring &buff, int nchars, bool shell, bool silent, int exit_res = STATUS_CMD_OK; const wchar_t *line; - auto &vars = env_stack_t::principal(); + // TODO: rationalize this. + const auto &vars = env_stack_t::principal(); wcstring read_history_ID = history_session_id(vars); if (!read_history_ID.empty()) read_history_ID += L"_read"; reader_push(read_history_ID); @@ -490,7 +491,7 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } if (!opts.have_delimiter) { - auto ifs = env_get(L"IFS"); + auto ifs = parser.vars().get(L"IFS"); if (!ifs.missing_or_empty()) opts.delimiter = ifs->as_string(); } diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index fdd38b33b..0858e5413 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -234,9 +234,9 @@ static int validate_cmd_opts(const wchar_t *cmd, set_cmd_opts_t &opts, //!OCLIN // Check if we are setting a uvar and a global of the same name exists. See // https://github.com/fish-shell/fish-shell/issues/806 static int check_global_scope_exists(const wchar_t *cmd, set_cmd_opts_t &opts, const wchar_t *dest, - io_streams_t &streams) { + io_streams_t &streams, const environment_t &vars) { if (opts.universal) { - auto global_dest = env_get(dest, ENV_GLOBAL); + auto global_dest = vars.get(dest, ENV_GLOBAL); if (global_dest && shell_is_interactive()) { streams.err.append_format(BUILTIN_SET_UVAR_ERR, cmd, dest); } @@ -249,7 +249,8 @@ static int check_global_scope_exists(const wchar_t *cmd, set_cmd_opts_t &opts, c // contain a colon, then complain. Return true if any path element was valid, false if not. static bool validate_path_warning_on_colons(const wchar_t *cmd, const wchar_t *key, //!OCLINT(npath complexity) - const wcstring_list_t &list, io_streams_t &streams) { + const wcstring_list_t &list, io_streams_t &streams, + const environment_t &vars) { // Always allow setting an empty value. if (list.empty()) return true; @@ -265,7 +266,7 @@ static bool validate_path_warning_on_colons(const wchar_t *cmd, // 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 auto existing_variable = env_get(key, ENV_DEFAULT); + const auto existing_variable = vars.get(key, ENV_DEFAULT); if (!existing_variable.missing_or_empty()) existing_variable->to_list(existing_values); for (const wcstring &dir : list) { @@ -346,7 +347,7 @@ static void handle_env_return(int retval, const wchar_t *cmd, const wchar_t *key static int env_set_reporting_errors(const wchar_t *cmd, const wchar_t *key, int scope, const wcstring_list_t &list, io_streams_t &streams, env_stack_t &vars) { - if (is_path_variable(key) && !validate_path_warning_on_colons(cmd, key, list, streams)) { + if (is_path_variable(key) && !validate_path_warning_on_colons(cmd, key, list, streams, vars)) { return STATUS_CMD_ERROR; } @@ -365,13 +366,14 @@ static int env_set_reporting_errors(const wchar_t *cmd, const wchar_t *key, int /// Returns: /// The total number of indexes parsed, or -1 on error. If any indexes were found the `src` string /// is modified to omit the index expression leaving just the var name. -static int parse_index(std::vector &indexes, wchar_t *src, int scope, io_streams_t &streams) { +static int parse_index(std::vector &indexes, wchar_t *src, int scope, io_streams_t &streams, + const environment_t &vars) { wchar_t *p = wcschr(src, L'['); if (!p) return 0; // no slices so nothing for us to do *p = L'\0'; // split the var name from the indexes/slices p++; - auto var_str = env_get(src, scope); + auto var_str = vars.get(src, scope); wcstring_list_t var; if (var_str) var_str->to_list(var); @@ -484,7 +486,7 @@ static int builtin_set_list(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, streams.out.append(e_key); if (!names_only) { - auto var = env_get(key, compute_scope(opts)); + auto var = parser.vars().get(key, compute_scope(opts)); if (!var.missing_or_empty()) { bool shorten = false; wcstring val = expand_escape_variable(*var); @@ -517,7 +519,7 @@ static int builtin_set_query(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, assert(dest); std::vector indexes; - int idx_count = parse_index(indexes, dest, scope, streams); + int idx_count = parse_index(indexes, dest, scope, streams, parser.vars()); if (idx_count == -1) { free(dest); builtin_print_help(parser, streams, cmd, streams.err); @@ -526,14 +528,14 @@ static int builtin_set_query(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, if (idx_count) { wcstring_list_t result; - auto dest_str = env_get(dest, scope); + auto dest_str = parser.vars().get(dest, scope); if (dest_str) dest_str->to_list(result); for (auto idx : indexes) { if (idx < 1 || (size_t)idx > result.size()) retval++; } } else { - if (! env_get(arg, scope)) retval++; + if (!parser.vars().get(arg, scope)) retval++; } free(dest); @@ -542,7 +544,8 @@ static int builtin_set_query(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, return retval; } -static void show_scope(const wchar_t *var_name, int scope, io_streams_t &streams) { +static void show_scope(const wchar_t *var_name, int scope, io_streams_t &streams, + const environment_t &vars) { const wchar_t *scope_name; switch (scope) { case ENV_LOCAL: { @@ -563,7 +566,7 @@ static void show_scope(const wchar_t *var_name, int scope, io_streams_t &streams } } - const auto var = env_get(var_name, scope); + const auto var = vars.get(var_name, scope); if (!var) { streams.out.append_format(_(L"$%ls: not set in %ls scope\n"), var_name, scope_name); return; @@ -590,14 +593,14 @@ static void show_scope(const wchar_t *var_name, int scope, io_streams_t &streams static int builtin_set_show(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) { UNUSED(opts); - + auto &vars = parser.vars(); if (argc == 0) { // show all vars wcstring_list_t names = parser.vars().get_names(ENV_USER); sort(names.begin(), names.end()); for (auto it : names) { - show_scope(it.c_str(), ENV_LOCAL, streams); - show_scope(it.c_str(), ENV_GLOBAL, streams); - show_scope(it.c_str(), ENV_UNIVERSAL, streams); + show_scope(it.c_str(), ENV_LOCAL, streams, vars); + show_scope(it.c_str(), ENV_GLOBAL, streams, vars); + show_scope(it.c_str(), ENV_UNIVERSAL, streams, vars); streams.out.push_back(L'\n'); } } else { @@ -616,9 +619,9 @@ static int builtin_set_show(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, return STATUS_CMD_ERROR; } - show_scope(arg, ENV_LOCAL, streams); - show_scope(arg, ENV_GLOBAL, streams); - show_scope(arg, ENV_UNIVERSAL, streams); + show_scope(arg, ENV_LOCAL, streams, vars); + show_scope(arg, ENV_GLOBAL, streams, vars); + show_scope(arg, ENV_UNIVERSAL, streams, vars); streams.out.push_back(L'\n'); } } @@ -639,7 +642,7 @@ static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, wchar_t *dest = argv[0]; std::vector indexes; - int idx_count = parse_index(indexes, dest, scope, streams); + int idx_count = parse_index(indexes, dest, scope, streams, parser.vars()); if (idx_count == -1) { builtin_print_help(parser, streams, cmd, streams.err); return STATUS_CMD_ERROR; @@ -660,7 +663,7 @@ static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, handle_env_return(retval, cmd, dest, streams); } } else { // remove just the specified indexes of the var - const auto dest_var = env_get(dest, scope); + const auto dest_var = parser.vars().get(dest, scope); if (!dest_var) return STATUS_CMD_ERROR; wcstring_list_t result; dest_var->to_list(result); @@ -669,7 +672,7 @@ static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, } if (retval != STATUS_CMD_OK) return retval; - return check_global_scope_exists(cmd, opts, dest, streams); + return check_global_scope_exists(cmd, opts, dest, streams, parser.vars()); } /// This handles the common case of setting the entire var to a set of values. @@ -684,8 +687,7 @@ static int set_var_array(const wchar_t *cmd, set_cmd_opts_t &opts, const wchar_t if (opts.prepend) { for (int i = 0; i < argc; i++) new_values.push_back(argv[i]); } - - auto var_str = env_get(varname, ENV_DEFAULT); + auto var_str = parser.vars().get(varname, ENV_DEFAULT); wcstring_list_t var_array; if (var_str) var_str->to_list(var_array); new_values.insert(new_values.end(), var_array.begin(), var_array.end()); @@ -719,7 +721,7 @@ static int set_var_slices(const wchar_t *cmd, set_cmd_opts_t &opts, const wchar_ } int scope = compute_scope(opts); // calculate the variable scope based on the provided options - const auto var_str = env_get(varname, scope); + const auto var_str = parser.vars().get(varname, scope); if (var_str) var_str->to_list(new_values); // Slice indexes have been calculated, do the actual work. @@ -750,7 +752,7 @@ static int builtin_set_set(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, w argc--; std::vector indexes; - int idx_count = parse_index(indexes, varname, scope, streams); + int idx_count = parse_index(indexes, varname, scope, streams, parser.vars()); if (idx_count == -1) { builtin_print_help(parser, streams, cmd, streams.err); return STATUS_INVALID_ARGS; @@ -776,7 +778,7 @@ static int builtin_set_set(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, w retval = env_set_reporting_errors(cmd, varname, scope, new_values, streams, parser.vars()); if (retval != STATUS_CMD_OK) return retval; - return check_global_scope_exists(cmd, opts, varname, streams); + return check_global_scope_exists(cmd, opts, varname, streams, parser.vars()); } /// The set builtin creates, updates, and erases (removes, deletes) variables. diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp index e0f98261f..f1b11efe4 100644 --- a/src/builtin_set_color.cpp +++ b/src/builtin_set_color.cpp @@ -27,6 +27,7 @@ #include "env.h" #include "io.h" #include "output.h" +#include "parser.h" #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep @@ -75,10 +76,10 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // Hack in missing italics and dim capabilities omitted from MacOS xterm-256color terminfo // Helps Terminal.app/iTerm #if __APPLE__ - const auto term_prog = env_get(L"TERM_PROGRAM"); + const auto term_prog = parser.vars().get(L"TERM_PROGRAM"); if (!term_prog.missing_or_empty() && (term_prog->as_string() == L"Apple_Terminal" || term_prog->as_string() == L"iTerm.app")) { - const auto term = env_get(L"TERM"); + const auto term = parser.vars().get(L"TERM"); if (!term.missing_or_empty() && (term->as_string() == L"xterm-256color")) { enter_italics_mode = sitm_esc; exit_italics_mode = ritm_esc; diff --git a/src/common.cpp b/src/common.cpp index 193a6320d..718bffed0 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1721,7 +1721,7 @@ void common_handle_winch(int signal) { /// Validate the new terminal size. Fallback to the env vars if necessary. Ensure the values are /// sane and if not fallback to a default of 80x24. -static void validate_new_termsize(struct winsize *new_termsize) { +static void validate_new_termsize(struct winsize *new_termsize, const environment_t &vars) { if (new_termsize->ws_col == 0 || new_termsize->ws_row == 0) { #ifdef HAVE_WINSIZE if (shell_is_interactive()) { @@ -1731,8 +1731,8 @@ static void validate_new_termsize(struct winsize *new_termsize) { } #endif // Fallback to the environment vars. - maybe_t col_var = env_get(L"COLUMNS"); - maybe_t row_var = env_get(L"LINES"); + maybe_t col_var = vars.get(L"COLUMNS"); + maybe_t row_var = vars.get(L"LINES"); if (!col_var.missing_or_empty() && !row_var.missing_or_empty()) { // Both vars have to have valid values. int col = fish_wcstoi(col_var->as_string().c_str()); @@ -1757,16 +1757,15 @@ static void validate_new_termsize(struct winsize *new_termsize) { } /// Export the new terminal size as env vars and to the kernel if possible. -static void export_new_termsize(struct winsize *new_termsize) { - auto &vars = env_stack_t::globals(); +static void export_new_termsize(struct winsize *new_termsize, env_stack_t &vars) { wchar_t buf[64]; - auto cols = env_get(L"COLUMNS", ENV_EXPORT); + auto cols = vars.get(L"COLUMNS", ENV_EXPORT); swprintf(buf, 64, L"%d", (int)new_termsize->ws_col); vars.set_one(L"COLUMNS", ENV_GLOBAL | (cols.missing_or_empty() ? ENV_DEFAULT : ENV_EXPORT), buf); - auto lines = env_get(L"LINES", ENV_EXPORT); + auto lines = vars.get(L"LINES", ENV_EXPORT); swprintf(buf, 64, L"%d", (int)new_termsize->ws_row); vars.set_one(L"LINES", ENV_GLOBAL | (lines.missing_or_empty() ? ENV_DEFAULT : ENV_EXPORT), buf); @@ -1793,9 +1792,9 @@ struct winsize get_current_winsize() { return termsize; } #endif - - validate_new_termsize(&new_termsize); - export_new_termsize(&new_termsize); + auto &vars = env_stack_t::globals(); + validate_new_termsize(&new_termsize, vars); + export_new_termsize(&new_termsize, vars); termsize.ws_col = new_termsize.ws_col; termsize.ws_row = new_termsize.ws_row; termsize_valid = true; diff --git a/src/complete.cpp b/src/complete.cpp index 88c3208c0..effcb05e9 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -410,7 +410,7 @@ bool completer_t::condition_test(const wcstring &condition) { condition_cache_t::iterator cached_entry = condition_cache.find(condition); if (cached_entry == condition_cache.end()) { // Compute new value and reinsert it. - // TODO: rationalize this parser_t usage. + // TODO: rationalize this principal_parser. test_res = (0 == exec_subshell(condition, parser_t::principal_parser(), false /* don't apply exit status */)); condition_cache[condition] = test_res; @@ -593,7 +593,7 @@ void completer_t::complete_cmd_desc(const wcstring &str) { // 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; - // TODO: rationalize this use of principal_parser. + // TODO: justify this use of parser_t::principal_parser. if (exec_subshell(lookup_cmd, parser_t::principal_parser(), list, false /* don't apply exit status */) != -1) { std::unordered_map lookup; @@ -1140,7 +1140,7 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) { wcstring desc; if (this->wants_descriptions()) { // Can't use this->vars here, it could be any variable. - auto var = env_get(env_name); + auto var = vars.get(env_name); if (!var) continue; wcstring value = expand_escape_variable(*var); diff --git a/src/fish.cpp b/src/fish.cpp index 4cf5e268c..ceb49c053 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -364,12 +364,13 @@ int main(int argc, char **argv) { save_term_foreground_process_group(); } + auto &globals = env_stack_t::globals(); const struct config_paths_t paths = determine_config_directory_paths(argv[0]); env_init(&paths); // Set features early in case other initialization depends on them. // Start with the ones set in the environment, then those set on the command line (so the // command line takes precedence). - if (auto features_var = env_get(L"fish_features")) { + if (auto features_var = globals.get(L"fish_features")) { for (const wcstring &s : features_var->as_list()) { mutable_fish_features().set_from_string(s); } diff --git a/src/function.cpp b/src/function.cpp index 2a8e21710..d2ef6e9c7 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -25,29 +25,31 @@ #include "fallback.h" // IWYU pragma: keep #include "function.h" #include "intern.h" +#include "parser.h" #include "parser_keywords.h" #include "reader.h" #include "wutil.h" // IWYU pragma: keep class function_info_t { - public: - /// Immutable properties of the function. - std::shared_ptr props; - /// Function description. This 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; - /// 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; +public: + /// Immutable properties of the function. + std::shared_ptr props; + /// Function description. This 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; + /// 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; - /// Constructs relevant information from the function_data. - function_info_t(function_data_t data, const wchar_t *filename, bool autoload); + /// Constructs relevant information from the function_data. + function_info_t(function_data_t data, const environment_t &vars, const wchar_t *filename, + bool autoload); - /// Used by function_copy. - function_info_t(const function_info_t &data, const wchar_t *filename, bool autoload); + /// Used by function_copy. + function_info_t(const function_info_t &data, const wchar_t *filename, bool autoload); }; /// Table containing all functions. @@ -101,7 +103,9 @@ static int load(const wcstring &name) { static void autoload_names(std::unordered_set &names, int get_hidden) { size_t i; - const auto path_var = env_get(L"fish_function_path"); + // TODO: justfy this. + auto &vars = env_stack_t::principal(); + const auto path_var = vars.get(L"fish_function_path"); if (path_var.missing_or_empty()) return; wcstring_list_t path_list; @@ -127,20 +131,22 @@ static void autoload_names(std::unordered_set &names, int get_hidden) } } -static std::map snapshot_vars(const wcstring_list_t &vars) { +static std::map snapshot_vars(const wcstring_list_t &vars, + const environment_t &src) { std::map result; for (const wcstring &name : vars) { - auto var = env_get(name); + auto var = src.get(name); if (var) result[name] = std::move(*var); } return result; } -function_info_t::function_info_t(function_data_t data, const wchar_t *filename, bool autoload) +function_info_t::function_info_t(function_data_t data, const environment_t &vars, + const wchar_t *filename, bool autoload) : props(std::make_shared(std::move(data.props))), description(std::move(data.description)), definition_file(intern(filename)), - inherit_vars(snapshot_vars(data.inherit_vars)), + inherit_vars(snapshot_vars(data.inherit_vars, vars)), is_autoload(autoload) {} function_info_t::function_info_t(const function_info_t &data, const wchar_t *filename, @@ -164,8 +170,8 @@ void function_add(const function_data_t &data, const parser_t &parser) { // 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, is_autoload)); + const function_map_t::value_type new_pair( + data.name, function_info_t(data, parser.vars(), filename, is_autoload)); loaded_functions.insert(new_pair); // Add event handlers. diff --git a/src/output.cpp b/src/output.cpp index 4246fd084..585c5fb3f 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -554,7 +554,7 @@ void writembs_check(const char *mbs, const char *mbs_name, bool critical, const if (mbs != NULL) { tputs(mbs, 1, &writeb); } else if (critical) { - auto term = env_get(L"TERM"); + auto term = env_stack_t::globals().get(L"TERM"); const wchar_t *fmt = _(L"Tried to use terminfo string %s on line %ld of %s, which is " L"undefined in terminal of type \"%ls\". Please report this error to %s"); diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index a2d739277..f1821176c 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -385,8 +385,9 @@ parse_execution_result_t parse_execution_context_t::run_for_statement( return ret; } - auto var = env_get(for_var_name, ENV_LOCAL); - if (!var && !is_function_context()) var = env_get(for_var_name, ENV_DEFAULT); + auto &vars = parser->vars(); + auto var = vars.get(for_var_name, ENV_LOCAL); + if (!var && !is_function_context()) var = vars.get(for_var_name, ENV_DEFAULT); if (!var || var->read_only()) { int retval = parser->vars().set_empty(for_var_name, ENV_LOCAL | ENV_USER); if (retval != ENV_OK) { diff --git a/src/path.cpp b/src/path.cpp index 32c4932c9..4117cec41 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -119,12 +119,8 @@ bool path_get_path(const wcstring &cmd, wcstring *out_path, const environment_t return path_get_path_core(cmd, out_path, vars.get(L"PATH")); } -bool path_get_path(const wcstring &cmd, wcstring *out_path) { - return path_get_path_core(cmd, out_path, env_get(L"PATH")); -} - -wcstring_list_t path_get_paths(const wcstring &cmd) { - debug(5, L"path_get_paths('%ls')", cmd.c_str()); +wcstring_list_t path_get_paths(const wcstring &cmd, const environment_t &vars) { + debug(3, L"path_get_paths('%ls')", cmd.c_str()); wcstring_list_t paths; // If the command has a slash, it must be an absolute or relative path and thus we don't bother @@ -138,7 +134,7 @@ wcstring_list_t path_get_paths(const wcstring &cmd) { return paths; } - auto path_var = env_get(L"PATH"); + auto path_var = vars.get(L"PATH"); std::vector pathsv; if (path_var) path_var->to_list(pathsv); for (auto path : pathsv) { @@ -291,7 +287,8 @@ static void path_create(wcstring &path, const wcstring &xdg_var, const wcstring // The vars we fetch must be exported. Allowing them to be universal doesn't make sense and // allowing that creates a lock inversion that deadlocks the shell since we're called before // uvars are available. - const auto xdg_dir = env_get(xdg_var, ENV_GLOBAL | ENV_EXPORT); + const auto &vars = env_stack_t::globals(); + const auto xdg_dir = vars.get(xdg_var, ENV_GLOBAL | ENV_EXPORT); if (!xdg_dir.missing_or_empty()) { using_xdg = true; path = xdg_dir->as_string() + L"/fish"; @@ -301,7 +298,7 @@ static void path_create(wcstring &path, const wcstring &xdg_var, const wcstring saved_errno = errno; } } else { - const auto home = env_get(L"HOME", ENV_GLOBAL | ENV_EXPORT); + const auto home = vars.get(L"HOME", ENV_GLOBAL | ENV_EXPORT); if (!home.missing_or_empty()) { path = home->as_string() + (which_dir == L"config" ? L"/.config/fish" : L"/.local/share/fish"); diff --git a/src/path.h b/src/path.h index 48ac8d654..ae0cc31c0 100644 --- a/src/path.h +++ b/src/path.h @@ -42,7 +42,7 @@ bool path_get_data(wcstring &path); bool path_get_path(const wcstring &cmd, wcstring *output_or_NULL, const environment_t &vars); /// Return all the paths that match the given command. -wcstring_list_t path_get_paths(const wcstring &cmd); +wcstring_list_t path_get_paths(const wcstring &cmd, const environment_t &vars); /// Returns the full path of the specified directory, using the CDPATH variable as a list of base /// directories for relative paths. From b98812dd1a8ae70c034133f92713467f6659f203 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 24 Sep 2018 23:59:55 -0400 Subject: [PATCH 149/439] Remove last vestiges of env_set --- src/builtin_set.cpp | 4 ++-- src/env.cpp | 10 ++-------- src/env.h | 11 ++++++----- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index 0858e5413..017d2f3cc 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -336,13 +336,13 @@ static void handle_env_return(int retval, const wchar_t *cmd, const wchar_t *key break; } default: { - DIE("unexpected env_set() ret val"); + DIE("unexpected vars.set() ret val"); break; } } } -/// Call env_set. If this is a path variable, e.g. PATH, validate the elements. On error, print a +/// Call vars.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 env_set_reporting_errors(const wchar_t *cmd, const wchar_t *key, int scope, const wcstring_list_t &list, io_streams_t &streams, diff --git a/src/env.cpp b/src/env.cpp index 52054e97a..f150accc0 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -802,7 +802,7 @@ static void handle_read_limit_change(const wcstring &op, const wcstring &var_nam env_stack_t &vars) { UNUSED(op); UNUSED(var_name); - env_set_read_limit(); + vars.set_read_limit(); } static void handle_fish_history_change(const wcstring &op, const wcstring &var_name, @@ -1241,11 +1241,7 @@ int env_stack_t::set_internal(const wcstring &key, env_mode_flags_t input_var_mo ev.arguments.push_back(L"VARIABLE"); ev.arguments.push_back(L"SET"); ev.arguments.push_back(key); - - // debug(1, L"env_set: fire events on variable |%ls|", key); event_fire(&ev); - // debug(1, L"env_set: return from event firing"); - react_to_variable_change(L"SET", key, *this); return ENV_OK; } @@ -1377,7 +1373,7 @@ maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) 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(). + // that in env_stack_t::set(). if (is_electric(key)) { if (!search_global) return none(); if (key == L"history") { @@ -1451,8 +1447,6 @@ maybe_t env_get(const wcstring &key, env_mode_flags_t mode) { void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } -void env_set_read_limit() { return env_stack_t::principal().set_read_limit(); } - /// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported /// variable. bool var_stack_t::local_scope_exports(const env_node_ref_t &n) const { diff --git a/src/env.h b/src/env.h index f5bcacaa0..8b82cc2ad 100644 --- a/src/env.h +++ b/src/env.h @@ -16,7 +16,11 @@ extern size_t read_byte_limit; extern bool curses_initialized; -// Flags that may be passed as the 'mode' in env_set / env_get. +/// 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 + +// Flags that may be passed as the 'mode' in env_stack_t::set() / environment_t::get(). enum { /// Default mode. Used with `env_get()` to indicate the caller doesn't care what scope the var /// is in or whether it is exported or unexported. @@ -43,7 +47,7 @@ enum { }; typedef uint32_t env_mode_flags_t; -/// Return values for `env_set()`. +/// Return values for `env_stack_t::set()`. enum { ENV_OK, ENV_PERM, ENV_SCOPE, ENV_INVALID, ENV_NOT_FOUND }; /// A struct of configuration directories, determined in main() that fish will optionally pass to @@ -162,9 +166,6 @@ maybe_t env_get(const wcstring &key, env_mode_flags_t mode = ENV_DEFA /// Synchronizes all universal variable changes: writes everything out, reads stuff in. void env_universal_barrier(); -/// Update the read_byte_limit variable. -void env_set_read_limit(); - /// A environment stack of scopes. This is the main class that tracks fish variables. struct var_stack_t; class env_node_t; From 77884bc21a991ead6713f61e2e34c6a8eaf2eb80 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 25 Sep 2018 00:17:05 -0400 Subject: [PATCH 150/439] Instantize env_get This removes env_get(). All fish variable accesses must go through an environment_t. --- src/builtin_pwd.cpp | 3 ++- src/builtin_status.cpp | 2 +- src/env.cpp | 16 +++++----------- src/env.h | 7 ++----- src/expand.cpp | 8 +++----- src/expand.h | 2 +- src/fish_tests.cpp | 37 ++++++++++++++++++++----------------- src/highlight.cpp | 2 +- src/reader.cpp | 13 +++++++------ src/reader.h | 2 +- src/screen.cpp | 12 +----------- 11 files changed, 44 insertions(+), 60 deletions(-) diff --git a/src/builtin_pwd.cpp b/src/builtin_pwd.cpp index f32a783c7..f9d7fda9c 100644 --- a/src/builtin_pwd.cpp +++ b/src/builtin_pwd.cpp @@ -6,6 +6,7 @@ #include "common.h" #include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "parser.h" #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep @@ -48,7 +49,7 @@ int builtin_pwd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } wcstring pwd; - if (auto tmp = env_get(L"PWD")) { + if (auto tmp = parser.vars().get(L"PWD")) { pwd = tmp->as_string(); } if (resolve_symlinks) { diff --git a/src/builtin_status.cpp b/src/builtin_status.cpp index 4862a8897..9a6b66c26 100644 --- a/src/builtin_status.cpp +++ b/src/builtin_status.cpp @@ -424,7 +424,7 @@ int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) { case STATUS_CURRENT_CMD: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) // HACK: Go via the deprecated variable to get the command. - const auto var = env_get(L"_"); + const auto var = parser.vars().get(L"_"); if (!var.missing_or_empty()) { streams.out.append(var->as_string()); streams.out.push_back(L'\n'); diff --git a/src/env.cpp b/src/env.cpp index f150accc0..f48cfde2b 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -349,7 +349,6 @@ static mode_t get_umask() { /// Properly sets all timezone information. static void handle_timezone(const wchar_t *env_var_name, const environment_t &vars) { - // const env_var_t var = env_get(env_var_name, ENV_EXPORT); const auto var = vars.get(env_var_name, ENV_DEFAULT); debug(2, L"handle_timezone() current timezone var: |%ls| => |%ls|", env_var_name, !var ? L"MISSING" : var->as_string().c_str()); @@ -697,7 +696,7 @@ void env_stack_t::mark_changed_exported() { vars_stack().mark_changed_exported() wcstring environment_t::get_pwd_slash() const { // Return "/" if PWD is missing. // See https://github.com/fish-shell/fish-shell/issues/5080 - auto pwd_var = env_get(L"PWD"); + auto pwd_var = get(L"PWD"); wcstring pwd; if (!pwd_var.missing_or_empty()) { pwd = pwd_var->as_string(); @@ -951,8 +950,8 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { get_hostname_identifier(hostname); vars.set_one(L"hostname", ENV_GLOBAL, hostname); - // Set up SHLVL variable. Not we can't use env_get because SHLVL is read-only, and therefore was - // not inherited from the environment. + // Set up SHLVL variable. Not we can't use vars.get() because SHLVL is read-only, and therefore + // was not inherited from the environment. wcstring nshlvl_str = L"1"; if (const char *shlvl_var = getenv("SHLVL")) { const wchar_t *end; @@ -1049,7 +1048,7 @@ static int set_umask(const wcstring_list_t &list_val) { } if (errno || mask > 0777 || mask < 0) return ENV_INVALID; - // Do not actually create a umask variable. On env_get() it will be calculated. + // Do not actually create a umask variable. On env_stack_t::get() it will be calculated. umask(mask); return ENV_OK; } @@ -1422,7 +1421,7 @@ maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) if (!search_universal) return none(); // 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()`. + // values). Make sure we do this outside the env_lock because it may itself call `get()`. if (is_main_thread() && !get_proc_had_barrier()) { set_proc_had_barrier(true); env_universal_barrier(); @@ -1440,11 +1439,6 @@ maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) return none(); } -/// Legacy versions. -maybe_t env_get(const wcstring &key, env_mode_flags_t mode) { - return env_stack_t::principal().get(key, mode); -} - void env_universal_barrier() { env_stack_t::principal().universal_barrier(); } /// Returns true if the specified scope or any non-shadowed non-global subscopes contain an exported diff --git a/src/env.h b/src/env.h index 8b82cc2ad..970d05cad 100644 --- a/src/env.h +++ b/src/env.h @@ -22,8 +22,8 @@ extern bool curses_initialized; // Flags that may be passed as the 'mode' in env_stack_t::set() / environment_t::get(). enum { - /// Default mode. Used with `env_get()` to indicate the caller doesn't care what scope the var - /// is in or whether it is exported or unexported. + /// Default mode. Used with `env_stack_t::get()` to indicate the caller doesn't care what scope + /// the var is in or whether it is exported or unexported. ENV_DEFAULT = 0, /// Flag for local (to the current block) variable. ENV_LOCAL = 1 << 0, @@ -160,9 +160,6 @@ class null_environment_t : public environment_t { wcstring_list_t get_names(int flags) const override; }; -/// Gets the variable with the specified name, or none() if it does not exist. -maybe_t env_get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT); - /// Synchronizes all universal variable changes: writes everything out, reads stuff in. void env_universal_barrier(); diff --git a/src/expand.cpp b/src/expand.cpp index 4beb86782..1d0b1601c 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -1010,7 +1010,7 @@ static expand_error_t expand_stage_wildcards(wcstring path_to_expand, std::vecto // 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. wcstring_list_t paths; - if (auto paths_var = env_get(for_cd ? L"CDPATH" : L"PATH")) { + if (auto paths_var = vars.get(for_cd ? L"CDPATH" : L"PATH")) { paths = paths_var->as_list(); } if (paths.empty()) { @@ -1193,17 +1193,15 @@ bool fish_xdm_login_hack_hack_hack_hack(std::vector *cmds, int argc return result; } -maybe_t expand_abbreviation(const wcstring &src) { +maybe_t expand_abbreviation(const wcstring &src, const environment_t &vars) { if (src.empty()) return none(); - const auto &vars = env_stack_t::principal(); wcstring unesc_src; if (!unescape_string(src, &unesc_src, STRING_STYLE_VAR)) { return none(); } wcstring var_name = L"_fish_abbr_" + unesc_src; - auto var_value = vars.get(var_name); - if (var_value) { + if (auto var_value = vars.get(var_name)) { return var_value->as_string(); } return none(); diff --git a/src/expand.h b/src/expand.h index 4cf67e71c..180ed9d2f 100644 --- a/src/expand.h +++ b/src/expand.h @@ -160,7 +160,7 @@ wcstring replace_home_directory_with_tilde(const wcstring &str, const environmen /// Abbreviation support. Expand src as an abbreviation, returning the expanded form if found, /// none() if not. -maybe_t expand_abbreviation(const wcstring &src); +maybe_t expand_abbreviation(const wcstring &src, const environment_t &vars); /// \return a snapshot of all abbreviations as a map abbreviation->expansion. std::map get_abbreviations(); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 9efe2c2e7..cd030583b 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1787,59 +1787,62 @@ static void test_abbreviations() { if (ret != 0) err(L"Unable to set abbreviation variable"); } - if (expand_abbreviation(L"")) err(L"Unexpected success with empty abbreviation"); - if (expand_abbreviation(L"nothing")) err(L"Unexpected success with missing abbreviation"); + if (expand_abbreviation(L"", vars)) err(L"Unexpected success with empty abbreviation"); + if (expand_abbreviation(L"nothing", vars)) err(L"Unexpected success with missing abbreviation"); - auto mresult = expand_abbreviation(L"gc"); + auto mresult = expand_abbreviation(L"gc", vars); if (!mresult) err(L"Unexpected failure with gc abbreviation"); if (*mresult != L"git checkout") err(L"Wrong abbreviation result for gc"); - mresult = expand_abbreviation(L"foo"); + mresult = expand_abbreviation(L"foo", vars); if (!mresult) err(L"Unexpected failure with foo abbreviation"); if (*mresult != L"bar") err(L"Wrong abbreviation result for foo"); bool expanded; wcstring result; - expanded = reader_expand_abbreviation_in_command(L"just a command", 3, &result); + expanded = reader_expand_abbreviation_in_command(L"just a command", 3, vars, &result); if (expanded) err(L"Command wrongly expanded on line %ld", (long)__LINE__); - expanded = reader_expand_abbreviation_in_command(L"gc somebranch", 0, &result); + expanded = reader_expand_abbreviation_in_command(L"gc somebranch", 0, vars, &result); 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); + expanded = + reader_expand_abbreviation_in_command(L"gc somebranch", wcslen(L"gc"), vars, &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()); // Space separation. - expanded = reader_expand_abbreviation_in_command(L"gx somebranch", wcslen(L"gc"), &result); + expanded = + reader_expand_abbreviation_in_command(L"gx somebranch", wcslen(L"gc"), vars, &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()); expanded = reader_expand_abbreviation_in_command(L"echo hi ; gc somebranch", - wcslen(L"echo hi ; g"), &result); + wcslen(L"echo hi ; g"), vars, &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); + L"echo (echo (echo (echo (gc ", wcslen(L"echo (echo (echo (echo (gc"), vars, &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. - expanded = reader_expand_abbreviation_in_command(L"if gc", wcslen(L"if gc"), &result); + expanded = reader_expand_abbreviation_in_command(L"if gc", wcslen(L"if gc"), vars, &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()); // Others should not be. - expanded = reader_expand_abbreviation_in_command(L"of gc", wcslen(L"of gc"), &result); + expanded = reader_expand_abbreviation_in_command(L"of gc", wcslen(L"of gc"), vars, &result); if (expanded) err(L"gc incorrectly expanded on line %ld", (long)__LINE__); // Others should not be. - expanded = reader_expand_abbreviation_in_command(L"command gc", wcslen(L"command gc"), &result); + expanded = + reader_expand_abbreviation_in_command(L"command gc", wcslen(L"command gc"), vars, &result); if (expanded) err(L"gc incorrectly expanded on line %ld", (long)__LINE__); vars.pop(); @@ -2662,9 +2665,9 @@ static void perform_one_autosuggestion_cd_test(const wcstring &command, const wc } static void perform_one_completion_cd_test(const wcstring &command, const wcstring &expected, - long line) { + const environment_t &vars, long line) { std::vector comps; - complete(command, &comps, COMPLETION_REQUEST_DEFAULT, env_vars_snapshot_t{}); + complete(command, &comps, COMPLETION_REQUEST_DEFAULT, vars); bool expects_error = (expected == L""); @@ -2795,8 +2798,8 @@ static void test_autosuggest_suggest_special() { if (system("mkdir -p '~hahaha/path1/path2/'")) err(L"mkdir failed"); perform_one_autosuggestion_cd_test(L"cd ~haha", L"ha/path1/path2/", vars, __LINE__); perform_one_autosuggestion_cd_test(L"cd ~hahaha/", L"path1/path2/", vars, __LINE__); - perform_one_completion_cd_test(L"cd ~haha", L"ha/", __LINE__); - perform_one_completion_cd_test(L"cd ~hahaha/", L"path1/", __LINE__); + perform_one_completion_cd_test(L"cd ~haha", L"ha/", vars, __LINE__); + perform_one_completion_cd_test(L"cd ~hahaha/", L"path1/", vars, __LINE__); parser_t::principal_parser().vars().remove(L"HOME", ENV_LOCAL | ENV_EXPORT); popd(); diff --git a/src/highlight.cpp b/src/highlight.cpp index 9b844f89f..cda55083b 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -1015,7 +1015,7 @@ static bool command_is_valid(const wcstring &cmd, enum parse_statement_decoratio if (!is_valid && function_ok) is_valid = function_exists_no_autoload(cmd, vars); // Abbreviations - if (!is_valid && abbreviation_ok) is_valid = expand_abbreviation(cmd).has_value(); + if (!is_valid && abbreviation_ok) is_valid = expand_abbreviation(cmd, vars).has_value(); // Regular commands if (!is_valid && command_ok) is_valid = path_get_path(cmd, NULL, vars); diff --git a/src/reader.cpp b/src/reader.cpp index 339e9d360..69a1d2d41 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -395,7 +395,7 @@ class reader_data_t { void pager_selection_changed(); /// Expand abbreviations at the current cursor position, minus backtrack_amt. - bool expand_abbreviation_as_necessary(size_t cursor_backtrack) const; + static bool expand_abbreviation_as_necessary(size_t cursor_backtrack); /// Return the variable set used for e.g. command duration. env_stack_t &vars() { return parser_t::principal_parser().vars(); } @@ -699,7 +699,7 @@ void reader_data_t::pager_selection_changed() { /// 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) { + const environment_t &vars, 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(); @@ -750,7 +750,7 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso bool result = false; if (matching_cmd_node) { const wcstring token = matching_cmd_node.get_source(subcmd); - if (auto abbreviation = expand_abbreviation(token)) { + if (auto abbreviation = expand_abbreviation(token, vars)) { // There was an abbreviation! Replace the token in the full command. Maintain the // relative position of the cursor. if (output != NULL) { @@ -767,15 +767,16 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso /// 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) const { +bool reader_data_t::expand_abbreviation_as_necessary(size_t cursor_backtrack) { reader_data_t *data = current_data(); bool result = false; editable_line_t *el = data->active_edit_line(); - if (this->expand_abbreviations && el == &data->command_line) { + if (data->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)) { + if (reader_expand_abbreviation_in_command(el->text, cursor_pos, data->vars(), + &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(); diff --git a/src/reader.h b/src/reader.h index 3854ae4f7..873b8341d 100644 --- a/src/reader.h +++ b/src/reader.h @@ -216,7 +216,7 @@ wcstring combine_command_and_autosuggestion(const wcstring &cmdline, /// 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); + const environment_t &vars, wcstring *output); /// Apply a completion string. Exposed for testing only. wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flags_t flags, diff --git a/src/screen.cpp b/src/screen.cpp index 63e0906e7..1f4058405 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -110,21 +110,11 @@ static bool allow_soft_wrap() { return auto_right_margin; } -/// Does this look like the escape sequence for setting a screen name. +/// 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) { if (code[1] != L'k') { return false; } - -#if 0 - // TODO: Decide if this should be removed or modified to also test for TERM values that begin - // with "tmux". See issue #3512. - const env_var_t term_name = env_get(L"TERM"); - if (term_name.missing_or_empty() || !string_prefixes_string(L"screen", term_name)) { - return false; - } -#endif - 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) { From a333c2f01d44f678ffd63b4d60c47049a4ed037f Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 10 Jan 2019 20:59:47 -0800 Subject: [PATCH 151/439] Fix some compile warnings --- src/common.cpp | 7 ++++--- src/fish_tests.cpp | 14 +++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 718bffed0..c66639000 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -2166,9 +2166,10 @@ __attribute__((noinline)) void debug_thread_error(void) { } void set_main_thread() { - // Just call is_main_thread() once to force increment of thread_id - // We store the result as `volatile` to guarantee the function call is not optimized away - volatile bool _x = is_main_thread(); + // Just call is_main_thread() once to force increment of thread_id. + bool x = is_main_thread(); + assert(x && "set_main_thread should be main thread"); + (void)x; } void configure_thread_assertions_for_testing() { thread_asserts_cfg_for_testing = true; } diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index cd030583b..df66fd4b5 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -4420,15 +4420,15 @@ static void test_pcre2_escape() { // all the following are intended to be ultimately matched literally - even if they don't look // like that's the intent - so we escape them. - const wchar_t * tests[][2] = { - L".ext", L"\\.ext", - L"{word}", L"\\{word\\}", - L"hola-mundo", L"hola\\-mundo", - L"$17.42 is your total?", L"\\$17\\.42 is your total\\?", - L"not really escaped\\?", L"not really escaped\\\\\\?", + const wchar_t * const tests[][2] = { + {L".ext", L"\\.ext"}, + {L"{word}", L"\\{word\\}"}, + {L"hola-mundo", L"hola\\-mundo"}, + {L"$17.42 is your total?", L"\\$17\\.42 is your total\\?"}, + {L"not really escaped\\?", L"not really escaped\\\\\\?"}, }; - for (auto &test : tests) { + for (const auto &test : tests) { auto escaped = escape_string(test[0], 0, STRING_STYLE_REGEX); if (escaped != test[1]) { err(L"pcre2_escape error: pcre2_escape(%ls) -> %ls, expected %ls", test[0], escaped.c_str(), test[1]); From 34e104ca35ae795051c64b47a88995f6fbc615c8 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 10 Jan 2019 22:20:17 -0600 Subject: [PATCH 152/439] Allow more flexibility with file completions for `yarn` Closes #5502 --- share/completions/yarn.fish | 63 +++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/share/completions/yarn.fish b/share/completions/yarn.fish index ffa11137e..e2650b7d6 100644 --- a/share/completions/yarn.fish +++ b/share/completions/yarn.fish @@ -23,7 +23,8 @@ function __yarn_filtered_list_packages return end - all-the-package-names | string match -er -- "(?:\\b|_)"(commandline -ct | string escape --style=regex) + all-the-package-names | string match -er -- "(?:\\b|_)"(commandline -ct | + string escape --style=regex) | head -n1000 end function __yarn_find_package_json @@ -153,7 +154,7 @@ function __fish_yarn_run end end -complete -f -c yarn -n '__fish_seen_subcommand_from run' -a "(__fish_yarn_run)" +complete -c yarn -n '__fish_seen_subcommand_from run' -a "(__fish_yarn_run)" complete -f -c yarn -n '__fish_use_subcommand' -a tag complete -f -c yarn -n '__fish_seen_subcommand_from tag' -a 'add rm ls' @@ -177,36 +178,36 @@ complete -f -c yarn -n '__fish_use_subcommand' -a why set -g yarn_cmds access add bin cache check clean config generate-lock-entry global info init install licenses link list login logout outdated owner pack publish remove run tag team unlink upgrade upgrade-interactive version versions why # Common short, long options -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l help -s h -d 'output usage information' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l version -s V -d 'output the version number' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l help -s h -d 'output usage information' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l version -s V -d 'output the version number' # The rest of common options are all of them long -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l verbose -d 'output verbose messages on internal operations' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l offline -d 'trigger an error if any required dependencies are not available in local cache' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l prefer-offline -d 'use network only if dependencies are not available in local cache' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l strict-semver -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l json -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-scripts -d 'don\'t run lifecycle scripts' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l har -d 'save HAR output of network traffic' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-platform -d 'ignore platform checks' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-engines -d 'ignore engines check' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-optional -d 'ignore optional dependencies' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l force -d 'ignore all caches' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-bin-links -d 'don\'t generate bin links when setting up packages' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l flat -d 'only allow one version of a package' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l 'prod production' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-lockfile -d 'don\'t read or generate a lockfile' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l pure-lockfile -d 'don\'t generate a lockfile' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l frozen-lockfile -d 'don\'t generate a lockfile and fail if an update is needed' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l global-folder -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l modules-folder -d 'rather than installing modules into the node_modules folder relative to the cwd, output them here' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l cache-folder -d 'specify a custom folder to store the yarn cache' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l verbose -d 'output verbose messages on internal operations' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l offline -d 'trigger an error if any required dependencies are not available in local cache' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l prefer-offline -d 'use network only if dependencies are not available in local cache' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l strict-semver +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l json +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-scripts -d 'don\'t run lifecycle scripts' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l har -d 'save HAR output of network traffic' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-platform -d 'ignore platform checks' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-engines -d 'ignore engines check' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-optional -d 'ignore optional dependencies' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l force -d 'ignore all caches' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-bin-links -d 'don\'t generate bin links when setting up packages' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l flat -d 'only allow one version of a package' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l 'prod production' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-lockfile -d 'don\'t read or generate a lockfile' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l pure-lockfile -d 'don\'t generate a lockfile' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l frozen-lockfile -d 'don\'t generate a lockfile and fail if an update is needed' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l global-folder +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l modules-folder -d 'rather than installing modules into the node_modules folder relative to the cwd, output them here' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l cache-folder -d 'specify a custom folder to store the yarn cache' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l mutex -d 'use a mutex to ensure only one yarn instance is executing' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l mutex -a 'file network' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l mutex -d 'use a mutex to ensure only one yarn instance is executing' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l mutex -a 'file network' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-emoji -d 'disable emoji in output' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l proxy -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l https-proxy -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-progress -d 'disable progress bar' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l network-concurrency -d 'maximum number of concurrent network requests' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-emoji -d 'disable emoji in output' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l proxy +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l https-proxy +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-progress -d 'disable progress bar' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l network-concurrency -d 'maximum number of concurrent network requests' From 848ca1c1ccbf060a3f5778a950f139eb31419413 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 10 Jan 2019 22:33:31 -0600 Subject: [PATCH 153/439] Improve UX by not providing yarn completions if no input Otherwise, the interface would hang while fish processed the output of `all-the-package-names` and then would ultimately not show any results anyhow. --- share/completions/yarn.fish | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/share/completions/yarn.fish b/share/completions/yarn.fish index e2650b7d6..75ccc82ab 100644 --- a/share/completions/yarn.fish +++ b/share/completions/yarn.fish @@ -23,8 +23,11 @@ function __yarn_filtered_list_packages return end - all-the-package-names | string match -er -- "(?:\\b|_)"(commandline -ct | + # Do not provide any completions if nothing has been entered yet + if string match -r . (commandline -ct) + all-the-package-names | string match -er -- "(?:\\b|_)"(commandline -ct | string escape --style=regex) | head -n1000 + end end function __yarn_find_package_json From 56cedac3b5584cdab1954022c2dbeb51c95d0017 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 10 Jan 2019 22:34:30 -0600 Subject: [PATCH 154/439] Show info about `all-the-package-names` helper for yarn/npm completions The informational message is only shown the first time an attempt at completing `yarn add` is made per session. This should vastly improve the discoverability of this feature as regular yarn/npm users would never have `all-the-package-names` installed normally. --- share/completions/yarn.fish | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/share/completions/yarn.fish b/share/completions/yarn.fish index 75ccc82ab..bd7b40228 100644 --- a/share/completions/yarn.fish +++ b/share/completions/yarn.fish @@ -4,27 +4,31 @@ # If all-the-package-names is installed, it will be used to generate npm completions. # Install globally with `sudo npm install -g all-the-package-names`. Keep it up to date. -function __yarn_list_packages +function __yarn_helper_installed if not type -q all-the-package-names - return + if not set -qg __fish_yarn_pkg_info_shown + set -l old (commandline) + commandline -r "" + echo \nfish: Run `yarn global add all-the-package-names` to gain intelligent \ + package completion > /dev/stderr + commandline -f repaint + commandline -r $old + set -g __fish_yarn_pkg_info_shown 1 + end + return 1 end - - all-the-package-names end # Entire list of packages is too long to be used efficiently in a `complete` subcommand. # Search it for matches instead. function __yarn_filtered_list_packages - # We used to avoid the duplication of this check by calling __yarn_list_packages - # instead of all-the-package-names directly below, but a) that breaks IO buffering - # because the output of all-the-package-names is > 10 MiB (#5267), and b) IO - # buffering slowed down the call considerably in all cases. - if not type -q all-the-package-names + if not __yarn_helper_installed return end - # Do not provide any completions if nothing has been entered yet + # Do not provide any completions if nothing has been entered yet to avoid long hang. if string match -r . (commandline -ct) + # Filter the results here rather than in the C++ code due to #5267 all-the-package-names | string match -er -- "(?:\\b|_)"(commandline -ct | string escape --style=regex) | head -n1000 end From 6ea7aa8a00529759caa8c2a113fc5135d125c3e3 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 10 Jan 2019 23:10:21 -0600 Subject: [PATCH 155/439] Share code between yarn and npm completions I had previously introduced a lot of updates and fixes to npm registry based completions for `yarn` but hadn't ported them to `npm` as well (although they can be dropped in as-is). This patch shares the code between the two, which resides in an explicitly sourced multi-function fish script. --- share/completions/npm.fish | 21 ++----- share/completions/yarn.fish | 89 +--------------------------- share/functions/fish_npm_helper.fish | 89 ++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 103 deletions(-) create mode 100644 share/functions/fish_npm_helper.fish diff --git a/share/completions/npm.fish b/share/completions/npm.fish index ff9731c8d..8fdb03d1d 100644 --- a/share/completions/npm.fish +++ b/share/completions/npm.fish @@ -4,21 +4,8 @@ # see also Fish's large set of completions for examples: # https://github.com/fish-shell/fish-shell/tree/master/share/completions -# If all-the-package-names is installed, it will be used to generate npm completions. -# Install globally with `sudo npm install -g all-the-package-names`. Keep it up to date. -function __npm_list_packages - if not type -q all-the-package-names - return - end - - all-the-package-names -end - -# Entire list of packages is too long to be used in a `complete` subcommand -# Search it for matches instead -function __npm_filtered_list_packages - __npm_list_packages | grep (commandline -ct) | head -n 50 -end +source $__fish_data_dir/functions/fish_npm_helper.fish +set -l npm_install "npm install --global" function __fish_npm_needs_command set cmd (commandline -opc) @@ -156,7 +143,7 @@ complete -f -c npm -n '__fish_npm_using_command owner' -a 'rm' -d 'Remove an own # remove for c in 'r' 'remove' 'rm' 'un' 'uninstall' 'unlink' - complete -f -c npm -n '__fish_npm_needs_command' -a "$c" -d 'remove package' + complete -f -c npm -n '__fish_npm_needs_command' -a "$c" -d 'remove package' -xa '(__yarn_installed_packages)' complete -x -c npm -n "__fish_npm_using_command $c" -s g -l global -d 'remove global package' complete -x -c npm -n "__fish_npm_using_command $c" -l save -d 'Package will be removed from your dependencies' complete -x -c npm -n "__fish_npm_using_command $c" -l save-dev -d 'Package will be removed from your devDependencies' @@ -208,4 +195,4 @@ complete -f -c npm -n '__fish_npm_needs_command' -a 'unpublish' -d 'Remove a pac complete -f -c npm -n '__fish_npm_needs_command' -a 'unstar' -d 'Remove star from a package' complete -f -c npm -n '__fish_npm_needs_command' -a 'version' -d 'Bump a package version' complete -f -c npm -n '__fish_npm_needs_command' -a 'whoami' -d 'Display npm username' -complete -f -c npm -n '__fish_seen_subcommand_from install' -a '(__npm_filtered_list_packages)' +complete -f -c npm -n '__fish_seen_subcommand_from install; and not __fish_is_switch' -a "(__yarn_filtered_list_packages \"$npm_install\")" diff --git a/share/completions/yarn.fish b/share/completions/yarn.fish index bd7b40228..378d466d8 100644 --- a/share/completions/yarn.fish +++ b/share/completions/yarn.fish @@ -1,95 +1,12 @@ -# NOTE: Fish helper functions are your best friend here! -# see https://github.com/fish-shell/fish-shell/blob/master/share/functions/__fish_seen_subcommand_from.fish -# and https://github.com/fish-shell/fish-shell/blob/master/share/functions/__fish_use_subcommand.fish - -# If all-the-package-names is installed, it will be used to generate npm completions. -# Install globally with `sudo npm install -g all-the-package-names`. Keep it up to date. -function __yarn_helper_installed - if not type -q all-the-package-names - if not set -qg __fish_yarn_pkg_info_shown - set -l old (commandline) - commandline -r "" - echo \nfish: Run `yarn global add all-the-package-names` to gain intelligent \ - package completion > /dev/stderr - commandline -f repaint - commandline -r $old - set -g __fish_yarn_pkg_info_shown 1 - end - return 1 - end -end - -# Entire list of packages is too long to be used efficiently in a `complete` subcommand. -# Search it for matches instead. -function __yarn_filtered_list_packages - if not __yarn_helper_installed - return - end - - # Do not provide any completions if nothing has been entered yet to avoid long hang. - if string match -r . (commandline -ct) - # Filter the results here rather than in the C++ code due to #5267 - all-the-package-names | string match -er -- "(?:\\b|_)"(commandline -ct | - string escape --style=regex) | head -n1000 - end -end - -function __yarn_find_package_json - set parents (__fish_parent_directories (pwd)) - - for p in $parents - if test -f "$p/package.json" - echo "$p/package.json" - return 0 - end - end - - return 1 -end - -function __yarn_installed_packages - set -l package_json (__yarn_find_package_json) - if not test $status -eq 0 - # no package.json in tree - return 1 - end - - if type -q jq - jq -r '.dependencies as $a1 | .devDependencies as $a2 | ($a1 + $a2) | to_entries[] | .key' $package_json - else - set -l depsFound 0 - for line in (cat $package_json) - # echo "evaluating $line" - if test $depsFound -eq 0 - # echo "mode: noDeps" - if string match -qr '(devD|d)ependencies"' -- $line - # echo "switching to mode: deps" - set depsFound 1 - continue - end - continue - end - - if string match -qr '\}' -- $line - # echo "switching to mode: noDeps" - set depsFound 0 - continue - end - - # echo "mode: deps" - - string replace -r '^\s*"([^"]+)".*' '$1' -- $line - end - end -end - +source $__fish_data_dir/functions/fish_npm_helper.fish +set -l yarn_add "yarn global add" # Typically there is no need to check if (commandline -ct) begins with `--` # because it won't be matched. But we can prevent the slowdown from getting # a list of all packages and filtering through it if we only do that when # completing what seems to be a package name. complete -f -c yarn -n '__fish_seen_subcommand_from remove; and not __fish_is_switch' -xa '(__yarn_installed_packages)' -complete -f -c yarn -n '__fish_seen_subcommand_from add; and not __fish_is_switch' -xa '(__yarn_filtered_list_packages)' +complete -f -c yarn -n '__fish_seen_subcommand_from add; and not __fish_is_switch' -xa "(__yarn_filtered_list_packages \"$yarn_add\")" complete -f -c yarn -n '__fish_use_subcommand' -a help diff --git a/share/functions/fish_npm_helper.fish b/share/functions/fish_npm_helper.fish new file mode 100644 index 000000000..f6eb1efc9 --- /dev/null +++ b/share/functions/fish_npm_helper.fish @@ -0,0 +1,89 @@ +# This file is explicitly sourced by the npm and fish completions +# It is used to enumerate entries from the npm registry, interact with a locally installed +# package.json file, and more. +# Other completions that would depend on this include jspm, pnpm, etc. + +# If all-the-package-names is installed, it will be used to generate npm completions. +# Install globally with `sudo npm install -g all-the-package-names`. Keep it up to date. +function __yarn_helper_installed + # This function takes the command to globally install a package as $argv[1] + if not type -q all-the-package-namesS + if not set -qg __fish_yarn_pkg_info_shown + set -l old (commandline) + commandline -r "" + echo \nfish: Run `$argv[1] all-the-package-names` to gain intelligent \ + package completion > /dev/stderr + commandline -f repaint + commandline -r $old + set -g __fish_yarn_pkg_info_shown 1 + end + return 1 + end +end + +# Entire list of packages is too long to be used efficiently in a `complete` subcommand. +# Search it for matches instead. +function __yarn_filtered_list_packages + # This function takes the command to globally install a package as $argv[1] + if not __yarn_helper_installed $argv[1] + return + end + + # Do not provide any completions if nothing has been entered yet to avoid long hang. + if string match -r . (commandline -ct) + # Filter the results here rather than in the C++ code due to #5267 + all-the-package-names | string match -er -- "(?:\\b|_)"(commandline -ct | + string escape --style=regex) | head -n1000 + end +end + +function __yarn_find_package_json + set parents (__fish_parent_directories (pwd)) + + for p in $parents + if test -f "$p/package.json" + echo "$p/package.json" + return 0 + end + end + + return 1 +end + +function __yarn_installed_packages + set -l package_json (__yarn_find_package_json) + if not test $status -eq 0 + # no package.json in tree + return 1 + end + + if type -q jq + jq -r '.dependencies as $a1 | .devDependencies as $a2 | ($a1 + $a2) | to_entries[] | .key' $package_json + else + set -l depsFound 0 + for line in (cat $package_json) + # echo "evaluating $line" + if test $depsFound -eq 0 + # echo "mode: noDeps" + if string match -qr '(devD|d)ependencies"' -- $line + # echo "switching to mode: deps" + set depsFound 1 + continue + end + continue + end + + if string match -qr '\}' -- $line + # echo "switching to mode: noDeps" + set depsFound 0 + continue + end + + # echo "mode: deps" + + string replace -r '^\s*"([^"]+)".*' '$1' -- $line + end + end +end + + From 6c9065e9ef948c0a257a102e4a7d884fb7d23fc4 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Fri, 11 Jan 2019 00:50:01 -0800 Subject: [PATCH 156/439] abbr -e: use same exit code as set -e if abbr doesn't exist Which is 4, apparently.. (builtin_set.cpp returns ENV_NOT_FOUND) here. This was previously hardcoded to our 121, which used to be what builtins used for invalid arguments. 4 is pretty arbitrary but at least this is more consistent. --- 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 09ad87a12..0ac8b24bd 100644 --- a/share/functions/abbr.fish +++ b/share/functions/abbr.fish @@ -102,7 +102,7 @@ function __fish_abbr_erase --no-scope-shadowing if not set -q $abbr_var_name printf ( _ "%s %s: No abbreviation named %s\n" ) abbr --erase $escaped_name >&2 - return 121 + return 4 # like `set -e doesnt_exist` end set -e $abbr_var_name From 455959ae7afcf3f33bd4700f18a0ec3da73c3271 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 12 Jan 2019 00:06:27 +0800 Subject: [PATCH 157/439] fish.spec: run tests with SHOW_INTERACTIVE_LOG=1 --- fish.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fish.spec.in b/fish.spec.in index d295e7749..e6eabacb1 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -81,7 +81,7 @@ make %{?_smp_mflags} %if 0%{?__builddir:1} cd %__builddir %endif -make test +make test SHOW_INTERACTIVE_LOG=1 %clean rm -rf $RPM_BUILD_ROOT From 59d62fdd538f3680d3e0eec6ec3b545ec8eb2989 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 11 Jan 2019 15:04:09 -0800 Subject: [PATCH 158/439] Thread the right PWD through autosuggestions These were getting / as the PWD, resulting in bogus suggestions. --- src/env.cpp | 6 +++--- src/reader.cpp | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index f48cfde2b..ea921e5e2 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1657,11 +1657,11 @@ maybe_t env_vars_snapshot_t::get(const wcstring &key, env_mode_flags_ wcstring_list_t env_vars_snapshot_t::get_names(int flags) const { return names; } -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", L"PWD", NULL}; const wchar_t *const env_vars_snapshot_t::completing_keys[] = {L"PATH", L"CDPATH", - L"fish_function_path", NULL}; + L"fish_function_path", L"PWD", NULL}; #if defined(__APPLE__) || defined(__CYGWIN__) static int check_runtime_path(const char *path) { diff --git a/src/reader.cpp b/src/reader.cpp index 69a1d2d41..4124aba74 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1292,13 +1292,14 @@ static std::function get_autosuggestion_performer const auto &parser_vars = parser_t::principal_parser().vars(); const unsigned int generation_count = read_generation_count(); const wcstring working_directory = parser_vars.get_pwd_slash(); - env_vars_snapshot_t vars(vars, env_vars_snapshot_t::highlighting_keys); + env_vars_snapshot_t vars(parser_vars, env_vars_snapshot_t::completing_keys); // TODO: suspicious use of 'history' here // This is safe because histories are immortal, but perhaps // this should use shared_ptr return [=]() -> autosuggestion_result_t { ASSERT_IS_BACKGROUND_THREAD(); + const autosuggestion_result_t nothing = {}; // If the main thread has moved on, skip all the work. if (generation_count != read_generation_count()) { From 82170b0862f61e3d845aa9da2fae2d8d174fcc7f Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 11 Jan 2019 15:12:17 -0800 Subject: [PATCH 159/439] Add HOME as a snapshotted variable Corrects certain autosuggestions involving tildes. --- src/env.cpp | 6 +++--- src/reader.cpp | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index ea921e5e2..15053f1d5 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1658,10 +1658,10 @@ maybe_t env_vars_snapshot_t::get(const wcstring &key, env_mode_flags_ wcstring_list_t env_vars_snapshot_t::get_names(int flags) const { return names; } const wchar_t *const env_vars_snapshot_t::highlighting_keys[] = { - L"PATH", L"CDPATH", L"fish_function_path", L"PWD", NULL}; + L"PATH", L"CDPATH", L"fish_function_path", L"PWD", L"HOME", NULL}; -const wchar_t *const env_vars_snapshot_t::completing_keys[] = {L"PATH", L"CDPATH", - L"fish_function_path", L"PWD", NULL}; +const wchar_t *const env_vars_snapshot_t::completing_keys[] = { + L"PATH", L"CDPATH", L"fish_function_path", L"PWD", L"HOME", NULL}; #if defined(__APPLE__) || defined(__CYGWIN__) static int check_runtime_path(const char *path) { diff --git a/src/reader.cpp b/src/reader.cpp index 4124aba74..b70576f3d 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1299,7 +1299,6 @@ static std::function get_autosuggestion_performer return [=]() -> autosuggestion_result_t { ASSERT_IS_BACKGROUND_THREAD(); - const autosuggestion_result_t nothing = {}; // If the main thread has moved on, skip all the work. if (generation_count != read_generation_count()) { From 2d3e8ec0a9646acd68dc5caaf5fbe4cd1f6f791f Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 11 Jan 2019 20:43:52 -0800 Subject: [PATCH 160/439] Correct highlighting of abbreviations Abbreviation highlighting cannot use the snapshot environment because we do not know up-front which variables to capture. Will revisit this later. --- src/expand.cpp | 6 ++++++ src/expand.h | 1 + src/highlight.cpp | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/expand.cpp b/src/expand.cpp index 1d0b1601c..6b5968034 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -1207,6 +1207,12 @@ maybe_t expand_abbreviation(const wcstring &src, const environment_t & return none(); } +maybe_t expand_abbreviation(const wcstring &src) { + // FIXME: We use the principal environment stack because we don't know which variables need to + // be captured in a snapshot. + return expand_abbreviation(src, env_stack_t::principal()); +} + std::map get_abbreviations() { // TODO: try to make this cheaper const auto &vars = env_stack_t::principal(); diff --git a/src/expand.h b/src/expand.h index 180ed9d2f..8dd0da247 100644 --- a/src/expand.h +++ b/src/expand.h @@ -161,6 +161,7 @@ wcstring replace_home_directory_with_tilde(const wcstring &str, const environmen /// Abbreviation support. Expand src as an abbreviation, returning the expanded form if found, /// none() if not. maybe_t expand_abbreviation(const wcstring &src, const environment_t &vars); +maybe_t expand_abbreviation(const wcstring &src); /// \return a snapshot of all abbreviations as a map abbreviation->expansion. std::map get_abbreviations(); diff --git a/src/highlight.cpp b/src/highlight.cpp index cda55083b..9b844f89f 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -1015,7 +1015,7 @@ static bool command_is_valid(const wcstring &cmd, enum parse_statement_decoratio if (!is_valid && function_ok) is_valid = function_exists_no_autoload(cmd, vars); // Abbreviations - if (!is_valid && abbreviation_ok) is_valid = expand_abbreviation(cmd, vars).has_value(); + if (!is_valid && abbreviation_ok) is_valid = expand_abbreviation(cmd).has_value(); // Regular commands if (!is_valid && command_ok) is_valid = path_get_path(cmd, NULL, vars); From db1d694aecd7ec0b08e19ff3c35a9de6953a1c54 Mon Sep 17 00:00:00 2001 From: Dror Levin Date: Sun, 13 Jan 2019 11:37:18 +0200 Subject: [PATCH 161/439] Remove bc from Dockerfile As math is now a builtin bc is no longer required. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 511ce928a..e907e95a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM centos:latest # Build dependency RUN yum update -y &&\ - yum install -y autoconf automake bc clang gcc-c++ make ncurses-devel &&\ + yum install -y autoconf automake clang gcc-c++ make ncurses-devel &&\ yum clean all # Test dependency From de145477be260786af50b89c7b3c43177a70bb7d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 13 Jan 2019 16:32:56 +0100 Subject: [PATCH 162/439] Remove bc from travis.yml This should be the last mention of it, according to `git grep`. --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1f68e79ec..e27a47156 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ matrix: addons: apt: packages: - - bc - expect - gettext - libncurses5-dev @@ -18,7 +17,6 @@ matrix: addons: apt: packages: - - bc - expect - gettext - lib32ncurses5-dev @@ -30,7 +28,6 @@ matrix: addons: apt: packages: - - bc - expect - gettext - libncurses5-dev @@ -55,7 +52,6 @@ matrix: packages: - clang-3.8 - llvm-3.8 # for llvm-symbolizer - - bc - expect - gettext - libncurses5-dev From 73bae383e0fee4dc93d247546df3b70504adbb2b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 13 Jan 2019 16:36:09 +0100 Subject: [PATCH 163/439] completions/git: Stop limiting to the token This enables fuzzy-matching outside of the current directory again. As it turns out, the performance impact here isn't as large as I thought - it's massively dependent on caching. Fixes #5476. --- share/completions/git.fish | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 60822fe49..7c4cb1a5c 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -144,19 +144,13 @@ function __fish_git_files # (don't use --ignored=no because that was only added in git 2.16, from Jan 2018. set -q ignored; and set -a status_opt --ignored - # Glob just the current token for performance - # and so git shows untracked files (even in untracked dirs) for that. - # If the current token is empty, this matches everything in $PWD. - set -l files (commandline -ct) - # The trailing "**" is necessary to match files inside the given directories. - set files "$files*" "$files*/**" set -q untracked; and set -a status_opt -unormal or set -a status_opt -uno # We need to set status.relativePaths to true because the porcelain v2 format still honors that, # and core.quotePath to false so characters > 0x80 (i.e. non-ASCII) aren't considered special. # We explicitly enable globs so we can use that to match the current token. - set -l git_opt -c status.relativePaths -c core.quotePath= --glob-pathspecs + set -l git_opt -c status.relativePaths -c core.quotePath= # We pick the v2 format if we can, because it shows relative filenames (if used without "-z"). # We fall back on the v1 format by reading git's _version_, because trying v2 first is too slow. @@ -278,7 +272,7 @@ function __fish_git_files set -l previousfile # Note that we can't use space as a delimiter between status and filename, because # the status can contain spaces - " M" is different from "M ". - command git $git_opt status --porcelain -z $status_opt -- $files \ + command git $git_opt status --porcelain -z $status_opt \ | while read -lz line set -l desc # The entire line is the "from" from a rename. From 787f453ec2d0b9e28e1e1d02877720ea67b97ef2 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 13 Jan 2019 17:18:39 +0100 Subject: [PATCH 164/439] completions/git: Skip "!" shell-aliases for wrapping We can't complete these, and now the user can do ``` set -g __fish_git_alias_$alias $command ``` e.g. ``` set -g __fish_git_alias_co checkout ``` if the arguments in the alias end up going to `git alias`. Fixes #5412. [ci skip] --- share/completions/git.fish | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index 7c4cb1a5c..7b7971881 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -440,6 +440,12 @@ end # This is because alias:command is an n:1 mapping (an alias can only have one corresponding command, # but a command can be aliased multiple times) git config -z --get-regexp 'alias\..*' | while read -lz alias command _ + # If the command starts with a "!", it's a shell command, run with /bin/sh, + # or any other shell defined at git's build time. + # + # We can't do anything with them, and we run git-config again for listing aliases, + # so we skip them here. + string match -q '!*' -- $command; and continue # Git aliases can contain chars that variable names can't - escape them. set alias (string replace 'alias.' '' -- $alias | string escape --style=var) set -g __fish_git_alias_$alias $command From 6d11e464283b3712d6bf970bc97faa5abfaec648 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 13 Jan 2019 21:31:49 +0100 Subject: [PATCH 165/439] completions/git: Also don't use files for porcelain=2 This was an oversight from the previous commit. Not that it matters much, because we already removed $files. Still, this would fail if someone defined a global $files, so let's fix it. [ci skip] --- 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 7b7971881..c8683e31e 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -157,7 +157,7 @@ function __fish_git_files set -l ver (command git --version | string replace -rf 'git version (\d+)\.(\d+)\.?.*' '$1\n$2') # Version >= 2.11.* has the v2 format. if test "$ver[1]" -gt 2 2>/dev/null; or test "$ver[1]" -eq 2 -a "$ver[2]" -ge 11 2>/dev/null - command git $git_opt status --porcelain=2 $status_opt -- $files \ + command git $git_opt status --porcelain=2 $status_opt \ | while read -la -d ' ' line set -l file set -l desc From 2fdcc4544a0e834c204f0b24324f5852195a21e4 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 13 Jan 2019 16:14:04 -0600 Subject: [PATCH 166/439] Fix extra space in `fish_title` Closes #5517. Credit goes to @jadenPete. [skip-ci] --- share/functions/fish_title.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/fish_title.fish b/share/functions/fish_title.fish index 5eacb73e9..87abe894b 100644 --- a/share/functions/fish_title.fish +++ b/share/functions/fish_title.fish @@ -1,3 +1,3 @@ function fish_title - echo (status current-command) " " (__fish_pwd) + echo (status current-command) (__fish_pwd) end From 027fc4373663cc3916abc2979225018f55d9fa23 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 13 Jan 2019 18:56:19 -0600 Subject: [PATCH 167/439] Fix result after explicit `return` in a `while` block Closes #5513. --- src/parse_execution.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index f1821176c..116804ffc 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -575,8 +575,13 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( } } + // $status after `while` should be 0 if it executed at least once, otherwise the last `$status` + // obtained by executing the condition is preserved. See #4982. if (loop_executed) { - proc_set_last_status(STATUS_CMD_OK); + // Do not override status if exiting due to the presence of an explict `return xxx` (#5513) + if (!associated_block->skip) { + proc_set_last_status(STATUS_CMD_OK); + } } return ret; From 03cdf89bfd64e8c93e0dfd491715eb2cf35b6566 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 13 Jan 2019 18:57:38 -0600 Subject: [PATCH 168/439] Add tests for `$status` after `while` in various scenarios Includes a regression test for #5513 and asserts the behavior defined in \#4982. --- tests/while.err | 0 tests/while.in | 27 +++++++++++++++++++++++++++ tests/while.out | 3 +++ 3 files changed, 30 insertions(+) create mode 100644 tests/while.err create mode 100644 tests/while.in create mode 100644 tests/while.out diff --git a/tests/while.err b/tests/while.err new file mode 100644 index 000000000..e69de29bb diff --git a/tests/while.in b/tests/while.in new file mode 100644 index 000000000..95cb2584c --- /dev/null +++ b/tests/while.in @@ -0,0 +1,27 @@ +# vim: set ft=fish: + +function never_runs + while false + end +end + +function early_return + while true + return 2 + end +end + +function runs_once + set -l i 1 + while test $i -ne 0 && set i (math $i - 1) + end +end + +# this should return 1 +never_runs; echo $status + +# this should return 0 +runs_once; echo $status + +# this should return 2 +early_return; echo $status diff --git a/tests/while.out b/tests/while.out new file mode 100644 index 000000000..56f24a18d --- /dev/null +++ b/tests/while.out @@ -0,0 +1,3 @@ +1 +0 +2 From 7d16714dd3845d3671c5f6717ba6bc5e71abdebe Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 14 Jan 2019 03:08:44 -0800 Subject: [PATCH 169/439] fish_config: tell the user some nice things without Python As discussed in #5492, it would be good if running fish_config without Python actually told the user to install Python. Further, let's give the person some hints on how to configure these things by hand, since they may have to. --- share/functions/fish_config.fish | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/share/functions/fish_config.fish b/share/functions/fish_config.fish index fa6c54e46..b8fb455d6 100644 --- a/share/functions/fish_config.fish +++ b/share/functions/fish_config.fish @@ -6,5 +6,12 @@ function fish_config --description "Launch fish's web based configuration" python2 "$__fish_data_dir/tools/web_config/webconfig.py" $argv else if command -sq python python "$__fish_data_dir/tools/web_config/webconfig.py" $argv + else + echo (set_color $fish_color_error)Cannot launch the web configuration tool.(set_color normal) + echo (set_color -o)fish_config(set_color normal) requires Python. + echo Installing Python will fix this, and also enable completions to be automatically generated from man pages.\n + echo To configure your prompt, create a (set_color -o)fish_prompt(set_color normal) function. + echo There are examples in (set_color $fish_color_valid_path)$__fish_data_dir/tools/web_config/sample_prompts(set_color normal).\n + echo You can tweak your colors by setting the (set_color $fish_color_search_match)\$fish_color_\*(set_color normal) variables. end end From c94adb9d3eab021ca118cb1856189a942389e886 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 14 Jan 2019 03:23:47 -0800 Subject: [PATCH 170/439] fish_config: make clear python 2 or 3 will both work. A person stuck installing it just for fish on their server doesn't want to waste time installing the wrong one, so assuage that. Also tweak to look nicer with 80 columns --- share/functions/fish_config.fish | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/share/functions/fish_config.fish b/share/functions/fish_config.fish index b8fb455d6..d4ef02c9a 100644 --- a/share/functions/fish_config.fish +++ b/share/functions/fish_config.fish @@ -7,10 +7,11 @@ function fish_config --description "Launch fish's web based configuration" else if command -sq python python "$__fish_data_dir/tools/web_config/webconfig.py" $argv else - echo (set_color $fish_color_error)Cannot launch the web configuration tool.(set_color normal) + echo (set_color $fish_color_error)Cannot launch the web configuration tool:(set_color normal) echo (set_color -o)fish_config(set_color normal) requires Python. - echo Installing Python will fix this, and also enable completions to be automatically generated from man pages.\n - echo To configure your prompt, create a (set_color -o)fish_prompt(set_color normal) function. + echo Installing python2 or python3 will fix this, and also enable completions to be + echo automatically generated from man pages.\n + echo To change your prompt, create a (set_color -o)fish_prompt(set_color normal) function. echo There are examples in (set_color $fish_color_valid_path)$__fish_data_dir/tools/web_config/sample_prompts(set_color normal).\n echo You can tweak your colors by setting the (set_color $fish_color_search_match)\$fish_color_\*(set_color normal) variables. end From 2abd0cde85ae47aa6cd786be4fe2f132fcfb27b4 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Tue, 15 Jan 2019 02:02:12 -0800 Subject: [PATCH 171/439] builtin_printf.cpp: remove is_hex_digit, redo is_octal_digit Our is_hex_digit() was redundant, we can just use iswxdigit; the libc implementation is a more efficient table lookup anyhow. Do is_octal_digit() in terms of iswdigit instead of using wcschr. --- src/builtin_printf.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/builtin_printf.cpp b/src/builtin_printf.cpp index 65e008235..6921f4097 100644 --- a/src/builtin_printf.cpp +++ b/src/builtin_printf.cpp @@ -101,11 +101,7 @@ 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_hex_digit(wchar_t c) { - return c != L'\0' && wcschr(L"0123456789ABCDEFabcdef", c) != NULL; -} +static bool is_octal_digit(wchar_t c) { return iswdigit(c) && c < L'8'; } static int hex_to_bin(const wchar_t &c) { switch (c) { @@ -350,7 +346,7 @@ long builtin_printf_state_t::print_esc(const wchar_t *escstart, bool octal_0) { 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) + for (esc_length = 0, ++p; esc_length < 2 && iswxdigit(*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")); this->append_output(ENCODE_DIRECT_BASE + esc_value % 256); @@ -369,7 +365,7 @@ long builtin_printf_state_t::print_esc(const wchar_t *escstart, bool octal_0) { 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)) { + if (!iswxdigit(*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")); From 2b426c1047cc4e4181ed252e7a8be28dcae22229 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 16 Jan 2019 09:29:17 +0100 Subject: [PATCH 172/439] webconfig: Fix binding tab This broke when --preset was introduced. We allow a "--preset" or "--user" to appear right after the "bind", and save the value, but don't use it yet. Fixes #5534. [ci skip] --- share/tools/web_config/webconfig.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 8fb945d7b..95e3e9d4b 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -702,6 +702,20 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): for line in out.split('\n'): comps = line.split(' ', 2) + # If we don't have "bind", a sequence and a mapping, + # it's not a valid binding. + if len(comps) < 3: + continue + + # Store the "--preset" value for later + if comps[1] == '--preset': + preset = True + # There's possibly a way to do this faster, but it's not important. + comps = line.split(' ', 3)[1:] + elif comps[1] == '--user': + preset = False + comps = line.split(' ', 3)[1:] + # Check again if we removed the level. if len(comps) < 3: continue From d7dac4d07718c875c1ac758c5f2b7b1378f6ef59 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 16 Jan 2019 09:31:03 +0100 Subject: [PATCH 173/439] webconfig: Use history -z This did some weird stuff with \x1e. [ci skip] --- share/tools/web_config/webconfig.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 95e3e9d4b..7703532e7 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -742,10 +742,9 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): return [binding.get_json_obj() for binding in bindings] def do_get_history(self): - # Use \x1e ("record separator") to distinguish between history items. - # The first backslash is so Python passes one backslash to fish. - out, err = run_fish_cmd('for val in $history; echo -n $val \\x1e; end') - result = out.split(' \x1e') + # Use NUL to distinguish between history items. + out, err = run_fish_cmd('builtin history -z') + result = out.split('\0') if result: result.pop() # trim off the trailing element return result From c9fe59237bac4644a9944e845236439b4d1071e9 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 16 Jan 2019 10:23:53 +0100 Subject: [PATCH 174/439] webconfig: Allow \co sgr0 in one more place Some $TERMs like tmux and linux use an sgr0 ("reset") value that ends in \co instead of "m". We need to adjust our regex here to catch that, or we'd miscount lines with it. --- share/tools/web_config/webconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 7703532e7..5171d22f5 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -290,9 +290,9 @@ def append_html_for_ansi_escape(full_val, result, span_open): def strip_ansi(val): # Make a half-assed effort to strip ANSI control sequences - # We assume that all such sequences start with 0x1b and end with m, + # We assume that all such sequences start with 0x1b and end with m or ctrl-o, # which catches most cases - return re.sub("\x1b[^m]*m", '', val) + return re.sub("\x1b[^m]*m\x0f?", '', val) def ansi_prompt_line_width(val): From 5779d99a818bac5066704a46e42ba0dda055da20 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 16 Jan 2019 10:50:08 +0100 Subject: [PATCH 175/439] fish_vi_cursor: Check for tput before using If tput isn't available, that's the same as if it failed. This is the last bit necessary to make the tests work on alpine on builds.sr.ht. --- share/functions/fish_vi_cursor.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/fish_vi_cursor.fish b/share/functions/fish_vi_cursor.fish index 27a20083f..d503f934f 100644 --- a/share/functions/fish_vi_cursor.fish +++ b/share/functions/fish_vi_cursor.fish @@ -32,7 +32,7 @@ function fish_vi_cursor -d 'Set cursor shape for different vi modes' # We use the `tput` here just to see if terminfo thinks we can change the cursor. # We cannot use that sequence directly as it's not the correct one for konsole and iTerm, # and because we may want to change the cursor even though terminfo says we can't (tmux). - if not tput Ss >/dev/null 2>/dev/null + if begin; not command -sq tput; or not tput Ss >/dev/null 2>/dev/null; end # Whitelist tmux... and not begin set -q TMUX From 6c1a2c1f30481d3cc9fc2212183a0a70b645af7d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 16 Jan 2019 11:15:38 +0100 Subject: [PATCH 176/439] tests: Don't use mktemp -u This works around a bug on FreeBSD 11. --- tests/cd.in | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/cd.in b/tests/cd.in index fa3964a22..ec212c1f6 100644 --- a/tests/cd.in +++ b/tests/cd.in @@ -1,8 +1,12 @@ # Store pwd to later go back before cleaning up set -l oldpwd (pwd) +# Create a test directory to store our stuff. +set -l base /tmp/cdcomp_test +rm -Rf $base +mkdir -p $base logmsg cd symlink non-resolution set real (mktemp -d) -set link (mktemp -u) +set link $base/link ln -s $real $link cd $link test "$PWD" = "$link" || echo "\$PWD != \$link:"\n "\$PWD: $PWD"\n "\$link: $link"\n @@ -16,9 +20,6 @@ logmsg cd symlink completion # create directory $base/through/the/looking/glass # symlink $base/somewhere/teleport -> $base/through/the/looking/glass # verify that .. completions work -set -l base /tmp/cdcomp_test/ -rm -Rf $base -mkdir -p $base cd $base mkdir -p $base/through/the/looking/glass From de326659390188203f36242f298067cca29a3d38 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 16 Jan 2019 11:47:38 +0100 Subject: [PATCH 177/439] tests/invocation: Disable set -e There's just waaayy too many things that could go wrong with it, so it annoys more than it helps, especially since we don't get any indication what failed. E.g. on FreeBSD, the test failed without a usable message just because `tput` couldn't find an attribute (so colors were unset). --- tests/invocation.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/invocation.sh b/tests/invocation.sh index 08fe332aa..831157208 100755 --- a/tests/invocation.sh +++ b/tests/invocation.sh @@ -54,8 +54,12 @@ # non-zero. # -# Errors will be fatal -set -e +# With this, errors would be fatal. +# However, a return value of non-zero doesn't signal something that necessarily should be fatal. +# For instance, `tput` returns 1 if an attribute isn't defined. +# But we don't want it to kill our script, especially not without any indication. +# +# set -e # The directory this script is in (as everything is relative to here) here="$(cd "$(dirname "$0")" && pwd -P)" From fb74ccb1f238487433a5f5e7a0da791292339218 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Wed, 16 Jan 2019 10:34:16 -0800 Subject: [PATCH 178/439] set's color completions: remove Color description. On `set fish_color_cwd `, a bunch of named colors are shown in the pager. Each and every one has a description of "Color". These are all very obviously colors, and none are not colors, the description does not tell us anything specific about the item. Descriptions in situations like this are actually a hinderance because of the way they cause less to fit into the pager. Remove it --- share/completions/set.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/set.fish b/share/completions/set.fish index f9b6974e8..e7891b934 100644 --- a/share/completions/set.fish +++ b/share/completions/set.fish @@ -90,7 +90,7 @@ complete -c set -n '__fish_seen_argument -s e -l erase; and __fish_seen_argument complete -c set -n '__fish_seen_argument -s e -l erase; and __fish_seen_argument -s l -l local' -f -a "(set -l | string match -rv '^_|^fish_' | string replace ' ' \t'Local Variable: ')" # Color completions -complete -c set -n '__fish_set_is_color' -x -a '(set_color --print-colors)' -d Color +complete -c set -n '__fish_set_is_color' -x -a '(set_color --print-colors)' complete -c set -n '__fish_set_is_color' -s b -l background -x -a '(set_color --print-colors)' -d "Change background color" complete -c set -n '__fish_set_is_color' -s o -l bold -d 'Make font bold' From bad3c5d79d9bfee9b800710b31ce853d3f5413d9 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 16 Jan 2019 15:26:56 -0600 Subject: [PATCH 179/439] Remove dead assignment and clarify ENV_NOT_FOUND behavior for `set -e` --- src/builtin_set.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index 017d2f3cc..e538689ca 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -303,36 +303,33 @@ static bool validate_path_warning_on_colons(const wchar_t *cmd, return any_success; } -static void handle_env_return(int retval, const wchar_t *cmd, const wchar_t *key, io_streams_t &streams) -{ +static void handle_env_return(int retval, const wchar_t *cmd, const wchar_t *key, + io_streams_t &streams) { + switch (retval) { case ENV_OK: { - retval = STATUS_CMD_OK; break; } case ENV_PERM: { streams.err.append_format(_(L"%ls: Tried to change the read-only variable '%ls'\n"), cmd, key); - retval = STATUS_CMD_ERROR; break; } case ENV_SCOPE: { streams.err.append_format( - _(L"%ls: Tried to modify the special variable '%ls' with the wrong scope\n"), cmd, - key); - retval = STATUS_CMD_ERROR; + _(L"%ls: Tried to modify the special variable '%ls' with the wrong scope\n"), + cmd, key); break; } case ENV_INVALID: { streams.err.append_format( - _(L"%ls: Tried to modify the special variable '%ls' to an invalid value\n"), cmd, key); - retval = STATUS_CMD_ERROR; + _(L"%ls: Tried to modify the special variable '%ls' to an invalid value\n"), + cmd, key); break; } case ENV_NOT_FOUND: { streams.err.append_format( _(L"%ls: The variable '%ls' does not exist\n"), cmd, key); - retval = STATUS_CMD_ERROR; break; } default: { @@ -657,8 +654,9 @@ static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, if (idx_count == 0) { // unset the var retval = parser.vars().remove(dest, scope); - // Temporarily swallowing ENV_NOT_FOUND errors to prevent - // breaking all tests that unset variables that aren't set. + // When a non-existent-variable is unset, return ENV_NOT_FOUND as $status + // but do not emit any errors at the console as a compromise between user + // friendliness and correctness. if (retval != ENV_NOT_FOUND) { handle_env_return(retval, cmd, dest, streams); } From 333bf1fd9fa3402982848b17eeb193afffd9d8c5 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 16 Jan 2019 15:38:27 -0600 Subject: [PATCH 180/439] Remove write-only `desc_width` local variable --- src/pager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pager.cpp b/src/pager.cpp index 6883e22e6..52da3581b 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -113,13 +113,12 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s UNUSED(column); UNUSED(row); UNUSED(rendering); - size_t comp_width, desc_width; + size_t comp_width; line_t line_data; if (c->preferred_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 From 53355885c8b148cfbca48a5781872e4b4a0c114e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 16 Jan 2019 15:44:10 -0600 Subject: [PATCH 181/439] Clean up dead code in `builtin_read.cpp` --- src/builtin_read.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 0c1ea1a00..9c767664f 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -496,13 +496,13 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } if (opts.delimiter.empty()) { - // Every character is a separate token with one wrinkle involving non-array mode where the - // final var gets the remaining characters as a single string. + // Every character is a separate token with one wrinkle involving non-array mode where + // the final var gets the remaining characters as a single string. size_t x = std::max(static_cast(1), buff.size()); size_t n_splits = (opts.array || static_cast(vars_left()) > x) ? x : vars_left(); wcstring_list_t chars; chars.reserve(n_splits); - x = x - n_splits + 1; + int i = 0; for (auto it = buff.begin(), end = buff.end(); it != end; ++i, ++it) { if (opts.array || i + 1 < vars_left()) { @@ -517,17 +517,16 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // Array mode: assign each char as a separate element of the sole var. vars.set(*var_ptr++, opts.place, chars); } else { - // Not array mode: assign each char to a separate var with the remainder being assigned - // to the last var. - auto c = chars.begin(); - for (; c != chars.end() && vars_left(); ++c) { + // Not array mode: assign each char to a separate var with the remainder being + // assigned to the last var. + for (auto c = chars.begin(); c != chars.end() && vars_left(); ++c) { vars.set_one(*var_ptr++, opts.place, *c); } } } else if (opts.array) { - // The user has requested the input be split into a sequence of tokens and all the tokens - // assigned to a single var. How we do the tokenizing depends on whether the user specified - // the delimiter string or we're using IFS. + // The user has requested the input be split into a sequence of tokens and all the + // tokens assigned to a single var. How we do the tokenizing depends on whether the user + // specified the delimiter string or we're using IFS. if (!opts.have_delimiter) { // We're using IFS, so tokenize the buffer using each IFS char. This is for backward // compatibility with old versions of fish. From 20cdcfadaca6ce52ac8d80c4203f89ab957b81f0 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 16 Jan 2019 15:46:11 -0600 Subject: [PATCH 182/439] Remove write-only assignments from `autload.cpp` --- src/autoload.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/autoload.cpp b/src/autoload.cpp index bcd980796..a3ad207c1 100644 --- a/src/autoload.cpp +++ b/src/autoload.cpp @@ -134,11 +134,10 @@ autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcs ASSERT_IS_LOCKED(lock); autoload_function_t *func = this->get(cmd); if (!func) { - bool added; if (allow_eviction) { - added = this->insert(cmd, autoload_function_t(false)); + this->insert(cmd, autoload_function_t(false)); } else { - added = this->insert_no_eviction(cmd, autoload_function_t(false)); + this->insert_no_eviction(cmd, autoload_function_t(false)); } func = this->get(cmd); assert(func); From dd31933c09ffecfba86cad0834fd8ee6602e0397 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 16 Jan 2019 15:48:25 -0600 Subject: [PATCH 183/439] Remove spurious initialization in `profiling_cmd_name_for_redirectable_block` --- src/parse_execution.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 116804ffc..9f09ed3e3 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -68,11 +68,10 @@ static wcstring profiling_cmd_name_for_redirectable_block(const parse_node_t &no // 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; auto term = tree.find_child(node); assert(term.has_source() && term.source_range()->start >= src_start); - src_len = term.source_range()->start - src_start; + size_t src_len = term.source_range()->start - src_start; wcstring result = wcstring(src, src_start, src_len); result.append(L"..."); From 1b23814f8b7f3c0452482320299292bf60f4d061 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 16 Jan 2019 17:41:21 -0600 Subject: [PATCH 184/439] Clarify the point of `type --force-path` [docs] [ci skip] --- doc_src/type.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/type.txt b/doc_src/type.txt index 2d1a56abf..f56ab8919 100644 --- a/doc_src/type.txt +++ b/doc_src/type.txt @@ -19,7 +19,7 @@ The following options are available: - `-p` or `--path` returns the name of the disk file that would be executed, or nothing if `type -t name` would not return `file`. -- `-P` or `--force-path` returns the name of the disk file that would be executed, or nothing if no file with the specified name could be found in the $PATH. +- `-P` or `--force-path` returns the path to the executable file `NAME`, presuming `NAME` is found in $PATH, or nothing otherwise. `--force-path` explicitly resolves only the path to executable files in $PATH, regardless of whether `$NAME` is shadowed by a function or builtin with the same name. - `-q` or `--quiet` suppresses all output; this is useful when testing the exit status. From 84339d5636ce38a559dd5d7b74c138bbc03dbbf9 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 17 Jan 2019 09:44:35 +0100 Subject: [PATCH 185/439] Don't wrap functions with themselves Our weird %-expanding function wrappers around kill et all defined "--wraps" for the same name. As it turns out, fish follows that one, and executes the completion multiple times. I didn't notice because these tend to be rather quick on linux, but on macOS that's apparently a real issue. Fixes #5541. [ci skip] --- share/config.fish | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/share/config.fish b/share/config.fish index 27e6222a3..18242e06a 100644 --- a/share/config.fish +++ b/share/config.fish @@ -259,23 +259,23 @@ function __fish_expand_pid_args end end -function bg --wraps bg +function bg builtin bg (__fish_expand_pid_args $argv) end -function fg --wraps fg +function fg builtin fg (__fish_expand_pid_args $argv) end -function kill --wraps kill +function kill command kill (__fish_expand_pid_args $argv) end -function wait --wraps wait +function wait builtin wait (__fish_expand_pid_args $argv) end -function disown --wraps disown +function disown builtin disown (__fish_expand_pid_args $argv) end From 58b696bed16d2b5c859f218e00fd82570be60ead Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 17 Jan 2019 09:49:50 +0100 Subject: [PATCH 186/439] complete: Don't allow wrapping a command with itself Double-fixes #5541, by not allowing it to happen. --- src/complete.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/complete.cpp b/src/complete.cpp index effcb05e9..5ef7d3dfd 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -1656,6 +1656,11 @@ bool complete_add_wrapper(const wcstring &command, const wcstring &new_target) { return false; } + // If the command and the target are the same, + // there's no point in following the wrap-chain because we'd only complete the same thing. + // TODO: This should maybe include full cycle detection. + if (command == new_target) return false; + auto locked_map = wrapper_map.acquire(); wrapper_map_t &wraps = *locked_map; wcstring_list_t *targets = &wraps[command]; From 02c32c638f155290fedbcf34a9c8c0438d136c47 Mon Sep 17 00:00:00 2001 From: Sam Yu Date: Sat, 12 Jan 2019 14:05:23 +0800 Subject: [PATCH 187/439] Fix completion of directories for configure --- share/completions/configure.fish | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/share/completions/configure.fish b/share/completions/configure.fish index 67eb5d519..6c7880ef2 100644 --- a/share/completions/configure.fish +++ b/share/completions/configure.fish @@ -4,9 +4,9 @@ complete -c configure -s q -l quiet -d "Quiet mode" complete -c configure -l cache-file -f -d "Cache test results in specified file" complete -c configure -s C -l config-cache -d "Cache test results in file config.cache" complete -c configure -s n -l no-create -d "Do not create output files" -complete -c configure -l srcdir -d "Set source directory" -a "__fish_complete_directories (commandline -ct)" -x -complete -c configure -l prefix -d "Architecture-independent install directory" -a "__fish_complete_directories (commandline -ct)" -x -complete -c configure -l exec-prefix -d "Architecture-dependent install directory" -a "__fish_complete_directories (commandline -ct)" -x +complete -c configure -l srcdir -d "Set source directory" -a "(__fish_complete_directories)" -x +complete -c configure -l prefix -d "Architecture-independent install directory" -a "(__fish_complete_directories)" -x +complete -c configure -l exec-prefix -d "Architecture-dependent install directory" -a "(__fish_complete_directories)" -x complete -c configure -l build -d "Configure for building on BUILD" -x complete -c configure -l host -d "Cross-compile to build programs to run on HOST" -x complete -c configure -l target -d "Configure for building compilers for TARGET" -x From 34ed958f72c3d36967cb2a79bad56e60562132c0 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 17 Jan 2019 16:43:24 +0100 Subject: [PATCH 188/439] Test that things can't wrap themselves This is a test belonging to the previous commit, 58b696bed. See #5541. --- tests/complete.in | 8 +++++++- tests/complete.out | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/complete.in b/tests/complete.in index 857cc9deb..647e4b3f1 100644 --- a/tests/complete.in +++ b/tests/complete.in @@ -18,4 +18,10 @@ alias myalias2='complete_test_alpha1 arg2' myalias1 call1 myalias2 call2 complete -C'myalias1 call3 ' -complete -C'myalias2 call3 ' \ No newline at end of file +complete -C'myalias2 call3 ' + +# Ensure that commands can't wrap themselves - if this did, +# the completion would be executed a bunch of times. +function t --wraps t; echo t; end +complete -c t -fa '(t)' +complete -C't ' diff --git a/tests/complete.out b/tests/complete.out index 419d0d3ac..d813b436e 100644 --- a/tests/complete.out +++ b/tests/complete.out @@ -11,3 +11,4 @@ arg1 call1 arg2 call2 complete_test_alpha1 arg1 call3 complete_test_alpha1 arg2 call3 +t From 857561ca1416e11d7e0781f9f9352e3b9414eabc Mon Sep 17 00:00:00 2001 From: Dan Zimmerman Date: Mon, 14 Jan 2019 07:03:19 +0800 Subject: [PATCH 189/439] Fix warnings when compiling on macos These warnings were appearing and annoying me so Im making a PR to fix them. --- src/builtin.cpp | 1 + src/builtin_test.cpp | 2 +- src/complete.cpp | 5 ++++- src/env.cpp | 17 +++++++++++++++-- src/expand.cpp | 2 ++ 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/builtin.cpp b/src/builtin.cpp index 8e3ddeca5..f74d5fa74 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -162,6 +162,7 @@ static int count_char(const wchar_t *str, wchar_t c) { /// wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t *name) { UNUSED(parser); + UNUSED(streams); // This won't ever work if no_exec is set. if (no_exec) return wcstring(); diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp index 7284034a3..85cd32d03 100644 --- a/src/builtin_test.cpp +++ b/src/builtin_test.cpp @@ -116,7 +116,7 @@ struct token_info_t { unsigned int flags; }; -const token_info_t * const token_for_string(const wcstring &str) { +const token_info_t *token_for_string(const wcstring &str) { static const std::map token_infos = { {L"", {test_unknown, 0}}, {L"!", {test_bang, 0}}, diff --git a/src/complete.cpp b/src/complete.cpp index 5ef7d3dfd..8beaade7d 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -186,7 +186,10 @@ const option_list_t &completion_entry_t::get_options() const { } description_func_t const_desc(const wcstring &s) { - return [=](const wcstring &ignored) { return s; }; + return [=](const wcstring &ignored) { + UNUSED(ignored); + return s; + }; } /// Clear the COMPLETE_AUTO_SPACE flag, and set COMPLETE_NO_SPACE appropriately depending on the diff --git a/src/env.cpp b/src/env.cpp index 15053f1d5..b41f61d6c 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -794,6 +794,7 @@ static void handle_term_size_change(const wcstring &op, const wcstring &var_name env_stack_t &vars) { UNUSED(op); UNUSED(var_name); + UNUSED(vars); invalidate_termsize(true); // force fish to update its idea of the terminal size plus vars } @@ -815,6 +816,7 @@ static void handle_function_path_change(const wcstring &op, const wcstring &var_ env_stack_t &vars) { UNUSED(op); UNUSED(var_name); + UNUSED(vars); function_invalidate_path(); } @@ -822,6 +824,7 @@ static void handle_complete_path_change(const wcstring &op, const wcstring &var_ env_stack_t &vars) { UNUSED(op); UNUSED(var_name); + UNUSED(vars); complete_invalidate_path(); } @@ -1614,9 +1617,14 @@ env_stack_t::env_stack_t(env_stack_t &&) = default; null_environment_t::null_environment_t() = default; null_environment_t::~null_environment_t() = default; maybe_t null_environment_t::get(const wcstring &key, env_mode_flags_t mode) const { + UNUSED(key); + UNUSED(mode); return none(); } -wcstring_list_t null_environment_t::get_names(int flags) const { return {}; } +wcstring_list_t null_environment_t::get_names(int flags) const { + UNUSED(flags); + return {}; +} env_stack_t env_stack_t::make_principal() { const env_stack_t &gl = env_stack_t::globals(); @@ -1650,12 +1658,16 @@ env_vars_snapshot_t::env_vars_snapshot_t(const environment_t &source, const wcha env_vars_snapshot_t::~env_vars_snapshot_t() = default; maybe_t env_vars_snapshot_t::get(const wcstring &key, env_mode_flags_t mode) const { + UNUSED(mode); auto iter = vars.find(key); if (iter == vars.end()) return none(); return iter->second; } -wcstring_list_t env_vars_snapshot_t::get_names(int flags) const { return names; } +wcstring_list_t env_vars_snapshot_t::get_names(int flags) const { + UNUSED(flags); + return names; +} const wchar_t *const env_vars_snapshot_t::highlighting_keys[] = { L"PATH", L"CDPATH", L"fish_function_path", L"PWD", L"HOME", NULL}; @@ -1665,6 +1677,7 @@ const wchar_t *const env_vars_snapshot_t::completing_keys[] = { #if defined(__APPLE__) || defined(__CYGWIN__) static int check_runtime_path(const char *path) { + UNUSED(path); return 0; } #else diff --git a/src/expand.cpp b/src/expand.cpp index 6b5968034..665387499 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -899,6 +899,7 @@ typedef expand_error_t (*expand_stage_t)(wcstring input, //!OCL static expand_error_t expand_stage_cmdsubst(wcstring input, std::vector *out, expand_flags_t flags, const environment_t &vars, parse_error_list_t *errors) { + UNUSED(vars); if (EXPAND_SKIP_CMDSUBST & flags) { wchar_t *begin, *end; if (parse_util_locate_cmdsubst(input.c_str(), &begin, &end, true) == 0) { @@ -943,6 +944,7 @@ static expand_error_t expand_stage_variables(wcstring input, std::vector *out, expand_flags_t flags, const environment_t &vars, parse_error_list_t *errors) { + UNUSED(vars); return expand_braces(input, flags, out, errors); } From 1398ee9bbbaa694c529450587234d7266b3ed91e Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Thu, 17 Jan 2019 17:35:05 -0600 Subject: [PATCH 190/439] docs: list full command to change default shell --- doc_src/tutorial.hdr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/tutorial.hdr b/doc_src/tutorial.hdr index b4ef8dfce..07b2a6387 100644 --- a/doc_src/tutorial.hdr +++ b/doc_src/tutorial.hdr @@ -696,7 +696,7 @@ vim If you wish to use fish (or any other shell) as your default shell, you need to enter your new shell's executable `/usr/local/bin/fish` in two places: - add `/usr/local/bin/fish` to `/etc/shells` -- change your default shell with `chsh -s` to `/usr/local/bin/fish` +- change your default shell with `chsh -s /usr/local/bin/fish` You can use the following commands for this: From 3847d2e9d1b96293407d331dea986c4757e39f13 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 18 Jan 2019 19:24:49 +0100 Subject: [PATCH 191/439] Also set the read-only flag for non-electric vars For some reason, we have two places where a variable can be read-only: - By key in env.cpp:is_read_only(), which is checked via set* - By flag on the actual env_var_t, which is checked e.g. in parse_execution The latter didn't happen for non-electric variables like hostname, because they used the default constructor, because they were constructed via operator[] (or some such C++-iness). This caused for-loops to crash on an assert if they used a non-electric read-only var like $hostname or $SHLVL. Instead, we explicitly set the flag. We might want to remove one of the two read-only checks, or something? Fixes #5548. --- src/env.cpp | 1 + src/env.h | 8 ++++++++ tests/test1.err | 6 ++++++ tests/test1.in | 5 +++++ tests/test1.out | 3 +++ 5 files changed, 23 insertions(+) diff --git a/src/env.cpp b/src/env.cpp index b41f61d6c..6582f928d 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1221,6 +1221,7 @@ int env_stack_t::set_internal(const wcstring &key, env_mode_flags_t input_var_mo var.set_vals(std::move(val)); var.set_pathvar(var_mode & ENV_PATHVAR); + var.set_read_only(is_read_only(key)); if (var_mode & ENV_EXPORT) { // The new variable is exported. diff --git a/src/env.h b/src/env.h index 970d05cad..5d2334efc 100644 --- a/src/env.h +++ b/src/env.h @@ -126,6 +126,14 @@ class env_var_t { } } + void set_read_only(bool read_only) { + if (read_only) { + flags |= flag_read_only; + } else { + flags &= ~flag_read_only; + } + } + static env_var_flags_t flags_for(const wchar_t *name); env_var_t &operator=(const env_var_t &var) = default; diff --git a/tests/test1.err b/tests/test1.err index 5e9cda705..e5a978376 100644 --- a/tests/test1.err +++ b/tests/test1.err @@ -38,6 +38,12 @@ fish: You cannot use read-only variable 'status' in a for loop for status in a b c ^ +#################### +# That goes for non-electric ones as well (#5548) +fish: You cannot use read-only variable 'hostname' in a for loop +for hostname in a b c + ^ + #################### # For loop control vars available outside the for block diff --git a/tests/test1.in b/tests/test1.in index d2910b6f4..f006e774a 100644 --- a/tests/test1.in +++ b/tests/test1.in @@ -163,6 +163,11 @@ for status in a b c echo $status end +logmsg "That goes for non-electric ones as well (#5548)" +for hostname in a b c + echo $hostname +end + logmsg For loop control vars available outside the for block begin set -l loop_var initial-value diff --git a/tests/test1.out b/tests/test1.out index 51ab3d525..6c141412d 100644 --- a/tests/test1.out +++ b/tests/test1.out @@ -96,6 +96,9 @@ Checking for infinite loops in no-execute #################### # For loops with read-only vars is an error (#4342) +#################### +# That goes for non-electric ones as well (#5548) + #################### # For loop control vars available outside the for block $loop_var: set in local scope, unexported, with 1 elements From b1f5cb9bf490f40b5444e8e2e0498969a3fcd64d Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 19 Jan 2019 13:23:19 -0800 Subject: [PATCH 192/439] Revert "Revert "Fix unsafe locale usage in `wcstod_l` fallback"" This reverts commit c15a702f18d5cee8a4e54c486f90467e8729a62e. The tests are no longer broken after rerunning CMake. --- src/fallback.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fallback.cpp b/src/fallback.cpp index a0db093a9..bcfc4d926 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -394,13 +394,13 @@ int flock(int fd, int op) { // For platforms without wcstod_l C extension, wrap wcstod after changing the // thread-specific locale. double fish_compat::wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc) { - char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL)); - // Yes, this is hardcoded to use the "C" locale. - // That's the only thing we need, and uselocale(loc) broke in my testing. - setlocale(LC_NUMERIC, "C"); + // Create and use a new, thread-specific locale + locale_t locale = newlocale(LC_NUMERIC, "C", nullptr); + locale_t prev_locale = uselocale(locale); double ret = wcstod(enptr, endptr); - setlocale(LC_NUMERIC, saved_locale); - free(saved_locale); + // Restore the old locale before freeing the locale we created and are still using + uselocale(prev_locale); + freelocale(locale); return ret; } #endif // defined(wcstod_l) From f90cb3957f7c695b2ac0a32a848b2efcaa32ccaa Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 16 Jan 2019 14:42:15 -0600 Subject: [PATCH 193/439] Add missing define for HAVE_WCSTOD_L to osx/config.h I believe this should take care of the reported problem with the corrected definition for `wcstod_l`. For future reference, any changes to `config.h.in` should also be reflected in `osx/config.h` --- osx/config.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osx/config.h b/osx/config.h index da6aadaab..cd7f2b746 100644 --- a/osx/config.h +++ b/osx/config.h @@ -181,6 +181,9 @@ /* Define to 1 if you have the `wcsndup' function. */ /* #undef HAVE_WCSNDUP */ +/* Define to 1 if you have the `wcstod_l' function. */ +#define HAVE_WCSTOD_L 1 + /* Define to 1 if the winsize struct and TIOCGWINSZ macro exist */ #define HAVE_WINSIZE 1 From c66b3128ec3d52897a2083b66574b8090bd11b1e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 20 Jan 2019 18:33:04 +0100 Subject: [PATCH 194/439] Use wcstod_l on NetBSD It has wcstod_l, but not uselocale, so we can't use the fallback. --- src/fallback.cpp | 2 +- src/fallback.h | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/fallback.cpp b/src/fallback.cpp index bcfc4d926..4826426b9 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -389,7 +389,7 @@ int flock(int fd, int op) { #endif // HAVE_FLOCK -#ifndef HAVE_WCSTOD_L +#if !defined(HAVE_WCSTOD_L) && !defined(__NetBSD__) #undef wcstod_l // For platforms without wcstod_l C extension, wrap wcstod after changing the // thread-specific locale. diff --git a/src/fallback.h b/src/fallback.h index 0a94c2b5f..62422f923 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -199,7 +199,10 @@ int flock(int fd, int op); #endif -#ifndef HAVE_WCSTOD_L +// NetBSD _has_ wcstod_l, but it's doing some weak linking hullabaloo that I don't get. +// Since it doesn't have uselocale (yes, the standard function isn't there, the non-standard extension is), +// we can't try to use the fallback. +#if !defined(HAVE_WCSTOD_L) && !defined(__NetBSD__) // On some platforms if this is incorrectly detected and a system-defined // defined version of `wcstod_l` exists, calling `wcstod` from our own // `wcstod_l` can call back into `wcstod_l` causing infinite recursion. From 2a190c6f3beb165559e8a231f68303296e9cfad1 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 13:52:03 -0800 Subject: [PATCH 195/439] exec to only warn on background jobs in interactive sessions Extension of fix for #5449 in b007248 --- src/parse_execution.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 9f09ed3e3..3ac569069 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -775,7 +775,7 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process( // Protect against exec with background processes running static uint32_t last_exec_run_counter = -1; - if (process_type == INTERNAL_EXEC) { + if (process_type == INTERNAL_EXEC && shell_is_interactive()) { job_iterator_t jobs; bool have_bg = false; const job_t *bg = nullptr; From 60ced5dbc70a2269ece22ce9d100ddeadc166dbb Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 30 Dec 2018 22:32:07 +0100 Subject: [PATCH 196/439] Only warn on exec for background jobs If it's a foreground job, it is related to the currently running exec. This fixes exec in functions, i.e. function reload exec fish end would previously always ask about the "function reload" job. Fixes #5449. Fixes oh-my-fish/oh-my-fish#664. --- src/parse_execution.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 701d41106..09b7f7d9d 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -773,7 +773,10 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process( bool have_bg = false; const job_t *bg = nullptr; while ((bg = jobs.next())) { - if (!bg->is_completed()) { + // The assumption here is that if it is a foreground job, + // it's related to us. + // This stops us from asking if we're doing `exec` inside a function. + if (!bg->is_completed() && !bg->is_foreground()) { have_bg = true; break; } From 1ce9721590b4d8dddf57be730ba82dccdd3df925 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 13:52:03 -0800 Subject: [PATCH 197/439] exec to only warn on background jobs in interactive sessions Extension of fix for #5449 in b007248 --- src/parse_execution.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 09b7f7d9d..68a63c7c1 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -768,7 +768,7 @@ parse_execution_result_t parse_execution_context_t::populate_plain_process( // Protect against exec with background processes running static uint32_t last_exec_run_counter = -1; - if (process_type == INTERNAL_EXEC) { + if (process_type == INTERNAL_EXEC && shell_is_interactive()) { job_iterator_t jobs; bool have_bg = false; const job_t *bg = nullptr; From f4351eb0f30facabf3f94c6804973d4487a035f6 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 13:55:18 -0800 Subject: [PATCH 198/439] Relnote fix for #5449 in 3.0.1 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2efdde7e..d7b6f1aa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# fish 3.0.1 + +### Fixes and improvements + +- exec now behaves properly inside functions (#5449) + + # fish 3.0.0 (released December 28, 2018) fish 3 is a major release, which introduces some breaking changes alongside improved functionality. Although most existing scripts will continue to work, they should be reviewed against the list contained in the 3.0b1 release notes below. From e490372fbf0f32e966ce228a2b4ae9b7b14cea34 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 13:55:18 -0800 Subject: [PATCH 199/439] Relnote fix for #5449 --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de6b12fc4..922b30a0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,14 @@ - fish_clipboard_* now supports wayland by means of [wl-clipboard](https://github.com/bugaevc/wl-clipboard). - mandoc can now be used to format the output from `--help` if nroff is not installed ---- + +======= +# fish 3.0.1 + +### Fixes and improvements + +- exec now behaves properly inside functions (#5449) + # fish 3.0.0 (released December 28, 2018) From e2f2dbf032c0a3bcc13b91a5d56cec2f5e294027 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 15:04:27 -0800 Subject: [PATCH 200/439] Correctly handle exited jobs in process_mark_finished_children This is effectively a pick of 2ebdcf82eebec230549b64dc740ea604c7772049 and the subsequent fixup. However we also avoid setting WNOHANG unless waitpid() indicates a process was reaped. Fixes #5438 --- src/proc.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/proc.cpp b/src/proc.cpp index 4790449c5..b7b30dc22 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -467,6 +467,11 @@ static bool process_mark_finished_children(bool block_on_fg) { break; } assert((*process)->pid != INVALID_PID && "Waiting by process on an invalid PID!"); + // Don't wait for completed jobs; see #5438. + if ((*process)->completed) { + process++; + continue; + } pid = waitpid((*process)->pid, &status, options); process++; } else { @@ -475,14 +480,14 @@ static bool process_mark_finished_children(bool block_on_fg) { pid = waitpid(-1 * j->pgid, &status, options); } - // Never make two calls to waitpid(2) without WNOHANG (i.e. with "HANG") in a row, - // because we might wait on a non-stopped job that becomes stopped, but we don't refresh - // our view of the process state before calling waitpid(2) again here. - options |= WNOHANG; - if (pid > 0) { // A child process has been reaped handle_child_status(pid, status); + + // Always set WNOHANG (that is, don't hang). Otherwise we might wait on a non-stopped job + // that becomes stopped, but we don't refresh our view of the process state before + // calling waitpid(2) again here. + options |= WNOHANG; } else if (pid == 0 || errno == ECHILD) { // No killed/dead children in this particular process group if (!wait_by_process) { From fec10830d302e812f8e1f39962e4f08ec35e09a6 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 15:04:27 -0800 Subject: [PATCH 201/439] Correctly handle exited jobs in process_mark_finished_children This is effectively a pick of 2ebdcf82eebec230549b64dc740ea604c7772049 and the subsequent fixup. However we also avoid setting WNOHANG unless waitpid() indicates a process was reaped. Fixes #5438 --- src/proc.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/proc.cpp b/src/proc.cpp index f8794b82e..8aa59fde1 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -517,14 +517,14 @@ static bool process_mark_finished_children(bool block_on_fg) { pid = waitpid(-1 * j->pgid, &status, options); } - // Never make two calls to waitpid(2) without WNOHANG (i.e. with "HANG") in a row, - // because we might wait on a non-stopped job that becomes stopped, but we don't refresh - // our view of the process state before calling waitpid(2) again here. - options |= WNOHANG; - if (pid > 0) { // A child process has been reaped handle_child_status(pid, status); + + // Always set WNOHANG (that is, don't hang). Otherwise we might wait on a non-stopped job + // that becomes stopped, but we don't refresh our view of the process state before + // calling waitpid(2) again here. + options |= WNOHANG; } else if (pid == 0 || errno == ECHILD) { // No killed/dead children in this particular process group if (!wait_by_process) { From 1680b741b2a7a8ab9665bf3b6020266c062225b5 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 16:37:20 -0800 Subject: [PATCH 202/439] Make while loops evaluate to the last executed command status A while loop now evaluates to the last executed command in the body, or zero if the loop body is empty. This matches POSIX semantics. Add a bunch of tricky tests. See #4982 --- CHANGELOG.md | 1 + src/parse_execution.cpp | 38 ++++++++++++++++++------------ tests/while.err | 3 +++ tests/while.in | 52 ++++++++++++++++++++++++++++++++++++++--- tests/while.out | 15 +++++++++--- 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 922b30a0f..e351b4279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ ### Fixes and improvements - exec now behaves properly inside functions (#5449) +- while loops now evaluate to the last executed command in the loop body (or zero if the body was empty), matching POSIX semantics. # fish 3.0.0 (released December 28, 2018) diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 3ac569069..cb2fa3b08 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -524,13 +524,28 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( const block_t *associated_block) { parse_execution_result_t ret = parse_execution_success; + // "The exit status of the while loop shall be the exit status of the last compound-list-2 + // executed, or zero if none was executed." + // Here are more detailed requirements: + // - If we execute the loop body zero times, or the loop body is empty, the status is success. + // - An empty loop body is treated as true, both in the loop condition and after loop exit. + // - The exit status of the last command is visible in the loop condition. (i.e. do not set the + // exit status to true BEFORE executing the loop condition). + // We achieve this by restoring the status if the loop condition fails, plus a special + // affordance for the first condition. + bool first_cond_check = true; + // The conditions of the while loop. tnode_t condition_head = header.child<1>(); tnode_t condition_boolean_tail = header.child<3>(); // Run while the condition is true. - bool loop_executed = false; for (;;) { + // Save off the exit status if it came from the loop body. We'll restore it if the condition + // is false. + int cond_saved_status = first_cond_check ? EXIT_SUCCESS : proc_get_last_status(); + first_cond_check = false; + // Check the condition. parse_execution_result_t cond_ret = this->run_job_conjunction(condition_head, associated_block); @@ -538,8 +553,13 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( cond_ret = run_job_list(condition_boolean_tail, associated_block); } - // We only continue on successful execution and EXIT_SUCCESS. - if (cond_ret != parse_execution_success || proc_get_last_status() != EXIT_SUCCESS) { + // If the loop condition failed to execute, then exit the loop without modifying the exit + // status. If the loop condition executed with a failure status, restore the status and then + // exit the loop. + if (cond_ret != parse_execution_success) { + break; + } else if (proc_get_last_status() != EXIT_SUCCESS) { + proc_set_last_status(cond_saved_status); break; } @@ -549,8 +569,6 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( break; } - loop_executed = true; - // Push a while block and then check its cancellation reason. while_block_t *wb = parser->push_block(); this->run_job_list(contents, wb); @@ -573,16 +591,6 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( break; } } - - // $status after `while` should be 0 if it executed at least once, otherwise the last `$status` - // obtained by executing the condition is preserved. See #4982. - if (loop_executed) { - // Do not override status if exiting due to the presence of an explict `return xxx` (#5513) - if (!associated_block->skip) { - proc_set_last_status(STATUS_CMD_OK); - } - } - return ret; } diff --git a/tests/while.err b/tests/while.err index e69de29bb..2549f3894 100644 --- a/tests/while.err +++ b/tests/while.err @@ -0,0 +1,3 @@ + +#################### +# Loops exit status handling diff --git a/tests/while.in b/tests/while.in index 95cb2584c..d183a54f6 100644 --- a/tests/while.in +++ b/tests/while.in @@ -18,10 +18,56 @@ function runs_once end # this should return 1 -never_runs; echo $status +never_runs; echo "Empty Loop in Function: $status" # this should return 0 -runs_once; echo $status +runs_once; echo "Runs Once: $status" # this should return 2 -early_return; echo $status +early_return; echo "Early Return: $status" + +logmsg Loops exit status handling + +function set_status ; return $argv[1]; end + +# The previous status is visible in the loop condition. +# This includes both the incoming status, and the last command in the +# loop body. +set_status 36 +while begin + set -l saved $status + echo "Condition Status: $status" + set_status $saved + end + true +end + +# The condition status IS visible in the loop body. +set_status 55 +while true + echo "Body Status: $status" + break +end + +# The status of the last command is visible in the loop condition +set_status 13 +while begin + set -l saved $status + echo "Condition 2 Status: $saved" + test $saved -ne 5 + end + set_status 5 +end + +# The status of the last command is visible outside the loop +set rem 5 7 11 +while [ (count $rem) -gt 0 ] + set_status $rem[1] + set rem $rem[2..-1] +end +echo "Loop Exit Status: $status" + +# Empty loops succeed. +false +while false; end +echo "Empty Loop Status: $status" diff --git a/tests/while.out b/tests/while.out index 56f24a18d..f23708aa4 100644 --- a/tests/while.out +++ b/tests/while.out @@ -1,3 +1,12 @@ -1 -0 -2 +Empty Loop in Function: 0 +Runs Once: 0 +Early Return: 2 + +#################### +# Loops exit status handling +Condition Status: 36 +Body Status: 0 +Condition 2 Status: 13 +Condition 2 Status: 5 +Loop Exit Status: 11 +Empty Loop Status: 0 From 1d21e3f47057f238b4fa3d3b9b8fcd8737d110c8 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 16:37:20 -0800 Subject: [PATCH 203/439] Make while loops evaluate to the last executed command status A while loop now evaluates to the last executed command in the body, or zero if the loop body is empty. This matches POSIX semantics. Add a bunch of tricky tests. See #4982 --- CHANGELOG.md | 1 + src/parse_execution.cpp | 33 +++++++++++++------ tests/while.err | 3 ++ tests/while.in | 73 +++++++++++++++++++++++++++++++++++++++++ tests/while.out | 12 +++++++ 5 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 tests/while.err create mode 100644 tests/while.in create mode 100644 tests/while.out diff --git a/CHANGELOG.md b/CHANGELOG.md index d7b6f1aa2..1b27bd483 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Fixes and improvements - exec now behaves properly inside functions (#5449) +- while loops now evaluate to the last executed command in the loop body (or zero if the body was empty), matching POSIX semantics. # fish 3.0.0 (released December 28, 2018) diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 68a63c7c1..5d16f66ad 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -523,13 +523,28 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( const block_t *associated_block) { parse_execution_result_t ret = parse_execution_success; + // "The exit status of the while loop shall be the exit status of the last compound-list-2 + // executed, or zero if none was executed." + // Here are more detailed requirements: + // - If we execute the loop body zero times, or the loop body is empty, the status is success. + // - An empty loop body is treated as true, both in the loop condition and after loop exit. + // - The exit status of the last command is visible in the loop condition. (i.e. do not set the + // exit status to true BEFORE executing the loop condition). + // We achieve this by restoring the status if the loop condition fails, plus a special + // affordance for the first condition. + bool first_cond_check = true; + // The conditions of the while loop. tnode_t condition_head = header.child<1>(); tnode_t condition_boolean_tail = header.child<3>(); // Run while the condition is true. - bool loop_executed = false; for (;;) { + // Save off the exit status if it came from the loop body. We'll restore it if the condition + // is false. + int cond_saved_status = first_cond_check ? EXIT_SUCCESS : proc_get_last_status(); + first_cond_check = false; + // Check the condition. parse_execution_result_t cond_ret = this->run_job_conjunction(condition_head, associated_block); @@ -537,8 +552,13 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( cond_ret = run_job_list(condition_boolean_tail, associated_block); } - // We only continue on successful execution and EXIT_SUCCESS. - if (cond_ret != parse_execution_success || proc_get_last_status() != EXIT_SUCCESS) { + // If the loop condition failed to execute, then exit the loop without modifying the exit + // status. If the loop condition executed with a failure status, restore the status and then + // exit the loop. + if (cond_ret != parse_execution_success) { + break; + } else if (proc_get_last_status() != EXIT_SUCCESS) { + proc_set_last_status(cond_saved_status); break; } @@ -548,8 +568,6 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( break; } - loop_executed = true; - // Push a while block and then check its cancellation reason. while_block_t *wb = parser->push_block(); this->run_job_list(contents, wb); @@ -572,11 +590,6 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( break; } } - - if (loop_executed) { - proc_set_last_status(STATUS_CMD_OK); - } - return ret; } diff --git a/tests/while.err b/tests/while.err new file mode 100644 index 000000000..2549f3894 --- /dev/null +++ b/tests/while.err @@ -0,0 +1,3 @@ + +#################### +# Loops exit status handling diff --git a/tests/while.in b/tests/while.in new file mode 100644 index 000000000..d183a54f6 --- /dev/null +++ b/tests/while.in @@ -0,0 +1,73 @@ +# vim: set ft=fish: + +function never_runs + while false + end +end + +function early_return + while true + return 2 + end +end + +function runs_once + set -l i 1 + while test $i -ne 0 && set i (math $i - 1) + end +end + +# this should return 1 +never_runs; echo "Empty Loop in Function: $status" + +# this should return 0 +runs_once; echo "Runs Once: $status" + +# this should return 2 +early_return; echo "Early Return: $status" + +logmsg Loops exit status handling + +function set_status ; return $argv[1]; end + +# The previous status is visible in the loop condition. +# This includes both the incoming status, and the last command in the +# loop body. +set_status 36 +while begin + set -l saved $status + echo "Condition Status: $status" + set_status $saved + end + true +end + +# The condition status IS visible in the loop body. +set_status 55 +while true + echo "Body Status: $status" + break +end + +# The status of the last command is visible in the loop condition +set_status 13 +while begin + set -l saved $status + echo "Condition 2 Status: $saved" + test $saved -ne 5 + end + set_status 5 +end + +# The status of the last command is visible outside the loop +set rem 5 7 11 +while [ (count $rem) -gt 0 ] + set_status $rem[1] + set rem $rem[2..-1] +end +echo "Loop Exit Status: $status" + +# Empty loops succeed. +false +while false; end +echo "Empty Loop Status: $status" diff --git a/tests/while.out b/tests/while.out new file mode 100644 index 000000000..f23708aa4 --- /dev/null +++ b/tests/while.out @@ -0,0 +1,12 @@ +Empty Loop in Function: 0 +Runs Once: 0 +Early Return: 2 + +#################### +# Loops exit status handling +Condition Status: 36 +Body Status: 0 +Condition 2 Status: 13 +Condition 2 Status: 5 +Loop Exit Status: 11 +Empty Loop Status: 0 From 3cc581fbb024cf40a26fc1ec82d3b6c32a53f7b0 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 17:36:31 -0800 Subject: [PATCH 204/439] Unconditionally set the tty mode in reader_readline There was a bogus check for is_interactive_session. But if we are in reader_readline we are necessarily interactive (even if we are not in an interactive session, i.e. a fish script invoked some interactive functionality). Remove this check. Fixes #5519 --- src/reader.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index b70576f3d..78e930f63 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -2491,11 +2491,9 @@ const wchar_t *reader_readline(int nchars) { // Get the current terminal modes. These will be restored when the function returns. if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output(); // Set the new modes. - if (is_interactive_session) { - if (tcsetattr(0, TCSANOW, &shell_modes) == -1) { - if (errno == EIO) redirect_tty_output(); - wperror(L"tcsetattr"); - } + if (tcsetattr(0, TCSANOW, &shell_modes) == -1) { + if (errno == EIO) redirect_tty_output(); + wperror(L"tcsetattr"); } while (!finished && !data->end_loop) { From 364c83927968c7fd8936bfb3d9f895d60886c071 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 17:36:31 -0800 Subject: [PATCH 205/439] Unconditionally set the tty mode in reader_readline There was a bogus check for is_interactive_session. But if we are in reader_readline we are necessarily interactive (even if we are not in an interactive session, i.e. a fish script invoked some interactive functionality). Remove this check. Fixes #5519 --- src/reader.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index 70b6c4a3d..727f79f9b 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -2474,11 +2474,9 @@ const wchar_t *reader_readline(int nchars) { // Get the current terminal modes. These will be restored when the function returns. if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output(); // Set the new modes. - if (is_interactive_session) { - if (tcsetattr(0, TCSANOW, &shell_modes) == -1) { - if (errno == EIO) redirect_tty_output(); - wperror(L"tcsetattr"); - } + if (tcsetattr(0, TCSANOW, &shell_modes) == -1) { + if (errno == EIO) redirect_tty_output(); + wperror(L"tcsetattr"); } while (!finished && !data->end_loop) { From 028bff7b4444a0eb8ee7c1607b6a87bacdd0e343 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 17:46:19 -0800 Subject: [PATCH 206/439] Relnote fix for #5519 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b27bd483..0baf3ef86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - exec now behaves properly inside functions (#5449) - while loops now evaluate to the last executed command in the loop body (or zero if the body was empty), matching POSIX semantics. +- `read --silent` no longer echoes to the tty when run from a non-interactive script (#5519) # fish 3.0.0 (released December 28, 2018) From 059804612a469e6ea6409be38e36c32f7a173187 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 4 Jan 2019 08:45:53 +0100 Subject: [PATCH 207/439] string: Fix crash with _GLIBCXX_ASSERTIONS This asserted because we accessed wcstring::front() when it was empty. Instead, check explicitly for it being empty before. Fixes #5479 --- src/builtin_string.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 6e810a32e..bbc7e40c0 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -622,9 +622,13 @@ class wildcard_matcher_t : public string_matcher_t { } } if (opts.entire) { - // If the pattern is empty, this becomes one ANY_STRING that matches everything. - if (wcpattern.front() != ANY_STRING) wcpattern.insert(0, 1, ANY_STRING); - if (wcpattern.back() != ANY_STRING) wcpattern.push_back(ANY_STRING); + if (!wcpattern.empty()) { + if (wcpattern.front() != ANY_STRING) wcpattern.insert(0, 1, ANY_STRING); + if (wcpattern.back() != ANY_STRING) wcpattern.push_back(ANY_STRING); + } else { + // If the pattern is empty, this becomes one ANY_STRING that matches everything. + wcpattern.push_back(ANY_STRING); + } } } From 40f5dd200be95f2932e2d0482ae89e48e1eb347a Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 4 Jan 2019 14:48:01 +0100 Subject: [PATCH 208/439] share/config: Don't split /etc/paths entries on spaces This used `read -la`, which _splits_. Instead, don't do that, each line is its own entry. Fixes #5481. [ci skip] --- share/config.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/config.fish b/share/config.fish index 1f393c5f5..160524b93 100644 --- a/share/config.fish +++ b/share/config.fish @@ -208,7 +208,7 @@ if command -sq /usr/libexec/path_helper for path_file in $argv[2] $argv[3]/* if test -f $path_file - while read -la entry + while read -l entry if not contains $entry $result set result $result $entry end From 5f7adb3c69ec5f31f573eb35575a56799d4c6c70 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 20 Jan 2019 18:05:07 -0800 Subject: [PATCH 209/439] Relnote fix for #5481 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0baf3ef86..5c1e9f45a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - exec now behaves properly inside functions (#5449) - while loops now evaluate to the last executed command in the loop body (or zero if the body was empty), matching POSIX semantics. - `read --silent` no longer echoes to the tty when run from a non-interactive script (#5519) +- (macOS only) /etc/paths and /etc/paths.d now correctly set path entries with spaces. Also affects MANPATH. (#5481) # fish 3.0.0 (released December 28, 2018) From b6aafda139608596720304dcedb26768105e749d Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 21 Jan 2019 17:25:24 +1100 Subject: [PATCH 210/439] CHANGELOG: 3.0.1 verbiage --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c1e9f45a..0782b2051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ +<<<<<<< HEAD # fish 3.0.1 +This release of fish fixes a number of major issues discovered in fish 3.0.0. + ### Fixes and improvements - exec now behaves properly inside functions (#5449) @@ -7,6 +10,9 @@ - `read --silent` no longer echoes to the tty when run from a non-interactive script (#5519) - (macOS only) /etc/paths and /etc/paths.d now correctly set path entries with spaces. Also affects MANPATH. (#5481) +If you are upgrading from version 2.7.1 or before, please also review the release notes for 3.0.0 and 3.0b1 (included below). + +--- # fish 3.0.0 (released December 28, 2018) From 8ff81247659cf9b9a0ea5bc6bee183e22d22c4de Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 31 Dec 2018 10:07:52 +0100 Subject: [PATCH 211/439] cmake: Add missing HAVE_WCSTOD_L #cmakedefine Turns out we've been using the fallback everywhere. See #5453. (cherry picked from commit 7078aa46428aa083053bcefb9e67d41293d6d13a) --- config_cmake.h.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config_cmake.h.in b/config_cmake.h.in index d234acba9..47cfcc5e2 100644 --- a/config_cmake.h.in +++ b/config_cmake.h.in @@ -115,6 +115,9 @@ /* Define to 1 if you have the `wcsndup' function. */ #cmakedefine HAVE_WCSNDUP 1 +/* Define to 1 if you have the `wcstod_l' function. */ +#cmakedefine HAVE_WCSTOD_L 1 + /* Define to 1 if the winsize struct and TIOCGWINSZ macro exist */ #cmakedefine HAVE_WINSIZE 1 From 3855608c690cec92555d5ec8a95703422403137f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 3 Jan 2019 11:47:32 +0100 Subject: [PATCH 212/439] docs: Document $hostname Fixes #5469. [ci skip] (cherry picked from commit 72c0213d42b77b2f01223b5515669840110ea9b9) --- 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 a0e326e89..7f2fe7efc 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -931,6 +931,8 @@ The user can change the settings of `fish` by changing the values of certain var - `HOME`, the user's home directory. This variable can be changed by the user. +- `hostname`, the machine's hostname. + - `IFS`, the internal field separator that is used for word splitting with the read builtin. Setting this to the empty string will also disable line splitting in command substitution. This variable can be changed by the user. - `PWD`, the current working directory. From 7a163e8e985aaaf11da9e88b3ce8096895484a01 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 13 Jan 2019 16:36:09 +0100 Subject: [PATCH 213/439] completions/git: Stop limiting to the token This enables fuzzy-matching outside of the current directory again. As it turns out, the performance impact here isn't as large as I thought - it's massively dependent on caching. Fixes #5476. (cherry picked from commit 73bae383e0fee4dc93d247546df3b70504adbb2b) --- share/completions/git.fish | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 60822fe49..7c4cb1a5c 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -144,19 +144,13 @@ function __fish_git_files # (don't use --ignored=no because that was only added in git 2.16, from Jan 2018. set -q ignored; and set -a status_opt --ignored - # Glob just the current token for performance - # and so git shows untracked files (even in untracked dirs) for that. - # If the current token is empty, this matches everything in $PWD. - set -l files (commandline -ct) - # The trailing "**" is necessary to match files inside the given directories. - set files "$files*" "$files*/**" set -q untracked; and set -a status_opt -unormal or set -a status_opt -uno # We need to set status.relativePaths to true because the porcelain v2 format still honors that, # and core.quotePath to false so characters > 0x80 (i.e. non-ASCII) aren't considered special. # We explicitly enable globs so we can use that to match the current token. - set -l git_opt -c status.relativePaths -c core.quotePath= --glob-pathspecs + set -l git_opt -c status.relativePaths -c core.quotePath= # We pick the v2 format if we can, because it shows relative filenames (if used without "-z"). # We fall back on the v1 format by reading git's _version_, because trying v2 first is too slow. @@ -278,7 +272,7 @@ function __fish_git_files set -l previousfile # Note that we can't use space as a delimiter between status and filename, because # the status can contain spaces - " M" is different from "M ". - command git $git_opt status --porcelain -z $status_opt -- $files \ + command git $git_opt status --porcelain -z $status_opt \ | while read -lz line set -l desc # The entire line is the "from" from a rename. From 749347ff4c45fd217054714f53c6df064bb87a1d Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 8 Jan 2019 14:50:08 +0800 Subject: [PATCH 214/439] debian packaging: recommend python3 or python2 Closes #5492. (cherry picked from commit 1f897d2c4318a78976bcfb81d668f65ef2d909f0) --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 56c1a692f..5368d3c6a 100644 --- a/debian/control +++ b/debian/control @@ -22,7 +22,7 @@ Description: friendly interactive shell Package: fish-common Architecture: all Depends: ${misc:Depends} -Recommends: fish, python (>=2.6) +Recommends: fish, python3 (>= 3.3) | python (>=2.7) Suggests: xdg-utils Replaces: fish (<= 2.1.1.dfsg-2) Description: friendly interactive shell (architecture-independent files) From cb09f9aef217aadc6cfe81517ca72dc1b0b07e65 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 2 Jan 2019 14:16:39 +0100 Subject: [PATCH 215/439] Switch to readdir from readdir_r It's deprecated in glibc, and does not work properly on Solaris. Fixes #5458. --- src/wutil.cpp | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/wutil.cpp b/src/wutil.cpp index f58eb529a..2dc25019f 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -37,27 +37,27 @@ const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, static owning_lock> wgettext_map; bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name, bool *out_is_dir) { - struct dirent d; - struct dirent *result = NULL; - int retval = readdir_r(dir, &d, &result); - if (retval || !result) { + struct dirent *result = readdir(dir); + if (!result) { out_name = L""; return false; } - out_name = str2wcstring(d.d_name); - if (!out_is_dir) return true; + out_name = str2wcstring(result->d_name); + if (!out_is_dir) { + return true; + } // 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. bool check_with_stat = true; #ifdef HAVE_STRUCT_DIRENT_D_TYPE - if (d.d_type == DT_DIR) { + if (result->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) { + } else if (result->d_type == DT_LNK || result->d_type == DT_UNKNOWN) { // We want to treat symlinks to directories as directories. Use stat to resolve it. check_with_stat = true; } else { @@ -70,7 +70,7 @@ bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name, // 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); + fullpath.append(result->d_name); struct stat buf; if (stat(fullpath.c_str(), &buf) != 0) { is_dir = false; @@ -83,34 +83,24 @@ bool wreaddir_resolving(DIR *dir, const wcstring &dir_path, wcstring &out_name, } bool wreaddir(DIR *dir, wcstring &out_name) { - // We need to use a union to ensure that the dirent struct is large enough to avoid stomping on - // the stack. Some platforms incorrectly defined the `d_name[]` member as being one element - // long when it should be at least NAME_MAX + 1. - union { - struct dirent d; - char c[offsetof(struct dirent, d_name) + NAME_MAX + 1]; - } d_u; - struct dirent *result = NULL; - - int retval = readdir_r(dir, &d_u.d, &result); - if (retval || !result) { + struct dirent *result = readdir(dir); + if (!result) { out_name = L""; return false; } - out_name = str2wcstring(d_u.d.d_name); + out_name = str2wcstring(result->d_name); return true; } bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) { - struct dirent d; struct dirent *result = NULL; while (!result) { - int retval = readdir_r(dir, &d, &result); - if (retval || !result) break; + result = readdir(dir); + if (!result) break; #if HAVE_STRUCT_DIRENT_D_TYPE - switch (d.d_type) { + switch (result->d_type) { case DT_DIR: case DT_LNK: case DT_UNKNOWN: { @@ -129,7 +119,8 @@ bool wreaddir_for_dirs(DIR *dir, wcstring *out_name) { if (result && out_name) { *out_name = str2wcstring(result->d_name); } - return result != NULL; + if (!result) return false; + return true; } const wcstring wgetcwd() { From 5dc0ff0a90de856a57c693876d9dd449eaaa68e8 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 21 Jan 2019 03:15:43 -0800 Subject: [PATCH 216/439] ls.fish: remove for loop `command -s` can take multiple arguments to try. --- share/functions/ls.fish | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/share/functions/ls.fish b/share/functions/ls.fish index 9023b74e4..d162c8c44 100644 --- a/share/functions/ls.fish +++ b/share/functions/ls.fish @@ -12,13 +12,7 @@ if command ls --version >/dev/null 2>/dev/null end if not set -q LS_COLORS - set -l dircolors - for d in gdircolors dircolors - if command -sq $d - set dircolors $d - break - end - end + set -l dircolors (command -s {g,}dircolors)[1] if set -q dircolors[1] set -l colorfile From 488e208cca9da3e6ecb07a6bfe13646805db59ed Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 21 Jan 2019 06:18:17 -0800 Subject: [PATCH 217/439] ls.fish: also show indicators on non-GNU ls, refactor GNU ls's --indicator-style=classify is the same as POSIX -F. Refactor and change command testing logic so that we define the function in the same place for all platforms, and use -F on all the platforms when stdout is a TTY. --- share/functions/ls.fish | 46 ++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/share/functions/ls.fish b/share/functions/ls.fish index d162c8c44..1cd4e659f 100644 --- a/share/functions/ls.fish +++ b/share/functions/ls.fish @@ -1,20 +1,22 @@ # -# Make ls use colors if we are on a system that supports that feature and writing to stdout. +# Make ls use colors and show indicators if we are on a system that supports that feature and writing to stdout. # -if command ls --version >/dev/null 2>/dev/null - # This appears to be GNU ls. - function ls --description "List contents of directory" - set -l param --color=auto - if isatty 1 - set -a param --indicator-style=classify + +# BSD, macOS and others support colors with ls -G. +# GNU ls and FreeBSD ls takes --color=auto. Order of this test is important because ls also takes -G but it has a different meaning. +# Solaris 11's ls command takes a --color flag. +# Also test a no-op -- because we'll want to define this function even with an ls that can't do colors (like NetBSD). + +for opt in --color=auto -G --color -- + if command ls $opt / >/dev/null 2>/dev/null + + function ls --description "List contents of directory" -V opt + isatty stdout + and set -a opt -F + command ls $opt $argv end - command ls $param $argv - end - if not set -q LS_COLORS - set -l dircolors (command -s {g,}dircolors)[1] - - if set -q dircolors[1] + if [ $opt = --color=auto ] &&! set -qx LS_COLORS && set -l cmd (command -s {g,}dircolors)[1] set -l colorfile for file in ~/.dir_colors ~/.dircolors /etc/DIR_COLORS if test -f $file @@ -22,23 +24,15 @@ if command ls --version >/dev/null 2>/dev/null break end end - # Here we rely on the legacy behavior of `dircolors -c` producing output suitable for - # csh in order to extract just the data we're interested in. - set -gx LS_COLORS ($dircolors -c $colorfile | string split ' ')[3] + # Here we rely on the legacy behavior of `dircolors -c` producing output + # suitable for csh in order to extract just the data we're interested in. + set -gx LS_COLORS ($cmd -c $colorfile | string split ' ')[3] # The value should always be quoted but be conservative and check first. if string match -qr '^([\'"]).*\1$' -- $LS_COLORS set LS_COLORS (string match -r '^.(.*).$' $LS_COLORS)[2] end end - end -else if command ls -G / >/dev/null 2>/dev/null - # It looks like BSD, OS X and a few more which support colors through the -G switch instead. - function ls --description "List contents of directory" - command ls -G $argv - end -else if command ls --color / >/dev/null 2>/dev/null - # Solaris 11's ls command takes a --color flag - function ls --description "List contents of directory" - command ls --color $argv + + break end end From a5e5f90f739956938d2b2165c99b6eb963310bef Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 21 Jan 2019 07:52:26 -0800 Subject: [PATCH 218/439] ls.fish: fix colorless ls not taking options That -- no-op would have the effect that a user can not pass more options to ls, they would be interpreted as file names. --- share/functions/ls.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/functions/ls.fish b/share/functions/ls.fish index 1cd4e659f..aab2f0e49 100644 --- a/share/functions/ls.fish +++ b/share/functions/ls.fish @@ -5,9 +5,9 @@ # BSD, macOS and others support colors with ls -G. # GNU ls and FreeBSD ls takes --color=auto. Order of this test is important because ls also takes -G but it has a different meaning. # Solaris 11's ls command takes a --color flag. -# Also test a no-op -- because we'll want to define this function even with an ls that can't do colors (like NetBSD). +# Also test -F because we'll want to define this function even with an ls that can't do colors (like NetBSD). -for opt in --color=auto -G --color -- +for opt in --color=auto -G --color -F if command ls $opt / >/dev/null 2>/dev/null function ls --description "List contents of directory" -V opt From 8743961301f82a200ee1c15fba9d611544952488 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 21 Jan 2019 13:59:36 -0800 Subject: [PATCH 219/439] Fix fish_config rendering brights as normal on prompt previews I noticed our default brgreen for fish_color_user was rendering as just unstyled white. --- share/tools/web_config/fishconfig.css | 1 + share/tools/web_config/webconfig.py | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/share/tools/web_config/fishconfig.css b/share/tools/web_config/fishconfig.css index ca74fa570..34eafd050 100644 --- a/share/tools/web_config/fishconfig.css +++ b/share/tools/web_config/fishconfig.css @@ -474,6 +474,7 @@ img.delete_icon { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + color: #c0c0c0; /* set_color normal, assume white (not brwhite) */ } .prompt_demo { diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 5171d22f5..c6aa3a5ba 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -19,6 +19,7 @@ import socket import string import subprocess import sys +from itertools import chain FISH_BIN_PATH = False # will be set later IS_PY2 = sys.version_info[0] == 2 @@ -252,7 +253,6 @@ def get_special_ansi_escapes(): def append_html_for_ansi_escape(full_val, result, span_open): - # Strip off the initial \x1b[ and terminating m val = full_val[2:-1] @@ -269,10 +269,10 @@ def append_html_for_ansi_escape(full_val, result, span_open): result.append('') return True # span now open - # term8 foreground color - if val in [str(x) for x in range(30, 38)]: + # term16 foreground color + if val in (str(x) for x in chain(range(90, 97), range(30, 38))): close_span() - html_color = html_color_for_ansi_color_index(int(val) - 30) + html_color = html_color_for_ansi_color_index(int(val) - (30 if int(val) < 90 else 82)) result.append('') return True # span now open @@ -282,7 +282,7 @@ def append_html_for_ansi_escape(full_val, result, span_open): close_span() return False - # We don't handle bold or underline yet + # TODO We don't handle bold, underline, italics, dim, or reverse yet # Do nothing on failure return span_open From 3115446a07c843efb87f6ebc5ddc5230d3b41103 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 21 Jan 2019 17:08:49 -0800 Subject: [PATCH 220/439] string completions: add missing upper, lower, split0, join0, unescape and --style=regex --- share/completions/string.fish | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/share/completions/string.fish b/share/completions/string.fish index ea2bae47b..37d72f5b6 100644 --- a/share/completions/string.fish +++ b/share/completions/string.fish @@ -2,23 +2,29 @@ # This follows a strict command-then-options approach, so we can just test the number of tokens complete -f -c string complete -f -c string -n "test (count (commandline -opc)) -ge 2; and not contains -- (commandline -opc)[2] escape" -s q -l quiet -d "Do not print output" +complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "lower" +complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "upper" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "length" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "sub" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] sub" -s s -l start -a "(seq 1 10)" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] sub" -s l -l length -a "(seq 1 10)" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] sub" -s s -l start -a "(seq 1 10)" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] sub" -s l -l length -a "(seq 1 10)" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "split" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] split" -s m -l max -a "(seq 1 10)" -d "Specify maximum number of splits" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] split" -s r -l right -d "Split right-to-left" +complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "split0" +complete -x -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s m -l max -a "(seq 1 10)" -d "Specify maximum number of splits" +complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s r -l right -d "Split right-to-left" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "join" +complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "join0" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "trim" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s l -l left -d "Trim only leading characters" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s r -l right -d "Trim only trailing characters" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s c -l chars -d "Specify the chars to trim (default: whitespace)" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "escape" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape" -s n -l no-quoted -d "Escape with \\ instead of quoting" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape" -l style -d "Pick escaping style" -a " +complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "unescape" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -s n -l no-quoted -d "Escaped with \\ instead of quoted" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -l style -d "Escaping style" -a " (printf '%s\t%s\n' script 'For use in scripts' \ var 'For use as a variable name' \ + regex 'For string match -r, string replace -r' \ url 'For use as a URL')" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "match" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s n -l index -d "Report index and length of the matches" @@ -29,6 +35,6 @@ complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s i -l ignore-case -d "Case insensitive" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s r -l regex -d "Use regex instead of globs" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "repeat" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s n -l count -a "(seq 1 10)" -d "Repetition count" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s m -l max -a "(seq 1 10)" -d "Maximum number of printed char" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s n -l count -a "(seq 1 10)" -d "Repetition count" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s m -l max -a "(seq 1 10)" -d "Maximum number of printed char" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s N -l no-newline -d "Remove newline" From f2a1130afd9aa9132c9297d850ee6a74404a669e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 18 Jan 2019 19:24:49 +0100 Subject: [PATCH 221/439] Also set the read-only flag for non-electric vars For some reason, we have two places where a variable can be read-only: - By key in env.cpp:is_read_only(), which is checked via set* - By flag on the actual env_var_t, which is checked e.g. in parse_execution The latter didn't happen for non-electric variables like hostname, because they used the default constructor, because they were constructed via operator[] (or some such C++-iness). This caused for-loops to crash on an assert if they used a non-electric read-only var like $hostname or $SHLVL. Instead, we explicitly set the flag. We might want to remove one of the two read-only checks, or something? Fixes #5548. --- src/env.cpp | 1 + src/env.h | 8 ++++++++ tests/test1.err | 6 ++++++ tests/test1.in | 5 +++++ tests/test1.out | 3 +++ 5 files changed, 23 insertions(+) diff --git a/src/env.cpp b/src/env.cpp index dbb85ed66..2d25cb5de 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1184,6 +1184,7 @@ static int env_set_internal(const wcstring &key, env_mode_flags_t input_var_mode var.set_vals(std::move(val)); var.set_pathvar(var_mode & ENV_PATHVAR); + var.set_read_only(is_read_only(key)); if (var_mode & ENV_EXPORT) { // The new variable is exported. diff --git a/src/env.h b/src/env.h index 30fb4ab0d..f055eb623 100644 --- a/src/env.h +++ b/src/env.h @@ -122,6 +122,14 @@ class env_var_t { } } + void set_read_only(bool read_only) { + if (read_only) { + flags |= flag_read_only; + } else { + flags &= ~flag_read_only; + } + } + static env_var_flags_t flags_for(const wchar_t *name); env_var_t &operator=(const env_var_t &var) = default; diff --git a/tests/test1.err b/tests/test1.err index 5e9cda705..e5a978376 100644 --- a/tests/test1.err +++ b/tests/test1.err @@ -38,6 +38,12 @@ fish: You cannot use read-only variable 'status' in a for loop for status in a b c ^ +#################### +# That goes for non-electric ones as well (#5548) +fish: You cannot use read-only variable 'hostname' in a for loop +for hostname in a b c + ^ + #################### # For loop control vars available outside the for block diff --git a/tests/test1.in b/tests/test1.in index 104f984ea..7f0f1dcaa 100644 --- a/tests/test1.in +++ b/tests/test1.in @@ -163,6 +163,11 @@ for status in a b c echo $status end +logmsg "That goes for non-electric ones as well (#5548)" +for hostname in a b c + echo $hostname +end + logmsg For loop control vars available outside the for block begin set -l loop_var initial-value diff --git a/tests/test1.out b/tests/test1.out index 56fd4f64d..b3b075fab 100644 --- a/tests/test1.out +++ b/tests/test1.out @@ -96,6 +96,9 @@ Checking for infinite loops in no-execute #################### # For loops with read-only vars is an error (#4342) +#################### +# That goes for non-electric ones as well (#5548) + #################### # For loop control vars available outside the for block $loop_var: set in local scope, unexported, with 1 elements From afb9094b4ce05250f6908cba46882cb2b25bf1a6 Mon Sep 17 00:00:00 2001 From: Sam Yu Date: Sat, 12 Jan 2019 14:05:23 +0800 Subject: [PATCH 222/439] Fix completion of directories for configure --- share/completions/configure.fish | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/share/completions/configure.fish b/share/completions/configure.fish index 67eb5d519..6c7880ef2 100644 --- a/share/completions/configure.fish +++ b/share/completions/configure.fish @@ -4,9 +4,9 @@ complete -c configure -s q -l quiet -d "Quiet mode" complete -c configure -l cache-file -f -d "Cache test results in specified file" complete -c configure -s C -l config-cache -d "Cache test results in file config.cache" complete -c configure -s n -l no-create -d "Do not create output files" -complete -c configure -l srcdir -d "Set source directory" -a "__fish_complete_directories (commandline -ct)" -x -complete -c configure -l prefix -d "Architecture-independent install directory" -a "__fish_complete_directories (commandline -ct)" -x -complete -c configure -l exec-prefix -d "Architecture-dependent install directory" -a "__fish_complete_directories (commandline -ct)" -x +complete -c configure -l srcdir -d "Set source directory" -a "(__fish_complete_directories)" -x +complete -c configure -l prefix -d "Architecture-independent install directory" -a "(__fish_complete_directories)" -x +complete -c configure -l exec-prefix -d "Architecture-dependent install directory" -a "(__fish_complete_directories)" -x complete -c configure -l build -d "Configure for building on BUILD" -x complete -c configure -l host -d "Cross-compile to build programs to run on HOST" -x complete -c configure -l target -d "Configure for building compilers for TARGET" -x From 97f0cc966211aa2daad6e5e2d6a616feb230cb4e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 17 Jan 2019 09:44:35 +0100 Subject: [PATCH 223/439] Don't wrap functions with themselves Our weird %-expanding function wrappers around kill et all defined "--wraps" for the same name. As it turns out, fish follows that one, and executes the completion multiple times. I didn't notice because these tend to be rather quick on linux, but on macOS that's apparently a real issue. Fixes #5541. [ci skip] --- share/config.fish | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/share/config.fish b/share/config.fish index 160524b93..a3abb82f3 100644 --- a/share/config.fish +++ b/share/config.fish @@ -271,23 +271,23 @@ function __fish_expand_pid_args end end -function bg --wraps bg +function bg builtin bg (__fish_expand_pid_args $argv) end -function fg --wraps fg +function fg builtin fg (__fish_expand_pid_args $argv) end -function kill --wraps kill +function kill command kill (__fish_expand_pid_args $argv) end -function wait --wraps wait +function wait builtin wait (__fish_expand_pid_args $argv) end -function disown --wraps disown +function disown builtin disown (__fish_expand_pid_args $argv) end From d5d80c0742a5022f543cffbd1e01d6cdf3d1507f Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 13 Jan 2019 16:14:04 -0600 Subject: [PATCH 224/439] Fix extra space in `fish_title` Closes #5517. Credit goes to @jadenPete. [skip-ci] --- share/functions/fish_title.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/fish_title.fish b/share/functions/fish_title.fish index 5eacb73e9..87abe894b 100644 --- a/share/functions/fish_title.fish +++ b/share/functions/fish_title.fish @@ -1,3 +1,3 @@ function fish_title - echo (status current-command) " " (__fish_pwd) + echo (status current-command) (__fish_pwd) end From ec77135cf28f3ab6e62fb98459c5f9232eb4956a Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 10 Jan 2019 22:20:17 -0600 Subject: [PATCH 225/439] Allow more flexibility with file completions for `yarn` Closes #5502 --- share/completions/yarn.fish | 63 +++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/share/completions/yarn.fish b/share/completions/yarn.fish index ffa11137e..e2650b7d6 100644 --- a/share/completions/yarn.fish +++ b/share/completions/yarn.fish @@ -23,7 +23,8 @@ function __yarn_filtered_list_packages return end - all-the-package-names | string match -er -- "(?:\\b|_)"(commandline -ct | string escape --style=regex) + all-the-package-names | string match -er -- "(?:\\b|_)"(commandline -ct | + string escape --style=regex) | head -n1000 end function __yarn_find_package_json @@ -153,7 +154,7 @@ function __fish_yarn_run end end -complete -f -c yarn -n '__fish_seen_subcommand_from run' -a "(__fish_yarn_run)" +complete -c yarn -n '__fish_seen_subcommand_from run' -a "(__fish_yarn_run)" complete -f -c yarn -n '__fish_use_subcommand' -a tag complete -f -c yarn -n '__fish_seen_subcommand_from tag' -a 'add rm ls' @@ -177,36 +178,36 @@ complete -f -c yarn -n '__fish_use_subcommand' -a why set -g yarn_cmds access add bin cache check clean config generate-lock-entry global info init install licenses link list login logout outdated owner pack publish remove run tag team unlink upgrade upgrade-interactive version versions why # Common short, long options -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l help -s h -d 'output usage information' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l version -s V -d 'output the version number' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l help -s h -d 'output usage information' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l version -s V -d 'output the version number' # The rest of common options are all of them long -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l verbose -d 'output verbose messages on internal operations' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l offline -d 'trigger an error if any required dependencies are not available in local cache' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l prefer-offline -d 'use network only if dependencies are not available in local cache' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l strict-semver -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l json -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-scripts -d 'don\'t run lifecycle scripts' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l har -d 'save HAR output of network traffic' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-platform -d 'ignore platform checks' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-engines -d 'ignore engines check' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-optional -d 'ignore optional dependencies' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l force -d 'ignore all caches' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-bin-links -d 'don\'t generate bin links when setting up packages' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l flat -d 'only allow one version of a package' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l 'prod production' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-lockfile -d 'don\'t read or generate a lockfile' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l pure-lockfile -d 'don\'t generate a lockfile' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l frozen-lockfile -d 'don\'t generate a lockfile and fail if an update is needed' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l global-folder -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l modules-folder -d 'rather than installing modules into the node_modules folder relative to the cwd, output them here' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l cache-folder -d 'specify a custom folder to store the yarn cache' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l verbose -d 'output verbose messages on internal operations' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l offline -d 'trigger an error if any required dependencies are not available in local cache' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l prefer-offline -d 'use network only if dependencies are not available in local cache' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l strict-semver +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l json +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-scripts -d 'don\'t run lifecycle scripts' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l har -d 'save HAR output of network traffic' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-platform -d 'ignore platform checks' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-engines -d 'ignore engines check' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l ignore-optional -d 'ignore optional dependencies' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l force -d 'ignore all caches' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-bin-links -d 'don\'t generate bin links when setting up packages' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l flat -d 'only allow one version of a package' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l 'prod production' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-lockfile -d 'don\'t read or generate a lockfile' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l pure-lockfile -d 'don\'t generate a lockfile' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l frozen-lockfile -d 'don\'t generate a lockfile and fail if an update is needed' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l global-folder +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l modules-folder -d 'rather than installing modules into the node_modules folder relative to the cwd, output them here' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l cache-folder -d 'specify a custom folder to store the yarn cache' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l mutex -d 'use a mutex to ensure only one yarn instance is executing' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l mutex -a 'file network' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l mutex -d 'use a mutex to ensure only one yarn instance is executing' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l mutex -a 'file network' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-emoji -d 'disable emoji in output' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l proxy -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l https-proxy -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-progress -d 'disable progress bar' -complete -f -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l network-concurrency -d 'maximum number of concurrent network requests' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-emoji -d 'disable emoji in output' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l proxy +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l https-proxy +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l no-progress -d 'disable progress bar' +complete -c yarn -n '__fish_seen_subcommand_from $yarn_cmds' -l network-concurrency -d 'maximum number of concurrent network requests' From 462cb6044c5c2f78cf7e23c52af495c6f5e33d91 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 21 Jan 2019 20:06:16 -0600 Subject: [PATCH 226/439] Use standard __CYGWIN__ define for Cygwin detection --- cmake/ConfigureChecks.cmake | 5 ----- config_cmake.h.in | 3 --- src/common.h | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index c53be74ae..a13dbdbd5 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -43,11 +43,6 @@ if (CMAKE_HOST_SYSTEM_VERSION MATCHES ".*-Microsoft") SET(WSL 1) endif() -# Detect Cygwin. -if (CMAKE_SYSTEM_NAME MATCHES "CYGWIN_NT.*") - SET(CYGWIN 1) -endif() - # Set up the config.h file. SET(PACKAGE_NAME "fish") SET(PACKAGE_TARNAME "fish") diff --git a/config_cmake.h.in b/config_cmake.h.in index 2c6fde27d..8071546fd 100644 --- a/config_cmake.h.in +++ b/config_cmake.h.in @@ -4,9 +4,6 @@ /* Define to 1 if compiled on WSL */ #cmakedefine WSL 1 -/* Define to 1 if complied under Cygwin */ -#cmakedefine CYGWIN 1 - /* Define to 1 if you have the `clock_gettime' function. */ #cmakedefine HAVE_CLOCK_GETTIME 1 diff --git a/src/common.h b/src/common.h index d52204929..8b45a808c 100644 --- a/src/common.h +++ b/src/common.h @@ -949,7 +949,7 @@ constexpr bool is_windows_subsystem_for_linux() { /// Detect if we are running under Cygwin or Cgywin64 constexpr bool is_cygwin() { -#ifdef CYGWIN +#ifdef __CYGWIN__ return true; #else return false; From 07e03dd79476b883a6e2862f5c81eb943a04f7ac Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 21 Jan 2019 20:08:38 -0600 Subject: [PATCH 227/439] Release notes for #5426 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e351b4279..134c43729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - exec now behaves properly inside functions (#5449) - while loops now evaluate to the last executed command in the loop body (or zero if the body was empty), matching POSIX semantics. +- fish does not hang on launch when running under Cygwin/MSYS2 # fish 3.0.0 (released December 28, 2018) From 96f7924661016a29231c2523f68c794dcb2e5a89 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 7 Jan 2019 17:24:17 +0100 Subject: [PATCH 228/439] Fix nim prompt (via web_config) This had a helper function defined outside of the fish_prompt function, so `funcsave` missed it (see #736). Fixes #5490. [ci skip] --- .../tools/web_config/sample_prompts/nim.fish | 83 ++++++++++--------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/share/tools/web_config/sample_prompts/nim.fish b/share/tools/web_config/sample_prompts/nim.fish index b935f8413..93c574dd5 100644 --- a/share/tools/web_config/sample_prompts/nim.fish +++ b/share/tools/web_config/sample_prompts/nim.fish @@ -1,49 +1,50 @@ # name: Nim # author: Guilhem "Nim" Saurel − https://github.com/nim65s/dotfiles/ -# This prompt shows: -# - green lines if the last return command is OK, red otherwise -# - your user name, in red if root or yellow otherwise -# - your hostname, in cyan if ssh or blue otherwise -# - the current path (with prompt_pwd) -# - date +%X -# - the current virtual environment, if any -# - the current git status, if any, with __fish_git_prompt -# - the current battery state, if any, and if your power cable is unplugged, and if you have "acpi" -# - current background jobs, if any - -# It goes from: -# ┬─[nim@Hattori:~]─[11:39:00] -# ╰─>$ echo here - -# To: -# ┬─[nim@Hattori:~/w/dashboard]─[11:37:14]─[V:django20]─[G:master↑1|●1✚1…1]─[B:85%, 05:41:42 remaining] -# │ 2 15054 0% arrêtée sleep 100000 -# │ 1 15048 0% arrêtée sleep 100000 -# ╰─>$ echo there - -set __fish_git_prompt_showupstream auto - -function _nim_prompt_wrapper - set retc $argv[1] - set field_name $argv[2] - set field_value $argv[3] - - set_color normal - set_color $retc - echo -n '─' - set_color -o green - echo -n '[' - set_color normal - test -n $field_name - and echo -n $field_name: - set_color $retc - echo -n $field_value - set_color -o green - echo -n ']' -end function fish_prompt + # This prompt shows: + # - green lines if the last return command is OK, red otherwise + # - your user name, in red if root or yellow otherwise + # - your hostname, in cyan if ssh or blue otherwise + # - the current path (with prompt_pwd) + # - date +%X + # - the current virtual environment, if any + # - the current git status, if any, with __fish_git_prompt + # - the current battery state, if any, and if your power cable is unplugged, and if you have "acpi" + # - current background jobs, if any + + # It goes from: + # ┬─[nim@Hattori:~]─[11:39:00] + # ╰─>$ echo here + + # To: + # ┬─[nim@Hattori:~/w/dashboard]─[11:37:14]─[V:django20]─[G:master↑1|●1✚1…1]─[B:85%, 05:41:42 remaining] + # │ 2 15054 0% arrêtée sleep 100000 + # │ 1 15048 0% arrêtée sleep 100000 + # ╰─>$ echo there + + set -q __fish_git_prompt_showupstream + or set -g __fish_git_prompt_showupstream auto + + function _nim_prompt_wrapper + set retc $argv[1] + set field_name $argv[2] + set field_value $argv[3] + + set_color normal + set_color $retc + echo -n '─' + set_color -o green + echo -n '[' + set_color normal + test -n $field_name + and echo -n $field_name: + set_color $retc + echo -n $field_value + set_color -o green + echo -n ']' + end and set retc green or set retc red From 171ae992955761bb1169dc3c36883e8e7d4f84db Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 30 Dec 2018 18:54:09 +0100 Subject: [PATCH 229/439] Don't ASSERT_IS_NOT_FORKED_CHILD so much This is hammered sooo much that it actually hurts performance. for i in (seq 100000); test 1 = 1; end is about 40% (!) slower with it. --- src/parser.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parser.cpp b/src/parser.cpp index b16c86e47..4196c3dd8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -108,7 +108,6 @@ parser_t::~parser_t() = default; static parser_t s_principal_parser; parser_t &parser_t::principal_parser() { - ASSERT_IS_NOT_FORKED_CHILD(); ASSERT_IS_MAIN_THREAD(); return s_principal_parser; } From 0edaf42d100053ed35051c0281f039572007d16a Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 21 Jan 2019 20:29:31 -0600 Subject: [PATCH 230/439] Fix regression for #4178 and others introduced by 364c839 ...while still keeping intact the fix for #5519. --- src/reader.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index 78e930f63..b2c241952 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -2492,8 +2492,16 @@ const wchar_t *reader_readline(int nchars) { if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output(); // Set the new modes. if (tcsetattr(0, TCSANOW, &shell_modes) == -1) { - if (errno == EIO) redirect_tty_output(); - wperror(L"tcsetattr"); + int err = errno; + if (err == EIO) { + redirect_tty_output(); + } + // This check is required to work around certain issues with fish's approach to + // terminal control when launching interactive processes while in non-interactive + // mode. See #4178 for one such example. + if (err != ENOTTY || is_interactive_session) { + wperror(L"tcsetattr"); + } } while (!finished && !data->end_loop) { From 367661d4f124d74819ba2ac4bd021161233b807f Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 21 Jan 2019 22:32:40 -0800 Subject: [PATCH 231/439] __fish_complete_suffix: do not show description when not passed one Make it so that the generated completion has the form \t\n when the optional description has been ommitted - otherwise the original option's description gets inherited and is seen hundreds of times repeating in the pager. --- share/functions/__fish_complete_suffix.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/functions/__fish_complete_suffix.fish b/share/functions/__fish_complete_suffix.fish index 7397e252c..ef1059157 100644 --- a/share/functions/__fish_complete_suffix.fish +++ b/share/functions/__fish_complete_suffix.fish @@ -21,12 +21,12 @@ function __fish_complete_suffix -d "Complete using files" case 1 set comp (commandline -ct) set suff $argv - set desc "" + set desc "\n" case 2 set comp $argv[1] set suff $argv[2] - set desc "" + set desc "\n" case 3 set comp $argv[1] From 23e94d834937d3fed82fa2ab8dbb0d0f552de3e3 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 21 Jan 2019 22:35:32 -0800 Subject: [PATCH 232/439] improve GNU patch completions shorter descriptions that can fit in a terminal window, and option arguments added. hide one option that is only functional on Cygwin unless we are on Cygwin --- share/completions/patch.fish | 79 +++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/share/completions/patch.fish b/share/completions/patch.fish index be71824ad..6f329a155 100644 --- a/share/completions/patch.fish +++ b/share/completions/patch.fish @@ -1,41 +1,46 @@ # -# Completions for patch +# Completions for GNU patch +# TODO: POSIX patch # -complete -c patch -s b -l backup -d "Make backup files, when patching a file, rename or copy the original instead of removing it" -complete -c patch -l backup-if-mismatch -d "Back up a file if the patch does not match the file exactly" -complete -c patch -l no-backup-if-mismatch -d "Do not back up a file if the patch does not match the file exactly" -complete -c patch -s B -l prefix -r -f -d "Prefix pref to a file name when generating its simple backup file name" -complete -c patch -l binary -d "Read and write all files in binary mode" -complete -c patch -s c -l context -d "Interpret the patch file as a ordinary context diff" -complete -c patch -s d -l directory -xa '(__fish_complete_directories (commandline -ct))' -d "Change to the directory dir immediately" -complete -c patch -s D -l ifdef -r -f -d "Use the #ifdef…#endif construct to mark changes" -complete -c patch -l dry-run -d "Print the results of applying the patches without actually changing any files" -complete -c patch -s e -l ed -d "Interpret the patch file as an ed script" -complete -c patch -s E -l remove-empty-files -d "Remove output files that are empty after the patches have been applied" -complete -c patch -s f -l force -d "Assume that the user knows exactly what he/she is doing, and do not ask questions" -complete -c patch -s F -l fuzz -r -f -d "Set the maximum fuzz factor" -complete -c patch -s g -l get -r -f -d "This option controls patch's actions when a file is under RCS or SCCS control, and does not exist or is read-only and matches the default version, or when a file is under ClearCase control and does not exist" -complete -c patch -l help -d "Display help and exit" -complete -c patch -s i -l input -r -d "Read the patch from patchfile" -complete -c patch -s l -l ignore-whitespace -d "Match patterns loosely, in case tabs or spaces have been munged in your files" -complete -c patch -s n -l normal -d "Interpret the patch file as a normal diff" -complete -c patch -s N -l forward -d "Ignore patches that seem to be reversed or already applied" -complete -c patch -s o -l output -r -d "Send output to outfile instead of patching files in place" -complete -c patch -s p -l strip -r -f -d "Strip the smallest prefix containing num leading slashes from each file name found in the patch file" -complete -c patch -l posix -d "Conform more strictly to the POSIX standard" -complete -c patch -l quoting-style -r -f -d "Use style word to quote output names" -a "literal shell shell-always c escape" -complete -c patch -s r -l reject-file -r -d "Put rejects into rejectfile instead of the default .rej file" -complete -c patch -s R -l reverse -d "Assume that this patch was created with the old and new files swapped" -complete -c patch -s s -l silent -l quiet -d "Work silently, unless an error occurs" -complete -c patch -s t -l batch -d "Suppress questions like -f, but make some different assumptions" -complete -c patch -s T -l set-time -d "Set the modification and access times of patched files from time stamps given in context diff headers, local time" -complete -c patch -s u -l unified -d "Interpret the patch file as a unified context diff" -complete -c patch -s v -l version -d "Display version and exit" -complete -c patch -s V -l version-control -r -f -d "Use method to determine backup file names" -complete -c patch -l verbose -d "Output extra information about the work being done" -complete -c patch -s x -l debug -r -f -d "Set internal debugging flags of interest only to patch patchers" -complete -c patch -s Y -l basename-prefix -r -f -d "Prefix pref to the basename of a file name when generating its simple backup file name" -complete -c patch -s z -l suffix -r -f -d "Use suffix as the simple backup suffix" -complete -c patch -s Z -l set-utc -d "Set the modification and access times of patched files from time stamps given in context diff headers, UTC, GMT" +complete -c patch -s b -l backup -d "Back up the original contents of each file" +complete -c patch -l backup-if-mismatch -d "Back up files if patch doesn't match exactly" +complete -c patch -l no-backup-if-mismatch -d "Don't back up for mismatching patches" +complete -c patch -s B -l prefix -x -d "Prepend PREFIX to backup filenames" +complete -c patch -s c -l context -d "Interpret patch as context diff" +complete -c patch -s d -l directory -xa '(__fish_complete_directories (commandline -ct))' -d "Change directory to DIR first" +complete -c patch -s D -l ifdef -x -d "Make merged if-then-else output using NAME" +complete -c patch -l dry-run -d "Don't change files; just print what would happen" +complete -c patch -s e -l ed -d "Interpret patch as an 'ed' script" +complete -c patch -s E -l remove-empty-files -d "Remove output files empty after patching" +complete -c patch -s f -l force -d "Like -t, but ignore bad-Prereq patches, assume unreversed" +complete -c patch -s F -l fuzz -x -d "Number of LINES for inexact 'fuzzy' matching" -a "(seq 0 9){\tfuzz lines}" +complete -c patch -s g -l get -x -d "Get files from RCS etc. if positive; ask if negative" -a '(seq -1 1){\t\n}' +complete -c patch -l help -f -d "Display help" +complete -c patch -s i -l input -x -d "Read patch from FILE instead of stdin" -a "( __fish_complete_suffix .patch + __fish_complete_suffix .diff )" +complete -c patch -s l -l ignore-whitespace -d "Ignore whitespace changes between patch & input" +complete -c patch -s n -l normal -d "Interpret patch as normal diff" +complete -c patch -s N -l forward -d "Ignore patches that seem reversed or already applied" +complete -c patch -s o -l output -r -d "Output patched files to FILE" +complete -c patch -s p -l strip -x -d "Strip NUM path components from filenames" -a "(seq 0 9){\t\n}" +complete -c patch -l posix -d "Conform to the POSIX standard" +complete -c patch -l quoting-style -x -d "Output file names using quoting style" -a "literal shell shell-always c escape" +complete -c patch -s r -l reject-file -r -d "Output rejects to FILE" +complete -c patch -s R -l reverse -d "Assume patches were created with old/new files swapped" +complete -c patch -s s -l silent -l quiet -d "Work silently unless an error occurs" +complete -c patch -s t -l batch -d "Ask no questions; skip bad-Prereq patches; assume reversed" +complete -c patch -s T -l set-time -d "Set times of patched files assuming diff uses local time" +complete -c patch -s u -l unified -d "Interpret patch file as a unified diff" +complete -c patch -s v -l version -d "Display version" +complete -c patch -s V -l version-control -x -d "Use method to determine backup file names" -a "simple numbered existing" +complete -c patch -l verbose -d "Output extra information about work being done" +complete -c patch -s x -l debug -x -d "Set internal debugging flags" +complete -c patch -s Y -l basename-prefix -x -d "Prepend PREFIX to backup file basenames" +complete -c patch -s z -l suffix -x -d "Append SUFFIX to backup file name" +complete -c patch -s Z -l set-utc -d "Set times of patched files assuming diff uses UTC" + +# No effect on POSIX systems that don't use O_BINARY/O_TEXT +uname -s | string match -q CYGWIN\* +and complete -c patch -l binary -d "Read & write data in binary mode" From b23403ee6b52ac8652d55d8d90c98eafd9a030ed Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 21 Jan 2019 22:55:56 -0800 Subject: [PATCH 233/439] Revert "__fish_complete_suffix: do not show description when not passed one" This reverts commit 367661d4f124d74819ba2ac4bd021161233b807f. This was the wrong way to address this annoyance of mine. --- share/functions/__fish_complete_suffix.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/functions/__fish_complete_suffix.fish b/share/functions/__fish_complete_suffix.fish index ef1059157..7397e252c 100644 --- a/share/functions/__fish_complete_suffix.fish +++ b/share/functions/__fish_complete_suffix.fish @@ -21,12 +21,12 @@ function __fish_complete_suffix -d "Complete using files" case 1 set comp (commandline -ct) set suff $argv - set desc "\n" + set desc "" case 2 set comp $argv[1] set suff $argv[2] - set desc "\n" + set desc "" case 3 set comp $argv[1] From 288cfa8fb249e27aab8abe966f10a96f64b61a2e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 13 Jan 2019 21:31:49 +0100 Subject: [PATCH 234/439] completions/git: Also don't use files for porcelain=2 This was an oversight from the previous commit. Not that it matters much, because we already removed $files. Still, this would fail if someone defined a global $files, so let's fix it. [ci skip] --- 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 7c4cb1a5c..d8dd64ea9 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -157,7 +157,7 @@ function __fish_git_files set -l ver (command git --version | string replace -rf 'git version (\d+)\.(\d+)\.?.*' '$1\n$2') # Version >= 2.11.* has the v2 format. if test "$ver[1]" -gt 2 2>/dev/null; or test "$ver[1]" -eq 2 -a "$ver[2]" -ge 11 2>/dev/null - command git $git_opt status --porcelain=2 $status_opt -- $files \ + command git $git_opt status --porcelain=2 $status_opt \ | while read -la -d ' ' line set -l file set -l desc From 91ecd3b9b5503ff066874961ec18913679e19738 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 13 Jan 2019 17:18:39 +0100 Subject: [PATCH 235/439] completions/git: Skip "!" shell-aliases for wrapping We can't complete these, and now the user can do ``` set -g __fish_git_alias_$alias $command ``` e.g. ``` set -g __fish_git_alias_co checkout ``` if the arguments in the alias end up going to `git alias`. Fixes #5412. [ci skip] --- share/completions/git.fish | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index d8dd64ea9..c8683e31e 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -440,6 +440,12 @@ end # This is because alias:command is an n:1 mapping (an alias can only have one corresponding command, # but a command can be aliased multiple times) git config -z --get-regexp 'alias\..*' | while read -lz alias command _ + # If the command starts with a "!", it's a shell command, run with /bin/sh, + # or any other shell defined at git's build time. + # + # We can't do anything with them, and we run git-config again for listing aliases, + # so we skip them here. + string match -q '!*' -- $command; and continue # Git aliases can contain chars that variable names can't - escape them. set alias (string replace 'alias.' '' -- $alias | string escape --style=var) set -g __fish_git_alias_$alias $command From 82b4d7225c147860a881e02ed0a77a90c08d8be1 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 22 Jan 2019 17:14:33 +0100 Subject: [PATCH 236/439] env_get_runtime_path: Check for getpwuid() failure Otherwise this is a NULL dereference and then crash. Fixes #5550. --- src/env.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 6582f928d..a046d9ab7 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1723,12 +1723,16 @@ wcstring env_get_runtime_path() { } else { // Don't rely on $USER being set, as setup_user() has not yet been called. // See https://github.com/fish-shell/fish-shell/issues/5180 - const char *uname = getpwuid(geteuid())->pw_name; + // getpeuid() can't fail, but getpwuid sure can. + auto pwuid = getpwuid(geteuid()); + const char *uname = pwuid ? pwuid->pw_name : NULL; // /tmp/fish.user std::string tmpdir = "/tmp/fish."; - tmpdir.append(uname); + if (uname) { + tmpdir.append(uname); + } - if (check_runtime_path(tmpdir.c_str()) != 0) { + if (!uname || check_runtime_path(tmpdir.c_str()) != 0) { debug(0, L"Runtime path not available."); debug(0, L"Try deleting the directory %s and restarting fish.", tmpdir.c_str()); return result; From 963e3217e5b97f48ec8ad9229cef40c826a8888e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 22 Jan 2019 17:14:33 +0100 Subject: [PATCH 237/439] env_get_runtime_path: Check for getpwuid() failure Otherwise this is a NULL dereference and then crash. Fixes #5550. --- src/env.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 2d25cb5de..c5a83ed43 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1655,12 +1655,16 @@ wcstring env_get_runtime_path() { } else { // Don't rely on $USER being set, as setup_user() has not yet been called. // See https://github.com/fish-shell/fish-shell/issues/5180 - const char *uname = getpwuid(geteuid())->pw_name; + // getpeuid() can't fail, but getpwuid sure can. + auto pwuid = getpwuid(geteuid()); + const char *uname = pwuid ? pwuid->pw_name : NULL; // /tmp/fish.user std::string tmpdir = "/tmp/fish."; - tmpdir.append(uname); + if (uname) { + tmpdir.append(uname); + } - if (check_runtime_path(tmpdir.c_str()) != 0) { + if (!uname || check_runtime_path(tmpdir.c_str()) != 0) { debug(0, L"Runtime path not available."); debug(0, L"Try deleting the directory %s and restarting fish.", tmpdir.c_str()); return result; From 1b551e553b5b06fca777396d4c44b0e9de56c10b Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 21 Jan 2019 20:29:31 -0600 Subject: [PATCH 238/439] Fix regression for #4178 and others introduced by 364c839 ...while still keeping intact the fix for #5519. --- src/reader.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/reader.cpp b/src/reader.cpp index 727f79f9b..893ac885f 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -2475,8 +2475,16 @@ const wchar_t *reader_readline(int nchars) { if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output(); // Set the new modes. if (tcsetattr(0, TCSANOW, &shell_modes) == -1) { - if (errno == EIO) redirect_tty_output(); - wperror(L"tcsetattr"); + int err = errno; + if (err == EIO) { + redirect_tty_output(); + } + // This check is required to work around certain issues with fish's approach to + // terminal control when launching interactive processes while in non-interactive + // mode. See #4178 for one such example. + if (err != ENOTTY || is_interactive_session) { + wperror(L"tcsetattr"); + } } while (!finished && !data->end_loop) { From e97c27c177a1c8d93100ff758e58849fc3d13210 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 22 Jan 2019 21:54:17 +0100 Subject: [PATCH 239/439] CHANGELOG: Remove erroneous second "set --show" entry This was added in 2.7.0 (confirmed by checking the tag), and it already has an entry there, so the second entry in 3.0.0 is wrong. [ci skip] --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 134c43729..47413cdec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,7 +109,6 @@ A new feature flags mechanism is added for staging deprecations and breaking cha - `read` writes directly to stdout if called without arguments (#4407). - `read` can now read individual lines into separate variables without consuming the input in its entirety via the new `/--line` option. - `set` has new `--append` and `--prepend` options (#1326). -- `set` has a new `--show` option to show lots of information about variables (#4265). - `string match` with an empty pattern and `--entire` in glob mode now matches everything instead of nothing (#4971). - `string split` supports a new `--no-empty` option to exclude empty strings from the result (#4779). - `string` has new subcommands `split0` and `join0` for working with NUL-delimited output. From 91a9c98974ed8bb775629167c83f9bd48d020ad4 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 22 Jan 2019 13:07:55 -0800 Subject: [PATCH 240/439] Correctly inherit a virtual PWD PWD is not set in fish vars because it is read only. Use getenv() to fetch it, allowing fish to inherit a virtual PWD. Fixes #5525 --- src/env.cpp | 6 +++++- tests/cd.err | 3 +++ tests/cd.in | 10 ++++++++++ tests/cd.out | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/env.cpp b/src/env.cpp index a046d9ab7..49a89687d 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1005,7 +1005,11 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { // initialize the PWD variable if necessary // Note we may inherit a virtual PWD that doesn't match what getcwd would return; respect that. - if (vars.get(L"PWD").missing_or_empty()) { + // Note we treat PWD as read-only so it was not set in vars. + const char *incoming_pwd = getenv("PWD"); + if (incoming_pwd && incoming_pwd[0]) { + vars.set_one(L"PWD", ENV_EXPORT | ENV_GLOBAL, str2wcstring(incoming_pwd)); + } else { vars.set_pwd_from_getcwd(); } vars.set_termsize(); // initialize the terminal size variables diff --git a/tests/cd.err b/tests/cd.err index 80bc5c0e6..4653e2ee6 100644 --- a/tests/cd.err +++ b/tests/cd.err @@ -4,3 +4,6 @@ #################### # cd symlink completion + +#################### +# Virtual PWD inheritance diff --git a/tests/cd.in b/tests/cd.in index ec212c1f6..8b668e614 100644 --- a/tests/cd.in +++ b/tests/cd.in @@ -42,3 +42,13 @@ complete -C'cd ../' # cd back before removing the test directory again. cd $oldpwd rm -Rf $base + +logmsg Virtual PWD inheritance +# PWD should be imported and respected by fish + +mkdir -p $base/realhome +set fish_path $PWD/../test/root/bin/fish +ln -s $base/realhome $base/linkhome +cd $base/linkhome +env HOME=$base/linkhome $fish_path -c 'echo PWD is $PWD' + diff --git a/tests/cd.out b/tests/cd.out index 86ea3299c..73081263d 100644 --- a/tests/cd.out +++ b/tests/cd.out @@ -17,3 +17,7 @@ cd: ../a2/ ../a3/ ../rabbithole/ + +#################### +# Virtual PWD inheritance +PWD is /tmp/cdcomp_test/linkhome From d88be7b5c884b594595197e0ffe6f0eb4fba363e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 3 Jan 2019 23:17:20 +0100 Subject: [PATCH 241/439] tests/cd: cd back before cleaning up Otherwise this'd run afoul of OpenIndiana's "no removing $PWD" rule. Spoilsports! See #5472. --- tests/cd.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/cd.in b/tests/cd.in index 21104a432..fa3964a22 100644 --- a/tests/cd.in +++ b/tests/cd.in @@ -1,3 +1,5 @@ +# Store pwd to later go back before cleaning up +set -l oldpwd (pwd) logmsg cd symlink non-resolution set real (mktemp -d) set link (mktemp -u) @@ -35,4 +37,7 @@ echo "ls:" complete -C'ls ../' echo "cd:" complete -C'cd ../' + +# cd back before removing the test directory again. +cd $oldpwd rm -Rf $base From 24f251e0449eeb170ed0ba619dc2c2ebb20e38d9 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 22 Jan 2019 14:07:25 -0800 Subject: [PATCH 242/439] Correctly remove the test directory again in cd test --- tests/cd.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/cd.in b/tests/cd.in index 8b668e614..a537b1ea8 100644 --- a/tests/cd.in +++ b/tests/cd.in @@ -39,16 +39,16 @@ complete -C'ls ../' echo "cd:" complete -C'cd ../' -# cd back before removing the test directory again. -cd $oldpwd -rm -Rf $base - logmsg Virtual PWD inheritance # PWD should be imported and respected by fish +cd $oldpwd mkdir -p $base/realhome set fish_path $PWD/../test/root/bin/fish ln -s $base/realhome $base/linkhome cd $base/linkhome env HOME=$base/linkhome $fish_path -c 'echo PWD is $PWD' +# cd back before removing the test directory again. +cd $oldpwd +rm -Rf $base From dfa61926e8f48a012872873e8b889c02401d898e Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 22 Jan 2019 13:07:55 -0800 Subject: [PATCH 243/439] Correctly inherit a virtual PWD PWD is not set in fish vars because it is read only. Use getenv() to fetch it, allowing fish to inherit a virtual PWD. This cherry pick includes both: 24f251e04 Correctly remove the test directory again in cd test 91a9c9897 Correctly inherit a virtual PWD Fixes #5525 --- src/env.cpp | 6 +++++- tests/cd.err | 3 +++ tests/cd.in | 10 ++++++++++ tests/cd.out | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/env.cpp b/src/env.cpp index c5a83ed43..7c617cb2e 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -962,7 +962,11 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { // initialize the PWD variable if necessary // Note we may inherit a virtual PWD that doesn't match what getcwd would return; respect that. - if (env_get(L"PWD").missing_or_empty()) { + // Note we treat PWD as read-only so it was not set in vars. + const char *incoming_pwd = getenv("PWD"); + if (incoming_pwd && incoming_pwd[0]) { + env_set_one(L"PWD", ENV_EXPORT | ENV_GLOBAL, str2wcstring(incoming_pwd)); + } else { env_set_pwd_from_getcwd(); } env_set_termsize(); // initialize the terminal size variables diff --git a/tests/cd.err b/tests/cd.err index 80bc5c0e6..4653e2ee6 100644 --- a/tests/cd.err +++ b/tests/cd.err @@ -4,3 +4,6 @@ #################### # cd symlink completion + +#################### +# Virtual PWD inheritance diff --git a/tests/cd.in b/tests/cd.in index fa3964a22..fde7cd558 100644 --- a/tests/cd.in +++ b/tests/cd.in @@ -38,6 +38,16 @@ complete -C'ls ../' echo "cd:" complete -C'cd ../' +logmsg Virtual PWD inheritance +# PWD should be imported and respected by fish + +cd $oldpwd +mkdir -p $base/realhome +set fish_path $PWD/../test/root/bin/fish +ln -s $base/realhome $base/linkhome +cd $base/linkhome +env HOME=$base/linkhome $fish_path -c 'echo PWD is $PWD' + # cd back before removing the test directory again. cd $oldpwd rm -Rf $base diff --git a/tests/cd.out b/tests/cd.out index 86ea3299c..73081263d 100644 --- a/tests/cd.out +++ b/tests/cd.out @@ -17,3 +17,7 @@ cd: ../a2/ ../a3/ ../rabbithole/ + +#################### +# Virtual PWD inheritance +PWD is /tmp/cdcomp_test/linkhome From 87b7b6b2bb8e79ffd8d02ffb0427dd2f37f57a28 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 22 Jan 2019 14:33:47 -0800 Subject: [PATCH 244/439] Make control-S begin navigating the pager contents In addition to showing the search field, actually allow the user to type in it. --- CHANGELOG.md | 1 + src/reader.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47413cdec..31fab2284 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - exec now behaves properly inside functions (#5449) - while loops now evaluate to the last executed command in the loop body (or zero if the body was empty), matching POSIX semantics. - fish does not hang on launch when running under Cygwin/MSYS2 +- The pager-toggle-search binding (by default Control-S) now positions the cursor in the completions list. # fish 3.0.0 (released December 28, 2018) diff --git a/src/reader.cpp b/src/reader.cpp index b2c241952..34b0df044 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -2728,10 +2728,15 @@ const wchar_t *reader_readline(int nchars) { break; } case R_PAGER_TOGGLE_SEARCH: { - if (data->is_navigating_pager_contents()) { + if (!data->pager.empty()) { + // Toggle search, and begin navigating if we are now searching. bool sfs = data->pager.is_search_field_shown(); data->pager.set_search_field_shown(!sfs); data->pager.set_fully_disclosed(true); + if (data->pager.is_search_field_shown() && + !data->is_navigating_pager_contents()) { + select_completion_in_direction(direction_south); + } reader_repaint_needed(); } break; From 71b5591f21df6150cd1fddafb368caa152631cb7 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 22 Jan 2019 14:36:25 -0800 Subject: [PATCH 245/439] Update docs on tab completions and searching Fixes #5547 --- doc_src/index.hdr.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 6acfa4a22..aab92b398 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -320,7 +320,9 @@ 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. 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. +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. + +If the list is visible, pressing control-S (or the `pager-toggle-search` binding) will allow filtering the list. Shift-tab (or the `complete-and-search` binding) will trigger completion with the search field immediately visible. These are the general purpose tab completions that `fish` provides: From 6bd3474daf9d8b5a9c4bd19fc9b3368a85c49dcc Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 22 Jan 2019 14:33:47 -0800 Subject: [PATCH 246/439] Make control-S begin navigating the pager contents In addition to showing the search field, actually allow the user to type in it. --- CHANGELOG.md | 3 ++- src/reader.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0782b2051..e9474eb3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,3 @@ -<<<<<<< HEAD # fish 3.0.1 This release of fish fixes a number of major issues discovered in fish 3.0.0. @@ -9,6 +8,8 @@ This release of fish fixes a number of major issues discovered in fish 3.0.0. - while loops now evaluate to the last executed command in the loop body (or zero if the body was empty), matching POSIX semantics. - `read --silent` no longer echoes to the tty when run from a non-interactive script (#5519) - (macOS only) /etc/paths and /etc/paths.d now correctly set path entries with spaces. Also affects MANPATH. (#5481) +- fish does not hang on launch when running under Cygwin/MSYS2 +- The pager-toggle-search binding (by default Control-S) now positions the cursor in the completions list. If you are upgrading from version 2.7.1 or before, please also review the release notes for 3.0.0 and 3.0b1 (included below). diff --git a/src/reader.cpp b/src/reader.cpp index 893ac885f..2bc74d79d 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -2711,10 +2711,15 @@ const wchar_t *reader_readline(int nchars) { break; } case R_PAGER_TOGGLE_SEARCH: { - if (data->is_navigating_pager_contents()) { + if (!data->pager.empty()) { + // Toggle search, and begin navigating if we are now searching. bool sfs = data->pager.is_search_field_shown(); data->pager.set_search_field_shown(!sfs); data->pager.set_fully_disclosed(true); + if (data->pager.is_search_field_shown() && + !data->is_navigating_pager_contents()) { + select_completion_in_direction(direction_south); + } reader_repaint_needed(); } break; From 88ee55443cd6001a74db799c58e58d268ad01ec4 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 22 Jan 2019 14:36:25 -0800 Subject: [PATCH 247/439] Update docs on tab completions and searching Fixes #5547 --- doc_src/index.hdr.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 7f2fe7efc..c75c6b82d 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -320,7 +320,9 @@ 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. 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. +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. + +If the list is visible, pressing control-S (or the `pager-toggle-search` binding) will allow filtering the list. Shift-tab (or the `complete-and-search` binding) will trigger completion with the search field immediately visible. These are the general purpose tab completions that `fish` provides: From 72423c517a788843caa0f7cfaf99aa32b4b9a5fa Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 16 Jan 2019 09:29:17 +0100 Subject: [PATCH 248/439] webconfig: Fix binding tab This broke when --preset was introduced. We allow a "--preset" or "--user" to appear right after the "bind", and save the value, but don't use it yet. Fixes #5534. [ci skip] --- share/tools/web_config/webconfig.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 58702f2fa..a5c5ef981 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -704,6 +704,20 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): for line in out.split('\n'): comps = line.split(' ', 2) + # If we don't have "bind", a sequence and a mapping, + # it's not a valid binding. + if len(comps) < 3: + continue + + # Store the "--preset" value for later + if comps[1] == '--preset': + preset = True + # There's possibly a way to do this faster, but it's not important. + comps = line.split(' ', 3)[1:] + elif comps[1] == '--user': + preset = False + comps = line.split(' ', 3)[1:] + # Check again if we removed the level. if len(comps) < 3: continue From a1df72dbb60f26b1ca6e24b6c3fd39e07a6ebf14 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 17:25:33 -0600 Subject: [PATCH 249/439] Fix `wcstod_l` infinite recursion under FreeBSD This was the actual issue leading to memory corruption under FreeBSD in issue #5453, worked around by correcting the detection of `wcstod_l` so that our version of the function is not called at all. If we are 100% certain that `wcstod_l` does not exist, then then the existing code is fine. But given that our checks have failed seperately on two different platforms already (FreeBSD and Cygwin/newlib), it's a good precaution to take. --- src/fallback.cpp | 7 ++++--- src/fallback.h | 12 +++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/fallback.cpp b/src/fallback.cpp index d7e4e508e..8a7d6a0e2 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -390,9 +390,10 @@ int flock(int fd, int op) { #endif // HAVE_FLOCK #ifndef HAVE_WCSTOD_L -// musl doesn't feature wcstod_l, -// so we just wrap wcstod. -double wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc) { +#undef wcstod_l +// For platforms without wcstod_l C extension, wrap wcstod after changing the +// thread-specific locale. +double fish_compat::wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc) { char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL)); // Yes, this is hardcoded to use the "C" locale. // That's the only thing we need, and uselocale(loc) broke in my testing. diff --git a/src/fallback.h b/src/fallback.h index f8b7d4977..b935c8460 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -200,5 +200,15 @@ int flock(int fd, int op); #endif #ifndef HAVE_WCSTOD_L -double wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc); +// On some platforms if this is incorrectly detected and a system-defined +// defined version of `wcstod_l` exists, calling `wcstod` from our own +// `wcstod_l` can call back into `wcstod_l` causing infinite recursion. +// e.g. FreeBSD defines `wcstod(x, y)` as `wcstod_l(x, y, __get_locale())`. +// Solution: namespace our implementation to make sure there is no symbol +// duplication. +#undef wcstod_l +namespace fish_compat { + double wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc); +} +#define wcstod_l(x, y, z) fish_compat::wcstod_l(x, y, z) #endif From eee4dd8248494fd3b2d08b7069c50110a02b62f9 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Tue, 22 Jan 2019 01:12:57 +0100 Subject: [PATCH 250/439] __fish_complete_man.fish: escape for regex Previously, using special regex characters or slashes would result in an error message, when pressing tab in a command-line such as "man /usr/bin/time ". --- share/functions/__fish_complete_man.fish | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/functions/__fish_complete_man.fish b/share/functions/__fish_complete_man.fish index e65d61c07..a8d3daf97 100644 --- a/share/functions/__fish_complete_man.fish +++ b/share/functions/__fish_complete_man.fish @@ -10,7 +10,8 @@ function __fish_complete_man case '-**' case '*' - set section $prev[1] + set section (string escape --style=regex $prev[1]) + set section (string replace --all / \\/ $section) end set -e prev[1] end From 1d80028e246b219f981318378a881f4ede03c1c1 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Tue, 22 Jan 2019 01:12:57 +0100 Subject: [PATCH 251/439] __fish_complete_man.fish: escape for regex Previously, using special regex characters or slashes would result in an error message, when pressing tab in a command-line such as "man /usr/bin/time ". --- share/functions/__fish_complete_man.fish | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/functions/__fish_complete_man.fish b/share/functions/__fish_complete_man.fish index e65d61c07..a8d3daf97 100644 --- a/share/functions/__fish_complete_man.fish +++ b/share/functions/__fish_complete_man.fish @@ -10,7 +10,8 @@ function __fish_complete_man case '-**' case '*' - set section $prev[1] + set section (string escape --style=regex $prev[1]) + set section (string replace --all / \\/ $section) end set -e prev[1] end From da44ee1d087423cd95298d19ef97c32cac4a8130 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 30 Dec 2018 16:04:57 +0100 Subject: [PATCH 252/439] Don't wait for disowned pgids if they are special If a job is disowned that, for some reason, has a pgid that is special to waitpid, like 0 (process with pgid of the calling process), -1 (any process), or our actual pgid, that would lead to us waiting for too many processes when we later try to reap the disowned processes (to stop zombies from appearing). And that means we'd snag away the processes we actually do want to wait for, which would end with us in a waiting loop. This is tough to reproduce, the easiest I've found was fish -ic 'sleep 5 &; disown; set -g __fish_git_prompt_showupstream auto; __fish_git_prompt' in a git repo. What we do is to not allow special pgids in the disowned_pids list. That means we might leave a zombie around (though we probably wait on 0 somewhere), but that's preferable to infinitely looping. See #5426. --- src/proc.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/proc.cpp b/src/proc.cpp index b7b30dc22..e09747f8a 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -354,7 +354,12 @@ typedef unsigned int process_generation_count_t; static std::vector s_disowned_pids; void add_disowned_pgid(pid_t pgid) { - s_disowned_pids.push_back(pgid * -1); + // NEVER add our own pgid, or one of the special values, + // or waiting for it will + // snag other processes away. + if (pgid != getpgrp() && (pgid > 0 || pgid < -1)) { + s_disowned_pids.push_back(pgid * -1); + } } /// A static value tracking how many SIGCHLDs we have seen, which is used in a heurstic to From 4af2a681b8fa69a12da9ab8015474841d15e26a9 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 23 Jan 2019 14:02:03 +0100 Subject: [PATCH 253/439] completions/git: Stop offering :/ files so much Don't do it when the relative path is simple (purely descending), unless the token starts with ":/". Also stop offering directories - if they need to be disambiguated, the normal completion logic will take care of that. Fixes #5574. [ci skip] --- share/completions/git.fish | 46 ++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index c8683e31e..da7842e6b 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -121,8 +121,6 @@ function __fish_git_files contains -- copied $argv; and set -l copied and set -l copied_desc (_ "Copied file") - set -l dir_desc (_ "Directory") - # A literal "?" for use in `case`. set -l q '\\?' if status test-feature qmark-noglob @@ -245,27 +243,22 @@ function __fish_git_files # First the relative filename. printf '%s\t%s\n' "$file" $desc # Now from repo root. - set -l fromroot (builtin realpath -- $file 2>/dev/null) - and set fromroot (string replace -- "$root/" ":/" "$fromroot") - and printf '%s\t%s\n' "$fromroot" $desc - - # And the containing directory. - # TODO: We may want to offer the parent, but only if another child of that also has a change. - # E.g: - # - a/b/c is added - # - a/d/e is modified - # - a/ should be offered, but only a/b/ and a/d/ are. - # - # Always offering all parents is overkill however, which is why we don't currently do it. - set -l dir (string replace -rf '/[^/]+$' '/' -- $file) - and printf '%s\t%s\n' $dir "$dir_desc" + # Only do this if the filename isn't a simple child, + # or the current token starts with ":" + if string match -q '../*' -- $file + or string match -q ':*' -- (commandline -ct) + set -l fromroot (builtin realpath -- $file 2>/dev/null) + and set fromroot (string replace -- "$root/" ":/" "$fromroot") + and printf '%s\t%s\n' "$fromroot" $desc + end end end else # v1 format logic # We need to compute relative paths on our own, which is slow. # Pre-remove the root at least, so we have fewer components to deal with. - set -l _pwd_list (string replace "$root/" "" -- $PWD | string split /) + set -l _pwd_list (string replace "$root/" "" -- $PWD/ | string split /) + test -z "$_pwd_list[-1]"; and set -e _pwd_list[-1] # Cache the previous relative path because these are sorted, so we can reuse it # often for files in the same directory. set -l previous @@ -349,9 +342,7 @@ function __fish_git_files # Again: "XY filename", so the filename starts on character 4. set -l relfile (string sub -s 4 -- $line) - # The filename with ":/" prepended. - set -l file (string replace -- "$root/" ":/" "$root/$relfile") - + set -l file # Computing relative path by hand. set -l abs (string split / -- $relfile) # If it's in the same directory, we just need to change the filename. @@ -359,7 +350,6 @@ function __fish_git_files set previous[-1] $abs[-1] else set -l pwd_list $_pwd_list - set previousfile $abs # Remove common prefix while test "$pwd_list[1]" = "$abs[1]" set -e pwd_list[1] @@ -369,10 +359,18 @@ function __fish_git_files set previous (string replace -r '.*' '..' -- $pwd_list) $abs end set -a file (string join / -- $previous) - printf '%s\n' $file\t$desc - set -l dir (string replace -rf '/[^/]+$' '/' -- $file) - and printf '%s\t%s\n' $dir "$dir_desc" + # The filename with ":/" prepended. + if string match -q '../*' -- $file + or string match -q ':*' -- (commandline -ct) + set file (string replace -- "$root/" ":/" "$root/$relfile") + end + + if test "$root/$relfile" = (pwd -P)/$relfile + set file $relfile + end + + printf '%s\n' $file\t$desc end end end From c20187e858a257025912b8a17043ed76e774d3b8 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 23 Jan 2019 14:02:03 +0100 Subject: [PATCH 254/439] completions/git: Stop offering :/ files so much Don't do it when the relative path is simple (purely descending), unless the token starts with ":/". Also stop offering directories - if they need to be disambiguated, the normal completion logic will take care of that. Fixes #5574. [ci skip] --- share/completions/git.fish | 46 ++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index c8683e31e..da7842e6b 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -121,8 +121,6 @@ function __fish_git_files contains -- copied $argv; and set -l copied and set -l copied_desc (_ "Copied file") - set -l dir_desc (_ "Directory") - # A literal "?" for use in `case`. set -l q '\\?' if status test-feature qmark-noglob @@ -245,27 +243,22 @@ function __fish_git_files # First the relative filename. printf '%s\t%s\n' "$file" $desc # Now from repo root. - set -l fromroot (builtin realpath -- $file 2>/dev/null) - and set fromroot (string replace -- "$root/" ":/" "$fromroot") - and printf '%s\t%s\n' "$fromroot" $desc - - # And the containing directory. - # TODO: We may want to offer the parent, but only if another child of that also has a change. - # E.g: - # - a/b/c is added - # - a/d/e is modified - # - a/ should be offered, but only a/b/ and a/d/ are. - # - # Always offering all parents is overkill however, which is why we don't currently do it. - set -l dir (string replace -rf '/[^/]+$' '/' -- $file) - and printf '%s\t%s\n' $dir "$dir_desc" + # Only do this if the filename isn't a simple child, + # or the current token starts with ":" + if string match -q '../*' -- $file + or string match -q ':*' -- (commandline -ct) + set -l fromroot (builtin realpath -- $file 2>/dev/null) + and set fromroot (string replace -- "$root/" ":/" "$fromroot") + and printf '%s\t%s\n' "$fromroot" $desc + end end end else # v1 format logic # We need to compute relative paths on our own, which is slow. # Pre-remove the root at least, so we have fewer components to deal with. - set -l _pwd_list (string replace "$root/" "" -- $PWD | string split /) + set -l _pwd_list (string replace "$root/" "" -- $PWD/ | string split /) + test -z "$_pwd_list[-1]"; and set -e _pwd_list[-1] # Cache the previous relative path because these are sorted, so we can reuse it # often for files in the same directory. set -l previous @@ -349,9 +342,7 @@ function __fish_git_files # Again: "XY filename", so the filename starts on character 4. set -l relfile (string sub -s 4 -- $line) - # The filename with ":/" prepended. - set -l file (string replace -- "$root/" ":/" "$root/$relfile") - + set -l file # Computing relative path by hand. set -l abs (string split / -- $relfile) # If it's in the same directory, we just need to change the filename. @@ -359,7 +350,6 @@ function __fish_git_files set previous[-1] $abs[-1] else set -l pwd_list $_pwd_list - set previousfile $abs # Remove common prefix while test "$pwd_list[1]" = "$abs[1]" set -e pwd_list[1] @@ -369,10 +359,18 @@ function __fish_git_files set previous (string replace -r '.*' '..' -- $pwd_list) $abs end set -a file (string join / -- $previous) - printf '%s\n' $file\t$desc - set -l dir (string replace -rf '/[^/]+$' '/' -- $file) - and printf '%s\t%s\n' $dir "$dir_desc" + # The filename with ":/" prepended. + if string match -q '../*' -- $file + or string match -q ':*' -- (commandline -ct) + set file (string replace -- "$root/" ":/" "$root/$relfile") + end + + if test "$root/$relfile" = (pwd -P)/$relfile + set file $relfile + end + + printf '%s\n' $file\t$desc end end end From 70f618d5989b7fb54b6efd5556a63478fbe6d65b Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 31 Dec 2018 02:31:48 -0600 Subject: [PATCH 255/439] Fix wcstod_l detection under FreeBSD --- cmake/ConfigureChecks.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index 1444361e9..5182ba808 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -72,7 +72,7 @@ CHECK_CXX_SYMBOL_EXISTS(wcsdup wchar.h HAVE_WCSDUP) CHECK_CXX_SYMBOL_EXISTS(wcslcpy wchar.h HAVE_WCSLCPY) CHECK_CXX_SYMBOL_EXISTS(wcsncasecmp wchar.h HAVE_WCSNCASECMP) CHECK_CXX_SYMBOL_EXISTS(wcsndup wchar.h HAVE_WCSNDUP) -CHECK_CXX_SYMBOL_EXISTS(wcstod_l wchar.h HAVE_WCSTOD_L) +CHECK_CXX_SYMBOL_EXISTS(wcstod_l "wchar.h;xlocale.h" HAVE_WCSTOD_L) CHECK_CXX_SYMBOL_EXISTS(_sys_errs stdlib.h HAVE__SYS__ERRS) From 3875615f45f861a2cf0b17b9d79a29e3a0b563ac Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 14:05:06 -0600 Subject: [PATCH 256/439] Define _GNU_SOURCE for wcstod_l check --- cmake/ConfigureChecks.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index 5182ba808..0b5dfd26a 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -28,6 +28,7 @@ INCLUDE(CheckIncludeFiles) INCLUDE(CheckStructHasMember) INCLUDE(CheckCXXSourceCompiles) INCLUDE(CheckTypeSize) +INCLUDE(CMakePushCheckState) CHECK_CXX_SYMBOL_EXISTS(backtrace_symbols execinfo.h HAVE_BACKTRACE_SYMBOLS) CHECK_CXX_SYMBOL_EXISTS(clock_gettime time.h HAVE_CLOCK_GETTIME) CHECK_CXX_SYMBOL_EXISTS(ctermid_r stdio.h HAVE_CTERMID_R) @@ -72,7 +73,12 @@ CHECK_CXX_SYMBOL_EXISTS(wcsdup wchar.h HAVE_WCSDUP) CHECK_CXX_SYMBOL_EXISTS(wcslcpy wchar.h HAVE_WCSLCPY) CHECK_CXX_SYMBOL_EXISTS(wcsncasecmp wchar.h HAVE_WCSNCASECMP) CHECK_CXX_SYMBOL_EXISTS(wcsndup wchar.h HAVE_WCSNDUP) + +CMAKE_PUSH_CHECK_STATE(RESET) +# wcstod_l is a GNU-extension, sometimes hidden behind the following define +LIST(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE=1) CHECK_CXX_SYMBOL_EXISTS(wcstod_l "wchar.h;xlocale.h" HAVE_WCSTOD_L) +CMAKE_POP_CHECK_STATE() CHECK_CXX_SYMBOL_EXISTS(_sys_errs stdlib.h HAVE__SYS__ERRS) From 0f2470d45a1b4f0ebe7f46a24ae4bb97b5a418c1 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 17:29:48 -0600 Subject: [PATCH 257/439] Fix unsafe locale usage in `wcstod_l` fallback Using `setlocale` is both not thread-safe and not correct, as a) The global locale is usually stored in static storage, so simultaneous calls to `setlocale` can result in corruption, and b) `setlocale` changes the locale for the entire application, not just the calling thread. This means that even if we wrapped the `wcstod_l` in a mutex to prevent the previous point, the results would still be incorrect because this would incorrectly influence the results of locale-aware functions executed in other threads while this thread is executing. The previous comment mentioned that `uselocale` hadn't worked. I'm not sure what the failing implementation looked like, but `uselocale` can be tricky. The committed implementation passes the tests for me under Linux and FreeBSD. --- src/fallback.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fallback.cpp b/src/fallback.cpp index 8a7d6a0e2..41d897296 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -394,13 +394,13 @@ int flock(int fd, int op) { // For platforms without wcstod_l C extension, wrap wcstod after changing the // thread-specific locale. double fish_compat::wcstod_l(const wchar_t *enptr, wchar_t **endptr, locale_t loc) { - char *saved_locale = strdup(setlocale(LC_NUMERIC, NULL)); - // Yes, this is hardcoded to use the "C" locale. - // That's the only thing we need, and uselocale(loc) broke in my testing. - setlocale(LC_NUMERIC, "C"); + // Create and use a new, thread-specific locale + locale_t locale = newlocale(LC_NUMERIC, "C", nullptr); + locale_t prev_locale = uselocale(locale); double ret = wcstod(enptr, endptr); - setlocale(LC_NUMERIC, saved_locale); - free(saved_locale); + // Restore the old locale before freeing the locale we created and are still using + uselocale(prev_locale); + freelocale(locale); return ret; } #endif // defined(wcstod_l) From 8737b654bfde81e45cf39976b9ea22f0ddc80c7e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 2 Jan 2019 19:07:53 -0600 Subject: [PATCH 258/439] Fix `wcstod_l` detection under Linux This was broken in a8eb02f9f59350f222295e16b8f68dc3b45d9ed5 when the detection was corrected for FreeBSD. This patch makes the detection work for both Linux and FreeBSD instead of one or the other (tested). --- cmake/ConfigureChecks.cmake | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index 0b5dfd26a..22b0066a0 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -75,9 +75,17 @@ CHECK_CXX_SYMBOL_EXISTS(wcsncasecmp wchar.h HAVE_WCSNCASECMP) CHECK_CXX_SYMBOL_EXISTS(wcsndup wchar.h HAVE_WCSNDUP) CMAKE_PUSH_CHECK_STATE(RESET) -# wcstod_l is a GNU-extension, sometimes hidden behind the following define +# `wcstod_l` is a GNU-extension, sometimes hidden behind the following define LIST(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE=1) -CHECK_CXX_SYMBOL_EXISTS(wcstod_l "wchar.h;xlocale.h" HAVE_WCSTOD_L) +# `xlocale.h` is required to find `wcstod_l` in `wchar.h` under FreeBSD, but +# it's not present under Linux. +SET(WCSTOD_L_INCLUDES "") +CHECK_INCLUDE_FILES("xlocale.h" HAVE_XLOCALE_H) +IF(HAVE_XLOCALE_H) + LIST(APPEND WCSTOD_L_INCLUDES "xlocale.h") +ENDIF() +LIST(APPEND WCSTOD_L_INCLUDES "wchar.h") +CHECK_CXX_SYMBOL_EXISTS(wcstod_l "${WCSTOD_L_INCLUDES}" HAVE_WCSTOD_L) CMAKE_POP_CHECK_STATE() CHECK_CXX_SYMBOL_EXISTS(_sys_errs stdlib.h HAVE__SYS__ERRS) From 55526947d256e9933bac886876c044a25fc4820f Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 10 Jan 2019 19:51:12 -0600 Subject: [PATCH 259/439] Fix `locale_t` under macOS 10.10 `xlocale.h` is not available on Linux, so we can't just universally include it. `HAVE_XLOCALE_H` was already being tested/set in the CMake script as a possible requirement for `wcstod_l` support, this just adds it to `config_cmake_h.in` and uses it in `wutil.h` to gate the include. --- config_cmake.h.in | 3 +++ src/wutil.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/config_cmake.h.in b/config_cmake.h.in index 47cfcc5e2..8071546fd 100644 --- a/config_cmake.h.in +++ b/config_cmake.h.in @@ -153,6 +153,9 @@ /* The size of wchar_t in bits. */ #define WCHAR_T_BITS ${WCHAR_T_BITS} +/* Define if xlocale.h is required for locale_t or wide character support */ +#cmakedefine HAVE_XLOCALE_H 1 + /* Enable large inode numbers on Mac OS X 10.5. */ #ifndef _DARWIN_USE_64_BIT_INODE # define _DARWIN_USE_64_BIT_INODE 1 diff --git a/src/wutil.h b/src/wutil.h index 28a8540d1..661e82d05 100644 --- a/src/wutil.h +++ b/src/wutil.h @@ -10,6 +10,10 @@ #include #include +#ifdef HAVE_XLOCALE_H +#include +#endif + #include "common.h" #include "maybe.h" From aee8e5250a1b3d0ae1cbe969c37daadae7a48001 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 16 Jan 2019 14:42:15 -0600 Subject: [PATCH 260/439] Add missing define for HAVE_WCSTOD_L to osx/config.h I believe this should take care of the reported problem with the corrected definition for `wcstod_l`. For future reference, any changes to `config.h.in` should also be reflected in `osx/config.h` --- osx/config.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osx/config.h b/osx/config.h index d8036fee7..233464b40 100644 --- a/osx/config.h +++ b/osx/config.h @@ -181,6 +181,9 @@ /* Define to 1 if you have the `wcsndup' function. */ /* #undef HAVE_WCSNDUP */ +/* Define to 1 if you have the `wcstod_l' function. */ +#define HAVE_WCSTOD_L 1 + /* Define to 1 if the winsize struct and TIOCGWINSZ macro exist */ #define HAVE_WINSIZE 1 From 432ed621fd59e61b029cf55c05cc7ea205797bd5 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 23 Jan 2019 15:52:13 +0100 Subject: [PATCH 261/439] completions/git: Don't sort tags or commits Sorting these alphabetically just makes no sense, but takes time. [ci skip] --- share/completions/git.fish | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index da7842e6b..bda9ac692 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -764,8 +764,8 @@ complete -f -c git -n "__fish_git_using_command remote; and __fish_seen_subcomma ### show 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)' -complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_tags)' -d 'Tag' -complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_commits)' +complete -f -c git -n '__fish_git_using_command show' -ka '(__fish_git_tags)' -d 'Tag' +complete -f -c git -n '__fish_git_using_command show' -ka '(__fish_git_commits)' complete -f -c git -n '__fish_git_using_command show' -l format -d 'Pretty-print the contents of the commit logs in a given format' -a '(__fish_git_show_opt format)' complete -f -c git -n '__fish_git_using_command show' -l abbrev-commit -d 'Show only a partial hexadecimal commit object name' complete -f -c git -n '__fish_git_using_command show' -l no-abbrev-commit -d 'Show the full 40-byte hexadecimal commit object name' @@ -845,7 +845,7 @@ 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)' # 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; and __fish_git_possible_commithash' -ka '(__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' @@ -1270,7 +1270,7 @@ complete -f -c git -n '__fish_git_using_command reset; and not contains -- -- (c ### 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)' +complete -f -c git -n '__fish_git_using_command revert' -ka '(__fish_git_commits)' # TODO options ### rm @@ -1303,7 +1303,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' -l contains -xka '(__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 delete -s v verify' -a '(__fish_git_tags)' -d 'Tag' # TODO options From e8f340a03d8792a97610703d5e41458fea60af44 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 21 Jan 2019 17:08:49 -0800 Subject: [PATCH 262/439] string completions: add missing upper, lower, split0, join0, unescape and --style=regex --- share/completions/string.fish | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/share/completions/string.fish b/share/completions/string.fish index ea2bae47b..37d72f5b6 100644 --- a/share/completions/string.fish +++ b/share/completions/string.fish @@ -2,23 +2,29 @@ # This follows a strict command-then-options approach, so we can just test the number of tokens complete -f -c string complete -f -c string -n "test (count (commandline -opc)) -ge 2; and not contains -- (commandline -opc)[2] escape" -s q -l quiet -d "Do not print output" +complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "lower" +complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "upper" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "length" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "sub" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] sub" -s s -l start -a "(seq 1 10)" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] sub" -s l -l length -a "(seq 1 10)" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] sub" -s s -l start -a "(seq 1 10)" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] sub" -s l -l length -a "(seq 1 10)" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "split" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] split" -s m -l max -a "(seq 1 10)" -d "Specify maximum number of splits" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] split" -s r -l right -d "Split right-to-left" +complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "split0" +complete -x -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s m -l max -a "(seq 1 10)" -d "Specify maximum number of splits" +complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s r -l right -d "Split right-to-left" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "join" +complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "join0" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "trim" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s l -l left -d "Trim only leading characters" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s r -l right -d "Trim only trailing characters" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s c -l chars -d "Specify the chars to trim (default: whitespace)" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "escape" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape" -s n -l no-quoted -d "Escape with \\ instead of quoting" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape" -l style -d "Pick escaping style" -a " +complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "unescape" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -s n -l no-quoted -d "Escaped with \\ instead of quoted" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -l style -d "Escaping style" -a " (printf '%s\t%s\n' script 'For use in scripts' \ var 'For use as a variable name' \ + regex 'For string match -r, string replace -r' \ url 'For use as a URL')" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "match" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s n -l index -d "Report index and length of the matches" @@ -29,6 +35,6 @@ complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s i -l ignore-case -d "Case insensitive" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s r -l regex -d "Use regex instead of globs" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "repeat" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s n -l count -a "(seq 1 10)" -d "Repetition count" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s m -l max -a "(seq 1 10)" -d "Maximum number of printed char" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s n -l count -a "(seq 1 10)" -d "Repetition count" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s m -l max -a "(seq 1 10)" -d "Maximum number of printed char" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s N -l no-newline -d "Remove newline" From 5b12d703dd6c136a5fd7c7c4c55d5b3d3922238a Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 21 Jan 2019 13:59:36 -0800 Subject: [PATCH 263/439] Fix fish_config rendering brights as normal on prompt previews I noticed our default brgreen for fish_color_user was rendering as just unstyled white. --- share/tools/web_config/fishconfig.css | 1 + share/tools/web_config/webconfig.py | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/share/tools/web_config/fishconfig.css b/share/tools/web_config/fishconfig.css index ca74fa570..34eafd050 100644 --- a/share/tools/web_config/fishconfig.css +++ b/share/tools/web_config/fishconfig.css @@ -474,6 +474,7 @@ img.delete_icon { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + color: #c0c0c0; /* set_color normal, assume white (not brwhite) */ } .prompt_demo { diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index a5c5ef981..15ae8d2a5 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -21,6 +21,7 @@ import socket import string import subprocess import sys +from itertools import chain FISH_BIN_PATH = False # will be set later IS_PY2 = sys.version_info[0] == 2 @@ -254,7 +255,6 @@ def get_special_ansi_escapes(): def append_html_for_ansi_escape(full_val, result, span_open): - # Strip off the initial \x1b[ and terminating m val = full_val[2:-1] @@ -271,10 +271,10 @@ def append_html_for_ansi_escape(full_val, result, span_open): result.append('') return True # span now open - # term8 foreground color - if val in [str(x) for x in range(30, 38)]: + # term16 foreground color + if val in (str(x) for x in chain(range(90, 97), range(30, 38))): close_span() - html_color = html_color_for_ansi_color_index(int(val) - 30) + html_color = html_color_for_ansi_color_index(int(val) - (30 if int(val) < 90 else 82)) result.append('') return True # span now open @@ -284,7 +284,7 @@ def append_html_for_ansi_escape(full_val, result, span_open): close_span() return False - # We don't handle bold or underline yet + # TODO We don't handle bold, underline, italics, dim, or reverse yet # Do nothing on failure return span_open From 3bf702067af06325362ad7e24626aff0f61da7f6 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 14 Jan 2019 03:08:44 -0800 Subject: [PATCH 264/439] fish_config: tell the user some nice things without Python As discussed in #5492, it would be good if running fish_config without Python actually told the user to install Python. Further, let's give the person some hints on how to configure these things by hand, since they may have to. --- share/functions/fish_config.fish | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/share/functions/fish_config.fish b/share/functions/fish_config.fish index fa6c54e46..b8fb455d6 100644 --- a/share/functions/fish_config.fish +++ b/share/functions/fish_config.fish @@ -6,5 +6,12 @@ function fish_config --description "Launch fish's web based configuration" python2 "$__fish_data_dir/tools/web_config/webconfig.py" $argv else if command -sq python python "$__fish_data_dir/tools/web_config/webconfig.py" $argv + else + echo (set_color $fish_color_error)Cannot launch the web configuration tool.(set_color normal) + echo (set_color -o)fish_config(set_color normal) requires Python. + echo Installing Python will fix this, and also enable completions to be automatically generated from man pages.\n + echo To configure your prompt, create a (set_color -o)fish_prompt(set_color normal) function. + echo There are examples in (set_color $fish_color_valid_path)$__fish_data_dir/tools/web_config/sample_prompts(set_color normal).\n + echo You can tweak your colors by setting the (set_color $fish_color_search_match)\$fish_color_\*(set_color normal) variables. end end From b5cbdc906591c1eafc77a6d555bee6e8f6cbe700 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 14 Jan 2019 03:23:47 -0800 Subject: [PATCH 265/439] fish_config: make clear python 2 or 3 will both work. A person stuck installing it just for fish on their server doesn't want to waste time installing the wrong one, so assuage that. Also tweak to look nicer with 80 columns --- share/functions/fish_config.fish | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/share/functions/fish_config.fish b/share/functions/fish_config.fish index b8fb455d6..d4ef02c9a 100644 --- a/share/functions/fish_config.fish +++ b/share/functions/fish_config.fish @@ -7,10 +7,11 @@ function fish_config --description "Launch fish's web based configuration" else if command -sq python python "$__fish_data_dir/tools/web_config/webconfig.py" $argv else - echo (set_color $fish_color_error)Cannot launch the web configuration tool.(set_color normal) + echo (set_color $fish_color_error)Cannot launch the web configuration tool:(set_color normal) echo (set_color -o)fish_config(set_color normal) requires Python. - echo Installing Python will fix this, and also enable completions to be automatically generated from man pages.\n - echo To configure your prompt, create a (set_color -o)fish_prompt(set_color normal) function. + echo Installing python2 or python3 will fix this, and also enable completions to be + echo automatically generated from man pages.\n + echo To change your prompt, create a (set_color -o)fish_prompt(set_color normal) function. echo There are examples in (set_color $fish_color_valid_path)$__fish_data_dir/tools/web_config/sample_prompts(set_color normal).\n echo You can tweak your colors by setting the (set_color $fish_color_search_match)\$fish_color_\*(set_color normal) variables. end From e462c6fe0ef6821cd0ff8377fa7b867e0d255d27 Mon Sep 17 00:00:00 2001 From: John McKay <34872500+McKayJT@users.noreply.github.com> Date: Wed, 9 Jan 2019 23:07:09 +0000 Subject: [PATCH 266/439] print --help to stdout like other builtins (#5495) --- src/builtin_string.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index bbc7e40c0..8e7777111 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -1305,7 +1305,7 @@ int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } if (wcscmp(argv[1], L"-h") == 0 || wcscmp(argv[1], L"--help") == 0) { - builtin_print_help(parser, streams, L"string", streams.err); + builtin_print_help(parser, streams, L"string", streams.out); return STATUS_CMD_OK; } From a17e6fa4e8580c29435dfe1edcdf46b2e2fa5eb0 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 23 Jan 2019 22:14:21 +0100 Subject: [PATCH 267/439] tests/invocation: Remove one more `echo -n` This one was purely cosmetic in the runner output. --- tests/invocation.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/invocation.sh b/tests/invocation.sh index 831157208..ade86da2b 100755 --- a/tests/invocation.sh +++ b/tests/invocation.sh @@ -199,7 +199,7 @@ test_file() { rm -f "${temp_dir}/home/fish/config.fish" fi - echo -n "Testing file $file ${system_specific:+($system_name specific) }... " + echo "Testing file $file ${system_specific:+($system_name specific) }... " # The hoops we are jumping through here, with changing directory are # so that we always execute fish as './fish', which means that any From 09d0f7741d61a26571d077cb448238a5fd05e97c Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Tue, 22 Jan 2019 21:44:30 -0800 Subject: [PATCH 268/439] set_color: don't set color to black before resetting attributes I was surprised to see: > set_color normal | string escape \e\[30m\e\(B\e\[m I only expected to see a sgr0 here. Cleanup a nearby `else { if (...) {` and comment with a bogus example. --- src/builtin_set_color.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp index f1b11efe4..ae81acbd5 100644 --- a/src/builtin_set_color.cpp +++ b/src/builtin_set_color.cpp @@ -216,21 +216,18 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } if (bgcolor != NULL && bg.is_normal()) { - write_color(rgb_color_t::black(), false /* not is_fg */); writembs_nofail(tparm((char *)exit_attribute_mode)); } if (!fg.is_none()) { if (fg.is_normal() || fg.is_reset()) { - write_color(rgb_color_t::black(), true /* is_fg */); writembs_nofail(tparm((char *)exit_attribute_mode)); - } else { - if (!write_color(fg, true /* is_fg */)) { - // We need to do *something* or the lack of any output messes up - // when the cartesian product here would make "foo" disappear: - // $ echo (set_color foo)bar - set_color(rgb_color_t::reset(), rgb_color_t::none()); - } + } else if (!write_color(fg, true /* is_fg */)) { + // We need to do *something* or the lack of any output + // with a cartesian product here would make "foo" disappear + // on lame terminals: + // $ env TERM=vt100 fish -c 'echo (set_color red)"foo"' + set_color(rgb_color_t::reset(), rgb_color_t::none()); } } From 4e391abe3c739bf68edd6d77deb9bc3b8e324cd8 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Wed, 23 Jan 2019 13:39:16 -0800 Subject: [PATCH 269/439] tests/invocation.sh: use printf to omit the newline again --- tests/invocation.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/invocation.sh b/tests/invocation.sh index ade86da2b..5d9664e4c 100755 --- a/tests/invocation.sh +++ b/tests/invocation.sh @@ -199,7 +199,7 @@ test_file() { rm -f "${temp_dir}/home/fish/config.fish" fi - echo "Testing file $file ${system_specific:+($system_name specific) }... " + printf "Testing file $file ${system_specific:+($system_name specific) }... " # The hoops we are jumping through here, with changing directory are # so that we always execute fish as './fish', which means that any From 8079345207321dc3accc71b41d597996a24e47b8 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 23 Jan 2019 22:53:53 +0100 Subject: [PATCH 270/439] tests/invocation.sh: Use a formatting string with printf Using printf like printf "The message" is unsafe, because if the message contains any formatting characters, they'll be interpreted. In this case it's not all that important because the message contains only filenames of our tests and static strings, but still. --- tests/invocation.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/invocation.sh b/tests/invocation.sh index 5d9664e4c..4f1710c4a 100755 --- a/tests/invocation.sh +++ b/tests/invocation.sh @@ -199,7 +199,7 @@ test_file() { rm -f "${temp_dir}/home/fish/config.fish" fi - printf "Testing file $file ${system_specific:+($system_name specific) }... " + printf '%s' "Testing file $file ${system_specific:+($system_name specific) }... " # The hoops we are jumping through here, with changing directory are # so that we always execute fish as './fish', which means that any From 91ac0f1b188d79f8b0d71fd2d50d967ecdd5e3bc Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 23 Jan 2019 17:42:13 -0600 Subject: [PATCH 271/439] Work around for Cygwin's broken job control resolving fish 3.0 hang (cherry picked from commit d1913f0df0e5d05fa553b6d482f1cb71261f22ba) --- cmake/ConfigureChecks.cmake | 5 +++++ config_cmake.h.in | 3 +++ src/common.h | 9 +++++++++ src/proc.cpp | 32 ++++++++++++++++++++++++++++---- 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index 22b0066a0..4313582dc 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -19,6 +19,11 @@ if (CMAKE_HOST_SYSTEM_VERSION MATCHES ".*-Microsoft") SET(WSL 1) endif() +# Detect Cygwin. +if (CMAKE_SYSTEM_NAME MATCHES "CYGWIN_NT.*") + SET(CYGWIN 1) +endif() + # Set up the config.h file. SET(PACKAGE_NAME "fish") SET(PACKAGE_TARNAME "fish") diff --git a/config_cmake.h.in b/config_cmake.h.in index 8071546fd..2c6fde27d 100644 --- a/config_cmake.h.in +++ b/config_cmake.h.in @@ -4,6 +4,9 @@ /* Define to 1 if compiled on WSL */ #cmakedefine WSL 1 +/* Define to 1 if complied under Cygwin */ +#cmakedefine CYGWIN 1 + /* Define to 1 if you have the `clock_gettime' function. */ #cmakedefine HAVE_CLOCK_GETTIME 1 diff --git a/src/common.h b/src/common.h index 49a381f0f..d0436ff59 100644 --- a/src/common.h +++ b/src/common.h @@ -976,6 +976,15 @@ constexpr bool is_windows_subsystem_for_linux() { #endif } +/// Detect if we are running under Cygwin or Cgywin64 +constexpr bool is_cygwin() { +#ifdef CYGWIN + return true; +#else + return false; +#endif +} + extern "C" { __attribute__((noinline)) void debug_thread_error(void); } diff --git a/src/proc.cpp b/src/proc.cpp index e09747f8a..0228d176b 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -447,10 +447,34 @@ static bool process_mark_finished_children(bool block_on_fg) { options &= ~WNOHANG; } - // If the pgid is 0, we need to wait by process because that's invalid. - // This happens in firejail for reasons not entirely clear to me. - bool wait_by_process = !j->job_chain_is_fully_constructed() || j->pgid == 0; - process_list_t::iterator process = j->processes.begin(); + // Child jobs (produced via execution of functions) share job ids with their not-yet- + // fully-constructed parent jobs, so we have to wait on these by individual process id + // and not by the shared pgroup. End result is the same, but it just makes more calls + // to the kernel. + bool wait_by_process = !j->job_chain_is_fully_constructed(); + + // Firejail can result in jobs with pgroup 0, in which case we cannot wait by + // job id. See discussion in #5295. + if (j->pgid == 0) { + wait_by_process = true; + } + + // Cygwin does some voodoo with regards to process management that I do not understand, but + // long story short, we cannot reap processes by their pgroup. The way child processes are + // launched under Cygwin is... weird, and outwardly they do not appear to retain information + // about their parent process when viewed in Task Manager. Waiting on processes by their + // pgroup results in never reaping any, so we just wait on them by process id instead. + if (is_cygwin()) { + wait_by_process = true; + } + + // When waiting on processes individually in a pipeline, we need to enumerate in reverse + // order so that the first process we actually wait on (i.e. ~WNOHANG) is the last process + // in the IO chain, because that's the one that controls the lifetime of the foreground job + // - as long as it is still running, we are in the background and once it exits or is + // killed, all previous jobs in the IO pipeline must necessarily terminate as well. + auto process = j->processes.begin(); + // waitpid(2) returns 1 process each time, we need to keep calling it until we've reaped all // children of the pgrp in question or else we can't reset the dirty_state flag. In all // cases, calling waitpid(2) is faster than potentially calling select_try() on a process From a5ef1e395e6016a145dbbdd72e175a50a3a2004f Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 21 Jan 2019 20:06:16 -0600 Subject: [PATCH 272/439] Use standard __CYGWIN__ define for Cygwin detection (cherry picked from commit 462cb6044c5c2f78cf7e23c52af495c6f5e33d91) --- cmake/ConfigureChecks.cmake | 5 ----- config_cmake.h.in | 3 --- src/common.h | 2 +- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index 4313582dc..22b0066a0 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -19,11 +19,6 @@ if (CMAKE_HOST_SYSTEM_VERSION MATCHES ".*-Microsoft") SET(WSL 1) endif() -# Detect Cygwin. -if (CMAKE_SYSTEM_NAME MATCHES "CYGWIN_NT.*") - SET(CYGWIN 1) -endif() - # Set up the config.h file. SET(PACKAGE_NAME "fish") SET(PACKAGE_TARNAME "fish") diff --git a/config_cmake.h.in b/config_cmake.h.in index 2c6fde27d..8071546fd 100644 --- a/config_cmake.h.in +++ b/config_cmake.h.in @@ -4,9 +4,6 @@ /* Define to 1 if compiled on WSL */ #cmakedefine WSL 1 -/* Define to 1 if complied under Cygwin */ -#cmakedefine CYGWIN 1 - /* Define to 1 if you have the `clock_gettime' function. */ #cmakedefine HAVE_CLOCK_GETTIME 1 diff --git a/src/common.h b/src/common.h index d0436ff59..f0a4be570 100644 --- a/src/common.h +++ b/src/common.h @@ -978,7 +978,7 @@ constexpr bool is_windows_subsystem_for_linux() { /// Detect if we are running under Cygwin or Cgywin64 constexpr bool is_cygwin() { -#ifdef CYGWIN +#ifdef __CYGWIN__ return true; #else return false; From d651cdacbe2e5ca1608ec89730664a90257187c4 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 16 Jan 2019 17:29:43 -0600 Subject: [PATCH 273/439] Extend `type --path` to print path to script defining named function Expands the utility of `type -p foo` by allowing it to print the path to the script that defines `foo` when `foo` is a valid function that was sourced from a path on disk (rather than interactively defined). This does not change the behavior of `type -P`/`type --force-path`, which should have already been used if the desire was to resolve the path to an executable file (otherwise the output would have been blank if a function was shadowing an executable file of the same namea), so no backwards compatibility issues are expected. --- share/functions/type.fish | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/share/functions/type.fish b/share/functions/type.fish index b594aa3d0..e779739bc 100644 --- a/share/functions/type.fish +++ b/share/functions/type.fish @@ -56,6 +56,15 @@ function type --description 'Print the type of a command' end case type echo (_ 'function') + case path + set -l func_path (functions --details $i) + switch $func_path + case "n/a" + case "stdin" + break; + case "*" + echo $func_path + end end if test $multi != yes continue From 5adc07bf2840d1a1aaa0fe4f7d3997970ee0d5f8 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 16 Jan 2019 17:36:42 -0600 Subject: [PATCH 274/439] Document new `type -p` support for functions --- doc_src/type.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/type.txt b/doc_src/type.txt index f56ab8919..13a0836d3 100644 --- a/doc_src/type.txt +++ b/doc_src/type.txt @@ -17,7 +17,7 @@ The following options are available: - `-t` or `--type` prints `function`, `builtin`, or `file` if `NAME` is a shell function, builtin, or disk file, respectively. -- `-p` or `--path` returns the name of the disk file that would be executed, or nothing if `type -t name` would not return `file`. +- `-p` or `--path` prints the path to `NAME` if `NAME` resolves to an executable file in $PATH, the path to the script containing the definition of the function `NAME` if `NAME` resolves to a function loaded from a file on disk (i.e. not interactively defined at the prompt), or nothing otherwise. - `-P` or `--force-path` returns the path to the executable file `NAME`, presuming `NAME` is found in $PATH, or nothing otherwise. `--force-path` explicitly resolves only the path to executable files in $PATH, regardless of whether `$NAME` is shadowed by a function or builtin with the same name. From e9f0a8cf2cfd14ab4949db98f662233921b6f771 Mon Sep 17 00:00:00 2001 From: wyahiro Date: Thu, 3 Jan 2019 23:03:22 +0900 Subject: [PATCH 275/439] Redo `ant` completions Closes #5475 --- share/completions/ant.fish | 33 ++++++++ .../__fish_complete_ant_targets.fish | 76 ++++++++++++++++--- .../functions/__fish_filter_ant_targets.fish | 3 - 3 files changed, 97 insertions(+), 15 deletions(-) delete mode 100644 share/functions/__fish_filter_ant_targets.fish diff --git a/share/completions/ant.fish b/share/completions/ant.fish index a5448c4a6..82b82a7a0 100644 --- a/share/completions/ant.fish +++ b/share/completions/ant.fish @@ -1,3 +1,36 @@ +# Apache Ant (1.9.5) completion for Fish Shell. +# completion for ant targets complete -x -c ant -a "(__fish_complete_ant_targets)" +# Script Options: +complete -f -c ant -l help -l h -d 'print help message and ant help' +complete -f -c ant -l noconfig -d 'suppress sourcing of /etc/ant.conf, $HOME/.ant/ant.conf, and $HOME/.antrc configuration files' +complete -f -c ant -l usejikes -d 'enable use of jikes by default, unless set explicitly in configuration files' +complete -f -c ant -l execdebug -d 'print ant exec line generated by this launch script' +# Options: +complete -f -c ant -o help -s h -d 'print help message and exit' +complete -f -c ant -o projecthelp -s p -d 'print project help information and exit' +complete -f -c ant -o version -d 'print the version information and exit' +complete -f -c ant -o diagnostics -d 'print information that might be helpful to diagnose or report problems and exit' +complete -f -c ant -o quiet -s q -d 'be extra quiet' +complete -f -c ant -o silent -s S -d 'print nothing but task outputs and build failures' +complete -f -c ant -o verbose -s v -d 'be extra verbose' +complete -f -c ant -o debug -s d -d 'print debugging information' +complete -f -c ant -o emacs -s e -d 'produce logging information without adornments' +complete -f -c ant -o noinput -d 'do not allow interactive input' +complete -f -c ant -s D -d 'use value for given property like -D=' +complete -f -c ant -o keep-going -s k -d 'execute all targets that do not depend on failed target(s)' +complete -f -c ant -o nouserlib -d 'Run ant without using the jar files from ${user.home}/.ant/lib' +complete -f -c ant -o noclasspath -d 'Run ant without using CLASSPATH' +complete -f -c ant -o autoproxy -d 'Java1.5+: use the OS proxy settings' +complete -r -c ant -o lib -d 'specifies a path to search for jars and classes' +complete -r -c ant -o logfile -s l -d 'use given file for log' +complete -r -c ant -o logger -d 'the class which is to perform logging' +complete -r -c ant -o listener -d 'add an instance of class as a project listener' +complete -r -c ant -o buildfile -o file -s f -d 'use given buildfile' +complete -r -c ant -o propertyfile -d 'load all properties from file with -D properties taking precedence' +complete -r -c ant -o inputhandler -d 'the class which will handle input requests' +complete -r -c ant -o find -s s -d '(s)earch for buildfile towards the root of the filesystem and use it' +complete -r -c ant -o nice -d 'A niceness value for the main thread: 1 (lowest) to 10 (highest); 5 is the default' +complete -r -c ant -o main -d 'override Ant\'s normal entry point' diff --git a/share/functions/__fish_complete_ant_targets.fish b/share/functions/__fish_complete_ant_targets.fish index e93b855c6..f7324006c 100644 --- a/share/functions/__fish_complete_ant_targets.fish +++ b/share/functions/__fish_complete_ant_targets.fish @@ -1,16 +1,68 @@ function __fish_complete_ant_targets -d "Print list of targets from build.xml and imported files" - set -l buildfile "build.xml" - if test -f $buildfile - # show ant targets - __fish_filter_ant_targets $buildfile - - # find files with buildfile - set files (sed -n "s/^.*]* file=[\"']\([^\"']*\)[\"'].*\$/\1/p" < $buildfile) - - # iterate through files and display their targets - for file in $files - - __fish_filter_ant_targets $file + function __filter_xml_start_tag -d "Filter xml start-tags in a buildfile" + set -l buildfile $argv[1] # full path to buildfile + set -l tag_pattern $argv[2] # regex pattern for tagname + # regex to filter start-tags ignoring newlines and '>' in attr values + # https://www.debuggex.com/r/wRgxHE1yTIgnjfNz + string join ' ' <$buildfile | string match -ar "<(?:$tag_pattern)(?:[^>\"']*?(?:(?:'[^']*?')|(?:\"[^\"]*\"))?)*?>" + end + function __filter_xml_attr_value -d "Filter xml attr value in a start-tag" + set -l tag $argv[1] # start-tag + set -l attr $argv[2] # attr name + # regex to filter attr values ignoring (single|double) quotes in attr values + # https://www.debuggex.com/r/x7lhtLJSP4msleik + string replace -rf "^.*$attr=((?:'(?:.*?)')|(?:\"(?:.*?)\")).*\$" '$1' $tag | string trim -c='"\'' + end + function __get_buildfile -d "Get a buildfile that will be used by ant" + set -l tokens $argv # tokens from 'commandline -co' + set -l prev $tokens[1] + set -l buildfile "build.xml" + for token in $argv[2..-1] + switch $prev + case -buildfile -file -f + set buildfile (eval echo $token) + end + set prev $token + end + # return last one + echo $buildfile + end + function __parse_ant_targets -d "Parse ant targets in the given build file" + set -l buildfile $argv[1] # full path to buildfile + set -l targets (__filter_xml_start_tag $buildfile 'target|extension-point') + for target in $targets + set -l target_name (__filter_xml_attr_value $target 'name') + if [ $status -eq 0 ] + set -l target_description (__filter_xml_attr_value $target 'description') + if [ $status -eq 0 ] + echo $target_name\t$target_description + else + echo $target_name + end + end end end + function __get_ant_targets -d "Get ant targets recursively" + set -l buildfile $argv[1] # full path to buildfile + __parse_ant_targets $buildfile + + set -l basedir (string split -r -m 1 / $buildfile)[1] + set -l imports (__filter_xml_start_tag $buildfile 'import') + for import in $imports + set -l filepath (__filter_xml_attr_value $import 'file') + # Set basedir if $filepath is not a full path + if string match -rvq '^/.*' $filepath + set filename $basedir/$filepath + end + if [ -f $filepath ] + __get_ant_targets $filepath + end + end + end + + set -l tokens (commandline -co) + set -l buildfile (realpath -eq $buildfile (__get_buildfile $tokens)) + if [ $status -eq 0 ] + __get_ant_targets $buildfile + end end diff --git a/share/functions/__fish_filter_ant_targets.fish b/share/functions/__fish_filter_ant_targets.fish deleted file mode 100644 index 242cdb0bc..000000000 --- a/share/functions/__fish_filter_ant_targets.fish +++ /dev/null @@ -1,3 +0,0 @@ -function __fish_filter_ant_targets -d "Display targets within an ant build.xml file" - sed -n "s/^.*]* name=[\"']\([^\"']*\)[\"'].*\$/\1/p" <$argv[1] -end From 4567d3ae521cfebf13f8c33c6621c05639c79f18 Mon Sep 17 00:00:00 2001 From: Matan Kushner Date: Wed, 23 Jan 2019 20:34:28 -0500 Subject: [PATCH 276/439] Fix typo setting fish_color_error (#5577) --- share/functions/__fish_config_interactive.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 926c453d0..f58ba1faa 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -46,7 +46,7 @@ function __fish_config_interactive -d "Initializations that should be performed set -q fish_color_param || set -U fish_color_param 00afff set -q fish_color_redirection || set -U fish_color_redirection 00afff set -q fish_color_comment || set -U fish_color_comment 990000 - set -q fish_color_errorset -U fish_color_error ff0000 + set -q fish_color_error || set -U fish_color_error ff0000 set -q fish_color_escape || set -U fish_color_escape 00a6b2 set -q fish_color_operator || set -U fish_color_operator 00a6b2 set -q fish_color_end || set -U fish_color_end 009900 From 290d07a833d3c55654e38a69934e3e022bd968fa Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Thu, 24 Jan 2019 10:42:56 -0800 Subject: [PATCH 277/439] env.cpp: Better fallback for missing PATH Ask the system where utilities are available with confstr (POSIX). This is the same string printed by `getconf PATH`, which likely includes more directories. --- src/env.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 49a89687d..41af8f9e1 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -649,8 +649,15 @@ static void setup_path() { auto &vars = env_stack_t::globals(); const auto path = vars.get(L"PATH"); if (path.missing_or_empty()) { - wcstring_list_t value({L"/usr/bin", L"/bin"}); - vars.set(L"PATH", ENV_GLOBAL | ENV_EXPORT, value); +#if defined(_CS_PATH) + // _CS_PATH: colon-separated paths to find POSIX utilities + std::string cspath; + cspath.resize(confstr(_CS_PATH, nullptr, 0)); + confstr(_CS_PATH, &cspath[0], cspath.length()); +#else + std::string cspath = "/bin:/usr/bin"; // shouldn't really happen +#endif + vars.set_one(L"PATH", ENV_GLOBAL | ENV_EXPORT, str2wcstring(cspath)); } } From aafefb23006daf4c867561260ff422fc68855cce Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Fri, 25 Jan 2019 13:34:40 -0800 Subject: [PATCH 278/439] Report the guessed/effective emoji width with -d2 on startup This will print out along with the stuff we've guessed about color support. We get a lot of bug reports about these messing up rendering, this is useful diagnostic output. --- src/env.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/env.cpp b/src/env.cpp index 41af8f9e1..ef85cda3d 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -566,8 +566,10 @@ static void guess_emoji_width() { if (term == L"Apple_Terminal" && version >= 400) { // Apple Terminal on High Sierra g_guessed_fish_emoji_width = 2; + debug(2, "default emoji width: 2 for %ls", term.c_str()); } else { g_guessed_fish_emoji_width = 1; + debug(2, "default emoji width: 1"); } } @@ -655,7 +657,7 @@ static void setup_path() { cspath.resize(confstr(_CS_PATH, nullptr, 0)); confstr(_CS_PATH, &cspath[0], cspath.length()); #else - std::string cspath = "/bin:/usr/bin"; // shouldn't really happen + std::string cspath = "/bin:/usr/bin"; // I doubt this is even necessary #endif vars.set_one(L"PATH", ENV_GLOBAL | ENV_EXPORT, str2wcstring(cspath)); } @@ -784,6 +786,7 @@ static void handle_change_emoji_width(const wcstring &op, const wcstring &var_na new_width = fish_wcstol(width_str->as_string().c_str()); } g_fish_emoji_width = std::max(0, new_width); + debug(2, "'fish_emoji_width' preference: %d, overwriting default", g_fish_emoji_width); } static void handle_change_ambiguous_width(const wcstring &op, const wcstring &var_name, From 6ef617f8e7e8c2c1bc534e75c2f94025a1c77eff Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Fri, 25 Jan 2019 17:06:58 -0800 Subject: [PATCH 279/439] tinyexpr: use math.h constants, constexpr --- src/tinyexpr.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 95cc71f3c..2faf4bb9f 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -123,9 +123,10 @@ void te_free(te_expr *n) { } -static double pi() {return 3.14159265358979323846;} -static double e() {return 2.71828182845904523536;} -static double fac(double a) {/* simplest version of fac */ +static constexpr double pi() { return M_PI; } +static constexpr double e() { return M_E; } + +static double fac(double a) { /* simplest version of fac */ if (a < 0.0) return NAN; if (a > UINT_MAX) @@ -198,11 +199,11 @@ static const te_builtin *find_builtin(const char *name, int len) { return NULL; } -static double add(double a, double b) {return a + b;} -static double sub(double a, double b) {return a - b;} -static double mul(double a, double b) {return a * b;} -static double divide(double a, double b) {return a / b;} -static double negate(double a) {return -a;} +static constexpr double add(double a, double b) {return a + b;} +static constexpr double sub(double a, double b) {return a - b;} +static constexpr double mul(double a, double b) {return a * b;} +static constexpr double divide(double a, double b) {return a / b;} +static constexpr double negate(double a) {return -a;} void next_token(state *s) { s->type = TOK_NULL; From 5c689bb50c9b2cb008356c4e5fe05b5ccff0d136 Mon Sep 17 00:00:00 2001 From: "Hideki Hamada (jakalada)" Date: Fri, 4 Jan 2019 08:00:34 +0900 Subject: [PATCH 280/439] fix `dirh` output with reversed $dirnext --- share/functions/dirh.fish | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/share/functions/dirh.fish b/share/functions/dirh.fish index 60c205cb2..111a76a83 100644 --- a/share/functions/dirh.fish +++ b/share/functions/dirh.fish @@ -22,8 +22,9 @@ function dirh --description "Print the current directory history (the prev and n set -l dirc (count $dirnext) if test $dirc -gt 0 + set -l dirnext_rev $dirnext[-1..1] for i in (seq $dirc) - printf '%2d) %s\n' $i $dirnext[$i] + printf '%2d) %s\n' $i $dirnext_rev[$i] end end echo From 15cf45a2a0e37428aa51e7dc99d571326cd77097 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 19 Jan 2019 17:05:00 +0100 Subject: [PATCH 281/439] git_prompt: Allow overriding informative status via git config If either of the two git config variables: - bash.showDirtyState - bash.showUntrackedFiles is explicitly set to false, we will disable informative status, and fall back on the non-informative version (most likely still with either dirty or untracked files, since we already use the variables for that). These vars are read by the official git prompt, so we use them instead of inventing our own "fish.showInformativeStatus". (Note: This also uses $__fish_git_prompt_showdirtystate and friends, but only when there's nothing set in the repo, and there's really no reason to set those to false if using the informative status) Fixes #5551. [ci skip] --- share/functions/__fish_git_prompt.fish | 38 ++++++++++++++++++-------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index ef4165edc..f039162c5 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -372,16 +372,35 @@ function __fish_git_prompt --description "Prompt function for Git" set -l space "$___fish_git_prompt_color$___fish_git_prompt_char_stateseparator$___fish_git_prompt_color_done" + # Use our variables as defaults, but allow overrides via the local git config. + # That means if neither is set, this stays empty. + # + # So "!= true" or "!= false" are useful tests if you want to do something by default. + set -l dirty (command git config --bool bash.showDirtyState) + if not set -q dirty[1] + set -q __fish_git_prompt_showdirtystate + and set dirty true + end + + set -l untracked (command git config --bool bash.showUntrackedFiles) + if not set -q untracked[1] + set -q __fish_git_prompt_showuntrackedfiles + and set untracked true + end + if test "true" = $inside_worktree + # Use informative status, unless dirty or untracked have been set to false. + # + # This is to allow overrides for the repository. if set -q __fish_git_prompt_show_informative_status + and test "$dirty" != false + and test "$untracked" != false set informative_status "$space"(__fish_git_prompt_informative_status) else - if set -q __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 + # This has to be set explicitly. + if test "$dirty" = true + set w (__fish_git_prompt_dirty) + set i (__fish_git_prompt_staged $sha) end if set -q __fish_git_prompt_showstashstate @@ -389,11 +408,8 @@ function __fish_git_prompt --description "Prompt function for Git" set s $___fish_git_prompt_char_stashstate end - if set -q __fish_git_prompt_showuntrackedfiles - set -l config (command git config --bool bash.showUntrackedFiles) - if test "$config" != false - set u (__fish_git_prompt_untracked) - end + if test "$untracked" != true + set u (__fish_git_prompt_untracked) end end From 354c6b1b6708cedd4b37a75df174bb6558faadf4 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 13:33:15 +0100 Subject: [PATCH 282/439] git_prompt: Read "bash.showInformativeStatus" git config variable This allows disabling _just_ the informative status. We still also use the dirty and untracked variables, but only if informative status hasn't explicitly been enabled. --- share/functions/__fish_git_prompt.fish | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index f039162c5..d835e759b 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -376,6 +376,8 @@ function __fish_git_prompt --description "Prompt function for Git" # That means if neither is set, this stays empty. # # So "!= true" or "!= false" are useful tests if you want to do something by default. + set -l informative (command git config --bool bash.showInformativeStatus) + set -l dirty (command git config --bool bash.showDirtyState) if not set -q dirty[1] set -q __fish_git_prompt_showdirtystate @@ -389,12 +391,16 @@ function __fish_git_prompt --description "Prompt function for Git" end if test "true" = $inside_worktree - # Use informative status, unless dirty or untracked have been set to false. + # Use informative status if it has been enabled locally, or it has been + # enabled globally (via the fish variable) and dirty or untracked are not false. # # This is to allow overrides for the repository. - if set -q __fish_git_prompt_show_informative_status - and test "$dirty" != false - and test "$untracked" != false + if test "$informative" = true + or begin + set -q __fish_git_prompt_show_informative_status + and test "$dirty" != false + and test "$untracked" != false + end set informative_status "$space"(__fish_git_prompt_informative_status) else # This has to be set explicitly. From 1e3a46f42381f5ab28b30d42c8c27c605f085463 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 19:05:56 +0100 Subject: [PATCH 283/439] Remove __fish_bind_test* functions These were only used in the bind completions, so there's no need to keep these two separate. Part of #5279. [ci skip] --- share/completions/bind.fish | 44 ++++++++++++++++++++++++++ share/functions/__fish_bind_test1.fish | 24 -------------- share/functions/__fish_bind_test2.fish | 19 ----------- 3 files changed, 44 insertions(+), 43 deletions(-) delete mode 100644 share/functions/__fish_bind_test1.fish delete mode 100644 share/functions/__fish_bind_test2.fish diff --git a/share/completions/bind.fish b/share/completions/bind.fish index 3a221f8c9..c6153cf8a 100644 --- a/share/completions/bind.fish +++ b/share/completions/bind.fish @@ -1,3 +1,47 @@ +function __fish_bind_test1 + set -l args + set -l use_keys no + for i in (commandline -poc) + switch $i + case -k --k --ke --key + set use_keys yes + + case "-*" + + case "*" + set -a args $i + end + end + + switch $use_keys + case yes + switch (count $args) + case 1 + return 0 + end + end + return 1 +end + +function __fish_bind_test2 + set -l args + for i in (commandline -poc) + switch $i + case "-*" + + case "*" + set -a args $i + end + end + + switch (count $args) + case 2 + return 0 + end + + return 1 + +end complete -c bind -s a -l all -d 'Show unavailable key bindings/erase all bindings' complete -c bind -s e -l erase -d 'Erase mode' diff --git a/share/functions/__fish_bind_test1.fish b/share/functions/__fish_bind_test1.fish deleted file mode 100644 index a636ecd89..000000000 --- a/share/functions/__fish_bind_test1.fish +++ /dev/null @@ -1,24 +0,0 @@ -function __fish_bind_test1 - set -l args - set -l use_keys no - for i in (commandline -poc) - switch $i - case -k --k --ke --key - set use_keys yes - - case "-*" - - case "*" - set -a args $i - end - end - - switch $use_keys - case yes - switch (count $args) - case 1 - return 0 - end - end - return 1 -end diff --git a/share/functions/__fish_bind_test2.fish b/share/functions/__fish_bind_test2.fish deleted file mode 100644 index 74f594fcb..000000000 --- a/share/functions/__fish_bind_test2.fish +++ /dev/null @@ -1,19 +0,0 @@ -function __fish_bind_test2 - set -l args - for i in (commandline -poc) - switch $i - case "-*" - - case "*" - set -a args $i - end - end - - switch (count $args) - case 2 - return 0 - end - - return 1 - -end From 882da75d29cc649698922522d1459e99b531f88a Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 19:08:54 +0100 Subject: [PATCH 284/439] Remove __fish_commandline_test function This was only ever used in the commandline completions, and is equivalent to (a weird example of) __fish_contains_opt. Part of #5279. [ci skip] --- share/completions/commandline.fish | 2 +- share/functions/__fish_commandline_test.fish | 23 -------------------- 2 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 share/functions/__fish_commandline_test.fish diff --git a/share/completions/commandline.fish b/share/completions/commandline.fish index 1a7a62b30..f60f9b774 100644 --- a/share/completions/commandline.fish +++ b/share/completions/commandline.fish @@ -20,4 +20,4 @@ complete -c commandline -s S -l search-mode -d "Return true if performing a hist complete -c commandline -s P -l paging-mode -d "Return true if showing pager content" -complete -c commandline -n __fish_commandline_test -a '(bind --function-names)' -d 'Function name' -x +complete -c commandline -n '__fish_contains_opt -s f function' -a '(bind --function-names)' -d 'Function name' -x diff --git a/share/functions/__fish_commandline_test.fish b/share/functions/__fish_commandline_test.fish deleted file mode 100644 index e89b13f11..000000000 --- a/share/functions/__fish_commandline_test.fish +++ /dev/null @@ -1,23 +0,0 @@ - -function __fish_commandline_test - - set -l is_function no - for i in (commandline -poc) - switch $i - case -f --f --fu --fun --func --funct --functi --functio --function - set is_function yes - - case -- - break - - - end - end - - switch $is_function - case yes - return 0 - end - return 1 - -end From 7ad779ac00c975b3b6e00c1d1f7bc6f35d83d243 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 19:11:27 +0100 Subject: [PATCH 285/439] Remove __fish_can_complete_switches This was only used in __fish_should_complete_switches, and is a tiny helper function that is better integrated directly. Part of #5279. [ci skip] --- share/functions/__fish_can_complete_switches.fish | 14 -------------- .../functions/__fish_should_complete_switches.fish | 6 ++++-- 2 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 share/functions/__fish_can_complete_switches.fish diff --git a/share/functions/__fish_can_complete_switches.fish b/share/functions/__fish_can_complete_switches.fish deleted file mode 100644 index 9354f0892..000000000 --- a/share/functions/__fish_can_complete_switches.fish +++ /dev/null @@ -1,14 +0,0 @@ -# Returns whether it is at all possible (even if not recommended) -# to complete a -s or --long argument. -function __fish_can_complete_switches - # Search backwards - for arg in (commandline -ct)[-1..1] - if test "$arg" = "" - continue - else if not string match -qr -- "^-\S*\$" "$arg" - return 1 - end - end - - return 0 -end diff --git a/share/functions/__fish_should_complete_switches.fish b/share/functions/__fish_should_complete_switches.fish index 876a6a673..2563190fb 100644 --- a/share/functions/__fish_should_complete_switches.fish +++ b/share/functions/__fish_should_complete_switches.fish @@ -1,8 +1,10 @@ # Returns whether we *should* complete a -s or --long argument. # The preference is NOT to do so, i.e. prefer subcommands over switches. function __fish_should_complete_switches - if not __fish_can_complete_switches - return 1 + for arg in (commandline -ct)[-1..1] + if not string match -qr -- "^-\S*\$" "$arg" + return 1 + end end if string match -qr -- "^-" (commandline -ct)[-1] return 0 From 0640e7bae95cf0f2380482d4cca2cd1bed30aa64 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 19:15:08 +0100 Subject: [PATCH 286/439] Move prt-get functions into the completion script These were not used elsewhere. Part of #5279. [ci skip] --- share/completions/prt-get.fish | 42 +++++++++++++++++++ share/functions/__fish_prt_no_subcommand.fish | 11 ----- share/functions/__fish_prt_packages.fish | 4 -- share/functions/__fish_prt_ports.fish | 5 --- share/functions/__fish_prt_use_package.fish | 9 ---- share/functions/__fish_prt_use_port.fish | 9 ---- 6 files changed, 42 insertions(+), 38 deletions(-) delete mode 100644 share/functions/__fish_prt_no_subcommand.fish delete mode 100644 share/functions/__fish_prt_packages.fish delete mode 100644 share/functions/__fish_prt_ports.fish delete mode 100644 share/functions/__fish_prt_use_package.fish delete mode 100644 share/functions/__fish_prt_use_port.fish diff --git a/share/completions/prt-get.fish b/share/completions/prt-get.fish index e689cb6bc..3b7f71580 100644 --- a/share/completions/prt-get.fish +++ b/share/completions/prt-get.fish @@ -1,4 +1,46 @@ #completion for prt-get +# A function to verify if prt-get (the crux package management tool) needs to be completed by a further command + +# a function to obtain a list of installed packages with prt-get +function __fish_prt_packages -d 'Obtain a list of installed packages' + prt-get listinst +end + +# a function to obtain a list of ports with prt-get + +function __fish_prt_ports -d 'Obtain a list of ports' + prt-get list +end + +function __fish_prt_no_subcommand -d 'Test if prt-get has yet to be given the command' + for i in (commandline -opc) + if contains -- $i install depinst grpinst update remove sysup lock unlock listlocked diff quickdiff search dsearch fsearch info path readme depends quickdep dependent deptree dup list printf listinst listorphans isinst current ls cat edit help dumpconfig version cache + return 1 + end + end + return 0 +end + + +# a function to verify if prt-get should have packages as potential completion +function __fish_prt_use_package -d 'Test if prt-get should have packages as potential completion' + for i in (commandline -opc) + if contains -- $i update remove lock unlock current + return 0 + end + end + return 1 +end + +# a function to test if prt-get should have ports as potential completions +function __fish_prt_use_port -d 'Test if prt-get should have ports as potential completion' + for i in (commandline -opc) + if contains -- $i install depinst grpinst diff depends quickdep dependent deptree isinst info path readme ls cat edit + return 0 + end + end + return 1 +end complete -f -c prt-get -n '__fish_prt_use_package' -a '(__fish_prt_packages)' -d 'Package' complete -f -c prt-get -n '__fish_prt_use_port' -a '(__fish_prt_ports)' -d 'Port' diff --git a/share/functions/__fish_prt_no_subcommand.fish b/share/functions/__fish_prt_no_subcommand.fish deleted file mode 100644 index 7a3b8e0b3..000000000 --- a/share/functions/__fish_prt_no_subcommand.fish +++ /dev/null @@ -1,11 +0,0 @@ -# A function to verify if prt-get (the crux package management tool) needs to be completed by a further command - -function __fish_prt_no_subcommand -d 'Test if prt-get has yet to be given the command' - for i in (commandline -opc) - if contains -- $i install depinst grpinst update remove sysup lock unlock listlocked diff quickdiff search dsearch fsearch info path readme depends quickdep dependent deptree dup list printf listinst listorphans isinst current ls cat edit help dumpconfig version cache - return 1 - end - end - return 0 -end - diff --git a/share/functions/__fish_prt_packages.fish b/share/functions/__fish_prt_packages.fish deleted file mode 100644 index 001f732c8..000000000 --- a/share/functions/__fish_prt_packages.fish +++ /dev/null @@ -1,4 +0,0 @@ -# a function to obtain a list of installed packages with prt-get -function __fish_prt_packages -d 'Obtain a list of installed packages' - prt-get listinst -end diff --git a/share/functions/__fish_prt_ports.fish b/share/functions/__fish_prt_ports.fish deleted file mode 100644 index 5024bcd6f..000000000 --- a/share/functions/__fish_prt_ports.fish +++ /dev/null @@ -1,5 +0,0 @@ -# a function to obtain a list of ports with prt-get - -function __fish_prt_ports -d 'Obtain a list of ports' - prt-get list -end diff --git a/share/functions/__fish_prt_use_package.fish b/share/functions/__fish_prt_use_package.fish deleted file mode 100644 index b52d30560..000000000 --- a/share/functions/__fish_prt_use_package.fish +++ /dev/null @@ -1,9 +0,0 @@ -# a function to verify if prt-get should have packages as potential completion -function __fish_prt_use_package -d 'Test if prt-get should have packages as potential completion' - for i in (commandline -opc) - if contains -- $i update remove lock unlock current - return 0 - end - end - return 1 -end diff --git a/share/functions/__fish_prt_use_port.fish b/share/functions/__fish_prt_use_port.fish deleted file mode 100644 index 855c61d73..000000000 --- a/share/functions/__fish_prt_use_port.fish +++ /dev/null @@ -1,9 +0,0 @@ -# a function to test if prt-get should have ports as potential completions -function __fish_prt_use_port -d 'Test if prt-get should have ports as potential completion' - for i in (commandline -opc) - if contains -- $i install depinst grpinst diff depends quickdep dependent deptree isinst info path readme ls cat edit - return 0 - end - end - return 1 -end From b8fd08811cce30ddfc6adc9392ba5b7a83da636f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 19:19:24 +0100 Subject: [PATCH 287/439] functions/__terlar_git_prompt: Stringify [ci skip] --- share/functions/__terlar_git_prompt.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__terlar_git_prompt.fish b/share/functions/__terlar_git_prompt.fish index 1068944fb..617ef944f 100644 --- a/share/functions/__terlar_git_prompt.fish +++ b/share/functions/__terlar_git_prompt.fish @@ -46,7 +46,7 @@ function __terlar_git_prompt --description 'Write out the git prompt' set -l staged for i in $index - if echo $i | grep '^[AMRCD]' >/dev/null + if string match -rq '^[AMRCD]' -- $i set staged 1 end From c775335b2a0e59fd017fe0f9465f8ab6bc02c71c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 19:19:47 +0100 Subject: [PATCH 288/439] functions/__fish_print_cmd_args_without_options: Stringify [ci skip] --- share/functions/__fish_print_cmd_args_without_options.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_print_cmd_args_without_options.fish b/share/functions/__fish_print_cmd_args_without_options.fish index dadabc2a8..b38c224b1 100644 --- a/share/functions/__fish_print_cmd_args_without_options.fish +++ b/share/functions/__fish_print_cmd_args_without_options.fish @@ -1,3 +1,3 @@ function __fish_print_cmd_args_without_options - __fish_print_cmd_args | grep '^[^-]' + __fish_print_cmd_args | string match -re '^[^-]' end From 1cad15b01f33d3138a1a20c78c4b85b3af134f4c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 19:29:25 +0100 Subject: [PATCH 289/439] Stringify a few more completions [ci skip] --- share/completions/iptables.fish | 2 +- share/completions/svn.fish | 4 ++-- share/completions/wicd-cli.fish | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/share/completions/iptables.fish b/share/completions/iptables.fish index c28f184c8..e447b0feb 100644 --- a/share/completions/iptables.fish +++ b/share/completions/iptables.fish @@ -29,7 +29,7 @@ function __fish_iptables_user_chains set tablearg "--table=$table" end # This only works as root, so ignore errors - iptables $tablearg -L 2>/dev/null | grep Chain | while read a b c + iptables $tablearg -L 2>/dev/null | string match '*Chain*' | while read a b c echo $b end end diff --git a/share/completions/svn.fish b/share/completions/svn.fish index a62b87b74..6590ac876 100644 --- a/share/completions/svn.fish +++ b/share/completions/svn.fish @@ -296,12 +296,12 @@ _svn_cmpl_ svn:keywords -a "Id" -d 'A compressed summary of all keywords' # # Completions for the 'relocate' subcommand # -_svn_cmpl_ $relocate -xa '( svn info | grep URL: | cut -d " " -f 2 ) http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)' +_svn_cmpl_ $relocate -xa '( svn info | string match "*URL:*" | cut -d " " -f 2 ) http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)' # # Completions for the 'switch', 'sw' subcommands # -_svn_cmpl_ $switch -l relocate -d 'Relocate via URL-rewriting' -xa '( svn info | grep URL: | cut -d " " -f 2 ) http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)' +_svn_cmpl_ $switch -l relocate -d 'Relocate via URL-rewriting' -xa '( svn info | string match "*URL:*" | cut -d " " -f 2 ) http:// ftp:// svn+ssh:// svn+ssh://(__fish_print_hostnames)' # # Completions for the 'status', 'st' subcommands diff --git a/share/completions/wicd-cli.fish b/share/completions/wicd-cli.fish index 4bb90e9f2..78008fd0f 100644 --- a/share/completions/wicd-cli.fish +++ b/share/completions/wicd-cli.fish @@ -9,9 +9,7 @@ complete -c wicd-cli -s y -l wireless -d 'Perform operation on wireless n complete -c wicd-cli -s z -l wired -d 'Perform operation on wired network' complete -c wicd-cli -s w -l save -d 'Save profile' complete -c wicd-cli -s o -l load-profile -d 'Load profile' -complete -c wicd-cli -s m -l name -d 'Set name for profile to save/load' #-xa '(wicd-cli -zl | sed "/^#/d; s/\S\+\s\+\(\S\+\)/\1/")' -#complete -c wicd-cli -n '__fish_contains_opt wireless -s y' -s n -l network -xa '(wicd-cli -ySl | sed "/^#/d; s/\(\S\+\)\s\+\S\+\s\+\(\S\+\)\s\+\(.\+\)/\1\t\3, \2/")' -#complete -c wicd-cli -n '__fish_contains_opt wired -s z' -s n -l network -xa '(wicd-cli -zl | grep -v "^#")' +complete -c wicd-cli -s m -l name -d 'Set name for profile to save/load' complete -c wicd-cli -s p -l network-property -r -d 'Get or set network property' complete -c wicd-cli -s s -l set-to -r -d 'Set network property to' complete -c wicd-cli -s n -l network -d 'Set the network' From a0fbb8dea74e07c3bccf00c73254eec1090598a9 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 7 Mar 2018 11:06:23 +0100 Subject: [PATCH 290/439] Redraw vi cursor if tmux pane focus changes Fixes #4788. --- share/functions/__fish_config_interactive.fish | 14 ++++++++++++++ share/functions/__fish_shared_key_bindings.fish | 10 ++++++++++ share/functions/fish_default_key_bindings.fish | 5 ----- share/functions/fish_vi_cursor.fish | 2 +- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index f58ba1faa..b43d5d3a4 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -234,6 +234,20 @@ function __fish_config_interactive -d "Initializations that should be performed __fish_enable_bracketed_paste end + # Similarly, enable TMUX's focus reporting when in tmux. + # This will be handled by + # - The keybindings (reading the sequence and triggering an event) + # - Any listeners (like the vi-cursor) + if set -q TMUX + function __fish_enable_focus --on-event fish_postexec + echo \e\[\?1004h + end + function __fish_disable_focus --on-event fish_preexec + echo \e\[\?1004l + end + __fish_enable_focus + end + function __fish_winch_handler --on-signal WINCH -d "Repaint screen when window changes size" commandline -f repaint >/dev/null 2>/dev/null end diff --git a/share/functions/__fish_shared_key_bindings.fish b/share/functions/__fish_shared_key_bindings.fish index 0ab3b7827..29d7894a8 100644 --- a/share/functions/__fish_shared_key_bindings.fish +++ b/share/functions/__fish_shared_key_bindings.fish @@ -107,6 +107,16 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod bind --preset $argv \ee edit_command_buffer bind --preset $argv \ev edit_command_buffer + + # Tmux' focus events. + # Exclude paste mode because that should get _everything_ literally. + for mode in (bind --list-modes | string match -v paste) + # We only need the in-focus event currently (to redraw the vi-cursor). + bind -M $mode \e\[I 'emit fish_focus_in' + bind -M $mode \e\[O false + bind -M $mode \e\[\?1004h false + end + # Support for "bracketed paste" # The way it works is that we acknowledge our support by printing # \e\[?2004h diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 941477552..f3ac78349 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -84,11 +84,6 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis bind --preset $argv \ed kill-word - # Ignore some known-bad control sequences - # https://github.com/fish-shell/fish-shell/issues/1917 - bind --preset $argv \e\[I 'begin;end' - bind --preset $argv \e\[O 'begin;end' - # term-specific special bindings switch "$TERM" case 'rxvt*' diff --git a/share/functions/fish_vi_cursor.fish b/share/functions/fish_vi_cursor.fish index d503f934f..27d2474f3 100644 --- a/share/functions/fish_vi_cursor.fish +++ b/share/functions/fish_vi_cursor.fish @@ -107,7 +107,7 @@ function fish_vi_cursor -d 'Set cursor shape for different vi modes' or set -g fish_cursor_unknown block blink echo " - function fish_vi_cursor_handle --on-variable fish_bind_mode --on-event fish_postexec + function fish_vi_cursor_handle --on-variable fish_bind_mode --on-event fish_postexec --on-event fish_focus_in set -l varname fish_cursor_\$fish_bind_mode if not set -q \$varname set varname fish_cursor_unknown From 02628d1b0256d87412a17909c2771f24c6dd6681 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 21:16:50 +0100 Subject: [PATCH 291/439] default_command_not_found_handler: Join arguments Without it, this would print the error multiple times, like Unknown command: echs Unknown command: 1 Unknown command: 2 Unknown command: 3 Fixes #5588. --- share/config.fish | 2 +- tests/invocation/broken-config-continues.err | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/config.fish b/share/config.fish index 18242e06a..e67a1f1e7 100644 --- a/share/config.fish +++ b/share/config.fish @@ -12,7 +12,7 @@ or set -g __fish_added_user_paths # Create the default command_not_found handler # function __fish_default_command_not_found_handler - printf "fish: Unknown command '%s'\n" (string escape -- $argv) >&2 + printf "fish: Unknown command %s\n" (string escape -- "$argv") >&2 end if status --is-interactive diff --git a/tests/invocation/broken-config-continues.err b/tests/invocation/broken-config-continues.err index d313eb7f7..5ab2906b6 100644 --- a/tests/invocation/broken-config-continues.err +++ b/tests/invocation/broken-config-continues.err @@ -1,4 +1,4 @@ -fish: Unknown command 'syntax-error' +fish: Unknown command syntax-error $XDG_CONFIG_HOME/fish/config.fish (line 2): syntax-error ^ From 77b7f5513e240ec8385de3e3157bf2daf99e5628 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 21:16:50 +0100 Subject: [PATCH 292/439] default_command_not_found_handler: Join arguments Without it, this would print the error multiple times, like Unknown command: echs Unknown command: 1 Unknown command: 2 Unknown command: 3 Fixes #5588. --- share/config.fish | 2 +- tests/invocation/broken-config-continues.err | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/config.fish b/share/config.fish index a3abb82f3..3d6ba2417 100644 --- a/share/config.fish +++ b/share/config.fish @@ -12,7 +12,7 @@ or set -g __fish_added_user_paths # Create the default command_not_found handler # function __fish_default_command_not_found_handler - printf "fish: Unknown command '%s'\n" (string escape -- $argv) >&2 + printf "fish: Unknown command %s\n" (string escape -- "$argv") >&2 end if status --is-interactive diff --git a/tests/invocation/broken-config-continues.err b/tests/invocation/broken-config-continues.err index d313eb7f7..5ab2906b6 100644 --- a/tests/invocation/broken-config-continues.err +++ b/tests/invocation/broken-config-continues.err @@ -1,4 +1,4 @@ -fish: Unknown command 'syntax-error' +fish: Unknown command syntax-error $XDG_CONFIG_HOME/fish/config.fish (line 2): syntax-error ^ From 0d0a686ea2f28157f0b3e4c0ed8cb92d5f6202b0 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 21:34:30 +0100 Subject: [PATCH 293/439] CHANGELOG: Update for 3.0.1 This should now contain all closed issues for 3.0.1. [ci skip] --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9474eb3f..565420a05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,20 @@ This release of fish fixes a number of major issues discovered in fish 3.0.0. - (macOS only) /etc/paths and /etc/paths.d now correctly set path entries with spaces. Also affects MANPATH. (#5481) - fish does not hang on launch when running under Cygwin/MSYS2 - The pager-toggle-search binding (by default Control-S) now positions the cursor in the completions list. +- The error when a command is not found is now printed a single time instead of once per argument. (#5588) +- The git completions now print correct file paths instead of ../../../ (and so on) with older git versions directly inside a git root. (#5578) +- The git completions won't suggest :/ paths (relative to the git root) so much anymore. (#5574) +- The git completions will now fuzzy-match paths again. (#5476) +- The git completions will ignore shell aliases, so enterprising users can set up the wrapping command (via `set -g __fish_git_alias_$command $whatitwraps`). (#5412) +- A crash when the user's information can't be read was fixed. (#5550) +- fish no longer crashes when $hostname or some other non-electric read-only variable is used as a loop variable. (#5548) +- The "kill" completions won't invoke the same command 25 times anymore, speeding matters up considerably. (#5541) +- Fish now inherits symlinked paths correctly. (#5525) +- fish_title had a few spaces removed, saving space. (#5517) +- The `nim` prompt now works correctly when chosen in fish_config. (#5490) +- A potential crash in `string match` discovered via GLIBCXX_ASSERTIONS was fixed. (#5479) +- A crash on FreeBSD related to the wcstod_l function was fixed. (#5453) +- An assertion that checked getpid() in a tight loop was removed, increasing performance in some cases up to 40%. (#5447) If you are upgrading from version 2.7.1 or before, please also review the release notes for 3.0.0 and 3.0b1 (included below). From 368787060bb0fde0b740cc81d9c3ca27a73f0dc6 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 21:36:27 +0100 Subject: [PATCH 294/439] CHANGELOG: Add the PRs I missed the PRs without associated issue. [ci skip] --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 565420a05..cd377982e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ This release of fish fixes a number of major issues discovered in fish 3.0.0. - A potential crash in `string match` discovered via GLIBCXX_ASSERTIONS was fixed. (#5479) - A crash on FreeBSD related to the wcstod_l function was fixed. (#5453) - An assertion that checked getpid() in a tight loop was removed, increasing performance in some cases up to 40%. (#5447) +- `string` now prints help to stdout, like other builtins. (#5495) +- The completions for `configure` now correctly offer directories. (#5518) +- The `man` completions won't interpret the argument as a regex anymore. (#5566) If you are upgrading from version 2.7.1 or before, please also review the release notes for 3.0.0 and 3.0b1 (included below). From f01e8d9afe2fb7082d16c6fefba0db4e984bf14f Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 21:34:30 +0100 Subject: [PATCH 295/439] CHANGELOG: Update for 3.0.1 This should now contain all closed issues for 3.0.1. [ci skip] --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31fab2284..5c09f4331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,20 @@ - while loops now evaluate to the last executed command in the loop body (or zero if the body was empty), matching POSIX semantics. - fish does not hang on launch when running under Cygwin/MSYS2 - The pager-toggle-search binding (by default Control-S) now positions the cursor in the completions list. +- The error when a command is not found is now printed a single time instead of once per argument. (#5588) +- The git completions now print correct file paths instead of ../../../ (and so on) with older git versions directly inside a git root. (#5578) +- The git completions won't suggest :/ paths (relative to the git root) so much anymore. (#5574) +- The git completions will now fuzzy-match paths again. (#5476) +- The git completions will ignore shell aliases, so enterprising users can set up the wrapping command (via `set -g __fish_git_alias_$command $whatitwraps`). (#5412) +- A crash when the user's information can't be read was fixed. (#5550) +- fish no longer crashes when $hostname or some other non-electric read-only variable is used as a loop variable. (#5548) +- The "kill" completions won't invoke the same command 25 times anymore, speeding matters up considerably. (#5541) +- Fish now inherits symlinked paths correctly. (#5525) +- fish_title had a few spaces removed, saving space. (#5517) +- The `nim` prompt now works correctly when chosen in fish_config. (#5490) +- A potential crash in `string match` discovered via GLIBCXX_ASSERTIONS was fixed. (#5479) +- A crash on FreeBSD related to the wcstod_l function was fixed. (#5453) +- An assertion that checked getpid() in a tight loop was removed, increasing performance in some cases up to 40%. (#5447) # fish 3.0.0 (released December 28, 2018) From 6b060a54e31abb13f1ba824382db5ebc64603628 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 21:36:27 +0100 Subject: [PATCH 296/439] CHANGELOG: Add the PRs I missed the PRs without associated issue. [ci skip] --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c09f4331..7f91f26f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,9 @@ - A potential crash in `string match` discovered via GLIBCXX_ASSERTIONS was fixed. (#5479) - A crash on FreeBSD related to the wcstod_l function was fixed. (#5453) - An assertion that checked getpid() in a tight loop was removed, increasing performance in some cases up to 40%. (#5447) +- `string` now prints help to stdout, like other builtins. (#5495) +- The completions for `configure` now correctly offer directories. (#5518) +- The `man` completions won't interpret the argument as a regex anymore. (#5566) # fish 3.0.0 (released December 28, 2018) From 63c072e22503faa248bd31851311ef5c27c66de2 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 21:50:49 +0100 Subject: [PATCH 297/439] default_command_not_found_handler: Only use $argv[1] That's probably the nicer fix, otherwise this would print things like Unknown command 'aiohsd 1 2 3' when it should just say Unknown command aiohsd --- share/config.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/config.fish b/share/config.fish index e67a1f1e7..90055f1ef 100644 --- a/share/config.fish +++ b/share/config.fish @@ -12,7 +12,7 @@ or set -g __fish_added_user_paths # Create the default command_not_found handler # function __fish_default_command_not_found_handler - printf "fish: Unknown command %s\n" (string escape -- "$argv") >&2 + printf "fish: Unknown command %s\n" (string escape -- $argv[1]) >&2 end if status --is-interactive From 28ee5716fb5e7caed55d0c7047a1f9cbe1f0228e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 21:50:49 +0100 Subject: [PATCH 298/439] default_command_not_found_handler: Only use $argv[1] That's probably the nicer fix, otherwise this would print things like Unknown command 'aiohsd 1 2 3' when it should just say Unknown command aiohsd --- share/config.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/config.fish b/share/config.fish index 3d6ba2417..53fed8e35 100644 --- a/share/config.fish +++ b/share/config.fish @@ -12,7 +12,7 @@ or set -g __fish_added_user_paths # Create the default command_not_found handler # function __fish_default_command_not_found_handler - printf "fish: Unknown command %s\n" (string escape -- "$argv") >&2 + printf "fish: Unknown command %s\n" (string escape -- $argv[1]) >&2 end if status --is-interactive From f73b4fb7465f2c5f2b23e257adde87f611c96e61 Mon Sep 17 00:00:00 2001 From: Dan Zimmerman Date: Sun, 13 Jan 2019 00:35:12 -0500 Subject: [PATCH 299/439] Connect highlight env vars to their specs better I was hacking on this part of the codebase and found this comment mentioning to keep two things in sync, and felt like we could do better. --- src/highlight.cpp | 44 ++++++++++++++++++++++---------------------- src/highlight.h | 3 +++ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/highlight.cpp b/src/highlight.cpp index 9b844f89f..920f66b4c 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -41,28 +41,28 @@ namespace g = grammar; /// 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", - 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"}; +static const wchar_t *const highlight_var[] = { + [highlight_spec_normal] = L"fish_color_normal", + [highlight_spec_error] = L"fish_color_error", + [highlight_spec_command] = L"fish_color_command", + [highlight_spec_statement_terminator] = L"fish_color_end", + [highlight_spec_param] = L"fish_color_param", + [highlight_spec_comment] = L"fish_color_comment", + [highlight_spec_match] = L"fish_color_match", + [highlight_spec_search_match] = L"fish_color_search_match", + [highlight_spec_operator] = L"fish_color_operator", + [highlight_spec_escape] = L"fish_color_escape", + [highlight_spec_quote] = L"fish_color_quote", + [highlight_spec_redirection] = L"fish_color_redirection", + [highlight_spec_autosuggestion] = L"fish_color_autosuggestion", + [highlight_spec_selection] = L"fish_color_selection", + [highlight_spec_pager_prefix] = L"fish_pager_color_prefix", + [highlight_spec_pager_completion] = L"fish_pager_color_completion", + [highlight_spec_pager_description] = L"fish_pager_color_description", + [highlight_spec_pager_progress] = L"fish_pager_color_progress", + [highlight_spec_pager_secondary] = L"fish_pager_color_secondary", +}; +static_assert(VAR_COUNT == HIGHLIGHT_SPEC_MAX, "Every color spec has a corresponding env var"); /// Determine if the filesystem containing the given fd is case insensitive for lookups regardless /// of whether it preserves the case when saving a pathname. diff --git a/src/highlight.h b/src/highlight.h index a7aa66bcb..248d71af6 100644 --- a/src/highlight.h +++ b/src/highlight.h @@ -39,6 +39,9 @@ enum { highlight_spec_pager_progress, highlight_spec_pager_secondary, + // Used to double check a data structure in highlight.cpp + HIGHLIGHT_SPEC_MAX, + HIGHLIGHT_SPEC_PRIMARY_MASK = 0xFF, // The following values are modifiers. From 50448e4319007d71e4a160bfe440e9db11b0e4f1 Mon Sep 17 00:00:00 2001 From: Dan Zimmerman Date: Sun, 13 Jan 2019 02:16:30 -0500 Subject: [PATCH 300/439] Enable configuring more pager colors Originally I sought out to configure the foreground color of the selected text in the pager. After reading a thread on a github issue I was inpired to do more: now you can conifgure any part of the pager when selected, and when a row is secondary. More specifically this commit adds the ability to specify a pager row's: - Prefix - Completion text - Description - Background when said row is selected or secondary. --- doc_src/index.hdr.in | 20 +++++++++-- src/highlight.cpp | 80 +++++++++++++++++++++++++++++++++----------- src/highlight.h | 13 +++++-- src/pager.cpp | 40 +++++++++++----------- 4 files changed, 110 insertions(+), 43 deletions(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index aab92b398..71c74ccb7 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -1026,15 +1026,31 @@ The following variables are available to change the highlighting colors in fish: Additionally, the following variables are available to change the highlighting in the completion pager: +- `fish_pager_color_progress`, the color of the progress bar at the bottom left corner + +- `fish_pager_color_background`, the background color of a line + - `fish_pager_color_prefix`, the color of the prefix string, i.e. the string that is to be completed - `fish_pager_color_completion`, the color of the completion itself - `fish_pager_color_description`, the color of the completion description -- `fish_pager_color_progress`, the color of the progress bar at the bottom left corner +- `fish_pager_color_secondary_background`, `fish_pager_color_background` of every second unselected completion. Defaults to `fish_pager_color_background` -- `fish_pager_color_secondary`, the background color of the every second completion +- `fish_pager_color_secondary_ prefix`, `fish_pager_color_prefix` of every second unselected completion. Defaults to `fish_pager_color_prefix` + +- `fish_pager_color_secondary_completion`, `fish_pager_color_completion` of every second unselected completion. Defaults to `fish_pager_color_completion` + +- `fish_pager_color_secondary_description`, `fish_pager_color_description` of every second unselected completion. Defaults to `fish_pager_color_description` + +- `fish_pager_color_selected_background`, `fish_pager_color_background` of the selected completion. Defaults to `fish_color_search_match` + +- `fish_pager_color_selected_ prefix`, `fish_pager_color_prefix` of the selected completion. Defaults to `fish_pager_color_prefix` + +- `fish_pager_color_selected_completion`, `fish_pager_color_completion` of the selected completion. Defaults to `fish_pager_color_completion` + +- `fish_pager_color_selected_description`, `fish_pager_color_description` of the selected completion. Defaults to `fish_pager_color_description` Example: diff --git a/src/highlight.cpp b/src/highlight.cpp index 920f66b4c..cf20ba5d8 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -42,28 +42,69 @@ namespace g = grammar; /// Number of elements in the highlight_var array. #define VAR_COUNT (sizeof(highlight_var) / sizeof(wchar_t *)) static const wchar_t *const highlight_var[] = { - [highlight_spec_normal] = L"fish_color_normal", - [highlight_spec_error] = L"fish_color_error", - [highlight_spec_command] = L"fish_color_command", - [highlight_spec_statement_terminator] = L"fish_color_end", - [highlight_spec_param] = L"fish_color_param", - [highlight_spec_comment] = L"fish_color_comment", - [highlight_spec_match] = L"fish_color_match", - [highlight_spec_search_match] = L"fish_color_search_match", - [highlight_spec_operator] = L"fish_color_operator", - [highlight_spec_escape] = L"fish_color_escape", - [highlight_spec_quote] = L"fish_color_quote", - [highlight_spec_redirection] = L"fish_color_redirection", - [highlight_spec_autosuggestion] = L"fish_color_autosuggestion", - [highlight_spec_selection] = L"fish_color_selection", - [highlight_spec_pager_prefix] = L"fish_pager_color_prefix", - [highlight_spec_pager_completion] = L"fish_pager_color_completion", - [highlight_spec_pager_description] = L"fish_pager_color_description", - [highlight_spec_pager_progress] = L"fish_pager_color_progress", - [highlight_spec_pager_secondary] = L"fish_pager_color_secondary", + [highlight_spec_normal] = L"fish_color_normal", + [highlight_spec_error] = L"fish_color_error", + [highlight_spec_command] = L"fish_color_command", + [highlight_spec_statement_terminator] = L"fish_color_end", + [highlight_spec_param] = L"fish_color_param", + [highlight_spec_comment] = L"fish_color_comment", + [highlight_spec_match] = L"fish_color_match", + [highlight_spec_search_match] = L"fish_color_search_match", + [highlight_spec_operator] = L"fish_color_operator", + [highlight_spec_escape] = L"fish_color_escape", + [highlight_spec_quote] = L"fish_color_quote", + [highlight_spec_redirection] = L"fish_color_redirection", + [highlight_spec_autosuggestion] = L"fish_color_autosuggestion", + [highlight_spec_selection] = L"fish_color_selection", + [highlight_spec_pager_progress] = L"fish_pager_color_progress", + [highlight_spec_pager_background] = L"fish_pager_color_background", + [highlight_spec_pager_prefix] = L"fish_pager_color_prefix", + [highlight_spec_pager_completion] = L"fish_pager_color_completion", + [highlight_spec_pager_description] = L"fish_pager_color_description", + [highlight_spec_pager_secondary_background] = L"fish_pager_color_secondary_background", + [highlight_spec_pager_secondary_prefix] = L"fish_pager_color_secondary_prefix", + [highlight_spec_pager_secondary_completion] = L"fish_pager_color_secondary_completion", + [highlight_spec_pager_secondary_description] = L"fish_pager_color_secondary_description", + [highlight_spec_pager_selected_background] = L"fish_pager_color_selected_background", + [highlight_spec_pager_selected_prefix] = L"fish_pager_color_selected_prefix", + [highlight_spec_pager_selected_completion] = L"fish_pager_color_selected_completion", + [highlight_spec_pager_selected_description] = L"fish_pager_color_selected_description", }; static_assert(VAR_COUNT == HIGHLIGHT_SPEC_MAX, "Every color spec has a corresponding env var"); +// Table used to fetch fallback highlights in case the specified one +// wasn't set. +static const highlight_spec_t fallbacks[] = { + [highlight_spec_normal] = highlight_spec_normal, + [highlight_spec_error] = highlight_spec_normal, + [highlight_spec_command] = highlight_spec_normal, + [highlight_spec_statement_terminator] = highlight_spec_normal, + [highlight_spec_param] = highlight_spec_normal, + [highlight_spec_comment] = highlight_spec_normal, + [highlight_spec_match] = highlight_spec_normal, + [highlight_spec_search_match] = highlight_spec_normal, + [highlight_spec_operator] = highlight_spec_normal, + [highlight_spec_escape] = highlight_spec_normal, + [highlight_spec_quote] = highlight_spec_normal, + [highlight_spec_redirection] = highlight_spec_normal, + [highlight_spec_autosuggestion] = highlight_spec_normal, + [highlight_spec_selection] = highlight_spec_normal, + [highlight_spec_pager_progress] = highlight_spec_normal, + [highlight_spec_pager_background] = highlight_spec_normal, + [highlight_spec_pager_prefix] = highlight_spec_normal, + [highlight_spec_pager_completion] = highlight_spec_normal, + [highlight_spec_pager_description] = highlight_spec_normal, + [highlight_spec_pager_secondary_background] = highlight_spec_pager_background, + [highlight_spec_pager_secondary_prefix] = highlight_spec_pager_prefix, + [highlight_spec_pager_secondary_completion] = highlight_spec_pager_completion, + [highlight_spec_pager_secondary_description] = highlight_spec_pager_description, + [highlight_spec_pager_selected_background] = highlight_spec_search_match, + [highlight_spec_pager_selected_prefix] = highlight_spec_pager_prefix, + [highlight_spec_pager_selected_completion] = highlight_spec_pager_completion, + [highlight_spec_pager_selected_description] = highlight_spec_pager_description, +}; +static_assert(sizeof(fallbacks) / sizeof(fallbacks[0]) == HIGHLIGHT_SPEC_MAX, "No missing fallbacks"); + /// Determine if the filesystem containing the given fd is case insensitive for lookups regardless /// of whether it preserves the case when saving a pathname. /// @@ -270,6 +311,7 @@ rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background) // debug( 1, L"%d -> %d -> %ls", highlight, idx, val ); + if (!var) var = vars.get(highlight_var[fallbacks[idx]]); if (!var) var = vars.get(highlight_var[0]); if (var) result = parse_color(*var, treat_as_background); diff --git a/src/highlight.h b/src/highlight.h index 248d71af6..57c1f947a 100644 --- a/src/highlight.h +++ b/src/highlight.h @@ -33,11 +33,20 @@ enum { highlight_spec_selection, // Pager support. + // NOTE: pager.cpp relies on these being in this order. + highlight_spec_pager_progress, + highlight_spec_pager_background, highlight_spec_pager_prefix, highlight_spec_pager_completion, highlight_spec_pager_description, - highlight_spec_pager_progress, - highlight_spec_pager_secondary, + highlight_spec_pager_secondary_background, + highlight_spec_pager_secondary_prefix, + highlight_spec_pager_secondary_completion, + highlight_spec_pager_secondary_description, + highlight_spec_pager_selected_background, + highlight_spec_pager_selected_prefix, + highlight_spec_pager_selected_completion, + highlight_spec_pager_selected_description, // Used to double check a data structure in highlight.cpp HIGHLIGHT_SPEC_MAX, diff --git a/src/pager.cpp b/src/pager.cpp index 52da3581b..548a67223 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -137,18 +137,24 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s assert(comp_width <= width); } - int bg_color = secondary ? highlight_spec_pager_secondary : highlight_spec_normal; - if (selected) { - bg_color = highlight_spec_search_match; - } + int offset = selected + ? (highlight_spec_pager_selected_background - highlight_spec_pager_background) + : (secondary + ? (highlight_spec_pager_secondary_background - highlight_spec_pager_background) + : 0); + highlight_spec_t bg_color = highlight_spec_pager_background + offset; + highlight_spec_t prefix_fg = highlight_spec_pager_prefix + offset; + highlight_spec_t comp_fg = highlight_spec_pager_completion + offset; + highlight_spec_t desc_fg = highlight_spec_pager_description + offset; auto bg = highlight_make_background(bg_color); + auto prefix_col = prefix_fg | bg; + auto comp_col = comp_fg | bg; + auto desc_col = desc_fg | bg; // Print the completion part size_t comp_remaining = comp_width; for (size_t i = 0; i < c->comp.size(); i++) { const wcstring &comp = c->comp.at(i); - highlight_spec_t packed_color = - highlight_spec_pager_prefix | bg; if (i > 0) { comp_remaining -= print_max(PAGER_SPACER_STRING, bg, comp_remaining, @@ -156,33 +162,27 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s } comp_remaining -= - print_max(prefix, packed_color, comp_remaining, !comp.empty(), &line_data); - - packed_color = highlight_spec_pager_completion | bg; + print_max(prefix, prefix_col, comp_remaining, !comp.empty(), &line_data); comp_remaining -= - print_max(comp, packed_color, comp_remaining, i + 1 < c->comp.size(), &line_data); + print_max(comp, comp_col, comp_remaining, i + 1 < c->comp.size(), &line_data); } size_t desc_remaining = width - comp_width + comp_remaining; if (c->desc_width > 0 && desc_remaining > 4) { - highlight_spec_t desc_color = - highlight_spec_pager_description | bg; - highlight_spec_t punct_color = - highlight_spec_pager_completion | bg; - // always have at least two spaces to separate completion and description - desc_remaining -= print_max(L" ", punct_color, 2, false, &line_data); + desc_remaining -= print_max(L" ", bg, 2, false, &line_data); // right-justify the description by adding spaces // the 2 here refers to the parenthesis below while (desc_remaining > c->desc_width + 2) { - desc_remaining -= print_max(L" ", punct_color, 1, false, &line_data); + desc_remaining -= print_max(L" ", bg, 1, false, &line_data); } assert(desc_remaining >= 2); - desc_remaining -= print_max(L"(", punct_color, 1, false, &line_data); - desc_remaining -= print_max(c->desc, desc_color, desc_remaining - 1, false, &line_data); - desc_remaining -= print_max(L")", punct_color, 1, false, &line_data); + auto paren_col = highlight_spec_pager_completion | bg; + desc_remaining -= print_max(L"(", paren_col, 1, false, &line_data); + desc_remaining -= print_max(c->desc, desc_col, desc_remaining - 1, false, &line_data); + desc_remaining -= print_max(L")", paren_col, 1, false, &line_data); } else { // No description, or it won't fit. Just add spaces. print_max(wcstring(desc_remaining, L' '), bg, desc_remaining, false, &line_data); From 730effa0d990741bb4a23991902bb5eaeedbabff Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 26 Jan 2019 16:32:37 -0800 Subject: [PATCH 301/439] Remove an errant space from the docs --- 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 71c74ccb7..1b5d0d691 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -1046,7 +1046,7 @@ Additionally, the following variables are available to change the highlighting i - `fish_pager_color_selected_background`, `fish_pager_color_background` of the selected completion. Defaults to `fish_color_search_match` -- `fish_pager_color_selected_ prefix`, `fish_pager_color_prefix` of the selected completion. Defaults to `fish_pager_color_prefix` +- `fish_pager_color_selected_prefix`, `fish_pager_color_prefix` of the selected completion. Defaults to `fish_pager_color_prefix` - `fish_pager_color_selected_completion`, `fish_pager_color_completion` of the selected completion. Defaults to `fish_pager_color_completion` From 0047523cf4789ab515fa9821571cddb58e935f3e Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 26 Jan 2019 16:33:30 -0800 Subject: [PATCH 302/439] Relnote new pager color options --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f91f26f8..329760ced 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Lots of improvements to completions. - fish_clipboard_* now supports wayland by means of [wl-clipboard](https://github.com/bugaevc/wl-clipboard). - mandoc can now be used to format the output from `--help` if nroff is not installed +- New color options for the pager have been added (#5524). ======= From dc885d70b3d085fc091557418e6940daa0943314 Mon Sep 17 00:00:00 2001 From: Jeremy <1524722+jerr0328@users.noreply.github.com> Date: Sun, 27 Jan 2019 12:18:55 +0100 Subject: [PATCH 303/439] Rename Fisherman to Fisher in FAQ Fisherman became Fisher and moved to a new repo. The previous link still worked, but the name and URL change was needed to avoid confusion. --- doc_src/faq.hdr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/faq.hdr b/doc_src/faq.hdr index 482c152d9..d0525239e 100644 --- a/doc_src/faq.hdr +++ b/doc_src/faq.hdr @@ -292,7 +292,7 @@ Fish reserves the Unicode The fish user community extends fish in unique and useful ways via scripts that aren't always appropriate for bundling with the fish package. Typically because they solve a niche problem unlikely to appeal to a broad audience. You can find those extensions, including prompts, themes and useful functions, in various third-party repositories. These include: -- Fisherman +- Fisher - Fundle - Oh My Fish - Tacklebox From c776414674feb0cba4a2b3c91d01cded2c821ed7 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 27 Jan 2019 14:20:56 +0100 Subject: [PATCH 304/439] functions/trap: Don't `test` with one argument That's a POSIX misfeature. Also there was one actually wrong `test sig` that should have been `test $sig`. --- share/functions/trap.fish | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/share/functions/trap.fish b/share/functions/trap.fish index 119cdc1c3..fab5cd952 100644 --- a/share/functions/trap.fish +++ b/share/functions/trap.fish @@ -54,7 +54,7 @@ function trap -d 'Perform an action when the shell receives a signal' case clear for i in $argv set sig (__trap_translate_signal $i) - if test $sig + if test -n "$sig" functions -e __trap_handler_$sig end end @@ -67,7 +67,7 @@ function trap -d 'Perform an action when the shell receives a signal' set -l sig (__trap_translate_signal $i) set sw (__trap_switch $sig) - if test $sig + if test -n "$sig" echo "function __trap_handler_$sig $sw; $cmd; end" | source else return 1 @@ -85,7 +85,7 @@ function trap -d 'Perform an action when the shell receives a signal' for i in $names set sig (__trap_translate_signal $i) - if test sig + if test -n "$sig" functions __trap_handler_$i else return 1 From 7eee158292626e4b1d5437d691b3d3fe7bbfa857 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 27 Jan 2019 14:23:36 +0100 Subject: [PATCH 305/439] Don't test with one argument [ci skip] --- share/completions/adb.fish | 2 +- share/tools/web_config/sample_prompts/robbyrussell.fish | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/share/completions/adb.fish b/share/completions/adb.fish index 7684d0ef2..559a3e3bc 100644 --- a/share/completions/adb.fish +++ b/share/completions/adb.fish @@ -24,7 +24,7 @@ function __fish_adb_run_command -d 'Runs adb with any -s parameters already give set -l cmd (commandline -poc) set -e cmd[1] for i in $cmd - if test $sopt_is_next + if test -n "$sopt_is_next" set sopt -s $i break else diff --git a/share/tools/web_config/sample_prompts/robbyrussell.fish b/share/tools/web_config/sample_prompts/robbyrussell.fish index 80a600004..65275ccd9 100644 --- a/share/tools/web_config/sample_prompts/robbyrussell.fish +++ b/share/tools/web_config/sample_prompts/robbyrussell.fish @@ -80,7 +80,8 @@ function fish_prompt set -l repo_branch $red(_repo_branch_name $repo_type) set repo_info "$blue $repo_type:($repo_branch$blue)" - if [ (_is_repo_dirty $repo_type) ] + set -l dirty (_is_repo_dirty $repo_type) + if test -n "$dirty" set -l dirty "$yellow ✗" set repo_info "$repo_info$dirty" end From aa32fc92ac48c46e5715bc83a838695e3e965fe7 Mon Sep 17 00:00:00 2001 From: Siteshwar Vashisht Date: Mon, 28 Jan 2019 03:35:30 +0100 Subject: [PATCH 306/439] packaging: Disable `expect` based tests `expect` based tests fail randomly and can cause build failures. Do not run them while doing package builds. --- debian/control | 2 +- fish.spec.in | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/debian/control b/debian/control index f5624cec4..941edc38b 100644 --- a/debian/control +++ b/debian/control @@ -5,7 +5,7 @@ Maintainer: ridiculous_fish Uploaders: David Adam Build-Depends: debhelper (>= 9.0.0), libncurses5-dev, cmake3 (>= 3.2.0) | cmake (>= 3.2.0), gettext, # Test dependencies - expect, locales-all + locales-all # When libpcre2-dev is available on all supported Debian versions, add a dependency on that. Standards-Version: 3.9.4 Homepage: https://fishshell.com/ diff --git a/fish.spec.in b/fish.spec.in index e6eabacb1..652d75b36 100644 --- a/fish.spec.in +++ b/fish.spec.in @@ -26,7 +26,6 @@ BuildRequires: pcre2-devel %endif # for tests -BuildRequires: expect %if 0%{?fedora} # Need the en_US.utf-8 locale at a minimum BuildRequires: glibc-langpack-en From d8d9cfdc1015476aef7ae6e6c85f8f56c078ec0e Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 28 Jan 2019 21:51:57 +0800 Subject: [PATCH 307/439] interactive tests: exit quietly if expect not available It seems that 44cfe3e34 inadvertently included the requirement for the expect tests to pass; revert this to its original sense. --- tests/interactive.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/interactive.fish b/tests/interactive.fish index 2a0bbc7ad..933749446 100644 --- a/tests/interactive.fish +++ b/tests/interactive.fish @@ -39,7 +39,7 @@ cat interactive.config >>$XDG_CONFIG_HOME/fish/config.fish say -o cyan "Testing interactive functionality" if not type -q expect say red "Tests disabled: `expect` not found" - exit 1 + exit 0 end function test_file From ac3d3c399cbf6973e7be0a3d824ab6eab9dd742a Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 28 Jan 2019 18:10:41 +0100 Subject: [PATCH 308/439] Quit immediately with R_EOF If we read an R_EOF, we'd try to match mappings to it. In emacs mode, that's not an issue because the generic binding was always available, but in vi-normal mode there is no generic binding, so we'd endlessly loop, waiting for another character. Fixes #5528. --- src/input.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/input.cpp b/src/input.cpp index 2fe26049d..f62c11f32 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -515,6 +515,10 @@ wint_t input_readch(bool allow_commands) { } default: { return c; } } + } else if (c == R_EOF) { + // If we have R_EOF, we need to immediately quit. + // There's no need to go through the input functions. + return R_EOF; } else { input_common_next_ch(c); input_mapping_execute_matching_or_generic(allow_commands); From 8feabae1316a6d539d8eda3a17d6a4f2e5355f48 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 28 Jan 2019 18:10:41 +0100 Subject: [PATCH 309/439] Quit immediately with R_EOF If we read an R_EOF, we'd try to match mappings to it. In emacs mode, that's not an issue because the generic binding was always available, but in vi-normal mode there is no generic binding, so we'd endlessly loop, waiting for another character. Fixes #5528. --- src/input.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/input.cpp b/src/input.cpp index 596800167..8b5a94847 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -516,6 +516,10 @@ wint_t input_readch(bool allow_commands) { } default: { return c; } } + } else if (c == R_EOF) { + // If we have R_EOF, we need to immediately quit. + // There's no need to go through the input functions. + return R_EOF; } else { input_common_next_ch(c); input_mapping_execute_matching_or_generic(allow_commands); From a958617425c6be3f1428c1469d0aae960739d1f6 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 28 Jan 2019 21:25:54 +0100 Subject: [PATCH 310/439] CHANGELOG: Vi-mode-spinning The last bit for 3.0.1 [ci skip] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd377982e..ba6f4ca57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ This release of fish fixes a number of major issues discovered in fish 3.0.0. - `string` now prints help to stdout, like other builtins. (#5495) - The completions for `configure` now correctly offer directories. (#5518) - The `man` completions won't interpret the argument as a regex anymore. (#5566) +- Killing the terminal while fish is in vi-normal mode will no longer send it spinning and eating CPU. (#5528) If you are upgrading from version 2.7.1 or before, please also review the release notes for 3.0.0 and 3.0b1 (included below). From 0e282deb92b5a9f99ae2208dd7ebcd0112a95f94 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 28 Jan 2019 21:25:54 +0100 Subject: [PATCH 311/439] CHANGELOG: Vi-mode-spinning The last bit for 3.0.1 [ci skip] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 329760ced..f4f111f62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ - `string` now prints help to stdout, like other builtins. (#5495) - The completions for `configure` now correctly offer directories. (#5518) - The `man` completions won't interpret the argument as a regex anymore. (#5566) +- Killing the terminal while fish is in vi-normal mode will no longer send it spinning and eating CPU. (#5528) # fish 3.0.0 (released December 28, 2018) From 4064ab818390d71d064dbeaa72a3c9191526c0dc Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 28 Jan 2019 16:49:31 -0800 Subject: [PATCH 312/439] Stop emitting extra new lines under tmux --- share/functions/__fish_config_interactive.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index b43d5d3a4..157265146 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -240,10 +240,10 @@ function __fish_config_interactive -d "Initializations that should be performed # - Any listeners (like the vi-cursor) if set -q TMUX function __fish_enable_focus --on-event fish_postexec - echo \e\[\?1004h + echo -n \e\[\?1004h end function __fish_disable_focus --on-event fish_preexec - echo \e\[\?1004l + echo -n \e\[\?1004l end __fish_enable_focus end From 46c967903d39e02619c2172d1085c7d6cbb695b8 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 28 Jan 2019 19:27:16 -0800 Subject: [PATCH 313/439] env.cpp: swap entries of fallback PATH I had this backwards. Thanks @mqudsi --- src/env.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/env.cpp b/src/env.cpp index ef85cda3d..aee612cc1 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -657,7 +657,7 @@ static void setup_path() { cspath.resize(confstr(_CS_PATH, nullptr, 0)); confstr(_CS_PATH, &cspath[0], cspath.length()); #else - std::string cspath = "/bin:/usr/bin"; // I doubt this is even necessary + std::string cspath = "/usr/bin:/bin"; // I doubt this is even necessary #endif vars.set_one(L"PATH", ENV_GLOBAL | ENV_EXPORT, str2wcstring(cspath)); } From 6b169753596176e600f8946f6a408a979e52d753 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 28 Jan 2019 19:35:17 -0800 Subject: [PATCH 314/439] Add a colon to command-not-found dealio. The previous commits to fix #5588 removed quoting. --- share/config.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/config.fish b/share/config.fish index 90055f1ef..749da1a1b 100644 --- a/share/config.fish +++ b/share/config.fish @@ -12,7 +12,7 @@ or set -g __fish_added_user_paths # Create the default command_not_found handler # function __fish_default_command_not_found_handler - printf "fish: Unknown command %s\n" (string escape -- $argv[1]) >&2 + printf "fish: Unknown command: %s\n" (string escape -- $argv[1]) >&2 end if status --is-interactive From 276d00e0cfc4daf834f2a765ec5789ae699be6b5 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 28 Jan 2019 20:44:07 -0800 Subject: [PATCH 315/439] Fix invocation tests. 6b169753596176e600f8946f6a408a979e52d753 broke tests. My bad. --- tests/invocation/broken-config-continues.err | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/invocation/broken-config-continues.err b/tests/invocation/broken-config-continues.err index 5ab2906b6..c4165a1a8 100644 --- a/tests/invocation/broken-config-continues.err +++ b/tests/invocation/broken-config-continues.err @@ -1,4 +1,4 @@ -fish: Unknown command syntax-error +fish: Unknown command: syntax-error $XDG_CONFIG_HOME/fish/config.fish (line 2): syntax-error ^ From 38db3adbf6f316fea2b67f930375135479466ccb Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 28 Jan 2019 21:12:49 -0800 Subject: [PATCH 316/439] Makefile.in: run make depend Nobody has done this in a long while. May speed up the build. --- Makefile.in | 603 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 352 insertions(+), 251 deletions(-) diff --git a/Makefile.in b/Makefile.in index 01ac297da..aa5e0e2ee 100644 --- a/Makefile.in +++ b/Makefile.in @@ -888,8 +888,12 @@ v = $(V$(V)) # 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/env.h src/lru.h src/exec.h src/wutil.h -obj/builtin.o: config.h src/builtin.h src/common.h src/fallback.h +obj/autoload.o: src/maybe.h src/signal.h src/env.h src/lru.h src/exec.h +obj/autoload.o: src/parser.h src/event.h src/io.h src/expand.h +obj/autoload.o: src/parse_constants.h src/parse_tree.h src/parse_grammar.h +obj/autoload.o: src/tokenizer.h src/proc.h src/enum_set.h src/tnode.h +obj/autoload.o: src/wutil.h +obj/builtin.o: config.h src/builtin.h src/common.h src/fallback.h src/maybe.h obj/builtin.o: src/signal.h src/builtin_argparse.h src/builtin_bg.h obj/builtin.o: src/builtin_bind.h src/builtin_block.h src/builtin_builtin.h obj/builtin.o: src/builtin_cd.h src/builtin_command.h @@ -902,318 +906,415 @@ obj/builtin.o: src/builtin_pwd.h src/builtin_random.h src/builtin_read.h obj/builtin.o: src/builtin_realpath.h src/builtin_return.h src/builtin_set.h obj/builtin.o: src/builtin_set_color.h src/builtin_source.h obj/builtin.o: src/builtin_status.h src/builtin_string.h src/builtin_test.h -obj/builtin.o: src/builtin_ulimit.h src/complete.h src/exec.h src/intern.h -obj/builtin.o: src/io.h src/env.h src/parse_constants.h src/parse_util.h -obj/builtin.o: src/tokenizer.h src/parser.h src/event.h src/expand.h -obj/builtin.o: src/parse_tree.h src/proc.h src/reader.h src/highlight.h -obj/builtin.o: src/color.h src/wgetopt.h src/wutil.h +obj/builtin.o: src/builtin_ulimit.h src/builtin_wait.h src/complete.h +obj/builtin.o: src/exec.h src/intern.h src/io.h src/env.h +obj/builtin.o: src/parse_constants.h src/parse_util.h src/parse_tree.h +obj/builtin.o: src/parse_grammar.h src/tokenizer.h src/parser.h src/event.h +obj/builtin.o: src/expand.h src/proc.h src/enum_set.h src/tnode.h +obj/builtin.o: src/reader.h src/highlight.h src/color.h src/wgetopt.h +obj/builtin.o: src/wutil.h obj/builtin_argparse.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_argparse.o: src/signal.h src/builtin_argparse.h src/env.h -obj/builtin_argparse.o: src/exec.h src/io.h src/wgetopt.h src/wutil.h +obj/builtin_argparse.o: src/maybe.h src/signal.h src/builtin_argparse.h +obj/builtin_argparse.o: src/env.h src/exec.h src/io.h src/parser.h +obj/builtin_argparse.o: src/event.h src/expand.h src/parse_constants.h +obj/builtin_argparse.o: src/parse_tree.h src/parse_grammar.h src/tokenizer.h +obj/builtin_argparse.o: src/proc.h src/enum_set.h src/tnode.h src/wgetopt.h +obj/builtin_argparse.o: src/wutil.h obj/builtin_bg.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_bg.o: src/signal.h src/builtin_bg.h src/io.h src/env.h src/proc.h -obj/builtin_bg.o: src/parse_tree.h src/parse_constants.h src/tokenizer.h -obj/builtin_bg.o: src/wutil.h +obj/builtin_bg.o: src/maybe.h src/signal.h src/builtin_bg.h src/io.h +obj/builtin_bg.o: src/env.h src/proc.h src/enum_set.h src/parse_tree.h +obj/builtin_bg.o: src/parse_constants.h src/parse_grammar.h src/tokenizer.h +obj/builtin_bg.o: src/tnode.h src/wutil.h obj/builtin_bind.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_bind.o: src/signal.h src/builtin_bind.h src/input.h src/io.h -obj/builtin_bind.o: src/env.h src/wgetopt.h src/wutil.h +obj/builtin_bind.o: src/maybe.h src/signal.h src/builtin_bind.h src/input.h +obj/builtin_bind.o: src/io.h src/env.h src/wgetopt.h src/wutil.h obj/builtin_block.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_block.o: src/signal.h src/builtin_block.h src/event.h src/io.h -obj/builtin_block.o: src/env.h src/parser.h src/expand.h -obj/builtin_block.o: src/parse_constants.h src/parse_tree.h src/tokenizer.h -obj/builtin_block.o: src/proc.h src/wgetopt.h src/wutil.h +obj/builtin_block.o: src/maybe.h src/signal.h src/builtin_block.h src/event.h +obj/builtin_block.o: src/io.h src/env.h src/parser.h src/expand.h +obj/builtin_block.o: src/parse_constants.h src/parse_tree.h +obj/builtin_block.o: src/parse_grammar.h src/tokenizer.h src/proc.h +obj/builtin_block.o: src/enum_set.h src/tnode.h src/wgetopt.h src/wutil.h obj/builtin_builtin.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_builtin.o: src/signal.h src/builtin_builtin.h src/io.h src/env.h -obj/builtin_builtin.o: src/wgetopt.h src/wutil.h +obj/builtin_builtin.o: src/maybe.h src/signal.h src/builtin_builtin.h +obj/builtin_builtin.o: src/io.h src/env.h src/wgetopt.h src/wutil.h obj/builtin_cd.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_cd.o: src/signal.h src/builtin_cd.h src/env.h src/io.h -obj/builtin_cd.o: src/parser.h src/event.h src/expand.h src/parse_constants.h -obj/builtin_cd.o: src/parse_tree.h src/tokenizer.h src/proc.h src/path.h -obj/builtin_cd.o: src/wutil.h +obj/builtin_cd.o: src/maybe.h src/signal.h src/builtin_cd.h src/env.h +obj/builtin_cd.o: src/io.h src/parser.h src/event.h src/expand.h +obj/builtin_cd.o: src/parse_constants.h src/parse_tree.h src/parse_grammar.h +obj/builtin_cd.o: src/tokenizer.h src/proc.h src/enum_set.h src/tnode.h +obj/builtin_cd.o: src/path.h src/wutil.h obj/builtin_command.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_command.o: src/signal.h src/builtin_command.h src/io.h src/env.h -obj/builtin_command.o: src/path.h src/wgetopt.h src/wutil.h +obj/builtin_command.o: src/maybe.h src/signal.h src/builtin_command.h +obj/builtin_command.o: src/io.h src/env.h src/parser.h src/event.h +obj/builtin_command.o: src/expand.h src/parse_constants.h src/parse_tree.h +obj/builtin_command.o: src/parse_grammar.h src/tokenizer.h src/proc.h +obj/builtin_command.o: src/enum_set.h src/tnode.h src/path.h src/wgetopt.h +obj/builtin_command.o: src/wutil.h obj/builtin_commandline.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_commandline.o: src/signal.h src/input.h src/builtin_bind.h -obj/builtin_commandline.o: src/io.h src/env.h src/parse_util.h -obj/builtin_commandline.o: src/parse_constants.h src/tokenizer.h src/proc.h -obj/builtin_commandline.o: src/parse_tree.h src/reader.h src/complete.h +obj/builtin_commandline.o: src/maybe.h src/signal.h src/input.h +obj/builtin_commandline.o: src/builtin_bind.h src/io.h src/env.h +obj/builtin_commandline.o: src/parse_util.h src/parse_tree.h +obj/builtin_commandline.o: src/parse_constants.h src/parse_grammar.h +obj/builtin_commandline.o: src/tokenizer.h src/proc.h src/enum_set.h +obj/builtin_commandline.o: src/tnode.h src/reader.h src/complete.h obj/builtin_commandline.o: src/highlight.h src/color.h src/util.h obj/builtin_commandline.o: src/wgetopt.h src/wutil.h obj/builtin_complete.o: config.h src/builtin.h src/common.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/event.h src/expand.h -obj/builtin_complete.o: src/parse_tree.h src/proc.h src/reader.h +obj/builtin_complete.o: src/maybe.h src/signal.h src/complete.h src/env.h +obj/builtin_complete.o: src/io.h src/parse_constants.h src/parse_util.h +obj/builtin_complete.o: src/parse_tree.h src/parse_grammar.h src/tokenizer.h +obj/builtin_complete.o: src/parser.h src/event.h src/expand.h src/proc.h +obj/builtin_complete.o: src/enum_set.h src/tnode.h src/reader.h obj/builtin_complete.o: src/highlight.h src/color.h src/wgetopt.h src/wutil.h obj/builtin_contains.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_contains.o: src/signal.h src/builtin_contains.h src/io.h -obj/builtin_contains.o: src/env.h src/wgetopt.h src/wutil.h +obj/builtin_contains.o: src/maybe.h src/signal.h src/builtin_contains.h +obj/builtin_contains.o: src/io.h src/env.h src/wgetopt.h src/wutil.h obj/builtin_disown.o: config.h src/signal.h src/builtin.h src/common.h -obj/builtin_disown.o: src/fallback.h src/builtin_disown.h src/io.h src/env.h -obj/builtin_disown.o: src/parser.h src/event.h src/expand.h -obj/builtin_disown.o: src/parse_constants.h src/parse_tree.h src/tokenizer.h -obj/builtin_disown.o: src/proc.h src/wutil.h +obj/builtin_disown.o: src/fallback.h src/maybe.h src/builtin_disown.h +obj/builtin_disown.o: src/io.h src/env.h src/parser.h src/event.h +obj/builtin_disown.o: src/expand.h src/parse_constants.h src/parse_tree.h +obj/builtin_disown.o: src/parse_grammar.h src/tokenizer.h src/proc.h +obj/builtin_disown.o: src/enum_set.h src/tnode.h src/wutil.h obj/builtin_echo.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_echo.o: src/signal.h src/builtin_echo.h src/io.h src/env.h -obj/builtin_echo.o: src/wgetopt.h src/wutil.h +obj/builtin_echo.o: src/maybe.h src/signal.h src/builtin_echo.h src/io.h +obj/builtin_echo.o: src/env.h src/wgetopt.h src/wutil.h obj/builtin_emit.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_emit.o: src/signal.h src/builtin_emit.h src/event.h src/io.h -obj/builtin_emit.o: src/env.h src/wutil.h +obj/builtin_emit.o: src/maybe.h src/signal.h src/builtin_emit.h src/event.h +obj/builtin_emit.o: src/io.h src/env.h src/wutil.h obj/builtin_exit.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_exit.o: src/signal.h src/builtin_exit.h src/io.h src/env.h -obj/builtin_exit.o: src/proc.h src/parse_tree.h src/parse_constants.h -obj/builtin_exit.o: src/tokenizer.h src/reader.h src/complete.h -obj/builtin_exit.o: src/highlight.h src/color.h src/wgetopt.h src/wutil.h +obj/builtin_exit.o: src/maybe.h src/signal.h src/builtin_exit.h src/io.h +obj/builtin_exit.o: src/env.h src/proc.h src/enum_set.h src/parse_tree.h +obj/builtin_exit.o: src/parse_constants.h src/parse_grammar.h src/tokenizer.h +obj/builtin_exit.o: src/tnode.h src/reader.h src/complete.h src/highlight.h +obj/builtin_exit.o: src/color.h src/wgetopt.h src/wutil.h obj/builtin_fg.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_fg.o: src/signal.h src/builtin_fg.h src/env.h src/io.h src/proc.h -obj/builtin_fg.o: src/parse_tree.h src/parse_constants.h src/tokenizer.h +obj/builtin_fg.o: src/maybe.h src/signal.h src/builtin_fg.h src/env.h +obj/builtin_fg.o: src/io.h src/parser.h src/event.h src/expand.h +obj/builtin_fg.o: src/parse_constants.h src/parse_tree.h src/parse_grammar.h +obj/builtin_fg.o: src/tokenizer.h src/proc.h src/enum_set.h src/tnode.h obj/builtin_fg.o: src/reader.h src/complete.h src/highlight.h src/color.h obj/builtin_fg.o: src/wutil.h obj/builtin_function.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_function.o: src/signal.h src/builtin_function.h src/complete.h -obj/builtin_function.o: src/event.h src/function.h src/env.h src/io.h -obj/builtin_function.o: src/parser.h src/expand.h src/parse_constants.h -obj/builtin_function.o: src/parse_tree.h src/tokenizer.h src/proc.h -obj/builtin_function.o: src/parser_keywords.h src/wgetopt.h src/wutil.h +obj/builtin_function.o: src/maybe.h src/signal.h src/builtin_function.h +obj/builtin_function.o: src/parse_tree.h src/parse_constants.h +obj/builtin_function.o: src/parse_grammar.h src/tokenizer.h src/complete.h +obj/builtin_function.o: src/event.h src/io.h src/env.h src/function.h +obj/builtin_function.o: src/tnode.h src/parser.h src/expand.h src/proc.h +obj/builtin_function.o: src/enum_set.h src/parser_keywords.h src/wgetopt.h +obj/builtin_function.o: src/wutil.h obj/builtin_functions.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_functions.o: src/signal.h src/builtin_functions.h src/env.h -obj/builtin_functions.o: src/event.h src/function.h src/io.h -obj/builtin_functions.o: src/parser_keywords.h src/proc.h src/parse_tree.h -obj/builtin_functions.o: src/parse_constants.h src/tokenizer.h src/wgetopt.h -obj/builtin_functions.o: src/wutil.h +obj/builtin_functions.o: src/maybe.h src/signal.h src/builtin_functions.h +obj/builtin_functions.o: src/env.h src/event.h src/io.h src/function.h +obj/builtin_functions.o: src/parse_tree.h src/parse_constants.h +obj/builtin_functions.o: src/parse_grammar.h src/tokenizer.h src/tnode.h +obj/builtin_functions.o: src/parser_keywords.h src/proc.h src/enum_set.h +obj/builtin_functions.o: src/wgetopt.h src/wutil.h obj/builtin_history.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_history.o: src/signal.h src/builtin_history.h src/history.h -obj/builtin_history.o: src/wutil.h src/io.h src/env.h src/reader.h -obj/builtin_history.o: src/complete.h src/highlight.h src/color.h -obj/builtin_history.o: src/parse_constants.h src/wgetopt.h +obj/builtin_history.o: src/maybe.h src/signal.h src/builtin_history.h +obj/builtin_history.o: src/history.h src/wutil.h src/io.h src/env.h +obj/builtin_history.o: src/parser.h src/event.h src/expand.h +obj/builtin_history.o: src/parse_constants.h src/parse_tree.h +obj/builtin_history.o: src/parse_grammar.h src/tokenizer.h src/proc.h +obj/builtin_history.o: src/enum_set.h src/tnode.h src/reader.h src/complete.h +obj/builtin_history.o: src/highlight.h src/color.h src/wgetopt.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/env.h src/proc.h -obj/builtin_jobs.o: src/parse_tree.h src/parse_constants.h src/tokenizer.h +obj/builtin_jobs.o: src/maybe.h src/signal.h src/io.h src/env.h src/proc.h +obj/builtin_jobs.o: src/enum_set.h src/parse_tree.h src/parse_constants.h +obj/builtin_jobs.o: src/parse_grammar.h src/tokenizer.h src/tnode.h obj/builtin_jobs.o: src/wgetopt.h src/wutil.h -obj/builtin_math.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_math.o: src/signal.h src/builtin_math.h src/io.h src/env.h -obj/builtin_math.o: src/tinyexpr.h src/wgetopt.h src/wutil.h +obj/builtin_math.o: config.h src/tinyexpr.h src/builtin.h src/common.h +obj/builtin_math.o: src/fallback.h src/maybe.h src/signal.h +obj/builtin_math.o: src/builtin_math.h src/io.h src/env.h src/wgetopt.h +obj/builtin_math.o: src/wutil.h obj/builtin_printf.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_printf.o: src/signal.h src/io.h src/env.h src/wutil.h +obj/builtin_printf.o: src/maybe.h src/signal.h src/io.h src/env.h src/wutil.h obj/builtin_pwd.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_pwd.o: src/signal.h src/builtin_pwd.h src/io.h src/env.h -obj/builtin_pwd.o: src/wutil.h +obj/builtin_pwd.o: src/maybe.h src/signal.h src/builtin_pwd.h src/io.h +obj/builtin_pwd.o: src/env.h src/parser.h src/event.h src/expand.h +obj/builtin_pwd.o: src/parse_constants.h src/parse_tree.h src/parse_grammar.h +obj/builtin_pwd.o: src/tokenizer.h src/proc.h src/enum_set.h src/tnode.h +obj/builtin_pwd.o: src/wgetopt.h src/wutil.h obj/builtin_random.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_random.o: src/signal.h src/builtin_random.h src/io.h src/env.h -obj/builtin_random.o: src/wutil.h +obj/builtin_random.o: src/maybe.h src/signal.h src/builtin_random.h src/io.h +obj/builtin_random.o: src/env.h src/wutil.h obj/builtin_read.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_read.o: src/signal.h src/builtin_read.h src/complete.h src/env.h -obj/builtin_read.o: src/event.h src/highlight.h src/color.h src/history.h -obj/builtin_read.o: src/wutil.h src/io.h src/proc.h src/parse_tree.h -obj/builtin_read.o: src/parse_constants.h src/tokenizer.h src/reader.h +obj/builtin_read.o: src/maybe.h src/signal.h src/builtin_read.h +obj/builtin_read.o: src/complete.h src/env.h src/event.h src/io.h +obj/builtin_read.o: src/highlight.h src/color.h src/history.h src/wutil.h +obj/builtin_read.o: src/parser.h src/expand.h src/parse_constants.h +obj/builtin_read.o: src/parse_tree.h src/parse_grammar.h src/tokenizer.h +obj/builtin_read.o: src/proc.h src/enum_set.h src/tnode.h src/reader.h obj/builtin_read.o: src/wcstringutil.h src/wgetopt.h obj/builtin_realpath.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_realpath.o: src/signal.h src/builtin_realpath.h src/io.h -obj/builtin_realpath.o: src/env.h src/wutil.h +obj/builtin_realpath.o: src/maybe.h src/signal.h src/builtin_realpath.h +obj/builtin_realpath.o: src/io.h src/env.h src/wutil.h obj/builtin_return.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_return.o: src/signal.h src/builtin_return.h src/io.h src/env.h -obj/builtin_return.o: src/parser.h src/event.h src/expand.h -obj/builtin_return.o: src/parse_constants.h src/parse_tree.h src/tokenizer.h -obj/builtin_return.o: src/proc.h src/wgetopt.h src/wutil.h +obj/builtin_return.o: src/maybe.h src/signal.h src/builtin_return.h src/io.h +obj/builtin_return.o: src/env.h src/parser.h src/event.h src/expand.h +obj/builtin_return.o: src/parse_constants.h src/parse_tree.h +obj/builtin_return.o: src/parse_grammar.h src/tokenizer.h src/proc.h +obj/builtin_return.o: src/enum_set.h src/tnode.h src/wgetopt.h src/wutil.h obj/builtin_set.o: config.h src/builtin.h src/common.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.o: src/maybe.h src/signal.h src/env.h src/expand.h +obj/builtin_set.o: src/parse_constants.h src/io.h src/parser.h src/event.h +obj/builtin_set.o: src/parse_tree.h src/parse_grammar.h src/tokenizer.h +obj/builtin_set.o: src/proc.h src/enum_set.h src/tnode.h src/wgetopt.h +obj/builtin_set.o: 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/env.h src/io.h -obj/builtin_set_color.o: src/output.h src/wgetopt.h src/wutil.h +obj/builtin_set_color.o: src/maybe.h src/signal.h src/color.h src/env.h +obj/builtin_set_color.o: src/io.h src/output.h src/parser.h src/event.h +obj/builtin_set_color.o: src/expand.h src/parse_constants.h src/parse_tree.h +obj/builtin_set_color.o: src/parse_grammar.h src/tokenizer.h src/proc.h +obj/builtin_set_color.o: src/enum_set.h src/tnode.h src/wgetopt.h src/wutil.h obj/builtin_source.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_source.o: src/signal.h src/builtin_source.h src/env.h +obj/builtin_source.o: src/maybe.h src/signal.h src/builtin_source.h src/env.h obj/builtin_source.o: src/intern.h src/io.h src/parser.h src/event.h obj/builtin_source.o: src/expand.h src/parse_constants.h src/parse_tree.h -obj/builtin_source.o: src/tokenizer.h src/proc.h src/reader.h src/complete.h +obj/builtin_source.o: src/parse_grammar.h src/tokenizer.h src/proc.h +obj/builtin_source.o: src/enum_set.h src/tnode.h src/reader.h src/complete.h obj/builtin_source.o: src/highlight.h src/color.h src/wutil.h obj/builtin_status.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_status.o: src/signal.h src/builtin_status.h src/io.h src/env.h +obj/builtin_status.o: src/maybe.h src/signal.h src/builtin_status.h +obj/builtin_status.o: src/future_feature_flags.h src/io.h src/env.h obj/builtin_status.o: src/parser.h src/event.h src/expand.h -obj/builtin_status.o: src/parse_constants.h src/parse_tree.h src/tokenizer.h -obj/builtin_status.o: src/proc.h src/wgetopt.h src/wutil.h +obj/builtin_status.o: src/parse_constants.h src/parse_tree.h +obj/builtin_status.o: src/parse_grammar.h src/tokenizer.h src/proc.h +obj/builtin_status.o: src/enum_set.h src/tnode.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/env.h src/parse_util.h -obj/builtin_string.o: src/parse_constants.h src/tokenizer.h -obj/builtin_string.o: src/wcstringutil.h src/wgetopt.h src/wildcard.h -obj/builtin_string.o: src/complete.h src/expand.h src/wutil.h +obj/builtin_string.o: src/maybe.h src/signal.h src/io.h src/env.h +obj/builtin_string.o: src/parse_util.h src/parse_tree.h src/parse_constants.h +obj/builtin_string.o: src/parse_grammar.h src/tokenizer.h src/wcstringutil.h +obj/builtin_string.o: src/wgetopt.h src/wildcard.h src/complete.h +obj/builtin_string.o: src/expand.h src/wutil.h obj/builtin_test.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_test.o: src/signal.h src/io.h src/env.h src/wutil.h +obj/builtin_test.o: src/maybe.h src/signal.h src/io.h src/env.h src/wutil.h obj/builtin_ulimit.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_ulimit.o: src/signal.h src/io.h src/env.h src/util.h +obj/builtin_ulimit.o: src/maybe.h src/signal.h src/io.h src/env.h src/util.h obj/builtin_ulimit.o: src/wgetopt.h src/wutil.h -obj/color.o: config.h src/color.h src/common.h src/fallback.h src/signal.h -obj/common.o: config.h src/common.h src/fallback.h src/signal.h src/env.h -obj/common.o: src/expand.h src/parse_constants.h src/proc.h src/io.h -obj/common.o: src/parse_tree.h src/tokenizer.h src/wildcard.h src/complete.h -obj/common.o: src/wutil.h +obj/builtin_wait.o: src/builtin.h src/common.h config.h src/fallback.h +obj/builtin_wait.o: src/maybe.h src/signal.h src/builtin_wait.h src/proc.h +obj/builtin_wait.o: src/enum_set.h src/io.h src/env.h src/parse_tree.h +obj/builtin_wait.o: src/parse_constants.h src/parse_grammar.h src/tokenizer.h +obj/builtin_wait.o: src/tnode.h src/wgetopt.h src/wutil.h +obj/color.o: config.h src/color.h src/common.h src/fallback.h src/maybe.h +obj/color.o: src/signal.h +obj/common.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/common.o: src/env.h src/expand.h src/parse_constants.h +obj/common.o: src/future_feature_flags.h src/proc.h src/enum_set.h src/io.h +obj/common.o: src/parse_tree.h src/parse_grammar.h src/tokenizer.h +obj/common.o: src/tnode.h src/wildcard.h src/complete.h src/wutil.h obj/complete.o: config.h src/autoload.h src/common.h src/fallback.h -obj/complete.o: src/signal.h src/env.h src/lru.h src/builtin.h src/complete.h -obj/complete.o: src/exec.h src/expand.h src/parse_constants.h src/function.h -obj/complete.o: src/event.h src/iothread.h src/parse_tree.h src/tokenizer.h -obj/complete.o: src/parse_util.h src/parser.h src/proc.h src/io.h src/path.h -obj/complete.o: src/util.h src/wildcard.h src/wutil.h +obj/complete.o: src/maybe.h src/signal.h src/env.h src/lru.h src/builtin.h +obj/complete.o: src/complete.h src/exec.h src/expand.h src/parse_constants.h +obj/complete.o: src/function.h src/event.h src/io.h src/parse_tree.h +obj/complete.o: src/parse_grammar.h src/tokenizer.h src/tnode.h +obj/complete.o: src/iothread.h src/parse_util.h src/parser.h src/proc.h +obj/complete.o: src/enum_set.h src/path.h src/reader.h src/highlight.h +obj/complete.o: src/color.h src/util.h src/wildcard.h src/wutil.h obj/env.o: config.h src/builtin_bind.h src/common.h src/fallback.h -obj/env.o: src/signal.h src/env.h src/env_universal_common.h src/wutil.h -obj/env.o: src/event.h src/expand.h src/parse_constants.h src/fish_version.h -obj/env.o: src/history.h src/input.h src/input_common.h src/output.h -obj/env.o: src/color.h src/path.h src/proc.h src/io.h src/parse_tree.h -obj/env.o: src/tokenizer.h src/reader.h src/complete.h src/highlight.h -obj/env.o: src/sanity.h src/screen.h -obj/env_universal_common.o: config.h src/common.h src/fallback.h src/signal.h -obj/env_universal_common.o: src/env.h src/env_universal_common.h src/wutil.h -obj/env_universal_common.o: src/path.h src/utf8.h src/util.h -obj/event.o: config.h src/signal.h src/common.h src/fallback.h src/event.h -obj/event.o: src/input_common.h src/io.h src/env.h src/parser.h src/expand.h -obj/event.o: src/parse_constants.h src/parse_tree.h src/tokenizer.h -obj/event.o: src/proc.h src/wutil.h +obj/env.o: src/maybe.h src/signal.h src/complete.h src/env.h +obj/env.o: src/env_universal_common.h src/wutil.h src/event.h src/io.h +obj/env.o: src/expand.h src/parse_constants.h src/fish_version.h +obj/env.o: src/function.h src/parse_tree.h src/parse_grammar.h +obj/env.o: src/tokenizer.h src/tnode.h src/history.h src/input.h +obj/env.o: src/input_common.h src/output.h src/color.h src/path.h src/proc.h +obj/env.o: src/enum_set.h src/reader.h src/highlight.h src/sanity.h +obj/env.o: src/screen.h +obj/env_universal_common.o: config.h src/common.h src/fallback.h src/maybe.h +obj/env_universal_common.o: src/signal.h src/env.h src/env_universal_common.h +obj/env_universal_common.o: src/wutil.h src/path.h src/utf8.h src/util.h +obj/env_universal_common.o: src/wcstringutil.h +obj/event.o: config.h src/signal.h src/common.h src/fallback.h src/maybe.h +obj/event.o: src/event.h src/io.h src/env.h src/input_common.h src/parser.h +obj/event.o: src/expand.h src/parse_constants.h src/parse_tree.h +obj/event.o: src/parse_grammar.h src/tokenizer.h src/proc.h src/enum_set.h +obj/event.o: src/tnode.h src/wutil.h obj/exec.o: config.h src/signal.h src/builtin.h src/common.h src/fallback.h -obj/exec.o: src/env.h src/exec.h src/function.h src/event.h src/io.h -obj/exec.o: src/parse_tree.h src/parse_constants.h src/tokenizer.h -obj/exec.o: src/parser.h src/expand.h src/proc.h src/postfork.h src/reader.h -obj/exec.o: src/complete.h src/highlight.h src/color.h src/wutil.h -obj/expand.o: config.h src/common.h src/fallback.h src/signal.h +obj/exec.o: src/maybe.h src/env.h src/exec.h src/function.h src/event.h +obj/exec.o: src/io.h src/parse_tree.h src/parse_constants.h +obj/exec.o: src/parse_grammar.h src/tokenizer.h src/tnode.h src/parser.h +obj/exec.o: src/expand.h src/proc.h src/enum_set.h src/postfork.h +obj/exec.o: src/reader.h src/complete.h src/highlight.h src/color.h +obj/exec.o: src/wutil.h +obj/expand.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h obj/expand.o: src/complete.h src/env.h src/exec.h src/expand.h -obj/expand.o: src/parse_constants.h src/iothread.h src/parse_util.h -obj/expand.o: src/tokenizer.h src/path.h src/proc.h src/io.h src/parse_tree.h -obj/expand.o: src/wildcard.h src/wutil.h -obj/fallback.o: config.h src/signal.h src/common.h src/fallback.h src/util.h -obj/fish.o: config.h src/builtin.h src/common.h src/fallback.h src/signal.h -obj/fish.o: src/env.h src/event.h src/expand.h src/parse_constants.h -obj/fish.o: src/fish_version.h src/function.h src/history.h src/wutil.h -obj/fish.o: src/io.h src/parser.h src/parse_tree.h src/tokenizer.h src/proc.h -obj/fish.o: src/path.h src/reader.h src/complete.h src/highlight.h -obj/fish.o: src/color.h +obj/expand.o: src/parse_constants.h src/history.h src/wutil.h src/iothread.h +obj/expand.o: src/parse_util.h src/parse_tree.h src/parse_grammar.h +obj/expand.o: src/tokenizer.h src/parser.h src/event.h src/io.h src/proc.h +obj/expand.o: src/enum_set.h src/tnode.h src/path.h src/reader.h +obj/expand.o: src/highlight.h src/color.h src/wcstringutil.h src/wildcard.h +obj/fallback.o: config.h src/signal.h src/common.h src/fallback.h src/maybe.h +obj/fallback.o: src/util.h +obj/fish.o: config.h src/builtin.h src/common.h src/fallback.h src/maybe.h +obj/fish.o: src/signal.h src/env.h src/event.h src/io.h src/expand.h +obj/fish.o: src/parse_constants.h src/fish_version.h src/function.h +obj/fish.o: src/parse_tree.h src/parse_grammar.h src/tokenizer.h src/tnode.h +obj/fish.o: src/future_feature_flags.h src/history.h src/wutil.h src/parser.h +obj/fish.o: src/proc.h src/enum_set.h src/path.h src/reader.h src/complete.h +obj/fish.o: src/highlight.h src/color.h obj/fish_indent.o: config.h src/color.h src/common.h src/fallback.h -obj/fish_indent.o: src/signal.h src/env.h src/fish_version.h src/highlight.h -obj/fish_indent.o: src/output.h src/parse_constants.h src/parse_tree.h -obj/fish_indent.o: src/tokenizer.h src/print_help.h src/wutil.h +obj/fish_indent.o: src/maybe.h src/signal.h src/env.h src/fish_version.h +obj/fish_indent.o: src/highlight.h src/output.h src/parse_constants.h +obj/fish_indent.o: src/print_help.h src/tnode.h src/parse_grammar.h +obj/fish_indent.o: src/tokenizer.h src/parse_tree.h src/wutil.h obj/fish_key_reader.o: config.h src/signal.h src/common.h src/fallback.h -obj/fish_key_reader.o: src/env.h src/fish_version.h src/input.h +obj/fish_key_reader.o: src/maybe.h src/env.h src/fish_version.h src/input.h obj/fish_key_reader.o: src/builtin_bind.h src/input_common.h src/print_help.h -obj/fish_key_reader.o: src/proc.h src/io.h src/parse_tree.h -obj/fish_key_reader.o: src/parse_constants.h src/tokenizer.h src/reader.h +obj/fish_key_reader.o: src/proc.h src/enum_set.h src/io.h src/parse_tree.h +obj/fish_key_reader.o: src/parse_constants.h src/parse_grammar.h +obj/fish_key_reader.o: src/tokenizer.h src/tnode.h src/reader.h obj/fish_key_reader.o: src/complete.h src/highlight.h src/color.h src/wutil.h obj/fish_tests.o: config.h src/signal.h src/builtin.h src/common.h -obj/fish_tests.o: src/fallback.h src/color.h src/complete.h src/env.h -obj/fish_tests.o: src/env_universal_common.h src/wutil.h src/event.h -obj/fish_tests.o: src/expand.h src/parse_constants.h src/function.h +obj/fish_tests.o: src/fallback.h src/maybe.h src/color.h src/complete.h +obj/fish_tests.o: src/env.h src/env_universal_common.h src/wutil.h +obj/fish_tests.o: src/event.h src/io.h src/expand.h src/parse_constants.h +obj/fish_tests.o: src/function.h src/parse_tree.h src/parse_grammar.h +obj/fish_tests.o: src/tokenizer.h src/tnode.h src/future_feature_flags.h obj/fish_tests.o: src/highlight.h src/history.h src/input.h -obj/fish_tests.o: src/builtin_bind.h src/input_common.h src/io.h -obj/fish_tests.o: src/iothread.h src/lru.h src/pager.h src/reader.h -obj/fish_tests.o: src/screen.h src/parse_tree.h src/tokenizer.h -obj/fish_tests.o: src/parse_util.h src/parser.h src/proc.h src/path.h -obj/fish_tests.o: src/utf8.h src/util.h src/wcstringutil.h src/wildcard.h +obj/fish_tests.o: src/builtin_bind.h src/input_common.h src/iothread.h +obj/fish_tests.o: src/lru.h src/pager.h src/reader.h src/screen.h +obj/fish_tests.o: src/parse_util.h src/parser.h src/proc.h src/enum_set.h +obj/fish_tests.o: src/path.h src/utf8.h src/util.h src/wcstringutil.h +obj/fish_tests.o: src/wildcard.h obj/fish_version.o: src/fish_version.h obj/function.o: config.h src/autoload.h src/common.h src/fallback.h -obj/function.o: src/signal.h src/env.h src/lru.h src/event.h src/function.h -obj/function.o: src/intern.h src/parser_keywords.h src/reader.h -obj/function.o: src/complete.h src/highlight.h src/color.h -obj/function.o: src/parse_constants.h src/wutil.h +obj/function.o: src/maybe.h src/signal.h src/env.h src/lru.h src/event.h +obj/function.o: src/io.h src/function.h src/parse_tree.h +obj/function.o: src/parse_constants.h src/parse_grammar.h src/tokenizer.h +obj/function.o: src/tnode.h src/intern.h src/parser.h src/expand.h src/proc.h +obj/function.o: src/enum_set.h src/parser_keywords.h src/reader.h +obj/function.o: src/complete.h src/highlight.h src/color.h src/wutil.h +obj/future_feature_flags.o: config.h src/future_feature_flags.h src/common.h +obj/future_feature_flags.o: src/fallback.h src/maybe.h src/signal.h obj/highlight.o: config.h src/builtin.h src/common.h src/fallback.h -obj/highlight.o: src/signal.h src/color.h src/env.h src/expand.h -obj/highlight.o: src/parse_constants.h src/function.h src/event.h -obj/highlight.o: src/highlight.h src/history.h src/wutil.h src/output.h -obj/highlight.o: src/parse_tree.h src/tokenizer.h src/parse_util.h src/path.h -obj/highlight.o: src/wildcard.h src/complete.h -obj/history.o: config.h src/common.h src/fallback.h src/signal.h src/env.h -obj/history.o: src/history.h src/wutil.h src/io.h src/iothread.h src/lru.h -obj/history.o: src/parse_constants.h src/parse_tree.h src/tokenizer.h +obj/highlight.o: src/maybe.h src/signal.h src/color.h src/env.h src/expand.h +obj/highlight.o: src/parse_constants.h src/function.h src/event.h src/io.h +obj/highlight.o: src/parse_tree.h src/parse_grammar.h src/tokenizer.h +obj/highlight.o: src/tnode.h src/future_feature_flags.h src/highlight.h +obj/highlight.o: src/history.h src/wutil.h src/output.h src/parse_util.h +obj/highlight.o: src/path.h src/wildcard.h src/complete.h +obj/history.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/history.o: src/env.h src/history.h src/wutil.h src/io.h src/iothread.h +obj/history.o: src/lru.h src/parser.h src/event.h src/expand.h +obj/history.o: src/parse_constants.h src/parse_tree.h src/parse_grammar.h +obj/history.o: src/tokenizer.h src/proc.h src/enum_set.h src/tnode.h obj/history.o: src/parse_util.h src/path.h src/reader.h src/complete.h -obj/history.o: src/highlight.h src/color.h -obj/input.o: config.h src/common.h src/fallback.h src/signal.h src/env.h -obj/input.o: src/event.h src/input.h src/builtin_bind.h src/input_common.h -obj/input.o: src/io.h src/parser.h src/expand.h src/parse_constants.h -obj/input.o: src/parse_tree.h src/tokenizer.h src/proc.h src/reader.h -obj/input.o: src/complete.h src/highlight.h src/color.h src/wutil.h -obj/input_common.o: config.h src/common.h src/fallback.h src/signal.h -obj/input_common.o: src/env.h src/env_universal_common.h src/wutil.h -obj/input_common.o: src/input_common.h src/iothread.h src/util.h -obj/intern.o: config.h src/common.h src/fallback.h src/signal.h src/intern.h -obj/io.o: config.h src/common.h src/fallback.h src/signal.h src/exec.h -obj/io.o: src/io.h src/env.h src/wutil.h -obj/iothread.o: config.h src/signal.h src/common.h src/fallback.h +obj/history.o: src/highlight.h src/color.h src/wcstringutil.h src/wildcard.h +obj/input.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/input.o: src/env.h src/event.h src/io.h src/input.h src/builtin_bind.h +obj/input.o: src/input_common.h src/parser.h src/expand.h +obj/input.o: src/parse_constants.h src/parse_tree.h src/parse_grammar.h +obj/input.o: src/tokenizer.h src/proc.h src/enum_set.h src/tnode.h +obj/input.o: src/reader.h src/complete.h src/highlight.h src/color.h +obj/input.o: src/wutil.h +obj/input_common.o: config.h src/common.h src/fallback.h src/maybe.h +obj/input_common.o: src/signal.h src/env.h src/env_universal_common.h +obj/input_common.o: src/wutil.h src/input_common.h src/iothread.h src/util.h +obj/intern.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/intern.o: src/intern.h +obj/io.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/io.o: src/exec.h src/io.h src/env.h src/wutil.h +obj/iothread.o: config.h src/signal.h src/common.h src/fallback.h src/maybe.h obj/iothread.o: src/iothread.h src/wutil.h -obj/kill.o: config.h src/common.h src/fallback.h src/signal.h -obj/output.o: config.h src/color.h src/common.h src/fallback.h src/signal.h -obj/output.o: src/env.h src/output.h src/wutil.h -obj/pager.o: config.h src/common.h src/fallback.h src/signal.h src/complete.h -obj/pager.o: src/highlight.h src/color.h src/env.h src/pager.h src/reader.h -obj/pager.o: src/parse_constants.h src/screen.h src/util.h src/wutil.h +obj/kill.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/output.o: config.h src/color.h src/common.h src/fallback.h src/maybe.h +obj/output.o: src/signal.h src/env.h src/output.h src/wutil.h +obj/pager.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/pager.o: src/complete.h src/highlight.h src/color.h src/env.h src/pager.h +obj/pager.o: src/reader.h src/parse_constants.h src/screen.h src/util.h +obj/pager.o: src/wutil.h obj/parse_execution.o: config.h src/builtin.h src/common.h src/fallback.h -obj/parse_execution.o: src/signal.h src/builtin_function.h src/complete.h -obj/parse_execution.o: src/env.h src/event.h src/exec.h src/expand.h -obj/parse_execution.o: src/parse_constants.h src/function.h src/io.h -obj/parse_execution.o: src/parse_execution.h src/parse_tree.h src/tokenizer.h -obj/parse_execution.o: src/proc.h src/parse_util.h src/parser.h src/path.h -obj/parse_execution.o: src/reader.h src/highlight.h src/color.h src/util.h -obj/parse_execution.o: src/wildcard.h src/wutil.h -obj/parse_productions.o: config.h src/common.h src/fallback.h src/signal.h -obj/parse_productions.o: src/parse_constants.h src/parse_productions.h -obj/parse_productions.o: src/parse_tree.h src/tokenizer.h -obj/parse_tree.o: config.h src/common.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/proc.h src/io.h -obj/parse_tree.o: src/env.h src/wutil.h +obj/parse_execution.o: src/maybe.h src/signal.h src/builtin_function.h +obj/parse_execution.o: src/parse_tree.h src/parse_constants.h +obj/parse_execution.o: src/parse_grammar.h src/tokenizer.h src/complete.h +obj/parse_execution.o: src/env.h src/event.h src/io.h src/exec.h src/expand.h +obj/parse_execution.o: src/function.h src/tnode.h src/parse_execution.h +obj/parse_execution.o: src/proc.h src/enum_set.h src/parse_util.h +obj/parse_execution.o: src/parser.h src/path.h src/reader.h src/highlight.h +obj/parse_execution.o: src/color.h src/util.h src/wildcard.h src/wutil.h +obj/parse_productions.o: config.h src/common.h src/fallback.h src/maybe.h +obj/parse_productions.o: src/signal.h src/parse_constants.h +obj/parse_productions.o: src/parse_grammar.h src/tokenizer.h +obj/parse_productions.o: src/parse_productions.h src/parse_tree.h +obj/parse_tree.o: config.h src/common.h src/fallback.h src/maybe.h +obj/parse_tree.o: src/signal.h src/parse_constants.h src/parse_productions.h +obj/parse_tree.o: src/parse_tree.h src/parse_grammar.h src/tokenizer.h +obj/parse_tree.o: src/proc.h src/enum_set.h src/io.h src/env.h src/tnode.h +obj/parse_tree.o: src/wutil.h obj/parse_util.o: config.h src/builtin.h src/common.h src/fallback.h -obj/parse_util.o: src/signal.h src/expand.h src/parse_constants.h -obj/parse_util.o: src/parse_tree.h src/tokenizer.h src/parse_util.h -obj/parse_util.o: src/util.h src/wildcard.h src/complete.h src/wutil.h -obj/parser.o: config.h src/common.h src/fallback.h src/signal.h src/env.h -obj/parser.o: src/event.h src/expand.h src/parse_constants.h src/function.h -obj/parser.o: src/intern.h src/parse_execution.h src/io.h src/parse_tree.h -obj/parser.o: src/tokenizer.h src/proc.h src/parse_util.h src/parser.h -obj/parser.o: src/reader.h src/complete.h src/highlight.h src/color.h -obj/parser.o: src/sanity.h src/wutil.h -obj/parser_keywords.o: config.h src/common.h src/fallback.h src/signal.h -obj/parser_keywords.o: src/parser_keywords.h -obj/path.o: config.h src/common.h src/fallback.h src/signal.h src/env.h -obj/path.o: src/expand.h src/parse_constants.h src/path.h src/wutil.h -obj/postfork.o: config.h src/signal.h src/common.h src/fallback.h src/exec.h -obj/postfork.o: src/io.h src/env.h src/iothread.h src/postfork.h src/proc.h -obj/postfork.o: src/parse_tree.h src/parse_constants.h src/tokenizer.h -obj/postfork.o: src/wutil.h -obj/print_help.o: config.h src/common.h src/fallback.h src/signal.h -obj/print_help.o: src/print_help.h -obj/proc.o: config.h src/signal.h src/common.h src/fallback.h src/event.h -obj/proc.o: src/io.h src/env.h src/output.h src/color.h src/parse_tree.h -obj/proc.o: src/parse_constants.h src/tokenizer.h src/parser.h src/expand.h -obj/proc.o: src/proc.h src/reader.h src/complete.h src/highlight.h -obj/proc.o: src/sanity.h src/util.h src/wutil.h +obj/parse_util.o: src/maybe.h src/signal.h src/expand.h src/parse_constants.h +obj/parse_util.o: src/future_feature_flags.h src/parse_util.h +obj/parse_util.o: src/parse_tree.h src/parse_grammar.h src/tokenizer.h +obj/parse_util.o: src/parser.h src/event.h src/io.h src/env.h src/proc.h +obj/parse_util.o: src/enum_set.h src/tnode.h src/util.h src/wildcard.h +obj/parse_util.o: src/complete.h src/wutil.h +obj/parser.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/parser.o: src/env.h src/event.h src/io.h src/expand.h +obj/parser.o: src/parse_constants.h src/function.h src/parse_tree.h +obj/parser.o: src/parse_grammar.h src/tokenizer.h src/tnode.h src/intern.h +obj/parser.o: src/parse_execution.h src/proc.h src/enum_set.h +obj/parser.o: src/parse_util.h src/parser.h src/reader.h src/complete.h +obj/parser.o: src/highlight.h src/color.h src/sanity.h src/wutil.h +obj/parser_keywords.o: config.h src/common.h src/fallback.h src/maybe.h +obj/parser_keywords.o: src/signal.h src/parser_keywords.h +obj/path.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/path.o: src/env.h src/expand.h src/parse_constants.h src/path.h +obj/path.o: src/wutil.h +obj/postfork.o: config.h src/signal.h src/common.h src/fallback.h src/maybe.h +obj/postfork.o: src/exec.h src/io.h src/env.h src/iothread.h src/postfork.h +obj/postfork.o: src/proc.h src/enum_set.h src/parse_tree.h +obj/postfork.o: src/parse_constants.h src/parse_grammar.h src/tokenizer.h +obj/postfork.o: src/tnode.h src/wutil.h +obj/print_help.o: config.h src/common.h src/fallback.h src/maybe.h +obj/print_help.o: src/signal.h src/print_help.h +obj/proc.o: config.h src/signal.h src/common.h src/fallback.h src/maybe.h +obj/proc.o: src/event.h src/io.h src/env.h src/output.h src/color.h +obj/proc.o: src/parse_tree.h src/parse_constants.h src/parse_grammar.h +obj/proc.o: src/tokenizer.h src/parser.h src/expand.h src/proc.h +obj/proc.o: src/enum_set.h src/tnode.h src/reader.h src/complete.h +obj/proc.o: src/highlight.h src/sanity.h src/util.h src/wutil.h obj/reader.o: config.h src/signal.h src/color.h src/common.h src/fallback.h -obj/reader.o: src/complete.h src/env.h src/event.h src/exec.h src/expand.h -obj/reader.o: src/parse_constants.h src/function.h src/highlight.h -obj/reader.o: src/history.h src/wutil.h src/input.h src/builtin_bind.h -obj/reader.o: src/input_common.h src/intern.h src/io.h src/iothread.h -obj/reader.o: src/kill.h src/output.h src/pager.h src/reader.h src/screen.h -obj/reader.o: src/parse_tree.h src/tokenizer.h src/parse_util.h src/parser.h -obj/reader.o: src/proc.h src/sanity.h src/util.h -obj/sanity.o: config.h src/common.h src/fallback.h src/signal.h src/history.h -obj/sanity.o: src/wutil.h src/kill.h src/proc.h src/io.h src/env.h -obj/sanity.o: src/parse_tree.h src/parse_constants.h src/tokenizer.h -obj/sanity.o: src/reader.h src/complete.h src/highlight.h src/color.h -obj/sanity.o: src/sanity.h -obj/screen.o: config.h src/common.h src/fallback.h src/signal.h src/env.h -obj/screen.o: src/highlight.h src/color.h src/output.h src/pager.h +obj/reader.o: src/maybe.h src/complete.h src/env.h src/event.h src/io.h +obj/reader.o: src/exec.h src/expand.h src/parse_constants.h src/function.h +obj/reader.o: src/parse_tree.h src/parse_grammar.h src/tokenizer.h +obj/reader.o: src/tnode.h src/highlight.h src/history.h src/wutil.h +obj/reader.o: src/input.h src/builtin_bind.h src/input_common.h src/intern.h +obj/reader.o: src/iothread.h src/kill.h src/output.h src/pager.h src/reader.h +obj/reader.o: src/screen.h src/parse_util.h src/parser.h src/proc.h +obj/reader.o: src/enum_set.h src/sanity.h src/util.h +obj/sanity.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/sanity.o: src/history.h src/wutil.h src/kill.h src/proc.h src/enum_set.h +obj/sanity.o: src/io.h src/env.h src/parse_tree.h src/parse_constants.h +obj/sanity.o: src/parse_grammar.h src/tokenizer.h src/tnode.h src/reader.h +obj/sanity.o: src/complete.h src/highlight.h src/color.h src/sanity.h +obj/screen.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/screen.o: src/env.h src/highlight.h src/color.h src/output.h src/pager.h obj/screen.o: src/complete.h src/reader.h src/parse_constants.h src/screen.h obj/screen.o: src/util.h -obj/signal.o: config.h src/signal.h src/common.h src/fallback.h src/event.h -obj/signal.o: src/proc.h src/io.h src/env.h src/parse_tree.h -obj/signal.o: src/parse_constants.h src/tokenizer.h src/reader.h -obj/signal.o: src/complete.h src/highlight.h src/color.h src/wutil.h -obj/tokenizer.o: config.h src/common.h src/fallback.h src/signal.h -obj/tokenizer.o: src/tokenizer.h src/wutil.h -obj/utf8.o: config.h src/common.h src/fallback.h src/signal.h src/utf8.h -obj/util.o: config.h src/common.h src/fallback.h src/signal.h src/util.h -obj/util.o: src/wutil.h -obj/wcstringutil.o: config.h src/common.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/signal.o: config.h src/signal.h src/common.h src/fallback.h src/maybe.h +obj/signal.o: src/event.h src/io.h src/env.h src/parser.h src/expand.h +obj/signal.o: src/parse_constants.h src/parse_tree.h src/parse_grammar.h +obj/signal.o: src/tokenizer.h src/proc.h src/enum_set.h src/tnode.h +obj/signal.o: src/reader.h src/complete.h src/highlight.h src/color.h +obj/signal.o: src/wutil.h +obj/tinyexpr.o: src/tinyexpr.h +obj/tnode.o: src/tnode.h src/parse_grammar.h src/parse_constants.h +obj/tnode.o: src/common.h config.h src/fallback.h src/maybe.h src/signal.h +obj/tnode.o: src/tokenizer.h src/parse_tree.h +obj/tokenizer.o: config.h src/common.h src/fallback.h src/maybe.h +obj/tokenizer.o: src/signal.h src/future_feature_flags.h src/tokenizer.h +obj/tokenizer.o: src/parse_constants.h src/wutil.h +obj/utf8.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/utf8.o: src/utf8.h +obj/util.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/util.o: src/util.h src/wutil.h +obj/wcstringutil.o: config.h src/common.h src/fallback.h src/maybe.h +obj/wcstringutil.o: src/signal.h src/wcstringutil.h +obj/wgetopt.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h obj/wgetopt.o: src/wgetopt.h src/wutil.h -obj/wildcard.o: config.h src/common.h src/fallback.h src/signal.h +obj/wildcard.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h obj/wildcard.o: src/complete.h src/expand.h src/parse_constants.h -obj/wildcard.o: src/reader.h src/highlight.h src/color.h src/env.h -obj/wildcard.o: src/wildcard.h src/wutil.h -obj/wutil.o: config.h src/common.h src/fallback.h src/signal.h src/wutil.h +obj/wildcard.o: src/future_feature_flags.h src/reader.h src/highlight.h +obj/wildcard.o: src/color.h src/env.h src/wildcard.h src/wutil.h +obj/wutil.o: config.h src/common.h src/fallback.h src/maybe.h src/signal.h +obj/wutil.o: src/wutil.h From d9d2ad1cd6f883492c701be80dcf27530cf68c07 Mon Sep 17 00:00:00 2001 From: Ashe Connor Date: Wed, 30 Jan 2019 15:51:45 +1100 Subject: [PATCH 317/439] pcre2 -> regex --- doc_src/string.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/string.txt b/doc_src/string.txt index 52bca9445..c4e63b414 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -48,7 +48,7 @@ The following subcommands are available. `--style=regex` escapes an input string for literal matching within a regex expression. The string is first converted to UTF-8 before being encoded. -`string unescape` performs the inverse of the `string escape` command. If the string to be unescaped is not properly formatted it is ignored. For example, doing `string unescape --style=var (string escape --style=var $str)` will return the original string. There is no support for unescaping pcre2. +`string unescape` performs the inverse of the `string escape` command. If the string to be unescaped is not properly formatted it is ignored. For example, doing `string unescape --style=var (string escape --style=var $str)` will return the original string. There is no support for unescaping `--style=regex`. \subsection string-join "join" subcommand From 09ca268d50b2100eec7c0639adeb97920ec8018a Mon Sep 17 00:00:00 2001 From: Ashe Connor Date: Wed, 30 Jan 2019 14:07:04 +1100 Subject: [PATCH 318/439] fix "are equivalent" with same example This was introduced in 87eb073 when ^ redirection was removed from the docs. --- 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 1b5d0d691..73d8ed6d9 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -166,7 +166,7 @@ Any file descriptor can be redirected in an arbitrary way by prefixing the redir - To redirect output of FD N, write `N>DESTINATION` - To append the output of FD N to a file, write `N>>DESTINATION_FILE` -Example: `echo Hello 2>output.stderr` and `echo Hello 2>output.stderr` are equivalent, and write the standard error (file descriptor 2) of the target program to `output.stderr`. +Example: `echo Hello 2>output.stderr` writes the standard error (file descriptor 2) of the target program to `output.stderr`. \subsection piping Piping From d15de117b931921a7ab25ae743b30f4a79088e48 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 30 Jan 2019 14:24:59 -0600 Subject: [PATCH 319/439] Resolve CMAKE_* directories before saving as build defines Since fish began resolving symlinks it broke the running-from-build-dir detection in fish.cpp if the build directory were a symlink (which is common on some platforms where the default user HOME directory is a symlink in the first place, e.g. FreeBSD). --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ebfc73e6..882888c7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,8 +105,10 @@ ADD_DEFINITIONS(-D_UNICODE=1 INCLUDE(Version) # Let fish pick up when we're running out of the build directory without installing -ADD_DEFINITIONS(-DCMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}") -ADD_DEFINITIONS(-DCMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}") +GET_FILENAME_COMPONENT(REAL_CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}" REALPATH) +GET_FILENAME_COMPONENT(REAL_CMAKE_SOURCE_DIR "${CMAKE_SOURCE_DIR}" REALPATH) +ADD_DEFINITIONS(-DCMAKE_BINARY_DIR="${REAL_CMAKE_BINARY_DIR}") +ADD_DEFINITIONS(-DCMAKE_SOURCE_DIR="${REAL_CMAKE_SOURCE_DIR}") # Teach fish_version.o to rebuild when FBVF changes. # The standard C++ include detection machinery misses this. From c7635ed2c08ba6e15537b2c285e06637a9637e37 Mon Sep 17 00:00:00 2001 From: Ashe Connor Date: Wed, 30 Jan 2019 14:14:19 +1100 Subject: [PATCH 320/439] `***.fish* -> `**.fish` --- 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 73d8ed6d9..3d0f9bd99 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -426,7 +426,7 @@ 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 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. +- `**` 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, From 9103dc2c23b49cbe76851b6ad02a9cc99fc6077c Mon Sep 17 00:00:00 2001 From: Ashe Connor Date: Wed, 30 Jan 2019 15:51:45 +1100 Subject: [PATCH 321/439] pcre2 -> regex (cherry picked from commit d9d2ad1cd6f883492c701be80dcf27530cf68c07) --- doc_src/string.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/string.txt b/doc_src/string.txt index 52bca9445..c4e63b414 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -48,7 +48,7 @@ The following subcommands are available. `--style=regex` escapes an input string for literal matching within a regex expression. The string is first converted to UTF-8 before being encoded. -`string unescape` performs the inverse of the `string escape` command. If the string to be unescaped is not properly formatted it is ignored. For example, doing `string unescape --style=var (string escape --style=var $str)` will return the original string. There is no support for unescaping pcre2. +`string unescape` performs the inverse of the `string escape` command. If the string to be unescaped is not properly formatted it is ignored. For example, doing `string unescape --style=var (string escape --style=var $str)` will return the original string. There is no support for unescaping `--style=regex`. \subsection string-join "join" subcommand From f0c03ab73e6a8bda993f1703baf66f13347c796d Mon Sep 17 00:00:00 2001 From: Ashe Connor Date: Wed, 30 Jan 2019 14:07:04 +1100 Subject: [PATCH 322/439] fix "are equivalent" with same example This was introduced in 87eb073 when ^ redirection was removed from the docs. (cherry picked from commit 09ca268d50b2100eec7c0639adeb97920ec8018a) --- 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 c75c6b82d..d6f82425a 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -166,7 +166,7 @@ Any file descriptor can be redirected in an arbitrary way by prefixing the redir - To redirect output of FD N, write `N>DESTINATION` - To append the output of FD N to a file, write `N>>DESTINATION_FILE` -Example: `echo Hello 2>output.stderr` and `echo Hello 2>output.stderr` are equivalent, and write the standard error (file descriptor 2) of the target program to `output.stderr`. +Example: `echo Hello 2>output.stderr` writes the standard error (file descriptor 2) of the target program to `output.stderr`. \subsection piping Piping From bfa051e466a55f2acb7308cdda1e09b0ef7d4893 Mon Sep 17 00:00:00 2001 From: Ashe Connor Date: Wed, 30 Jan 2019 14:14:19 +1100 Subject: [PATCH 323/439] `***.fish* -> `**.fish` (cherry picked from commit c7635ed2c08ba6e15537b2c285e06637a9637e37) --- 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 d6f82425a..0fab0a2c3 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -426,7 +426,7 @@ 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 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. +- `**` 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, From 0770fd1f890bf7cb01e8d12c44e42d8e1c1a66fe Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 31 Jan 2019 16:01:12 +0100 Subject: [PATCH 324/439] src/function.cpp: Fix possible NULL-dereference UBSan complained, so let's check. --- src/function.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/function.cpp b/src/function.cpp index d2ef6e9c7..9d51d1699 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -251,7 +251,10 @@ bool function_get_definition(const wcstring &name, wcstring &out_definition) { scoped_rlock locker(functions_lock); const function_info_t *func = function_get(name); if (func) { - out_definition = func->props->body_node.get_source(func->props->parsed_source->src); + auto props = func->props; + if (props && props->parsed_source) { + out_definition = props->body_node.get_source(props->parsed_source->src); + } } return func != NULL; } From aebe040fdc8bc336597206d2b5452f9213e8a26d Mon Sep 17 00:00:00 2001 From: raichoo Date: Thu, 31 Jan 2019 20:22:58 +0100 Subject: [PATCH 325/439] document private mode in fish completion --- share/completions/fish.fish | 1 + 1 file changed, 1 insertion(+) diff --git a/share/completions/fish.fish b/share/completions/fish.fish index d33d20bc4..2038f0c78 100644 --- a/share/completions/fish.fish +++ b/share/completions/fish.fish @@ -6,3 +6,4 @@ complete -c fish -s i -l interactive -d "Run in interactive mode" complete -c fish -s l -l login -d "Run in login mode" complete -c fish -s p -l profile -d "Output profiling information to specified file" -f complete -c fish -s d -l debug -d "Run with the specified verbosity level" +complete -c fish -s P -l private -d "Run fish in private mode" From a2aab24db70fcc7e52ea323d5e6b9d8c2f7595de Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 31 Jan 2019 12:12:46 -0800 Subject: [PATCH 326/439] Switch io_mode to an enum class --- src/exec.cpp | 36 ++++++++++++++++++------------------ src/io.cpp | 2 +- src/io.h | 20 +++++++++++--------- src/postfork.cpp | 30 +++++++++++++++--------------- src/proc.cpp | 4 ++-- 5 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index 294f6eef7..d180ef826 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -102,7 +102,7 @@ int exec_pipe(int fd[2]) { /// 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) { + if (io != NULL && io->io_mode == io_mode_t::file) { // It's a file redirection. Compare the path to /dev/null. const io_file_t *io_file = static_cast(io); const char *path = io_file->filename_cstr; @@ -256,15 +256,15 @@ 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) { - case IO_PIPE: - case IO_FD: - case IO_BUFFER: - case IO_CLOSE: { + case io_mode_t::pipe: + case io_mode_t::fd: + case io_mode_t::buffer: + case io_mode_t::close: { // These redirections don't need transmogrification. They can be passed through. out = in; break; } - case IO_FILE: { + case io_mode_t::file: { // Transmogrify file redirections. int fd; io_file_t *in_file = static_cast(in.get()); @@ -452,7 +452,7 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptrpipe_fd[0]; } else if (const auto in = proc_io_chain.get_io_for_fd(STDIN_FILENO)) { switch (in->io_mode) { - case IO_FD: { + case io_mode_t::fd: { const io_fd_t *in_fd = static_cast(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, @@ -466,12 +466,12 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptr(in.get()); local_builtin_stdin = in_pipe->pipe_fd[0]; break; } - case IO_FILE: { + case io_mode_t::file: { // Do not set CLO_EXEC because child needs access. const io_file_t *in_file = static_cast(in.get()); local_builtin_stdin = open(in_file->filename_cstr, in_file->flags, OPEN_MASK); @@ -484,7 +484,7 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptr stdin_io = io_chain_get(p->io_chain(), STDIN_FILENO); - stdin_is_directly_redirected = stdin_io && stdin_io->io_mode != IO_CLOSE; + stdin_is_directly_redirected = stdin_io && stdin_io->io_mode != io_mode_t::close; } streams.stdin_fd = local_builtin_stdin; @@ -568,7 +568,7 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, if (!must_fork && p->is_last_in_job) { // We are handling reads directly in the main loop. Note that we may still end // up forking. - const bool stdout_is_to_buffer = stdout_io && stdout_io->io_mode == IO_BUFFER; + const bool stdout_is_to_buffer = stdout_io && stdout_io->io_mode == io_mode_t::buffer; const bool no_stdout_output = stdout_stream.empty(); const bool no_stderr_output = stderr_stream.empty(); const bool stdout_discarded = stdout_stream.buffer().discarded(); @@ -1012,15 +1012,15 @@ bool exec_job(parser_t &parser, shared_ptr j) { } } - // 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_mode_t::buffers are output. We used to support a (single, hacked-in) + // magical input io_mode_t::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. size_t stdout_read_limit = 0; const io_chain_t all_ios = j->all_io_redirections(); 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_mode_t::buffer)) { io_buffer_t *io_buffer = static_cast(io.get()); assert(!io_buffer->is_input); stdout_read_limit = io_buffer->buffer().limit(); @@ -1036,7 +1036,7 @@ bool exec_job(parser_t &parser, shared_ptr j) { // with a redireciton like <&3; we may also have chosen 3 as the fd for our pipe. Ensure we have // no conflicts. for (const auto io : all_ios) { - if (io->io_mode == IO_BUFFER) { + if (io->io_mode == io_mode_t::buffer) { auto *io_buffer = static_cast(io.get()); if (!io_buffer->avoid_conflicts_with_io_chain(all_ios)) { // We could not avoid conflicts, probably due to fd exhaustion. Mark an error. diff --git a/src/io.cpp b/src/io.cpp index dc916c0b8..2f8fa32af 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -43,7 +43,7 @@ void io_buffer_t::append_from_stream(const output_stream_t &stream) { void io_buffer_t::read() { exec_close(pipe_fd[1]); - if (io_mode == IO_BUFFER) { + if (io_mode == io_mode_t::buffer) { debug(4, L"io_buffer_t::read: blocking read on fd %d", pipe_fd[0]); while (1) { char b[4096]; diff --git a/src/io.h b/src/io.h index f6475d363..cee99ccab 100644 --- a/src/io.h +++ b/src/io.h @@ -150,7 +150,7 @@ class separated_buffer_t { }; /// 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 }; +enum class io_mode_t { file, pipe, fd, buffer, close }; /// Represents an FD redirection. class io_data_t { @@ -174,7 +174,7 @@ class io_data_t { class io_close_t : public io_data_t { public: - explicit io_close_t(int f) : io_data_t(IO_CLOSE, f) {} + explicit io_close_t(int f) : io_data_t(io_mode_t::close, f) {} void print() const override; }; @@ -191,7 +191,8 @@ class io_fd_t : public io_data_t { void print() const override; - 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_mode_t::fd, f), old_fd(old), user_supplied(us) {} }; class io_file_t : public io_data_t { @@ -204,7 +205,7 @@ class io_file_t : public io_data_t { void print() const override; io_file_t(int f, const wcstring &fname, int fl = 0) - : io_data_t(IO_FILE, f), filename_cstr(wcs2str(fname)), flags(fl) {} + : io_data_t(io_mode_t::file, f), filename_cstr(wcs2str(fname)), flags(fl) {} ~io_file_t() override { free((void *)filename_cstr); } }; @@ -221,7 +222,9 @@ class io_pipe_t : public io_data_t { void print() const override; - 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_mode_t::pipe, f), is_input(i) { + pipe_fd[0] = pipe_fd[1] = -1; + } }; class io_chain_t; @@ -231,8 +234,7 @@ class io_buffer_t : public io_pipe_t { separated_buffer_t buffer_; explicit io_buffer_t(int f, size_t limit) - : io_pipe_t(IO_BUFFER, f, false /* not input */), - buffer_(limit) { + : io_pipe_t(io_mode_t::buffer, f, false /* not input */), buffer_(limit) { // Explicitly reset the discard flag because we share this buffer. buffer_.reset_discard(); } @@ -258,8 +260,8 @@ class io_buffer_t : public io_pipe_t { /// Marks the receiver as discarded if the stream was discarded. void append_from_stream(const output_stream_t &stream); - /// 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. + /// Create a io_mode_t::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 diff --git a/src/postfork.cpp b/src/postfork.cpp index dccef08bc..7a6f655c0 100644 --- a/src/postfork.cpp +++ b/src/postfork.cpp @@ -187,12 +187,12 @@ 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_mode_t::fd && io->fd == static_cast(io)->old_fd) { continue; } switch (io->io_mode) { - case IO_CLOSE: { + case io_mode_t::close: { if (log_redirections) fwprintf(stderr, L"%d: close %d\n", getpid(), io->fd); if (close(io->fd)) { debug_safe_int(0, "Failed to close file descriptor %s", io->fd); @@ -201,7 +201,7 @@ static int handle_child_io(const io_chain_t &io_chain) { break; } - case IO_FILE: { + case io_mode_t::file: { // Here we definitely do not want to set CLO_EXEC because our child needs access. const io_file_t *io_file = static_cast(io); int tmp = open(io_file->filename_cstr, io_file->flags, OPEN_MASK); @@ -229,7 +229,7 @@ static int handle_child_io(const io_chain_t &io_chain) { break; } - case IO_FD: { + case io_mode_t::fd: { int old_fd = static_cast(io)->old_fd; if (log_redirections) fwprintf(stderr, L"%d: fd dup %d to %d\n", getpid(), old_fd, io->fd); @@ -245,20 +245,20 @@ static int handle_child_io(const io_chain_t &io_chain) { break; } - case IO_BUFFER: - case IO_PIPE: { + case io_mode_t::buffer: + case io_mode_t::pipe: { const io_pipe_t *io_pipe = static_cast(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). unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1); #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->io_mode == io_mode_t::buffer)?L"buffer":L"pipe", io->fd, io->pipe_fd[0], io->pipe_fd[1]); #endif if (log_redirections) fwprintf(stderr, L"%d: %s dup %d to %d\n", getpid(), - io->io_mode == IO_BUFFER ? "buffer" : "pipe", + io->io_mode == io_mode_t::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); @@ -280,7 +280,7 @@ int setup_child_process(process_t *p, const io_chain_t &io_chain) { bool ok = true; if (ok) { - // In the case of IO_FILE, this can hang until data is available to read/write! + // In the case of io_mode_t::file, this can hang until data is available to read/write! ok = (0 == handle_child_io(io_chain)); if (p != 0 && !ok) { debug_safe(4, "handle_child_io failed in setup_child_process"); @@ -405,18 +405,18 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, 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_mode_t::fd) { const io_fd_t *io_fd = static_cast(io.get()); if (io->fd == io_fd->old_fd) continue; } switch (io->io_mode) { - case IO_CLOSE: { + case io_mode_t::close: { if (!err) err = posix_spawn_file_actions_addclose(actions, io->fd); break; } - case IO_FILE: { + case io_mode_t::file: { const io_file_t *io_file = static_cast(io.get()); if (!err) err = posix_spawn_file_actions_addopen(actions, io->fd, io_file->filename_cstr, @@ -424,7 +424,7 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, break; } - case IO_FD: { + case io_mode_t::fd: { const io_fd_t *io_fd = static_cast(io.get()); if (!err) err = posix_spawn_file_actions_adddup2(actions, io_fd->old_fd /* from */, @@ -432,8 +432,8 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, break; } - case IO_BUFFER: - case IO_PIPE: { + case io_mode_t::buffer: + case io_mode_t::pipe: { const io_pipe_t *io_pipe = static_cast(io.get()); unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1); int from_fd = io_pipe->pipe_fd[write_pipe_idx]; diff --git a/src/proc.cpp b/src/proc.cpp index 8aa59fde1..162ae35f6 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -850,7 +850,7 @@ static select_try_t select_try(job_t *j) { const io_chain_t chain = j->all_io_redirections(); for (const auto &io : chain) { - if (io->io_mode == IO_BUFFER) { + if (io->io_mode == io_mode_t::buffer) { auto io_pipe = static_cast(io.get()); int fd = io_pipe->pipe_fd[0]; FD_SET(fd, &fds); @@ -886,7 +886,7 @@ static void read_try(job_t *j) { const io_chain_t chain = j->all_io_redirections(); 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_mode_t::buffer) { buff = static_cast(d); } } From 0c17210f056f5d278f2a12290448b8eaca80d827 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 31 Jan 2019 12:29:10 -0800 Subject: [PATCH 327/439] Fix the expect tests under tmux by inspecting TERM We were checking for the $TMUX variable to determine if we were running under tmux. However when running the tests, the terminal becomes expect, even though the TMUX variable is still set, so we spew tmux-isms at expect. Check the value of $TERM for 'screen'. --- share/functions/__fish_config_interactive.fish | 1 + 1 file changed, 1 insertion(+) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 157265146..d59491b16 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -239,6 +239,7 @@ function __fish_config_interactive -d "Initializations that should be performed # - The keybindings (reading the sequence and triggering an event) # - Any listeners (like the vi-cursor) if set -q TMUX + and [ "$TERM" = screen ] function __fish_enable_focus --on-event fish_postexec echo -n \e\[\?1004h end From 371f67f1b567a08d5b9f62c97ae5a619a5051420 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 31 Jan 2019 17:58:59 -0800 Subject: [PATCH 328/439] Remove pipe_read_fd In practice it was always STDIN_FILENO. --- src/exec.cpp | 2 +- src/proc.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index d180ef826..c6115c477 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -902,7 +902,7 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< // Read pipe goes last. if (!p->is_first_in_job) { - pipe_read.reset(new io_pipe_t(p->pipe_read_fd, true)); + pipe_read.reset(new io_pipe_t(STDIN_FILENO, true)); // Record the current read in pipe_read. pipe_read->pipe_fd[0] = pipe_current_read.fd(); process_net_io_chain.push_back(pipe_read); diff --git a/src/proc.h b/src/proc.h index b48e5d215..1620e4e5b 100644 --- a/src/proc.h +++ b/src/proc.h @@ -118,8 +118,6 @@ class process_t { pid_t pid{0}; /// File descriptor that pipe output should bind to. int pipe_write_fd{0}; - /// File descriptor that the _next_ process pipe input should bind to. - int pipe_read_fd{0}; /// True if process has completed. volatile int completed{false}; /// True if process has stopped. From b00f039489bbd0ec8e61144e40285744b567413a Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 31 Jan 2019 18:49:52 -0800 Subject: [PATCH 329/439] Clean up the io_chain_t interface --- src/exec.cpp | 2 +- src/fish_tests.cpp | 2 +- src/io.cpp | 17 ++++++----------- src/io.h | 9 ++++----- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index c6115c477..75af77921 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -1106,7 +1106,7 @@ static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, wcstrin io_buffer_t::create(STDOUT_FILENO, io_chain_t(), is_subcmd ? read_byte_limit : 0)); 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(); } diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index df66fd4b5..1760944a7 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -920,7 +920,7 @@ static void test_parser() { 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); + const io_chain_t io_chain{out_buff}; pthread_t thread = pthread_self(); double delay = 0.25 /* seconds */; iothread_perform([=]() { diff --git a/src/io.cpp b/src/io.cpp index 2f8fa32af..5cc0b733c 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -120,15 +120,15 @@ void io_chain_t::remove(const shared_ptr &element) { } } -void io_chain_t::push_back(const shared_ptr &element) { +void io_chain_t::push_back(shared_ptr element) { // Ensure we never push back NULL. - assert(element.get() != NULL); - std::vector >::push_back(element); + assert(element.get() != nullptr); + std::vector >::push_back(std::move(element)); } -void io_chain_t::push_front(const shared_ptr &element) { - assert(element.get() != NULL); - this->insert(this->begin(), element); +void io_chain_t::push_front(shared_ptr element) { + assert(element.get() != nullptr); + this->insert(this->begin(), std::move(element)); } void io_chain_t::append(const io_chain_t &chain) { @@ -253,8 +253,3 @@ shared_ptr io_chain_get(const io_chain_t &src, int 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 >() {} diff --git a/src/io.h b/src/io.h index cee99ccab..161ec5941 100644 --- a/src/io.h +++ b/src/io.h @@ -270,14 +270,13 @@ class io_buffer_t : public io_pipe_t { size_t buffer_limit = 0); }; -class io_chain_t : public std::vector > { +class io_chain_t : public std::vector> { public: - io_chain_t(); - explicit io_chain_t(const shared_ptr &); + using std::vector>::vector; void remove(const shared_ptr &element); - void push_back(const shared_ptr &element); - void push_front(const shared_ptr &element); + void push_back(shared_ptr element); + void push_front(shared_ptr element); void append(const io_chain_t &chain); shared_ptr get_io_for_fd(int fd) const; From df375ea12d9345a18e67ab95c71f35aadc3ff2d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Birger=20J=2E=20Nord=C3=B8lum?= Date: Fri, 1 Feb 2019 18:02:05 +0100 Subject: [PATCH 330/439] brew.fish: Add `update-reset` subcommand completion (#5608) * brew.fish: Add `update-reset` subcommand This command resets all tap's remotes to the latest available upstream. Ideal for debugging before reporting bugs or just housekeeping. Add missing newlines. * Add `brew.fish` changes to CHANGELOG.md --- CHANGELOG.md | 1 + share/completions/brew.fish | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4f111f62..4a92390c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ - The completions for `configure` now correctly offer directories. (#5518) - The `man` completions won't interpret the argument as a regex anymore. (#5566) - Killing the terminal while fish is in vi-normal mode will no longer send it spinning and eating CPU. (#5528) +- `brew.fish`: Add `update-reset` subcommand completion # fish 3.0.0 (released December 28, 2018) diff --git a/share/completions/brew.fish b/share/completions/brew.fish index 9b925645e..e3930c41b 100644 --- a/share/completions/brew.fish +++ b/share/completions/brew.fish @@ -356,6 +356,9 @@ complete -f -c brew -n '__fish_brew_using_command untap' -a '(__fish_brew_taps)' complete -f -c brew -n '__fish_brew_needs_command' -a update -d 'Fetch newest version of Homebrew and formulas' complete -f -c brew -n '__fish_brew_using_command update' -l rebase -d 'Use git pull --rebase' +# update-reset +complete -f -c brew -n '__fish_brew_needs_command' -a update-reset -d 'Reset all Homebrew taps to upstream state' + # upgrade complete -f -c brew -n '__fish_brew_needs_command' -a upgrade -d 'Upgrade outdated brews' complete -f -c brew -n '__fish_brew_using_command upgrade' -a '(__fish_brew_outdated_formulas)' @@ -431,6 +434,7 @@ complete -f -c brew -n '__fish_brew_is_subcommand_cask homepage' -a '(__fish_bre # info complete -f -c brew -n '__fish_brew_needs_cask_action' -a 'info abv' -d 'Dislay info about cask' complete -f -c brew -n '__fish_brew_is_subcommand_cask info' -a '(__fish_brew_casks)' + # adv complete -f -c brew -n '__fish_brew_is_subcommand_cask abv' -a '(__fish_brew_casks)' @@ -475,9 +479,11 @@ complete -f -c brew -n '__fish_brew_is_subcommand_cask style' -a '(__fish_brew_c complete -f -c brew -n '__fish_brew_needs_cask_action' -a 'remove rm uninstall' -d 'Uninstall cask' complete -f -c brew -n '__fish_brew_is_subcommand_cask uninstall' -l force -d 'Force the uninstall' complete -f -c brew -n '__fish_brew_is_subcommand_cask uninstall' -a '(__fish_brew_casks_installed)' + # remove complete -f -c brew -n '__fish_brew_is_subcommand_cask remove' -l force -d 'Force the uninstall' complete -f -c brew -n '__fish_brew_is_subcommand_cask remove' -a '(__fish_brew_casks_installed)' + # rm complete -f -c brew -n '__fish_brew_is_subcommand_cask rm' -l force -d 'Force the uninstall' complete -f -c brew -n '__fish_brew_is_subcommand_cask rm' -a '(__fish_brew_casks_installed)' From ff89c61afaffc5d963e7a7acd75afd7fd8cf0dab Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 1 Feb 2019 18:29:54 +0100 Subject: [PATCH 331/439] `functions -q`: Return false without an argument This erroneously listed functions and returned true. --- src/builtin_functions.cpp | 5 +++++ tests/function.err | 3 +++ tests/function.in | 3 +++ tests/function.out | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/src/builtin_functions.cpp b/src/builtin_functions.cpp index 8b5b16d97..42575b503 100644 --- a/src/builtin_functions.cpp +++ b/src/builtin_functions.cpp @@ -337,6 +337,11 @@ int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return STATUS_CMD_OK; } + // If we query with no argument, just return false. + if (opts.query && argc == optind) { + return STATUS_CMD_ERROR; + } + if (opts.list || argc == optind) { wcstring_list_t names = function_get_names(opts.show_hidden); std::sort(names.begin(), names.end()); diff --git a/tests/function.err b/tests/function.err index 2d69cbb05..7257df763 100644 --- a/tests/function.err +++ b/tests/function.err @@ -29,3 +29,6 @@ fish: function: The name 'test' is reserved, and can not be used as a function name function test; echo banana; end ^ + +#################### +# Checking `functions -q` without arguments diff --git a/tests/function.in b/tests/function.in index ef9bd537d..a65183076 100644 --- a/tests/function.in +++ b/tests/function.in @@ -58,4 +58,7 @@ test "$name3[2..-1]" = "$name3a[2..-1]"; and echo "3 = 3a" logmsg Checking reserved names function test; echo banana; end + +logmsg Checking `functions -q` without arguments +functions -q; or echo "False" exit 0 diff --git a/tests/function.out b/tests/function.out index 0e44e0182..aec07aabf 100644 --- a/tests/function.out +++ b/tests/function.out @@ -71,3 +71,7 @@ function name3a --argument arg1 arg2 #################### # Checking reserved names + +#################### +# Checking `functions -q` without arguments +False From fd1908e973413e3cdb86c12ea8386422a78a3273 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 1 Feb 2019 14:42:13 -0800 Subject: [PATCH 332/439] Switch TMUX check to FISH_UNIT_TESTS_RUNNING Per discussion in https://github.com/fish-shell/fish-shell/commit/0c17210f056 --- share/functions/__fish_config_interactive.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index d59491b16..42cead73f 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -239,7 +239,7 @@ function __fish_config_interactive -d "Initializations that should be performed # - The keybindings (reading the sequence and triggering an event) # - Any listeners (like the vi-cursor) if set -q TMUX - and [ "$TERM" = screen ] + and not set -q FISH_UNIT_TESTS_RUNNING function __fish_enable_focus --on-event fish_postexec echo -n \e\[\?1004h end From 93c0d3f4a58ee128624309ce91342f00cd87343c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 1 Feb 2019 18:29:54 +0100 Subject: [PATCH 333/439] `functions -q`: Return false without an argument This erroneously listed functions and returned true. --- src/builtin_functions.cpp | 5 +++++ tests/function.err | 3 +++ tests/function.in | 3 +++ tests/function.out | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/src/builtin_functions.cpp b/src/builtin_functions.cpp index 8b5b16d97..42575b503 100644 --- a/src/builtin_functions.cpp +++ b/src/builtin_functions.cpp @@ -337,6 +337,11 @@ int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return STATUS_CMD_OK; } + // If we query with no argument, just return false. + if (opts.query && argc == optind) { + return STATUS_CMD_ERROR; + } + if (opts.list || argc == optind) { wcstring_list_t names = function_get_names(opts.show_hidden); std::sort(names.begin(), names.end()); diff --git a/tests/function.err b/tests/function.err index 2d69cbb05..7257df763 100644 --- a/tests/function.err +++ b/tests/function.err @@ -29,3 +29,6 @@ fish: function: The name 'test' is reserved, and can not be used as a function name function test; echo banana; end ^ + +#################### +# Checking `functions -q` without arguments diff --git a/tests/function.in b/tests/function.in index 5f64ed777..a774167e4 100644 --- a/tests/function.in +++ b/tests/function.in @@ -48,4 +48,7 @@ diff (functions name3 | psub) (functions name3a | psub) logmsg Checking reserved names function test; echo banana; end + +logmsg Checking `functions -q` without arguments +functions -q; or echo "False" exit 0 diff --git a/tests/function.out b/tests/function.out index 95a79ed2f..6cfd7379e 100644 --- a/tests/function.out +++ b/tests/function.out @@ -73,3 +73,7 @@ Function name4 not found as expected #################### # Checking reserved names + +#################### +# Checking `functions -q` without arguments +False From b54f1842d5a6c7868eaea16035c000c2d1f88d5b Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 28 Jan 2019 22:25:55 -0600 Subject: [PATCH 334/439] Switch to wait_by_process when `waitpid` without WNOHANG returns nothing By exclusively waiting by pgrp, we can fail to reap processes that change their own pgrp then either crash or close their fds. If we wind up in a situation where `waitpid(2)` returns 0 or ECHLD even though we did not specify `WNOHANG` but we still have unreaped child processes, wait on them by pid. Closes #5596. --- src/proc.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/proc.cpp b/src/proc.cpp index 162ae35f6..cdacd05bb 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -519,6 +519,7 @@ static bool process_mark_finished_children(bool block_on_fg) { if (pid > 0) { // A child process has been reaped + debug(4, "Reaped PID %d", pid); handle_child_status(pid, status); // Always set WNOHANG (that is, don't hang). Otherwise we might wait on a non-stopped job @@ -528,15 +529,22 @@ static bool process_mark_finished_children(bool block_on_fg) { } else if (pid == 0 || errno == ECHILD) { // No killed/dead children in this particular process group if (!wait_by_process) { + if ((options & WNOHANG) == 0) { + // This normally implies that the job has completed, but if we try to wait + // on a job that includes a process that changed its own group before we + // enter `waitpid`, we will be waiting forever. See #5596 for such a case. + wait_by_process = true; + continue; + } break; } } else { // pid < 0 indicates an error. One likely failure is ECHILD (no children), which is - // not an error and is ignored. The other likely failure is EINTR, which means we - // got a signal, which is considered an error. We absolutely do not break or return - // on error, as we need to iterate over all constructed jobs but we only call - // waitpid for one pgrp at a time. We do bypass future waits in case of error, - // however. + // not an error and is ignored in the branch above. The other likely failure is + // EINTR, which means we got a signal, which is considered an error. We absolutely + // do not break or return on error, as we need to iterate over all constructed jobs + // but we only call waitpid for one pgrp at a time. We do bypass future waits in + // case of error, however. has_error = true; // Do not audibly complain on interrupt (see #5293) From 4dfaa33d95a2cc16f4a8eb757948bd9053dfbb71 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 28 Jan 2019 22:25:55 -0600 Subject: [PATCH 335/439] Switch to wait_by_process when `waitpid` without WNOHANG returns nothing By exclusively waiting by pgrp, we can fail to reap processes that change their own pgrp then either crash or close their fds. If we wind up in a situation where `waitpid(2)` returns 0 or ECHLD even though we did not specify `WNOHANG` but we still have unreaped child processes, wait on them by pid. Closes #5596. --- src/proc.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/proc.cpp b/src/proc.cpp index 0228d176b..f61b2d0c2 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -511,6 +511,7 @@ static bool process_mark_finished_children(bool block_on_fg) { if (pid > 0) { // A child process has been reaped + debug(4, "Reaped PID %d", pid); handle_child_status(pid, status); // Always set WNOHANG (that is, don't hang). Otherwise we might wait on a non-stopped job @@ -520,15 +521,22 @@ static bool process_mark_finished_children(bool block_on_fg) { } else if (pid == 0 || errno == ECHILD) { // No killed/dead children in this particular process group if (!wait_by_process) { + if ((options & WNOHANG) == 0) { + // This normally implies that the job has completed, but if we try to wait + // on a job that includes a process that changed its own group before we + // enter `waitpid`, we will be waiting forever. See #5596 for such a case. + wait_by_process = true; + continue; + } break; } } else { // pid < 0 indicates an error. One likely failure is ECHILD (no children), which is - // not an error and is ignored. The other likely failure is EINTR, which means we - // got a signal, which is considered an error. We absolutely do not break or return - // on error, as we need to iterate over all constructed jobs but we only call - // waitpid for one pgrp at a time. We do bypass future waits in case of error, - // however. + // not an error and is ignored in the branch above. The other likely failure is + // EINTR, which means we got a signal, which is considered an error. We absolutely + // do not break or return on error, as we need to iterate over all constructed jobs + // but we only call waitpid for one pgrp at a time. We do bypass future waits in + // case of error, however. has_error = true; // Do not audibly complain on interrupt (see #5293) From 88dc484858f708ef5ac14c928fc5cf7c3192397a Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 28 Jan 2019 13:26:22 -0800 Subject: [PATCH 336/439] Introduce dup2_list_t This represents a "resolved" io_chain_t, where all of the different io_data_t types have been reduced to a sequence of dup2() and close(). This will eliminate a lot of the logic duplication around posix_spawn vs fork, and pave the way for in-process redirections. --- CMakeLists.txt | 2 +- Makefile.in | 2 +- src/fish_tests.cpp | 27 ++++++++++++++ src/io.cpp | 9 ++--- src/io.h | 6 +++ src/redirection.cpp | 90 +++++++++++++++++++++++++++++++++++++++++++++ src/redirection.h | 62 +++++++++++++++++++++++++++++++ 7 files changed, 190 insertions(+), 8 deletions(-) create mode 100644 src/redirection.cpp create mode 100644 src/redirection.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 882888c7a..809f24162 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ SET(FISH_SRCS src/postfork.cpp src/proc.cpp src/reader.cpp src/sanity.cpp src/screen.cpp src/signal.cpp src/tinyexpr.cpp src/tnode.cpp src/tokenizer.cpp src/utf8.cpp src/util.cpp src/wcstringutil.cpp src/wgetopt.cpp src/wildcard.cpp src/wutil.cpp - src/future_feature_flags.cpp + src/future_feature_flags.cpp src/redirection.cpp ) # Header files are just globbed. diff --git a/Makefile.in b/Makefile.in index aa5e0e2ee..059e05847 100644 --- a/Makefile.in +++ b/Makefile.in @@ -119,7 +119,7 @@ FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_bg.o obj/builtin_bind.o ob 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/tinyexpr.o obj/tokenizer.o obj/tnode.o obj/utf8.o \ obj/util.o obj/wcstringutil.o obj/wgetopt.o obj/wildcard.o obj/wutil.o \ - obj/future_feature_flags.o + obj/future_feature_flags.o obj/redirection.o FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 1760944a7..8f266947d 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -64,6 +64,7 @@ #include "path.h" #include "proc.h" #include "reader.h" +#include "redirection.h" #include "screen.h" #include "signal.h" #include "tnode.h" @@ -2339,6 +2340,31 @@ static void test_wcstod() { tod_test(L"nope", "nope"); } +static void test_dup2s() { + using std::make_shared; + io_chain_t chain; + chain.push_back(make_shared(17)); + chain.push_back(make_shared(3, 19, true)); + auto list = dup2_list_t::resolve_chain(chain); + do_test(list.has_value()); + do_test(list->get_actions().size() == 2); + + auto act1 = list->get_actions().at(0); + do_test(act1.src == 17); + do_test(act1.target == -1); + + auto act2 = list->get_actions().at(1); + do_test(act2.src == 19); + do_test(act2.target == 3); + + // Invalid files should fail to open. + // Suppress the debug() message. + scoped_push saved_debug_level(&debug_level, -1); + chain.push_back(make_shared(2, L"/definitely/not/a/valid/path/for/this/test", 0666)); + list = dup2_list_t::resolve_chain(chain); + do_test(!list.has_value()); +} + /// Testing colors. static void test_colors() { say(L"Testing colors"); @@ -5071,6 +5097,7 @@ int main(int argc, char **argv) { if (should_test_function("abbreviations")) test_abbreviations(); if (should_test_function("test")) test_test(); if (should_test_function("wcstod")) test_wcstod(); + if (should_test_function("dup2s")) test_dup2s(); if (should_test_function("path")) test_path(); if (should_test_function("pager_navigation")) test_pager_navigation(); if (should_test_function("pager_layout")) test_pager_layout(); diff --git a/src/io.cpp b/src/io.cpp index 5cc0b733c..091d250f5 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -163,11 +163,8 @@ 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) { + +int move_fd_to_unused(int fd, const io_chain_t &io_chain, bool cloexec) { if (fd < 0 || io_chain.get_io_for_fd(fd).get() == NULL) { return fd; } @@ -188,7 +185,7 @@ static int move_fd_to_unused(int fd, const io_chain_t &io_chain) { // 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); + if (cloexec) set_cloexec(tmp_fd); new_fd = move_fd_to_unused(tmp_fd, io_chain); } diff --git a/src/io.h b/src/io.h index 161ec5941..e0200c9f3 100644 --- a/src/io.h +++ b/src/io.h @@ -293,6 +293,12 @@ shared_ptr io_chain_get(io_chain_t &src, int fd); /// set to -1). bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios); +/// 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. +/// If \p cloexec is set, any fd created is marked close-on-exec. +/// \returns -1 on failure (in which case the given fd is still closed). +int move_fd_to_unused(int fd, const io_chain_t &io_chain, bool cloexec = true); + /// Class representing the output that a builtin can generate. class output_stream_t { private: diff --git a/src/redirection.cpp b/src/redirection.cpp new file mode 100644 index 000000000..1301f1e4a --- /dev/null +++ b/src/redirection.cpp @@ -0,0 +1,90 @@ +#include "config.h" // IWYU pragma: keep + +#include "redirection.h" +#include "wutil.h" + +#include + +/// File descriptor redirection error message. +#define FD_ERROR "An error occurred while redirecting file descriptor %s" + +/// Pipe error message. +#define LOCAL_PIPE_ERROR "An error occurred while setting up pipe" + +#define NOCLOB_ERROR _(L"The file '%s' already exists") + +#define FILE_ERROR _(L"An error occurred while redirecting file '%s'") + +/// Base open mode to pass to calls to open. +#define OPEN_MASK 0666 + +dup2_list_t::~dup2_list_t() = default; + +maybe_t dup2_list_t::resolve_chain(const io_chain_t &io_chain) { + ASSERT_IS_NOT_FORKED_CHILD(); + dup2_list_t result; + for (const auto &io_ref : io_chain) { + switch (io_ref->io_mode) { + case io_mode_t::file: { + // Here we definitely do not want to set CLO_EXEC because our child needs access. + // Open the file. + const io_file_t *io_file = static_cast(io_ref.get()); + int file_fd = open(io_file->filename_cstr, io_file->flags, OPEN_MASK); + if (file_fd < 0) { + if ((io_file->flags & O_EXCL) && (errno == EEXIST)) { + debug(1, NOCLOB_ERROR, io_file->filename_cstr); + } else { + debug(1, FILE_ERROR, io_file->filename_cstr); + if (should_debug(1)) wperror(L"open"); + } + return none(); + } + + // If by chance we got the file we want, we're done. Otherwise move the fd to an unused place and dup2 it. + // Note move_fd_to_unused() will close the incoming file_fd. + if (file_fd != io_file->fd) { + file_fd = move_fd_to_unused(file_fd, io_chain, false /* cloexec */); + if (file_fd < 0) { + debug(1, FILE_ERROR, io_file->filename_cstr); + if (should_debug(1)) wperror(L"dup"); + return none(); + } + } + + // Record that we opened this file, so we will auto-close it. + assert(file_fd >= 0 && "Should have a valid file_fd"); + result.opened_fds_.emplace_back(file_fd); + + // Mark our dup2 and our close actions. + result.add_dup2(file_fd, io_file->fd); + result.add_close(file_fd); + break; + } + + case io_mode_t::close: { + const io_close_t *io = static_cast(io_ref.get()); + result.add_close(io->fd); + break; + } + + case io_mode_t::fd: { + const io_fd_t *io = static_cast(io_ref.get()); + result.add_dup2(io->old_fd, io->fd); + break; + } + + case io_mode_t::buffer: + case io_mode_t::pipe: { + const io_pipe_t *io = static_cast(io_ref.get()); + // If write_pipe_idx is 0, it means we're connecting to the read end (first pipe + // fd). If it's 1, we're connecting to the write end (second pipe fd). + unsigned int write_pipe_idx = (io->is_input ? 0 : 1); + result.add_dup2(io->pipe_fd[write_pipe_idx], io->fd); + if (io->pipe_fd[0] >= 0) result.add_close(io->pipe_fd[0]); + if (io->pipe_fd[1] >= 0) result.add_close(io->pipe_fd[1]); + break; + } + } + } + return result; +} diff --git a/src/redirection.h b/src/redirection.h new file mode 100644 index 000000000..e82d4376d --- /dev/null +++ b/src/redirection.h @@ -0,0 +1,62 @@ +#ifndef FISH_REDIRECTION_H +#define FISH_REDIRECTION_H + +#include "common.h" +#include "maybe.h" +#include "io.h" + +#include + +/// This file supports "applying" redirections. + +/// A class representing a sequence of basic redirections. +class dup2_list_t { + /// A type that represents the action dup2(src, target). + /// If target is negative, this represents close(src). + /// Note none of the fds here are considered 'owned'. + struct action_t { + int src; + int target; + }; + + /// The list of actions. + std::vector actions_; + + /// The list of fds that we opened, and are responsible for closing. + std::vector opened_fds_; + + /// Append a dup2 action. + void add_dup2(int src, int target) { + assert(src >= 0 && target >= 0 && "Invalid fd in add_dup2"); + if (src != target) { + actions_.push_back(action_t{src, target}); + } + } + + /// Append a close action. + void add_close(int fd) { + assert(fd >= 0 && "Invalid fd in add_close"); + actions_.push_back(action_t{fd, -1}); + } + + dup2_list_t() = default; + +public: + ~dup2_list_t(); + + /// Disable copying because we own our fds. + dup2_list_t(const dup2_list_t &) = delete; + void operator=(const dup2_list_t &) = delete; + + dup2_list_t(dup2_list_t &&) = default; + dup2_list_t &operator=(dup2_list_t &&) = default; + + /// \return the list of dup2 actions. + const std::vector &get_actions() const { return actions_; } + + /// Produce a dup_fd_list_t from an io_chain. This may not be called before fork(). + /// The result contains the list of fd actions (dup2 and close), as well as the list of fds opened. + static maybe_t resolve_chain(const io_chain_t &); +}; + +#endif From b956f1388009ab8a8653c28fcb7e2f3981547ca7 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 28 Jan 2019 23:17:42 -0800 Subject: [PATCH 337/439] Switch from tee to cat in psub --fifo Prior to this fix, we would write to a fifo via cat >$filename & . However in some cases (and soon in all cases) we open the file before the fork, not after. This results in a deadlock because the file open cannot succeed until a write begins. Switch to using tee to write to the file. Because tee opens the file itself, fish is no longer responsible and the deadlock is resolved. --- share/functions/psub.fish | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/share/functions/psub.fish b/share/functions/psub.fish index b764d6f35..8c8a7eac8 100644 --- a/share/functions/psub.fish +++ b/share/functions/psub.fish @@ -29,7 +29,10 @@ function psub --description "Read from stdin into a file and output the filename or return set filename $dirname/psub.fifo"$_flag_suffix" mkfifo $filename - cat >$filename & + # Note that if we were to do the obvious `cat >$filename &`, we would deadlock + # because $filename may be opened before the fork. Use tee to ensure it is opened + # after the fork. + tee $filename >/dev/null & else if test -z "$_flag_suffix" set filename (mktemp $tmpdir/.psub.XXXXXXXXXX) cat >$filename From d895075d9b41efef5ee784cbe56fdc67355f5fb9 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 28 Jan 2019 14:35:56 -0800 Subject: [PATCH 338/439] Adopt dup2_list_t in fork execution path This switches IO redirections after fork() to use the dup2_list_t, instead of io_chain_t. This results in simpler code with much simpler error handling. --- src/exec.cpp | 31 ++++++++-- src/postfork.cpp | 147 ++++------------------------------------------ src/postfork.h | 5 +- src/redirection.h | 2 + 4 files changed, 42 insertions(+), 143 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index 75af77921..3d51914b2 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -39,6 +39,7 @@ #include "postfork.h" #include "proc.h" #include "reader.h" +#include "redirection.h" #include "signal.h" #include "wutil.h" // IWYU pragma: keep @@ -367,7 +368,8 @@ void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &all_ios) { // 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(0, all_ios)) { + auto redirs = dup2_list_t::resolve_chain(all_ios); + if (redirs && !setup_child_process(0, *redirs)) { // Decrement SHLVL as we're removing ourselves from the shell "stack". auto shlvl_var = vars.get(L"SHLVL", ENV_GLOBAL | ENV_EXPORT); wcstring shlvl_str = L"0"; @@ -404,7 +406,7 @@ static void on_process_created(const std::shared_ptr &j, pid_t child_pid) /// Call fork() as part of executing a process \p p in a job \j. Execute \p child_action in the /// context of the child. Returns true if fork succeeded, false if fork failed. static bool fork_child_for_process(const std::shared_ptr &job, process_t *p, - const io_chain_t &io_chain, bool drain_threads, + const dup2_list_t &dup2s, bool drain_threads, const char *fork_type, const std::function &child_action) { pid_t pid = execute_fork(drain_threads); @@ -413,7 +415,7 @@ static bool fork_child_for_process(const std::shared_ptr &job, process_t // stdout and stderr, and then exit. p->pid = getpid(); child_set_group(job.get(), p); - setup_child_process(p, io_chain); + setup_child_process(p, dup2s); child_action(); DIE("Child process returned control to fork_child lambda!"); } @@ -634,9 +636,15 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, const char *errbuff = errbuff_str.data(); size_t errbuff_len = errbuff_str.size(); + // Resolve our IO chain to a sequence of dup2s. + auto dup2s = dup2_list_t::resolve_chain(*io_chain); + if (!dup2s) { + return false; + } + fflush(stdout); fflush(stderr); - if (!fork_child_for_process(j, p, *io_chain, false, "internal builtin", [&] { + if (!fork_child_for_process(j, p, *dup2s, false, "internal builtin", [&] { do_builtin_io(outbuff, outbuff_len, errbuff, errbuff_len); exit_without_destructors(p->status); })) { @@ -655,6 +663,11 @@ static bool exec_external_command(env_stack_t &vars, const std::shared_ptr argv_array; convert_wide_array_to_narrow(p->get_argv_array(), &argv_array); + // Convert our IO chain to a dup2 sequence. + auto dup2s = dup2_list_t::resolve_chain(proc_io_chain); + if (! dup2s) + return false; + // 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 @@ -741,7 +754,7 @@ static bool exec_external_command(env_stack_t &vars, const std::shared_ptr io_chain.remove(block_output_io_buffer); block_output_io_buffer->read(); + // Resolve our IO chain to a sequence of dup2s. + auto dup2s = dup2_list_t::resolve_chain(io_chain); + if (!dup2s) { + return false; + } + const std::string buffer_contents = block_output_io_buffer->buffer().newline_serialized(); const char *buffer = buffer_contents.data(); size_t count = buffer_contents.size(); @@ -827,7 +846,7 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr // We don't have to drain threads here because our child process is simple. const char *fork_reason = p->type == INTERNAL_BLOCK_NODE ? "internal block io" : "internal function io"; - if (!fork_child_for_process(j, p, io_chain, false, fork_reason, [&] { + if (!fork_child_for_process(j, p, *dup2s, false, fork_reason, [&] { exec_write_and_exit(block_output_io_buffer->fd, buffer, count, status); })) { return false; diff --git a/src/postfork.cpp b/src/postfork.cpp index 7a6f655c0..4326d077a 100644 --- a/src/postfork.cpp +++ b/src/postfork.cpp @@ -19,6 +19,7 @@ #include "iothread.h" #include "postfork.h" #include "proc.h" +#include "redirection.h" #include "signal.h" #include "wutil.h" // IWYU pragma: keep @@ -38,27 +39,6 @@ /// Fork error message. #define FORK_ERROR "Could not create child process - exiting" -/// File redirection clobbering error message. -#define NOCLOB_ERROR "The file '%s' already exists" - -/// 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" - -/// 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) { - char buff[128]; - format_long_safe(buff, val); - debug_safe(level, format, buff); -} - /// Called only by the child to set its own process group (possibly creating a new group in the /// process if it is the first in a JOB_CONTROL job. /// Returns true on sucess, false on failiure. @@ -175,126 +155,23 @@ bool maybe_assign_terminal(const job_t *j) { return true; } -/// 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_chain 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_mode_t::fd && io->fd == static_cast(io)->old_fd) { - continue; - } - - switch (io->io_mode) { - case io_mode_t::close: { - if (log_redirections) fwprintf(stderr, L"%d: close %d\n", getpid(), io->fd); - if (close(io->fd)) { - debug_safe_int(0, "Failed to close file descriptor %s", io->fd); - safe_perror("close"); - } - break; - } - - case io_mode_t::file: { - // Here we definitely do not want to set CLO_EXEC because our child needs access. - const io_file_t *io_file = static_cast(io); - int tmp = open(io_file->filename_cstr, io_file->flags, OPEN_MASK); - if (tmp < 0) { - if ((io_file->flags & O_EXCL) && (errno == EEXIST)) { - debug_safe(1, NOCLOB_ERROR, io_file->filename_cstr); - } 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. - close(io->fd); - - if (dup2(tmp, io->fd) == -1) { - debug_safe_int(1, FD_ERROR, io->fd); - safe_perror("dup2"); - exec_close(tmp); - return -1; - } - exec_close(tmp); - } - break; - } - - case io_mode_t::fd: { - int old_fd = static_cast(io)->old_fd; - if (log_redirections) - fwprintf(stderr, L"%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. - close(io->fd); - - if (dup2(old_fd, io->fd) == -1) { - debug_safe_int(1, FD_ERROR, io->fd); - safe_perror("dup2"); - return -1; - } - break; - } - - case io_mode_t::buffer: - case io_mode_t::pipe: { - const io_pipe_t *io_pipe = static_cast(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). - unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1); -#if 0 - debug(0, L"%ls %ls on fd %d (%d %d)", write_pipe?L"write":L"read", - (io->io_mode == io_mode_t::buffer)?L"buffer":L"pipe", io->fd, io->pipe_fd[0], - io->pipe_fd[1]); -#endif - if (log_redirections) - fwprintf(stderr, L"%d: %s dup %d to %d\n", getpid(), - io->io_mode == io_mode_t::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]); - break; +int setup_child_process(process_t *p, const dup2_list_t &dup2s) { + for (const auto &act : dup2s.get_actions()) { + int err = act.target < 0 ? close(act.src) : dup2(act.src, act.target); + if (err < 0) { + // We have a null p if this is for the exec (non-fork) path. + if (p != nullptr) { + debug_safe(4, "redirect_in_child_after_fork failed in setup_child_process"); + exit_without_destructors(1); } + return err; } } - + // Set the handling for job control signals back to the default. + signal_reset_handlers(); return 0; } -int setup_child_process(process_t *p, const io_chain_t &io_chain) { - bool ok = true; - - if (ok) { - // In the case of io_mode_t::file, this can hang until data is available to read/write! - ok = (0 == handle_child_io(io_chain)); - if (p != 0 && !ok) { - debug_safe(4, "handle_child_io failed in setup_child_process"); - exit_without_destructors(1); - } - } - - if (ok) { - // Set the handling for job control signals back to the default. - signal_reset_handlers(); - } - - return ok ? 0 : -1; -} int g_fork_count = 0; diff --git a/src/postfork.h b/src/postfork.h index f7cfe080d..8ac8e82a3 100644 --- a/src/postfork.h +++ b/src/postfork.h @@ -14,6 +14,7 @@ #define FISH_USE_POSIX_SPAWN HAVE_SPAWN_H #endif +class dup2_list_t; class io_chain_t; class job_t; class process_t; @@ -29,11 +30,11 @@ bool maybe_assign_terminal(const job_t *j); /// descriptor actions are performed. /// /// \param p the child process to set up -/// \param io_chain the IO chain to use +/// \param dup2 the dup2 list to apply /// /// \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(process_t *p, const io_chain_t &io_chain); +int setup_child_process(process_t *p, const dup2_list_t &dup2s); /// 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 diff --git a/src/redirection.h b/src/redirection.h index e82d4376d..95cc8fac0 100644 --- a/src/redirection.h +++ b/src/redirection.h @@ -11,6 +11,7 @@ /// A class representing a sequence of basic redirections. class dup2_list_t { + public: /// A type that represents the action dup2(src, target). /// If target is negative, this represents close(src). /// Note none of the fds here are considered 'owned'. @@ -19,6 +20,7 @@ class dup2_list_t { int target; }; + private: /// The list of actions. std::vector actions_; From 4c0b6a6add0c361c962b0b8f96b29e1b5d7a107c Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 29 Jan 2019 00:34:38 -0800 Subject: [PATCH 339/439] Use dup2_list_t in posix_spawn This simplifies the posix_spawn path and unifies it with the fork execution path. --- src/exec.cpp | 3 +-- src/postfork.cpp | 58 ++++++++---------------------------------------- src/postfork.h | 5 ++--- 3 files changed, 12 insertions(+), 54 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index 3d51914b2..bf978345f 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -691,8 +691,7 @@ static bool exec_external_command(env_stack_t &vars, const std::shared_ptr io = io_chain.at(idx); - - if (io->io_mode == io_mode_t::fd) { - const io_fd_t *io_fd = static_cast(io.get()); - if (io->fd == io_fd->old_fd) continue; - } - - switch (io->io_mode) { - case io_mode_t::close: { - if (!err) err = posix_spawn_file_actions_addclose(actions, io->fd); - break; - } - - case io_mode_t::file: { - const io_file_t *io_file = static_cast(io.get()); - if (!err) - err = posix_spawn_file_actions_addopen(actions, io->fd, io_file->filename_cstr, - io_file->flags /* mode */, OPEN_MASK); - break; - } - - case io_mode_t::fd: { - const io_fd_t *io_fd = static_cast(io.get()); - if (!err) - err = posix_spawn_file_actions_adddup2(actions, io_fd->old_fd /* from */, - io->fd /* to */); - break; - } - - case io_mode_t::buffer: - case io_mode_t::pipe: { - const io_pipe_t *io_pipe = static_cast(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]); - } - break; - } + // Apply our dup2s. + for (const auto &act : dup2s.get_actions()) { + if (err) break; + if (act.target < 0) { + err = posix_spawn_file_actions_addclose(actions, act.src); + } else { + err = posix_spawn_file_actions_adddup2(actions, act.src, act.target); } } diff --git a/src/postfork.h b/src/postfork.h index 8ac8e82a3..d91d335cf 100644 --- a/src/postfork.h +++ b/src/postfork.h @@ -15,7 +15,6 @@ #endif class dup2_list_t; -class io_chain_t; class job_t; class process_t; @@ -56,8 +55,8 @@ void run_as_keepalive(pid_t parent_pid); /// 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); + posix_spawn_file_actions_t *actions, const job_t *j, + const dup2_list_t &dup2s); #endif #endif From 7c256e7e5163d6ed4037c29f9229ff567c5b3f28 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 29 Jan 2019 00:40:55 -0800 Subject: [PATCH 340/439] Allow posix_spawn more often Now that we no longer open files after fork, we can correctly report errors for failed file opens. So allow posix_spawn even if there's redirections. --- src/exec.cpp | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index bf978345f..78444b8b8 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -115,18 +115,6 @@ static bool redirection_is_to_real_file(const io_data_t *io) { return result; } -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++) { - const io_data_t *io = io_chain.at(idx).get(); - if (redirection_is_to_real_file(io)) { - result = true; - break; - } - } - 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) { @@ -330,11 +318,9 @@ void internal_exec_helper(parser_t &parser, parsed_source_ref_t parsed_source, t job_reap(false); } -// 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. +// Returns whether we can use posix spawn for a given process in a given job. // -// Furthermore, to avoid the race between the caller calling tcsetpgrp() and the client checking the +// 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 std::shared_ptr &job, @@ -348,15 +334,7 @@ static bool can_use_posix_spawn_for_job(const std::shared_ptr &job, 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. - bool result = true; - if (chain_contains_redirection_to_real_file(job->block_io_chain()) || - chain_contains_redirection_to_real_file(process->io_chain())) { - result = false; - } - return result; + return true; } void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &all_ios) { From 78bbcef3562109a4fa9978b49e18117837515cf0 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 31 Jan 2019 16:05:42 -0800 Subject: [PATCH 341/439] io_buffer_t becomes io_bufferfill_t This makes some significant architectual improvements to io_pipe_t and io_buffer_t. Prior to this fix, io_buffer_t subclassed io_pipe_t. io_buffer_t is now replaced with a class io_bufferfill_t, which does not subclass pipe. io_pipe_t no longer remembers both fds. Instead it has an autoclose_fd_t, so that the file descriptor ownership is clear. --- src/common.h | 3 + src/exec.cpp | 212 ++++++++++++++++---------------------------- src/exec.h | 4 - src/fish_tests.cpp | 13 +-- src/io.cpp | 150 ++++++++++++++++++------------- src/io.h | 115 ++++++++++++++++-------- src/proc.cpp | 117 ++++++------------------ src/redirection.cpp | 16 ++-- 8 files changed, 289 insertions(+), 341 deletions(-) diff --git a/src/common.h b/src/common.h index 8b45a808c..5fa2119ba 100644 --- a/src/common.h +++ b/src/common.h @@ -786,6 +786,9 @@ class autoclose_fd_t { fd_ = fd; } + // \return if this has a valid fd. + bool valid() const { return fd_ >= 0; } + autoclose_fd_t(const autoclose_fd_t &) = delete; void operator=(const autoclose_fd_t &) = delete; autoclose_fd_t(autoclose_fd_t &&rhs) : fd_(rhs.fd_) { rhs.fd_ = -1; } diff --git a/src/exec.cpp b/src/exec.cpp index 78444b8b8..7de91f5be 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -81,25 +81,6 @@ void exec_close(int fd) { } } -int exec_pipe(int fd[2]) { - ASSERT_IS_MAIN_THREAD(); - - int 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. - 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) { bool result = false; @@ -246,8 +227,8 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t *out_chain, switch (in->io_mode) { case io_mode_t::pipe: + case io_mode_t::bufferfill: case io_mode_t::fd: - case io_mode_t::buffer: case io_mode_t::close: { // These redirections don't need transmogrification. They can be passed through. out = in; @@ -424,12 +405,12 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptrtype == INTERNAL_BUILTIN && "Process must be a builtin"); int local_builtin_stdin = STDIN_FILENO; - bool close_stdin = false; + autoclose_fd_t locally_opened_stdin{}; // If this is the first process, check the io redirections and see where we should // be reading from. if (pipe_read) { - local_builtin_stdin = pipe_read->pipe_fd[0]; + local_builtin_stdin = pipe_read->pipe_fd(); } else if (const auto in = proc_io_chain.get_io_for_fd(STDIN_FILENO)) { switch (in->io_mode) { case io_mode_t::fd: { @@ -448,20 +429,20 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptr(in.get()); - local_builtin_stdin = in_pipe->pipe_fd[0]; + if (in_pipe->fd == STDIN_FILENO) { + local_builtin_stdin = in_pipe->pipe_fd(); + } break; } case io_mode_t::file: { - // Do not set CLO_EXEC because child needs access. const io_file_t *in_file = static_cast(in.get()); - local_builtin_stdin = open(in_file->filename_cstr, in_file->flags, OPEN_MASK); - if (local_builtin_stdin == -1) { + locally_opened_stdin = + autoclose_fd_t{open(in_file->filename_cstr, in_file->flags, OPEN_MASK)}; + if (!locally_opened_stdin.valid()) { debug(1, FILE_ERROR, in_file->filename_cstr); wperror(L"open"); - } else { - close_stdin = true; } - + local_builtin_stdin = locally_opened_stdin.fd(); break; } case io_mode_t::close: { @@ -517,10 +498,6 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptrset_flag(job_flag_t::FOREGROUND, fg); - // If stdin has been redirected, close the redirection stream. - if (close_stdin) { - exec_close(local_builtin_stdin); - } return true; // "success" } @@ -548,7 +525,11 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, if (!must_fork && p->is_last_in_job) { // We are handling reads directly in the main loop. Note that we may still end // up forking. - const bool stdout_is_to_buffer = stdout_io && stdout_io->io_mode == io_mode_t::buffer; + const bool stdout_is_bufferfill = + (stdout_io && stdout_io->io_mode == io_mode_t::bufferfill); + const std::shared_ptr stdout_buffer = + stdout_is_bufferfill ? static_cast(stdout_io.get())->buffer() + : nullptr; const bool no_stdout_output = stdout_stream.empty(); const bool no_stderr_output = stderr_stream.empty(); const bool stdout_discarded = stdout_stream.buffer().discarded(); @@ -558,7 +539,7 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, // need to fork or even output anything. debug(4, L"Skipping fork: no output for internal builtin '%ls'", p->argv0()); fork_was_skipped = true; - } else if (no_stderr_output && stdout_is_to_buffer) { + } else if (no_stderr_output && stdout_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. @@ -570,8 +551,7 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, // also produce stderr. debug(4, L"Skipping fork: buffered output for internal builtin '%ls'", p->argv0()); - io_buffer_t *io_buffer = static_cast(stdout_io.get()); - io_buffer->append_from_stream(stdout_stream); + stdout_buffer->append_from_stream(stdout_stream); 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. @@ -749,20 +729,16 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr "Unexpected process type"); // Create an output buffer if we're piping to another process. - shared_ptr block_output_io_buffer{}; + shared_ptr block_output_bufferfill{}; if (!p->is_last_in_job) { // Be careful to handle failure, e.g. too many open fds. - block_output_io_buffer = io_buffer_t::create(STDOUT_FILENO, user_ios); - if (!block_output_io_buffer) { + block_output_bufferfill = io_bufferfill_t::create(user_ios); + if (!block_output_bufferfill) { job_mark_process_as_failed(j, p); return false; - } 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. - io_chain.push_back(block_output_io_buffer); } + // Teach the job about its bufferfill, and add it to our io chain. + io_chain.push_back(block_output_bufferfill); } if (p->type == INTERNAL_FUNCTION) { @@ -792,10 +768,8 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr 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()) { + // If we have a block output buffer, populate it now. + if (!block_output_bufferfill) { // No buffer, so we exit directly. This means we have to manually set the exit // status. if (p->is_last_in_job) { @@ -804,11 +778,16 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr p->completed = 1; return true; } + assert(block_output_bufferfill && "Must have a block output bufferfiller"); - // Here we must have a non-NULL block_output_io_buffer. - assert(block_output_io_buffer.get() != NULL); - io_chain.remove(block_output_io_buffer); - block_output_io_buffer->read(); + // Remove our write pipe and forget it. This may close the pipe, unless another thread has + // claimed it (background write) or another process has inherited it. + auto block_output_buffer = block_output_bufferfill->buffer(); + io_chain.remove(block_output_bufferfill); + block_output_bufferfill.reset(); + + // Make the buffer populate itself from whatever was written to the write pipe. + block_output_buffer->read_to_wouldblock(); // Resolve our IO chain to a sequence of dup2s. auto dup2s = dup2_list_t::resolve_chain(io_chain); @@ -816,7 +795,7 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr return false; } - const std::string buffer_contents = block_output_io_buffer->buffer().newline_serialized(); + const std::string buffer_contents = block_output_buffer->buffer().newline_serialized(); const char *buffer = buffer_contents.data(); size_t count = buffer_contents.size(); if (count > 0) { @@ -824,7 +803,7 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr const char *fork_reason = p->type == INTERNAL_BLOCK_NODE ? "internal block io" : "internal function io"; if (!fork_child_for_process(j, p, *dup2s, false, fork_reason, [&] { - exec_write_and_exit(block_output_io_buffer->fd, buffer, count, status); + exec_write_and_exit(STDOUT_FILENO, buffer, count, status); })) { return false; } @@ -844,19 +823,28 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< autoclose_fd_t pipe_current_read, autoclose_fd_t *out_pipe_next_read, const io_chain_t &all_ios, size_t stdout_read_limit) { - // 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(); + // The pipe this command will write to (if any). + shared_ptr pipe_write; + // The pipe this command will read from (if any). + shared_ptr pipe_read; - // See if we need a pipe. + // See if we need a pipe for the next command. const bool pipes_to_next_command = !p->is_last_in_job; + if (pipes_to_next_command) { + // Construct our pipes. + auto local_pipes = make_autoclose_pipes(all_ios); + if (!local_pipes) { + debug(1, PIPE_ERROR); + wperror(L"pipe"); + job_mark_process_as_failed(j, p); + return false; + } - // The write end of any pipe we create. - autoclose_fd_t pipe_current_write{}; + pipe_write = std::make_shared(p->pipe_write_fd, false /* not input */, + std::move(local_pipes->write)); + *out_pipe_next_read = std::move(local_pipes->read); + } - // 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: // @@ -884,12 +872,10 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< // // 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 (pipes_to_next_command) { - pipe_write.reset(new io_pipe_t(p->pipe_write_fd, false)); + // The IO chain for this process. + io_chain_t process_net_io_chain = j->block_io_chain(); + if (pipe_write) { process_net_io_chain.push_back(pipe_write); } @@ -897,10 +883,9 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< process_net_io_chain.append(p->io_chain()); // Read pipe goes last. - if (!p->is_first_in_job) { - pipe_read.reset(new io_pipe_t(STDIN_FILENO, true)); - // Record the current read in pipe_read. - pipe_read->pipe_fd[0] = pipe_current_read.fd(); + if (pipe_current_read.valid()) { + pipe_read = std::make_shared(STDIN_FILENO, true /* input */, + std::move(pipe_current_read)); process_net_io_chain.push_back(pipe_read); } @@ -918,36 +903,6 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< parser.vars().export_arr(); } - // 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) { - debug(1, PIPE_ERROR); - wperror(L"pipe"); - job_mark_process_as_failed(j, p); - return false; - } - - // 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"); - job_mark_process_as_failed(j, p); - return false; - } - - // 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); - - // Record our pipes. - pipe_current_write.reset(local_pipe[1]); - out_pipe_next_read->reset(local_pipe[0]); - } - // Execute the process. switch (p->type) { case INTERNAL_FUNCTION: @@ -1008,18 +963,13 @@ bool exec_job(parser_t &parser, shared_ptr j) { } } - // Verify that all io_mode_t::buffers are output. We used to support a (single, hacked-in) - // magical input io_mode_t::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. size_t stdout_read_limit = 0; const io_chain_t all_ios = j->all_io_redirections(); - for (size_t idx = 0; idx < all_ios.size(); idx++) { - const shared_ptr &io = all_ios.at(idx); - - if ((io->io_mode == io_mode_t::buffer)) { - io_buffer_t *io_buffer = static_cast(io.get()); - assert(!io_buffer->is_input); - stdout_read_limit = io_buffer->buffer().limit(); + for (auto &io : all_ios) { + if ((io->io_mode == io_mode_t::bufferfill)) { + // The read limit is dictated by the last bufferfill. + const auto *bf = static_cast(io.get()); + stdout_read_limit = bf->buffer()->buffer().limit(); } } @@ -1028,21 +978,6 @@ bool exec_job(parser_t &parser, shared_ptr j) { DIE("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 (const auto io : all_ios) { - if (io->io_mode == io_mode_t::buffer) { - auto *io_buffer = static_cast(io.get()); - 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->processes.front().get()); - break; - } - } - } - // 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. // @@ -1098,29 +1033,30 @@ static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, wcstrin // 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(), is_subcmd ? read_byte_limit : 0)); - if (io_buffer.get() != NULL) { + size_t read_limit = is_subcmd ? read_byte_limit : 0; + std::shared_ptr buffer; + if (auto bufferfill = io_bufferfill_t::create(io_chain_t{}, read_limit)) { 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{bufferfill}, SUBST) == 0) { subcommand_status = proc_get_last_status(); } - - io_buffer->read(); + buffer = bufferfill->buffer(); + bufferfill.reset(); + buffer->read_to_wouldblock(); } - if (io_buffer->buffer().discarded()) subcommand_status = STATUS_READ_TOO_MUCH; + if (buffer && buffer->buffer().discarded()) subcommand_status = STATUS_READ_TOO_MUCH; // 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 || !buffer) { return subcommand_status; } // Walk over all the elements. - for (const auto &elem : io_buffer->buffer().elements()) { + for (const auto &elem : buffer->buffer().elements()) { if (elem.is_explicitly_separated()) { // Just append this one. lst->push_back(str2wcstring(elem.contents)); diff --git a/src/exec.h b/src/exec.h index 010de4654..63b78abc7 100644 --- a/src/exec.h +++ b/src/exec.h @@ -31,10 +31,6 @@ int exec_subshell(const wcstring &cmd, parser_t &parser, bool preserve_exit_stat /// 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. -int exec_pipe(int fd[2]); - /// Gets the interpreter for a given command. char *get_interpreter(const char *command, char *interpreter, size_t buff_size); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 8f266947d..2ed26d3b2 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -920,8 +920,7 @@ static void test_parser() { } 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}; + auto filler = io_bufferfill_t::create(io_chain_t{}); pthread_t thread = pthread_self(); double delay = 0.25 /* seconds */; iothread_perform([=]() { @@ -929,11 +928,13 @@ static void test_1_cancellation(const wchar_t *src) { usleep(delay * 1E6); pthread_kill(thread, SIGINT); }); - parser_t::principal_parser().eval(src, io_chain, TOP); - out_buff->read(); - if (out_buff->buffer().size() != 0) { + parser_t::principal_parser().eval(src, io_chain_t{filler}, TOP); + auto buffer = filler->buffer(); + filler.reset(); + buffer->read_to_wouldblock(); + if (buffer->buffer().size() != 0) { err(L"Expected 0 bytes in out_buff, but instead found %lu bytes\n", - out_buff->buffer().size()); + buffer->buffer().size()); } iothread_drain_all(); } diff --git a/src/io.cpp b/src/io.cpp index 091d250f5..074b83159 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -22,14 +22,10 @@ void io_fd_t::print() const { fwprintf(stderr, L"FD map %d -> %d\n", old_fd, fd) void io_file_t::print() const { fwprintf(stderr, L"file (%s)\n", filename_cstr); } void io_pipe_t::print() const { - fwprintf(stderr, L"pipe {%d, %d} (input: %s)\n", pipe_fd[0], pipe_fd[1], - is_input ? "yes" : "no"); + fwprintf(stderr, L"pipe {%d} (input: %s)\n", pipe_fd(), is_input_ ? "yes" : "no"); } -void io_buffer_t::print() const { - fwprintf(stderr, L"buffer (input: %s, size %lu)\n", - is_input ? "yes" : "no", (unsigned long)buffer_.size()); -} +void io_bufferfill_t::print() const { fwprintf(stderr, L"bufferfill {%d}\n", write_fd_.fd()); } void io_buffer_t::append_from_stream(const output_stream_t &stream) { if (buffer_.discarded()) return; @@ -40,75 +36,84 @@ void io_buffer_t::append_from_stream(const output_stream_t &stream) { buffer_.append_wide_buffer(stream.buffer()); } -void io_buffer_t::read() { - exec_close(pipe_fd[1]); +long io_buffer_t::read_some() { + int fd = read_.fd(); + assert(fd >= 0 && "Should have a valid fd"); + debug(4, L"io_buffer_t::read: blocking read on fd %d", fd); + long len; + char b[4096]; + do { + len = read(fd, b, sizeof b); + } while (len < 0 && errno == EINTR); + if (len > 0) { + buffer_.append(&b[0], &b[len]); + } + return len; +} - if (io_mode == io_mode_t::buffer) { - debug(4, L"io_buffer_t::read: blocking read on fd %d", pipe_fd[0]); - while (1) { - char b[4096]; - long len = read_blocked(pipe_fd[0], b, 4096); - if (len == 0) { - break; - } else if (len < 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) { - const wchar_t *fmt = - _(L"An error occured while reading output from code block on fd %d"); - debug(1, fmt, pipe_fd[0]); - wperror(L"io_buffer_t::read"); - } +void io_buffer_t::read_to_wouldblock() { + long len; + do { + len = read_some(); + } while (len > 0); + if (len < 0 && errno != EAGAIN) { + debug(1, _(L"An error occured while reading output from code block on fd %d"), read_.fd()); + wperror(L"io_buffer_t::read"); + } +} - break; - } else { - buffer_.append(&b[0], &b[len]); - } +bool io_buffer_t::try_read(unsigned long timeout_usec) { + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = timeout_usec; + int fd = read_.fd(); + assert(fd >= 0 && "Should have a valid fd"); + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + int ret = select(fd + 1, &fds, nullptr, nullptr, &timeout); + if (ret < 0) { + // Treat EINTR as timeout. + if (errno != EINTR) { + debug(1, _(L"An error occured inside select on fd %d"), fd); + wperror(L"io_buffer_t::try_read"); } + return false; } + if (ret > 0) { + read_some(); + } + return ret > 0; } -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) { - wperror(L"dup"); +shared_ptr io_bufferfill_t::create(const io_chain_t &conflicts, + size_t buffer_limit) { + // Construct our pipes. + auto pipes = make_autoclose_pipes(conflicts); + if (!pipes) { + return nullptr; } - return result; -} -shared_ptr io_buffer_t::create(int fd, const io_chain_t &conflicts, - size_t buffer_limit) { - bool success = true; - assert(fd >= 0); - shared_ptr buffer_redirect(new io_buffer_t(fd, buffer_limit)); - - 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. - success = false; - } else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) { + // Our buffer will read from the read end of the pipe. This end must be non-blocking. This is + // because we retain the write end of the pipe in this process (even after handing it off to a + // child process); therefore a read on the pipe may block forever. What we should do is arrange + // for the write end of the pipe to be closed at the right time; then the read could just block. + if (make_fd_nonblocking(pipes->read.fd())) { debug(1, PIPE_ERROR); wperror(L"fcntl"); - success = false; + return nullptr; } - if (!success) { - buffer_redirect.reset(); - } - return buffer_redirect; + // Our buffer gets the read end of the pipe; out_pipe gets the write end. + auto buffer = std::make_shared(std::move(pipes->read), buffer_limit); + return std::make_shared(std::move(pipes->write), buffer); } -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. -} +io_pipe_t::~io_pipe_t() = default; + +io_bufferfill_t::~io_bufferfill_t() = default; + +io_buffer_t::~io_buffer_t() = default; void io_chain_t::remove(const shared_ptr &element) { // See if you can guess why std::find doesn't work here. @@ -197,7 +202,7 @@ int move_fd_to_unused(int fd, const io_chain_t &io_chain, bool cloexec) { return new_fd; } -bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios) { +static 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++) { fds[i] = move_fd_to_unused(fds[i], ios); @@ -221,6 +226,27 @@ bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios) { return success; } +maybe_t make_autoclose_pipes(const io_chain_t &ios) { + int pipes[2] = {-1, -1}; + + if (pipe(pipes) < 0) { + debug(1, PIPE_ERROR); + wperror(L"pipe"); + return none(); + } + set_cloexec(pipes[0]); + set_cloexec(pipes[1]); + + if (!pipe_avoid_conflicts_with_io_chain(pipes, ios)) { + // The pipes are closed on failure here. + return none(); + } + autoclose_pipes_t result; + result.read = autoclose_fd_t(pipes[0]); + result.write = autoclose_fd_t(pipes[1]); + return result; +} + /// 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(); diff --git a/src/io.h b/src/io.h index e0200c9f3..ce656cfec 100644 --- a/src/io.h +++ b/src/io.h @@ -150,7 +150,7 @@ class separated_buffer_t { }; /// Describes what type of IO operation an io_data_t represents. -enum class io_mode_t { file, pipe, fd, buffer, close }; +enum class io_mode_t { file, pipe, fd, close, bufferfill }; /// Represents an FD redirection. class io_data_t { @@ -210,39 +210,83 @@ class io_file_t : public io_data_t { ~io_file_t() override { free((void *)filename_cstr); } }; +/// Represents (one end) of a pipe. 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; - } + // The pipe's fd. Conceptually this is dup2'd to io_data_t::fd. + autoclose_fd_t pipe_fd_; + + /// Whether this is an input pipe. This is used only for informational purposes. + const bool is_input_; public: - int pipe_fd[2]; - const bool is_input; - void print() const override; - io_pipe_t(int f, bool i) : io_data_t(io_mode_t::pipe, f), is_input(i) { - pipe_fd[0] = pipe_fd[1] = -1; - } + io_pipe_t(int fd, bool is_input, autoclose_fd_t pipe_fd) + : io_data_t(io_mode_t::pipe, fd), pipe_fd_(std::move(pipe_fd)), is_input_(is_input) {} + + ~io_pipe_t(); + + int pipe_fd() const { return pipe_fd_.fd(); } }; +class io_buffer_t; class io_chain_t; + +/// Represents filling an io_buffer_t. Very similar to io_pipe_t. +/// Bufferfills always target stdout. +class io_bufferfill_t : public io_data_t { + /// Write end. The other end is connected to an io_buffer_t. + const autoclose_fd_t write_fd_; + + /// The receiving buffer. + const std::shared_ptr buffer_; + + public: + void print() const override; + + io_bufferfill_t(autoclose_fd_t write_fd, std::shared_ptr buffer) + : io_data_t(io_mode_t::bufferfill, STDOUT_FILENO), + write_fd_(std::move(write_fd)), + buffer_(std::move(buffer)) {} + + ~io_bufferfill_t(); + + std::shared_ptr buffer() const { return buffer_; } + + int write_fd() const { return write_fd_.fd(); } + + /// Create an io_bufferfill_t which, when written from, populates a buffer (also created). + /// \returns nullptr on failure, e.g. too many open fds. + /// + /// \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 shared_ptr create(const io_chain_t &conflicts, size_t buffer_limit = 0); +}; + class output_stream_t; -class io_buffer_t : public io_pipe_t { + +/// An io_buffer_t is a buffer which can populate itself by reading from an fd. +/// It is not an io_data_t. +class io_buffer_t { private: + /// fd from which to read. + autoclose_fd_t read_; + + /// Buffer storing what we have read. separated_buffer_t buffer_; - explicit io_buffer_t(int f, size_t limit) - : io_pipe_t(io_mode_t::buffer, f, false /* not input */), buffer_(limit) { + /// Read some. Append it to our buffer. + /// \return positive if we read, 0 on EOF, -1 on error. + long read_some(); + + public: + explicit io_buffer_t(autoclose_fd_t read, size_t limit) + : read_(std::move(read)), buffer_(limit) { // Explicitly reset the discard flag because we share this buffer. buffer_.reset_discard(); } - public: - void print() const override; - - ~io_buffer_t() override; + ~io_buffer_t(); /// Access the underlying buffer. const separated_buffer_t &buffer() const { return buffer_; } @@ -250,24 +294,16 @@ class io_buffer_t : public io_pipe_t { /// Function to append to the buffer. void append(const char *ptr, size_t count) { buffer_.append(ptr, ptr + count); } - /// 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); + /// Read from input pipe until EOF or EAGAIN (i.e. would block). + void read_to_wouldblock(); - /// Close output pipe, and read from input pipe until eof. - void read(); + /// Read a bit, if our fd is readable, with the given timeout. + /// \return true if we read some, false on timeout. + bool try_read(unsigned long timeout_usec); /// Appends data from a given output_stream_t. /// Marks the receiver as discarded if the stream was discarded. void append_from_stream(const output_stream_t &stream); - - /// Create a io_mode_t::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 shared_ptr create(int fd, const io_chain_t &conflicts, - size_t buffer_limit = 0); }; class io_chain_t : public std::vector> { @@ -287,11 +323,18 @@ class io_chain_t : public std::vector> { 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). -bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios); +/// Helper type returned from making autoclose pipes. +struct autoclose_pipes_t { + /// Read end of the pipe. + autoclose_fd_t read; + + /// Write end of the pipe. + autoclose_fd_t write; +}; +/// Call pipe(), populating autoclose fds, avoiding conflicts. +/// The pipes are marked CLO_EXEC. +/// \return pipes on success, none() on error. +maybe_t make_autoclose_pipes(const io_chain_t &ios); /// 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. diff --git a/src/proc.cpp b/src/proc.cpp index cdacd05bb..a58217f66 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -837,86 +837,24 @@ void proc_update_jiffies() { #endif /// The return value of select_try(), indicating IO readiness or an error -enum class select_try_t { - /// One or more fds have data ready for read - DATA_READY, - /// The timeout elapsed without any data becoming available for read +enum class block_receive_try_t { + /// There is no buffer to select on. + NO_BUFFER, + /// We have a block buffer, and we read some. + DATA_READ, + /// We have a block buffer, but we were unable to read any. TIMEOUT, - /// There were no FDs in the io chain for which to select on. - IOCHAIN_EMPTY, }; -/// 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 the status of the select operation -static select_try_t select_try(job_t *j) { - fd_set fds; - int maxfd = -1; - - FD_ZERO(&fds); - - const io_chain_t chain = j->all_io_redirections(); - for (const auto &io : chain) { - if (io->io_mode == io_mode_t::buffer) { - auto io_pipe = static_cast(io.get()); - int fd = io_pipe->pipe_fd[0]; - FD_SET(fd, &fds); - maxfd = std::max(maxfd, fd); - debug(4, L"select_try on fd %d", fd); - } - } - - if (maxfd >= 0) { - struct timeval timeout; - - timeout.tv_sec = 0; - timeout.tv_usec = 10000; - - int retval = select(maxfd + 1, &fds, 0, 0, &timeout); - if (retval == 0) { - debug(4, L"select_try hit timeout"); - return select_try_t::TIMEOUT; - } - return select_try_t::DATA_READY; - } - - return select_try_t::IOCHAIN_EMPTY; -} - -/// 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. - const io_chain_t chain = j->all_io_redirections(); - for (size_t idx = 0; idx < chain.size(); idx++) { - io_data_t *d = chain.at(idx).get(); - if (d->io_mode == io_mode_t::buffer) { - buff = static_cast(d); - } - } - - if (buff) { - debug(4, L"proc::read_try('%ls')", j->command_wcstr()); - while (1) { - char b[BUFFER_SIZE]; - long len = read_blocked(buff->pipe_fd[0], b, BUFFER_SIZE); - if (len == 0) { - break; - } else if (len < 0) { - if (errno != EAGAIN) { - debug(1, _(L"An error occured while reading output from code block")); - wperror(L"read_try"); - } - break; - } else { - buff->append(b, len); - } +/// \return the last IO buffer in job j, or nullptr if none. +std::shared_ptr last_buffer(job_t *j) { + std::shared_ptr buff{}; + for (const auto &io : j->all_io_redirections()) { + if (io->io_mode == io_mode_t::bufferfill) { + buff = static_cast(io.get())->buffer(); } } + return buff; } // Return control of the terminal to a job's process group. restore_attrs is true if we are restoring @@ -1154,26 +1092,27 @@ void job_t::continue_job(bool send_sigcont) { // Wait for data to become available or the status of our own job to change while (!reader_exit_forced() && !is_stopped() && !is_completed()) { - auto result = select_try(this); read_attempted = true; - - switch (result) { - case select_try_t::DATA_READY: - // Read the data that we know is now available, then scan for finished processes - // but do not block. We don't block so long as we have IO to process, once the - // fd buffers are empty we'll block in the second case below. - read_try(this); + auto read_result = block_receive_try_t::NO_BUFFER; + if (auto buff = last_buffer(this)) { + const unsigned long SELECT_TIMEOUT_USEC = 10000; + bool did_read = buff->try_read(SELECT_TIMEOUT_USEC); + read_result = did_read ? block_receive_try_t::DATA_READ : block_receive_try_t::TIMEOUT; + } + switch (read_result) { + case block_receive_try_t::DATA_READ: + // We read some data. process_mark_finished_children(false); break; - case select_try_t::TIMEOUT: - // No FDs are ready. Look for finished processes instead. + case block_receive_try_t::TIMEOUT: + // We read some data or timed out. Poll for finished processes. debug(4, L"select_try: no fds returned valid data within the timeout" ); process_mark_finished_children(block_on_fg); break; - case select_try_t::IOCHAIN_EMPTY: - // There were no IO fds to select on. + case block_receive_try_t::NO_BUFFER: + // We are not populating a buffer. debug(4, L"select_try: no IO fds" ); process_mark_finished_children(true); @@ -1195,7 +1134,9 @@ void job_t::continue_job(bool send_sigcont) { // `echo` calls were sometimes having their output combined with the `set_color` calls // in the wrong order! if (!read_attempted) { - read_try(this); + if (auto buff = last_buffer(this)) { + buff->read_to_wouldblock(); + } } // Set $status only if we are in the foreground and the last process in the job has diff --git a/src/redirection.cpp b/src/redirection.cpp index 1301f1e4a..6c00c4d55 100644 --- a/src/redirection.cpp +++ b/src/redirection.cpp @@ -73,15 +73,17 @@ maybe_t dup2_list_t::resolve_chain(const io_chain_t &io_chain) { break; } - case io_mode_t::buffer: case io_mode_t::pipe: { const io_pipe_t *io = static_cast(io_ref.get()); - // If write_pipe_idx is 0, it means we're connecting to the read end (first pipe - // fd). If it's 1, we're connecting to the write end (second pipe fd). - unsigned int write_pipe_idx = (io->is_input ? 0 : 1); - result.add_dup2(io->pipe_fd[write_pipe_idx], io->fd); - if (io->pipe_fd[0] >= 0) result.add_close(io->pipe_fd[0]); - if (io->pipe_fd[1] >= 0) result.add_close(io->pipe_fd[1]); + result.add_dup2(io->pipe_fd(), io->fd); + result.add_close(io->pipe_fd()); + break; + } + + case io_mode_t::bufferfill: { + const io_bufferfill_t *io = static_cast(io_ref.get()); + result.add_dup2(io->write_fd(), io->fd); + result.add_close(io->write_fd()); break; } } From 6e0dd06f431481a794e0b62cdc5aee7eb0a9ddd1 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 1 Feb 2019 01:04:14 -0800 Subject: [PATCH 342/439] Introduce make_pthread This allows creating a pthread directly, which can be joined. iothread_spawn wraps this. --- src/fish_tests.cpp | 15 +++++++++++ src/iothread.cpp | 64 ++++++++++++++++++++++++++++++++++++---------- src/iothread.h | 8 +++++- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 2ed26d3b2..e45f22e34 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -678,6 +678,20 @@ static void test_iothread() { max_achieved_thread_count); } +static void test_pthread() { + say(L"Testing pthreads"); + pthread_t result = {}; + int val = 3; + bool made = make_pthread(&result, [&val](){ + val += 2; + }); + do_test(made); + void *ignore = nullptr; + int ret = pthread_join(result, &ignore); + do_test(ret == 0); + do_test(val == 5); +} + 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)) { @@ -5083,6 +5097,7 @@ int main(int argc, char **argv) { if (should_test_function("convert_nulls")) test_convert_nulls(); if (should_test_function("tok")) test_tokenizer(); if (should_test_function("iothread")) test_iothread(); + if (should_test_function("pthread")) test_pthread(); if (should_test_function("parser")) test_parser(); if (should_test_function("cancellation")) test_cancellation(); if (should_test_function("indents")) test_indents(); diff --git a/src/iothread.cpp b/src/iothread.cpp index d834da8c1..c19f69339 100644 --- a/src/iothread.cpp +++ b/src/iothread.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -149,24 +150,14 @@ static void *iothread_worker(void *unused) { /// 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); - DIE_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. pthread_t thread = 0; - pthread_create(&thread, NULL, iothread_worker, NULL); - - // We will never join this thread. - DIE_ON_FAILURE(pthread_detach(thread)); - debug(5, "pthread %p spawned", (void *)(intptr_t)thread); - // Restore our sigmask. - DIE_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &saved_set, NULL)); + if (make_pthread(&thread, iothread_worker, nullptr)) { + // We will never join this thread. + DIE_ON_FAILURE(pthread_detach(thread)); + } } int iothread_perform_impl(void_function_t &&func, void_function_t &&completion) { @@ -342,3 +333,48 @@ void iothread_perform_on_main(void_function_t &&func) { // Ok, the request must now be done. assert(req.done); } + +bool make_pthread(pthread_t *result, void *(*func)(void *), void *param) { + // 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); + DIE_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. + pthread_t thread = 0; + int err = pthread_create(&thread, NULL, func, param); + if (err == 0) { + // Success, return the thread. + debug(5, "pthread %p spawned", (void *)(intptr_t)thread); + *result = thread; + } else { + perror("pthread_create"); + } + // Restore our sigmask. + DIE_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &saved_set, NULL)); + return err == 0; +} + +using void_func_t = std::function; + +static void *func_invoker(void *param) { + void_func_t *vf = static_cast(param); + (*vf)(); + delete vf; + return nullptr; +} + +bool make_pthread(pthread_t *result, void_func_t &&func) { + // Copy the function into a heap allocation. + void_func_t *vf = new void_func_t(std::move(func)); + if (make_pthread(result, func_invoker, vf)) { + return true; + } + // Thread spawning failed, clean up our heap allocation. + delete vf; + return false; +} diff --git a/src/iothread.h b/src/iothread.h index 8b29370c8..5e677b639 100644 --- a/src/iothread.h +++ b/src/iothread.h @@ -69,10 +69,16 @@ int iothread_perform(const HANDLER &handler, const COMPLETION &completion) { // variant of iothread_perform without a completion handler inline int iothread_perform(std::function &&func) { - return iothread_perform_impl(std::move(func), std::function()); + return iothread_perform_impl(std::move(func), {}); } /// Performs a function on the main thread, blocking until it completes. void iothread_perform_on_main(std::function &&func); +/// Creates a pthread, manipulating the signal mask so that the thread receives no signals. +/// The pthread runs \p func. +/// \returns true on success, false on failure. +bool make_pthread(pthread_t *result, void *(*func)(void *), void *param); +bool make_pthread(pthread_t *result, std::function &&func); + #endif From 9a4153f5e2f0a0ea74f395248f5dbd40b55e8dc8 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 1 Feb 2019 01:58:06 -0800 Subject: [PATCH 343/439] Fill io_buffer via background thread This is a large change to how io_buffers are filled. The essential problem comes about with code like (example): echo ( /bin/pwd ) The output of /bin/pwd must go to fish, not the tty. To arrange for this, fish does the following: 1. Invoke pipe() to create a pipe. 2. Add an io_bufferfill_t redirection that owns the write end of the pipe. 3. After fork (or equiv), call dup2() to replace pwd's stdout with this pipe. Now when /bin/pwd writes, it will send output to the read end of the pipe. But who reads it? Prior to this fix, fish would do the following in a loop: 1. select() on the pipe with a 10 msec timeout 2. waitpid(WNOHANG) on the pwd proc This polling is ugly and confusing and is what is replaced here. With this new change, fish now reads from the pipe via a background thread: 1. Spawn a background pthread, which select()s on the pipe's read end with a long (100 msec) timeout. 2. In the foreground, waitpid() (allowing hanging) on the pwd proc. The big win here is a major simplification of job_t::continue_job() since it no longer has to worry about filling buffers. This will make things easier for concurrent execution. It may not be obvious why the background thread still needs a poll (100 msec). The answer is for cases where the write end of the fd escapes, in particular background processes invoked inside command substitutions. psub is perhaps the only important case of this (other shells typically just hang here). --- src/exec.cpp | 12 +--- src/fish_tests.cpp | 4 +- src/io.cpp | 164 +++++++++++++++++++++++++++++++-------------- src/io.h | 76 +++++++++++++-------- src/proc.cpp | 85 +---------------------- 5 files changed, 167 insertions(+), 174 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index 7de91f5be..eb1a88259 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -782,12 +782,8 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr // Remove our write pipe and forget it. This may close the pipe, unless another thread has // claimed it (background write) or another process has inherited it. - auto block_output_buffer = block_output_bufferfill->buffer(); io_chain.remove(block_output_bufferfill); - block_output_bufferfill.reset(); - - // Make the buffer populate itself from whatever was written to the write pipe. - block_output_buffer->read_to_wouldblock(); + auto block_output_buffer = io_bufferfill_t::finish(std::move(block_output_bufferfill)); // Resolve our IO chain to a sequence of dup2s. auto dup2s = dup2_list_t::resolve_chain(io_chain); @@ -969,7 +965,7 @@ bool exec_job(parser_t &parser, shared_ptr j) { if ((io->io_mode == io_mode_t::bufferfill)) { // The read limit is dictated by the last bufferfill. const auto *bf = static_cast(io.get()); - stdout_read_limit = bf->buffer()->buffer().limit(); + stdout_read_limit = bf->buffer()->read_limit(); } } @@ -1040,9 +1036,7 @@ static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, wcstrin if (parser.eval(cmd, io_chain_t{bufferfill}, SUBST) == 0) { subcommand_status = proc_get_last_status(); } - buffer = bufferfill->buffer(); - bufferfill.reset(); - buffer->read_to_wouldblock(); + buffer = io_bufferfill_t::finish(std::move(bufferfill)); } if (buffer && buffer->buffer().discarded()) subcommand_status = STATUS_READ_TOO_MUCH; diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index e45f22e34..cecb566f3 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -943,9 +943,7 @@ static void test_1_cancellation(const wchar_t *src) { pthread_kill(thread, SIGINT); }); parser_t::principal_parser().eval(src, io_chain_t{filler}, TOP); - auto buffer = filler->buffer(); - filler.reset(); - buffer->read_to_wouldblock(); + auto buffer = io_bufferfill_t::finish(std::move(filler)); if (buffer->buffer().size() != 0) { err(L"Expected 0 bytes in out_buff, but instead found %lu bytes\n", buffer->buffer().size()); diff --git a/src/io.cpp b/src/io.cpp index 074b83159..a8246504c 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -11,6 +11,7 @@ #include "exec.h" #include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "iothread.h" #include "wutil.h" // IWYU pragma: keep io_data_t::~io_data_t() = default; @@ -28,6 +29,7 @@ void io_pipe_t::print() const { void io_bufferfill_t::print() const { fwprintf(stderr, L"bufferfill {%d}\n", write_fd_.fd()); } void io_buffer_t::append_from_stream(const output_stream_t &stream) { + scoped_lock locker(append_lock_); if (buffer_.discarded()) return; if (stream.buffer().discarded()) { buffer_.set_discard(); @@ -36,54 +38,102 @@ void io_buffer_t::append_from_stream(const output_stream_t &stream) { buffer_.append_wide_buffer(stream.buffer()); } -long io_buffer_t::read_some() { - int fd = read_.fd(); - assert(fd >= 0 && "Should have a valid fd"); - debug(4, L"io_buffer_t::read: blocking read on fd %d", fd); - long len; - char b[4096]; - do { - len = read(fd, b, sizeof b); - } while (len < 0 && errno == EINTR); - if (len > 0) { - buffer_.append(&b[0], &b[len]); - } - return len; -} +void io_buffer_t::run_background_fillthread(autoclose_fd_t readfd) { + // Here we are running the background fillthread, executing in a background thread. + // Our plan is: + // 1. poll via select() until the fd is readable. + // 2. Acquire the append lock. + // 3. read until EAGAIN (would block), appending + // 4. release the lock + // The purpose of holding the lock around the read calls is to ensure that data from background + // processes isn't weirdly interspersed with data directly transferred (from a builtin to a buffer). -void io_buffer_t::read_to_wouldblock() { - long len; - do { - len = read_some(); - } while (len > 0); - if (len < 0 && errno != EAGAIN) { - debug(1, _(L"An error occured while reading output from code block on fd %d"), read_.fd()); - wperror(L"io_buffer_t::read"); - } -} + const int fd = readfd.fd(); -bool io_buffer_t::try_read(unsigned long timeout_usec) { - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = timeout_usec; - int fd = read_.fd(); - assert(fd >= 0 && "Should have a valid fd"); - fd_set fds; - FD_ZERO(&fds); - FD_SET(fd, &fds); - int ret = select(fd + 1, &fds, nullptr, nullptr, &timeout); - if (ret < 0) { - // Treat EINTR as timeout. - if (errno != EINTR) { - debug(1, _(L"An error occured inside select on fd %d"), fd); - wperror(L"io_buffer_t::try_read"); + // 100 msec poll rate. Note that in most cases, the write end of the pipe will be closed so + // select() will return; the polling is important only for weird cases like a background process + // launched in a command substitution. + const long poll_timeout_usec = 100000; + struct timeval tv = {}; + tv.tv_usec = poll_timeout_usec; + + bool shutdown = false; + while (!shutdown) { + bool readable = false; + // Check the shutdown flag. + shutdown |= this->shutdown_fillthread_.load(std::memory_order_relaxed); + + // Poll if our fd is readable. + // Do this even if the shutdown flag is set. It's important we wait for the fd at least + // once. For short-lived processes, it's possible for the process to execute, produce output + // (fits in the pipe buffer) and be reaped before we are even scheduled. So always wait at + // least once on the fd. Note that doesn't mean we will wait for the full poll duration; + // typically what will happen is our pipe will be widowed and so this will return quickly. + // It's only for weird cases (e.g. a background process launched inside a command + // substitution) that we'll wait out the entire poll time. + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + int ret = select(fd + 1, &fds, NULL, NULL, &tv); + readable = ret > 0; + if (ret < 0 && errno != EINTR) { + // Surprising error. + wperror(L"select"); + return; + } + + if (readable || shutdown) { + // Now either our fd is readable, or we have set the shutdown flag. + // Either way acquire the lock and read until we reach EOF, or EAGAIN / EINTR. + scoped_lock locker(append_lock_); + ssize_t ret; + do { + char buff[4096]; + ret = read(fd, buff, sizeof buff); + if (ret > 0) { + buffer_.append(&buff[0], &buff[ret]); + } else if (ret == 0) { + shutdown = true; + } else if (errno != EINTR && errno != EAGAIN) { + wperror(L"read"); + return; + } + } while (ret > 0); } - return false; } - if (ret > 0) { - read_some(); + assert(shutdown && "Should only exit loop if shutdown flag is set"); +} + +void io_buffer_t::begin_background_fillthread(autoclose_fd_t fd) { + ASSERT_IS_MAIN_THREAD(); + assert(!fillthread_ && "Already have a fillthread"); + + // We want our background thread to own the fd but it's not easy to move into a std::function. + // Use a shared_ptr. + auto fdref = std::make_shared(std::move(fd)); + + // Our function to read until the receiver is closed. + // It's OK to capture 'this' by value because 'this' owns the background thread and joins it + // before dtor. + std::function func = [this, fdref]() { + this->run_background_fillthread(std::move(*fdref)); + }; + + pthread_t fillthread{}; + if (!make_pthread(&fillthread, std::move(func))) { + wperror(L"make_pthread"); } - return ret > 0; + fillthread_ = fillthread; +} + +void io_buffer_t::complete_background_fillthread() { + ASSERT_IS_MAIN_THREAD(); + assert(fillthread_ && "Should have a fillthread"); + shutdown_fillthread_.store(true, std::memory_order_relaxed); + void *ignored = nullptr; + int err = pthread_join(*fillthread_, &ignored); + DIE_ON_FAILURE(err); + fillthread_.reset(); } shared_ptr io_bufferfill_t::create(const io_chain_t &conflicts, @@ -93,27 +143,39 @@ shared_ptr io_bufferfill_t::create(const io_chain_t &conflicts, if (!pipes) { return nullptr; } - // Our buffer will read from the read end of the pipe. This end must be non-blocking. This is - // because we retain the write end of the pipe in this process (even after handing it off to a - // child process); therefore a read on the pipe may block forever. What we should do is arrange - // for the write end of the pipe to be closed at the right time; then the read could just block. + // because our fillthread needs to poll to decide if it should shut down, and also accept input + // from direct buffer transfers. if (make_fd_nonblocking(pipes->read.fd())) { debug(1, PIPE_ERROR); wperror(L"fcntl"); return nullptr; } - - // Our buffer gets the read end of the pipe; out_pipe gets the write end. - auto buffer = std::make_shared(std::move(pipes->read), buffer_limit); + // Our fillthread gets the read end of the pipe; out_pipe gets the write end. + auto buffer = std::make_shared(buffer_limit); + buffer->begin_background_fillthread(std::move(pipes->read)); return std::make_shared(std::move(pipes->write), buffer); } +std::shared_ptr io_bufferfill_t::finish(std::shared_ptr &&filler) { + // The io filler is passed in. This typically holds the only instance of the write side of the + // pipe used by the buffer's fillthread (except for that side held by other processes). Get the + // buffer out of the bufferfill and clear the shared_ptr; this will typically widow the pipe. + // Then allow the buffer to finish. + assert(filler && "Null pointer in finish"); + auto buffer = filler->buffer(); + filler.reset(); + buffer->complete_background_fillthread(); + return buffer; +} + io_pipe_t::~io_pipe_t() = default; io_bufferfill_t::~io_bufferfill_t() = default; -io_buffer_t::~io_buffer_t() = default; +io_buffer_t::~io_buffer_t() { + assert(! fillthread_ && "io_buffer_t destroyed with outstanding fillthread"); +} void io_chain_t::remove(const shared_ptr &element) { // See if you can guess why std::find doesn't work here. diff --git a/src/io.h b/src/io.h index ce656cfec..5cffd0d21 100644 --- a/src/io.h +++ b/src/io.h @@ -1,26 +1,21 @@ #ifndef FISH_IO_H #define FISH_IO_H +#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 -#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 #include -using std::shared_ptr; -#else -// C++03 or libstdc++ -#include -using std::tr1::shared_ptr; -#endif +#include +#include #include "common.h" #include "env.h" +#include "maybe.h" + +using std::shared_ptr; /// separated_buffer_t is composed of a sequence of elements, some of which may be explicitly /// separated (e.g. through string spit0) and some of which the separation is inferred. This enum @@ -244,6 +239,8 @@ class io_bufferfill_t : public io_data_t { public: void print() const override; + // The ctor is public to support make_shared() in the static create function below. + // Do not invoke this directly. io_bufferfill_t(autoclose_fd_t write_fd, std::shared_ptr buffer) : io_data_t(io_mode_t::bufferfill, STDOUT_FILENO), write_fd_(std::move(write_fd)), @@ -253,14 +250,19 @@ class io_bufferfill_t : public io_data_t { std::shared_ptr buffer() const { return buffer_; } + /// \return the fd that, when written to, fills the buffer. int write_fd() const { return write_fd_.fd(); } - /// Create an io_bufferfill_t which, when written from, populates a buffer (also created). + /// Create an io_bufferfill_t which, when written from, fills a buffer with the contents. /// \returns nullptr on failure, e.g. too many open fds. /// /// \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 shared_ptr create(const io_chain_t &conflicts, size_t buffer_limit = 0); + + /// Reset the receiver (possibly closing the write end of the pipe), and complete the fillthread + /// of the buffer. \return the buffer. + static std::shared_ptr finish(std::shared_ptr &&filler); }; class output_stream_t; @@ -269,19 +271,34 @@ class output_stream_t; /// It is not an io_data_t. class io_buffer_t { private: - /// fd from which to read. - autoclose_fd_t read_; + friend io_bufferfill_t; /// Buffer storing what we have read. separated_buffer_t buffer_; - /// Read some. Append it to our buffer. - /// \return positive if we read, 0 on EOF, -1 on error. - long read_some(); + /// Atomic flag indicating our fillthread should shut down. + std::atomic shutdown_fillthread_; + + /// The background fillthread itself, if any. + maybe_t fillthread_{}; + + /// Read limit of the buffer. + const size_t read_limit_; + + /// Lock for appending. + std::mutex append_lock_{}; + + /// Called in the background thread to run it. + void run_background_fillthread(autoclose_fd_t readfd); + + /// Begin the background fillthread operation, reading from the given fd. + void begin_background_fillthread(autoclose_fd_t readfd); + + /// End the background fillthread operation. + void complete_background_fillthread(); public: - explicit io_buffer_t(autoclose_fd_t read, size_t limit) - : read_(std::move(read)), buffer_(limit) { + explicit io_buffer_t(size_t limit) : buffer_(limit), read_limit_(limit) { // Explicitly reset the discard flag because we share this buffer. buffer_.reset_discard(); } @@ -289,17 +306,20 @@ class io_buffer_t { ~io_buffer_t(); /// Access the underlying buffer. - const separated_buffer_t &buffer() const { return buffer_; } + /// This requires that the background fillthread be none. + const separated_buffer_t &buffer() const { + assert(!fillthread_ && "Cannot access buffer during background fill"); + return buffer_; + } /// Function to append to the buffer. - void append(const char *ptr, size_t count) { buffer_.append(ptr, ptr + count); } + void append(const char *ptr, size_t count) { + scoped_lock locker(append_lock_); + buffer_.append(ptr, ptr + count); + } - /// Read from input pipe until EOF or EAGAIN (i.e. would block). - void read_to_wouldblock(); - - /// Read a bit, if our fd is readable, with the given timeout. - /// \return true if we read some, false on timeout. - bool try_read(unsigned long timeout_usec); + /// \return the read limit. + size_t read_limit() const { return read_limit_; } /// Appends data from a given output_stream_t. /// Marks the receiver as discarded if the stream was discarded. diff --git a/src/proc.cpp b/src/proc.cpp index a58217f66..a440d1cda 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -836,27 +836,6 @@ void proc_update_jiffies() { #endif -/// The return value of select_try(), indicating IO readiness or an error -enum class block_receive_try_t { - /// There is no buffer to select on. - NO_BUFFER, - /// We have a block buffer, and we read some. - DATA_READ, - /// We have a block buffer, but we were unable to read any. - TIMEOUT, -}; - -/// \return the last IO buffer in job j, or nullptr if none. -std::shared_ptr last_buffer(job_t *j) { - std::shared_ptr buff{}; - for (const auto &io : j->all_io_redirections()) { - if (io->io_mode == io_mode_t::bufferfill) { - buff = static_cast(io.get())->buffer(); - } - } - return buff; -} - // Return control of the terminal to a job's process group. restore_attrs is true if we are restoring // a previously-stopped job, in which case we need to restore terminal attributes. bool terminal_give_to_job(const job_t *j, bool restore_attrs) { @@ -1038,7 +1017,6 @@ void job_t::continue_job(bool send_sigcont) { } }); - bool read_attempted = false; if (!is_completed()) { if (get_flag(job_flag_t::TERMINAL) && is_foreground()) { // Put the job into the foreground and give it control of the terminal. @@ -1071,74 +1049,15 @@ void job_t::continue_job(bool send_sigcont) { } if (is_foreground()) { - // This is an optimization to not call select_try() in case a process has exited. While - // it may seem silly, unless there is IO (and there usually isn't in terms of total CPU - // time), select_try() will wait for 10ms (our timeout) before returning. If during - // these 10ms a process exited, the shell will basically hang until the timeout happens - // and we are free to call `process_mark_finished_children()` to discover that fact. By - // calling it here before calling `select_try()` below, shell responsiveness can be - // dramatically improved (noticably so, not just "theoretically speaking" per the - // discussion in #5219). - process_mark_finished_children(false); - - // If this is a child job and the parent job is still under construction (i.e. job1 | - // some_func), we can't block on execution of the nested job for `some_func`. Doing - // so can cause hangs if job1 emits more data than fits in the OS pipe buffer. - // The solution is to to not block on fg from the initial call in exec_job(), which - // is also the only place that send_sigcont is false. parent_job.is_constructed() - // must also be true, which coincides with WAIT_BY_PROCESS (which will have to do - // since we don't store a reference to the parent job in the job_t structure). - bool block_on_fg = send_sigcont && job_chain_is_fully_constructed(); - - // Wait for data to become available or the status of our own job to change + // Wait for the status of our own job to change. while (!reader_exit_forced() && !is_stopped() && !is_completed()) { - read_attempted = true; - auto read_result = block_receive_try_t::NO_BUFFER; - if (auto buff = last_buffer(this)) { - const unsigned long SELECT_TIMEOUT_USEC = 10000; - bool did_read = buff->try_read(SELECT_TIMEOUT_USEC); - read_result = did_read ? block_receive_try_t::DATA_READ : block_receive_try_t::TIMEOUT; - } - switch (read_result) { - case block_receive_try_t::DATA_READ: - // We read some data. - process_mark_finished_children(false); - break; - - case block_receive_try_t::TIMEOUT: - // We read some data or timed out. Poll for finished processes. - debug(4, L"select_try: no fds returned valid data within the timeout" ); - process_mark_finished_children(block_on_fg); - break; - - case block_receive_try_t::NO_BUFFER: - // We are not populating a buffer. - debug(4, L"select_try: no IO fds" ); - process_mark_finished_children(true); - - // If it turns out that we encountered this because the file descriptor we were - // reading from has died, process_mark_finished_children() should take care of - // changing the status of our is_completed() (assuming it is appropriate to do - // so), in which case we will break out of this loop. - break; - } + process_mark_finished_children(true); } } } if (is_foreground()) { if (is_completed()) { - // It's possible that the job will produce output and exit before we've even read from - // it. In that case, make sure we read that output now, before we've executed any - // subsequent calls. This is why prompt colors were getting screwed up - the builtin - // `echo` calls were sometimes having their output combined with the `set_color` calls - // in the wrong order! - if (!read_attempted) { - if (auto buff = last_buffer(this)) { - buff->read_to_wouldblock(); - } - } - // Set $status only if we are in the foreground and the last process in the job has // finished and is not a short-circuited builtin. auto &p = processes.back(); From 38b4d47560868cfb10bac6c226fa853e46b48729 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 3 Feb 2019 00:14:03 +0100 Subject: [PATCH 344/439] Initialize empty_ios emptier This placates the compiler. The compiler is pleased. --- src/fish.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fish.cpp b/src/fish.cpp index ceb49c053..fc798bf4a 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -383,7 +383,7 @@ int main(int argc, char **argv) { parser_t &parser = parser_t::principal_parser(); - const io_chain_t empty_ios; + const io_chain_t empty_ios {}; if (read_init(paths)) { // Stomp the exit status of any initialization commands (issue #635). proc_set_last_status(STATUS_CMD_OK); From f4e1d7c97e518f4f5bd2c8081abc99cb5422aba1 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 2 Feb 2019 17:38:12 -0800 Subject: [PATCH 345/439] Satisfy the compiler harder --- src/io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io.cpp b/src/io.cpp index a8246504c..51ce05e89 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -306,7 +306,7 @@ maybe_t make_autoclose_pipes(const io_chain_t &ios) { autoclose_pipes_t result; result.read = autoclose_fd_t(pipes[0]); result.write = autoclose_fd_t(pipes[1]); - return result; + return maybe_t{std::move(result)}; } /// Return the last IO for the given fd. From 6ba0d4c88af0e3b34f92763feda6f555c0ce74d8 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 2 Feb 2019 17:53:40 -0800 Subject: [PATCH 346/439] Revert io_bufferfill_t stack This reverts commit 88dc484858f708ef5ac14c928fc5cf7c3192397a onwards. --- CMakeLists.txt | 2 +- Makefile.in | 2 +- share/functions/psub.fish | 5 +- src/common.h | 3 - src/exec.cpp | 268 ++++++++++++++++++++++++-------------- src/exec.h | 4 + src/fish.cpp | 2 +- src/fish_tests.cpp | 53 +------- src/io.cpp | 215 +++++++++--------------------- src/io.h | 171 ++++++++---------------- src/iothread.cpp | 64 ++------- src/iothread.h | 8 +- src/postfork.cpp | 205 ++++++++++++++++++++++++++--- src/postfork.h | 10 +- src/proc.cpp | 144 +++++++++++++++++++- src/redirection.cpp | 92 ------------- src/redirection.h | 64 --------- 17 files changed, 647 insertions(+), 665 deletions(-) delete mode 100644 src/redirection.cpp delete mode 100644 src/redirection.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 809f24162..882888c7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ SET(FISH_SRCS src/postfork.cpp src/proc.cpp src/reader.cpp src/sanity.cpp src/screen.cpp src/signal.cpp src/tinyexpr.cpp src/tnode.cpp src/tokenizer.cpp src/utf8.cpp src/util.cpp src/wcstringutil.cpp src/wgetopt.cpp src/wildcard.cpp src/wutil.cpp - src/future_feature_flags.cpp src/redirection.cpp + src/future_feature_flags.cpp ) # Header files are just globbed. diff --git a/Makefile.in b/Makefile.in index 059e05847..aa5e0e2ee 100644 --- a/Makefile.in +++ b/Makefile.in @@ -119,7 +119,7 @@ FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_bg.o obj/builtin_bind.o ob 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/tinyexpr.o obj/tokenizer.o obj/tnode.o obj/utf8.o \ obj/util.o obj/wcstringutil.o obj/wgetopt.o obj/wildcard.o obj/wutil.o \ - obj/future_feature_flags.o obj/redirection.o + obj/future_feature_flags.o FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS) diff --git a/share/functions/psub.fish b/share/functions/psub.fish index 8c8a7eac8..b764d6f35 100644 --- a/share/functions/psub.fish +++ b/share/functions/psub.fish @@ -29,10 +29,7 @@ function psub --description "Read from stdin into a file and output the filename or return set filename $dirname/psub.fifo"$_flag_suffix" mkfifo $filename - # Note that if we were to do the obvious `cat >$filename &`, we would deadlock - # because $filename may be opened before the fork. Use tee to ensure it is opened - # after the fork. - tee $filename >/dev/null & + cat >$filename & else if test -z "$_flag_suffix" set filename (mktemp $tmpdir/.psub.XXXXXXXXXX) cat >$filename diff --git a/src/common.h b/src/common.h index 5fa2119ba..8b45a808c 100644 --- a/src/common.h +++ b/src/common.h @@ -786,9 +786,6 @@ class autoclose_fd_t { fd_ = fd; } - // \return if this has a valid fd. - bool valid() const { return fd_ >= 0; } - autoclose_fd_t(const autoclose_fd_t &) = delete; void operator=(const autoclose_fd_t &) = delete; autoclose_fd_t(autoclose_fd_t &&rhs) : fd_(rhs.fd_) { rhs.fd_ = -1; } diff --git a/src/exec.cpp b/src/exec.cpp index eb1a88259..75af77921 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -39,7 +39,6 @@ #include "postfork.h" #include "proc.h" #include "reader.h" -#include "redirection.h" #include "signal.h" #include "wutil.h" // IWYU pragma: keep @@ -81,6 +80,25 @@ void exec_close(int fd) { } } +int exec_pipe(int fd[2]) { + ASSERT_IS_MAIN_THREAD(); + + int 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. + 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) { bool result = false; @@ -96,6 +114,18 @@ static bool redirection_is_to_real_file(const io_data_t *io) { return result; } +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++) { + const io_data_t *io = io_chain.at(idx).get(); + if (redirection_is_to_real_file(io)) { + result = true; + break; + } + } + 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) { @@ -227,8 +257,8 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t *out_chain, switch (in->io_mode) { case io_mode_t::pipe: - case io_mode_t::bufferfill: case io_mode_t::fd: + case io_mode_t::buffer: case io_mode_t::close: { // These redirections don't need transmogrification. They can be passed through. out = in; @@ -299,9 +329,11 @@ void internal_exec_helper(parser_t &parser, parsed_source_ref_t parsed_source, t job_reap(false); } -// Returns whether we can use posix spawn for a given process in a given job. +// 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. // -// To avoid the race between the caller calling tcsetpgrp() and the client checking the +// 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 std::shared_ptr &job, @@ -315,7 +347,15 @@ static bool can_use_posix_spawn_for_job(const std::shared_ptr &job, return false; } } - return true; + + // 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())) { + result = false; + } + return result; } void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &all_ios) { @@ -327,8 +367,7 @@ void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &all_ios) { // 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. - auto redirs = dup2_list_t::resolve_chain(all_ios); - if (redirs && !setup_child_process(0, *redirs)) { + if (!setup_child_process(0, all_ios)) { // Decrement SHLVL as we're removing ourselves from the shell "stack". auto shlvl_var = vars.get(L"SHLVL", ENV_GLOBAL | ENV_EXPORT); wcstring shlvl_str = L"0"; @@ -365,7 +404,7 @@ static void on_process_created(const std::shared_ptr &j, pid_t child_pid) /// Call fork() as part of executing a process \p p in a job \j. Execute \p child_action in the /// context of the child. Returns true if fork succeeded, false if fork failed. static bool fork_child_for_process(const std::shared_ptr &job, process_t *p, - const dup2_list_t &dup2s, bool drain_threads, + const io_chain_t &io_chain, bool drain_threads, const char *fork_type, const std::function &child_action) { pid_t pid = execute_fork(drain_threads); @@ -374,7 +413,7 @@ static bool fork_child_for_process(const std::shared_ptr &job, process_t // stdout and stderr, and then exit. p->pid = getpid(); child_set_group(job.get(), p); - setup_child_process(p, dup2s); + setup_child_process(p, io_chain); child_action(); DIE("Child process returned control to fork_child lambda!"); } @@ -405,12 +444,12 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptrtype == INTERNAL_BUILTIN && "Process must be a builtin"); int local_builtin_stdin = STDIN_FILENO; - autoclose_fd_t locally_opened_stdin{}; + bool close_stdin = false; // If this is the first process, check the io redirections and see where we should // be reading from. if (pipe_read) { - local_builtin_stdin = pipe_read->pipe_fd(); + local_builtin_stdin = pipe_read->pipe_fd[0]; } else if (const auto in = proc_io_chain.get_io_for_fd(STDIN_FILENO)) { switch (in->io_mode) { case io_mode_t::fd: { @@ -429,20 +468,20 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptr(in.get()); - if (in_pipe->fd == STDIN_FILENO) { - local_builtin_stdin = in_pipe->pipe_fd(); - } + local_builtin_stdin = in_pipe->pipe_fd[0]; break; } case io_mode_t::file: { + // Do not set CLO_EXEC because child needs access. const io_file_t *in_file = static_cast(in.get()); - locally_opened_stdin = - autoclose_fd_t{open(in_file->filename_cstr, in_file->flags, OPEN_MASK)}; - if (!locally_opened_stdin.valid()) { + 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 { + close_stdin = true; } - local_builtin_stdin = locally_opened_stdin.fd(); + break; } case io_mode_t::close: { @@ -498,6 +537,10 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptrset_flag(job_flag_t::FOREGROUND, fg); + // If stdin has been redirected, close the redirection stream. + if (close_stdin) { + exec_close(local_builtin_stdin); + } return true; // "success" } @@ -525,11 +568,7 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, if (!must_fork && p->is_last_in_job) { // We are handling reads directly in the main loop. Note that we may still end // up forking. - const bool stdout_is_bufferfill = - (stdout_io && stdout_io->io_mode == io_mode_t::bufferfill); - const std::shared_ptr stdout_buffer = - stdout_is_bufferfill ? static_cast(stdout_io.get())->buffer() - : nullptr; + const bool stdout_is_to_buffer = stdout_io && stdout_io->io_mode == io_mode_t::buffer; const bool no_stdout_output = stdout_stream.empty(); const bool no_stderr_output = stderr_stream.empty(); const bool stdout_discarded = stdout_stream.buffer().discarded(); @@ -539,7 +578,7 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, // need to fork or even output anything. debug(4, L"Skipping fork: no output for internal builtin '%ls'", p->argv0()); fork_was_skipped = true; - } else if (no_stderr_output && stdout_buffer) { + } 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. @@ -551,7 +590,8 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, // also produce stderr. debug(4, L"Skipping fork: buffered output for internal builtin '%ls'", p->argv0()); - stdout_buffer->append_from_stream(stdout_stream); + io_buffer_t *io_buffer = static_cast(stdout_io.get()); + io_buffer->append_from_stream(stdout_stream); 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. @@ -594,15 +634,9 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, const char *errbuff = errbuff_str.data(); size_t errbuff_len = errbuff_str.size(); - // Resolve our IO chain to a sequence of dup2s. - auto dup2s = dup2_list_t::resolve_chain(*io_chain); - if (!dup2s) { - return false; - } - fflush(stdout); fflush(stderr); - if (!fork_child_for_process(j, p, *dup2s, false, "internal builtin", [&] { + if (!fork_child_for_process(j, p, *io_chain, false, "internal builtin", [&] { do_builtin_io(outbuff, outbuff_len, errbuff, errbuff_len); exit_without_destructors(p->status); })) { @@ -621,11 +655,6 @@ static bool exec_external_command(env_stack_t &vars, const std::shared_ptr argv_array; convert_wide_array_to_narrow(p->get_argv_array(), &argv_array); - // Convert our IO chain to a dup2 sequence. - auto dup2s = dup2_list_t::resolve_chain(proc_io_chain); - if (! dup2s) - return false; - // 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 @@ -649,7 +678,8 @@ static bool exec_external_command(env_stack_t &vars, const std::shared_ptr "Unexpected process type"); // Create an output buffer if we're piping to another process. - shared_ptr block_output_bufferfill{}; + shared_ptr block_output_io_buffer{}; if (!p->is_last_in_job) { // Be careful to handle failure, e.g. too many open fds. - block_output_bufferfill = io_bufferfill_t::create(user_ios); - if (!block_output_bufferfill) { + block_output_io_buffer = io_buffer_t::create(STDOUT_FILENO, user_ios); + if (!block_output_io_buffer) { job_mark_process_as_failed(j, p); return false; + } 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. + io_chain.push_back(block_output_io_buffer); } - // Teach the job about its bufferfill, and add it to our io chain. - io_chain.push_back(block_output_bufferfill); } if (p->type == INTERNAL_FUNCTION) { @@ -768,8 +802,10 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr int status = proc_get_last_status(); - // If we have a block output buffer, populate it now. - if (!block_output_bufferfill) { + // 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->is_last_in_job) { @@ -778,28 +814,21 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr p->completed = 1; return true; } - assert(block_output_bufferfill && "Must have a block output bufferfiller"); - // Remove our write pipe and forget it. This may close the pipe, unless another thread has - // claimed it (background write) or another process has inherited it. - io_chain.remove(block_output_bufferfill); - auto block_output_buffer = io_bufferfill_t::finish(std::move(block_output_bufferfill)); + // Here we must have a non-NULL block_output_io_buffer. + assert(block_output_io_buffer.get() != NULL); + io_chain.remove(block_output_io_buffer); + block_output_io_buffer->read(); - // Resolve our IO chain to a sequence of dup2s. - auto dup2s = dup2_list_t::resolve_chain(io_chain); - if (!dup2s) { - return false; - } - - const std::string buffer_contents = block_output_buffer->buffer().newline_serialized(); + const std::string buffer_contents = block_output_io_buffer->buffer().newline_serialized(); const char *buffer = buffer_contents.data(); size_t count = buffer_contents.size(); if (count > 0) { // We don't have to drain threads here because our child process is simple. const char *fork_reason = p->type == INTERNAL_BLOCK_NODE ? "internal block io" : "internal function io"; - if (!fork_child_for_process(j, p, *dup2s, false, fork_reason, [&] { - exec_write_and_exit(STDOUT_FILENO, buffer, count, status); + if (!fork_child_for_process(j, p, io_chain, false, fork_reason, [&] { + exec_write_and_exit(block_output_io_buffer->fd, buffer, count, status); })) { return false; } @@ -819,28 +848,19 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< autoclose_fd_t pipe_current_read, autoclose_fd_t *out_pipe_next_read, const io_chain_t &all_ios, size_t stdout_read_limit) { - // The pipe this command will write to (if any). - shared_ptr pipe_write; - // The pipe this command will read from (if any). - shared_ptr pipe_read; + // 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(); - // See if we need a pipe for the next command. + // See if we need a pipe. const bool pipes_to_next_command = !p->is_last_in_job; - if (pipes_to_next_command) { - // Construct our pipes. - auto local_pipes = make_autoclose_pipes(all_ios); - if (!local_pipes) { - debug(1, PIPE_ERROR); - wperror(L"pipe"); - job_mark_process_as_failed(j, p); - return false; - } - pipe_write = std::make_shared(p->pipe_write_fd, false /* not input */, - std::move(local_pipes->write)); - *out_pipe_next_read = std::move(local_pipes->read); - } + // The write end of any pipe we create. + autoclose_fd_t pipe_current_write{}; + // 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: // @@ -868,10 +888,12 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< // // 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; - // The IO chain for this process. - io_chain_t process_net_io_chain = j->block_io_chain(); - if (pipe_write) { + // Write pipe goes first. + if (pipes_to_next_command) { + pipe_write.reset(new io_pipe_t(p->pipe_write_fd, false)); process_net_io_chain.push_back(pipe_write); } @@ -879,9 +901,10 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< process_net_io_chain.append(p->io_chain()); // Read pipe goes last. - if (pipe_current_read.valid()) { - pipe_read = std::make_shared(STDIN_FILENO, true /* input */, - std::move(pipe_current_read)); + if (!p->is_first_in_job) { + pipe_read.reset(new io_pipe_t(STDIN_FILENO, true)); + // Record the current read in pipe_read. + pipe_read->pipe_fd[0] = pipe_current_read.fd(); process_net_io_chain.push_back(pipe_read); } @@ -899,6 +922,36 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< parser.vars().export_arr(); } + // 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) { + debug(1, PIPE_ERROR); + wperror(L"pipe"); + job_mark_process_as_failed(j, p); + return false; + } + + // 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"); + job_mark_process_as_failed(j, p); + return false; + } + + // 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); + + // Record our pipes. + pipe_current_write.reset(local_pipe[1]); + out_pipe_next_read->reset(local_pipe[0]); + } + // Execute the process. switch (p->type) { case INTERNAL_FUNCTION: @@ -959,13 +1012,18 @@ bool exec_job(parser_t &parser, shared_ptr j) { } } + // Verify that all io_mode_t::buffers are output. We used to support a (single, hacked-in) + // magical input io_mode_t::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. size_t stdout_read_limit = 0; const io_chain_t all_ios = j->all_io_redirections(); - for (auto &io : all_ios) { - if ((io->io_mode == io_mode_t::bufferfill)) { - // The read limit is dictated by the last bufferfill. - const auto *bf = static_cast(io.get()); - stdout_read_limit = bf->buffer()->read_limit(); + for (size_t idx = 0; idx < all_ios.size(); idx++) { + const shared_ptr &io = all_ios.at(idx); + + if ((io->io_mode == io_mode_t::buffer)) { + io_buffer_t *io_buffer = static_cast(io.get()); + assert(!io_buffer->is_input); + stdout_read_limit = io_buffer->buffer().limit(); } } @@ -974,6 +1032,21 @@ bool exec_job(parser_t &parser, shared_ptr j) { DIE("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 (const auto io : all_ios) { + if (io->io_mode == io_mode_t::buffer) { + auto *io_buffer = static_cast(io.get()); + 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->processes.front().get()); + break; + } + } + } + // 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. // @@ -1029,28 +1102,29 @@ static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, wcstrin // IO buffer creation may fail (e.g. if we have too many open files to make a pipe), so this may // be null. - size_t read_limit = is_subcmd ? read_byte_limit : 0; - std::shared_ptr buffer; - if (auto bufferfill = io_bufferfill_t::create(io_chain_t{}, read_limit)) { + const shared_ptr io_buffer( + io_buffer_t::create(STDOUT_FILENO, io_chain_t(), is_subcmd ? read_byte_limit : 0)); + if (io_buffer.get() != NULL) { parser_t &parser = parser_t::principal_parser(); - if (parser.eval(cmd, io_chain_t{bufferfill}, SUBST) == 0) { + if (parser.eval(cmd, io_chain_t{io_buffer}, SUBST) == 0) { subcommand_status = proc_get_last_status(); } - buffer = io_bufferfill_t::finish(std::move(bufferfill)); + + io_buffer->read(); } - if (buffer && buffer->buffer().discarded()) subcommand_status = STATUS_READ_TOO_MUCH; + if (io_buffer->buffer().discarded()) subcommand_status = STATUS_READ_TOO_MUCH; // 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 || !buffer) { + if (lst == NULL || io_buffer.get() == NULL) { return subcommand_status; } // Walk over all the elements. - for (const auto &elem : buffer->buffer().elements()) { + for (const auto &elem : io_buffer->buffer().elements()) { if (elem.is_explicitly_separated()) { // Just append this one. lst->push_back(str2wcstring(elem.contents)); diff --git a/src/exec.h b/src/exec.h index 63b78abc7..010de4654 100644 --- a/src/exec.h +++ b/src/exec.h @@ -31,6 +31,10 @@ int exec_subshell(const wcstring &cmd, parser_t &parser, bool preserve_exit_stat /// 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. +int exec_pipe(int fd[2]); + /// Gets the interpreter for a given command. char *get_interpreter(const char *command, char *interpreter, size_t buff_size); diff --git a/src/fish.cpp b/src/fish.cpp index fc798bf4a..ceb49c053 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -383,7 +383,7 @@ int main(int argc, char **argv) { parser_t &parser = parser_t::principal_parser(); - const io_chain_t empty_ios {}; + const io_chain_t empty_ios; if (read_init(paths)) { // Stomp the exit status of any initialization commands (issue #635). proc_set_last_status(STATUS_CMD_OK); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index cecb566f3..1760944a7 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -64,7 +64,6 @@ #include "path.h" #include "proc.h" #include "reader.h" -#include "redirection.h" #include "screen.h" #include "signal.h" #include "tnode.h" @@ -678,20 +677,6 @@ static void test_iothread() { max_achieved_thread_count); } -static void test_pthread() { - say(L"Testing pthreads"); - pthread_t result = {}; - int val = 3; - bool made = make_pthread(&result, [&val](){ - val += 2; - }); - do_test(made); - void *ignore = nullptr; - int ret = pthread_join(result, &ignore); - do_test(ret == 0); - do_test(val == 5); -} - 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)) { @@ -934,7 +919,8 @@ static void test_parser() { } static void test_1_cancellation(const wchar_t *src) { - auto filler = io_bufferfill_t::create(io_chain_t{}); + shared_ptr out_buff(io_buffer_t::create(STDOUT_FILENO, io_chain_t())); + const io_chain_t io_chain{out_buff}; pthread_t thread = pthread_self(); double delay = 0.25 /* seconds */; iothread_perform([=]() { @@ -942,11 +928,11 @@ static void test_1_cancellation(const wchar_t *src) { usleep(delay * 1E6); pthread_kill(thread, SIGINT); }); - parser_t::principal_parser().eval(src, io_chain_t{filler}, TOP); - auto buffer = io_bufferfill_t::finish(std::move(filler)); - if (buffer->buffer().size() != 0) { + parser_t::principal_parser().eval(src, io_chain, TOP); + out_buff->read(); + if (out_buff->buffer().size() != 0) { err(L"Expected 0 bytes in out_buff, but instead found %lu bytes\n", - buffer->buffer().size()); + out_buff->buffer().size()); } iothread_drain_all(); } @@ -2353,31 +2339,6 @@ static void test_wcstod() { tod_test(L"nope", "nope"); } -static void test_dup2s() { - using std::make_shared; - io_chain_t chain; - chain.push_back(make_shared(17)); - chain.push_back(make_shared(3, 19, true)); - auto list = dup2_list_t::resolve_chain(chain); - do_test(list.has_value()); - do_test(list->get_actions().size() == 2); - - auto act1 = list->get_actions().at(0); - do_test(act1.src == 17); - do_test(act1.target == -1); - - auto act2 = list->get_actions().at(1); - do_test(act2.src == 19); - do_test(act2.target == 3); - - // Invalid files should fail to open. - // Suppress the debug() message. - scoped_push saved_debug_level(&debug_level, -1); - chain.push_back(make_shared(2, L"/definitely/not/a/valid/path/for/this/test", 0666)); - list = dup2_list_t::resolve_chain(chain); - do_test(!list.has_value()); -} - /// Testing colors. static void test_colors() { say(L"Testing colors"); @@ -5095,7 +5056,6 @@ int main(int argc, char **argv) { if (should_test_function("convert_nulls")) test_convert_nulls(); if (should_test_function("tok")) test_tokenizer(); if (should_test_function("iothread")) test_iothread(); - if (should_test_function("pthread")) test_pthread(); if (should_test_function("parser")) test_parser(); if (should_test_function("cancellation")) test_cancellation(); if (should_test_function("indents")) test_indents(); @@ -5111,7 +5071,6 @@ int main(int argc, char **argv) { if (should_test_function("abbreviations")) test_abbreviations(); if (should_test_function("test")) test_test(); if (should_test_function("wcstod")) test_wcstod(); - if (should_test_function("dup2s")) test_dup2s(); if (should_test_function("path")) test_path(); if (should_test_function("pager_navigation")) test_pager_navigation(); if (should_test_function("pager_layout")) test_pager_layout(); diff --git a/src/io.cpp b/src/io.cpp index 51ce05e89..5cc0b733c 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -11,7 +11,6 @@ #include "exec.h" #include "fallback.h" // IWYU pragma: keep #include "io.h" -#include "iothread.h" #include "wutil.h" // IWYU pragma: keep io_data_t::~io_data_t() = default; @@ -23,13 +22,16 @@ void io_fd_t::print() const { fwprintf(stderr, L"FD map %d -> %d\n", old_fd, fd) void io_file_t::print() const { fwprintf(stderr, L"file (%s)\n", filename_cstr); } void io_pipe_t::print() const { - fwprintf(stderr, L"pipe {%d} (input: %s)\n", pipe_fd(), is_input_ ? "yes" : "no"); + fwprintf(stderr, L"pipe {%d, %d} (input: %s)\n", pipe_fd[0], pipe_fd[1], + is_input ? "yes" : "no"); } -void io_bufferfill_t::print() const { fwprintf(stderr, L"bufferfill {%d}\n", write_fd_.fd()); } +void io_buffer_t::print() const { + fwprintf(stderr, L"buffer (input: %s, size %lu)\n", + is_input ? "yes" : "no", (unsigned long)buffer_.size()); +} void io_buffer_t::append_from_stream(const output_stream_t &stream) { - scoped_lock locker(append_lock_); if (buffer_.discarded()) return; if (stream.buffer().discarded()) { buffer_.set_discard(); @@ -38,143 +40,74 @@ void io_buffer_t::append_from_stream(const output_stream_t &stream) { buffer_.append_wide_buffer(stream.buffer()); } -void io_buffer_t::run_background_fillthread(autoclose_fd_t readfd) { - // Here we are running the background fillthread, executing in a background thread. - // Our plan is: - // 1. poll via select() until the fd is readable. - // 2. Acquire the append lock. - // 3. read until EAGAIN (would block), appending - // 4. release the lock - // The purpose of holding the lock around the read calls is to ensure that data from background - // processes isn't weirdly interspersed with data directly transferred (from a builtin to a buffer). +void io_buffer_t::read() { + exec_close(pipe_fd[1]); - const int fd = readfd.fd(); - - // 100 msec poll rate. Note that in most cases, the write end of the pipe will be closed so - // select() will return; the polling is important only for weird cases like a background process - // launched in a command substitution. - const long poll_timeout_usec = 100000; - struct timeval tv = {}; - tv.tv_usec = poll_timeout_usec; - - bool shutdown = false; - while (!shutdown) { - bool readable = false; - // Check the shutdown flag. - shutdown |= this->shutdown_fillthread_.load(std::memory_order_relaxed); - - // Poll if our fd is readable. - // Do this even if the shutdown flag is set. It's important we wait for the fd at least - // once. For short-lived processes, it's possible for the process to execute, produce output - // (fits in the pipe buffer) and be reaped before we are even scheduled. So always wait at - // least once on the fd. Note that doesn't mean we will wait for the full poll duration; - // typically what will happen is our pipe will be widowed and so this will return quickly. - // It's only for weird cases (e.g. a background process launched inside a command - // substitution) that we'll wait out the entire poll time. - fd_set fds; - FD_ZERO(&fds); - FD_SET(fd, &fds); - int ret = select(fd + 1, &fds, NULL, NULL, &tv); - readable = ret > 0; - if (ret < 0 && errno != EINTR) { - // Surprising error. - wperror(L"select"); - return; - } - - if (readable || shutdown) { - // Now either our fd is readable, or we have set the shutdown flag. - // Either way acquire the lock and read until we reach EOF, or EAGAIN / EINTR. - scoped_lock locker(append_lock_); - ssize_t ret; - do { - char buff[4096]; - ret = read(fd, buff, sizeof buff); - if (ret > 0) { - buffer_.append(&buff[0], &buff[ret]); - } else if (ret == 0) { - shutdown = true; - } else if (errno != EINTR && errno != EAGAIN) { - wperror(L"read"); - return; + if (io_mode == io_mode_t::buffer) { + debug(4, L"io_buffer_t::read: blocking read on fd %d", pipe_fd[0]); + while (1) { + char b[4096]; + long len = read_blocked(pipe_fd[0], b, 4096); + if (len == 0) { + break; + } else if (len < 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) { + const wchar_t *fmt = + _(L"An error occured while reading output from code block on fd %d"); + debug(1, fmt, pipe_fd[0]); + wperror(L"io_buffer_t::read"); } - } while (ret > 0); + + break; + } else { + buffer_.append(&b[0], &b[len]); + } } } - assert(shutdown && "Should only exit loop if shutdown flag is set"); } -void io_buffer_t::begin_background_fillthread(autoclose_fd_t fd) { - ASSERT_IS_MAIN_THREAD(); - assert(!fillthread_ && "Already have a fillthread"); - - // We want our background thread to own the fd but it's not easy to move into a std::function. - // Use a shared_ptr. - auto fdref = std::make_shared(std::move(fd)); - - // Our function to read until the receiver is closed. - // It's OK to capture 'this' by value because 'this' owns the background thread and joins it - // before dtor. - std::function func = [this, fdref]() { - this->run_background_fillthread(std::move(*fdref)); - }; - - pthread_t fillthread{}; - if (!make_pthread(&fillthread, std::move(func))) { - wperror(L"make_pthread"); +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) { + wperror(L"dup"); } - fillthread_ = fillthread; + return result; } -void io_buffer_t::complete_background_fillthread() { - ASSERT_IS_MAIN_THREAD(); - assert(fillthread_ && "Should have a fillthread"); - shutdown_fillthread_.store(true, std::memory_order_relaxed); - void *ignored = nullptr; - int err = pthread_join(*fillthread_, &ignored); - DIE_ON_FAILURE(err); - fillthread_.reset(); -} +shared_ptr io_buffer_t::create(int fd, const io_chain_t &conflicts, + size_t buffer_limit) { + bool success = true; + assert(fd >= 0); + shared_ptr buffer_redirect(new io_buffer_t(fd, buffer_limit)); -shared_ptr io_bufferfill_t::create(const io_chain_t &conflicts, - size_t buffer_limit) { - // Construct our pipes. - auto pipes = make_autoclose_pipes(conflicts); - if (!pipes) { - return nullptr; - } - // Our buffer will read from the read end of the pipe. This end must be non-blocking. This is - // because our fillthread needs to poll to decide if it should shut down, and also accept input - // from direct buffer transfers. - if (make_fd_nonblocking(pipes->read.fd())) { + 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. + success = false; + } else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) { debug(1, PIPE_ERROR); wperror(L"fcntl"); - return nullptr; + success = false; } - // Our fillthread gets the read end of the pipe; out_pipe gets the write end. - auto buffer = std::make_shared(buffer_limit); - buffer->begin_background_fillthread(std::move(pipes->read)); - return std::make_shared(std::move(pipes->write), buffer); + + if (!success) { + buffer_redirect.reset(); + } + return buffer_redirect; } -std::shared_ptr io_bufferfill_t::finish(std::shared_ptr &&filler) { - // The io filler is passed in. This typically holds the only instance of the write side of the - // pipe used by the buffer's fillthread (except for that side held by other processes). Get the - // buffer out of the bufferfill and clear the shared_ptr; this will typically widow the pipe. - // Then allow the buffer to finish. - assert(filler && "Null pointer in finish"); - auto buffer = filler->buffer(); - filler.reset(); - buffer->complete_background_fillthread(); - return buffer; -} - -io_pipe_t::~io_pipe_t() = default; - -io_bufferfill_t::~io_bufferfill_t() = default; - io_buffer_t::~io_buffer_t() { - assert(! fillthread_ && "io_buffer_t destroyed with outstanding fillthread"); + 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. } void io_chain_t::remove(const shared_ptr &element) { @@ -230,8 +163,11 @@ void io_print(const io_chain_t &chain) } #endif - -int move_fd_to_unused(int fd, const io_chain_t &io_chain, bool cloexec) { +/// 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 (fd < 0 || io_chain.get_io_for_fd(fd).get() == NULL) { return fd; } @@ -252,7 +188,7 @@ int move_fd_to_unused(int fd, const io_chain_t &io_chain, bool cloexec) { // 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. - if (cloexec) set_cloexec(tmp_fd); + set_cloexec(tmp_fd); new_fd = move_fd_to_unused(tmp_fd, io_chain); } @@ -264,7 +200,7 @@ int move_fd_to_unused(int fd, const io_chain_t &io_chain, bool cloexec) { return new_fd; } -static 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++) { fds[i] = move_fd_to_unused(fds[i], ios); @@ -288,27 +224,6 @@ static bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios return success; } -maybe_t make_autoclose_pipes(const io_chain_t &ios) { - int pipes[2] = {-1, -1}; - - if (pipe(pipes) < 0) { - debug(1, PIPE_ERROR); - wperror(L"pipe"); - return none(); - } - set_cloexec(pipes[0]); - set_cloexec(pipes[1]); - - if (!pipe_avoid_conflicts_with_io_chain(pipes, ios)) { - // The pipes are closed on failure here. - return none(); - } - autoclose_pipes_t result; - result.read = autoclose_fd_t(pipes[0]); - result.write = autoclose_fd_t(pipes[1]); - return maybe_t{std::move(result)}; -} - /// 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(); diff --git a/src/io.h b/src/io.h index 5cffd0d21..161ec5941 100644 --- a/src/io.h +++ b/src/io.h @@ -1,21 +1,26 @@ #ifndef FISH_IO_H #define FISH_IO_H -#include #include #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 +#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 +using std::shared_ptr; +#else +// C++03 or libstdc++ +#include +using std::tr1::shared_ptr; +#endif #include "common.h" #include "env.h" -#include "maybe.h" - -using std::shared_ptr; /// separated_buffer_t is composed of a sequence of elements, some of which may be explicitly /// separated (e.g. through string spit0) and some of which the separation is inferred. This enum @@ -145,7 +150,7 @@ class separated_buffer_t { }; /// Describes what type of IO operation an io_data_t represents. -enum class io_mode_t { file, pipe, fd, close, bufferfill }; +enum class io_mode_t { file, pipe, fd, buffer, close }; /// Represents an FD redirection. class io_data_t { @@ -205,125 +210,64 @@ class io_file_t : public io_data_t { ~io_file_t() override { free((void *)filename_cstr); } }; -/// Represents (one end) of a pipe. class io_pipe_t : public io_data_t { - // The pipe's fd. Conceptually this is dup2'd to io_data_t::fd. - autoclose_fd_t pipe_fd_; - - /// Whether this is an input pipe. This is used only for informational purposes. - const bool is_input_; + 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: + int pipe_fd[2]; + const bool is_input; + void print() const override; - io_pipe_t(int fd, bool is_input, autoclose_fd_t pipe_fd) - : io_data_t(io_mode_t::pipe, fd), pipe_fd_(std::move(pipe_fd)), is_input_(is_input) {} - - ~io_pipe_t(); - - int pipe_fd() const { return pipe_fd_.fd(); } + io_pipe_t(int f, bool i) : io_data_t(io_mode_t::pipe, f), is_input(i) { + pipe_fd[0] = pipe_fd[1] = -1; + } }; -class io_buffer_t; class io_chain_t; - -/// Represents filling an io_buffer_t. Very similar to io_pipe_t. -/// Bufferfills always target stdout. -class io_bufferfill_t : public io_data_t { - /// Write end. The other end is connected to an io_buffer_t. - const autoclose_fd_t write_fd_; - - /// The receiving buffer. - const std::shared_ptr buffer_; - - public: - void print() const override; - - // The ctor is public to support make_shared() in the static create function below. - // Do not invoke this directly. - io_bufferfill_t(autoclose_fd_t write_fd, std::shared_ptr buffer) - : io_data_t(io_mode_t::bufferfill, STDOUT_FILENO), - write_fd_(std::move(write_fd)), - buffer_(std::move(buffer)) {} - - ~io_bufferfill_t(); - - std::shared_ptr buffer() const { return buffer_; } - - /// \return the fd that, when written to, fills the buffer. - int write_fd() const { return write_fd_.fd(); } - - /// Create an io_bufferfill_t which, when written from, fills a buffer with the contents. - /// \returns nullptr on failure, e.g. too many open fds. - /// - /// \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 shared_ptr create(const io_chain_t &conflicts, size_t buffer_limit = 0); - - /// Reset the receiver (possibly closing the write end of the pipe), and complete the fillthread - /// of the buffer. \return the buffer. - static std::shared_ptr finish(std::shared_ptr &&filler); -}; - class output_stream_t; - -/// An io_buffer_t is a buffer which can populate itself by reading from an fd. -/// It is not an io_data_t. -class io_buffer_t { +class io_buffer_t : public io_pipe_t { private: - friend io_bufferfill_t; - - /// Buffer storing what we have read. separated_buffer_t buffer_; - /// Atomic flag indicating our fillthread should shut down. - std::atomic shutdown_fillthread_; - - /// The background fillthread itself, if any. - maybe_t fillthread_{}; - - /// Read limit of the buffer. - const size_t read_limit_; - - /// Lock for appending. - std::mutex append_lock_{}; - - /// Called in the background thread to run it. - void run_background_fillthread(autoclose_fd_t readfd); - - /// Begin the background fillthread operation, reading from the given fd. - void begin_background_fillthread(autoclose_fd_t readfd); - - /// End the background fillthread operation. - void complete_background_fillthread(); - - public: - explicit io_buffer_t(size_t limit) : buffer_(limit), read_limit_(limit) { + explicit io_buffer_t(int f, size_t limit) + : io_pipe_t(io_mode_t::buffer, f, false /* not input */), buffer_(limit) { // Explicitly reset the discard flag because we share this buffer. buffer_.reset_discard(); } - ~io_buffer_t(); + public: + void print() const override; + + ~io_buffer_t() override; /// Access the underlying buffer. - /// This requires that the background fillthread be none. - const separated_buffer_t &buffer() const { - assert(!fillthread_ && "Cannot access buffer during background fill"); - return buffer_; - } + const separated_buffer_t &buffer() const { return buffer_; } /// Function to append to the buffer. - void append(const char *ptr, size_t count) { - scoped_lock locker(append_lock_); - buffer_.append(ptr, ptr + count); - } + void append(const char *ptr, size_t count) { buffer_.append(ptr, ptr + count); } - /// \return the read limit. - size_t read_limit() const { return read_limit_; } + /// 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. + void read(); /// Appends data from a given output_stream_t. /// Marks the receiver as discarded if the stream was discarded. void append_from_stream(const output_stream_t &stream); + + /// Create a io_mode_t::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 shared_ptr create(int fd, const io_chain_t &conflicts, + size_t buffer_limit = 0); }; class io_chain_t : public std::vector> { @@ -343,24 +287,11 @@ class io_chain_t : public std::vector> { shared_ptr io_chain_get(const io_chain_t &src, int fd); shared_ptr io_chain_get(io_chain_t &src, int fd); -/// Helper type returned from making autoclose pipes. -struct autoclose_pipes_t { - /// Read end of the pipe. - autoclose_fd_t read; - - /// Write end of the pipe. - autoclose_fd_t write; -}; -/// Call pipe(), populating autoclose fds, avoiding conflicts. -/// The pipes are marked CLO_EXEC. -/// \return pipes on success, none() on error. -maybe_t make_autoclose_pipes(const io_chain_t &ios); - -/// 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. -/// If \p cloexec is set, any fd created is marked close-on-exec. -/// \returns -1 on failure (in which case the given fd is still closed). -int move_fd_to_unused(int fd, const io_chain_t &io_chain, bool cloexec = true); +/// 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 { diff --git a/src/iothread.cpp b/src/iothread.cpp index c19f69339..d834da8c1 100644 --- a/src/iothread.cpp +++ b/src/iothread.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -150,14 +149,24 @@ static void *iothread_worker(void *unused) { /// 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); + DIE_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. pthread_t thread = 0; - if (make_pthread(&thread, iothread_worker, nullptr)) { - // We will never join this thread. - DIE_ON_FAILURE(pthread_detach(thread)); - } + pthread_create(&thread, NULL, iothread_worker, NULL); + + // We will never join this thread. + DIE_ON_FAILURE(pthread_detach(thread)); + debug(5, "pthread %p spawned", (void *)(intptr_t)thread); + // Restore our sigmask. + DIE_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &saved_set, NULL)); } int iothread_perform_impl(void_function_t &&func, void_function_t &&completion) { @@ -333,48 +342,3 @@ void iothread_perform_on_main(void_function_t &&func) { // Ok, the request must now be done. assert(req.done); } - -bool make_pthread(pthread_t *result, void *(*func)(void *), void *param) { - // 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); - DIE_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. - pthread_t thread = 0; - int err = pthread_create(&thread, NULL, func, param); - if (err == 0) { - // Success, return the thread. - debug(5, "pthread %p spawned", (void *)(intptr_t)thread); - *result = thread; - } else { - perror("pthread_create"); - } - // Restore our sigmask. - DIE_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &saved_set, NULL)); - return err == 0; -} - -using void_func_t = std::function; - -static void *func_invoker(void *param) { - void_func_t *vf = static_cast(param); - (*vf)(); - delete vf; - return nullptr; -} - -bool make_pthread(pthread_t *result, void_func_t &&func) { - // Copy the function into a heap allocation. - void_func_t *vf = new void_func_t(std::move(func)); - if (make_pthread(result, func_invoker, vf)) { - return true; - } - // Thread spawning failed, clean up our heap allocation. - delete vf; - return false; -} diff --git a/src/iothread.h b/src/iothread.h index 5e677b639..8b29370c8 100644 --- a/src/iothread.h +++ b/src/iothread.h @@ -69,16 +69,10 @@ int iothread_perform(const HANDLER &handler, const COMPLETION &completion) { // variant of iothread_perform without a completion handler inline int iothread_perform(std::function &&func) { - return iothread_perform_impl(std::move(func), {}); + return iothread_perform_impl(std::move(func), std::function()); } /// Performs a function on the main thread, blocking until it completes. void iothread_perform_on_main(std::function &&func); -/// Creates a pthread, manipulating the signal mask so that the thread receives no signals. -/// The pthread runs \p func. -/// \returns true on success, false on failure. -bool make_pthread(pthread_t *result, void *(*func)(void *), void *param); -bool make_pthread(pthread_t *result, std::function &&func); - #endif diff --git a/src/postfork.cpp b/src/postfork.cpp index d8ce8272e..7a6f655c0 100644 --- a/src/postfork.cpp +++ b/src/postfork.cpp @@ -19,7 +19,6 @@ #include "iothread.h" #include "postfork.h" #include "proc.h" -#include "redirection.h" #include "signal.h" #include "wutil.h" // IWYU pragma: keep @@ -39,6 +38,27 @@ /// Fork error message. #define FORK_ERROR "Could not create child process - exiting" +/// File redirection clobbering error message. +#define NOCLOB_ERROR "The file '%s' already exists" + +/// 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" + +/// 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) { + char buff[128]; + format_long_safe(buff, val); + debug_safe(level, format, buff); +} + /// Called only by the child to set its own process group (possibly creating a new group in the /// process if it is the first in a JOB_CONTROL job. /// Returns true on sucess, false on failiure. @@ -155,23 +175,126 @@ bool maybe_assign_terminal(const job_t *j) { return true; } -int setup_child_process(process_t *p, const dup2_list_t &dup2s) { - for (const auto &act : dup2s.get_actions()) { - int err = act.target < 0 ? close(act.src) : dup2(act.src, act.target); - if (err < 0) { - // We have a null p if this is for the exec (non-fork) path. - if (p != nullptr) { - debug_safe(4, "redirect_in_child_after_fork failed in setup_child_process"); - exit_without_destructors(1); +/// 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_chain 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_mode_t::fd && io->fd == static_cast(io)->old_fd) { + continue; + } + + switch (io->io_mode) { + case io_mode_t::close: { + if (log_redirections) fwprintf(stderr, L"%d: close %d\n", getpid(), io->fd); + if (close(io->fd)) { + debug_safe_int(0, "Failed to close file descriptor %s", io->fd); + safe_perror("close"); + } + break; + } + + case io_mode_t::file: { + // Here we definitely do not want to set CLO_EXEC because our child needs access. + const io_file_t *io_file = static_cast(io); + int tmp = open(io_file->filename_cstr, io_file->flags, OPEN_MASK); + if (tmp < 0) { + if ((io_file->flags & O_EXCL) && (errno == EEXIST)) { + debug_safe(1, NOCLOB_ERROR, io_file->filename_cstr); + } 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. + close(io->fd); + + if (dup2(tmp, io->fd) == -1) { + debug_safe_int(1, FD_ERROR, io->fd); + safe_perror("dup2"); + exec_close(tmp); + return -1; + } + exec_close(tmp); + } + break; + } + + case io_mode_t::fd: { + int old_fd = static_cast(io)->old_fd; + if (log_redirections) + fwprintf(stderr, L"%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. + close(io->fd); + + if (dup2(old_fd, io->fd) == -1) { + debug_safe_int(1, FD_ERROR, io->fd); + safe_perror("dup2"); + return -1; + } + break; + } + + case io_mode_t::buffer: + case io_mode_t::pipe: { + const io_pipe_t *io_pipe = static_cast(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). + unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1); +#if 0 + debug(0, L"%ls %ls on fd %d (%d %d)", write_pipe?L"write":L"read", + (io->io_mode == io_mode_t::buffer)?L"buffer":L"pipe", io->fd, io->pipe_fd[0], + io->pipe_fd[1]); +#endif + if (log_redirections) + fwprintf(stderr, L"%d: %s dup %d to %d\n", getpid(), + io->io_mode == io_mode_t::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]); + break; } - return err; } } - // Set the handling for job control signals back to the default. - signal_reset_handlers(); + return 0; } +int setup_child_process(process_t *p, const io_chain_t &io_chain) { + bool ok = true; + + if (ok) { + // In the case of io_mode_t::file, this can hang until data is available to read/write! + ok = (0 == handle_child_io(io_chain)); + if (p != 0 && !ok) { + debug_safe(4, "handle_child_io failed in setup_child_process"); + exit_without_destructors(1); + } + } + + if (ok) { + // Set the handling for job control signals back to the default. + signal_reset_handlers(); + } + + return ok ? 0 : -1; +} int g_fork_count = 0; @@ -223,8 +346,9 @@ 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, const job_t *j, - const dup2_list_t &dup2s) { + posix_spawn_file_actions_t *actions, job_t *j, process_t *p, + const io_chain_t &io_chain) { + UNUSED(p); // Initialize the output. if (posix_spawnattr_init(attr) != 0) { return false; @@ -278,13 +402,52 @@ bool fork_actions_make_spawn_properties(posix_spawnattr_t *attr, sigemptyset(&sigmask); if (!err && reset_sigmask) err = posix_spawnattr_setsigmask(attr, &sigmask); - // Apply our dup2s. - for (const auto &act : dup2s.get_actions()) { - if (err) break; - if (act.target < 0) { - err = posix_spawn_file_actions_addclose(actions, act.src); - } else { - err = posix_spawn_file_actions_adddup2(actions, act.src, act.target); + for (size_t idx = 0; idx < io_chain.size(); idx++) { + const shared_ptr io = io_chain.at(idx); + + if (io->io_mode == io_mode_t::fd) { + const io_fd_t *io_fd = static_cast(io.get()); + if (io->fd == io_fd->old_fd) continue; + } + + switch (io->io_mode) { + case io_mode_t::close: { + if (!err) err = posix_spawn_file_actions_addclose(actions, io->fd); + break; + } + + case io_mode_t::file: { + const io_file_t *io_file = static_cast(io.get()); + if (!err) + err = posix_spawn_file_actions_addopen(actions, io->fd, io_file->filename_cstr, + io_file->flags /* mode */, OPEN_MASK); + break; + } + + case io_mode_t::fd: { + const io_fd_t *io_fd = static_cast(io.get()); + if (!err) + err = posix_spawn_file_actions_adddup2(actions, io_fd->old_fd /* from */, + io->fd /* to */); + break; + } + + case io_mode_t::buffer: + case io_mode_t::pipe: { + const io_pipe_t *io_pipe = static_cast(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]); + } + break; + } } } diff --git a/src/postfork.h b/src/postfork.h index d91d335cf..f7cfe080d 100644 --- a/src/postfork.h +++ b/src/postfork.h @@ -14,7 +14,7 @@ #define FISH_USE_POSIX_SPAWN HAVE_SPAWN_H #endif -class dup2_list_t; +class io_chain_t; class job_t; class process_t; @@ -29,11 +29,11 @@ bool maybe_assign_terminal(const job_t *j); /// descriptor actions are performed. /// /// \param p the child process to set up -/// \param dup2 the dup2 list to apply +/// \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(process_t *p, const dup2_list_t &dup2s); +int setup_child_process(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 @@ -55,8 +55,8 @@ void run_as_keepalive(pid_t parent_pid); /// 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, const job_t *j, - const dup2_list_t &dup2s); + posix_spawn_file_actions_t *actions, job_t *j, process_t *p, + const io_chain_t &io_chain); #endif #endif diff --git a/src/proc.cpp b/src/proc.cpp index a440d1cda..cdacd05bb 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -836,6 +836,89 @@ void proc_update_jiffies() { #endif +/// The return value of select_try(), indicating IO readiness or an error +enum class select_try_t { + /// One or more fds have data ready for read + DATA_READY, + /// The timeout elapsed without any data becoming available for read + TIMEOUT, + /// There were no FDs in the io chain for which to select on. + IOCHAIN_EMPTY, +}; + +/// 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 the status of the select operation +static select_try_t select_try(job_t *j) { + fd_set fds; + int maxfd = -1; + + FD_ZERO(&fds); + + const io_chain_t chain = j->all_io_redirections(); + for (const auto &io : chain) { + if (io->io_mode == io_mode_t::buffer) { + auto io_pipe = static_cast(io.get()); + int fd = io_pipe->pipe_fd[0]; + FD_SET(fd, &fds); + maxfd = std::max(maxfd, fd); + debug(4, L"select_try on fd %d", fd); + } + } + + if (maxfd >= 0) { + struct timeval timeout; + + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + + int retval = select(maxfd + 1, &fds, 0, 0, &timeout); + if (retval == 0) { + debug(4, L"select_try hit timeout"); + return select_try_t::TIMEOUT; + } + return select_try_t::DATA_READY; + } + + return select_try_t::IOCHAIN_EMPTY; +} + +/// 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. + const io_chain_t chain = j->all_io_redirections(); + for (size_t idx = 0; idx < chain.size(); idx++) { + io_data_t *d = chain.at(idx).get(); + if (d->io_mode == io_mode_t::buffer) { + buff = static_cast(d); + } + } + + if (buff) { + debug(4, L"proc::read_try('%ls')", j->command_wcstr()); + while (1) { + char b[BUFFER_SIZE]; + long len = read_blocked(buff->pipe_fd[0], b, BUFFER_SIZE); + if (len == 0) { + break; + } else if (len < 0) { + if (errno != EAGAIN) { + debug(1, _(L"An error occured while reading output from code block")); + wperror(L"read_try"); + } + break; + } else { + buff->append(b, len); + } + } + } +} + // Return control of the terminal to a job's process group. restore_attrs is true if we are restoring // a previously-stopped job, in which case we need to restore terminal attributes. bool terminal_give_to_job(const job_t *j, bool restore_attrs) { @@ -1017,6 +1100,7 @@ void job_t::continue_job(bool send_sigcont) { } }); + bool read_attempted = false; if (!is_completed()) { if (get_flag(job_flag_t::TERMINAL) && is_foreground()) { // Put the job into the foreground and give it control of the terminal. @@ -1049,15 +1133,71 @@ void job_t::continue_job(bool send_sigcont) { } if (is_foreground()) { - // Wait for the status of our own job to change. + // This is an optimization to not call select_try() in case a process has exited. While + // it may seem silly, unless there is IO (and there usually isn't in terms of total CPU + // time), select_try() will wait for 10ms (our timeout) before returning. If during + // these 10ms a process exited, the shell will basically hang until the timeout happens + // and we are free to call `process_mark_finished_children()` to discover that fact. By + // calling it here before calling `select_try()` below, shell responsiveness can be + // dramatically improved (noticably so, not just "theoretically speaking" per the + // discussion in #5219). + process_mark_finished_children(false); + + // If this is a child job and the parent job is still under construction (i.e. job1 | + // some_func), we can't block on execution of the nested job for `some_func`. Doing + // so can cause hangs if job1 emits more data than fits in the OS pipe buffer. + // The solution is to to not block on fg from the initial call in exec_job(), which + // is also the only place that send_sigcont is false. parent_job.is_constructed() + // must also be true, which coincides with WAIT_BY_PROCESS (which will have to do + // since we don't store a reference to the parent job in the job_t structure). + bool block_on_fg = send_sigcont && job_chain_is_fully_constructed(); + + // Wait for data to become available or the status of our own job to change while (!reader_exit_forced() && !is_stopped() && !is_completed()) { - process_mark_finished_children(true); + auto result = select_try(this); + read_attempted = true; + + switch (result) { + case select_try_t::DATA_READY: + // Read the data that we know is now available, then scan for finished processes + // but do not block. We don't block so long as we have IO to process, once the + // fd buffers are empty we'll block in the second case below. + read_try(this); + process_mark_finished_children(false); + break; + + case select_try_t::TIMEOUT: + // No FDs are ready. Look for finished processes instead. + debug(4, L"select_try: no fds returned valid data within the timeout" ); + process_mark_finished_children(block_on_fg); + break; + + case select_try_t::IOCHAIN_EMPTY: + // There were no IO fds to select on. + debug(4, L"select_try: no IO fds" ); + process_mark_finished_children(true); + + // If it turns out that we encountered this because the file descriptor we were + // reading from has died, process_mark_finished_children() should take care of + // changing the status of our is_completed() (assuming it is appropriate to do + // so), in which case we will break out of this loop. + break; + } } } } if (is_foreground()) { if (is_completed()) { + // It's possible that the job will produce output and exit before we've even read from + // it. In that case, make sure we read that output now, before we've executed any + // subsequent calls. This is why prompt colors were getting screwed up - the builtin + // `echo` calls were sometimes having their output combined with the `set_color` calls + // in the wrong order! + if (!read_attempted) { + read_try(this); + } + // Set $status only if we are in the foreground and the last process in the job has // finished and is not a short-circuited builtin. auto &p = processes.back(); diff --git a/src/redirection.cpp b/src/redirection.cpp deleted file mode 100644 index 6c00c4d55..000000000 --- a/src/redirection.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "config.h" // IWYU pragma: keep - -#include "redirection.h" -#include "wutil.h" - -#include - -/// File descriptor redirection error message. -#define FD_ERROR "An error occurred while redirecting file descriptor %s" - -/// Pipe error message. -#define LOCAL_PIPE_ERROR "An error occurred while setting up pipe" - -#define NOCLOB_ERROR _(L"The file '%s' already exists") - -#define FILE_ERROR _(L"An error occurred while redirecting file '%s'") - -/// Base open mode to pass to calls to open. -#define OPEN_MASK 0666 - -dup2_list_t::~dup2_list_t() = default; - -maybe_t dup2_list_t::resolve_chain(const io_chain_t &io_chain) { - ASSERT_IS_NOT_FORKED_CHILD(); - dup2_list_t result; - for (const auto &io_ref : io_chain) { - switch (io_ref->io_mode) { - case io_mode_t::file: { - // Here we definitely do not want to set CLO_EXEC because our child needs access. - // Open the file. - const io_file_t *io_file = static_cast(io_ref.get()); - int file_fd = open(io_file->filename_cstr, io_file->flags, OPEN_MASK); - if (file_fd < 0) { - if ((io_file->flags & O_EXCL) && (errno == EEXIST)) { - debug(1, NOCLOB_ERROR, io_file->filename_cstr); - } else { - debug(1, FILE_ERROR, io_file->filename_cstr); - if (should_debug(1)) wperror(L"open"); - } - return none(); - } - - // If by chance we got the file we want, we're done. Otherwise move the fd to an unused place and dup2 it. - // Note move_fd_to_unused() will close the incoming file_fd. - if (file_fd != io_file->fd) { - file_fd = move_fd_to_unused(file_fd, io_chain, false /* cloexec */); - if (file_fd < 0) { - debug(1, FILE_ERROR, io_file->filename_cstr); - if (should_debug(1)) wperror(L"dup"); - return none(); - } - } - - // Record that we opened this file, so we will auto-close it. - assert(file_fd >= 0 && "Should have a valid file_fd"); - result.opened_fds_.emplace_back(file_fd); - - // Mark our dup2 and our close actions. - result.add_dup2(file_fd, io_file->fd); - result.add_close(file_fd); - break; - } - - case io_mode_t::close: { - const io_close_t *io = static_cast(io_ref.get()); - result.add_close(io->fd); - break; - } - - case io_mode_t::fd: { - const io_fd_t *io = static_cast(io_ref.get()); - result.add_dup2(io->old_fd, io->fd); - break; - } - - case io_mode_t::pipe: { - const io_pipe_t *io = static_cast(io_ref.get()); - result.add_dup2(io->pipe_fd(), io->fd); - result.add_close(io->pipe_fd()); - break; - } - - case io_mode_t::bufferfill: { - const io_bufferfill_t *io = static_cast(io_ref.get()); - result.add_dup2(io->write_fd(), io->fd); - result.add_close(io->write_fd()); - break; - } - } - } - return result; -} diff --git a/src/redirection.h b/src/redirection.h deleted file mode 100644 index 95cc8fac0..000000000 --- a/src/redirection.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef FISH_REDIRECTION_H -#define FISH_REDIRECTION_H - -#include "common.h" -#include "maybe.h" -#include "io.h" - -#include - -/// This file supports "applying" redirections. - -/// A class representing a sequence of basic redirections. -class dup2_list_t { - public: - /// A type that represents the action dup2(src, target). - /// If target is negative, this represents close(src). - /// Note none of the fds here are considered 'owned'. - struct action_t { - int src; - int target; - }; - - private: - /// The list of actions. - std::vector actions_; - - /// The list of fds that we opened, and are responsible for closing. - std::vector opened_fds_; - - /// Append a dup2 action. - void add_dup2(int src, int target) { - assert(src >= 0 && target >= 0 && "Invalid fd in add_dup2"); - if (src != target) { - actions_.push_back(action_t{src, target}); - } - } - - /// Append a close action. - void add_close(int fd) { - assert(fd >= 0 && "Invalid fd in add_close"); - actions_.push_back(action_t{fd, -1}); - } - - dup2_list_t() = default; - -public: - ~dup2_list_t(); - - /// Disable copying because we own our fds. - dup2_list_t(const dup2_list_t &) = delete; - void operator=(const dup2_list_t &) = delete; - - dup2_list_t(dup2_list_t &&) = default; - dup2_list_t &operator=(dup2_list_t &&) = default; - - /// \return the list of dup2 actions. - const std::vector &get_actions() const { return actions_; } - - /// Produce a dup_fd_list_t from an io_chain. This may not be called before fork(). - /// The result contains the list of fd actions (dup2 and close), as well as the list of fds opened. - static maybe_t resolve_chain(const io_chain_t &); -}; - -#endif From 22d05dc18b26eafbd521a45aa442f87a5965a1f1 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 2 Feb 2019 17:45:21 -0800 Subject: [PATCH 347/439] Try once more to fix the Travis build --- src/fish.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index ceb49c053..59ff89f0e 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -383,14 +383,13 @@ int main(int argc, char **argv) { parser_t &parser = parser_t::principal_parser(); - const io_chain_t empty_ios; if (read_init(paths)) { // Stomp the exit status of any initialization commands (issue #635). proc_set_last_status(STATUS_CMD_OK); // Run post-config commands specified as arguments, if any. if (!opts.postconfig_cmds.empty()) { - res = run_command_list(&opts.postconfig_cmds, empty_ios); + res = run_command_list(&opts.postconfig_cmds, {}); } if (!opts.batch_cmds.empty()) { @@ -400,11 +399,11 @@ int main(int argc, char **argv) { fish_xdm_login_hack_hack_hack_hack(&opts.batch_cmds, argc - my_optind, argv + my_optind); } - res = run_command_list(&opts.batch_cmds, empty_ios); + res = run_command_list(&opts.batch_cmds, {}); reader_exit(0, 0); } else if (my_optind == argc) { // Implicitly interactive mode. - res = reader_read(STDIN_FILENO, empty_ios); + res = reader_read(STDIN_FILENO, {}); } else { char *file = *(argv + (my_optind++)); int fd = open(file, O_RDONLY); @@ -424,7 +423,7 @@ int main(int argc, char **argv) { reader_push_current_filename(rel_filename.c_str()); - res = reader_read(fd, empty_ios); + res = reader_read(fd, {}); if (res) { debug(1, _(L"Error while reading file %ls\n"), From e3dcb01e6774d5cc813c50bb5d41a21acabd0069 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 2 Feb 2019 17:54:48 -0800 Subject: [PATCH 348/439] Fix travis via a user-declared ctor --- src/io.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/io.h b/src/io.h index 161ec5941..8dadf5001 100644 --- a/src/io.h +++ b/src/io.h @@ -273,6 +273,8 @@ class io_buffer_t : public io_pipe_t { class io_chain_t : public std::vector> { public: using std::vector>::vector; + // user-declared ctor to allow const init. Do not default this, it will break the build. + io_chain_t() {} void remove(const shared_ptr &element); void push_back(shared_ptr element); From dbe906b79e3fc55b7f99905d0c8c14ee003ddc39 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 28 Jan 2019 13:26:22 -0800 Subject: [PATCH 349/439] Introduce dup2_list_t This represents a "resolved" io_chain_t, where all of the different io_data_t types have been reduced to a sequence of dup2() and close(). This will eliminate a lot of the logic duplication around posix_spawn vs fork, and pave the way for in-process redirections. --- CMakeLists.txt | 2 +- Makefile.in | 2 +- src/fish_tests.cpp | 27 ++++++++++++++ src/io.cpp | 9 ++--- src/io.h | 6 +++ src/redirection.cpp | 90 +++++++++++++++++++++++++++++++++++++++++++++ src/redirection.h | 62 +++++++++++++++++++++++++++++++ 7 files changed, 190 insertions(+), 8 deletions(-) create mode 100644 src/redirection.cpp create mode 100644 src/redirection.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 882888c7a..809f24162 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ SET(FISH_SRCS src/postfork.cpp src/proc.cpp src/reader.cpp src/sanity.cpp src/screen.cpp src/signal.cpp src/tinyexpr.cpp src/tnode.cpp src/tokenizer.cpp src/utf8.cpp src/util.cpp src/wcstringutil.cpp src/wgetopt.cpp src/wildcard.cpp src/wutil.cpp - src/future_feature_flags.cpp + src/future_feature_flags.cpp src/redirection.cpp ) # Header files are just globbed. diff --git a/Makefile.in b/Makefile.in index aa5e0e2ee..059e05847 100644 --- a/Makefile.in +++ b/Makefile.in @@ -119,7 +119,7 @@ FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_bg.o obj/builtin_bind.o ob 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/tinyexpr.o obj/tokenizer.o obj/tnode.o obj/utf8.o \ obj/util.o obj/wcstringutil.o obj/wgetopt.o obj/wildcard.o obj/wutil.o \ - obj/future_feature_flags.o + obj/future_feature_flags.o obj/redirection.o FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 1760944a7..8f266947d 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -64,6 +64,7 @@ #include "path.h" #include "proc.h" #include "reader.h" +#include "redirection.h" #include "screen.h" #include "signal.h" #include "tnode.h" @@ -2339,6 +2340,31 @@ static void test_wcstod() { tod_test(L"nope", "nope"); } +static void test_dup2s() { + using std::make_shared; + io_chain_t chain; + chain.push_back(make_shared(17)); + chain.push_back(make_shared(3, 19, true)); + auto list = dup2_list_t::resolve_chain(chain); + do_test(list.has_value()); + do_test(list->get_actions().size() == 2); + + auto act1 = list->get_actions().at(0); + do_test(act1.src == 17); + do_test(act1.target == -1); + + auto act2 = list->get_actions().at(1); + do_test(act2.src == 19); + do_test(act2.target == 3); + + // Invalid files should fail to open. + // Suppress the debug() message. + scoped_push saved_debug_level(&debug_level, -1); + chain.push_back(make_shared(2, L"/definitely/not/a/valid/path/for/this/test", 0666)); + list = dup2_list_t::resolve_chain(chain); + do_test(!list.has_value()); +} + /// Testing colors. static void test_colors() { say(L"Testing colors"); @@ -5071,6 +5097,7 @@ int main(int argc, char **argv) { if (should_test_function("abbreviations")) test_abbreviations(); if (should_test_function("test")) test_test(); if (should_test_function("wcstod")) test_wcstod(); + if (should_test_function("dup2s")) test_dup2s(); if (should_test_function("path")) test_path(); if (should_test_function("pager_navigation")) test_pager_navigation(); if (should_test_function("pager_layout")) test_pager_layout(); diff --git a/src/io.cpp b/src/io.cpp index 5cc0b733c..091d250f5 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -163,11 +163,8 @@ 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) { + +int move_fd_to_unused(int fd, const io_chain_t &io_chain, bool cloexec) { if (fd < 0 || io_chain.get_io_for_fd(fd).get() == NULL) { return fd; } @@ -188,7 +185,7 @@ static int move_fd_to_unused(int fd, const io_chain_t &io_chain) { // 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); + if (cloexec) set_cloexec(tmp_fd); new_fd = move_fd_to_unused(tmp_fd, io_chain); } diff --git a/src/io.h b/src/io.h index 8dadf5001..3081a240f 100644 --- a/src/io.h +++ b/src/io.h @@ -295,6 +295,12 @@ shared_ptr io_chain_get(io_chain_t &src, int fd); /// set to -1). bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios); +/// 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. +/// If \p cloexec is set, any fd created is marked close-on-exec. +/// \returns -1 on failure (in which case the given fd is still closed). +int move_fd_to_unused(int fd, const io_chain_t &io_chain, bool cloexec = true); + /// Class representing the output that a builtin can generate. class output_stream_t { private: diff --git a/src/redirection.cpp b/src/redirection.cpp new file mode 100644 index 000000000..8aed4f9a4 --- /dev/null +++ b/src/redirection.cpp @@ -0,0 +1,90 @@ +#include "config.h" // IWYU pragma: keep + +#include "redirection.h" +#include "wutil.h" + +#include + +/// File descriptor redirection error message. +#define FD_ERROR "An error occurred while redirecting file descriptor %s" + +/// Pipe error message. +#define LOCAL_PIPE_ERROR "An error occurred while setting up pipe" + +#define NOCLOB_ERROR _(L"The file '%s' already exists") + +#define FILE_ERROR _(L"An error occurred while redirecting file '%s'") + +/// Base open mode to pass to calls to open. +#define OPEN_MASK 0666 + +dup2_list_t::~dup2_list_t() = default; + +maybe_t dup2_list_t::resolve_chain(const io_chain_t &io_chain) { + ASSERT_IS_NOT_FORKED_CHILD(); + dup2_list_t result; + for (const auto &io_ref : io_chain) { + switch (io_ref->io_mode) { + case io_mode_t::file: { + // Here we definitely do not want to set CLO_EXEC because our child needs access. + // Open the file. + const io_file_t *io_file = static_cast(io_ref.get()); + int file_fd = open(io_file->filename_cstr, io_file->flags, OPEN_MASK); + if (file_fd < 0) { + if ((io_file->flags & O_EXCL) && (errno == EEXIST)) { + debug(1, NOCLOB_ERROR, io_file->filename_cstr); + } else { + debug(1, FILE_ERROR, io_file->filename_cstr); + if (should_debug(1)) wperror(L"open"); + } + return none(); + } + + // If by chance we got the file we want, we're done. Otherwise move the fd to an unused place and dup2 it. + // Note move_fd_to_unused() will close the incoming file_fd. + if (file_fd != io_file->fd) { + file_fd = move_fd_to_unused(file_fd, io_chain, false /* cloexec */); + if (file_fd < 0) { + debug(1, FILE_ERROR, io_file->filename_cstr); + if (should_debug(1)) wperror(L"dup"); + return none(); + } + } + + // Record that we opened this file, so we will auto-close it. + assert(file_fd >= 0 && "Should have a valid file_fd"); + result.opened_fds_.emplace_back(file_fd); + + // Mark our dup2 and our close actions. + result.add_dup2(file_fd, io_file->fd); + result.add_close(file_fd); + break; + } + + case io_mode_t::close: { + const io_close_t *io = static_cast(io_ref.get()); + result.add_close(io->fd); + break; + } + + case io_mode_t::fd: { + const io_fd_t *io = static_cast(io_ref.get()); + result.add_dup2(io->old_fd, io->fd); + break; + } + + case io_mode_t::buffer: + case io_mode_t::pipe: { + const io_pipe_t *io = static_cast(io_ref.get()); + // If write_pipe_idx is 0, it means we're connecting to the read end (first pipe + // fd). If it's 1, we're connecting to the write end (second pipe fd). + unsigned int write_pipe_idx = (io->is_input ? 0 : 1); + result.add_dup2(io->pipe_fd[write_pipe_idx], io->fd); + if (io->pipe_fd[0] >= 0) result.add_close(io->pipe_fd[0]); + if (io->pipe_fd[1] >= 0) result.add_close(io->pipe_fd[1]); + break; + } + } + } + return {std::move(result)}; +} diff --git a/src/redirection.h b/src/redirection.h new file mode 100644 index 000000000..e82d4376d --- /dev/null +++ b/src/redirection.h @@ -0,0 +1,62 @@ +#ifndef FISH_REDIRECTION_H +#define FISH_REDIRECTION_H + +#include "common.h" +#include "maybe.h" +#include "io.h" + +#include + +/// This file supports "applying" redirections. + +/// A class representing a sequence of basic redirections. +class dup2_list_t { + /// A type that represents the action dup2(src, target). + /// If target is negative, this represents close(src). + /// Note none of the fds here are considered 'owned'. + struct action_t { + int src; + int target; + }; + + /// The list of actions. + std::vector actions_; + + /// The list of fds that we opened, and are responsible for closing. + std::vector opened_fds_; + + /// Append a dup2 action. + void add_dup2(int src, int target) { + assert(src >= 0 && target >= 0 && "Invalid fd in add_dup2"); + if (src != target) { + actions_.push_back(action_t{src, target}); + } + } + + /// Append a close action. + void add_close(int fd) { + assert(fd >= 0 && "Invalid fd in add_close"); + actions_.push_back(action_t{fd, -1}); + } + + dup2_list_t() = default; + +public: + ~dup2_list_t(); + + /// Disable copying because we own our fds. + dup2_list_t(const dup2_list_t &) = delete; + void operator=(const dup2_list_t &) = delete; + + dup2_list_t(dup2_list_t &&) = default; + dup2_list_t &operator=(dup2_list_t &&) = default; + + /// \return the list of dup2 actions. + const std::vector &get_actions() const { return actions_; } + + /// Produce a dup_fd_list_t from an io_chain. This may not be called before fork(). + /// The result contains the list of fd actions (dup2 and close), as well as the list of fds opened. + static maybe_t resolve_chain(const io_chain_t &); +}; + +#endif From 6c22c8893bfe9a01679f9dbb10d0ef2486df3364 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 28 Jan 2019 23:17:42 -0800 Subject: [PATCH 350/439] Switch from tee to cat in psub --fifo Prior to this fix, we would write to a fifo via cat >$filename & . However in some cases (and soon in all cases) we open the file before the fork, not after. This results in a deadlock because the file open cannot succeed until a write begins. Switch to using tee to write to the file. Because tee opens the file itself, fish is no longer responsible and the deadlock is resolved. --- share/functions/psub.fish | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/share/functions/psub.fish b/share/functions/psub.fish index b764d6f35..8c8a7eac8 100644 --- a/share/functions/psub.fish +++ b/share/functions/psub.fish @@ -29,7 +29,10 @@ function psub --description "Read from stdin into a file and output the filename or return set filename $dirname/psub.fifo"$_flag_suffix" mkfifo $filename - cat >$filename & + # Note that if we were to do the obvious `cat >$filename &`, we would deadlock + # because $filename may be opened before the fork. Use tee to ensure it is opened + # after the fork. + tee $filename >/dev/null & else if test -z "$_flag_suffix" set filename (mktemp $tmpdir/.psub.XXXXXXXXXX) cat >$filename From d62576ce22b6ba53d9632011325cdd605933bc9d Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 28 Jan 2019 14:35:56 -0800 Subject: [PATCH 351/439] Adopt dup2_list_t in fork execution path This switches IO redirections after fork() to use the dup2_list_t, instead of io_chain_t. This results in simpler code with much simpler error handling. --- src/exec.cpp | 31 ++++++++-- src/postfork.cpp | 147 ++++------------------------------------------ src/postfork.h | 5 +- src/redirection.h | 2 + 4 files changed, 42 insertions(+), 143 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index 75af77921..3d51914b2 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -39,6 +39,7 @@ #include "postfork.h" #include "proc.h" #include "reader.h" +#include "redirection.h" #include "signal.h" #include "wutil.h" // IWYU pragma: keep @@ -367,7 +368,8 @@ void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &all_ios) { // 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(0, all_ios)) { + auto redirs = dup2_list_t::resolve_chain(all_ios); + if (redirs && !setup_child_process(0, *redirs)) { // Decrement SHLVL as we're removing ourselves from the shell "stack". auto shlvl_var = vars.get(L"SHLVL", ENV_GLOBAL | ENV_EXPORT); wcstring shlvl_str = L"0"; @@ -404,7 +406,7 @@ static void on_process_created(const std::shared_ptr &j, pid_t child_pid) /// Call fork() as part of executing a process \p p in a job \j. Execute \p child_action in the /// context of the child. Returns true if fork succeeded, false if fork failed. static bool fork_child_for_process(const std::shared_ptr &job, process_t *p, - const io_chain_t &io_chain, bool drain_threads, + const dup2_list_t &dup2s, bool drain_threads, const char *fork_type, const std::function &child_action) { pid_t pid = execute_fork(drain_threads); @@ -413,7 +415,7 @@ static bool fork_child_for_process(const std::shared_ptr &job, process_t // stdout and stderr, and then exit. p->pid = getpid(); child_set_group(job.get(), p); - setup_child_process(p, io_chain); + setup_child_process(p, dup2s); child_action(); DIE("Child process returned control to fork_child lambda!"); } @@ -634,9 +636,15 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, const char *errbuff = errbuff_str.data(); size_t errbuff_len = errbuff_str.size(); + // Resolve our IO chain to a sequence of dup2s. + auto dup2s = dup2_list_t::resolve_chain(*io_chain); + if (!dup2s) { + return false; + } + fflush(stdout); fflush(stderr); - if (!fork_child_for_process(j, p, *io_chain, false, "internal builtin", [&] { + if (!fork_child_for_process(j, p, *dup2s, false, "internal builtin", [&] { do_builtin_io(outbuff, outbuff_len, errbuff, errbuff_len); exit_without_destructors(p->status); })) { @@ -655,6 +663,11 @@ static bool exec_external_command(env_stack_t &vars, const std::shared_ptr argv_array; convert_wide_array_to_narrow(p->get_argv_array(), &argv_array); + // Convert our IO chain to a dup2 sequence. + auto dup2s = dup2_list_t::resolve_chain(proc_io_chain); + if (! dup2s) + return false; + // 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 @@ -741,7 +754,7 @@ static bool exec_external_command(env_stack_t &vars, const std::shared_ptr io_chain.remove(block_output_io_buffer); block_output_io_buffer->read(); + // Resolve our IO chain to a sequence of dup2s. + auto dup2s = dup2_list_t::resolve_chain(io_chain); + if (!dup2s) { + return false; + } + const std::string buffer_contents = block_output_io_buffer->buffer().newline_serialized(); const char *buffer = buffer_contents.data(); size_t count = buffer_contents.size(); @@ -827,7 +846,7 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr // We don't have to drain threads here because our child process is simple. const char *fork_reason = p->type == INTERNAL_BLOCK_NODE ? "internal block io" : "internal function io"; - if (!fork_child_for_process(j, p, io_chain, false, fork_reason, [&] { + if (!fork_child_for_process(j, p, *dup2s, false, fork_reason, [&] { exec_write_and_exit(block_output_io_buffer->fd, buffer, count, status); })) { return false; diff --git a/src/postfork.cpp b/src/postfork.cpp index 7a6f655c0..4326d077a 100644 --- a/src/postfork.cpp +++ b/src/postfork.cpp @@ -19,6 +19,7 @@ #include "iothread.h" #include "postfork.h" #include "proc.h" +#include "redirection.h" #include "signal.h" #include "wutil.h" // IWYU pragma: keep @@ -38,27 +39,6 @@ /// Fork error message. #define FORK_ERROR "Could not create child process - exiting" -/// File redirection clobbering error message. -#define NOCLOB_ERROR "The file '%s' already exists" - -/// 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" - -/// 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) { - char buff[128]; - format_long_safe(buff, val); - debug_safe(level, format, buff); -} - /// Called only by the child to set its own process group (possibly creating a new group in the /// process if it is the first in a JOB_CONTROL job. /// Returns true on sucess, false on failiure. @@ -175,126 +155,23 @@ bool maybe_assign_terminal(const job_t *j) { return true; } -/// 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_chain 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_mode_t::fd && io->fd == static_cast(io)->old_fd) { - continue; - } - - switch (io->io_mode) { - case io_mode_t::close: { - if (log_redirections) fwprintf(stderr, L"%d: close %d\n", getpid(), io->fd); - if (close(io->fd)) { - debug_safe_int(0, "Failed to close file descriptor %s", io->fd); - safe_perror("close"); - } - break; - } - - case io_mode_t::file: { - // Here we definitely do not want to set CLO_EXEC because our child needs access. - const io_file_t *io_file = static_cast(io); - int tmp = open(io_file->filename_cstr, io_file->flags, OPEN_MASK); - if (tmp < 0) { - if ((io_file->flags & O_EXCL) && (errno == EEXIST)) { - debug_safe(1, NOCLOB_ERROR, io_file->filename_cstr); - } 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. - close(io->fd); - - if (dup2(tmp, io->fd) == -1) { - debug_safe_int(1, FD_ERROR, io->fd); - safe_perror("dup2"); - exec_close(tmp); - return -1; - } - exec_close(tmp); - } - break; - } - - case io_mode_t::fd: { - int old_fd = static_cast(io)->old_fd; - if (log_redirections) - fwprintf(stderr, L"%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. - close(io->fd); - - if (dup2(old_fd, io->fd) == -1) { - debug_safe_int(1, FD_ERROR, io->fd); - safe_perror("dup2"); - return -1; - } - break; - } - - case io_mode_t::buffer: - case io_mode_t::pipe: { - const io_pipe_t *io_pipe = static_cast(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). - unsigned int write_pipe_idx = (io_pipe->is_input ? 0 : 1); -#if 0 - debug(0, L"%ls %ls on fd %d (%d %d)", write_pipe?L"write":L"read", - (io->io_mode == io_mode_t::buffer)?L"buffer":L"pipe", io->fd, io->pipe_fd[0], - io->pipe_fd[1]); -#endif - if (log_redirections) - fwprintf(stderr, L"%d: %s dup %d to %d\n", getpid(), - io->io_mode == io_mode_t::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]); - break; +int setup_child_process(process_t *p, const dup2_list_t &dup2s) { + for (const auto &act : dup2s.get_actions()) { + int err = act.target < 0 ? close(act.src) : dup2(act.src, act.target); + if (err < 0) { + // We have a null p if this is for the exec (non-fork) path. + if (p != nullptr) { + debug_safe(4, "redirect_in_child_after_fork failed in setup_child_process"); + exit_without_destructors(1); } + return err; } } - + // Set the handling for job control signals back to the default. + signal_reset_handlers(); return 0; } -int setup_child_process(process_t *p, const io_chain_t &io_chain) { - bool ok = true; - - if (ok) { - // In the case of io_mode_t::file, this can hang until data is available to read/write! - ok = (0 == handle_child_io(io_chain)); - if (p != 0 && !ok) { - debug_safe(4, "handle_child_io failed in setup_child_process"); - exit_without_destructors(1); - } - } - - if (ok) { - // Set the handling for job control signals back to the default. - signal_reset_handlers(); - } - - return ok ? 0 : -1; -} int g_fork_count = 0; diff --git a/src/postfork.h b/src/postfork.h index f7cfe080d..8ac8e82a3 100644 --- a/src/postfork.h +++ b/src/postfork.h @@ -14,6 +14,7 @@ #define FISH_USE_POSIX_SPAWN HAVE_SPAWN_H #endif +class dup2_list_t; class io_chain_t; class job_t; class process_t; @@ -29,11 +30,11 @@ bool maybe_assign_terminal(const job_t *j); /// descriptor actions are performed. /// /// \param p the child process to set up -/// \param io_chain the IO chain to use +/// \param dup2 the dup2 list to apply /// /// \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(process_t *p, const io_chain_t &io_chain); +int setup_child_process(process_t *p, const dup2_list_t &dup2s); /// 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 diff --git a/src/redirection.h b/src/redirection.h index e82d4376d..95cc8fac0 100644 --- a/src/redirection.h +++ b/src/redirection.h @@ -11,6 +11,7 @@ /// A class representing a sequence of basic redirections. class dup2_list_t { + public: /// A type that represents the action dup2(src, target). /// If target is negative, this represents close(src). /// Note none of the fds here are considered 'owned'. @@ -19,6 +20,7 @@ class dup2_list_t { int target; }; + private: /// The list of actions. std::vector actions_; From 2742267b9e1ee771c43953a474e33e51cb011c3d Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 29 Jan 2019 00:34:38 -0800 Subject: [PATCH 352/439] Use dup2_list_t in posix_spawn This simplifies the posix_spawn path and unifies it with the fork execution path. --- src/exec.cpp | 3 +-- src/postfork.cpp | 58 ++++++++---------------------------------------- src/postfork.h | 5 ++--- 3 files changed, 12 insertions(+), 54 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index 3d51914b2..bf978345f 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -691,8 +691,7 @@ static bool exec_external_command(env_stack_t &vars, const std::shared_ptr io = io_chain.at(idx); - - if (io->io_mode == io_mode_t::fd) { - const io_fd_t *io_fd = static_cast(io.get()); - if (io->fd == io_fd->old_fd) continue; - } - - switch (io->io_mode) { - case io_mode_t::close: { - if (!err) err = posix_spawn_file_actions_addclose(actions, io->fd); - break; - } - - case io_mode_t::file: { - const io_file_t *io_file = static_cast(io.get()); - if (!err) - err = posix_spawn_file_actions_addopen(actions, io->fd, io_file->filename_cstr, - io_file->flags /* mode */, OPEN_MASK); - break; - } - - case io_mode_t::fd: { - const io_fd_t *io_fd = static_cast(io.get()); - if (!err) - err = posix_spawn_file_actions_adddup2(actions, io_fd->old_fd /* from */, - io->fd /* to */); - break; - } - - case io_mode_t::buffer: - case io_mode_t::pipe: { - const io_pipe_t *io_pipe = static_cast(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]); - } - break; - } + // Apply our dup2s. + for (const auto &act : dup2s.get_actions()) { + if (err) break; + if (act.target < 0) { + err = posix_spawn_file_actions_addclose(actions, act.src); + } else { + err = posix_spawn_file_actions_adddup2(actions, act.src, act.target); } } diff --git a/src/postfork.h b/src/postfork.h index 8ac8e82a3..d91d335cf 100644 --- a/src/postfork.h +++ b/src/postfork.h @@ -15,7 +15,6 @@ #endif class dup2_list_t; -class io_chain_t; class job_t; class process_t; @@ -56,8 +55,8 @@ void run_as_keepalive(pid_t parent_pid); /// 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); + posix_spawn_file_actions_t *actions, const job_t *j, + const dup2_list_t &dup2s); #endif #endif From 084ff64f4fb5b5595bc1ca0f52a8a1ddad20608e Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 29 Jan 2019 00:40:55 -0800 Subject: [PATCH 353/439] Allow posix_spawn more often Now that we no longer open files after fork, we can correctly report errors for failed file opens. So allow posix_spawn even if there's redirections. --- src/exec.cpp | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index bf978345f..78444b8b8 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -115,18 +115,6 @@ static bool redirection_is_to_real_file(const io_data_t *io) { return result; } -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++) { - const io_data_t *io = io_chain.at(idx).get(); - if (redirection_is_to_real_file(io)) { - result = true; - break; - } - } - 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) { @@ -330,11 +318,9 @@ void internal_exec_helper(parser_t &parser, parsed_source_ref_t parsed_source, t job_reap(false); } -// 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. +// Returns whether we can use posix spawn for a given process in a given job. // -// Furthermore, to avoid the race between the caller calling tcsetpgrp() and the client checking the +// 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 std::shared_ptr &job, @@ -348,15 +334,7 @@ static bool can_use_posix_spawn_for_job(const std::shared_ptr &job, 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. - bool result = true; - if (chain_contains_redirection_to_real_file(job->block_io_chain()) || - chain_contains_redirection_to_real_file(process->io_chain())) { - result = false; - } - return result; + return true; } void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &all_ios) { From 178b72b2fd67d37215f973bd55290a5d0ec6eb06 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 31 Jan 2019 16:05:42 -0800 Subject: [PATCH 354/439] io_buffer_t becomes io_bufferfill_t This makes some significant architectual improvements to io_pipe_t and io_buffer_t. Prior to this fix, io_buffer_t subclassed io_pipe_t. io_buffer_t is now replaced with a class io_bufferfill_t, which does not subclass pipe. io_pipe_t no longer remembers both fds. Instead it has an autoclose_fd_t, so that the file descriptor ownership is clear. --- src/common.h | 3 + src/exec.cpp | 212 ++++++++++++++++---------------------------- src/exec.h | 4 - src/fish_tests.cpp | 13 +-- src/io.cpp | 150 ++++++++++++++++++------------- src/io.h | 115 ++++++++++++++++-------- src/proc.cpp | 117 ++++++------------------ src/redirection.cpp | 16 ++-- 8 files changed, 289 insertions(+), 341 deletions(-) diff --git a/src/common.h b/src/common.h index 8b45a808c..5fa2119ba 100644 --- a/src/common.h +++ b/src/common.h @@ -786,6 +786,9 @@ class autoclose_fd_t { fd_ = fd; } + // \return if this has a valid fd. + bool valid() const { return fd_ >= 0; } + autoclose_fd_t(const autoclose_fd_t &) = delete; void operator=(const autoclose_fd_t &) = delete; autoclose_fd_t(autoclose_fd_t &&rhs) : fd_(rhs.fd_) { rhs.fd_ = -1; } diff --git a/src/exec.cpp b/src/exec.cpp index 78444b8b8..7de91f5be 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -81,25 +81,6 @@ void exec_close(int fd) { } } -int exec_pipe(int fd[2]) { - ASSERT_IS_MAIN_THREAD(); - - int 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. - 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) { bool result = false; @@ -246,8 +227,8 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t *out_chain, switch (in->io_mode) { case io_mode_t::pipe: + case io_mode_t::bufferfill: case io_mode_t::fd: - case io_mode_t::buffer: case io_mode_t::close: { // These redirections don't need transmogrification. They can be passed through. out = in; @@ -424,12 +405,12 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptrtype == INTERNAL_BUILTIN && "Process must be a builtin"); int local_builtin_stdin = STDIN_FILENO; - bool close_stdin = false; + autoclose_fd_t locally_opened_stdin{}; // If this is the first process, check the io redirections and see where we should // be reading from. if (pipe_read) { - local_builtin_stdin = pipe_read->pipe_fd[0]; + local_builtin_stdin = pipe_read->pipe_fd(); } else if (const auto in = proc_io_chain.get_io_for_fd(STDIN_FILENO)) { switch (in->io_mode) { case io_mode_t::fd: { @@ -448,20 +429,20 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptr(in.get()); - local_builtin_stdin = in_pipe->pipe_fd[0]; + if (in_pipe->fd == STDIN_FILENO) { + local_builtin_stdin = in_pipe->pipe_fd(); + } break; } case io_mode_t::file: { - // Do not set CLO_EXEC because child needs access. const io_file_t *in_file = static_cast(in.get()); - local_builtin_stdin = open(in_file->filename_cstr, in_file->flags, OPEN_MASK); - if (local_builtin_stdin == -1) { + locally_opened_stdin = + autoclose_fd_t{open(in_file->filename_cstr, in_file->flags, OPEN_MASK)}; + if (!locally_opened_stdin.valid()) { debug(1, FILE_ERROR, in_file->filename_cstr); wperror(L"open"); - } else { - close_stdin = true; } - + local_builtin_stdin = locally_opened_stdin.fd(); break; } case io_mode_t::close: { @@ -517,10 +498,6 @@ static bool exec_internal_builtin_proc(parser_t &parser, const std::shared_ptrset_flag(job_flag_t::FOREGROUND, fg); - // If stdin has been redirected, close the redirection stream. - if (close_stdin) { - exec_close(local_builtin_stdin); - } return true; // "success" } @@ -548,7 +525,11 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, if (!must_fork && p->is_last_in_job) { // We are handling reads directly in the main loop. Note that we may still end // up forking. - const bool stdout_is_to_buffer = stdout_io && stdout_io->io_mode == io_mode_t::buffer; + const bool stdout_is_bufferfill = + (stdout_io && stdout_io->io_mode == io_mode_t::bufferfill); + const std::shared_ptr stdout_buffer = + stdout_is_bufferfill ? static_cast(stdout_io.get())->buffer() + : nullptr; const bool no_stdout_output = stdout_stream.empty(); const bool no_stderr_output = stderr_stream.empty(); const bool stdout_discarded = stdout_stream.buffer().discarded(); @@ -558,7 +539,7 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, // need to fork or even output anything. debug(4, L"Skipping fork: no output for internal builtin '%ls'", p->argv0()); fork_was_skipped = true; - } else if (no_stderr_output && stdout_is_to_buffer) { + } else if (no_stderr_output && stdout_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. @@ -570,8 +551,7 @@ static bool handle_builtin_output(const std::shared_ptr &j, process_t *p, // also produce stderr. debug(4, L"Skipping fork: buffered output for internal builtin '%ls'", p->argv0()); - io_buffer_t *io_buffer = static_cast(stdout_io.get()); - io_buffer->append_from_stream(stdout_stream); + stdout_buffer->append_from_stream(stdout_stream); 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. @@ -749,20 +729,16 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr "Unexpected process type"); // Create an output buffer if we're piping to another process. - shared_ptr block_output_io_buffer{}; + shared_ptr block_output_bufferfill{}; if (!p->is_last_in_job) { // Be careful to handle failure, e.g. too many open fds. - block_output_io_buffer = io_buffer_t::create(STDOUT_FILENO, user_ios); - if (!block_output_io_buffer) { + block_output_bufferfill = io_bufferfill_t::create(user_ios); + if (!block_output_bufferfill) { job_mark_process_as_failed(j, p); return false; - } 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. - io_chain.push_back(block_output_io_buffer); } + // Teach the job about its bufferfill, and add it to our io chain. + io_chain.push_back(block_output_bufferfill); } if (p->type == INTERNAL_FUNCTION) { @@ -792,10 +768,8 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr 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()) { + // If we have a block output buffer, populate it now. + if (!block_output_bufferfill) { // No buffer, so we exit directly. This means we have to manually set the exit // status. if (p->is_last_in_job) { @@ -804,11 +778,16 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr p->completed = 1; return true; } + assert(block_output_bufferfill && "Must have a block output bufferfiller"); - // Here we must have a non-NULL block_output_io_buffer. - assert(block_output_io_buffer.get() != NULL); - io_chain.remove(block_output_io_buffer); - block_output_io_buffer->read(); + // Remove our write pipe and forget it. This may close the pipe, unless another thread has + // claimed it (background write) or another process has inherited it. + auto block_output_buffer = block_output_bufferfill->buffer(); + io_chain.remove(block_output_bufferfill); + block_output_bufferfill.reset(); + + // Make the buffer populate itself from whatever was written to the write pipe. + block_output_buffer->read_to_wouldblock(); // Resolve our IO chain to a sequence of dup2s. auto dup2s = dup2_list_t::resolve_chain(io_chain); @@ -816,7 +795,7 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr return false; } - const std::string buffer_contents = block_output_io_buffer->buffer().newline_serialized(); + const std::string buffer_contents = block_output_buffer->buffer().newline_serialized(); const char *buffer = buffer_contents.data(); size_t count = buffer_contents.size(); if (count > 0) { @@ -824,7 +803,7 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr const char *fork_reason = p->type == INTERNAL_BLOCK_NODE ? "internal block io" : "internal function io"; if (!fork_child_for_process(j, p, *dup2s, false, fork_reason, [&] { - exec_write_and_exit(block_output_io_buffer->fd, buffer, count, status); + exec_write_and_exit(STDOUT_FILENO, buffer, count, status); })) { return false; } @@ -844,19 +823,28 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< autoclose_fd_t pipe_current_read, autoclose_fd_t *out_pipe_next_read, const io_chain_t &all_ios, size_t stdout_read_limit) { - // 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(); + // The pipe this command will write to (if any). + shared_ptr pipe_write; + // The pipe this command will read from (if any). + shared_ptr pipe_read; - // See if we need a pipe. + // See if we need a pipe for the next command. const bool pipes_to_next_command = !p->is_last_in_job; + if (pipes_to_next_command) { + // Construct our pipes. + auto local_pipes = make_autoclose_pipes(all_ios); + if (!local_pipes) { + debug(1, PIPE_ERROR); + wperror(L"pipe"); + job_mark_process_as_failed(j, p); + return false; + } - // The write end of any pipe we create. - autoclose_fd_t pipe_current_write{}; + pipe_write = std::make_shared(p->pipe_write_fd, false /* not input */, + std::move(local_pipes->write)); + *out_pipe_next_read = std::move(local_pipes->read); + } - // 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: // @@ -884,12 +872,10 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< // // 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 (pipes_to_next_command) { - pipe_write.reset(new io_pipe_t(p->pipe_write_fd, false)); + // The IO chain for this process. + io_chain_t process_net_io_chain = j->block_io_chain(); + if (pipe_write) { process_net_io_chain.push_back(pipe_write); } @@ -897,10 +883,9 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< process_net_io_chain.append(p->io_chain()); // Read pipe goes last. - if (!p->is_first_in_job) { - pipe_read.reset(new io_pipe_t(STDIN_FILENO, true)); - // Record the current read in pipe_read. - pipe_read->pipe_fd[0] = pipe_current_read.fd(); + if (pipe_current_read.valid()) { + pipe_read = std::make_shared(STDIN_FILENO, true /* input */, + std::move(pipe_current_read)); process_net_io_chain.push_back(pipe_read); } @@ -918,36 +903,6 @@ static bool exec_process_in_job(parser_t &parser, process_t *p, std::shared_ptr< parser.vars().export_arr(); } - // 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) { - debug(1, PIPE_ERROR); - wperror(L"pipe"); - job_mark_process_as_failed(j, p); - return false; - } - - // 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"); - job_mark_process_as_failed(j, p); - return false; - } - - // 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); - - // Record our pipes. - pipe_current_write.reset(local_pipe[1]); - out_pipe_next_read->reset(local_pipe[0]); - } - // Execute the process. switch (p->type) { case INTERNAL_FUNCTION: @@ -1008,18 +963,13 @@ bool exec_job(parser_t &parser, shared_ptr j) { } } - // Verify that all io_mode_t::buffers are output. We used to support a (single, hacked-in) - // magical input io_mode_t::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. size_t stdout_read_limit = 0; const io_chain_t all_ios = j->all_io_redirections(); - for (size_t idx = 0; idx < all_ios.size(); idx++) { - const shared_ptr &io = all_ios.at(idx); - - if ((io->io_mode == io_mode_t::buffer)) { - io_buffer_t *io_buffer = static_cast(io.get()); - assert(!io_buffer->is_input); - stdout_read_limit = io_buffer->buffer().limit(); + for (auto &io : all_ios) { + if ((io->io_mode == io_mode_t::bufferfill)) { + // The read limit is dictated by the last bufferfill. + const auto *bf = static_cast(io.get()); + stdout_read_limit = bf->buffer()->buffer().limit(); } } @@ -1028,21 +978,6 @@ bool exec_job(parser_t &parser, shared_ptr j) { DIE("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 (const auto io : all_ios) { - if (io->io_mode == io_mode_t::buffer) { - auto *io_buffer = static_cast(io.get()); - 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->processes.front().get()); - break; - } - } - } - // 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. // @@ -1098,29 +1033,30 @@ static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, wcstrin // 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(), is_subcmd ? read_byte_limit : 0)); - if (io_buffer.get() != NULL) { + size_t read_limit = is_subcmd ? read_byte_limit : 0; + std::shared_ptr buffer; + if (auto bufferfill = io_bufferfill_t::create(io_chain_t{}, read_limit)) { 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{bufferfill}, SUBST) == 0) { subcommand_status = proc_get_last_status(); } - - io_buffer->read(); + buffer = bufferfill->buffer(); + bufferfill.reset(); + buffer->read_to_wouldblock(); } - if (io_buffer->buffer().discarded()) subcommand_status = STATUS_READ_TOO_MUCH; + if (buffer && buffer->buffer().discarded()) subcommand_status = STATUS_READ_TOO_MUCH; // 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 || !buffer) { return subcommand_status; } // Walk over all the elements. - for (const auto &elem : io_buffer->buffer().elements()) { + for (const auto &elem : buffer->buffer().elements()) { if (elem.is_explicitly_separated()) { // Just append this one. lst->push_back(str2wcstring(elem.contents)); diff --git a/src/exec.h b/src/exec.h index 010de4654..63b78abc7 100644 --- a/src/exec.h +++ b/src/exec.h @@ -31,10 +31,6 @@ int exec_subshell(const wcstring &cmd, parser_t &parser, bool preserve_exit_stat /// 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. -int exec_pipe(int fd[2]); - /// Gets the interpreter for a given command. char *get_interpreter(const char *command, char *interpreter, size_t buff_size); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 8f266947d..2ed26d3b2 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -920,8 +920,7 @@ static void test_parser() { } 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}; + auto filler = io_bufferfill_t::create(io_chain_t{}); pthread_t thread = pthread_self(); double delay = 0.25 /* seconds */; iothread_perform([=]() { @@ -929,11 +928,13 @@ static void test_1_cancellation(const wchar_t *src) { usleep(delay * 1E6); pthread_kill(thread, SIGINT); }); - parser_t::principal_parser().eval(src, io_chain, TOP); - out_buff->read(); - if (out_buff->buffer().size() != 0) { + parser_t::principal_parser().eval(src, io_chain_t{filler}, TOP); + auto buffer = filler->buffer(); + filler.reset(); + buffer->read_to_wouldblock(); + if (buffer->buffer().size() != 0) { err(L"Expected 0 bytes in out_buff, but instead found %lu bytes\n", - out_buff->buffer().size()); + buffer->buffer().size()); } iothread_drain_all(); } diff --git a/src/io.cpp b/src/io.cpp index 091d250f5..32731776c 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -22,14 +22,10 @@ void io_fd_t::print() const { fwprintf(stderr, L"FD map %d -> %d\n", old_fd, fd) void io_file_t::print() const { fwprintf(stderr, L"file (%s)\n", filename_cstr); } void io_pipe_t::print() const { - fwprintf(stderr, L"pipe {%d, %d} (input: %s)\n", pipe_fd[0], pipe_fd[1], - is_input ? "yes" : "no"); + fwprintf(stderr, L"pipe {%d} (input: %s)\n", pipe_fd(), is_input_ ? "yes" : "no"); } -void io_buffer_t::print() const { - fwprintf(stderr, L"buffer (input: %s, size %lu)\n", - is_input ? "yes" : "no", (unsigned long)buffer_.size()); -} +void io_bufferfill_t::print() const { fwprintf(stderr, L"bufferfill {%d}\n", write_fd_.fd()); } void io_buffer_t::append_from_stream(const output_stream_t &stream) { if (buffer_.discarded()) return; @@ -40,75 +36,84 @@ void io_buffer_t::append_from_stream(const output_stream_t &stream) { buffer_.append_wide_buffer(stream.buffer()); } -void io_buffer_t::read() { - exec_close(pipe_fd[1]); +long io_buffer_t::read_some() { + int fd = read_.fd(); + assert(fd >= 0 && "Should have a valid fd"); + debug(4, L"io_buffer_t::read: blocking read on fd %d", fd); + long len; + char b[4096]; + do { + len = read(fd, b, sizeof b); + } while (len < 0 && errno == EINTR); + if (len > 0) { + buffer_.append(&b[0], &b[len]); + } + return len; +} - if (io_mode == io_mode_t::buffer) { - debug(4, L"io_buffer_t::read: blocking read on fd %d", pipe_fd[0]); - while (1) { - char b[4096]; - long len = read_blocked(pipe_fd[0], b, 4096); - if (len == 0) { - break; - } else if (len < 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) { - const wchar_t *fmt = - _(L"An error occured while reading output from code block on fd %d"); - debug(1, fmt, pipe_fd[0]); - wperror(L"io_buffer_t::read"); - } +void io_buffer_t::read_to_wouldblock() { + long len; + do { + len = read_some(); + } while (len > 0); + if (len < 0 && errno != EAGAIN) { + debug(1, _(L"An error occured while reading output from code block on fd %d"), read_.fd()); + wperror(L"io_buffer_t::read"); + } +} - break; - } else { - buffer_.append(&b[0], &b[len]); - } +bool io_buffer_t::try_read(unsigned long timeout_usec) { + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = timeout_usec; + int fd = read_.fd(); + assert(fd >= 0 && "Should have a valid fd"); + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + int ret = select(fd + 1, &fds, nullptr, nullptr, &timeout); + if (ret < 0) { + // Treat EINTR as timeout. + if (errno != EINTR) { + debug(1, _(L"An error occured inside select on fd %d"), fd); + wperror(L"io_buffer_t::try_read"); } + return false; } + if (ret > 0) { + read_some(); + } + return ret > 0; } -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) { - wperror(L"dup"); +shared_ptr io_bufferfill_t::create(const io_chain_t &conflicts, + size_t buffer_limit) { + // Construct our pipes. + auto pipes = make_autoclose_pipes(conflicts); + if (!pipes) { + return nullptr; } - return result; -} -shared_ptr io_buffer_t::create(int fd, const io_chain_t &conflicts, - size_t buffer_limit) { - bool success = true; - assert(fd >= 0); - shared_ptr buffer_redirect(new io_buffer_t(fd, buffer_limit)); - - 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. - success = false; - } else if (make_fd_nonblocking(buffer_redirect->pipe_fd[0]) != 0) { + // Our buffer will read from the read end of the pipe. This end must be non-blocking. This is + // because we retain the write end of the pipe in this process (even after handing it off to a + // child process); therefore a read on the pipe may block forever. What we should do is arrange + // for the write end of the pipe to be closed at the right time; then the read could just block. + if (make_fd_nonblocking(pipes->read.fd())) { debug(1, PIPE_ERROR); wperror(L"fcntl"); - success = false; + return nullptr; } - if (!success) { - buffer_redirect.reset(); - } - return buffer_redirect; + // Our buffer gets the read end of the pipe; out_pipe gets the write end. + auto buffer = std::make_shared(std::move(pipes->read), buffer_limit); + return std::make_shared(std::move(pipes->write), buffer); } -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. -} +io_pipe_t::~io_pipe_t() = default; + +io_bufferfill_t::~io_bufferfill_t() = default; + +io_buffer_t::~io_buffer_t() = default; void io_chain_t::remove(const shared_ptr &element) { // See if you can guess why std::find doesn't work here. @@ -197,7 +202,7 @@ int move_fd_to_unused(int fd, const io_chain_t &io_chain, bool cloexec) { return new_fd; } -bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios) { +static 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++) { fds[i] = move_fd_to_unused(fds[i], ios); @@ -221,6 +226,27 @@ bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios) { return success; } +maybe_t make_autoclose_pipes(const io_chain_t &ios) { + int pipes[2] = {-1, -1}; + + if (pipe(pipes) < 0) { + debug(1, PIPE_ERROR); + wperror(L"pipe"); + return none(); + } + set_cloexec(pipes[0]); + set_cloexec(pipes[1]); + + if (!pipe_avoid_conflicts_with_io_chain(pipes, ios)) { + // The pipes are closed on failure here. + return none(); + } + autoclose_pipes_t result; + result.read = autoclose_fd_t(pipes[0]); + result.write = autoclose_fd_t(pipes[1]); + return {std::move(result)}; +} + /// 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(); diff --git a/src/io.h b/src/io.h index 3081a240f..6599790ff 100644 --- a/src/io.h +++ b/src/io.h @@ -150,7 +150,7 @@ class separated_buffer_t { }; /// Describes what type of IO operation an io_data_t represents. -enum class io_mode_t { file, pipe, fd, buffer, close }; +enum class io_mode_t { file, pipe, fd, close, bufferfill }; /// Represents an FD redirection. class io_data_t { @@ -210,39 +210,83 @@ class io_file_t : public io_data_t { ~io_file_t() override { free((void *)filename_cstr); } }; +/// Represents (one end) of a pipe. 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; - } + // The pipe's fd. Conceptually this is dup2'd to io_data_t::fd. + autoclose_fd_t pipe_fd_; + + /// Whether this is an input pipe. This is used only for informational purposes. + const bool is_input_; public: - int pipe_fd[2]; - const bool is_input; - void print() const override; - io_pipe_t(int f, bool i) : io_data_t(io_mode_t::pipe, f), is_input(i) { - pipe_fd[0] = pipe_fd[1] = -1; - } + io_pipe_t(int fd, bool is_input, autoclose_fd_t pipe_fd) + : io_data_t(io_mode_t::pipe, fd), pipe_fd_(std::move(pipe_fd)), is_input_(is_input) {} + + ~io_pipe_t(); + + int pipe_fd() const { return pipe_fd_.fd(); } }; +class io_buffer_t; class io_chain_t; + +/// Represents filling an io_buffer_t. Very similar to io_pipe_t. +/// Bufferfills always target stdout. +class io_bufferfill_t : public io_data_t { + /// Write end. The other end is connected to an io_buffer_t. + const autoclose_fd_t write_fd_; + + /// The receiving buffer. + const std::shared_ptr buffer_; + + public: + void print() const override; + + io_bufferfill_t(autoclose_fd_t write_fd, std::shared_ptr buffer) + : io_data_t(io_mode_t::bufferfill, STDOUT_FILENO), + write_fd_(std::move(write_fd)), + buffer_(std::move(buffer)) {} + + ~io_bufferfill_t(); + + std::shared_ptr buffer() const { return buffer_; } + + int write_fd() const { return write_fd_.fd(); } + + /// Create an io_bufferfill_t which, when written from, populates a buffer (also created). + /// \returns nullptr on failure, e.g. too many open fds. + /// + /// \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 shared_ptr create(const io_chain_t &conflicts, size_t buffer_limit = 0); +}; + class output_stream_t; -class io_buffer_t : public io_pipe_t { + +/// An io_buffer_t is a buffer which can populate itself by reading from an fd. +/// It is not an io_data_t. +class io_buffer_t { private: + /// fd from which to read. + autoclose_fd_t read_; + + /// Buffer storing what we have read. separated_buffer_t buffer_; - explicit io_buffer_t(int f, size_t limit) - : io_pipe_t(io_mode_t::buffer, f, false /* not input */), buffer_(limit) { + /// Read some. Append it to our buffer. + /// \return positive if we read, 0 on EOF, -1 on error. + long read_some(); + + public: + explicit io_buffer_t(autoclose_fd_t read, size_t limit) + : read_(std::move(read)), buffer_(limit) { // Explicitly reset the discard flag because we share this buffer. buffer_.reset_discard(); } - public: - void print() const override; - - ~io_buffer_t() override; + ~io_buffer_t(); /// Access the underlying buffer. const separated_buffer_t &buffer() const { return buffer_; } @@ -250,24 +294,16 @@ class io_buffer_t : public io_pipe_t { /// Function to append to the buffer. void append(const char *ptr, size_t count) { buffer_.append(ptr, ptr + count); } - /// 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); + /// Read from input pipe until EOF or EAGAIN (i.e. would block). + void read_to_wouldblock(); - /// Close output pipe, and read from input pipe until eof. - void read(); + /// Read a bit, if our fd is readable, with the given timeout. + /// \return true if we read some, false on timeout. + bool try_read(unsigned long timeout_usec); /// Appends data from a given output_stream_t. /// Marks the receiver as discarded if the stream was discarded. void append_from_stream(const output_stream_t &stream); - - /// Create a io_mode_t::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 shared_ptr create(int fd, const io_chain_t &conflicts, - size_t buffer_limit = 0); }; class io_chain_t : public std::vector> { @@ -289,11 +325,18 @@ class io_chain_t : public std::vector> { 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). -bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios); +/// Helper type returned from making autoclose pipes. +struct autoclose_pipes_t { + /// Read end of the pipe. + autoclose_fd_t read; + + /// Write end of the pipe. + autoclose_fd_t write; +}; +/// Call pipe(), populating autoclose fds, avoiding conflicts. +/// The pipes are marked CLO_EXEC. +/// \return pipes on success, none() on error. +maybe_t make_autoclose_pipes(const io_chain_t &ios); /// 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. diff --git a/src/proc.cpp b/src/proc.cpp index cdacd05bb..a58217f66 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -837,86 +837,24 @@ void proc_update_jiffies() { #endif /// The return value of select_try(), indicating IO readiness or an error -enum class select_try_t { - /// One or more fds have data ready for read - DATA_READY, - /// The timeout elapsed without any data becoming available for read +enum class block_receive_try_t { + /// There is no buffer to select on. + NO_BUFFER, + /// We have a block buffer, and we read some. + DATA_READ, + /// We have a block buffer, but we were unable to read any. TIMEOUT, - /// There were no FDs in the io chain for which to select on. - IOCHAIN_EMPTY, }; -/// 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 the status of the select operation -static select_try_t select_try(job_t *j) { - fd_set fds; - int maxfd = -1; - - FD_ZERO(&fds); - - const io_chain_t chain = j->all_io_redirections(); - for (const auto &io : chain) { - if (io->io_mode == io_mode_t::buffer) { - auto io_pipe = static_cast(io.get()); - int fd = io_pipe->pipe_fd[0]; - FD_SET(fd, &fds); - maxfd = std::max(maxfd, fd); - debug(4, L"select_try on fd %d", fd); - } - } - - if (maxfd >= 0) { - struct timeval timeout; - - timeout.tv_sec = 0; - timeout.tv_usec = 10000; - - int retval = select(maxfd + 1, &fds, 0, 0, &timeout); - if (retval == 0) { - debug(4, L"select_try hit timeout"); - return select_try_t::TIMEOUT; - } - return select_try_t::DATA_READY; - } - - return select_try_t::IOCHAIN_EMPTY; -} - -/// 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. - const io_chain_t chain = j->all_io_redirections(); - for (size_t idx = 0; idx < chain.size(); idx++) { - io_data_t *d = chain.at(idx).get(); - if (d->io_mode == io_mode_t::buffer) { - buff = static_cast(d); - } - } - - if (buff) { - debug(4, L"proc::read_try('%ls')", j->command_wcstr()); - while (1) { - char b[BUFFER_SIZE]; - long len = read_blocked(buff->pipe_fd[0], b, BUFFER_SIZE); - if (len == 0) { - break; - } else if (len < 0) { - if (errno != EAGAIN) { - debug(1, _(L"An error occured while reading output from code block")); - wperror(L"read_try"); - } - break; - } else { - buff->append(b, len); - } +/// \return the last IO buffer in job j, or nullptr if none. +std::shared_ptr last_buffer(job_t *j) { + std::shared_ptr buff{}; + for (const auto &io : j->all_io_redirections()) { + if (io->io_mode == io_mode_t::bufferfill) { + buff = static_cast(io.get())->buffer(); } } + return buff; } // Return control of the terminal to a job's process group. restore_attrs is true if we are restoring @@ -1154,26 +1092,27 @@ void job_t::continue_job(bool send_sigcont) { // Wait for data to become available or the status of our own job to change while (!reader_exit_forced() && !is_stopped() && !is_completed()) { - auto result = select_try(this); read_attempted = true; - - switch (result) { - case select_try_t::DATA_READY: - // Read the data that we know is now available, then scan for finished processes - // but do not block. We don't block so long as we have IO to process, once the - // fd buffers are empty we'll block in the second case below. - read_try(this); + auto read_result = block_receive_try_t::NO_BUFFER; + if (auto buff = last_buffer(this)) { + const unsigned long SELECT_TIMEOUT_USEC = 10000; + bool did_read = buff->try_read(SELECT_TIMEOUT_USEC); + read_result = did_read ? block_receive_try_t::DATA_READ : block_receive_try_t::TIMEOUT; + } + switch (read_result) { + case block_receive_try_t::DATA_READ: + // We read some data. process_mark_finished_children(false); break; - case select_try_t::TIMEOUT: - // No FDs are ready. Look for finished processes instead. + case block_receive_try_t::TIMEOUT: + // We read some data or timed out. Poll for finished processes. debug(4, L"select_try: no fds returned valid data within the timeout" ); process_mark_finished_children(block_on_fg); break; - case select_try_t::IOCHAIN_EMPTY: - // There were no IO fds to select on. + case block_receive_try_t::NO_BUFFER: + // We are not populating a buffer. debug(4, L"select_try: no IO fds" ); process_mark_finished_children(true); @@ -1195,7 +1134,9 @@ void job_t::continue_job(bool send_sigcont) { // `echo` calls were sometimes having their output combined with the `set_color` calls // in the wrong order! if (!read_attempted) { - read_try(this); + if (auto buff = last_buffer(this)) { + buff->read_to_wouldblock(); + } } // Set $status only if we are in the foreground and the last process in the job has diff --git a/src/redirection.cpp b/src/redirection.cpp index 8aed4f9a4..1cce609df 100644 --- a/src/redirection.cpp +++ b/src/redirection.cpp @@ -73,15 +73,17 @@ maybe_t dup2_list_t::resolve_chain(const io_chain_t &io_chain) { break; } - case io_mode_t::buffer: case io_mode_t::pipe: { const io_pipe_t *io = static_cast(io_ref.get()); - // If write_pipe_idx is 0, it means we're connecting to the read end (first pipe - // fd). If it's 1, we're connecting to the write end (second pipe fd). - unsigned int write_pipe_idx = (io->is_input ? 0 : 1); - result.add_dup2(io->pipe_fd[write_pipe_idx], io->fd); - if (io->pipe_fd[0] >= 0) result.add_close(io->pipe_fd[0]); - if (io->pipe_fd[1] >= 0) result.add_close(io->pipe_fd[1]); + result.add_dup2(io->pipe_fd(), io->fd); + result.add_close(io->pipe_fd()); + break; + } + + case io_mode_t::bufferfill: { + const io_bufferfill_t *io = static_cast(io_ref.get()); + result.add_dup2(io->write_fd(), io->fd); + result.add_close(io->write_fd()); break; } } From ec29a5b9130d2d509a08dd1af6f7009c1230a68f Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 1 Feb 2019 01:04:14 -0800 Subject: [PATCH 355/439] Introduce make_pthread This allows creating a pthread directly, which can be joined. iothread_spawn wraps this. --- src/fish_tests.cpp | 15 +++++++++++ src/iothread.cpp | 64 ++++++++++++++++++++++++++++++++++++---------- src/iothread.h | 8 +++++- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 2ed26d3b2..e45f22e34 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -678,6 +678,20 @@ static void test_iothread() { max_achieved_thread_count); } +static void test_pthread() { + say(L"Testing pthreads"); + pthread_t result = {}; + int val = 3; + bool made = make_pthread(&result, [&val](){ + val += 2; + }); + do_test(made); + void *ignore = nullptr; + int ret = pthread_join(result, &ignore); + do_test(ret == 0); + do_test(val == 5); +} + 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)) { @@ -5083,6 +5097,7 @@ int main(int argc, char **argv) { if (should_test_function("convert_nulls")) test_convert_nulls(); if (should_test_function("tok")) test_tokenizer(); if (should_test_function("iothread")) test_iothread(); + if (should_test_function("pthread")) test_pthread(); if (should_test_function("parser")) test_parser(); if (should_test_function("cancellation")) test_cancellation(); if (should_test_function("indents")) test_indents(); diff --git a/src/iothread.cpp b/src/iothread.cpp index d834da8c1..c19f69339 100644 --- a/src/iothread.cpp +++ b/src/iothread.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -149,24 +150,14 @@ static void *iothread_worker(void *unused) { /// 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); - DIE_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. pthread_t thread = 0; - pthread_create(&thread, NULL, iothread_worker, NULL); - - // We will never join this thread. - DIE_ON_FAILURE(pthread_detach(thread)); - debug(5, "pthread %p spawned", (void *)(intptr_t)thread); - // Restore our sigmask. - DIE_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &saved_set, NULL)); + if (make_pthread(&thread, iothread_worker, nullptr)) { + // We will never join this thread. + DIE_ON_FAILURE(pthread_detach(thread)); + } } int iothread_perform_impl(void_function_t &&func, void_function_t &&completion) { @@ -342,3 +333,48 @@ void iothread_perform_on_main(void_function_t &&func) { // Ok, the request must now be done. assert(req.done); } + +bool make_pthread(pthread_t *result, void *(*func)(void *), void *param) { + // 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); + DIE_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. + pthread_t thread = 0; + int err = pthread_create(&thread, NULL, func, param); + if (err == 0) { + // Success, return the thread. + debug(5, "pthread %p spawned", (void *)(intptr_t)thread); + *result = thread; + } else { + perror("pthread_create"); + } + // Restore our sigmask. + DIE_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &saved_set, NULL)); + return err == 0; +} + +using void_func_t = std::function; + +static void *func_invoker(void *param) { + void_func_t *vf = static_cast(param); + (*vf)(); + delete vf; + return nullptr; +} + +bool make_pthread(pthread_t *result, void_func_t &&func) { + // Copy the function into a heap allocation. + void_func_t *vf = new void_func_t(std::move(func)); + if (make_pthread(result, func_invoker, vf)) { + return true; + } + // Thread spawning failed, clean up our heap allocation. + delete vf; + return false; +} diff --git a/src/iothread.h b/src/iothread.h index 8b29370c8..5e677b639 100644 --- a/src/iothread.h +++ b/src/iothread.h @@ -69,10 +69,16 @@ int iothread_perform(const HANDLER &handler, const COMPLETION &completion) { // variant of iothread_perform without a completion handler inline int iothread_perform(std::function &&func) { - return iothread_perform_impl(std::move(func), std::function()); + return iothread_perform_impl(std::move(func), {}); } /// Performs a function on the main thread, blocking until it completes. void iothread_perform_on_main(std::function &&func); +/// Creates a pthread, manipulating the signal mask so that the thread receives no signals. +/// The pthread runs \p func. +/// \returns true on success, false on failure. +bool make_pthread(pthread_t *result, void *(*func)(void *), void *param); +bool make_pthread(pthread_t *result, std::function &&func); + #endif From 6f682c84052a8bd52f14122a36e878037cbb868e Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 1 Feb 2019 01:58:06 -0800 Subject: [PATCH 356/439] Fill io_buffer via background thread This is a large change to how io_buffers are filled. The essential problem comes about with code like (example): echo ( /bin/pwd ) The output of /bin/pwd must go to fish, not the tty. To arrange for this, fish does the following: 1. Invoke pipe() to create a pipe. 2. Add an io_bufferfill_t redirection that owns the write end of the pipe. 3. After fork (or equiv), call dup2() to replace pwd's stdout with this pipe. Now when /bin/pwd writes, it will send output to the read end of the pipe. But who reads it? Prior to this fix, fish would do the following in a loop: 1. select() on the pipe with a 10 msec timeout 2. waitpid(WNOHANG) on the pwd proc This polling is ugly and confusing and is what is replaced here. With this new change, fish now reads from the pipe via a background thread: 1. Spawn a background pthread, which select()s on the pipe's read end with a long (100 msec) timeout. 2. In the foreground, waitpid() (allowing hanging) on the pwd proc. The big win here is a major simplification of job_t::continue_job() since it no longer has to worry about filling buffers. This will make things easier for concurrent execution. It may not be obvious why the background thread still needs a poll (100 msec). The answer is for cases where the write end of the fd escapes, in particular background processes invoked inside command substitutions. psub is perhaps the only important case of this (other shells typically just hang here). --- src/exec.cpp | 12 +--- src/fish_tests.cpp | 4 +- src/io.cpp | 170 +++++++++++++++++++++++++++++++-------------- src/io.h | 76 ++++++++++++-------- src/proc.cpp | 85 +---------------------- tests/count.err | 3 + tests/count.in | 12 ++++ tests/count.out | 3 + 8 files changed, 191 insertions(+), 174 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index 7de91f5be..eb1a88259 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -782,12 +782,8 @@ static bool exec_block_or_func_process(parser_t &parser, std::shared_ptr // Remove our write pipe and forget it. This may close the pipe, unless another thread has // claimed it (background write) or another process has inherited it. - auto block_output_buffer = block_output_bufferfill->buffer(); io_chain.remove(block_output_bufferfill); - block_output_bufferfill.reset(); - - // Make the buffer populate itself from whatever was written to the write pipe. - block_output_buffer->read_to_wouldblock(); + auto block_output_buffer = io_bufferfill_t::finish(std::move(block_output_bufferfill)); // Resolve our IO chain to a sequence of dup2s. auto dup2s = dup2_list_t::resolve_chain(io_chain); @@ -969,7 +965,7 @@ bool exec_job(parser_t &parser, shared_ptr j) { if ((io->io_mode == io_mode_t::bufferfill)) { // The read limit is dictated by the last bufferfill. const auto *bf = static_cast(io.get()); - stdout_read_limit = bf->buffer()->buffer().limit(); + stdout_read_limit = bf->buffer()->read_limit(); } } @@ -1040,9 +1036,7 @@ static int exec_subshell_internal(const wcstring &cmd, parser_t &parser, wcstrin if (parser.eval(cmd, io_chain_t{bufferfill}, SUBST) == 0) { subcommand_status = proc_get_last_status(); } - buffer = bufferfill->buffer(); - bufferfill.reset(); - buffer->read_to_wouldblock(); + buffer = io_bufferfill_t::finish(std::move(bufferfill)); } if (buffer && buffer->buffer().discarded()) subcommand_status = STATUS_READ_TOO_MUCH; diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index e45f22e34..cecb566f3 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -943,9 +943,7 @@ static void test_1_cancellation(const wchar_t *src) { pthread_kill(thread, SIGINT); }); parser_t::principal_parser().eval(src, io_chain_t{filler}, TOP); - auto buffer = filler->buffer(); - filler.reset(); - buffer->read_to_wouldblock(); + auto buffer = io_bufferfill_t::finish(std::move(filler)); if (buffer->buffer().size() != 0) { err(L"Expected 0 bytes in out_buff, but instead found %lu bytes\n", buffer->buffer().size()); diff --git a/src/io.cpp b/src/io.cpp index 32731776c..713bc1432 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -11,6 +11,7 @@ #include "exec.h" #include "fallback.h" // IWYU pragma: keep #include "io.h" +#include "iothread.h" #include "wutil.h" // IWYU pragma: keep io_data_t::~io_data_t() = default; @@ -28,6 +29,7 @@ void io_pipe_t::print() const { void io_bufferfill_t::print() const { fwprintf(stderr, L"bufferfill {%d}\n", write_fd_.fd()); } void io_buffer_t::append_from_stream(const output_stream_t &stream) { + scoped_lock locker(append_lock_); if (buffer_.discarded()) return; if (stream.buffer().discarded()) { buffer_.set_discard(); @@ -36,54 +38,108 @@ void io_buffer_t::append_from_stream(const output_stream_t &stream) { buffer_.append_wide_buffer(stream.buffer()); } -long io_buffer_t::read_some() { - int fd = read_.fd(); - assert(fd >= 0 && "Should have a valid fd"); - debug(4, L"io_buffer_t::read: blocking read on fd %d", fd); - long len; - char b[4096]; - do { - len = read(fd, b, sizeof b); - } while (len < 0 && errno == EINTR); - if (len > 0) { - buffer_.append(&b[0], &b[len]); - } - return len; -} +void io_buffer_t::run_background_fillthread(autoclose_fd_t readfd) { + // Here we are running the background fillthread, executing in a background thread. + // Our plan is: + // 1. poll via select() until the fd is readable. + // 2. Acquire the append lock. + // 3. read until EAGAIN (would block), appending + // 4. release the lock + // The purpose of holding the lock around the read calls is to ensure that data from background + // processes isn't weirdly interspersed with data directly transferred (from a builtin to a buffer). -void io_buffer_t::read_to_wouldblock() { - long len; - do { - len = read_some(); - } while (len > 0); - if (len < 0 && errno != EAGAIN) { - debug(1, _(L"An error occured while reading output from code block on fd %d"), read_.fd()); - wperror(L"io_buffer_t::read"); - } -} + const int fd = readfd.fd(); -bool io_buffer_t::try_read(unsigned long timeout_usec) { - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = timeout_usec; - int fd = read_.fd(); - assert(fd >= 0 && "Should have a valid fd"); - fd_set fds; - FD_ZERO(&fds); - FD_SET(fd, &fds); - int ret = select(fd + 1, &fds, nullptr, nullptr, &timeout); - if (ret < 0) { - // Treat EINTR as timeout. - if (errno != EINTR) { - debug(1, _(L"An error occured inside select on fd %d"), fd); - wperror(L"io_buffer_t::try_read"); + // 100 msec poll rate. Note that in most cases, the write end of the pipe will be closed so + // select() will return; the polling is important only for weird cases like a background process + // launched in a command substitution. + const long poll_timeout_usec = 100000; + struct timeval tv = {}; + tv.tv_usec = poll_timeout_usec; + + bool shutdown = false; + while (!shutdown) { + bool readable = false; + + // Poll if our fd is readable. + // Do this even if the shutdown flag is set. It's important we wait for the fd at least + // once. For short-lived processes, it's possible for the process to execute, produce output + // (fits in the pipe buffer) and be reaped before we are even scheduled. So always wait at + // least once on the fd. Note that doesn't mean we will wait for the full poll duration; + // typically what will happen is our pipe will be widowed and so this will return quickly. + // It's only for weird cases (e.g. a background process launched inside a command + // substitution) that we'll wait out the entire poll time. + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + int ret = select(fd + 1, &fds, NULL, NULL, &tv); + readable = ret > 0; + if (ret < 0 && errno != EINTR) { + // Surprising error. + wperror(L"select"); + return; + } + + // Only check the shutdown flag if we timed out. + // It's important that if select() indicated we were readable, that we call select() again + // allowing it to time out. Note the typical case is that the fd will be closed, in which + // case select will return immediately. + if (! readable) { + shutdown = this->shutdown_fillthread_.load(std::memory_order_relaxed); + } + + if (readable || shutdown) { + // Now either our fd is readable, or we have set the shutdown flag. + // Either way acquire the lock and read until we reach EOF, or EAGAIN / EINTR. + scoped_lock locker(append_lock_); + ssize_t ret; + do { + char buff[4096]; + ret = read(fd, buff, sizeof buff); + if (ret > 0) { + buffer_.append(&buff[0], &buff[ret]); + } else if (ret == 0) { + shutdown = true; + } else if (errno != EINTR && errno != EAGAIN) { + wperror(L"read"); + return; + } + } while (ret > 0); } - return false; } - if (ret > 0) { - read_some(); + assert(shutdown && "Should only exit loop if shutdown flag is set"); +} + +void io_buffer_t::begin_background_fillthread(autoclose_fd_t fd) { + ASSERT_IS_MAIN_THREAD(); + assert(!fillthread_ && "Already have a fillthread"); + + // We want our background thread to own the fd but it's not easy to move into a std::function. + // Use a shared_ptr. + auto fdref = std::make_shared(std::move(fd)); + + // Our function to read until the receiver is closed. + // It's OK to capture 'this' by value because 'this' owns the background thread and joins it + // before dtor. + std::function func = [this, fdref]() { + this->run_background_fillthread(std::move(*fdref)); + }; + + pthread_t fillthread{}; + if (!make_pthread(&fillthread, std::move(func))) { + wperror(L"make_pthread"); } - return ret > 0; + fillthread_ = fillthread; +} + +void io_buffer_t::complete_background_fillthread() { + ASSERT_IS_MAIN_THREAD(); + assert(fillthread_ && "Should have a fillthread"); + shutdown_fillthread_.store(true, std::memory_order_relaxed); + void *ignored = nullptr; + int err = pthread_join(*fillthread_, &ignored); + DIE_ON_FAILURE(err); + fillthread_.reset(); } shared_ptr io_bufferfill_t::create(const io_chain_t &conflicts, @@ -93,27 +149,39 @@ shared_ptr io_bufferfill_t::create(const io_chain_t &conflicts, if (!pipes) { return nullptr; } - // Our buffer will read from the read end of the pipe. This end must be non-blocking. This is - // because we retain the write end of the pipe in this process (even after handing it off to a - // child process); therefore a read on the pipe may block forever. What we should do is arrange - // for the write end of the pipe to be closed at the right time; then the read could just block. + // because our fillthread needs to poll to decide if it should shut down, and also accept input + // from direct buffer transfers. if (make_fd_nonblocking(pipes->read.fd())) { debug(1, PIPE_ERROR); wperror(L"fcntl"); return nullptr; } - - // Our buffer gets the read end of the pipe; out_pipe gets the write end. - auto buffer = std::make_shared(std::move(pipes->read), buffer_limit); + // Our fillthread gets the read end of the pipe; out_pipe gets the write end. + auto buffer = std::make_shared(buffer_limit); + buffer->begin_background_fillthread(std::move(pipes->read)); return std::make_shared(std::move(pipes->write), buffer); } +std::shared_ptr io_bufferfill_t::finish(std::shared_ptr &&filler) { + // The io filler is passed in. This typically holds the only instance of the write side of the + // pipe used by the buffer's fillthread (except for that side held by other processes). Get the + // buffer out of the bufferfill and clear the shared_ptr; this will typically widow the pipe. + // Then allow the buffer to finish. + assert(filler && "Null pointer in finish"); + auto buffer = filler->buffer(); + filler.reset(); + buffer->complete_background_fillthread(); + return buffer; +} + io_pipe_t::~io_pipe_t() = default; io_bufferfill_t::~io_bufferfill_t() = default; -io_buffer_t::~io_buffer_t() = default; +io_buffer_t::~io_buffer_t() { + assert(! fillthread_ && "io_buffer_t destroyed with outstanding fillthread"); +} void io_chain_t::remove(const shared_ptr &element) { // See if you can guess why std::find doesn't work here. diff --git a/src/io.h b/src/io.h index 6599790ff..fbc44c4fb 100644 --- a/src/io.h +++ b/src/io.h @@ -1,26 +1,21 @@ #ifndef FISH_IO_H #define FISH_IO_H +#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 -#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 #include -using std::shared_ptr; -#else -// C++03 or libstdc++ -#include -using std::tr1::shared_ptr; -#endif +#include +#include #include "common.h" #include "env.h" +#include "maybe.h" + +using std::shared_ptr; /// separated_buffer_t is composed of a sequence of elements, some of which may be explicitly /// separated (e.g. through string spit0) and some of which the separation is inferred. This enum @@ -244,6 +239,8 @@ class io_bufferfill_t : public io_data_t { public: void print() const override; + // The ctor is public to support make_shared() in the static create function below. + // Do not invoke this directly. io_bufferfill_t(autoclose_fd_t write_fd, std::shared_ptr buffer) : io_data_t(io_mode_t::bufferfill, STDOUT_FILENO), write_fd_(std::move(write_fd)), @@ -253,14 +250,19 @@ class io_bufferfill_t : public io_data_t { std::shared_ptr buffer() const { return buffer_; } + /// \return the fd that, when written to, fills the buffer. int write_fd() const { return write_fd_.fd(); } - /// Create an io_bufferfill_t which, when written from, populates a buffer (also created). + /// Create an io_bufferfill_t which, when written from, fills a buffer with the contents. /// \returns nullptr on failure, e.g. too many open fds. /// /// \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 shared_ptr create(const io_chain_t &conflicts, size_t buffer_limit = 0); + + /// Reset the receiver (possibly closing the write end of the pipe), and complete the fillthread + /// of the buffer. \return the buffer. + static std::shared_ptr finish(std::shared_ptr &&filler); }; class output_stream_t; @@ -269,19 +271,34 @@ class output_stream_t; /// It is not an io_data_t. class io_buffer_t { private: - /// fd from which to read. - autoclose_fd_t read_; + friend io_bufferfill_t; /// Buffer storing what we have read. separated_buffer_t buffer_; - /// Read some. Append it to our buffer. - /// \return positive if we read, 0 on EOF, -1 on error. - long read_some(); + /// Atomic flag indicating our fillthread should shut down. + std::atomic shutdown_fillthread_; + + /// The background fillthread itself, if any. + maybe_t fillthread_{}; + + /// Read limit of the buffer. + const size_t read_limit_; + + /// Lock for appending. + std::mutex append_lock_{}; + + /// Called in the background thread to run it. + void run_background_fillthread(autoclose_fd_t readfd); + + /// Begin the background fillthread operation, reading from the given fd. + void begin_background_fillthread(autoclose_fd_t readfd); + + /// End the background fillthread operation. + void complete_background_fillthread(); public: - explicit io_buffer_t(autoclose_fd_t read, size_t limit) - : read_(std::move(read)), buffer_(limit) { + explicit io_buffer_t(size_t limit) : buffer_(limit), read_limit_(limit) { // Explicitly reset the discard flag because we share this buffer. buffer_.reset_discard(); } @@ -289,17 +306,20 @@ class io_buffer_t { ~io_buffer_t(); /// Access the underlying buffer. - const separated_buffer_t &buffer() const { return buffer_; } + /// This requires that the background fillthread be none. + const separated_buffer_t &buffer() const { + assert(!fillthread_ && "Cannot access buffer during background fill"); + return buffer_; + } /// Function to append to the buffer. - void append(const char *ptr, size_t count) { buffer_.append(ptr, ptr + count); } + void append(const char *ptr, size_t count) { + scoped_lock locker(append_lock_); + buffer_.append(ptr, ptr + count); + } - /// Read from input pipe until EOF or EAGAIN (i.e. would block). - void read_to_wouldblock(); - - /// Read a bit, if our fd is readable, with the given timeout. - /// \return true if we read some, false on timeout. - bool try_read(unsigned long timeout_usec); + /// \return the read limit. + size_t read_limit() const { return read_limit_; } /// Appends data from a given output_stream_t. /// Marks the receiver as discarded if the stream was discarded. diff --git a/src/proc.cpp b/src/proc.cpp index a58217f66..a440d1cda 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -836,27 +836,6 @@ void proc_update_jiffies() { #endif -/// The return value of select_try(), indicating IO readiness or an error -enum class block_receive_try_t { - /// There is no buffer to select on. - NO_BUFFER, - /// We have a block buffer, and we read some. - DATA_READ, - /// We have a block buffer, but we were unable to read any. - TIMEOUT, -}; - -/// \return the last IO buffer in job j, or nullptr if none. -std::shared_ptr last_buffer(job_t *j) { - std::shared_ptr buff{}; - for (const auto &io : j->all_io_redirections()) { - if (io->io_mode == io_mode_t::bufferfill) { - buff = static_cast(io.get())->buffer(); - } - } - return buff; -} - // Return control of the terminal to a job's process group. restore_attrs is true if we are restoring // a previously-stopped job, in which case we need to restore terminal attributes. bool terminal_give_to_job(const job_t *j, bool restore_attrs) { @@ -1038,7 +1017,6 @@ void job_t::continue_job(bool send_sigcont) { } }); - bool read_attempted = false; if (!is_completed()) { if (get_flag(job_flag_t::TERMINAL) && is_foreground()) { // Put the job into the foreground and give it control of the terminal. @@ -1071,74 +1049,15 @@ void job_t::continue_job(bool send_sigcont) { } if (is_foreground()) { - // This is an optimization to not call select_try() in case a process has exited. While - // it may seem silly, unless there is IO (and there usually isn't in terms of total CPU - // time), select_try() will wait for 10ms (our timeout) before returning. If during - // these 10ms a process exited, the shell will basically hang until the timeout happens - // and we are free to call `process_mark_finished_children()` to discover that fact. By - // calling it here before calling `select_try()` below, shell responsiveness can be - // dramatically improved (noticably so, not just "theoretically speaking" per the - // discussion in #5219). - process_mark_finished_children(false); - - // If this is a child job and the parent job is still under construction (i.e. job1 | - // some_func), we can't block on execution of the nested job for `some_func`. Doing - // so can cause hangs if job1 emits more data than fits in the OS pipe buffer. - // The solution is to to not block on fg from the initial call in exec_job(), which - // is also the only place that send_sigcont is false. parent_job.is_constructed() - // must also be true, which coincides with WAIT_BY_PROCESS (which will have to do - // since we don't store a reference to the parent job in the job_t structure). - bool block_on_fg = send_sigcont && job_chain_is_fully_constructed(); - - // Wait for data to become available or the status of our own job to change + // Wait for the status of our own job to change. while (!reader_exit_forced() && !is_stopped() && !is_completed()) { - read_attempted = true; - auto read_result = block_receive_try_t::NO_BUFFER; - if (auto buff = last_buffer(this)) { - const unsigned long SELECT_TIMEOUT_USEC = 10000; - bool did_read = buff->try_read(SELECT_TIMEOUT_USEC); - read_result = did_read ? block_receive_try_t::DATA_READ : block_receive_try_t::TIMEOUT; - } - switch (read_result) { - case block_receive_try_t::DATA_READ: - // We read some data. - process_mark_finished_children(false); - break; - - case block_receive_try_t::TIMEOUT: - // We read some data or timed out. Poll for finished processes. - debug(4, L"select_try: no fds returned valid data within the timeout" ); - process_mark_finished_children(block_on_fg); - break; - - case block_receive_try_t::NO_BUFFER: - // We are not populating a buffer. - debug(4, L"select_try: no IO fds" ); - process_mark_finished_children(true); - - // If it turns out that we encountered this because the file descriptor we were - // reading from has died, process_mark_finished_children() should take care of - // changing the status of our is_completed() (assuming it is appropriate to do - // so), in which case we will break out of this loop. - break; - } + process_mark_finished_children(true); } } } if (is_foreground()) { if (is_completed()) { - // It's possible that the job will produce output and exit before we've even read from - // it. In that case, make sure we read that output now, before we've executed any - // subsequent calls. This is why prompt colors were getting screwed up - the builtin - // `echo` calls were sometimes having their output combined with the `set_color` calls - // in the wrong order! - if (!read_attempted) { - if (auto buff = last_buffer(this)) { - buff->read_to_wouldblock(); - } - } - // Set $status only if we are in the foreground and the last process in the job has // finished and is not a short-circuited builtin. auto &p = processes.back(); diff --git a/tests/count.err b/tests/count.err index a4d49599c..b9b02b750 100644 --- a/tests/count.err +++ b/tests/count.err @@ -10,3 +10,6 @@ #################### # args that look like flags or are otherwise special + +#################### +# big counts diff --git a/tests/count.in b/tests/count.in index ede959942..81b54ce7d 100644 --- a/tests/count.in +++ b/tests/count.in @@ -15,3 +15,15 @@ count --help count -- count -- abc count def -- abc + +logmsg big counts + +# See #5611 +for i in seq 500 + set c1 (count (seq 1 10000)) + test $c1 -eq 10000 + or begin + echo "Count $c1 is not 10000" + break + end +end diff --git a/tests/count.out b/tests/count.out index a81214195..f3c2bb10e 100644 --- a/tests/count.out +++ b/tests/count.out @@ -18,3 +18,6 @@ 1 2 3 + +#################### +# big counts From d3fa58d6213810b0ffd69051828ec96199c24ffd Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 3 Feb 2019 16:06:10 -0800 Subject: [PATCH 357/439] Cleanup common.h Remove a bunch of headers, simplify lots of code, migrate it into .cpp files. Debug build time improves by ~3 seconds on my Mac. --- configure.ac | 2 +- src/builtin_math.cpp | 1 + src/builtin_pwd.cpp | 2 + src/builtin_realpath.cpp | 1 + src/builtin_test.cpp | 2 + src/common.cpp | 105 +++++++++++++++++----------- src/common.h | 145 +++++++-------------------------------- src/complete.cpp | 27 ++++---- src/complete.h | 2 - src/env.cpp | 10 +-- src/exec.cpp | 2 +- src/expand.cpp | 2 +- src/fallback.h | 4 -- src/fish.cpp | 2 +- src/maybe.h | 1 - src/proc.cpp | 4 +- 16 files changed, 121 insertions(+), 191 deletions(-) diff --git a/configure.ac b/configure.ac index 82f859368..77be63258 100644 --- a/configure.ac +++ b/configure.ac @@ -283,7 +283,7 @@ fi # Check presense of various header files # -AC_CHECK_HEADERS([getopt.h termios.h sys/resource.h term.h ncurses/term.h ncurses.h ncurses/curses.h curses.h stropts.h siginfo.h sys/select.h sys/ioctl.h execinfo.h spawn.h sys/sysctl.h]) +AC_CHECK_HEADERS([getopt.h termios.h sys/resource.h term.h ncurses/term.h ncurses.h ncurses/curses.h curses.h stropts.h siginfo.h sys/select.h sys/ioctl.h execinfo.h spawn.h sys/sysctl.h xlocale.h]) if test x$local_gettext != xno; then AC_CHECK_HEADERS([libintl.h]) diff --git a/src/builtin_math.cpp b/src/builtin_math.cpp index cf51111d2..284e47a2b 100644 --- a/src/builtin_math.cpp +++ b/src/builtin_math.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include diff --git a/src/builtin_pwd.cpp b/src/builtin_pwd.cpp index f9d7fda9c..1dce15da3 100644 --- a/src/builtin_pwd.cpp +++ b/src/builtin_pwd.cpp @@ -1,6 +1,8 @@ // Implementation of the pwd builtin. #include "config.h" // IWYU pragma: keep +#include + #include "builtin.h" #include "builtin_pwd.h" #include "common.h" diff --git a/src/builtin_realpath.cpp b/src/builtin_realpath.cpp index b87fe905a..89a1dab56 100644 --- a/src/builtin_realpath.cpp +++ b/src/builtin_realpath.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "builtin.h" diff --git a/src/builtin_test.cpp b/src/builtin_test.cpp index 85cd32d03..327b0ea79 100644 --- a/src/builtin_test.cpp +++ b/src/builtin_test.cpp @@ -5,10 +5,12 @@ #include #include +#include #include #include #include #include +#include #include #include diff --git a/src/common.cpp b/src/common.cpp index c66639000..7a88e569f 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -38,6 +38,7 @@ #include #include +#include #include // IWYU pragma: keep #include @@ -692,58 +693,54 @@ void debug_safe(int level, const char *msg, const char *param1, const char *para errno = errno_old; } -void format_long_safe(char buff[64], long val) { - if (val == 0) { - strcpy(buff, "0"); - } else { - // Generate the string in reverse. - size_t idx = 0; - bool negative = (val < 0); +// Careful to not negate LLONG_MIN. +static unsigned long long absolute_value(long long x) { + if (x >= 0) return static_cast(x); + x = -(x + 1); + return static_cast(x) + 1; +} - // 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. +template +void format_safe_impl(CharT *buff, size_t size, unsigned long long val) { + size_t idx = 0; + if (val == 0) { + buff[idx++] = '0'; + } else { + // Generate the string backwards, then reverse it. while (val != 0) { - long rem = val % 10; - buff[idx++] = '0' + (rem < 0 ? -rem : rem); + buff[idx++] = (val % 10) + '0'; val /= 10; } - if (negative) buff[idx++] = '-'; - buff[idx] = 0; + std::reverse(buff, buff + idx); + } + buff[idx++] = '\0'; + assert(idx <= size && "Buffer overflowed"); +} - size_t left = 0, right = idx - 1; - while (left < right) { - char tmp = buff[left]; - buff[left++] = buff[right]; - buff[right--] = tmp; - } +void format_long_safe(char buff[64], long val) { + unsigned long long uval = absolute_value(val); + if (val >= 0) { + format_safe_impl(buff, 64, uval); + } else { + buff[0] = '-'; + format_safe_impl(buff + 1, 63, uval); } } void format_long_safe(wchar_t buff[64], long val) { - if (val == 0) { - wcscpy(buff, L"0"); + unsigned long long uval = absolute_value(val); + if (val >= 0) { + format_safe_impl(buff, 64, uval); } else { - // Generate the string in reverse. - size_t idx = 0; - bool negative = (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'-'; - buff[idx] = 0; - - size_t left = 0, right = idx - 1; - while (left < right) { - wchar_t tmp = buff[left]; - buff[left++] = buff[right]; - buff[right--] = tmp; - } + buff[0] = '-'; + format_safe_impl(buff + 1, 63, uval); } } +void format_ullong_safe(wchar_t buff[64], unsigned long long val) { + return format_safe_impl(buff, 64, val); +} + 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++) { @@ -1956,6 +1953,36 @@ int string_fuzzy_match_t::compare(const string_fuzzy_match_t &rhs) const { return 0; // equal } +template +size_t ifind_impl(const T &haystack, const T &needle) { + using char_t = typename T::value_type; + std::locale locale; + + auto ieq = [&locale](char_t c1, char_t c2) { + if (c1 == c2 || std::toupper(c1, locale) == std::toupper(c2, locale)) return true; + + // In fuzzy matching treat treat `-` and `_` as equal (#3584). + if (Fuzzy) { + if ((c1 == '-' || c1 == '_') && (c2 == '-' || c2 == '_')) return true; + } + return false; + }; + + auto result = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), ieq); + if (result != haystack.end()) { + return result - haystack.begin(); + } + return T::npos; +} + +size_t ifind(const wcstring &haystack, const wcstring &needle, bool fuzzy) { + return fuzzy ? ifind_impl(haystack, needle) : ifind_impl(haystack, needle); +} + +size_t ifind(const std::string &haystack, const std::string &needle, bool fuzzy) { + return fuzzy ? ifind_impl(haystack, needle) : ifind_impl(haystack, needle); +} + wcstring_list_t split_string(const wcstring &val, wchar_t sep) { wcstring_list_t out; size_t pos = 0, end = val.size(); diff --git a/src/common.h b/src/common.h index 5fa2119ba..8e94364be 100644 --- a/src/common.h +++ b/src/common.h @@ -5,33 +5,19 @@ #include #include -#include -#include // IWYU pragma: keep -#include -#include -#include -#include -#include -#include #ifdef HAVE_SYS_IOCTL_H #include // IWYU pragma: keep #endif #include #include -#include #include #include -#include #include -#include -#include -#include #include #include "fallback.h" // IWYU pragma: keep #include "maybe.h" -#include "signal.h" // IWYU pragma: keep // Define a symbol we can use elsewhere in our code to determine if we're being built on MS Windows // under Cygwin. @@ -361,61 +347,13 @@ bool string_suffixes_string_case_insensitive(const wcstring &proposed_suffix, bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, const wcstring &value); -/// Case-insensitive string search, templated for use with both std::string and std::wstring. -/// Modeled after std::string::find(). +/// Case-insensitive string search, modeled after std::string::find(). /// \param fuzzy indicates this is being used for fuzzy matching and case insensitivity is /// expanded to include symbolic characters (#3584). /// \return the offset of the first case-insensitive matching instance of `needle` within /// `haystack`, or `string::npos()` if no results were found. -template -size_t ifind(const T &haystack, const T &needle, bool fuzzy = false) { - using char_t = typename T::value_type; - auto locale = std::locale(); - - std::function icase_eq; - - if (!fuzzy) { - icase_eq = [&locale](char_t c1, char_t c2) { - return std::toupper(c1, locale) == std::toupper(c2, locale); - }; - } else { - icase_eq = [&locale](char_t c1, char_t c2) { - // This `ifind()` call is being used for fuzzy string matching. Further extend case - // insensitivity to treat `-` and `_` as equal (#3584). - - // The two lines below were tested to be 27% faster than - // (c1 == '_' || c1 == '-') && (c2 == '-' || c2 == '_') - // while returning no false positives for all (c1, c2) combinations in the printable - // range (0x20-0x7E). It might return false positives outside that range, but fuzzy - // comparisons are typically called for file names only, which are unlikely to have - // such characters and this entire function is 100% broken on unicode so there's no - // point in worrying about anything outside of the ANSII range. - // ((c1 == Literal('_') || c1 == Literal('-')) && - // ((c1 ^ c2) == (Literal('-') ^ Literal('_')))); - - // One of the following would be an illegal comparison between a char and a wchar_t. - // However, placing them behind a constexpr gate results in the elision of the if - // statement and the incorrect branch, with the compiler's SFINAE support suppressing - // any errors in the branch not taken. - if (sizeof(char_t) == sizeof(char)) { - return std::toupper(c1, locale) == std::toupper(c2, locale) || - ((c1 == '_' || c1 == '-') && - ((c1 ^ c2) == ('-' ^ '_'))); - } else { - return std::toupper(c1, locale) == std::toupper(c2, locale) || - ((c1 == L'_' || c1 == L'-') && - ((c1 ^ c2) == (L'-' ^ L'_'))); - } - }; - } - - auto result = - std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), icase_eq); - if (result != haystack.end()) { - return result - haystack.begin(); - } - return T::npos; -} +size_t ifind(const wcstring &haystack, const wcstring &needle, bool fuzzy = false); +size_t ifind(const std::string &haystack, const std::string &needle, bool fuzzy = false); /// Split a string by a separator character. wcstring_list_t split_string(const wcstring &val, wchar_t sep); @@ -565,55 +503,40 @@ void debug_safe(int level, const char *msg, const char *param1 = NULL, const cha /// Writes out a long safely. void format_long_safe(char buff[64], long val); void format_long_safe(wchar_t buff[64], long val); +void format_ullong_safe(wchar_t buff[64], unsigned long long val); /// "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) { - T result; - std::wstringstream stream(x); - stream >> result; - return result; -} - -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) { - 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) { - wchar_t buff[128]; +inline wcstring to_string(long x) { + wchar_t buff[64]; 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)); +inline wcstring to_string(int x) { return to_string(static_cast(x)); } + +inline wcstring to_string(size_t x) { + wchar_t buff[64]; + format_ullong_safe(buff, x); + return wcstring(buff); } -template <> -inline bool from_string(const wcstring &x) { - return !x.empty() && wcschr(L"YTyt1", x.at(0)); +inline bool bool_from_string(const std::string &x) { + if (x.empty()) return false; + switch (x.front()) { + case 'Y': + case 'T': + case 'y': + case 't': + case '1': + return true; + default: + return false; + } } -template <> -inline wcstring to_string(const int &x) { - return to_string(static_cast(x)); -} +inline bool bool_from_string(const wcstring &x) { return !x.empty() && wcschr(L"YTyt1", x.at(0)); } wchar_t **make_null_terminated_array(const wcstring_list_t &lst); char **make_null_terminated_array(const std::vector &lst); @@ -1022,22 +945,6 @@ static const wchar_t *enum_to_str(T enum_val, const enum_map map[]) { return NULL; }; -template -using tuple_list = std::vector>; - -// Given a container mapping one X to many Y, return a list of {X,Y} -template -inline tuple_list flatten(const std::unordered_map> &list) { - tuple_list results(list.size() * 1.5); // just a guess as to the initial size - for (auto &kv : list) { - for (auto &v : kv.second) { - results.emplace_back(std::make_tuple(kv.first, v)); - } - } - - return results; -} - void redirect_tty_output(); // Minimum allowed terminal size and default size if the detected size is not reasonable. @@ -1123,7 +1030,7 @@ struct cleanup_t { const std::function cleanup; public: cleanup_t(std::function exit_actions) - : cleanup{exit_actions} {} + : cleanup{std::move(exit_actions)} {} ~cleanup_t() { cleanup(); } diff --git a/src/complete.cpp b/src/complete.cpp index 8beaade7d..e9e00307e 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -171,6 +171,10 @@ struct equal_to { typedef std::unordered_set completion_entry_set_t; static owning_lock s_completion_set; +/// Completion "wrapper" support. The map goes from wrapping-command to wrapped-command-list. +using wrapper_map_t = std::unordered_map; +static owning_lock wrapper_map; + /// 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) { @@ -1637,22 +1641,20 @@ wcstring complete_print() { } } - // Append wraps. This is a wonky interface where even values are the commands, and odd values - // are the targets that they wrap. - auto wrap_pairs = complete_get_wrap_pairs(); - for (const auto &entry : wrap_pairs) { - append_format(out, L"complete --command %ls --wraps %ls\n", std::get<0>(entry).c_str(), - std::get<1>(entry).c_str()); + // Append wraps. + auto locked_wrappers = wrapper_map.acquire(); + for (const auto &entry : *locked_wrappers) { + const wcstring &src = entry.first; + for (const wcstring &target : entry.second) { + append_format(out, L"complete --command %ls --wraps %ls\n", src.c_str(), + target.c_str()); + } } return out; } void complete_invalidate_path() { completion_autoloader.invalidate(); } -/// Completion "wrapper" support. The map goes from wrapping-command to wrapped-command-list. -using wrapper_map_t = std::unordered_map; -static owning_lock wrapper_map; - /// Add a new target that wraps a command. Example: __fish_XYZ (function) wraps XYZ (target). bool complete_add_wrapper(const wcstring &command, const wcstring &new_target) { if (command.empty() || new_target.empty()) { @@ -1704,8 +1706,3 @@ wcstring_list_t complete_get_wrap_targets(const wcstring &command) { if (iter == wraps.end()) return {}; return iter->second; } - -tuple_list complete_get_wrap_pairs() { - auto locked_map = wrapper_map.acquire(); - return flatten(*locked_map); -} diff --git a/src/complete.h b/src/complete.h index d73ae1d8c..3b1655f5d 100644 --- a/src/complete.h +++ b/src/complete.h @@ -204,8 +204,6 @@ bool complete_remove_wrapper(const wcstring &command, const wcstring &wrap_targe /// Returns a list of wrap targets for a given command. wcstring_list_t complete_get_wrap_targets(const wcstring &command); -tuple_list complete_get_wrap_pairs(); - // Observes that fish_complete_path has changed. void complete_invalidate_path(); diff --git a/src/env.cpp b/src/env.cpp index aee612cc1..56b6be201 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -469,7 +469,7 @@ static void update_fish_color_support(const environment_t &vars) { wcstring term = term_var.missing_or_empty() ? L"" : term_var->as_string(); bool support_term256 = false; // default to no support if (!fish_term256.missing_or_empty()) { - support_term256 = from_string(fish_term256->as_string()); + support_term256 = bool_from_string(fish_term256->as_string()); debug(2, L"256 color support determined by 'fish_term256'"); } else if (term.find(L"256color") != wcstring::npos) { // TERM=*256color*: Explicitly supported. @@ -501,7 +501,7 @@ static void update_fish_color_support(const environment_t &vars) { auto fish_term24bit = vars.get(L"fish_term24bit"); bool support_term24bit; if (!fish_term24bit.missing_or_empty()) { - support_term24bit = from_string(fish_term24bit->as_string()); + support_term24bit = bool_from_string(fish_term24bit->as_string()); debug(2, L"'fish_term24bit' preference: 24-bit color %s", support_term24bit ? L"enabled" : L"disabled"); } else { @@ -956,7 +956,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { vars.set_one(L"FISH_VERSION", ENV_GLOBAL, version); // Set the $fish_pid variable. - vars.set_one(L"fish_pid", ENV_GLOBAL, to_string(getpid())); + vars.set_one(L"fish_pid", ENV_GLOBAL, to_string(getpid())); // Set the $hostname variable wcstring hostname = L"fish"; @@ -971,7 +971,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { // TODO: Figure out how to handle invalid numbers better. Shouldn't we issue a diagnostic? long shlvl_i = fish_wcstol(str2wcstring(shlvl_var).c_str(), &end); if (!errno && shlvl_i >= 0) { - nshlvl_str = to_string(shlvl_i + 1); + nshlvl_str = to_string(shlvl_i + 1); } } vars.set_one(L"SHLVL", ENV_GLOBAL | ENV_EXPORT, nshlvl_str); @@ -1028,7 +1028,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { // Set g_use_posix_spawn. Default to true. auto use_posix_spawn = vars.get(L"fish_use_posix_spawn"); g_use_posix_spawn = - use_posix_spawn.missing_or_empty() ? true : from_string(use_posix_spawn->as_string()); + use_posix_spawn.missing_or_empty() ? true : bool_from_string(use_posix_spawn->as_string()); // Set fish_bind_mode to "default". vars.set_one(FISH_BIND_MODE_VAR, ENV_GLOBAL, DEFAULT_BIND_MODE); diff --git a/src/exec.cpp b/src/exec.cpp index eb1a88259..63b10759c 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -335,7 +335,7 @@ void internal_exec(env_stack_t &vars, job_t *j, const io_chain_t &all_ios) { if (shlvl_var) { long shlvl = fish_wcstol(shlvl_var->as_string().c_str()); if (!errno && shlvl > 0) { - shlvl_str = to_string(shlvl - 1); + shlvl_str = to_string(shlvl - 1); } } vars.set_one(L"SHLVL", ENV_GLOBAL | ENV_EXPORT, std::move(shlvl_str)); diff --git a/src/expand.cpp b/src/expand.cpp index 665387499..77ed98c1b 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -787,7 +787,7 @@ static void expand_home_directory(wcstring &input, const environment_t &vars) { /// Expand the %self escape. Note this can only come at the beginning of the string. static void expand_percent_self(wcstring &input) { if (!input.empty() && input.front() == PROCESS_EXPAND_SELF) { - input.replace(0, 1, to_string(getpid())); + input.replace(0, 1, to_string(getpid())); } } diff --git a/src/fallback.h b/src/fallback.h index 62422f923..de6771057 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -3,10 +3,6 @@ #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 diff --git a/src/fish.cpp b/src/fish.cpp index 59ff89f0e..9652f310f 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -442,7 +442,7 @@ int main(int argc, char **argv) { proc_fire_event(L"PROCESS_EXIT", EVENT_EXIT, getpid(), exit_status); // Trigger any exit handlers. - wcstring_list_t event_args = {to_string(exit_status)}; + wcstring_list_t event_args = {to_string(exit_status)}; event_fire_generic(L"fish_exit", &event_args); restore_term_mode(); diff --git a/src/maybe.h b/src/maybe.h index 3281dca02..7f3f96cde 100644 --- a/src/maybe.h +++ b/src/maybe.h @@ -2,7 +2,6 @@ #define FISH_MAYBE_H #include -#include // A none_t is a helper type used to implicitly initialize maybe_t. // Example usage: diff --git a/src/proc.cpp b/src/proc.cpp index a440d1cda..94b5358a6 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -625,8 +625,8 @@ void proc_fire_event(const wchar_t *msg, int type, pid_t pid, int status) { event.param1.pid = pid; event.arguments.push_back(msg); - event.arguments.push_back(to_string(pid)); - event.arguments.push_back(to_string(status)); + event.arguments.push_back(to_string(pid)); + event.arguments.push_back(to_string(status)); event_fire(&event); event.arguments.resize(0); } From 4cc168ae115cfef19c53b4f00d7fd18361037b8e Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 4 Feb 2019 22:26:59 +0800 Subject: [PATCH 358/439] Documentation for while: note updated exit status From updates in #4982. --- doc_src/while.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc_src/while.txt b/doc_src/while.txt index 38338792f..5fb851768 100644 --- a/doc_src/while.txt +++ b/doc_src/while.txt @@ -9,10 +9,8 @@ while CONDITION; COMMANDS...; end `while` repeatedly executes `CONDITION`, and if the exit status is 0, then executes `COMMANDS`. -If the exit status of `CONDITION` is non-zero on the first iteration, `COMMANDS` will not be -executed at all, and the exit status of the loop set to the exit status of `CONDITION`. - -The exit status of the loop is 0 otherwise. +The exit status of the while loop is the exit status of the last iteration of the `COMMANDS` executed, +or 0 if none were executed. (This matches other shells and is POSIX-compatible.) You can use `and` or `or` for complex conditions. Even more complex control can be achieved with `while true` containing a break. From c9e529951bd68461f68f643bb9fdc9df729ca810 Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 4 Feb 2019 22:26:59 +0800 Subject: [PATCH 359/439] Documentation for while: note updated exit status From updates in #4982. (cherry picked from commit 4cc168ae115cfef19c53b4f00d7fd18361037b8e) --- doc_src/while.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc_src/while.txt b/doc_src/while.txt index 38338792f..5fb851768 100644 --- a/doc_src/while.txt +++ b/doc_src/while.txt @@ -9,10 +9,8 @@ while CONDITION; COMMANDS...; end `while` repeatedly executes `CONDITION`, and if the exit status is 0, then executes `COMMANDS`. -If the exit status of `CONDITION` is non-zero on the first iteration, `COMMANDS` will not be -executed at all, and the exit status of the loop set to the exit status of `CONDITION`. - -The exit status of the loop is 0 otherwise. +The exit status of the while loop is the exit status of the last iteration of the `COMMANDS` executed, +or 0 if none were executed. (This matches other shells and is POSIX-compatible.) You can use `and` or `or` for complex conditions. Even more complex control can be achieved with `while true` containing a break. From 9f469e576b3e72c78a84a44b7cedcec6f03a817c Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 4 Feb 2019 16:29:33 +0100 Subject: [PATCH 360/439] tinyexpr: Prevent possible division by zero This is possibly not actually undefined behavior because IEEE754 defines _more_ of division-by-zero, but let's be careful. Fixes #2852. --- src/tinyexpr.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 2faf4bb9f..6cfba346d 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -202,7 +202,13 @@ static const te_builtin *find_builtin(const char *name, int len) { static constexpr double add(double a, double b) {return a + b;} static constexpr double sub(double a, double b) {return a - b;} static constexpr double mul(double a, double b) {return a * b;} -static constexpr double divide(double a, double b) {return a / b;} +static constexpr double divide(double a, double b) { + // If b isn't zero, divide. + // If a isn't zero, return signed INFINITY. + // Else, return NAN. + return b ? a / b : a ? copysign(1, a) * copysign(1,b) * INFINITY : NAN; +} + static constexpr double negate(double a) {return -a;} void next_token(state *s) { From eb8a93f4991bee7d27cb1fd5460fb3a49094c510 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 4 Feb 2019 16:58:38 +0100 Subject: [PATCH 361/439] lint.fish: Modernize a bit Use argparse, variable-as-command, skip missingInclude. --- build_tools/lint.fish | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/build_tools/lint.fish b/build_tools/lint.fish index 88bf8445b..d8e9ce89a 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -3,28 +3,26 @@ # This is meant to be run by "make lint" or "make lint-all". It is not meant to # be run directly from a shell prompt. # -set cppchecks warning,performance,portability,information,missingInclude + +# We don't include "missingInclude" as that doesn't find our config.h. +# Missing includes will quickly be found by... compiling the thing anyway. +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] +argparse a/all -- $argv +set -q argv[1]; and set -gx CXX $argv[1] set -e argv[1] -if test "$argv[1]" = "--all" - set all yes - set cppchecks "$cppchecks,unusedFunction" - 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 | \ + set -l sys_includes ($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 @@ -49,8 +47,9 @@ if test "$machine_type" = "x86_64" set cppcheck_args -D__x86_64__ -D__LP64__ $cppcheck_args end -if test $all = yes +if set -q _flag_all set c_files src/*.cpp + set cppchecks "$cppchecks,unusedFunction" 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. From ddfad001eb45e511800750e4203071074617f58d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 4 Feb 2019 17:10:53 +0100 Subject: [PATCH 362/439] Remove unnecessary string copy This called function_exists_no_autoload with a c_str(). Only that takes a wcstring, so the constructor gets called which copies that. --- src/complete.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/complete.cpp b/src/complete.cpp index e9e00307e..3198299ae 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -898,7 +898,7 @@ bool completer_t::complete_param(const wcstring &cmd_orig, const wcstring &popt, bool head_exists = builtin_exists(cmd); // Only reload environment variables if builtin_exists returned false, as an optimization if (head_exists == false) { - head_exists = function_exists_no_autoload(cmd.c_str(), vars); + head_exists = function_exists_no_autoload(cmd, vars); // While it may seem like first testing `path_get_path` before resorting to an env lookup // may be faster, path_get_path can potentially do a lot of FS/IO access, so env.get() + // function_exists() should still be faster. From c7656e66220cb386dc541e77712919911926f127 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 4 Feb 2019 17:11:34 +0100 Subject: [PATCH 363/439] Remove run_as_keepalive Dead code, innit? --- src/postfork.cpp | 12 ------------ src/postfork.h | 3 --- 2 files changed, 15 deletions(-) diff --git a/src/postfork.cpp b/src/postfork.cpp index d8ce8272e..a9e7b94e9 100644 --- a/src/postfork.cpp +++ b/src/postfork.cpp @@ -406,15 +406,3 @@ bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errle errno = saved_errno; return success; } - -void run_as_keepalive(pid_t parent_pid) { - // Run this process as a keepalive. In typical usage the parent process will kill us. However - // this may not happen if the parent process exits abruptly, either via kill or exec. What we do - // is poll our ppid() and exit when it differs from parent_pid. We can afford to do this with - // low frequency because in the great majority of cases, fish will kill(9) us. - for (;;) { - // Note sleep is async-safe. - if (sleep(1)) break; - if (getppid() != parent_pid) break; - } -} diff --git a/src/postfork.h b/src/postfork.h index d91d335cf..27c9d57bb 100644 --- a/src/postfork.h +++ b/src/postfork.h @@ -48,9 +48,6 @@ bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errle void safe_report_exec_error(int err, const char *actual_cmd, const char *const *argv, const char *const *envv); -/// Runs the process as a keepalive, until the parent process given by parent_pid exits. -void run_as_keepalive(pid_t parent_pid); - #if FISH_USE_POSIX_SPAWN /// Initializes and fills in a posix_spawnattr_t; on success, the caller should destroy it via /// posix_spawnattr_destroy. From a042a4cb6272fb021404717545b63fbd22330a0f Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 6 Feb 2019 06:51:27 +0800 Subject: [PATCH 364/439] low level tests: set pwd from getcwd before starting Fixes the tests in Debian pbuilder environments. Closes #5599. --- src/fish_tests.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index cecb566f3..19172dff3 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -5075,6 +5075,9 @@ int main(int argc, char **argv) { // Set default signal handlers, so we can ctrl-C out of this. signal_reset_handlers(); + // Set PWD from getcwd - fixes #5599 + env_stack_t::principal().set_pwd_from_getcwd(); + if (should_test_function("utility_functions")) test_utility_functions(); if (should_test_function("wcstring_tok")) test_wcstring_tok(); if (should_test_function("env_vars")) test_env_vars(); From 2e542d782286e6e9783b22a1827717f6d197e3ab Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 5 Feb 2019 21:44:21 -0800 Subject: [PATCH 365/439] Initialize shutdown_fillthread_ to false It was left uninitialized which was causing certain command substitutions to exit too early. Fixes #5616 --- src/io.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io.h b/src/io.h index fbc44c4fb..026ce8b36 100644 --- a/src/io.h +++ b/src/io.h @@ -277,7 +277,7 @@ class io_buffer_t { separated_buffer_t buffer_; /// Atomic flag indicating our fillthread should shut down. - std::atomic shutdown_fillthread_; + std::atomic shutdown_fillthread_{false}; /// The background fillthread itself, if any. maybe_t fillthread_{}; From eaefed434da2ccc007e289a1a630e0272ba44814 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 25 Jan 2019 16:45:06 -0800 Subject: [PATCH 366/439] make_pkg.sh to build with CMake instead of Xcode --- build_tools/make_pkg.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build_tools/make_pkg.sh b/build_tools/make_pkg.sh index 84888321c..53a20dd21 100755 --- a/build_tools/make_pkg.sh +++ b/build_tools/make_pkg.sh @@ -23,10 +23,11 @@ set -e PKGDIR=`mktemp -d` +SRC_DIR=$PWD 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/ +mkdir -p $PKGDIR/build $PKGDIR/root $PKGDIR/intermediates $PKGDIR/dst +( cd "$PKGDIR/build" && cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo "$SRC_DIR" && make -j 4 && env DESTDIR=$PKGDIR/root/ make install ) 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/ $OUTPUT_PATH/fish-$VERSION.pkg From e0396d8ef8820d2b49e81640b40d3a033c2939fc Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 25 Jan 2019 18:43:52 -0800 Subject: [PATCH 367/439] CMake support for building Mac app --- CMakeLists.txt | 3 +++ cmake/MacApp.cmake | 47 ++++++++++++++++++++++++++++++++++++ osx/CMakeMacAppInfo.plist.in | 33 +++++++++++++++++++++++++ osx/osx_fish_launcher.m | 2 +- 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 cmake/MacApp.cmake create mode 100644 osx/CMakeMacAppInfo.plist.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 809f24162..f29d33986 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,5 +161,8 @@ INCLUDE(cmake/Tests.cmake) # Set up install. INCLUDE(cmake/Install.cmake) +# Mac app. +INCLUDE(cmake/MacApp.cmake) + INCLUDE(FeatureSummary) FEATURE_SUMMARY(WHAT ALL) diff --git a/cmake/MacApp.cmake b/cmake/MacApp.cmake new file mode 100644 index 000000000..df5987868 --- /dev/null +++ b/cmake/MacApp.cmake @@ -0,0 +1,47 @@ +# This is Mac-only. +if (NOT APPLE) + RETURN() +endif (NOT APPLE) + +# The source tree containing certain macOS resources. +SET(OSX_DIR ${CMAKE_CURRENT_SOURCE_DIR}/osx) + +SET(RESOURCE_FILES + ${OSX_DIR}/launch_fish.scpt + ${OSX_DIR}/fish_term_icon.icns + ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/osx_package_scripts/add-shell + ${OSX_DIR}/install.sh +) + +# Resource files must be present in the source list. +ADD_EXECUTABLE(fish_macapp EXCLUDE_FROM_ALL + ${OSX_DIR}/osx_fish_launcher.m + ${RESOURCE_FILES} +) + +# Note CMake appends .app, so the real output name will be fish.app. +# This target does not include the 'base' resource. +SET_TARGET_PROPERTIES(fish_macapp PROPERTIES OUTPUT_NAME "fish") + +FIND_LIBRARY(FOUNDATION_LIB Foundation) +TARGET_LINK_LIBRARIES(fish_macapp ${FOUNDATION_LIB}) + +SET_TARGET_PROPERTIES(fish_macapp PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST ${OSX_DIR}/CMakeMacAppInfo.plist.in + MACOSX_BUNDLE_GUI_IDENTIFIER "com.ridiculousfish.fish-shell" + RESOURCE "${RESOURCE_FILES}" +) + +# The fish Mac app contains a fish installation inside the package. +# Here is where it gets built. +# Copy into the fish mac app after. +SET(MACAPP_FISH_BUILDROOT ${CMAKE_CURRENT_BINARY_DIR}/macapp_buildroot/base) + +ADD_CUSTOM_COMMAND(TARGET fish_macapp POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${MACAPP_FISH_BUILDROOT} + COMMAND DESTDIR=${MACAPP_FISH_BUILDROOT} ${CMAKE_COMMAND} + --build ${CMAKE_CURRENT_BINARY_DIR} --target install + COMMAND ${CMAKE_COMMAND} -E copy_directory ${MACAPP_FISH_BUILDROOT}/.. + $/Resources/ +) diff --git a/osx/CMakeMacAppInfo.plist.in b/osx/CMakeMacAppInfo.plist.in new file mode 100644 index 000000000..ef0db1ad2 --- /dev/null +++ b/osx/CMakeMacAppInfo.plist.in @@ -0,0 +1,33 @@ + + + + + CFBundleDisplayName + fish shell + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleIconFile + fish_term_icon + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + fish shell + CFBundlePackageType + APPL + CFBundleShortVersionString + 3.0.0 + CFBundleVersion + 0.1 + LSApplicationCategoryType + public.app-category.productivity + LSMinimumSystemVersion + 10.6 + LSUIElement + + NSHumanReadableCopyright + Copyright © 2012, ridiculous_fish +All rights reserved. + + diff --git a/osx/osx_fish_launcher.m b/osx/osx_fish_launcher.m index 875ae75f7..40cff3ee7 100644 --- a/osx/osx_fish_launcher.m +++ b/osx/osx_fish_launcher.m @@ -88,7 +88,7 @@ int main(void) { @autoreleasepool { /* Get the fish executable. Make sure it's absolute. */ NSURL *fish_executable = [[NSBundle mainBundle] URLForResource:@"fish" withExtension:@"" - subdirectory:@"base/bin"]; + subdirectory:@"base/usr/local/bin"]; if (! fish_executable) die("Could not find fish executable in bundle"); From ecb808fb39eefa0fc89ea9f98fdf76e1e5568046 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 26 Jan 2019 14:51:37 -0800 Subject: [PATCH 368/439] Teach make_pkg.sh to build fish.app via cmake --- build_tools/make_pkg.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/build_tools/make_pkg.sh b/build_tools/make_pkg.sh index 53a20dd21..55a8bb496 100755 --- a/build_tools/make_pkg.sh +++ b/build_tools/make_pkg.sh @@ -34,9 +34,6 @@ productbuild --package-path $PKGDIR/intermediates --distribution build_tools/os # 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 $OUTPUT_PATH/fish-$VERSION.app.zip fish.app +( cd "$PKGDIR/build" && make fish_macapp && zip -r "$OUTPUT_PATH/fish-$VERSION.app.zip" fish.app ) rm -r $PKGDIR From 9396f79e3ecb6c8c87b0f1724d4560e149603fc3 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 26 Jan 2019 14:41:31 -0800 Subject: [PATCH 369/439] Remove xcode project This removes the Xcode project and associated headers. A better Xcode project can be generated via CMake: cmake -G Xcode /path/to/fish-shell/ --- fish.xcodeproj/project.pbxproj | 2536 ----------------- .../contents.xcworkspacedata | 7 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcshareddata/xcschemes/Makefile.xcscheme | 71 - .../xcshareddata/xcschemes/base.xcscheme | 73 - .../xcshareddata/xcschemes/fish.app.xcscheme | 91 - .../xcschemes/fish_tests.xcscheme | 98 - .../xcschemes/install_tree.xcscheme | 73 - osx/Info.plist | 33 - osx/config.h | 295 -- osx/pcre2/config.h | 304 -- osx/shared_headers/pcre2.h | 732 ----- 12 files changed, 4321 deletions(-) delete mode 100644 fish.xcodeproj/project.pbxproj delete mode 100644 fish.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 fish.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 fish.xcodeproj/xcshareddata/xcschemes/Makefile.xcscheme delete mode 100644 fish.xcodeproj/xcshareddata/xcschemes/base.xcscheme delete mode 100644 fish.xcodeproj/xcshareddata/xcschemes/fish.app.xcscheme delete mode 100644 fish.xcodeproj/xcshareddata/xcschemes/fish_tests.xcscheme delete mode 100644 fish.xcodeproj/xcshareddata/xcschemes/install_tree.xcscheme delete mode 100644 osx/Info.plist delete mode 100644 osx/config.h delete mode 100644 osx/pcre2/config.h delete mode 100644 osx/shared_headers/pcre2.h diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj deleted file mode 100644 index c19f683d1..000000000 --- a/fish.xcodeproj/project.pbxproj +++ /dev/null @@ -1,2536 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXAggregateTarget section */ - D008D0C41BC58F8800841177 /* generate-version-header */ = { - isa = PBXAggregateTarget; - buildConfigurationList = D008D0C81BC58F8800841177 /* Build configuration list for PBXAggregateTarget "generate-version-header" */; - buildPhases = ( - D008D0C91BC58F9500841177 /* ShellScript */, - ); - dependencies = ( - ); - name = "generate-version-header"; - productName = "generate-version-header"; - }; - D07D265615E33B86009E43F6 /* install_tree */ = { - isa = PBXAggregateTarget; - buildConfigurationList = D07D266F15E33B86009E43F6 /* Build configuration list for PBXAggregateTarget "install_tree" */; - buildPhases = ( - D07D266915E33B86009E43F6 /* CopyFiles */, - D07D266B15E33B86009E43F6 /* Copy Files */, - D01A2CA716965ADD00767098 /* CopyFiles */, - ); - dependencies = ( - D07D265715E33B86009E43F6 /* PBXTargetDependency */, - D07D265D15E33B86009E43F6 /* PBXTargetDependency */, - D0A56500168D257900AF6161 /* PBXTargetDependency */, - ); - name = install_tree; - productName = base; - }; - D0A564E6168CFDD800AF6161 /* man_pages */ = { - isa = PBXAggregateTarget; - buildConfigurationList = D0A564E9168CFDD800AF6161 /* Build configuration list for PBXAggregateTarget "man_pages" */; - buildPhases = ( - D0A564EB168CFDDE00AF6161 /* ShellScript */, - ); - dependencies = ( - ); - name = man_pages; - productName = man_pages; - }; - D0F019EC15A976F30034B3B1 /* base */ = { - isa = PBXAggregateTarget; - buildConfigurationList = D0F019ED15A976F30034B3B1 /* Build configuration list for PBXAggregateTarget "base" */; - buildPhases = ( - D0F019F015A977010034B3B1 /* CopyFiles */, - D0F019F715A977A00034B3B1 /* CopyFiles */, - D0F019FC15A977B40034B3B1 /* CopyFiles */, - D033780F15DC6D2A00A634BA /* CopyFiles */, - D01A2C9B16964C8200767098 /* Copy Files */, - ); - dependencies = ( - 9C7A55801DCD73930049C25D /* PBXTargetDependency */, - D0F01A1315AA36280034B3B1 /* PBXTargetDependency */, - D0F01A1715AA36300034B3B1 /* PBXTargetDependency */, - D0A564EF168D09C000AF6161 /* PBXTargetDependency */, - ); - name = base; - productName = base; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 4F2D55CF2013ECDD00822920 /* tnode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F2D55CE2013ECDD00822920 /* tnode.cpp */; }; - 4F2D55D02013ECDD00822920 /* tnode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F2D55CE2013ECDD00822920 /* tnode.cpp */; }; - 4F2D55D12013ED0100822920 /* tnode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F2D55CE2013ECDD00822920 /* tnode.cpp */; }; - 63A2C0E91CC60F3B00973404 /* pcre2_find_bracket.c in Sources */ = {isa = PBXBuildFile; fileRef = 63A2C0E81CC5F9FB00973404 /* pcre2_find_bracket.c */; }; - 9C7A55271DCD651F0049C25D /* fallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853E13B3ACEE0099B651 /* fallback.cpp */; }; - 9C7A552F1DCD65820049C25D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855E13B3ACEE0099B651 /* util.cpp */; }; - 9C7A55361DCD71330049C25D /* autoload.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */; }; - 9C7A55401DCD71330049C25D /* color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B6B0FE14E88BA400AD6C10 /* color.cpp */; }; - 9C7A55411DCD71330049C25D /* common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853613B3ACEE0099B651 /* common.cpp */; }; - 9C7A55421DCD71330049C25D /* event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853B13B3ACEE0099B651 /* event.cpp */; }; - 9C7A55431DCD71330049C25D /* input_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854913B3ACEE0099B651 /* input_common.cpp */; }; - 9C7A55441DCD71330049C25D /* io.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854C13B3ACEE0099B651 /* io.cpp */; }; - 9C7A55451DCD71330049C25D /* iothread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854D13B3ACEE0099B651 /* iothread.cpp */; }; - 9C7A55461DCD71330049C25D /* parse_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855213B3ACEE0099B651 /* parse_util.cpp */; }; - 9C7A55471DCD71330049C25D /* path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855513B3ACEE0099B651 /* path.cpp */; }; - 9C7A55481DCD71330049C25D /* parse_execution.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D052D8091868F7FC003ABCBD /* parse_execution.cpp */; }; - 9C7A55491DCD71330049C25D /* postfork.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D09B1C1914FC7B5B00F91077 /* postfork.cpp */; }; - 9C7A554A1DCD71330049C25D /* screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855A13B3ACEE0099B651 /* screen.cpp */; }; - 9C7A554B1DCD71330049C25D /* signal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855C13B3ACEE0099B651 /* signal.cpp */; }; - 9C7A554C1DCD71330049C25D /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C9733718DE5449002D7C81 /* utf8.cpp */; }; - 9C7A554E1DCD71330049C25D /* function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854413B3ACEE0099B651 /* function.cpp */; }; - 9C7A554F1DCD71330049C25D /* complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853713B3ACEE0099B651 /* complete.cpp */; }; - 9C7A55501DCD71330049C25D /* env.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853A13B3ACEE0099B651 /* env.cpp */; }; - 9C7A55511DCD71330049C25D /* exec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853C13B3ACEE0099B651 /* exec.cpp */; }; - 9C7A55521DCD71330049C25D /* wcstringutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0F5B46319CFCDE80090665E /* wcstringutil.cpp */; }; - 9C7A55531DCD71330049C25D /* expand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853D13B3ACEE0099B651 /* expand.cpp */; }; - 9C7A55541DCD71330049C25D /* fallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853E13B3ACEE0099B651 /* fallback.cpp */; }; - 9C7A55551DCD71330049C25D /* fish_version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D00F63F019137E9D00FCCDEC /* fish_version.cpp */; settings = {COMPILER_FLAGS = "-I$(DERIVED_FILE_DIR)"; }; }; - 9C7A55561DCD71330049C25D /* highlight.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854713B3ACEE0099B651 /* highlight.cpp */; }; - 9C7A55571DCD71330049C25D /* history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854813B3ACEE0099B651 /* history.cpp */; }; - 9C7A55581DCD71330049C25D /* kill.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854F13B3ACEE0099B651 /* kill.cpp */; }; - 9C7A55591DCD71330049C25D /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855413B3ACEE0099B651 /* parser.cpp */; }; - 9C7A555A1DCD71330049C25D /* parser_keywords.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855313B3ACEE0099B651 /* parser_keywords.cpp */; }; - 9C7A555B1DCD71330049C25D /* proc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855713B3ACEE0099B651 /* proc.cpp */; }; - 9C7A555C1DCD71330049C25D /* reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855813B3ACEE0099B651 /* reader.cpp */; }; - 9C7A555D1DCD71330049C25D /* sanity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855913B3ACEE0099B651 /* sanity.cpp */; }; - 9C7A555E1DCD71330049C25D /* tokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855D13B3ACEE0099B651 /* tokenizer.cpp */; }; - 9C7A555F1DCD71330049C25D /* wildcard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0856013B3ACEE0099B651 /* wildcard.cpp */; }; - 9C7A55601DCD71330049C25D /* wgetopt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855F13B3ACEE0099B651 /* wgetopt.cpp */; }; - 9C7A55611DCD71330049C25D /* wutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0856113B3ACEE0099B651 /* wutil.cpp */; }; - 9C7A55621DCD71330049C25D /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854A13B3ACEE0099B651 /* input.cpp */; }; - 9C7A55631DCD71330049C25D /* output.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855113B3ACEE0099B651 /* output.cpp */; }; - 9C7A55641DCD71330049C25D /* intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854B13B3ACEE0099B651 /* intern.cpp */; }; - 9C7A55651DCD71330049C25D /* env_universal_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853813B3ACEE0099B651 /* env_universal_common.cpp */; }; - 9C7A55661DCD71330049C25D /* pager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D03238891849D1980032CF2C /* pager.cpp */; }; - 9C7A55681DCD71330049C25D /* parse_tree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C52F351765284C00BFAB82 /* parse_tree.cpp */; }; - 9C7A55691DCD71330049C25D /* parse_productions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0FE8EE7179FB75F008C9F21 /* parse_productions.cpp */; }; - 9C7A556A1DCD71330049C25D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855E13B3ACEE0099B651 /* util.cpp */; }; - 9C7A556C1DCD71330049C25D /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D02A8C15983CFA008E62BD /* libncurses.dylib */; }; - 9C7A556D1DCD71330049C25D /* libpcre2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D04F7FD01BA4E29300B0F227 /* libpcre2.a */; }; - 9C7A557D1DCD71890049C25D /* fish_key_reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9C7A557C1DCD717C0049C25D /* fish_key_reader.cpp */; }; - 9C7A557E1DCD71CD0049C25D /* print_help.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855613B3ACEE0099B651 /* print_help.cpp */; }; - 9C7A55811DCD739C0049C25D /* fish_key_reader in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9C7A55721DCD71330049C25D /* fish_key_reader */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 9CC8D8C51F7AF0D40095062A /* lynx.lss in Copy Files */ = {isa = PBXBuildFile; fileRef = 9CC8D8C41F7AF0610095062A /* lynx.lss */; }; - CB0F034C1F156FE3001827D3 /* builtin_argparse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB0F034A1F156FE3001827D3 /* builtin_argparse.cpp */; }; - CB0F034D1F156FE3001827D3 /* builtin_argparse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB0F034A1F156FE3001827D3 /* builtin_argparse.cpp */; }; - CB0F034E1F156FE3001827D3 /* builtin_argparse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB0F034A1F156FE3001827D3 /* builtin_argparse.cpp */; }; - D001B5EE1F041CBD000838CC /* builtin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F592F1F041AE4003EE978 /* builtin.cpp */; }; - D001B5F01F041CBD000838CC /* builtin_ulimit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59311F041AE4003EE978 /* builtin_ulimit.cpp */; }; - D001B5F21F041CBD000838CC /* builtin_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59331F041AE4003EE978 /* builtin_test.cpp */; }; - D001B5F41F041CBD000838CC /* builtin_string.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59351F041AE4003EE978 /* builtin_string.cpp */; }; - D001B5F61F041CBD000838CC /* builtin_status.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59371F041AE4003EE978 /* builtin_status.cpp */; }; - D001B5F81F041CBD000838CC /* builtin_source.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59391F041AE4003EE978 /* builtin_source.cpp */; }; - D001B5FA1F041CBD000838CC /* builtin_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593B1F041AE4003EE978 /* builtin_set.cpp */; }; - D001B5FC1F041CBD000838CC /* builtin_set_color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593D1F041AE4003EE978 /* builtin_set_color.cpp */; }; - D001B5FE1F041CBD000838CC /* builtin_return.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593F1F041AE4003EE978 /* builtin_return.cpp */; }; - D001B6001F041CBD000838CC /* builtin_realpath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59411F041AE4003EE978 /* builtin_realpath.cpp */; }; - D001B6021F041CBD000838CC /* builtin_read.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59431F041AE4003EE978 /* builtin_read.cpp */; }; - D001B6041F041CBD000838CC /* builtin_random.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59451F041AE4003EE978 /* builtin_random.cpp */; }; - D001B6061F041CBD000838CC /* builtin_pwd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59471F041AE4003EE978 /* builtin_pwd.cpp */; }; - D001B6081F041CBD000838CC /* builtin_printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59491F041AE4003EE978 /* builtin_printf.cpp */; }; - D001B60A1F041CBD000838CC /* builtin_jobs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594B1F041AE4003EE978 /* builtin_jobs.cpp */; }; - D001B60C1F041CBD000838CC /* builtin_history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594D1F041AE4003EE978 /* builtin_history.cpp */; }; - D001B60E1F041CBD000838CC /* builtin_functions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594F1F041AE4003EE978 /* builtin_functions.cpp */; }; - D001B6101F041CBD000838CC /* builtin_function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59511F041AE4003EE978 /* builtin_function.cpp */; }; - D001B6121F041CBD000838CC /* builtin_fg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59531F041AE4003EE978 /* builtin_fg.cpp */; }; - D001B6141F041CBD000838CC /* builtin_exit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59551F041AE4003EE978 /* builtin_exit.cpp */; }; - D001B6161F041CBD000838CC /* builtin_emit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59571F041AE4003EE978 /* builtin_emit.cpp */; }; - D001B6181F041CBD000838CC /* builtin_echo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59591F041AE4003EE978 /* builtin_echo.cpp */; }; - D001B61A1F041CBD000838CC /* builtin_disown.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595B1F041AE4003EE978 /* builtin_disown.cpp */; }; - D001B61C1F041CBD000838CC /* builtin_contains.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595D1F041AE4003EE978 /* builtin_contains.cpp */; }; - D001B61E1F041CBD000838CC /* builtin_complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595F1F041AE4003EE978 /* builtin_complete.cpp */; }; - D001B6201F041CBD000838CC /* builtin_commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59611F041AE4003EE978 /* builtin_commandline.cpp */; }; - D001B6221F041CBD000838CC /* builtin_command.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59631F041AE4003EE978 /* builtin_command.cpp */; }; - D001B6241F041CBD000838CC /* builtin_cd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59651F041AE4003EE978 /* builtin_cd.cpp */; }; - D001B6261F041CBD000838CC /* builtin_builtin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59671F041AE4003EE978 /* builtin_builtin.cpp */; }; - D001B6281F041CBD000838CC /* builtin_block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59691F041AE4003EE978 /* builtin_block.cpp */; }; - D001B62A1F041CBD000838CC /* builtin_bind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F596B1F041AE4003EE978 /* builtin_bind.cpp */; }; - D001B62C1F041CBD000838CC /* builtin_bg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F596D1F041AE4003EE978 /* builtin_bg.cpp */; }; - D00769121990137800CA4627 /* autoload.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */; }; - D00769141990137800CA4627 /* color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B6B0FE14E88BA400AD6C10 /* color.cpp */; }; - D00769151990137800CA4627 /* common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853613B3ACEE0099B651 /* common.cpp */; }; - D00769161990137800CA4627 /* event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853B13B3ACEE0099B651 /* event.cpp */; }; - D00769171990137800CA4627 /* input_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854913B3ACEE0099B651 /* input_common.cpp */; }; - D00769181990137800CA4627 /* io.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854C13B3ACEE0099B651 /* io.cpp */; }; - D00769191990137800CA4627 /* iothread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854D13B3ACEE0099B651 /* iothread.cpp */; }; - D007691A1990137800CA4627 /* parse_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855213B3ACEE0099B651 /* parse_util.cpp */; }; - D007691B1990137800CA4627 /* path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855513B3ACEE0099B651 /* path.cpp */; }; - D007691C1990137800CA4627 /* parse_execution.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D052D8091868F7FC003ABCBD /* parse_execution.cpp */; }; - D007691D1990137800CA4627 /* postfork.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D09B1C1914FC7B5B00F91077 /* postfork.cpp */; }; - D007691E1990137800CA4627 /* screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855A13B3ACEE0099B651 /* screen.cpp */; }; - D007691F1990137800CA4627 /* signal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855C13B3ACEE0099B651 /* signal.cpp */; }; - D00769201990137800CA4627 /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C9733718DE5449002D7C81 /* utf8.cpp */; }; - D00769221990137800CA4627 /* function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854413B3ACEE0099B651 /* function.cpp */; }; - D00769231990137800CA4627 /* complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853713B3ACEE0099B651 /* complete.cpp */; }; - D00769241990137800CA4627 /* env.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853A13B3ACEE0099B651 /* env.cpp */; }; - D00769251990137800CA4627 /* exec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853C13B3ACEE0099B651 /* exec.cpp */; }; - D00769261990137800CA4627 /* expand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853D13B3ACEE0099B651 /* expand.cpp */; }; - D00769271990137800CA4627 /* fish_version.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D00F63F019137E9D00FCCDEC /* fish_version.cpp */; }; - D00769281990137800CA4627 /* highlight.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854713B3ACEE0099B651 /* highlight.cpp */; }; - D00769291990137800CA4627 /* history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854813B3ACEE0099B651 /* history.cpp */; }; - D007692A1990137800CA4627 /* kill.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854F13B3ACEE0099B651 /* kill.cpp */; }; - D007692B1990137800CA4627 /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855413B3ACEE0099B651 /* parser.cpp */; }; - D007692C1990137800CA4627 /* parser_keywords.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855313B3ACEE0099B651 /* parser_keywords.cpp */; }; - D007692D1990137800CA4627 /* proc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855713B3ACEE0099B651 /* proc.cpp */; }; - D007692E1990137800CA4627 /* reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855813B3ACEE0099B651 /* reader.cpp */; }; - D007692F1990137800CA4627 /* sanity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855913B3ACEE0099B651 /* sanity.cpp */; }; - D00769301990137800CA4627 /* tokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855D13B3ACEE0099B651 /* tokenizer.cpp */; }; - D00769311990137800CA4627 /* wildcard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0856013B3ACEE0099B651 /* wildcard.cpp */; }; - D00769321990137800CA4627 /* wgetopt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855F13B3ACEE0099B651 /* wgetopt.cpp */; }; - D00769331990137800CA4627 /* wutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0856113B3ACEE0099B651 /* wutil.cpp */; }; - D00769341990137800CA4627 /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854A13B3ACEE0099B651 /* input.cpp */; }; - D00769351990137800CA4627 /* output.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855113B3ACEE0099B651 /* output.cpp */; }; - D00769361990137800CA4627 /* intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854B13B3ACEE0099B651 /* intern.cpp */; }; - D00769371990137800CA4627 /* env_universal_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853813B3ACEE0099B651 /* env_universal_common.cpp */; }; - D00769381990137800CA4627 /* pager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D03238891849D1980032CF2C /* pager.cpp */; }; - D007693A1990137800CA4627 /* parse_tree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C52F351765284C00BFAB82 /* parse_tree.cpp */; }; - D007693B1990137800CA4627 /* parse_productions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0FE8EE7179FB75F008C9F21 /* parse_productions.cpp */; }; - D007693D1990137800CA4627 /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D02A8C15983CFA008E62BD /* libncurses.dylib */; }; - 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)"; }; }; - 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 */; }; - D02960E61FBD726200CA3985 /* builtin_wait.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D02960E51FBD726100CA3985 /* builtin_wait.cpp */; }; - D02960E71FBD726200CA3985 /* builtin_wait.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D02960E51FBD726100CA3985 /* builtin_wait.cpp */; }; - D02960E81FBD726200CA3985 /* builtin_wait.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D02960E51FBD726100CA3985 /* builtin_wait.cpp */; }; - D02960E91FBD726200CA3985 /* builtin_wait.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D02960E51FBD726100CA3985 /* builtin_wait.cpp */; }; - D030FBEF1A4A382000F7ADA0 /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854A13B3ACEE0099B651 /* input.cpp */; }; - D030FBF01A4A382B00F7ADA0 /* event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853B13B3ACEE0099B651 /* event.cpp */; }; - D030FBF11A4A384000F7ADA0 /* output.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855113B3ACEE0099B651 /* output.cpp */; }; - D030FBF21A4A384A00F7ADA0 /* signal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855C13B3ACEE0099B651 /* signal.cpp */; }; - D030FBF31A4A386A00F7ADA0 /* reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855813B3ACEE0099B651 /* reader.cpp */; }; - D030FBF41A4A38F300F7ADA0 /* autoload.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */; }; - D030FBF61A4A38F300F7ADA0 /* color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B6B0FE14E88BA400AD6C10 /* color.cpp */; }; - D030FBF71A4A38F300F7ADA0 /* complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853713B3ACEE0099B651 /* complete.cpp */; }; - D030FBF81A4A38F300F7ADA0 /* env_universal_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853813B3ACEE0099B651 /* env_universal_common.cpp */; }; - D030FBF91A4A38F300F7ADA0 /* env.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853A13B3ACEE0099B651 /* env.cpp */; }; - D030FBFA1A4A38F300F7ADA0 /* exec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853C13B3ACEE0099B651 /* exec.cpp */; }; - D030FBFB1A4A38F300F7ADA0 /* expand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853D13B3ACEE0099B651 /* expand.cpp */; }; - D030FBFC1A4A38F300F7ADA0 /* parse_productions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0FE8EE7179FB75F008C9F21 /* parse_productions.cpp */; }; - D030FBFD1A4A38F300F7ADA0 /* parse_tree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C52F351765284C00BFAB82 /* parse_tree.cpp */; }; - D030FBFE1A4A38F300F7ADA0 /* parse_execution.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D052D8091868F7FC003ABCBD /* parse_execution.cpp */; }; - D030FC001A4A38F300F7ADA0 /* function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854413B3ACEE0099B651 /* function.cpp */; }; - D030FC011A4A38F300F7ADA0 /* highlight.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854713B3ACEE0099B651 /* highlight.cpp */; }; - D030FC021A4A38F300F7ADA0 /* history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854813B3ACEE0099B651 /* history.cpp */; }; - D030FC031A4A38F300F7ADA0 /* input_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854913B3ACEE0099B651 /* input_common.cpp */; }; - D030FC041A4A38F300F7ADA0 /* intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854B13B3ACEE0099B651 /* intern.cpp */; }; - D030FC051A4A38F300F7ADA0 /* io.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854C13B3ACEE0099B651 /* io.cpp */; }; - D030FC061A4A38F300F7ADA0 /* iothread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854D13B3ACEE0099B651 /* iothread.cpp */; }; - D030FC071A4A38F300F7ADA0 /* kill.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854F13B3ACEE0099B651 /* kill.cpp */; }; - D030FC081A4A38F300F7ADA0 /* pager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D03238891849D1980032CF2C /* pager.cpp */; }; - D030FC091A4A38F300F7ADA0 /* parse_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855213B3ACEE0099B651 /* parse_util.cpp */; }; - D030FC0A1A4A38F300F7ADA0 /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855413B3ACEE0099B651 /* parser.cpp */; }; - D030FC0B1A4A38F300F7ADA0 /* path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855513B3ACEE0099B651 /* path.cpp */; }; - D030FC0C1A4A38F300F7ADA0 /* postfork.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D09B1C1914FC7B5B00F91077 /* postfork.cpp */; }; - D030FC0D1A4A38F300F7ADA0 /* proc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855713B3ACEE0099B651 /* proc.cpp */; }; - D030FC0E1A4A38F300F7ADA0 /* sanity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855913B3ACEE0099B651 /* sanity.cpp */; }; - D030FC0F1A4A38F300F7ADA0 /* screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855A13B3ACEE0099B651 /* screen.cpp */; }; - D030FC101A4A38F300F7ADA0 /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C9733718DE5449002D7C81 /* utf8.cpp */; }; - D030FC121A4A38F300F7ADA0 /* wcstringutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0F5B46319CFCDE80090665E /* wcstringutil.cpp */; }; - D030FC131A4A38F300F7ADA0 /* wgetopt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855F13B3ACEE0099B651 /* wgetopt.cpp */; }; - D030FC141A4A38F300F7ADA0 /* wildcard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0856013B3ACEE0099B651 /* wildcard.cpp */; }; - D031890C15E36E4600D9CC39 /* base in Resources */ = {isa = PBXBuildFile; fileRef = D031890915E36D9800D9CC39 /* base */; }; - D032388B1849D1980032CF2C /* pager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D03238891849D1980032CF2C /* pager.cpp */; }; - D033781115DC6D4C00A634BA /* completions in CopyFiles */ = {isa = PBXBuildFile; fileRef = D025C02715D1FEA100B9DB63 /* completions */; }; - D033781215DC6D5200A634BA /* functions in CopyFiles */ = {isa = PBXBuildFile; fileRef = D025C02815D1FEA100B9DB63 /* functions */; }; - D033781315DC6D5400A634BA /* tools in CopyFiles */ = {isa = PBXBuildFile; fileRef = D025C02915D1FEA100B9DB63 /* tools */; }; - D046A0FA2070245000C8DFF7 /* tinyexpr.c in Sources */ = {isa = PBXBuildFile; fileRef = D046A0F92070245000C8DFF7 /* tinyexpr.c */; }; - D046A0FB2070245000C8DFF7 /* tinyexpr.c in Sources */ = {isa = PBXBuildFile; fileRef = D046A0F92070245000C8DFF7 /* tinyexpr.c */; }; - D046A0FC2070245000C8DFF7 /* tinyexpr.c in Sources */ = {isa = PBXBuildFile; fileRef = D046A0F92070245000C8DFF7 /* tinyexpr.c */; }; - D046A0FD2070245000C8DFF7 /* tinyexpr.c in Sources */ = {isa = PBXBuildFile; fileRef = D046A0F92070245000C8DFF7 /* tinyexpr.c */; }; - D04C863920B3D83900C675A6 /* future_feature_flags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D04C863820B3D83900C675A6 /* future_feature_flags.cpp */; }; - D04C863A20B3D83900C675A6 /* future_feature_flags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D04C863820B3D83900C675A6 /* future_feature_flags.cpp */; }; - D04C863B20B3D83900C675A6 /* future_feature_flags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D04C863820B3D83900C675A6 /* future_feature_flags.cpp */; }; - D04C863C20B3D83900C675A6 /* future_feature_flags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D04C863820B3D83900C675A6 /* future_feature_flags.cpp */; }; - D04F7FD51BA4E3AC00B0F227 /* pcre2_compile.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F8D1BA4DCD900B0F227 /* pcre2_compile.c */; }; - D04F7FD61BA4E3AC00B0F227 /* pcre2_config.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F901BA4DCE900B0F227 /* pcre2_config.c */; }; - D04F7FD71BA4E3AC00B0F227 /* pcre2_context.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F931BA4DCFA00B0F227 /* pcre2_context.c */; }; - D04F7FD81BA4E3AC00B0F227 /* pcre2_dfa_match.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F961BA4DD1100B0F227 /* pcre2_dfa_match.c */; }; - D04F7FD91BA4E3AC00B0F227 /* pcre2_error.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F991BA4DD2000B0F227 /* pcre2_error.c */; }; - D04F7FDA1BA4E3AC00B0F227 /* pcre2_jit_compile.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FC91BA4DE3500B0F227 /* pcre2_jit_compile.c */; }; - D04F7FDB1BA4E3AC00B0F227 /* pcre2_maketables.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F9C1BA4DD4A00B0F227 /* pcre2_maketables.c */; }; - D04F7FDC1BA4E3AC00B0F227 /* pcre2_match.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F9F1BA4DD5900B0F227 /* pcre2_match.c */; }; - D04F7FDD1BA4E3AC00B0F227 /* pcre2_match_data.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FA21BA4DD6900B0F227 /* pcre2_match_data.c */; }; - D04F7FDE1BA4E3AC00B0F227 /* pcre2_newline.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FA51BA4DD7300B0F227 /* pcre2_newline.c */; }; - D04F7FDF1BA4E3AC00B0F227 /* pcre2_ord2utf.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FA81BA4DD8400B0F227 /* pcre2_ord2utf.c */; }; - D04F7FE01BA4E3AC00B0F227 /* pcre2_pattern_info.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FAB1BA4DDA500B0F227 /* pcre2_pattern_info.c */; }; - D04F7FE11BA4E3AC00B0F227 /* pcre2_serialize.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FAE1BA4DDB500B0F227 /* pcre2_serialize.c */; }; - D04F7FE21BA4E3AC00B0F227 /* pcre2_string_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FB11BA4DDBF00B0F227 /* pcre2_string_utils.c */; }; - D04F7FE31BA4E3AC00B0F227 /* pcre2_study.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FB41BA4DDC900B0F227 /* pcre2_study.c */; }; - D04F7FE41BA4E3AC00B0F227 /* pcre2_substitute.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FB71BA4DDEB00B0F227 /* pcre2_substitute.c */; }; - D04F7FE51BA4E3AC00B0F227 /* pcre2_substring.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FB81BA4DDEB00B0F227 /* pcre2_substring.c */; }; - D04F7FE61BA4E3AC00B0F227 /* pcre2_tables.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FB91BA4DDEB00B0F227 /* pcre2_tables.c */; }; - D04F7FE71BA4E3AC00B0F227 /* pcre2_ucd.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FBA1BA4DDEB00B0F227 /* pcre2_ucd.c */; }; - D04F7FE81BA4E3AC00B0F227 /* pcre2_valid_utf.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FBB1BA4DDEB00B0F227 /* pcre2_valid_utf.c */; }; - D04F7FE91BA4E3AC00B0F227 /* pcre2_xclass.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FBC1BA4DDEB00B0F227 /* pcre2_xclass.c */; }; - D04F7FF11BA4E68200B0F227 /* libpcre2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D04F7FD01BA4E29300B0F227 /* libpcre2.a */; }; - D04F7FF21BA4E68A00B0F227 /* libpcre2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D04F7FD01BA4E29300B0F227 /* libpcre2.a */; }; - D04F7FF41BA4E6F300B0F227 /* pcre2_auto_possess.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FF31BA4E6F300B0F227 /* pcre2_auto_possess.c */; }; - D04F7FF91BA4E87B00B0F227 /* pcre2_chartables.c.dist in Sources */ = {isa = PBXBuildFile; fileRef = D04F7FF71BA4E82C00B0F227 /* pcre2_chartables.c.dist */; }; - D04F7FFA1BA4E9A400B0F227 /* libpcre2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D04F7FD01BA4E29300B0F227 /* libpcre2.a */; }; - D052D80B1868F7FC003ABCBD /* parse_execution.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D052D8091868F7FC003ABCBD /* parse_execution.cpp */; }; - D05F596E1F041AE4003EE978 /* builtin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F592F1F041AE4003EE978 /* builtin.cpp */; }; - D05F596F1F041AE4003EE978 /* builtin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F592F1F041AE4003EE978 /* builtin.cpp */; }; - D05F59701F041AE4003EE978 /* builtin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F592F1F041AE4003EE978 /* builtin.cpp */; }; - D05F59711F041AE4003EE978 /* builtin_ulimit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59311F041AE4003EE978 /* builtin_ulimit.cpp */; }; - D05F59721F041AE4003EE978 /* builtin_ulimit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59311F041AE4003EE978 /* builtin_ulimit.cpp */; }; - D05F59731F041AE4003EE978 /* builtin_ulimit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59311F041AE4003EE978 /* builtin_ulimit.cpp */; }; - D05F59741F041AE4003EE978 /* builtin_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59331F041AE4003EE978 /* builtin_test.cpp */; }; - D05F59751F041AE4003EE978 /* builtin_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59331F041AE4003EE978 /* builtin_test.cpp */; }; - D05F59761F041AE4003EE978 /* builtin_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59331F041AE4003EE978 /* builtin_test.cpp */; }; - D05F59771F041AE4003EE978 /* builtin_string.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59351F041AE4003EE978 /* builtin_string.cpp */; }; - D05F59781F041AE4003EE978 /* builtin_string.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59351F041AE4003EE978 /* builtin_string.cpp */; }; - D05F59791F041AE4003EE978 /* builtin_string.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59351F041AE4003EE978 /* builtin_string.cpp */; }; - D05F597A1F041AE4003EE978 /* builtin_status.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59371F041AE4003EE978 /* builtin_status.cpp */; }; - D05F597B1F041AE4003EE978 /* builtin_status.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59371F041AE4003EE978 /* builtin_status.cpp */; }; - D05F597C1F041AE4003EE978 /* builtin_status.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59371F041AE4003EE978 /* builtin_status.cpp */; }; - D05F597D1F041AE4003EE978 /* builtin_source.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59391F041AE4003EE978 /* builtin_source.cpp */; }; - D05F597E1F041AE4003EE978 /* builtin_source.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59391F041AE4003EE978 /* builtin_source.cpp */; }; - D05F597F1F041AE4003EE978 /* builtin_source.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59391F041AE4003EE978 /* builtin_source.cpp */; }; - D05F59801F041AE4003EE978 /* builtin_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593B1F041AE4003EE978 /* builtin_set.cpp */; }; - D05F59811F041AE4003EE978 /* builtin_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593B1F041AE4003EE978 /* builtin_set.cpp */; }; - D05F59821F041AE4003EE978 /* builtin_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593B1F041AE4003EE978 /* builtin_set.cpp */; }; - D05F59831F041AE4003EE978 /* builtin_set_color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593D1F041AE4003EE978 /* builtin_set_color.cpp */; }; - D05F59841F041AE4003EE978 /* builtin_set_color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593D1F041AE4003EE978 /* builtin_set_color.cpp */; }; - D05F59851F041AE4003EE978 /* builtin_set_color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593D1F041AE4003EE978 /* builtin_set_color.cpp */; }; - D05F59861F041AE4003EE978 /* builtin_return.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593F1F041AE4003EE978 /* builtin_return.cpp */; }; - D05F59871F041AE4003EE978 /* builtin_return.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593F1F041AE4003EE978 /* builtin_return.cpp */; }; - D05F59881F041AE4003EE978 /* builtin_return.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F593F1F041AE4003EE978 /* builtin_return.cpp */; }; - D05F59891F041AE4003EE978 /* builtin_realpath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59411F041AE4003EE978 /* builtin_realpath.cpp */; }; - D05F598A1F041AE4003EE978 /* builtin_realpath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59411F041AE4003EE978 /* builtin_realpath.cpp */; }; - D05F598B1F041AE4003EE978 /* builtin_realpath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59411F041AE4003EE978 /* builtin_realpath.cpp */; }; - D05F598C1F041AE4003EE978 /* builtin_read.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59431F041AE4003EE978 /* builtin_read.cpp */; }; - D05F598D1F041AE4003EE978 /* builtin_read.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59431F041AE4003EE978 /* builtin_read.cpp */; }; - D05F598E1F041AE4003EE978 /* builtin_read.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59431F041AE4003EE978 /* builtin_read.cpp */; }; - D05F598F1F041AE4003EE978 /* builtin_random.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59451F041AE4003EE978 /* builtin_random.cpp */; }; - D05F59901F041AE4003EE978 /* builtin_random.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59451F041AE4003EE978 /* builtin_random.cpp */; }; - D05F59911F041AE4003EE978 /* builtin_random.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59451F041AE4003EE978 /* builtin_random.cpp */; }; - D05F59921F041AE4003EE978 /* builtin_pwd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59471F041AE4003EE978 /* builtin_pwd.cpp */; }; - D05F59931F041AE4003EE978 /* builtin_pwd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59471F041AE4003EE978 /* builtin_pwd.cpp */; }; - D05F59941F041AE4003EE978 /* builtin_pwd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59471F041AE4003EE978 /* builtin_pwd.cpp */; }; - D05F59951F041AE4003EE978 /* builtin_printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59491F041AE4003EE978 /* builtin_printf.cpp */; }; - D05F59961F041AE4003EE978 /* builtin_printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59491F041AE4003EE978 /* builtin_printf.cpp */; }; - D05F59971F041AE4003EE978 /* builtin_printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59491F041AE4003EE978 /* builtin_printf.cpp */; }; - D05F59981F041AE4003EE978 /* builtin_jobs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594B1F041AE4003EE978 /* builtin_jobs.cpp */; }; - D05F59991F041AE4003EE978 /* builtin_jobs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594B1F041AE4003EE978 /* builtin_jobs.cpp */; }; - D05F599A1F041AE4003EE978 /* builtin_jobs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594B1F041AE4003EE978 /* builtin_jobs.cpp */; }; - D05F599B1F041AE4003EE978 /* builtin_history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594D1F041AE4003EE978 /* builtin_history.cpp */; }; - D05F599C1F041AE4003EE978 /* builtin_history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594D1F041AE4003EE978 /* builtin_history.cpp */; }; - D05F599D1F041AE4003EE978 /* builtin_history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594D1F041AE4003EE978 /* builtin_history.cpp */; }; - D05F599E1F041AE4003EE978 /* builtin_functions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594F1F041AE4003EE978 /* builtin_functions.cpp */; }; - D05F599F1F041AE4003EE978 /* builtin_functions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594F1F041AE4003EE978 /* builtin_functions.cpp */; }; - D05F59A01F041AE4003EE978 /* builtin_functions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F594F1F041AE4003EE978 /* builtin_functions.cpp */; }; - D05F59A11F041AE4003EE978 /* builtin_function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59511F041AE4003EE978 /* builtin_function.cpp */; }; - D05F59A21F041AE4003EE978 /* builtin_function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59511F041AE4003EE978 /* builtin_function.cpp */; }; - D05F59A31F041AE4003EE978 /* builtin_function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59511F041AE4003EE978 /* builtin_function.cpp */; }; - D05F59A41F041AE4003EE978 /* builtin_fg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59531F041AE4003EE978 /* builtin_fg.cpp */; }; - D05F59A51F041AE4003EE978 /* builtin_fg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59531F041AE4003EE978 /* builtin_fg.cpp */; }; - D05F59A61F041AE4003EE978 /* builtin_fg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59531F041AE4003EE978 /* builtin_fg.cpp */; }; - D05F59A71F041AE4003EE978 /* builtin_exit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59551F041AE4003EE978 /* builtin_exit.cpp */; }; - D05F59A81F041AE4003EE978 /* builtin_exit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59551F041AE4003EE978 /* builtin_exit.cpp */; }; - D05F59A91F041AE4003EE978 /* builtin_exit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59551F041AE4003EE978 /* builtin_exit.cpp */; }; - D05F59AA1F041AE4003EE978 /* builtin_emit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59571F041AE4003EE978 /* builtin_emit.cpp */; }; - D05F59AB1F041AE4003EE978 /* builtin_emit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59571F041AE4003EE978 /* builtin_emit.cpp */; }; - D05F59AC1F041AE4003EE978 /* builtin_emit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59571F041AE4003EE978 /* builtin_emit.cpp */; }; - D05F59AD1F041AE4003EE978 /* builtin_echo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59591F041AE4003EE978 /* builtin_echo.cpp */; }; - D05F59AE1F041AE4003EE978 /* builtin_echo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59591F041AE4003EE978 /* builtin_echo.cpp */; }; - D05F59AF1F041AE4003EE978 /* builtin_echo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59591F041AE4003EE978 /* builtin_echo.cpp */; }; - D05F59B01F041AE4003EE978 /* builtin_disown.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595B1F041AE4003EE978 /* builtin_disown.cpp */; }; - D05F59B11F041AE4003EE978 /* builtin_disown.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595B1F041AE4003EE978 /* builtin_disown.cpp */; }; - D05F59B21F041AE4003EE978 /* builtin_disown.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595B1F041AE4003EE978 /* builtin_disown.cpp */; }; - D05F59B31F041AE4003EE978 /* builtin_contains.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595D1F041AE4003EE978 /* builtin_contains.cpp */; }; - D05F59B41F041AE4003EE978 /* builtin_contains.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595D1F041AE4003EE978 /* builtin_contains.cpp */; }; - D05F59B51F041AE4003EE978 /* builtin_contains.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595D1F041AE4003EE978 /* builtin_contains.cpp */; }; - D05F59B61F041AE4003EE978 /* builtin_complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595F1F041AE4003EE978 /* builtin_complete.cpp */; }; - D05F59B71F041AE4003EE978 /* builtin_complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595F1F041AE4003EE978 /* builtin_complete.cpp */; }; - D05F59B81F041AE4003EE978 /* builtin_complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F595F1F041AE4003EE978 /* builtin_complete.cpp */; }; - D05F59B91F041AE4003EE978 /* builtin_commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59611F041AE4003EE978 /* builtin_commandline.cpp */; }; - D05F59BA1F041AE4003EE978 /* builtin_commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59611F041AE4003EE978 /* builtin_commandline.cpp */; }; - D05F59BB1F041AE4003EE978 /* builtin_commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59611F041AE4003EE978 /* builtin_commandline.cpp */; }; - D05F59BC1F041AE4003EE978 /* builtin_command.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59631F041AE4003EE978 /* builtin_command.cpp */; }; - D05F59BD1F041AE4003EE978 /* builtin_command.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59631F041AE4003EE978 /* builtin_command.cpp */; }; - D05F59BE1F041AE4003EE978 /* builtin_command.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59631F041AE4003EE978 /* builtin_command.cpp */; }; - D05F59BF1F041AE4003EE978 /* builtin_cd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59651F041AE4003EE978 /* builtin_cd.cpp */; }; - D05F59C01F041AE4003EE978 /* builtin_cd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59651F041AE4003EE978 /* builtin_cd.cpp */; }; - D05F59C11F041AE4003EE978 /* builtin_cd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59651F041AE4003EE978 /* builtin_cd.cpp */; }; - D05F59C21F041AE4003EE978 /* builtin_builtin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59671F041AE4003EE978 /* builtin_builtin.cpp */; }; - D05F59C31F041AE4003EE978 /* builtin_builtin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59671F041AE4003EE978 /* builtin_builtin.cpp */; }; - D05F59C41F041AE4003EE978 /* builtin_builtin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59671F041AE4003EE978 /* builtin_builtin.cpp */; }; - D05F59C51F041AE4003EE978 /* builtin_block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59691F041AE4003EE978 /* builtin_block.cpp */; }; - D05F59C61F041AE4003EE978 /* builtin_block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59691F041AE4003EE978 /* builtin_block.cpp */; }; - D05F59C71F041AE4003EE978 /* builtin_block.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F59691F041AE4003EE978 /* builtin_block.cpp */; }; - D05F59C81F041AE4003EE978 /* builtin_bind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F596B1F041AE4003EE978 /* builtin_bind.cpp */; }; - D05F59C91F041AE4003EE978 /* builtin_bind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F596B1F041AE4003EE978 /* builtin_bind.cpp */; }; - D05F59CA1F041AE4003EE978 /* builtin_bind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F596B1F041AE4003EE978 /* builtin_bind.cpp */; }; - D05F59CB1F041AE4003EE978 /* builtin_bg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F596D1F041AE4003EE978 /* builtin_bg.cpp */; }; - D05F59CC1F041AE4003EE978 /* builtin_bg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F596D1F041AE4003EE978 /* builtin_bg.cpp */; }; - D05F59CD1F041AE4003EE978 /* builtin_bg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F596D1F041AE4003EE978 /* builtin_bg.cpp */; }; - D06821601F5148AE00040321 /* builtin_math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B51D811F5018B30051C61A /* builtin_math.cpp */; }; - D07B247315BCC15700D4ADB4 /* add-shell in Resources */ = {isa = PBXBuildFile; fileRef = D07B247215BCC15700D4ADB4 /* add-shell */; }; - D07B247615BCC4BE00D4ADB4 /* install.sh in Resources */ = {isa = PBXBuildFile; fileRef = D07B247515BCC4BE00D4ADB4 /* install.sh */; }; - D07D266A15E33B86009E43F6 /* config.fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0C4FD9415A7D7EE00212EF1 /* config.fish */; }; - D07D266C15E33B86009E43F6 /* completions in Copy Files */ = {isa = PBXBuildFile; fileRef = D025C02715D1FEA100B9DB63 /* completions */; }; - D07D266D15E33B86009E43F6 /* functions in Copy Files */ = {isa = PBXBuildFile; fileRef = D025C02815D1FEA100B9DB63 /* functions */; }; - D07D266E15E33B86009E43F6 /* tools in Copy Files */ = {isa = PBXBuildFile; fileRef = D025C02915D1FEA100B9DB63 /* tools */; }; - D07D267215E34171009E43F6 /* config.fish in Copy Files */ = {isa = PBXBuildFile; fileRef = D0CBD580159EE48F0024809C /* config.fish */; }; - D08500A41F63A65800C0E329 /* builtin_argparse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB0F034A1F156FE3001827D3 /* builtin_argparse.cpp */; }; - D08500A51F63A6EB00C0E329 /* builtin_math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B51D811F5018B30051C61A /* builtin_math.cpp */; }; - D0879AC816BF9AAB00E98E56 /* fish_term_icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = D0879AC616BF9A1A00E98E56 /* fish_term_icon.icns */; }; - D0A564FE168D23D800AF6161 /* man in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0A564F1168D0BAB00AF6161 /* man */; }; - D0A56501168D258300AF6161 /* man in Copy Files */ = {isa = PBXBuildFile; fileRef = D0A564F1168D0BAB00AF6161 /* man */; }; - D0B51D831F5018E80051C61A /* builtin_math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B51D811F5018B30051C61A /* builtin_math.cpp */; }; - D0B51D841F5018F30051C61A /* builtin_math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B51D811F5018B30051C61A /* builtin_math.cpp */; }; - D0C52F371765284C00BFAB82 /* parse_tree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C52F351765284C00BFAB82 /* parse_tree.cpp */; }; - D0C9733818DE5449002D7C81 /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C9733718DE5449002D7C81 /* utf8.cpp */; }; - D0CBD587159EF0E10024809C /* launch_fish.scpt in Resources */ = {isa = PBXBuildFile; fileRef = D0CBD586159EF0E10024809C /* launch_fish.scpt */; }; - D0D02A67159837AD008E62BD /* complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853713B3ACEE0099B651 /* complete.cpp */; }; - D0D02A69159837B2008E62BD /* env.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853A13B3ACEE0099B651 /* env.cpp */; }; - D0D02A6A1598381A008E62BD /* exec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853C13B3ACEE0099B651 /* exec.cpp */; }; - D0D02A6B1598381F008E62BD /* expand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853D13B3ACEE0099B651 /* expand.cpp */; }; - D0D02A6C15983829008E62BD /* highlight.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854713B3ACEE0099B651 /* highlight.cpp */; }; - D0D02A6D1598382C008E62BD /* history.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854813B3ACEE0099B651 /* history.cpp */; }; - D0D02A6E15983838008E62BD /* kill.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854F13B3ACEE0099B651 /* kill.cpp */; }; - D0D02A6F1598383E008E62BD /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855413B3ACEE0099B651 /* parser.cpp */; }; - D0D02A7015983842008E62BD /* proc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855713B3ACEE0099B651 /* proc.cpp */; }; - D0D02A7115983848008E62BD /* reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855813B3ACEE0099B651 /* reader.cpp */; }; - D0D02A721598384C008E62BD /* sanity.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855913B3ACEE0099B651 /* sanity.cpp */; }; - D0D02A7315983852008E62BD /* tokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855D13B3ACEE0099B651 /* tokenizer.cpp */; }; - D0D02A7415983857008E62BD /* wildcard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0856013B3ACEE0099B651 /* wildcard.cpp */; }; - D0D02A751598385E008E62BD /* wgetopt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855F13B3ACEE0099B651 /* wgetopt.cpp */; }; - D0D02A7615983869008E62BD /* wutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0856113B3ACEE0099B651 /* wutil.cpp */; }; - D0D02A7715983875008E62BD /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854A13B3ACEE0099B651 /* input.cpp */; }; - D0D02A781598387E008E62BD /* output.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855113B3ACEE0099B651 /* output.cpp */; }; - D0D02A7915983888008E62BD /* intern.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854B13B3ACEE0099B651 /* intern.cpp */; }; - D0D02A7B15983928008E62BD /* env_universal_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853813B3ACEE0099B651 /* env_universal_common.cpp */; }; - D0D02A7C159839D5008E62BD /* autoload.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */; }; - D0D02A7E159839D5008E62BD /* color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B6B0FE14E88BA400AD6C10 /* color.cpp */; }; - D0D02A7F159839D5008E62BD /* common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853613B3ACEE0099B651 /* common.cpp */; }; - D0D02A80159839D5008E62BD /* event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853B13B3ACEE0099B651 /* event.cpp */; }; - D0D02A81159839D5008E62BD /* input_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854913B3ACEE0099B651 /* input_common.cpp */; }; - D0D02A82159839D5008E62BD /* io.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854C13B3ACEE0099B651 /* io.cpp */; }; - D0D02A83159839D5008E62BD /* iothread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854D13B3ACEE0099B651 /* iothread.cpp */; }; - D0D02A84159839D5008E62BD /* parse_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855213B3ACEE0099B651 /* parse_util.cpp */; }; - D0D02A85159839D5008E62BD /* path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855513B3ACEE0099B651 /* path.cpp */; }; - D0D02A86159839D5008E62BD /* postfork.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D09B1C1914FC7B5B00F91077 /* postfork.cpp */; }; - D0D02A87159839D5008E62BD /* screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855A13B3ACEE0099B651 /* screen.cpp */; }; - D0D02A88159839D5008E62BD /* signal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855C13B3ACEE0099B651 /* signal.cpp */; }; - D0D02A89159839DF008E62BD /* fish.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854213B3ACEE0099B651 /* fish.cpp */; }; - D0D02A8D15983CFA008E62BD /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D02A8C15983CFA008E62BD /* libncurses.dylib */; }; - D0D02A8F15983D8F008E62BD /* parser_keywords.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855313B3ACEE0099B651 /* parser_keywords.cpp */; }; - D0D02AD615986492008E62BD /* fish_indent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853F13B3ACEE0099B651 /* fish_indent.cpp */; }; - D0D02AD715986498008E62BD /* print_help.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855613B3ACEE0099B651 /* print_help.cpp */; }; - D0D02AD81598649E008E62BD /* common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853613B3ACEE0099B651 /* common.cpp */; }; - D0D02AD9159864A6008E62BD /* parser_keywords.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855313B3ACEE0099B651 /* parser_keywords.cpp */; }; - D0D02ADA159864AB008E62BD /* wutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0856113B3ACEE0099B651 /* wutil.cpp */; }; - D0D02ADB159864C2008E62BD /* tokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855D13B3ACEE0099B651 /* tokenizer.cpp */; }; - D0D02ADC159864D5008E62BD /* libncurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D0D02A8C15983CFA008E62BD /* libncurses.dylib */; }; - D0D2694915983772005D9B9C /* function.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0854413B3ACEE0099B651 /* function.cpp */; }; - D0F019F115A977140034B3B1 /* fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0D2693C159835CA005D9B9C /* fish */; }; - D0F019F315A977290034B3B1 /* fish_indent in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0D02AD01598642A008E62BD /* fish_indent */; }; - D0F019F815A977AB0034B3B1 /* config.fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0CBD580159EE48F0024809C /* config.fish */; }; - D0F019FD15A977CA0034B3B1 /* config.fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0C4FD9415A7D7EE00212EF1 /* config.fish */; }; - D0F01A0315A978910034B3B1 /* osx_fish_launcher.m in Sources */ = {isa = PBXBuildFile; fileRef = D0D02AFA159871B2008E62BD /* osx_fish_launcher.m */; }; - D0F01A0515A978A10034B3B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0CBD583159EEE010024809C /* Foundation.framework */; }; - D0F5B46519CFCDE80090665E /* wcstringutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0F5B46319CFCDE80090665E /* wcstringutil.cpp */; }; - D0F5B46619CFCEBC0090665E /* wcstringutil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0F5B46319CFCDE80090665E /* wcstringutil.cpp */; }; - D0FE8EE8179FB760008C9F21 /* parse_productions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0FE8EE7179FB75F008C9F21 /* parse_productions.cpp */; }; -/* End PBXBuildFile section */ - -/* Begin PBXBuildRule section */ - D04F7FF81BA4E84B00B0F227 /* PBXBuildRule */ = { - isa = PBXBuildRule; - compilerSpec = com.apple.compilers.gcc; - filePatterns = "*.dist"; - fileType = pattern.proxy; - isEditable = 1; - outputFiles = ( - ); - script = "# \n"; - }; -/* End PBXBuildRule section */ - -/* Begin PBXContainerItemProxy section */ - 9C7A55321DCD71330049C25D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D008D0C41BC58F8800841177; - remoteInfo = "generate-version-header"; - }; - 9C7A55341DCD71330049C25D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D04F7FCF1BA4E29300B0F227; - remoteInfo = libpcre2.a; - }; - 9C7A557F1DCD73930049C25D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 9C7A55301DCD71330049C25D; - remoteInfo = fish_key_reader; - }; - D008D0CA1BC58FDD00841177 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D008D0C41BC58F8800841177; - remoteInfo = "generate-version-header"; - }; - D008D0CC1BC58FE100841177 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D008D0C41BC58F8800841177; - remoteInfo = "generate-version-header"; - }; - D008D0CE1BC58FE500841177 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D008D0C41BC58F8800841177; - remoteInfo = "generate-version-header"; - }; - D031890715E36CC000D9CC39 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D0F019EC15A976F30034B3B1; - remoteInfo = base; - }; - D04F7FEA1BA4E3DB00B0F227 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D04F7FCF1BA4E29300B0F227; - remoteInfo = libpcre2.a; - }; - D04F7FEC1BA4E3DF00B0F227 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D04F7FCF1BA4E29300B0F227; - remoteInfo = libpcre2.a; - }; - D07D265815E33B86009E43F6 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D0D2693B159835CA005D9B9C; - remoteInfo = fish_shell; - }; - D07D265E15E33B86009E43F6 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D0D02ACF1598642A008E62BD; - remoteInfo = fish_indent; - }; - D0A564EE168D09C000AF6161 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D0A564E6168CFDD800AF6161; - remoteInfo = man_pages; - }; - D0A564FF168D257900AF6161 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D0A564E6168CFDD800AF6161; - remoteInfo = man_pages; - }; - D0F01A1215AA36280034B3B1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D0D2693B159835CA005D9B9C; - remoteInfo = fish_shell; - }; - D0F01A1615AA36300034B3B1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D0D02ACF1598642A008E62BD; - remoteInfo = fish_indent; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - D01A2C9B16964C8200767098 /* Copy Files */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = base/share/man; - dstSubfolderSpec = 1; - files = ( - D01A2D24169B736200767098 /* man1 in Copy Files */, - ); - name = "Copy Files"; - runOnlyForDeploymentPostprocessing = 0; - }; - D01A2CA716965ADD00767098 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = "${INSTALL_PATH}/share/man"; - dstSubfolderSpec = 0; - files = ( - D01A2D25169B737700767098 /* man1 in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 1; - }; - D033780F15DC6D2A00A634BA /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = base/share/fish; - dstSubfolderSpec = 1; - files = ( - D033781115DC6D4C00A634BA /* completions in CopyFiles */, - D033781215DC6D5200A634BA /* functions in CopyFiles */, - D033781315DC6D5400A634BA /* tools in CopyFiles */, - D0A564FE168D23D800AF6161 /* man in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D07D266915E33B86009E43F6 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = "${INSTALL_PATH}/etc/fish"; - dstSubfolderSpec = 0; - files = ( - D07D266A15E33B86009E43F6 /* config.fish in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 1; - }; - D07D266B15E33B86009E43F6 /* Copy Files */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; - dstPath = "${INSTALL_PATH}/share/fish"; - dstSubfolderSpec = 0; - files = ( - 9CC8D8C51F7AF0D40095062A /* lynx.lss in Copy Files */, - D07D267215E34171009E43F6 /* config.fish in Copy Files */, - D07D266C15E33B86009E43F6 /* completions in Copy Files */, - D07D266D15E33B86009E43F6 /* functions in Copy Files */, - D07D266E15E33B86009E43F6 /* tools in Copy Files */, - D0A56501168D258300AF6161 /* man in Copy Files */, - ); - name = "Copy Files"; - runOnlyForDeploymentPostprocessing = 1; - }; - D0F019F015A977010034B3B1 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = base/bin; - dstSubfolderSpec = 1; - files = ( - 9C7A55811DCD739C0049C25D /* fish_key_reader in CopyFiles */, - D0F019F115A977140034B3B1 /* fish in CopyFiles */, - D0F019F315A977290034B3B1 /* fish_indent in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0F019F715A977A00034B3B1 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = base/share/fish; - dstSubfolderSpec = 1; - files = ( - D0F019F815A977AB0034B3B1 /* config.fish in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0F019FC15A977B40034B3B1 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = base/etc/fish; - dstSubfolderSpec = 1; - files = ( - D0F019FD15A977CA0034B3B1 /* config.fish in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 4E142D731B56B5D7008783C8 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config.h; path = ../osx/config.h; sourceTree = ""; }; - 4F2D55CD2013ECDD00822920 /* tnode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tnode.h; sourceTree = ""; }; - 4F2D55CE2013ECDD00822920 /* tnode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tnode.cpp; sourceTree = ""; }; - 63A2C0E81CC5F9FB00973404 /* pcre2_find_bracket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_find_bracket.c; sourceTree = ""; }; - 9C7A55721DCD71330049C25D /* fish_key_reader */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fish_key_reader; sourceTree = BUILT_PRODUCTS_DIR; }; - 9C7A557C1DCD717C0049C25D /* fish_key_reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fish_key_reader.cpp; sourceTree = ""; }; - 9CC8D8C41F7AF0610095062A /* lynx.lss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = lynx.lss; path = share/lynx.lss; sourceTree = ""; }; - CB0F034A1F156FE3001827D3 /* builtin_argparse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_argparse.cpp; sourceTree = ""; }; - CB0F034B1F156FE3001827D3 /* builtin_argparse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_argparse.h; 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; }; - D025C02715D1FEA100B9DB63 /* completions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = completions; path = share/completions; sourceTree = ""; }; - D025C02815D1FEA100B9DB63 /* functions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = functions; path = share/functions; sourceTree = ""; }; - D025C02915D1FEA100B9DB63 /* tools */ = {isa = PBXFileReference; lastKnownFileType = folder; name = tools; path = share/tools; sourceTree = ""; }; - D02960E51FBD726100CA3985 /* builtin_wait.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_wait.cpp; sourceTree = ""; }; - D0301C1D2002B90500B1F463 /* parse_grammar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = parse_grammar.h; sourceTree = ""; }; - D031890915E36D9800D9CC39 /* base */ = {isa = PBXFileReference; lastKnownFileType = text; path = base; sourceTree = BUILT_PRODUCTS_DIR; }; - D03238891849D1980032CF2C /* pager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pager.cpp; sourceTree = ""; }; - 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 = ""; }; - D043012D1F5350E400942A50 /* maybe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = maybe.h; sourceTree = ""; }; - D046A0F92070245000C8DFF7 /* tinyexpr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tinyexpr.c; sourceTree = ""; }; - D04C863820B3D83900C675A6 /* future_feature_flags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = future_feature_flags.cpp; sourceTree = ""; }; - 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; 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 = ""; }; - D05F592E1F041AE4003EE978 /* builtin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin.h; sourceTree = ""; }; - D05F592F1F041AE4003EE978 /* builtin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin.cpp; sourceTree = ""; }; - D05F59301F041AE4003EE978 /* builtin_ulimit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_ulimit.h; sourceTree = ""; }; - D05F59311F041AE4003EE978 /* builtin_ulimit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_ulimit.cpp; sourceTree = ""; }; - D05F59321F041AE4003EE978 /* builtin_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_test.h; sourceTree = ""; }; - D05F59331F041AE4003EE978 /* builtin_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_test.cpp; sourceTree = ""; }; - D05F59341F041AE4003EE978 /* builtin_string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_string.h; sourceTree = ""; }; - D05F59351F041AE4003EE978 /* builtin_string.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_string.cpp; sourceTree = ""; }; - D05F59361F041AE4003EE978 /* builtin_status.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_status.h; sourceTree = ""; }; - D05F59371F041AE4003EE978 /* builtin_status.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_status.cpp; sourceTree = ""; }; - D05F59381F041AE4003EE978 /* builtin_source.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_source.h; sourceTree = ""; }; - D05F59391F041AE4003EE978 /* builtin_source.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_source.cpp; sourceTree = ""; }; - D05F593A1F041AE4003EE978 /* builtin_set.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_set.h; sourceTree = ""; }; - D05F593B1F041AE4003EE978 /* builtin_set.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_set.cpp; sourceTree = ""; }; - D05F593C1F041AE4003EE978 /* builtin_set_color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_set_color.h; sourceTree = ""; }; - D05F593D1F041AE4003EE978 /* builtin_set_color.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_set_color.cpp; sourceTree = ""; }; - D05F593E1F041AE4003EE978 /* builtin_return.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_return.h; sourceTree = ""; }; - D05F593F1F041AE4003EE978 /* builtin_return.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_return.cpp; sourceTree = ""; }; - D05F59401F041AE4003EE978 /* builtin_realpath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_realpath.h; sourceTree = ""; }; - D05F59411F041AE4003EE978 /* builtin_realpath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_realpath.cpp; sourceTree = ""; }; - D05F59421F041AE4003EE978 /* builtin_read.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_read.h; sourceTree = ""; }; - D05F59431F041AE4003EE978 /* builtin_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_read.cpp; sourceTree = ""; }; - D05F59441F041AE4003EE978 /* builtin_random.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_random.h; sourceTree = ""; }; - D05F59451F041AE4003EE978 /* builtin_random.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_random.cpp; sourceTree = ""; }; - D05F59461F041AE4003EE978 /* builtin_pwd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_pwd.h; sourceTree = ""; }; - D05F59471F041AE4003EE978 /* builtin_pwd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_pwd.cpp; sourceTree = ""; }; - D05F59481F041AE4003EE978 /* builtin_printf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_printf.h; sourceTree = ""; }; - D05F59491F041AE4003EE978 /* builtin_printf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_printf.cpp; sourceTree = ""; }; - D05F594A1F041AE4003EE978 /* builtin_jobs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_jobs.h; sourceTree = ""; }; - D05F594B1F041AE4003EE978 /* builtin_jobs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_jobs.cpp; sourceTree = ""; }; - D05F594C1F041AE4003EE978 /* builtin_history.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_history.h; sourceTree = ""; }; - D05F594D1F041AE4003EE978 /* builtin_history.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_history.cpp; sourceTree = ""; }; - D05F594E1F041AE4003EE978 /* builtin_functions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_functions.h; sourceTree = ""; }; - D05F594F1F041AE4003EE978 /* builtin_functions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_functions.cpp; sourceTree = ""; }; - D05F59501F041AE4003EE978 /* builtin_function.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_function.h; sourceTree = ""; }; - D05F59511F041AE4003EE978 /* builtin_function.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_function.cpp; sourceTree = ""; }; - D05F59521F041AE4003EE978 /* builtin_fg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_fg.h; sourceTree = ""; }; - D05F59531F041AE4003EE978 /* builtin_fg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_fg.cpp; sourceTree = ""; }; - D05F59541F041AE4003EE978 /* builtin_exit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_exit.h; sourceTree = ""; }; - D05F59551F041AE4003EE978 /* builtin_exit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_exit.cpp; sourceTree = ""; }; - D05F59561F041AE4003EE978 /* builtin_emit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_emit.h; sourceTree = ""; }; - D05F59571F041AE4003EE978 /* builtin_emit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_emit.cpp; sourceTree = ""; }; - D05F59581F041AE4003EE978 /* builtin_echo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_echo.h; sourceTree = ""; }; - D05F59591F041AE4003EE978 /* builtin_echo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_echo.cpp; sourceTree = ""; }; - D05F595A1F041AE4003EE978 /* builtin_disown.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_disown.h; sourceTree = ""; }; - D05F595B1F041AE4003EE978 /* builtin_disown.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_disown.cpp; sourceTree = ""; }; - D05F595C1F041AE4003EE978 /* builtin_contains.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_contains.h; sourceTree = ""; }; - D05F595D1F041AE4003EE978 /* builtin_contains.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_contains.cpp; sourceTree = ""; }; - D05F595E1F041AE4003EE978 /* builtin_complete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_complete.h; sourceTree = ""; }; - D05F595F1F041AE4003EE978 /* builtin_complete.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_complete.cpp; sourceTree = ""; }; - D05F59601F041AE4003EE978 /* builtin_commandline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_commandline.h; sourceTree = ""; }; - D05F59611F041AE4003EE978 /* builtin_commandline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_commandline.cpp; sourceTree = ""; }; - D05F59621F041AE4003EE978 /* builtin_command.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_command.h; sourceTree = ""; }; - D05F59631F041AE4003EE978 /* builtin_command.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_command.cpp; sourceTree = ""; }; - D05F59641F041AE4003EE978 /* builtin_cd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_cd.h; sourceTree = ""; }; - D05F59651F041AE4003EE978 /* builtin_cd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_cd.cpp; sourceTree = ""; }; - D05F59661F041AE4003EE978 /* builtin_builtin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_builtin.h; sourceTree = ""; }; - D05F59671F041AE4003EE978 /* builtin_builtin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_builtin.cpp; sourceTree = ""; }; - D05F59681F041AE4003EE978 /* builtin_block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_block.h; sourceTree = ""; }; - D05F59691F041AE4003EE978 /* builtin_block.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_block.cpp; sourceTree = ""; }; - D05F596A1F041AE4003EE978 /* builtin_bind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_bind.h; sourceTree = ""; }; - D05F596B1F041AE4003EE978 /* builtin_bind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_bind.cpp; sourceTree = ""; }; - D05F596C1F041AE4003EE978 /* builtin_bg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_bg.h; sourceTree = ""; }; - D05F596D1F041AE4003EE978 /* builtin_bg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_bg.cpp; sourceTree = ""; }; - D07B247215BCC15700D4ADB4 /* add-shell */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = "add-shell"; path = "build_tools/osx_package_scripts/add-shell"; sourceTree = ""; }; - D07B247515BCC4BE00D4ADB4 /* install.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = install.sh; path = osx/install.sh; sourceTree = ""; }; - D0879AC616BF9A1A00E98E56 /* fish_term_icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = fish_term_icon.icns; path = osx/fish_term_icon.icns; sourceTree = ""; }; - D09B1C1914FC7B5B00F91077 /* postfork.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = postfork.cpp; sourceTree = ""; }; - D09B1C1A14FC7B5B00F91077 /* postfork.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = postfork.h; sourceTree = ""; }; - D0A0850413B3ACEE0099B651 /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; }; - D0A0850513B3ACEE0099B651 /* complete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = complete.h; sourceTree = ""; }; - D0A0850713B3ACEE0099B651 /* env_universal_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = env_universal_common.h; sourceTree = ""; }; - D0A0850913B3ACEE0099B651 /* env.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = env.h; sourceTree = ""; }; - D0A0850A13B3ACEE0099B651 /* event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = event.h; sourceTree = ""; }; - D0A0850B13B3ACEE0099B651 /* exec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exec.h; sourceTree = ""; }; - D0A0850C13B3ACEE0099B651 /* expand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = expand.h; sourceTree = ""; }; - D0A0850D13B3ACEE0099B651 /* fallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fallback.h; sourceTree = ""; }; - D0A0850E13B3ACEE0099B651 /* function.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = function.h; sourceTree = ""; }; - D0A0851113B3ACEE0099B651 /* highlight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = highlight.h; sourceTree = ""; }; - D0A0851213B3ACEE0099B651 /* history.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = history.h; sourceTree = ""; }; - D0A0851313B3ACEE0099B651 /* input_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input_common.h; sourceTree = ""; }; - D0A0851413B3ACEE0099B651 /* input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input.h; sourceTree = ""; }; - D0A0851513B3ACEE0099B651 /* intern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = intern.h; sourceTree = ""; }; - D0A0851613B3ACEE0099B651 /* io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io.h; sourceTree = ""; }; - D0A0851713B3ACEE0099B651 /* iothread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iothread.h; sourceTree = ""; }; - D0A0851813B3ACEE0099B651 /* kill.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kill.h; sourceTree = ""; }; - D0A0851A13B3ACEE0099B651 /* output.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = output.h; sourceTree = ""; }; - D0A0851B13B3ACEE0099B651 /* parse_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parse_util.h; sourceTree = ""; }; - D0A0851C13B3ACEE0099B651 /* parser_keywords.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parser_keywords.h; sourceTree = ""; }; - D0A0851D13B3ACEE0099B651 /* parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parser.h; sourceTree = ""; }; - D0A0851E13B3ACEE0099B651 /* path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = path.h; sourceTree = ""; }; - D0A0851F13B3ACEE0099B651 /* print_help.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = print_help.h; sourceTree = ""; }; - D0A0852013B3ACEE0099B651 /* proc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = proc.h; sourceTree = ""; }; - D0A0852113B3ACEE0099B651 /* reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = reader.h; sourceTree = ""; }; - D0A0852213B3ACEE0099B651 /* sanity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sanity.h; sourceTree = ""; }; - D0A0852313B3ACEE0099B651 /* screen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = screen.h; sourceTree = ""; }; - D0A0852413B3ACEE0099B651 /* signal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = signal.h; sourceTree = ""; }; - D0A0852513B3ACEE0099B651 /* tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tokenizer.h; sourceTree = ""; }; - D0A0852613B3ACEE0099B651 /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = util.h; sourceTree = ""; }; - D0A0852713B3ACEE0099B651 /* wgetopt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wgetopt.h; sourceTree = ""; }; - D0A0852813B3ACEE0099B651 /* wildcard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wildcard.h; sourceTree = ""; }; - D0A0852913B3ACEE0099B651 /* wutil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wutil.h; sourceTree = ""; }; - D0A0853613B3ACEE0099B651 /* common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = common.cpp; sourceTree = ""; }; - D0A0853713B3ACEE0099B651 /* complete.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = complete.cpp; sourceTree = ""; }; - D0A0853813B3ACEE0099B651 /* env_universal_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = env_universal_common.cpp; sourceTree = ""; }; - D0A0853A13B3ACEE0099B651 /* env.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = env.cpp; sourceTree = ""; }; - D0A0853B13B3ACEE0099B651 /* event.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = event.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; - D0A0853C13B3ACEE0099B651 /* exec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = exec.cpp; sourceTree = ""; }; - D0A0853D13B3ACEE0099B651 /* expand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = expand.cpp; sourceTree = ""; }; - D0A0853E13B3ACEE0099B651 /* fallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fallback.cpp; sourceTree = ""; }; - D0A0853F13B3ACEE0099B651 /* fish_indent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fish_indent.cpp; sourceTree = ""; }; - D0A0854113B3ACEE0099B651 /* fish_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fish_tests.cpp; sourceTree = ""; }; - D0A0854213B3ACEE0099B651 /* fish.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fish.cpp; sourceTree = ""; }; - D0A0854413B3ACEE0099B651 /* function.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = function.cpp; sourceTree = ""; }; - D0A0854713B3ACEE0099B651 /* highlight.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = highlight.cpp; sourceTree = ""; }; - D0A0854813B3ACEE0099B651 /* history.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = history.cpp; sourceTree = ""; }; - D0A0854913B3ACEE0099B651 /* input_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = input_common.cpp; sourceTree = ""; }; - D0A0854A13B3ACEE0099B651 /* input.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = input.cpp; sourceTree = ""; }; - 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 = ""; }; - 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 = ""; }; - D0A0855313B3ACEE0099B651 /* parser_keywords.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parser_keywords.cpp; sourceTree = ""; }; - D0A0855413B3ACEE0099B651 /* parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = parser.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; - D0A0855513B3ACEE0099B651 /* path.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = path.cpp; sourceTree = ""; }; - D0A0855613B3ACEE0099B651 /* print_help.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = print_help.cpp; sourceTree = ""; }; - D0A0855713B3ACEE0099B651 /* proc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = proc.cpp; sourceTree = ""; }; - D0A0855813B3ACEE0099B651 /* reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = reader.cpp; sourceTree = ""; }; - D0A0855913B3ACEE0099B651 /* sanity.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sanity.cpp; sourceTree = ""; }; - D0A0855A13B3ACEE0099B651 /* screen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = screen.cpp; sourceTree = ""; }; - D0A0855C13B3ACEE0099B651 /* signal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = signal.cpp; sourceTree = ""; }; - D0A0855D13B3ACEE0099B651 /* tokenizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tokenizer.cpp; sourceTree = ""; }; - D0A0855E13B3ACEE0099B651 /* util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = util.cpp; sourceTree = ""; }; - D0A0855F13B3ACEE0099B651 /* wgetopt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wgetopt.cpp; sourceTree = ""; }; - D0A0856013B3ACEE0099B651 /* wildcard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wildcard.cpp; sourceTree = ""; }; - D0A0856113B3ACEE0099B651 /* wutil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wutil.cpp; sourceTree = ""; }; - D0A564D2168CF34A00AF6161 /* doc_src */ = {isa = PBXFileReference; lastKnownFileType = folder; path = doc_src; sourceTree = ""; }; - D0A564F1168D0BAB00AF6161 /* man */ = {isa = PBXFileReference; lastKnownFileType = text; path = man; sourceTree = BUILT_PRODUCTS_DIR; }; - D0A564F2168D1F2000AF6161 /* build_documentation.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = build_documentation.sh; path = build_tools/build_documentation.sh; sourceTree = ""; }; - D0B51D811F5018B30051C61A /* builtin_math.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_math.cpp; sourceTree = ""; }; - D0B51D821F5018BE0051C61A /* builtin_math.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = builtin_math.h; sourceTree = ""; }; - D0B6B0FE14E88BA400AD6C10 /* color.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = color.cpp; sourceTree = ""; }; - D0B6B0FF14E88BA400AD6C10 /* color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = color.h; sourceTree = ""; }; - D0C4FD9415A7D7EE00212EF1 /* config.fish */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.fish; path = etc/config.fish; sourceTree = ""; }; - D0C52F351765284C00BFAB82 /* parse_tree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parse_tree.cpp; sourceTree = ""; }; - D0C52F361765284C00BFAB82 /* parse_tree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parse_tree.h; sourceTree = ""; }; - D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = autoload.cpp; sourceTree = ""; }; - D0C6FCCB14CFA4B7004CE8AD /* autoload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = autoload.h; sourceTree = ""; }; - D0C9733718DE5449002D7C81 /* utf8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = utf8.cpp; sourceTree = ""; }; - D0C9733A18DE5451002D7C81 /* utf8.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = utf8.h; sourceTree = ""; }; - D0CBD580159EE48F0024809C /* config.fish */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.fish; path = share/config.fish; sourceTree = ""; }; - D0CBD583159EEE010024809C /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - D0CBD586159EF0E10024809C /* launch_fish.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; name = launch_fish.scpt; path = osx/launch_fish.scpt; sourceTree = ""; }; - D0D02A8C15983CFA008E62BD /* libncurses.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libncurses.dylib; path = usr/lib/libncurses.dylib; sourceTree = SDKROOT; }; - D0D02A9A15985A75008E62BD /* fish.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = fish.app; sourceTree = BUILT_PRODUCTS_DIR; }; - D0D02AA915985C0C008E62BD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = osx/Info.plist; sourceTree = ""; }; - D0D02AD01598642A008E62BD /* fish_indent */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fish_indent; sourceTree = BUILT_PRODUCTS_DIR; }; - D0D02AFA159871B2008E62BD /* osx_fish_launcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = osx_fish_launcher.m; path = osx/osx_fish_launcher.m; sourceTree = ""; }; - D0D2693C159835CA005D9B9C /* fish */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fish; sourceTree = BUILT_PRODUCTS_DIR; }; - D0D9B2B318555D92001AE279 /* parse_constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = parse_constants.h; sourceTree = ""; }; - D0F5B46319CFCDE80090665E /* wcstringutil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wcstringutil.cpp; sourceTree = ""; }; - D0F5B46419CFCDE80090665E /* wcstringutil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wcstringutil.h; sourceTree = ""; }; - D0FE8EE6179CA8A5008C9F21 /* parse_productions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parse_productions.h; sourceTree = ""; }; - D0FE8EE7179FB75F008C9F21 /* parse_productions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = parse_productions.cpp; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 9C7A556B1DCD71330049C25D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 9C7A556C1DCD71330049C25D /* libncurses.dylib in Frameworks */, - 9C7A556D1DCD71330049C25D /* libpcre2.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D007693C1990137800CA4627 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D007693D1990137800CA4627 /* libncurses.dylib in Frameworks */, - D04F7FFA1BA4E9A400B0F227 /* libpcre2.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04F7FCD1BA4E29300B0F227 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0D02ACD1598642A008E62BD /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D0D02ADC159864D5008E62BD /* libncurses.dylib in Frameworks */, - D04F7FF21BA4E68A00B0F227 /* libpcre2.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0D26939159835CA005D9B9C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D0D02A8D15983CFA008E62BD /* libncurses.dylib in Frameworks */, - D04F7FF11BA4E68200B0F227 /* libpcre2.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0F01A0415A9789C0034B3B1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - D0F01A0515A978A10034B3B1 /* Foundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - D01A2C9C16964CF600767098 /* pages_for_manpath */ = { - isa = PBXGroup; - children = ( - D01A2D23169B730A00767098 /* man1 */, - ); - name = pages_for_manpath; - sourceTree = ""; - }; - D031890A15E36DB500D9CC39 /* Other Build Products */ = { - isa = PBXGroup; - children = ( - D031890915E36D9800D9CC39 /* base */, - ); - name = "Other Build Products"; - sourceTree = ""; - }; - D04F7F8B1BA4DC7600B0F227 /* pcre */ = { - isa = PBXGroup; - children = ( - 63A2C0E81CC5F9FB00973404 /* pcre2_find_bracket.c */, - D04F7FF31BA4E6F300B0F227 /* pcre2_auto_possess.c */, - D04F7F8D1BA4DCD900B0F227 /* pcre2_compile.c */, - D04F7F901BA4DCE900B0F227 /* pcre2_config.c */, - D04F7F931BA4DCFA00B0F227 /* pcre2_context.c */, - D04F7F961BA4DD1100B0F227 /* pcre2_dfa_match.c */, - D04F7F991BA4DD2000B0F227 /* pcre2_error.c */, - D04F7FC91BA4DE3500B0F227 /* pcre2_jit_compile.c */, - D04F7F9C1BA4DD4A00B0F227 /* pcre2_maketables.c */, - D04F7F9F1BA4DD5900B0F227 /* pcre2_match.c */, - D04F7FA21BA4DD6900B0F227 /* pcre2_match_data.c */, - D04F7FA51BA4DD7300B0F227 /* pcre2_newline.c */, - D04F7FA81BA4DD8400B0F227 /* pcre2_ord2utf.c */, - D04F7FAB1BA4DDA500B0F227 /* pcre2_pattern_info.c */, - D04F7FAE1BA4DDB500B0F227 /* pcre2_serialize.c */, - D04F7FB11BA4DDBF00B0F227 /* pcre2_string_utils.c */, - D04F7FB41BA4DDC900B0F227 /* pcre2_study.c */, - D04F7FB71BA4DDEB00B0F227 /* pcre2_substitute.c */, - D04F7FB81BA4DDEB00B0F227 /* pcre2_substring.c */, - D04F7FB91BA4DDEB00B0F227 /* pcre2_tables.c */, - D04F7FBA1BA4DDEB00B0F227 /* pcre2_ucd.c */, - D04F7FBB1BA4DDEB00B0F227 /* pcre2_valid_utf.c */, - D04F7FBC1BA4DDEB00B0F227 /* pcre2_xclass.c */, - D04F7FF71BA4E82C00B0F227 /* pcre2_chartables.c.dist */, - ); - name = pcre; - path = "pcre2-10.32/src"; - sourceTree = SOURCE_ROOT; - }; - D08A328E17B4455100F3A533 /* fish_tests */ = { - isa = PBXGroup; - children = ( - ); - path = fish_tests; - sourceTree = ""; - }; - D0A084F013B3AC130099B651 = { - isa = PBXGroup; - children = ( - D0D02A91159845EF008E62BD /* Sources */, - D0D02AFC159871BF008E62BD /* Launcher */, - D0D02A8E15983D5F008E62BD /* Libraries */, - D0D02AAB15985C14008E62BD /* Resources */, - D031890A15E36DB500D9CC39 /* Other Build Products */, - D08A328E17B4455100F3A533 /* fish_tests */, - D0D2693215983562005D9B9C /* Products */, - ); - sourceTree = ""; - }; - D0D02A8E15983D5F008E62BD /* Libraries */ = { - isa = PBXGroup; - children = ( - D0D02A8C15983CFA008E62BD /* libncurses.dylib */, - D0CBD583159EEE010024809C /* Foundation.framework */, - ); - name = Libraries; - sourceTree = ""; - }; - D0D02A91159845EF008E62BD /* Sources */ = { - isa = PBXGroup; - children = ( - CB0F034A1F156FE3001827D3 /* builtin_argparse.cpp */, - CB0F034B1F156FE3001827D3 /* builtin_argparse.h */, - 9C7A557C1DCD717C0049C25D /* fish_key_reader.cpp */, - 4E142D731B56B5D7008783C8 /* config.h */, - D0C6FCCB14CFA4B7004CE8AD /* autoload.h */, - D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */, - D05F592E1F041AE4003EE978 /* builtin.h */, - D05F592F1F041AE4003EE978 /* builtin.cpp */, - D02960E51FBD726100CA3985 /* builtin_wait.cpp */, - D05F59301F041AE4003EE978 /* builtin_ulimit.h */, - D05F59311F041AE4003EE978 /* builtin_ulimit.cpp */, - D05F59321F041AE4003EE978 /* builtin_test.h */, - D05F59331F041AE4003EE978 /* builtin_test.cpp */, - D05F59341F041AE4003EE978 /* builtin_string.h */, - D05F59351F041AE4003EE978 /* builtin_string.cpp */, - D05F59361F041AE4003EE978 /* builtin_status.h */, - D05F59371F041AE4003EE978 /* builtin_status.cpp */, - D05F59381F041AE4003EE978 /* builtin_source.h */, - D05F59391F041AE4003EE978 /* builtin_source.cpp */, - D05F593A1F041AE4003EE978 /* builtin_set.h */, - D05F593B1F041AE4003EE978 /* builtin_set.cpp */, - D05F593C1F041AE4003EE978 /* builtin_set_color.h */, - D05F593D1F041AE4003EE978 /* builtin_set_color.cpp */, - D05F593E1F041AE4003EE978 /* builtin_return.h */, - D05F593F1F041AE4003EE978 /* builtin_return.cpp */, - D05F59401F041AE4003EE978 /* builtin_realpath.h */, - D05F59411F041AE4003EE978 /* builtin_realpath.cpp */, - D05F59421F041AE4003EE978 /* builtin_read.h */, - D05F59431F041AE4003EE978 /* builtin_read.cpp */, - D05F59441F041AE4003EE978 /* builtin_random.h */, - D05F59451F041AE4003EE978 /* builtin_random.cpp */, - D05F59461F041AE4003EE978 /* builtin_pwd.h */, - D05F59471F041AE4003EE978 /* builtin_pwd.cpp */, - D05F59481F041AE4003EE978 /* builtin_printf.h */, - D05F59491F041AE4003EE978 /* builtin_printf.cpp */, - D05F594A1F041AE4003EE978 /* builtin_jobs.h */, - D05F594B1F041AE4003EE978 /* builtin_jobs.cpp */, - D05F594C1F041AE4003EE978 /* builtin_history.h */, - D05F594D1F041AE4003EE978 /* builtin_history.cpp */, - D05F594E1F041AE4003EE978 /* builtin_functions.h */, - D05F594F1F041AE4003EE978 /* builtin_functions.cpp */, - D05F59501F041AE4003EE978 /* builtin_function.h */, - D05F59511F041AE4003EE978 /* builtin_function.cpp */, - D05F59521F041AE4003EE978 /* builtin_fg.h */, - D05F59531F041AE4003EE978 /* builtin_fg.cpp */, - D05F59541F041AE4003EE978 /* builtin_exit.h */, - D05F59551F041AE4003EE978 /* builtin_exit.cpp */, - D05F59561F041AE4003EE978 /* builtin_emit.h */, - D05F59571F041AE4003EE978 /* builtin_emit.cpp */, - D05F59581F041AE4003EE978 /* builtin_echo.h */, - D05F59591F041AE4003EE978 /* builtin_echo.cpp */, - D05F595A1F041AE4003EE978 /* builtin_disown.h */, - D05F595B1F041AE4003EE978 /* builtin_disown.cpp */, - D05F595C1F041AE4003EE978 /* builtin_contains.h */, - D05F595D1F041AE4003EE978 /* builtin_contains.cpp */, - D05F595E1F041AE4003EE978 /* builtin_complete.h */, - D05F595F1F041AE4003EE978 /* builtin_complete.cpp */, - D05F59601F041AE4003EE978 /* builtin_commandline.h */, - D05F59611F041AE4003EE978 /* builtin_commandline.cpp */, - D05F59621F041AE4003EE978 /* builtin_command.h */, - D05F59631F041AE4003EE978 /* builtin_command.cpp */, - D05F59641F041AE4003EE978 /* builtin_cd.h */, - D05F59651F041AE4003EE978 /* builtin_cd.cpp */, - D05F59661F041AE4003EE978 /* builtin_builtin.h */, - D05F59671F041AE4003EE978 /* builtin_builtin.cpp */, - D05F59681F041AE4003EE978 /* builtin_block.h */, - D05F59691F041AE4003EE978 /* builtin_block.cpp */, - D05F596A1F041AE4003EE978 /* builtin_bind.h */, - D05F596B1F041AE4003EE978 /* builtin_bind.cpp */, - D05F596C1F041AE4003EE978 /* builtin_bg.h */, - D05F596D1F041AE4003EE978 /* builtin_bg.cpp */, - D0B51D821F5018BE0051C61A /* builtin_math.h */, - D0B51D811F5018B30051C61A /* builtin_math.cpp */, - D0B6B0FF14E88BA400AD6C10 /* color.h */, - D0B6B0FE14E88BA400AD6C10 /* color.cpp */, - D0A0850413B3ACEE0099B651 /* common.h */, - D0A0853613B3ACEE0099B651 /* common.cpp */, - D0A0850513B3ACEE0099B651 /* complete.h */, - D0A0853713B3ACEE0099B651 /* complete.cpp */, - D0A0850713B3ACEE0099B651 /* env_universal_common.h */, - D0A0853813B3ACEE0099B651 /* env_universal_common.cpp */, - D0A0850913B3ACEE0099B651 /* env.h */, - D0A0853A13B3ACEE0099B651 /* env.cpp */, - D0A0850A13B3ACEE0099B651 /* event.h */, - D0A0853B13B3ACEE0099B651 /* event.cpp */, - D0A0850B13B3ACEE0099B651 /* exec.h */, - D0A0853C13B3ACEE0099B651 /* exec.cpp */, - D0A0850C13B3ACEE0099B651 /* expand.h */, - D0A0853D13B3ACEE0099B651 /* expand.cpp */, - D0D9B2B318555D92001AE279 /* parse_constants.h */, - D0FE8EE6179CA8A5008C9F21 /* parse_productions.h */, - D0FE8EE7179FB75F008C9F21 /* parse_productions.cpp */, - D0C52F361765284C00BFAB82 /* parse_tree.h */, - D0C52F351765284C00BFAB82 /* parse_tree.cpp */, - D052D80A1868F7FC003ABCBD /* parse_execution.h */, - D052D8091868F7FC003ABCBD /* parse_execution.cpp */, - D0A0850D13B3ACEE0099B651 /* fallback.h */, - D0A0853E13B3ACEE0099B651 /* fallback.cpp */, - D0A0850E13B3ACEE0099B651 /* function.h */, - D0A0854413B3ACEE0099B651 /* function.cpp */, - D0A0853F13B3ACEE0099B651 /* fish_indent.cpp */, - D0A0854113B3ACEE0099B651 /* fish_tests.cpp */, - D0A0854213B3ACEE0099B651 /* fish.cpp */, - D00F63F019137E9D00FCCDEC /* fish_version.cpp */, - D04C863820B3D83900C675A6 /* future_feature_flags.cpp */, - D0A0851113B3ACEE0099B651 /* highlight.h */, - D0A0854713B3ACEE0099B651 /* highlight.cpp */, - D0A0851213B3ACEE0099B651 /* history.h */, - D0A0854813B3ACEE0099B651 /* history.cpp */, - D0A0851313B3ACEE0099B651 /* input_common.h */, - D0A0854913B3ACEE0099B651 /* input_common.cpp */, - D0A0851413B3ACEE0099B651 /* input.h */, - D0A0854A13B3ACEE0099B651 /* input.cpp */, - D0A0851513B3ACEE0099B651 /* intern.h */, - D0A0854B13B3ACEE0099B651 /* intern.cpp */, - D0A0851613B3ACEE0099B651 /* io.h */, - D0A0854C13B3ACEE0099B651 /* io.cpp */, - D0A0851713B3ACEE0099B651 /* iothread.h */, - D0A0854D13B3ACEE0099B651 /* iothread.cpp */, - D0A0851813B3ACEE0099B651 /* kill.h */, - D0A0854F13B3ACEE0099B651 /* kill.cpp */, - D03EE83814DF88B200FC7150 /* lru.h */, - D0A0851A13B3ACEE0099B651 /* output.h */, - D0A0855113B3ACEE0099B651 /* output.cpp */, - D043012D1F5350E400942A50 /* maybe.h */, - D032388A1849D1980032CF2C /* pager.h */, - D03238891849D1980032CF2C /* pager.cpp */, - D0A0851B13B3ACEE0099B651 /* parse_util.h */, - D0A0855213B3ACEE0099B651 /* parse_util.cpp */, - D0301C1D2002B90500B1F463 /* parse_grammar.h */, - D0A0851C13B3ACEE0099B651 /* parser_keywords.h */, - D0A0855313B3ACEE0099B651 /* parser_keywords.cpp */, - D0A0851D13B3ACEE0099B651 /* parser.h */, - D0A0855413B3ACEE0099B651 /* parser.cpp */, - D0A0851E13B3ACEE0099B651 /* path.h */, - D0A0855513B3ACEE0099B651 /* path.cpp */, - D09B1C1A14FC7B5B00F91077 /* postfork.h */, - D09B1C1914FC7B5B00F91077 /* postfork.cpp */, - D0A0851F13B3ACEE0099B651 /* print_help.h */, - D0A0855613B3ACEE0099B651 /* print_help.cpp */, - D0A0852013B3ACEE0099B651 /* proc.h */, - D0A0855713B3ACEE0099B651 /* proc.cpp */, - D0A0852113B3ACEE0099B651 /* reader.h */, - D0A0855813B3ACEE0099B651 /* reader.cpp */, - D0A0852213B3ACEE0099B651 /* sanity.h */, - D0A0855913B3ACEE0099B651 /* sanity.cpp */, - D0A0852313B3ACEE0099B651 /* screen.h */, - D0A0855A13B3ACEE0099B651 /* screen.cpp */, - D0A0852413B3ACEE0099B651 /* signal.h */, - D0A0855C13B3ACEE0099B651 /* signal.cpp */, - D046A0F92070245000C8DFF7 /* tinyexpr.c */, - D0A0852513B3ACEE0099B651 /* tokenizer.h */, - D0A0855D13B3ACEE0099B651 /* tokenizer.cpp */, - 4F2D55CD2013ECDD00822920 /* tnode.h */, - 4F2D55CE2013ECDD00822920 /* tnode.cpp */, - D0C9733A18DE5451002D7C81 /* utf8.h */, - D0C9733718DE5449002D7C81 /* utf8.cpp */, - D0A0852613B3ACEE0099B651 /* util.h */, - D0A0855E13B3ACEE0099B651 /* util.cpp */, - D0F5B46419CFCDE80090665E /* wcstringutil.h */, - D0F5B46319CFCDE80090665E /* wcstringutil.cpp */, - D0A0852713B3ACEE0099B651 /* wgetopt.h */, - D0A0855F13B3ACEE0099B651 /* wgetopt.cpp */, - D0A0852813B3ACEE0099B651 /* wildcard.h */, - D0A0856013B3ACEE0099B651 /* wildcard.cpp */, - D0A0852913B3ACEE0099B651 /* wutil.h */, - D0A0856113B3ACEE0099B651 /* wutil.cpp */, - D04F7F8B1BA4DC7600B0F227 /* pcre */, - ); - name = Sources; - path = src; - sourceTree = ""; - }; - D0D02AAB15985C14008E62BD /* Resources */ = { - isa = PBXGroup; - children = ( - 9CC8D8C41F7AF0610095062A /* lynx.lss */, - D0879AC616BF9A1A00E98E56 /* fish_term_icon.icns */, - D07B247215BCC15700D4ADB4 /* add-shell */, - D0CBD586159EF0E10024809C /* launch_fish.scpt */, - D0CBD580159EE48F0024809C /* config.fish */, - D0C4FD9415A7D7EE00212EF1 /* config.fish */, - D07B247515BCC4BE00D4ADB4 /* install.sh */, - D0D02AA915985C0C008E62BD /* Info.plist */, - D0A564F2168D1F2000AF6161 /* build_documentation.sh */, - D01A2C9C16964CF600767098 /* pages_for_manpath */, - D0A564F1168D0BAB00AF6161 /* man */, - D025C02715D1FEA100B9DB63 /* completions */, - D025C02815D1FEA100B9DB63 /* functions */, - D025C02915D1FEA100B9DB63 /* tools */, - D0A564D2168CF34A00AF6161 /* doc_src */, - ); - name = Resources; - sourceTree = ""; - }; - D0D02AFC159871BF008E62BD /* Launcher */ = { - isa = PBXGroup; - children = ( - D0D02AFA159871B2008E62BD /* osx_fish_launcher.m */, - ); - name = Launcher; - sourceTree = ""; - }; - D0D2693215983562005D9B9C /* Products */ = { - isa = PBXGroup; - children = ( - D0D2693C159835CA005D9B9C /* fish */, - D0D02A9A15985A75008E62BD /* fish.app */, - D0D02AD01598642A008E62BD /* fish_indent */, - D00769421990137800CA4627 /* fish_tests */, - D04F7FD01BA4E29300B0F227 /* libpcre2.a */, - 9C7A55721DCD71330049C25D /* fish_key_reader */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - D04F7FCE1BA4E29300B0F227 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 9C7A55301DCD71330049C25D /* fish_key_reader */ = { - isa = PBXNativeTarget; - buildConfigurationList = 9C7A556E1DCD71330049C25D /* Build configuration list for PBXNativeTarget "fish_key_reader" */; - buildPhases = ( - 9C7A55351DCD71330049C25D /* Sources */, - 9C7A556B1DCD71330049C25D /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 9C7A55311DCD71330049C25D /* PBXTargetDependency */, - 9C7A55331DCD71330049C25D /* PBXTargetDependency */, - ); - name = fish_key_reader; - productName = fish_Xcode; - productReference = 9C7A55721DCD71330049C25D /* fish_key_reader */; - productType = "com.apple.product-type.tool"; - }; - D00769101990137800CA4627 /* fish_tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = D007693E1990137800CA4627 /* Build configuration list for PBXNativeTarget "fish_tests" */; - buildPhases = ( - D00769111990137800CA4627 /* Sources */, - D007693C1990137800CA4627 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - D008D0CF1BC58FE500841177 /* PBXTargetDependency */, - D04F7FED1BA4E3DF00B0F227 /* PBXTargetDependency */, - ); - name = fish_tests; - productName = fish_Xcode; - productReference = D00769421990137800CA4627 /* fish_tests */; - productType = "com.apple.product-type.tool"; - }; - D04F7FCF1BA4E29300B0F227 /* pcre2 */ = { - isa = PBXNativeTarget; - buildConfigurationList = D04F7FD11BA4E29300B0F227 /* Build configuration list for PBXNativeTarget "pcre2" */; - buildPhases = ( - D04F7FCC1BA4E29300B0F227 /* Sources */, - D04F7FCD1BA4E29300B0F227 /* Frameworks */, - D04F7FCE1BA4E29300B0F227 /* Headers */, - ); - buildRules = ( - D04F7FF81BA4E84B00B0F227 /* PBXBuildRule */, - ); - dependencies = ( - ); - name = pcre2; - productName = libpcre2.a; - productReference = D04F7FD01BA4E29300B0F227 /* libpcre2.a */; - productType = "com.apple.product-type.library.static"; - }; - D0D02A9915985A75008E62BD /* fish.app */ = { - isa = PBXNativeTarget; - buildConfigurationList = D0D02AA415985A75008E62BD /* Build configuration list for PBXNativeTarget "fish.app" */; - buildPhases = ( - D0F01A0215A9788B0034B3B1 /* Sources */, - D0CBD585159EF09F0024809C /* Resources */, - D0F01A0415A9789C0034B3B1 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - D031890815E36CC000D9CC39 /* PBXTargetDependency */, - ); - name = fish.app; - productName = fish; - productReference = D0D02A9A15985A75008E62BD /* fish.app */; - productType = "com.apple.product-type.application"; - }; - D0D02ACF1598642A008E62BD /* fish_indent */ = { - isa = PBXNativeTarget; - buildConfigurationList = D0D02AD31598642A008E62BD /* Build configuration list for PBXNativeTarget "fish_indent" */; - buildPhases = ( - D0D02ACC1598642A008E62BD /* Sources */, - D0D02ACD1598642A008E62BD /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - D008D0CD1BC58FE100841177 /* PBXTargetDependency */, - ); - name = fish_indent; - productName = fish_indent; - productReference = D0D02AD01598642A008E62BD /* fish_indent */; - productType = "com.apple.product-type.tool"; - }; - D0D2693B159835CA005D9B9C /* fish_shell */ = { - isa = PBXNativeTarget; - buildConfigurationList = D0D26943159835CA005D9B9C /* Build configuration list for PBXNativeTarget "fish_shell" */; - buildPhases = ( - D0D26938159835CA005D9B9C /* Sources */, - D0D26939159835CA005D9B9C /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - D008D0CB1BC58FDD00841177 /* PBXTargetDependency */, - D04F7FEB1BA4E3DB00B0F227 /* PBXTargetDependency */, - ); - name = fish_shell; - productName = fish_Xcode; - productReference = D0D2693C159835CA005D9B9C /* fish */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - D0A084F213B3AC130099B651 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0820; - TargetAttributes = { - D008D0C41BC58F8800841177 = { - CreatedOnToolsVersion = 7.0.1; - }; - D04F7FCF1BA4E29300B0F227 = { - CreatedOnToolsVersion = 6.4; - }; - }; - }; - buildConfigurationList = D0A084F513B3AC130099B651 /* Build configuration list for PBXProject "fish" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = D0A084F013B3AC130099B651; - productRefGroup = D0D2693215983562005D9B9C /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - D07D265615E33B86009E43F6 /* install_tree */, - D0F019EC15A976F30034B3B1 /* base */, - D0D02A9915985A75008E62BD /* fish.app */, - D0D2693B159835CA005D9B9C /* fish_shell */, - D0D02ACF1598642A008E62BD /* fish_indent */, - D0A564E6168CFDD800AF6161 /* man_pages */, - D00769101990137800CA4627 /* fish_tests */, - D04F7FCF1BA4E29300B0F227 /* pcre2 */, - D008D0C41BC58F8800841177 /* generate-version-header */, - 9C7A55301DCD71330049C25D /* fish_key_reader */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - D0CBD585159EF09F0024809C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D031890C15E36E4600D9CC39 /* base in Resources */, - D0CBD587159EF0E10024809C /* launch_fish.scpt in Resources */, - D0879AC816BF9AAB00E98E56 /* fish_term_icon.icns in Resources */, - D07B247315BCC15700D4ADB4 /* add-shell in Resources */, - D07B247615BCC4BE00D4ADB4 /* install.sh in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - D008D0C91BC58F9500841177 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - "$(SHARED_DERIVED_FILE_DIR)/FISH-BUILD-VERSION-FILE", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = ./build_tools/xcode_version_gen.sh; - }; - D0A564EB168CFDDE00AF6161 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(SRCROOT)/doc_src/alias.txt", - "$(SRCROOT)/doc_src/and.txt", - "$(SRCROOT)/doc_src/begin.txt", - "$(SRCROOT)/doc_src/bg.txt", - "$(SRCROOT)/doc_src/bind.txt", - "$(SRCROOT)/doc_src/block.txt", - "$(SRCROOT)/doc_src/break.txt", - "$(SRCROOT)/doc_src/breakpoint.txt", - "$(SRCROOT)/doc_src/builtin.txt", - "$(SRCROOT)/doc_src/case.txt", - "$(SRCROOT)/doc_src/cd.txt", - "$(SRCROOT)/doc_src/command.txt", - "$(SRCROOT)/doc_src/commandline.txt", - "$(SRCROOT)/doc_src/complete.txt", - "$(SRCROOT)/doc_src/contains.txt", - "$(SRCROOT)/doc_src/continue.txt", - "$(SRCROOT)/doc_src/count.txt", - "$(SRCROOT)/doc_src/dirh.txt", - "$(SRCROOT)/doc_src/dirs.txt", - "$(SRCROOT)/doc_src/echo.txt", - "$(SRCROOT)/doc_src/else.txt", - "$(SRCROOT)/doc_src/emit.txt", - "$(SRCROOT)/doc_src/end.txt", - "$(SRCROOT)/doc_src/eval.txt", - "$(SRCROOT)/doc_src/exec.txt", - "$(SRCROOT)/doc_src/exit.txt", - "$(SRCROOT)/doc_src/fg.txt", - "$(SRCROOT)/doc_src/fish.txt", - "$(SRCROOT)/doc_src/fish_config.txt", - "$(SRCROOT)/doc_src/fish_indent.txt", - "$(SRCROOT)/doc_src/fish_prompt.txt", - "$(SRCROOT)/doc_src/fish_right_prompt.txt", - "$(SRCROOT)/doc_src/fish_update_completions.txt", - "$(SRCROOT)/doc_src/fishd.txt", - "$(SRCROOT)/doc_src/for.txt", - "$(SRCROOT)/doc_src/funced.txt", - "$(SRCROOT)/doc_src/funcsave.txt", - "$(SRCROOT)/doc_src/function.txt", - "$(SRCROOT)/doc_src/functions.txt", - "$(SRCROOT)/doc_src/help.txt", - "$(SRCROOT)/doc_src/history.txt", - "$(SRCROOT)/doc_src/if.txt", - "$(SRCROOT)/doc_src/isatty.txt", - "$(SRCROOT)/doc_src/jobs.txt", - "$(SRCROOT)/doc_src/math.txt", - "$(SRCROOT)/doc_src/mimedb.txt", - "$(SRCROOT)/doc_src/nextd.txt", - "$(SRCROOT)/doc_src/not.txt", - "$(SRCROOT)/doc_src/open.txt", - "$(SRCROOT)/doc_src/or.txt", - "$(SRCROOT)/doc_src/popd.txt", - "$(SRCROOT)/doc_src/prevd.txt", - "$(SRCROOT)/doc_src/psub.txt", - "$(SRCROOT)/doc_src/pushd.txt", - "$(SRCROOT)/doc_src/pwd.txt", - "$(SRCROOT)/doc_src/random.txt", - "$(SRCROOT)/doc_src/read.txt", - "$(SRCROOT)/doc_src/return.txt", - "$(SRCROOT)/doc_src/set.txt", - "$(SRCROOT)/doc_src/set_color.txt", - "$(SRCROOT)/doc_src/source.txt", - "$(SRCROOT)/doc_src/status.txt", - "$(SRCROOT)/doc_src/switch.txt", - "$(SRCROOT)/doc_src/test.txt", - "$(SRCROOT)/doc_src/trap.txt", - "$(SRCROOT)/doc_src/type.txt", - "$(SRCROOT)/doc_src/ulimit.txt", - "$(SRCROOT)/doc_src/umask.txt", - "$(SRCROOT)/doc_src/vared.txt", - "$(SRCROOT)/doc_src/while.txt", - "$(SRCROOT)/doc_src/true.txt", - "$(SRCROOT)/doc_src/false.txt", - ); - outputPaths = ( - "$(BUILT_PRODUCTS_DIR)/man/man1/alias.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/and.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/begin.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/bg.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/bind.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/block.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/break.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/breakpoint.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/builtin.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/case.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/cd.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/command.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/commandline.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/complete.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/contains.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/continue.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/count.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/dirh.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/dirs.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/echo.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/else.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/emit.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/end.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/eval.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/exec.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/exit.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/fg.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/fish.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/fish_config.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/fish_indent.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/fish_prompt.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/fish_right_prompt.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/fish_update_completions.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/fishd.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/for.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/funced.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/funcsave.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/function.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/functions.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/help.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/history.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/if.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/isatty.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/jobs.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/math.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/mimedb.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/nextd.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/not.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/open.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/or.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/popd.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/prevd.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/psub.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/pushd.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/pwd.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/random.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/read.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/return.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/set.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/set_color.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/source.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/status.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/switch.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/test.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/trap.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/type.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/ulimit.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/umask.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/vared.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/while.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/true.1", - "$(BUILT_PRODUCTS_DIR)/man/man1/false.1", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "cd \"${SRCROOT}\" ;\n# Run build_documentation.sh\n# Do this in a subshell so that we keep going even if it calls exit\nif [ -f lexicon_filter ] ; then\n export INPUT_FILTER=./lexicon_filter\nelse\n echo \"build_documentation:0: warning: lexicon_filter not found, ignoring it\" >&2\nfi\n\n( . \"./build_tools/build_documentation.sh\" \"./Doxyfile.help\" \"./doc_src\" \"$BUILT_PRODUCTS_DIR\" )\n\n# Copy certain files into man1, destined for share/man/man1 (instead of share/fish/man/man1)\n# These copies will fail if the documentation did not build; that's OK\n# We want to create the directory even if the documentation did not build, so that the Xcode build can still succeed\nmanpathdir=\"${BUILT_PRODUCTS_DIR}/pages_for_manpath/man1\"\necho \"Copying pages destined for manpath into $manpathdir\"\nrm -Rf \"$manpathdir\"\nmkdir -p \"$manpathdir\"\nfor manpage in fish.1 fish_indent.1; do\n manpagepath=\"${BUILT_PRODUCTS_DIR}/man/man1/${manpage}\"\n test -f \"$manpagepath\" && cp \"$manpagepath\" \"${BUILT_PRODUCTS_DIR}/pages_for_manpath/man1/\"\ndone\n\n# Always succeed\ntrue\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 9C7A55351DCD71330049C25D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D001B5EE1F041CBD000838CC /* builtin.cpp in Sources */, - D08500A41F63A65800C0E329 /* builtin_argparse.cpp in Sources */, - D001B5F01F041CBD000838CC /* builtin_ulimit.cpp in Sources */, - D001B5F21F041CBD000838CC /* builtin_test.cpp in Sources */, - D001B5F41F041CBD000838CC /* builtin_string.cpp in Sources */, - D001B5F61F041CBD000838CC /* builtin_status.cpp in Sources */, - D001B5F81F041CBD000838CC /* builtin_source.cpp in Sources */, - D001B5FA1F041CBD000838CC /* builtin_set.cpp in Sources */, - D001B5FC1F041CBD000838CC /* builtin_set_color.cpp in Sources */, - D001B5FE1F041CBD000838CC /* builtin_return.cpp in Sources */, - D001B6001F041CBD000838CC /* builtin_realpath.cpp in Sources */, - D001B6021F041CBD000838CC /* builtin_read.cpp in Sources */, - D001B6041F041CBD000838CC /* builtin_random.cpp in Sources */, - D001B6061F041CBD000838CC /* builtin_pwd.cpp in Sources */, - D0B51D831F5018E80051C61A /* builtin_math.cpp in Sources */, - D001B6081F041CBD000838CC /* builtin_printf.cpp in Sources */, - D001B60A1F041CBD000838CC /* builtin_jobs.cpp in Sources */, - D001B60C1F041CBD000838CC /* builtin_history.cpp in Sources */, - D001B60E1F041CBD000838CC /* builtin_functions.cpp in Sources */, - D001B6101F041CBD000838CC /* builtin_function.cpp in Sources */, - D001B6121F041CBD000838CC /* builtin_fg.cpp in Sources */, - D001B6141F041CBD000838CC /* builtin_exit.cpp in Sources */, - D001B6161F041CBD000838CC /* builtin_emit.cpp in Sources */, - D001B6181F041CBD000838CC /* builtin_echo.cpp in Sources */, - D046A0FD2070245000C8DFF7 /* tinyexpr.c in Sources */, - D001B61A1F041CBD000838CC /* builtin_disown.cpp in Sources */, - D001B61C1F041CBD000838CC /* builtin_contains.cpp in Sources */, - D001B61E1F041CBD000838CC /* builtin_complete.cpp in Sources */, - D001B6201F041CBD000838CC /* builtin_commandline.cpp in Sources */, - D001B6221F041CBD000838CC /* builtin_command.cpp in Sources */, - D001B6241F041CBD000838CC /* builtin_cd.cpp in Sources */, - D001B6261F041CBD000838CC /* builtin_builtin.cpp in Sources */, - D001B6281F041CBD000838CC /* builtin_block.cpp in Sources */, - D001B62A1F041CBD000838CC /* builtin_bind.cpp in Sources */, - D001B62C1F041CBD000838CC /* builtin_bg.cpp in Sources */, - 9C7A557E1DCD71CD0049C25D /* print_help.cpp in Sources */, - 9C7A557D1DCD71890049C25D /* fish_key_reader.cpp in Sources */, - 9C7A55361DCD71330049C25D /* autoload.cpp in Sources */, - 9C7A55401DCD71330049C25D /* color.cpp in Sources */, - 9C7A55411DCD71330049C25D /* common.cpp in Sources */, - 9C7A55421DCD71330049C25D /* event.cpp in Sources */, - 9C7A55431DCD71330049C25D /* input_common.cpp in Sources */, - 9C7A55441DCD71330049C25D /* io.cpp in Sources */, - 9C7A55451DCD71330049C25D /* iothread.cpp in Sources */, - 9C7A55461DCD71330049C25D /* parse_util.cpp in Sources */, - 9C7A55471DCD71330049C25D /* path.cpp in Sources */, - 9C7A55481DCD71330049C25D /* parse_execution.cpp in Sources */, - 9C7A55491DCD71330049C25D /* postfork.cpp in Sources */, - 9C7A554A1DCD71330049C25D /* screen.cpp in Sources */, - 9C7A554B1DCD71330049C25D /* signal.cpp in Sources */, - 4F2D55D12013ED0100822920 /* tnode.cpp in Sources */, - 9C7A554C1DCD71330049C25D /* utf8.cpp in Sources */, - 9C7A554E1DCD71330049C25D /* function.cpp in Sources */, - 9C7A554F1DCD71330049C25D /* complete.cpp in Sources */, - 9C7A55501DCD71330049C25D /* env.cpp in Sources */, - D02960E91FBD726200CA3985 /* builtin_wait.cpp in Sources */, - 9C7A55511DCD71330049C25D /* exec.cpp in Sources */, - 9C7A55521DCD71330049C25D /* wcstringutil.cpp in Sources */, - 9C7A55531DCD71330049C25D /* expand.cpp in Sources */, - 9C7A55541DCD71330049C25D /* fallback.cpp in Sources */, - 9C7A55551DCD71330049C25D /* fish_version.cpp in Sources */, - 9C7A55561DCD71330049C25D /* highlight.cpp in Sources */, - 9C7A55571DCD71330049C25D /* history.cpp in Sources */, - 9C7A55581DCD71330049C25D /* kill.cpp in Sources */, - 9C7A55591DCD71330049C25D /* parser.cpp in Sources */, - 9C7A555A1DCD71330049C25D /* parser_keywords.cpp in Sources */, - 9C7A555B1DCD71330049C25D /* proc.cpp in Sources */, - 9C7A555C1DCD71330049C25D /* reader.cpp in Sources */, - 9C7A555D1DCD71330049C25D /* sanity.cpp in Sources */, - 9C7A555E1DCD71330049C25D /* tokenizer.cpp in Sources */, - 9C7A555F1DCD71330049C25D /* wildcard.cpp in Sources */, - 9C7A55601DCD71330049C25D /* wgetopt.cpp in Sources */, - 9C7A55611DCD71330049C25D /* wutil.cpp in Sources */, - 9C7A55621DCD71330049C25D /* input.cpp in Sources */, - 9C7A55631DCD71330049C25D /* output.cpp in Sources */, - 9C7A55641DCD71330049C25D /* intern.cpp in Sources */, - 9C7A55651DCD71330049C25D /* env_universal_common.cpp in Sources */, - D04C863C20B3D83900C675A6 /* future_feature_flags.cpp in Sources */, - 9C7A55661DCD71330049C25D /* pager.cpp in Sources */, - 9C7A55681DCD71330049C25D /* parse_tree.cpp in Sources */, - 9C7A55691DCD71330049C25D /* parse_productions.cpp in Sources */, - 9C7A556A1DCD71330049C25D /* util.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D00769111990137800CA4627 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D02960E81FBD726200CA3985 /* builtin_wait.cpp in Sources */, - 9C7A552F1DCD65820049C25D /* util.cpp in Sources */, - D046A0FC2070245000C8DFF7 /* tinyexpr.c in Sources */, - D05F59971F041AE4003EE978 /* builtin_printf.cpp in Sources */, - D05F59A31F041AE4003EE978 /* builtin_function.cpp in Sources */, - 9C7A55271DCD651F0049C25D /* fallback.cpp in Sources */, - D05F59CA1F041AE4003EE978 /* builtin_bind.cpp in Sources */, - D05F59AF1F041AE4003EE978 /* builtin_echo.cpp in Sources */, - D05F59A61F041AE4003EE978 /* builtin_fg.cpp in Sources */, - D08500A51F63A6EB00C0E329 /* builtin_math.cpp in Sources */, - D00769121990137800CA4627 /* autoload.cpp in Sources */, - D04C863B20B3D83900C675A6 /* future_feature_flags.cpp in Sources */, - D05F59BE1F041AE4003EE978 /* builtin_command.cpp in Sources */, - D05F59AC1F041AE4003EE978 /* builtin_emit.cpp in Sources */, - D00769141990137800CA4627 /* color.cpp in Sources */, - D05F59731F041AE4003EE978 /* builtin_ulimit.cpp in Sources */, - D05F59C71F041AE4003EE978 /* builtin_block.cpp in Sources */, - D05F59CD1F041AE4003EE978 /* builtin_bg.cpp in Sources */, - D00769151990137800CA4627 /* common.cpp in Sources */, - D05F59B21F041AE4003EE978 /* builtin_disown.cpp in Sources */, - D00769161990137800CA4627 /* event.cpp in Sources */, - D00769171990137800CA4627 /* input_common.cpp in Sources */, - D00769181990137800CA4627 /* io.cpp in Sources */, - D00769191990137800CA4627 /* iothread.cpp in Sources */, - D007691A1990137800CA4627 /* parse_util.cpp in Sources */, - D007691B1990137800CA4627 /* path.cpp in Sources */, - D007691C1990137800CA4627 /* parse_execution.cpp in Sources */, - D05F59881F041AE4003EE978 /* builtin_return.cpp in Sources */, - D007691D1990137800CA4627 /* postfork.cpp in Sources */, - D05F598E1F041AE4003EE978 /* builtin_read.cpp in Sources */, - D007691E1990137800CA4627 /* screen.cpp in Sources */, - D007691F1990137800CA4627 /* signal.cpp in Sources */, - D05F59C41F041AE4003EE978 /* builtin_builtin.cpp in Sources */, - D05F59BB1F041AE4003EE978 /* builtin_commandline.cpp in Sources */, - D05F59C11F041AE4003EE978 /* builtin_cd.cpp in Sources */, - D05F59701F041AE4003EE978 /* builtin.cpp in Sources */, - D00769201990137800CA4627 /* utf8.cpp in Sources */, - D05F599A1F041AE4003EE978 /* builtin_jobs.cpp in Sources */, - D00769221990137800CA4627 /* function.cpp in Sources */, - D00769231990137800CA4627 /* complete.cpp in Sources */, - D00769241990137800CA4627 /* env.cpp in Sources */, - D05F59A01F041AE4003EE978 /* builtin_functions.cpp in Sources */, - D00769251990137800CA4627 /* exec.cpp in Sources */, - D00769261990137800CA4627 /* expand.cpp in Sources */, - D05F59941F041AE4003EE978 /* builtin_pwd.cpp in Sources */, - D00769271990137800CA4627 /* fish_version.cpp in Sources */, - D00769281990137800CA4627 /* highlight.cpp in Sources */, - D00769291990137800CA4627 /* history.cpp in Sources */, - D007692A1990137800CA4627 /* kill.cpp in Sources */, - D007692B1990137800CA4627 /* parser.cpp in Sources */, - D05F59791F041AE4003EE978 /* builtin_string.cpp in Sources */, - D05F597C1F041AE4003EE978 /* builtin_status.cpp in Sources */, - D007692C1990137800CA4627 /* parser_keywords.cpp in Sources */, - D007692D1990137800CA4627 /* proc.cpp in Sources */, - D007692E1990137800CA4627 /* reader.cpp in Sources */, - D007692F1990137800CA4627 /* sanity.cpp in Sources */, - CB0F034E1F156FE3001827D3 /* builtin_argparse.cpp in Sources */, - D05F597F1F041AE4003EE978 /* builtin_source.cpp in Sources */, - D05F59B51F041AE4003EE978 /* builtin_contains.cpp in Sources */, - D05F59911F041AE4003EE978 /* builtin_random.cpp in Sources */, - D00769301990137800CA4627 /* tokenizer.cpp in Sources */, - D05F59761F041AE4003EE978 /* builtin_test.cpp in Sources */, - D05F599D1F041AE4003EE978 /* builtin_history.cpp in Sources */, - D0F5B46619CFCEBC0090665E /* wcstringutil.cpp in Sources */, - D05F59B81F041AE4003EE978 /* builtin_complete.cpp in Sources */, - D00769311990137800CA4627 /* wildcard.cpp in Sources */, - D00769321990137800CA4627 /* wgetopt.cpp in Sources */, - D00769331990137800CA4627 /* wutil.cpp in Sources */, - D00769341990137800CA4627 /* input.cpp in Sources */, - D05F59821F041AE4003EE978 /* builtin_set.cpp in Sources */, - D00769351990137800CA4627 /* output.cpp in Sources */, - D00769361990137800CA4627 /* intern.cpp in Sources */, - D05F59A91F041AE4003EE978 /* builtin_exit.cpp in Sources */, - D00769371990137800CA4627 /* env_universal_common.cpp in Sources */, - D05F59851F041AE4003EE978 /* builtin_set_color.cpp in Sources */, - D00769381990137800CA4627 /* pager.cpp in Sources */, - D007693A1990137800CA4627 /* parse_tree.cpp in Sources */, - D007693B1990137800CA4627 /* parse_productions.cpp in Sources */, - D0076943199013B900CA4627 /* fish_tests.cpp in Sources */, - D05F598B1F041AE4003EE978 /* builtin_realpath.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D04F7FCC1BA4E29300B0F227 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D04F7FD51BA4E3AC00B0F227 /* pcre2_compile.c in Sources */, - D04F7FD61BA4E3AC00B0F227 /* pcre2_config.c in Sources */, - D04F7FD71BA4E3AC00B0F227 /* pcre2_context.c in Sources */, - D04F7FD81BA4E3AC00B0F227 /* pcre2_dfa_match.c in Sources */, - D04F7FD91BA4E3AC00B0F227 /* pcre2_error.c in Sources */, - D04F7FDA1BA4E3AC00B0F227 /* pcre2_jit_compile.c in Sources */, - D04F7FDB1BA4E3AC00B0F227 /* pcre2_maketables.c in Sources */, - D04F7FDC1BA4E3AC00B0F227 /* pcre2_match.c in Sources */, - D04F7FDD1BA4E3AC00B0F227 /* pcre2_match_data.c in Sources */, - 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 */, - D04F7FE31BA4E3AC00B0F227 /* pcre2_study.c in Sources */, - D04F7FE41BA4E3AC00B0F227 /* pcre2_substitute.c in Sources */, - D04F7FE51BA4E3AC00B0F227 /* pcre2_substring.c in Sources */, - D04F7FE61BA4E3AC00B0F227 /* pcre2_tables.c in Sources */, - D04F7FE71BA4E3AC00B0F227 /* pcre2_ucd.c in Sources */, - D04F7FE81BA4E3AC00B0F227 /* pcre2_valid_utf.c in Sources */, - D04F7FE91BA4E3AC00B0F227 /* pcre2_xclass.c in Sources */, - D04F7FF91BA4E87B00B0F227 /* pcre2_chartables.c.dist in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0D02ACC1598642A008E62BD /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D05F59BD1F041AE4003EE978 /* builtin_command.cpp in Sources */, - D030FBF41A4A38F300F7ADA0 /* autoload.cpp in Sources */, - D05F59BA1F041AE4003EE978 /* builtin_commandline.cpp in Sources */, - D030FBF61A4A38F300F7ADA0 /* color.cpp in Sources */, - D0D02AD81598649E008E62BD /* common.cpp in Sources */, - D030FBF71A4A38F300F7ADA0 /* complete.cpp in Sources */, - D030FBF81A4A38F300F7ADA0 /* env_universal_common.cpp in Sources */, - D030FBF91A4A38F300F7ADA0 /* env.cpp in Sources */, - D030FBF01A4A382B00F7ADA0 /* event.cpp in Sources */, - D05F59B11F041AE4003EE978 /* builtin_disown.cpp in Sources */, - D030FBFA1A4A38F300F7ADA0 /* exec.cpp in Sources */, - D06821601F5148AE00040321 /* builtin_math.cpp in Sources */, - D05F597E1F041AE4003EE978 /* builtin_source.cpp in Sources */, - D05F597B1F041AE4003EE978 /* builtin_status.cpp in Sources */, - D05F59811F041AE4003EE978 /* builtin_set.cpp in Sources */, - D030FBFB1A4A38F300F7ADA0 /* expand.cpp in Sources */, - D030FBFC1A4A38F300F7ADA0 /* parse_productions.cpp in Sources */, - D030FBFD1A4A38F300F7ADA0 /* parse_tree.cpp in Sources */, - D05F598A1F041AE4003EE978 /* builtin_realpath.cpp in Sources */, - D05F59931F041AE4003EE978 /* builtin_pwd.cpp in Sources */, - D030FBFE1A4A38F300F7ADA0 /* parse_execution.cpp in Sources */, - D05F59A81F041AE4003EE978 /* builtin_exit.cpp in Sources */, - D012436B1CD4019700C64313 /* fallback.cpp in Sources */, - D05F59CC1F041AE4003EE978 /* builtin_bg.cpp in Sources */, - D046A0FB2070245000C8DFF7 /* tinyexpr.c in Sources */, - D05F59AB1F041AE4003EE978 /* builtin_emit.cpp in Sources */, - D05F59961F041AE4003EE978 /* builtin_printf.cpp in Sources */, - D030FC001A4A38F300F7ADA0 /* function.cpp in Sources */, - CB0F034D1F156FE3001827D3 /* builtin_argparse.cpp in Sources */, - D05F59C61F041AE4003EE978 /* builtin_block.cpp in Sources */, - D05F59C91F041AE4003EE978 /* builtin_bind.cpp in Sources */, - D05F599C1F041AE4003EE978 /* builtin_history.cpp in Sources */, - D05F599F1F041AE4003EE978 /* builtin_functions.cpp in Sources */, - D030FC011A4A38F300F7ADA0 /* highlight.cpp in Sources */, - D05F59841F041AE4003EE978 /* builtin_set_color.cpp in Sources */, - D05F59AE1F041AE4003EE978 /* builtin_echo.cpp in Sources */, - D05F59721F041AE4003EE978 /* builtin_ulimit.cpp in Sources */, - D05F59781F041AE4003EE978 /* builtin_string.cpp in Sources */, - D030FC021A4A38F300F7ADA0 /* history.cpp in Sources */, - D030FC031A4A38F300F7ADA0 /* input_common.cpp in Sources */, - D030FBEF1A4A382000F7ADA0 /* input.cpp in Sources */, - D05F59A21F041AE4003EE978 /* builtin_function.cpp in Sources */, - D05F59871F041AE4003EE978 /* builtin_return.cpp in Sources */, - D05F59901F041AE4003EE978 /* builtin_random.cpp in Sources */, - D030FC041A4A38F300F7ADA0 /* intern.cpp in Sources */, - D030FC051A4A38F300F7ADA0 /* io.cpp in Sources */, - D030FC061A4A38F300F7ADA0 /* iothread.cpp in Sources */, - D05F59751F041AE4003EE978 /* builtin_test.cpp in Sources */, - D030FC071A4A38F300F7ADA0 /* kill.cpp in Sources */, - D030FBF11A4A384000F7ADA0 /* output.cpp in Sources */, - D05F59B41F041AE4003EE978 /* builtin_contains.cpp in Sources */, - D030FC081A4A38F300F7ADA0 /* pager.cpp in Sources */, - D030FC091A4A38F300F7ADA0 /* parse_util.cpp in Sources */, - 4F2D55D02013ECDD00822920 /* tnode.cpp in Sources */, - D0D02AD9159864A6008E62BD /* parser_keywords.cpp in Sources */, - D02960E71FBD726200CA3985 /* builtin_wait.cpp in Sources */, - D05F59A51F041AE4003EE978 /* builtin_fg.cpp in Sources */, - D05F596F1F041AE4003EE978 /* builtin.cpp in Sources */, - D05F598D1F041AE4003EE978 /* builtin_read.cpp in Sources */, - D05F59C31F041AE4003EE978 /* builtin_builtin.cpp in Sources */, - D030FC0A1A4A38F300F7ADA0 /* parser.cpp in Sources */, - D030FC0B1A4A38F300F7ADA0 /* path.cpp in Sources */, - D030FC0C1A4A38F300F7ADA0 /* postfork.cpp in Sources */, - D0D02AD715986498008E62BD /* print_help.cpp in Sources */, - D030FC0D1A4A38F300F7ADA0 /* proc.cpp in Sources */, - D030FBF31A4A386A00F7ADA0 /* reader.cpp in Sources */, - D030FC0E1A4A38F300F7ADA0 /* sanity.cpp in Sources */, - D030FC0F1A4A38F300F7ADA0 /* screen.cpp in Sources */, - D030FBF21A4A384A00F7ADA0 /* signal.cpp in Sources */, - D0D02ADB159864C2008E62BD /* tokenizer.cpp in Sources */, - D030FC101A4A38F300F7ADA0 /* utf8.cpp in Sources */, - D030FC121A4A38F300F7ADA0 /* wcstringutil.cpp in Sources */, - 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 */, - D04C863A20B3D83900C675A6 /* future_feature_flags.cpp in Sources */, - D05F59C01F041AE4003EE978 /* builtin_cd.cpp in Sources */, - D05F59991F041AE4003EE978 /* builtin_jobs.cpp in Sources */, - D05F59B71F041AE4003EE978 /* builtin_complete.cpp in Sources */, - D00F63F219137E9D00FCCDEC /* fish_version.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0D26938159835CA005D9B9C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D02960E61FBD726200CA3985 /* builtin_wait.cpp in Sources */, - D0D02A7C159839D5008E62BD /* autoload.cpp in Sources */, - D05F59951F041AE4003EE978 /* builtin_printf.cpp in Sources */, - D05F59A11F041AE4003EE978 /* builtin_function.cpp in Sources */, - D0D02A7E159839D5008E62BD /* color.cpp in Sources */, - D05F59C81F041AE4003EE978 /* builtin_bind.cpp in Sources */, - D05F59AD1F041AE4003EE978 /* builtin_echo.cpp in Sources */, - D05F59A41F041AE4003EE978 /* builtin_fg.cpp in Sources */, - D0D02A7F159839D5008E62BD /* common.cpp in Sources */, - D05F59BC1F041AE4003EE978 /* builtin_command.cpp in Sources */, - D05F59AA1F041AE4003EE978 /* builtin_emit.cpp in Sources */, - D0D02A80159839D5008E62BD /* event.cpp in Sources */, - D05F59711F041AE4003EE978 /* builtin_ulimit.cpp in Sources */, - D05F59C51F041AE4003EE978 /* builtin_block.cpp in Sources */, - D05F59CB1F041AE4003EE978 /* builtin_bg.cpp in Sources */, - D0B51D841F5018F30051C61A /* builtin_math.cpp in Sources */, - D0D02A81159839D5008E62BD /* input_common.cpp in Sources */, - D05F59B01F041AE4003EE978 /* builtin_disown.cpp in Sources */, - D0D02A82159839D5008E62BD /* io.cpp in Sources */, - D0D02A83159839D5008E62BD /* iothread.cpp in Sources */, - D0D02A84159839D5008E62BD /* parse_util.cpp in Sources */, - D0D02A85159839D5008E62BD /* path.cpp in Sources */, - D052D80B1868F7FC003ABCBD /* parse_execution.cpp in Sources */, - D0D02A86159839D5008E62BD /* postfork.cpp in Sources */, - D0D02A87159839D5008E62BD /* screen.cpp in Sources */, - D05F59861F041AE4003EE978 /* builtin_return.cpp in Sources */, - D0D02A88159839D5008E62BD /* signal.cpp in Sources */, - D05F598C1F041AE4003EE978 /* builtin_read.cpp in Sources */, - D0C9733818DE5449002D7C81 /* utf8.cpp in Sources */, - D0D2694915983772005D9B9C /* function.cpp in Sources */, - D05F59C21F041AE4003EE978 /* builtin_builtin.cpp in Sources */, - D05F59B91F041AE4003EE978 /* builtin_commandline.cpp in Sources */, - D05F59BF1F041AE4003EE978 /* builtin_cd.cpp in Sources */, - D05F596E1F041AE4003EE978 /* builtin.cpp in Sources */, - D0D02A67159837AD008E62BD /* complete.cpp in Sources */, - D05F59981F041AE4003EE978 /* builtin_jobs.cpp in Sources */, - D0D02A69159837B2008E62BD /* env.cpp in Sources */, - D0D02A6A1598381A008E62BD /* exec.cpp in Sources */, - D0F5B46519CFCDE80090665E /* wcstringutil.cpp in Sources */, - D05F599E1F041AE4003EE978 /* builtin_functions.cpp in Sources */, - D0D02A6B1598381F008E62BD /* expand.cpp in Sources */, - D012436A1CD4018100C64313 /* fallback.cpp in Sources */, - D05F59921F041AE4003EE978 /* builtin_pwd.cpp in Sources */, - D00F63F119137E9D00FCCDEC /* fish_version.cpp in Sources */, - D0D02A6C15983829008E62BD /* highlight.cpp in Sources */, - D0D02A6D1598382C008E62BD /* history.cpp in Sources */, - D0D02A6E15983838008E62BD /* kill.cpp in Sources */, - D0D02A6F1598383E008E62BD /* parser.cpp in Sources */, - D05F59771F041AE4003EE978 /* builtin_string.cpp in Sources */, - 4F2D55CF2013ECDD00822920 /* tnode.cpp in Sources */, - D05F597A1F041AE4003EE978 /* builtin_status.cpp in Sources */, - D0D02A8F15983D8F008E62BD /* parser_keywords.cpp in Sources */, - D0D02A7015983842008E62BD /* proc.cpp in Sources */, - D0D02A7115983848008E62BD /* reader.cpp in Sources */, - D0D02A721598384C008E62BD /* sanity.cpp in Sources */, - CB0F034C1F156FE3001827D3 /* builtin_argparse.cpp in Sources */, - D05F597D1F041AE4003EE978 /* builtin_source.cpp in Sources */, - D05F59B31F041AE4003EE978 /* builtin_contains.cpp in Sources */, - D05F598F1F041AE4003EE978 /* builtin_random.cpp in Sources */, - D0D02A7315983852008E62BD /* tokenizer.cpp in Sources */, - D05F59741F041AE4003EE978 /* builtin_test.cpp in Sources */, - D05F599B1F041AE4003EE978 /* builtin_history.cpp in Sources */, - D0D02A7415983857008E62BD /* wildcard.cpp in Sources */, - D05F59B61F041AE4003EE978 /* builtin_complete.cpp in Sources */, - D0D02A751598385E008E62BD /* wgetopt.cpp in Sources */, - D0D02A7615983869008E62BD /* wutil.cpp in Sources */, - D0D02A7715983875008E62BD /* input.cpp in Sources */, - D0D02A781598387E008E62BD /* output.cpp in Sources */, - D05F59801F041AE4003EE978 /* builtin_set.cpp in Sources */, - D0D02A7915983888008E62BD /* intern.cpp in Sources */, - D0D02A7B15983928008E62BD /* env_universal_common.cpp in Sources */, - D05F59A71F041AE4003EE978 /* builtin_exit.cpp in Sources */, - D032388B1849D1980032CF2C /* pager.cpp in Sources */, - D05F59831F041AE4003EE978 /* builtin_set_color.cpp in Sources */, - D0D02A89159839DF008E62BD /* fish.cpp in Sources */, - D0C52F371765284C00BFAB82 /* parse_tree.cpp in Sources */, - D0FE8EE8179FB760008C9F21 /* parse_productions.cpp in Sources */, - D01243681CD4015600C64313 /* util.cpp in Sources */, - D046A0FA2070245000C8DFF7 /* tinyexpr.c in Sources */, - D05F59891F041AE4003EE978 /* builtin_realpath.cpp in Sources */, - D04C863920B3D83900C675A6 /* future_feature_flags.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0F01A0215A9788B0034B3B1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D0F01A0315A978910034B3B1 /* osx_fish_launcher.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 9C7A55311DCD71330049C25D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D008D0C41BC58F8800841177 /* generate-version-header */; - targetProxy = 9C7A55321DCD71330049C25D /* PBXContainerItemProxy */; - }; - 9C7A55331DCD71330049C25D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D04F7FCF1BA4E29300B0F227 /* pcre2 */; - targetProxy = 9C7A55341DCD71330049C25D /* PBXContainerItemProxy */; - }; - 9C7A55801DCD73930049C25D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 9C7A55301DCD71330049C25D /* fish_key_reader */; - targetProxy = 9C7A557F1DCD73930049C25D /* PBXContainerItemProxy */; - }; - D008D0CB1BC58FDD00841177 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D008D0C41BC58F8800841177 /* generate-version-header */; - targetProxy = D008D0CA1BC58FDD00841177 /* PBXContainerItemProxy */; - }; - D008D0CD1BC58FE100841177 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D008D0C41BC58F8800841177 /* generate-version-header */; - targetProxy = D008D0CC1BC58FE100841177 /* PBXContainerItemProxy */; - }; - D008D0CF1BC58FE500841177 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D008D0C41BC58F8800841177 /* generate-version-header */; - targetProxy = D008D0CE1BC58FE500841177 /* PBXContainerItemProxy */; - }; - D031890815E36CC000D9CC39 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D0F019EC15A976F30034B3B1 /* base */; - targetProxy = D031890715E36CC000D9CC39 /* PBXContainerItemProxy */; - }; - D04F7FEB1BA4E3DB00B0F227 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D04F7FCF1BA4E29300B0F227 /* pcre2 */; - targetProxy = D04F7FEA1BA4E3DB00B0F227 /* PBXContainerItemProxy */; - }; - D04F7FED1BA4E3DF00B0F227 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D04F7FCF1BA4E29300B0F227 /* pcre2 */; - targetProxy = D04F7FEC1BA4E3DF00B0F227 /* PBXContainerItemProxy */; - }; - D07D265715E33B86009E43F6 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D0D2693B159835CA005D9B9C /* fish_shell */; - targetProxy = D07D265815E33B86009E43F6 /* PBXContainerItemProxy */; - }; - D07D265D15E33B86009E43F6 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D0D02ACF1598642A008E62BD /* fish_indent */; - targetProxy = D07D265E15E33B86009E43F6 /* PBXContainerItemProxy */; - }; - D0A564EF168D09C000AF6161 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D0A564E6168CFDD800AF6161 /* man_pages */; - targetProxy = D0A564EE168D09C000AF6161 /* PBXContainerItemProxy */; - }; - D0A56500168D257900AF6161 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D0A564E6168CFDD800AF6161 /* man_pages */; - targetProxy = D0A564FF168D257900AF6161 /* PBXContainerItemProxy */; - }; - D0F01A1315AA36280034B3B1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D0D2693B159835CA005D9B9C /* fish_shell */; - targetProxy = D0F01A1215AA36280034B3B1 /* PBXContainerItemProxy */; - }; - D0F01A1715AA36300034B3B1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D0D02ACF1598642A008E62BD /* fish_indent */; - targetProxy = D0F01A1615AA36300034B3B1 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 9C7A556F1DCD71330049C25D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - LLVM_LTO = NO; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 9C7A55701DCD71330049C25D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - LLVM_LTO = YES_THIN; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - D007693F1990137800CA4627 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - LLVM_LTO = NO; - PRODUCT_NAME = fish_tests; - }; - name = Debug; - }; - D00769401990137800CA4627 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = YES; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - LLVM_LTO = YES_THIN; - PRODUCT_NAME = fish_tests; - }; - name = Release; - }; - D008D0C51BC58F8800841177 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - D008D0C61BC58F8800841177 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - D04F7FD21BA4E29300B0F227 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - GCC_INPUT_FILETYPE = sourcecode.c.c; - GCC_PREPROCESSOR_DEFINITIONS = ( - "LOCALEDIR=\\\"/usr/local/share/locale\\\"", - "PREFIX=L\\\"/usr/local\\\"", - "DATADIR=L\\\"/usr/local/share\\\"", - "SYSCONFDIR=L\\\"/usr/local/etc\\\"", - "BINDIR=L\\\"/usr/local/bin\\\"", - "DOCDIR=L\\\"/usr/local/share/doc\\\"", - "PCRE2_CODE_UNIT_WIDTH=32", - "HAVE_CONFIG_H=1", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = NO; - GCC_WARN_UNUSED_VARIABLE = NO; - LLVM_LTO = NO; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/osx/pcre2 $(SRCROOT)/osx/shared_headers/"; - USE_HEADERMAP = NO; - WARNING_CFLAGS = "-Wno-unreachable-code"; - }; - name = Debug; - }; - D04F7FD31BA4E29300B0F227 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - GCC_INPUT_FILETYPE = sourcecode.c.c; - GCC_PREPROCESSOR_DEFINITIONS = ( - "LOCALEDIR=\\\"/usr/local/share/locale\\\"", - "PREFIX=L\\\"/usr/local\\\"", - "DATADIR=L\\\"/usr/local/share\\\"", - "SYSCONFDIR=L\\\"/usr/local/etc\\\"", - "BINDIR=L\\\"/usr/local/bin\\\"", - "DOCDIR=L\\\"/usr/local/share/doc\\\"", - "PCRE2_CODE_UNIT_WIDTH=32", - "HAVE_CONFIG_H=1", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = NO; - GCC_WARN_UNUSED_VARIABLE = NO; - LLVM_LTO = YES_THIN; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/osx/pcre2 $(SRCROOT)/osx/shared_headers/"; - USE_HEADERMAP = NO; - WARNING_CFLAGS = "-Wno-unreachable-code"; - }; - name = Release; - }; - D07D267015E33B86009E43F6 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - INSTALL_PATH = /usr/local; - PRODUCT_NAME = "base copy"; - SKIP_INSTALL = NO; - }; - name = Debug; - }; - D07D267115E33B86009E43F6 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - INSTALL_PATH = /usr/local; - PRODUCT_NAME = "base copy"; - SKIP_INSTALL = NO; - }; - name = Release; - }; - D0A084F813B3AC130099B651 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_ENABLE_CPP_EXCEPTIONS = NO; - GCC_ENABLE_CPP_RTTI = YES; - GCC_INLINES_ARE_PRIVATE_EXTERN = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "LOCALEDIR=\\\"/usr/local/share/locale\\\"", - "PREFIX=L\\\"/usr/local\\\"", - "DATADIR=L\\\"/usr/local/share\\\"", - "SYSCONFDIR=L\\\"/usr/local/etc\\\"", - "BINDIR=L\\\"/usr/local/bin\\\"", - "DOCDIR=L\\\"/usr/local/share/doc\\\"", - "_UNICODE=1", - ); - 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_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_LABEL = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.7; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - USER_HEADER_SEARCH_PATHS = "$(SHARED_DERIVED_FILE_DIR) $(SRCROOT)/osx $(SRCROOT)/osx/shared_headers $(SRCROOT)/muparser-2.2.5/include"; - WARNING_CFLAGS = ( - "-Wall", - "-Wunused-macros", - ); - }; - name = Debug; - }; - D0A084F913B3AC130099B651 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - DEAD_CODE_STRIPPING = YES; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_ENABLE_CPP_EXCEPTIONS = NO; - GCC_ENABLE_CPP_RTTI = YES; - GCC_INLINES_ARE_PRIVATE_EXTERN = YES; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "LOCALEDIR=\\\"/usr/local/share/locale\\\"", - "PREFIX=L\\\"/usr/local\\\"", - "DATADIR=L\\\"/usr/local/share\\\"", - "SYSCONFDIR=L\\\"/usr/local/etc\\\"", - "BINDIR=L\\\"/usr/local/bin\\\"", - "DOCDIR=L\\\"/usr/local/share/doc\\\"", - "_UNICODE=1", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_MISSING_NEWLINE = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_LABEL = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.7; - SDKROOT = macosx; - USER_HEADER_SEARCH_PATHS = "$(SHARED_DERIVED_FILE_DIR) $(SRCROOT)/osx $(SRCROOT)/osx/shared_headers $(SRCROOT)/muparser-2.2.5/include"; - WARNING_CFLAGS = ( - "-Wall", - "-Wunused-macros", - ); - }; - name = Release; - }; - D0A564E7168CFDD800AF6161 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - D0A564E8168CFDD800AF6161 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - D0D02AA515985A75008E62BD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - EXECUTABLE_NAME = fish_launcher; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_UNINITIALIZED_AUTOS = YES; - INFOPLIST_FILE = osx/Info.plist; - LLVM_LTO = NO; - PRODUCT_BUNDLE_IDENTIFIER = "com.ridiculousfish.fish-shell"; - PRODUCT_NAME = fish; - WRAPPER_EXTENSION = app; - }; - name = Debug; - }; - D0D02AA615985A75008E62BD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = YES; - EXECUTABLE_NAME = fish_launcher; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - INFOPLIST_FILE = osx/Info.plist; - LLVM_LTO = YES_THIN; - PRODUCT_BUNDLE_IDENTIFIER = "com.ridiculousfish.fish-shell"; - PRODUCT_NAME = fish; - WRAPPER_EXTENSION = app; - }; - name = Release; - }; - D0D02AD41598642A008E62BD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_UNINITIALIZED_AUTOS = YES; - LLVM_LTO = NO; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - D0D02AD51598642A008E62BD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - COPY_PHASE_STRIP = YES; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - LLVM_LTO = YES_THIN; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - D0D26944159835CA005D9B9C /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - LLVM_LTO = NO; - PRODUCT_NAME = fish; - }; - name = Debug; - }; - D0D26945159835CA005D9B9C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_OBJC_EXCEPTIONS = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - LLVM_LTO = YES_THIN; - PRODUCT_NAME = fish; - }; - name = Release; - }; - D0F019EE15A976F30034B3B1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - INSTALL_PATH = /usr/local; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = NO; - }; - name = Debug; - }; - D0F019EF15A976F30034B3B1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - INSTALL_PATH = /usr/local; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = NO; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 9C7A556E1DCD71330049C25D /* Build configuration list for PBXNativeTarget "fish_key_reader" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 9C7A556F1DCD71330049C25D /* Debug */, - 9C7A55701DCD71330049C25D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D007693E1990137800CA4627 /* Build configuration list for PBXNativeTarget "fish_tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D007693F1990137800CA4627 /* Debug */, - D00769401990137800CA4627 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D008D0C81BC58F8800841177 /* Build configuration list for PBXAggregateTarget "generate-version-header" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D008D0C51BC58F8800841177 /* Debug */, - D008D0C61BC58F8800841177 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D04F7FD11BA4E29300B0F227 /* Build configuration list for PBXNativeTarget "pcre2" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D04F7FD21BA4E29300B0F227 /* Debug */, - D04F7FD31BA4E29300B0F227 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D07D266F15E33B86009E43F6 /* Build configuration list for PBXAggregateTarget "install_tree" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D07D267015E33B86009E43F6 /* Debug */, - D07D267115E33B86009E43F6 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D0A084F513B3AC130099B651 /* Build configuration list for PBXProject "fish" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D0A084F813B3AC130099B651 /* Debug */, - D0A084F913B3AC130099B651 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D0A564E9168CFDD800AF6161 /* Build configuration list for PBXAggregateTarget "man_pages" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D0A564E7168CFDD800AF6161 /* Debug */, - D0A564E8168CFDD800AF6161 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D0D02AA415985A75008E62BD /* Build configuration list for PBXNativeTarget "fish.app" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D0D02AA515985A75008E62BD /* Debug */, - D0D02AA615985A75008E62BD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D0D02AD31598642A008E62BD /* Build configuration list for PBXNativeTarget "fish_indent" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D0D02AD41598642A008E62BD /* Debug */, - D0D02AD51598642A008E62BD /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D0D26943159835CA005D9B9C /* Build configuration list for PBXNativeTarget "fish_shell" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D0D26944159835CA005D9B9C /* Debug */, - D0D26945159835CA005D9B9C /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D0F019ED15A976F30034B3B1 /* Build configuration list for PBXAggregateTarget "base" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D0F019EE15A976F30034B3B1 /* Debug */, - D0F019EF15A976F30034B3B1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = D0A084F213B3AC130099B651 /* Project object */; -} diff --git a/fish.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/fish.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index ce8b5087f..000000000 --- a/fish.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/fish.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/fish.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index 08de0be8d..000000000 --- a/fish.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded - - - diff --git a/fish.xcodeproj/xcshareddata/xcschemes/Makefile.xcscheme b/fish.xcodeproj/xcshareddata/xcschemes/Makefile.xcscheme deleted file mode 100644 index 6706aea40..000000000 --- a/fish.xcodeproj/xcshareddata/xcschemes/Makefile.xcscheme +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fish.xcodeproj/xcshareddata/xcschemes/base.xcscheme b/fish.xcodeproj/xcshareddata/xcschemes/base.xcscheme deleted file mode 100644 index d05da718f..000000000 --- a/fish.xcodeproj/xcshareddata/xcschemes/base.xcscheme +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fish.xcodeproj/xcshareddata/xcschemes/fish.app.xcscheme b/fish.xcodeproj/xcshareddata/xcschemes/fish.app.xcscheme deleted file mode 100644 index 3a13732de..000000000 --- a/fish.xcodeproj/xcshareddata/xcschemes/fish.app.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fish.xcodeproj/xcshareddata/xcschemes/fish_tests.xcscheme b/fish.xcodeproj/xcshareddata/xcschemes/fish_tests.xcscheme deleted file mode 100644 index ba8a033aa..000000000 --- a/fish.xcodeproj/xcshareddata/xcschemes/fish_tests.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fish.xcodeproj/xcshareddata/xcschemes/install_tree.xcscheme b/fish.xcodeproj/xcshareddata/xcschemes/install_tree.xcscheme deleted file mode 100644 index c9421426c..000000000 --- a/fish.xcodeproj/xcshareddata/xcschemes/install_tree.xcscheme +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/osx/Info.plist b/osx/Info.plist deleted file mode 100644 index 2eb4b5a8e..000000000 --- a/osx/Info.plist +++ /dev/null @@ -1,33 +0,0 @@ - - - - - CFBundleDisplayName - fish shell - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIconFile - fish_term_icon - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - fish shell - CFBundlePackageType - APPL - CFBundleShortVersionString - 3.0.0 - CFBundleVersion - 0.1 - LSApplicationCategoryType - public.app-category.productivity - LSMinimumSystemVersion - 10.6 - LSUIElement - - NSHumanReadableCopyright - Copyright © 2012, ridiculous_fish -All rights reserved. - - diff --git a/osx/config.h b/osx/config.h deleted file mode 100644 index cd7f2b746..000000000 --- a/osx/config.h +++ /dev/null @@ -1,295 +0,0 @@ -/* config.h. Generated from config.h.in by configure. */ -/* config.h.in. Generated from configure.ac by autoheader. */ - -/* Define to 1 if you have the `backtrace_symbols' function. */ -#define HAVE_BACKTRACE_SYMBOLS 1 - -/* banana */ -#define HAVE_BROKEN_WCWIDTH 1 - -/* Define to 1 if you have the `clock_gettime' function. */ -/* #undef HAVE_CLOCK_GETTIME */ - -/* Define to 1 if you have the `ctermid_r' function. */ -#define HAVE_CTERMID_R 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_CURSES_H 1 - -/* define if the compiler supports basic C++11 syntax */ -#define HAVE_CXX11 1 - -/* Define to 1 if you have the header file, and it defines `DIR'. - */ -#define HAVE_DIRENT_H 1 - -/* Define to 1 if you have the `dirfd' function. */ -#define HAVE_DIRFD 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_EXECINFO_H 1 - -/* Define to 1 if you have the `flock' function. */ -#define HAVE_FLOCK 1 - -/* Define to 1 if you have the `futimens' function. */ -/* #undef HAVE_FUTIMENS */ - -/* Define to 1 if you have the `futimes' function. */ -#define HAVE_FUTIMES 1 - -/* Define to 1 if you have the `getifaddrs' function. */ -#define HAVE_GETIFADDRS 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_GETOPT_H 1 - -/* Define to 1 if you have the `getpwent' function. */ -#define HAVE_GETPWENT 1 - -/* Define to 1 if you have the `gettext' function. */ -/* #undef HAVE_GETTEXT */ - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the `killpg' function. */ -#define HAVE_KILLPG 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_LIBINTL_H */ - -/* Define to 1 if you have the `lrand48_r' function. */ -/* #undef HAVE_LRAND48_R */ - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the `mkostemp' function. */ -/* #undef HAVE_MKOSTEMP */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NCURSES_CURSES_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_NCURSES_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NCURSES_TERM_H */ - -/* Define to 1 if you have the header file, and it defines `DIR'. */ -/* #undef HAVE_NDIR_H */ - -/* Define to 1 if the shm_open() function exists */ -#define HAVE_SHM_OPEN 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SIGINFO_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SPAWN_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `std::make_unique' function. */ -/* #undef HAVE_STD__MAKE_UNIQUE */ - -/* Define to 1 if you have the `std::wcscasecmp' function. */ -/* #undef HAVE_STD__WCSCASECMP */ - -/* Define to 1 if you have the `std::wcsdup' function. */ -/* #undef HAVE_STD__WCSDUP */ - -/* Define to 1 if you have the `std::wcsncasecmp' function. */ -/* #undef HAVE_STD__WCSNCASECMP */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_STROPTS_H */ - -/* Define to 1 if `d_type' is a member of `struct dirent'. */ -#define HAVE_STRUCT_DIRENT_D_TYPE 1 - -/* Define to 1 if `st_ctime_nsec' is a member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_CTIME_NSEC */ - -/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */ -#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 - -/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC */ - -/* Define to 1 if you have the header file, and it defines `DIR'. - */ -/* #undef HAVE_SYS_DIR_H */ - -/* Define to 1 if the sys_errlist array is available. */ -#define HAVE_SYS_ERRLIST 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_IOCTL_H 1 - -/* Define to 1 if you have the header file, and it defines `DIR'. - */ -/* #undef HAVE_SYS_NDIR_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_RESOURCE_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_SELECT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_SYSCTL_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_TERMIOS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_TERM_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to 1 if you have the `wcscasecmp' function. */ -#define HAVE_WCSCASECMP 1 - -/* Define to 1 if you have the `wcsdup' function. */ -#define HAVE_WCSDUP 1 - -/* Define to 1 if you have the `wcslcpy' function. */ -#define HAVE_WCSLCPY 1 - -/* Define to 1 if you have the `wcsncasecmp' function. */ -#define HAVE_WCSNCASECMP 1 - -/* Define to 1 if you have the `wcsndup' function. */ -/* #undef HAVE_WCSNDUP */ - -/* Define to 1 if you have the `wcstod_l' function. */ -#define HAVE_WCSTOD_L 1 - -/* Define to 1 if the winsize struct and TIOCGWINSZ macro exist */ -#define HAVE_WINSIZE 1 - -/* Define to 1 if the _nl_msg_cat_cntr symbol is exported. */ -/* #undef HAVE__NL_MSG_CAT_CNTR */ - -/* Define to 1 if you have the file `/proc/self/stat'. */ -/* #undef HAVE__PROC_SELF_STAT */ - -/* Define to 1 if the _sys_errs array is available. */ -/* #undef HAVE__SYS__ERRS */ - -/* Define to 1 to disable ncurses macros that conflict with the STL */ -#define NCURSES_NOMACROS 1 - -/* Define to 1 to disable curses macros that conflict with the STL */ -#define NOMACROS 1 - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "https://github.com/fish-shell/fish-shell/issues" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "fish" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "fish 3.0.0-git" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "fish" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "3.0.0-git" - -/* The size of `wchar_t', as computed by sizeof. */ -#define SIZEOF_WCHAR_T 4 - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define to 1 if tparm accepts a fixed amount of paramters. */ -/* #undef TPARM_SOLARIS_KLUDGE */ - -/* 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 - -/* Enable large inode numbers on Mac OS X 10.5. */ -#ifndef _DARWIN_USE_64_BIT_INODE -# define _DARWIN_USE_64_BIT_INODE 1 -#endif - -/* Number of bits in a file offset, on hosts where this is settable. */ -/* #undef _FILE_OFFSET_BITS */ - -/* Define for large files, on AIX-style hosts. */ -/* #undef _LARGE_FILES */ - -/* Define to 1 if on MINIX. */ -/* #undef _MINIX */ - -/* 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 */ - -#if __GNUC__ >= 3 -#ifndef __warn_unused -#define __warn_unused __attribute__ ((warn_unused_result)) -#endif -#ifndef __sentinel -#define __sentinel __attribute__ ((sentinel)) -#endif -#ifndef __packed -#define __packed __attribute__ ((packed)) -#endif -#else -#define __warn_unused -#define __sentinel -#define __packed -#endif diff --git a/osx/pcre2/config.h b/osx/pcre2/config.h deleted file mode 100644 index c663bd26b..000000000 --- a/osx/pcre2/config.h +++ /dev/null @@ -1,304 +0,0 @@ -/* src/config.h. Generated from config.h.in by configure. */ -/* src/config.h.in. Generated from configure.ac by autoheader. */ - - -/* PCRE2 is written in Standard C, but there are a few non-standard things it -can cope with, allowing it to run on SunOS4 and other "close to standard" -systems. - -In environments that support the GNU autotools, config.h.in is converted into -config.h by the "configure" script. In environments that use CMake, -config-cmake.in is converted into config.h. If you are going to build PCRE2 "by -hand" without using "configure" or CMake, you should copy the distributed -config.h.generic to config.h, and edit the macro definitions to be the way you -need them. You must then add -DHAVE_CONFIG_H to all of your compile commands, -so that config.h is included at the start of every source. - -Alternatively, you can avoid editing by using -D on the compiler command line -to set the macro values. In this case, you do not have to set -DHAVE_CONFIG_H, -but if you do, default values will be taken from config.h for non-boolean -macros that are not defined on the command line. - -Boolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be defined -(conventionally to 1) for TRUE, and not defined at all for FALSE. All such -macros are listed as a commented #undef in config.h.generic. Macros such as -MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are -surrounded by #ifndef/#endif lines so that the value can be overridden by -D. - -PCRE2 uses memmove() if HAVE_MEMMOVE is defined; otherwise it uses bcopy() if -HAVE_BCOPY is defined. If your system has neither bcopy() nor memmove(), make -sure both macros are undefined; an emulation function will then be used. */ - -/* By default, the \R escape sequence matches any Unicode line ending - character or sequence of characters. If BSR_ANYCRLF is defined (to any - value), this is changed so that backslash-R matches only CR, LF, or CRLF. - The build-time default can be overridden by the user of PCRE2 at runtime. - */ -/* #undef BSR_ANYCRLF */ - -/* If you are compiling for a system that uses EBCDIC instead of ASCII - character codes, define this macro to any value. When EBCDIC is set, PCRE2 - assumes that all input strings are in EBCDIC. If you do not define this - macro, PCRE2 will assume input strings are ASCII or UTF-8/16/32 Unicode. It - is not possible to build a version of PCRE2 that supports both EBCDIC and - UTF-8/16/32. */ -/* #undef EBCDIC */ - -/* In an EBCDIC environment, define this macro to any value to arrange for the - NL character to be 0x25 instead of the default 0x15. NL plays the role that - LF does in an ASCII/Unicode environment. */ -/* #undef EBCDIC_NL25 */ - -/* Define to 1 if you have the `bcopy' function. */ -#define HAVE_BCOPY 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_BZLIB_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_DIRENT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_EDITLINE_READLINE_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_EDIT_READLINE_READLINE_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_LIMITS_H 1 - -/* Define to 1 if you have the `memmove' function. */ -#define HAVE_MEMMOVE 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define if you have POSIX threads libraries and header files. */ -/* #undef HAVE_PTHREAD */ - -/* Have PTHREAD_PRIO_INHERIT. */ -/* #undef HAVE_PTHREAD_PRIO_INHERIT */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_READLINE_HISTORY_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_READLINE_READLINE_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `strerror' function. */ -#define HAVE_STRERROR 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_WAIT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to 1 if the compiler supports simple visibility declarations. */ -#define HAVE_VISIBILITY 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_WINDOWS_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_ZLIB_H 1 - -/* PCRE2 uses recursive function calls to handle backtracking while matching. - This can sometimes be a problem on systems that have stacks of limited - size. Define HEAP_MATCH_RECURSE to any value to get a version that doesn't - use recursion in the match() function; instead it creates its own stack by - steam using memory from the heap. For more detail, see the comments and - other stuff just above the match() function. */ -/* #undef HEAP_MATCH_RECURSE */ - -/* 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 allows for - compiled patterns up to 64K long. This covers the vast majority of cases. - However, PCRE2 can also be compiled to use 3 or 4 bytes instead. This - allows for longer patterns in extreme cases. */ -#define LINK_SIZE 2 - -/* Define to the sub-directory where libtool stores uninstalled libraries. */ -#define LT_OBJDIR ".libs/" - -/* The value of MATCH_LIMIT determines the default number of times the - internal match() function can be called during a single execution of - pcre2_match(). There is a runtime interface for setting a different limit. - The limit exists in order to catch runaway regular expressions that take - for ever to determine that they do not match. The default is set very large - so that it does not accidentally catch legitimate cases. */ -#define MATCH_LIMIT 10000000 - -/* The above limit applies to all calls of match(), whether or not they - increase the recursion depth. In some environments it is desirable to limit - the depth of recursive calls of match() more strictly, in order to restrict - the maximum amount of stack (or heap, if HEAP_MATCH_RECURSE is defined) - that is used. The value of MATCH_LIMIT_RECURSION applies only to recursive - calls of match(). To have any useful effect, it must be less than the value - of MATCH_LIMIT. The default is to use the same value as MATCH_LIMIT. There - is a runtime method for setting a different limit. */ -#define MATCH_LIMIT_RECURSION MATCH_LIMIT - -/* This limit is parameterized just in case anybody ever wants to change it. - Care must be taken if it is increased, because it guards against integer - overflow caused by enormously large patterns. */ -#define MAX_NAME_COUNT 10000 - -/* This limit is parameterized just in case anybody ever wants to change it. - Care must be taken if it is increased, because it guards against integer - overflow caused by enormously large patterns. */ -#define MAX_NAME_SIZE 32 - -/* 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 - (ANYCRLF). */ -#define NEWLINE_DEFAULT 2 - -/* Name of package */ -#define PACKAGE "pcre2" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "PCRE2" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "PCRE2 10.22" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "pcre2" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "10.22" - -/* 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 - stack that is used while compiling a pattern. */ -#define PARENS_NEST_LIMIT 250 - -/* The value of PCRE2GREP_BUFSIZE determines the size of buffer used by - pcre2grep to hold parts of the file it is searching. This is also the - minimum value. The actual amount of memory used by pcre2grep is three times - this number, because it allows for the buffering of "before" and "after" - lines. */ -#define PCRE2GREP_BUFSIZE 20480 - -/* to make a symbol visible */ -#define PCRE2POSIX_EXP_DECL extern __attribute__ ((visibility ("default"))) - -/* to make a symbol visible */ -#define PCRE2POSIX_EXP_DEFN extern __attribute__ ((visibility ("default"))) - -/* Define to any value to include debugging code. */ -/* #undef PCRE2_DEBUG */ - -/* to make a symbol visible */ -#define PCRE2_EXP_DECL extern __attribute__ ((visibility ("default"))) - - -/* If you are compiling for a system other than a Unix-like system or - Win32, and it needs some magic to be inserted before the definition - of a function that is exported by the library, define this macro to - contain the relevant magic. If you do not define this macro, a suitable - __declspec value is used for Windows systems; in other environments - "extern" is used for a C compiler and "extern C" for a C++ compiler. - This macro apears at the start of every exported function that is part - of the external API. It does not appear on functions that are "external" - in the C sense, but which are internal to the library. */ -#define PCRE2_EXP_DEFN __attribute__ ((visibility ("default"))) - -/* Define to any value if linking statically (TODO: make nice with Libtool) */ -/* #undef PCRE2_STATIC */ - -/* Define to necessary symbol if this constant uses a non-standard name on - your system. */ -/* #undef PTHREAD_CREATE_JOINABLE */ - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define to any value to enable support for Just-In-Time compiling. */ -/* #undef SUPPORT_JIT */ - -/* Define to any value to allow pcre2grep to be linked with libbz2, so that it - is able to handle .bz2 files. */ -/* #undef SUPPORT_LIBBZ2 */ - -/* Define to any value to allow pcre2test to be linked with libedit. */ -/* #undef SUPPORT_LIBEDIT */ - -/* Define to any value to allow pcre2test to be linked with libreadline. */ -/* #undef SUPPORT_LIBREADLINE */ - -/* Define to any value to allow pcre2grep to be linked with libz, so that it - is able to handle .gz files. */ -/* #undef SUPPORT_LIBZ */ - -/* Define to any value to enable callout script support in pcre2grep. */ -#define SUPPORT_PCRE2GREP_CALLOUT /**/ - -/* Define to any value to enable JIT support in pcre2grep. */ -/* #undef SUPPORT_PCRE2GREP_JIT */ - -/* Define to any value to enable the 16 bit PCRE2 library. */ -/* #undef SUPPORT_PCRE2_16 */ - -/* Define to any value to enable the 32 bit PCRE2 library. */ -/* #undef SUPPORT_PCRE2_32 */ - -/* Define to any value to enable the 8 bit PCRE2 library. */ -#define SUPPORT_PCRE2_8 /**/ - -/* Define to any value to enable support for Unicode and UTF encoding. This - will work even in an EBCDIC environment, but it is incompatible with the - EBCDIC macro. That is, PCRE2 can support *either* EBCDIC code *or* - ASCII/Unicode, but not both at once. */ -#define SUPPORT_UNICODE /**/ - -/* Define to any value for valgrind support to find invalid memory reads. */ -/* #undef SUPPORT_VALGRIND */ - -/* Version number of package */ -#define VERSION "10.22" - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ - -/* Define to the type of a signed integer type of width exactly 64 bits if - such a type exists and the standard includes do not define it. */ -/* #undef int64_t */ - -/* Define to `unsigned int' if does not define. */ -/* #undef size_t */ diff --git a/osx/shared_headers/pcre2.h b/osx/shared_headers/pcre2.h deleted file mode 100644 index 20d221b80..000000000 --- a/osx/shared_headers/pcre2.h +++ /dev/null @@ -1,732 +0,0 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - -/* This is the public header file for the PCRE library, second API, to be -#included by applications that call PCRE2 functions. - - 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. ------------------------------------------------------------------------------ -*/ - -#ifndef _PCRE2_H -#define _PCRE2_H - -/* The current PCRE version information. */ - -#define PCRE2_MAJOR 10 -#define PCRE2_MINOR 22 -#define PCRE2_PRERELEASE -#define PCRE2_DATE 2016-07-29 - -/* 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 -export setting is defined in pcre2_internal.h, which includes this file. So we -don't change existing definitions of PCRE2_EXP_DECL. */ - -#if defined(_WIN32) && !defined(PCRE2_STATIC) -# ifndef PCRE2_EXP_DECL -# define PCRE2_EXP_DECL extern __declspec(dllimport) -# endif -#endif - -/* By default, we use the standard "extern" declarations. */ - -#ifndef PCRE2_EXP_DECL -# ifdef __cplusplus -# define PCRE2_EXP_DECL extern "C" -# else -# define PCRE2_EXP_DECL extern -# endif -#endif - -/* Have to include limits.h, stdlib.h and stdint.h to ensure that size_t and -uint8_t, UCHAR_MAX, etc are defined. */ - -#include -#include -#include - -/* Allow for C++ users compiling this directly. */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* The following option bits can be passed to pcre2_compile(), pcre2_match(), -or pcre2_dfa_match(). PCRE2_NO_UTF_CHECK affects only the function to which it -is passed. Put these bits at the most significant end of the options word so -others can be added next to them */ - -#define PCRE2_ANCHORED 0x80000000u -#define PCRE2_NO_UTF_CHECK 0x40000000u - -/* The following option bits can be passed only to pcre2_compile(). However, -they may affect compilation, JIT compilation, and/or interpretive execution. -The following tags indicate which: - -C alters what is compiled by pcre2_compile() -J alters what is compiled by pcre2_jit_compile() -M is inspected during pcre2_match() execution -D is inspected during pcre2_dfa_match() execution -*/ - -#define PCRE2_ALLOW_EMPTY_CLASS 0x00000001u /* C */ -#define PCRE2_ALT_BSUX 0x00000002u /* C */ -#define PCRE2_AUTO_CALLOUT 0x00000004u /* C */ -#define PCRE2_CASELESS 0x00000008u /* C */ -#define PCRE2_DOLLAR_ENDONLY 0x00000010u /* J M D */ -#define PCRE2_DOTALL 0x00000020u /* C */ -#define PCRE2_DUPNAMES 0x00000040u /* C */ -#define PCRE2_EXTENDED 0x00000080u /* C */ -#define PCRE2_FIRSTLINE 0x00000100u /* J M D */ -#define PCRE2_MATCH_UNSET_BACKREF 0x00000200u /* C J M */ -#define PCRE2_MULTILINE 0x00000400u /* C */ -#define PCRE2_NEVER_UCP 0x00000800u /* C */ -#define PCRE2_NEVER_UTF 0x00001000u /* C */ -#define PCRE2_NO_AUTO_CAPTURE 0x00002000u /* C */ -#define PCRE2_NO_AUTO_POSSESS 0x00004000u /* C */ -#define PCRE2_NO_DOTSTAR_ANCHOR 0x00008000u /* C */ -#define PCRE2_NO_START_OPTIMIZE 0x00010000u /* J M D */ -#define PCRE2_UCP 0x00020000u /* C J M D */ -#define PCRE2_UNGREEDY 0x00040000u /* C */ -#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(). */ - -#define PCRE2_JIT_COMPLETE 0x00000001u /* For full matching */ -#define PCRE2_JIT_PARTIAL_SOFT 0x00000002u -#define PCRE2_JIT_PARTIAL_HARD 0x00000004u - -/* These are for pcre2_match(), pcre2_dfa_match(), and pcre2_jit_match(). Note -that PCRE2_ANCHORED and PCRE2_NO_UTF_CHECK can also be passed to these -functions (though pcre2_jit_match() ignores the latter since it bypasses all -sanity checks). */ - -#define PCRE2_NOTBOL 0x00000001u -#define PCRE2_NOTEOL 0x00000002u -#define PCRE2_NOTEMPTY 0x00000004u /* ) These two must be kept */ -#define PCRE2_NOTEMPTY_ATSTART 0x00000008u /* ) adjacent to each other. */ -#define PCRE2_PARTIAL_SOFT 0x00000010u -#define PCRE2_PARTIAL_HARD 0x00000020u - -/* These are additional options for pcre2_dfa_match(). */ - -#define PCRE2_DFA_RESTART 0x00000040u -#define PCRE2_DFA_SHORTEST 0x00000080u - -/* These are additional options for pcre2_substitute(), which passes any others -through to pcre2_match(). */ - -#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 - -/* A further option for pcre2_match(), not allowed for pcre2_dfa_match(), -ignored for pcre2_jit_match(). */ - -#define PCRE2_NO_JIT 0x00002000u - -/* 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 -greater than zero. */ - -#define PCRE2_NEWLINE_CR 1 -#define PCRE2_NEWLINE_LF 2 -#define PCRE2_NEWLINE_CRLF 3 -#define PCRE2_NEWLINE_ANY 4 -#define PCRE2_NEWLINE_ANYCRLF 5 - -#define PCRE2_BSR_UNICODE 1 -#define PCRE2_BSR_ANYCRLF 2 - -/* Error codes: no match and partial match are "expected" errors. */ - -#define PCRE2_ERROR_NOMATCH (-1) -#define PCRE2_ERROR_PARTIAL (-2) - -/* Error codes for UTF-8 validity checks */ - -#define PCRE2_ERROR_UTF8_ERR1 (-3) -#define PCRE2_ERROR_UTF8_ERR2 (-4) -#define PCRE2_ERROR_UTF8_ERR3 (-5) -#define PCRE2_ERROR_UTF8_ERR4 (-6) -#define PCRE2_ERROR_UTF8_ERR5 (-7) -#define PCRE2_ERROR_UTF8_ERR6 (-8) -#define PCRE2_ERROR_UTF8_ERR7 (-9) -#define PCRE2_ERROR_UTF8_ERR8 (-10) -#define PCRE2_ERROR_UTF8_ERR9 (-11) -#define PCRE2_ERROR_UTF8_ERR10 (-12) -#define PCRE2_ERROR_UTF8_ERR11 (-13) -#define PCRE2_ERROR_UTF8_ERR12 (-14) -#define PCRE2_ERROR_UTF8_ERR13 (-15) -#define PCRE2_ERROR_UTF8_ERR14 (-16) -#define PCRE2_ERROR_UTF8_ERR15 (-17) -#define PCRE2_ERROR_UTF8_ERR16 (-18) -#define PCRE2_ERROR_UTF8_ERR17 (-19) -#define PCRE2_ERROR_UTF8_ERR18 (-20) -#define PCRE2_ERROR_UTF8_ERR19 (-21) -#define PCRE2_ERROR_UTF8_ERR20 (-22) -#define PCRE2_ERROR_UTF8_ERR21 (-23) - -/* Error codes for UTF-16 validity checks */ - -#define PCRE2_ERROR_UTF16_ERR1 (-24) -#define PCRE2_ERROR_UTF16_ERR2 (-25) -#define PCRE2_ERROR_UTF16_ERR3 (-26) - -/* Error codes for UTF-32 validity checks */ - -#define PCRE2_ERROR_UTF32_ERR1 (-27) -#define PCRE2_ERROR_UTF32_ERR2 (-28) - -/* Error codes for pcre2[_dfa]_match(), substring extraction functions, context -functions, and serializing functions. They are in numerical order. Originally -they were in alphabetical order too, but now that PCRE2 is released, the -numbers must not be changed. */ - -#define PCRE2_ERROR_BADDATA (-29) -#define PCRE2_ERROR_MIXEDTABLES (-30) /* Name was changed */ -#define PCRE2_ERROR_BADMAGIC (-31) -#define PCRE2_ERROR_BADMODE (-32) -#define PCRE2_ERROR_BADOFFSET (-33) -#define PCRE2_ERROR_BADOPTION (-34) -#define PCRE2_ERROR_BADREPLACEMENT (-35) -#define PCRE2_ERROR_BADUTFOFFSET (-36) -#define PCRE2_ERROR_CALLOUT (-37) /* Never used by PCRE2 itself */ -#define PCRE2_ERROR_DFA_BADRESTART (-38) -#define PCRE2_ERROR_DFA_RECURSE (-39) -#define PCRE2_ERROR_DFA_UCOND (-40) -#define PCRE2_ERROR_DFA_UFUNC (-41) -#define PCRE2_ERROR_DFA_UITEM (-42) -#define PCRE2_ERROR_DFA_WSSIZE (-43) -#define PCRE2_ERROR_INTERNAL (-44) -#define PCRE2_ERROR_JIT_BADOPTION (-45) -#define PCRE2_ERROR_JIT_STACKLIMIT (-46) -#define PCRE2_ERROR_MATCHLIMIT (-47) -#define PCRE2_ERROR_NOMEMORY (-48) -#define PCRE2_ERROR_NOSUBSTRING (-49) -#define PCRE2_ERROR_NOUNIQUESUBSTRING (-50) -#define PCRE2_ERROR_NULL (-51) -#define PCRE2_ERROR_RECURSELOOP (-52) -#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) -#define PCRE2_ERROR_BADSERIALIZEDDATA (-62) - -/* Request types for pcre2_pattern_info() */ - -#define PCRE2_INFO_ALLOPTIONS 0 -#define PCRE2_INFO_ARGOPTIONS 1 -#define PCRE2_INFO_BACKREFMAX 2 -#define PCRE2_INFO_BSR 3 -#define PCRE2_INFO_CAPTURECOUNT 4 -#define PCRE2_INFO_FIRSTCODEUNIT 5 -#define PCRE2_INFO_FIRSTCODETYPE 6 -#define PCRE2_INFO_FIRSTBITMAP 7 -#define PCRE2_INFO_HASCRORLF 8 -#define PCRE2_INFO_JCHANGED 9 -#define PCRE2_INFO_JITSIZE 10 -#define PCRE2_INFO_LASTCODEUNIT 11 -#define PCRE2_INFO_LASTCODETYPE 12 -#define PCRE2_INFO_MATCHEMPTY 13 -#define PCRE2_INFO_MATCHLIMIT 14 -#define PCRE2_INFO_MAXLOOKBEHIND 15 -#define PCRE2_INFO_MINLENGTH 16 -#define PCRE2_INFO_NAMECOUNT 17 -#define PCRE2_INFO_NAMEENTRYSIZE 18 -#define PCRE2_INFO_NAMETABLE 19 -#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(). */ - -#define PCRE2_CONFIG_BSR 0 -#define PCRE2_CONFIG_JIT 1 -#define PCRE2_CONFIG_JITTARGET 2 -#define PCRE2_CONFIG_LINKSIZE 3 -#define PCRE2_CONFIG_MATCHLIMIT 4 -#define PCRE2_CONFIG_NEWLINE 5 -#define PCRE2_CONFIG_PARENSLIMIT 6 -#define PCRE2_CONFIG_RECURSIONLIMIT 7 -#define PCRE2_CONFIG_STACKRECURSE 8 -#define PCRE2_CONFIG_UNICODE 9 -#define PCRE2_CONFIG_UNICODE_VERSION 10 -#define PCRE2_CONFIG_VERSION 11 - -/* Types for code units in patterns and subject strings. */ - -typedef uint8_t PCRE2_UCHAR8; -typedef uint16_t PCRE2_UCHAR16; -typedef uint32_t PCRE2_UCHAR32; - -typedef const PCRE2_UCHAR8 *PCRE2_SPTR8; -typedef const PCRE2_UCHAR16 *PCRE2_SPTR16; -typedef const PCRE2_UCHAR32 *PCRE2_SPTR32; - -/* The PCRE2_SIZE type is used for all string lengths and offsets in PCRE2, -including pattern offsets for errors and subject offsets after a match. We -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) - -/* Generic types for opaque structures and JIT callback functions. These -declarations are defined in a macro that is expanded for each width later. */ - -#define PCRE2_TYPES_LIST \ -struct pcre2_real_general_context; \ -typedef struct pcre2_real_general_context pcre2_general_context; \ -\ -struct pcre2_real_compile_context; \ -typedef struct pcre2_real_compile_context pcre2_compile_context; \ -\ -struct pcre2_real_match_context; \ -typedef struct pcre2_real_match_context pcre2_match_context; \ -\ -struct pcre2_real_code; \ -typedef struct pcre2_real_code pcre2_code; \ -\ -struct pcre2_real_match_data; \ -typedef struct pcre2_real_match_data pcre2_match_data; \ -\ -struct pcre2_real_jit_stack; \ -typedef struct pcre2_real_jit_stack pcre2_jit_stack; \ -\ -typedef pcre2_jit_stack *(*pcre2_jit_callback)(void *); - - -/* The structure for passing out data via the pcre_callout_function. We use a -structure so that new fields can be added on the end in future versions, -without changing the API of the function, thereby allowing old clients to work -without modification. Define the generic version in a macro; the width-specific -versions are generated from this macro below. */ - -#define PCRE2_STRUCTURE_LIST \ -typedef struct pcre2_callout_block { \ - uint32_t version; /* Identifies version of block */ \ - /* ------------------------ Version 0 ------------------------------- */ \ - uint32_t callout_number; /* Number compiled into pattern */ \ - uint32_t capture_top; /* Max current capture */ \ - uint32_t capture_last; /* Most recently closed capture */ \ - PCRE2_SIZE *offset_vector; /* The offset vector */ \ - PCRE2_SPTR mark; /* Pointer to current mark or NULL */ \ - PCRE2_SPTR subject; /* The subject being matched */ \ - PCRE2_SIZE subject_length; /* The length of the subject */ \ - PCRE2_SIZE start_match; /* Offset to start of this match attempt */ \ - PCRE2_SIZE current_position; /* Where we currently are in the subject */ \ - PCRE2_SIZE pattern_position; /* Offset to next item in the pattern */ \ - PCRE2_SIZE next_item_length; /* Length of next item in the pattern */ \ - /* ------------------- Added for Version 1 -------------------------- */ \ - PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \ - PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \ - PCRE2_SPTR callout_string; /* String compiled into pattern */ \ - /* ------------------------------------------------------------------ */ \ -} pcre2_callout_block; \ -\ -typedef struct pcre2_callout_enumerate_block { \ - uint32_t version; /* Identifies version of block */ \ - /* ------------------------ Version 0 ------------------------------- */ \ - PCRE2_SIZE pattern_position; /* Offset to next item in the pattern */ \ - PCRE2_SIZE next_item_length; /* Length of next item in the pattern */ \ - uint32_t callout_number; /* Number compiled into pattern */ \ - PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \ - PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \ - PCRE2_SPTR callout_string; /* String compiled into pattern */ \ - /* ------------------------------------------------------------------ */ \ -} pcre2_callout_enumerate_block; - - -/* List the generic forms of all other functions in macros, which will be -expanded for each width below. Start with functions that give general -information. */ - -#define PCRE2_GENERAL_INFO_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_config(uint32_t, void *); - - -/* Functions for manipulating contexts. */ - -#define PCRE2_GENERAL_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_general_context *pcre2_general_context_copy(pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_general_context *pcre2_general_context_create( \ - void *(*)(PCRE2_SIZE, void *), \ - void (*)(void *, void *), void *); \ -PCRE2_EXP_DECL void pcre2_general_context_free(pcre2_general_context *); - -#define PCRE2_COMPILE_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_compile_context *pcre2_compile_context_copy(pcre2_compile_context *); \ -PCRE2_EXP_DECL \ - pcre2_compile_context *pcre2_compile_context_create(pcre2_general_context *);\ -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); \ -PCRE2_EXP_DECL int pcre2_set_compile_recursion_guard(\ - pcre2_compile_context *, int (*)(uint32_t, void *), \ - void *); - -#define PCRE2_MATCH_CONTEXT_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_match_context *pcre2_match_context_copy(pcre2_match_context *); \ -PCRE2_EXP_DECL \ - pcre2_match_context *pcre2_match_context_create(pcre2_general_context *); \ -PCRE2_EXP_DECL void pcre2_match_context_free(pcre2_match_context *); \ -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( \ - pcre2_match_context *, void *(*)(PCRE2_SIZE, void *), \ - void (*)(void *, void *), void *); - - -/* Functions concerned with compiling a pattern to PCRE internal code. */ - -#define PCRE2_COMPILE_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_code *pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, \ - int *, PCRE2_SIZE *, pcre2_compile_context *); \ -PCRE2_EXP_DECL void pcre2_code_free(pcre2_code *); \ -PCRE2_EXP_DECL \ - pcre2_code *pcre2_code_copy(const pcre2_code *); - - -/* Functions that give information about a compiled pattern. */ - -#define PCRE2_PATTERN_INFO_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_pattern_info(const pcre2_code *, uint32_t, \ - void *); \ -PCRE2_EXP_DECL int pcre2_callout_enumerate(const pcre2_code *, \ - int (*)(pcre2_callout_enumerate_block *, void *), \ - void *); - - -/* Functions for running a match and inspecting the result. */ - -#define PCRE2_MATCH_FUNCTIONS \ -PCRE2_EXP_DECL \ - pcre2_match_data *pcre2_match_data_create(uint32_t, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_match_data *pcre2_match_data_create_from_pattern(\ - const pcre2_code *, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL int pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, \ - PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *, int *, \ - PCRE2_SIZE); \ -PCRE2_EXP_DECL int pcre2_match(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *); \ -PCRE2_EXP_DECL void pcre2_match_data_free(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SPTR pcre2_get_mark(pcre2_match_data *); \ -PCRE2_EXP_DECL uint32_t pcre2_get_ovector_count(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SIZE *pcre2_get_ovector_pointer(pcre2_match_data *); \ -PCRE2_EXP_DECL PCRE2_SIZE pcre2_get_startchar(pcre2_match_data *); - - -/* Convenience functions for handling matched substrings. */ - -#define PCRE2_SUBSTRING_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_substring_copy_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_UCHAR *, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_copy_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_UCHAR *, PCRE2_SIZE *); \ -PCRE2_EXP_DECL void pcre2_substring_free(PCRE2_UCHAR *); \ -PCRE2_EXP_DECL int pcre2_substring_get_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_UCHAR **, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_get_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_UCHAR **, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_length_byname(pcre2_match_data *, \ - PCRE2_SPTR, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_length_bynumber(pcre2_match_data *, \ - uint32_t, PCRE2_SIZE *); \ -PCRE2_EXP_DECL int pcre2_substring_nametable_scan(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SPTR *, PCRE2_SPTR *); \ -PCRE2_EXP_DECL int pcre2_substring_number_from_name(\ - const pcre2_code *, PCRE2_SPTR); \ -PCRE2_EXP_DECL void pcre2_substring_list_free(PCRE2_SPTR *); \ -PCRE2_EXP_DECL int pcre2_substring_list_get(pcre2_match_data *, \ - PCRE2_UCHAR ***, PCRE2_SIZE **); - -/* Functions for serializing / deserializing compiled patterns. */ - -#define PCRE2_SERIALIZE_FUNCTIONS \ -PCRE2_EXP_DECL int32_t pcre2_serialize_encode(const pcre2_code **, \ - int32_t, uint8_t **, PCRE2_SIZE *, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL int32_t pcre2_serialize_decode(pcre2_code **, int32_t, \ - const uint8_t *, pcre2_general_context *); \ -PCRE2_EXP_DECL int32_t pcre2_serialize_get_number_of_codes(const uint8_t *); \ -PCRE2_EXP_DECL void pcre2_serialize_free(uint8_t *); - - -/* Convenience function for match + substitute. */ - -#define PCRE2_SUBSTITUTE_FUNCTION \ -PCRE2_EXP_DECL int pcre2_substitute(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_UCHAR *, \ - PCRE2_SIZE *); - - -/* Functions for JIT processing */ - -#define PCRE2_JIT_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_jit_compile(pcre2_code *, uint32_t); \ -PCRE2_EXP_DECL int pcre2_jit_match(const pcre2_code *, \ - PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, \ - pcre2_match_data *, pcre2_match_context *); \ -PCRE2_EXP_DECL void pcre2_jit_free_unused_memory(pcre2_general_context *); \ -PCRE2_EXP_DECL \ - pcre2_jit_stack *pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, \ - pcre2_general_context *); \ -PCRE2_EXP_DECL void pcre2_jit_stack_assign(pcre2_match_context *, \ - pcre2_jit_callback, void *); \ -PCRE2_EXP_DECL void pcre2_jit_stack_free(pcre2_jit_stack *); - - -/* Other miscellaneous functions. */ - -#define PCRE2_OTHER_FUNCTIONS \ -PCRE2_EXP_DECL int pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \ -PCRE2_EXP_DECL \ - const uint8_t *pcre2_maketables(pcre2_general_context *); \ - - -/* Define macros that generate width-specific names from generic versions. The -three-level macro scheme is necessary to get the macros expanded when we want -them to be. First we get the width from PCRE2_LOCAL_WIDTH, which is used for -generating three versions of everything below. After that, PCRE2_SUFFIX will be -re-defined to use PCRE2_CODE_UNIT_WIDTH, for use when macros such as -pcre2_compile are called by application code. */ - -#define PCRE2_JOIN(a,b) a ## b -#define PCRE2_GLUE(a,b) PCRE2_JOIN(a,b) -#define PCRE2_SUFFIX(a) PCRE2_GLUE(a,PCRE2_LOCAL_WIDTH) - - -/* Data types */ - -#define PCRE2_UCHAR PCRE2_SUFFIX(PCRE2_UCHAR) -#define PCRE2_SPTR PCRE2_SUFFIX(PCRE2_SPTR) - -#define pcre2_code PCRE2_SUFFIX(pcre2_code_) -#define pcre2_jit_callback PCRE2_SUFFIX(pcre2_jit_callback_) -#define pcre2_jit_stack PCRE2_SUFFIX(pcre2_jit_stack_) - -#define pcre2_real_code PCRE2_SUFFIX(pcre2_real_code_) -#define pcre2_real_general_context PCRE2_SUFFIX(pcre2_real_general_context_) -#define pcre2_real_compile_context PCRE2_SUFFIX(pcre2_real_compile_context_) -#define pcre2_real_match_context PCRE2_SUFFIX(pcre2_real_match_context_) -#define pcre2_real_jit_stack PCRE2_SUFFIX(pcre2_real_jit_stack_) -#define pcre2_real_match_data PCRE2_SUFFIX(pcre2_real_match_data_) - - -/* Data blocks */ - -#define pcre2_callout_block PCRE2_SUFFIX(pcre2_callout_block_) -#define pcre2_callout_enumerate_block PCRE2_SUFFIX(pcre2_callout_enumerate_block_) -#define pcre2_general_context PCRE2_SUFFIX(pcre2_general_context_) -#define pcre2_compile_context PCRE2_SUFFIX(pcre2_compile_context_) -#define pcre2_match_context PCRE2_SUFFIX(pcre2_match_context_) -#define pcre2_match_data PCRE2_SUFFIX(pcre2_match_data_) - - -/* Functions: the complete list in alphabetical order */ - -#define pcre2_callout_enumerate PCRE2_SUFFIX(pcre2_callout_enumerate_) -#define pcre2_code_copy PCRE2_SUFFIX(pcre2_code_copy_) -#define pcre2_code_free PCRE2_SUFFIX(pcre2_code_free_) -#define pcre2_compile PCRE2_SUFFIX(pcre2_compile_) -#define pcre2_compile_context_copy PCRE2_SUFFIX(pcre2_compile_context_copy_) -#define pcre2_compile_context_create PCRE2_SUFFIX(pcre2_compile_context_create_) -#define pcre2_compile_context_free PCRE2_SUFFIX(pcre2_compile_context_free_) -#define pcre2_config PCRE2_SUFFIX(pcre2_config_) -#define pcre2_dfa_match PCRE2_SUFFIX(pcre2_dfa_match_) -#define pcre2_general_context_copy PCRE2_SUFFIX(pcre2_general_context_copy_) -#define pcre2_general_context_create PCRE2_SUFFIX(pcre2_general_context_create_) -#define pcre2_general_context_free PCRE2_SUFFIX(pcre2_general_context_free_) -#define pcre2_get_error_message PCRE2_SUFFIX(pcre2_get_error_message_) -#define pcre2_get_mark PCRE2_SUFFIX(pcre2_get_mark_) -#define pcre2_get_ovector_pointer PCRE2_SUFFIX(pcre2_get_ovector_pointer_) -#define pcre2_get_ovector_count PCRE2_SUFFIX(pcre2_get_ovector_count_) -#define pcre2_get_startchar PCRE2_SUFFIX(pcre2_get_startchar_) -#define pcre2_jit_compile PCRE2_SUFFIX(pcre2_jit_compile_) -#define pcre2_jit_match PCRE2_SUFFIX(pcre2_jit_match_) -#define pcre2_jit_free_unused_memory PCRE2_SUFFIX(pcre2_jit_free_unused_memory_) -#define pcre2_jit_stack_assign PCRE2_SUFFIX(pcre2_jit_stack_assign_) -#define pcre2_jit_stack_create PCRE2_SUFFIX(pcre2_jit_stack_create_) -#define pcre2_jit_stack_free PCRE2_SUFFIX(pcre2_jit_stack_free_) -#define pcre2_maketables PCRE2_SUFFIX(pcre2_maketables_) -#define pcre2_match PCRE2_SUFFIX(pcre2_match_) -#define pcre2_match_context_copy PCRE2_SUFFIX(pcre2_match_context_copy_) -#define pcre2_match_context_create PCRE2_SUFFIX(pcre2_match_context_create_) -#define pcre2_match_context_free PCRE2_SUFFIX(pcre2_match_context_free_) -#define pcre2_match_data_create PCRE2_SUFFIX(pcre2_match_data_create_) -#define pcre2_match_data_create_from_pattern PCRE2_SUFFIX(pcre2_match_data_create_from_pattern_) -#define pcre2_match_data_free PCRE2_SUFFIX(pcre2_match_data_free_) -#define pcre2_pattern_info PCRE2_SUFFIX(pcre2_pattern_info_) -#define pcre2_serialize_decode PCRE2_SUFFIX(pcre2_serialize_decode_) -#define pcre2_serialize_encode PCRE2_SUFFIX(pcre2_serialize_encode_) -#define pcre2_serialize_free PCRE2_SUFFIX(pcre2_serialize_free_) -#define pcre2_serialize_get_number_of_codes PCRE2_SUFFIX(pcre2_serialize_get_number_of_codes_) -#define pcre2_set_bsr PCRE2_SUFFIX(pcre2_set_bsr_) -#define pcre2_set_callout PCRE2_SUFFIX(pcre2_set_callout_) -#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_) -#define pcre2_substring_copy_byname PCRE2_SUFFIX(pcre2_substring_copy_byname_) -#define pcre2_substring_copy_bynumber PCRE2_SUFFIX(pcre2_substring_copy_bynumber_) -#define pcre2_substring_free PCRE2_SUFFIX(pcre2_substring_free_) -#define pcre2_substring_get_byname PCRE2_SUFFIX(pcre2_substring_get_byname_) -#define pcre2_substring_get_bynumber PCRE2_SUFFIX(pcre2_substring_get_bynumber_) -#define pcre2_substring_length_byname PCRE2_SUFFIX(pcre2_substring_length_byname_) -#define pcre2_substring_length_bynumber PCRE2_SUFFIX(pcre2_substring_length_bynumber_) -#define pcre2_substring_list_get PCRE2_SUFFIX(pcre2_substring_list_get_) -#define pcre2_substring_list_free PCRE2_SUFFIX(pcre2_substring_list_free_) -#define pcre2_substring_nametable_scan PCRE2_SUFFIX(pcre2_substring_nametable_scan_) -#define pcre2_substring_number_from_name PCRE2_SUFFIX(pcre2_substring_number_from_name_) - - -/* Now generate all three sets of width-specific structures and function -prototypes. */ - -#define PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS \ -PCRE2_TYPES_LIST \ -PCRE2_STRUCTURE_LIST \ -PCRE2_GENERAL_INFO_FUNCTIONS \ -PCRE2_GENERAL_CONTEXT_FUNCTIONS \ -PCRE2_COMPILE_CONTEXT_FUNCTIONS \ -PCRE2_MATCH_CONTEXT_FUNCTIONS \ -PCRE2_COMPILE_FUNCTIONS \ -PCRE2_PATTERN_INFO_FUNCTIONS \ -PCRE2_MATCH_FUNCTIONS \ -PCRE2_SUBSTRING_FUNCTIONS \ -PCRE2_SERIALIZE_FUNCTIONS \ -PCRE2_SUBSTITUTE_FUNCTION \ -PCRE2_JIT_FUNCTIONS \ -PCRE2_OTHER_FUNCTIONS - -#define PCRE2_LOCAL_WIDTH 8 -PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS -#undef PCRE2_LOCAL_WIDTH - -#define PCRE2_LOCAL_WIDTH 16 -PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS -#undef PCRE2_LOCAL_WIDTH - -#define PCRE2_LOCAL_WIDTH 32 -PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS -#undef PCRE2_LOCAL_WIDTH - -/* Undefine the list macros; they are no longer needed. */ - -#undef PCRE2_TYPES_LIST -#undef PCRE2_STRUCTURE_LIST -#undef PCRE2_GENERAL_INFO_FUNCTIONS -#undef PCRE2_GENERAL_CONTEXT_FUNCTIONS -#undef PCRE2_COMPILE_CONTEXT_FUNCTIONS -#undef PCRE2_MATCH_CONTEXT_FUNCTIONS -#undef PCRE2_COMPILE_FUNCTIONS -#undef PCRE2_PATTERN_INFO_FUNCTIONS -#undef PCRE2_MATCH_FUNCTIONS -#undef PCRE2_SUBSTRING_FUNCTIONS -#undef PCRE2_SERIALIZE_FUNCTIONS -#undef PCRE2_SUBSTITUTE_FUNCTION -#undef PCRE2_JIT_FUNCTIONS -#undef PCRE2_OTHER_FUNCTIONS -#undef PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS - -/* PCRE2_CODE_UNIT_WIDTH must be defined. If it is 8, 16, or 32, redefine -PCRE2_SUFFIX to use it. If it is 0, undefine the other macros and make -PCRE2_SUFFIX a no-op. Otherwise, generate an error. */ - -#undef PCRE2_SUFFIX -#ifndef PCRE2_CODE_UNIT_WIDTH -#error PCRE2_CODE_UNIT_WIDTH must be defined before including pcre2.h. -#error Use 8, 16, or 32; or 0 for a multi-width application. -#else /* PCRE2_CODE_UNIT_WIDTH is defined */ -#if PCRE2_CODE_UNIT_WIDTH == 8 || \ - PCRE2_CODE_UNIT_WIDTH == 16 || \ - PCRE2_CODE_UNIT_WIDTH == 32 -#define PCRE2_SUFFIX(a) PCRE2_GLUE(a, PCRE2_CODE_UNIT_WIDTH) -#elif PCRE2_CODE_UNIT_WIDTH == 0 -#undef PCRE2_JOIN -#undef PCRE2_GLUE -#define PCRE2_SUFFIX(a) a -#else -#error PCRE2_CODE_UNIT_WIDTH must be 0, 8, 16, or 32. -#endif -#endif /* PCRE2_CODE_UNIT_WIDTH is defined */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* End of pcre2.h */ From 807f79df03212f271b1c1c047803bb8f1c4b7440 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 26 Jan 2019 14:56:16 -0800 Subject: [PATCH 370/439] Remove xcode instructions from README.md --- README.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/README.md b/README.md index 9a70cfa83..d91d43d75 100644 --- a/README.md +++ b/README.md @@ -128,19 +128,6 @@ make sudo make install ``` -### Building from source (macOS only) - -* Build the `base` target in Xcode -* Run the fish executable, for example, in `DerivedData/fish/Build/Products/Debug/base/bin/fish` - -To build and install fish with Xcode on macOS, execute the following in a terminal: - -```bash -xcodebuild install -sudo ditto /tmp/fish.dst / -sudo make install-doc -``` - ### Help, it didn't build! If fish reports that it could not find curses, try installing a curses development package and build again. From c869ab541d0fe6c1bf738346e7773d6e3b78c3ae Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 26 Jan 2019 14:58:22 -0800 Subject: [PATCH 371/439] Remove some additional xcode references --- CONTRIBUTING.md | 7 ++----- Makefile.in | 9 --------- build_tools/lint.fish | 21 +-------------------- build_tools/xcode_version_gen.sh | 12 ------------ 4 files changed, 3 insertions(+), 46 deletions(-) delete mode 100755 build_tools/xcode_version_gen.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d72ffca31..b5dc2e304 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -179,12 +179,9 @@ You are strongly encouraged to add tests when changing the functionality of fish 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](https://brew.sh/) to install these tools. - ``` -autoconf -./configure -make test # or "gmake test" on BSD +cmake path/to/fish-shell +make test ``` ### Travis CI Build and Test diff --git a/Makefile.in b/Makefile.in index 059e05847..eba0b41be 100644 --- a/Makefile.in +++ b/Makefile.in @@ -590,15 +590,6 @@ install: all install-force | check-legacy-binaries @echo "$(bo)Have fun! <><$(sgr0)" .PHONY: install -# -# Xcode install -# -xcode-install: - rm -Rf /tmp/fish_build - xcrun xcodebuild install DSTROOT=/tmp/fish_build - ditto /tmp/fish_build / -.PHONY: xcode-install - # # Actually do the installation. # diff --git a/build_tools/lint.fish b/build_tools/lint.fish index d8e9ce89a..e6fd1ee09 100755 --- a/build_tools/lint.fish +++ b/build_tools/lint.fish @@ -127,26 +127,7 @@ 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 "$kernel_name" = "Darwin" - if not test -f compile_commands.json - xcodebuild -alltargets >xcodebuild.log - oclint-xcodebuild xcodebuild.log >/dev/null - end - if test $all = yes - oclint-json-compilation-database -e '/pcre2-10.32/' -- -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.32/' $i_files - oclint-json-compilation-database -e '/pcre2-10.32/' $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 - end + oclint $c_files -- $argv 2>&1 end else echo diff --git a/build_tools/xcode_version_gen.sh b/build_tools/xcode_version_gen.sh deleted file mode 100755 index 41cad41c3..000000000 --- a/build_tools/xcode_version_gen.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# Expects to be called from Xcode (Run Script build phase), -# write version number C preprocessor macro to header file. - -ver="$SCRIPT_OUTPUT_FILE_0" - -./build_tools/git_version_gen.sh - -cmp --quiet "FISH-BUILD-VERSION-FILE" "$ver" -if [ $? -ne 0 ]; then - /bin/cp FISH-BUILD-VERSION-FILE "$ver" -fi From 0f5dc0b4e273f4114b0134ef39c838959b0f0592 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 27 Jan 2019 18:32:37 -0800 Subject: [PATCH 372/439] Add --stdout param to git_version_gen.sh This causes git_version_gen.sh to print to stdout. --- build_tools/git_version_gen.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/build_tools/git_version_gen.sh b/build_tools/git_version_gen.sh index 6f30cd65f..86132888e 100755 --- a/build_tools/git_version_gen.sh +++ b/build_tools/git_version_gen.sh @@ -8,10 +8,6 @@ set -e # Find the fish git directory as two levels up from script directory. GIT_DIR="$( cd "$( dirname $( dirname "$0" ) )" && pwd )" - -# Set the output directory as either the first param or cwd. -test -n "$1" && OUTPUT_DIR=$1/ || OUTPUT_DIR= -FBVF=${OUTPUT_DIR}FISH-BUILD-VERSION-FILE DEF_VER=unknown # First see if there is a version file (included in release tarballs), @@ -23,6 +19,17 @@ elif ! VN=$(git -C "$GIT_DIR" describe --always --dirty 2>/dev/null); then VN="$DEF_VER" fi +# If the first param is --stdout, then output to stdout and exit. +if test "$1" = '--stdout' +then + echo $VN + exit 0 +fi + +# Set the output directory as either the first param or cwd. +test -n "$1" && OUTPUT_DIR=$1/ || OUTPUT_DIR= +FBVF=${OUTPUT_DIR}FISH-BUILD-VERSION-FILE + if test -r $FBVF then VC=$(grep -v '^#' $FBVF | tr -d '"' | sed -e 's/^FISH_BUILD_VERSION=//') From b9526064304d18a734bf40f6a826d0607b58a801 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 28 Jan 2019 11:47:58 -0800 Subject: [PATCH 373/439] Teach CMake Mac App build to dynamically derive the version Populate CFBundleShortVersionString with the fish version from git_version_gen.sh. Note this happens at build generation time, not at build time. --- cmake/MacApp.cmake | 13 ++++++++++++- osx/CMakeMacAppInfo.plist.in | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cmake/MacApp.cmake b/cmake/MacApp.cmake index df5987868..6ae1622e7 100644 --- a/cmake/MacApp.cmake +++ b/cmake/MacApp.cmake @@ -18,7 +18,17 @@ ADD_EXECUTABLE(fish_macapp EXCLUDE_FROM_ALL ${OSX_DIR}/osx_fish_launcher.m ${RESOURCE_FILES} ) - + +# Compute the version. Note this is done at generation time, not build time, +# so cmake must be re-run after version changes for the app to be updated. But +# generally this will be run by make_pkg.sh which always re-runs cmake. +EXECUTE_PROCESS( + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh --stdout + COMMAND cut -d- -f1 + OUTPUT_VARIABLE FISH_SHORT_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Note CMake appends .app, so the real output name will be fish.app. # This target does not include the 'base' resource. SET_TARGET_PROPERTIES(fish_macapp PROPERTIES OUTPUT_NAME "fish") @@ -30,6 +40,7 @@ SET_TARGET_PROPERTIES(fish_macapp PROPERTIES MACOSX_BUNDLE TRUE MACOSX_BUNDLE_INFO_PLIST ${OSX_DIR}/CMakeMacAppInfo.plist.in MACOSX_BUNDLE_GUI_IDENTIFIER "com.ridiculousfish.fish-shell" + MACOSX_BUNDLE_SHORT_VERSION_STRING ${FISH_SHORT_VERSION} RESOURCE "${RESOURCE_FILES}" ) diff --git a/osx/CMakeMacAppInfo.plist.in b/osx/CMakeMacAppInfo.plist.in index ef0db1ad2..212143cfa 100644 --- a/osx/CMakeMacAppInfo.plist.in +++ b/osx/CMakeMacAppInfo.plist.in @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 3.0.0 + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} CFBundleVersion 0.1 LSApplicationCategoryType From 23f8f1b6fb4b8828b0612d8536116966805622cf Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 31 Jan 2019 16:06:34 -0800 Subject: [PATCH 374/439] Remove some stale bits from make_pkg.sh --- build_tools/make_pkg.sh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/build_tools/make_pkg.sh b/build_tools/make_pkg.sh index 55a8bb496..66891cc4b 100755 --- a/build_tools/make_pkg.sh +++ b/build_tools/make_pkg.sh @@ -5,19 +5,12 @@ VERSION=`git describe --always --dirty 2>/dev/null` if test -z "$VERSION" ; then 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" set -x -make distclean - #Exit on error set -e From 7f1dfb6284008e16b1aeb71fa5ae47c83617a6d5 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Thu, 31 Jan 2019 15:56:27 -0800 Subject: [PATCH 375/439] Set CMAKE_ARCHIVE_OUTPUT_DIRECTORY This allows Xcode archive builds to succeed. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f29d33986..32dee2061 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,9 @@ SOURCE_GROUP("Builtins" REGULAR_EXPRESSION "builtin_.*") # Support folders. SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON) +# Work around issue where archive-built libs go in the wrong place. +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") IF(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) From 6025c28efc97720b04a23640eab805114f73276d Mon Sep 17 00:00:00 2001 From: Brian Malehorn Date: Tue, 5 Feb 2019 20:36:38 -0800 Subject: [PATCH 376/439] Create function to retrieve tmpdir `/tmp` isn't present / writeable on every system. Instead of always using `/tmp`, try to use standard environment variables and configuration to find a temporary directory. Adapted from #3974, with updates based on those comments. Closes #3845. --- src/common.cpp | 23 +++++++++++++++++++++++ src/common.h | 7 +++++++ src/env.cpp | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/common.cpp b/src/common.cpp index 7a88e569f..3a02dadf5 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -2442,3 +2443,25 @@ std::string get_executable_path(const char *argv0) { return std::string(argv0 ? argv0 : ""); } +/// Return a path to a directory where we can store temporary files. +std::string get_path_to_tmp_dir() { + char *env_tmpdir = getenv("TMPDIR"); + if (env_tmpdir) { + return env_tmpdir; + } +#if defined(_CS_DARWIN_USER_TEMP_DIR) + char osx_tmpdir[PATH_MAX]; + size_t n = confstr(_CS_DARWIN_USER_TEMP_DIR, osx_tmpdir, PATH_MAX); + if (0 < n && n <= PATH_MAX) { + return osx_tmpdir; + } else { + return "/tmp"; + } +#elif defined(P_tmpdir) + return P_tmpdir; +#elif defined(_PATH_TMP) + return _PATH_TMP; +#else + return "/tmp"; +#endif +} diff --git a/src/common.h b/src/common.h index 8e94364be..fbf8c183c 100644 --- a/src/common.h +++ b/src/common.h @@ -19,6 +19,11 @@ #include "fallback.h" // IWYU pragma: keep #include "maybe.h" +// PATH_MAX may not exist. +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + // Define a symbol we can use elsewhere in our code to determine if we're being built on MS Windows // under Cygwin. #if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(__CYGWIN__) || \ @@ -947,6 +952,8 @@ static const wchar_t *enum_to_str(T enum_val, const enum_map map[]) { void redirect_tty_output(); +std::string get_path_to_tmp_dir(); + // Minimum allowed terminal size and default size if the detected size is not reasonable. #define MIN_TERM_COL 20 #define MIN_TERM_ROW 2 diff --git a/src/env.cpp b/src/env.cpp index 56b6be201..ff787bc47 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1741,7 +1741,7 @@ wcstring env_get_runtime_path() { auto pwuid = getpwuid(geteuid()); const char *uname = pwuid ? pwuid->pw_name : NULL; // /tmp/fish.user - std::string tmpdir = "/tmp/fish."; + std::string tmpdir = get_path_to_tmp_dir() + "/fish."; if (uname) { tmpdir.append(uname); } From 26ada583a06dc06a042bd355af983f37e431d8b6 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 5 Feb 2019 22:08:15 -0800 Subject: [PATCH 377/439] Fix te_expr's flexible array member te_expr has a flexible array member, but it's declared as size 1. Stop declaring its size so UBSan stops complaining. Noted in #2852 --- src/tinyexpr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tinyexpr.cpp b/src/tinyexpr.cpp index 6cfba346d..842219dea 100644 --- a/src/tinyexpr.cpp +++ b/src/tinyexpr.cpp @@ -56,7 +56,7 @@ int get_arity(const int type) { typedef struct te_expr { int type; union {double value; const void *function;}; - te_expr *parameters[1]; + te_expr *parameters[]; } te_expr; // TODO: Rename since variables have been removed. @@ -92,7 +92,7 @@ void te_free(te_expr *n); static te_expr *new_expr(const int type, const te_expr *parameters[]) { const int arity = get_arity(type); const int psize = sizeof(te_expr*) * arity; - const int size = (sizeof(te_expr) - sizeof(void*)) + psize; + const int size = sizeof(te_expr) + psize; te_expr *ret = (te_expr *)malloc(size); // This sets float to 0, which depends on the implementation. // We rely on IEEE-754 floats anyway, so it's okay. From d4e3f49571893443e7cbd3f93f349fabf27168d3 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Tue, 5 Feb 2019 23:20:43 -0800 Subject: [PATCH 378/439] Correct the read_blocked comment --- src/common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common.h b/src/common.h index fbf8c183c..89488162c 100644 --- a/src/common.h +++ b/src/common.h @@ -764,8 +764,8 @@ void error_reset(); /// initialization. void fish_setlocale(); -/// 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, blocking and repeating on EINTR. Exits on EAGAIN. +/// \return the number of bytes read, or 0 on EOF. On EAGAIN, returns -1 if nothing was read. 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 From 988283c7177d8496f18c1fea1a1007aa8d45d984 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 5 Feb 2019 23:08:54 +0100 Subject: [PATCH 379/439] Improved performance of the sorin theme This improves performance of the sorin theme tremendously for repositories with a large number of changes. --- .../web_config/sample_prompts/sorin.fish | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/share/tools/web_config/sample_prompts/sorin.fish b/share/tools/web_config/sample_prompts/sorin.fish index a7d379b32..3836bb33d 100644 --- a/share/tools/web_config/sample_prompts/sorin.fish +++ b/share/tools/web_config/sample_prompts/sorin.fish @@ -107,36 +107,31 @@ function fish_right_prompt # B | | | | m | r | m | u | | | | # ? | | | | m | r | m | u | | | t | # _ | | | d | m | r | m | u | | | | + set -l porcelain_status (command git status --porcelain | string sub -l2) set -l status_added 0 + if string match -qr '[ACDMT][ MT]|[ACMT]D' $porcelain_status + set status_added 1 + end set -l status_deleted 0 + if string match -qr '[ ACMRT]D' $porcelain_status + set status_deleted 1 + end set -l status_modified 0 + if string match -qr '[MT]$' $porcelain_status + set status_modified 1 + end set -l status_renamed 0 + if string match -qe 'R' $porcelain_status + set status_renamed 1 + end set -l status_unmerged 0 + if string match -qr 'AA|DD|U' $porcelain_status + set status_unmerged 1 + end set -l status_untracked 0 - for line in (command git status --porcelain | string sub -l 2) - # Check unambiguous cases first which allows us - # to skip running all the other regexps. - if test "$line" = '??' - set status_untracked 1 - continue - end - if string match -r '^(?:AA|DD|U.|.U)$' "$line" >/dev/null - set status_unmerged 1 - continue - end - if string match -r '^(?:[ACDMT][ MT]|[ACMT]D)$' "$line" >/dev/null - set status_added 1 - end - if string match -r '^[ ACMRT]D$' "$line" >/dev/null - set status_deleted 1 - end - if string match -r '^.[MT]$' "$line" >/dev/null - set status_modified 1 - end - if string match -e 'R' "$line" >/dev/null - set status_renamed 1 - end + if string match -qe '\?\?' $porcelain_status + set status_untracked 1 end set_color -o From 87e71bcde786d2b2f8a04b699f2f64e276d983dc Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 6 Feb 2019 23:45:14 +0100 Subject: [PATCH 380/439] input: Remove useless .c_str (Also removes some dead code) --- src/input.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index f62c11f32..0a9e9a008 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -176,7 +176,7 @@ void input_set_bind_mode(const wcstring &bm) { ASSERT_IS_MAIN_THREAD(); auto &vars = parser_t::principal_parser().vars(); assert(!bm.empty()); - if (input_get_bind_mode(vars) != bm.c_str()) { + if (input_get_bind_mode(vars) != bm) { vars.set_one(FISH_BIND_MODE_VAR, ENV_GLOBAL, bm); } } @@ -224,10 +224,6 @@ void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands, CHECK(mode, ); CHECK(sets_mode, ); - // debug( 0, L"Add mapping from %ls to %ls in mode %ls", escape_string(sequence, - // ESCAPE_ALL).c_str(), - // escape_string(command, ESCAPE_ALL).c_str(), mode); - // Remove existing mappings with this sequence. const wcstring_list_t commands_vector(commands, commands + commands_len); @@ -372,7 +368,7 @@ static void input_mapping_execute(const input_mapping_t &m, bool allow_commands) // see that until all other commands have also been run. int last_status = proc_get_last_status(); for (const wcstring &cmd : m.commands) { - parser_t::principal_parser().eval(cmd.c_str(), io_chain_t(), TOP); + parser_t::principal_parser().eval(cmd, io_chain_t(), TOP); } proc_set_last_status(last_status); input_common_next_ch(R_NULL); From b03c62bd299f4b097a800d30e2fc4dc38f52c5c4 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 7 Feb 2019 08:55:40 +0100 Subject: [PATCH 381/439] docs: Fix status is-command-substitution The docs spoke of a short "is-command-sub" variant, which does not exist. Fixes #5624. [ci skip] --- doc_src/status.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc_src/status.txt b/doc_src/status.txt index c2f99f800..052121311 100644 --- a/doc_src/status.txt +++ b/doc_src/status.txt @@ -28,7 +28,7 @@ With no arguments, `status` displays a summary of the current login and job cont The following operations (sub-commands) are available: -- `is-command-sub` returns 0 if fish is currently executing a command substitution. Also `-c` or `--is-command-substitution`. +- `is-command-substitution` returns 0 if fish is currently executing a command substitution. Also `-c` or `--is-command-substitution`. - `is-block` returns 0 if fish is currently executing a block of code. Also `-b` or `--is-block`. From 8e60ebcd344c3851d3ac4194a0435adbdeacc417 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 7 Feb 2019 09:47:15 +0100 Subject: [PATCH 382/439] Revert "funced: Use variable-as-command" This reverts commit 3253893923ff8fdb78bc200d82a9a0308c675b96. Fixes #5625. --- share/functions/funced.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/funced.fish b/share/functions/funced.fish index 8723a0ccb..7ad717e0a 100644 --- a/share/functions/funced.fish +++ b/share/functions/funced.fish @@ -94,7 +94,7 @@ function funced --description 'Edit function definition' while true set -l checksum (__funced_md5 "$tmpname") - if not $editor $tmpname + if not eval $editor $tmpname echo (_ "Editing failed or was cancelled") else # Verify the checksum (if present) to detect potential problems From 1a3471fa7dc5ca2d15632387e11ad1028da50d62 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 7 Feb 2019 09:47:24 +0100 Subject: [PATCH 383/439] Revert "edit_command_buffer: Use variable-as-command" This reverts commit 3c6844d4f4b16962a1372e179366fc01d64098f9. See #5625. --- share/functions/edit_command_buffer.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/functions/edit_command_buffer.fish b/share/functions/edit_command_buffer.fish index 423816190..6ea2b0d2c 100644 --- a/share/functions/edit_command_buffer.fish +++ b/share/functions/edit_command_buffer.fish @@ -18,11 +18,11 @@ function edit_command_buffer --description 'Edit the command buffer in an extern commandline -b >$f if set -q VISUAL __fish_disable_bracketed_paste - $VISUAL $f + eval $VISUAL $f __fish_enable_bracketed_paste else if set -q EDITOR __fish_disable_bracketed_paste - $EDITOR $f + eval $EDITOR $f __fish_enable_bracketed_paste else echo From 7c8b4449273f3f0983b3f858f46988ef42cbdc18 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Thu, 7 Feb 2019 12:08:24 +0100 Subject: [PATCH 384/439] Reduce default escape delay 300ms was waaay too long, and even 100ms wasn't necessary. Emacs' evil mode uses 10ms (0.01s), so let's stay a tad higher in case some terminals are slow. If anyone really wants to be able to type alt+h with escape, let them raise the timeout. Fixes #3904. --- CHANGELOG.md | 1 + doc_src/index.hdr.in | 2 +- share/functions/fish_vi_key_bindings.fish | 6 ----- src/input_common.cpp | 2 +- tests/bind.expect | 28 +++++++---------------- tests/bind.expect.out | 5 ++-- 6 files changed, 13 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a92390c6..9c7844b17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - fish_clipboard_* now supports wayland by means of [wl-clipboard](https://github.com/bugaevc/wl-clipboard). - mandoc can now be used to format the output from `--help` if nroff is not installed - New color options for the pager have been added (#5524). +- The default escape delay (to differentiate between the escape key and an alt-combination) has been reduced to 30ms, down from 300ms for the default mode and 100ms for vi-mode (#3904). ======= diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 3d0f9bd99..d3be1e5d2 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -901,7 +901,7 @@ The user can change the settings of `fish` by changing the values of certain var - `fish_ambiguous_width` controls the computed width of ambiguous East Asian characters. This should be set to 1 if your terminal emulator renders these characters as single-width (typical), or 2 if double-width. -- `fish_escape_delay_ms` overrides the default timeout of 300ms (default key bindings) or 10ms (vi key bindings) after seeing an escape character before giving up on matching a key binding. See the documentation for the bind builtin command. This delay facilitates using escape as a meta key. +- `fish_escape_delay_ms` overrides the default timeout of 30ms after seeing an escape character before giving up on matching a key binding. See the documentation for the bind builtin command. This delay facilitates using escape as a meta key. - `fish_greeting`, the greeting message printed on startup. diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 44b05913b..5d39be4c9 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -31,12 +31,6 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' 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 # These are only the special vi-style keys # not end/home, we share those. diff --git a/src/input_common.cpp b/src/input_common.cpp index a9fc3bc11..1e04176f4 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -31,7 +31,7 @@ /// 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 +#define WAIT_ON_ESCAPE_DEFAULT 30 static int wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; /// Characters that have been read and returned by the sequence matching code. diff --git a/tests/bind.expect b/tests/bind.expect index 98bebc295..6f9e97e52 100644 --- a/tests/bind.expect +++ b/tests/bind.expect @@ -3,7 +3,7 @@ spawn $fish expect_prompt # Fish should start in default-mode (i.e., emacs) bindings. The default escape -# timeout is 300ms. +# timeout is 30ms. # Verify the emacs transpose word (\et) behavior using various delays, # including none, after the escape character. @@ -21,7 +21,7 @@ expect_prompt -re {\r\ndef abc\r\n} { # the words. send "echo ghi jkl" send "\033" -sleep 0.050 +sleep 0.010 send "t\r" expect_prompt -re {\r\njkl ghi\r\n} { puts "emacs transpose words, default timeout: short delay" @@ -33,7 +33,7 @@ expect_prompt -re {\r\njkl ghi\r\n} { # occur and the "t" should become part of the text that is echoed. send "echo mno pqr" send "\033" -sleep 0.400 +sleep 0.200 send "t\r" expect_prompt -re {\r\nmno pqrt\r\n} { puts "emacs transpose words, default timeout: long delay" @@ -54,10 +54,6 @@ expect_prompt -re {\r\ndefault\r\npaste} { send "set -g fish_key_bindings fish_vi_key_bindings\r" expect_prompt -# These vi tests assume the fish_vi_key_bindings default escape timeout of -# 10ms is in effect; not the 300ms timeout for the default-mode. -# - # Go through a prompt cycle to let fish catch up, it may be slow due to ASAN send "echo success: default escape timeout\r" expect_prompt -re {\r\nsuccess: default escape timeout\r\n} { @@ -66,14 +62,6 @@ expect_prompt -re {\r\nsuccess: default escape timeout\r\n} { puts stderr "prime vi mode, default timeout" } -# 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=100\r\n} { - puts "vi-mode default timeout set correctly" -} unmatched { - puts stderr "vi-mode default timeout not set correctly" -} - send "echo fail: default escape timeout" send "\033" # Delay needed to allow fish to transition to vi "normal" mode. The delay is @@ -170,9 +158,9 @@ sleep 0.350 send "ddi" send "echo success: lengthened escape timeout\r" expect_prompt -re {\r\nsuccess: lengthened escape timeout\r\n} { - puts "vi replace line, 100ms timeout: long delay" + puts "vi replace line, 200ms timeout: long delay" } unmatched { - puts stderr "vi replace line, 100ms timeout: long delay" + puts stderr "vi replace line, 200ms timeout: long delay" } # Verify that we don't switch to vi normal mode if we don't wait long enough @@ -183,9 +171,9 @@ sleep 0.050 send "ddi" send "inserted\r" expect_prompt -re {\r\nfail: no normal modediinserted\r\n} { - puts "vi replace line, 100ms timeout: short delay" + puts "vi replace line, 200ms timeout: short delay" } unmatched { - puts stderr "vi replace line, 100ms timeout: short delay" + puts stderr "vi replace line, 200ms timeout: short delay" } # Test 't' binding that contains non-zero arity function (forward-jump) followed @@ -223,7 +211,7 @@ expect_prompt -re {\r\nfish_escape_delay_ms=200\r\n} { puts stderr "default-mode custom timeout not set correctly" } -# Reset it to 100ms. +# Set it to 100ms. send "set -g fish_escape_delay_ms 100\r" expect_prompt diff --git a/tests/bind.expect.out b/tests/bind.expect.out index 6fd079f7f..55667d0f6 100644 --- a/tests/bind.expect.out +++ b/tests/bind.expect.out @@ -3,7 +3,6 @@ emacs transpose words, default timeout: short delay emacs transpose words, default timeout: long delay emacs bind modes prime vi mode, default timeout -vi-mode default timeout set correctly vi replace line, default timeout: long delay vi mode replace char, default timeout: long delay vi mode delete char, default timeout: long delay @@ -11,8 +10,8 @@ vi mode forward-jump-till character, default timeout: long delay vi mode backward-jump-till character, default timeout: long delay vi mode backward-jump-to character and repeat, default timeout: long delay vi mode backward-jump-to character, and reverse, default timeout: long delay -vi replace line, 100ms timeout: long delay -vi replace line, 100ms timeout: short delay +vi replace line, 200ms timeout: long delay +vi replace line, 200ms timeout: short delay t-binding success vi bind modes default-mode custom timeout set correctly From 1049bed5f8171a449e8e1cdcde51d230e54f9d71 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Thu, 7 Feb 2019 03:45:17 -0800 Subject: [PATCH 385/439] string completions: add -e, -f, --no-empty, shorten -d's I hope this is now complete. Also, shorten enough descriptions to make `string match --` show a two column pager with 80 cols. We really should have shown more retraint in the design of `string`, not all of the flags required both a long and short option created. --- share/completions/string.fish | 21 +++++++++++++-------- src/builtin_string.cpp | 1 + 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/share/completions/string.fish b/share/completions/string.fish index 37d72f5b6..890abf870 100644 --- a/share/completions/string.fish +++ b/share/completions/string.fish @@ -12,29 +12,34 @@ complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "split" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "split0" complete -x -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s m -l max -a "(seq 1 10)" -d "Specify maximum number of splits" complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s r -l right -d "Split right-to-left" +complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s n -l no-empty -d "Empty results excluded" + complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "join" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "join0" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "trim" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s l -l left -d "Trim only leading characters" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s r -l right -d "Trim only trailing characters" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s l -l left -d "Trim only leading chars" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s r -l right -d "Trim only trailing chars" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s c -l chars -d "Specify the chars to trim (default: whitespace)" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "escape" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "unescape" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -s n -l no-quoted -d "Escaped with \\ instead of quoted" -complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -l style -d "Escaping style" -a " +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -s n -l no-quoted -d "Escape with \\ instead of quotes" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -l style -d "Specify escaping style" -a " (printf '%s\t%s\n' script 'For use in scripts' \ var 'For use as a variable name' \ regex 'For string match -r, string replace -r' \ url 'For use as a URL')" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "match" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s n -l index -d "Report index and length of the matches" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s v -l invert -d "Report only non-matching input" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s n -l index -d "Report index, length of match" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s v -l invert -d "Report only non-matches" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s e -l entire -d "Show entire matching lines" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "replace" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] replace" -s f -l filter -d "Report only actual replacements" # All replace options are also valid for match -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s a -l all -d "Report all matches per line/string" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s a -l all -d "Report every match" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s i -l ignore-case -d "Case insensitive" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s r -l regex -d "Use regex instead of globs" + complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "repeat" complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s n -l count -a "(seq 1 10)" -d "Repetition count" -complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s m -l max -a "(seq 1 10)" -d "Maximum number of printed char" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s m -l max -a "(seq 1 10)" -d "Maximum number of printed chars" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s N -l no-newline -d "Remove newline" diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 8e7777111..ad945fdef 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -413,6 +413,7 @@ static wcstring construct_short_opts(options_t *opts) { //!OCLINT(high npath co // Note that several long flags share the same short flag. That is okay. The caller is expected // to indicate that a max of one of the long flags sharing a short flag is valid. +// Remember: adjust share/functions/string.fish when `string` options change static const struct woption long_options[] = { {L"all", no_argument, NULL, 'a'}, {L"chars", required_argument, NULL, 'c'}, {L"count", required_argument, NULL, 'n'}, {L"entire", no_argument, NULL, 'e'}, From cfd9b520809e6f1141cdb8e642fa39eab09f4d4c Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Thu, 7 Feb 2019 03:45:17 -0800 Subject: [PATCH 386/439] string completions: add -e, -f, --no-empty, shorten -d's I hope this is now complete. Also, shorten enough descriptions to make `string match --` show a two column pager with 80 cols. We really should have shown more retraint in the design of `string`, not all of the flags required both a long and short option created. --- share/completions/string.fish | 21 +++++++++++++-------- src/builtin_string.cpp | 1 + 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/share/completions/string.fish b/share/completions/string.fish index 37d72f5b6..890abf870 100644 --- a/share/completions/string.fish +++ b/share/completions/string.fish @@ -12,29 +12,34 @@ complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "split" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "split0" complete -x -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s m -l max -a "(seq 1 10)" -d "Specify maximum number of splits" complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s r -l right -d "Split right-to-left" +complete -f -c string -n 'test (count (commandline -opc)) -ge 2; and string match -qr split0\?\$ -- (commandline -opc)[2]' -s n -l no-empty -d "Empty results excluded" + complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "join" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "join0" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "trim" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s l -l left -d "Trim only leading characters" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s r -l right -d "Trim only trailing characters" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s l -l left -d "Trim only leading chars" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s r -l right -d "Trim only trailing chars" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] trim" -s c -l chars -d "Specify the chars to trim (default: whitespace)" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "escape" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "unescape" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -s n -l no-quoted -d "Escaped with \\ instead of quoted" -complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -l style -d "Escaping style" -a " +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -s n -l no-quoted -d "Escape with \\ instead of quotes" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] escape; or contains -- (commandline -opc)[2] unescape" -l style -d "Specify escaping style" -a " (printf '%s\t%s\n' script 'For use in scripts' \ var 'For use as a variable name' \ regex 'For string match -r, string replace -r' \ url 'For use as a URL')" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "match" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s n -l index -d "Report index and length of the matches" -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s v -l invert -d "Report only non-matching input" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s n -l index -d "Report index, length of match" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s v -l invert -d "Report only non-matches" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match" -s e -l entire -d "Show entire matching lines" complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "replace" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] replace" -s f -l filter -d "Report only actual replacements" # All replace options are also valid for match -complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s a -l all -d "Report all matches per line/string" +complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s a -l all -d "Report every match" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s i -l ignore-case -d "Case insensitive" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] match replace" -s r -l regex -d "Use regex instead of globs" + complete -f -c string -n "test (count (commandline -opc)) -lt 2" -a "repeat" complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s n -l count -a "(seq 1 10)" -d "Repetition count" -complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s m -l max -a "(seq 1 10)" -d "Maximum number of printed char" +complete -x -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s m -l max -a "(seq 1 10)" -d "Maximum number of printed chars" complete -f -c string -n "test (count (commandline -opc)) -ge 2; and contains -- (commandline -opc)[2] repeat" -s N -l no-newline -d "Remove newline" diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 8e7777111..ad945fdef 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -413,6 +413,7 @@ static wcstring construct_short_opts(options_t *opts) { //!OCLINT(high npath co // Note that several long flags share the same short flag. That is okay. The caller is expected // to indicate that a max of one of the long flags sharing a short flag is valid. +// Remember: adjust share/functions/string.fish when `string` options change static const struct woption long_options[] = { {L"all", no_argument, NULL, 'a'}, {L"chars", required_argument, NULL, 'c'}, {L"count", required_argument, NULL, 'n'}, {L"entire", no_argument, NULL, 'e'}, From 0abcf9265efe5eb43e9992133638356f0f9c5c09 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Thu, 7 Feb 2019 12:37:05 -0800 Subject: [PATCH 387/439] {forward,backward}-bigword on Shift-Left/Right There was no way to do this at all without vi keybindings, and it turns out shift-left/shift-right was available. Fixes #1605 --- doc_src/index.hdr.in | 4 +++- share/functions/__fish_shared_key_bindings.fish | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index d3be1e5d2..de4cd4ec8 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -1097,7 +1097,9 @@ Some bindings are shared between emacs- and vi-mode because they aren't text edi - @key{Tab} completes the current token. @key{Shift, Tab} completes the current token and starts the pager's search mode. -- @key{Alt,←,Left} and @key{Alt,→,Right} move the cursor one word left or right, or moves forward/backward in the directory history if the command line is empty. If the cursor is already at the end of the line, and an autosuggestion is available, @key{Alt,→,Right} (or @key{Alt,F}) accepts the first word in the suggestion. +- @key{Alt,←,Left} and @key{Alt,→,Right} move the cursor one word left or right (to the next space or punctuation mark), or moves forward/backward in the directory history if the command line is empty. If the cursor is already at the end of the line, and an autosuggestion is available, @key{Alt,→,Right} (or @key{Alt,F}) accepts the first word in the suggestion. + +- @key{Shift,←,Left} and @key{Shift,→,Right} move the cursor one word left or right, without stopping on punctuation. - @cursor_key{↑,Up} and @cursor_key{↓,Down} (or @key{Control,P} and @key{Control,N} for emacs aficionados) search the command history for the previous/next command containing the string that was specified on the commandline before the search was started. If the commandline was empty when the search started, all commands match. See the history section for more information on history searching. diff --git a/share/functions/__fish_shared_key_bindings.fish b/share/functions/__fish_shared_key_bindings.fish index 29d7894a8..ea1fc7f08 100644 --- a/share/functions/__fish_shared_key_bindings.fish +++ b/share/functions/__fish_shared_key_bindings.fish @@ -45,6 +45,9 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod bind --preset $argv \eOA up-or-search bind --preset $argv \eOB down-or-search + bind --preset $argv -k sright forward-bigword + bind --preset $argv -k sleft backward-bigword + # Alt-left/Alt-right bind --preset $argv \e\eOC nextd-or-forward-word bind --preset $argv \e\eOD prevd-or-backward-word From 965fef739c12588567fb2c47ac14174cf09fa828 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 18:48:38 +0100 Subject: [PATCH 388/439] docs/tutorial: Remove mention of caret (^) While this is still technically included, the tutorial should not steer people towards it. [ci skip] --- doc_src/tutorial.hdr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/tutorial.hdr b/doc_src/tutorial.hdr index 07b2a6387..898e43a64 100644 --- a/doc_src/tutorial.hdr +++ b/doc_src/tutorial.hdr @@ -172,10 +172,10 @@ You can pipe between commands with the usual vertical bar: 1 2 12 \endfish -stdin and stdout can be redirected via the familiar < and >. Unlike other shells, stderr is redirected with a caret ^ +stdin and stdout can be redirected via the familiar < and >. stderr is redirected with a >2. \fish{cli-dark} ->_ grep fish < /etc/shells > ~/output.txt ^ ~/errors.txt +>_ grep fish < /etc/shells > ~/output.txt 2> ~/errors.txt \endfish From 0c706e45eb6f0ec9cf2a7f8faf603cfb830a1e97 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 18:48:38 +0100 Subject: [PATCH 389/439] docs/tutorial: Remove mention of caret (^) While this is still technically included, the tutorial should not steer people towards it. [ci skip] --- doc_src/tutorial.hdr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/tutorial.hdr b/doc_src/tutorial.hdr index b4ef8dfce..d11fed687 100644 --- a/doc_src/tutorial.hdr +++ b/doc_src/tutorial.hdr @@ -172,10 +172,10 @@ You can pipe between commands with the usual vertical bar: 1 2 12 \endfish -stdin and stdout can be redirected via the familiar < and >. Unlike other shells, stderr is redirected with a caret ^ +stdin and stdout can be redirected via the familiar < and >. stderr is redirected with a >2. \fish{cli-dark} ->_ grep fish < /etc/shells > ~/output.txt ^ ~/errors.txt +>_ grep fish < /etc/shells > ~/output.txt 2> ~/errors.txt \endfish From c1504576f9dc654d4708fdd83413ddf3e3b3a91f Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 9 Feb 2019 20:32:13 -0800 Subject: [PATCH 390/439] redirection.cpp: remove unused error message macro LOCAL_PIPE_ERROR is no longer used anywhere. --- src/redirection.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/redirection.cpp b/src/redirection.cpp index 1cce609df..e5cccebd9 100644 --- a/src/redirection.cpp +++ b/src/redirection.cpp @@ -8,9 +8,6 @@ /// File descriptor redirection error message. #define FD_ERROR "An error occurred while redirecting file descriptor %s" -/// Pipe error message. -#define LOCAL_PIPE_ERROR "An error occurred while setting up pipe" - #define NOCLOB_ERROR _(L"The file '%s' already exists") #define FILE_ERROR _(L"An error occurred while redirecting file '%s'") From 2445a76d2ef5fd6f7725679c36797c5f4e81cfe3 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 10 Feb 2019 00:09:21 -0600 Subject: [PATCH 391/439] Fix typo in yarn/npm completions helper script Hat-tip @billyjanitsch --- share/functions/fish_npm_helper.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/fish_npm_helper.fish b/share/functions/fish_npm_helper.fish index f6eb1efc9..bcbe42d7b 100644 --- a/share/functions/fish_npm_helper.fish +++ b/share/functions/fish_npm_helper.fish @@ -7,7 +7,7 @@ # Install globally with `sudo npm install -g all-the-package-names`. Keep it up to date. function __yarn_helper_installed # This function takes the command to globally install a package as $argv[1] - if not type -q all-the-package-namesS + if not type -q all-the-package-names if not set -qg __fish_yarn_pkg_info_shown set -l old (commandline) commandline -r "" From a00ef4aa2ecea56dae09095cda16f93ac9a8ca8c Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sun, 10 Feb 2019 00:14:42 -0600 Subject: [PATCH 392/439] Wrap long lines --- src/redirection.cpp | 3 ++- src/redirection.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/redirection.cpp b/src/redirection.cpp index e5cccebd9..64acdda23 100644 --- a/src/redirection.cpp +++ b/src/redirection.cpp @@ -37,7 +37,8 @@ maybe_t dup2_list_t::resolve_chain(const io_chain_t &io_chain) { return none(); } - // If by chance we got the file we want, we're done. Otherwise move the fd to an unused place and dup2 it. + // If by chance we got the file we want, we're done. Otherwise move the fd to an + // unused place and dup2 it. // Note move_fd_to_unused() will close the incoming file_fd. if (file_fd != io_file->fd) { file_fd = move_fd_to_unused(file_fd, io_chain, false /* cloexec */); diff --git a/src/redirection.h b/src/redirection.h index 95cc8fac0..3f1902e8a 100644 --- a/src/redirection.h +++ b/src/redirection.h @@ -57,7 +57,8 @@ class dup2_list_t { const std::vector &get_actions() const { return actions_; } /// Produce a dup_fd_list_t from an io_chain. This may not be called before fork(). - /// The result contains the list of fd actions (dup2 and close), as well as the list of fds opened. + /// The result contains the list of fd actions (dup2 and close), as well as the list + /// of fds opened. static maybe_t resolve_chain(const io_chain_t &); }; From 0377198fc882e2412d0132b5dc3c3563b4fcddba Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 9 Feb 2019 22:22:58 -0800 Subject: [PATCH 393/439] Drastically improve `fish ` completions * complete .fish files * --debug -> --debug-level * add --init-command/-C * add --debug-stack-frames/-D * add --private/-P * add --features/-f: lists supported features, supports foo, * -c now completes commands * -d requires argument, describes 0..5 * --profile: require argument, allow file completion --- share/completions/fish.fish | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/share/completions/fish.fish b/share/completions/fish.fish index 2038f0c78..ab3bfcdde 100644 --- a/share/completions/fish.fish +++ b/share/completions/fish.fish @@ -1,9 +1,24 @@ -complete -c fish -s c -l "command" -d "Run fish with this command" +complete -c fish -s c -l command -d "Run specified command instead of interactive session" -x -a "(__fish_complete_command)" +complete -c fish -s C -l init-command -d "Run specified command before session" -x -a "(__fish_complete_command)" complete -c fish -s h -l help -d "Display help and exit" complete -c fish -s v -l version -d "Display version and exit" complete -c fish -s n -l no-execute -d "Only parse input, do not execute" complete -c fish -s i -l interactive -d "Run in interactive mode" -complete -c fish -s l -l login -d "Run in login mode" -complete -c fish -s p -l profile -d "Output profiling information to specified file" -f -complete -c fish -s d -l debug -d "Run with the specified verbosity level" -complete -c fish -s P -l private -d "Run fish in private mode" +complete -c fish -s l -l login -d "Run as a login shell" +complete -c fish -s p -l profile -d "Output profiling information to specified file" -r +complete -c fish -s d -l debug-level -d "Specify verbosity level" -x -a "0\t'Warnings silenced' +1\t'Default' +2\t'Basic debug output' +3\t'More debug output' +4\t'Much more debug output' +5\t'Too much debug output'" +complete -c fish -s D -l debug-stack-frames -d "Show specified # of frames with debug output" -x -a "(seq 128)\t\n" +complete -c fish -s P -l private -d "Do not persist history" + +function __fish_complete_features + set -l arg_comma (commandline -tc | string replace -rf '(.*,)[^,]*' '$1' | string replace -r -- '--.*=' '') + set -l features (status features | string replace -rf '^([\w-]+).*\t(.*)$' '$1\t$2') + printf "%s\n" "$arg_comma"$features #TODO: remove existing args +end +complete -c fish -s f -l features -d "Run with comma-separated feature flags enabled" -a "(__fish_complete_features)" -x +complete -c fish -x -a "(__fish_complete_suffix .fish)" \ No newline at end of file From 662708e72d221ca5057bc2cb10bd761844706fcc Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 10 Feb 2019 15:57:06 +0800 Subject: [PATCH 394/439] src/exec: drop unused parameter in can_use_posix_spawn_for_job Process object is not checked since 084ff64f4fb5b5. --- src/exec.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index 63b10759c..431c4e042 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -304,8 +304,7 @@ void internal_exec_helper(parser_t &parser, parsed_source_ref_t parsed_source, t // 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 std::shared_ptr &job, - const process_t *process) { +static bool can_use_posix_spawn_for_job(const std::shared_ptr &job) { if (job->get_flag(job_flag_t::JOB_CONTROL)) { //!OCLINT(collapsible if statements) // 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? @@ -642,7 +641,7 @@ static bool exec_external_command(env_stack_t &vars, const std::shared_ptr Date: Tue, 5 Feb 2019 13:37:01 +0800 Subject: [PATCH 395/439] travis: turn on UBsan Closes #2852. --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e27a47156..53bd075f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,9 @@ matrix: - os: linux compiler: clang env: - - CXXFLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=address" ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1:detect_leaks=1 + - CXXFLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=address" + - ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1:detect_leaks=1 + - UBSAN_OPTIONS=print_stacktrace=1:report_error_type=1 before_install: export CXX=clang++-3.8 addons: apt: From aaa6cf44922de5760a21084aeb1614cbe319c8b8 Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 5 Feb 2019 13:37:43 +0800 Subject: [PATCH 396/439] travis: use default system clang for sanitizers --- .travis.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 53bd075f7..8f5a75632 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,15 +45,9 @@ matrix: - CXXFLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=address" - ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1:detect_leaks=1 - UBSAN_OPTIONS=print_stacktrace=1:report_error_type=1 - before_install: export CXX=clang++-3.8 addons: apt: - sources: - - llvm-toolchain-precise-3.8 - - ubuntu-toolchain-r-test packages: - - clang-3.8 - - llvm-3.8 # for llvm-symbolizer - expect - gettext - libncurses5-dev From 191e6679b310a42b024c449b800ca823949b8bc1 Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 5 Feb 2019 13:55:56 +0800 Subject: [PATCH 397/439] travis: build bundled PCRE2 less often --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8f5a75632..356a174c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,11 +12,12 @@ matrix: - expect - gettext - libncurses5-dev + - libpcre2-dev - os: linux compiler: gcc addons: apt: - packages: + packages: # Don't use libpcre2-dev here, so that one build uses the vendored code - expect - gettext - lib32ncurses5-dev @@ -32,6 +33,7 @@ matrix: - gettext - libncurses5-dev - cmake + - libpcre2-dev env: - USE_CMAKE="1" # Dummy value, shows up in the Travis UI only script: @@ -51,6 +53,7 @@ matrix: - expect - gettext - libncurses5-dev + - libpcre2-dev - os: osx before_install: - brew update From e461858964d6e2cfaa81930ee27bbf4b85ff1985 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 9 Feb 2019 21:41:10 +0800 Subject: [PATCH 398/439] travis: blacklist stl_tree from UBSan Work on #2852. --- .travis.yml | 2 +- build_tools/ubsan.blacklist | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 build_tools/ubsan.blacklist diff --git a/.travis.yml b/.travis.yml index 356a174c3..1dbf4a069 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,7 +46,7 @@ matrix: env: - CXXFLAGS="-g -O2 -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=address" - ASAN_OPTIONS=check_initialization_order=1:detect_stack_use_after_return=1:detect_leaks=1 - - UBSAN_OPTIONS=print_stacktrace=1:report_error_type=1 + - UBSAN_OPTIONS=print_stacktrace=1:report_error_type=1:suppressions=$TRAVIS_BUILD_DIR/build_tools/ubsan.blacklist addons: apt: packages: diff --git a/build_tools/ubsan.blacklist b/build_tools/ubsan.blacklist new file mode 100644 index 000000000..62f1317dd --- /dev/null +++ b/build_tools/ubsan.blacklist @@ -0,0 +1,3 @@ +# Ubuntu Xenial (used for Travis CI builds) ships libstdc++ 5.4.0 which contains undefined behaviour +# See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63345 +object-size:*bits/stl_tree.h From be47d46e6a139359a27b43b56474ea82c6b7ced4 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sun, 10 Feb 2019 17:07:59 +0800 Subject: [PATCH 399/439] CHANGELOG: updates for 3.0.1 --- CHANGELOG.md | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba6f4ca57..2d0e1933d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,30 +4,22 @@ This release of fish fixes a number of major issues discovered in fish 3.0.0. ### Fixes and improvements -- exec now behaves properly inside functions (#5449) -- while loops now evaluate to the last executed command in the loop body (or zero if the body was empty), matching POSIX semantics. -- `read --silent` no longer echoes to the tty when run from a non-interactive script (#5519) -- (macOS only) /etc/paths and /etc/paths.d now correctly set path entries with spaces. Also affects MANPATH. (#5481) -- fish does not hang on launch when running under Cygwin/MSYS2 -- The pager-toggle-search binding (by default Control-S) now positions the cursor in the completions list. -- The error when a command is not found is now printed a single time instead of once per argument. (#5588) -- The git completions now print correct file paths instead of ../../../ (and so on) with older git versions directly inside a git root. (#5578) -- The git completions won't suggest :/ paths (relative to the git root) so much anymore. (#5574) -- The git completions will now fuzzy-match paths again. (#5476) -- The git completions will ignore shell aliases, so enterprising users can set up the wrapping command (via `set -g __fish_git_alias_$command $whatitwraps`). (#5412) -- A crash when the user's information can't be read was fixed. (#5550) -- fish no longer crashes when $hostname or some other non-electric read-only variable is used as a loop variable. (#5548) -- The "kill" completions won't invoke the same command 25 times anymore, speeding matters up considerably. (#5541) -- Fish now inherits symlinked paths correctly. (#5525) -- fish_title had a few spaces removed, saving space. (#5517) -- The `nim` prompt now works correctly when chosen in fish_config. (#5490) -- A potential crash in `string match` discovered via GLIBCXX_ASSERTIONS was fixed. (#5479) -- A crash on FreeBSD related to the wcstod_l function was fixed. (#5453) -- An assertion that checked getpid() in a tight loop was removed, increasing performance in some cases up to 40%. (#5447) -- `string` now prints help to stdout, like other builtins. (#5495) -- The completions for `configure` now correctly offer directories. (#5518) -- The `man` completions won't interpret the argument as a regex anymore. (#5566) -- Killing the terminal while fish is in vi-normal mode will no longer send it spinning and eating CPU. (#5528) +- `exec` does not complain about running foreground jobs when called (#5449). +- while loops now evaluate to the last executed command in the loop body (or zero if the body was empty), matching POSIX semantics (#4982). +- `read --silent` no longer echoes to the tty when run from a non-interactive script (#5519). +- On macOS, path entries with spaces in `/etc/paths` and `/etc/paths.d` now correctly set path entries with spaces. Likewise, `MANPATH` is correctly set from `/etc/manpaths` and `/etc/manpaths.d` (#5481). +- fish starts correctly under Cygwin/MSYS2 (#5426). +- The `pager-toggle-search` binding (Control-S by default) now positions the cursor in the completions list. +- The error when a command is not found is now printed a single time, instead of once per argument (#5588). +- Fixes and improvements to the git completions, including printing correct paths with older git versions, fuzzy matching again, reducing unnecessary offers of root paths (starting with `:/`) (#5578, #5574, #5476), and ignoring shell aliases, so enterprising users can set up the wrapping command (via `set -g __fish_git_alias_$command $whatitwraps`) (#5412). +- Significant performance improvements to core shell functions (#5447) and to the `kill` completions (#5541). +- Starting in symbolically-linked working directories works correctly (#5525). +- The default `fish_title` function no longer contains extra spaces (#5517). +- The `nim` prompt now works correctly when chosen in the Web-based configuration (#5490). +- `string` now prints help to stdout, like other builtins (#5495), +- Improvements to the completions for `configure` (#5518) and `man` (#5566). +- Killing the terminal while fish is in vi normal mode will no longer send it spinning and eating CPU. (#5528) +- A number of crashes have been fixed (#5550, #5548, #5479, #5453). If you are upgrading from version 2.7.1 or before, please also review the release notes for 3.0.0 and 3.0b1 (included below). From 34fa8ef2d23a7a2db40c5b4ec6273831147f4044 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sun, 10 Feb 2019 04:20:01 -0800 Subject: [PATCH 400/439] Prefer c++11-style [[attr]] syntax over __attribute__ (attr) Where Clang and GCC both support __attribute__ (attr) and GCC supports [[gnu::attr]], Clang promises it will support [[gnu::attr]] --- src/common.cpp | 15 +++++++-------- src/common.h | 7 +++---- src/fallback.cpp | 6 +++--- src/fallback.h | 6 +++--- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index 3a02dadf5..5b8f628b0 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -151,7 +151,7 @@ long convert_hex_digit(wchar_t d) { #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)) +[[gnu::noinline]] static const wcstring_list_t demangled_backtrace(int max_frames, int skip_levels) { void *callstack[128]; const int n_max_frames = sizeof(callstack) / sizeof(callstack[0]); @@ -182,8 +182,7 @@ 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) { +[[gnu::noinline]] void show_stackframe(const wchar_t msg_level, int frame_count, int skip_levels) { if (frame_count < 1) return; // TODO: Decide if this is still needed. I'm commenting it out because it caused me some grief @@ -202,7 +201,7 @@ show_stackframe(const wchar_t msg_level, int frame_count, int skip_levels) { #else // HAVE_BACKTRACE_SYMBOLS -void __attribute__((noinline)) show_stackframe(const wchar_t msg_level, int, int) { +[[gnu::noinline]] void show_stackframe(const wchar_t msg_level, int, int) { debug_shared(msg_level, L"Sorry, but your system does not support backtraces"); } #endif // HAVE_BACKTRACE_SYMBOLS @@ -622,7 +621,7 @@ static void debug_shared(const wchar_t level, const wcstring &msg) { } static const wchar_t level_char[] = {L'E', L'W', L'2', L'3', L'4', L'5'}; -void __attribute__((noinline)) debug_impl(int level, const wchar_t *msg, ...) { +[[gnu::noinline]] void debug_impl(int level, const wchar_t *msg, ...) { int errno_old = errno; va_list va; va_start(va, msg); @@ -636,7 +635,7 @@ void __attribute__((noinline)) debug_impl(int level, const wchar_t *msg, ...) { errno = errno_old; } -void __attribute__((noinline)) debug_impl(int level, const char *msg, ...) { +[[gnu::noinline]] void debug_impl(int level, const char *msg, ...) { if (!should_debug(level)) return; int errno_old = errno; char local_msg[512]; @@ -2041,7 +2040,7 @@ int create_directory(const wcstring &d) { return ok ? 0 : -1; } -__attribute__((noinline)) void bugreport() { +[[gnu::noinline]] void bugreport() { debug(0, _(L"This is a bug. Break on 'bugreport' to debug.")); debug(0, _(L"If you can reproduce it, please report: %s."), PACKAGE_BUGREPORT); } @@ -2188,7 +2187,7 @@ void append_path_component(wcstring &path, const wcstring &component) { } extern "C" { -__attribute__((noinline)) void debug_thread_error(void) { +[[gnu::noinline]] void debug_thread_error(void) { while (1) sleep(9999999); } } diff --git a/src/common.h b/src/common.h index 89488162c..95dd176af 100644 --- a/src/common.h +++ b/src/common.h @@ -171,9 +171,8 @@ enum selection_direction_t { /// /// will print the string 'fish: Pi = 3.141', given that debug_level is 1 or higher, and that /// program_name is 'fish'. -void __attribute__((noinline)) debug_impl(int level, const char *msg, ...) - __attribute__((format(printf, 2, 3))); -void __attribute__((noinline)) debug_impl(int level, const wchar_t *msg, ...); +[[gnu::noinline, gnu::format(printf, 2, 3)]] void debug_impl(int level, const char *msg, ...); +[[gnu::noinline]] void debug_impl(int level, const wchar_t *msg, ...); /// The verbosity level of fish. If a call to debug has a severity level higher than \c debug_level, /// it will not be printed. @@ -888,7 +887,7 @@ constexpr bool is_cygwin() { } extern "C" { -__attribute__((noinline)) void debug_thread_error(void); +[[gnu::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 diff --git a/src/fallback.cpp b/src/fallback.cpp index 4826426b9..a8d044d31 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -74,7 +74,7 @@ int fish_mkstemp_cloexec(char *name_template) { /// building on Linux) these should end up just being stripped, as they are static functions that /// are not referenced in this file. // cppcheck-suppress unusedFunction -__attribute__((unused)) static wchar_t *wcsdup_fallback(const wchar_t *in) { +[[gnu::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) { @@ -85,7 +85,7 @@ __attribute__((unused)) static wchar_t *wcsdup_fallback(const wchar_t *in) { return out; } -__attribute__((unused)) static int wcscasecmp_fallback(const wchar_t *a, const wchar_t *b) { +[[gnu::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) { @@ -98,7 +98,7 @@ __attribute__((unused)) static int wcscasecmp_fallback(const wchar_t *a, const w return wcscasecmp_fallback(a + 1, b + 1); } -__attribute__((unused)) static int wcsncasecmp_fallback(const wchar_t *a, const wchar_t *b, +[[gnu::unused]] static int wcsncasecmp_fallback(const wchar_t *a, const wchar_t *b, size_t count) { if (count == 0) return 0; diff --git a/src/fallback.h b/src/fallback.h index de6771057..93745df55 100644 --- a/src/fallback.h +++ b/src/fallback.h @@ -78,9 +78,9 @@ char *tparm_solaris_kludge(char *str, long p1 = 0, long p2 = 0, long p3 = 0, lon // We have to explicitly redeclare these as weak, // since we are forced to set the MIN_REQUIRED availability macro to 10.7 // to use libc++, which in turn exposes these as strong -wchar_t *wcsdup(const wchar_t *) __attribute__((weak_import)); -int wcscasecmp(const wchar_t *, const wchar_t *) __attribute__((weak_import)); -int wcsncasecmp(const wchar_t *, const wchar_t *, size_t n) __attribute__((weak_import)); +[[clang::weak_import]] wchar_t *wcsdup(const wchar_t *); +[[clang::weak_import]] int wcscasecmp(const wchar_t *, const wchar_t *); +[[clang::weak_import]] int wcsncasecmp(const wchar_t *, const wchar_t *, size_t n); 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); From cb7762b7c045b84f30c50f8defe92ef1df8b8c10 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 13:36:54 +0100 Subject: [PATCH 401/439] Move __fish_git_prompt -> fish_git_prompt This exposes it more, since it's quite an important function. We should do the same with the other vcs functions. We leave a compatibility shim in place for now. --- share/functions/__fish_git_prompt.fish | 824 +----------------- share/functions/__fish_vcs_prompt.fish | 2 +- share/functions/fish_git_prompt.fish | 822 +++++++++++++++++ .../tools/web_config/sample_prompts/nim.fish | 4 +- 4 files changed, 828 insertions(+), 824 deletions(-) create mode 100644 share/functions/fish_git_prompt.fish diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index d835e759b..65a4b2c88 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -1,822 +1,4 @@ -# based off of the git-prompt script that ships with git -# -# Written by Kevin Ballard -# Updated by Brian Gernhardt -# -# This is heavily based off of the git-prompt.bash script that ships with -# git, which is Copyright (C) 2006,2007 Shawn O. Pearce . -# The act of porting the code, along with any new code, are Copyright (C) 2012 -# Kevin Ballard . -# -# By virtue of being based on the git-prompt.bash script, this script is -# distributed under the GNU General Public License, version 2.0. -# -# This script vends a function __fish_git_prompt which takes a format string, -# exactly how the bash script works. This can be used in your fish_prompt -# function. -# -# The behavior of __fish_git_prompt is very heavily based off of the bash -# script's __git_ps1 function. As such, usage and customization is very -# similar, although some extra features are provided in this script. -# Due to differences between bash and fish, the PROMPT_COMMAND style where -# passing two or three arguments causes the fucnction to set PS1 is not -# supported. More information on the additional features is found after the -# bash-compatable documentation. -# -# The argument to __fish_git_prompt will be displayed only if you are currently -# in a git repository. The %s token will be the name of the branch. -# -# In addition, if you set __fish_git_prompt_showdirtystate to a nonempty value, -# unstaged (*) and staged (+) changes will be shown next to the branch name. -# You can configure this per-repository with the bash.showDirtyState variable, -# which defaults to true once __fish_git_prompt_showdirtystate is enabled. The -# choice to leave the variable as 'bash' instead of renaming to 'fish' is done -# to preserve compatibility with existing configured repositories. -# -# You can also see if currently something is stashed, by setting -# __fish_git_prompt_showstashstate to a nonempty value. If something is -# stashed, then a '$' will be shown next to the branch name. -# -# If you would like to see if there are untracked files, then you can set -# __fish_git_prompt_showuntrackedfiles to a nonempty value. If there are -# untracked files, then a '%' will be shown next to the branch name. Once you -# have set __fish_git_prompt_showuntrackedfiles, you can override it on a -# per-repository basis by setting the bash.showUntrackedFiles config variable. -# As before, this variable remains named 'bash' to preserve compatibility. -# -# If you would like to see the difference between HEAD and its upstream, set -# __fish_git_prompt_showupstream to 'auto'. A "<" indicates you are behind, ">" -# indicates you are ahead, "<>" indicates you have diverged and "=" indicates -# that there is no difference. You can further control behavior by setting -# __fish_git_prompt_showupstream to a space-separated list of values: -# -# 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) -# git always compare HEAD to @{upstream} -# svn always compare HEAD to your SVN upstream -# none disables (fish only, useful with show_informative_status) -# -# By default, __fish_git_prompt will compare HEAD to your SVN upstream if it -# can find one, or @{upstream} otherwise. Once you have set -# __fish_git_prompt_showupstream, you can override it on a per-repository basis -# by setting the bash.showUpstream config variable. As before, this variable -# remains named 'bash' to preserve compatibility. -# -# If you would like to see more information about the identity of commits -# checked out as a detached HEAD, set __fish_git_prompt_describe_style to -# one of the following values: -# -# contains relative to newer annotated tag (v1.6.3.2~35) -# branch relative to newer tag or branch (master~4) -# describe relative to older annotated tag (v1.6.3.1-13-gdd42c2f) -# default exactly matching tag -# -# If you would like a colored hint about the current dirty state, set -# __fish_git_prompt_showcolorhints. The default colors are -# based on the colored output of "git status -sb" - - -# __fish_git_prompt includes some additional features on top of the -# above-documented bash-compatible features: -# -# -# An "informative git prompt" mode similar to the scripts for bash and zsh -# can be activated by setting __fish_git_prompt_show_informative_status -# This works more like the "informative git prompt" scripts for bash and zsh, -# giving prompts like (master↑1↓2|●3✖4✚5…6) where master is the current branch, -# you have 1 commit your upstream doesn't and it has 2 you don't, and you have -# 3 staged, 4 unmerged, 5 dirty, and 6 untracked files. If you have no -# changes, it displays (master|✔). -# -# Setting __fish_git_prompt_show_informative_status changes several defaults. -# The default mode for __fish_git_prompt_showupstream changes to informative -# and the following characters have their defaults changed. (The characters -# and colors can still be customized as described below.) -# -# upstream_prefix () -# upstream_ahead (↑) -# upstream_behind (↓) -# stateseparator (|) -# dirtystate (✚) -# invalidstate (✖) -# stagedstate (●) -# untrackedfiles (…) -# cleanstate (✔) -# -# -# The color for each component of the prompt can specified using -# __fish_git_prompt_color_, where is one of the following and the -# values are specified as arguments to `set_color`. The variable -# __fish_git_prompt_color is used for any component that does not have an -# individual color set. -# -# prefix Anything before %s in the format string -# suffix Anything after %s in the format string -# bare Marker for a bare repository -# merging Current operation (|MERGING, |REBASE, etc.) -# branch Branch name -# flags Optional flags (see below) -# upstream Upstream name and flags (with showupstream) -# -# -# The following optional flags have both colors, as above, and custom -# characters via __fish_git_prompt_char_. The default character is -# shown in parenthesis. The default color for these flags can be also be set -# via the __fish_git_prompt_color_flags variable. -# -# __fish_git_prompt_showdirtystate -# dirtystate unstaged changes (*) -# stagedstate staged changes (+) -# invalidstate HEAD invalid (#, colored as stagedstate) -# -# __fish_git_prompt_showstashstate -# stashstate stashed changes ($) -# -# __fish_git_prompt_showuntrackedfiles -# untrackedfiles untracked files (%) -# -# __fish_git_prompt_showupstream (all colored as upstream) -# upstream_equal Branch matches upstream (=) -# upstream_behind Upstream has more commits (<) -# upstream_ahead Branch has more commits (>) -# upstream_diverged Upstream and branch have new commits (<>) -# -# __fish_git_prompt_show_informative_status -# (see also the flags for showdirtystate and showuntrackedfiles, above) -# cleanstate Working directory has no changes (✔) -# -# -# The separator between the branch name and flags can also be customized via -# __fish_git_prompt_char_stateseparator. It can only be colored by -# __fish_git_prompt_color. It normally defaults to a space ( ) and defaults -# to a vertical bar (|) when __fish_git_prompt_show_informative_status is set. -# -# The separator before the upstream information can be customized via -# __fish_git_prompt_char_upstream_prefix. It is colored like the rest of -# the upstream information. It normally defaults to nothing () and defaults -# to a space ( ) when __fish_git_prompt_showupstream contains verbose. -# -# -# Turning on __fish_git_prompt_showcolorhints changes the colors as follows to -# more closely match the behavior in bash. Note that setting any of these -# colors manually will override these defaults. -# -# branch Defaults to green -# branch_detached New color, when head is detached, default red -# dirtystate Defaults to red -# stagedstate Defaults to green -# flags Defaults to --bold blue -# -# -# The branch name could be shorten via -# __fish_git_prompt_shorten_branch_len. Define the branch max len. -# __fish_git_prompt_shorten_branch_char_suffix. Customize suffixed char of shorten branch. Defaults to (…). - -function __fish_git_prompt_show_upstream --description "Helper function for __fish_git_prompt" - set -q __fish_git_prompt_showupstream - or set -l __fish_git_prompt_showupstream - 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 - - # Default to informative if __fish_git_prompt_show_informative_status is set - if set -q __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)$' 2>/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 - - # 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\)" 2>/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 '/' '') - - # Use fetch config to fix upstream - set -l fetch_val (command git config "$cur_prefix".fetch) - if test -n "$fetch_val" - string split -m1 : -- "$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 2>/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 - - 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" 2>/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 -sq git - 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 2>/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 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 - - if not set -q ___fish_git_prompt_init - # This takes a while, so it only needs to be done once, - # and then whenever the configuration changes. - __fish_git_prompt_validate_chars - __fish_git_prompt_validate_colors - set -g ___fish_git_prompt_init - end - - set -l space "$___fish_git_prompt_color$___fish_git_prompt_char_stateseparator$___fish_git_prompt_color_done" - - # Use our variables as defaults, but allow overrides via the local git config. - # That means if neither is set, this stays empty. - # - # So "!= true" or "!= false" are useful tests if you want to do something by default. - set -l informative (command git config --bool bash.showInformativeStatus) - - set -l dirty (command git config --bool bash.showDirtyState) - if not set -q dirty[1] - set -q __fish_git_prompt_showdirtystate - and set dirty true - end - - set -l untracked (command git config --bool bash.showUntrackedFiles) - if not set -q untracked[1] - set -q __fish_git_prompt_showuntrackedfiles - and set untracked true - end - - if test "true" = $inside_worktree - # Use informative status if it has been enabled locally, or it has been - # enabled globally (via the fish variable) and dirty or untracked are not false. - # - # This is to allow overrides for the repository. - if test "$informative" = true - or begin - set -q __fish_git_prompt_show_informative_status - and test "$dirty" != false - and test "$untracked" != false - end - set informative_status "$space"(__fish_git_prompt_informative_status) - else - # This has to be set explicitly. - if test "$dirty" = true - set w (__fish_git_prompt_dirty) - set i (__fish_git_prompt_staged $sha) - end - - if set -q __fish_git_prompt_showstashstate - and test -r $git_dir/refs/stash - set s $___fish_git_prompt_char_stashstate - end - - if test "$untracked" != true - set u (__fish_git_prompt_untracked) - end - end - - if set -q __fish_git_prompt_showupstream - or set -q __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 set -q __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) - set -q __fish_git_prompt_shorten_branch_char_suffix - or set -l __fish_git_prompt_shorten_branch_char_suffix "…" - if string match -qr '^\d+$' "$__fish_git_prompt_shorten_branch_len"; and test (string length "$b") -gt $__fish_git_prompt_shorten_branch_len - set b (string sub -l "$__fish_git_prompt_shorten_branch_len" "$b")"$__fish_git_prompt_shorten_branch_char_suffix" - end - 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 - - 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" "$___fish_git_prompt_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 staged - - if test -n "$sha" - command git diff-index --cached --quiet HEAD -- 2>/dev/null - or set staged $___fish_git_prompt_char_stagedstate - else - set staged $___fish_git_prompt_char_invalidstate - end - echo $staged -end - -function __fish_git_prompt_untracked --description "__fish_git_prompt helper, tells whether or not the current repository has untracked files" - set -l untracked - if command git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- :/ >/dev/null 2>&1 - set untracked $___fish_git_prompt_char_untrackedfiles - end - echo $untracked -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 os - command git diff --no-ext-diff --quiet --exit-code 2>/dev/null - 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 - - set -l changedFiles (command git diff --name-status 2>/dev/null | string match -r \\w) - set -l stagedFiles (command git diff --staged --name-status | string match -r \\w) - - set -l x (count $changedFiles) - set -l y (count (string match -r "U" -- $changedFiles)) - set -l dirtystate (math $x - $y) - set -l x (count $stagedFiles) - set -l invalidstate (count (string match -r "U" -- $stagedFiles)) - set -l stagedstate (math $x - $invalidstate) - set -l untrackedfiles (command git ls-files --others --exclude-standard | wc -l | string trim) - - set -l info - - # If `math` fails for some reason, assume the state is clean - it's the simpler path - set -l state (math $dirtystate + $invalidstate + $stagedstate + $untrackedfiles 2>/dev/null) - if test -z "$state" - or test "$state" = 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 count - - if not set -q __fish_git_prompt_hide_$i - set count $$i - end - - set info "$info$color$symbol$count$color_done" - end - end - end - - 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] - - 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 2>/dev/null) - set step (cat $git_dir/rebase-merge/msgnum 2>/dev/null) - set total (cat $git_dir/rebase-merge/end 2>/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 2>/dev/null) - set total (cat $git_dir/rebase-apply/last 2>/dev/null) - if test -f $git_dir/rebase-apply/rebasing - set branch (cat $git_dir/rebase-apply/head-name 2>/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 -z "$branch" - set branch (command git symbolic-ref HEAD 2>/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 - command git describe --contains --all HEAD - case describe - command git describe HEAD - case default '*' - command git describe --tags --exact-match HEAD - end 2>/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 "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 -end - -function __fish_git_prompt_set_char - set -l user_variable_name "$argv[1]" - set -l char $argv[2] - set -l user_variable - if set -q $user_variable_name - set user_variable $$user_variable_name - end - - if set -q argv[3] - and set -q __fish_git_prompt_show_informative_status - set char $argv[3] - end - - 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 -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 '' - -end - -function __fish_git_prompt_set_color - set -l user_variable_name "$argv[1]" - set -l user_variable - if set -q $user_variable_name - set user_variable $$user_variable_name - end - 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 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 - -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 '' '' - - # 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 set -q __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) - - # 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 -a 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 - - commandline -f repaint 2>/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 -a varargs --on-variable __fish_git_prompt_color$var -end -set -a 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 -e ___fish_git_prompt_init - 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 2>/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 -a 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 ___fish_git_prompt_init - set -e _$argv[3] - commandline -f repaint 2>/dev/null - end +function __fish_git_prompt + # TODO: This name is deprecated, figure out a way to tell users. + fish_git_prompt $argv end diff --git a/share/functions/__fish_vcs_prompt.fish b/share/functions/__fish_vcs_prompt.fish index 04101a73b..8b6cf307f 100644 --- a/share/functions/__fish_vcs_prompt.fish +++ b/share/functions/__fish_vcs_prompt.fish @@ -1,5 +1,5 @@ function __fish_vcs_prompt --description "Print the prompts for all available vcsen" - __fish_git_prompt + fish_git_prompt __fish_hg_prompt __fish_svn_prompt end diff --git a/share/functions/fish_git_prompt.fish b/share/functions/fish_git_prompt.fish new file mode 100644 index 000000000..91d3c737e --- /dev/null +++ b/share/functions/fish_git_prompt.fish @@ -0,0 +1,822 @@ +# based off of the git-prompt script that ships with git +# +# Written by Kevin Ballard +# Updated by Brian Gernhardt +# +# This is heavily based off of the git-prompt.bash script that ships with +# git, which is Copyright (C) 2006,2007 Shawn O. Pearce . +# The act of porting the code, along with any new code, are Copyright (C) 2012 +# Kevin Ballard . +# +# By virtue of being based on the git-prompt.bash script, this script is +# distributed under the GNU General Public License, version 2.0. +# +# This script includes a function fish_git_prompt which takes a format string, +# exactly how the bash script works. This can be used in your fish_prompt +# function. +# +# The behavior of fish_git_prompt is very heavily based off of the bash +# script's __git_ps1 function. As such, usage and customization is very +# similar, although some extra features are provided in this script. +# Due to differences between bash and fish, the PROMPT_COMMAND style where +# passing two or three arguments causes the fucnction to set PS1 is not +# supported. More information on the additional features is found after the +# bash-compatable documentation. +# +# The argument to fish_git_prompt will be displayed only if you are currently +# in a git repository. The %s token will be the name of the branch. +# +# In addition, if you set __fish_git_prompt_showdirtystate to a nonempty value, +# unstaged (*) and staged (+) changes will be shown next to the branch name. +# You can configure this per-repository with the bash.showDirtyState variable, +# which defaults to true once __fish_git_prompt_showdirtystate is enabled. The +# choice to leave the variable as 'bash' instead of renaming to 'fish' is done +# to preserve compatibility with existing configured repositories. +# +# You can also see if currently something is stashed, by setting +# __fish_git_prompt_showstashstate to a nonempty value. If something is +# stashed, then a '$' will be shown next to the branch name. +# +# If you would like to see if there are untracked files, then you can set +# __fish_git_prompt_showuntrackedfiles to a nonempty value. If there are +# untracked files, then a '%' will be shown next to the branch name. Once you +# have set __fish_git_prompt_showuntrackedfiles, you can override it on a +# per-repository basis by setting the bash.showUntrackedFiles config variable. +# As before, this variable remains named 'bash' to preserve compatibility. +# +# If you would like to see the difference between HEAD and its upstream, set +# __fish_git_prompt_showupstream to 'auto'. A "<" indicates you are behind, ">" +# indicates you are ahead, "<>" indicates you have diverged and "=" indicates +# that there is no difference. You can further control behavior by setting +# __fish_git_prompt_showupstream to a space-separated list of values: +# +# 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) +# git always compare HEAD to @{upstream} +# svn always compare HEAD to your SVN upstream +# none disables (fish only, useful with show_informative_status) +# +# By default, fish_git_prompt will compare HEAD to your SVN upstream if it +# can find one, or @{upstream} otherwise. Once you have set +# __fish_git_prompt_showupstream, you can override it on a per-repository basis +# by setting the bash.showUpstream config variable. As before, this variable +# remains named 'bash' to preserve compatibility. +# +# If you would like to see more information about the identity of commits +# checked out as a detached HEAD, set __fish_git_prompt_describe_style to +# one of the following values: +# +# contains relative to newer annotated tag (v1.6.3.2~35) +# branch relative to newer tag or branch (master~4) +# describe relative to older annotated tag (v1.6.3.1-13-gdd42c2f) +# default exactly matching tag +# +# If you would like a colored hint about the current dirty state, set +# __fish_git_prompt_showcolorhints. The default colors are +# based on the colored output of "git status -sb" + + +# fish_git_prompt includes some additional features on top of the +# above-documented bash-compatible features: +# +# +# An "informative git prompt" mode similar to the scripts for bash and zsh +# can be activated by setting __fish_git_prompt_show_informative_status +# This works more like the "informative git prompt" scripts for bash and zsh, +# giving prompts like (master↑1↓2|●3✖4✚5…6) where master is the current branch, +# you have 1 commit your upstream doesn't and it has 2 you don't, and you have +# 3 staged, 4 unmerged, 5 dirty, and 6 untracked files. If you have no +# changes, it displays (master|✔). +# +# Setting __fish_git_prompt_show_informative_status changes several defaults. +# The default mode for __fish_git_prompt_showupstream changes to informative +# and the following characters have their defaults changed. (The characters +# and colors can still be customized as described below.) +# +# upstream_prefix () +# upstream_ahead (↑) +# upstream_behind (↓) +# stateseparator (|) +# dirtystate (✚) +# invalidstate (✖) +# stagedstate (●) +# untrackedfiles (…) +# cleanstate (✔) +# +# +# The color for each component of the prompt can specified using +# __fish_git_prompt_color_, where is one of the following and the +# values are specified as arguments to `set_color`. The variable +# __fish_git_prompt_color is used for any component that does not have an +# individual color set. +# +# prefix Anything before %s in the format string +# suffix Anything after %s in the format string +# bare Marker for a bare repository +# merging Current operation (|MERGING, |REBASE, etc.) +# branch Branch name +# flags Optional flags (see below) +# upstream Upstream name and flags (with showupstream) +# +# +# The following optional flags have both colors, as above, and custom +# characters via __fish_git_prompt_char_. The default character is +# shown in parenthesis. The default color for these flags can be also be set +# via the __fish_git_prompt_color_flags variable. +# +# __fish_git_prompt_showdirtystate +# dirtystate unstaged changes (*) +# stagedstate staged changes (+) +# invalidstate HEAD invalid (#, colored as stagedstate) +# +# __fish_git_prompt_showstashstate +# stashstate stashed changes ($) +# +# __fish_git_prompt_showuntrackedfiles +# untrackedfiles untracked files (%) +# +# __fish_git_prompt_showupstream (all colored as upstream) +# upstream_equal Branch matches upstream (=) +# upstream_behind Upstream has more commits (<) +# upstream_ahead Branch has more commits (>) +# upstream_diverged Upstream and branch have new commits (<>) +# +# __fish_git_prompt_show_informative_status +# (see also the flags for showdirtystate and showuntrackedfiles, above) +# cleanstate Working directory has no changes (✔) +# +# +# The separator between the branch name and flags can also be customized via +# __fish_git_prompt_char_stateseparator. It can only be colored by +# __fish_git_prompt_color. It normally defaults to a space ( ) and defaults +# to a vertical bar (|) when __fish_git_prompt_show_informative_status is set. +# +# The separator before the upstream information can be customized via +# __fish_git_prompt_char_upstream_prefix. It is colored like the rest of +# the upstream information. It normally defaults to nothing () and defaults +# to a space ( ) when __fish_git_prompt_showupstream contains verbose. +# +# +# Turning on __fish_git_prompt_showcolorhints changes the colors as follows to +# more closely match the behavior in bash. Note that setting any of these +# colors manually will override these defaults. +# +# branch Defaults to green +# branch_detached New color, when head is detached, default red +# dirtystate Defaults to red +# stagedstate Defaults to green +# flags Defaults to --bold blue +# +# +# The branch name could be shorten via +# __fish_git_prompt_shorten_branch_len. Define the branch max len. +# __fish_git_prompt_shorten_branch_char_suffix. Customize suffixed char of shorten branch. Defaults to (…). + +function __fish_git_prompt_show_upstream --description "Helper function for fish_git_prompt" + set -q __fish_git_prompt_showupstream + or set -l __fish_git_prompt_showupstream + 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 + + # Default to informative if __fish_git_prompt_show_informative_status is set + if set -q __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)$' 2>/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 + + # 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\)" 2>/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 '/' '') + + # Use fetch config to fix upstream + set -l fetch_val (command git config "$cur_prefix".fetch) + if test -n "$fetch_val" + string split -m1 : -- "$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 2>/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 + + 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" 2>/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 -sq git + 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 2>/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 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 + + if not set -q ___fish_git_prompt_init + # This takes a while, so it only needs to be done once, + # and then whenever the configuration changes. + __fish_git_prompt_validate_chars + __fish_git_prompt_validate_colors + set -g ___fish_git_prompt_init + end + + set -l space "$___fish_git_prompt_color$___fish_git_prompt_char_stateseparator$___fish_git_prompt_color_done" + + # Use our variables as defaults, but allow overrides via the local git config. + # That means if neither is set, this stays empty. + # + # So "!= true" or "!= false" are useful tests if you want to do something by default. + set -l informative (command git config --bool bash.showInformativeStatus) + + set -l dirty (command git config --bool bash.showDirtyState) + if not set -q dirty[1] + set -q __fish_git_prompt_showdirtystate + and set dirty true + end + + set -l untracked (command git config --bool bash.showUntrackedFiles) + if not set -q untracked[1] + set -q __fish_git_prompt_showuntrackedfiles + and set untracked true + end + + if test "true" = $inside_worktree + # Use informative status if it has been enabled locally, or it has been + # enabled globally (via the fish variable) and dirty or untracked are not false. + # + # This is to allow overrides for the repository. + if test "$informative" = true + or begin + set -q __fish_git_prompt_show_informative_status + and test "$dirty" != false + and test "$untracked" != false + end + set informative_status "$space"(__fish_git_prompt_informative_status) + else + # This has to be set explicitly. + if test "$dirty" = true + set w (__fish_git_prompt_dirty) + set i (__fish_git_prompt_staged $sha) + end + + if set -q __fish_git_prompt_showstashstate + and test -r $git_dir/refs/stash + set s $___fish_git_prompt_char_stashstate + end + + if test "$untracked" != true + set u (__fish_git_prompt_untracked) + end + end + + if set -q __fish_git_prompt_showupstream + or set -q __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 set -q __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) + set -q __fish_git_prompt_shorten_branch_char_suffix + or set -l __fish_git_prompt_shorten_branch_char_suffix "…" + if string match -qr '^\d+$' "$__fish_git_prompt_shorten_branch_len"; and test (string length "$b") -gt $__fish_git_prompt_shorten_branch_len + set b (string sub -l "$__fish_git_prompt_shorten_branch_len" "$b")"$__fish_git_prompt_shorten_branch_char_suffix" + end + 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 + + 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" "$___fish_git_prompt_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 staged + + if test -n "$sha" + command git diff-index --cached --quiet HEAD -- 2>/dev/null + or set staged $___fish_git_prompt_char_stagedstate + else + set staged $___fish_git_prompt_char_invalidstate + end + echo $staged +end + +function __fish_git_prompt_untracked --description "fish_git_prompt helper, tells whether or not the current repository has untracked files" + set -l untracked + if command git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- :/ >/dev/null 2>&1 + set untracked $___fish_git_prompt_char_untrackedfiles + end + echo $untracked +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 os + command git diff --no-ext-diff --quiet --exit-code 2>/dev/null + 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 + + set -l changedFiles (command git diff --name-status 2>/dev/null | string match -r \\w) + set -l stagedFiles (command git diff --staged --name-status | string match -r \\w) + + set -l x (count $changedFiles) + set -l y (count (string match -r "U" -- $changedFiles)) + set -l dirtystate (math $x - $y) + set -l x (count $stagedFiles) + set -l invalidstate (count (string match -r "U" -- $stagedFiles)) + set -l stagedstate (math $x - $invalidstate) + set -l untrackedfiles (command git ls-files --others --exclude-standard | wc -l | string trim) + + set -l info + + # If `math` fails for some reason, assume the state is clean - it's the simpler path + set -l state (math $dirtystate + $invalidstate + $stagedstate + $untrackedfiles 2>/dev/null) + if test -z "$state" + or test "$state" = 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 count + + if not set -q __fish_git_prompt_hide_$i + set count $$i + end + + set info "$info$color$symbol$count$color_done" + end + end + end + + 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] + + 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 2>/dev/null) + set step (cat $git_dir/rebase-merge/msgnum 2>/dev/null) + set total (cat $git_dir/rebase-merge/end 2>/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 2>/dev/null) + set total (cat $git_dir/rebase-apply/last 2>/dev/null) + if test -f $git_dir/rebase-apply/rebasing + set branch (cat $git_dir/rebase-apply/head-name 2>/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 -z "$branch" + set branch (command git symbolic-ref HEAD 2>/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 + command git describe --contains --all HEAD + case describe + command git describe HEAD + case default '*' + command git describe --tags --exact-match HEAD + end 2>/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 "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 +end + +function __fish_git_prompt_set_char + set -l user_variable_name "$argv[1]" + set -l char $argv[2] + set -l user_variable + if set -q $user_variable_name + set user_variable $$user_variable_name + end + + if set -q argv[3] + and set -q __fish_git_prompt_show_informative_status + set char $argv[3] + end + + 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 +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 '' + +end + +function __fish_git_prompt_set_color + set -l user_variable_name "$argv[1]" + set -l user_variable + if set -q $user_variable_name + set user_variable $$user_variable_name + end + 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 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 + +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 '' '' + + # 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 set -q __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) + + # 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 -a 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 + + commandline -f repaint 2>/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 -a varargs --on-variable __fish_git_prompt_color$var +end +set -a 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 -e ___fish_git_prompt_init + 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 2>/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 -a 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 ___fish_git_prompt_init + set -e _$argv[3] + commandline -f repaint 2>/dev/null + end +end diff --git a/share/tools/web_config/sample_prompts/nim.fish b/share/tools/web_config/sample_prompts/nim.fish index 93c574dd5..a6a886580 100644 --- a/share/tools/web_config/sample_prompts/nim.fish +++ b/share/tools/web_config/sample_prompts/nim.fish @@ -10,7 +10,7 @@ function fish_prompt # - the current path (with prompt_pwd) # - date +%X # - the current virtual environment, if any - # - the current git status, if any, with __fish_git_prompt + # - the current git status, if any, with fish_git_prompt # - the current battery state, if any, and if your power cable is unplugged, and if you have "acpi" # - current background jobs, if any @@ -79,7 +79,7 @@ function fish_prompt and _nim_prompt_wrapper $retc V (basename "$VIRTUAL_ENV") # git - set prompt_git (__fish_git_prompt | string trim -c ' ()') + set prompt_git (fish_git_prompt | string trim -c ' ()') test -n "$prompt_git" and _nim_prompt_wrapper $retc G $prompt_git From f2b08ec592cd73d518de4e0219eb1b4891e0a143 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 26 Jan 2019 13:58:18 +0100 Subject: [PATCH 402/439] Document fish_git_prompt Shows how convoluted the thing is, really. --- doc_src/fish_git_prompt.txt | 93 +++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 doc_src/fish_git_prompt.txt diff --git a/doc_src/fish_git_prompt.txt b/doc_src/fish_git_prompt.txt new file mode 100644 index 000000000..929a1b8f4 --- /dev/null +++ b/doc_src/fish_git_prompt.txt @@ -0,0 +1,93 @@ +\section fish_git_prompt fish_git_prompt - output git information for use in a prompt + +\subsection fish_git_prompt-description Description + +The fish_git_prompt function can be used to display information about the current git repository, if any. + +For obvious reasons, it requires having git installed. + +There are numerous configuration options, either as fish variables or git config variables. If a git config variable is supported, it will be used if set, and the fish variable will only be used if it isn't. + +- $__fish_git_prompt_showdirtystate or the git config option "bash.showDirtyState" can be set to show if the repository is "dirty", i.e. has uncommitted changes. + +- $__fish_git_prompt_showuntrackedfiles or the git config option "bash.showUntrackedFiles" can be set to show if the repository has untracked files (that aren't ignored). + +- $__fish_git_prompt_show_informative_status or the git config option "bash.showInformativeStatus" can be set to enable the "informative" display, which will show a large amount of information - the number of untracked files, dirty files, unpushed/unpulled commits, etc... In large repositories, this can take a lot of time, so it is recommended to disable it there. + +- $__fish_git_prompt_showupstream can be set to a number of values to determine how changes between HEAD and upstream are shown: + + 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) + git always compare HEAD to @{upstream} + svn always compare HEAD to your SVN upstream + none disables (fish only, useful with show_informative_status) + +- $__fish_git_prompt_showstashstate can be set to display the state of the stash. + +- $__fish_git_prompt_shorten_branch_len can be set to the number of characters that the branch name will be shortened to. + +- $__fish_git_prompt_describe_style can be set to a number of styles that describe the current HEAD: + + contains + branch + describe + default + +- $__fish_git_prompt_showcolorhints can be set to enable coloring for certain things. + +A number of variables to set characters and color used to indicate things. + +- $__fish_git_prompt_char_cleanstate +- $__fish_git_prompt_char_dirtystate +- $__fish_git_prompt_char_invalidstate +- $__fish_git_prompt_char_stagedstate +- $__fish_git_prompt_char_stashstate +- $__fish_git_prompt_char_stateseparator +- $__fish_git_prompt_char_untrackedfiles +- $__fish_git_prompt_char_upstream_ahead +- $__fish_git_prompt_char_upstream_behind +- $__fish_git_prompt_char_upstream_diverged +- $__fish_git_prompt_char_upstream_equal +- $__fish_git_prompt_char_upstream_prefix +- $__fish_git_prompt_color +- $__fish_git_prompt_color_prefix +- $__fish_git_prompt_color_suffix +- $__fish_git_prompt_color_bare +- $__fish_git_prompt_color_merging +- $__fish_git_prompt_color_cleanstate +- $__fish_git_prompt_color_invalidstate +- $__fish_git_prompt_color_upstream + +Colors used with showcolorhints: + +- $__fish_git_prompt_color_flags +- $__fish_git_prompt_color_branch +- $__fish_git_prompt_color_dirtystate +- $__fish_git_prompt_color_stagedstate +- $__fish_git_prompt_color_flags +- $__fish_git_prompt_color_branch +- $__fish_git_prompt_color_dirtystate +- $__fish_git_prompt_color_stagedstate +- $__fish_git_prompt_color_branch_detached + +Colors used with their respective flags enabled: +- $__fish_git_prompt_color_stashstate +- $__fish_git_prompt_color_untrackedfiles + +Note that all colors can also have a corresponding "_done" color. E.g. $__fish_git_prompt_color_upstream_done, used right _after_ the upstream. + +See also __fish_vcs_prompt, which will call all supported vcs-prompt functions, including git, hg and svn. + +\subsection fish_git_prompt-example Example + +A simple prompt that displays git info: + +\fish +function fish_prompt + ... + set -g __fish_git_prompt_showupstream auto + printf '%s %s$' $PWD (fish_git_prompt) +end +\endfish + From c77133492434592bb07c6b15cae56e7c097c16aa Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 15:43:05 +0100 Subject: [PATCH 403/439] Rename __fish_vcs_prompt -> fish_vcs_prompt Still keep a stub under the old name for compatibility. --- doc_src/fish_git_prompt.txt | 2 +- share/functions/__fish_vcs_prompt.fish | 4 +--- share/functions/fish_vcs_prompt.fish | 5 +++++ 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 share/functions/fish_vcs_prompt.fish diff --git a/doc_src/fish_git_prompt.txt b/doc_src/fish_git_prompt.txt index 929a1b8f4..a4867d7c5 100644 --- a/doc_src/fish_git_prompt.txt +++ b/doc_src/fish_git_prompt.txt @@ -77,7 +77,7 @@ Colors used with their respective flags enabled: Note that all colors can also have a corresponding "_done" color. E.g. $__fish_git_prompt_color_upstream_done, used right _after_ the upstream. -See also __fish_vcs_prompt, which will call all supported vcs-prompt functions, including git, hg and svn. +See also fish_vcs_prompt, which will call all supported vcs-prompt functions, including git, hg and svn. \subsection fish_git_prompt-example Example diff --git a/share/functions/__fish_vcs_prompt.fish b/share/functions/__fish_vcs_prompt.fish index 8b6cf307f..552fcd3f6 100644 --- a/share/functions/__fish_vcs_prompt.fish +++ b/share/functions/__fish_vcs_prompt.fish @@ -1,5 +1,3 @@ function __fish_vcs_prompt --description "Print the prompts for all available vcsen" - fish_git_prompt - __fish_hg_prompt - __fish_svn_prompt + fish_vcs_prompt end diff --git a/share/functions/fish_vcs_prompt.fish b/share/functions/fish_vcs_prompt.fish new file mode 100644 index 000000000..b81ef1358 --- /dev/null +++ b/share/functions/fish_vcs_prompt.fish @@ -0,0 +1,5 @@ +function fish_vcs_prompt --description "Print the prompts for all available vcsen" + fish_git_prompt + __fish_hg_prompt + __fish_svn_prompt +end From c29023b3e86d43531e945b8e2f0359f15a0b9eba Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 15:45:10 +0100 Subject: [PATCH 404/439] Rename __fish_svn_prompt -> fish_svn_prompt --- share/functions/__fish_svn_prompt.fish | 150 +------------------------ share/functions/fish_svn_prompt.fish | 149 ++++++++++++++++++++++++ share/functions/fish_vcs_prompt.fish | 2 +- 3 files changed, 152 insertions(+), 149 deletions(-) create mode 100644 share/functions/fish_svn_prompt.fish diff --git a/share/functions/__fish_svn_prompt.fish b/share/functions/__fish_svn_prompt.fish index 0cae12cf4..8d394086f 100644 --- a/share/functions/__fish_svn_prompt.fish +++ b/share/functions/__fish_svn_prompt.fish @@ -1,149 +1,3 @@ -# colour of the revision number to display in the prompt -set -g __fish_svn_prompt_color_revision yellow - -# setting the prompt status separator character -set -g __fish_svn_prompt_char_separator "|" - -# ============================== - -# SVN status display variables -# these are paired in groups of two: -# 1. the character to display in the prompt -# 2. the colour that should be used to display the character -# -# these variables are user-configurable and can be set to customize the display output - -set -g __fish_svn_prompt_char_added_display 'A' -set -g __fish_svn_prompt_char_added_color green - -set -g __fish_svn_prompt_char_conflicted_display 'C' -set -g __fish_svn_prompt_char_conflicted_color --underline magenta - -set -g __fish_svn_prompt_char_deleted_display 'D' -set -g __fish_svn_prompt_char_deleted_color red - -set -g __fish_svn_prompt_char_ignored_display 'I' -set -g __fish_svn_prompt_char_ignored_color --bold yellow - -set -g __fish_svn_prompt_char_modified_display 'M' -set -g __fish_svn_prompt_char_modified_color blue - -set -g __fish_svn_prompt_char_replaced_display 'R' -set -g __fish_svn_prompt_char_replaced_color cyan - -set -g __fish_svn_prompt_char_unversioned_external_display 'X' -set -g __fish_svn_prompt_char_unversioned_external_color --underline cyan - -set -g __fish_svn_prompt_char_unversioned_display '?' -set -g __fish_svn_prompt_char_unversioned_color purple - -set -g __fish_svn_prompt_char_missing_display '!' -set -g __fish_svn_prompt_char_missing_color yellow - -set -g __fish_svn_prompt_char_versioned_obstructed_display '~' -set -g __fish_svn_prompt_char_versioned_obstructed_color magenta - -set -g __fish_svn_prompt_char_locked_display 'L' -set -g __fish_svn_prompt_char_locked_color --bold red - -set -g __fish_svn_prompt_char_scheduled_display '+' -set -g __fish_svn_prompt_char_scheduled_color --bold green - -set -g __fish_svn_prompt_char_switched_display 'S' -set -g __fish_svn_prompt_char_switched_color --bold blue - -set -g __fish_svn_prompt_char_token_present_display 'K' -set -g __fish_svn_prompt_char_token_present_color --bold cyan - -set -g __fish_svn_prompt_char_token_other_display 'O' -set -g __fish_svn_prompt_char_token_other_color --underline purple - -set -g __fish_svn_prompt_char_token_stolen_display 'T' -set -g __fish_svn_prompt_char_token_stolen_color --bold purple - -set -g __fish_svn_prompt_char_token_broken_display 'B' -set -g __fish_svn_prompt_char_token_broken_color --bold magenta - - -# ============================== - -function __fish_svn_prompt_parse_status --argument flag_status_string --description "helper function that does pretty formatting on svn status" - # SVN status symbols - # Do not change these! These are the expected characters that are output from `svn status`, they are taken from `svn help status` - set -l __fish_svn_prompt_chars A C D I M R X \? ! ~ L + S K O T B - # this sets up an array of all the types of status codes that could be returned. - set -l __fish_svn_prompt_flag_names added conflicted deleted ignored modified replaced unversioned_external unversioned missing locked scheduled switched token_present token_other token_stolen token_broken - # iterate over the different status types - for flag_type in $__fish_svn_prompt_flag_names - # resolve the name of the variable for the character representing the current status type - set -l flag_index (contains -i $flag_type $__fish_svn_prompt_flag_names) - # check to see if the status string for this column contains the character representing the current status type - if test (echo $flag_status_string | grep -c $__fish_svn_prompt_chars[$flag_index]) -eq 1 - # if it does, then get the names of the variables for the display character and colour to format it with - set -l flag_var_display __fish_svn_prompt_char_{$flag_type}_display - set -l flag_var_color __fish_svn_prompt_char_{$flag_type}_color - # set the colour and print display character, then restore to default display colour - printf '%s%s%s' (set_color $$flag_var_color) $$flag_var_display (set_color normal) - end - end -end - - -function __fish_svn_prompt --description "Prompt function for svn" - # if svn isn't installed then don't do anything - if not command -sq svn - return 1 - end - - # make sure that this is a svn repo - set -l checkout_info (command svn info 2>/dev/null) - if [ $status -ne 0 ] - - return - end - - # get the current revision number - printf '(%s%s%s' (set_color $__fish_svn_prompt_color_revision) (__fish_print_svn_rev) (set_color normal) - - # resolve the status of the checkout - # 1. perform `svn status` - # 2. remove extra lines that aren't necessary - # 3. cut the output down to the first 7 columns, as these contain the information needed - set -l svn_status_lines (command svn status | sed -e 's=^Summary of conflicts.*==' -e 's=^ Text conflicts.*==' -e 's=^ Property conflicts.*==' -e 's=^ Tree conflicts.*==' -e 's=.*incoming .* upon update.*==' | cut -c 1-7) - - # track the last column to contain a status flag - set -l last_column 0 - - # iterate over the 7 columns of output (the 7 columns are defined on `svn help status`) - for col in (seq 7) - # get the output for a particular column - # 1. echo the whole status flag text - # 2. cut out the current column of characters - # 3. remove spaces and newline characters - set -l column_status (printf '%s\n' $svn_status_lines | cut -c $col | tr -d ' \n') - - # check that the character count is not zero (this would indicate that there are status flags in this column) - if [ (count $column_status) -ne 0 ] - - # we only want to display unique status flags (eg: if there are 5 modified files, the prompt should only show the modified status once) - set -l column_unique_status (echo $column_status | sort -u) - # parse the status flags for this column and create the formatting by calling out to the helper function - set -l svn_status_flags (__fish_svn_prompt_parse_status $column_unique_status) - - # the default separator is empty - set -l prompt_separator "" - for index in (seq (math "$col - $last_column")) - # the prompt separator variable has to be updated with the number of separators needed to represent empty status columns (eg: if a file has the status "A +" then it should display as "A|||+" in the prompt) - set prompt_separator $prompt_separator$__fish_svn_prompt_char_separator - end - - # record that the current column was the last one printed to the prompt - set last_column $col - # print the separator string then the current column's status flags - printf '%s%s' $prompt_separator $svn_status_flags - end - end - - # print the close of the svn status prompt - printf ')' +function __fish_svn_prompt + fish_svn_prompt end diff --git a/share/functions/fish_svn_prompt.fish b/share/functions/fish_svn_prompt.fish new file mode 100644 index 000000000..5b3a90e23 --- /dev/null +++ b/share/functions/fish_svn_prompt.fish @@ -0,0 +1,149 @@ +# colour of the revision number to display in the prompt +set -g __fish_svn_prompt_color_revision yellow + +# setting the prompt status separator character +set -g __fish_svn_prompt_char_separator "|" + +# ============================== + +# SVN status display variables +# these are paired in groups of two: +# 1. the character to display in the prompt +# 2. the colour that should be used to display the character +# +# these variables are user-configurable and can be set to customize the display output + +set -g __fish_svn_prompt_char_added_display 'A' +set -g __fish_svn_prompt_char_added_color green + +set -g __fish_svn_prompt_char_conflicted_display 'C' +set -g __fish_svn_prompt_char_conflicted_color --underline magenta + +set -g __fish_svn_prompt_char_deleted_display 'D' +set -g __fish_svn_prompt_char_deleted_color red + +set -g __fish_svn_prompt_char_ignored_display 'I' +set -g __fish_svn_prompt_char_ignored_color --bold yellow + +set -g __fish_svn_prompt_char_modified_display 'M' +set -g __fish_svn_prompt_char_modified_color blue + +set -g __fish_svn_prompt_char_replaced_display 'R' +set -g __fish_svn_prompt_char_replaced_color cyan + +set -g __fish_svn_prompt_char_unversioned_external_display 'X' +set -g __fish_svn_prompt_char_unversioned_external_color --underline cyan + +set -g __fish_svn_prompt_char_unversioned_display '?' +set -g __fish_svn_prompt_char_unversioned_color purple + +set -g __fish_svn_prompt_char_missing_display '!' +set -g __fish_svn_prompt_char_missing_color yellow + +set -g __fish_svn_prompt_char_versioned_obstructed_display '~' +set -g __fish_svn_prompt_char_versioned_obstructed_color magenta + +set -g __fish_svn_prompt_char_locked_display 'L' +set -g __fish_svn_prompt_char_locked_color --bold red + +set -g __fish_svn_prompt_char_scheduled_display '+' +set -g __fish_svn_prompt_char_scheduled_color --bold green + +set -g __fish_svn_prompt_char_switched_display 'S' +set -g __fish_svn_prompt_char_switched_color --bold blue + +set -g __fish_svn_prompt_char_token_present_display 'K' +set -g __fish_svn_prompt_char_token_present_color --bold cyan + +set -g __fish_svn_prompt_char_token_other_display 'O' +set -g __fish_svn_prompt_char_token_other_color --underline purple + +set -g __fish_svn_prompt_char_token_stolen_display 'T' +set -g __fish_svn_prompt_char_token_stolen_color --bold purple + +set -g __fish_svn_prompt_char_token_broken_display 'B' +set -g __fish_svn_prompt_char_token_broken_color --bold magenta + + +# ============================== + +function __fish_svn_prompt_parse_status --argument flag_status_string --description "helper function that does pretty formatting on svn status" + # SVN status symbols + # Do not change these! These are the expected characters that are output from `svn status`, they are taken from `svn help status` + set -l __fish_svn_prompt_chars A C D I M R X \? ! ~ L + S K O T B + # this sets up an array of all the types of status codes that could be returned. + set -l __fish_svn_prompt_flag_names added conflicted deleted ignored modified replaced unversioned_external unversioned missing locked scheduled switched token_present token_other token_stolen token_broken + # iterate over the different status types + for flag_type in $__fish_svn_prompt_flag_names + # resolve the name of the variable for the character representing the current status type + set -l flag_index (contains -i $flag_type $__fish_svn_prompt_flag_names) + # check to see if the status string for this column contains the character representing the current status type + if test (echo $flag_status_string | grep -c $__fish_svn_prompt_chars[$flag_index]) -eq 1 + # if it does, then get the names of the variables for the display character and colour to format it with + set -l flag_var_display __fish_svn_prompt_char_{$flag_type}_display + set -l flag_var_color __fish_svn_prompt_char_{$flag_type}_color + # set the colour and print display character, then restore to default display colour + printf '%s%s%s' (set_color $$flag_var_color) $$flag_var_display (set_color normal) + end + end +end + + +function fish_svn_prompt --description "Prompt function for svn" + # if svn isn't installed then don't do anything + if not command -sq svn + return 1 + end + + # make sure that this is a svn repo + set -l checkout_info (command svn info 2>/dev/null) + if [ $status -ne 0 ] + + return + end + + # get the current revision number + printf '(%s%s%s' (set_color $__fish_svn_prompt_color_revision) (__fish_print_svn_rev) (set_color normal) + + # resolve the status of the checkout + # 1. perform `svn status` + # 2. remove extra lines that aren't necessary + # 3. cut the output down to the first 7 columns, as these contain the information needed + set -l svn_status_lines (command svn status | sed -e 's=^Summary of conflicts.*==' -e 's=^ Text conflicts.*==' -e 's=^ Property conflicts.*==' -e 's=^ Tree conflicts.*==' -e 's=.*incoming .* upon update.*==' | cut -c 1-7) + + # track the last column to contain a status flag + set -l last_column 0 + + # iterate over the 7 columns of output (the 7 columns are defined on `svn help status`) + for col in (seq 7) + # get the output for a particular column + # 1. echo the whole status flag text + # 2. cut out the current column of characters + # 3. remove spaces and newline characters + set -l column_status (printf '%s\n' $svn_status_lines | cut -c $col | tr -d ' \n') + + # check that the character count is not zero (this would indicate that there are status flags in this column) + if [ (count $column_status) -ne 0 ] + + # we only want to display unique status flags (eg: if there are 5 modified files, the prompt should only show the modified status once) + set -l column_unique_status (echo $column_status | sort -u) + # parse the status flags for this column and create the formatting by calling out to the helper function + set -l svn_status_flags (__fish_svn_prompt_parse_status $column_unique_status) + + # the default separator is empty + set -l prompt_separator "" + for index in (seq (math "$col - $last_column")) + # the prompt separator variable has to be updated with the number of separators needed to represent empty status columns (eg: if a file has the status "A +" then it should display as "A|||+" in the prompt) + set prompt_separator $prompt_separator$__fish_svn_prompt_char_separator + end + + # record that the current column was the last one printed to the prompt + set last_column $col + # print the separator string then the current column's status flags + printf '%s%s' $prompt_separator $svn_status_flags + end + end + + # print the close of the svn status prompt + printf ')' +end diff --git a/share/functions/fish_vcs_prompt.fish b/share/functions/fish_vcs_prompt.fish index b81ef1358..3df909695 100644 --- a/share/functions/fish_vcs_prompt.fish +++ b/share/functions/fish_vcs_prompt.fish @@ -1,5 +1,5 @@ function fish_vcs_prompt --description "Print the prompts for all available vcsen" fish_git_prompt __fish_hg_prompt - __fish_svn_prompt + fish_svn_prompt end From 9c829794597a201f441645b452b457717a61f96d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 15:46:13 +0100 Subject: [PATCH 405/439] Rename __fish_hg_prompt -> fish_hg_prompt --- share/functions/__fish_hg_prompt.fish | 100 +------------------------- share/functions/fish_hg_prompt.fish | 99 +++++++++++++++++++++++++ share/functions/fish_vcs_prompt.fish | 2 +- 3 files changed, 102 insertions(+), 99 deletions(-) create mode 100644 share/functions/fish_hg_prompt.fish diff --git a/share/functions/__fish_hg_prompt.fish b/share/functions/__fish_hg_prompt.fish index 63aa0a57c..b4ac91c8c 100644 --- a/share/functions/__fish_hg_prompt.fish +++ b/share/functions/__fish_hg_prompt.fish @@ -1,99 +1,3 @@ -# Adapted from __terlar_git_prompt - -set -g fish_color_hg_clean green -set -g fish_color_hg_modified yellow -set -g fish_color_hg_dirty red - -set -g fish_color_hg_added green -set -g fish_color_hg_renamed magenta -set -g fish_color_hg_copied magenta -set -g fish_color_hg_deleted red -set -g fish_color_hg_untracked yellow -set -g fish_color_hg_unmerged red - -set -g fish_prompt_hg_status_added '✚' -set -g fish_prompt_hg_status_modified '*' -set -g fish_prompt_hg_status_copied '⇒' -set -g fish_prompt_hg_status_deleted '✖' -set -g fish_prompt_hg_status_untracked '?' -set -g fish_prompt_hg_status_unmerged '!' - -set -g fish_prompt_hg_status_order added modified copied deleted untracked unmerged - -function __fish_hg_prompt --description 'Write out the hg prompt' - # If hg isn't installed, there's nothing we can do - # Return 1 so the calling prompt can deal with it - if not command -sq hg - return 1 - end - - set -l root (fish_print_hg_root) - or return 0 - - # Read branch and bookmark - set -l branch (cat $root/branch 2>/dev/null; or echo default) - if set -l bookmark (cat $root/bookmarks.current 2>/dev/null) - set branch "$branch|$bookmark" - end - - echo -n '|' - - # For some reason, "-q" still prints the same output, but ~20% faster. - # Disabling color and pager is always a good idea. - set -l repo_status (hg status -q --color never --pager never | string sub -l 2 | sort -u) - - # Show nice color for a clean repo - if test -z "$repo_status" - set_color $fish_color_hg_clean - echo -n "($branch)"'✓' - set_color normal - - # Handle modified or dirty (unknown state) - else - set -l hg_statuses - - # Take actions for the statuses of the files in the repo - for line in $repo_status - - # Add a character for each file status if we have one - # HACK: To allow this to work both with and without '?' globs - set -l q '?' - switch $line - case 'A ' - set -a hg_statuses added - case 'M ' ' M' - set -a hg_statuses modified - case 'C ' - set -a hg_statuses copied - case 'D ' ' D' - set -a hg_statuses deleted - case "$dq " - set -a hg_statuses untracked - case 'U*' '*U' 'DD' 'AA' - set -a hg_statuses unmerged - end - end - - if string match -qr '^[AMCD]' $repo_status - set_color $fish_color_hg_modified - else - set_color $fish_color_hg_dirty - end - - echo -n "($branch)"'⚡' - - # Sort status symbols - for i in $fish_prompt_hg_status_order - if contains -- $i $hg_statuses - set -l color_name fish_color_hg_$i - set -l status_name fish_prompt_hg_status_$i - - set_color $$color_name - echo -n $$status_name - end - end - - set_color normal - end - +function __fish_hg_prompt + fish_hg_prompt end diff --git a/share/functions/fish_hg_prompt.fish b/share/functions/fish_hg_prompt.fish new file mode 100644 index 000000000..584695f54 --- /dev/null +++ b/share/functions/fish_hg_prompt.fish @@ -0,0 +1,99 @@ +# Adapted from __terlar_git_prompt + +set -g fish_color_hg_clean green +set -g fish_color_hg_modified yellow +set -g fish_color_hg_dirty red + +set -g fish_color_hg_added green +set -g fish_color_hg_renamed magenta +set -g fish_color_hg_copied magenta +set -g fish_color_hg_deleted red +set -g fish_color_hg_untracked yellow +set -g fish_color_hg_unmerged red + +set -g fish_prompt_hg_status_added '✚' +set -g fish_prompt_hg_status_modified '*' +set -g fish_prompt_hg_status_copied '⇒' +set -g fish_prompt_hg_status_deleted '✖' +set -g fish_prompt_hg_status_untracked '?' +set -g fish_prompt_hg_status_unmerged '!' + +set -g fish_prompt_hg_status_order added modified copied deleted untracked unmerged + +function fish_hg_prompt --description 'Write out the hg prompt' + # If hg isn't installed, there's nothing we can do + # Return 1 so the calling prompt can deal with it + if not command -sq hg + return 1 + end + + set -l root (fish_print_hg_root) + or return 0 + + # Read branch and bookmark + set -l branch (cat $root/branch 2>/dev/null; or echo default) + if set -l bookmark (cat $root/bookmarks.current 2>/dev/null) + set branch "$branch|$bookmark" + end + + echo -n '|' + + # For some reason, "-q" still prints the same output, but ~20% faster. + # Disabling color and pager is always a good idea. + set -l repo_status (hg status -q --color never --pager never | string sub -l 2 | sort -u) + + # Show nice color for a clean repo + if test -z "$repo_status" + set_color $fish_color_hg_clean + echo -n "($branch)"'✓' + set_color normal + + # Handle modified or dirty (unknown state) + else + set -l hg_statuses + + # Take actions for the statuses of the files in the repo + for line in $repo_status + + # Add a character for each file status if we have one + # HACK: To allow this to work both with and without '?' globs + set -l q '?' + switch $line + case 'A ' + set -a hg_statuses added + case 'M ' ' M' + set -a hg_statuses modified + case 'C ' + set -a hg_statuses copied + case 'D ' ' D' + set -a hg_statuses deleted + case "$dq " + set -a hg_statuses untracked + case 'U*' '*U' 'DD' 'AA' + set -a hg_statuses unmerged + end + end + + if string match -qr '^[AMCD]' $repo_status + set_color $fish_color_hg_modified + else + set_color $fish_color_hg_dirty + end + + echo -n "($branch)"'⚡' + + # Sort status symbols + for i in $fish_prompt_hg_status_order + if contains -- $i $hg_statuses + set -l color_name fish_color_hg_$i + set -l status_name fish_prompt_hg_status_$i + + set_color $$color_name + echo -n $$status_name + end + end + + set_color normal + end + +end diff --git a/share/functions/fish_vcs_prompt.fish b/share/functions/fish_vcs_prompt.fish index 3df909695..6d94e6e06 100644 --- a/share/functions/fish_vcs_prompt.fish +++ b/share/functions/fish_vcs_prompt.fish @@ -1,5 +1,5 @@ function fish_vcs_prompt --description "Print the prompts for all available vcsen" fish_git_prompt - __fish_hg_prompt + fish_hg_prompt fish_svn_prompt end From f7c981af6337d8d8ad44874809b016fab4cccbca Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 15:48:34 +0100 Subject: [PATCH 406/439] Document vcs prompt --- doc_src/fish_vcs_prompt.txt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 doc_src/fish_vcs_prompt.txt diff --git a/doc_src/fish_vcs_prompt.txt b/doc_src/fish_vcs_prompt.txt new file mode 100644 index 000000000..fd743473e --- /dev/null +++ b/doc_src/fish_vcs_prompt.txt @@ -0,0 +1,28 @@ +\section fish_vcs_prompt fish_vcs_prompt - output vcs information for use in a prompt + +\subsection fish_vcs_prompt-description Description + +The fish_vcs_prompt function can be used to display information about the current vcs repository, if any. + +It calls out to vcs-specific functions. The currently supported ones are: + +- fish_git_prompt +- fish_hg_prompt +- fish_svn_prompt + +If a vcs isn't installed, the respective function does nothing. + +For more information, see their documentation. + +\subsection fish_vcs_prompt-example Example + +A simple prompt that displays vcs info: + +\fish +function fish_prompt + ... + set -g __fish_git_prompt_showupstream auto + printf '%s %s$' $PWD (fish_vcs_prompt) +end +\endfish + From b19b1ee23a4da0e247d786309c0c4d4cc6a45755 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 15:54:49 +0100 Subject: [PATCH 407/439] Document fish_hg_prompt --- doc_src/fish_hg_prompt.txt | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 doc_src/fish_hg_prompt.txt diff --git a/doc_src/fish_hg_prompt.txt b/doc_src/fish_hg_prompt.txt new file mode 100644 index 000000000..044af98c3 --- /dev/null +++ b/doc_src/fish_hg_prompt.txt @@ -0,0 +1,45 @@ +\section fish_hg_prompt fish_hg_prompt - output mercurial information for use in a prompt + +\subsection fish_hg_prompt-description Description + +The fish_hg_prompt function can be used to display information about the current mercurial repository, if any. + +For obvious reasons, it requires having hg installed. + +There are numerous configuration options: + +- $fish_color_hg_clean, $fish_color_hg_modified and $fish_color_hg_dirty: The color to use when the repo has the respective status + +Some colors for status symbols: + +- $fish_color_hg_added +- $fish_color_hg_renamed +- $fish_color_hg_copied +- $fish_color_hg_deleted +- $fish_color_hg_untracked +- $fish_color_hg_unmerged + +And the status symbols themselves: + +- $fish_prompt_hg_status_added, default '✚' +- $fish_prompt_hg_status_modified, default '*' +- $fish_prompt_hg_status_copied, default '⇒' +- $fish_prompt_hg_status_deleted, default '✖' +- $fish_prompt_hg_status_untracked, default '?' +- $fish_prompt_hg_status_unmerged, default '!' + +And $fish_prompt_hg_status_order, which can be used to change the order the status symbols appear in. It defaults to `added modified copied deleted untracked unmerged`. + +See also fish_vcs_prompt, which will call all supported vcs-prompt functions, including git, hg and svn. + +\subsection fish_hg_prompt-example Example + +A simple prompt that displays hg info: + +\fish +function fish_prompt + ... + printf '%s %s$' $PWD (fish_hg_prompt) +end +\endfish + From 8c6ae4612b48b4f52d1b0f6c72177ff912b6db0b Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 15:59:33 +0100 Subject: [PATCH 408/439] Document fish_svn_prompt --- doc_src/fish_svn_prompt.txt | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 doc_src/fish_svn_prompt.txt diff --git a/doc_src/fish_svn_prompt.txt b/doc_src/fish_svn_prompt.txt new file mode 100644 index 000000000..e5561ecad --- /dev/null +++ b/doc_src/fish_svn_prompt.txt @@ -0,0 +1,82 @@ +\section fish_svn_prompt fish_svn_prompt - output svn information for use in a prompt + +\subsection fish_svn_prompt-description Description + +The fish_svn_prompt function can be used to display information about the current svn repository, if any. + +For obvious reasons, it requires having svn installed. + +There are numerous configuration options: + + +- $__fish_svn_prompt_color_revision, the colour of the revision number to display in the prompt +# setting the prompt status separator character +- $__fish_svn_prompt_char_separator, the separator between status characters + +And +- $__fish_svn_prompt_char_added_display +- $__fish_svn_prompt_char_added_color + +- $__fish_svn_prompt_char_conflicted_display +- $__fish_svn_prompt_char_conflicted_color + +- $__fish_svn_prompt_char_deleted_display +- $__fish_svn_prompt_char_deleted_color + +- $__fish_svn_prompt_char_ignored_display +- $__fish_svn_prompt_char_ignored_color + +- $__fish_svn_prompt_char_modified_display +- $__fish_svn_prompt_char_modified_color + +- $__fish_svn_prompt_char_replaced_display +- $__fish_svn_prompt_char_replaced_color + +- $__fish_svn_prompt_char_unversioned_external_display +- $__fish_svn_prompt_char_unversioned_external_color + +- $__fish_svn_prompt_char_unversioned_display +- $__fish_svn_prompt_char_unversioned_color + +- $__fish_svn_prompt_char_missing_display +- $__fish_svn_prompt_char_missing_color + +- $__fish_svn_prompt_char_versioned_obstructed_display +- $__fish_svn_prompt_char_versioned_obstructed_color + +- $__fish_svn_prompt_char_locked_display +- $__fish_svn_prompt_char_locked_color + +- $__fish_svn_prompt_char_scheduled_display +- $__fish_svn_prompt_char_scheduled_color + +- $__fish_svn_prompt_char_switched_display +- $__fish_svn_prompt_char_switched_color + +- $__fish_svn_prompt_char_token_present_display +- $__fish_svn_prompt_char_token_present_color + +- $__fish_svn_prompt_char_token_other_display +- $__fish_svn_prompt_char_token_other_color + +- $__fish_svn_prompt_char_token_stolen_display +- $__fish_svn_prompt_char_token_stolen_color + +- $__fish_svn_prompt_char_token_broken_display +- $__fish_svn_prompt_char_token_broken_color + +to choose the color and symbol for different parts of the prompt. + +See also fish_vcs_prompt, which will call all supported vcs-prompt functions, including git, hg and svn. + +\subsection fish_svn_prompt-example Example + +A simple prompt that displays svn info: + +\fish +function fish_prompt + ... + printf '%s %s$' $PWD (fish_svn_prompt) +end +\endfish + From 3a320d7584e861123d000ca381ed4071c7a511ad Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 16:02:57 +0100 Subject: [PATCH 409/439] Update fish_vcs_prompt reference in sampleprompts --- share/tools/web_config/sample_prompts/classic_vcs.fish | 2 +- share/tools/web_config/sample_prompts/informative_vcs.fish | 2 +- share/tools/web_config/sample_prompts/lonetwin.fish | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/share/tools/web_config/sample_prompts/classic_vcs.fish b/share/tools/web_config/sample_prompts/classic_vcs.fish index 68e663552..01608b958 100644 --- a/share/tools/web_config/sample_prompts/classic_vcs.fish +++ b/share/tools/web_config/sample_prompts/classic_vcs.fish @@ -67,5 +67,5 @@ function fish_prompt --description 'Write out the prompt' set prompt_status ' ' (set_color $fish_color_status) "[$last_status]" "$normal" end - echo -n -s (set_color $fish_color_user) "$USER" $normal @ (set_color $fish_color_host) (prompt_hostname) $normal ' ' (set_color $color_cwd) (prompt_pwd) $normal (__fish_vcs_prompt) $normal $prompt_status $suffix " " + echo -n -s (set_color $fish_color_user) "$USER" $normal @ (set_color $fish_color_host) (prompt_hostname) $normal ' ' (set_color $color_cwd) (prompt_pwd) $normal (fish_vcs_prompt) $normal $prompt_status $suffix " " end diff --git a/share/tools/web_config/sample_prompts/informative_vcs.fish b/share/tools/web_config/sample_prompts/informative_vcs.fish index c643d8b96..a9eeeace8 100644 --- a/share/tools/web_config/sample_prompts/informative_vcs.fish +++ b/share/tools/web_config/sample_prompts/informative_vcs.fish @@ -86,7 +86,7 @@ function fish_prompt --description 'Write out the prompt' echo -n (prompt_pwd) set_color normal - printf '%s ' (__fish_vcs_prompt) + printf '%s ' (fish_vcs_prompt) if not test $last_status -eq 0 set_color $fish_color_error diff --git a/share/tools/web_config/sample_prompts/lonetwin.fish b/share/tools/web_config/sample_prompts/lonetwin.fish index a8844017d..d14ccbdf7 100644 --- a/share/tools/web_config/sample_prompts/lonetwin.fish +++ b/share/tools/web_config/sample_prompts/lonetwin.fish @@ -10,5 +10,5 @@ function fish_prompt --description 'Write out the prompt' set -g __fish_prompt_cwd (set_color $fish_color_cwd) end - echo -n -s "$USER" @ (prompt_hostname) ' ' "$__fish_prompt_cwd" (prompt_pwd) (__fish_vcs_prompt) "$__fish_prompt_normal" '> ' + echo -n -s "$USER" @ (prompt_hostname) ' ' "$__fish_prompt_cwd" (prompt_pwd) (fish_vcs_prompt) "$__fish_prompt_normal" '> ' end From b999ba7b4782e2f7b809adadfbf558ede324900e Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 16:03:52 +0100 Subject: [PATCH 410/439] Update fish_hg_prompt reference in terlar prompt --- share/tools/web_config/sample_prompts/terlar.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/tools/web_config/sample_prompts/terlar.fish b/share/tools/web_config/sample_prompts/terlar.fish index d49ef340c..ec1d8b5e4 100644 --- a/share/tools/web_config/sample_prompts/terlar.fish +++ b/share/tools/web_config/sample_prompts/terlar.fish @@ -24,7 +24,7 @@ function fish_prompt --description 'Write out the prompt' set_color normal __terlar_git_prompt - __fish_hg_prompt + fish_hg_prompt echo if not test $last_status -eq 0 From b7e0f14fe0f21a4661b75df42a4ac2b4158644f1 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 10 Feb 2019 14:44:21 +0100 Subject: [PATCH 411/439] CHANGELOG: vcs prompt renamination --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c7844b17..ff7e38d99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # fish next-minor ## Deprecations -- None yet. +- The vcs-prompt functions have been renamed to names without double-underscore, so __fish_git_prompt is now fish_git_prompt, __fish_vcs_prompt is now fish_vcs_prompt, __fish_hg_prompt is now fish_hg_prompt and __fish_svn_prompt is now fish_svn_prompt. Shims at the old names have been added, and the variables have kept their old names (#5586). ## Notable fixes and improvements ### Syntax changes and new commands From df28f7669891d88f68b4f237750bfab73c526e9d Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 10 Feb 2019 13:59:22 +0100 Subject: [PATCH 412/439] git-prompt: Test untracked the right way around This only showed untracked files if showuntrackedfiles was != true. That's just exactly wrong. Fortunately this wasn't in a release. [ci skip] --- share/functions/fish_git_prompt.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/fish_git_prompt.fish b/share/functions/fish_git_prompt.fish index 91d3c737e..ffa52ea62 100644 --- a/share/functions/fish_git_prompt.fish +++ b/share/functions/fish_git_prompt.fish @@ -414,7 +414,7 @@ function fish_git_prompt --description "Prompt function for Git" set s $___fish_git_prompt_char_stashstate end - if test "$untracked" != true + if test "$untracked" = true set u (__fish_git_prompt_untracked) end end From fe73fbdb9a95f9e9c880d83bbbb141a02424ae14 Mon Sep 17 00:00:00 2001 From: Andrew Childs Date: Mon, 11 Feb 2019 01:57:24 +0900 Subject: [PATCH 413/439] Document that snippets are run before system config --- 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 de4cd4ec8..5bce40c54 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -1280,7 +1280,6 @@ 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, in `$__fish_data_dir/config.fish` (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 - in `$__fish_sysconf_dir` (usually `/etc/fish/config.fish`); - Configuration snippets in files ending in `.fish`, in the directories: - `$__fish_config_dir/conf.d` (by default, `~/.config/fish/conf.d/`) - `$__fish_sysconf_dir/conf.d` (by default, `/etc/fish/conf.d`) @@ -1289,6 +1288,7 @@ Configuration files are evaluated in the following order: If there are multiple files with the same name in these directories, only the first will be executed. They are executed in order of their filename, sorted (like globs) in a natural order (i.e. "01" sorts before "2"). +- 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 - in `$__fish_sysconf_dir` (usually `/etc/fish/config.fish`); - User initialization, usually in `~/.config/fish/config.fish` (controlled by the `XDG_CONFIG_HOME` environment variable, and accessible as `$__fish_config_dir`). These paths are controlled by parameters set at build, install, or run time, and may vary from the defaults listed above. From 28e2cfeb4be2e272b46d07c31d9301ffadb14a8f Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 10 Feb 2019 12:07:48 -0800 Subject: [PATCH 414/439] Switch signal table lookups to range-based for loops --- src/signal.cpp | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/signal.cpp b/src/signal.cpp index 47c5bf410..983a5e7a8 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -32,7 +32,7 @@ struct lookup_entry { static int block_count = 0; /// Lookup table used to convert between signal names and signal ids, etc. -static const struct lookup_entry lookup[] = { +static const struct lookup_entry signal_table[] = { #ifdef SIGHUP {SIGHUP, L"SIGHUP", N_(L"Terminal hung up")}, #endif @@ -141,7 +141,7 @@ static const struct lookup_entry lookup[] = { #ifdef SIGUNUSED {SIGUNUSED, L"SIGUNUSED", N_(L"Unused signal")}, #endif - {0, NULL, NULL}}; +}; /// 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) { @@ -151,9 +151,9 @@ static int match_signal_name(const wchar_t *canonical, const wchar_t *name) { } int wcs2sig(const wchar_t *str) { - for (int i = 0; lookup[i].desc; i++) { - if (match_signal_name(lookup[i].name, str)) { - return lookup[i].signal; + for (const auto &data : signal_table) { + if (match_signal_name(data.name, str)) { + return data.signal; } } @@ -163,11 +163,9 @@ int wcs2sig(const wchar_t *str) { } const wchar_t *sig2wcs(int sig) { - int i; - - for (i = 0; lookup[i].desc; i++) { - if (lookup[i].signal == sig) { - return lookup[i].name; + for (const auto &data : signal_table) { + if (data.signal == sig) { + return data.name; } } @@ -175,11 +173,9 @@ const wchar_t *sig2wcs(int sig) { } const wchar_t *signal_get_desc(int sig) { - int i; - - for (i = 0; lookup[i].desc; i++) { - if (lookup[i].signal == sig) { - return _(lookup[i].desc); + for (const auto &data : signal_table) { + if (data.signal == sig) { + return _(data.desc); } } @@ -255,20 +251,18 @@ static void handle_sigalarm(int sig, siginfo_t *info, void *context) { } void signal_reset_handlers() { - int i; - struct sigaction act; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_DFL; - for (i = 0; lookup[i].desc; i++) { - if (lookup[i].signal == SIGHUP) { + for (const auto &data : signal_table) { + if (data.signal == SIGHUP) { struct sigaction oact; sigaction(SIGHUP, NULL, &oact); if (oact.sa_handler == SIG_IGN) continue; } - sigaction(lookup[i].signal, &act, NULL); + sigaction(data.signal, &act, NULL); } } @@ -382,14 +376,14 @@ void signal_handle(int sig, int do_handle) { void get_signals_with_handlers(sigset_t *set) { sigemptyset(set); - for (int i = 0; lookup[i].desc; i++) { + for (const auto &data : signal_table) { struct sigaction act = {}; - sigaction(lookup[i].signal, NULL, &act); + sigaction(data.signal, NULL, &act); // If SIGHUP is being ignored (e.g., because were were run via `nohup`) don't reset it. // We don't special case other signals because if they're being ignored that shouldn't // affect processes we spawn. They should get the default behavior for those signals. - if (lookup[i].signal == SIGHUP && act.sa_handler == SIG_IGN) continue; - if (act.sa_handler != SIG_DFL) sigaddset(set, lookup[i].signal); + if (data.signal == SIGHUP && act.sa_handler == SIG_IGN) continue; + if (act.sa_handler != SIG_DFL) sigaddset(set, data.signal); } } From 83884c2c13fa03d72a7b08d37e0c314d52706dbc Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 10 Feb 2019 12:54:42 -0800 Subject: [PATCH 415/439] Improve reliability of signal handling in child processes fish forks child processes when (for example) writing out builtin output. After fork it resets signal handlers, but if a signal is delivered before the signal handlers are reset, it will inherit fish's default handlers, which do things like swallow SIGINT. Teach fish's default signal handlers to detect this case and re-raise signals with default handlers. This improves the reliability of control-C in the face of builtins. --- src/signal.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/signal.cpp b/src/signal.cpp index 983a5e7a8..f1ab10581 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -182,6 +182,23 @@ const wchar_t *signal_get_desc(int sig) { return _(L"Unknown"); } +/// Store the "main" pid. This allows us to reliably determine if we are in a forked child. +static const pid_t s_main_pid = getpid(); + +/// It's possible that we receive a signal after we have forked, but before we have reset the signal +/// handlers (or even run the pthread_atfork calls). In that event we will do something dumb like +/// swallow SIGINT. Ensure that doesn't happen. Check if we are the main fish process; if not reset +/// and re-raise the signal. \return whether we re-raised the signal. +static bool reraise_if_forked_child(int sig) { + // Don't use is_forked_child, that relies on atfork handlers which maybe have not run yet. + if (getpid() == s_main_pid) { + return false; + } + signal(sig, SIG_DFL); + raise(sig); + return true; +} + /// Standard signal handler. static void default_handler(int signal, siginfo_t *info, void *context) { UNUSED(info); @@ -196,6 +213,7 @@ static void default_handler(int signal, siginfo_t *info, void *context) { static void handle_winch(int sig, siginfo_t *info, void *context) { UNUSED(info); UNUSED(context); + if (reraise_if_forked_child(sig)) return; common_handle_winch(sig); default_handler(sig, 0, 0); } @@ -206,6 +224,7 @@ static void handle_winch(int sig, siginfo_t *info, void *context) { static void handle_hup(int sig, siginfo_t *info, void *context) { UNUSED(info); UNUSED(context); + if (reraise_if_forked_child(sig)) return; if (event_is_signal_observed(SIGHUP)) { default_handler(sig, 0, 0); } else { @@ -215,9 +234,9 @@ static void handle_hup(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_sigterm(int sig, siginfo_t *info, void *context) { - UNUSED(sig); UNUSED(info); UNUSED(context); + if (reraise_if_forked_child(sig)) return; restore_term_foreground_process_group(); signal(SIGTERM, SIG_DFL); raise(SIGTERM); @@ -226,18 +245,21 @@ static void handle_sigterm(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) { + if (reraise_if_forked_child(sig)) return; reader_handle_sigint(); default_handler(sig, info, context); } /// Non-interactive ^C handler. static void handle_int_notinteractive(int sig, siginfo_t *info, void *context) { + if (reraise_if_forked_child(sig)) return; parser_t::skip_all_blocks(); 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) { + if (reraise_if_forked_child(sig)) return; job_handle_signal(sig, info, context); default_handler(sig, info, context); } @@ -245,9 +267,9 @@ static void handle_chld(int sig, siginfo_t *info, void *context) { // We have a sigalarm handler that does nothing. This is used in the signal torture test, to verify // that we behave correctly when receiving lots of irrelevant signals. static void handle_sigalarm(int sig, siginfo_t *info, void *context) { - UNUSED(sig); UNUSED(info); UNUSED(context); + if (reraise_if_forked_child(sig)) return; } void signal_reset_handlers() { From ec290209db5f19a71339f5e20763a37d20a7b316 Mon Sep 17 00:00:00 2001 From: zabereer Date: Thu, 7 Feb 2019 18:47:30 +0000 Subject: [PATCH 416/439] add `$pipestatus` support --- CHANGELOG.md | 2 ++ src/env.cpp | 14 +++++++-- src/exec.cpp | 1 + src/proc.cpp | 18 ++++++++++++ src/proc.h | 6 ++++ tests/pipestatus.err | 12 ++++++++ tests/pipestatus.in | 67 ++++++++++++++++++++++++++++++++++++++++++++ tests/pipestatus.out | 52 ++++++++++++++++++++++++++++++++++ 8 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 tests/pipestatus.err create mode 100644 tests/pipestatus.in create mode 100644 tests/pipestatus.out diff --git a/CHANGELOG.md b/CHANGELOG.md index ff7e38d99..874c07b97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - The vcs-prompt functions have been renamed to names without double-underscore, so __fish_git_prompt is now fish_git_prompt, __fish_vcs_prompt is now fish_vcs_prompt, __fish_hg_prompt is now fish_hg_prompt and __fish_svn_prompt is now fish_svn_prompt. Shims at the old names have been added, and the variables have kept their old names (#5586). ## Notable fixes and improvements +- Add `$pipestatus` support + ### Syntax changes and new commands - None yet. diff --git a/src/env.cpp b/src/env.cpp index ff787bc47..fb1867c93 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -315,8 +315,8 @@ bool string_set_contains(const T &set, const wchar_t *val) { /// Check if a variable may not be set using the set command. static bool is_read_only(const wchar_t *val) { const string_set_t env_read_only = { - L"PWD", L"SHLVL", L"history", L"status", L"version", - L"FISH_VERSION", L"fish_pid", L"hostname", L"_", L"fish_private_mode"}; + L"PWD", L"SHLVL", L"history", L"pipestatus", L"status", L"version", + L"FISH_VERSION", L"fish_pid", L"hostname", L"_", L"fish_private_mode"}; return string_set_contains(env_read_only, val) || (in_private_mode() && wcscmp(L"fish_history", val) == 0); } @@ -329,7 +329,7 @@ static bool variable_should_auto_pathvar(const wcstring &name) { } /// Table of variables whose value is dynamically calculated, such as umask, status, etc. -static const string_set_t env_electric = {L"history", L"status", L"umask"}; +static const string_set_t env_electric = {L"history", L"pipestatus", L"status", L"umask"}; static bool is_electric(const wcstring &key) { return contains(env_electric, key); } @@ -1407,6 +1407,14 @@ maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) wcstring_list_t result; if (history) history->get_history(result); return env_var_t(L"history", result); + } else if (key == L"pipestatus") { + const auto& js = proc_get_last_job_statuses(); + wcstring_list_t result; + result.reserve(js->size()); + for (auto&& i : *js) { + result.emplace_back(to_string(i)); + } + return env_var_t(L"pipestatus", result); } else if (key == L"status") { return env_var_t(L"status", to_string(proc_get_last_status())); } else if (key == L"umask") { diff --git a/src/exec.cpp b/src/exec.cpp index 431c4e042..b3da399a4 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -1008,6 +1008,7 @@ bool exec_job(parser_t &parser, shared_ptr j) { } j->continue_job(false); + proc_set_last_job_statuses(*j); return true; } diff --git a/src/proc.cpp b/src/proc.cpp index 94b5358a6..78addc711 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -56,6 +56,9 @@ /// Status of last process to exit. static int last_status = 0; +/// Statuses of last job's processes to exit. +static std::shared_ptr> last_job_statuses{new std::vector{0}}; + /// The signals that signify crashes to us. static const int crashsignals[] = {SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGSYS}; @@ -145,6 +148,21 @@ void proc_set_last_status(int s) { int proc_get_last_status() { return last_status; } +void proc_set_last_job_statuses(const job_t &last_job) { + ASSERT_IS_MAIN_THREAD(); + std::shared_ptr> ljs{new std::vector}; + ljs->reserve(last_job.processes.size()); + for (auto &&p : last_job.processes) { + ljs->emplace_back(p->pid ? proc_format_status(p->status) : p->status); + } + last_job_statuses = std::move(ljs); +} + +std::shared_ptr> proc_get_last_job_statuses() { + ASSERT_IS_MAIN_THREAD(); + return last_job_statuses; +} + // 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 owning_lock> locked_consumed_job_ids; diff --git a/src/proc.h b/src/proc.h index 1620e4e5b..d743e9ce0 100644 --- a/src/proc.h +++ b/src/proc.h @@ -349,6 +349,12 @@ void proc_set_last_status(int s); /// Returns the status of the last process to exit. int proc_get_last_status(); +/// Sets the status of the last job's processes to exit from last_job. +void proc_set_last_job_statuses(const job_t &last_job); + +/// Returns the status of the last job's processes to exit. +std::shared_ptr> proc_get_last_job_statuses(); + /// 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 diff --git a/tests/pipestatus.err b/tests/pipestatus.err new file mode 100644 index 000000000..263a0a3d8 --- /dev/null +++ b/tests/pipestatus.err @@ -0,0 +1,12 @@ + +#################### +# pipestatus variable - builtins only + +#################### +# pipestatus variable - no builtins + +#################### +# pipestatus variable - mixed + +#################### +# pipestatus variable - non-pipe diff --git a/tests/pipestatus.in b/tests/pipestatus.in new file mode 100644 index 000000000..32a423f1b --- /dev/null +++ b/tests/pipestatus.in @@ -0,0 +1,67 @@ +logmsg "pipestatus variable - builtins only" + +false | false | false; echo $pipestatus +false | false | false; echo $status + +true | true | true; echo $pipestatus +true | true | true; echo $status + +false | true | false; echo $pipestatus +false | true | false; echo $status + +true | false | true; echo $pipestatus +true | false | true; echo $status + +logmsg "pipestatus variable - no builtins" + +command false | command false | command false; echo $pipestatus +command false | command false | command false; echo $status + +command true | command true | command true; echo $pipestatus +command true | command true | command true; echo $status + +command false | command true | command false; echo $pipestatus +command false | command true | command false; echo $status + +command true | command false | command true; echo $pipestatus +command true | command false | command true; echo $status + +logmsg "pipestatus variable - mixed" + +command false | command false | false; echo $pipestatus +command false | command false | false; echo $status + +command true | true | command true; echo $pipestatus +command true | true | command true; echo $status + +false | command true | command false; echo $pipestatus +false | command true | command false; echo $status + +true | false | command true; echo $pipestatus +true | false | command true; echo $status + +sh -c 'exit 5' | sh -c 'exit 2'; echo $pipestatus +sh -c 'exit 5' | sh -c 'exit 2'; echo $status + +sh -c 'exit 3' | false | sh -c 'exit 6'; echo $pipestatus +sh -c 'exit 3' | false | sh -c 'exit 6'; echo $status + +sh -c 'exit 9' | true | sh -c 'exit 3' | false; echo $pipestatus +sh -c 'exit 9' | true | sh -c 'exit 3' | false; echo $status + +logmsg "pipestatus variable - non-pipe" + +true; echo $pipestatus +true; echo $status + +false; echo $pipestatus +false; echo $status + +command true; echo $pipestatus +command true; echo $status + +command false; echo $pipestatus +command false; echo $status + +sh -c 'exit 4'; echo $pipestatus +sh -c 'exit 4'; echo $status diff --git a/tests/pipestatus.out b/tests/pipestatus.out new file mode 100644 index 000000000..3a717080b --- /dev/null +++ b/tests/pipestatus.out @@ -0,0 +1,52 @@ + +#################### +# pipestatus variable - builtins only +1 1 1 +1 +0 0 0 +0 +1 0 1 +1 +0 1 0 +0 + +#################### +# pipestatus variable - no builtins +1 1 1 +1 +0 0 0 +0 +1 0 1 +1 +0 1 0 +0 + +#################### +# pipestatus variable - mixed +1 1 1 +1 +0 0 0 +0 +1 0 1 +1 +0 1 0 +0 +5 2 +2 +3 1 6 +6 +9 0 3 1 +1 + +#################### +# pipestatus variable - non-pipe +0 +0 +1 +1 +0 +0 +1 +1 +4 +4 From 34c1f2471636a481023d354d270ea607607118ff Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 10 Feb 2019 13:43:02 -0800 Subject: [PATCH 417/439] Make $pipestatus thread safe and other misc cleanup --- src/env.cpp | 10 +++++----- src/proc.cpp | 17 ++++++++--------- src/proc.h | 4 ++-- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index fb1867c93..b661e4a72 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1408,13 +1408,13 @@ maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) if (history) history->get_history(result); return env_var_t(L"history", result); } else if (key == L"pipestatus") { - const auto& js = proc_get_last_job_statuses(); + const auto js = proc_get_last_job_statuses(); wcstring_list_t result; - result.reserve(js->size()); - for (auto&& i : *js) { - result.emplace_back(to_string(i)); + result.reserve(js.size()); + for (int i : js) { + result.push_back(to_string(i)); } - return env_var_t(L"pipestatus", result); + return env_var_t(L"pipestatus", std::move(result)); } else if (key == L"status") { return env_var_t(L"status", to_string(proc_get_last_status())); } else if (key == L"umask") { diff --git a/src/proc.cpp b/src/proc.cpp index 78addc711..d7ce320ec 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -57,7 +57,7 @@ static int last_status = 0; /// Statuses of last job's processes to exit. -static std::shared_ptr> last_job_statuses{new std::vector{0}}; +static owning_lock> last_job_statuses; /// The signals that signify crashes to us. static const int crashsignals[] = {SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGSYS}; @@ -150,17 +150,16 @@ int proc_get_last_status() { return last_status; } void proc_set_last_job_statuses(const job_t &last_job) { ASSERT_IS_MAIN_THREAD(); - std::shared_ptr> ljs{new std::vector}; - ljs->reserve(last_job.processes.size()); - for (auto &&p : last_job.processes) { - ljs->emplace_back(p->pid ? proc_format_status(p->status) : p->status); + auto lockedstats = last_job_statuses.acquire(); + lockedstats->clear(); + lockedstats->reserve(last_job.processes.size()); + for (const auto &p : last_job.processes) { + lockedstats->emplace_back(p->pid ? proc_format_status(p->status) : p->status); } - last_job_statuses = std::move(ljs); } -std::shared_ptr> proc_get_last_job_statuses() { - ASSERT_IS_MAIN_THREAD(); - return last_job_statuses; +std::vector proc_get_last_job_statuses() { + return *last_job_statuses.acquire(); } // Basic thread safe job IDs. The vector consumed_job_ids has a true value wherever the job ID diff --git a/src/proc.h b/src/proc.h index d743e9ce0..6a427284e 100644 --- a/src/proc.h +++ b/src/proc.h @@ -352,8 +352,8 @@ int proc_get_last_status(); /// Sets the status of the last job's processes to exit from last_job. void proc_set_last_job_statuses(const job_t &last_job); -/// Returns the status of the last job's processes to exit. -std::shared_ptr> proc_get_last_job_statuses(); +/// Returns the statuses of the last job's processes to exit. +std::vector proc_get_last_job_statuses(); /// Notify the user about stopped or terminated jobs. Delete terminated jobs from the job list. /// From 6da9d96241321d7ff2b195ce0eec78b51ddaf9f4 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 10 Feb 2019 13:46:49 -0800 Subject: [PATCH 418/439] Revert "Make $pipestatus thread safe and other misc cleanup" This reverts commit 34c1f2471636a481023d354d270ea607607118ff. --- src/env.cpp | 10 +++++----- src/proc.cpp | 17 +++++++++-------- src/proc.h | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index b661e4a72..fb1867c93 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1408,13 +1408,13 @@ maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) if (history) history->get_history(result); return env_var_t(L"history", result); } else if (key == L"pipestatus") { - const auto js = proc_get_last_job_statuses(); + const auto& js = proc_get_last_job_statuses(); wcstring_list_t result; - result.reserve(js.size()); - for (int i : js) { - result.push_back(to_string(i)); + result.reserve(js->size()); + for (auto&& i : *js) { + result.emplace_back(to_string(i)); } - return env_var_t(L"pipestatus", std::move(result)); + return env_var_t(L"pipestatus", result); } else if (key == L"status") { return env_var_t(L"status", to_string(proc_get_last_status())); } else if (key == L"umask") { diff --git a/src/proc.cpp b/src/proc.cpp index d7ce320ec..78addc711 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -57,7 +57,7 @@ static int last_status = 0; /// Statuses of last job's processes to exit. -static owning_lock> last_job_statuses; +static std::shared_ptr> last_job_statuses{new std::vector{0}}; /// The signals that signify crashes to us. static const int crashsignals[] = {SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGSYS}; @@ -150,16 +150,17 @@ int proc_get_last_status() { return last_status; } void proc_set_last_job_statuses(const job_t &last_job) { ASSERT_IS_MAIN_THREAD(); - auto lockedstats = last_job_statuses.acquire(); - lockedstats->clear(); - lockedstats->reserve(last_job.processes.size()); - for (const auto &p : last_job.processes) { - lockedstats->emplace_back(p->pid ? proc_format_status(p->status) : p->status); + std::shared_ptr> ljs{new std::vector}; + ljs->reserve(last_job.processes.size()); + for (auto &&p : last_job.processes) { + ljs->emplace_back(p->pid ? proc_format_status(p->status) : p->status); } + last_job_statuses = std::move(ljs); } -std::vector proc_get_last_job_statuses() { - return *last_job_statuses.acquire(); +std::shared_ptr> proc_get_last_job_statuses() { + ASSERT_IS_MAIN_THREAD(); + return last_job_statuses; } // Basic thread safe job IDs. The vector consumed_job_ids has a true value wherever the job ID diff --git a/src/proc.h b/src/proc.h index 6a427284e..d743e9ce0 100644 --- a/src/proc.h +++ b/src/proc.h @@ -352,8 +352,8 @@ int proc_get_last_status(); /// Sets the status of the last job's processes to exit from last_job. void proc_set_last_job_statuses(const job_t &last_job); -/// Returns the statuses of the last job's processes to exit. -std::vector proc_get_last_job_statuses(); +/// Returns the status of the last job's processes to exit. +std::shared_ptr> proc_get_last_job_statuses(); /// Notify the user about stopped or terminated jobs. Delete terminated jobs from the job list. /// From 1701e2c558455e7e79d00a8529e4e8b46569f1cd Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 10 Feb 2019 13:46:58 -0800 Subject: [PATCH 419/439] Revert "add `$pipestatus` support" This reverts commit ec290209db5f19a71339f5e20763a37d20a7b316. --- CHANGELOG.md | 2 -- src/env.cpp | 14 ++------- src/exec.cpp | 1 - src/proc.cpp | 18 ------------ src/proc.h | 6 ---- tests/pipestatus.err | 12 -------- tests/pipestatus.in | 67 -------------------------------------------- tests/pipestatus.out | 52 ---------------------------------- 8 files changed, 3 insertions(+), 169 deletions(-) delete mode 100644 tests/pipestatus.err delete mode 100644 tests/pipestatus.in delete mode 100644 tests/pipestatus.out diff --git a/CHANGELOG.md b/CHANGELOG.md index 874c07b97..ff7e38d99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,6 @@ - The vcs-prompt functions have been renamed to names without double-underscore, so __fish_git_prompt is now fish_git_prompt, __fish_vcs_prompt is now fish_vcs_prompt, __fish_hg_prompt is now fish_hg_prompt and __fish_svn_prompt is now fish_svn_prompt. Shims at the old names have been added, and the variables have kept their old names (#5586). ## Notable fixes and improvements -- Add `$pipestatus` support - ### Syntax changes and new commands - None yet. diff --git a/src/env.cpp b/src/env.cpp index fb1867c93..ff787bc47 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -315,8 +315,8 @@ bool string_set_contains(const T &set, const wchar_t *val) { /// Check if a variable may not be set using the set command. static bool is_read_only(const wchar_t *val) { const string_set_t env_read_only = { - L"PWD", L"SHLVL", L"history", L"pipestatus", L"status", L"version", - L"FISH_VERSION", L"fish_pid", L"hostname", L"_", L"fish_private_mode"}; + L"PWD", L"SHLVL", L"history", L"status", L"version", + L"FISH_VERSION", L"fish_pid", L"hostname", L"_", L"fish_private_mode"}; return string_set_contains(env_read_only, val) || (in_private_mode() && wcscmp(L"fish_history", val) == 0); } @@ -329,7 +329,7 @@ static bool variable_should_auto_pathvar(const wcstring &name) { } /// Table of variables whose value is dynamically calculated, such as umask, status, etc. -static const string_set_t env_electric = {L"history", L"pipestatus", L"status", L"umask"}; +static const string_set_t env_electric = {L"history", L"status", L"umask"}; static bool is_electric(const wcstring &key) { return contains(env_electric, key); } @@ -1407,14 +1407,6 @@ maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) wcstring_list_t result; if (history) history->get_history(result); return env_var_t(L"history", result); - } else if (key == L"pipestatus") { - const auto& js = proc_get_last_job_statuses(); - wcstring_list_t result; - result.reserve(js->size()); - for (auto&& i : *js) { - result.emplace_back(to_string(i)); - } - return env_var_t(L"pipestatus", result); } else if (key == L"status") { return env_var_t(L"status", to_string(proc_get_last_status())); } else if (key == L"umask") { diff --git a/src/exec.cpp b/src/exec.cpp index b3da399a4..431c4e042 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -1008,7 +1008,6 @@ bool exec_job(parser_t &parser, shared_ptr j) { } j->continue_job(false); - proc_set_last_job_statuses(*j); return true; } diff --git a/src/proc.cpp b/src/proc.cpp index 78addc711..94b5358a6 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -56,9 +56,6 @@ /// Status of last process to exit. static int last_status = 0; -/// Statuses of last job's processes to exit. -static std::shared_ptr> last_job_statuses{new std::vector{0}}; - /// The signals that signify crashes to us. static const int crashsignals[] = {SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGSYS}; @@ -148,21 +145,6 @@ void proc_set_last_status(int s) { int proc_get_last_status() { return last_status; } -void proc_set_last_job_statuses(const job_t &last_job) { - ASSERT_IS_MAIN_THREAD(); - std::shared_ptr> ljs{new std::vector}; - ljs->reserve(last_job.processes.size()); - for (auto &&p : last_job.processes) { - ljs->emplace_back(p->pid ? proc_format_status(p->status) : p->status); - } - last_job_statuses = std::move(ljs); -} - -std::shared_ptr> proc_get_last_job_statuses() { - ASSERT_IS_MAIN_THREAD(); - return last_job_statuses; -} - // 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 owning_lock> locked_consumed_job_ids; diff --git a/src/proc.h b/src/proc.h index d743e9ce0..1620e4e5b 100644 --- a/src/proc.h +++ b/src/proc.h @@ -349,12 +349,6 @@ void proc_set_last_status(int s); /// Returns the status of the last process to exit. int proc_get_last_status(); -/// Sets the status of the last job's processes to exit from last_job. -void proc_set_last_job_statuses(const job_t &last_job); - -/// Returns the status of the last job's processes to exit. -std::shared_ptr> proc_get_last_job_statuses(); - /// 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 diff --git a/tests/pipestatus.err b/tests/pipestatus.err deleted file mode 100644 index 263a0a3d8..000000000 --- a/tests/pipestatus.err +++ /dev/null @@ -1,12 +0,0 @@ - -#################### -# pipestatus variable - builtins only - -#################### -# pipestatus variable - no builtins - -#################### -# pipestatus variable - mixed - -#################### -# pipestatus variable - non-pipe diff --git a/tests/pipestatus.in b/tests/pipestatus.in deleted file mode 100644 index 32a423f1b..000000000 --- a/tests/pipestatus.in +++ /dev/null @@ -1,67 +0,0 @@ -logmsg "pipestatus variable - builtins only" - -false | false | false; echo $pipestatus -false | false | false; echo $status - -true | true | true; echo $pipestatus -true | true | true; echo $status - -false | true | false; echo $pipestatus -false | true | false; echo $status - -true | false | true; echo $pipestatus -true | false | true; echo $status - -logmsg "pipestatus variable - no builtins" - -command false | command false | command false; echo $pipestatus -command false | command false | command false; echo $status - -command true | command true | command true; echo $pipestatus -command true | command true | command true; echo $status - -command false | command true | command false; echo $pipestatus -command false | command true | command false; echo $status - -command true | command false | command true; echo $pipestatus -command true | command false | command true; echo $status - -logmsg "pipestatus variable - mixed" - -command false | command false | false; echo $pipestatus -command false | command false | false; echo $status - -command true | true | command true; echo $pipestatus -command true | true | command true; echo $status - -false | command true | command false; echo $pipestatus -false | command true | command false; echo $status - -true | false | command true; echo $pipestatus -true | false | command true; echo $status - -sh -c 'exit 5' | sh -c 'exit 2'; echo $pipestatus -sh -c 'exit 5' | sh -c 'exit 2'; echo $status - -sh -c 'exit 3' | false | sh -c 'exit 6'; echo $pipestatus -sh -c 'exit 3' | false | sh -c 'exit 6'; echo $status - -sh -c 'exit 9' | true | sh -c 'exit 3' | false; echo $pipestatus -sh -c 'exit 9' | true | sh -c 'exit 3' | false; echo $status - -logmsg "pipestatus variable - non-pipe" - -true; echo $pipestatus -true; echo $status - -false; echo $pipestatus -false; echo $status - -command true; echo $pipestatus -command true; echo $status - -command false; echo $pipestatus -command false; echo $status - -sh -c 'exit 4'; echo $pipestatus -sh -c 'exit 4'; echo $status diff --git a/tests/pipestatus.out b/tests/pipestatus.out deleted file mode 100644 index 3a717080b..000000000 --- a/tests/pipestatus.out +++ /dev/null @@ -1,52 +0,0 @@ - -#################### -# pipestatus variable - builtins only -1 1 1 -1 -0 0 0 -0 -1 0 1 -1 -0 1 0 -0 - -#################### -# pipestatus variable - no builtins -1 1 1 -1 -0 0 0 -0 -1 0 1 -1 -0 1 0 -0 - -#################### -# pipestatus variable - mixed -1 1 1 -1 -0 0 0 -0 -1 0 1 -1 -0 1 0 -0 -5 2 -2 -3 1 6 -6 -9 0 3 1 -1 - -#################### -# pipestatus variable - non-pipe -0 -0 -1 -1 -0 -0 -1 -1 -4 -4 From 038fea1a47c4288ca331188962cdfe14216ed8e3 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sun, 10 Feb 2019 14:14:48 -0800 Subject: [PATCH 420/439] Fix builtin $var expansion A special case added for #1252 needed adjustment. Fixes #5639 --- src/parse_util.cpp | 5 +++-- tests/vars_as_commands.in | 3 +++ tests/vars_as_commands.out | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 6d4d3f5a7..aa909b1ce 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -1195,9 +1195,10 @@ static bool detect_errors_in_plain_statement(const wcstring &buff_src, // Check that we don't do an invalid builtin (issue #1252). if (!errored && decoration == parse_statement_decoration_builtin && - !builtin_exists(command)) { + expand_one(*unexp_command, 0, null_environment_t{}, parse_errors) && + !builtin_exists(*unexp_command)) { errored = append_syntax_error(parse_errors, source_start, UNKNOWN_BUILTIN_ERR_MSG, - command.c_str()); + unexp_command->c_str()); } } return errored; diff --git a/tests/vars_as_commands.in b/tests/vars_as_commands.in index 93d299454..3b73c9f00 100644 --- a/tests/vars_as_commands.in +++ b/tests/vars_as_commands.in @@ -9,6 +9,9 @@ $CMD1 set CMD2 echo '(' not expanded again $CMD2 +# Test using variables with the builtin decorator +builtin $CMD1 + # Test implicit cd set CMD3 /usr/bin $CMD3 && echo $PWD diff --git a/tests/vars_as_commands.out b/tests/vars_as_commands.out index 6f59b82a7..64e4976b7 100644 --- a/tests/vars_as_commands.out +++ b/tests/vars_as_commands.out @@ -1,3 +1,4 @@ basic command as variable ( not expanded again +basic command as variable /usr/bin From 1c9fe71240f890b968934940c192646bbe201051 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sun, 10 Feb 2019 15:01:38 -0800 Subject: [PATCH 421/439] config.fish: loop over bg, fg, etc. wrappers Now that we can do `builtin $x`, this code can be simplified. --- share/config.fish | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/share/config.fish b/share/config.fish index 749da1a1b..a3d1a29c0 100644 --- a/share/config.fish +++ b/share/config.fish @@ -259,24 +259,10 @@ function __fish_expand_pid_args end end -function bg - builtin bg (__fish_expand_pid_args $argv) -end - -function fg - builtin fg (__fish_expand_pid_args $argv) -end - -function kill - command kill (__fish_expand_pid_args $argv) -end - -function wait - builtin wait (__fish_expand_pid_args $argv) -end - -function disown - builtin disown (__fish_expand_pid_args $argv) +for jobcmd in bg fg kill wait disown + function $jobcmd -V jobcmd + builtin $jobcmd (__fish_expand_pid_args $argv) + end end # As last part of initialization, source the conf directories. From 634e97a85e052d740e0b5de1ed9c9eafb825de47 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sun, 10 Feb 2019 16:47:05 -0800 Subject: [PATCH 422/439] Remove unnecessary _NSGetExecutablePath declaration We do this in common.cpp now, and are including dyld.h anyhow. --- src/fish.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/fish.cpp b/src/fish.cpp index 9652f310f..e481cd4cc 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -84,11 +84,6 @@ static bool get_realpath(std::string &path) { return ptr != NULL; } -// OS X function for getting the executable path. -extern "C" { -int _NSGetExecutablePath(char *buf, uint32_t *bufsize); -} - static struct config_paths_t determine_config_directory_paths(const char *argv0) { struct config_paths_t paths; bool done = false; From 4c6b4188a07724eee3a94a73eca182df46f387c2 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sat, 9 Feb 2019 22:22:58 -0800 Subject: [PATCH 423/439] Drastically improve `fish ` completions * complete .fish files * --debug -> --debug-level * add --init-command/-C * add --debug-stack-frames/-D * add --private/-P * add --features/-f: lists supported features, supports foo, * -c now completes commands * -d requires argument, describes 0..5 * --profile: require argument, allow file completion --- share/completions/fish.fish | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/share/completions/fish.fish b/share/completions/fish.fish index d33d20bc4..d11745b03 100644 --- a/share/completions/fish.fish +++ b/share/completions/fish.fish @@ -1,8 +1,24 @@ -complete -c fish -s c -l "command" -d "Run fish with this command" +complete -c fish -s c -l command -d "Run specified command instead of interactive session" -x -a "(__fish_complete_command)" +complete -c fish -s C -l init-command -d "Run specified command before session" -x -a "(__fish_complete_command)" complete -c fish -s h -l help -d "Display help and exit" complete -c fish -s v -l version -d "Display version and exit" complete -c fish -s n -l no-execute -d "Only parse input, do not execute" complete -c fish -s i -l interactive -d "Run in interactive mode" -complete -c fish -s l -l login -d "Run in login mode" -complete -c fish -s p -l profile -d "Output profiling information to specified file" -f -complete -c fish -s d -l debug -d "Run with the specified verbosity level" +complete -c fish -s l -l login -d "Run as a login shell" +complete -c fish -s p -l profile -d "Output profiling information to specified file" -r +complete -c fish -s d -l debug-level -d "Specify verbosity level" -x -a "0\t'Warnings silenced' +1\t'Default' +2\t'Basic debug output' +3\t'More debug output' +4\t'Much more debug output' +5\t'Too much debug output'" +complete -c fish -s D -l debug-stack-frames -d "Show specified # of frames with debug output" -x -a "(seq 128)\t\n" +complete -c fish -s P -l private -d "Do not persist history" + +function __fish_complete_features + set -l arg_comma (commandline -tc | string replace -rf '(.*,)[^,]*' '$1' | string replace -r -- '--.*=' '') + set -l features (status features | string replace -rf '^([\w-]+).*\t(.*)$' '$1\t$2') + printf "%s\n" "$arg_comma"$features #TODO: remove existing args +end +complete -c fish -s f -l features -d "Run with comma-separated feature flags enabled" -a "(__fish_complete_features)" -x +complete -c fish -x -a "(__fish_complete_suffix .fish)" From 4d6a97d35c0ca7544d4ec52459b2e3bcc1722ee6 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sun, 10 Feb 2019 21:47:10 -0800 Subject: [PATCH 424/439] Retire the bit of config.fish that detected missing builtin string It's been a few years. --- share/config.fish | 44 +++++++++++++------------------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/share/config.fish b/share/config.fish index a3d1a29c0..d657332fd 100644 --- a/share/config.fish +++ b/share/config.fish @@ -16,38 +16,20 @@ function __fish_default_command_not_found_handler end if status --is-interactive - # The user has seemingly explicitly launched an old fish with too-new scripts installed. - if not contains -- "string" (builtin -n) - set -g __is_launched_without_string 1 - # XXX nostring - fix old fish binaries with no `string' builtin. - # When executed on fish 2.2.0, the `else' block after this would - # force on 24-bit mode due to changes to in test behavior. - # These "XXX nostring" hacks were added for 2.3.1 - set_color --bold - echo "You appear to be trying to launch an old fish binary with newer scripts " - echo "installed into" (set_color --underline)"$__fish_data_dir" - set_color normal - echo -e "\nThis is an unsupported configuration.\n" - set_color yellow - echo "You may need to uninstall and reinstall fish!" - set_color normal - # Remove this code when we've made it safer to upgrade fish. - else - # Enable truecolor/24-bit support for select terminals - # Ignore Screen and emacs' ansi-term as they swallow the sequences, rendering the text white. - if not set -q STY - and not string match -q -- 'eterm*' $TERM - 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 -n "$VTE_VERSION" -a "$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 - # Only set it if it isn't to allow override by setting to 0 - set -q fish_term24bit - or set -g fish_term24bit 1 + # Enable truecolor/24-bit support for select terminals + # Ignore Screen and emacs' ansi-term as they swallow the sequences, rendering the text white. + if not set -q STY + and not string match -q -- 'eterm*' $TERM + 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 -n "$VTE_VERSION" -a "$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 + # 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 bcc4240d2bb934b456c68510addb7dd6c3b501ae Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Sun, 10 Feb 2019 23:18:38 -0800 Subject: [PATCH 425/439] config.fish: Clean up the . function a bit. --- share/config.fish | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/share/config.fish b/share/config.fish index d657332fd..fdded6c7a 100644 --- a/share/config.fish +++ b/share/config.fish @@ -142,12 +142,10 @@ end # in UTF-8 (with non-ASCII characters). __fish_set_locale -# "." command for compatibility with old fish versions. -function . --description 'Evaluate contents of file (deprecated, see "source")' --no-scope-shadowing - if test (count $argv) -eq 0 - # Uses tty directly, as isatty depends on "." - and tty 0>&0 >/dev/null - echo "source: '.' command is deprecated, and doesn't work with STDIN anymore. Did you mean 'source' or './'?" >&2 +# "." alias for source; deprecated +function . -d 'Evaluate a file (deprecated, use "source")' --no-scope-shadowing --wraps source + if [ (count $argv) -eq 0 ] && isatty 0 + echo "source: using source via '.' is deprecated, and stdin doesn't work."\n"Did you mean 'source' or './'?" >&2 return 1 else source $argv From 339b195e74f0c42a894c3759b4832e9a270d755d Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 11 Feb 2019 19:30:31 +0800 Subject: [PATCH 426/439] CHANGELOG: updates for 3.0.1 --- CHANGELOG.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d0e1933d..1c9062ad8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,17 +9,23 @@ This release of fish fixes a number of major issues discovered in fish 3.0.0. - `read --silent` no longer echoes to the tty when run from a non-interactive script (#5519). - On macOS, path entries with spaces in `/etc/paths` and `/etc/paths.d` now correctly set path entries with spaces. Likewise, `MANPATH` is correctly set from `/etc/manpaths` and `/etc/manpaths.d` (#5481). - fish starts correctly under Cygwin/MSYS2 (#5426). -- The `pager-toggle-search` binding (Control-S by default) now positions the cursor in the completions list. +- The `pager-toggle-search` binding (Ctrl-S by default) will now activate the search field, even when the pager is not focused. - The error when a command is not found is now printed a single time, instead of once per argument (#5588). - Fixes and improvements to the git completions, including printing correct paths with older git versions, fuzzy matching again, reducing unnecessary offers of root paths (starting with `:/`) (#5578, #5574, #5476), and ignoring shell aliases, so enterprising users can set up the wrapping command (via `set -g __fish_git_alias_$command $whatitwraps`) (#5412). - Significant performance improvements to core shell functions (#5447) and to the `kill` completions (#5541). - Starting in symbolically-linked working directories works correctly (#5525). - The default `fish_title` function no longer contains extra spaces (#5517). - The `nim` prompt now works correctly when chosen in the Web-based configuration (#5490). -- `string` now prints help to stdout, like other builtins (#5495), -- Improvements to the completions for `configure` (#5518) and `man` (#5566). +- `string` now prints help to stdout, like other builtins (#5495). - Killing the terminal while fish is in vi normal mode will no longer send it spinning and eating CPU. (#5528) - A number of crashes have been fixed (#5550, #5548, #5479, #5453). +- Improvements to the documentation and certain completions. + +### Known issues + +There is one significant known issue that was not corrected before the release: + +- fish does not run correctly under Windows Services for Linux before Windows 10 version 1809/17763, and the message warning of this may not be displayed (#5619). If you are upgrading from version 2.7.1 or before, please also review the release notes for 3.0.0 and 3.0b1 (included below). From e26ab3d81ce8a31a88d0e19bccb44aca9ec3d3c1 Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 11 Feb 2019 19:40:26 +0800 Subject: [PATCH 427/439] Bump version for 3.0.1 --- CHANGELOG.md | 2 +- osx/Info.plist | 2 +- osx/config.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c9062ad8..77a3a4771 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# fish 3.0.1 +# fish 3.0.1 (released February 11, 2019) This release of fish fixes a number of major issues discovered in fish 3.0.0. diff --git a/osx/Info.plist b/osx/Info.plist index 2eb4b5a8e..10b7d27df 100644 --- a/osx/Info.plist +++ b/osx/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 3.0.0 + 3.0.1 CFBundleVersion 0.1 LSApplicationCategoryType diff --git a/osx/config.h b/osx/config.h index 233464b40..77128ac1e 100644 --- a/osx/config.h +++ b/osx/config.h @@ -209,7 +209,7 @@ #define PACKAGE_NAME "fish" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "fish 3.0.0" +#define PACKAGE_STRING "fish 3.0.1" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "fish" @@ -218,7 +218,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "3.0.0" +#define PACKAGE_VERSION "3.0.1" /* The size of `wchar_t', as computed by sizeof. */ #define SIZEOF_WCHAR_T 4 From 1c6efc637861d63cac9aeca92c6505e63ca475de Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Mon, 11 Feb 2019 09:08:28 -0800 Subject: [PATCH 428/439] 'kill' is not a builtin. I guess I was on autopilot. --- share/config.fish | 50 ++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/share/config.fish b/share/config.fish index fdded6c7a..7f701d2b4 100644 --- a/share/config.fish +++ b/share/config.fish @@ -61,25 +61,21 @@ end if not set -q fish_function_path set fish_function_path $__fish_config_dir/functions $__fish_sysconf_dir/functions $__extra_functionsdir $__fish_data_dir/functions -end - -if not contains -- $__fish_data_dir/functions $fish_function_path - set fish_function_path $fish_function_path $__fish_data_dir/functions +else if not contains -- $__fish_data_dir/functions $fish_function_path + set -a fish_function_path $__fish_data_dir/functions end if not set -q fish_complete_path set fish_complete_path $__fish_config_dir/completions $__fish_sysconf_dir/completions $__extra_completionsdir $__fish_data_dir/completions $__fish_user_data_dir/generated_completions -end - -if not contains -- $__fish_data_dir/completions $fish_complete_path - set fish_complete_path $fish_complete_path $__fish_data_dir/completions +else if not contains -- $__fish_data_dir/completions $fish_complete_path + set -a fish_complete_path $__fish_data_dir/completions end # This cannot be in an autoload-file because `:.fish` is an invalid filename on windows. -function : - # no-op function for compatibility with sh, bash, and others. +function : -d "no-op function" + # for compatibility with sh, bash, and others. # Often used to insert a comment into a chain of commands without having - # it eat up the remainder of the line, handy in Makefiles. + # it eat up the remainder of the line, handy in Makefiles. end # @@ -91,9 +87,8 @@ end # if test -d /usr/xpg4/bin - if not contains -- /usr/xpg4/bin $PATH - set PATH /usr/xpg4/bin $PATH - end + not contains -- /usr/xpg4/bin $PATH + and set PATH /usr/xpg4/bin $PATH end # Add a handler for when fish_user_path changes, so we can apply the same changes to PATH @@ -111,9 +106,9 @@ function __fish_reconstruct_path -d "Update PATH when fish_user_paths changes" - if set -l idx (contains --index -- $x $local_path) set -e local_path[$idx] else - set -g __fish_added_user_paths $__fish_added_user_paths $x + set -ga __fish_added_user_paths $x end - set local_path $x $local_path + set -p local_path $x end end @@ -159,7 +154,7 @@ if not set -q __fish_init_2_3_0 if set -q fish_user_abbreviations set -l fab for abbr in $fish_user_abbreviations - set fab $fab (string replace -r '^([^ =]+)=(.*)$' '$1 $2' -- $abbr) + set -a fab (string replace -r '^([^ =]+)=(.*)$' '$1 $2' -- $abbr) end set fish_user_abbreviations $fab end @@ -175,10 +170,10 @@ if command -sq /usr/libexec/path_helper set -l result for path_file in $argv[2] $argv[3]/* - if test -f $path_file + if [ -f $path_file ] while read -l entry if not contains $entry $result - set result $result $entry + set -a result $entry end end <$path_file end @@ -211,11 +206,9 @@ if status --is-login # Put linux consoles in unicode mode. # if test "$TERM" = linux - if string match -qir '\.UTF' -- $LANG - if command -sq unicode_start - unicode_start - end - end + and string match -qir '\.UTF' -- $LANG + and command -sq unicode_start + unicode_start end end @@ -239,11 +232,14 @@ function __fish_expand_pid_args end end -for jobcmd in bg fg kill wait disown - function $jobcmd -V jobcmd - builtin $jobcmd (__fish_expand_pid_args $argv) +for jobbltn in bg fg wait disown + function $jobbltn -V jobbltn + builtin $jobbltn (__fish_expand_pid_args $argv) end end +function kill + command kill (__fish_expand_pid_args $argv) +end # As last part of initialization, source the conf directories. # Implement precedence (User > Admin > Extra (e.g. vendors) > Fish) by basically doing "basename". From bbc3fecbeb2f0bc32862a7f824c262ad2702bf2c Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Tue, 12 Feb 2019 01:32:06 -0800 Subject: [PATCH 429/439] env.cpp: Simplify update_fish_color_support Taking advantage of the maybe_t's, the logic and nesting here can be a bit less intense. Small adjustments to debug output, and found a more accurate version number for Lion Terminal.app. Longer term we should have a terminal_t class or something encapsulating all the kinds of terminal detection we have with methods that return the color support, and also stuff like whether the terminal has the newline glitch, the ambiguous width character behavior, etc. --- src/env.cpp | 54 ++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index ff787bc47..5b840859a 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -464,51 +464,51 @@ static bool does_term_support_setting_title(const environment_t &vars) { static void update_fish_color_support(const environment_t &vars) { // Detect or infer term256 support. If fish_term256 is set, we respect it; // otherwise infer it from the TERM variable or use terminfo. - auto fish_term256 = vars.get(L"fish_term256"); - auto term_var = vars.get(L"TERM"); - wcstring term = term_var.missing_or_empty() ? L"" : term_var->as_string(); - bool support_term256 = false; // default to no support - if (!fish_term256.missing_or_empty()) { + wcstring term; + bool support_term256 = false; + bool support_term24bit = false; + + if (auto term_var = vars.get(L"TERM")) term = term_var->as_string(); + + if (auto fish_term256 = vars.get(L"fish_term256")) { + // $fish_term256 support_term256 = bool_from_string(fish_term256->as_string()); - debug(2, L"256 color support determined by 'fish_term256'"); + debug(2, L"256 color support determined by '$fish_term256'"); } else if (term.find(L"256color") != wcstring::npos) { - // TERM=*256color*: Explicitly supported. + // TERM is *256color*: 256 colors explicitly supported support_term256 = true; - debug(2, L"256 color support enabled for '256color' in TERM"); + debug(2, L"256 color support enabled for TERM=%ls", term.c_str()); } else if (term.find(L"xterm") != wcstring::npos) { - // Assume that all xterms are 256, except for OS X SnowLeopard - const auto prog_var = vars.get(L"TERM_PROGRAM"); - const auto progver_var = vars.get(L"TERM_PROGRAM_VERSION"); - wcstring term_program = prog_var.missing_or_empty() ? L"" : prog_var->as_string(); - if (term_program == L"Apple_Terminal" && !progver_var.missing_or_empty()) { - // OS X Lion is version 300+, it has 256 color support - if (strtod(wcs2str(progver_var->as_string()), NULL) > 300) { + // Assume that all 'xterm's can handle 256, except for Terminal.app from Snow Leopard + wcstring term_program, term_version; + if (auto tp = vars.get(L"TERM_PROGRAM")) term_program = tp->as_string(); + if (auto tpv = vars.get(L"TERM_PROGRAM_VERSION")) { + if (term_program == L"Apple_Terminal" && + fish_wcstod(tpv->as_string().c_str(), NULL) > 299) { + // OS X Lion is version 299+, it has 256 color support (see github Wiki) support_term256 = true; - debug(2, L"256 color support enabled for TERM=xterm + modern Terminal.app"); + debug(2, L"256 color support enabled for TERM=%ls on Terminal.app", term.c_str()); + } else { + support_term256 = true; + debug(2, L"256 color support enabled for TERM=%ls", term.c_str()); } - } else { - support_term256 = true; - debug(2, L"256 color support enabled for TERM=xterm"); } } else if (cur_term != NULL) { // See if terminfo happens to identify 256 colors support_term256 = (max_colors >= 256); - debug(2, L"256 color support: using %d colors per terminfo", max_colors); - } else { - debug(2, L"256 color support not enabled (yet)"); + debug(2, L"256 color support: %d colors per terminfo entry for %ls", max_colors, term.c_str()); } - auto fish_term24bit = vars.get(L"fish_term24bit"); - bool support_term24bit; - if (!fish_term24bit.missing_or_empty()) { + // Handle $fish_term24bit + if (auto fish_term24bit = vars.get(L"fish_term24bit")) { support_term24bit = bool_from_string(fish_term24bit->as_string()); debug(2, L"'fish_term24bit' preference: 24-bit color %s", support_term24bit ? L"enabled" : L"disabled"); } else { // We don't attempt to infer term24 bit support yet. - support_term24bit = false; + // XXX: actually, we do, in config.fish. + // So we actually change the color mode shortly after startup } - color_support_t support = (support_term256 ? color_support_term256 : 0) | (support_term24bit ? color_support_term24bit : 0); output_set_color_support(support); From 7200f7ff4a4ecefd5ef17f5cc3fd1f122f115192 Mon Sep 17 00:00:00 2001 From: David Adam Date: Tue, 12 Feb 2019 22:30:53 +0800 Subject: [PATCH 430/439] Revert "Drop hard requirement on explicit `-lpthread` support" This reverts commit b402b635a9f7466616cef6e2b9cfd55a6e7068c5; as discussed in #5512 it is not required. --- cmake/ConfigureChecks.cmake | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index a13dbdbd5..2b8cd2a4a 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -31,12 +31,7 @@ set(THREADS_PREFER_PTHREAD_FLAG ON) IF(CMAKE_VERSION VERSION_LESS 3.4.0) ENABLE_LANGUAGE(C) ENDIF() -# Don't set pthreads to required. Either we're on a platform where explict -# linking with -lpthread is the norm (e.g. Linux) and it'll be found, or we're -# on a platform that include pthreads by default (e.g. BSD, macOS) where this -# won't find anything, or we're on a road-much-less-traveled OS where the user -# can figure out what's wrong without a hard error here. See #5512. -FIND_PACKAGE(Threads) +FIND_PACKAGE(Threads REQUIRED) # Detect WSL. Does not match against native Windows/WIN32. if (CMAKE_HOST_SYSTEM_VERSION MATCHES ".*-Microsoft") From 016d83c3fcc42a8bef4e5739a85309beb212a917 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 12 Feb 2019 16:54:14 +0100 Subject: [PATCH 431/439] completions/git: Handle MM files These are files with staged modifications, and additional unstaged ones. In practice what happened was that you ran git add somefile then editted it some more and tried to git add which didn't offer it anymore. Now, we offer it if either modified or modified-staged is set. Currently modified-staged isn't ever set alone, but through all-staged, so we still need to keep offering the file then. (This shows that the current switch/case might have some holes) Fixes #5648. [ci skip] --- share/completions/git.fish | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index bda9ac692..869883921 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -206,6 +206,16 @@ function __fish_git_files set -ql modified_staged and set file "$line[9..-1]" and set desc $staged_modified_desc + case '1 MM*' + # Staged-modified with unstaged modifications + # These need to be offered for both kinds of modified. + if set -ql modified + set file "$line[9..-1]" + set desc $modified_desc + else if set -ql modified_staged + set file "$line[9..-1]" + set desc $staged_modified_desc + end case '1 .D*' set -ql deleted and set file "$line[9..-1]" From 3382a2145fb0be715db79dce8ebb4f14af225caa Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 12 Feb 2019 18:12:52 +0100 Subject: [PATCH 432/439] doc_src/math: Document needed escaping harder Fixes #5650. [ci skip] --- doc_src/math.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc_src/math.txt b/doc_src/math.txt index 790933a93..4a53f572a 100644 --- a/doc_src/math.txt +++ b/doc_src/math.txt @@ -11,7 +11,7 @@ math [-sN | --scale=N] [--] EXPRESSION By default, the output is as a float with trailing zeroes trimmed. To get a fixed representation, the `--scale` option can be used, including `--scale=0` for integer output. -Keep in mind that parameter expansion takes before expressions 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 and the asterisk glob character have to be escaped or quoted. +Keep in mind that parameter expansion takes before expressions 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 (`()`) and the asterisk (`*`) glob character have to be escaped or quoted. `math` ignores whitespace between arguments and takes its input as multiple arguments (internally joined with a space), so `math 2 +2` and `math "2 + 2"` work the same. `math 2 2` is an error. @@ -35,13 +35,13 @@ For numbers, `.` is always the radix character regardless of locale - `2.5`, not - `+` for addition and `-` for subtraction. -- `*` for multiplication, `/` for division. +- `*` for multiplication, `/` for division. (Note that `*` is the glob character and needs to be quoted or escaped) - `^` for exponentiation. - `%` for modulo. -- `(` and `)` for grouping. +- `(` and `)` for grouping. (These need to be quoted or escaped because `()` denotes a command substitution.) They are all used in an infix manner - `5 + 2`, not `+ 5 2`. @@ -97,6 +97,8 @@ All of the trigonometric functions use radians. `math "sin(pi)"` outputs `0`. +`math 5 \* 2` or `math "5 * 2"` or `math 5 "*" 2` all output `10`. + \subsection math-notes Compatibility notes Fish 1.x and 2.x releases relied on the `bc` command for handling `math` expressions. Starting with fish 3.0.0 fish uses the tinyexpr library and evaluates the expression without the involvement of any external commands. From fb7a6e5f347ab240e0dbce6ee0ee265393ca7f8a Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 12:34:00 +0100 Subject: [PATCH 433/439] Add `builtin -q` Used to query for a builtin's existence, like `type -q` and `functions -q` can be used to query for a things and a functions existence respectively. --- doc_src/builtin.txt | 4 +++- src/builtin_builtin.cpp | 29 +++++++++++++++++++++++++++-- tests/test_builtinbuiltin.err | 2 ++ tests/test_builtinbuiltin.in | 7 +++++++ tests/test_builtinbuiltin.out | 3 +++ 5 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 tests/test_builtinbuiltin.err create mode 100644 tests/test_builtinbuiltin.in create mode 100644 tests/test_builtinbuiltin.out diff --git a/doc_src/builtin.txt b/doc_src/builtin.txt index 4c465ef18..20664823e 100644 --- a/doc_src/builtin.txt +++ b/doc_src/builtin.txt @@ -2,7 +2,8 @@ \subsection builtin-synopsis Synopsis \fish{synopsis} -builtin BUILTINNAME [OPTIONS...] +builtin [OPTIONS...] BUILTINNAME +builtin --query BUILTINNAMES... \endfish \subsection builtin-description Description @@ -12,6 +13,7 @@ builtin BUILTINNAME [OPTIONS...] The following parameters are available: - `-n` or `--names` List the names of all defined builtins +- `-q` or `--query` tests if any of the specified builtins exists. \subsection builtin-example Example diff --git a/src/builtin_builtin.cpp b/src/builtin_builtin.cpp index bc59f1369..4ac852b60 100644 --- a/src/builtin_builtin.cpp +++ b/src/builtin_builtin.cpp @@ -17,10 +17,13 @@ struct builtin_cmd_opts_t { bool print_help = false; bool list_names = false; + bool query = false; }; -static const wchar_t *const short_options = L":hn"; +static const wchar_t *const short_options = L":hnq"; static const struct woption long_options[] = { - {L"help", no_argument, NULL, 'h'}, {L"names", no_argument, NULL, 'n'}, {NULL, 0, NULL, 0}}; + {L"help", no_argument, NULL, 'h'}, {L"names", no_argument, NULL, 'n'}, + {L"query", no_argument, NULL, 'q'}, + {NULL, 0, NULL, 0}}; static int parse_cmd_opts(builtin_cmd_opts_t &opts, int *optind, int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) { @@ -37,6 +40,10 @@ static int parse_cmd_opts(builtin_cmd_opts_t &opts, int *optind, int argc, wchar opts.list_names = true; break; } + case 'q': { + opts.query = true; + break; + } case ':': { builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]); return STATUS_INVALID_ARGS; @@ -73,6 +80,24 @@ int builtin_builtin(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return STATUS_CMD_OK; } + if (opts.query && opts.list_names) { + streams.err.append_format(BUILTIN_ERR_COMBO2, cmd, + _(L"--query and --names are mutually exclusive")); + return STATUS_INVALID_ARGS; + } + + if (opts.query) { + wcstring_list_t names = builtin_get_names(); + retval = STATUS_CMD_ERROR; + for (int i = optind; i < argc; i++) { + if (contains(names, argv[i])) { + retval = STATUS_CMD_OK; + break; + } + } + return retval; + } + if (opts.list_names) { wcstring_list_t names = builtin_get_names(); std::sort(names.begin(), names.end()); diff --git a/tests/test_builtinbuiltin.err b/tests/test_builtinbuiltin.err new file mode 100644 index 000000000..ff818897a --- /dev/null +++ b/tests/test_builtinbuiltin.err @@ -0,0 +1,2 @@ +builtin: Invalid combination of options, +--query and --names are mutually exclusive diff --git a/tests/test_builtinbuiltin.in b/tests/test_builtinbuiltin.in new file mode 100644 index 000000000..4293a96e1 --- /dev/null +++ b/tests/test_builtinbuiltin.in @@ -0,0 +1,7 @@ +# Tests for the "builtin" builtin/keyword. +builtin -q string; and echo String exists +builtin -q; and echo None exists +builtin -q string echo banana; and echo Some of these exist +builtin -nq string +echo $status +exit 0 diff --git a/tests/test_builtinbuiltin.out b/tests/test_builtinbuiltin.out new file mode 100644 index 000000000..11c0882b4 --- /dev/null +++ b/tests/test_builtinbuiltin.out @@ -0,0 +1,3 @@ +String exists +Some of these exist +2 From dc0746bc451effbe56baf0d94d9cc8cf8d747762 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 9 Feb 2019 12:51:55 +0100 Subject: [PATCH 434/439] Let `command -q` work This required "-sq" to be used and errored if just "-q" was given. Instead, if only "-q" is given, we behave just as if "-sq" was. --- doc_src/command.txt | 4 ++-- src/builtin_command.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc_src/command.txt b/doc_src/command.txt index e4f5c2cf6..673522587 100644 --- a/doc_src/command.txt +++ b/doc_src/command.txt @@ -13,7 +13,7 @@ The following options are available: - `-a` or `--all` returns all the external commands that are found in `$PATH` in the order they are found. -- `-q` or `--quiet`, in conjunction with `-s`, silences the output and prints nothing, setting only the exit code. +- `-q` or `--quiet`, silences the output and prints nothing, setting only the exit code. Implies `--search`. - `-s` or `--search` returns the name of the external command that would be executed, or nothing if no file with the specified name could be found in the `$PATH`. @@ -27,4 +27,4 @@ For basic compatibility with POSIX `command`, the `-v` flag is recognized as an `command -s ls` returns the path to the `ls` program. -`command -sq git; and command git log` runs `git log` only if `git` exists. +`command -q git; and command git log` runs `git log` only if `git` exists. diff --git a/src/builtin_command.cpp b/src/builtin_command.cpp index 1e038e68d..e8308bb76 100644 --- a/src/builtin_command.cpp +++ b/src/builtin_command.cpp @@ -87,7 +87,8 @@ int builtin_command(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return STATUS_CMD_OK; } - if (!opts.find_path && !opts.all_paths) { + // Quiet implies find_path. + if (!opts.find_path && !opts.all_paths && !opts.quiet) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_INVALID_ARGS; } @@ -101,7 +102,7 @@ int builtin_command(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (!opts.quiet) streams.out.append_format(L"%ls\n", path.c_str()); ++found; } - } else { + } else { // Either find_path explicitly or just quiet. wcstring path; if (path_get_path(command_name, &path, parser.vars())) { if (!opts.quiet) streams.out.append_format(L"%ls\n", path.c_str()); From c588d5866361a3ba628ee8c61049aa8c5e5a42e4 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 12 Feb 2019 20:33:42 +0100 Subject: [PATCH 435/439] CHANGELOG: Add `-q` [ci skip] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cd04bb82..f3c260f80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - mandoc can now be used to format the output from `--help` if nroff is not installed - New color options for the pager have been added (#5524). - The default escape delay (to differentiate between the escape key and an alt-combination) has been reduced to 30ms, down from 300ms for the default mode and 100ms for vi-mode (#3904). +- In the interest of consistency, `builtin -q` and `command -q` can now be used to query if a builtin or command exists (#5631). --- From a19206036cef9ed804860563147c2dcc3a1f2e7b Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Tue, 12 Feb 2019 13:53:04 -0800 Subject: [PATCH 436/439] output.{h,cpp}: remove unused enum and correct a comment --- src/output.cpp | 4 +--- src/output.h | 14 -------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/output.cpp b/src/output.cpp index 585c5fb3f..d0848b3e1 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -148,9 +148,7 @@ bool write_color(rgb_color_t color, bool is_fg) { /// 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. +/// Possible values for colors are rgb_color_t colors or special values like rgb_color_t::normal() /// /// In order to set the color to normal, three terminfo strings may have to be written. /// diff --git a/src/output.h b/src/output.h index 8ce26b598..17a076814 100644 --- a/src/output.h +++ b/src/output.h @@ -14,20 +14,6 @@ class env_var_t; -/// Constants for various colors as used by the set_color function. -enum { - FISH_COLOR_BLACK, // 0 - FISH_COLOR_RED, // 1 - FISH_COLOR_GREEN, // 2 - FISH_COLOR_YELLOW, // 3 - FISH_COLOR_BLUE, // 4 - FISH_COLOR_MAGENTA, // 5 - FISH_COLOR_CYAN, // 6 - FISH_COLOR_WHITE, // 7 - FISH_COLOR_NORMAL, // 8 terminal default - FISH_COLOR_RESET // 9 -}; - void set_color(rgb_color_t c, rgb_color_t c2); void writembs_check(const char *mbs, const char *mbs_name, bool critical, const char *file, long line); From c931e3375946e8cd6798d8575b90dc33d5cdd623 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Tue, 12 Feb 2019 15:37:09 -0800 Subject: [PATCH 437/439] io.cpp: use BUFFER_SIZE -Wunused-macros showed that a recent change used 4096 instead of BUFFER_SIZE as previously (also 4096). --- src/io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io.cpp b/src/io.cpp index 713bc1432..2dbc10ed3 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -94,7 +94,7 @@ void io_buffer_t::run_background_fillthread(autoclose_fd_t readfd) { scoped_lock locker(append_lock_); ssize_t ret; do { - char buff[4096]; + char buff[BUFFER_SIZE]; ret = read(fd, buff, sizeof buff); if (ret > 0) { buffer_.append(&buff[0], &buff[ret]); From 8d9089c78b9dfe76eeddc2b543feca0722ef3322 Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Tue, 12 Feb 2019 15:50:43 -0800 Subject: [PATCH 438/439] Revert "io.cpp: use BUFFER_SIZE" This reverts commit c931e3375946e8cd6798d8575b90dc33d5cdd623. --- src/io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io.cpp b/src/io.cpp index 2dbc10ed3..713bc1432 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -94,7 +94,7 @@ void io_buffer_t::run_background_fillthread(autoclose_fd_t readfd) { scoped_lock locker(append_lock_); ssize_t ret; do { - char buff[BUFFER_SIZE]; + char buff[4096]; ret = read(fd, buff, sizeof buff); if (ret > 0) { buffer_.append(&buff[0], &buff[ret]); From 717718353ee811d7f31d89a77890001c25488c4b Mon Sep 17 00:00:00 2001 From: Aaron Gyes Date: Tue, 12 Feb 2019 16:10:18 -0800 Subject: [PATCH 439/439] Remove unused macros --- src/postfork.cpp | 3 --- src/proc.cpp | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/postfork.cpp b/src/postfork.cpp index a9e7b94e9..77d7e04f3 100644 --- a/src/postfork.cpp +++ b/src/postfork.cpp @@ -33,9 +33,6 @@ /// 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. -#define OPEN_MASK 0666 - /// Fork error message. #define FORK_ERROR "Could not create child process - exiting" diff --git a/src/proc.cpp b/src/proc.cpp index 94b5358a6..c52cf09dd 100644 --- a/src/proc.cpp +++ b/src/proc.cpp @@ -50,9 +50,6 @@ #include "util.h" #include "wutil.h" // IWYU pragma: keep -/// Size of buffer for reading buffered output. -#define BUFFER_SIZE 4096 - /// Status of last process to exit. static int last_status = 0;