mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-31 20:31:19 -03:00
Merge branch 'master' into coverity_scan_master
This commit is contained in:
14
.oclint
14
.oclint
@@ -51,3 +51,17 @@ disable-rules:
|
||||
# and is therefore just noise. Disable this rule.
|
||||
#
|
||||
- InvertedLogic
|
||||
#
|
||||
# The idea behind the "double negative" rule is sound since constructs
|
||||
# like "!!(var & flag)" should be written as "static_cast<bool>(var &
|
||||
# flag)". Unfortunately this rule has way too many false positives;
|
||||
# especially in the context of assert statements. So disable this rule.
|
||||
#
|
||||
- DoubleNegative
|
||||
#
|
||||
# Avoiding bitwise operators in a conditional is a good idea with one
|
||||
# exception: testing whether a bit flag is set. Which happens to be the
|
||||
# only time you'll see something like `if (j->flags & JOB_CONSTRUCTED)`
|
||||
# in fish source.
|
||||
#
|
||||
- BitwiseOperatorInConditional
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
- `history search` is now case-insensitive by default (which also affects `history delete`) (#3236).
|
||||
- `history delete` now correctly handles multiline commands (#31).
|
||||
- Vi-style bindings no longer include all of the default emacs-style bindings; instead, they share some definitions (#3068).
|
||||
- If there is no locale set in the environment, various known system configuration files will be checked for a default, otherwise forcing en_US-UTF.8 (#277).
|
||||
- If there is no locale set in the environment, various known system configuration files will be checked for a default. If no locale can be found, `en_US-UTF.8` will be used (#277).
|
||||
- A number followed by a caret (e.g. `5^`) is no longer treated as a redirection (#1873).
|
||||
- The `$version` special variable can be overwritten, so that it can be used for other purposes if required.
|
||||
|
||||
## Notable fixes and improvements
|
||||
- The `fish_realpath` builtin has been renamed to `realpath` and made compatible with GNU `realpath` when run without arguments (#3400). It is used only for systems without a `realpath` or `grealpath` utility (#3374).
|
||||
- `fish_indent` can now read from named files, rather than just standard input (#3037).
|
||||
- Improved color handling on terminals/consoles with 8-16 colors, particularly the use of bright named color (#3176, #3260).
|
||||
- `fish_indent` can now read from files given as arguments, rather than just standard input (#3037).
|
||||
- Fuzzy tab completions behave in a less surprising manner (#3090, #3211).
|
||||
- `jobs` should only print its header line once (#3127).
|
||||
- Improved color handling on terminals with limited colors (#3176, #3260).
|
||||
- Wildcards in redirections are highlighted appropriately (#2789).
|
||||
- Suggestions will be offered more often, like after removing characters (#3069).
|
||||
- `history --merge` now correctly interleaves items in chronological order (#2312).
|
||||
|
||||
@@ -386,7 +386,7 @@ toc.txt: $(HDR_FILES:index.hdr=index.hdr.in) | show-SED
|
||||
@echo " SED $(em)$@$(sgr0)"
|
||||
$v rm -f toc.tmp $@
|
||||
# Ugly hack to set the toc initial title for the main page
|
||||
$v echo '- <a href="index.html" id="toc-index">fish shell documentation - $FISH_BUILD_VERSION</a>' > toc.tmp
|
||||
$v echo '- <a href="index.html" id="toc-index">fish shell documentation - $(FISH_BUILD_VERSION)</a>' > toc.tmp
|
||||
# The first sed command captures the page name, followed by the description
|
||||
# The second sed command captures the command name \1 and the description \2, but only up to a dash
|
||||
# This is to reduce the size of the TOC in the command listing on the main page
|
||||
@@ -454,7 +454,7 @@ lexicon_filter: lexicon.txt lexicon_filter.in | show-SED
|
||||
else \
|
||||
WORDBL='\\<'; WORDBR='\\>'; \
|
||||
fi; $(SED) <lexicon.txt >>$@.tmp -n -e "s|^\([a-z][a-z][a-z][a-z]\) \([a-z_-]*\)$$|s,$$WORDBL\2$$WORDBR,@\1{\2},g|p" -e '$$G;s/.*\n/b tidy/p';
|
||||
mv $@.tmp $@; test -x $@ || chmod a+x $@;
|
||||
$v mv $@.tmp $@; test -x $@ || chmod a+x $@;
|
||||
|
||||
|
||||
#
|
||||
@@ -649,6 +649,7 @@ install-force: all install-translations | show-datadir show-sysconfdir show-extr
|
||||
$v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish
|
||||
$v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/completions
|
||||
$v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/functions
|
||||
$v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/groff
|
||||
$v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/man/man1
|
||||
$v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools
|
||||
$v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config
|
||||
@@ -673,6 +674,7 @@ install-force: all install-translations | show-datadir show-sysconfdir show-extr
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/functions/; \
|
||||
done;
|
||||
@echo "Installing $(bo)man pages$(sgr0)";
|
||||
$v $(INSTALL) -m 644 share/groff/* $(DESTDIR)$(datadir)/fish/groff/
|
||||
$v for i in $(wildcard share/man/man1/*.1); do \
|
||||
$(INSTALL) -m 644 $$i $(DESTDIR)$(datadir)/fish/man/man1/; \
|
||||
done;
|
||||
|
||||
@@ -92,7 +92,7 @@ if set -q c_files[1]
|
||||
# The stderr to stdout redirection is because cppcheck, incorrectly IMHO, writes its
|
||||
# diagnostic messages to stderr. Anyone running this who wants to capture its output will
|
||||
# expect those messages to be written to stdout.
|
||||
cppcheck -q --verbose --std=posix --language=c++ --template "[{file}:{line}]: {severity} ({id}): {message}" --suppress=missingIncludeSystem --inline-suppr --enable=$cppchecks --rule-file=.cppcheck.rule $cppcheck_args $c_files 2>&1
|
||||
cppcheck -q --verbose --std=posix --language=c++ --template \[(set_color --bold)(set_color --underline)"{file}"(set_color normal)(set_color --bold)":{line}"(set_color normal)"] "(set_color brmagenta)"{severity}"(set_color magenta)" ({id}):"\n(set_color normal)" {message}" --suppress=missingIncludeSystem --inline-suppr --enable=$cppchecks --rule-file=.cppcheck.rule $cppcheck_args $c_files 2>&1
|
||||
end
|
||||
|
||||
if type -q oclint
|
||||
@@ -105,7 +105,7 @@ if set -q c_files[1]
|
||||
# output will expect those messages to be written to stdout.
|
||||
if test "$kernel_name" = "Darwin"
|
||||
if not test -f compile_commands.json
|
||||
xcodebuild >xcodebuild.log
|
||||
xcodebuild -alltargets >xcodebuild.log
|
||||
oclint-xcodebuild xcodebuild.log >/dev/null
|
||||
end
|
||||
if test $all = yes
|
||||
|
||||
@@ -2,39 +2,34 @@
|
||||
|
||||
\subsection alias-synopsis Synopsis
|
||||
\fish{synopsis}
|
||||
alias
|
||||
alias NAME DEFINITION
|
||||
alias NAME=DEFINITION
|
||||
\endfish
|
||||
|
||||
\subsection alias-description Description
|
||||
|
||||
`alias` is a simple wrapper for the `function` builtin. It exists for backwards compatibility with Posix shells. For other uses, it is recommended to define a <a href='#function'>function</a>.
|
||||
`alias` is a simple wrapper for the `function` builtin, which creates a function wrapping a command. It has similar syntax to POSIX shell `alias`. For other uses, it is recommended to define a <a href='#function'>function</a>.
|
||||
|
||||
`fish` does not keep track of which functions have been defined using `alias`. They must be erased using `functions -e`.
|
||||
`fish` marks functions that have been created by `alias` by including the command used to create them in the function description. You can list `alias`-created functions by running `alias` without arguments. They must be erased using `functions -e`.
|
||||
|
||||
- `NAME` is the name of the alias
|
||||
|
||||
- `DEFINITION` is the actual command to execute. The string `$argv` will be appended.
|
||||
|
||||
You cannot create an alias to a function with the same name.
|
||||
|
||||
Note that spaces need to be escaped in the call to alias just like in the commandline _even inside the quotes_.
|
||||
|
||||
You cannot create an alias to a function with the same name. Note that spaces need to be escaped in the call to `alias` just like at the command line, _even inside quoted parts_.
|
||||
|
||||
\subsection alias-example Example
|
||||
|
||||
The following code will create `rmi`, which runs `rm` with additional arguments on every invocation.
|
||||
|
||||
\fish
|
||||
alias rmi "rm -i"
|
||||
alias rmi="rm -i"
|
||||
|
||||
# This is equivalent to entering the following function:
|
||||
|
||||
function rmi
|
||||
function rmi --wraps rm --description 'alias rmi=rm -i'
|
||||
rm -i $argv
|
||||
end
|
||||
|
||||
# This needs to have the spaces escaped or "Chrome.app..." will be seen as an argument to "/Applications/Google":
|
||||
|
||||
alias chrome='/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome banana'
|
||||
\endfish
|
||||
|
||||
@@ -14,8 +14,6 @@ history ( -h | --help )
|
||||
|
||||
`history` is used to search, delete, and otherwise manipulate the history of interactive commands.
|
||||
|
||||
Note that for backwards compatibility each subcommand can also be specified as a long option. For example, rather than `history search` you can type `history --search`. Those long options are deprecated and will be removed in a future release.
|
||||
|
||||
The following operations (sub-commands) are available:
|
||||
|
||||
- `search` returns history items matching the search string. If no search string is provided it returns all history items. This is the default operation if no other operation is specified. You only have to explicitly say `history search` if you wish to search for one of the subcommands. The `--contains` search option will be used if you don't specify a different search option. Entries are ordered newest to oldest. If stdout is attached to a tty the output will be piped through your pager by the history function. The history builtin simply writes the results to stdout.
|
||||
@@ -60,9 +58,10 @@ history --search --contains "foo"
|
||||
history --delete --prefix "foo"
|
||||
# Interactively deletes commands which start with "foo" from the history.
|
||||
# You can select more than one entry by entering their IDs seperated by a space.
|
||||
\endfish
|
||||
|
||||
\subsection history-notes Notes
|
||||
|
||||
If you specify both `--prefix` and `--contains` the last flag seen is used.
|
||||
|
||||
\endfish
|
||||
Note that for backwards compatibility each subcommand can also be specified as a long option. For example, rather than `history search` you can type `history --search`. Those long options are deprecated and will be removed in a future release.
|
||||
|
||||
@@ -9,6 +9,8 @@ open FILES...
|
||||
|
||||
`open` opens a file in its default application, using the appropriate tool for the operating system. On GNU/Linux, this requires the common but optional `xdg-open` utility, from the `xdg-utils` package.
|
||||
|
||||
Note that this function will not be used if a command by this name exists (which is the case on macOS or Haiku).
|
||||
|
||||
|
||||
\subsection open-example Example
|
||||
|
||||
|
||||
@@ -2,33 +2,50 @@
|
||||
|
||||
\subsection status-synopsis Synopsis
|
||||
\fish{synopsis}
|
||||
status [OPTION]
|
||||
status
|
||||
status is-login
|
||||
status is-interactive
|
||||
status is-block
|
||||
status is-command-substitution
|
||||
status is-no-job-control
|
||||
status is-full-job-control
|
||||
status is-interactive-job-control
|
||||
status current-filename
|
||||
status current-line-number
|
||||
status print-stack-trace
|
||||
status job-control CONTROL-TYPE
|
||||
\endfish
|
||||
|
||||
\subsection status-description Description
|
||||
|
||||
With no arguments, `status` displays a summary of the current login and job control status of the shell.
|
||||
|
||||
The following options are available:
|
||||
The following operations (sub-commands) are available:
|
||||
|
||||
- `-c` or `--is-command-substitution` returns 0 if fish is currently executing a command substitution.
|
||||
- `is-command-sub` returns 0 if fish is currently executing a command substitution. Also `-c` or `--is-command-substitution`.
|
||||
|
||||
- `-b` or `--is-block` returns 0 if fish is currently executing a block of code.
|
||||
- `is-block` returns 0 if fish is currently executing a block of code. Also `-b` or `--is-block`.
|
||||
|
||||
- `-i` or `--is-interactive` returns 0 if fish is interactive - that is, connected to a keyboard.
|
||||
- `is-interactive` returns 0 if fish is interactive - that is, connected to a keyboard. Also `-i` or `--is-interactive`.
|
||||
|
||||
- `-l` or `--is-login` returns 0 if fish is a login shell - that is, if fish should perform login tasks such as setting up the PATH.
|
||||
- `is-login` returns 0 if fish is a login shell - that is, if fish should perform login tasks such as setting up the PATH. Also `-l` or `--is-login`.
|
||||
|
||||
- `--is-full-job-control` returns 0 if full job control is enabled.
|
||||
- `is-full-job-control` returns 0 if full job control is enabled. Also `--is-full-job-control` (no short flag).
|
||||
|
||||
- `--is-interactive-job-control` returns 0 if interactive job control is enabled.
|
||||
- `is-interactive-job-control` returns 0 if interactive job control is enabled. Also, `--is-interactive-job-control` (no short flag).
|
||||
|
||||
- `--is-no-job-control` returns 0 if no job control is enabled.
|
||||
- `is-no-job-control` returns 0 if no job control is enabled. Also `--is-no-job-control` (no short flag).
|
||||
|
||||
- `-f` or `--current-filename` prints the filename of the currently running script.
|
||||
- `current-filename` prints the filename of the currently running script. Also `-f` or `--current-filename`.
|
||||
|
||||
- `-n` or `--current-line-number` prints the line number of the currently running script.
|
||||
- `current-line-number` prints the line number of the currently running script. Also `-n` or `--current-line-number`.
|
||||
|
||||
- `-j CONTROLTYPE` or `--job-control=CONTROLTYPE` sets the job control type, which can be `none`, `full`, or `interactive`.
|
||||
- `job-control CONTROL-TYPE` sets the job control type, which can be `none`, `full`, or `interactive`. Also `-j CONTROL-TYPE` or `--job-control=CONTROL-TYPE`.
|
||||
|
||||
- `-t` or `--print-stack-trace` prints a stack trace of all function calls on the call stack.
|
||||
- `print-stack-trace` prints a stack trace of all function calls on the call stack. Also `-t` or `--print-stack-trace`.
|
||||
|
||||
\subsection status-notes Notes
|
||||
|
||||
For backwards compatibility each subcommand can also be specified as a long or short option. For example, rather than `status is-login` you can type `status --is-login`. The flag forms are deprecated and may be removed in a future release (but not before fish 3.0).
|
||||
|
||||
You can only specify one subcommand per invocation even if you use the flag form of the subcommand.
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
D01A2C9B16964C8200767098 /* Copy Files */,
|
||||
);
|
||||
dependencies = (
|
||||
9C7A55801DCD73930049C25D /* PBXTargetDependency */,
|
||||
D0F01A1315AA36280034B3B1 /* PBXTargetDependency */,
|
||||
D0F01A1715AA36300034B3B1 /* PBXTargetDependency */,
|
||||
D0A564EF168D09C000AF6161 /* PBXTargetDependency */,
|
||||
@@ -67,6 +68,72 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
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 */; };
|
||||
9C7A55281DCD65540049C25D /* builtin_commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853013B3ACEE0099B651 /* builtin_commandline.cpp */; };
|
||||
9C7A55291DCD65540049C25D /* builtin_complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853113B3ACEE0099B651 /* builtin_complete.cpp */; };
|
||||
9C7A552A1DCD65540049C25D /* builtin_jobs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853213B3ACEE0099B651 /* builtin_jobs.cpp */; };
|
||||
9C7A552B1DCD65540049C25D /* builtin_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853313B3ACEE0099B651 /* builtin_set.cpp */; };
|
||||
9C7A552C1DCD65540049C25D /* builtin_set_color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C861EA16CC7054003B5A04 /* builtin_set_color.cpp */; };
|
||||
9C7A552D1DCD65540049C25D /* builtin_ulimit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853413B3ACEE0099B651 /* builtin_ulimit.cpp */; };
|
||||
9C7A552E1DCD65540049C25D /* builtin_printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0CA63F316FC275F00093BD4 /* builtin_printf.cpp */; };
|
||||
9C7A552F1DCD65820049C25D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855E13B3ACEE0099B651 /* util.cpp */; };
|
||||
9C7A55361DCD71330049C25D /* autoload.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */; };
|
||||
9C7A55371DCD71330049C25D /* builtin_commandline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853013B3ACEE0099B651 /* builtin_commandline.cpp */; };
|
||||
9C7A55381DCD71330049C25D /* builtin_complete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853113B3ACEE0099B651 /* builtin_complete.cpp */; };
|
||||
9C7A55391DCD71330049C25D /* builtin_jobs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853213B3ACEE0099B651 /* builtin_jobs.cpp */; };
|
||||
9C7A553A1DCD71330049C25D /* builtin_set.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853313B3ACEE0099B651 /* builtin_set.cpp */; };
|
||||
9C7A553B1DCD71330049C25D /* builtin_set_color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C861EA16CC7054003B5A04 /* builtin_set_color.cpp */; };
|
||||
9C7A553C1DCD71330049C25D /* builtin_ulimit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853413B3ACEE0099B651 /* builtin_ulimit.cpp */; };
|
||||
9C7A553D1DCD71330049C25D /* builtin_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0F3373A1506DE3C00ECEFC0 /* builtin_test.cpp */; };
|
||||
9C7A553E1DCD71330049C25D /* builtin_printf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0CA63F316FC275F00093BD4 /* builtin_printf.cpp */; };
|
||||
9C7A553F1DCD71330049C25D /* builtin_string.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F7B1BA4BF4000B0F227 /* builtin_string.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 */; };
|
||||
9C7A554D1DCD71330049C25D /* builtin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853513B3ACEE0099B651 /* builtin.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, ); }; };
|
||||
D00769121990137800CA4627 /* autoload.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */; };
|
||||
D00769131990137800CA4627 /* builtin_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0F3373A1506DE3C00ECEFC0 /* builtin_test.cpp */; };
|
||||
D00769141990137800CA4627 /* color.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B6B0FE14E88BA400AD6C10 /* color.cpp */; };
|
||||
@@ -286,6 +353,27 @@
|
||||
/* 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 */;
|
||||
@@ -438,6 +526,7 @@
|
||||
dstPath = base/bin;
|
||||
dstSubfolderSpec = 1;
|
||||
files = (
|
||||
9C7A55811DCD739C0049C25D /* fish_key_reader in CopyFiles */,
|
||||
D0F019F115A977140034B3B1 /* fish in CopyFiles */,
|
||||
D0F019F315A977290034B3B1 /* fish_indent in CopyFiles */,
|
||||
);
|
||||
@@ -468,6 +557,17 @@
|
||||
/* Begin PBXFileReference section */
|
||||
4E142D731B56B5D7008783C8 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config.h; path = ../osx/config.h; sourceTree = "<group>"; };
|
||||
63A2C0E81CC5F9FB00973404 /* pcre2_find_bracket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_find_bracket.c; sourceTree = "<group>"; };
|
||||
9C7A55721DCD71330049C25D /* fish_key_reader */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fish_key_reader; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9C7A55731DCD716F0049C25D /* builtin_commandline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_commandline.h; sourceTree = "<group>"; };
|
||||
9C7A55741DCD716F0049C25D /* builtin_complete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_complete.h; sourceTree = "<group>"; };
|
||||
9C7A55751DCD716F0049C25D /* builtin_jobs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_jobs.h; sourceTree = "<group>"; };
|
||||
9C7A55761DCD716F0049C25D /* builtin_printf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_printf.h; sourceTree = "<group>"; };
|
||||
9C7A55771DCD716F0049C25D /* builtin_set_color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_set_color.h; sourceTree = "<group>"; };
|
||||
9C7A55781DCD716F0049C25D /* builtin_set.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_set.h; sourceTree = "<group>"; };
|
||||
9C7A55791DCD716F0049C25D /* builtin_string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_string.h; sourceTree = "<group>"; };
|
||||
9C7A557A1DCD716F0049C25D /* builtin_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_test.h; sourceTree = "<group>"; };
|
||||
9C7A557B1DCD716F0049C25D /* builtin_ulimit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_ulimit.h; sourceTree = "<group>"; };
|
||||
9C7A557C1DCD717C0049C25D /* fish_key_reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fish_key_reader.cpp; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
D01A2D23169B730A00767098 /* man1 */ = {isa = PBXFileReference; lastKnownFileType = text; name = man1; path = pages_for_manpath/man1; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -618,6 +718,15 @@
|
||||
/* 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;
|
||||
@@ -743,6 +852,16 @@
|
||||
D0D02A91159845EF008E62BD /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9C7A557C1DCD717C0049C25D /* fish_key_reader.cpp */,
|
||||
9C7A55731DCD716F0049C25D /* builtin_commandline.h */,
|
||||
9C7A55741DCD716F0049C25D /* builtin_complete.h */,
|
||||
9C7A55751DCD716F0049C25D /* builtin_jobs.h */,
|
||||
9C7A55761DCD716F0049C25D /* builtin_printf.h */,
|
||||
9C7A55771DCD716F0049C25D /* builtin_set_color.h */,
|
||||
9C7A55781DCD716F0049C25D /* builtin_set.h */,
|
||||
9C7A55791DCD716F0049C25D /* builtin_string.h */,
|
||||
9C7A557A1DCD716F0049C25D /* builtin_test.h */,
|
||||
9C7A557B1DCD716F0049C25D /* builtin_ulimit.h */,
|
||||
4E142D731B56B5D7008783C8 /* config.h */,
|
||||
D0C6FCCB14CFA4B7004CE8AD /* autoload.h */,
|
||||
D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */,
|
||||
@@ -888,6 +1007,7 @@
|
||||
D0D02AD01598642A008E62BD /* fish_indent */,
|
||||
D00769421990137800CA4627 /* fish_tests */,
|
||||
D04F7FD01BA4E29300B0F227 /* libpcre2.a */,
|
||||
9C7A55721DCD71330049C25D /* fish_key_reader */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -905,6 +1025,24 @@
|
||||
/* 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" */;
|
||||
@@ -1031,6 +1169,7 @@
|
||||
D00769101990137800CA4627 /* fish_tests */,
|
||||
D04F7FCF1BA4E29300B0F227 /* pcre2 */,
|
||||
D008D0C41BC58F8800841177 /* generate-version-header */,
|
||||
9C7A55301DCD71330049C25D /* fish_key_reader */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -1226,10 +1365,80 @@
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
9C7A55351DCD71330049C25D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9C7A557E1DCD71CD0049C25D /* print_help.cpp in Sources */,
|
||||
9C7A557D1DCD71890049C25D /* fish_key_reader.cpp in Sources */,
|
||||
9C7A55361DCD71330049C25D /* autoload.cpp in Sources */,
|
||||
9C7A55371DCD71330049C25D /* builtin_commandline.cpp in Sources */,
|
||||
9C7A55381DCD71330049C25D /* builtin_complete.cpp in Sources */,
|
||||
9C7A55391DCD71330049C25D /* builtin_jobs.cpp in Sources */,
|
||||
9C7A553A1DCD71330049C25D /* builtin_set.cpp in Sources */,
|
||||
9C7A553B1DCD71330049C25D /* builtin_set_color.cpp in Sources */,
|
||||
9C7A553C1DCD71330049C25D /* builtin_ulimit.cpp in Sources */,
|
||||
9C7A553D1DCD71330049C25D /* builtin_test.cpp in Sources */,
|
||||
9C7A553E1DCD71330049C25D /* builtin_printf.cpp in Sources */,
|
||||
9C7A553F1DCD71330049C25D /* builtin_string.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 */,
|
||||
9C7A554C1DCD71330049C25D /* utf8.cpp in Sources */,
|
||||
9C7A554D1DCD71330049C25D /* builtin.cpp in Sources */,
|
||||
9C7A554E1DCD71330049C25D /* function.cpp in Sources */,
|
||||
9C7A554F1DCD71330049C25D /* complete.cpp in Sources */,
|
||||
9C7A55501DCD71330049C25D /* env.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 */,
|
||||
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 = (
|
||||
9C7A552F1DCD65820049C25D /* util.cpp in Sources */,
|
||||
9C7A55281DCD65540049C25D /* builtin_commandline.cpp in Sources */,
|
||||
9C7A55291DCD65540049C25D /* builtin_complete.cpp in Sources */,
|
||||
9C7A552A1DCD65540049C25D /* builtin_jobs.cpp in Sources */,
|
||||
9C7A552B1DCD65540049C25D /* builtin_set.cpp in Sources */,
|
||||
9C7A552C1DCD65540049C25D /* builtin_set_color.cpp in Sources */,
|
||||
9C7A552D1DCD65540049C25D /* builtin_ulimit.cpp in Sources */,
|
||||
9C7A552E1DCD65540049C25D /* builtin_printf.cpp in Sources */,
|
||||
9C7A55271DCD651F0049C25D /* fallback.cpp in Sources */,
|
||||
D00769121990137800CA4627 /* autoload.cpp in Sources */,
|
||||
D00769131990137800CA4627 /* builtin_test.cpp in Sources */,
|
||||
D00769141990137800CA4627 /* color.cpp in Sources */,
|
||||
@@ -1440,6 +1649,21 @@
|
||||
/* 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 */;
|
||||
@@ -1503,6 +1727,42 @@
|
||||
/* 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;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = YES_THIN;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
9C7A55711DCD71330049C25D /* Release_C++11 */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = YES_THIN;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = "Release_C++11";
|
||||
};
|
||||
D007693F1990137800CA4627 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@@ -1511,6 +1771,7 @@
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = NO;
|
||||
PRODUCT_NAME = fish_tests;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1523,6 +1784,7 @@
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = YES_THIN;
|
||||
PRODUCT_NAME = fish_tests;
|
||||
};
|
||||
name = Release;
|
||||
@@ -1535,6 +1797,7 @@
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = YES_THIN;
|
||||
PRODUCT_NAME = fish_tests;
|
||||
};
|
||||
name = "Release_C++11";
|
||||
@@ -1619,6 +1882,7 @@
|
||||
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;
|
||||
@@ -1632,6 +1896,7 @@
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = YES_THIN;
|
||||
PRODUCT_NAME = fish;
|
||||
};
|
||||
name = "Release_C++11";
|
||||
@@ -1644,6 +1909,7 @@
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = YES_THIN;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = "Release_C++11";
|
||||
@@ -1694,6 +1960,7 @@
|
||||
);
|
||||
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/";
|
||||
@@ -1719,6 +1986,7 @@
|
||||
);
|
||||
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/";
|
||||
@@ -1744,6 +2012,7 @@
|
||||
);
|
||||
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/";
|
||||
@@ -1900,6 +2169,7 @@
|
||||
);
|
||||
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;
|
||||
@@ -1917,6 +2187,7 @@
|
||||
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;
|
||||
@@ -1935,6 +2206,7 @@
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = NO;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1947,6 +2219,7 @@
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = YES_THIN;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
@@ -1958,6 +2231,7 @@
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = NO;
|
||||
PRODUCT_NAME = fish;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1969,6 +2243,7 @@
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
LLVM_LTO = YES_THIN;
|
||||
PRODUCT_NAME = fish;
|
||||
};
|
||||
name = Release;
|
||||
@@ -1996,6 +2271,16 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
9C7A556E1DCD71330049C25D /* Build configuration list for PBXNativeTarget "fish_key_reader" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
9C7A556F1DCD71330049C25D /* Debug */,
|
||||
9C7A55701DCD71330049C25D /* Release */,
|
||||
9C7A55711DCD71330049C25D /* Release_C++11 */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D007693E1990137800CA4627 /* Build configuration list for PBXNativeTarget "fish_tests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
||||
@@ -18,11 +18,11 @@ static void die(const char *format, ...) {
|
||||
vfprintf(stderr, format, ap);
|
||||
va_end(ap);
|
||||
fputc('\n', stderr);
|
||||
|
||||
|
||||
if (s_command_path[0] != '\0') {
|
||||
unlink(s_command_path);
|
||||
}
|
||||
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -31,14 +31,14 @@ static void launch_fish_with_applescript(NSString *fish_binary_path)
|
||||
// load the script from a resource by fetching its URL from within our bundle
|
||||
NSString *path = [[NSBundle mainBundle] pathForResource:@"launch_fish" ofType:@"scpt"];
|
||||
if (! path) die("Couldn't get path to launch_fish.scpt");
|
||||
|
||||
|
||||
NSURL *url = [NSURL fileURLWithPath:path isDirectory:NO];
|
||||
if (! url) die("Couldn't get URL to launch_fish.scpt");
|
||||
|
||||
|
||||
NSDictionary *errors = nil;
|
||||
NSAppleScript *appleScript = [[NSAppleScript alloc] initWithContentsOfURL:url error:&errors];
|
||||
if (! appleScript) die("Couldn't load AppleScript");
|
||||
|
||||
|
||||
// create the first parameter
|
||||
NSAppleEventDescriptor *firstParameter =
|
||||
[NSAppleEventDescriptor descriptorWithString:fish_binary_path];
|
||||
@@ -84,16 +84,17 @@ static void launch_fish_with_applescript(NSString *fish_binary_path)
|
||||
|
||||
/* This approach asks Terminal to open a script that we control */
|
||||
int main(void) {
|
||||
|
||||
|
||||
@autoreleasepool {
|
||||
/* Get the fish executable. Make sure it's absolute. */
|
||||
NSURL *fish_executable = [[NSBundle mainBundle] URLForResource:@"fish" withExtension:@"" subdirectory:@"base/bin"];
|
||||
NSURL *fish_executable = [[NSBundle mainBundle] URLForResource:@"fish" withExtension:@""
|
||||
subdirectory:@"base/bin"];
|
||||
if (! fish_executable)
|
||||
die("Could not find fish executable in bundle");
|
||||
|
||||
|
||||
launch_fish_with_applescript([fish_executable path]);
|
||||
}
|
||||
|
||||
|
||||
/* If we succeeded, it will clean itself up */
|
||||
return 0;
|
||||
}
|
||||
|
||||
11
share/completions/caffeinate.fish
Normal file
11
share/completions/caffeinate.fish
Normal file
@@ -0,0 +1,11 @@
|
||||
# completion for caffeinate (macOS)
|
||||
|
||||
complete -c caffeinate -s d -f -d 'Create an assertion to prevent the display from sleeping'
|
||||
complete -c caffeinate -s i -f -d 'Create an assertion to prevent the system from idle sleeping'
|
||||
complete -c caffeinate -s m -f -d 'Create an assertion to prevent the disk from idle sleeping'
|
||||
complete -c caffeinate -s s -f -d 'Create an assertion to prevent the system from sleeping (AC power)'
|
||||
complete -c caffeinate -s u -f -d 'Create an assertion to declare that user is active'
|
||||
complete -c caffeinate -s t -x -a '10 60 300 600 1800 3600' -d 'Specifies the timeout value in seconds'
|
||||
complete -c caffeinate -s w -x -a '(__fish_complete_pids)' -d 'Waits for the process with the specified PID to exit'
|
||||
|
||||
complete -c caffeinate -x -a '(__fish_complete_subcommand)'
|
||||
33
share/completions/defaults.fish
Normal file
33
share/completions/defaults.fish
Normal file
@@ -0,0 +1,33 @@
|
||||
# completion for defaults (macOS)
|
||||
|
||||
function __fish_defaults_domains
|
||||
defaults domains | string split ", "
|
||||
end
|
||||
|
||||
complete -f -c defaults -o 'currentHost' -d 'Restricts preferences operations to the current logged-in host'
|
||||
complete -f -c defaults -o 'host' -d 'Restricts preferences operations to hostname'
|
||||
|
||||
# read
|
||||
complete -f -c defaults -n '__fish_use_subcommand' -a read -d 'Shows defaults entire given domain'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from read read-type write rename delete' -a '(__fish_defaults_domains)'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from read read-type write rename delete' -o 'app'
|
||||
|
||||
# write
|
||||
complete -f -c defaults -n '__fish_use_subcommand' -a write -d 'Writes domain or or a key in the domain'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from write' -o 'string' -d 'String as the value for the given key'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from write' -o 'data' -d 'Raw data bytes for given key'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from write' -o 'int' -d 'Integer as the value for the given key'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from write' -o 'float' -d 'Floating point number as the value for the given key'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from write' -o 'bool' -d 'Boolean as the value for the given key'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from write' -o 'date' -d 'Date as the value for the given key'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from write' -o 'array' -d 'Array as the value for the given key'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from write' -o 'array-add' -d 'Add new elements to the end of an array'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from write' -o 'dict' -d 'Add a dictionary to domain'
|
||||
complete -f -c defaults -n '__fish_seen_subcommand_from write' -o 'dict-add' -d 'Add new key/value pairs to a dictionary'
|
||||
|
||||
complete -f -c defaults -n '__fish_use_subcommand' -a read-type -d 'Shows the type for the given domain, key'
|
||||
complete -f -c defaults -n '__fish_use_subcommand' -a rename -d 'Renames old_key to new_key'
|
||||
complete -f -c defaults -n '__fish_use_subcommand' -a delete -d 'Deletes domain or a key in the domain'
|
||||
complete -f -c defaults -n '__fish_use_subcommand' -a domains -d 'Prints the names of all domains in the users defaults system'
|
||||
complete -f -c defaults -n '__fish_use_subcommand' -a find -d 'Searches for word in domain names, keys, and values'
|
||||
complete -f -c defaults -n '__fish_use_subcommand' -a help -d 'Prints a list of possible command formats'
|
||||
64
share/completions/dig.fish
Normal file
64
share/completions/dig.fish
Normal file
@@ -0,0 +1,64 @@
|
||||
# completion for dig
|
||||
|
||||
function __fish_complete_dig
|
||||
set -l token (commandline -ct)
|
||||
switch $token
|
||||
case '+tries=*' '+retry=*' '+time=*' '+bufsize=*' '+ndots=*' '+edns=*'
|
||||
printf '%s\n' $token(seq 0 50)
|
||||
end
|
||||
end
|
||||
|
||||
complete -f -c dig -s 4 -d 'Use IPv4 query transport only'
|
||||
complete -f -c dig -s 6 -d 'Use IPv6 query transport only'
|
||||
complete -f -c dig -s m -d 'Enable memory usage debugging'
|
||||
complete -c dig -s b -x -d 'Bind to source address/port'
|
||||
complete -c dig -s f -r -d 'Specify batch mode file'
|
||||
complete -c dig -s c -x -a 'IN CH HS QCLASS' -d 'Specify query class'
|
||||
complete -c dig -s k -r -d 'Specify TSIG key file'
|
||||
complete -c dig -s y -x -d 'specify named base64 TSIG key'
|
||||
complete -c dig -s p -x -d 'Specify port number'
|
||||
complete -c dig -s q -x -d 'Specify query name'
|
||||
complete -c dig -s t -x -a 'A AAAA AFSDB APL CAA CDNSKEY CDS CERT CNAME DHCID DLV DNAME DNSKEY DS HIP IPSECKEY KEY KX LOC MX NAPTR NS NSEC NSEC3 NSEC3PARAM PTR RRSIG RP SIG SOA SRV SSHFP TA TKEY TLSA TSIG TXT URI' -d 'Specify query type'
|
||||
complete -c dig -s x -x -d 'Reverse lookup'
|
||||
complete -f -c dig -s h -d 'Print help and exit'
|
||||
complete -f -c dig -s v -d 'Print version and exit'
|
||||
|
||||
complete -f -c dig -a '+vc +novc' -d 'TCP mode'
|
||||
complete -f -c dig -a '+tcp +notcp' -d 'TCP mode, alternate syntax'
|
||||
complete -f -c dig -a '+search +nosearch' -d 'Set whether to use searchlist'
|
||||
complete -f -c dig -a '+showsearch +noshowsearch' -d 'Search with intermediate results'
|
||||
complete -f -c dig -a '+defname +nodefname' -d 'Deprecated, treated as a synonym for +[no]search'
|
||||
complete -f -c dig -a '+recurse +norecurse' -d 'Recursive mode'
|
||||
complete -f -c dig -a '+ignore +noignore' -d 'Dont revert to TCP for TC responses.'
|
||||
complete -f -c dig -a '+fail +nofail' -d 'Dont try next server on SERVFAIL'
|
||||
complete -f -c dig -a '+besteffort +nobesteffort' -d 'Try to parse even illegal messages'
|
||||
complete -f -c dig -a '+aaonly +noaaonly' -d 'Set AA flag in query (+[no]aaflag)'
|
||||
complete -f -c dig -a '+adflag +noadflag' -d 'Set AD flag in query'
|
||||
complete -f -c dig -a '+cdflag +nocdflag' -d 'Set CD flag in query'
|
||||
complete -f -c dig -a '+cl +nocl' -d 'Control display of class in records'
|
||||
complete -f -c dig -a '+cmd +nocmd' -d 'Control display of command line'
|
||||
complete -f -c dig -a '+comments +nocomments' -d 'Control display of comment lines'
|
||||
complete -f -c dig -a '+question +noquestion' -d 'Control display of question'
|
||||
complete -f -c dig -a '+answer +noanswer' -d 'Control display of answer'
|
||||
complete -f -c dig -a '+authority +noauthority' -d 'Control display of authority'
|
||||
complete -f -c dig -a '+additional +noadditional' -d 'Control display of additional'
|
||||
complete -f -c dig -a '+stats +nostats' -d 'Control display of statistics'
|
||||
complete -f -c dig -a '+short +noshort' -d 'Disable everything except short form of answer'
|
||||
complete -f -c dig -a '+ttlid +nottlid' -d 'Control display of ttls in records'
|
||||
complete -f -c dig -a '+all +noall' -d 'Set or clear all display flags'
|
||||
complete -f -c dig -a '+qr +noqr' -d 'Print question before sending'
|
||||
complete -f -c dig -a '+nssearch +nonssearch' -d 'Search all authoritative nameservers'
|
||||
complete -f -c dig -a '+identify +noidentify' -d 'ID responders in short answers'
|
||||
complete -f -c dig -a '+trace +notrace' -d 'Trace delegation down from root'
|
||||
complete -f -c dig -a '+dnssec +nodnssec' -d 'Request DNSSEC records'
|
||||
complete -f -c dig -a '+nsid +nonsid' -d 'Request Name Server ID'
|
||||
complete -f -c dig -a '+multiline +nomultiline' -d 'Print records in an expanded format'
|
||||
complete -f -c dig -a '+onesoa +noonesoa' -d 'AXFR prints only one soa record'
|
||||
|
||||
complete -f -c dig -a '+tries=' -d 'Set number of UDP attempts'
|
||||
complete -f -c dig -a '+retry=' -d 'Set number of UDP retries'
|
||||
complete -f -c dig -a '+time=' -d 'Set query timeout'
|
||||
complete -f -c dig -a '+bufsize=' -d 'Set EDNS0 Max UDP packet size'
|
||||
complete -f -c dig -a '+ndots=' -d 'Set NDOTS value'
|
||||
complete -f -c dig -a '+edns=' -d 'Set EDNS version'
|
||||
complete -c dig -a '(__fish_complete_dig)'
|
||||
@@ -1,4 +1,4 @@
|
||||
#completion for diskutil
|
||||
# completion for diskutil (macOS)
|
||||
|
||||
function __fish_diskutil_devices
|
||||
set -l mountpoints /dev/disk*; printf '%s\n' $mountpoints
|
||||
@@ -22,6 +22,10 @@ complete -f -c diskutil -n '__fish_seen_subcommand_from info' -o 'all' -d 'Proce
|
||||
# activity
|
||||
complete -f -c diskutil -n '__fish_use_subcommand' -a activity -d 'Continuously display system-wide disk manipulation activity'
|
||||
|
||||
# listFilesystems
|
||||
complete -f -c diskutil -n '__fish_use_subcommand' -a listFilesystems -d 'Show the file system personalities available'
|
||||
complete -f -c diskutil -n '__fish_seen_subcommand_from listFilesystems' -o 'plist' -d 'Return a property list'
|
||||
|
||||
# umount
|
||||
complete -f -c diskutil -n '__fish_use_subcommand' -a umount -d 'Unmount a single volume'
|
||||
complete -f -c diskutil -n '__fish_seen_subcommand_from umount' -a '(__fish_diskutil_mounted_volumes)'
|
||||
|
||||
14
share/completions/dpkg-reconfigure.fish
Normal file
14
share/completions/dpkg-reconfigure.fish
Normal file
@@ -0,0 +1,14 @@
|
||||
# Completions for the `dpkg-reconfigure` command
|
||||
|
||||
complete -f -c dpkg-reconfigure -a '(__fish_print_packages)' --description 'Package'
|
||||
|
||||
# Support flags
|
||||
complete -x -f -c dpkg-reconfigure -s h -l help --description 'Display help'
|
||||
|
||||
# General options
|
||||
complete -f -c dpkg-reconfigure -s f -l frontend -r -a "dialog readline noninteractive gnome kde editor web" --description 'Set configuration frontend'
|
||||
complete -f -c dpkg-reconfigure -s p -l priority -r -a "low medium high critical" --description 'Set priority threshold'
|
||||
complete -f -c dpkg-reconfigure -l default-priority --description 'Use default priority threshold'
|
||||
complete -f -c dpkg-reconfigure -s u -l unseen-only --description 'Show only unseen question'
|
||||
complete -f -c dpkg-reconfigure -l force --description 'Reconfigure also inconsistent packages'
|
||||
complete -f -c dpkg-reconfigure -l no-reload --description 'Prevent reloading templates'
|
||||
12
share/completions/mddiagnose.fish
Normal file
12
share/completions/mddiagnose.fish
Normal file
@@ -0,0 +1,12 @@
|
||||
# completion for mddiagnose (macOS)
|
||||
|
||||
complete -c mddiagnose -s h -f -d 'Display help'
|
||||
complete -c mddiagnose -s d -f -d 'Ignore unknown options'
|
||||
complete -c mddiagnose -s n -f -d 'Do not reveal the resulting package in the Finder'
|
||||
complete -c mddiagnose -s r -f -d 'Avoid restricted operations such as heap'
|
||||
complete -c mddiagnose -s s -f -d 'Skip gathering system.log'
|
||||
complete -c mddiagnose -s v -f -d 'Prints version of mddiagnose'
|
||||
complete -c mddiagnose -s m -f -d 'Minimal report'
|
||||
complete -c mddiagnose -s e -r -d 'Evalute indexing information for path'
|
||||
complete -c mddiagnose -s p -r -d 'Evalute permissions information for path'
|
||||
complete -c mddiagnose -s f -r -d 'Write the diagnostic to the specified path'
|
||||
12
share/completions/mdfind.fish
Normal file
12
share/completions/mdfind.fish
Normal file
@@ -0,0 +1,12 @@
|
||||
# completion for mdfind (macOS)
|
||||
|
||||
complete -c mdfind -o attr -x -d 'Fetches the value of the specified attribute'
|
||||
complete -c mdfind -o count -f -d 'Query only reports matching items count'
|
||||
complete -c mdfind -o onlyin -x -a '(__fish_complete_directories (commandline -ct))' -d 'Search only within given directory'
|
||||
complete -c mdfind -o live -f -d 'Query should stay active'
|
||||
complete -c mdfind -o name -x -d 'Search on file name only'
|
||||
complete -c mdfind -o reprint -f -d 'Reprint results on live update'
|
||||
complete -c mdfind -s s -x -d 'Show contents of smart folder'
|
||||
complete -c mdfind -s 0 -f -d 'Use NUL (\0) as a path separator, for use with xargs -0'
|
||||
complete -c mdfind -o literal -f -d 'Force the provided query string to be taken as a literal'
|
||||
complete -c mdfind -o interpret -f -d 'Interprete query string as Spotlight query'
|
||||
13
share/completions/mdimport.fish
Normal file
13
share/completions/mdimport.fish
Normal file
@@ -0,0 +1,13 @@
|
||||
# completion for mdimport (macOS)
|
||||
|
||||
complete -c mdimport -s g -r -d 'Import files using the listed plugin'
|
||||
complete -c mdimport -s V -f -d 'Print timing information for this run'
|
||||
complete -c mdimport -s A -f -d 'Print out the list of all of the attributes and exit'
|
||||
complete -c mdimport -s X -f -d 'Print out the schema file and exit'
|
||||
complete -c mdimport -s r -f -d 'Ask the server to reimport files for UTIs claimed by the listed plugin'
|
||||
complete -c mdimport -s p -f -d 'Print out performance information gathered during the run'
|
||||
complete -c mdimport -s L -f -d 'Print the list of installed importers and exit'
|
||||
complete -c mdimport -s d -x -a '1 2 3 4' -d 'Print debugging information'
|
||||
complete -c mdimport -s n -f -d 'Dont send the imported attributes to the data store'
|
||||
complete -c mdimport -s w -x -d 'Wait for the specified interval between scanning files'
|
||||
complete -c mdimport -s o -r -d 'Write the imported attributes to a file'
|
||||
6
share/completions/mdls.fish
Normal file
6
share/completions/mdls.fish
Normal file
@@ -0,0 +1,6 @@
|
||||
# completion for mdls (macOS)
|
||||
|
||||
complete -c mdls -s n -o name -x -d 'Print only the matching metadata attribute value'
|
||||
complete -c mdls -s r -o raw -f -d 'Print raw attribute data'
|
||||
complete -c mdls -n '__fish_seen_subcommand_from -raw -r' -o nullMarker -x -d 'Sets a marker string to be used when a requested attribute is null'
|
||||
complete -c mdls -s p -o plist -r -d 'Output attributes in XML format to file'
|
||||
24
share/completions/mdutil.fish
Normal file
24
share/completions/mdutil.fish
Normal file
@@ -0,0 +1,24 @@
|
||||
# completion for mdutil (macOS)
|
||||
|
||||
function __fish_mdutil_volumes
|
||||
command mdutil -a -s | while read -l line
|
||||
if string match -q \t"*" -- $line
|
||||
printf "%s\n" $line
|
||||
else
|
||||
# Use printf to not output a newline so indented lines are joined
|
||||
# to non-indented ones
|
||||
printf "%s" (string replace -r ':$' '' -- $line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
complete -c mdutil -s p -f -d 'Publish metadata'
|
||||
complete -c mdutil -s i -f -a 'on off' -d 'Turn indexing on or off'
|
||||
complete -c mdutil -s d -f -d 'Disable Spotlight activity for volume'
|
||||
complete -c mdutil -s E -f -d 'Erase and rebuild index'
|
||||
complete -c mdutil -s s -f -d 'Print indexing status'
|
||||
complete -c mdutil -s t -x -a '(__fish_mdutil_volumes)' -d 'Resolve files from file id with an optional volume path or device id'
|
||||
complete -c mdutil -s a -f -d 'Apply command to all volumes'
|
||||
complete -c mdutil -s V -x -a '(__fish_mdutil_volumes)' -d 'Apply command to all stores on the specified volume'
|
||||
complete -c mdutil -s v -f -d 'Display verbose information'
|
||||
complete -c mdutil -x -a '(__fish_mdutil_volumes)'
|
||||
12
share/completions/nvram.fish
Normal file
12
share/completions/nvram.fish
Normal file
@@ -0,0 +1,12 @@
|
||||
# completion for nvram (macOS)
|
||||
|
||||
function __fish_nvram_variables
|
||||
command nvram -p
|
||||
end
|
||||
|
||||
complete -c nvram -s x -f -d 'Use XML format for reading and writing variables'
|
||||
complete -c nvram -s p -f -d 'Print all of the firmware variables'
|
||||
complete -c nvram -s f -r -d 'Set firmware variables from a text file'
|
||||
complete -c nvram -s d -x -a '(__fish_nvram_variables)' -d 'Deletes the named firmware variable'
|
||||
complete -c nvram -s c -f -d 'Delete all of the firmware variable'
|
||||
complete -c nvram -x -a '(__fish_nvram_variables)'
|
||||
@@ -1,14 +1,25 @@
|
||||
# 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-command-substitution is-no-job-control is-interactive-job-control is-full-job-control current-filename current-line-number print-stack-trace job-control
|
||||
|
||||
# These are the recognized flags.
|
||||
complete -c status -s h -l help --description "Display help and exit"
|
||||
complete -c status -l is-command-substitution --description "Test if a command substitution is currently evaluated"
|
||||
complete -c status -l is-block --description "Test if a code block is currently evaluated"
|
||||
complete -c status -l is-interactive --description "Test if this is an interactive shell"
|
||||
complete -c status -l is-login --description "Test if this is a login shell"
|
||||
complete -c status -l is-full-job-control --description "Test if all new jobs are put under job control"
|
||||
complete -c status -l is-interactive-job-control --description "Test if only interactive new jobs are put under job control"
|
||||
complete -c status -l is-no-job-control --description "Test if new jobs are never put under job control"
|
||||
complete -c status -s j -l job-control -xa "full interactive none" --description "Set which jobs are out under job control"
|
||||
complete -c status -s t -l print-stack-trace --description "Print a list of all function calls leading up to running the current command"
|
||||
complete -c status -s f -l current-filename --description "Print the filename of the currently running script"
|
||||
complete -c status -s n -l current-line-number --description "Print the line number of the currently running script"
|
||||
complete -c status -s t -l print-stack-trace --description "Prints a trace of all function calls on the stack"
|
||||
|
||||
# The "is-something" subcommands.
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-login -d "Test if this is a login shell"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-interactive -d "Test if this is an interactive shell"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-command-substitution -d "Test if a command substitution is currently evaluated"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-block -d "Test if a code block is currently evaluated"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-no-job-control -d "Test if new jobs are never put under job control"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-interactive-job-control -d "Test if only interactive new jobs are put under job control"
|
||||
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-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-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"
|
||||
|
||||
# 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"
|
||||
complete -f -c status -n "__fish_seen_subcommand_from job-control" -a full -d "Set all jobs under job control"
|
||||
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"
|
||||
|
||||
@@ -165,7 +165,7 @@ for cmd in $blame $diff $log $merge
|
||||
_svn_cmpl_ $cmd -l extensions -s x -d 'Ignore changes in amount of whitespace' -xa '-b --ignore-space-change'
|
||||
_svn_cmpl_ $cmd -l extensions -s x -d 'Ignore all whitespace' -xa '-w --ignore-all-space'
|
||||
_svn_cmpl_ $cmd -l extensions -s x -d 'Ignore eol style' -xa '-w --ignore-eol-style'
|
||||
_svn_cmpl_ $cmd -l extensions -s x -d 'Show C function name' -xa '-p --shoe-c-function'
|
||||
_svn_cmpl_ $cmd -l extensions -s x -d 'Show C function name' -xa '-p --show-c-function'
|
||||
|
||||
# Next completion doesn't work, since fish doesn't respect -x key
|
||||
#_svn_cmpl_ $cmd -l extensions -n '__fish_seen_subcommand_from --diff-cmd' -xa '(__fish_complete_svn_diff)'
|
||||
|
||||
111
share/completions/sysbench.fish
Normal file
111
share/completions/sysbench.fish
Normal file
@@ -0,0 +1,111 @@
|
||||
### Auto-complete for sysbench (cross-platform and multi-threaded benchmark tool) ###
|
||||
|
||||
### sub commands specification ###
|
||||
complete -c sysbench -f -a "run\t'Run the test'"
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -a "
|
||||
prepare\t'Prepare and create test file'
|
||||
cleanup\t'Cleanup test files'
|
||||
"
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -a "
|
||||
prepare\t'Prepare test table'
|
||||
cleanup\t'Cleanup test table'
|
||||
"
|
||||
|
||||
### generic long options specification ###
|
||||
complete -c sysbench -x -l num-threads -d 'The total number of worker threads to create (default: 1)'
|
||||
complete -c sysbench -x -l max-requests -d 'Limit for total number of requests. 0 means unlimited (default: 10000)'
|
||||
complete -c sysbench -x -l max-time -d 'Limit for total execution time in seconds. 0 means unlimited (default: 0)'
|
||||
complete -c sysbench -x -l thread-stack-size -d 'Size of stack for each thread (defaut: 32K)'
|
||||
complete -c sysbench -f -l init-rng -d 'Specifies if random numbers generator should be initialized from timer (defaut: off)' -a 'on off'
|
||||
complete -c sysbench -x -l test -d 'Name of the test mode to run(required)' -a "
|
||||
cpu\t'Benchmark cpu by calculating prime numbers'
|
||||
threads\t'Benchmark scheduler performance'
|
||||
mutex\t'Benchmark mutex implementation'
|
||||
fileio\t'Benchmark various file I/O workloads'
|
||||
oltp\t'Benchmark a real database performance'
|
||||
"
|
||||
complete -c sysbench -f -l debug -d 'Print more debug info (default: off)' -a 'on off'
|
||||
complete -c sysbench -f -l validate -d 'Perform validation of test results where possible (default: off)' -a 'on off'
|
||||
complete -c sysbench -l help -d 'Print help on general syntax'
|
||||
complete -c sysbench -l version -d 'Show version of program'
|
||||
complete -c sysbench -x -l percentile -d 'A percentile rank of query execution times to count (default: 95)'
|
||||
complete -c sysbench -f -l batch -d 'Dump current results periodically (default: off)' -a 'on off'
|
||||
complete -c sysbench -x -l batch-delay -d 'Delay between batch dumps in secods (default: 300)'
|
||||
|
||||
### options for test=`cpu` mode ###
|
||||
complete -c sysbench -n "__fish_contains_opt test=cpu" -x -l cpu-max-prime -d 'Calculation of prime numbers up to the specified value'
|
||||
|
||||
### options for test=`threads` mode ###
|
||||
complete -c sysbench -n "__fish_contains_opt test=threads" -x -l thread-yields -d 'Number of lock/yield/unlock loops to execute per each request (default: 1000)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=threads" -x -l thread-locks -d 'Number of mutexes to create (default: 8)'
|
||||
|
||||
### options for test=`mutex` mode ###
|
||||
complete -c sysbench -n "__fish_contains_opt test=mutex" -x -l mutex-num -d 'Number of mutexes to create (default: 4096)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=mutex" -x -l memory-scope -d 'Specifies whether each thread uses a global or local allocation (default:global)' -a "
|
||||
local\t'Allocate memory locally'
|
||||
global\t'Allocate memory globally'
|
||||
"
|
||||
complete -c sysbench -n "__fish_contains_opt test=mutex" -x -l memory-total-size -d 'Total size of data to transfer (default: 100G)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=mutex" -x -l memory-oper -d 'Type of memory operations' -a 'read write'
|
||||
|
||||
### options for test=`fileio` mode ###
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-num -d 'Number of files to create (default: 128)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-block-size -d 'Block size to use in all I/O operations (default: 16K)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-total-size -d 'Total size of files (default: 2G)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-test-mode -d 'Type of workload to produce' -a "
|
||||
seqwr\t'Sequential write'
|
||||
seqrewr\t'Sequential rewrite'
|
||||
seqrd\t'Sequential read'
|
||||
rndrd\t'Random read'
|
||||
rndwr\t'Random write'
|
||||
rndrw\t'Random read/write'
|
||||
"
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-io-mode -d 'I/O mode (default: sync)' -a 'sync async fastmmap slowmmap'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-async-backlog -d 'Number of asynchronous operations to queue per thread (default: 128)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-extra-flags -d 'Additional flags to use with open(2)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-fsync-freq -d 'Do fsync() after this number of requests (default: 0)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -f -l file-fsync-all -d 'Do fsync() after each write operation (default: no)' -a 'yes no'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -f -l file-fsync-end -d 'Do fsync() at the end of the test (default: yes)' -a 'yes no'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-fsync-mode -d 'Method used for synchronization: fsync, fdatasync (default: fsync)' -a 'fsync fdatasync'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-merged-requests -d 'Upper limit of I/O requests merge (default: 0)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=fileio" -x -l file-rw-ratio -d 'reads/writes ratio for combined random read/write test (default: 1.5)'
|
||||
|
||||
### options for test=`oltp` mode ###
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-test-mode -d 'Execution mode: simple, complex and nontrx(non-transactional)(default: complex)' -a "
|
||||
simple\t'Simple'
|
||||
complex\t'Advanced transactional'
|
||||
nontrx\t'Non-transactional'
|
||||
"
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -f -l oltp-read-only -d 'Read-only mode. No UPDATE, DELETE or INSERT queries will be performed. (default: off)' -a 'on off'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-range-size -d 'Range size for range queries (default: 100)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-point-selects -d 'Number of point select queries in a single transaction (default: 10)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-simple-ranges -d 'Number of simple range queries in a single transaction (default: 1)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-sum-ranges -d 'Number of SUM range queries in a single transaction (default: 1)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-order-ranges -d 'Number of ORDER range queries in a single transaction (default: 1)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-distinct-ranges -d 'Number of DISTINCT range queries in a single transaction (default: 1)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-index-updates -d 'Number of index UPDATE queries in a single transaction (default: 1)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-non-index-updates -d 'Number of non-index UPDATE queries in a single transaction (default: 1)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-nontrx-mode -d 'Type of queries for non-transactional execution mode (default: select)' -a 'select update_key update_nokey insert delete'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-connect-delay -d 'Time to sleep(in microseconds) after each connection to database (default: 10000)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-user-delay-min -d 'Minimum time to sleep(in microseconds) after each request (default: 0)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-user-delay-max -d 'Maximum time to sleep(in microseconds) after each request (default: 0)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-table-name -d 'Name of the test table (default: sbtest)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-table-size -d 'Number of rows in the test table (default: 10000)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l oltp-dist-type -d 'Distribution type of random numbers (default: special)' -a "
|
||||
uniform\t'Uniform distribution'
|
||||
gauss\t'Gaussian distribution'
|
||||
special\t'Specified percent of numbers is generated in a specified percent of cases'
|
||||
"
|
||||
complete -c sysbench -n "__fish_contains_opt oltp-dist-type=special" -x -l oltp-dist-pct -d 'Percentage of values to be treated as \'special\'(default: 1)'
|
||||
complete -c sysbench -n "__fish_contains_opt oltp-dist-type=special" -x -l oltp-dist-res -d 'Percentage of cases when \'special\' values are generated (default: 75)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l db-ps-mode -d 'Use "Prepared Statements" API if supported, otherwise - use clientside statements: disable, auto (default: auto)' -a 'disable auto'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l mysql-host -d 'MySQL server host (default: localhost)' -a "(__fish_print_hostnames)"
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l mysql-port -d 'MySQL server port (default: 3306)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -l mysql-socket -d 'Unix socket file to communicate with the MySQL server'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l mysql-user -d 'MySQL user (default: sbtest)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l mysql-password -d 'MySQL password'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l mysql-db -d 'MySQL database name (default: sbtest)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l mysql-table-engine -d 'Type of the test table to use' -a 'myisam innodb heap ndbcluster bdb maria falcon pbxt'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l mysql-ssl -d 'Use SSL connections. (default: no)' -a 'yes no'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -x -l myisam-max-rows -d 'MAX_ROWS option for MyISAM tables (required for big tables) (default: 1000000)'
|
||||
complete -c sysbench -n "__fish_contains_opt test=oltp" -l mysql-create-options -d 'Additional options passed to CREATE TABLE.'
|
||||
56
share/completions/tmutil.fish
Normal file
56
share/completions/tmutil.fish
Normal file
@@ -0,0 +1,56 @@
|
||||
# completion for tmutil (macOS)
|
||||
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a addexclusion -d 'Add an exclusion not to back up a file'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from addexclusion' -s v -d 'Volume exclusion'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from addexclusion' -s p -d 'Path exclusion'
|
||||
complete -r -c tmutil -n '__fish_use_subcommand' -a associatedisk -d 'Bind a snapshot volume directory to the specified local disk'
|
||||
complete -r -c tmutil -n '__fish_use_subcommand' -a calculatedrift -d 'Determine the amount of change between snapshots'
|
||||
complete -r -c tmutil -n '__fish_use_subcommand' -a compare -d 'Perform a backup diff'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s a -d 'Compare all supported metadata'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s n -d 'No metadata comparison'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s @ -d 'Compare extended attributes'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s c -d 'Compare creation times'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s d -d 'Compare file data forks'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s e -d 'Compare ACLs'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s f -d 'Compare file flags'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s g -d 'Compare GIDs'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s m -d 'Compare file modes'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s s -d 'Compare sizes'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s t -d 'Compare modification times'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s u -d 'Compare UIDs'
|
||||
complete -r -c tmutil -n '__fish_seen_subcommand_from compare' -s D -d 'Limit traversal depth to depth levels from the beginning of iteration'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s E -d 'Dont take exclusions into account'
|
||||
complete -r -c tmutil -n '__fish_seen_subcommand_from compare' -s I -d 'Ignore path'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from compare' -s U -d 'Ignore logical volume identity'
|
||||
complete -r -c tmutil -n '__fish_use_subcommand' -a delete -d 'Delete one or more snapshots'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a destinationinfo -d 'Print information about destinations'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a disable -d 'Turn off automatic backups'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a disablelocal -d 'Turn off local Time Machine snapshots'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a enable -d 'Turn on automatic backups'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a enablelocal -d 'Turn on local Time Machine snapshots'
|
||||
complete -r -c tmutil -n '__fish_use_subcommand' -a inheritbackup -d 'Claim a machine directory or sparsebundle for use by the current machine'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a isexcluded -d 'Determine if a file, directory, or volume are excluded from backups'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a latestbackup -d 'Print the path to the latest snapshot'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a listbackups -d 'Print paths for all snapshots'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a machinedirectory -d 'Print the path to the current machine directory'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a removedestination -d 'Removes a backup destination'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a removeexclusion -d 'Remove an exclusion'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from removeexclusion' -s v -d 'Volume exclusion'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from removeexclusion' -s p -d 'Path exclusion'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a restore -d 'Restore an item'
|
||||
complete -r -c tmutil -n '__fish_seen_subcommand_from restore' -s v
|
||||
complete -r -c tmutil -n '__fish_use_subcommand' -a setdestination -d 'Set a backup destination'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from setdestination' -s a -d 'Add to the list of destinations'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from setdestination' -s p -d 'Enter the password at a non-echoing interactive prompt'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a snapshot -d 'Create new local Time Machine snapshot'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a startbackup -d 'Begin a backup if one is not already running'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from startbackup' -s a -l auto -d 'Automatic mode'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from startbackup' -s b -l block -d 'Block until finished'
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from startbackup' -s r -l rotation -d 'Autmatic rotation'
|
||||
complete -r -c tmutil -n '__fish_seen_subcommand_from startbackup' -s d -l destination -d 'Backup destination'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a stopbackup -d 'Cancel a backup currently in progress'
|
||||
complete -r -c tmutil -n '__fish_use_subcommand' -a uniquesize -d 'Analyze the specified path and determine its unique size'
|
||||
complete -r -c tmutil -n '__fish_use_subcommand' -a verifychecksums -d 'Verify snapshot'
|
||||
complete -f -c tmutil -n '__fish_use_subcommand' -a version -d 'Print version'
|
||||
|
||||
complete -f -c tmutil -n '__fish_seen_subcommand_from destinationinfo isexcluded compare' -s X -d 'Print as XML'
|
||||
@@ -220,13 +220,18 @@ function __fish_config_interactive -d "Initializations that should be performed
|
||||
# Notify terminals when $PWD changes (issue #906)
|
||||
# VTE and Terminal.app support this in practice.
|
||||
if test "0$VTE_VERSION" -ge 3405 -o "$TERM_PROGRAM" = "Apple_Terminal"
|
||||
function fish_title; end
|
||||
function __update_cwd_osc --on-variable PWD --description 'Notify capable terminals when $PWD changes'
|
||||
status --is-command-substitution
|
||||
or test -n "$INSIDE_EMACS"
|
||||
and return
|
||||
printf \e\]7\;file://\%s\%s\a (hostname) (echo -n $PWD | __fish_urlencode)
|
||||
end
|
||||
if test "$TERM_PROGRAM" = "Apple_Terminal"
|
||||
# Suppress duplicative title display on Terminal.app
|
||||
echo -n \e\]0\;\a # clear existing title
|
||||
function fish_title
|
||||
end
|
||||
end
|
||||
__update_cwd_osc # Run once because we might have already inherited a PWD from an old tab
|
||||
end
|
||||
|
||||
|
||||
@@ -39,10 +39,11 @@ function __fish_print_help --description "Print help message for the specified f
|
||||
set cols (math $cols - 4) # leave a bit of space on the right
|
||||
set rLL -rLL=$cols[1]n
|
||||
end
|
||||
set -lx GROFF_TMAC_PATH $__fish_datadir/groff
|
||||
if test -e "$__fish_datadir/man/man1/$item.1"
|
||||
set help (nroff -man -c -t $rLL "$__fish_datadir/man/man1/$item.1" ^/dev/null)
|
||||
set help (nroff -c -man -mfish -t $rLL "$__fish_datadir/man/man1/$item.1" ^/dev/null)
|
||||
else if test -e "$__fish_datadir/man/man1/$item.1.gz"
|
||||
set help (gunzip -c "$__fish_datadir/man/man1/$item.1.gz" ^/dev/null | nroff -man -c -t $rLL ^/dev/null)
|
||||
set help (gunzip -c "$__fish_datadir/man/man1/$item.1.gz" ^/dev/null | nroff -c -man -mfish -t $rLL ^/dev/null)
|
||||
end
|
||||
|
||||
# The original implementation trimmed off the top 5 lines and bottom 3 lines
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function alias --description 'Legacy function for creating shellscript functions using an alias-like syntax'
|
||||
function alias --description 'Creates a function wrapping a command'
|
||||
if count $argv > /dev/null
|
||||
switch $argv[1]
|
||||
case -h --h --he --hel --help
|
||||
@@ -14,8 +14,12 @@ function alias --description 'Legacy function for creating shellscript functions
|
||||
switch (count $argv)
|
||||
|
||||
case 0
|
||||
echo "Fish implements aliases using functions. Use 'functions' builtin to see list of functions and 'functions function_name' to see function definition, type 'help alias' for more information."
|
||||
return 1
|
||||
for func in (functions -n)
|
||||
set -l output (functions $func | string match -r -- "function .* --description '(alias .*)'" | string split \n)
|
||||
set -q output[2]
|
||||
and echo $output[2]
|
||||
end
|
||||
return 0
|
||||
case 1
|
||||
set -l tmp (string replace -r "=" '\n' -- $argv) ""
|
||||
set name $tmp[1]
|
||||
@@ -60,5 +64,6 @@ function alias --description 'Legacy function for creating shellscript functions
|
||||
set prefix command
|
||||
end
|
||||
end
|
||||
echo "function $name --wraps $first_word; $prefix $first_word $body \$argv; end" | source
|
||||
set -l cmd_string (string escape "alias $argv")
|
||||
echo "function $name --wraps $first_word --description $cmd_string; $prefix $first_word $body \$argv; end" | source
|
||||
end
|
||||
|
||||
@@ -3,11 +3,6 @@
|
||||
# Set the default prompt command.
|
||||
|
||||
function fish_fallback_prompt --description "A simple fallback prompt without too much color or special characters for linux VTs"
|
||||
# Just calculate this once, to save a few cycles when displaying the prompt
|
||||
if not set -q __fish_prompt_hostname
|
||||
set -g __fish_prompt_hostname (hostname|cut -d . -f 1)
|
||||
end
|
||||
|
||||
set -l color_cwd
|
||||
set -l suffix
|
||||
switch $USER
|
||||
@@ -23,5 +18,5 @@ function fish_fallback_prompt --description "A simple fallback prompt without to
|
||||
set suffix '>'
|
||||
end
|
||||
|
||||
echo -n -s "$USER" @ "$__fish_prompt_hostname" ' ' (set_color $color_cwd) (prompt_pwd) (set_color normal) "$suffix "
|
||||
echo -n -s "$USER" @ (prompt_hostname) ' ' (set_color $color_cwd) (prompt_pwd) (set_color normal) "$suffix "
|
||||
end
|
||||
|
||||
7
share/functions/fish_key_reader.fish
Normal file
7
share/functions/fish_key_reader.fish
Normal file
@@ -0,0 +1,7 @@
|
||||
# check if command fish_key_reader works and is the same version that
|
||||
# came with this fish. This will happen one time.
|
||||
command -s fish_key_reader > /dev/null
|
||||
and command fish_key_reader --version 2>&1 | string match -rq $FISH_VERSION
|
||||
# if alias doesn't define the function here, this is an autoloaded "nothing".
|
||||
# the command (if there is one) will be used by default.
|
||||
or alias fish_key_reader=(string escape $__fish_bin_dir/fish_key_reader)
|
||||
@@ -3,11 +3,6 @@
|
||||
# Set the default prompt command.
|
||||
|
||||
function fish_prompt --description "Write out the prompt"
|
||||
# Just calculate this once, to save a few cycles when displaying the prompt
|
||||
if not set -q __fish_prompt_hostname
|
||||
set -g __fish_prompt_hostname (hostname|cut -d . -f 1)
|
||||
end
|
||||
|
||||
set -l color_cwd
|
||||
set -l suffix
|
||||
switch $USER
|
||||
@@ -23,5 +18,5 @@ function fish_prompt --description "Write out the prompt"
|
||||
set suffix '>'
|
||||
end
|
||||
|
||||
echo -n -s "$USER" @ "$__fish_prompt_hostname" ' ' (set_color $color_cwd) (prompt_pwd) (set_color normal) "$suffix "
|
||||
echo -n -s "$USER" @ (prompt_hostname) ' ' (set_color $color_cwd) (prompt_pwd) (set_color normal) "$suffix "
|
||||
end
|
||||
|
||||
@@ -1,11 +1,46 @@
|
||||
function fish_vi_cursor -d 'Set cursor shape for different vi modes'
|
||||
# Since we read exported variables (KONSOLE_PROFILE_NAME and ITERM_PROFILE)
|
||||
# we need to check harder if we're actually in a supported terminal,
|
||||
# because we might be in a term-in-a-term (emacs ansi-term).
|
||||
if not contains -- $TERM xterm konsole xterm-256color konsole-256color
|
||||
and not set -q TMUX
|
||||
# Check hard if we are in a supporting terminal.
|
||||
#
|
||||
# Challenges here are term-in-a-terms (emacs ansi-term does not support this, tmux does),
|
||||
# that we can only figure out if we are in konsole/iterm/vte via exported variables,
|
||||
# and ancient xterm versions.
|
||||
#
|
||||
# tmux defaults to $TERM = screen, but can do this if it is in a supporting terminal.
|
||||
# Unfortunately, we can only detect this via the exported variables, so we miss some cases.
|
||||
#
|
||||
# We will also miss some cases of terminal-stacking,
|
||||
# e.g. tmux started in suckless' st (no support) started in konsole.
|
||||
# But since tmux in konsole seems rather common and that case so uncommon,
|
||||
# we will just fail there (though it seems that tmux or st swallow it anyway).
|
||||
#
|
||||
# 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 ^/dev/null
|
||||
# Whitelist tmux...
|
||||
and not begin
|
||||
set -q TMUX
|
||||
# ...in a supporting term...
|
||||
and begin set -q KONSOLE_PROFILE_NAME
|
||||
or set -q ITERM_PROFILE
|
||||
or test "$VTE_VERSION" -gt 1910
|
||||
end
|
||||
# .. unless an unsupporting terminal has been started in tmux inside a supporting one
|
||||
and begin string match -q "screen*" -- $TERM
|
||||
or string match -q "tmux*" -- $TERM
|
||||
end
|
||||
end
|
||||
and not string match -q "konsole*" -- $TERM
|
||||
# Blacklist
|
||||
or begin
|
||||
# vte-based terms set $TERM = xterm*, but only gained support relatively recently.
|
||||
set -q VTE_VERSION
|
||||
and test "$VTE_VERSION" -le 1910
|
||||
end
|
||||
or set -q INSIDE_EMACS
|
||||
return
|
||||
end
|
||||
|
||||
set -l terminal $argv[1]
|
||||
set -q terminal[1]
|
||||
or set terminal auto
|
||||
@@ -18,11 +53,9 @@ function fish_vi_cursor -d 'Set cursor shape for different vi modes'
|
||||
or set -q ITERM_PROFILE
|
||||
set function __fish_cursor_konsole
|
||||
set uses_echo 1
|
||||
else if string match -q "xterm*" -- $TERM; or test "$VTE_VERSION" -gt 1910
|
||||
else
|
||||
set function __fish_cursor_xterm
|
||||
set uses_echo 1
|
||||
else
|
||||
return 1
|
||||
end
|
||||
case konsole
|
||||
set function __fish_cursor_konsole
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# application for the file.
|
||||
#
|
||||
|
||||
if not test (uname) = Darwin
|
||||
if not command -s open >/dev/null
|
||||
function open --description "Open file in default application"
|
||||
if count $argv >/dev/null
|
||||
switch $argv[1]
|
||||
|
||||
10
share/functions/prompt_hostname.fish
Normal file
10
share/functions/prompt_hostname.fish
Normal file
@@ -0,0 +1,10 @@
|
||||
# Fetching the host name can be expensive if there is a problem with DNS or whatever subsystem the
|
||||
# hostname command uses. So cache the answer so including it in the prompt doesn't make fish seem
|
||||
# slow.
|
||||
if not set -q __fish_prompt_hostname
|
||||
set -g __fish_prompt_hostname (hostname | string split '.')[1]
|
||||
end
|
||||
|
||||
function prompt_hostname
|
||||
echo $__fish_prompt_hostname
|
||||
end
|
||||
@@ -1,4 +1,8 @@
|
||||
function suspend -d "Suspend the current shell."
|
||||
if contains -- $argv --help; or contains -- $argv -h
|
||||
__fish_print_help suspend
|
||||
and return 0
|
||||
end
|
||||
if begin contains -- $argv --force
|
||||
or not status --is-interactive and not status --is-login
|
||||
end
|
||||
|
||||
14
share/groff/fish.tmac
Normal file
14
share/groff/fish.tmac
Normal file
@@ -0,0 +1,14 @@
|
||||
.\" This is needed on systems that ship with groff versions older than 1.20;
|
||||
.\" such as macOS up thru Sierra (10.12). See fish issue #2673.
|
||||
.\"
|
||||
.\" For UTF-8, map some characters conservatively for the sake
|
||||
.\" of easy cut and paste.
|
||||
.
|
||||
.if '\*[.T]'utf8' \{\
|
||||
. rchar \- - ' `
|
||||
.
|
||||
. char \- \N'45'
|
||||
. char - \N'45'
|
||||
. char ' \N'39'
|
||||
. char ` \N'96'
|
||||
.\}
|
||||
@@ -12,8 +12,8 @@ function fish_prompt -d "Write out the prompt"
|
||||
if [ (_git_branch_name) ]
|
||||
set -l git_branch (set_color -o blue)(_git_branch_name)
|
||||
if [ (_is_git_dirty) ]
|
||||
for i in (git branch -qv --no-color| string match -r \*|cut -d' ' -f4-|cut -d] -f1|tr , \n)\
|
||||
(git status --porcelain | cut -c 1-2 | uniq)
|
||||
for i in (git branch -qv --no-color | string match -r \* | cut -d' ' -f4- | cut -d] -f1 | tr , \n)\
|
||||
(git status --porcelain | cut -c 1-2 | uniq)
|
||||
switch $i
|
||||
case "*[ahead *"
|
||||
set git_status "$git_status"(set_color red)⬆
|
||||
@@ -39,29 +39,10 @@ function fish_prompt -d "Write out the prompt"
|
||||
set git_info "(git$git_status$git_branch"(set_color white)")"
|
||||
end
|
||||
set_color -b black
|
||||
printf '%s%s%s%s%s%s%s%s%s%s%s%s%s'\
|
||||
(set_color -o white) \
|
||||
'❰' \
|
||||
(set_color green) \
|
||||
$USER \
|
||||
(set_color white) \
|
||||
'❙' \
|
||||
(set_color yellow) \
|
||||
(echo $PWD | sed -e "s|^$HOME|~|") \
|
||||
(set_color white) \
|
||||
$git_info \
|
||||
(set_color white) \
|
||||
'❱' \
|
||||
(set_color white)
|
||||
printf '%s%s%s%s%s%s%s%s%s%s%s%s%s' (set_color -o white) '❰' (set_color green) $USER (set_color white) '❙' (set_color yellow) (echo $PWD | sed -e "s|^$HOME|~|") (set_color white) $git_info (set_color white) '❱' (set_color white)
|
||||
if test $laststatus -eq 0
|
||||
printf "%s✔%s≻%s " \
|
||||
(set_color -o green)\
|
||||
(set_color white) \
|
||||
(set_color normal)
|
||||
printf "%s✔%s≻%s " (set_color -o green) (set_color white) (set_color normal)
|
||||
else
|
||||
printf "%s✘%s≻%s " \
|
||||
(set_color -o red) \
|
||||
(set_color white) \
|
||||
(set_color normal)
|
||||
printf "%s✘%s≻%s " (set_color -o red) (set_color white) (set_color normal)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
# name: Classic
|
||||
function fish_prompt --description "Write out the prompt"
|
||||
# Just calculate this once, to save a few cycles when displaying the prompt
|
||||
if not set -q __fish_prompt_hostname
|
||||
set -g __fish_prompt_hostname (hostname|cut -d . -f 1)
|
||||
end
|
||||
set -l color_cwd
|
||||
set -l suffix
|
||||
switch $USER
|
||||
case root toor
|
||||
if set -q fish_color_cwd_root
|
||||
set color_cwd $fish_color_cwd_root
|
||||
else
|
||||
set color_cwd $fish_color_cwd
|
||||
end
|
||||
set suffix '#'
|
||||
case '*'
|
||||
set color_cwd $fish_color_cwd
|
||||
set suffix '>'
|
||||
end
|
||||
|
||||
set -l color_cwd
|
||||
set -l suffix
|
||||
switch $USER
|
||||
case root toor
|
||||
if set -q fish_color_cwd_root
|
||||
set color_cwd $fish_color_cwd_root
|
||||
else
|
||||
set color_cwd $fish_color_cwd
|
||||
end
|
||||
set suffix '#'
|
||||
case '*'
|
||||
set color_cwd $fish_color_cwd
|
||||
set suffix '>'
|
||||
end
|
||||
|
||||
echo -n -s "$USER" @ "$__fish_prompt_hostname" ' ' (set_color $color_cwd) (prompt_pwd) (set_color normal) "$suffix "
|
||||
echo -n -s "$USER" @ (prompt_hostname) ' ' (set_color $color_cwd) (prompt_pwd) (set_color normal) "$suffix "
|
||||
end
|
||||
|
||||
@@ -10,25 +10,20 @@ function fish_prompt --description "Write out the prompt"
|
||||
printf "%s(%d)%s " (set_color red --bold) $last_status (set_color normal)
|
||||
end
|
||||
|
||||
# Just calculate this once, to save a few cycles when displaying the prompt
|
||||
if not set -q __fish_prompt_hostname
|
||||
set -g __fish_prompt_hostname (hostname|cut -d . -f 1)
|
||||
end
|
||||
|
||||
set -l color_cwd
|
||||
set -l suffix
|
||||
switch $USER
|
||||
case root toor
|
||||
if set -q fish_color_cwd_root
|
||||
set color_cwd $fish_color_cwd_root
|
||||
else
|
||||
case root toor
|
||||
if set -q fish_color_cwd_root
|
||||
set color_cwd $fish_color_cwd_root
|
||||
else
|
||||
set color_cwd $fish_color_cwd
|
||||
end
|
||||
set suffix '#'
|
||||
case '*'
|
||||
set color_cwd $fish_color_cwd
|
||||
end
|
||||
set suffix '#'
|
||||
case '*'
|
||||
set color_cwd $fish_color_cwd
|
||||
set suffix '>'
|
||||
set suffix '>'
|
||||
end
|
||||
|
||||
echo -n -s "$USER" @ "$__fish_prompt_hostname" ' ' (set_color $color_cwd) (prompt_pwd) (set_color normal) "$suffix "
|
||||
echo -n -s "$USER" @ (prompt_hostname) ' ' (set_color $color_cwd) (prompt_pwd) (set_color normal) "$suffix "
|
||||
end
|
||||
|
||||
@@ -3,71 +3,68 @@
|
||||
# vim: set noet:
|
||||
|
||||
function fish_prompt --description 'Write out the prompt'
|
||||
set -l last_status $status
|
||||
set -l last_status $status
|
||||
set -l normal (set_color normal)
|
||||
|
||||
# Just calculate this once, to save a few cycles when displaying the prompt
|
||||
if not set -q __fish_prompt_hostname
|
||||
set -g __fish_prompt_hostname (hostname|cut -d . -f 1)
|
||||
end
|
||||
# Hack; fish_config only copies the fish_prompt function (see #736)
|
||||
if not set -q -g __fish_classic_git_functions_defined
|
||||
set -g __fish_classic_git_functions_defined
|
||||
|
||||
set -l normal (set_color normal)
|
||||
function __fish_repaint_user --on-variable fish_color_user --description "Event handler, repaint when fish_color_user changes"
|
||||
if status --is-interactive
|
||||
commandline -f repaint ^/dev/null
|
||||
end
|
||||
end
|
||||
|
||||
# Hack; fish_config only copies the fish_prompt function (see #736)
|
||||
if not set -q -g __fish_classic_git_functions_defined
|
||||
set -g __fish_classic_git_functions_defined
|
||||
function __fish_repaint_host --on-variable fish_color_host --description "Event handler, repaint when fish_color_host changes"
|
||||
if status --is-interactive
|
||||
commandline -f repaint ^/dev/null
|
||||
end
|
||||
end
|
||||
|
||||
function __fish_repaint_user --on-variable fish_color_user --description "Event handler, repaint when fish_color_user changes"
|
||||
if status --is-interactive
|
||||
commandline -f repaint ^/dev/null
|
||||
end
|
||||
end
|
||||
|
||||
function __fish_repaint_host --on-variable fish_color_host --description "Event handler, repaint when fish_color_host changes"
|
||||
if status --is-interactive
|
||||
commandline -f repaint ^/dev/null
|
||||
end
|
||||
end
|
||||
|
||||
function __fish_repaint_status --on-variable fish_color_status --description "Event handler; repaint when fish_color_status changes"
|
||||
if status --is-interactive
|
||||
commandline -f repaint ^/dev/null
|
||||
end
|
||||
end
|
||||
function __fish_repaint_status --on-variable fish_color_status --description "Event handler; repaint when fish_color_status changes"
|
||||
if status --is-interactive
|
||||
commandline -f repaint ^/dev/null
|
||||
end
|
||||
end
|
||||
|
||||
function __fish_repaint_bind_mode --on-variable fish_key_bindings --description "Event handler; repaint when fish_key_bindings changes"
|
||||
if status --is-interactive
|
||||
commandline -f repaint ^/dev/null
|
||||
end
|
||||
end
|
||||
function __fish_repaint_bind_mode --on-variable fish_key_bindings --description "Event handler; repaint when fish_key_bindings changes"
|
||||
if status --is-interactive
|
||||
commandline -f repaint ^/dev/null
|
||||
end
|
||||
end
|
||||
|
||||
# initialize our new variables
|
||||
if not set -q __fish_classic_git_prompt_initialized
|
||||
set -qU fish_color_user; or set -U fish_color_user -o green
|
||||
set -qU fish_color_host; or set -U fish_color_host -o cyan
|
||||
set -qU fish_color_status; or set -U fish_color_status red
|
||||
set -U __fish_classic_git_prompt_initialized
|
||||
end
|
||||
end
|
||||
# initialize our new variables
|
||||
if not set -q __fish_classic_git_prompt_initialized
|
||||
set -qU fish_color_user
|
||||
or set -U fish_color_user -o green
|
||||
set -qU fish_color_host
|
||||
or set -U fish_color_host -o cyan
|
||||
set -qU fish_color_status
|
||||
or set -U fish_color_status red
|
||||
set -U __fish_classic_git_prompt_initialized
|
||||
end
|
||||
end
|
||||
|
||||
set -l color_cwd
|
||||
set -l prefix
|
||||
switch $USER
|
||||
case root toor
|
||||
if set -q fish_color_cwd_root
|
||||
set color_cwd $fish_color_cwd_root
|
||||
else
|
||||
set color_cwd $fish_color_cwd
|
||||
end
|
||||
set suffix '#'
|
||||
case '*'
|
||||
set color_cwd $fish_color_cwd
|
||||
set suffix '>'
|
||||
end
|
||||
set -l color_cwd
|
||||
set -l prefix
|
||||
switch $USER
|
||||
case root toor
|
||||
if set -q fish_color_cwd_root
|
||||
set color_cwd $fish_color_cwd_root
|
||||
else
|
||||
set color_cwd $fish_color_cwd
|
||||
end
|
||||
set suffix '#'
|
||||
case '*'
|
||||
set color_cwd $fish_color_cwd
|
||||
set suffix '>'
|
||||
end
|
||||
|
||||
set -l prompt_status
|
||||
if test $last_status -ne 0
|
||||
set prompt_status ' ' (set_color $fish_color_status) "[$last_status]" "$normal"
|
||||
end
|
||||
set -l prompt_status
|
||||
if test $last_status -ne 0
|
||||
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) "$__fish_prompt_hostname" $normal ' ' (set_color $color_cwd) (prompt_pwd) $normal (__fish_vcs_prompt) $normal $prompt_status "> "
|
||||
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 "> "
|
||||
end
|
||||
|
||||
@@ -2,54 +2,55 @@
|
||||
# author: Maurizio De Santis
|
||||
|
||||
function fish_prompt --description 'Write out the prompt, prepending the Debian chroot environment if present'
|
||||
if not set -q __fish_prompt_normal
|
||||
set -g __fish_prompt_normal (set_color normal)
|
||||
end
|
||||
|
||||
# Just calculate these once, to save a few cycles when displaying the prompt
|
||||
if not set -q __fish_prompt_hostname
|
||||
set -g __fish_prompt_hostname (hostname|cut -d . -f 1)
|
||||
if not set -q __fish_prompt_chroot_env
|
||||
set -g __fish_prompt_chroot_env (set_color yellow)
|
||||
end
|
||||
|
||||
# Set variable identifying the chroot you work in (used in the prompt below)
|
||||
if begin
|
||||
not set -q debian_chroot
|
||||
and test -r /etc/debian_chroot
|
||||
end
|
||||
|
||||
if not set -q __fish_prompt_normal
|
||||
set -g __fish_prompt_normal (set_color normal)
|
||||
set debian_chroot (cat /etc/debian_chroot)
|
||||
end
|
||||
if begin
|
||||
not set -q __fish_debian_chroot_prompt
|
||||
and set -q debian_chroot
|
||||
and test -n $debian_chroot
|
||||
end
|
||||
set -g __fish_debian_chroot_prompt "($debian_chroot)"
|
||||
end
|
||||
|
||||
if not set -q __fish_prompt_chroot_env
|
||||
set -g __fish_prompt_chroot_env (set_color yellow)
|
||||
end
|
||||
# Prepend the chroot environment if present
|
||||
if set -q __fish_debian_chroot_prompt
|
||||
echo -n -s "$__fish_prompt_chroot_env" "$__fish_debian_chroot_prompt" "$__fish_prompt_normal" ' '
|
||||
end
|
||||
|
||||
# Set variable identifying the chroot you work in (used in the prompt below)
|
||||
if begin; not set -q debian_chroot; and test -r /etc/debian_chroot; end
|
||||
set debian_chroot (cat /etc/debian_chroot)
|
||||
end
|
||||
if begin; not set -q __fish_debian_chroot_prompt; and set -q debian_chroot; and test -n $debian_chroot; end
|
||||
set -g __fish_debian_chroot_prompt "($debian_chroot)"
|
||||
end
|
||||
switch $USER
|
||||
|
||||
# Prepend the chroot environment if present
|
||||
if set -q __fish_debian_chroot_prompt
|
||||
echo -n -s "$__fish_prompt_chroot_env" "$__fish_debian_chroot_prompt" "$__fish_prompt_normal" ' '
|
||||
end
|
||||
case root toor
|
||||
|
||||
switch $USER
|
||||
|
||||
case root toor
|
||||
|
||||
if not set -q __fish_prompt_cwd
|
||||
if set -q fish_color_cwd_root
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd_root)
|
||||
else
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd)
|
||||
end
|
||||
if not set -q __fish_prompt_cwd
|
||||
if set -q fish_color_cwd_root
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd_root)
|
||||
else
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd)
|
||||
end
|
||||
end
|
||||
|
||||
echo -n -s "$USER" @ "$__fish_prompt_hostname" ' ' "$__fish_prompt_cwd" (prompt_pwd) "$__fish_prompt_normal" '# '
|
||||
echo -n -s "$USER" @ (prompt_hostname) ' ' "$__fish_prompt_cwd" (prompt_pwd) "$__fish_prompt_normal" '# '
|
||||
|
||||
case '*'
|
||||
case '*'
|
||||
|
||||
if not set -q __fish_prompt_cwd
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd)
|
||||
end
|
||||
if not set -q __fish_prompt_cwd
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd)
|
||||
end
|
||||
|
||||
echo -n -s "$USER" @ "$__fish_prompt_hostname" ' ' "$__fish_prompt_cwd" (prompt_pwd) "$__fish_prompt_normal" '> '
|
||||
echo -n -s "$USER" @ (prompt_hostname) ' ' "$__fish_prompt_cwd" (prompt_pwd) "$__fish_prompt_normal" '> '
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,46 +6,41 @@ function fish_prompt --description 'Write out the prompt'
|
||||
#Save the return status of the previous command
|
||||
set stat $status
|
||||
|
||||
# Just calculate these once, to save a few cycles when displaying the prompt
|
||||
if not set -q __fish_prompt_hostname
|
||||
set -g __fish_prompt_hostname (hostname|cut -d . -f 1)
|
||||
end
|
||||
|
||||
if not set -q __fish_prompt_normal
|
||||
if not set -q __fish_prompt_normal
|
||||
set -g __fish_prompt_normal (set_color normal)
|
||||
end
|
||||
|
||||
if not set -q __fish_color_blue
|
||||
if not set -q __fish_color_blue
|
||||
set -g __fish_color_blue (set_color -o blue)
|
||||
end
|
||||
|
||||
#Set the color for the status depending on the value
|
||||
#Set the color for the status depending on the value
|
||||
set __fish_color_status (set_color -o green)
|
||||
if test $stat -gt 0
|
||||
set __fish_color_status (set_color -o red)
|
||||
end
|
||||
|
||||
switch $USER
|
||||
switch $USER
|
||||
|
||||
case root toor
|
||||
case root toor
|
||||
|
||||
if not set -q __fish_prompt_cwd
|
||||
if set -q fish_color_cwd_root
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd_root)
|
||||
else
|
||||
if not set -q __fish_prompt_cwd
|
||||
if set -q fish_color_cwd_root
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd_root)
|
||||
else
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd)
|
||||
end
|
||||
end
|
||||
|
||||
printf '%s@%s %s%s%s# ' $USER (prompt_hostname) "$__fish_prompt_cwd" (prompt_pwd) "$__fish_prompt_normal"
|
||||
|
||||
case '*'
|
||||
|
||||
if not set -q __fish_prompt_cwd
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd)
|
||||
end
|
||||
end
|
||||
|
||||
printf '%s@%s %s%s%s# ' $USER $__fish_prompt_hostname "$__fish_prompt_cwd" (prompt_pwd) "$__fish_prompt_normal"
|
||||
|
||||
case '*'
|
||||
|
||||
if not set -q __fish_prompt_cwd
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd)
|
||||
end
|
||||
|
||||
printf '[%s] %s%s@%s %s%s %s(%s)%s \f\r> ' (date "+%H:%M:%S") "$__fish_color_blue" $USER $__fish_prompt_hostname "$__fish_prompt_cwd" "$PWD" "$__fish_color_status" "$stat" "$__fish_prompt_normal"
|
||||
printf '[%s] %s%s@%s %s%s %s(%s)%s \f\r> ' (date "+%H:%M:%S") "$__fish_color_blue" $USER (prompt_hostname) "$__fish_prompt_cwd" "$PWD" "$__fish_color_status" "$stat" "$__fish_prompt_normal"
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,94 +4,94 @@
|
||||
|
||||
|
||||
function fish_prompt --description 'Write out the prompt'
|
||||
if not set -q __fish_git_prompt_show_informative_status
|
||||
set -g __fish_git_prompt_show_informative_status 1
|
||||
end
|
||||
if not set -q __fish_git_prompt_hide_untrackedfiles
|
||||
set -g __fish_git_prompt_hide_untrackedfiles 1
|
||||
end
|
||||
if not set -q __fish_git_prompt_show_informative_status
|
||||
set -g __fish_git_prompt_show_informative_status 1
|
||||
end
|
||||
if not set -q __fish_git_prompt_hide_untrackedfiles
|
||||
set -g __fish_git_prompt_hide_untrackedfiles 1
|
||||
end
|
||||
|
||||
if not set -q __fish_git_prompt_color_branch
|
||||
set -g __fish_git_prompt_color_branch magenta --bold
|
||||
end
|
||||
if not set -q __fish_git_prompt_showupstream
|
||||
set -g __fish_git_prompt_showupstream "informative"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_upstream_ahead
|
||||
set -g __fish_git_prompt_char_upstream_ahead "↑"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_upstream_behind
|
||||
set -g __fish_git_prompt_char_upstream_behind "↓"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_upstream_prefix
|
||||
set -g __fish_git_prompt_char_upstream_prefix ""
|
||||
end
|
||||
if not set -q __fish_git_prompt_color_branch
|
||||
set -g __fish_git_prompt_color_branch magenta --bold
|
||||
end
|
||||
if not set -q __fish_git_prompt_showupstream
|
||||
set -g __fish_git_prompt_showupstream "informative"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_upstream_ahead
|
||||
set -g __fish_git_prompt_char_upstream_ahead "↑"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_upstream_behind
|
||||
set -g __fish_git_prompt_char_upstream_behind "↓"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_upstream_prefix
|
||||
set -g __fish_git_prompt_char_upstream_prefix ""
|
||||
end
|
||||
|
||||
if not set -q __fish_git_prompt_char_stagedstate
|
||||
set -g __fish_git_prompt_char_stagedstate "●"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_dirtystate
|
||||
set -g __fish_git_prompt_char_dirtystate "✚"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_untrackedfiles
|
||||
set -g __fish_git_prompt_char_untrackedfiles "…"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_conflictedstate
|
||||
set -g __fish_git_prompt_char_conflictedstate "✖"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_cleanstate
|
||||
set -g __fish_git_prompt_char_cleanstate "✔"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_stagedstate
|
||||
set -g __fish_git_prompt_char_stagedstate "●"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_dirtystate
|
||||
set -g __fish_git_prompt_char_dirtystate "✚"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_untrackedfiles
|
||||
set -g __fish_git_prompt_char_untrackedfiles "…"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_conflictedstate
|
||||
set -g __fish_git_prompt_char_conflictedstate "✖"
|
||||
end
|
||||
if not set -q __fish_git_prompt_char_cleanstate
|
||||
set -g __fish_git_prompt_char_cleanstate "✔"
|
||||
end
|
||||
|
||||
if not set -q __fish_git_prompt_color_dirtystate
|
||||
set -g __fish_git_prompt_color_dirtystate blue
|
||||
end
|
||||
if not set -q __fish_git_prompt_color_stagedstate
|
||||
set -g __fish_git_prompt_color_stagedstate yellow
|
||||
end
|
||||
if not set -q __fish_git_prompt_color_invalidstate
|
||||
set -g __fish_git_prompt_color_invalidstate red
|
||||
end
|
||||
if not set -q __fish_git_prompt_color_untrackedfiles
|
||||
set -g __fish_git_prompt_color_untrackedfiles $fish_color_normal
|
||||
end
|
||||
if not set -q __fish_git_prompt_color_cleanstate
|
||||
set -g __fish_git_prompt_color_cleanstate green --bold
|
||||
end
|
||||
if not set -q __fish_git_prompt_color_dirtystate
|
||||
set -g __fish_git_prompt_color_dirtystate blue
|
||||
end
|
||||
if not set -q __fish_git_prompt_color_stagedstate
|
||||
set -g __fish_git_prompt_color_stagedstate yellow
|
||||
end
|
||||
if not set -q __fish_git_prompt_color_invalidstate
|
||||
set -g __fish_git_prompt_color_invalidstate red
|
||||
end
|
||||
if not set -q __fish_git_prompt_color_untrackedfiles
|
||||
set -g __fish_git_prompt_color_untrackedfiles $fish_color_normal
|
||||
end
|
||||
if not set -q __fish_git_prompt_color_cleanstate
|
||||
set -g __fish_git_prompt_color_cleanstate green --bold
|
||||
end
|
||||
|
||||
set -l last_status $status
|
||||
set -l last_status $status
|
||||
|
||||
if not set -q __fish_prompt_normal
|
||||
set -g __fish_prompt_normal (set_color normal)
|
||||
end
|
||||
if not set -q __fish_prompt_normal
|
||||
set -g __fish_prompt_normal (set_color normal)
|
||||
end
|
||||
|
||||
set -l color_cwd
|
||||
set -l prefix
|
||||
switch $USER
|
||||
case root toor
|
||||
if set -q fish_color_cwd_root
|
||||
set color_cwd $fish_color_cwd_root
|
||||
else
|
||||
set color_cwd $fish_color_cwd
|
||||
end
|
||||
set suffix '#'
|
||||
case '*'
|
||||
set color_cwd $fish_color_cwd
|
||||
set suffix '$'
|
||||
end
|
||||
set -l color_cwd
|
||||
set -l prefix
|
||||
switch $USER
|
||||
case root toor
|
||||
if set -q fish_color_cwd_root
|
||||
set color_cwd $fish_color_cwd_root
|
||||
else
|
||||
set color_cwd $fish_color_cwd
|
||||
end
|
||||
set suffix '#'
|
||||
case '*'
|
||||
set color_cwd $fish_color_cwd
|
||||
set suffix '$'
|
||||
end
|
||||
|
||||
# PWD
|
||||
set_color $color_cwd
|
||||
echo -n (prompt_pwd)
|
||||
set_color normal
|
||||
# PWD
|
||||
set_color $color_cwd
|
||||
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
|
||||
end
|
||||
if not test $last_status -eq 0
|
||||
set_color $fish_color_error
|
||||
end
|
||||
|
||||
echo -n "$suffix "
|
||||
echo -n "$suffix "
|
||||
|
||||
set_color normal
|
||||
set_color normal
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# name: Just a Dollar
|
||||
function fish_prompt
|
||||
echo -n '$ '
|
||||
echo -n '$ '
|
||||
end
|
||||
|
||||
@@ -2,19 +2,13 @@
|
||||
# author: Steve
|
||||
|
||||
function fish_prompt --description 'Write out the prompt'
|
||||
# Just calculate these once, to save a few cycles when displaying the prompt
|
||||
if not set -q __fish_prompt_hostname
|
||||
set -g __fish_prompt_hostname (hostname -s)
|
||||
end
|
||||
if not set -q __fish_prompt_normal
|
||||
set -g __fish_prompt_normal (set_color normal)
|
||||
end
|
||||
|
||||
if not set -q __fish_prompt_normal
|
||||
set -g __fish_prompt_normal (set_color normal)
|
||||
end
|
||||
|
||||
if not set -q __fish_prompt_cwd
|
||||
set -g __fish_prompt_cwd (set_color $fish_color_cwd)
|
||||
end
|
||||
|
||||
echo -n -s "$USER" @ "$__fish_prompt_hostname" ' ' "$__fish_prompt_cwd" (prompt_pwd) (__fish_vcs_prompt) "$__fish_prompt_normal" '> '
|
||||
if not set -q __fish_prompt_cwd
|
||||
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" '> '
|
||||
end
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
# author: ridiculous_fish
|
||||
|
||||
function fish_prompt
|
||||
set_color $fish_color_cwd
|
||||
echo -n (basename $PWD)
|
||||
set_color normal
|
||||
echo -n ' ) '
|
||||
set_color $fish_color_cwd
|
||||
echo -n (basename $PWD)
|
||||
set_color normal
|
||||
echo -n ' ) '
|
||||
end
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
# author: Guilhem "Nim" Saurel − https://github.com/nim65s/dotfiles/
|
||||
|
||||
function fish_prompt
|
||||
and set retc green; or set retc red
|
||||
tty|string match -q -r tty; and set tty tty; or set tty pts
|
||||
and set retc green
|
||||
or set retc red
|
||||
tty | string match -q -r tty
|
||||
and set tty tty
|
||||
or set tty pts
|
||||
|
||||
set_color $retc
|
||||
if [ $tty = tty ]
|
||||
@@ -26,7 +29,7 @@ function fish_prompt
|
||||
else
|
||||
set_color -o cyan
|
||||
end
|
||||
echo -n (hostname)
|
||||
echo -n (prompt_hostname)
|
||||
set_color -o white
|
||||
#echo -n :(prompt_pwd)
|
||||
echo -n :(pwd|sed "s=$HOME=~=")
|
||||
@@ -46,16 +49,16 @@ function fish_prompt
|
||||
echo -n (date +%X)
|
||||
set_color -o green
|
||||
echo -n ]
|
||||
|
||||
|
||||
if type -q acpi
|
||||
if [ (acpi -a 2> /dev/null | string match -r off) ]
|
||||
echo -n '─['
|
||||
set_color -o red
|
||||
echo -n (acpi -b|cut -d' ' -f 4-)
|
||||
set_color -o green
|
||||
echo -n ']'
|
||||
end
|
||||
end
|
||||
if [ (acpi -a 2> /dev/null | string match -r off) ]
|
||||
echo -n '─['
|
||||
set_color -o red
|
||||
echo -n (acpi -b|cut -d' ' -f 4-)
|
||||
set_color -o green
|
||||
echo -n ']'
|
||||
end
|
||||
end
|
||||
echo
|
||||
set_color normal
|
||||
for job in (jobs)
|
||||
|
||||
@@ -3,28 +3,28 @@
|
||||
|
||||
|
||||
function fish_prompt
|
||||
if not set -q VIRTUAL_ENV_DISABLE_PROMPT
|
||||
set -g VIRTUAL_ENV_DISABLE_PROMPT true
|
||||
end
|
||||
set_color yellow
|
||||
printf '%s' (whoami)
|
||||
set_color normal
|
||||
printf ' at '
|
||||
if not set -q VIRTUAL_ENV_DISABLE_PROMPT
|
||||
set -g VIRTUAL_ENV_DISABLE_PROMPT true
|
||||
end
|
||||
set_color yellow
|
||||
printf '%s' (whoami)
|
||||
set_color normal
|
||||
printf ' at '
|
||||
|
||||
set_color magenta
|
||||
printf '%s' (hostname|cut -d . -f 1)
|
||||
set_color normal
|
||||
printf ' in '
|
||||
set_color magenta
|
||||
echo -n (prompt_hostname)
|
||||
set_color normal
|
||||
printf ' in '
|
||||
|
||||
set_color $fish_color_cwd
|
||||
printf '%s' (prompt_pwd)
|
||||
set_color normal
|
||||
set_color $fish_color_cwd
|
||||
printf '%s' (prompt_pwd)
|
||||
set_color normal
|
||||
|
||||
# Line 2
|
||||
echo
|
||||
if test $VIRTUAL_ENV
|
||||
printf "(%s) " (set_color blue)(basename $VIRTUAL_ENV)(set_color normal)
|
||||
end
|
||||
printf '↪ '
|
||||
set_color normal
|
||||
# Line 2
|
||||
echo
|
||||
if test $VIRTUAL_ENV
|
||||
printf "(%s) " (set_color blue)(basename $VIRTUAL_ENV)(set_color normal)
|
||||
end
|
||||
printf '↪ '
|
||||
set_color normal
|
||||
end
|
||||
|
||||
@@ -3,74 +3,76 @@
|
||||
|
||||
function fish_prompt
|
||||
|
||||
if not set -q -g __fish_robbyrussell_functions_defined
|
||||
set -g __fish_robbyrussell_functions_defined
|
||||
function _git_branch_name
|
||||
echo (git symbolic-ref HEAD ^/dev/null | sed -e 's|^refs/heads/||')
|
||||
if not set -q -g __fish_robbyrussell_functions_defined
|
||||
set -g __fish_robbyrussell_functions_defined
|
||||
function _git_branch_name
|
||||
echo (git symbolic-ref HEAD ^/dev/null | sed -e 's|^refs/heads/||')
|
||||
end
|
||||
|
||||
function _is_git_dirty
|
||||
echo (git status -s --ignore-submodules=dirty ^/dev/null)
|
||||
end
|
||||
|
||||
function _is_git_repo
|
||||
type -q git
|
||||
or return 1
|
||||
git status -s >/dev/null ^/dev/null
|
||||
end
|
||||
|
||||
function _hg_branch_name
|
||||
echo (hg branch ^/dev/null)
|
||||
end
|
||||
|
||||
function _is_hg_dirty
|
||||
echo (hg status -mard ^/dev/null)
|
||||
end
|
||||
|
||||
function _is_hg_repo
|
||||
type -q hg
|
||||
or return 1
|
||||
hg summary >/dev/null ^/dev/null
|
||||
end
|
||||
|
||||
function _repo_branch_name
|
||||
eval "_$argv[1]_branch_name"
|
||||
end
|
||||
|
||||
function _is_repo_dirty
|
||||
eval "_is_$argv[1]_dirty"
|
||||
end
|
||||
|
||||
function _repo_type
|
||||
if _is_hg_repo
|
||||
echo 'hg'
|
||||
else if _is_git_repo
|
||||
echo 'git'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _is_git_dirty
|
||||
echo (git status -s --ignore-submodules=dirty ^/dev/null)
|
||||
set -l cyan (set_color -o cyan)
|
||||
set -l yellow (set_color -o yellow)
|
||||
set -l red (set_color -o red)
|
||||
set -l blue (set_color -o blue)
|
||||
set -l normal (set_color normal)
|
||||
|
||||
set -l arrow "$red➜ "
|
||||
if [ $USER = 'root' ]
|
||||
set arrow "$red# "
|
||||
end
|
||||
|
||||
function _is_git_repo
|
||||
type -q git; or return 1
|
||||
git status -s >/dev/null ^/dev/null
|
||||
set -l cwd $cyan(basename (prompt_pwd))
|
||||
|
||||
set -l repo_type (_repo_type)
|
||||
if [ $repo_type ]
|
||||
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 "$yellow ✗"
|
||||
set repo_info "$repo_info$dirty"
|
||||
end
|
||||
end
|
||||
|
||||
function _hg_branch_name
|
||||
echo (hg branch ^/dev/null)
|
||||
end
|
||||
|
||||
function _is_hg_dirty
|
||||
echo (hg status -mard ^/dev/null)
|
||||
end
|
||||
|
||||
function _is_hg_repo
|
||||
type -q hg; or return 1
|
||||
hg summary >/dev/null ^/dev/null
|
||||
end
|
||||
|
||||
function _repo_branch_name
|
||||
eval "_$argv[1]_branch_name"
|
||||
end
|
||||
|
||||
function _is_repo_dirty
|
||||
eval "_is_$argv[1]_dirty"
|
||||
end
|
||||
|
||||
function _repo_type
|
||||
if _is_hg_repo
|
||||
echo 'hg'
|
||||
else if _is_git_repo
|
||||
echo 'git'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set -l cyan (set_color -o cyan)
|
||||
set -l yellow (set_color -o yellow)
|
||||
set -l red (set_color -o red)
|
||||
set -l blue (set_color -o blue)
|
||||
set -l normal (set_color normal)
|
||||
|
||||
set -l arrow "$red➜ "
|
||||
if [ $USER = 'root' ]
|
||||
set arrow "$red# "
|
||||
end
|
||||
|
||||
set -l cwd $cyan(basename (prompt_pwd))
|
||||
|
||||
set -l repo_type (_repo_type)
|
||||
if [ $repo_type ]
|
||||
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 "$yellow ✗"
|
||||
set repo_info "$repo_info$dirty"
|
||||
end
|
||||
end
|
||||
|
||||
echo -n -s $arrow ' '$cwd $repo_info $normal ' '
|
||||
echo -n -s $arrow ' '$cwd $repo_info $normal ' '
|
||||
end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# name: Screen Savvy
|
||||
# author: Matthias
|
||||
function fish_prompt -d "Write out the prompt"
|
||||
if test -z $WINDOW
|
||||
printf '%s%s@%s%s%s%s%s> ' (set_color yellow) (whoami) (set_color purple) (hostname|cut -d . -f 1) (set_color $fish_color_cwd) (prompt_pwd) (set_color normal)
|
||||
else
|
||||
printf '%s%s@%s%s%s(%s)%s%s%s> ' (set_color yellow) (whoami) (set_color purple) (hostname|cut -d . -f 1) (set_color white) (echo $WINDOW) (set_color $fish_color_cwd) (prompt_pwd) (set_color normal)
|
||||
end
|
||||
if test -z $WINDOW
|
||||
printf '%s%s@%s%s%s%s%s> ' (set_color yellow) (whoami) (set_color purple) (prompt_hostname) (set_color $fish_color_cwd) (prompt_pwd) (set_color normal)
|
||||
else
|
||||
printf '%s%s@%s%s%s(%s)%s%s%s> ' (set_color yellow) (whoami) (set_color purple) (prompt_hostname) (set_color white) (echo $WINDOW) (set_color $fish_color_cwd) (prompt_pwd) (set_color normal)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,52 +2,55 @@
|
||||
# author: Ivan Tham <ivanthamjunhoe@gmail.com>
|
||||
|
||||
function fish_prompt
|
||||
test $SSH_TTY; and printf (set_color red)(whoami)(set_color white)'@'(set_color yellow)(hostname)' '
|
||||
|
||||
test $USER = 'root'; and echo (set_color red)"#"
|
||||
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)"#"
|
||||
|
||||
# Main
|
||||
echo -n (set_color cyan)(prompt_pwd) (set_color red)'❯'(set_color yellow)'❯'(set_color green)'❯ '
|
||||
echo -n (set_color cyan)(prompt_pwd) (set_color red)'❯'(set_color yellow)'❯'(set_color green)'❯ '
|
||||
end
|
||||
|
||||
function fish_right_prompt
|
||||
# last status
|
||||
test $status != 0; and printf (set_color red)"⏎ "
|
||||
test $status != 0
|
||||
and printf (set_color red)"⏎ "
|
||||
|
||||
if git rev-parse ^ /dev/null
|
||||
# Purple if branch detached else green
|
||||
git branch -qv | grep "\*" | grep -q detached
|
||||
and set_color purple --bold
|
||||
or set_color green --bold
|
||||
if git rev-parse ^/dev/null
|
||||
# Magenta if branch detached else green
|
||||
git branch -qv | grep "\*" | string match -rq detached
|
||||
and set_color brmagenta
|
||||
or set_color brgreen
|
||||
|
||||
# Need optimization on this block (eliminate space)
|
||||
git name-rev --name-only HEAD
|
||||
|
||||
# Merging state
|
||||
git merge -q ^ /dev/null; or printf ':'(set_color red)'merge'
|
||||
git merge -q ^/dev/null
|
||||
or printf ':'(set_color red)'merge'
|
||||
printf ' '
|
||||
|
||||
# Symbols
|
||||
for i in (git branch -qv --no-color|grep \*|cut -d' ' -f4-|cut -d] -f1|tr , \n)\
|
||||
(git status --porcelain | cut -c 1-2 | uniq)
|
||||
switch $i
|
||||
for i in (git branch -qv --no-color|grep \*|cut -d' ' -f4-|cut -d] -f1|tr , \n)\
|
||||
(git status --porcelain | cut -c 1-2 | uniq)
|
||||
switch $i
|
||||
case "*[ahead *"
|
||||
printf (set_color purple)⬆' '
|
||||
printf (set_color magenta)⬆' '
|
||||
case "*behind *"
|
||||
printf (set_color purple)⬇' '
|
||||
case "."
|
||||
printf (set_color green)✚' '
|
||||
case " D"
|
||||
printf (set_color red)✖' '
|
||||
case "*M*"
|
||||
printf (set_color blue)✱' '
|
||||
printf (set_color magenta)⬇' '
|
||||
case "."
|
||||
printf (set_color green)✚' '
|
||||
case " D"
|
||||
printf (set_color red)✖' '
|
||||
case "*M*"
|
||||
printf (set_color blue)✱' '
|
||||
case "*R*"
|
||||
printf (set_color purple)➜' '
|
||||
printf (set_color brmagenta)➜' '
|
||||
case "*U*"
|
||||
printf (set_color brown)═' '
|
||||
case "??"
|
||||
printf (set_color white)◼' '
|
||||
end
|
||||
end
|
||||
end
|
||||
printf (set_color bryellow)═' '
|
||||
case "??"
|
||||
printf (set_color brwhite)◼' '
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,35 +2,35 @@
|
||||
# author: terlar - https://github.com/terlar
|
||||
|
||||
function fish_prompt --description 'Write out the prompt'
|
||||
set -l last_status $status
|
||||
set -l last_status $status
|
||||
|
||||
# User
|
||||
set_color $fish_color_user
|
||||
echo -n (whoami)
|
||||
set_color normal
|
||||
# User
|
||||
set_color $fish_color_user
|
||||
echo -n (whoami)
|
||||
set_color normal
|
||||
|
||||
echo -n '@'
|
||||
echo -n '@'
|
||||
|
||||
# Host
|
||||
set_color $fish_color_host
|
||||
echo -n (hostname -s)
|
||||
set_color normal
|
||||
# Host
|
||||
set_color $fish_color_host
|
||||
echo -n (prompt_hostname)
|
||||
set_color normal
|
||||
|
||||
echo -n ':'
|
||||
echo -n ':'
|
||||
|
||||
# PWD
|
||||
set_color $fish_color_cwd
|
||||
echo -n (prompt_pwd)
|
||||
set_color normal
|
||||
# PWD
|
||||
set_color $fish_color_cwd
|
||||
echo -n (prompt_pwd)
|
||||
set_color normal
|
||||
|
||||
__terlar_git_prompt
|
||||
__fish_hg_prompt
|
||||
echo
|
||||
__terlar_git_prompt
|
||||
__fish_hg_prompt
|
||||
echo
|
||||
|
||||
if not test $last_status -eq 0
|
||||
set_color $fish_color_error
|
||||
end
|
||||
if not test $last_status -eq 0
|
||||
set_color $fish_color_error
|
||||
end
|
||||
|
||||
echo -n '➤ '
|
||||
set_color normal
|
||||
echo -n '➤ '
|
||||
set_color normal
|
||||
end
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
# author: Jon Clayden
|
||||
|
||||
function fish_prompt -d "Write out the prompt"
|
||||
set -l home_escaped (echo -n $HOME | sed 's/\//\\\\\//g')
|
||||
set -l pwd (echo -n $PWD | sed "s/^$home_escaped/~/" | sed 's/ /%20/g')
|
||||
set -l prompt_symbol ''
|
||||
switch $USER
|
||||
case root toor; set prompt_symbol '#'
|
||||
case '*'; set prompt_symbol '$'
|
||||
end
|
||||
printf "[%s@%s %s%s%s]%s " $USER (hostname -s) (set_color $fish_color_cwd) $pwd (set_color normal) $prompt_symbol
|
||||
set -l home_escaped (echo -n $HOME | sed 's/\//\\\\\//g')
|
||||
set -l pwd (echo -n $PWD | sed "s/^$home_escaped/~/" | sed 's/ /%20/g')
|
||||
set -l prompt_symbol ''
|
||||
switch $USER
|
||||
case root toor
|
||||
set prompt_symbol '#'
|
||||
case '*'
|
||||
set prompt_symbol '$'
|
||||
end
|
||||
printf "[%s@%s %s%s%s]%s " $USER (prompt_hostname) (set_color $fish_color_cwd) $pwd (set_color normal) $prompt_symbol
|
||||
end
|
||||
|
||||
122
src/autoload.cpp
122
src/autoload.cpp
@@ -157,6 +157,19 @@ autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcs
|
||||
return func;
|
||||
}
|
||||
|
||||
static bool use_cached(autoload_function_t *func, bool really_load, bool allow_stale_functions) {
|
||||
if (!func) {
|
||||
return false; // can't use a function that doesn't exist
|
||||
}
|
||||
if (really_load && !func->is_placeholder && !func->is_loaded) {
|
||||
return false; // can't use an unloaded function
|
||||
}
|
||||
if (!allow_stale_functions && is_stale(func)) {
|
||||
return false; // can't use a stale function
|
||||
}
|
||||
return true; // I guess we can use it
|
||||
}
|
||||
|
||||
/// This internal helper function does all the real work. By using two functions, the internal
|
||||
/// function can return on various places in the code, and the caller can take care of various
|
||||
/// cleanup work.
|
||||
@@ -169,35 +182,21 @@ autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcs
|
||||
bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_load, bool reload,
|
||||
const wcstring_list_t &path_list) {
|
||||
// Note that we are NOT locked in this function!
|
||||
bool reloaded = 0;
|
||||
bool reloaded = false;
|
||||
|
||||
// Try using a cached function. If we really want the function to be loaded, require that it be
|
||||
// really loaded. If we're not reloading, allow stale functions.
|
||||
{
|
||||
bool allow_stale_functions = !reload;
|
||||
|
||||
scoped_lock locker(lock);
|
||||
autoload_function_t *func = this->get_node(cmd); // get the function
|
||||
|
||||
// Determine if we can use this cached function.
|
||||
bool use_cached;
|
||||
if (!func) {
|
||||
// Can't use a function that doesn't exist.
|
||||
use_cached = false;
|
||||
} else if (really_load && !func->is_placeholder && !func->is_loaded) {
|
||||
use_cached = false; // can't use an unloaded function
|
||||
} else if (!allow_stale_functions && is_stale(func)) {
|
||||
use_cached = false; // can't use a stale function
|
||||
} else {
|
||||
use_cached = true; // I guess we can use it
|
||||
}
|
||||
|
||||
// If we can use this function, return whether we were able to access it.
|
||||
if (use_cached) {
|
||||
assert(func != NULL);
|
||||
if (use_cached(func, really_load, allow_stale_functions)) {
|
||||
return func->is_internalized || func->access.accessible;
|
||||
}
|
||||
}
|
||||
|
||||
// The source of the script will end up here.
|
||||
wcstring script_source;
|
||||
bool has_script_source = false;
|
||||
@@ -234,55 +233,52 @@ bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_
|
||||
|
||||
if (!has_script_source) {
|
||||
// Iterate over path searching for suitable completion files.
|
||||
for (size_t i = 0; i < path_list.size(); i++) {
|
||||
for (size_t i = 0; i < path_list.size() && !found_file; i++) {
|
||||
wcstring next = path_list.at(i);
|
||||
wcstring path = next + L"/" + cmd + L".fish";
|
||||
|
||||
const file_access_attempt_t access = access_file(path, R_OK);
|
||||
if (access.accessible) {
|
||||
found_file = true;
|
||||
|
||||
// Now we're actually going to take the lock.
|
||||
scoped_lock locker(lock);
|
||||
autoload_function_t *func = this->get_node(cmd);
|
||||
|
||||
// Generate the source if we need to load it.
|
||||
bool need_to_load_function =
|
||||
really_load &&
|
||||
(func == NULL || func->access.mod_time != access.mod_time || !func->is_loaded);
|
||||
if (need_to_load_function) {
|
||||
// Generate the script source.
|
||||
wcstring esc = escape_string(path, 1);
|
||||
script_source = L"source " + esc;
|
||||
has_script_source = true;
|
||||
|
||||
// Remove any loaded command because we are going to reload it. Note that this
|
||||
// will deadlock if command_removed calls back into us.
|
||||
if (func && func->is_loaded) {
|
||||
command_removed(cmd);
|
||||
func->is_placeholder = false;
|
||||
}
|
||||
|
||||
// Mark that we're reloading it.
|
||||
reloaded = true;
|
||||
}
|
||||
|
||||
// Create the function if we haven't yet. This does not load it. Do not trigger
|
||||
// eviction unless we are actually loading, because we don't want to evict off of
|
||||
// the main thread.
|
||||
if (!func) {
|
||||
func = get_autoloaded_function_with_creation(cmd, really_load);
|
||||
}
|
||||
|
||||
// It's a fiction to say the script is loaded at this point, but we're definitely
|
||||
// going to load it down below.
|
||||
if (need_to_load_function) func->is_loaded = true;
|
||||
|
||||
// Unconditionally record our access time.
|
||||
func->access = access;
|
||||
|
||||
break;
|
||||
if (!access.accessible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now we're actually going to take the lock.
|
||||
scoped_lock locker(lock);
|
||||
autoload_function_t *func = this->get_node(cmd);
|
||||
|
||||
// Generate the source if we need to load it.
|
||||
bool need_to_load_function =
|
||||
really_load &&
|
||||
(func == NULL || func->access.mod_time != access.mod_time || !func->is_loaded);
|
||||
if (need_to_load_function) {
|
||||
// Generate the script source.
|
||||
wcstring esc = escape_string(path, 1);
|
||||
script_source = L"source " + esc;
|
||||
has_script_source = true;
|
||||
|
||||
// Remove any loaded command because we are going to reload it. Note that this
|
||||
// will deadlock if command_removed calls back into us.
|
||||
if (func && func->is_loaded) {
|
||||
command_removed(cmd);
|
||||
func->is_placeholder = false;
|
||||
}
|
||||
|
||||
// Mark that we're reloading it.
|
||||
reloaded = true;
|
||||
}
|
||||
|
||||
// Create the function if we haven't yet. This does not load it. Do not trigger
|
||||
// eviction unless we are actually loading, because we don't want to evict off of
|
||||
// the main thread.
|
||||
if (!func) func = get_autoloaded_function_with_creation(cmd, really_load);
|
||||
|
||||
// It's a fiction to say the script is loaded at this point, but we're definitely
|
||||
// going to load it down below.
|
||||
if (need_to_load_function) func->is_loaded = true;
|
||||
|
||||
// Unconditionally record our access time.
|
||||
func->access = access;
|
||||
found_file = true;
|
||||
}
|
||||
|
||||
// If no file or builtin script was found we insert a placeholder function. Later we only
|
||||
@@ -313,7 +309,7 @@ bool autoload_t::locate_file_and_maybe_load_it(const wcstring &cmd, bool really_
|
||||
|
||||
if (really_load) {
|
||||
return reloaded;
|
||||
} else {
|
||||
return found_file || has_script_source;
|
||||
}
|
||||
|
||||
return found_file || has_script_source;
|
||||
}
|
||||
|
||||
1215
src/builtin.cpp
1215
src/builtin.cpp
File diff suppressed because it is too large
Load Diff
@@ -127,6 +127,10 @@ static void replace_part(const wchar_t *begin, const wchar_t *end, const wchar_t
|
||||
out_pos += wcslen(insert);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected append_mode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
out.append(end);
|
||||
reader_set_buffer(out, out_pos);
|
||||
@@ -152,29 +156,23 @@ static void write_part(const wchar_t *begin, const wchar_t *end, int cut_at_curs
|
||||
while (tok.next(&token)) {
|
||||
if ((cut_at_cursor) && (token.offset + token.text.size() >= pos)) break;
|
||||
|
||||
switch (token.type) {
|
||||
case TOK_STRING: {
|
||||
wcstring tmp = token.text;
|
||||
unescape_string_in_place(&tmp, UNESCAPE_INCOMPLETE);
|
||||
out.append(tmp);
|
||||
out.push_back(L'\n');
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
if (token.type == TOK_STRING) {
|
||||
wcstring tmp = token.text;
|
||||
unescape_string_in_place(&tmp, UNESCAPE_INCOMPLETE);
|
||||
out.append(tmp);
|
||||
out.push_back(L'\n');
|
||||
}
|
||||
}
|
||||
|
||||
streams.out.append(out);
|
||||
|
||||
free(buff);
|
||||
} else {
|
||||
if (cut_at_cursor) {
|
||||
end = begin + pos;
|
||||
streams.out.append(begin, pos);
|
||||
} else {
|
||||
streams.out.append(begin, end - begin);
|
||||
}
|
||||
|
||||
// debug( 0, L"woot2 %ls -> %ls", buff, esc );
|
||||
streams.out.append(begin, end - begin);
|
||||
streams.out.append(L"\n");
|
||||
streams.out.push_back(L'\n');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,6 +330,10 @@ int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv)
|
||||
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return 1;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,26 +478,24 @@ int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv)
|
||||
parse_util_token_extent(get_buffer(), get_cursor_pos(), &begin, &end, 0, 0);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected buffer_part");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (argc - w.woptind) {
|
||||
case 0: {
|
||||
write_part(begin, end, cut_at_cursor, tokenize, streams);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
replace_part(begin, end, argv[w.woptind], append_mode);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
wcstring sb = argv[w.woptind];
|
||||
for (int i = w.woptind + 1; i < argc; i++) {
|
||||
sb.push_back(L'\n');
|
||||
sb.append(argv[i]);
|
||||
}
|
||||
replace_part(begin, end, sb.c_str(), append_mode);
|
||||
break;
|
||||
int arg_count = argc - w.woptind;
|
||||
if (arg_count == 0) {
|
||||
write_part(begin, end, cut_at_cursor, tokenize, streams);
|
||||
} else if (arg_count == 1) {
|
||||
replace_part(begin, end, argv[w.woptind], append_mode);
|
||||
} else {
|
||||
wcstring sb = argv[w.woptind];
|
||||
for (int i = w.woptind + 1; i < argc; i++) {
|
||||
sb.push_back(L'\n');
|
||||
sb.append(argv[i]);
|
||||
}
|
||||
replace_part(begin, end, sb.c_str(), append_mode);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -283,125 +283,127 @@ int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
if (condition && wcslen(condition)) {
|
||||
const wcstring condition_string = condition;
|
||||
parse_error_list_t errors;
|
||||
if (parse_util_detect_errors(condition_string, &errors,
|
||||
false /* do not accept incomplete */)) {
|
||||
streams.err.append_format(L"%ls: Condition '%ls' contained a syntax error", argv[0],
|
||||
condition);
|
||||
for (size_t i = 0; i < errors.size(); i++) {
|
||||
streams.err.append_format(L"\n%s: ", argv[0]);
|
||||
streams.err.append(errors.at(i).describe(condition_string));
|
||||
}
|
||||
res = true;
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
if (comp && wcslen(comp)) {
|
||||
wcstring prefix;
|
||||
if (argv[0]) {
|
||||
prefix.append(argv[0]);
|
||||
prefix.append(L": ");
|
||||
if (!res && condition && wcslen(condition)) {
|
||||
const wcstring condition_string = condition;
|
||||
parse_error_list_t errors;
|
||||
if (parse_util_detect_errors(condition_string, &errors,
|
||||
false /* do not accept incomplete */)) {
|
||||
streams.err.append_format(L"%ls: Condition '%ls' contained a syntax error", argv[0],
|
||||
condition);
|
||||
for (size_t i = 0; i < errors.size(); i++) {
|
||||
streams.err.append_format(L"\n%s: ", argv[0]);
|
||||
streams.err.append(errors.at(i).describe(condition_string));
|
||||
}
|
||||
|
||||
wcstring err_text;
|
||||
if (parser.detect_errors_in_argument_list(comp, &err_text, prefix.c_str())) {
|
||||
streams.err.append_format(L"%ls: Completion '%ls' contained a syntax error\n",
|
||||
argv[0], comp);
|
||||
streams.err.append(err_text);
|
||||
streams.err.push_back(L'\n');
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
if (do_complete) {
|
||||
const wchar_t *token;
|
||||
|
||||
parse_util_token_extent(do_complete_param.c_str(), do_complete_param.size(), &token, 0,
|
||||
0, 0);
|
||||
|
||||
// Create a scoped transient command line, so that bulitin_commandline will see our
|
||||
// argument, not the reader buffer.
|
||||
builtin_commandline_scoped_transient_t temp_buffer(do_complete_param);
|
||||
|
||||
if (recursion_level < 1) {
|
||||
recursion_level++;
|
||||
|
||||
std::vector<completion_t> comp;
|
||||
complete(do_complete_param, &comp, COMPLETION_REQUEST_DEFAULT,
|
||||
env_vars_snapshot_t::current());
|
||||
|
||||
for (size_t i = 0; i < comp.size(); i++) {
|
||||
const completion_t &next = comp.at(i);
|
||||
|
||||
// Make a fake commandline, and then apply the completion to it.
|
||||
const wcstring faux_cmdline = token;
|
||||
size_t tmp_cursor = faux_cmdline.size();
|
||||
wcstring faux_cmdline_with_completion = completion_apply_to_command_line(
|
||||
next.completion, next.flags, faux_cmdline, &tmp_cursor, false);
|
||||
|
||||
// completion_apply_to_command_line will append a space unless COMPLETE_NO_SPACE
|
||||
// is set. We don't want to set COMPLETE_NO_SPACE because that won't close
|
||||
// quotes. What we want is to close the quote, but not append the space. So we
|
||||
// just look for the space and clear it.
|
||||
if (!(next.flags & COMPLETE_NO_SPACE) &&
|
||||
string_suffixes_string(L" ", faux_cmdline_with_completion)) {
|
||||
faux_cmdline_with_completion.resize(faux_cmdline_with_completion.size() -
|
||||
1);
|
||||
}
|
||||
|
||||
// The input data is meant to be something like you would have on the command
|
||||
// line, e.g. includes backslashes. The output should be raw, i.e. unescaped. So
|
||||
// we need to unescape the command line. See #1127.
|
||||
unescape_string_in_place(&faux_cmdline_with_completion, UNESCAPE_DEFAULT);
|
||||
streams.out.append(faux_cmdline_with_completion);
|
||||
|
||||
// Append any description.
|
||||
if (!next.description.empty()) {
|
||||
streams.out.push_back(L'\t');
|
||||
streams.out.append(next.description);
|
||||
}
|
||||
streams.out.push_back(L'\n');
|
||||
}
|
||||
|
||||
recursion_level--;
|
||||
}
|
||||
} else if (w.woptind != argc) {
|
||||
streams.err.append_format(_(L"%ls: Too many arguments\n"), argv[0]);
|
||||
builtin_print_help(parser, streams, argv[0], streams.err);
|
||||
|
||||
res = true;
|
||||
} else if (cmd.empty() && path.empty()) {
|
||||
// No arguments specified, meaning we print the definitions of all specified completions
|
||||
// to stdout.
|
||||
streams.out.append(complete_print());
|
||||
} else {
|
||||
int flags = COMPLETE_AUTO_SPACE;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
builtin_complete_remove(cmd, path, short_opt.c_str(), gnu_opt, old_opt);
|
||||
if (!res && comp && wcslen(comp)) {
|
||||
wcstring prefix;
|
||||
if (argv[0]) {
|
||||
prefix.append(argv[0]);
|
||||
prefix.append(L": ");
|
||||
}
|
||||
|
||||
} else {
|
||||
builtin_complete_add(cmd, path, short_opt.c_str(), gnu_opt, old_opt, result_mode,
|
||||
authoritative, condition, comp, desc, flags);
|
||||
wcstring err_text;
|
||||
if (parser.detect_errors_in_argument_list(comp, &err_text, prefix.c_str())) {
|
||||
streams.err.append_format(L"%ls: Completion '%ls' contained a syntax error\n", argv[0],
|
||||
comp);
|
||||
streams.err.append(err_text);
|
||||
streams.err.push_back(L'\n');
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (res) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (do_complete) {
|
||||
const wchar_t *token;
|
||||
|
||||
parse_util_token_extent(do_complete_param.c_str(), do_complete_param.size(), &token, 0,
|
||||
0, 0);
|
||||
|
||||
// Create a scoped transient command line, so that bulitin_commandline will see our
|
||||
// argument, not the reader buffer.
|
||||
builtin_commandline_scoped_transient_t temp_buffer(do_complete_param);
|
||||
|
||||
if (recursion_level < 1) {
|
||||
recursion_level++;
|
||||
|
||||
std::vector<completion_t> comp;
|
||||
complete(do_complete_param, &comp, COMPLETION_REQUEST_DEFAULT,
|
||||
env_vars_snapshot_t::current());
|
||||
|
||||
for (size_t i = 0; i < comp.size(); i++) {
|
||||
const completion_t &next = comp.at(i);
|
||||
|
||||
// Make a fake commandline, and then apply the completion to it.
|
||||
const wcstring faux_cmdline = token;
|
||||
size_t tmp_cursor = faux_cmdline.size();
|
||||
wcstring faux_cmdline_with_completion = completion_apply_to_command_line(
|
||||
next.completion, next.flags, faux_cmdline, &tmp_cursor, false);
|
||||
|
||||
// completion_apply_to_command_line will append a space unless COMPLETE_NO_SPACE
|
||||
// is set. We don't want to set COMPLETE_NO_SPACE because that won't close
|
||||
// quotes. What we want is to close the quote, but not append the space. So we
|
||||
// just look for the space and clear it.
|
||||
if (!(next.flags & COMPLETE_NO_SPACE) &&
|
||||
string_suffixes_string(L" ", faux_cmdline_with_completion)) {
|
||||
faux_cmdline_with_completion.resize(faux_cmdline_with_completion.size() -
|
||||
1);
|
||||
}
|
||||
|
||||
// The input data is meant to be something like you would have on the command
|
||||
// line, e.g. includes backslashes. The output should be raw, i.e. unescaped. So
|
||||
// we need to unescape the command line. See #1127.
|
||||
unescape_string_in_place(&faux_cmdline_with_completion, UNESCAPE_DEFAULT);
|
||||
streams.out.append(faux_cmdline_with_completion);
|
||||
|
||||
// Append any description.
|
||||
if (!next.description.empty()) {
|
||||
streams.out.push_back(L'\t');
|
||||
streams.out.append(next.description);
|
||||
}
|
||||
streams.out.push_back(L'\n');
|
||||
}
|
||||
|
||||
// Handle wrap targets (probably empty). We only wrap commands, not paths.
|
||||
for (size_t w = 0; w < wrap_targets.size(); w++) {
|
||||
const wcstring &wrap_target = wrap_targets.at(w);
|
||||
for (size_t i = 0; i < cmd.size(); i++) {
|
||||
(remove ? complete_remove_wrapper : complete_add_wrapper)(cmd.at(i),
|
||||
wrap_target);
|
||||
}
|
||||
recursion_level--;
|
||||
}
|
||||
} else if (w.woptind != argc) {
|
||||
streams.err.append_format(_(L"%ls: Too many arguments\n"), argv[0]);
|
||||
builtin_print_help(parser, streams, argv[0], streams.err);
|
||||
|
||||
res = true;
|
||||
} else if (cmd.empty() && path.empty()) {
|
||||
// No arguments specified, meaning we print the definitions of all specified completions
|
||||
// to stdout.
|
||||
streams.out.append(complete_print());
|
||||
} else {
|
||||
int flags = COMPLETE_AUTO_SPACE;
|
||||
|
||||
if (remove) {
|
||||
builtin_complete_remove(cmd, path, short_opt.c_str(), gnu_opt, old_opt);
|
||||
|
||||
} else {
|
||||
builtin_complete_add(cmd, path, short_opt.c_str(), gnu_opt, old_opt, result_mode,
|
||||
authoritative, condition, comp, desc, flags);
|
||||
}
|
||||
|
||||
// Handle wrap targets (probably empty). We only wrap commands, not paths.
|
||||
for (size_t w = 0; w < wrap_targets.size(); w++) {
|
||||
const wcstring &wrap_target = wrap_targets.at(w);
|
||||
for (size_t i = 0; i < cmd.size(); i++) {
|
||||
(remove ? complete_remove_wrapper : complete_add_wrapper)(cmd.at(i),
|
||||
wrap_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,10 @@ static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected mode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +163,10 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return 1;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -508,39 +508,48 @@ void builtin_printf_state_t::print_direc(const wchar_t *start, size_t length, wc
|
||||
case L'G': {
|
||||
long double arg = string_to_scalar_type<long double>(argument, this);
|
||||
if (!have_field_width) {
|
||||
if (!have_precision)
|
||||
if (!have_precision) {
|
||||
this->append_format_output(fmt.c_str(), arg);
|
||||
else
|
||||
} else {
|
||||
this->append_format_output(fmt.c_str(), precision, arg);
|
||||
}
|
||||
} else {
|
||||
if (!have_precision)
|
||||
if (!have_precision) {
|
||||
this->append_format_output(fmt.c_str(), field_width, arg);
|
||||
else
|
||||
} else {
|
||||
this->append_format_output(fmt.c_str(), field_width, precision, arg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case L'c': {
|
||||
if (!have_field_width)
|
||||
if (!have_field_width) {
|
||||
this->append_format_output(fmt.c_str(), *argument);
|
||||
else
|
||||
} else {
|
||||
this->append_format_output(fmt.c_str(), field_width, *argument);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case L's': {
|
||||
if (!have_field_width) {
|
||||
if (!have_precision) {
|
||||
this->append_format_output(fmt.c_str(), argument);
|
||||
} else
|
||||
} else {
|
||||
this->append_format_output(fmt.c_str(), precision, argument);
|
||||
}
|
||||
} else {
|
||||
if (!have_precision)
|
||||
if (!have_precision) {
|
||||
this->append_format_output(fmt.c_str(), field_width, argument);
|
||||
else
|
||||
} else {
|
||||
this->append_format_output(fmt.c_str(), field_width, precision, argument);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,8 +597,7 @@ int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wch
|
||||
}
|
||||
|
||||
modify_allowed_format_specifiers(ok, "aAcdeEfFgGiosuxX", true);
|
||||
|
||||
for (;; f++, direc_length++) {
|
||||
for (bool continue_looking_for_flags = true; continue_looking_for_flags;) {
|
||||
switch (*f) {
|
||||
case L'I':
|
||||
case L'\'': {
|
||||
@@ -609,10 +617,16 @@ int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wch
|
||||
modify_allowed_format_specifiers(ok, "cs", false);
|
||||
break;
|
||||
}
|
||||
default: { goto no_more_flag_characters; }
|
||||
default: {
|
||||
continue_looking_for_flags = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (continue_looking_for_flags) {
|
||||
f++;
|
||||
direc_length++;
|
||||
}
|
||||
}
|
||||
no_more_flag_characters:;
|
||||
|
||||
if (*f == L'*') {
|
||||
++f;
|
||||
@@ -687,7 +701,10 @@ int builtin_printf_state_t::print_formatted(const wchar_t *format, int argc, wch
|
||||
f += print_esc(f, false);
|
||||
break;
|
||||
}
|
||||
default: { this->append_output(*f); }
|
||||
default: {
|
||||
this->append_output(*f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return save_argc - argc;
|
||||
|
||||
@@ -77,12 +77,10 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope,
|
||||
struct stat buff;
|
||||
if (wstat(dir, &buff) == -1) {
|
||||
error = true;
|
||||
}
|
||||
else if (!S_ISDIR(buff.st_mode)) {
|
||||
} else if (!S_ISDIR(buff.st_mode)) {
|
||||
error = true;
|
||||
errno = ENOTDIR;
|
||||
}
|
||||
else if (waccess(dir, X_OK) == -1) {
|
||||
} else if (waccess(dir, X_OK) == -1) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
@@ -90,7 +88,7 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope,
|
||||
any_success = true;
|
||||
} else {
|
||||
streams.err.append_format(_(BUILTIN_SET_PATH_ERROR), L"set", key, dir.c_str(),
|
||||
strerror(errno));
|
||||
strerror(errno));
|
||||
const wchar_t *colon = wcschr(dir.c_str(), L':');
|
||||
|
||||
if (colon && *(colon + 1)) {
|
||||
@@ -123,6 +121,9 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope,
|
||||
}
|
||||
|
||||
switch (env_set(key, val_str, scope | ENV_USER)) {
|
||||
case ENV_OK: {
|
||||
break;
|
||||
}
|
||||
case ENV_PERM: {
|
||||
streams.err.append_format(_(L"%ls: Tried to change the read-only variable '%ls'\n"),
|
||||
L"set", key);
|
||||
@@ -143,6 +144,10 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope,
|
||||
retcode = 1;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected env_set() ret val");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retcode;
|
||||
@@ -159,7 +164,6 @@ static int my_env_set(const wchar_t *key, const wcstring_list_t &val, int scope,
|
||||
static int parse_index(std::vector<long> &indexes, const wchar_t *src, const wchar_t *name,
|
||||
size_t var_count, io_streams_t &streams) {
|
||||
size_t len;
|
||||
|
||||
int count = 0;
|
||||
const wchar_t *src_orig = src;
|
||||
|
||||
@@ -167,9 +171,7 @@ static int parse_index(std::vector<long> &indexes, const wchar_t *src, const wch
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (*src != L'\0' && (iswalnum(*src) || *src == L'_')) {
|
||||
src++;
|
||||
}
|
||||
while (*src != L'\0' && (iswalnum(*src) || *src == L'_')) src++;
|
||||
|
||||
if (*src != L'[') {
|
||||
streams.err.append_format(_(BUILTIN_SET_ARG_COUNT), L"set");
|
||||
@@ -177,7 +179,6 @@ static int parse_index(std::vector<long> &indexes, const wchar_t *src, const wch
|
||||
}
|
||||
|
||||
len = src - src_orig;
|
||||
|
||||
if ((wcsncmp(src_orig, name, len) != 0) || (wcslen(name) != (len))) {
|
||||
streams.err.append_format(
|
||||
_(L"%ls: Multiple variable names specified in single call (%ls and %.*ls)\n"), L"set",
|
||||
@@ -186,37 +187,29 @@ static int parse_index(std::vector<long> &indexes, const wchar_t *src, const wch
|
||||
}
|
||||
|
||||
src++;
|
||||
|
||||
while (iswspace(*src)) {
|
||||
src++;
|
||||
}
|
||||
while (iswspace(*src)) src++;
|
||||
|
||||
while (*src != L']') {
|
||||
wchar_t *end;
|
||||
|
||||
long l_ind;
|
||||
|
||||
errno = 0;
|
||||
|
||||
l_ind = wcstol(src, &end, 10);
|
||||
|
||||
if (end == src || errno) {
|
||||
streams.err.append_format(_(L"%ls: Invalid index starting at '%ls'\n"), L"set", src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (l_ind < 0) {
|
||||
l_ind = var_count + l_ind + 1;
|
||||
}
|
||||
if (l_ind < 0) l_ind = var_count + l_ind + 1;
|
||||
|
||||
src = end;
|
||||
src = end; //!OCLINT(parameter reassignment)
|
||||
if (*src == L'.' && *(src + 1) == L'.') {
|
||||
src += 2;
|
||||
long l_ind2 = wcstol(src, &end, 10);
|
||||
if (end == src || errno) {
|
||||
return 1;
|
||||
}
|
||||
src = end;
|
||||
src = end; //!OCLINT(parameter reassignment)
|
||||
|
||||
if (l_ind2 < 0) {
|
||||
l_ind2 = var_count + l_ind2 + 1;
|
||||
@@ -231,6 +224,7 @@ static int parse_index(std::vector<long> &indexes, const wchar_t *src, const wch
|
||||
indexes.push_back(l_ind);
|
||||
count++;
|
||||
}
|
||||
|
||||
while (iswspace(*src)) src++;
|
||||
}
|
||||
|
||||
@@ -342,7 +336,7 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
int erase = 0, list = 0, unexport = 0;
|
||||
int universal = 0, query = 0;
|
||||
bool shorten_ok = true;
|
||||
bool preserve_incoming_failure_exit_status = true;
|
||||
bool preserve_failure_exit_status = true;
|
||||
const int incoming_exit_status = proc_get_last_status();
|
||||
|
||||
// Variables used for performing the actual work.
|
||||
@@ -368,12 +362,12 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
}
|
||||
case 'e': {
|
||||
erase = 1;
|
||||
preserve_incoming_failure_exit_status = false;
|
||||
preserve_failure_exit_status = false;
|
||||
break;
|
||||
}
|
||||
case 'n': {
|
||||
list = 1;
|
||||
preserve_incoming_failure_exit_status = false;
|
||||
preserve_failure_exit_status = false;
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
@@ -402,7 +396,7 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
}
|
||||
case 'q': {
|
||||
query = 1;
|
||||
preserve_incoming_failure_exit_status = false;
|
||||
preserve_failure_exit_status = false;
|
||||
break;
|
||||
}
|
||||
case 'h': {
|
||||
@@ -633,7 +627,7 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
|
||||
free(dest);
|
||||
|
||||
if (retcode == STATUS_BUILTIN_OK && preserve_incoming_failure_exit_status)
|
||||
if (retcode == STATUS_BUILTIN_OK && preserve_failure_exit_status)
|
||||
retcode = incoming_exit_status;
|
||||
return retcode;
|
||||
}
|
||||
|
||||
@@ -77,13 +77,13 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
// Parse options to obtain the requested operation and the modifiers.
|
||||
w.woptind = 0;
|
||||
while (1) {
|
||||
int c = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
int opt = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
|
||||
if (c == -1) {
|
||||
if (opt == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
switch (opt) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
@@ -110,6 +110,10 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
case '?': {
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,19 +163,17 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
builtin_set_color_output.clear();
|
||||
output_set_writer(set_color_builtin_outputter);
|
||||
|
||||
if (bold) {
|
||||
if (enter_bold_mode) writembs(tparm(enter_bold_mode));
|
||||
if (bold && enter_bold_mode) {
|
||||
writembs(tparm(enter_bold_mode));
|
||||
}
|
||||
|
||||
if (underline) {
|
||||
if (enter_underline_mode) writembs(enter_underline_mode);
|
||||
if (underline && enter_underline_mode) {
|
||||
writembs(enter_underline_mode);
|
||||
}
|
||||
|
||||
if (bgcolor != NULL) {
|
||||
if (bg.is_normal()) {
|
||||
write_color(rgb_color_t::black(), false /* not is_fg */);
|
||||
writembs(tparm(exit_attribute_mode));
|
||||
}
|
||||
if (bgcolor != NULL && bg.is_normal()) {
|
||||
write_color(rgb_color_t::black(), false /* not is_fg */);
|
||||
writembs(tparm(exit_attribute_mode));
|
||||
}
|
||||
|
||||
if (!fg.is_none()) {
|
||||
@@ -188,10 +190,8 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (bgcolor != NULL) {
|
||||
if (!bg.is_normal() && !bg.is_reset()) {
|
||||
write_color(bg, false /* not is_fg */);
|
||||
}
|
||||
if (bgcolor != NULL && !bg.is_normal() && !bg.is_reset()) {
|
||||
write_color(bg, false /* not is_fg */);
|
||||
}
|
||||
|
||||
// Restore saved writer function.
|
||||
|
||||
@@ -100,12 +100,13 @@ static int string_escape(parser_t &parser, io_streams_t &streams, int argc, wcha
|
||||
escape_flags_t flags = ESCAPE_ALL;
|
||||
wgetopter_t w;
|
||||
for (;;) {
|
||||
int c = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
int opt = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
|
||||
if (c == -1) {
|
||||
if (opt == -1) {
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
|
||||
switch (opt) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
@@ -117,6 +118,10 @@ static int string_escape(parser_t &parser, io_streams_t &streams, int argc, wcha
|
||||
string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return BUILTIN_STRING_ERROR;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,11 +150,13 @@ static int string_join(parser_t &parser, io_streams_t &streams, int argc, wchar_
|
||||
bool quiet = false;
|
||||
wgetopter_t w;
|
||||
for (;;) {
|
||||
int c = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
if (c == -1) {
|
||||
int opt = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
|
||||
if (opt == -1) {
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
|
||||
switch (opt) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
@@ -161,6 +168,10 @@ static int string_join(parser_t &parser, io_streams_t &streams, int argc, wchar_
|
||||
string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return BUILTIN_STRING_ERROR;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,12 +213,12 @@ static int string_length(parser_t &parser, io_streams_t &streams, int argc, wcha
|
||||
bool quiet = false;
|
||||
wgetopter_t w;
|
||||
for (;;) {
|
||||
int c = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
int opt = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
|
||||
if (c == -1) {
|
||||
if (opt == -1) {
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
switch (opt) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
@@ -219,6 +230,10 @@ static int string_length(parser_t &parser, io_streams_t &streams, int argc, wcha
|
||||
string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return BUILTIN_STRING_ERROR;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,12 +514,12 @@ static int string_match(parser_t &parser, io_streams_t &streams, int argc, wchar
|
||||
bool regex = false;
|
||||
wgetopter_t w;
|
||||
for (;;) {
|
||||
int c = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
int opt = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
|
||||
if (c == -1) {
|
||||
if (opt == -1) {
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
switch (opt) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
@@ -536,6 +551,10 @@ static int string_match(parser_t &parser, io_streams_t &streams, int argc, wchar
|
||||
string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return BUILTIN_STRING_ERROR;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,59 +685,63 @@ class regex_replacer_t : public string_replacer_t {
|
||||
regex(argv0, pattern, opts.ignore_case, streams),
|
||||
replacement(interpret_escapes(replacement_)) {}
|
||||
|
||||
bool replace_matches(const wchar_t *arg) {
|
||||
// A return value of true means all is well (even if no replacements were performed), false
|
||||
// indicates an unrecoverable error.
|
||||
if (regex.code == 0) {
|
||||
// pcre2_compile() failed
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t options = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH | PCRE2_SUBSTITUTE_EXTENDED |
|
||||
(opts.all ? PCRE2_SUBSTITUTE_GLOBAL : 0);
|
||||
size_t arglen = wcslen(arg);
|
||||
PCRE2_SIZE bufsize = (arglen == 0) ? 16 : 2 * arglen;
|
||||
wchar_t *output = (wchar_t *)malloc(sizeof(wchar_t) * bufsize);
|
||||
int pcre2_rc = 0;
|
||||
for (;;) {
|
||||
if (output == NULL) {
|
||||
DIE_MEM();
|
||||
}
|
||||
PCRE2_SIZE outlen = bufsize;
|
||||
pcre2_rc = pcre2_substitute(regex.code, PCRE2_SPTR(arg), arglen,
|
||||
0, // start offset
|
||||
options, regex.match,
|
||||
0, // match context
|
||||
PCRE2_SPTR(replacement.c_str()), PCRE2_ZERO_TERMINATED,
|
||||
(PCRE2_UCHAR *)output, &outlen);
|
||||
|
||||
if (pcre2_rc == PCRE2_ERROR_NOMEMORY && bufsize < outlen) {
|
||||
bufsize = outlen;
|
||||
// cppcheck-suppress memleakOnRealloc
|
||||
output = (wchar_t *)realloc(output, sizeof(wchar_t) * bufsize);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
bool rc = true;
|
||||
if (pcre2_rc < 0) {
|
||||
string_error(streams, _(L"%ls: Regular expression substitute error: %ls\n"), argv0,
|
||||
pcre2_strerror(pcre2_rc).c_str());
|
||||
rc = false;
|
||||
} else {
|
||||
if (!opts.quiet) {
|
||||
streams.out.append(output);
|
||||
streams.out.append(L'\n');
|
||||
}
|
||||
total_replaced += pcre2_rc;
|
||||
}
|
||||
|
||||
free(output);
|
||||
return rc;
|
||||
}
|
||||
bool replace_matches(const wchar_t *arg);
|
||||
};
|
||||
|
||||
/// A return value of true means all is well (even if no replacements were performed), false
|
||||
/// indicates an unrecoverable error.
|
||||
bool regex_replacer_t::replace_matches(const wchar_t *arg) {
|
||||
if (regex.code == 0) {
|
||||
// pcre2_compile() failed
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t options = PCRE2_SUBSTITUTE_OVERFLOW_LENGTH | PCRE2_SUBSTITUTE_EXTENDED |
|
||||
(opts.all ? PCRE2_SUBSTITUTE_GLOBAL : 0);
|
||||
size_t arglen = wcslen(arg);
|
||||
PCRE2_SIZE bufsize = (arglen == 0) ? 16 : 2 * arglen;
|
||||
wchar_t *output = (wchar_t *)malloc(sizeof(wchar_t) * bufsize);
|
||||
int pcre2_rc;
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
if (output == NULL) {
|
||||
DIE_MEM();
|
||||
}
|
||||
PCRE2_SIZE outlen = bufsize;
|
||||
pcre2_rc = pcre2_substitute(regex.code, PCRE2_SPTR(arg), arglen,
|
||||
0, // start offset
|
||||
options, regex.match,
|
||||
0, // match context
|
||||
PCRE2_SPTR(replacement.c_str()), PCRE2_ZERO_TERMINATED,
|
||||
(PCRE2_UCHAR *)output, &outlen);
|
||||
|
||||
if (pcre2_rc != PCRE2_ERROR_NOMEMORY || bufsize >= outlen) {
|
||||
done = true;
|
||||
} else {
|
||||
bufsize = outlen;
|
||||
// cppcheck-suppress memleakOnRealloc
|
||||
output = (wchar_t *)realloc(output, sizeof(wchar_t) * bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
bool rc = true;
|
||||
if (pcre2_rc < 0) {
|
||||
string_error(streams, _(L"%ls: Regular expression substitute error: %ls\n"), argv0,
|
||||
pcre2_strerror(pcre2_rc).c_str());
|
||||
rc = false;
|
||||
} else {
|
||||
if (!opts.quiet) {
|
||||
streams.out.append(output);
|
||||
streams.out.append(L'\n');
|
||||
}
|
||||
total_replaced += pcre2_rc;
|
||||
}
|
||||
|
||||
free(output);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int string_replace(parser_t &parser, io_streams_t &streams, int argc, wchar_t **argv) {
|
||||
const wchar_t *short_options = L"aiqr";
|
||||
const struct woption long_options[] = {{L"all", no_argument, 0, 'a'},
|
||||
@@ -731,12 +754,12 @@ static int string_replace(parser_t &parser, io_streams_t &streams, int argc, wch
|
||||
bool regex = false;
|
||||
wgetopter_t w;
|
||||
for (;;) {
|
||||
int c = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
int opt = w.wgetopt_long(argc, argv, short_options, long_options, 0);
|
||||
|
||||
if (c == -1) {
|
||||
if (opt == -1) {
|
||||
break;
|
||||
}
|
||||
switch (c) {
|
||||
switch (opt) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
@@ -760,6 +783,10 @@ static int string_replace(parser_t &parser, io_streams_t &streams, int argc, wch
|
||||
string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return BUILTIN_STRING_ERROR;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -876,6 +903,10 @@ static int string_split(parser_t &parser, io_streams_t &streams, int argc, wchar
|
||||
string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return BUILTIN_STRING_ERROR;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -989,6 +1020,10 @@ static int string_sub(parser_t &parser, io_streams_t &streams, int argc, wchar_t
|
||||
string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return BUILTIN_STRING_ERROR;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1078,6 +1113,10 @@ static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_
|
||||
string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return BUILTIN_STRING_ERROR;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1124,7 +1163,8 @@ static int string_trim(parser_t &parser, io_streams_t &streams, int argc, wchar_
|
||||
|
||||
static const struct string_subcommand {
|
||||
const wchar_t *name;
|
||||
int (*handler)(parser_t &, io_streams_t &, int argc, wchar_t **argv);
|
||||
int (*handler)(parser_t &, io_streams_t &, int argc, //!OCLINT(unused param)
|
||||
wchar_t **argv); //!OCLINT(unused param)
|
||||
}
|
||||
|
||||
string_subcommands[] = {
|
||||
|
||||
@@ -518,8 +518,8 @@ expression *test_parser::parse_expression(unsigned int start, unsigned int end)
|
||||
unsigned int argc = end - start;
|
||||
switch (argc) {
|
||||
case 0: {
|
||||
assert(0); // should have been caught by the above test
|
||||
return NULL;
|
||||
DIE("argc should not be zero"); // should have been caught by the above test
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
return error(L"Missing argument at index %u", start + 1);
|
||||
@@ -579,62 +579,55 @@ bool binary_primary::evaluate(wcstring_list_t &errors) {
|
||||
}
|
||||
|
||||
bool unary_operator::evaluate(wcstring_list_t &errors) {
|
||||
switch (token) {
|
||||
case test_bang: {
|
||||
assert(subject.get());
|
||||
return !subject->evaluate(errors);
|
||||
}
|
||||
default: {
|
||||
errors.push_back(format_string(L"Unknown token type in %s", __func__));
|
||||
return false;
|
||||
}
|
||||
if (token == test_bang) {
|
||||
assert(subject.get());
|
||||
return !subject->evaluate(errors);
|
||||
}
|
||||
|
||||
errors.push_back(format_string(L"Unknown token type in %s", __func__));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool combining_expression::evaluate(wcstring_list_t &errors) {
|
||||
switch (token) {
|
||||
case test_combine_and:
|
||||
case test_combine_or: {
|
||||
// One-element case.
|
||||
if (subjects.size() == 1) return subjects.at(0)->evaluate(errors);
|
||||
if (token == test_combine_and || token == test_combine_or) {
|
||||
assert(!subjects.empty()); //!OCLINT(multiple unary operator)
|
||||
assert(combiners.size() + 1 == subjects.size());
|
||||
|
||||
// Evaluate our lists, remembering that AND has higher precedence than OR. We can
|
||||
// visualize this as a sequence of OR expressions of AND expressions.
|
||||
assert(combiners.size() + 1 == subjects.size());
|
||||
assert(!subjects.empty());
|
||||
// One-element case.
|
||||
if (subjects.size() == 1) return subjects.at(0)->evaluate(errors);
|
||||
|
||||
size_t idx = 0, max = subjects.size();
|
||||
bool or_result = false;
|
||||
while (idx < max) {
|
||||
if (or_result) { // short circuit
|
||||
// Evaluate our lists, remembering that AND has higher precedence than OR. We can
|
||||
// visualize this as a sequence of OR expressions of AND expressions.
|
||||
size_t idx = 0, max = subjects.size();
|
||||
bool or_result = false;
|
||||
while (idx < max) {
|
||||
if (or_result) { // short circuit
|
||||
break;
|
||||
}
|
||||
|
||||
// Evaluate a stream of AND starting at given subject index. It may only have one
|
||||
// element.
|
||||
bool and_result = true;
|
||||
for (; idx < max; idx++) {
|
||||
// Evaluate it, short-circuiting.
|
||||
and_result = and_result && subjects.at(idx)->evaluate(errors);
|
||||
|
||||
// If the combiner at this index (which corresponding to how we combine with the
|
||||
// next subject) is not AND, then exit the loop.
|
||||
if (idx + 1 < max && combiners.at(idx) != test_combine_and) {
|
||||
idx++;
|
||||
break;
|
||||
}
|
||||
|
||||
// Evaluate a stream of AND starting at given subject index. It may only have one
|
||||
// element.
|
||||
bool and_result = true;
|
||||
for (; idx < max; idx++) {
|
||||
// Evaluate it, short-circuiting.
|
||||
and_result = and_result && subjects.at(idx)->evaluate(errors);
|
||||
|
||||
// If the combiner at this index (which corresponding to how we combine with the
|
||||
// next subject) is not AND, then exit the loop.
|
||||
if (idx + 1 < max && combiners.at(idx) != test_combine_and) {
|
||||
idx++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// OR it in.
|
||||
or_result = or_result || and_result;
|
||||
}
|
||||
return or_result;
|
||||
}
|
||||
default: {
|
||||
errors.push_back(format_string(L"Unknown token type in %s", __func__));
|
||||
return BUILTIN_TEST_FAIL;
|
||||
|
||||
// OR it in.
|
||||
or_result = or_result || and_result;
|
||||
}
|
||||
return or_result;
|
||||
}
|
||||
|
||||
errors.push_back(format_string(L"Unknown token type in %s", __func__));
|
||||
return BUILTIN_TEST_FAIL;
|
||||
}
|
||||
|
||||
bool parenthetical_expression::evaluate(wcstring_list_t &errors) {
|
||||
@@ -798,42 +791,36 @@ int builtin_test(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
// Collect the arguments into a list.
|
||||
const wcstring_list_t args(argv + 1, argv + 1 + argc);
|
||||
|
||||
switch (argc) {
|
||||
case 0: {
|
||||
// Per 1003.1, exit false.
|
||||
return BUILTIN_TEST_FAIL;
|
||||
}
|
||||
case 1: {
|
||||
// Per 1003.1, exit true if the arg is non-empty.
|
||||
return args.at(0).empty() ? BUILTIN_TEST_FAIL : BUILTIN_TEST_SUCCESS;
|
||||
}
|
||||
default: {
|
||||
// Try parsing. If expr is not nil, we are responsible for deleting it.
|
||||
wcstring err;
|
||||
expression *expr = test_parser::parse_args(args, err);
|
||||
if (!expr) {
|
||||
#if 0
|
||||
printf("Oops! test was given args:\n");
|
||||
for (size_t i=0; i < argc; i++) {
|
||||
printf("\t%ls\n", args.at(i).c_str());
|
||||
}
|
||||
printf("and returned parse error: %ls\n", err.c_str());
|
||||
#endif
|
||||
streams.err.append(err);
|
||||
return BUILTIN_TEST_FAIL;
|
||||
}
|
||||
if (argc == 0) {
|
||||
return BUILTIN_TEST_FAIL; // 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() ? BUILTIN_TEST_FAIL : BUILTIN_TEST_SUCCESS;
|
||||
}
|
||||
|
||||
wcstring_list_t eval_errors;
|
||||
bool result = expr->evaluate(eval_errors);
|
||||
if (!eval_errors.empty()) {
|
||||
printf("test returned eval errors:\n");
|
||||
for (size_t i = 0; i < eval_errors.size(); i++) {
|
||||
printf("\t%ls\n", eval_errors.at(i).c_str());
|
||||
}
|
||||
}
|
||||
delete expr;
|
||||
return result ? BUILTIN_TEST_SUCCESS : BUILTIN_TEST_FAIL;
|
||||
// Try parsing. If expr is not nil, we are responsible for deleting it.
|
||||
wcstring err;
|
||||
expression *expr = test_parser::parse_args(args, err);
|
||||
if (!expr) {
|
||||
#if 0
|
||||
printf("Oops! test was given args:\n");
|
||||
for (size_t i=0; i < argc; i++) {
|
||||
printf("\t%ls\n", args.at(i).c_str());
|
||||
}
|
||||
printf("and returned parse error: %ls\n", err.c_str());
|
||||
#endif
|
||||
streams.err.append(err);
|
||||
return BUILTIN_TEST_FAIL;
|
||||
}
|
||||
|
||||
wcstring_list_t eval_errors;
|
||||
bool result = expr->evaluate(eval_errors);
|
||||
if (!eval_errors.empty()) {
|
||||
printf("test returned eval errors:\n");
|
||||
for (size_t i = 0; i < eval_errors.size(); i++) {
|
||||
printf("\t%ls\n", eval_errors.at(i).c_str());
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
delete expr;
|
||||
return result ? BUILTIN_TEST_SUCCESS : BUILTIN_TEST_FAIL;
|
||||
}
|
||||
|
||||
@@ -262,6 +262,10 @@ int builtin_ulimit(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]);
|
||||
return 1;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected opt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,46 +282,42 @@ int builtin_ulimit(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (argc - w.woptind) {
|
||||
case 0: { // show current limit value
|
||||
print(what, hard, streams);
|
||||
break;
|
||||
}
|
||||
case 1: { // change current limit value
|
||||
rlim_t new_limit;
|
||||
wchar_t *end;
|
||||
int arg_count = argc - w.woptind;
|
||||
if (arg_count == 0) {
|
||||
// Show current limit value.
|
||||
print(what, hard, streams);
|
||||
} else if (arg_count == 1) {
|
||||
// Change current limit value.
|
||||
rlim_t new_limit;
|
||||
wchar_t *end;
|
||||
|
||||
// Set both hard and soft limits if nothing else was specified.
|
||||
if (!(hard + soft)) {
|
||||
hard = soft = 1;
|
||||
// Set both hard and soft limits if nothing else was specified.
|
||||
if (!(hard + soft)) {
|
||||
hard = soft = 1;
|
||||
}
|
||||
|
||||
if (wcscasecmp(argv[w.woptind], L"unlimited") == 0) {
|
||||
new_limit = RLIM_INFINITY;
|
||||
} else if (wcscasecmp(argv[w.woptind], L"hard") == 0) {
|
||||
new_limit = get(what, 1);
|
||||
} else if (wcscasecmp(argv[w.woptind], L"soft") == 0) {
|
||||
new_limit = get(what, soft);
|
||||
} else {
|
||||
errno = 0;
|
||||
new_limit = wcstol(argv[w.woptind], &end, 10);
|
||||
if (errno || *end) {
|
||||
streams.err.append_format(L"%ls: Invalid limit '%ls'\n", argv[0], argv[w.woptind]);
|
||||
builtin_print_help(parser, streams, argv[0], streams.err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (wcscasecmp(argv[w.woptind], L"unlimited") == 0) {
|
||||
new_limit = RLIM_INFINITY;
|
||||
} else if (wcscasecmp(argv[w.woptind], L"hard") == 0) {
|
||||
new_limit = get(what, 1);
|
||||
} else if (wcscasecmp(argv[w.woptind], L"soft") == 0) {
|
||||
new_limit = get(what, soft);
|
||||
} else {
|
||||
errno = 0;
|
||||
new_limit = wcstol(argv[w.woptind], &end, 10);
|
||||
if (errno || *end) {
|
||||
streams.err.append_format(L"%ls: Invalid limit '%ls'\n", argv[0],
|
||||
argv[w.woptind]);
|
||||
builtin_print_help(parser, streams, argv[0], streams.err);
|
||||
return 1;
|
||||
}
|
||||
new_limit *= get_multiplier(what);
|
||||
}
|
||||
|
||||
return set(what, hard, soft, new_limit, streams);
|
||||
}
|
||||
default: {
|
||||
streams.err.append(argv[0]);
|
||||
streams.err.append(L": Too many arguments\n");
|
||||
builtin_print_help(parser, streams, argv[0], streams.err);
|
||||
return 1;
|
||||
new_limit *= get_multiplier(what);
|
||||
}
|
||||
|
||||
return set(what, hard, soft, new_limit, streams);
|
||||
}
|
||||
return 0;
|
||||
|
||||
streams.err.append(argv[0]);
|
||||
streams.err.append(L": Too many arguments\n");
|
||||
builtin_print_help(parser, streams, argv[0], streams.err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ class rgb_color_t {
|
||||
color24_t to_color24() const;
|
||||
|
||||
/// Returns whether the color is bold.
|
||||
bool is_bold() const { return !!(flags & flag_bold); }
|
||||
bool is_bold() const { return static_cast<bool>(flags & flag_bold); }
|
||||
|
||||
/// Set whether the color is bold.
|
||||
void set_bold(bool x) {
|
||||
@@ -107,7 +107,7 @@ class rgb_color_t {
|
||||
}
|
||||
|
||||
/// Returns whether the color is underlined.
|
||||
bool is_underline() const { return !!(flags & flag_underline); }
|
||||
bool is_underline() const { return static_cast<bool>(flags & flag_underline); }
|
||||
|
||||
/// Set whether the color is underlined.
|
||||
void set_underline(bool x) {
|
||||
|
||||
119
src/common.cpp
119
src/common.cpp
@@ -43,7 +43,7 @@ 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 bool thread_assertions_configured_for_testing = false;
|
||||
static bool thread_asserts_cfg_for_testing = false;
|
||||
|
||||
wchar_t ellipsis_char;
|
||||
wchar_t omitted_newline_char;
|
||||
@@ -56,7 +56,7 @@ int debug_stack_frames = 0; // default number of stack frames to show on debug(
|
||||
static pid_t initial_pid = 0;
|
||||
|
||||
/// Be able to restore the term's foreground process group.
|
||||
static pid_t initial_foreground_process_group = -1;
|
||||
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
|
||||
@@ -107,6 +107,7 @@ demangled_backtrace(int max_frames, int skip_levels) {
|
||||
void __attribute__((noinline))
|
||||
show_stackframe(const wchar_t msg_level, int frame_count, int skip_levels) {
|
||||
ASSERT_IS_NOT_FORKED_CHILD();
|
||||
if (frame_count < 1) return;
|
||||
|
||||
// TODO: Decide if this is still needed. I'm commenting it out because it caused me some grief
|
||||
// while trying to debug a test failure. And the tests run just fine without spurious failures
|
||||
@@ -115,7 +116,6 @@ show_stackframe(const wchar_t msg_level, int frame_count, int skip_levels) {
|
||||
// Hack to avoid showing backtraces in the tester.
|
||||
// if (program_name && !wcscmp(program_name, L"(ignore)")) return;
|
||||
|
||||
if (frame_count < 1) frame_count = 999;
|
||||
debug_shared(msg_level, L"Backtrace:");
|
||||
std::vector<wcstring> bt = demangled_backtrace(frame_count, skip_levels + 2);
|
||||
for (int i = 0; (size_t)i < bt.size(); i++) {
|
||||
@@ -196,15 +196,14 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) {
|
||||
// Protect against broken mbrtowc() implementations which attempt to encode UTF-8
|
||||
// sequences longer than four bytes (e.g., OS X Snow Leopard).
|
||||
use_encode_direct = true;
|
||||
} else if (sizeof(wchar_t) == 2 && (in[in_pos] & 0xF8) == 0xF0) {
|
||||
} else if (sizeof(wchar_t) == 2 && //!OCLINT(constant if expression)
|
||||
(in[in_pos] & 0xF8) == 0xF0) {
|
||||
// Assume we are in a UTF-16 environment (e.g., Cygwin) using a UTF-8 encoding.
|
||||
// The bits set check will be true for a four byte UTF-8 sequence that requires
|
||||
// two UTF-16 chars. Something that doesn't work with our simple use of mbrtowc().
|
||||
use_encode_direct = true;
|
||||
} else {
|
||||
ret = mbrtowc(&wc, &in[in_pos], in_len - in_pos, &state);
|
||||
// fprintf(stderr, "WTF in_pos %d ret %d\n", in_pos, ret);
|
||||
|
||||
// Determine whether to encode this character with our crazy scheme.
|
||||
if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE + 256) {
|
||||
use_encode_direct = true;
|
||||
@@ -219,7 +218,8 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) {
|
||||
} else if (ret > in_len - in_pos) {
|
||||
// Other error codes? Terrifying, should never happen.
|
||||
use_encode_direct = true;
|
||||
} else if (sizeof(wchar_t) == 2 && wc >= 0xD800 && wc <= 0xDFFF) {
|
||||
} else if (sizeof(wchar_t) == 2 && wc >= 0xD800 && //!OCLINT(constant if expression)
|
||||
wc <= 0xDFFF) {
|
||||
// If we get a surrogate pair char on a UTF-16 system (e.g., Cygwin) then
|
||||
// it's guaranteed the UTF-8 decoding is wrong so use direct encoding.
|
||||
use_encode_direct = true;
|
||||
@@ -227,24 +227,20 @@ static wcstring str2wcs_internal(const char *in, const size_t in_len) {
|
||||
}
|
||||
|
||||
if (use_encode_direct) {
|
||||
// fprintf(stderr, "WTF use_encode_direct\n");
|
||||
wc = ENCODE_DIRECT_BASE + (unsigned char)in[in_pos];
|
||||
result.push_back(wc);
|
||||
in_pos++;
|
||||
memset(&state, 0, sizeof state);
|
||||
} else if (ret == 0) {
|
||||
// fprintf(stderr, "WTF null byte\n");
|
||||
// Embedded null byte!
|
||||
} else if (ret == 0) { // embedded null byte!
|
||||
result.push_back(L'\0');
|
||||
in_pos++;
|
||||
memset(&state, 0, sizeof state);
|
||||
} else {
|
||||
// fprintf(stderr, "WTF null byte\n");
|
||||
// Normal case.
|
||||
} else { // normal case
|
||||
result.push_back(wc);
|
||||
in_pos += ret;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -286,16 +282,15 @@ std::string wcs2string(const wcstring &input) {
|
||||
result.reserve(input.size());
|
||||
|
||||
mbstate_t state = {};
|
||||
char converted[MB_LEN_MAX + 1];
|
||||
char converted[MB_LEN_MAX];
|
||||
|
||||
for (size_t i = 0; i < input.size(); i++) {
|
||||
wchar_t wc = input[i];
|
||||
if (wc == INTERNAL_SEPARATOR) {
|
||||
// Do nothing.
|
||||
; // do nothing
|
||||
} else if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE + 256) {
|
||||
result.push_back(wc - ENCODE_DIRECT_BASE);
|
||||
} else if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859)
|
||||
{
|
||||
} else if (MB_CUR_MAX == 1) { // single-byte locale (C/POSIX/ISO-8859)
|
||||
// If `wc` contains a wide character we emit a question-mark.
|
||||
if (wc & ~0xFF) {
|
||||
wc = '?';
|
||||
@@ -305,8 +300,8 @@ std::string wcs2string(const wcstring &input) {
|
||||
} else {
|
||||
memset(converted, 0, sizeof converted);
|
||||
size_t len = wcrtomb(converted, wc, &state);
|
||||
if (len == (size_t)(-1)) {
|
||||
debug(1, L"Wide character %d has no narrow representation", wc);
|
||||
if (len == (size_t)-1) {
|
||||
debug(1, L"Wide character U+%4X has no narrow representation", wc);
|
||||
memset(&state, 0, sizeof(state));
|
||||
} else {
|
||||
result.append(converted, len);
|
||||
@@ -332,7 +327,7 @@ static char *wcs2str_internal(const wchar_t *in, char *out) {
|
||||
|
||||
while (in[in_pos]) {
|
||||
if (in[in_pos] == INTERNAL_SEPARATOR) {
|
||||
// Do nothing.
|
||||
; // do nothing
|
||||
} else if (in[in_pos] >= ENCODE_DIRECT_BASE && in[in_pos] < ENCODE_DIRECT_BASE + 256) {
|
||||
out[out_pos++] = in[in_pos] - ENCODE_DIRECT_BASE;
|
||||
} else if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859)
|
||||
@@ -346,7 +341,7 @@ static char *wcs2str_internal(const wchar_t *in, char *out) {
|
||||
} else {
|
||||
size_t len = wcrtomb(&out[out_pos], in[in_pos], &state);
|
||||
if (len == (size_t)-1) {
|
||||
debug(1, L"Wide character %d has no narrow representation", in[in_pos]);
|
||||
debug(1, L"Wide character U+%4X has no narrow representation", in[in_pos]);
|
||||
memset(&state, 0, sizeof(state));
|
||||
} else {
|
||||
out_pos += len;
|
||||
@@ -359,6 +354,14 @@ static char *wcs2str_internal(const wchar_t *in, char *out) {
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Test if the character can be encoded using the current locale.
|
||||
static bool can_be_encoded(wchar_t wc) {
|
||||
char converted[MB_LEN_MAX];
|
||||
mbstate_t state = {};
|
||||
|
||||
return wcrtomb(converted, wc, &state) != (size_t)-1;
|
||||
}
|
||||
|
||||
wcstring format_string(const wchar_t *format, ...) {
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
@@ -449,11 +452,11 @@ wchar_t *quote_end(const wchar_t *pos) {
|
||||
}
|
||||
|
||||
void fish_setlocale() {
|
||||
// Use ellipsis if on known unicode system, otherwise use $.
|
||||
ellipsis_char = (fish_wcwidth(L'\x2026') > 0) ? L'\x2026' : L'$';
|
||||
// Use the Unicode "ellipsis" symbol if it can be encoded using the current locale.
|
||||
ellipsis_char = can_be_encoded(L'\x2026') ? L'\x2026' : L'$';
|
||||
|
||||
// U+23CE is the "return" character
|
||||
omitted_newline_char = (fish_wcwidth(L'\x23CE') > 0) ? L'\x23CE' : L'~';
|
||||
// Use the Unicode "return" symbol if it can be encoded using the current locale.
|
||||
omitted_newline_char = can_be_encoded(L'\x23CE') ? L'\x23CE' : L'~';
|
||||
}
|
||||
|
||||
bool contains_internal(const wchar_t *a, int vararg_handle, ...) {
|
||||
@@ -768,9 +771,9 @@ static void escape_string_internal(const wchar_t *orig_in, size_t in_len, wcstri
|
||||
assert(orig_in != NULL);
|
||||
|
||||
const wchar_t *in = orig_in;
|
||||
bool escape_all = !!(flags & ESCAPE_ALL);
|
||||
bool no_quoted = !!(flags & ESCAPE_NO_QUOTED);
|
||||
bool no_tilde = !!(flags & ESCAPE_NO_TILDE);
|
||||
bool escape_all = static_cast<bool>(flags & ESCAPE_ALL);
|
||||
bool no_quoted = static_cast<bool>(flags & ESCAPE_NO_QUOTED);
|
||||
bool no_tilde = static_cast<bool>(flags & ESCAPE_NO_TILDE);
|
||||
|
||||
int need_escape = 0;
|
||||
int need_complex_escape = 0;
|
||||
@@ -1117,8 +1120,8 @@ static bool unescape_string_internal(const wchar_t *const input, const size_t in
|
||||
wcstring result;
|
||||
result.reserve(input_len);
|
||||
|
||||
const bool unescape_special = !!(flags & UNESCAPE_SPECIAL);
|
||||
const bool allow_incomplete = !!(flags & UNESCAPE_INCOMPLETE);
|
||||
const bool unescape_special = static_cast<bool>(flags & UNESCAPE_SPECIAL);
|
||||
const bool allow_incomplete = static_cast<bool>(flags & UNESCAPE_INCOMPLETE);
|
||||
|
||||
int bracket_count = 0;
|
||||
|
||||
@@ -1220,6 +1223,7 @@ static bool unescape_string_internal(const wchar_t *const input, const size_t in
|
||||
to_append_or_none = unescape_special ? INTERNAL_SEPARATOR : NOT_A_WCHAR;
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
}
|
||||
} else if (mode == mode_single_quotes) {
|
||||
if (c == L'\\') {
|
||||
@@ -1297,6 +1301,7 @@ static bool unescape_string_internal(const wchar_t *const input, const size_t in
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1505,7 +1510,7 @@ bool list_contains_string(const wcstring_list_t &list, const wcstring &str) {
|
||||
}
|
||||
|
||||
int create_directory(const wcstring &d) {
|
||||
int ok = 0;
|
||||
bool ok = false;
|
||||
struct stat buf;
|
||||
int stat_res = 0;
|
||||
|
||||
@@ -1514,18 +1519,10 @@ int create_directory(const wcstring &d) {
|
||||
}
|
||||
|
||||
if (stat_res == 0) {
|
||||
if (S_ISDIR(buf.st_mode)) {
|
||||
ok = 1;
|
||||
}
|
||||
} else {
|
||||
if (errno == ENOENT) {
|
||||
wcstring dir = wdirname(d);
|
||||
if (!create_directory(dir)) {
|
||||
if (!wmkdir(d, 0700)) {
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (S_ISDIR(buf.st_mode)) ok = true;
|
||||
} else if (errno == ENOENT) {
|
||||
wcstring dir = wdirname(d);
|
||||
if (!create_directory(dir) && !wmkdir(d, 0700)) ok = true;
|
||||
}
|
||||
|
||||
return ok ? 0 : -1;
|
||||
@@ -1682,9 +1679,7 @@ __attribute__((noinline)) void debug_thread_error(void) {
|
||||
|
||||
void set_main_thread() { main_thread_id = pthread_self(); }
|
||||
|
||||
void configure_thread_assertions_for_testing(void) {
|
||||
thread_assertions_configured_for_testing = true;
|
||||
}
|
||||
void configure_thread_assertions_for_testing(void) { thread_asserts_cfg_for_testing = true; }
|
||||
|
||||
bool is_forked_child(void) {
|
||||
// Just bail if nobody's called setup_fork_guards, e.g. some of our tools.
|
||||
@@ -1704,14 +1699,14 @@ void setup_fork_guards(void) {
|
||||
}
|
||||
|
||||
void save_term_foreground_process_group(void) {
|
||||
initial_foreground_process_group = tcgetpgrp(STDIN_FILENO);
|
||||
initial_fg_process_group = tcgetpgrp(STDIN_FILENO);
|
||||
}
|
||||
|
||||
void restore_term_foreground_process_group(void) {
|
||||
if (initial_foreground_process_group != -1) {
|
||||
if (initial_fg_process_group != -1) {
|
||||
// This is called during shutdown and from a signal handler. We don't bother to complain on
|
||||
// failure.
|
||||
tcsetpgrp(STDIN_FILENO, initial_foreground_process_group);
|
||||
tcsetpgrp(STDIN_FILENO, initial_fg_process_group);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1721,7 +1716,7 @@ bool is_main_thread() {
|
||||
}
|
||||
|
||||
void assert_is_main_thread(const char *who) {
|
||||
if (!is_main_thread() && !thread_assertions_configured_for_testing) {
|
||||
if (!is_main_thread() && !thread_asserts_cfg_for_testing) {
|
||||
fprintf(stderr,
|
||||
"Warning: %s called off of main thread. Break on debug_thread_error to debug.\n",
|
||||
who);
|
||||
@@ -1739,7 +1734,7 @@ void assert_is_not_forked_child(const char *who) {
|
||||
}
|
||||
|
||||
void assert_is_background_thread(const char *who) {
|
||||
if (is_main_thread() && !thread_assertions_configured_for_testing) {
|
||||
if (is_main_thread() && !thread_asserts_cfg_for_testing) {
|
||||
fprintf(stderr,
|
||||
"Warning: %s called on the main thread (may block!). Break on debug_thread_error "
|
||||
"to debug.\n",
|
||||
@@ -1761,7 +1756,7 @@ void assert_is_locked(void *vmutex, const char *who, const char *caller) {
|
||||
}
|
||||
|
||||
void scoped_lock::lock(void) {
|
||||
assert(!locked);
|
||||
assert(!locked); //!OCLINT(multiple unary operator)
|
||||
ASSERT_IS_NOT_FORKED_CHILD();
|
||||
VOMIT_ON_FAILURE_NO_ERRNO(pthread_mutex_lock(lock_obj));
|
||||
locked = true;
|
||||
@@ -1785,7 +1780,7 @@ scoped_lock::~scoped_lock() {
|
||||
}
|
||||
|
||||
void scoped_rwlock::lock(void) {
|
||||
assert(!(locked || locked_shared));
|
||||
assert(!(locked || locked_shared)); //!OCLINT(multiple unary operator)
|
||||
ASSERT_IS_NOT_FORKED_CHILD();
|
||||
VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_rdlock(rwlock_obj));
|
||||
locked = true;
|
||||
@@ -1799,7 +1794,7 @@ void scoped_rwlock::unlock(void) {
|
||||
}
|
||||
|
||||
void scoped_rwlock::lock_shared(void) {
|
||||
assert(!(locked || locked_shared));
|
||||
assert(!(locked || locked_shared)); //!OCLINT(multiple unary operator)
|
||||
ASSERT_IS_NOT_FORKED_CHILD();
|
||||
VOMIT_ON_FAILURE_NO_ERRNO(pthread_rwlock_wrlock(rwlock_obj));
|
||||
locked_shared = true;
|
||||
@@ -1935,3 +1930,17 @@ long convert_digit(wchar_t d, int base) {
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// Test if the specified character is in a range that fish uses interally to store special tokens.
|
||||
//
|
||||
// NOTE: This is used when tokenizing the input. It is also used when reading input, before
|
||||
// tokenization, to replace such chars with REPLACEMENT_WCHAR if they're not part of a quoted
|
||||
// string. We don't want external input to be able to feed reserved characters into our lexer/parser
|
||||
// or code evaluator.
|
||||
//
|
||||
// TODO: Actually implement the replacement as documented above.
|
||||
bool fish_reserved_codepoint(wchar_t c) {
|
||||
return (c >= RESERVED_CHAR_BASE && c < RESERVED_CHAR_END) ||
|
||||
(c >= ENCODE_DIRECT_BASE && c < ENCODE_DIRECT_END) ||
|
||||
(c >= INPUT_COMMON_BASE && c < INPUT_COMMON_END);
|
||||
}
|
||||
|
||||
@@ -211,6 +211,9 @@ extern bool has_working_tty_timestamps;
|
||||
}
|
||||
|
||||
/// Pause for input, then exit the program. If supported, print a backtrace first.
|
||||
// The `return` will never be run but silences oclint warnings. Especially when this is called
|
||||
// from within a `switch` block. As of the time I'm writing this oclint doesn't recognize the
|
||||
// `__attribute__((noreturn))` on the exit_without_destructors() function.
|
||||
#define FATAL_EXIT() \
|
||||
{ \
|
||||
char exit_read_buff; \
|
||||
@@ -258,7 +261,7 @@ extern bool has_working_tty_timestamps;
|
||||
#define contains(str, ...) contains_internal(str, 0, __VA_ARGS__, NULL)
|
||||
|
||||
/// Print a stack trace to stderr.
|
||||
void show_stackframe(const wchar_t msg_level, int frame_count = -1, int skip_levels = 0);
|
||||
void show_stackframe(const wchar_t msg_level, int frame_count = 100, int skip_levels = 0);
|
||||
|
||||
/// Read a line from the stream f into the string. Returns the number of bytes read or -1 on
|
||||
/// failure.
|
||||
@@ -776,3 +779,6 @@ long convert_digit(wchar_t d, int base);
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
// Return true if the character is in a range reserved for fish's private use.
|
||||
bool fish_reserved_codepoint(wchar_t c);
|
||||
|
||||
286
src/complete.cpp
286
src/complete.cpp
@@ -119,7 +119,7 @@ typedef struct complete_entry_opt {
|
||||
case option_type_double_long:
|
||||
return 2;
|
||||
}
|
||||
assert(0 && "Unreachable");
|
||||
DIE("unreachable");
|
||||
}
|
||||
|
||||
} complete_entry_opt_t;
|
||||
@@ -289,9 +289,11 @@ class completer_t {
|
||||
return flags & COMPLETION_REQUEST_AUTOSUGGESTION ? COMPLETE_AUTOSUGGEST : COMPLETE_DEFAULT;
|
||||
}
|
||||
|
||||
bool wants_descriptions() const { return !!(flags & COMPLETION_REQUEST_DESCRIPTIONS); }
|
||||
bool wants_descriptions() const {
|
||||
return static_cast<bool>(flags & COMPLETION_REQUEST_DESCRIPTIONS);
|
||||
}
|
||||
|
||||
bool fuzzy() const { return !!(flags & COMPLETION_REQUEST_FUZZY_MATCH); }
|
||||
bool fuzzy() const { return static_cast<bool>(flags & COMPLETION_REQUEST_FUZZY_MATCH); }
|
||||
|
||||
fuzzy_match_type_t max_fuzzy_match_type() const {
|
||||
// If we are doing fuzzy matching, request all types; if not request only prefix matching.
|
||||
@@ -668,22 +670,22 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool
|
||||
std::vector<completion_t> possible_comp;
|
||||
|
||||
if (use_command) {
|
||||
if (expand_string(str_cmd, &this->completions,
|
||||
EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS | EXECUTABLES_ONLY |
|
||||
this->expand_flags(),
|
||||
NULL) != EXPAND_ERROR) {
|
||||
if (this->wants_descriptions()) {
|
||||
this->complete_cmd_desc(str_cmd);
|
||||
expand_error_t result = expand_string(str_cmd, &this->completions,
|
||||
EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS |
|
||||
EXECUTABLES_ONLY | this->expand_flags(),
|
||||
NULL);
|
||||
if (result != EXPAND_ERROR && this->wants_descriptions()) {
|
||||
this->complete_cmd_desc(str_cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (use_implicit_cd) {
|
||||
if (!expand_string(str_cmd, &this->completions,
|
||||
EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(),
|
||||
NULL)) {
|
||||
// Not valid as implicit cd.
|
||||
}
|
||||
// We don't really care if this succeeds or fails. If it succeeds this->completions will be
|
||||
// updated with choices for the user.
|
||||
(void)expand_string(str_cmd, &this->completions,
|
||||
EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), NULL);
|
||||
}
|
||||
|
||||
if (str_cmd.find(L'/') == wcstring::npos && str_cmd.at(0) != L'~') {
|
||||
if (use_function) {
|
||||
wcstring_list_t names = function_get_names(str_cmd.at(0) == L'_');
|
||||
@@ -875,11 +877,10 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop
|
||||
if (this->type() == COMPLETE_DEFAULT) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
complete_load(cmd, true);
|
||||
} else if (this->type() == COMPLETE_AUTOSUGGEST) {
|
||||
// Maybe load this command (on the main thread).
|
||||
if (!completion_autoloader.has_tried_loading(cmd)) {
|
||||
iothread_perform_on_main(complete_load_no_reload, &cmd);
|
||||
}
|
||||
} else if (this->type() == COMPLETE_AUTOSUGGEST &&
|
||||
!completion_autoloader.has_tried_loading(cmd)) {
|
||||
// Load this command (on the main thread).
|
||||
iothread_perform_on_main(complete_load_no_reload, &cmd);
|
||||
}
|
||||
|
||||
// Make a list of lists of all options that we care about.
|
||||
@@ -925,13 +926,12 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop
|
||||
for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end();
|
||||
++oiter) {
|
||||
const complete_entry_opt_t *o = &*oiter;
|
||||
if (o->type == option_type_single_long) {
|
||||
if (param_match(o, popt) && this->condition_test(o->condition)) {
|
||||
old_style_match = true;
|
||||
if (o->result_mode & NO_COMMON) use_common = false;
|
||||
if (o->result_mode & NO_FILES) use_files = false;
|
||||
complete_from_args(str, o->comp, o->localized_desc(), o->flags);
|
||||
}
|
||||
if (o->type == option_type_single_long && param_match(o, popt) &&
|
||||
this->condition_test(o->condition)) {
|
||||
old_style_match = true;
|
||||
if (o->result_mode & NO_COMMON) use_common = false;
|
||||
if (o->result_mode & NO_FILES) use_files = false;
|
||||
complete_from_args(str, o->comp, o->localized_desc(), o->flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -956,71 +956,73 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop
|
||||
}
|
||||
}
|
||||
|
||||
if (use_common) {
|
||||
for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end();
|
||||
++oiter) {
|
||||
const complete_entry_opt_t *o = &*oiter;
|
||||
// If this entry is for the base command, check if any of the arguments match.
|
||||
if (!this->condition_test(o->condition)) continue;
|
||||
if (o->option.empty()) {
|
||||
use_files = use_files && ((o->result_mode & NO_FILES) == 0);
|
||||
complete_from_args(str, o->comp, o->localized_desc(), o->flags);
|
||||
}
|
||||
if (!use_common) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wcslen(str) > 0 && use_switches) {
|
||||
// Check if the short style option matches.
|
||||
if (short_ok(str, o, options)) {
|
||||
// It's a match.
|
||||
const wcstring desc = o->localized_desc();
|
||||
append_completion(&this->completions, o->option, desc, 0);
|
||||
}
|
||||
|
||||
// Check if the long style option matches.
|
||||
if (o->type == option_type_single_long || o->type == option_type_double_long) {
|
||||
int match = 0, match_no_case = 0;
|
||||
|
||||
wcstring whole_opt(o->expected_dash_count(), L'-');
|
||||
whole_opt.append(o->option);
|
||||
|
||||
match = string_prefixes_string(str, whole_opt);
|
||||
|
||||
if (!match) {
|
||||
match_no_case = wcsncasecmp(str, whole_opt.c_str(), wcslen(str)) == 0;
|
||||
}
|
||||
|
||||
if (match || match_no_case) {
|
||||
int has_arg = 0; // does this switch have any known arguments
|
||||
int req_arg = 0; // does this switch _require_ an argument
|
||||
|
||||
size_t offset = 0;
|
||||
complete_flags_t flags = 0;
|
||||
|
||||
if (match) {
|
||||
offset = wcslen(str);
|
||||
} else {
|
||||
flags = COMPLETE_REPLACES_TOKEN;
|
||||
}
|
||||
|
||||
has_arg = !o->comp.empty();
|
||||
req_arg = (o->result_mode & NO_COMMON);
|
||||
|
||||
if (o->type == option_type_double_long && (has_arg && !req_arg)) {
|
||||
// Optional arguments to a switch can only be handled using the '=',
|
||||
// so we add it as a completion. By default we avoid using '=' and
|
||||
// instead rely on '--switch switch-arg', since it is more commonly
|
||||
// supported by homebrew getopt-like functions.
|
||||
wcstring completion =
|
||||
format_string(L"%ls=", whole_opt.c_str() + offset);
|
||||
append_completion(&this->completions, completion, C_(o->desc),
|
||||
flags);
|
||||
}
|
||||
|
||||
append_completion(&this->completions, whole_opt.c_str() + offset,
|
||||
C_(o->desc), flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (option_list_t::const_iterator oiter = options.begin(); oiter != options.end();
|
||||
++oiter) {
|
||||
const complete_entry_opt_t *o = &*oiter;
|
||||
// If this entry is for the base command, check if any of the arguments match.
|
||||
if (!this->condition_test(o->condition)) continue;
|
||||
if (o->option.empty()) {
|
||||
use_files = use_files && ((o->result_mode & NO_FILES) == 0);
|
||||
complete_from_args(str, o->comp, o->localized_desc(), o->flags);
|
||||
}
|
||||
|
||||
if (wcslen(str) == 0 || !use_switches) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the short style option matches.
|
||||
if (short_ok(str, o, options)) {
|
||||
// It's a match.
|
||||
const wcstring desc = o->localized_desc();
|
||||
append_completion(&this->completions, o->option, desc, 0);
|
||||
}
|
||||
|
||||
// Check if the long style option matches.
|
||||
if (o->type != option_type_single_long && o->type != option_type_double_long) {
|
||||
continue;
|
||||
}
|
||||
int match = 0, match_no_case = 0;
|
||||
|
||||
wcstring whole_opt(o->expected_dash_count(), L'-');
|
||||
whole_opt.append(o->option);
|
||||
|
||||
match = string_prefixes_string(str, whole_opt);
|
||||
if (!match) {
|
||||
match_no_case = wcsncasecmp(str, whole_opt.c_str(), wcslen(str)) == 0;
|
||||
}
|
||||
|
||||
if (!match && !match_no_case) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int has_arg = 0; // does this switch have any known arguments
|
||||
int req_arg = 0; // does this switch _require_ an argument
|
||||
size_t offset = 0;
|
||||
complete_flags_t flags = 0;
|
||||
|
||||
if (match) {
|
||||
offset = wcslen(str);
|
||||
} else {
|
||||
flags = COMPLETE_REPLACES_TOKEN;
|
||||
}
|
||||
|
||||
has_arg = !o->comp.empty();
|
||||
req_arg = (o->result_mode & NO_COMMON);
|
||||
|
||||
if (o->type == option_type_double_long && (has_arg && !req_arg)) {
|
||||
// Optional arguments to a switch can only be handled using the '=', so we add it as
|
||||
// a completion. By default we avoid using '=' and instead rely on '--switch
|
||||
// switch-arg', since it is more commonly supported by homebrew getopt-like
|
||||
// functions.
|
||||
wcstring completion = format_string(L"%ls=", whole_opt.c_str() + offset);
|
||||
append_completion(&this->completions, completion, C_(o->desc), flags);
|
||||
}
|
||||
|
||||
append_completion(&this->completions, whole_opt.c_str() + offset, C_(o->desc), flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1144,31 +1146,35 @@ bool completer_t::try_complete_variable(const wcstring &str) {
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case L'\\':
|
||||
case L'\\': {
|
||||
in_pos++;
|
||||
break;
|
||||
|
||||
case L'$':
|
||||
}
|
||||
case L'$': {
|
||||
if (mode == e_unquoted || mode == e_double_quoted) {
|
||||
variable_start = in_pos;
|
||||
}
|
||||
break;
|
||||
|
||||
case L'\'':
|
||||
}
|
||||
case L'\'': {
|
||||
if (mode == e_single_quoted) {
|
||||
mode = e_unquoted;
|
||||
} else if (mode == e_unquoted) {
|
||||
mode = e_single_quoted;
|
||||
}
|
||||
break;
|
||||
|
||||
case L'"':
|
||||
}
|
||||
case L'"': {
|
||||
if (mode == e_double_quoted) {
|
||||
mode = e_unquoted;
|
||||
} else if (mode == e_unquoted) {
|
||||
mode = e_double_quoted;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break; // all other chars ignored here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1196,50 +1202,52 @@ bool completer_t::try_complete_user(const wcstring &str) {
|
||||
#else
|
||||
const wchar_t *cmd = str.c_str();
|
||||
const wchar_t *first_char = cmd;
|
||||
int res = 0;
|
||||
double start_time = timef();
|
||||
|
||||
if (*first_char == L'~' && !wcschr(first_char, L'/')) {
|
||||
const wchar_t *user_name = first_char + 1;
|
||||
const wchar_t *name_end = wcschr(user_name, L'~');
|
||||
if (name_end == 0) {
|
||||
struct passwd *pw;
|
||||
size_t name_len = wcslen(user_name);
|
||||
|
||||
setpwent();
|
||||
|
||||
while ((pw = getpwent()) != 0) {
|
||||
double current_time = timef();
|
||||
|
||||
if (current_time - start_time > 0.2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pw->pw_name) {
|
||||
const wcstring pw_name_str = str2wcstring(pw->pw_name);
|
||||
const wchar_t *pw_name = pw_name_str.c_str();
|
||||
if (wcsncmp(user_name, pw_name, name_len) == 0) {
|
||||
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
|
||||
append_completion(&this->completions, &pw_name[name_len], desc,
|
||||
COMPLETE_NO_SPACE);
|
||||
|
||||
res = 1;
|
||||
} else if (wcsncasecmp(user_name, pw_name, name_len) == 0) {
|
||||
wcstring name = format_string(L"~%ls", pw_name);
|
||||
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
|
||||
|
||||
append_completion(&this->completions, name, desc, COMPLETE_REPLACES_TOKEN |
|
||||
COMPLETE_DONT_ESCAPE |
|
||||
COMPLETE_NO_SPACE);
|
||||
res = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
endpwent();
|
||||
}
|
||||
if (*first_char != L'~' || wcschr(first_char, L'/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return res;
|
||||
const wchar_t *user_name = first_char + 1;
|
||||
const wchar_t *name_end = wcschr(user_name, L'~');
|
||||
if (name_end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double start_time = timef();
|
||||
bool result = false;
|
||||
struct passwd *pw;
|
||||
size_t name_len = wcslen(user_name);
|
||||
|
||||
setpwent();
|
||||
while ((pw = getpwent()) != 0) {
|
||||
double current_time = timef();
|
||||
if (current_time - start_time > 0.2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!pw->pw_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const wcstring pw_name_str = str2wcstring(pw->pw_name);
|
||||
const wchar_t *pw_name = pw_name_str.c_str();
|
||||
if (wcsncmp(user_name, pw_name, name_len) == 0) {
|
||||
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
|
||||
append_completion(&this->completions, &pw_name[name_len], desc, COMPLETE_NO_SPACE);
|
||||
|
||||
result = true;
|
||||
} else if (wcsncasecmp(user_name, pw_name, name_len) == 0) {
|
||||
wcstring name = format_string(L"~%ls", pw_name);
|
||||
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
|
||||
|
||||
append_completion(&this->completions, name, desc, COMPLETE_REPLACES_TOKEN |
|
||||
COMPLETE_DONT_ESCAPE |
|
||||
COMPLETE_NO_SPACE);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
endpwent();
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1534,7 +1542,7 @@ wcstring complete_print() {
|
||||
break;
|
||||
}
|
||||
case option_type_short: {
|
||||
assert(!o->option.empty());
|
||||
assert(!o->option.empty()); //!OCLINT(multiple unary operator)
|
||||
append_format(out, L" --short-option '%lc'", o->option.at(0));
|
||||
break;
|
||||
}
|
||||
|
||||
41
src/env.cpp
41
src/env.cpp
@@ -283,10 +283,6 @@ static void universal_callback(fish_message_type_t type, const wchar_t *name) {
|
||||
str = L"ERASE";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(0 && "Unhandled fish_message_type_t constant!");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (str) {
|
||||
@@ -476,6 +472,25 @@ static env_node_t *env_get_node(const wcstring &key) {
|
||||
return env;
|
||||
}
|
||||
|
||||
/// Set the value of the environment variable whose name matches key to val.
|
||||
///
|
||||
/// Memory policy: All keys and values are copied, the parameters can and should be freed by the
|
||||
/// caller afterwards
|
||||
///
|
||||
/// \param key The key
|
||||
/// \param val The value
|
||||
/// \param var_mode The type of the variable. Can be any combination of ENV_GLOBAL, ENV_LOCAL,
|
||||
/// ENV_EXPORT and ENV_USER. If mode is zero, the current variable space is searched and the current
|
||||
/// mode is used. If no current variable with the same name is found, ENV_LOCAL is assumed.
|
||||
///
|
||||
/// Returns:
|
||||
///
|
||||
/// * ENV_OK on success.
|
||||
/// * ENV_PERM, can only be returned when setting as a user, e.g. ENV_USER is set. This means that
|
||||
/// the user tried to change a read-only variable.
|
||||
/// * ENV_SCOPE, the variable cannot be set in the given scope. This applies to readonly/electric
|
||||
/// variables set from the local or universal scopes, or set as exported.
|
||||
/// * ENV_INVALID, the variable value was invalid. This applies only to special variables.
|
||||
int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
bool has_changed_old = has_changed_exported;
|
||||
@@ -512,7 +527,7 @@ int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode)
|
||||
umask(mask);
|
||||
// Do not actually create a umask variable, on env_get, it will be calculated
|
||||
// dynamically.
|
||||
return 0;
|
||||
return ENV_OK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,7 +536,7 @@ int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode)
|
||||
|
||||
// Zero element arrays are internaly not coded as null but as this placeholder string.
|
||||
if (!val) {
|
||||
val = ENV_NULL;
|
||||
val = ENV_NULL; //!OCLINT(parameter reassignment)
|
||||
}
|
||||
|
||||
if (var_mode & ENV_UNIVERSAL) {
|
||||
@@ -566,10 +581,10 @@ int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode)
|
||||
node = top;
|
||||
} else if (preexisting_node != NULL) {
|
||||
node = preexisting_node;
|
||||
|
||||
if ((var_mode & (ENV_EXPORT | ENV_UNEXPORT)) == 0) {
|
||||
// use existing entry's exportv
|
||||
var_mode = preexisting_entry_exportv ? ENV_EXPORT : 0;
|
||||
var_mode = //!OCLINT(parameter reassignment)
|
||||
preexisting_entry_exportv ? ENV_EXPORT : 0;
|
||||
}
|
||||
} else {
|
||||
if (!get_proc_had_barrier()) {
|
||||
@@ -635,7 +650,7 @@ int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t var_mode)
|
||||
// debug( 1, L"env_set: return from event firing" );
|
||||
|
||||
react_to_variable_change(key);
|
||||
return 0;
|
||||
return ENV_OK;
|
||||
}
|
||||
|
||||
/// Attempt to remove/free the specified key/value pair from the specified map.
|
||||
@@ -713,7 +728,7 @@ int env_remove(const wcstring &key, int var_mode) {
|
||||
}
|
||||
|
||||
const wchar_t *env_var_t::c_str(void) const {
|
||||
assert(!is_missing);
|
||||
assert(!is_missing); //!OCLINT(multiple unary operator)
|
||||
return wcstring::c_str();
|
||||
}
|
||||
|
||||
@@ -864,9 +879,7 @@ void env_push(bool new_scope) {
|
||||
node->next = top;
|
||||
node->new_scope = new_scope;
|
||||
|
||||
if (new_scope) {
|
||||
if (local_scope_exports(top)) mark_changed_exported();
|
||||
}
|
||||
if (new_scope && local_scope_exports(top)) mark_changed_exported();
|
||||
top = node;
|
||||
}
|
||||
|
||||
@@ -884,7 +897,7 @@ void env_pop() {
|
||||
}
|
||||
}
|
||||
|
||||
if (killme->new_scope) {
|
||||
if (killme->new_scope) { //!OCLINT(collapsible if statements)
|
||||
if (killme->exportv || local_scope_exports(killme->next)) mark_changed_exported();
|
||||
}
|
||||
|
||||
|
||||
24
src/env.h
24
src/env.h
@@ -36,8 +36,8 @@ enum {
|
||||
};
|
||||
typedef uint32_t env_mode_flags_t;
|
||||
|
||||
/// Error code for trying to alter read-only variable.
|
||||
enum { ENV_PERM = 1, ENV_SCOPE, ENV_INVALID };
|
||||
/// Return values for `env_set()`.
|
||||
enum { ENV_OK, ENV_PERM, ENV_SCOPE, ENV_INVALID };
|
||||
|
||||
/// A struct of configuration directories, determined in main() that fish will optionally pass to
|
||||
/// env_init.
|
||||
@@ -51,26 +51,6 @@ struct config_paths_t {
|
||||
/// Initialize environment variable data.
|
||||
void env_init(const struct config_paths_t *paths = NULL);
|
||||
|
||||
/// Set the value of the environment variable whose name matches key to val.
|
||||
///
|
||||
/// Memory policy: All keys and values are copied, the parameters can and should be freed by the
|
||||
/// caller afterwards
|
||||
///
|
||||
/// \param key The key
|
||||
/// \param val The value
|
||||
/// \param mode The type of the variable. Can be any combination of ENV_GLOBAL, ENV_LOCAL,
|
||||
/// ENV_EXPORT and ENV_USER. If mode is zero, the current variable space is searched and the current
|
||||
/// mode is used. If no current variable with the same name is found, ENV_LOCAL is assumed.
|
||||
///
|
||||
/// \returns 0 on success or an error code on failiure.
|
||||
///
|
||||
/// The current error codes are:
|
||||
///
|
||||
/// * ENV_PERM, can only be returned when setting as a user, e.g. ENV_USER is set. This means that
|
||||
/// the user tried to change a read-only variable.
|
||||
/// * ENV_SCOPE, the variable cannot be set in the given scope. This applies to readonly/electric
|
||||
/// variables set from the local or universal scopes, or set as exported.
|
||||
/// * ENV_INVALID, the variable value was invalid. This applies only to special variables.
|
||||
int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t mode);
|
||||
|
||||
class env_var_t : public wcstring {
|
||||
|
||||
@@ -930,23 +930,24 @@ static bool get_mac_address(unsigned char macaddr[MAC_ADDRESS_MAX_LEN],
|
||||
struct ifaddrs *ifap;
|
||||
bool ok = false;
|
||||
|
||||
if (getifaddrs(&ifap) == 0) {
|
||||
for (const ifaddrs *p = ifap; p; p = p->ifa_next) {
|
||||
if (p->ifa_addr && p->ifa_addr->sa_family == AF_LINK) {
|
||||
if (p->ifa_name && p->ifa_name[0] &&
|
||||
!strcmp((const char *)p->ifa_name, interface)) {
|
||||
const sockaddr_dl &sdl = *reinterpret_cast<sockaddr_dl *>(p->ifa_addr);
|
||||
|
||||
size_t alen = sdl.sdl_alen;
|
||||
if (alen > MAC_ADDRESS_MAX_LEN) alen = MAC_ADDRESS_MAX_LEN;
|
||||
memcpy(macaddr, sdl.sdl_data + sdl.sdl_nlen, alen);
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
freeifaddrs(ifap);
|
||||
if (getifaddrs(&ifap) != 0) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
for (const ifaddrs *p = ifap; p; p = p->ifa_next) {
|
||||
bool is_af_link = p->ifa_addr && p->ifa_addr->sa_family == AF_LINK;
|
||||
if (is_af_link && p->ifa_name && p->ifa_name[0] &&
|
||||
!strcmp((const char *)p->ifa_name, interface)) {
|
||||
const sockaddr_dl &sdl = *reinterpret_cast<sockaddr_dl *>(p->ifa_addr);
|
||||
|
||||
size_t alen = sdl.sdl_alen;
|
||||
if (alen > MAC_ADDRESS_MAX_LEN) alen = MAC_ADDRESS_MAX_LEN;
|
||||
memcpy(macaddr, sdl.sdl_data + sdl.sdl_nlen, alen);
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeifaddrs(ifap);
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -978,11 +979,8 @@ wcstring get_machine_identifier() {
|
||||
for (size_t i = 0; i < MAC_ADDRESS_MAX_LEN; i++) {
|
||||
append_format(result, L"%02x", mac_addr[i]);
|
||||
}
|
||||
} else if (get_hostname_identifier(&result)) {
|
||||
// Hooray
|
||||
} else {
|
||||
// Fallback
|
||||
result.assign(L"nohost");
|
||||
} else if (!get_hostname_identifier(&result)) {
|
||||
result.assign(L"nohost"); // fallback to a dummy value
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1033,12 +1031,11 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
||||
}
|
||||
|
||||
// Set the size, if it's too small.
|
||||
if (!errored && size < (off_t)sizeof(universal_notifier_shmem_t)) {
|
||||
if (ftruncate(fd, sizeof(universal_notifier_shmem_t)) < 0) {
|
||||
int err = errno;
|
||||
report_error(err, L"Unable to truncate shared memory object with path '%s'", path);
|
||||
errored = true;
|
||||
}
|
||||
bool set_size = !errored && size < (off_t)sizeof(universal_notifier_shmem_t);
|
||||
if (set_size && ftruncate(fd, sizeof(universal_notifier_shmem_t)) < 0) {
|
||||
int err = errno;
|
||||
report_error(err, L"Unable to truncate shared memory object with path '%s'", path);
|
||||
errored = true;
|
||||
}
|
||||
|
||||
// Memory map the region.
|
||||
@@ -1074,7 +1071,7 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
||||
void post_notification() {
|
||||
if (region != NULL) {
|
||||
/* Read off the seed */
|
||||
uint32_t seed = ntohl(region->universal_variable_seed);
|
||||
uint32_t seed = ntohl(region->universal_variable_seed); //!OCLINT(constant cond op)
|
||||
|
||||
// Increment it. Don't let it wrap to zero.
|
||||
do {
|
||||
@@ -1083,9 +1080,9 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
||||
last_seed = seed;
|
||||
|
||||
// Write out our data.
|
||||
region->magic = htonl(SHMEM_MAGIC_NUMBER);
|
||||
region->version = htonl(SHMEM_VERSION_CURRENT);
|
||||
region->universal_variable_seed = htonl(seed);
|
||||
region->magic = htonl(SHMEM_MAGIC_NUMBER); //!OCLINT(constant cond op)
|
||||
region->version = htonl(SHMEM_VERSION_CURRENT); //!OCLINT(constant cond op)
|
||||
region->universal_variable_seed = htonl(seed); //!OCLINT(constant cond op)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1106,7 +1103,7 @@ class universal_notifier_shmem_poller_t : public universal_notifier_t {
|
||||
bool poll() {
|
||||
bool result = false;
|
||||
if (region != NULL) {
|
||||
uint32_t seed = ntohl(region->universal_variable_seed);
|
||||
uint32_t seed = ntohl(region->universal_variable_seed); //!OCLINT(constant cond op)
|
||||
if (seed != last_seed) {
|
||||
result = true;
|
||||
last_seed = seed;
|
||||
@@ -1237,10 +1234,12 @@ class universal_notifier_named_pipe_t : public universal_notifier_t {
|
||||
int fd = wopen_cloexec(vars_path, O_RDWR | O_NONBLOCK, 0600);
|
||||
if (fd < 0 && errno == ENOENT) {
|
||||
// File doesn't exist, try creating it.
|
||||
if (mkfifo(narrow_path.c_str(), 0600) >= 0) {
|
||||
int mkfifo_status = mkfifo(narrow_path.c_str(), 0600);
|
||||
if (mkfifo_status != -1) {
|
||||
fd = wopen_cloexec(vars_path, O_RDWR | O_NONBLOCK, 0600);
|
||||
}
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
// Maybe open failed, maybe mkfifo failed.
|
||||
int err = errno;
|
||||
@@ -1314,13 +1313,11 @@ class universal_notifier_named_pipe_t : public universal_notifier_t {
|
||||
if (pipe_fd >= 0) {
|
||||
// We need to write some data (any data) to the pipe, then wait for a while, then read
|
||||
// it back. Nobody is expected to read it except us.
|
||||
int pid_nbo = htonl(getpid());
|
||||
int pid_nbo = htonl(getpid()); //!OCLINT(constant cond op)
|
||||
ssize_t amt_written = write(this->pipe_fd, &pid_nbo, sizeof pid_nbo);
|
||||
if (amt_written < 0) {
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
// Very unsual: the pipe is full!
|
||||
drain_excessive_data();
|
||||
}
|
||||
if (amt_written < 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
|
||||
// Very unsual: the pipe is full!
|
||||
drain_excessive_data();
|
||||
}
|
||||
|
||||
// Now schedule a read for some time in the future.
|
||||
@@ -1358,8 +1355,6 @@ class universal_notifier_named_pipe_t : public universal_notifier_t {
|
||||
}
|
||||
|
||||
bool poll() {
|
||||
bool result = false;
|
||||
|
||||
// Check if we are past the readback time.
|
||||
if (this->readback_time_usec > 0 && get_time() >= this->readback_time_usec) {
|
||||
// Read back what we wrote. We do nothing with the value.
|
||||
@@ -1374,30 +1369,29 @@ class universal_notifier_named_pipe_t : public universal_notifier_t {
|
||||
}
|
||||
|
||||
// Check to see if we are doing readability polling.
|
||||
if (polling_due_to_readable_fd && pipe_fd >= 0) {
|
||||
// We are polling, so we are definitely going to sync.
|
||||
result = true;
|
||||
|
||||
// See if this is still readable.
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(this->pipe_fd, &fds);
|
||||
struct timeval timeout = {};
|
||||
select(this->pipe_fd + 1, &fds, NULL, NULL, &timeout);
|
||||
if (!FD_ISSET(this->pipe_fd, &fds)) {
|
||||
// No longer readable, no longer polling.
|
||||
polling_due_to_readable_fd = false;
|
||||
drain_if_still_readable_time_usec = 0;
|
||||
} else {
|
||||
// Still readable. If it's been readable for a long time, there is probably
|
||||
// lingering data on the pipe.
|
||||
if (get_time() >= drain_if_still_readable_time_usec) {
|
||||
drain_excessive_data();
|
||||
}
|
||||
}
|
||||
if (!polling_due_to_readable_fd || pipe_fd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
// We are polling, so we are definitely going to sync.
|
||||
// See if this is still readable.
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(this->pipe_fd, &fds);
|
||||
struct timeval timeout = {};
|
||||
select(this->pipe_fd + 1, &fds, NULL, NULL, &timeout);
|
||||
if (!FD_ISSET(this->pipe_fd, &fds)) {
|
||||
// No longer readable, no longer polling.
|
||||
polling_due_to_readable_fd = false;
|
||||
drain_if_still_readable_time_usec = 0;
|
||||
} else {
|
||||
// Still readable. If it's been readable for a long time, there is probably
|
||||
// lingering data on the pipe.
|
||||
if (get_time() >= drain_if_still_readable_time_usec) {
|
||||
drain_excessive_data();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1416,23 +1410,25 @@ static universal_notifier_t::notifier_strategy_t fetch_default_strategy_from_env
|
||||
const size_t opt_count = sizeof options / sizeof *options;
|
||||
|
||||
const char *var = getenv(UNIVERSAL_NOTIFIER_ENV_NAME);
|
||||
if (var != NULL && var[0] != '\0') {
|
||||
size_t i;
|
||||
for (i = 0; i < opt_count; i++) {
|
||||
if (!strcmp(var, options[i].name)) {
|
||||
result = options[i].strat;
|
||||
break;
|
||||
}
|
||||
if (var == NULL || var[0] == '\0') {
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < opt_count; i++) {
|
||||
if (!strcmp(var, options[i].name)) {
|
||||
result = options[i].strat;
|
||||
break;
|
||||
}
|
||||
if (i >= opt_count) {
|
||||
fprintf(stderr, "Warning: unrecognized value for %s: '%s'\n",
|
||||
UNIVERSAL_NOTIFIER_ENV_NAME, var);
|
||||
fprintf(stderr, "Warning: valid values are ");
|
||||
for (size_t j = 0; j < opt_count; j++) {
|
||||
fprintf(stderr, "%s%s", j > 0 ? ", " : "", options[j].name);
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
if (i >= opt_count) {
|
||||
fprintf(stderr, "Warning: unrecognized value for %s: '%s'\n",
|
||||
UNIVERSAL_NOTIFIER_ENV_NAME, var);
|
||||
fprintf(stderr, "Warning: valid values are ");
|
||||
for (size_t j = 0; j < opt_count; j++) {
|
||||
fprintf(stderr, "%s%s", j > 0 ? ", " : "", options[j].name);
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1460,7 +1456,7 @@ universal_notifier_t &universal_notifier_t::default_notifier() {
|
||||
universal_notifier_t *universal_notifier_t::new_notifier_for_strategy(
|
||||
universal_notifier_t::notifier_strategy_t strat, const wchar_t *test_path) {
|
||||
if (strat == strategy_default) {
|
||||
strat = resolve_default_strategy();
|
||||
strat = resolve_default_strategy(); //!OCLINT(parameter reassignment)
|
||||
}
|
||||
switch (strat) {
|
||||
case strategy_shmem_polling: {
|
||||
|
||||
@@ -88,6 +88,10 @@ static int event_match(const event_t &classv, const event_t &instance) {
|
||||
case EVENT_GENERIC: {
|
||||
return instance.str_param1 == classv.str_param1;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected classv.type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This should never be reached.
|
||||
@@ -226,7 +230,10 @@ static wcstring event_desc_compact(const event_t &event) {
|
||||
res = format_string(L"EVENT_GENERIC(%ls)", event.str_param1.c_str());
|
||||
break;
|
||||
}
|
||||
default: { res = format_string(L"unknown/illegal event(%x)", event.type); }
|
||||
default: {
|
||||
res = format_string(L"unknown/illegal event(%x)", event.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (event.function_name.size()) {
|
||||
return format_string(L"%ls: \"%ls\"", res.c_str(), event.function_name.c_str());
|
||||
|
||||
141
src/exec.cpp
141
src/exec.cpp
@@ -282,11 +282,6 @@ static bool io_transmogrify(const io_chain_t &in_chain, io_chain_t *out_chain,
|
||||
out.reset(new io_fd_t(in->fd, fd, false));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Unknown type, should never happen.
|
||||
assert(0 && "Unhandled io_mode constant");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (out.get() != NULL) result_chain.push_back(out);
|
||||
@@ -353,7 +348,7 @@ static void internal_exec_helper(parser_t &parser, const wcstring &def, node_off
|
||||
// foreground process group, we don't use posix_spawn if we're going to foreground the process. (If
|
||||
// we use fork(), we can call tcsetpgrp after the fork, before the exec, and avoid the race).
|
||||
static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *process) {
|
||||
if (job_get_flag(job, JOB_CONTROL)) {
|
||||
if (job_get_flag(job, 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?
|
||||
if (job_get_flag(job, JOB_TERMINAL) && job_get_flag(job, JOB_FOREGROUND)) {
|
||||
@@ -406,7 +401,7 @@ void exec_job(parser_t &parser, job_t *j) {
|
||||
|
||||
if ((io->io_mode == IO_BUFFER)) {
|
||||
io_buffer_t *io_buffer = static_cast<io_buffer_t *>(io.get());
|
||||
assert(!io_buffer->is_input);
|
||||
assert(!io_buffer->is_input); //!OCLINT(multiple unary operator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +437,7 @@ void exec_job(parser_t &parser, job_t *j) {
|
||||
j->first_process->completed = 1;
|
||||
return;
|
||||
}
|
||||
assert(0 && "This should be unreachable");
|
||||
DIE("this should be unreachable");
|
||||
}
|
||||
|
||||
// We may have block IOs that conflict with fd redirections. For example, we may have a command
|
||||
@@ -827,9 +822,7 @@ void exec_job(parser_t &parser, job_t *j) {
|
||||
|
||||
case INTERNAL_EXEC:
|
||||
// We should have handled exec up above.
|
||||
assert(
|
||||
0 &&
|
||||
"INTERNAL_EXEC process found in pipeline, where it should never be. Aborting.");
|
||||
DIE("INTERNAL_EXEC process found in pipeline, where it should never be. Aborting.");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -913,45 +906,42 @@ void exec_job(parser_t &parser, job_t *j) {
|
||||
// output, so that we can truncate the file. Does not apply to /dev/null.
|
||||
bool must_fork = redirection_is_to_real_file(stdout_io.get()) ||
|
||||
redirection_is_to_real_file(stderr_io.get());
|
||||
if (!must_fork) {
|
||||
if (p->next == NULL) {
|
||||
const bool stdout_is_to_buffer =
|
||||
stdout_io && stdout_io->io_mode == IO_BUFFER;
|
||||
const bool no_stdout_output = stdout_buffer.empty();
|
||||
const bool no_stderr_output = stderr_buffer.empty();
|
||||
if (!must_fork && p->next == NULL) {
|
||||
const bool stdout_is_to_buffer = stdout_io && stdout_io->io_mode == IO_BUFFER;
|
||||
const bool no_stdout_output = stdout_buffer.empty();
|
||||
const bool no_stderr_output = stderr_buffer.empty();
|
||||
|
||||
if (no_stdout_output && no_stderr_output) {
|
||||
// The builtin produced no output and is not inside of a pipeline. No
|
||||
// need to fork or even output anything.
|
||||
debug(3, L"Skipping fork: no output for internal builtin '%ls'",
|
||||
p->argv0());
|
||||
fork_was_skipped = true;
|
||||
} else if (no_stderr_output && stdout_is_to_buffer) {
|
||||
// The builtin produced no stderr, and its stdout is going to an
|
||||
// internal buffer. There is no need to fork. This helps out the
|
||||
// performance quite a bit in complex completion code.
|
||||
debug(3, L"Skipping fork: buffered output for internal builtin '%ls'",
|
||||
p->argv0());
|
||||
if (no_stdout_output && no_stderr_output) {
|
||||
// The builtin produced no output and is not inside of a pipeline. No
|
||||
// need to fork or even output anything.
|
||||
debug(3, L"Skipping fork: no output for internal builtin '%ls'",
|
||||
p->argv0());
|
||||
fork_was_skipped = true;
|
||||
} else if (no_stderr_output && stdout_is_to_buffer) {
|
||||
// The builtin produced no stderr, and its stdout is going to an
|
||||
// internal buffer. There is no need to fork. This helps out the
|
||||
// performance quite a bit in complex completion code.
|
||||
debug(3, L"Skipping fork: buffered output for internal builtin '%ls'",
|
||||
p->argv0());
|
||||
|
||||
io_buffer_t *io_buffer = static_cast<io_buffer_t *>(stdout_io.get());
|
||||
const std::string res = wcs2string(builtin_io_streams->out.buffer());
|
||||
io_buffer_t *io_buffer = static_cast<io_buffer_t *>(stdout_io.get());
|
||||
const std::string res = wcs2string(builtin_io_streams->out.buffer());
|
||||
|
||||
io_buffer->out_buffer_append(res.data(), res.size());
|
||||
fork_was_skipped = true;
|
||||
} else if (stdout_io.get() == NULL && stderr_io.get() == NULL) {
|
||||
// We are writing to normal stdout and stderr. Just do it - no need to
|
||||
// fork.
|
||||
debug(3, L"Skipping fork: ordinary output for internal builtin '%ls'",
|
||||
p->argv0());
|
||||
const std::string outbuff = wcs2string(stdout_buffer);
|
||||
const std::string errbuff = wcs2string(stderr_buffer);
|
||||
bool builtin_io_done = do_builtin_io(outbuff.data(), outbuff.size(),
|
||||
errbuff.data(), errbuff.size());
|
||||
if (!builtin_io_done && errno != EPIPE) {
|
||||
show_stackframe(L'E');
|
||||
}
|
||||
fork_was_skipped = true;
|
||||
io_buffer->out_buffer_append(res.data(), res.size());
|
||||
fork_was_skipped = true;
|
||||
} else if (stdout_io.get() == NULL && stderr_io.get() == NULL) {
|
||||
// We are writing to normal stdout and stderr. Just do it - no need to
|
||||
// fork.
|
||||
debug(3, L"Skipping fork: ordinary output for internal builtin '%ls'",
|
||||
p->argv0());
|
||||
const std::string outbuff = wcs2string(stdout_buffer);
|
||||
const std::string errbuff = wcs2string(stderr_buffer);
|
||||
bool builtin_io_done = do_builtin_io(outbuff.data(), outbuff.size(),
|
||||
errbuff.data(), errbuff.size());
|
||||
if (!builtin_io_done && errno != EPIPE) {
|
||||
show_stackframe(L'E');
|
||||
}
|
||||
fork_was_skipped = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1074,7 +1064,7 @@ void exec_job(parser_t &parser, job_t *j) {
|
||||
setup_child_process(j, p, process_net_io_chain);
|
||||
safe_launch_process(p, actual_cmd, argv, envv);
|
||||
// safe_launch_process _never_ returns...
|
||||
assert(0 && "safe_launch_process should not have returned");
|
||||
DIE("safe_launch_process should not have returned");
|
||||
} else {
|
||||
debug(2, L"Fork #%d, pid %d: external command '%s' from '%ls'\n",
|
||||
g_fork_count, pid, p->argv0(), file ? file : L"<no file>");
|
||||
@@ -1088,17 +1078,13 @@ void exec_job(parser_t &parser, job_t *j) {
|
||||
// This is the parent process. Store away information on the child, and possibly
|
||||
// fice it control over the terminal.
|
||||
p->pid = pid;
|
||||
|
||||
set_child_group(j, p, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case INTERNAL_EXEC: {
|
||||
// We should have handled exec up above.
|
||||
assert(
|
||||
0 &&
|
||||
"INTERNAL_EXEC process found in pipeline, where it should never be. Aborting.");
|
||||
DIE("INTERNAL_EXEC process found in pipeline, where it should never be. Aborting.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1177,37 +1163,38 @@ static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst,
|
||||
// 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) {
|
||||
const char *begin = io_buffer->out_buffer_ptr();
|
||||
const char *end = begin + io_buffer->out_buffer_size();
|
||||
if (split_output) {
|
||||
const char *cursor = begin;
|
||||
while (cursor < end) {
|
||||
// Look for the next separator.
|
||||
const char *stop = (const char *)memchr(cursor, '\n', end - cursor);
|
||||
const bool hit_separator = (stop != NULL);
|
||||
if (!hit_separator) {
|
||||
// If it's not found, just use the end.
|
||||
stop = end;
|
||||
}
|
||||
// Stop now points at the first character we do not want to copy.
|
||||
const wcstring wc = str2wcstring(cursor, stop - cursor);
|
||||
lst->push_back(wc);
|
||||
if (lst == NULL || io_buffer.get() == NULL) {
|
||||
return subcommand_status;
|
||||
}
|
||||
|
||||
// If we hit a separator, skip over it; otherwise we're at the end.
|
||||
cursor = stop + (hit_separator ? 1 : 0);
|
||||
const char *begin = io_buffer->out_buffer_ptr();
|
||||
const char *end = begin + io_buffer->out_buffer_size();
|
||||
if (split_output) {
|
||||
const char *cursor = begin;
|
||||
while (cursor < end) {
|
||||
// Look for the next separator.
|
||||
const char *stop = (const char *)memchr(cursor, '\n', end - cursor);
|
||||
const bool hit_separator = (stop != NULL);
|
||||
if (!hit_separator) {
|
||||
// If it's not found, just use the end.
|
||||
stop = end;
|
||||
}
|
||||
} else {
|
||||
// we're not splitting output, but we still want to trim off a trailing newline.
|
||||
if (end != begin && end[-1] == '\n') {
|
||||
--end;
|
||||
}
|
||||
const wcstring wc = str2wcstring(begin, end - begin);
|
||||
// Stop now points at the first character we do not want to copy.
|
||||
const wcstring wc = str2wcstring(cursor, stop - cursor);
|
||||
lst->push_back(wc);
|
||||
|
||||
// If we hit a separator, skip over it; otherwise we're at the end.
|
||||
cursor = stop + (hit_separator ? 1 : 0);
|
||||
}
|
||||
} else {
|
||||
// We're not splitting output, but we still want to trim off a trailing newline.
|
||||
if (end != begin && end[-1] == '\n') {
|
||||
--end;
|
||||
}
|
||||
const wcstring wc = str2wcstring(begin, end - begin);
|
||||
lst->push_back(wc);
|
||||
}
|
||||
|
||||
return subcommand_status;
|
||||
|
||||
540
src/expand.cpp
540
src/expand.cpp
@@ -166,36 +166,31 @@ wcstring expand_escape_variable(const wcstring &in) {
|
||||
|
||||
tokenize_variable_array(in, lst);
|
||||
|
||||
switch (lst.size()) {
|
||||
case 0: {
|
||||
buff.append(L"''");
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
const wcstring &el = lst.at(0);
|
||||
int size = lst.size();
|
||||
if (size == 0) {
|
||||
buff.append(L"''");
|
||||
} else if (size == 1) {
|
||||
const wcstring &el = lst.at(0);
|
||||
|
||||
if (el.find(L' ') != wcstring::npos && is_quotable(el)) {
|
||||
if (el.find(L' ') != wcstring::npos && is_quotable(el)) {
|
||||
buff.append(L"'");
|
||||
buff.append(el);
|
||||
buff.append(L"'");
|
||||
} else {
|
||||
buff.append(escape_string(el, 1));
|
||||
}
|
||||
} else {
|
||||
for (size_t j = 0; j < lst.size(); j++) {
|
||||
const wcstring &el = lst.at(j);
|
||||
if (j) buff.append(L" ");
|
||||
|
||||
if (is_quotable(el)) {
|
||||
buff.append(L"'");
|
||||
buff.append(el);
|
||||
buff.append(L"'");
|
||||
} else {
|
||||
buff.append(escape_string(el, 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
for (size_t j = 0; j < lst.size(); j++) {
|
||||
const wcstring &el = lst.at(j);
|
||||
if (j) buff.append(L" ");
|
||||
|
||||
if (is_quotable(el)) {
|
||||
buff.append(L"'");
|
||||
buff.append(el);
|
||||
buff.append(L"'");
|
||||
} else {
|
||||
buff.append(escape_string(el, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return buff;
|
||||
@@ -225,10 +220,8 @@ static bool match_pid(const wcstring &cmd, const wchar_t *proc, size_t *offset)
|
||||
const wcstring base_cmd = wbasename(cmd);
|
||||
|
||||
bool result = string_prefixes_string(proc, base_cmd);
|
||||
if (result) {
|
||||
// It's a match. Return the offset within the full command.
|
||||
if (offset) *offset = cmd.size() - base_cmd.size();
|
||||
}
|
||||
// It's a match. Return the offset within the full command.
|
||||
if (result && offset) *offset = cmd.size() - base_cmd.size();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -442,7 +435,7 @@ struct find_job_data_t {
|
||||
|
||||
/// The following function is invoked on the main thread, because the job list is not thread safe.
|
||||
/// It should search the job list for something matching the given proc, and then return 1 to stop
|
||||
/// the search, 0 to continue it .
|
||||
/// the search, 0 to continue it.
|
||||
static int find_job(const struct find_job_data_t *info) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
@@ -501,42 +494,45 @@ static int find_job(const struct find_job_data_t *info) {
|
||||
found = 1;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
job_iterator_t jobs;
|
||||
while ((j = jobs.next())) {
|
||||
if (j->command_is_empty()) continue;
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
|
||||
size_t offset;
|
||||
if (match_pid(j->command(), proc, &offset)) {
|
||||
if (flags & EXPAND_FOR_COMPLETIONS) {
|
||||
append_completion(&completions, j->command_wcstr() + offset + wcslen(proc),
|
||||
COMPLETE_JOB_DESC, 0);
|
||||
} else {
|
||||
append_completion(&completions, to_string<long>(j->pgid));
|
||||
found = 1;
|
||||
}
|
||||
job_iterator_t jobs;
|
||||
while ((j = jobs.next())) {
|
||||
if (j->command_is_empty()) continue;
|
||||
|
||||
size_t offset;
|
||||
if (match_pid(j->command(), proc, &offset)) {
|
||||
if (flags & EXPAND_FOR_COMPLETIONS) {
|
||||
append_completion(&completions, j->command_wcstr() + offset + wcslen(proc),
|
||||
COMPLETE_JOB_DESC, 0);
|
||||
} else {
|
||||
append_completion(&completions, to_string<long>(j->pgid));
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
jobs.reset();
|
||||
while ((j = jobs.next())) {
|
||||
process_t *p;
|
||||
if (j->command_is_empty()) continue;
|
||||
for (p = j->first_process; p; p = p->next) {
|
||||
if (p->actual_cmd.empty()) continue;
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
|
||||
size_t offset;
|
||||
if (match_pid(p->actual_cmd, proc, &offset)) {
|
||||
if (flags & EXPAND_FOR_COMPLETIONS) {
|
||||
append_completion(&completions,
|
||||
wcstring(p->actual_cmd, offset + wcslen(proc)),
|
||||
COMPLETE_CHILD_PROCESS_DESC, 0);
|
||||
} else {
|
||||
append_completion(&completions, to_string<long>(p->pid), L"", 0);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
jobs.reset();
|
||||
while ((j = jobs.next())) {
|
||||
process_t *p;
|
||||
if (j->command_is_empty()) continue;
|
||||
for (p = j->first_process; p; p = p->next) {
|
||||
if (p->actual_cmd.empty()) continue;
|
||||
|
||||
size_t offset;
|
||||
if (match_pid(p->actual_cmd, proc, &offset)) {
|
||||
if (flags & EXPAND_FOR_COMPLETIONS) {
|
||||
append_completion(&completions, wcstring(p->actual_cmd, offset + wcslen(proc)),
|
||||
COMPLETE_CHILD_PROCESS_DESC, 0);
|
||||
} else {
|
||||
append_completion(&completions, to_string<long>(p->pid), L"", 0);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -632,13 +628,11 @@ static bool expand_pid(const wcstring &instr_with_sep, expand_flags_t flags,
|
||||
const size_t prev_count = out->size();
|
||||
find_process(in + 1, flags, out);
|
||||
|
||||
if (prev_count == out->size()) {
|
||||
if (!(flags & EXPAND_FOR_COMPLETIONS)) {
|
||||
// We failed to find anything.
|
||||
append_syntax_error(errors, 1, FAILED_EXPANSION_PROCESS_ERR_MSG,
|
||||
escape(in + 1, ESCAPE_NO_QUOTED).c_str());
|
||||
return false;
|
||||
}
|
||||
if (prev_count == out->size() && !(flags & EXPAND_FOR_COMPLETIONS)) {
|
||||
// We failed to find anything.
|
||||
append_syntax_error(errors, 1, FAILED_EXPANSION_PROCESS_ERR_MSG,
|
||||
escape(in + 1, ESCAPE_NO_QUOTED).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -755,193 +749,196 @@ static int expand_variables(const wcstring &instr, std::vector<completion_t> *ou
|
||||
|
||||
for (long i = last_idx - 1; (i >= 0) && is_ok && !empty; i--) {
|
||||
const wchar_t c = instr.at(i);
|
||||
if ((c == VARIABLE_EXPAND) || (c == VARIABLE_EXPAND_SINGLE)) {
|
||||
size_t start_pos = i + 1;
|
||||
size_t stop_pos;
|
||||
long var_len;
|
||||
int is_single = (c == VARIABLE_EXPAND_SINGLE);
|
||||
if (c != VARIABLE_EXPAND && c != VARIABLE_EXPAND_SINGLE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stop_pos = start_pos;
|
||||
while (stop_pos < insize) {
|
||||
const wchar_t nc = instr.at(stop_pos);
|
||||
if (nc == VARIABLE_EXPAND_EMPTY) {
|
||||
stop_pos++;
|
||||
break;
|
||||
}
|
||||
if (!wcsvarchr(nc)) break;
|
||||
long var_len;
|
||||
int is_single = (c == VARIABLE_EXPAND_SINGLE);
|
||||
size_t start_pos = i + 1;
|
||||
size_t stop_pos = start_pos;
|
||||
|
||||
while (stop_pos < insize) {
|
||||
const wchar_t nc = instr.at(stop_pos);
|
||||
if (nc == VARIABLE_EXPAND_EMPTY) {
|
||||
stop_pos++;
|
||||
}
|
||||
|
||||
// printf( "Stop for '%c'\n", in[stop_pos]);
|
||||
var_len = stop_pos - start_pos;
|
||||
|
||||
if (var_len == 0) {
|
||||
if (errors) {
|
||||
parse_util_expand_variable_error(instr, 0 /* global_token_pos */, i, errors);
|
||||
}
|
||||
|
||||
is_ok = false;
|
||||
break;
|
||||
}
|
||||
if (!wcsvarchr(nc)) break;
|
||||
|
||||
var_tmp.append(instr, start_pos, var_len);
|
||||
env_var_t var_val;
|
||||
if (var_len == 1 && var_tmp[0] == VARIABLE_EXPAND_EMPTY) {
|
||||
var_val = env_var_t::missing_var();
|
||||
} else {
|
||||
var_val = expand_var(var_tmp.c_str());
|
||||
stop_pos++;
|
||||
}
|
||||
|
||||
// printf( "Stop for '%c'\n", in[stop_pos]);
|
||||
var_len = stop_pos - start_pos;
|
||||
|
||||
if (var_len == 0) {
|
||||
if (errors) {
|
||||
parse_util_expand_variable_error(instr, 0 /* global_token_pos */, i, errors);
|
||||
}
|
||||
|
||||
if (!var_val.missing()) {
|
||||
int all_vars = 1;
|
||||
wcstring_list_t var_item_list;
|
||||
is_ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_ok) {
|
||||
tokenize_variable_array(var_val, var_item_list);
|
||||
var_tmp.append(instr, start_pos, var_len);
|
||||
env_var_t var_val;
|
||||
if (var_len == 1 && var_tmp[0] == VARIABLE_EXPAND_EMPTY) {
|
||||
var_val = env_var_t::missing_var();
|
||||
} else {
|
||||
var_val = expand_var(var_tmp.c_str());
|
||||
}
|
||||
|
||||
const size_t slice_start = stop_pos;
|
||||
if (slice_start < insize && instr.at(slice_start) == L'[') {
|
||||
wchar_t *slice_end;
|
||||
size_t bad_pos;
|
||||
all_vars = 0;
|
||||
const wchar_t *in = instr.c_str();
|
||||
bad_pos = parse_slice(in + slice_start, &slice_end, var_idx_list,
|
||||
var_pos_list, var_item_list.size());
|
||||
if (bad_pos != 0) {
|
||||
append_syntax_error(errors, stop_pos + bad_pos, L"Invalid index value");
|
||||
if (!var_val.missing()) {
|
||||
int all_vars = 1;
|
||||
wcstring_list_t var_item_list;
|
||||
|
||||
if (is_ok) {
|
||||
tokenize_variable_array(var_val, var_item_list);
|
||||
|
||||
const size_t slice_start = stop_pos;
|
||||
if (slice_start < insize && instr.at(slice_start) == L'[') {
|
||||
wchar_t *slice_end;
|
||||
size_t bad_pos;
|
||||
all_vars = 0;
|
||||
const wchar_t *in = instr.c_str();
|
||||
bad_pos = parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list,
|
||||
var_item_list.size());
|
||||
if (bad_pos != 0) {
|
||||
append_syntax_error(errors, stop_pos + bad_pos, L"Invalid index value");
|
||||
is_ok = false;
|
||||
break;
|
||||
}
|
||||
stop_pos = (slice_end - in);
|
||||
}
|
||||
|
||||
if (!all_vars) {
|
||||
wcstring_list_t string_values(var_idx_list.size());
|
||||
for (size_t j = 0; j < var_idx_list.size(); j++) {
|
||||
long tmp = var_idx_list.at(j);
|
||||
// Check that we are within array bounds. If not, truncate the list to
|
||||
// exit.
|
||||
if (tmp < 1 || (size_t)tmp > var_item_list.size()) {
|
||||
size_t var_src_pos = var_pos_list.at(j);
|
||||
// The slice was parsed starting at stop_pos, so we have to add that
|
||||
// to the error position.
|
||||
append_syntax_error(errors, slice_start + var_src_pos,
|
||||
ARRAY_BOUNDS_ERR);
|
||||
is_ok = false;
|
||||
var_idx_list.resize(j);
|
||||
break;
|
||||
} else {
|
||||
// Replace each index in var_idx_list inplace with the string value
|
||||
// at the specified index.
|
||||
// al_set( var_idx_list, j, wcsdup((const wchar_t *)al_get(
|
||||
// &var_item_list, tmp-1 ) ) );
|
||||
string_values.at(j) = var_item_list.at(tmp - 1);
|
||||
}
|
||||
stop_pos = (slice_end - in);
|
||||
}
|
||||
|
||||
if (!all_vars) {
|
||||
wcstring_list_t string_values(var_idx_list.size());
|
||||
for (size_t j = 0; j < var_idx_list.size(); j++) {
|
||||
long tmp = var_idx_list.at(j);
|
||||
// Check that we are within array bounds. If not, truncate the list to
|
||||
// exit.
|
||||
if (tmp < 1 || (size_t)tmp > var_item_list.size()) {
|
||||
size_t var_src_pos = var_pos_list.at(j);
|
||||
// The slice was parsed starting at stop_pos, so we have to add that
|
||||
// to the error position.
|
||||
append_syntax_error(errors, slice_start + var_src_pos,
|
||||
ARRAY_BOUNDS_ERR);
|
||||
is_ok = false;
|
||||
var_idx_list.resize(j);
|
||||
break;
|
||||
} else {
|
||||
// Replace each index in var_idx_list inplace with the string value
|
||||
// at the specified index.
|
||||
// al_set( var_idx_list, j, wcsdup((const wchar_t *)al_get(
|
||||
// &var_item_list, tmp-1 ) ) );
|
||||
string_values.at(j) = var_item_list.at(tmp - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// string_values is the new var_item_list.
|
||||
var_item_list.swap(string_values);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_ok) {
|
||||
if (is_single) {
|
||||
wcstring res(instr, 0, i);
|
||||
if (i > 0) {
|
||||
if (instr.at(i - 1) != VARIABLE_EXPAND_SINGLE) {
|
||||
res.push_back(INTERNAL_SEPARATOR);
|
||||
} else if (var_item_list.empty() || var_item_list.front().empty()) {
|
||||
// First expansion is empty, but we need to recursively expand.
|
||||
res.push_back(VARIABLE_EXPAND_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < var_item_list.size(); j++) {
|
||||
const wcstring &next = var_item_list.at(j);
|
||||
if (is_ok) {
|
||||
if (j != 0) res.append(L" ");
|
||||
res.append(next);
|
||||
}
|
||||
}
|
||||
assert(stop_pos <= insize);
|
||||
res.append(instr, stop_pos, insize - stop_pos);
|
||||
is_ok &= expand_variables(res, out, i, errors);
|
||||
} else {
|
||||
for (size_t j = 0; j < var_item_list.size(); j++) {
|
||||
const wcstring &next = var_item_list.at(j);
|
||||
if (is_ok && (i == 0) && stop_pos == insize) {
|
||||
append_completion(out, next);
|
||||
} else {
|
||||
if (is_ok) {
|
||||
wcstring new_in;
|
||||
new_in.append(instr, 0, i);
|
||||
|
||||
if (i > 0) {
|
||||
if (instr.at(i - 1) != VARIABLE_EXPAND) {
|
||||
new_in.push_back(INTERNAL_SEPARATOR);
|
||||
} else if (next.empty()) {
|
||||
new_in.push_back(VARIABLE_EXPAND_EMPTY);
|
||||
}
|
||||
}
|
||||
assert(stop_pos <= insize);
|
||||
new_in.append(next);
|
||||
new_in.append(instr, stop_pos, insize - stop_pos);
|
||||
is_ok &= expand_variables(new_in, out, i, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// string_values is the new var_item_list.
|
||||
var_item_list.swap(string_values);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_ok) {
|
||||
return is_ok;
|
||||
}
|
||||
|
||||
// Even with no value, we still need to parse out slice syntax. Behave as though we
|
||||
// had 1 value, so $foo[1] always works.
|
||||
const size_t slice_start = stop_pos;
|
||||
if (slice_start < insize && instr.at(slice_start) == L'[') {
|
||||
const wchar_t *in = instr.c_str();
|
||||
wchar_t *slice_end;
|
||||
size_t bad_pos;
|
||||
|
||||
bad_pos = parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, 1);
|
||||
if (bad_pos != 0) {
|
||||
append_syntax_error(errors, stop_pos + bad_pos, L"Invalid index value");
|
||||
is_ok = 0;
|
||||
return is_ok;
|
||||
}
|
||||
stop_pos = (slice_end - in);
|
||||
|
||||
// Validate that the parsed indexes are valid.
|
||||
for (size_t j = 0; j < var_idx_list.size(); j++) {
|
||||
long tmp = var_idx_list.at(j);
|
||||
if (tmp != 1) {
|
||||
size_t var_src_pos = var_pos_list.at(j);
|
||||
append_syntax_error(errors, slice_start + var_src_pos, ARRAY_BOUNDS_ERR);
|
||||
is_ok = 0;
|
||||
return is_ok;
|
||||
if (is_single) {
|
||||
wcstring res(instr, 0, i);
|
||||
if (i > 0) {
|
||||
if (instr.at(i - 1) != VARIABLE_EXPAND_SINGLE) {
|
||||
res.push_back(INTERNAL_SEPARATOR);
|
||||
} else if (var_item_list.empty() || var_item_list.front().empty()) {
|
||||
// First expansion is empty, but we need to recursively expand.
|
||||
res.push_back(VARIABLE_EXPAND_EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expand a non-existing variable.
|
||||
if (c == VARIABLE_EXPAND) {
|
||||
// Regular expansion, i.e. expand this argument to nothing.
|
||||
empty = true;
|
||||
} else {
|
||||
// Expansion to single argument.
|
||||
wcstring res;
|
||||
res.append(instr, 0, i);
|
||||
if (i > 0 && instr.at(i - 1) == VARIABLE_EXPAND_SINGLE) {
|
||||
res.push_back(VARIABLE_EXPAND_EMPTY);
|
||||
for (size_t j = 0; j < var_item_list.size(); j++) {
|
||||
const wcstring &next = var_item_list.at(j);
|
||||
if (is_ok) {
|
||||
if (j != 0) res.append(L" ");
|
||||
res.append(next);
|
||||
}
|
||||
}
|
||||
assert(stop_pos <= insize);
|
||||
res.append(instr, stop_pos, insize - stop_pos);
|
||||
|
||||
is_ok &= expand_variables(res, out, i, errors);
|
||||
} else {
|
||||
for (size_t j = 0; j < var_item_list.size(); j++) {
|
||||
const wcstring &next = var_item_list.at(j);
|
||||
if (is_ok && i == 0 && stop_pos == insize) {
|
||||
append_completion(out, next);
|
||||
} else {
|
||||
if (is_ok) {
|
||||
wcstring new_in;
|
||||
new_in.append(instr, 0, i);
|
||||
|
||||
if (i > 0) {
|
||||
if (instr.at(i - 1) != VARIABLE_EXPAND) {
|
||||
new_in.push_back(INTERNAL_SEPARATOR);
|
||||
} else if (next.empty()) {
|
||||
new_in.push_back(VARIABLE_EXPAND_EMPTY);
|
||||
}
|
||||
}
|
||||
assert(stop_pos <= insize);
|
||||
new_in.append(next);
|
||||
new_in.append(instr, stop_pos, insize - stop_pos);
|
||||
is_ok &= expand_variables(new_in, out, i, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return is_ok;
|
||||
}
|
||||
|
||||
// Even with no value, we still need to parse out slice syntax. Behave as though we
|
||||
// had 1 value, so $foo[1] always works.
|
||||
const size_t slice_start = stop_pos;
|
||||
if (slice_start < insize && instr.at(slice_start) == L'[') {
|
||||
const wchar_t *in = instr.c_str();
|
||||
wchar_t *slice_end;
|
||||
size_t bad_pos;
|
||||
|
||||
bad_pos = parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list, 1);
|
||||
if (bad_pos != 0) {
|
||||
append_syntax_error(errors, stop_pos + bad_pos, L"Invalid index value");
|
||||
is_ok = 0;
|
||||
return is_ok;
|
||||
}
|
||||
stop_pos = (slice_end - in);
|
||||
|
||||
// Validate that the parsed indexes are valid.
|
||||
for (size_t j = 0; j < var_idx_list.size(); j++) {
|
||||
long tmp = var_idx_list.at(j);
|
||||
if (tmp != 1) {
|
||||
size_t var_src_pos = var_pos_list.at(j);
|
||||
append_syntax_error(errors, slice_start + var_src_pos, ARRAY_BOUNDS_ERR);
|
||||
is_ok = 0;
|
||||
return is_ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expand a non-existing variable.
|
||||
if (c == VARIABLE_EXPAND) {
|
||||
// Regular expansion, i.e. expand this argument to nothing.
|
||||
empty = true;
|
||||
} else {
|
||||
// Expansion to single argument.
|
||||
wcstring res;
|
||||
res.append(instr, 0, i);
|
||||
if (i > 0 && instr.at(i - 1) == VARIABLE_EXPAND_SINGLE) {
|
||||
res.push_back(VARIABLE_EXPAND_EMPTY);
|
||||
}
|
||||
assert(stop_pos <= insize);
|
||||
res.append(instr, stop_pos, insize - stop_pos);
|
||||
|
||||
is_ok &= expand_variables(res, out, i, errors);
|
||||
return is_ok;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -985,6 +982,10 @@ static expand_error_t expand_brackets(const wcstring &instr, expand_flags_t flag
|
||||
}
|
||||
case BRACKET_SEP: {
|
||||
if (bracket_count == 1) last_sep = pos;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break; // we ignore all other characters here
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1025,21 +1026,19 @@ static expand_error_t expand_brackets(const wcstring &instr, expand_flags_t flag
|
||||
tot_len = length_preceding_brackets + length_following_brackets;
|
||||
item_begin = bracket_begin + 1;
|
||||
for (const wchar_t *pos = (bracket_begin + 1); true; pos++) {
|
||||
if (bracket_count == 0) {
|
||||
if ((*pos == BRACKET_SEP) || (pos == bracket_end)) {
|
||||
assert(pos >= item_begin);
|
||||
size_t item_len = pos - item_begin;
|
||||
if (bracket_count == 0 && ((*pos == BRACKET_SEP) || (pos == bracket_end))) {
|
||||
assert(pos >= item_begin);
|
||||
size_t item_len = pos - item_begin;
|
||||
|
||||
wcstring whole_item;
|
||||
whole_item.reserve(tot_len + item_len + 2);
|
||||
whole_item.append(in, length_preceding_brackets);
|
||||
whole_item.append(item_begin, item_len);
|
||||
whole_item.append(bracket_end + 1);
|
||||
expand_brackets(whole_item, flags, out, errors);
|
||||
wcstring whole_item;
|
||||
whole_item.reserve(tot_len + item_len + 2);
|
||||
whole_item.append(in, length_preceding_brackets);
|
||||
whole_item.append(item_begin, item_len);
|
||||
whole_item.append(bracket_end + 1);
|
||||
expand_brackets(whole_item, flags, out, errors);
|
||||
|
||||
item_begin = pos + 1;
|
||||
if (pos == bracket_end) break;
|
||||
}
|
||||
item_begin = pos + 1;
|
||||
if (pos == bracket_end) break;
|
||||
}
|
||||
|
||||
if (*pos == BRACKET_BEGIN) {
|
||||
@@ -1076,6 +1075,10 @@ static int expand_cmdsubst(const wcstring &input, std::vector<completion_t> *out
|
||||
case 1: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("unhandled parse_ret value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const wcstring subcmd(paran_begin + 1, paran_end - paran_begin - 1);
|
||||
@@ -1299,6 +1302,9 @@ static void remove_internal_separator(wcstring *str, bool conv) {
|
||||
str->at(idx) = L'*';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break; // we ignore all other characters
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1307,8 +1313,10 @@ 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
|
||||
/// halts.
|
||||
typedef expand_error_t (*expand_stage_t)(const wcstring &input, std::vector<completion_t> *out,
|
||||
expand_flags_t flags, parse_error_list_t *errors);
|
||||
typedef expand_error_t (*expand_stage_t)(const wcstring &input, //!OCLINT(unused param)
|
||||
std::vector<completion_t> *out, //!OCLINT(unused param)
|
||||
expand_flags_t flags, //!OCLINT(unused param)
|
||||
parse_error_list_t *errors); //!OCLINT(unused param)
|
||||
|
||||
static expand_error_t expand_stage_cmdsubst(const wcstring &input, std::vector<completion_t> *out,
|
||||
expand_flags_t flags, parse_error_list_t *errors) {
|
||||
@@ -1389,7 +1397,7 @@ static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector<
|
||||
const bool has_wildcard = wildcard_has(path_to_expand, true /* internal, i.e. ANY_CHAR */);
|
||||
|
||||
if (has_wildcard && (flags & EXECUTABLES_ONLY)) {
|
||||
// Don't do wildcard expansion for executables. See #785. Make them expand to nothing here.
|
||||
; // don't do wildcard expansion for executables, see issue #785
|
||||
} else if (((flags & EXPAND_FOR_COMPLETIONS) && (!(flags & EXPAND_SKIP_WILDCARDS))) ||
|
||||
has_wildcard) {
|
||||
// We either have a wildcard, or we don't have a wildcard but we're doing completion
|
||||
@@ -1401,8 +1409,8 @@ static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector<
|
||||
// which may be CDPATH if the special flag is set.
|
||||
const wcstring working_dir = env_get_pwd_slash();
|
||||
wcstring_list_t effective_working_dirs;
|
||||
bool for_cd = !!(flags & EXPAND_SPECIAL_FOR_CD);
|
||||
bool for_command = !!(flags & EXPAND_SPECIAL_FOR_COMMAND);
|
||||
bool for_cd = static_cast<bool>(flags & EXPAND_SPECIAL_FOR_CD);
|
||||
bool for_command = static_cast<bool>(flags & EXPAND_SPECIAL_FOR_COMMAND);
|
||||
if (!for_cd && !for_command) {
|
||||
// Common case.
|
||||
effective_working_dirs.push_back(working_dir);
|
||||
@@ -1517,19 +1525,17 @@ expand_error_t expand_string(const wcstring &input, std::vector<completion_t> *o
|
||||
|
||||
bool expand_one(wcstring &string, expand_flags_t flags, parse_error_list_t *errors) {
|
||||
std::vector<completion_t> completions;
|
||||
bool result = false;
|
||||
|
||||
if ((!(flags & EXPAND_FOR_COMPLETIONS)) && expand_is_clean(string)) {
|
||||
if (!(flags & EXPAND_FOR_COMPLETIONS) && expand_is_clean(string)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, errors)) {
|
||||
if (completions.size() == 1) {
|
||||
string = completions.at(0).completion;
|
||||
result = true;
|
||||
}
|
||||
if (expand_string(string, &completions, flags | EXPAND_NO_DESCRIPTIONS, errors) &&
|
||||
completions.size() == 1) {
|
||||
string = completions.at(0).completion;
|
||||
return true;
|
||||
}
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://github.com/fish-shell/fish-shell/issues/367
|
||||
@@ -1556,24 +1562,26 @@ static std::string escape_single_quoted_hack_hack_hack_hack(const char *str) {
|
||||
|
||||
bool fish_xdm_login_hack_hack_hack_hack(std::vector<std::string> *cmds, int argc,
|
||||
const char *const *argv) {
|
||||
bool result = false;
|
||||
if (cmds && cmds->size() == 1) {
|
||||
const std::string &cmd = cmds->at(0);
|
||||
if (cmd == "exec \"${@}\"" || cmd == "exec \"$@\"") {
|
||||
// We're going to construct a new command that starts with exec, and then has the
|
||||
// remaining arguments escaped.
|
||||
std::string new_cmd = "exec";
|
||||
for (int i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (arg) {
|
||||
new_cmd.push_back(' ');
|
||||
new_cmd.append(escape_single_quoted_hack_hack_hack_hack(arg));
|
||||
}
|
||||
}
|
||||
if (!cmds || cmds->size() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cmds->at(0) = new_cmd;
|
||||
result = true;
|
||||
bool result = false;
|
||||
const std::string &cmd = cmds->at(0);
|
||||
if (cmd == "exec \"${@}\"" || cmd == "exec \"$@\"") {
|
||||
// We're going to construct a new command that starts with exec, and then has the
|
||||
// remaining arguments escaped.
|
||||
std::string new_cmd = "exec";
|
||||
for (int i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (arg) {
|
||||
new_cmd.push_back(' ');
|
||||
new_cmd.append(escape_single_quoted_hack_hack_hack_hack(arg));
|
||||
}
|
||||
}
|
||||
|
||||
cmds->at(0) = new_cmd;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -203,13 +203,10 @@ size_t wcslcpy(wchar_t *dst, const wchar_t *src, size_t siz) {
|
||||
|
||||
// Not enough room in dst, add NUL and traverse rest of src.
|
||||
if (n == 0) {
|
||||
if (siz != 0) *d = '\0';
|
||||
// NUL-terminate dst.
|
||||
while (*s++)
|
||||
;
|
||||
if (siz != 0) *d = '\0'; // NUL-terminate dst
|
||||
while (*s++) ; // ignore rest of src
|
||||
}
|
||||
return s - src - 1;
|
||||
// Count does not include NUL.
|
||||
return s - src - 1; // count does not include NUL
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -353,7 +350,7 @@ static int bisearch(wchar_t ucs, const struct interval *table, int max) {
|
||||
if (ucs > table[mid].last)
|
||||
min = mid + 1;
|
||||
else if (ucs < table[mid].first)
|
||||
max = mid - 1;
|
||||
max = mid - 1; //!OCLINT(parameter reassignment)
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -314,6 +314,7 @@ static int fish_parse_opt(int argc, char **argv, std::vector<std::string> *cmds)
|
||||
case 0: {
|
||||
fwprintf(stderr, _(L"getopt_long() unexpectedly returned zero\n"));
|
||||
exit(127);
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
cmds->push_back(optarg);
|
||||
@@ -358,6 +359,7 @@ static int fish_parse_opt(int argc, char **argv, std::vector<std::string> *cmds)
|
||||
case 'v': {
|
||||
fwprintf(stdout, _(L"%s, version %s\n"), PACKAGE_NAME, get_fish_version());
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
case 'D': {
|
||||
char *end;
|
||||
@@ -377,6 +379,7 @@ static int fish_parse_opt(int argc, char **argv, std::vector<std::string> *cmds)
|
||||
default: {
|
||||
// We assume getopt_long() has already emitted a diagnostic msg.
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,11 +427,6 @@ int main(int argc, char **argv) {
|
||||
int res = 1;
|
||||
int my_optind = 0;
|
||||
|
||||
// We can't do this at compile time due to the use of enum symbols.
|
||||
assert(EXPAND_SENTINAL >= EXPAND_RESERVED_BASE && EXPAND_SENTINAL <= EXPAND_RESERVED_END);
|
||||
assert(ANY_SENTINAL >= WILDCARD_RESERVED_BASE && ANY_SENTINAL <= WILDCARD_RESERVED_END);
|
||||
assert(R_SENTINAL >= INPUT_COMMON_BASE && R_SENTINAL <= INPUT_COMMON_END);
|
||||
|
||||
program_name = L"fish";
|
||||
set_main_thread();
|
||||
setup_fork_guards();
|
||||
|
||||
@@ -102,7 +102,7 @@ static void dump_node(indent_t node_indent, const parse_node_t &node, const wcst
|
||||
nextc_str[1] = L'c';
|
||||
nextc_str[2] = nextc + '@';
|
||||
}
|
||||
fwprintf(stderr, L"{off %4d, len %4d, indent %2u, kw %ls, %ls} [%ls|%ls|%ls]\n",
|
||||
fwprintf(stderr, L"{off %4u, len %4u, indent %2u, kw %ls, %ls} [%ls|%ls|%ls]\n",
|
||||
node.source_start, node.source_length, node_indent, keyword_description(node.keyword),
|
||||
token_type_description(node.type), prevc_str, source_txt.c_str(), nextc_str);
|
||||
}
|
||||
|
||||
@@ -206,9 +206,8 @@ static void process_input(bool continuous_mode) {
|
||||
output_bind_command(bind_chars);
|
||||
if (first_char_seen && !continuous_mode) {
|
||||
return;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
prev_tstamp = output_elapsed_time(prev_tstamp, first_char_seen);
|
||||
@@ -307,11 +306,13 @@ int main(int argc, char **argv) {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{NULL, 0, NULL, 0}};
|
||||
int opt;
|
||||
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
|
||||
bool error = false;
|
||||
while (!error && (opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 0: {
|
||||
fprintf(stderr, "getopt_long() unexpectedly returned zero\n");
|
||||
exit(1);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
continuous_mode = true;
|
||||
@@ -320,6 +321,7 @@ int main(int argc, char **argv) {
|
||||
case 'h': {
|
||||
print_help("fish_key_reader", 0);
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
char *end;
|
||||
@@ -332,7 +334,7 @@ int main(int argc, char **argv) {
|
||||
debug_level = (int)tmp;
|
||||
} else {
|
||||
fwprintf(stderr, _(L"Invalid value '%s' for debug-level flag"), optarg);
|
||||
exit(1);
|
||||
error = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -347,16 +349,19 @@ int main(int argc, char **argv) {
|
||||
debug_stack_frames = (int)tmp;
|
||||
} else {
|
||||
fwprintf(stderr, _(L"Invalid value '%s' for debug-stack-frames flag"), optarg);
|
||||
exit(1);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// We assume getopt_long() has already emitted a diagnostic msg.
|
||||
exit(1);
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error) return 1;
|
||||
|
||||
argc -= optind;
|
||||
if (argc != 0) {
|
||||
|
||||
@@ -95,8 +95,6 @@ static bool should_test_function(const char *func_name) {
|
||||
#define ESCAPE_TEST_LENGTH 100
|
||||
/// The higest character number of character to try and escape.
|
||||
#define ESCAPE_TEST_CHAR 4000
|
||||
/// Number of laps to run performance testing loop.
|
||||
#define LAPS 50
|
||||
|
||||
/// Number of encountered errors.
|
||||
static int err_count = 0;
|
||||
@@ -156,19 +154,22 @@ static int chdir_set_pwd(const char *path) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The odd formulation of these macros is to avoid "multiple unary operator" warnings from oclint
|
||||
// were we to use the more natural "if (!(e)) err(..." form. We have to do this because the rules
|
||||
// for the C preprocessor make it practically impossible to embed a comment in the body of a macro.
|
||||
#define do_test(e) \
|
||||
do { \
|
||||
if (!(e)) err(L"Test failed on line %lu: %s", __LINE__, #e); \
|
||||
if (e) { ; } else { err(L"Test failed on line %lu: %s", __LINE__, #e); } \
|
||||
} while (0)
|
||||
|
||||
#define do_test_from(e, from_line) \
|
||||
#define do_test_from(e, from) \
|
||||
do { \
|
||||
if (!(e)) err(L"Test failed on line %lu (from %lu): %s", __LINE__, from_line, #e); \
|
||||
if (e) { ; } else { err(L"Test failed on line %lu (from %lu): %s", __LINE__, from, #e); } \
|
||||
} while (0)
|
||||
|
||||
#define do_test1(e, msg) \
|
||||
do { \
|
||||
if (!(e)) err(L"Test failed on line %lu: %ls", __LINE__, (msg)); \
|
||||
if (e) { ; } else { err(L"Test failed on line %lu: %ls", __LINE__, (msg)); } \
|
||||
} while (0)
|
||||
|
||||
/// Test sane escapes.
|
||||
@@ -201,7 +202,7 @@ static void test_unescape_sane() {
|
||||
err(L"Should not have been able to unescape \\U110000\n");
|
||||
}
|
||||
if (is_wchar_ucs2()) {
|
||||
// TODO: Make this work on MS Windows.
|
||||
; // TODO: Make this work on MS Windows.
|
||||
} else {
|
||||
if (!unescape_string(L"echo \\U10FFFF", &output, UNESCAPE_DEFAULT)) {
|
||||
err(L"Should have been able to unescape \\U10FFFF\n");
|
||||
@@ -486,7 +487,7 @@ static parser_test_error_bits_t detect_argument_errors(const wcstring &src) {
|
||||
return PARSER_TEST_ERROR;
|
||||
}
|
||||
|
||||
assert(!tree.empty());
|
||||
assert(!tree.empty()); //!OCLINT(multiple unary operator)
|
||||
const parse_node_t *first_arg = tree.next_node_in_node_list(tree.at(0), symbol_argument, NULL);
|
||||
assert(first_arg != NULL);
|
||||
return parse_util_detect_errors_in_argument(*first_arg, first_arg->get_source(src));
|
||||
@@ -495,7 +496,6 @@ static parser_test_error_bits_t detect_argument_errors(const wcstring &src) {
|
||||
/// Test the parser.
|
||||
static void test_parser() {
|
||||
say(L"Testing parser");
|
||||
parser_t parser;
|
||||
|
||||
say(L"Testing block nesting");
|
||||
if (!parse_util_detect_errors(L"if; end")) {
|
||||
@@ -629,7 +629,8 @@ static void test_parser() {
|
||||
#if 0
|
||||
// This is disabled since it produces a long backtrace. We should find a way to either visually
|
||||
// compress the backtrace, or disable error spewing.
|
||||
parser_t::principal_parser().eval(L"function recursive1 ; recursive2 ; end ; function recursive2 ; recursive1 ; end ; recursive1; ", io_chain_t(), TOP);
|
||||
parser_t::principal_parser().eval(L"function recursive1 ; recursive2 ; end ; "
|
||||
L"function recursive2 ; recursive1 ; end ; recursive1; ", io_chain_t(), TOP);
|
||||
#endif
|
||||
|
||||
say(L"Testing empty function name");
|
||||
@@ -842,8 +843,8 @@ static void test_utf82wchar(const char *src, size_t slen, const wchar_t *dst, si
|
||||
const unsigned char astral_mask = 0xF0;
|
||||
for (size_t i = 0; i < slen; i++) {
|
||||
if ((src[i] & astral_mask) == astral_mask) {
|
||||
// Astral char. We expect this conversion to just fail.
|
||||
res = 0;
|
||||
// Astral char. We want this conversion to fail.
|
||||
res = 0; //!OCLINT(parameter reassignment)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -890,8 +891,8 @@ static void test_wchar2utf8(const wchar_t *src, size_t slen, const char *dst, si
|
||||
const uint32_t astral_mask = 0xFFFF0000U;
|
||||
for (size_t i = 0; i < slen; i++) {
|
||||
if ((src[i] & astral_mask) != 0) {
|
||||
/* astral char */
|
||||
res = 0;
|
||||
// Astral char. We want this conversion to fail.
|
||||
res = 0; //!OCLINT(parameter reassignment)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1139,7 +1140,8 @@ static bool expand_test(const wchar_t *in, expand_flags_t flags, ...) {
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
if ((arg = va_arg(va, wchar_t *)) != 0) {
|
||||
arg = va_arg(va, wchar_t *);
|
||||
if (arg) {
|
||||
wcstring msg = L"Expected [";
|
||||
bool first = true;
|
||||
for (wcstring_list_t::const_iterator it = expected.begin(), end = expected.end();
|
||||
@@ -2209,7 +2211,6 @@ static void test_history_matches(history_search_t &search, size_t matches, unsig
|
||||
size_t i;
|
||||
for (i = 0; i < matches; i++) {
|
||||
do_test(search.go_backwards());
|
||||
wcstring item = search.current_string();
|
||||
}
|
||||
// do_test_from(!search.go_backwards(), from_line);
|
||||
bool result = search.go_backwards();
|
||||
@@ -2405,7 +2406,7 @@ static void trigger_or_wait_for_notification(universal_notifier_t *notifier,
|
||||
universal_notifier_t::notifier_strategy_t strategy) {
|
||||
switch (strategy) {
|
||||
case universal_notifier_t::strategy_default: {
|
||||
assert(0 && "strategy_default should be passed");
|
||||
DIE("strategy_default should be passed");
|
||||
break;
|
||||
}
|
||||
case universal_notifier_t::strategy_shmem_polling: {
|
||||
@@ -3063,35 +3064,36 @@ static bool test_1_parse_ll2(const wcstring &src, wcstring *out_cmd, wcstring *o
|
||||
out_joined_args->clear();
|
||||
*out_deco = parse_statement_decoration_none;
|
||||
|
||||
bool result = false;
|
||||
parse_node_tree_t tree;
|
||||
if (parse_tree_from_string(src, parse_flag_none, &tree, NULL)) {
|
||||
// Get the statement. Should only have one.
|
||||
const parse_node_tree_t::parse_node_list_t stmt_nodes =
|
||||
tree.find_nodes(tree.at(0), symbol_plain_statement);
|
||||
if (stmt_nodes.size() != 1) {
|
||||
say(L"Unexpected number of statements (%lu) found in '%ls'", stmt_nodes.size(),
|
||||
src.c_str());
|
||||
return false;
|
||||
}
|
||||
const parse_node_t &stmt = *stmt_nodes.at(0);
|
||||
|
||||
// Return its decoration.
|
||||
*out_deco = tree.decoration_for_plain_statement(stmt);
|
||||
|
||||
// Return its command.
|
||||
tree.command_for_plain_statement(stmt, src, out_cmd);
|
||||
|
||||
// Return arguments separated by spaces.
|
||||
const parse_node_tree_t::parse_node_list_t arg_nodes =
|
||||
tree.find_nodes(stmt, symbol_argument);
|
||||
for (size_t i = 0; i < arg_nodes.size(); i++) {
|
||||
if (i > 0) out_joined_args->push_back(L' ');
|
||||
out_joined_args->append(arg_nodes.at(i)->get_source(src));
|
||||
}
|
||||
result = true;
|
||||
if (!parse_tree_from_string(src, parse_flag_none, &tree, NULL)) {
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
|
||||
// Get the statement. Should only have one.
|
||||
const parse_node_tree_t::parse_node_list_t stmt_nodes =
|
||||
tree.find_nodes(tree.at(0), symbol_plain_statement);
|
||||
if (stmt_nodes.size() != 1) {
|
||||
say(L"Unexpected number of statements (%lu) found in '%ls'", stmt_nodes.size(),
|
||||
src.c_str());
|
||||
return false;
|
||||
}
|
||||
const parse_node_t &stmt = *stmt_nodes.at(0);
|
||||
|
||||
// Return its decoration.
|
||||
*out_deco = tree.decoration_for_plain_statement(stmt);
|
||||
|
||||
// Return its command.
|
||||
tree.command_for_plain_statement(stmt, src, out_cmd);
|
||||
|
||||
// Return arguments separated by spaces.
|
||||
const parse_node_tree_t::parse_node_list_t arg_nodes =
|
||||
tree.find_nodes(stmt, symbol_argument);
|
||||
for (size_t i = 0; i < arg_nodes.size(); i++) {
|
||||
if (i > 0) out_joined_args->push_back(L' ');
|
||||
out_joined_args->append(arg_nodes.at(i)->get_source(src));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test the LL2 (two token lookahead) nature of the parser by exercising the special builtin and
|
||||
@@ -3255,29 +3257,31 @@ static wcstring_list_t separate_by_format_specifiers(const wchar_t *format) {
|
||||
|
||||
// Walk over the format specifier (if any).
|
||||
cursor = next_specifier;
|
||||
if (*cursor == '%') {
|
||||
cursor++;
|
||||
// Flag
|
||||
if (wcschr(L"#0- +'", *cursor)) cursor++;
|
||||
// Minimum field width
|
||||
while (iswdigit(*cursor)) cursor++;
|
||||
// Precision
|
||||
if (*cursor == L'.') {
|
||||
cursor++;
|
||||
while (iswdigit(*cursor)) cursor++;
|
||||
}
|
||||
// Length modifier
|
||||
if (!wcsncmp(cursor, L"ll", 2) || !wcsncmp(cursor, L"hh", 2)) {
|
||||
cursor += 2;
|
||||
} else if (wcschr(L"hljtzqL", *cursor)) {
|
||||
cursor++;
|
||||
}
|
||||
// The format specifier itself. We allow any character except NUL.
|
||||
if (*cursor != L'\0') {
|
||||
cursor += 1;
|
||||
}
|
||||
assert(cursor <= end);
|
||||
if (*cursor != '%') {
|
||||
continue;
|
||||
}
|
||||
|
||||
cursor++;
|
||||
// Flag
|
||||
if (wcschr(L"#0- +'", *cursor)) cursor++;
|
||||
// Minimum field width
|
||||
while (iswdigit(*cursor)) cursor++;
|
||||
// Precision
|
||||
if (*cursor == L'.') {
|
||||
cursor++;
|
||||
while (iswdigit(*cursor)) cursor++;
|
||||
}
|
||||
// Length modifier
|
||||
if (!wcsncmp(cursor, L"ll", 2) || !wcsncmp(cursor, L"hh", 2)) {
|
||||
cursor += 2;
|
||||
} else if (wcschr(L"hljtzqL", *cursor)) {
|
||||
cursor++;
|
||||
}
|
||||
// The format specifier itself. We allow any character except NUL.
|
||||
if (*cursor != L'\0') {
|
||||
cursor += 1;
|
||||
}
|
||||
assert(cursor <= end);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ void function_add(const function_data_t &data, const parser_t &parser, int defin
|
||||
UNUSED(parser);
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
CHECK(!data.name.empty(), );
|
||||
CHECK(!data.name.empty(), ); //!OCLINT(multiple unary operator)
|
||||
CHECK(data.definition, );
|
||||
scoped_lock locker(functions_lock);
|
||||
|
||||
@@ -272,9 +272,9 @@ bool function_get_desc(const wcstring &name, wcstring *out_desc) {
|
||||
if (out_desc && func && !func->description.empty()) {
|
||||
out_desc->assign(_(func->description.c_str()));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void function_set_desc(const wcstring &name, const wcstring &desc) {
|
||||
@@ -311,8 +311,8 @@ wcstring_list_t function_get_names(int get_hidden) {
|
||||
const wcstring &name = iter->first;
|
||||
|
||||
// Maybe skip hidden.
|
||||
if (!get_hidden) {
|
||||
if (name.empty() || name.at(0) == L'_') continue;
|
||||
if (!get_hidden && (name.empty() || name.at(0) == L'_')) {
|
||||
continue;
|
||||
}
|
||||
names.insert(name);
|
||||
}
|
||||
|
||||
@@ -52,11 +52,15 @@ static const wchar_t *const highlight_var[] = {
|
||||
|
||||
};
|
||||
|
||||
/// Determine if the filesystem containing the given fd is case insensitive.
|
||||
/// Determine if the filesystem containing the given fd is case insensitive for lookups regardless
|
||||
/// of whether it preserves the case when saving a pathname.
|
||||
///
|
||||
/// Returns:
|
||||
/// false: the filesystem is not case insensitive
|
||||
/// true: the file system is case insensitive
|
||||
typedef std::map<wcstring, bool> case_sensitivity_cache_t;
|
||||
bool fs_is_case_insensitive(const wcstring &path, int fd,
|
||||
case_sensitivity_cache_t &case_sensitivity_cache) {
|
||||
// If _PC_CASE_SENSITIVE is not defined, assume case sensitive.
|
||||
bool result = false;
|
||||
#ifdef _PC_CASE_SENSITIVE
|
||||
// Try the cache first.
|
||||
@@ -71,6 +75,11 @@ bool fs_is_case_insensitive(const wcstring &path, int fd,
|
||||
result = (ret == 0);
|
||||
case_sensitivity_cache[path] = result;
|
||||
}
|
||||
#else
|
||||
// Silence lint tools about the unused parameters.
|
||||
UNUSED(path);
|
||||
UNUSED(fd);
|
||||
UNUSED(case_sensitivity_cache);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
@@ -87,7 +96,7 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l
|
||||
path_flags_t flags) {
|
||||
ASSERT_IS_BACKGROUND_THREAD();
|
||||
|
||||
const bool require_dir = !!(flags & PATH_REQUIRE_DIR);
|
||||
const bool require_dir = static_cast<bool>(flags & PATH_REQUIRE_DIR);
|
||||
wcstring clean_potential_path_fragment;
|
||||
int has_magic = 0;
|
||||
bool result = false;
|
||||
@@ -122,73 +131,73 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_magic && !clean_potential_path_fragment.empty()) {
|
||||
// Don't test the same path multiple times, which can happen if the path is absolute and the
|
||||
// CDPATH contains multiple entries.
|
||||
std::set<wcstring> checked_paths;
|
||||
if (has_magic || clean_potential_path_fragment.empty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Keep a cache of which paths / filesystems are case sensitive.
|
||||
case_sensitivity_cache_t case_sensitivity_cache;
|
||||
// Don't test the same path multiple times, which can happen if the path is absolute and the
|
||||
// CDPATH contains multiple entries.
|
||||
std::set<wcstring> checked_paths;
|
||||
|
||||
for (size_t wd_idx = 0; wd_idx < directories.size() && !result; wd_idx++) {
|
||||
const wcstring &wd = directories.at(wd_idx);
|
||||
// Keep a cache of which paths / filesystems are case sensitive.
|
||||
case_sensitivity_cache_t case_sensitivity_cache;
|
||||
|
||||
const wcstring abs_path =
|
||||
path_apply_working_directory(clean_potential_path_fragment, wd);
|
||||
for (size_t wd_idx = 0; wd_idx < directories.size() && !result; wd_idx++) {
|
||||
const wcstring &wd = directories.at(wd_idx);
|
||||
|
||||
// Skip this if it's empty or we've already checked it.
|
||||
if (abs_path.empty() || checked_paths.count(abs_path)) continue;
|
||||
checked_paths.insert(abs_path);
|
||||
const wcstring abs_path = path_apply_working_directory(clean_potential_path_fragment, wd);
|
||||
|
||||
// If we end with a slash, then it must be a directory.
|
||||
bool must_be_full_dir = abs_path.at(abs_path.size() - 1) == L'/';
|
||||
if (must_be_full_dir) {
|
||||
struct stat buf;
|
||||
if (0 == wstat(abs_path, &buf) && S_ISDIR(buf.st_mode)) {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
// We do not end with a slash; it does not have to be a directory.
|
||||
DIR *dir = NULL;
|
||||
const wcstring dir_name = wdirname(abs_path);
|
||||
const wcstring filename_fragment = wbasename(abs_path);
|
||||
if (dir_name == L"/" && filename_fragment == L"/") {
|
||||
// cd ///.... No autosuggestion.
|
||||
result = true;
|
||||
} else if ((dir = wopendir(dir_name))) {
|
||||
// Check if we're case insensitive.
|
||||
const bool do_case_insensitive =
|
||||
fs_is_case_insensitive(dir_name, dirfd(dir), case_sensitivity_cache);
|
||||
// Skip this if it's empty or we've already checked it.
|
||||
if (abs_path.empty() || checked_paths.count(abs_path)) continue;
|
||||
checked_paths.insert(abs_path);
|
||||
|
||||
wcstring matched_file;
|
||||
// If we end with a slash, then it must be a directory.
|
||||
bool must_be_full_dir = abs_path.at(abs_path.size() - 1) == L'/';
|
||||
if (must_be_full_dir) {
|
||||
struct stat buf;
|
||||
if (0 == wstat(abs_path, &buf) && S_ISDIR(buf.st_mode)) {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
// We do not end with a slash; it does not have to be a directory.
|
||||
DIR *dir = NULL;
|
||||
const wcstring dir_name = wdirname(abs_path);
|
||||
const wcstring filename_fragment = wbasename(abs_path);
|
||||
if (dir_name == L"/" && filename_fragment == L"/") {
|
||||
// cd ///.... No autosuggestion.
|
||||
result = true;
|
||||
} else if ((dir = wopendir(dir_name))) {
|
||||
// Check if we're case insensitive.
|
||||
const bool do_case_insensitive =
|
||||
fs_is_case_insensitive(dir_name, dirfd(dir), case_sensitivity_cache);
|
||||
|
||||
// We opened the dir_name; look for a string where the base name prefixes it
|
||||
// Don't ask for the is_dir value unless we care, because it can cause extra
|
||||
// filesystem access.
|
||||
wcstring ent;
|
||||
bool is_dir = false;
|
||||
while (wreaddir_resolving(dir, dir_name, ent, require_dir ? &is_dir : NULL)) {
|
||||
// Maybe skip directories.
|
||||
if (require_dir && !is_dir) {
|
||||
continue;
|
||||
}
|
||||
wcstring matched_file;
|
||||
|
||||
if (string_prefixes_string(filename_fragment, ent) ||
|
||||
(do_case_insensitive &&
|
||||
string_prefixes_string_case_insensitive(filename_fragment, ent))) {
|
||||
// We matched.
|
||||
matched_file = ent;
|
||||
break;
|
||||
}
|
||||
// We opened the dir_name; look for a string where the base name prefixes it Don't
|
||||
// ask for the is_dir value unless we care, because it can cause extra filesystem
|
||||
// access.
|
||||
wcstring ent;
|
||||
bool is_dir = false;
|
||||
while (wreaddir_resolving(dir, dir_name, ent, require_dir ? &is_dir : NULL)) {
|
||||
// Maybe skip directories.
|
||||
if (require_dir && !is_dir) {
|
||||
continue;
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
// We succeeded if we found a match.
|
||||
result = !matched_file.empty();
|
||||
if (string_prefixes_string(filename_fragment, ent) ||
|
||||
(do_case_insensitive &&
|
||||
string_prefixes_string_case_insensitive(filename_fragment, ent))) {
|
||||
matched_file = ent; // we matched
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
result = !matched_file.empty(); // we succeeded if we found a match
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -224,19 +233,16 @@ static bool is_potential_cd_path(const wcstring &path, const wcstring &working_d
|
||||
bool plain_statement_get_expanded_command(const wcstring &src, const parse_node_tree_t &tree,
|
||||
const parse_node_t &plain_statement, wcstring *out_cmd) {
|
||||
assert(plain_statement.type == symbol_plain_statement);
|
||||
bool result = false;
|
||||
|
||||
// Get the command.
|
||||
// Get the command. Try expanding it. If we cannot, it's an error.
|
||||
wcstring cmd;
|
||||
if (tree.command_for_plain_statement(plain_statement, src, &cmd)) {
|
||||
// Try expanding it. If we cannot, it's an error.
|
||||
if (expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS)) {
|
||||
// Success, return the expanded string by reference.
|
||||
out_cmd->swap(cmd);
|
||||
result = true;
|
||||
}
|
||||
if (tree.command_for_plain_statement(plain_statement, src, &cmd) &&
|
||||
expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS)) {
|
||||
// Success, return the expanded string by reference.
|
||||
out_cmd->swap(cmd);
|
||||
return true;
|
||||
}
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background) {
|
||||
@@ -297,8 +303,6 @@ static bool has_expand_reserved(const wcstring &str) {
|
||||
// (as a copied node), if any. This is used by autosuggestions.
|
||||
static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expanded_command,
|
||||
parse_node_t *out_last_arg) {
|
||||
bool result = false;
|
||||
|
||||
// Parse the buffer.
|
||||
parse_node_tree_t parse_tree;
|
||||
parse_tree_from_string(buff,
|
||||
@@ -308,21 +312,17 @@ static bool autosuggest_parse_command(const wcstring &buff, wcstring *out_expand
|
||||
// Find the last statement.
|
||||
const parse_node_t *last_statement =
|
||||
parse_tree.find_last_node_of_type(symbol_plain_statement, NULL);
|
||||
if (last_statement != NULL) {
|
||||
if (plain_statement_get_expanded_command(buff, parse_tree, *last_statement,
|
||||
out_expanded_command)) {
|
||||
// We got it.
|
||||
result = true;
|
||||
|
||||
// Find the last argument. If we don't get one, return an invalid node.
|
||||
const parse_node_t *last_arg =
|
||||
parse_tree.find_last_node_of_type(symbol_argument, last_statement);
|
||||
if (last_arg != NULL) {
|
||||
*out_last_arg = *last_arg;
|
||||
}
|
||||
if (last_statement != NULL && plain_statement_get_expanded_command(
|
||||
buff, parse_tree, *last_statement, out_expanded_command)) {
|
||||
// Find the last argument. If we don't get one, return an invalid node.
|
||||
const parse_node_t *last_arg =
|
||||
parse_tree.find_last_node_of_type(symbol_argument, last_statement);
|
||||
if (last_arg != NULL) {
|
||||
*out_last_arg = *last_arg;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool autosuggest_validate_from_history(const history_item_t &item,
|
||||
@@ -346,41 +346,35 @@ bool autosuggest_validate_from_history(const history_item_t &item,
|
||||
handled = true;
|
||||
bool is_help =
|
||||
string_prefixes_string(dir, L"--help") || string_prefixes_string(dir, L"-h");
|
||||
if (is_help) {
|
||||
suggestionOK = false;
|
||||
} else {
|
||||
if (!is_help) {
|
||||
wcstring path;
|
||||
bool can_cd = path_get_cdpath(dir, &path, working_directory.c_str(), vars);
|
||||
if (!can_cd) {
|
||||
suggestionOK = false;
|
||||
} else if (paths_are_same_file(working_directory, path)) {
|
||||
// Don't suggest the working directory as the path!
|
||||
suggestionOK = false;
|
||||
} else {
|
||||
if (can_cd && !paths_are_same_file(working_directory, path)) {
|
||||
suggestionOK = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not handled specially, handle it here.
|
||||
if (!handled) {
|
||||
bool cmd_ok = false;
|
||||
if (handled) {
|
||||
return suggestionOK;
|
||||
}
|
||||
|
||||
if (path_get_path(parsed_command, NULL)) {
|
||||
cmd_ok = true;
|
||||
} else if (builtin_exists(parsed_command) ||
|
||||
function_exists_no_autoload(parsed_command, vars)) {
|
||||
cmd_ok = true;
|
||||
}
|
||||
// Not handled specially so handle it here.
|
||||
bool cmd_ok = false;
|
||||
if (path_get_path(parsed_command, NULL)) {
|
||||
cmd_ok = true;
|
||||
} else if (builtin_exists(parsed_command) ||
|
||||
function_exists_no_autoload(parsed_command, vars)) {
|
||||
cmd_ok = true;
|
||||
}
|
||||
|
||||
if (cmd_ok) {
|
||||
const path_list_t &paths = item.get_required_paths();
|
||||
if (paths.empty()) {
|
||||
suggestionOK = true;
|
||||
} else {
|
||||
suggestionOK = detector.paths_are_valid(paths);
|
||||
}
|
||||
if (cmd_ok) {
|
||||
const path_list_t &paths = item.get_required_paths();
|
||||
if (paths.empty()) {
|
||||
suggestionOK = true;
|
||||
} else {
|
||||
suggestionOK = detector.paths_are_valid(paths);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,6 +581,9 @@ static void color_argument_internal(const wcstring &buffstr,
|
||||
mode = e_double_quoted;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break; // we ignore all other characters
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -640,6 +637,9 @@ static void color_argument_internal(const wcstring &buffstr,
|
||||
in_pos -= 1;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break; // we ignore all other characters
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1102,26 +1102,27 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
|
||||
// Color the command.
|
||||
const parse_node_t *cmd_node =
|
||||
parse_tree.get_child(node, 0, parse_token_type_string);
|
||||
if (cmd_node != NULL && cmd_node->has_source()) {
|
||||
bool is_valid_cmd = false;
|
||||
if (!this->io_ok) {
|
||||
// We cannot check if the command is invalid, so just assume it's valid.
|
||||
is_valid_cmd = true;
|
||||
} else {
|
||||
// Check to see if the command is valid.
|
||||
wcstring cmd(buff, cmd_node->source_start, cmd_node->source_length);
|
||||
|
||||
// Try expanding it. If we cannot, it's an error.
|
||||
bool expanded = expand_one(
|
||||
cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS);
|
||||
if (expanded && !has_expand_reserved(cmd)) {
|
||||
is_valid_cmd =
|
||||
command_is_valid(cmd, decoration, working_directory, vars);
|
||||
}
|
||||
}
|
||||
this->color_node(*cmd_node,
|
||||
is_valid_cmd ? highlight_spec_command : highlight_spec_error);
|
||||
if (cmd_node == NULL || !cmd_node->has_source()) {
|
||||
break; // not much as we can do without a node that has source text
|
||||
}
|
||||
|
||||
bool is_valid_cmd = false;
|
||||
if (!this->io_ok) {
|
||||
// We cannot check if the command is invalid, so just assume it's valid.
|
||||
is_valid_cmd = true;
|
||||
} else {
|
||||
// Check to see if the command is valid.
|
||||
wcstring cmd(buff, cmd_node->source_start, cmd_node->source_length);
|
||||
|
||||
// Try expanding it. If we cannot, it's an error.
|
||||
bool expanded = expand_one(
|
||||
cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_JOBS);
|
||||
if (expanded && !has_expand_reserved(cmd)) {
|
||||
is_valid_cmd = command_is_valid(cmd, decoration, working_directory, vars);
|
||||
}
|
||||
}
|
||||
this->color_node(*cmd_node,
|
||||
is_valid_cmd ? highlight_spec_command : highlight_spec_error);
|
||||
break;
|
||||
}
|
||||
case symbol_arguments_or_redirections_list:
|
||||
@@ -1150,32 +1151,29 @@ const highlighter_t::color_array_t &highlighter_t::highlight() {
|
||||
}
|
||||
}
|
||||
|
||||
if (this->io_ok && this->cursor_pos <= this->buff.size()) {
|
||||
// If the cursor is over an argument, and that argument is a valid path, underline it.
|
||||
for (parse_node_tree_t::const_iterator iter = parse_tree.begin(); iter != parse_tree.end();
|
||||
++iter) {
|
||||
const parse_node_t &node = *iter;
|
||||
if (!this->io_ok || this->cursor_pos > this->buff.size()) {
|
||||
return color_array;
|
||||
}
|
||||
|
||||
// Must be an argument with source.
|
||||
if (node.type != symbol_argument || !node.has_source()) continue;
|
||||
// If the cursor is over an argument, and that argument is a valid path, underline it.
|
||||
for (parse_node_tree_t::const_iterator iter = parse_tree.begin(); iter != parse_tree.end();
|
||||
++iter) {
|
||||
const parse_node_t &node = *iter;
|
||||
|
||||
// See if this node contains the cursor. We check <= source_length so that, when
|
||||
// backspacing (and the cursor is just beyond the last token), we may still underline
|
||||
// it.
|
||||
if (this->cursor_pos >= node.source_start &&
|
||||
this->cursor_pos - node.source_start <= node.source_length) {
|
||||
// See if this is a valid path.
|
||||
if (node_is_potential_path(buff, node, working_directory)) {
|
||||
// It is, underline it.
|
||||
for (size_t i = node.source_start; i < node.source_start + node.source_length;
|
||||
i++) {
|
||||
// Don't color highlight_spec_error because it looks dorky. For example,
|
||||
// trying to cd into a non-directory would show an underline and also red.
|
||||
if (highlight_get_primary(this->color_array.at(i)) !=
|
||||
highlight_spec_error) {
|
||||
this->color_array.at(i) |= highlight_modifier_valid_path;
|
||||
}
|
||||
}
|
||||
// Must be an argument with source.
|
||||
if (node.type != symbol_argument || !node.has_source()) continue;
|
||||
|
||||
// See if this node contains the cursor. We check <= source_length so that, when backspacing
|
||||
// (and the cursor is just beyond the last token), we may still underline it.
|
||||
if (this->cursor_pos >= node.source_start &&
|
||||
this->cursor_pos - node.source_start <= node.source_length &&
|
||||
node_is_potential_path(buff, node, working_directory)) {
|
||||
// It is, underline it.
|
||||
for (size_t i = node.source_start; i < node.source_start + node.source_length; i++) {
|
||||
// Don't color highlight_spec_error because it looks dorky. For example,
|
||||
// trying to cd into a non-directory would show an underline and also red.
|
||||
if (highlight_get_primary(this->color_array.at(i)) != highlight_spec_error) {
|
||||
this->color_array.at(i) |= highlight_modifier_valid_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1257,6 +1255,9 @@ static void highlight_universal_internal(const wcstring &buffstr,
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break; // we ignore all other characters
|
||||
}
|
||||
}
|
||||
if ((*str == L'\0')) break;
|
||||
str++;
|
||||
|
||||
@@ -769,7 +769,7 @@ void history_t::save_internal_unless_disabled() {
|
||||
}
|
||||
|
||||
// This might be a good candidate for moving to a background thread.
|
||||
time_profiler_t profiler(vacuum ? "save_internal vacuum"
|
||||
time_profiler_t profiler(vacuum ? "save_internal vacuum" //!OCLINT(unused var)
|
||||
: "save_internal no vacuum"); //!OCLINT(side-effect)
|
||||
this->save_internal(vacuum);
|
||||
|
||||
@@ -794,11 +794,10 @@ void history_t::add(const wcstring &str, history_identifier_t ident, bool pendin
|
||||
bool icompare_pred(wchar_t a, wchar_t b) { return std::tolower(a) == std::tolower(b); }
|
||||
|
||||
bool icompare(wcstring const &a, wcstring const &b) {
|
||||
if (a.length() == b.length()) {
|
||||
return std::equal(b.begin(), b.end(), a.begin(), icompare_pred);
|
||||
} else {
|
||||
if (a.length() != b.length()) {
|
||||
return false;
|
||||
}
|
||||
return std::equal(b.begin(), b.end(), a.begin(), icompare_pred);
|
||||
}
|
||||
|
||||
// Remove matching history entries from our list of new items. This only supports literal,
|
||||
@@ -939,43 +938,45 @@ void history_t::populate_from_mmap(void) {
|
||||
/// if successful. Returns the mapped memory region by reference.
|
||||
bool history_t::map_file(const wcstring &name, const char **out_map_start, size_t *out_map_len,
|
||||
file_id_t *file_id) {
|
||||
bool result = false;
|
||||
wcstring filename = history_filename(name, L"");
|
||||
if (!filename.empty()) {
|
||||
int fd = wopen_cloexec(filename, O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
// Get the file ID if requested.
|
||||
if (file_id != NULL) *file_id = file_id_for_fd(fd);
|
||||
if (filename.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Take a read lock to guard against someone else appending. This is released when the
|
||||
// file is closed (below). We will read the file after releasing the lock, but that's
|
||||
// not a problem, because we never modify already written data. In short, the purpose of
|
||||
// this lock is to ensure we don't see the file size change mid-update.
|
||||
//
|
||||
// We may fail to lock (e.g. on lockless NFS - see
|
||||
// https://github.com/fish-shell/fish-shell/issues/685 ). In that case, we proceed as
|
||||
// if it did not fail. The risk is that we may get an incomplete history item; this
|
||||
// is unlikely because we only treat an item as valid if it has a terminating
|
||||
// newline.
|
||||
//
|
||||
// Simulate a failing lock in chaos_mode.
|
||||
if (!chaos_mode) history_file_lock(fd, F_RDLCK);
|
||||
off_t len = lseek(fd, 0, SEEK_END);
|
||||
if (len != (off_t)-1) {
|
||||
size_t mmap_length = (size_t)len;
|
||||
if (lseek(fd, 0, SEEK_SET) == 0) {
|
||||
char *mmap_start;
|
||||
if ((mmap_start = (char *)mmap(0, mmap_length, PROT_READ, MAP_PRIVATE, fd,
|
||||
0)) != MAP_FAILED) {
|
||||
result = true;
|
||||
*out_map_start = mmap_start;
|
||||
*out_map_len = mmap_length;
|
||||
}
|
||||
}
|
||||
int fd = wopen_cloexec(filename, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
// Get the file ID if requested.
|
||||
if (file_id != NULL) *file_id = file_id_for_fd(fd);
|
||||
|
||||
// Take a read lock to guard against someone else appending. This is released when the file
|
||||
// is closed (below). We will read the file after releasing the lock, but that's not a
|
||||
// problem, because we never modify already written data. In short, the purpose of this lock
|
||||
// is to ensure we don't see the file size change mid-update.
|
||||
//
|
||||
// We may fail to lock (e.g. on lockless NFS - see issue #685. In that case, we proceed as
|
||||
// if it did not fail. The risk is that we may get an incomplete history item; this is
|
||||
// unlikely because we only treat an item as valid if it has a terminating newline.
|
||||
//
|
||||
// Simulate a failing lock in chaos_mode.
|
||||
if (!chaos_mode) history_file_lock(fd, F_RDLCK);
|
||||
off_t len = lseek(fd, 0, SEEK_END);
|
||||
if (len != (off_t)-1) {
|
||||
size_t mmap_length = (size_t)len;
|
||||
if (lseek(fd, 0, SEEK_SET) == 0) {
|
||||
char *mmap_start;
|
||||
if ((mmap_start = (char *)mmap(0, mmap_length, PROT_READ, MAP_PRIVATE, fd,
|
||||
0)) != MAP_FAILED) {
|
||||
result = true;
|
||||
*out_map_start = mmap_start;
|
||||
*out_map_len = mmap_length;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ struct input_mapping_t {
|
||||
input_mapping_t(const wcstring &s, const std::vector<wcstring> &c,
|
||||
const wcstring &m = DEFAULT_BIND_MODE, const wcstring &sm = DEFAULT_BIND_MODE)
|
||||
: seq(s), commands(c), mode(m), sets_mode(sm) {
|
||||
static unsigned int s_last_input_mapping_specification_order = 0;
|
||||
specification_order = ++s_last_input_mapping_specification_order;
|
||||
static unsigned int s_last_input_map_spec_order = 0;
|
||||
specification_order = ++s_last_input_map_spec_order;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -223,14 +223,8 @@ void input_set_bind_mode(const wcstring &bm) {
|
||||
}
|
||||
|
||||
/// Returns the arity of a given input function.
|
||||
int input_function_arity(int function) {
|
||||
switch (function) {
|
||||
case R_FORWARD_JUMP:
|
||||
case R_BACKWARD_JUMP: {
|
||||
return 1;
|
||||
}
|
||||
default: { return 0; }
|
||||
}
|
||||
static int input_function_arity(int function) {
|
||||
return (function == R_FORWARD_JUMP || function == R_BACKWARD_JUMP) ? 1 : 0;
|
||||
}
|
||||
|
||||
/// Sets the return status of the most recently executed input function.
|
||||
@@ -378,7 +372,6 @@ int input_init() {
|
||||
} else {
|
||||
debug(0, _(L"Using fallback terminal type '%ls'"), DEFAULT_TERM);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
input_terminfo_init();
|
||||
@@ -614,8 +607,9 @@ wint_t input_readch(bool allow_commands) {
|
||||
if (input_function_status) {
|
||||
return input_readch();
|
||||
}
|
||||
while ((c = input_common_readch(0)) && c >= R_MIN && c <= R_MAX) {
|
||||
// do nothing
|
||||
c = input_common_readch(0);
|
||||
while (c >= R_MIN && c <= R_MAX) {
|
||||
c = input_common_readch(0);
|
||||
}
|
||||
input_common_next_ch(c);
|
||||
return input_readch();
|
||||
|
||||
@@ -110,26 +110,17 @@ static wint_t readb() {
|
||||
|
||||
res = select(fd_max + 1, &fdset, 0, 0, usecs_delay > 0 ? &tv : NULL);
|
||||
if (res == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN: {
|
||||
if (interrupt_handler) {
|
||||
int res = interrupt_handler();
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
if (has_lookahead()) {
|
||||
return lookahead_pop();
|
||||
}
|
||||
}
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
if (interrupt_handler) {
|
||||
int res = interrupt_handler();
|
||||
if (res) return res;
|
||||
if (has_lookahead()) return lookahead_pop();
|
||||
}
|
||||
|
||||
do_loop = true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// The terminal has been closed. Save and exit.
|
||||
return R_EOF;
|
||||
}
|
||||
do_loop = true;
|
||||
} else {
|
||||
// The terminal has been closed. Save and exit.
|
||||
return R_EOF;
|
||||
}
|
||||
} else {
|
||||
// Assume we loop unless we see a character in stdin.
|
||||
@@ -167,9 +158,6 @@ static wint_t readb() {
|
||||
return arr[0];
|
||||
}
|
||||
|
||||
// Directly set the input timeout.
|
||||
void set_wait_on_escape_ms(int ms) { wait_on_escape_ms = ms; }
|
||||
|
||||
// Update the wait_on_escape_ms value in response to the fish_escape_delay_ms user variable being
|
||||
// set.
|
||||
void update_wait_on_escape_ms() {
|
||||
|
||||
@@ -86,9 +86,6 @@ void input_common_destroy();
|
||||
/// Adjust the escape timeout.
|
||||
void update_wait_on_escape_ms();
|
||||
|
||||
/// Set the escape timeout directly.
|
||||
void set_wait_on_escape_ms(int ms);
|
||||
|
||||
/// Function used by input_readch to read bytes from stdin until enough bytes have been read to
|
||||
/// convert them to a wchar_t. Conversion is done using mbrtowc. If a character has previously been
|
||||
/// read and then 'unread' using \c input_common_unreadch, that character is returned. If timed is
|
||||
|
||||
56
src/io.cpp
56
src/io.cpp
@@ -165,35 +165,35 @@ void io_print(const io_chain_t &chain)
|
||||
/// created is marked close-on-exec. Returns -1 on failure (in which case the given fd is still
|
||||
/// closed).
|
||||
static int move_fd_to_unused(int fd, const io_chain_t &io_chain) {
|
||||
int new_fd = fd;
|
||||
if (fd >= 0 && io_chain.get_io_for_fd(fd).get() != NULL) {
|
||||
// We have fd >= 0, and it's a conflict. dup it and recurse. Note that we recurse before
|
||||
// anything is closed; this forces the kernel to give us a new one (or report fd
|
||||
// exhaustion).
|
||||
int tmp_fd;
|
||||
do {
|
||||
tmp_fd = dup(fd);
|
||||
} while (tmp_fd < 0 && errno == EINTR);
|
||||
|
||||
assert(tmp_fd != fd);
|
||||
if (tmp_fd < 0) {
|
||||
// Likely fd exhaustion.
|
||||
new_fd = -1;
|
||||
} else {
|
||||
// Ok, we have a new candidate fd. Recurse. If we get a valid fd, either it's the same
|
||||
// as what we gave it, or it's a new fd and what we gave it has been closed. If we get a
|
||||
// negative value, the fd also has been closed.
|
||||
set_cloexec(tmp_fd);
|
||||
new_fd = move_fd_to_unused(tmp_fd, io_chain);
|
||||
}
|
||||
|
||||
// We're either returning a new fd or an error. In both cases, we promise to close the old
|
||||
// one.
|
||||
assert(new_fd != fd);
|
||||
int saved_errno = errno;
|
||||
exec_close(fd);
|
||||
errno = saved_errno;
|
||||
if (fd < 0 || io_chain.get_io_for_fd(fd).get() == NULL) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
// We have fd >= 0, and it's a conflict. dup it and recurse. Note that we recurse before
|
||||
// anything is closed; this forces the kernel to give us a new one (or report fd exhaustion).
|
||||
int new_fd = fd;
|
||||
int tmp_fd;
|
||||
do {
|
||||
tmp_fd = dup(fd);
|
||||
} while (tmp_fd < 0 && errno == EINTR);
|
||||
|
||||
assert(tmp_fd != fd);
|
||||
if (tmp_fd < 0) {
|
||||
// Likely fd exhaustion.
|
||||
new_fd = -1;
|
||||
} else {
|
||||
// Ok, we have a new candidate fd. Recurse. If we get a valid fd, either it's the same as
|
||||
// what we gave it, or it's a new fd and what we gave it has been closed. If we get a
|
||||
// negative value, the fd also has been closed.
|
||||
set_cloexec(tmp_fd);
|
||||
new_fd = move_fd_to_unused(tmp_fd, io_chain);
|
||||
}
|
||||
|
||||
// We're either returning a new fd or an error. In both cases, we promise to close the old one.
|
||||
assert(new_fd != fd);
|
||||
int saved_errno = errno;
|
||||
exec_close(fd);
|
||||
errno = saved_errno;
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
#define IO_SERVICE_MAIN_THREAD_REQUEST_QUEUE 99
|
||||
#define IO_SERVICE_RESULT_QUEUE 100
|
||||
|
||||
#define IOTHREAD_LOG if (0)
|
||||
|
||||
static void iothread_service_main_thread_requests(void);
|
||||
static void iothread_service_result_queue();
|
||||
|
||||
@@ -59,9 +57,9 @@ static pthread_mutex_t s_result_queue_lock;
|
||||
static std::queue<SpawnRequest_t *> s_result_queue;
|
||||
|
||||
// "Do on main thread" support.
|
||||
static pthread_mutex_t s_main_thread_performer_lock; // protects the main thread requests
|
||||
static pthread_cond_t s_main_thread_performer_condition; // protects the main thread requests
|
||||
static pthread_mutex_t s_main_thread_request_queue_lock; // protects the queue
|
||||
static pthread_mutex_t s_main_thread_performer_lock; // protects the main thread requests
|
||||
static pthread_cond_t s_main_thread_performer_cond; // protects the main thread requests
|
||||
static pthread_mutex_t s_main_thread_request_q_lock; // protects the queue
|
||||
static std::queue<MainThreadRequest_t *> s_main_thread_request_queue;
|
||||
|
||||
// Notifying pipes.
|
||||
@@ -75,9 +73,9 @@ static void iothread_init(void) {
|
||||
// Initialize some locks.
|
||||
VOMIT_ON_FAILURE(pthread_mutex_init(&s_spawn_queue_lock, NULL));
|
||||
VOMIT_ON_FAILURE(pthread_mutex_init(&s_result_queue_lock, NULL));
|
||||
VOMIT_ON_FAILURE(pthread_mutex_init(&s_main_thread_request_queue_lock, NULL));
|
||||
VOMIT_ON_FAILURE(pthread_mutex_init(&s_main_thread_request_q_lock, NULL));
|
||||
VOMIT_ON_FAILURE(pthread_mutex_init(&s_main_thread_performer_lock, NULL));
|
||||
VOMIT_ON_FAILURE(pthread_cond_init(&s_main_thread_performer_condition, NULL));
|
||||
VOMIT_ON_FAILURE(pthread_cond_init(&s_main_thread_performer_cond, NULL));
|
||||
|
||||
// Initialize the completion pipes.
|
||||
int pipes[2] = {0, 0};
|
||||
@@ -120,7 +118,7 @@ static void *iothread_worker(void *unused) {
|
||||
scoped_lock locker(s_spawn_queue_lock);
|
||||
struct SpawnRequest_t *req;
|
||||
while ((req = dequeue_spawn_request()) != NULL) {
|
||||
IOTHREAD_LOG fprintf(stderr, "pthread %p dequeued %p\n", this_thread(), req);
|
||||
debug(5, "pthread %p dequeued %p\n", this_thread(), req);
|
||||
// Unlock the queue while we execute the request.
|
||||
locker.unlock();
|
||||
|
||||
@@ -153,7 +151,7 @@ static void *iothread_worker(void *unused) {
|
||||
assert(s_active_thread_count > 0);
|
||||
s_active_thread_count -= 1;
|
||||
|
||||
IOTHREAD_LOG fprintf(stderr, "pthread %p exiting\n", this_thread());
|
||||
debug(5, "pthread %p exiting\n", this_thread());
|
||||
// We're done.
|
||||
return NULL;
|
||||
}
|
||||
@@ -175,7 +173,7 @@ static void iothread_spawn() {
|
||||
|
||||
// We will never join this thread.
|
||||
VOMIT_ON_FAILURE(pthread_detach(thread));
|
||||
IOTHREAD_LOG fprintf(stderr, "pthread %p spawned\n", (void *)(intptr_t)thread);
|
||||
debug(5, "pthread %p spawned\n", (void *)(intptr_t)thread);
|
||||
// Restore our sigmask.
|
||||
VOMIT_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &saved_set, NULL));
|
||||
}
|
||||
@@ -222,21 +220,15 @@ int iothread_port(void) {
|
||||
|
||||
void iothread_service_completion(void) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
char wakeup_byte = 0;
|
||||
char wakeup_byte;
|
||||
|
||||
VOMIT_ON_FAILURE(1 != read_loop(iothread_port(), &wakeup_byte, sizeof wakeup_byte));
|
||||
switch (wakeup_byte) {
|
||||
case IO_SERVICE_MAIN_THREAD_REQUEST_QUEUE: {
|
||||
iothread_service_main_thread_requests();
|
||||
break;
|
||||
}
|
||||
case IO_SERVICE_RESULT_QUEUE: {
|
||||
iothread_service_result_queue();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
fprintf(stderr, "Unknown wakeup byte %02x in %s\n", wakeup_byte, __FUNCTION__);
|
||||
break;
|
||||
}
|
||||
if (wakeup_byte == IO_SERVICE_MAIN_THREAD_REQUEST_QUEUE) {
|
||||
iothread_service_main_thread_requests();
|
||||
} else if (wakeup_byte == IO_SERVICE_RESULT_QUEUE) {
|
||||
iothread_service_result_queue();
|
||||
} else {
|
||||
fprintf(stderr, "Unknown wakeup byte %02x in %s\n", wakeup_byte, __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +287,7 @@ static void iothread_service_main_thread_requests(void) {
|
||||
// Move the queue to a local variable.
|
||||
std::queue<MainThreadRequest_t *> request_queue;
|
||||
{
|
||||
scoped_lock queue_lock(s_main_thread_request_queue_lock);
|
||||
scoped_lock queue_lock(s_main_thread_request_q_lock);
|
||||
std::swap(request_queue, s_main_thread_request_queue);
|
||||
}
|
||||
|
||||
@@ -319,7 +311,7 @@ static void iothread_service_main_thread_requests(void) {
|
||||
// Because the waiting thread performs step 1 under the lock, if we take the lock, we avoid
|
||||
// posting before the waiting thread is waiting.
|
||||
scoped_lock broadcast_lock(s_main_thread_performer_lock);
|
||||
VOMIT_ON_FAILURE(pthread_cond_broadcast(&s_main_thread_performer_condition));
|
||||
VOMIT_ON_FAILURE(pthread_cond_broadcast(&s_main_thread_performer_cond));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +351,7 @@ int iothread_perform_on_main_base(int (*handler)(void *), void *context) {
|
||||
// Append it. Do not delete the nested scope as it is crucial to the proper functioning of this
|
||||
// code by virtue of the lock management.
|
||||
{
|
||||
scoped_lock queue_lock(s_main_thread_request_queue_lock);
|
||||
scoped_lock queue_lock(s_main_thread_request_q_lock);
|
||||
s_main_thread_request_queue.push(&req);
|
||||
}
|
||||
|
||||
@@ -373,7 +365,7 @@ int iothread_perform_on_main_base(int (*handler)(void *), void *context) {
|
||||
// It would be nice to support checking for cancellation here, but the clients need a
|
||||
// deterministic way to clean up to avoid leaks
|
||||
VOMIT_ON_FAILURE(
|
||||
pthread_cond_wait(&s_main_thread_performer_condition, &s_main_thread_performer_lock));
|
||||
pthread_cond_wait(&s_main_thread_performer_cond, &s_main_thread_performer_lock));
|
||||
}
|
||||
|
||||
// Ok, the request must now be done.
|
||||
|
||||
103
src/output.cpp
103
src/output.cpp
@@ -32,7 +32,7 @@
|
||||
static int writeb_internal(char c);
|
||||
|
||||
/// The function used for output.
|
||||
static int (*out)(char c) = writeb_internal;
|
||||
static int (*out)(char c) = writeb_internal; //!OCLINT(unused param)
|
||||
|
||||
/// Whether term256 and term24bit are supported.
|
||||
static color_support_t color_support = 0;
|
||||
@@ -66,33 +66,32 @@ 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));
|
||||
return true;
|
||||
} else {
|
||||
// We are attempting to bypass the term here. Generate the ANSI escape sequence ourself.
|
||||
char buff[16] = "";
|
||||
if (idx < 16) {
|
||||
// this allows the non-bright color to happen instead of no color working at all when
|
||||
// a bright is attempted when only colors 0-7 are supported.
|
||||
// TODO: enter bold mode in builtin_set_color in the same circumstance- doing that
|
||||
// combined
|
||||
// with what we do here, will make the brights actually work for virtual
|
||||
// consoles/ancient emulators.
|
||||
if (max_colors == 8 && idx > 8) idx -= 8;
|
||||
|
||||
snprintf(buff, sizeof buff, "\x1b[%dm", ((idx > 7) ? 82 : 30) + idx + !is_fg * 10);
|
||||
} else {
|
||||
snprintf(buff, sizeof buff, "\x1b[%d;5;%dm", is_fg ? 38 : 48, idx);
|
||||
}
|
||||
|
||||
int (*writer)(char) = output_get_writer();
|
||||
if (writer) {
|
||||
for (size_t i = 0; buff[i]; i++) {
|
||||
writer(buff[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// We are attempting to bypass the term here. Generate the ANSI escape sequence ourself.
|
||||
char buff[16] = "";
|
||||
if (idx < 16) {
|
||||
// this allows the non-bright color to happen instead of no color working at all when a
|
||||
// bright is attempted when only colors 0-7 are supported.
|
||||
//
|
||||
// TODO: enter bold mode in builtin_set_color in the same circumstance- doing that combined
|
||||
// with what we do here, will make the brights actually work for virtual consoles/ancient
|
||||
// emulators.
|
||||
if (max_colors == 8 && idx > 8) idx -= 8;
|
||||
snprintf(buff, sizeof buff, "\x1b[%dm", ((idx > 7) ? 82 : 30) + idx + !is_fg * 10);
|
||||
} else {
|
||||
snprintf(buff, sizeof buff, "\x1b[%d;5;%dm", is_fg ? 38 : 48, idx);
|
||||
}
|
||||
|
||||
int (*writer)(char) = output_get_writer();
|
||||
if (writer) {
|
||||
for (size_t i = 0; buff[i]; i++) {
|
||||
writer(buff[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_foreground_color(unsigned char idx) {
|
||||
@@ -115,26 +114,28 @@ static bool write_background_color(unsigned char idx) {
|
||||
|
||||
// Exported for builtin_set_color's usage only.
|
||||
bool write_color(rgb_color_t color, bool is_fg) {
|
||||
bool supports_term24bit = !!(output_get_color_support() & color_support_term24bit);
|
||||
bool supports_term24bit =
|
||||
static_cast<bool>(output_get_color_support() & color_support_term24bit);
|
||||
if (!supports_term24bit || !color.is_rgb()) {
|
||||
// Indexed or non-24 bit color.
|
||||
unsigned char idx = index_for_color(color);
|
||||
return (is_fg ? write_foreground_color : write_background_color)(idx);
|
||||
} else {
|
||||
// 24 bit! No tparm here, just ANSI escape sequences.
|
||||
// Foreground: ^[38;2;<r>;<g>;<b>m
|
||||
// Background: ^[48;2;<r>;<g>;<b>m
|
||||
color24_t rgb = color.to_color24();
|
||||
char buff[128];
|
||||
snprintf(buff, sizeof buff, "\x1b[%d;2;%u;%u;%um", is_fg ? 38 : 48, rgb.rgb[0], rgb.rgb[1],
|
||||
rgb.rgb[2]);
|
||||
int (*writer)(char) = output_get_writer();
|
||||
if (writer) {
|
||||
for (size_t i = 0; buff[i]; i++) {
|
||||
writer(buff[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 24 bit! No tparm here, just ANSI escape sequences.
|
||||
// Foreground: ^[38;2;<r>;<g>;<b>m
|
||||
// Background: ^[48;2;<r>;<g>;<b>m
|
||||
color24_t rgb = color.to_color24();
|
||||
char buff[128];
|
||||
snprintf(buff, sizeof buff, "\x1b[%d;2;%u;%u;%um", is_fg ? 38 : 48, rgb.rgb[0], rgb.rgb[1],
|
||||
rgb.rgb[2]);
|
||||
int (*writer)(char) = output_get_writer();
|
||||
if (writer) {
|
||||
for (size_t i = 0; buff[i]; i++) {
|
||||
writer(buff[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -271,12 +272,8 @@ void set_color(rgb_color_t c, rgb_color_t c2) {
|
||||
}
|
||||
|
||||
// Lastly, we set bold mode and underline mode correctly.
|
||||
if ((enter_bold_mode != 0) && (strlen(enter_bold_mode) > 0) && !bg_set) {
|
||||
if (is_bold && !was_bold) {
|
||||
if (enter_bold_mode) {
|
||||
writembs(tparm(enter_bold_mode));
|
||||
}
|
||||
}
|
||||
if (is_bold && !was_bold && enter_bold_mode && strlen(enter_bold_mode) > 0 && !bg_set) {
|
||||
writembs(tparm(enter_bold_mode));
|
||||
was_bold = is_bold;
|
||||
}
|
||||
|
||||
@@ -312,13 +309,10 @@ int writech(wint_t ch) {
|
||||
if (ch >= ENCODE_DIRECT_BASE && ch < ENCODE_DIRECT_BASE + 256) {
|
||||
buff[0] = ch - ENCODE_DIRECT_BASE;
|
||||
len = 1;
|
||||
} else if (MB_CUR_MAX == 1) // single-byte locale (C/POSIX/ISO-8859)
|
||||
{
|
||||
} else if (MB_CUR_MAX == 1) {
|
||||
// single-byte locale (C/POSIX/ISO-8859)
|
||||
// If `wc` contains a wide character we emit a question-mark.
|
||||
if (ch & ~0xFF) {
|
||||
ch = '?';
|
||||
}
|
||||
buff[0] = ch;
|
||||
buff[0] = ch & ~0xFF ? '?' : ch;
|
||||
len = 1;
|
||||
} else {
|
||||
mbstate_t state = {};
|
||||
@@ -389,7 +383,7 @@ rgb_color_t best_color(const std::vector<rgb_color_t> &candidates, color_support
|
||||
}
|
||||
// If we have both RGB and named colors, then prefer rgb if term256 is supported.
|
||||
rgb_color_t result = rgb_color_t::none();
|
||||
bool has_term256 = !!(support & color_support_term256);
|
||||
bool has_term256 = static_cast<bool>(support & color_support_term256);
|
||||
if ((!first_rgb.is_none() && has_term256) || first_named.is_none()) {
|
||||
result = first_rgb;
|
||||
} else {
|
||||
@@ -446,7 +440,8 @@ rgb_color_t parse_color(const wcstring &val, bool is_background) {
|
||||
|
||||
#if 0
|
||||
wcstring desc = result.description();
|
||||
printf("Parsed %ls from %ls (%s)\n", desc.c_str(), val.c_str(), is_background ? "background" : "foreground");
|
||||
printf("Parsed %ls from %ls (%s)\n", desc.c_str(), val.c_str(),
|
||||
is_background ? "background" : "foreground");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
|
||||
235
src/pager.cpp
235
src/pager.cpp
@@ -84,16 +84,15 @@ static int print_max(const wcstring &str, highlight_spec_t color, int max, bool
|
||||
|
||||
/// Print the specified item using at the specified amount of space.
|
||||
line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, size_t row,
|
||||
size_t column, size_t width, bool secondary, bool selected,
|
||||
size_t column, int width, bool secondary, bool selected,
|
||||
page_rendering_t *rendering) const {
|
||||
UNUSED(column);
|
||||
UNUSED(row);
|
||||
UNUSED(rendering);
|
||||
size_t comp_width = 0, desc_width = 0;
|
||||
size_t written = 0;
|
||||
int comp_width, desc_width;
|
||||
line_t line_data;
|
||||
|
||||
if (c->pref_width <= (size_t)width) {
|
||||
if (c->pref_width <= width) {
|
||||
// The entry fits, we give it as much space as it wants.
|
||||
comp_width = c->comp_width;
|
||||
desc_width = c->desc_width;
|
||||
@@ -102,8 +101,8 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s
|
||||
// the space to the completion, and whatever is left to the description.
|
||||
int desc_all = c->desc_width ? c->desc_width + 4 : 0;
|
||||
|
||||
comp_width = maxi(mini(c->comp_width, 2 * (width - 4) / 3), width - desc_all);
|
||||
if (c->desc_width) desc_width = width - comp_width - 4;
|
||||
comp_width = maxi(mini((int)c->comp_width, 2 * (width - 4) / 3), width - desc_all);
|
||||
desc_width = c->desc_width ? width - 4 - comp_width : 0;
|
||||
}
|
||||
|
||||
int bg_color = secondary ? highlight_spec_pager_secondary : highlight_spec_normal;
|
||||
@@ -111,6 +110,7 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s
|
||||
bg_color = highlight_spec_search_match;
|
||||
}
|
||||
|
||||
int written = 0;
|
||||
for (size_t i = 0; i < c->comp.size(); i++) {
|
||||
const wcstring &comp = c->comp.at(i);
|
||||
|
||||
@@ -214,8 +214,7 @@ static void mangle_1_completion_description(wcstring *str) {
|
||||
str->at(trailing++) = wc;
|
||||
} else if (!was_space) { // initial space in a run
|
||||
str->at(trailing++) = L' ';
|
||||
} else { // non-initial space in a run, do nothing
|
||||
}
|
||||
} // else non-initial space in a run, do nothing
|
||||
was_space = is_space;
|
||||
}
|
||||
|
||||
@@ -365,7 +364,7 @@ void pager_t::set_term_size(int w, int h) {
|
||||
}
|
||||
|
||||
/// Try to print the list of completions l with the prefix prefix using cols as the number of
|
||||
/// columns. Return true if the completion list was printed, false if the terminal is to narrow for
|
||||
/// columns. Return true if the completion list was printed, false if the terminal is too narrow for
|
||||
/// the specified number of columns. Always succeeds if cols is 1.
|
||||
bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const comp_info_list_t &lst,
|
||||
page_rendering_t *rendering, size_t suggested_start_row) const {
|
||||
@@ -446,75 +445,78 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
|
||||
print = true;
|
||||
}
|
||||
|
||||
if (print) {
|
||||
// Determine the starting and stop row.
|
||||
size_t start_row = 0, stop_row = 0;
|
||||
if (row_count <= term_height) {
|
||||
// Easy, we can show everything.
|
||||
start_row = 0;
|
||||
stop_row = row_count;
|
||||
} else {
|
||||
// We can only show part of the full list. Determine which part based on the
|
||||
// suggested_start_row.
|
||||
assert(row_count > term_height);
|
||||
size_t last_starting_row = row_count - term_height;
|
||||
start_row = mini(suggested_start_row, last_starting_row);
|
||||
stop_row = start_row + term_height;
|
||||
assert(start_row >= 0 && start_row <= last_starting_row);
|
||||
}
|
||||
|
||||
assert(stop_row >= start_row);
|
||||
assert(stop_row <= row_count);
|
||||
assert(stop_row - start_row <= term_height);
|
||||
completion_print(cols, width, start_row, stop_row, prefix, lst, rendering);
|
||||
|
||||
// Ellipsis helper string. Either empty or containing the ellipsis char.
|
||||
const wchar_t ellipsis_string[] = {ellipsis_char == L'\x2026' ? L'\x2026' : L'\0', L'\0'};
|
||||
|
||||
// Add the progress line. It's a "more to disclose" line if necessary, or a row listing if
|
||||
// it's scrollable; otherwise ignore it.
|
||||
wcstring progress_text;
|
||||
if (rendering->remaining_to_disclose == 1) {
|
||||
// I don't expect this case to ever happen.
|
||||
progress_text = format_string(_(L"%lsand 1 more row"), ellipsis_string);
|
||||
} else if (rendering->remaining_to_disclose > 1) {
|
||||
progress_text = format_string(_(L"%lsand %lu more rows"), ellipsis_string,
|
||||
(unsigned long)rendering->remaining_to_disclose);
|
||||
} else if (start_row > 0 || stop_row < row_count) {
|
||||
// We have a scrollable interface. The +1 here is because we are zero indexed, but want
|
||||
// to present things as 1-indexed. We do not add 1 to stop_row or row_count because
|
||||
// these are the "past the last value".
|
||||
progress_text =
|
||||
format_string(_(L"rows %lu to %lu of %lu"), start_row + 1, stop_row, row_count);
|
||||
} else if (completion_infos.empty() && !unfiltered_completion_infos.empty()) {
|
||||
// Everything is filtered.
|
||||
progress_text = _(L"(no matches)");
|
||||
}
|
||||
|
||||
if (!progress_text.empty()) {
|
||||
line_t &line = rendering->screen_data.add_line();
|
||||
print_max(progress_text, highlight_spec_pager_progress |
|
||||
highlight_make_background(highlight_spec_pager_progress),
|
||||
term_width, true /* has_more */, &line);
|
||||
}
|
||||
|
||||
if (search_field_shown) {
|
||||
// Add the search field.
|
||||
wcstring search_field_text = search_field_line.text;
|
||||
// Append spaces to make it at least the required width.
|
||||
if (search_field_text.size() < PAGER_SEARCH_FIELD_WIDTH) {
|
||||
search_field_text.append(PAGER_SEARCH_FIELD_WIDTH - search_field_text.size(), L' ');
|
||||
}
|
||||
line_t *search_field = &rendering->screen_data.insert_line_at_index(0);
|
||||
|
||||
// We limit the width to term_width - 1.
|
||||
int search_field_written = print_max(SEARCH_FIELD_PROMPT, highlight_spec_normal,
|
||||
term_width - 1, false, search_field);
|
||||
print_max(search_field_text, highlight_modifier_force_underline,
|
||||
term_width - search_field_written - 1, false, search_field);
|
||||
}
|
||||
if (!print) {
|
||||
return false; // no need to continue
|
||||
}
|
||||
return print;
|
||||
|
||||
// Determine the starting and stop row.
|
||||
size_t start_row = 0, stop_row = 0;
|
||||
if (row_count <= term_height) {
|
||||
// Easy, we can show everything.
|
||||
start_row = 0;
|
||||
stop_row = row_count;
|
||||
} else {
|
||||
// We can only show part of the full list. Determine which part based on the
|
||||
// suggested_start_row.
|
||||
assert(row_count > term_height);
|
||||
size_t last_starting_row = row_count - term_height;
|
||||
start_row = mini(suggested_start_row, last_starting_row);
|
||||
stop_row = start_row + term_height;
|
||||
assert(start_row >= 0 && start_row <= last_starting_row);
|
||||
}
|
||||
|
||||
assert(stop_row >= start_row);
|
||||
assert(stop_row <= row_count);
|
||||
assert(stop_row - start_row <= term_height);
|
||||
completion_print(cols, width, start_row, stop_row, prefix, lst, rendering);
|
||||
|
||||
// Ellipsis helper string. Either empty or containing the ellipsis char.
|
||||
const wchar_t ellipsis_string[] = {ellipsis_char == L'\x2026' ? L'\x2026' : L'\0', L'\0'};
|
||||
|
||||
// Add the progress line. It's a "more to disclose" line if necessary, or a row listing if
|
||||
// it's scrollable; otherwise ignore it.
|
||||
wcstring progress_text;
|
||||
if (rendering->remaining_to_disclose == 1) {
|
||||
// I don't expect this case to ever happen.
|
||||
progress_text = format_string(_(L"%lsand 1 more row"), ellipsis_string);
|
||||
} else if (rendering->remaining_to_disclose > 1) {
|
||||
progress_text = format_string(_(L"%lsand %lu more rows"), ellipsis_string,
|
||||
(unsigned long)rendering->remaining_to_disclose);
|
||||
} else if (start_row > 0 || stop_row < row_count) {
|
||||
// We have a scrollable interface. The +1 here is because we are zero indexed, but want
|
||||
// to present things as 1-indexed. We do not add 1 to stop_row or row_count because
|
||||
// these are the "past the last value".
|
||||
progress_text =
|
||||
format_string(_(L"rows %lu to %lu of %lu"), start_row + 1, stop_row, row_count);
|
||||
} else if (completion_infos.empty() && !unfiltered_completion_infos.empty()) {
|
||||
// Everything is filtered.
|
||||
progress_text = _(L"(no matches)");
|
||||
}
|
||||
|
||||
if (!progress_text.empty()) {
|
||||
line_t &line = rendering->screen_data.add_line();
|
||||
print_max(progress_text, highlight_spec_pager_progress |
|
||||
highlight_make_background(highlight_spec_pager_progress),
|
||||
term_width, true /* has_more */, &line);
|
||||
}
|
||||
|
||||
if (search_field_shown) {
|
||||
// Add the search field.
|
||||
wcstring search_field_text = search_field_line.text;
|
||||
// Append spaces to make it at least the required width.
|
||||
if (search_field_text.size() < PAGER_SEARCH_FIELD_WIDTH) {
|
||||
search_field_text.append(PAGER_SEARCH_FIELD_WIDTH - search_field_text.size(), L' ');
|
||||
}
|
||||
line_t *search_field = &rendering->screen_data.insert_line_at_index(0);
|
||||
|
||||
// We limit the width to term_width - 1.
|
||||
int search_field_written = print_max(SEARCH_FIELD_PROMPT, highlight_spec_normal,
|
||||
term_width - 1, false, search_field);
|
||||
print_max(search_field_text, highlight_modifier_force_underline,
|
||||
term_width - search_field_written - 1, false, search_field);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
page_rendering_t pager_t::render() const {
|
||||
@@ -611,10 +613,6 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio
|
||||
// These do nothing.
|
||||
return false;
|
||||
}
|
||||
default: {
|
||||
assert(0 && "Unhandled selection_direction_t constant");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,7 +634,7 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio
|
||||
new_selected_completion_idx = selected_completion_idx - 1;
|
||||
}
|
||||
} else {
|
||||
assert(0 && "Unknown non-cardinal direction");
|
||||
DIE("unknown non-cardinal direction");
|
||||
}
|
||||
} else {
|
||||
// Cardinal directions. We have a completion index; we wish to compute its row and column.
|
||||
@@ -646,10 +644,11 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio
|
||||
|
||||
switch (direction) {
|
||||
case direction_page_north: {
|
||||
if (current_row > page_height)
|
||||
if (current_row > page_height) {
|
||||
current_row = current_row - page_height;
|
||||
else
|
||||
} else {
|
||||
current_row = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case direction_north: {
|
||||
@@ -708,7 +707,7 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(0 && "Unknown cardinal direction");
|
||||
DIE("unknown cardinal direction");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -717,42 +716,40 @@ bool pager_t::select_next_completion_in_direction(selection_direction_t directio
|
||||
new_selected_completion_idx = current_col * rendering.rows + current_row;
|
||||
}
|
||||
|
||||
if (new_selected_completion_idx != selected_completion_idx) {
|
||||
selected_completion_idx = new_selected_completion_idx;
|
||||
|
||||
// Update suggested_row_start to ensure the selection is visible. suggested_row_start *
|
||||
// rendering.cols is the first suggested visible completion; add the visible completion
|
||||
// count to that to get the last one.
|
||||
size_t visible_row_count = rendering.row_end - rendering.row_start;
|
||||
|
||||
if (visible_row_count > 0 && selected_completion_idx != PAGER_SELECTION_NONE) // paranoia
|
||||
{
|
||||
size_t row_containing_selection = this->get_selected_row(rendering);
|
||||
|
||||
// Ensure our suggested row start is not past the selected row.
|
||||
if (suggested_row_start > row_containing_selection) {
|
||||
suggested_row_start = row_containing_selection;
|
||||
}
|
||||
|
||||
// Ensure our suggested row start is not too early before it.
|
||||
if (suggested_row_start + visible_row_count <= row_containing_selection) {
|
||||
// The user moved south past the bottom completion.
|
||||
if (!fully_disclosed && rendering.remaining_to_disclose > 0) {
|
||||
fully_disclosed = true; // perform disclosure
|
||||
} else {
|
||||
// Scroll
|
||||
suggested_row_start = row_containing_selection - visible_row_count + 1;
|
||||
// Ensure fully_disclosed is set. I think we can hit this case if the user
|
||||
// resizes the window - we don't want to drop back to the disclosed style.
|
||||
fully_disclosed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
if (selected_completion_idx == new_selected_completion_idx) {
|
||||
return false;
|
||||
}
|
||||
selected_completion_idx = new_selected_completion_idx;
|
||||
|
||||
// Update suggested_row_start to ensure the selection is visible. suggested_row_start *
|
||||
// rendering.cols is the first suggested visible completion; add the visible completion
|
||||
// count to that to get the last one.
|
||||
size_t visible_row_count = rendering.row_end - rendering.row_start;
|
||||
if (visible_row_count == 0 || selected_completion_idx == PAGER_SELECTION_NONE) {
|
||||
return true; // this should never happen but be paranoid
|
||||
}
|
||||
|
||||
// Ensure our suggested row start is not past the selected row.
|
||||
size_t row_containing_selection = this->get_selected_row(rendering);
|
||||
if (suggested_row_start > row_containing_selection) {
|
||||
suggested_row_start = row_containing_selection;
|
||||
}
|
||||
|
||||
// Ensure our suggested row start is not too early before it.
|
||||
if (suggested_row_start + visible_row_count <= row_containing_selection) {
|
||||
// The user moved south past the bottom completion.
|
||||
if (!fully_disclosed && rendering.remaining_to_disclose > 0) {
|
||||
fully_disclosed = true; // perform disclosure
|
||||
} else {
|
||||
// Scroll
|
||||
suggested_row_start = row_containing_selection - visible_row_count + 1;
|
||||
// Ensure fully_disclosed is set. I think we can hit this case if the user
|
||||
// resizes the window - we don't want to drop back to the disclosed style.
|
||||
fully_disclosed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t pager_t::visual_selected_completion_index(size_t rows, size_t cols) const {
|
||||
|
||||
@@ -114,7 +114,7 @@ class pager_t {
|
||||
const wcstring &prefix, const comp_info_list_t &lst,
|
||||
page_rendering_t *rendering) const;
|
||||
line_t completion_print_item(const wcstring &prefix, const comp_t *c, size_t row, size_t column,
|
||||
size_t width, bool secondary, bool selected,
|
||||
int width, bool secondary, bool selected,
|
||||
page_rendering_t *rendering) const;
|
||||
|
||||
public:
|
||||
|
||||
@@ -372,38 +372,41 @@ parse_execution_result_t parse_execution_context_t::run_function_statement(
|
||||
wcstring_list_t argument_list;
|
||||
parse_execution_result_t result = this->determine_arguments(header, &argument_list, failglob);
|
||||
|
||||
if (result == parse_execution_success) {
|
||||
// The function definition extends from the end of the header to the function end. It's not
|
||||
// just the range of the contents because that loses comments - see issue #1710.
|
||||
assert(block_end_command.has_source());
|
||||
size_t contents_start = header.source_start + header.source_length;
|
||||
size_t contents_end =
|
||||
block_end_command.source_start; // 1 past the last character in the function definition
|
||||
assert(contents_end >= contents_start);
|
||||
|
||||
// Swallow whitespace at both ends.
|
||||
while (contents_start < contents_end && iswspace(this->src.at(contents_start))) {
|
||||
contents_start++;
|
||||
}
|
||||
while (contents_start < contents_end && iswspace(this->src.at(contents_end - 1))) {
|
||||
contents_end--;
|
||||
}
|
||||
|
||||
assert(contents_end >= contents_start);
|
||||
const wcstring contents_str =
|
||||
wcstring(this->src, contents_start, contents_end - contents_start);
|
||||
int definition_line_offset = this->line_offset_of_character_at_offset(contents_start);
|
||||
wcstring error_str;
|
||||
io_streams_t streams;
|
||||
int err = builtin_function(*parser, streams, argument_list, contents_str,
|
||||
definition_line_offset, &error_str);
|
||||
proc_set_last_status(err);
|
||||
|
||||
if (!error_str.empty()) {
|
||||
this->report_error(header, L"%ls", error_str.c_str());
|
||||
result = parse_execution_errored;
|
||||
}
|
||||
if (result != parse_execution_success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// The function definition extends from the end of the header to the function end. It's not
|
||||
// just the range of the contents because that loses comments - see issue #1710.
|
||||
assert(block_end_command.has_source());
|
||||
size_t contents_start = header.source_start + header.source_length;
|
||||
size_t contents_end =
|
||||
block_end_command.source_start; // 1 past the last character in the function definition
|
||||
assert(contents_end >= contents_start);
|
||||
|
||||
// Swallow whitespace at both ends.
|
||||
while (contents_start < contents_end && iswspace(this->src.at(contents_start))) {
|
||||
contents_start++;
|
||||
}
|
||||
while (contents_start < contents_end && iswspace(this->src.at(contents_end - 1))) {
|
||||
contents_end--;
|
||||
}
|
||||
|
||||
assert(contents_end >= contents_start);
|
||||
const wcstring contents_str =
|
||||
wcstring(this->src, contents_start, contents_end - contents_start);
|
||||
int definition_line_offset = this->line_offset_of_character_at_offset(contents_start);
|
||||
wcstring error_str;
|
||||
io_streams_t streams;
|
||||
int err = builtin_function(*parser, streams, argument_list, contents_str,
|
||||
definition_line_offset, &error_str);
|
||||
proc_set_last_status(err);
|
||||
|
||||
if (!error_str.empty()) {
|
||||
this->report_error(header, L"%ls", error_str.c_str());
|
||||
result = parse_execution_errored;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -534,6 +537,10 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement(
|
||||
case EXPAND_OK: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected expand_string() return value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == parse_execution_success && switch_values_expanded.size() != 1) {
|
||||
@@ -542,66 +549,67 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement(
|
||||
switch_values_expanded.size());
|
||||
}
|
||||
|
||||
if (result == parse_execution_success) {
|
||||
const wcstring &switch_value_expanded = switch_values_expanded.at(0).completion;
|
||||
if (result != parse_execution_success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
switch_block_t *sb = new switch_block_t();
|
||||
parser->push_block(sb);
|
||||
const wcstring &switch_value_expanded = switch_values_expanded.at(0).completion;
|
||||
|
||||
// Expand case statements.
|
||||
const parse_node_t *case_item_list = get_child(statement, 3, symbol_case_item_list);
|
||||
switch_block_t *sb = new switch_block_t();
|
||||
parser->push_block(sb);
|
||||
|
||||
// Loop while we don't have a match but do have more of the list.
|
||||
const parse_node_t *matching_case_item = NULL;
|
||||
while (matching_case_item == NULL && case_item_list != NULL) {
|
||||
if (should_cancel_execution(sb)) {
|
||||
result = parse_execution_cancelled;
|
||||
break;
|
||||
}
|
||||
// Expand case statements.
|
||||
const parse_node_t *case_item_list = get_child(statement, 3, symbol_case_item_list);
|
||||
|
||||
// Get the next item and the remainder of the list.
|
||||
const parse_node_t *case_item =
|
||||
tree.next_node_in_node_list(*case_item_list, symbol_case_item, &case_item_list);
|
||||
if (case_item == NULL) {
|
||||
// No more items.
|
||||
break;
|
||||
}
|
||||
// Loop while we don't have a match but do have more of the list.
|
||||
const parse_node_t *matching_case_item = NULL;
|
||||
while (matching_case_item == NULL && case_item_list != NULL) {
|
||||
if (should_cancel_execution(sb)) {
|
||||
result = parse_execution_cancelled;
|
||||
break;
|
||||
}
|
||||
|
||||
// Pull out the argument list.
|
||||
const parse_node_t &arg_list = *get_child(*case_item, 1, symbol_argument_list);
|
||||
// Get the next item and the remainder of the list.
|
||||
const parse_node_t *case_item =
|
||||
tree.next_node_in_node_list(*case_item_list, symbol_case_item, &case_item_list);
|
||||
if (case_item == NULL) {
|
||||
// No more items.
|
||||
break;
|
||||
}
|
||||
|
||||
// Expand arguments. A case item list may have a wildcard that fails to expand to
|
||||
// anything. We also report case errors, but don't stop execution; i.e. a case item that
|
||||
// contains an unexpandable process will report and then fail to match.
|
||||
wcstring_list_t case_args;
|
||||
parse_execution_result_t case_result =
|
||||
this->determine_arguments(arg_list, &case_args, failglob);
|
||||
if (case_result == parse_execution_success) {
|
||||
for (size_t i = 0; i < case_args.size(); i++) {
|
||||
const wcstring &arg = case_args.at(i);
|
||||
// Pull out the argument list.
|
||||
const parse_node_t &arg_list = *get_child(*case_item, 1, symbol_argument_list);
|
||||
|
||||
// Unescape wildcards so they can be expanded again.
|
||||
wcstring unescaped_arg = parse_util_unescape_wildcards(arg);
|
||||
bool match = wildcard_match(switch_value_expanded, unescaped_arg);
|
||||
// Expand arguments. A case item list may have a wildcard that fails to expand to
|
||||
// anything. We also report case errors, but don't stop execution; i.e. a case item that
|
||||
// contains an unexpandable process will report and then fail to match.
|
||||
wcstring_list_t case_args;
|
||||
parse_execution_result_t case_result =
|
||||
this->determine_arguments(arg_list, &case_args, failglob);
|
||||
if (case_result == parse_execution_success) {
|
||||
for (size_t i = 0; i < case_args.size(); i++) {
|
||||
const wcstring &arg = case_args.at(i);
|
||||
|
||||
// If this matched, we're done.
|
||||
if (match) {
|
||||
matching_case_item = case_item;
|
||||
break;
|
||||
}
|
||||
// Unescape wildcards so they can be expanded again.
|
||||
wcstring unescaped_arg = parse_util_unescape_wildcards(arg);
|
||||
bool match = wildcard_match(switch_value_expanded, unescaped_arg);
|
||||
|
||||
// If this matched, we're done.
|
||||
if (match) {
|
||||
matching_case_item = case_item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == parse_execution_success && matching_case_item != NULL) {
|
||||
// Success, evaluate the job list.
|
||||
const parse_node_t *job_list = get_child(*matching_case_item, 3, symbol_job_list);
|
||||
result = this->run_job_list(*job_list, sb);
|
||||
}
|
||||
|
||||
parser->pop_block(sb);
|
||||
}
|
||||
|
||||
if (result == parse_execution_success && matching_case_item != NULL) {
|
||||
// Success, evaluate the job list.
|
||||
const parse_node_t *job_list = get_child(*matching_case_item, 3, symbol_job_list);
|
||||
result = this->run_job_list(*job_list, sb);
|
||||
}
|
||||
|
||||
parser->pop_block(sb);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -945,6 +953,10 @@ parse_execution_result_t parse_execution_context_t::determine_arguments(
|
||||
case EXPAND_OK: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected expand_string() return value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now copy over any expanded arguments. Do it using swap() to avoid extra allocations; this
|
||||
@@ -1184,7 +1196,7 @@ parse_execution_result_t parse_execution_context_t::populate_job_from_job_node(
|
||||
// Return what happened.
|
||||
if (result == parse_execution_success) {
|
||||
// Link up the processes.
|
||||
assert(!processes.empty());
|
||||
assert(!processes.empty()); //!OCLINT(multiple unary operator)
|
||||
j->first_process = processes.at(0);
|
||||
for (size_t i = 1; i < processes.size(); i++) {
|
||||
processes.at(i - 1)->next = processes.at(i);
|
||||
@@ -1208,12 +1220,10 @@ parse_execution_result_t parse_execution_context_t::run_1_job(const parse_node_t
|
||||
|
||||
// Get terminal modes.
|
||||
struct termios tmodes = {};
|
||||
if (shell_is_interactive()) {
|
||||
if (tcgetattr(STDIN_FILENO, &tmodes)) {
|
||||
// Need real error handling here.
|
||||
wperror(L"tcgetattr");
|
||||
return parse_execution_errored;
|
||||
}
|
||||
if (shell_is_interactive() && tcgetattr(STDIN_FILENO, &tmodes)) {
|
||||
// Need real error handling here.
|
||||
wperror(L"tcgetattr");
|
||||
return parse_execution_errored;
|
||||
}
|
||||
|
||||
// Increment the eval_level for the duration of this command.
|
||||
@@ -1370,7 +1380,7 @@ parse_execution_result_t parse_execution_context_t::run_job_list(const parse_nod
|
||||
parse_execution_result_t parse_execution_context_t::eval_node_at_offset(
|
||||
node_offset_t offset, const block_t *associated_block, const io_chain_t &io) {
|
||||
// Don't ever expect to have an empty tree if this is called.
|
||||
assert(!tree.empty());
|
||||
assert(!tree.empty()); //!OCLINT(multiple unary operator)
|
||||
assert(offset < tree.size());
|
||||
|
||||
// Apply this block IO for the duration of this function.
|
||||
|
||||
@@ -428,19 +428,18 @@ RESOLVE_ONLY(end_command) = {KEYWORD(parse_keyword_end)};
|
||||
case (symbol_##sym): \
|
||||
resolver = resolve_##sym; \
|
||||
break;
|
||||
|
||||
const production_t *parse_productions::production_for_token(parse_token_type_t node_type,
|
||||
const parse_token_t &input1,
|
||||
const parse_token_t &input2,
|
||||
parse_node_tag_t *out_tag) {
|
||||
const bool log_it = false;
|
||||
if (log_it) {
|
||||
fprintf(stderr, "Resolving production for %ls with input token <%ls>\n",
|
||||
token_type_description(node_type), input1.describe().c_str());
|
||||
}
|
||||
debug(5, "Resolving production for %ls with input token <%ls>\n",
|
||||
token_type_description(node_type), input1.describe().c_str());
|
||||
|
||||
// Fetch the function to resolve the list of productions.
|
||||
const production_t *(*resolver)(const parse_token_t &input1, const parse_token_t &input2,
|
||||
parse_node_tag_t *out_tag) = NULL;
|
||||
const production_t *(*resolver)(const parse_token_t &input1, //!OCLINT(unused param)
|
||||
const parse_token_t &input2, //!OCLINT(unused param)
|
||||
parse_node_tag_t *out_tag) = NULL; //!OCLINT(unused param)
|
||||
switch (node_type) {
|
||||
TEST(job_list)
|
||||
TEST(job)
|
||||
@@ -501,10 +500,8 @@ const production_t *parse_productions::production_for_token(parse_token_type_t n
|
||||
|
||||
const production_t *result = resolver(input1, input2, out_tag);
|
||||
if (result == NULL) {
|
||||
if (log_it) {
|
||||
fprintf(stderr, "Node type '%ls' has no production for input '%ls' (in %s)\n",
|
||||
token_type_description(node_type), input1.describe().c_str(), __FUNCTION__);
|
||||
}
|
||||
debug(5, "Node type '%ls' has no production for input '%ls' (in %s)\n",
|
||||
token_type_description(node_type), input1.describe().c_str(), __FUNCTION__);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -73,70 +73,74 @@ static bool production_is_empty(const production_t *production) {
|
||||
wcstring parse_error_t::describe_with_prefix(const wcstring &src, const wcstring &prefix,
|
||||
bool is_interactive, bool skip_caret) const {
|
||||
wcstring result = text;
|
||||
if (!skip_caret && source_start < src.size() && source_start + source_length <= src.size()) {
|
||||
// Locate the beginning of this line of source.
|
||||
size_t line_start = 0;
|
||||
|
||||
// Look for a newline prior to source_start. If we don't find one, start at the beginning of
|
||||
// the string; otherwise start one past the newline. Note that source_start may itself point
|
||||
// at a newline; we want to find the newline before it.
|
||||
if (source_start > 0) {
|
||||
size_t newline = src.find_last_of(L'\n', source_start - 1);
|
||||
if (newline != wcstring::npos) {
|
||||
line_start = newline + 1;
|
||||
}
|
||||
}
|
||||
if (skip_caret || source_start >= src.size() || source_start + source_length > src.size()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Look for the newline after the source range. If the source range itself includes a
|
||||
// newline, that's the one we want, so start just before the end of the range.
|
||||
size_t last_char_in_range =
|
||||
(source_length == 0 ? source_start : source_start + source_length - 1);
|
||||
size_t line_end = src.find(L'\n', last_char_in_range);
|
||||
if (line_end == wcstring::npos) {
|
||||
line_end = src.size();
|
||||
}
|
||||
// Locate the beginning of this line of source.
|
||||
size_t line_start = 0;
|
||||
|
||||
assert(line_end >= line_start);
|
||||
assert(source_start >= line_start);
|
||||
|
||||
// Don't include the caret and line if we're interactive this is the first line, because
|
||||
// then it's obvious.
|
||||
bool skip_caret = (is_interactive && source_start == 0);
|
||||
|
||||
if (!skip_caret) {
|
||||
// Append the line of text.
|
||||
if (!result.empty()) {
|
||||
result.push_back(L'\n');
|
||||
}
|
||||
result.append(prefix);
|
||||
result.append(src, line_start, line_end - line_start);
|
||||
|
||||
// Append the caret line. The input source may include tabs; for that reason we
|
||||
// construct a "caret line" that has tabs in corresponding positions.
|
||||
const wcstring line_to_measure =
|
||||
prefix + wcstring(src, line_start, source_start - line_start);
|
||||
wcstring caret_space_line;
|
||||
caret_space_line.reserve(source_start - line_start);
|
||||
for (size_t i = 0; i < line_to_measure.size(); i++) {
|
||||
wchar_t wc = line_to_measure.at(i);
|
||||
if (wc == L'\t') {
|
||||
caret_space_line.push_back(L'\t');
|
||||
} else if (wc == L'\n') {
|
||||
// It's possible that the source_start points at a newline itself. In that case,
|
||||
// pretend it's a space. We only expect this to be at the end of the string.
|
||||
caret_space_line.push_back(L' ');
|
||||
} else {
|
||||
int width = fish_wcwidth(wc);
|
||||
if (width > 0) {
|
||||
caret_space_line.append(static_cast<size_t>(width), L' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push_back(L'\n');
|
||||
result.append(caret_space_line);
|
||||
result.push_back(L'^');
|
||||
// Look for a newline prior to source_start. If we don't find one, start at the beginning of
|
||||
// the string; otherwise start one past the newline. Note that source_start may itself point
|
||||
// at a newline; we want to find the newline before it.
|
||||
if (source_start > 0) {
|
||||
size_t newline = src.find_last_of(L'\n', source_start - 1);
|
||||
if (newline != wcstring::npos) {
|
||||
line_start = newline + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for the newline after the source range. If the source range itself includes a
|
||||
// newline, that's the one we want, so start just before the end of the range.
|
||||
size_t last_char_in_range =
|
||||
(source_length == 0 ? source_start : source_start + source_length - 1);
|
||||
size_t line_end = src.find(L'\n', last_char_in_range);
|
||||
if (line_end == wcstring::npos) {
|
||||
line_end = src.size();
|
||||
}
|
||||
|
||||
assert(line_end >= line_start);
|
||||
assert(source_start >= line_start);
|
||||
|
||||
// Don't include the caret and line if we're interactive this is the first line, because
|
||||
// then it's obvious.
|
||||
bool interactive_skip_caret = is_interactive && source_start == 0;
|
||||
|
||||
if (interactive_skip_caret) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Append the line of text.
|
||||
if (!result.empty()) {
|
||||
result.push_back(L'\n');
|
||||
}
|
||||
result.append(prefix);
|
||||
result.append(src, line_start, line_end - line_start);
|
||||
|
||||
// Append the caret line. The input source may include tabs; for that reason we
|
||||
// construct a "caret line" that has tabs in corresponding positions.
|
||||
const wcstring line_to_measure = prefix + wcstring(src, line_start, source_start - line_start);
|
||||
wcstring caret_space_line;
|
||||
caret_space_line.reserve(source_start - line_start);
|
||||
for (size_t i = 0; i < line_to_measure.size(); i++) {
|
||||
wchar_t wc = line_to_measure.at(i);
|
||||
if (wc == L'\t') {
|
||||
caret_space_line.push_back(L'\t');
|
||||
} else if (wc == L'\n') {
|
||||
// It's possible that the source_start points at a newline itself. In that case,
|
||||
// pretend it's a space. We only expect this to be at the end of the string.
|
||||
caret_space_line.push_back(L' ');
|
||||
} else {
|
||||
int width = fish_wcwidth(wc);
|
||||
if (width > 0) {
|
||||
caret_space_line.append(static_cast<size_t>(width), L' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push_back(L'\n');
|
||||
result.append(caret_space_line);
|
||||
result.push_back(L'^');
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -328,7 +332,7 @@ static inline parse_token_type_t parse_token_type_from_tokenizer_token(
|
||||
default: {
|
||||
fprintf(stderr, "Bad token type %d passed to %s\n", (int)tokenizer_token_type,
|
||||
__FUNCTION__);
|
||||
assert(0);
|
||||
DIE("bad token type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -475,7 +479,7 @@ class parse_ll_t {
|
||||
|
||||
/// Get the node corresponding to the top element of the stack.
|
||||
parse_node_t &node_for_top_symbol() {
|
||||
PARSE_ASSERT(!symbol_stack.empty());
|
||||
PARSE_ASSERT(!symbol_stack.empty()); //!OCLINT(multiple unary operator)
|
||||
const parse_stack_element_t &top_symbol = symbol_stack.back();
|
||||
PARSE_ASSERT(top_symbol.node_idx != NODE_OFFSET_INVALID);
|
||||
PARSE_ASSERT(top_symbol.node_idx < nodes.size());
|
||||
@@ -888,116 +892,112 @@ bool parse_ll_t::report_error_for_unclosed_block() {
|
||||
block_node = this->nodes.get_child(*block_node, 0, symbol_block_header);
|
||||
block_node = this->nodes.get_child(*block_node, 0); // specific statement
|
||||
}
|
||||
if (block_node != NULL) {
|
||||
// block_node is now an if_statement, switch_statement, for_header, while_header,
|
||||
// function_header, or begin_header.
|
||||
//
|
||||
// Hackish: descend down the first node until we reach the bottom. This will be a keyword
|
||||
// node like SWITCH, which will have the source range. Ordinarily the source range would be
|
||||
// known by the parent node too, but we haven't completed parsing yet, so we haven't yet
|
||||
// propagated source ranges.
|
||||
const parse_node_t *cursor = block_node;
|
||||
while (cursor->child_count > 0) {
|
||||
cursor = this->nodes.get_child(*cursor, 0);
|
||||
assert(cursor != NULL);
|
||||
}
|
||||
if (cursor->source_start != NODE_OFFSET_INVALID) {
|
||||
const wcstring node_desc = block_type_user_presentable_description(block_node->type);
|
||||
this->parse_error_at_location(cursor->source_start, parse_error_generic,
|
||||
L"Missing end to balance this %ls", node_desc.c_str());
|
||||
reported_error = true;
|
||||
}
|
||||
if (block_node == NULL) {
|
||||
return reported_error;
|
||||
}
|
||||
|
||||
// block_node is now an if_statement, switch_statement, for_header, while_header,
|
||||
// function_header, or begin_header.
|
||||
//
|
||||
// Hackish: descend down the first node until we reach the bottom. This will be a keyword
|
||||
// node like SWITCH, which will have the source range. Ordinarily the source range would be
|
||||
// known by the parent node too, but we haven't completed parsing yet, so we haven't yet
|
||||
// propagated source ranges.
|
||||
const parse_node_t *cursor = block_node;
|
||||
while (cursor->child_count > 0) {
|
||||
cursor = this->nodes.get_child(*cursor, 0);
|
||||
assert(cursor != NULL);
|
||||
}
|
||||
if (cursor->source_start != NODE_OFFSET_INVALID) {
|
||||
const wcstring node_desc = block_type_user_presentable_description(block_node->type);
|
||||
this->parse_error_at_location(cursor->source_start, parse_error_generic,
|
||||
L"Missing end to balance this %ls", node_desc.c_str());
|
||||
reported_error = true;
|
||||
}
|
||||
return reported_error;
|
||||
}
|
||||
|
||||
bool parse_ll_t::top_node_handle_terminal_types(parse_token_t token) {
|
||||
PARSE_ASSERT(!symbol_stack.empty());
|
||||
PARSE_ASSERT(!symbol_stack.empty()); //!OCLINT(multiple unary operator)
|
||||
PARSE_ASSERT(token.type >= FIRST_PARSE_TOKEN_TYPE);
|
||||
bool handled = false;
|
||||
parse_stack_element_t &stack_top = symbol_stack.back();
|
||||
if (type_is_terminal_type(stack_top.type)) {
|
||||
// The top of the stack is terminal. We are going to handle this (because we can't produce
|
||||
// from a terminal type).
|
||||
handled = true;
|
||||
|
||||
// Now see if we actually matched
|
||||
bool matched = false;
|
||||
if (stack_top.type == token.type) {
|
||||
switch (stack_top.type) {
|
||||
case parse_token_type_string: {
|
||||
// We matched if the keywords match, or no keyword was required.
|
||||
matched = (stack_top.keyword == parse_keyword_none ||
|
||||
stack_top.keyword == token.keyword);
|
||||
if (!type_is_terminal_type(stack_top.type)) {
|
||||
return false; // was not handled
|
||||
}
|
||||
|
||||
// The top of the stack is terminal. We are going to handle this (because we can't produce
|
||||
// from a terminal type).
|
||||
|
||||
// Now see if we actually matched
|
||||
bool matched = false;
|
||||
if (stack_top.type == token.type) {
|
||||
if (stack_top.type == parse_token_type_string) {
|
||||
// We matched if the keywords match, or no keyword was required.
|
||||
matched =
|
||||
(stack_top.keyword == parse_keyword_none || stack_top.keyword == token.keyword);
|
||||
} else {
|
||||
// For other types, we only require that the types match.
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched) {
|
||||
// Success. Tell the node that it matched this token, and what its source range is in
|
||||
// the parse phase, we only set source ranges for terminal types. We propagate ranges to
|
||||
// parent nodes afterwards.
|
||||
parse_node_t &node = node_for_top_symbol();
|
||||
node.keyword = token.keyword;
|
||||
node.source_start = token.source_start;
|
||||
node.source_length = token.source_length;
|
||||
} else {
|
||||
// Failure
|
||||
if (stack_top.type == parse_token_type_string && token.type == parse_token_type_string) {
|
||||
// Keyword failure. We should unify this with the 'matched' computation above.
|
||||
assert(stack_top.keyword != parse_keyword_none && stack_top.keyword != token.keyword);
|
||||
|
||||
// Check to see which keyword we got which was considered wrong.
|
||||
switch (token.keyword) {
|
||||
// Some keywords are only valid in certain contexts. If this cascaded all the
|
||||
// way down through the outermost job_list, it was not in a valid context.
|
||||
case parse_keyword_case:
|
||||
case parse_keyword_end:
|
||||
case parse_keyword_else: {
|
||||
this->parse_error_unbalancing_token(token);
|
||||
break;
|
||||
}
|
||||
case parse_keyword_none: {
|
||||
// This is a random other string (not a keyword).
|
||||
const wcstring expected = keyword_description(stack_top.keyword);
|
||||
this->parse_error(token, parse_error_generic, L"Expected keyword '%ls'",
|
||||
expected.c_str());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// For other types, we only require that the types match.
|
||||
matched = true;
|
||||
// Got a real keyword we can report.
|
||||
const wcstring actual =
|
||||
(token.keyword == parse_keyword_none ? token.describe()
|
||||
: keyword_description(token.keyword));
|
||||
const wcstring expected = keyword_description(stack_top.keyword);
|
||||
this->parse_error(token, parse_error_generic,
|
||||
L"Expected keyword '%ls', instead got keyword '%ls'",
|
||||
expected.c_str(), actual.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matched) {
|
||||
// Success. Tell the node that it matched this token, and what its source range is in
|
||||
// the parse phase, we only set source ranges for terminal types. We propagate ranges to
|
||||
// parent nodes afterwards.
|
||||
parse_node_t &node = node_for_top_symbol();
|
||||
node.keyword = token.keyword;
|
||||
node.source_start = token.source_start;
|
||||
node.source_length = token.source_length;
|
||||
} else if (stack_top.keyword == parse_keyword_end &&
|
||||
token.type == parse_token_type_terminate &&
|
||||
this->report_error_for_unclosed_block()) {
|
||||
; // handled by report_error_for_unclosed_block
|
||||
} else {
|
||||
// Failure
|
||||
if (stack_top.type == parse_token_type_string &&
|
||||
token.type == parse_token_type_string) {
|
||||
// Keyword failure. We should unify this with the 'matched' computation above.
|
||||
assert(stack_top.keyword != parse_keyword_none &&
|
||||
stack_top.keyword != token.keyword);
|
||||
|
||||
// Check to see which keyword we got which was considered wrong.
|
||||
switch (token.keyword) {
|
||||
// Some keywords are only valid in certain contexts. If this cascaded all the
|
||||
// way down through the outermost job_list, it was not in a valid context.
|
||||
case parse_keyword_case:
|
||||
case parse_keyword_end:
|
||||
case parse_keyword_else: {
|
||||
this->parse_error_unbalancing_token(token);
|
||||
break;
|
||||
}
|
||||
case parse_keyword_none: {
|
||||
// This is a random other string (not a keyword).
|
||||
const wcstring expected = keyword_description(stack_top.keyword);
|
||||
this->parse_error(token, parse_error_generic, L"Expected keyword '%ls'",
|
||||
expected.c_str());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Got a real keyword we can report.
|
||||
const wcstring actual = (token.keyword == parse_keyword_none
|
||||
? token.describe()
|
||||
: keyword_description(token.keyword));
|
||||
const wcstring expected = keyword_description(stack_top.keyword);
|
||||
this->parse_error(token, parse_error_generic,
|
||||
L"Expected keyword '%ls', instead got keyword '%ls'",
|
||||
expected.c_str(), actual.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (stack_top.keyword == parse_keyword_end &&
|
||||
token.type == parse_token_type_terminate &&
|
||||
this->report_error_for_unclosed_block()) {
|
||||
// Handled by report_error_for_unclosed_block.
|
||||
} else {
|
||||
const wcstring expected = stack_top.user_presentable_description();
|
||||
this->parse_error_unexpected_token(expected.c_str(), token);
|
||||
}
|
||||
const wcstring expected = stack_top.user_presentable_description();
|
||||
this->parse_error_unexpected_token(expected.c_str(), token);
|
||||
}
|
||||
|
||||
// We handled the token, so pop the symbol stack.
|
||||
symbol_stack.pop_back();
|
||||
}
|
||||
return handled;
|
||||
|
||||
// We handled the token, so pop the symbol stack.
|
||||
symbol_stack.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
void parse_ll_t::accept_tokens(parse_token_t token1, parse_token_t token2) {
|
||||
@@ -1036,7 +1036,7 @@ void parse_ll_t::accept_tokens(parse_token_t token1, parse_token_t token2) {
|
||||
}
|
||||
|
||||
while (!consumed && !this->fatal_errored) {
|
||||
PARSE_ASSERT(!symbol_stack.empty());
|
||||
PARSE_ASSERT(!symbol_stack.empty()); //!OCLINT(multiple unary operator)
|
||||
|
||||
if (top_node_handle_terminal_types(token1)) {
|
||||
if (logit) {
|
||||
@@ -1238,32 +1238,33 @@ bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t parse_flags,
|
||||
parser.report_tokenizer_error(tokenizer_token);
|
||||
}
|
||||
|
||||
// Handle errors.
|
||||
if (parser.has_fatal_error()) {
|
||||
if (parse_flags & parse_flag_continue_after_error) {
|
||||
// Hack. Typically the parse error is due to the first token. However, if it's a
|
||||
// tokenizer error, then has_fatal_error was set due to the check above; in that
|
||||
// case the second token is what matters.
|
||||
size_t error_token_idx = 0;
|
||||
if (queue[1].type == parse_special_type_tokenizer_error) {
|
||||
error_token_idx = (queue[1].type == parse_special_type_tokenizer_error ? 1 : 0);
|
||||
token_count = -1; // so that it will be 0 after incrementing, and our tokenizer
|
||||
// error will be ignored
|
||||
}
|
||||
|
||||
// Mark a special error token, and then keep going.
|
||||
const parse_token_t token = {parse_special_type_parse_error,
|
||||
parse_keyword_none,
|
||||
false,
|
||||
false,
|
||||
queue[error_token_idx].source_start,
|
||||
queue[error_token_idx].source_length};
|
||||
parser.accept_tokens(token, kInvalidToken);
|
||||
parser.reset_symbols(goal);
|
||||
} else {
|
||||
break; // bail out
|
||||
}
|
||||
if (!parser.has_fatal_error()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle errors.
|
||||
if (!(parse_flags & parse_flag_continue_after_error)) {
|
||||
break; // bail out
|
||||
}
|
||||
// Hack. Typically the parse error is due to the first token. However, if it's a
|
||||
// tokenizer error, then has_fatal_error was set due to the check above; in that
|
||||
// case the second token is what matters.
|
||||
size_t error_token_idx = 0;
|
||||
if (queue[1].type == parse_special_type_tokenizer_error) {
|
||||
error_token_idx = (queue[1].type == parse_special_type_tokenizer_error ? 1 : 0);
|
||||
token_count = -1; // so that it will be 0 after incrementing, and our tokenizer
|
||||
// error will be ignored
|
||||
}
|
||||
|
||||
// Mark a special error token, and then keep going.
|
||||
const parse_token_t token = {parse_special_type_parse_error,
|
||||
parse_keyword_none,
|
||||
false,
|
||||
false,
|
||||
queue[error_token_idx].source_start,
|
||||
queue[error_token_idx].source_length};
|
||||
parser.accept_tokens(token, kInvalidToken);
|
||||
parser.reset_symbols(goal);
|
||||
}
|
||||
|
||||
// Teach each node where its source range is.
|
||||
@@ -1275,7 +1276,8 @@ bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t parse_flags,
|
||||
#if 0
|
||||
//wcstring result = dump_tree(this->parser->nodes, str);
|
||||
//fprintf(stderr, "Tree (%ld nodes):\n%ls", this->parser->nodes.size(), result.c_str());
|
||||
fprintf(stderr, "%lu nodes, node size %lu, %lu bytes\n", output->size(), sizeof(parse_node_t), output->size() * sizeof(parse_node_t));
|
||||
fprintf(stderr, "%lu nodes, node size %lu, %lu bytes\n", output->size(), sizeof(parse_node_t),
|
||||
output->size() * sizeof(parse_node_t));
|
||||
#endif
|
||||
|
||||
// Indicate if we had a fatal error.
|
||||
@@ -1310,8 +1312,7 @@ const parse_node_t &parse_node_tree_t::find_child(const parse_node_t &parent,
|
||||
return *child;
|
||||
}
|
||||
}
|
||||
PARSE_ASSERT(0);
|
||||
return *(parse_node_t *)(NULL); // unreachable
|
||||
DIE("failed to find child node");
|
||||
}
|
||||
|
||||
const parse_node_t *parse_node_tree_t::get_parent(const parse_node_t &node,
|
||||
@@ -1370,13 +1371,11 @@ const parse_node_t *parse_node_tree_t::find_last_node_of_type(parse_token_type_t
|
||||
size_t idx = this->size();
|
||||
while (idx--) {
|
||||
const parse_node_t &node = this->at(idx);
|
||||
if (node.type == type) {
|
||||
// Types match. Check if it has the right parent.
|
||||
if (parent == NULL || node_has_ancestor(*this, node, *parent)) {
|
||||
// Success
|
||||
result = &node;
|
||||
break;
|
||||
}
|
||||
bool expected_type = (node.type == type);
|
||||
if (expected_type && (parent == NULL || node_has_ancestor(*this, node, *parent))) {
|
||||
// The types match and it has the right parent.
|
||||
result = &node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -1387,7 +1386,7 @@ const parse_node_t *parse_node_tree_t::find_node_matching_source_location(
|
||||
const parse_node_t *result = NULL;
|
||||
// Find nodes of the given type in the tree, working backwards.
|
||||
const size_t len = this->size();
|
||||
for (size_t idx = 0; idx < len; idx++) {
|
||||
for (size_t idx = 0; idx < len && result == NULL; idx++) {
|
||||
const parse_node_t &node = this->at(idx);
|
||||
|
||||
// Types must match.
|
||||
@@ -1401,8 +1400,8 @@ const parse_node_t *parse_node_tree_t::find_node_matching_source_location(
|
||||
|
||||
// Found it.
|
||||
result = &node;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1552,8 +1551,7 @@ enum parse_bool_statement_type_t parse_node_tree_t::statement_boolean_type(
|
||||
bool parse_node_tree_t::job_should_be_backgrounded(const parse_node_t &job) const {
|
||||
assert(job.type == symbol_job);
|
||||
const parse_node_t *opt_background = get_child(job, 2, symbol_optional_background);
|
||||
bool result = opt_background != NULL && opt_background->tag == parse_background;
|
||||
return result;
|
||||
return opt_background != NULL && opt_background->tag == parse_background;
|
||||
}
|
||||
|
||||
const parse_node_t *parse_node_tree_t::next_node_in_node_list(
|
||||
|
||||
@@ -118,7 +118,9 @@ class parse_node_t {
|
||||
}
|
||||
|
||||
/// Indicate if the node has comment nodes.
|
||||
bool has_comments() const { return !!(this->flags & parse_node_flag_has_comments); }
|
||||
bool has_comments() const {
|
||||
return static_cast<bool>(this->flags & parse_node_flag_has_comments);
|
||||
}
|
||||
|
||||
/// Gets source for the node, or the empty string if it has no source.
|
||||
wcstring get_source(const wcstring &str) const {
|
||||
|
||||
@@ -88,13 +88,11 @@ size_t parse_util_get_offset(const wcstring &str, int line, long line_offset) {
|
||||
size_t off2 = parse_util_get_offset_from_line(buff, line + 1);
|
||||
|
||||
if (off == (size_t)-1) return (size_t)-1;
|
||||
|
||||
if (off2 == (size_t)-1) off2 = wcslen(buff) + 1;
|
||||
|
||||
if (line_offset < 0) line_offset = 0;
|
||||
if (line_offset < 0) line_offset = 0; //!OCLINT(parameter reassignment)
|
||||
|
||||
if ((size_t)line_offset >= off2 - off - 1) {
|
||||
line_offset = off2 - off - 1;
|
||||
line_offset = off2 - off - 1; //!OCLINT(parameter reassignment)
|
||||
}
|
||||
|
||||
return off + line_offset;
|
||||
@@ -199,26 +197,28 @@ static int parse_util_locate_brackets_range(const wcstring &str, size_t *inout_c
|
||||
int ret = parse_util_locate_brackets_of_type(valid_range_start, &bracket_range_begin,
|
||||
&bracket_range_end, accept_incomplete, open_type,
|
||||
close_type);
|
||||
if (ret > 0) {
|
||||
// The command substitutions must not be NULL and must be in the valid pointer range, and
|
||||
// the end must be bigger than the beginning.
|
||||
assert(bracket_range_begin != NULL && bracket_range_begin >= valid_range_start &&
|
||||
bracket_range_begin <= valid_range_end);
|
||||
assert(bracket_range_end != NULL && bracket_range_end > bracket_range_begin &&
|
||||
bracket_range_end >= valid_range_start && bracket_range_end <= valid_range_end);
|
||||
|
||||
// Assign the substring to the out_contents.
|
||||
const wchar_t *interior_begin = bracket_range_begin + 1;
|
||||
out_contents->assign(interior_begin, bracket_range_end - interior_begin);
|
||||
|
||||
// Return the start and end.
|
||||
*out_start = bracket_range_begin - buff;
|
||||
*out_end = bracket_range_end - buff;
|
||||
|
||||
// Update the inout_cursor_offset. Note this may cause it to exceed str.size(), though
|
||||
// overflow is not likely.
|
||||
*inout_cursor_offset = 1 + *out_end;
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The command substitutions must not be NULL and must be in the valid pointer range, and
|
||||
// the end must be bigger than the beginning.
|
||||
assert(bracket_range_begin != NULL && bracket_range_begin >= valid_range_start &&
|
||||
bracket_range_begin <= valid_range_end);
|
||||
assert(bracket_range_end != NULL && bracket_range_end > bracket_range_begin &&
|
||||
bracket_range_end >= valid_range_start && bracket_range_end <= valid_range_end);
|
||||
|
||||
// Assign the substring to the out_contents.
|
||||
const wchar_t *interior_begin = bracket_range_begin + 1;
|
||||
out_contents->assign(interior_begin, bracket_range_end - interior_begin);
|
||||
|
||||
// Return the start and end.
|
||||
*out_start = bracket_range_begin - buff;
|
||||
*out_end = bracket_range_end - buff;
|
||||
|
||||
// Update the inout_cursor_offset. Note this may cause it to exceed str.size(), though
|
||||
// overflow is not likely.
|
||||
*inout_cursor_offset = 1 + *out_end;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -472,8 +472,7 @@ static wchar_t get_quote(const wcstring &cmd_str, size_t len) {
|
||||
void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_t *quote,
|
||||
size_t *offset, enum token_type *out_type) {
|
||||
size_t prev_pos = 0;
|
||||
wchar_t last_quote = '\0';
|
||||
int unfinished;
|
||||
wchar_t last_quote = L'\0';
|
||||
|
||||
tokenizer_t tok(cmd.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS);
|
||||
tok_t token;
|
||||
@@ -490,30 +489,25 @@ void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_
|
||||
wchar_t *cmd_tmp = wcsdup(cmd.c_str());
|
||||
cmd_tmp[pos] = 0;
|
||||
size_t cmdlen = wcslen(cmd_tmp);
|
||||
unfinished = (cmdlen == 0);
|
||||
if (!unfinished) {
|
||||
unfinished = (quote != 0);
|
||||
|
||||
if (!unfinished) {
|
||||
if (wcschr(L" \t\n\r", cmd_tmp[cmdlen - 1]) != 0) {
|
||||
if ((cmdlen == 1) || (cmd_tmp[cmdlen - 2] != L'\\')) {
|
||||
unfinished = 1;
|
||||
}
|
||||
}
|
||||
bool finished = cmdlen != 0;
|
||||
if (finished) {
|
||||
finished = (quote == NULL);
|
||||
if (finished && wcschr(L" \t\n\r", cmd_tmp[cmdlen - 1]) != L'\0') {
|
||||
finished = cmdlen > 1 && cmd_tmp[cmdlen - 2] == L'\\';
|
||||
}
|
||||
}
|
||||
|
||||
if (quote) *quote = last_quote;
|
||||
|
||||
if (offset != 0) {
|
||||
if (!unfinished) {
|
||||
if (finished) {
|
||||
while ((cmd_tmp[prev_pos] != 0) && (wcschr(L";|", cmd_tmp[prev_pos]) != 0)) prev_pos++;
|
||||
|
||||
*offset = prev_pos;
|
||||
} else {
|
||||
*offset = pos;
|
||||
}
|
||||
}
|
||||
|
||||
free(cmd_tmp);
|
||||
}
|
||||
|
||||
@@ -763,10 +757,9 @@ static int parser_is_pipe_forbidden(const wcstring &word) {
|
||||
|
||||
bool parse_util_argument_is_help(const wchar_t *s, int min_match) {
|
||||
CHECK(s, 0);
|
||||
|
||||
size_t len = wcslen(s);
|
||||
|
||||
min_match = maxi(min_match, 3);
|
||||
min_match = maxi(min_match, 3); //!OCLINT(parameter reassignment)
|
||||
|
||||
return wcscmp(L"-h", s) == 0 || (len >= (size_t)min_match && (wcsncmp(L"--help", s, len) == 0));
|
||||
}
|
||||
@@ -930,25 +923,28 @@ static parser_test_error_bits_t detect_dollar_cmdsub_errors(size_t arg_src_offse
|
||||
parse_error_list_t *out_errors) {
|
||||
parser_test_error_bits_t result_bits = 0;
|
||||
wcstring unescaped_arg_src;
|
||||
if (unescape_string(arg_src, &unescaped_arg_src, UNESCAPE_SPECIAL)) {
|
||||
if (!unescaped_arg_src.empty()) {
|
||||
wchar_t last = unescaped_arg_src.at(unescaped_arg_src.size() - 1);
|
||||
if (last == VARIABLE_EXPAND) {
|
||||
result_bits |= PARSER_TEST_ERROR;
|
||||
if (out_errors != NULL) {
|
||||
wcstring subcommand_first_token = tok_first(cmdsubst_src);
|
||||
if (subcommand_first_token.empty()) {
|
||||
// e.g. $(). Report somthing.
|
||||
subcommand_first_token = L"...";
|
||||
}
|
||||
append_syntax_error(
|
||||
out_errors,
|
||||
arg_src_offset + arg_src.size() - 1, // global position of the dollar
|
||||
ERROR_BAD_VAR_SUBCOMMAND1, truncate_string(subcommand_first_token).c_str());
|
||||
}
|
||||
|
||||
if (!unescape_string(arg_src, &unescaped_arg_src, UNESCAPE_SPECIAL) ||
|
||||
unescaped_arg_src.empty()) {
|
||||
return result_bits;
|
||||
}
|
||||
|
||||
wchar_t last = unescaped_arg_src.at(unescaped_arg_src.size() - 1);
|
||||
if (last == VARIABLE_EXPAND) {
|
||||
result_bits |= PARSER_TEST_ERROR;
|
||||
if (out_errors != NULL) {
|
||||
wcstring subcommand_first_token = tok_first(cmdsubst_src);
|
||||
if (subcommand_first_token.empty()) {
|
||||
// e.g. $(). Report somthing.
|
||||
subcommand_first_token = L"...";
|
||||
}
|
||||
append_syntax_error(
|
||||
out_errors,
|
||||
arg_src_offset + arg_src.size() - 1, // global position of the dollar
|
||||
ERROR_BAD_VAR_SUBCOMMAND1, truncate_string(subcommand_first_token).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return result_bits;
|
||||
}
|
||||
|
||||
@@ -1014,6 +1010,10 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected parse_util_locate_cmdsubst() return value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1029,28 +1029,24 @@ parser_test_error_bits_t parse_util_detect_errors_in_argument(const parse_node_t
|
||||
// Check for invalid variable expansions.
|
||||
const size_t unesc_size = unesc.size();
|
||||
for (size_t idx = 0; idx < unesc_size; idx++) {
|
||||
switch (unesc.at(idx)) {
|
||||
case VARIABLE_EXPAND:
|
||||
case VARIABLE_EXPAND_SINGLE: {
|
||||
wchar_t next_char = idx + 1 < unesc_size ? unesc.at(idx + 1) : L'\0';
|
||||
if (unesc.at(idx) != VARIABLE_EXPAND && unesc.at(idx) != VARIABLE_EXPAND_SINGLE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (next_char != VARIABLE_EXPAND && next_char != VARIABLE_EXPAND_SINGLE &&
|
||||
!wcsvarchr(next_char)) {
|
||||
err = 1;
|
||||
if (out_errors) {
|
||||
// We have something like $$$^.... Back up until we reach the first $.
|
||||
size_t first_dollar = idx;
|
||||
while (first_dollar > 0 &&
|
||||
(unesc.at(first_dollar - 1) == VARIABLE_EXPAND ||
|
||||
unesc.at(first_dollar - 1) == VARIABLE_EXPAND_SINGLE)) {
|
||||
first_dollar--;
|
||||
}
|
||||
parse_util_expand_variable_error(unesc, node.source_start, first_dollar,
|
||||
out_errors);
|
||||
}
|
||||
wchar_t next_char = idx + 1 < unesc_size ? unesc.at(idx + 1) : L'\0';
|
||||
if (next_char != VARIABLE_EXPAND && next_char != VARIABLE_EXPAND_SINGLE &&
|
||||
!wcsvarchr(next_char)) {
|
||||
err = 1;
|
||||
if (out_errors) {
|
||||
// We have something like $$$^.... Back up until we reach the first $.
|
||||
size_t first_dollar = idx;
|
||||
while (first_dollar > 0 &&
|
||||
(unesc.at(first_dollar - 1) == VARIABLE_EXPAND ||
|
||||
unesc.at(first_dollar - 1) == VARIABLE_EXPAND_SINGLE)) {
|
||||
first_dollar--;
|
||||
}
|
||||
|
||||
break;
|
||||
parse_util_expand_variable_error(unesc, node.source_start, first_dollar,
|
||||
out_errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1158,33 +1154,33 @@ parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src,
|
||||
assert(next_job_list != NULL);
|
||||
const parse_node_t *next_job =
|
||||
node_tree.next_node_in_node_list(*next_job_list, symbol_job, NULL);
|
||||
if (next_job != NULL) {
|
||||
const parse_node_t *next_statement =
|
||||
node_tree.get_child(*next_job, 0, symbol_statement);
|
||||
if (next_statement != NULL) {
|
||||
const parse_node_t *spec_statement =
|
||||
node_tree.get_child(*next_statement, 0);
|
||||
if (spec_statement &&
|
||||
spec_statement->type == symbol_boolean_statement) {
|
||||
switch (parse_node_tree_t::statement_boolean_type(
|
||||
*spec_statement)) {
|
||||
// These are not allowed.
|
||||
case parse_bool_and:
|
||||
errored = append_syntax_error(
|
||||
&parse_errors, spec_statement->source_start,
|
||||
BOOL_AFTER_BACKGROUND_ERROR_MSG, L"and");
|
||||
break;
|
||||
case parse_bool_or:
|
||||
errored = append_syntax_error(
|
||||
&parse_errors, spec_statement->source_start,
|
||||
BOOL_AFTER_BACKGROUND_ERROR_MSG, L"or");
|
||||
break;
|
||||
case parse_bool_not:
|
||||
// This one is OK.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_job == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
const parse_node_t *next_statement =
|
||||
node_tree.get_child(*next_job, 0, symbol_statement);
|
||||
if (next_statement == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
const parse_node_t *spec_statement =
|
||||
node_tree.get_child(*next_statement, 0);
|
||||
if (!spec_statement ||
|
||||
spec_statement->type != symbol_boolean_statement) {
|
||||
break;
|
||||
}
|
||||
|
||||
parse_bool_statement_type_t bool_type =
|
||||
parse_node_tree_t::statement_boolean_type(*spec_statement);
|
||||
if (bool_type == parse_bool_and) { // this is not allowed
|
||||
errored = append_syntax_error(
|
||||
&parse_errors, spec_statement->source_start,
|
||||
BOOL_AFTER_BACKGROUND_ERROR_MSG, L"and");
|
||||
} else if (bool_type == parse_bool_or) { // this is not allowed
|
||||
errored = append_syntax_error(
|
||||
&parse_errors, spec_statement->source_start,
|
||||
BOOL_AFTER_BACKGROUND_ERROR_MSG, L"or");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -270,45 +270,46 @@ static void print_profile(const std::vector<profile_item_t *> &items, FILE *out)
|
||||
int my_time;
|
||||
|
||||
me = items.at(pos);
|
||||
if (!me->skipped) {
|
||||
my_time = me->parse + me->exec;
|
||||
if (me->skipped) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = pos + 1; i < items.size(); i++) {
|
||||
prev = items.at(i);
|
||||
if (prev->skipped) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev->level <= me->level) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (prev->level > me->level + 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
my_time -= prev->parse;
|
||||
my_time -= prev->exec;
|
||||
my_time = me->parse + me->exec;
|
||||
for (i = pos + 1; i < items.size(); i++) {
|
||||
prev = items.at(i);
|
||||
if (prev->skipped) {
|
||||
continue;
|
||||
}
|
||||
if (prev->level <= me->level) {
|
||||
break;
|
||||
}
|
||||
if (prev->level > me->level + 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (me->cmd.size() > 0) {
|
||||
if (fwprintf(out, L"%d\t%d\t", my_time, me->parse + me->exec) < 0) {
|
||||
wperror(L"fwprintf");
|
||||
return;
|
||||
}
|
||||
my_time -= prev->parse + prev->exec;
|
||||
}
|
||||
|
||||
for (i = 0; i < me->level; i++) {
|
||||
if (fwprintf(out, L"-") < 0) {
|
||||
wperror(L"fwprintf");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (fwprintf(out, L"> %ls\n", me->cmd.c_str()) < 0) {
|
||||
wperror(L"fwprintf");
|
||||
return;
|
||||
}
|
||||
if (me->cmd.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fwprintf(out, L"%d\t%d\t", my_time, me->parse + me->exec) < 0) {
|
||||
wperror(L"fwprintf");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < me->level; i++) {
|
||||
if (fwprintf(out, L"-") < 0) {
|
||||
wperror(L"fwprintf");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fwprintf(out, L"> %ls\n", me->cmd.c_str()) < 0) {
|
||||
wperror(L"fwprintf");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +345,7 @@ void parser_t::expand_argument_list(const wcstring &arg_list_src, expand_flags_t
|
||||
}
|
||||
|
||||
// Get the root argument list.
|
||||
assert(!tree.empty());
|
||||
assert(!tree.empty()); //!OCLINT(multiple unary operator)
|
||||
const parse_node_t *arg_list = &tree.at(0);
|
||||
assert(arg_list->type == symbol_freestanding_argument_list);
|
||||
|
||||
@@ -563,11 +564,11 @@ bool parser_t::job_remove(job_t *j) {
|
||||
if (iter != my_job_list.end()) {
|
||||
my_job_list.erase(iter);
|
||||
return true;
|
||||
} else {
|
||||
debug(1, _(L"Job inconsistency"));
|
||||
sanity_lose();
|
||||
return false;
|
||||
}
|
||||
|
||||
debug(1, _(L"Job inconsistency"));
|
||||
sanity_lose();
|
||||
return false;
|
||||
}
|
||||
|
||||
void parser_t::job_promote(job_t *job) {
|
||||
@@ -713,9 +714,7 @@ bool parser_t::detect_errors_in_argument_list(const wcstring &arg_list_src, wcst
|
||||
parse_error_list_t errors;
|
||||
|
||||
// Use empty string for the prefix if it's NULL.
|
||||
if (prefix == NULL) {
|
||||
prefix = L"";
|
||||
}
|
||||
if (!prefix) prefix = L""; //!OCLINT(parameter reassignment)
|
||||
|
||||
// Parse the string as an argument list.
|
||||
parse_node_tree_t tree;
|
||||
@@ -727,7 +726,7 @@ bool parser_t::detect_errors_in_argument_list(const wcstring &arg_list_src, wcst
|
||||
|
||||
if (!errored) {
|
||||
// Get the root argument list.
|
||||
assert(!tree.empty());
|
||||
assert(!tree.empty()); //!OCLINT(multiple unary operator)
|
||||
const parse_node_t *arg_list = &tree.at(0);
|
||||
assert(arg_list->type == symbol_freestanding_argument_list);
|
||||
|
||||
@@ -869,10 +868,6 @@ wcstring block_t::description() const {
|
||||
result.append(L"breakpoint");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assert(0 && "Unhandled block_type_t constant");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (this->src_lineno >= 0) {
|
||||
|
||||
29
src/path.cpp
29
src/path.cpp
@@ -84,6 +84,7 @@ static bool path_get_path_core(const wcstring &cmd, wcstring *out_path,
|
||||
default: {
|
||||
debug(1, MISSING_COMMAND_ERR_MSG, nxt_path.c_str());
|
||||
wperror(L"access");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,19 +190,7 @@ wcstring path_apply_working_directory(const wcstring &path, const wcstring &work
|
||||
|
||||
// We're going to make sure that if we want to prepend the wd, that the string has no leading
|
||||
// "/".
|
||||
bool prepend_wd;
|
||||
switch (path.at(0)) {
|
||||
case L'/':
|
||||
case HOME_DIRECTORY: {
|
||||
prepend_wd = false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
prepend_wd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool prepend_wd = path.at(0) != L'/' && path.at(0) != HOME_DIRECTORY;
|
||||
if (!prepend_wd) {
|
||||
// No need to prepend the wd, so just return the path we were given.
|
||||
return path;
|
||||
@@ -298,16 +287,6 @@ bool path_get_data(wcstring &path) {
|
||||
return !result.empty();
|
||||
}
|
||||
|
||||
__attribute__((unused)) static void replace_all(wcstring &str, const wchar_t *needle,
|
||||
const wchar_t *replacement) {
|
||||
size_t needle_len = wcslen(needle);
|
||||
size_t offset = 0;
|
||||
while ((offset = str.find(needle, offset)) != wcstring::npos) {
|
||||
str.replace(offset, needle_len, replacement);
|
||||
offset += needle_len;
|
||||
}
|
||||
}
|
||||
|
||||
void path_make_canonical(wcstring &path) {
|
||||
// Ignore trailing slashes, unless it's the first character.
|
||||
size_t len = path.size();
|
||||
@@ -386,7 +365,7 @@ bool paths_are_same_file(const wcstring &path1, const wcstring &path2) {
|
||||
struct stat s1, s2;
|
||||
if (wstat(path1, &s1) == 0 && wstat(path2, &s2) == 0) {
|
||||
return s1.st_ino == s2.st_ino && s1.st_dev == s2.st_dev;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,9 @@ int set_child_group(job_t *j, process_t *p, int print_errors) {
|
||||
j->pgid = p->pid;
|
||||
}
|
||||
|
||||
if (setpgid(p->pid, j->pgid)) {
|
||||
if (setpgid(p->pid, j->pgid)) { //!OCLINT(collapsible if statements)
|
||||
// TODO: Figure out why we're testing whether the pgid is correct after attempting to
|
||||
// set it failed. This was added in commit 4e912ef8 from 2012-02-27.
|
||||
if (getpgid(p->pid) != j->pgid && print_errors) {
|
||||
char pid_buff[128];
|
||||
char job_id_buff[128];
|
||||
@@ -95,7 +97,8 @@ int set_child_group(job_t *j, process_t *p, int print_errors) {
|
||||
}
|
||||
|
||||
if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND)) {
|
||||
if (tcsetpgrp(0, j->pgid) && print_errors) {
|
||||
int result = tcsetpgrp(0, j->pgid);
|
||||
if (result == -1 && print_errors) {
|
||||
char job_id_buff[64];
|
||||
char command_buff[64];
|
||||
format_long_safe(job_id_buff, j->job_id);
|
||||
@@ -485,20 +488,14 @@ void safe_report_exec_error(int err, const char *actual_cmd, const char *const *
|
||||
/// allocate memory, etc.
|
||||
bool do_builtin_io(const char *out, size_t outlen, const char *err, size_t errlen) {
|
||||
bool success = true;
|
||||
if (out && outlen) {
|
||||
if (write_loop(STDOUT_FILENO, out, outlen) < 0) {
|
||||
int e = errno;
|
||||
debug_safe(0, "Error while writing to stdout");
|
||||
safe_perror("write_loop");
|
||||
success = false;
|
||||
errno = e;
|
||||
}
|
||||
if (out && outlen && write_loop(STDOUT_FILENO, out, outlen) < 0) {
|
||||
int e = errno;
|
||||
debug_safe(0, "Error while writing to stdout");
|
||||
safe_perror("write_loop");
|
||||
success = false;
|
||||
errno = e;
|
||||
}
|
||||
|
||||
if (err && errlen) {
|
||||
if (write_loop(STDERR_FILENO, err, errlen) < 0) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (err && errlen && write_loop(STDERR_FILENO, err, errlen) < 0) success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@ void print_help(const char *c, int fd) {
|
||||
char cmd[CMD_LEN];
|
||||
int printed = snprintf(cmd, CMD_LEN, "fish -c '__fish_print_help %s >&%d'", c, fd);
|
||||
|
||||
if (printed < CMD_LEN) {
|
||||
if ((system(cmd) == -1)) {
|
||||
write_loop(2, HELP_ERR, strlen(HELP_ERR));
|
||||
}
|
||||
if (printed < CMD_LEN && system(cmd) == -1) {
|
||||
write_loop(2, HELP_ERR, strlen(HELP_ERR));
|
||||
}
|
||||
}
|
||||
|
||||
208
src/proc.cpp
208
src/proc.cpp
@@ -247,7 +247,7 @@ void job_set_flag(job_t *j, unsigned int flag, int set) {
|
||||
}
|
||||
}
|
||||
|
||||
int job_get_flag(const job_t *j, unsigned int flag) { return !!(j->flags & flag); }
|
||||
int job_get_flag(const job_t *j, unsigned int flag) { return static_cast<bool>(j->flags & flag); }
|
||||
|
||||
int job_signal(job_t *j, int signal) {
|
||||
pid_t my_pid = getpid();
|
||||
@@ -257,13 +257,9 @@ int job_signal(job_t *j, int signal) {
|
||||
res = killpg(j->pgid, signal);
|
||||
} else {
|
||||
for (process_t *p = j->first_process; p; p = p->next) {
|
||||
if (!p->completed) {
|
||||
if (p->pid) {
|
||||
if (kill(p->pid, signal)) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!p->completed && p->pid && kill(p->pid, signal)) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,10 +308,8 @@ static void handle_child_status(pid_t pid, int status) {
|
||||
for (p = j->first_process; p; p = p->next) {
|
||||
if (pid == p->pid) {
|
||||
mark_process_status(p, status);
|
||||
if (p->completed && prev != 0) {
|
||||
if (!prev->completed && prev->pid) {
|
||||
kill(prev->pid, SIGPIPE);
|
||||
}
|
||||
if (p->completed && prev && !prev->completed && prev->pid) {
|
||||
kill(prev->pid, SIGPIPE);
|
||||
}
|
||||
found_proc = true;
|
||||
break;
|
||||
@@ -324,28 +318,34 @@ static void handle_child_status(pid_t pid, int status) {
|
||||
}
|
||||
}
|
||||
|
||||
if (WIFSIGNALED(status) && (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGQUIT)) {
|
||||
if (!is_interactive_session) {
|
||||
struct sigaction act;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
act.sa_handler = SIG_DFL;
|
||||
sigaction(SIGINT, &act, 0);
|
||||
sigaction(SIGQUIT, &act, 0);
|
||||
kill(getpid(), WTERMSIG(status));
|
||||
} else {
|
||||
// In an interactive session, tell the principal parser to skip all blocks we're
|
||||
// executing so control-C returns control to the user.
|
||||
if (p && found_proc) {
|
||||
parser_t::skip_all_blocks();
|
||||
}
|
||||
}
|
||||
// If the child process was not killed by a signal or other than SIGINT or SIGQUIT we're done.
|
||||
if (!WIFSIGNALED(status) || (WTERMSIG(status) != SIGINT && WTERMSIG(status) != SIGQUIT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_interactive_session) {
|
||||
// In an interactive session, tell the principal parser to skip all blocks we're executing
|
||||
// so control-C returns control to the user.
|
||||
if (p && found_proc) parser_t::skip_all_blocks();
|
||||
} else {
|
||||
// Deliver the SIGINT or SIGQUIT signal to ourself since we're not interactive.
|
||||
struct sigaction act;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = 0;
|
||||
act.sa_handler = SIG_DFL;
|
||||
sigaction(SIGINT, &act, 0);
|
||||
sigaction(SIGQUIT, &act, 0);
|
||||
kill(getpid(), WTERMSIG(status));
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TODO: Decide whether to eliminate this block or have it emit a warning message.
|
||||
// WARNING: See the special short-circuit logic above vis-a-vis signals.
|
||||
if (!found_proc) {
|
||||
// A child we lost track of? There have been bugs in both subshell handling and in builtin
|
||||
// handling that have caused this previously...
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -391,7 +391,7 @@ typedef unsigned int process_generation_count_t;
|
||||
|
||||
/// A static value tracking how many SIGCHLDs we have seen. This is only ever modified from within
|
||||
/// the SIGCHLD signal handler, and therefore does not need atomics or locks.
|
||||
static volatile process_generation_count_t s_sigchld_generation_count = 0;
|
||||
static volatile process_generation_count_t s_sigchld_generation_cnt = 0;
|
||||
|
||||
/// If we have received a SIGCHLD signal, process any children. If await is false, this returns
|
||||
/// immediately if no SIGCHLD has been received. If await is true, this waits for one. Returns true
|
||||
@@ -400,10 +400,10 @@ static int process_mark_finished_children(bool wants_await) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
// A static value tracking the SIGCHLD gen count at the time we last processed it. When this is
|
||||
// different from s_sigchld_generation_count, it indicates there may be unreaped processes.
|
||||
// different from s_sigchld_generation_cnt, it indicates there may be unreaped processes.
|
||||
// There may not be if we reaped them via the other waitpid path. This is only ever modified
|
||||
// from the main thread, and not from a signal handler.
|
||||
static process_generation_count_t s_last_processed_sigchld_generation_count = 0;
|
||||
static process_generation_count_t s_last_sigchld_generation_cnt = 0;
|
||||
|
||||
int processed_count = 0;
|
||||
bool got_error = false;
|
||||
@@ -412,12 +412,12 @@ static int process_mark_finished_children(bool wants_await) {
|
||||
// needs to be an atomic read (we'd use sig_atomic_t, if we knew that were unsigned -
|
||||
// fortunately aligned unsigned int is atomic on pretty much any modern chip.) It also needs to
|
||||
// occur before we start reaping, since the signal handler can be invoked at any point.
|
||||
const process_generation_count_t local_count = s_sigchld_generation_count;
|
||||
const process_generation_count_t local_count = s_sigchld_generation_cnt;
|
||||
|
||||
// Determine whether we have children to process. Note that we can't reliably use the difference
|
||||
// because a single SIGCHLD may be delivered for multiple children - see #1768. Also if we are
|
||||
// awaiting, we always process.
|
||||
bool wants_waitpid = wants_await || local_count != s_last_processed_sigchld_generation_count;
|
||||
bool wants_waitpid = wants_await || local_count != s_last_sigchld_generation_cnt;
|
||||
|
||||
if (wants_waitpid) {
|
||||
for (;;) {
|
||||
@@ -451,7 +451,7 @@ static int process_mark_finished_children(bool wants_await) {
|
||||
if (got_error) {
|
||||
return -1;
|
||||
}
|
||||
s_last_processed_sigchld_generation_count = local_count;
|
||||
s_last_sigchld_generation_cnt = local_count;
|
||||
return processed_count;
|
||||
}
|
||||
|
||||
@@ -461,7 +461,7 @@ void job_handle_signal(int signal, siginfo_t *info, void *context) {
|
||||
UNUSED(info);
|
||||
UNUSED(context);
|
||||
// This is the only place that this generation count is modified. It's OK if it overflows.
|
||||
s_sigchld_generation_count += 1;
|
||||
s_sigchld_generation_cnt += 1;
|
||||
}
|
||||
|
||||
/// Given a command like "cat file", truncate it to a reasonable length.
|
||||
@@ -568,62 +568,57 @@ int job_reap(bool allow_interactive) {
|
||||
proc_fire_event(L"PROCESS_EXIT", EVENT_EXIT, p->pid,
|
||||
(WIFSIGNALED(s) ? -1 : WEXITSTATUS(s)));
|
||||
|
||||
if (WIFSIGNALED(s)) {
|
||||
// Ignore signal SIGPIPE.We issue it ourselves to the pipe writer when the pipe
|
||||
// reader dies.
|
||||
if (WTERMSIG(s) != SIGPIPE) {
|
||||
int proc_is_job = ((p == j->first_process) && (p->next == 0));
|
||||
if (proc_is_job) job_set_flag(j, JOB_NOTIFIED, 1);
|
||||
if (!job_get_flag(j, JOB_SKIP_NOTIFICATION)) {
|
||||
// Print nothing if we get SIGINT in the foreground process group, to avoid
|
||||
// spamming obvious stuff on the console (#1119). If we get SIGINT for the
|
||||
// foreground process, assume the user typed ^C and can see it working. It's
|
||||
// possible they didn't, and the signal was delivered via pkill, etc., but
|
||||
// the SIGINT/SIGTERM distinction is precisely to allow INT to be from a UI
|
||||
// and TERM to be programmatic, so this assumption is keeping with the
|
||||
// design of signals. If echoctl is on, then the terminal will have written
|
||||
// ^C to the console. If off, it won't have. We don't echo ^C either way, so
|
||||
// as to respect the user's preference.
|
||||
if (WTERMSIG(p->status) != SIGINT || !job_get_flag(j, JOB_FOREGROUND)) {
|
||||
if (proc_is_job) {
|
||||
// We want to report the job number, unless it's the only job, in
|
||||
// which case we don't need to.
|
||||
const wcstring job_number_desc =
|
||||
(job_count == 1) ? wcstring()
|
||||
: format_string(L"Job %d, ", j->job_id);
|
||||
fwprintf(stdout,
|
||||
_(L"%ls: %ls\'%ls\' terminated by signal %ls (%ls)"),
|
||||
program_name, job_number_desc.c_str(),
|
||||
truncate_command(j->command()).c_str(),
|
||||
sig2wcs(WTERMSIG(p->status)),
|
||||
signal_get_desc(WTERMSIG(p->status)));
|
||||
} else {
|
||||
const wcstring job_number_desc =
|
||||
(job_count == 1) ? wcstring()
|
||||
: format_string(L"from job %d, ", j->job_id);
|
||||
fwprintf(stdout, _(L"%ls: Process %d, \'%ls\' %ls\'%ls\' "
|
||||
L"terminated by signal %ls (%ls)"),
|
||||
program_name, p->pid, p->argv0(), job_number_desc.c_str(),
|
||||
truncate_command(j->command()).c_str(),
|
||||
sig2wcs(WTERMSIG(p->status)),
|
||||
signal_get_desc(WTERMSIG(p->status)));
|
||||
}
|
||||
|
||||
if (cur_term != NULL)
|
||||
tputs(clr_eol, 1, &writeb);
|
||||
else
|
||||
fwprintf(stdout,
|
||||
L"\x1b[K"); // no term set up - do clr_eol manually
|
||||
|
||||
fwprintf(stdout, L"\n");
|
||||
}
|
||||
found = 1;
|
||||
}
|
||||
|
||||
// Clear status so it is not reported more than once.
|
||||
p->status = 0;
|
||||
}
|
||||
// Ignore signal SIGPIPE.We issue it ourselves to the pipe writer when the pipe reader
|
||||
// dies.
|
||||
if (!WIFSIGNALED(s) || WTERMSIG(s) == SIGPIPE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle signals other than SIGPIPE.
|
||||
int proc_is_job = ((p == j->first_process) && (p->next == 0));
|
||||
if (proc_is_job) job_set_flag(j, JOB_NOTIFIED, 1);
|
||||
if (job_get_flag(j, JOB_SKIP_NOTIFICATION)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Print nothing if we get SIGINT in the foreground process group, to avoid spamming
|
||||
// obvious stuff on the console (#1119). If we get SIGINT for the foreground
|
||||
// process, assume the user typed ^C and can see it working. It's possible they
|
||||
// didn't, and the signal was delivered via pkill, etc., but the SIGINT/SIGTERM
|
||||
// distinction is precisely to allow INT to be from a UI
|
||||
// and TERM to be programmatic, so this assumption is keeping with the design of
|
||||
// signals. If echoctl is on, then the terminal will have written ^C to the console.
|
||||
// If off, it won't have. We don't echo ^C either way, so as to respect the user's
|
||||
// preference.
|
||||
if (WTERMSIG(p->status) != SIGINT || !job_get_flag(j, JOB_FOREGROUND)) {
|
||||
if (proc_is_job) {
|
||||
// We want to report the job number, unless it's the only job, in which case
|
||||
// we don't need to.
|
||||
const wcstring job_number_desc =
|
||||
(job_count == 1) ? wcstring() : format_string(L"Job %d, ", j->job_id);
|
||||
fwprintf(stdout, _(L"%ls: %ls\'%ls\' terminated by signal %ls (%ls)"),
|
||||
program_name, job_number_desc.c_str(),
|
||||
truncate_command(j->command()).c_str(), sig2wcs(WTERMSIG(p->status)),
|
||||
signal_get_desc(WTERMSIG(p->status)));
|
||||
} else {
|
||||
const wcstring job_number_desc =
|
||||
(job_count == 1) ? wcstring() : format_string(L"from job %d, ", j->job_id);
|
||||
fwprintf(stdout, _(L"%ls: Process %d, \'%ls\' %ls\'%ls\' "
|
||||
L"terminated by signal %ls (%ls)"),
|
||||
program_name, p->pid, p->argv0(), job_number_desc.c_str(),
|
||||
truncate_command(j->command()).c_str(), sig2wcs(WTERMSIG(p->status)),
|
||||
signal_get_desc(WTERMSIG(p->status)));
|
||||
}
|
||||
|
||||
if (cur_term != NULL) {
|
||||
tputs(clr_eol, 1, &writeb);
|
||||
} else {
|
||||
fwprintf(stdout, L"\x1b[K"); // no term set up - do clr_eol manually
|
||||
}
|
||||
fwprintf(stdout, L"\n");
|
||||
}
|
||||
found = 1;
|
||||
p->status = 0; // clear status so it is not reported more than once
|
||||
}
|
||||
|
||||
// If all processes have completed, tell the user the job has completed and delete it from
|
||||
@@ -803,13 +798,10 @@ static bool terminal_give_to_job(job_t *j, int cont) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cont) {
|
||||
if (tcsetattr(0, TCSADRAIN, &j->tmodes)) {
|
||||
debug(1, _(L"Could not send job %d ('%ls') to foreground"), j->job_id,
|
||||
j->command_wcstr());
|
||||
wperror(L"tcsetattr");
|
||||
return false;
|
||||
}
|
||||
if (cont && tcsetattr(0, TCSADRAIN, &j->tmodes)) {
|
||||
debug(1, _(L"Could not send job %d ('%ls') to foreground"), j->job_id, j->command_wcstr());
|
||||
wperror(L"tcsetattr");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -908,13 +900,11 @@ void job_continue(job_t *j, bool cont) {
|
||||
process_mark_finished_children(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0: {
|
||||
// No FDs are ready. Look for finished processes.
|
||||
process_mark_finished_children(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case -1: {
|
||||
// If there is no funky IO magic, we can use waitpid instead of handling
|
||||
// child deaths through signals. This gives a rather large speed boost (A
|
||||
@@ -925,6 +915,10 @@ void job_continue(job_t *j, bool cont) {
|
||||
process_mark_finished_children(true);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DIE("unexpected return value from select_try()");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -944,15 +938,13 @@ void job_continue(job_t *j, bool cont) {
|
||||
process_t *p = j->first_process;
|
||||
while (p->next) p = p->next;
|
||||
|
||||
if (WIFEXITED(p->status) || WIFSIGNALED(p->status)) {
|
||||
// Mark process status only if we are in the foreground and the last process in a
|
||||
// pipe, and it is not a short circuited builtin.
|
||||
if (p->pid) {
|
||||
int status = proc_format_status(p->status);
|
||||
// wprintf(L"setting status %d for %ls\n", job_get_flag( j, JOB_NEGATE
|
||||
// )?!status:status, j->command);
|
||||
proc_set_last_status(job_get_flag(j, JOB_NEGATE) ? !status : status);
|
||||
}
|
||||
// Mark process status only if we are in the foreground and the last process in a pipe,
|
||||
// and it is not a short circuited builtin.
|
||||
if ((WIFEXITED(p->status) || WIFSIGNALED(p->status)) && p->pid) {
|
||||
int status = proc_format_status(p->status);
|
||||
// wprintf(L"setting status %d for %ls\n", job_get_flag( j, JOB_NEGATE
|
||||
// )?!status:status, j->command);
|
||||
proc_set_last_status(job_get_flag(j, JOB_NEGATE) ? !status : status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user