When $IFS is empty, command substitution no longer splits on newlines.
However we still want to trim off a single trailing newline, as most
commands will emit a trailing newline and it makes it harder to work
with their output.
The screen size is fetched after a SIGWINCH is delivered. The current
implementation has two issues:
* It calls ioctl() from the SIGWINCH signal handler, despite ioctl() not
being a function that is known to be safe to call.
* It's not thread-safe.
Signals can be delivered on arbitrary threads, so we don't know if it's
actually safe to be modifying the cached winsize in response to a
signal. It's also plausible that the winsize may be requested from a
background thread.
To solve the first issue, we twiddle a volatile boolean flag in the
signal handler and defer the ioctl() call until we actually request the
screen size.
To solve the second issue, we introduce a pthread rwlock around the
cached winsize. A rwlock is used because it can be expected that there
are likely to be far more window size reads than window size writes. If
we were using C++11 we could probably get away with atomics, but since
we don't have that (or boost), a rwlock should suffice.
Fixes#1613.
When a key is bound to a fish function, if that function invokes
`commandline`, it gets a stale copy of the commandline. This is because
any keys passed to `self-insert` (the default) don't actually get added
to the commandline until a special character is processed, such as the
R_NULL that gets returned after running a binding for a fish command.
To fix this, don't allow fish commands to be run for bindings if we're
processing more than one key. When a key wants to invoke a fish command,
instead we push the invocation sequence back onto the input, followed by
an R_NULL, and return. This causes the input loop to break out and
update the commandline. When it starts up again, it will re-process the
keys and invoke the fish command.
This is primarily an issue with pasting text that includes bound keys in
it. Typed text is slow enough that fish will update the commandline
between each character.
---
I don't know of any way to write a test for this, but the issue can be
reproduced as follows:
> bind _ 'commandline -i _'
This binds _ to a command that inserts _. Typing the following works:
> echo wat_is_it
But if you copy that line and paste it instead of typing it, the end
result looks like
> _echo wat_isit
With this fix in place, the pasted output correctly matches the typed
output.
expand_variables() is slightly confused about how to handle last_idx. On
input, it expects it to be the index to start processing at, but when
called recursively it always passes the current index. This means that
it may sometimes pass an index 1 past the end of the input string.
Notably, that happens when typing something like
> echo "$foo
(where "foo" is any string that is not a prefix of some existing
variable name)
Fix this by explicitly defining last_idx as being the last processed
index, meaning the next index to process is actually last_idx-1. This
means we should call it with next.size() instead of next.size()-1.
gcc interpretes C99's compound literals more strictly by invalid the
compound literal on implicit to pointer cast (because of automatic
storage duration, 6.5.2.5.6 in C99 standard draft).
This fixes the issue by not using compound literals at all.
In the base config.fish, fish_function_path and fish_complete_path have
$__fish_datadir/{functions,completions} added to them if not already
present. For some reason they were replacing the final path component
instead of being added on to the end.
The new --wraps functionality was breaking aliases of the form
`alias foo='bar baz'`. That is, aliases where the body is multiple
words. Extract the first word of the body and use that instead.
Use better errors for aliases with no name or no body.
Remove the useless ASCII test of the first byte of IFS. We don't split
on the first character, we only use a non-empty IFS as a signal to split
on newlines.
IFS is used for more than just the read builtin. Setting it to the empty
string also disables line-splitting in command substitution, and it's
done this for the past 7 years. Some day we may have a better way to do
this, but for now, document the current solution.
The docs claimed that the $HOME and $USER variables could only be
changed by the root user. This is untrue. They can be changed by
non-root users as well.
Repurpose the ENV_INVALID return value for env_set(), which wasn't
currently used by anything. When a bad value is passed for the 'umask'
key, return ENV_INVALID to signal this and print a good error message
from the `set` builtin.
This makes `set umask foo` properly produce an error.
The span now properly points at the token that was invalid, rather than
the start of the slice.
Also fix the span for `()[1]` and `()[d]`, which were previously
reporting no source location at all.
We can't color the whole argument as an error, since the tokenizer is
responsible for that and doesn't care abou this case, but we can color
the `$foo[` bit as an error.
The backslash-escape wasn't being properly caught by the highlighter.
Also remove the highlighting of `"\'"`, as `\'` is not a valid escape in
double-quotes, and add highlighting for a backslash-escaped newline.
When a variable is parsed as being empty, parse out the slice and
validate the indexes anyway, behaving for slicing purposes as if the
variable had a single empty value.
Besides providing errors when expected, this also fixes the following:
set -l foo
echo "$foo[1]"
This used to print "[1]", now it properly prints nothing.
Double expansions of variables had the following issues:
* `"$$foo"` threw an error no matter what the value of `$foo` was.
* `set -l foo ''; echo $$foo` threw an error because of the expansion of
`$foo` to `''`.
With this change, double expansion always works properly. When
double-expanding a multi-valued variable, in a double-quoted string the
first word of the inner expansion is used for the outer expansion, and
outside of a quoted string every word is used for the double-expansion
in each of the arguments.
> set -l foo bar baz
> set -l bar one two
> set -l baz three four
> echo "$$foo"
one two baz
> echo $$foo
one two three four