mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-29 10:31:14 -03:00
Merge branch 'master' of https://github.com/fish-shell/fish-shell into coverity_scan_master
This commit is contained in:
11
doc_src/fish_mode_prompt.txt
Normal file
11
doc_src/fish_mode_prompt.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
\section fish_mode_prompt fish_mode_prompt - define the appearance of the mode indicator
|
||||
|
||||
\subsection fish_mode_prompt-synopsis Synopsis
|
||||
|
||||
fish_mode_prompt will output the mode indicator for use in vi-mode.
|
||||
|
||||
\subsection fish_mode_prompt-description Description
|
||||
|
||||
The output of `fish_mode_prompt` will be displayed in the mode indicator position to the left of the regular prompt.
|
||||
|
||||
Multiple lines are not supported in `fish_mode_prompt`.
|
||||
@@ -524,7 +524,7 @@ echo The plural of $WORD is {$WORD}s
|
||||
|
||||
Note that without the quotes or braces, fish will try to expand a variable called `$WORDs`, which may not exist.
|
||||
|
||||
The latter syntax `{$WORD}` works by exploiting <a href="#expand-brace">brace expansion</a>; care should be taken with array variables and undefined variables, as these expand as a <a href="#cartesian-product">cartesian product</a>, so undefined variables eliminate the string.
|
||||
The latter syntax `{$WORD}` works by exploiting <a href="#expand-brace">brace expansion</a>; care should be taken with zero-element array variables and undefined variables, as these expand as a <a href="#cartesian-product">cartesian product</a>, so they eliminate the string.
|
||||
|
||||
Variable expansion is the only type of expansion performed on double quoted strings. There is, however, an important difference in how variables are expanded when quoted and when unquoted. An unquoted variable expansion will result in a variable number of arguments. For example, if the variable `$foo` has zero elements or is undefined, the argument `$foo` will expand to zero elements. If the variable $foo is an array of five elements, the argument `$foo` will expand to five elements. When quoted, like `"$foo"`, a variable expansion will always result in exactly one argument. Undefined variables will expand to the empty string, and array variables will be concatenated using the space character.
|
||||
|
||||
@@ -1012,6 +1012,8 @@ You can change these key bindings using the <a href="commands.html#bind">bind</a
|
||||
|
||||
Vi mode allows for the use of Vi-like commands at the prompt. Initially, <a href="#vi-mode-insert">insert mode</a> is active. @key{Escape} enters <a href="#vi-mode-command">command mode</a>. The commands available in command, insert and visual mode are described below. Vi mode builds on top of <a href="#emacs-mode">Emacs mode</a>, so all keybindings mentioned there that do not contradict the ones mentioned here also work.
|
||||
|
||||
When in vi-mode, the <a href="fish_mode_prompt.html">`fish_mode_prompt`</a> function will display a mode indicator to the left of the prompt. The `fish_vi_cursor` function is available to change the cursor's shape depending on the mode in supported terminals.
|
||||
|
||||
\subsubsection vi-mode-command Command mode
|
||||
|
||||
Command mode is also known as normal mode.
|
||||
@@ -1200,6 +1202,10 @@ function fish_title
|
||||
end
|
||||
\endfish
|
||||
|
||||
\subsection prompt Programmable prompt
|
||||
|
||||
When fish waits for input, it will display a prompt by evaluating the `fish_prompt` and `fish_right_prompt` functions. The output of the former is displayed on the left and the latter's output on the right side of the terminal. The output of `fish_mode_prompt` will be prepended on the left, though the default function only does this when in <a href="index.html#vi-mode">vi-mode</a>.
|
||||
|
||||
\subsection greeting Configurable greeting
|
||||
|
||||
If a function named `fish_greeting` exists, it will be run when entering interactive mode. Otherwise, if an environment variable named `fish_greeting` exists, it will be printed.
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/signal.h>
|
||||
#include <wctype.h>
|
||||
#include <cmath>
|
||||
#include <iosfwd>
|
||||
@@ -44,7 +44,9 @@ static bool should_exit(unsigned char c) {
|
||||
recent_chars[1] = recent_chars[2];
|
||||
recent_chars[2] = recent_chars[3];
|
||||
recent_chars[3] = c;
|
||||
return memcmp(recent_chars, "exit", 4) == 0 || memcmp(recent_chars, "quit", 4) == 0;
|
||||
return (memcmp(recent_chars, "exit", 4) == 0 || memcmp(recent_chars, "quit", 4) == 0 ||
|
||||
memcmp(recent_chars + 2, "\x3\x3", 2) == 0 || // ctrl-C, ctrl-C
|
||||
memcmp(recent_chars + 2, "\x4\x4", 2) == 0); // ctrl-D, ctrl-D
|
||||
}
|
||||
|
||||
/// Return the key name if the recent sequence of characters matches a known terminfo sequence.
|
||||
@@ -148,11 +150,46 @@ static void process_input(bool continuous_mode) {
|
||||
|
||||
/// Make sure we cleanup before exiting if we receive a signal that should cause us to exit.
|
||||
/// Otherwise just report receipt of the signal.
|
||||
static void signal_handler(int signo) {
|
||||
printf("\nSignal #%d (%ls) received\n\n", signo, sig2wcs(signo));
|
||||
if (signo == SIGINT || signo == SIGTERM || signo == SIGABRT || signo == SIGSEGV) {
|
||||
static struct sigaction old_sigactions[32];
|
||||
static void signal_handler(int signo, siginfo_t *siginfo, void *siginfo_arg) {
|
||||
debug(2, L"signal #%d (%ls) received", signo, sig2wcs(signo));
|
||||
// SIGINT isn't included in the following conditional because it is handled specially by fish;
|
||||
// i.e., it causes \cC to be reinserted into the tty input stream.
|
||||
if (signo == SIGHUP || signo == SIGTERM || signo == SIGABRT || signo == SIGSEGV) {
|
||||
keep_running = false;
|
||||
}
|
||||
if (old_sigactions[signo].sa_handler != SIG_IGN &&
|
||||
old_sigactions[signo].sa_handler != SIG_DFL) {
|
||||
int needs_siginfo = old_sigactions[signo].sa_flags & SA_SIGINFO;
|
||||
if (needs_siginfo) {
|
||||
old_sigactions[signo].sa_sigaction(signo, siginfo, siginfo_arg);
|
||||
} else {
|
||||
old_sigactions[signo].sa_handler(signo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Install a handler for every signal. This allows us to restore the tty modes so the terminal is
|
||||
/// still usable when we die. If the signal already has a handler arrange to invoke it from within
|
||||
/// our handler.
|
||||
static void install_our_signal_handlers() {
|
||||
struct sigaction new_sa, old_sa;
|
||||
sigemptyset(&new_sa.sa_mask);
|
||||
new_sa.sa_flags = SA_SIGINFO;
|
||||
new_sa.sa_sigaction = signal_handler;
|
||||
|
||||
for (int signo = 1; signo < 32; signo++) {
|
||||
if (sigaction(signo, &new_sa, &old_sa) != -1) {
|
||||
memcpy(&old_sigactions[signo], &old_sa, sizeof(old_sa));
|
||||
if (old_sa.sa_handler == SIG_IGN) {
|
||||
debug(2, "signal #%d (%ls) was being ignored", signo, sig2wcs(signo));
|
||||
}
|
||||
if (old_sa.sa_flags && ~SA_SIGINFO != 0) {
|
||||
debug(2, L"signal #%d (%ls) handler had flags 0x%X", signo, sig2wcs(signo),
|
||||
old_sa.sa_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup our environment (e.g., tty modes), process key strokes, then reset the environment.
|
||||
@@ -161,24 +198,17 @@ static void setup_and_process_keys(bool continuous_mode) {
|
||||
setenv("LC_ALL", "POSIX", 1); // ensure we're in a single-byte locale
|
||||
set_main_thread();
|
||||
setup_fork_guards();
|
||||
|
||||
// Install a handler for every signal. This allows us to restore the tty modes so the terminal
|
||||
// is still usable when we die. We do this only to ensure any signal not handled by
|
||||
// signal_set_handlers() gets handled for a clean exit.
|
||||
for (int signo = 1; signo < 32; signo++) {
|
||||
signal(signo, signal_handler);
|
||||
}
|
||||
|
||||
env_init();
|
||||
reader_init();
|
||||
input_init();
|
||||
proc_push_interactive(1);
|
||||
signal_set_handlers();
|
||||
install_our_signal_handlers();
|
||||
|
||||
if (continuous_mode) {
|
||||
printf("\n");
|
||||
printf("To terminate this program type \"exit\" or \"quit\" in this window\n");
|
||||
printf("or \"kill %d\" in another window\n", getpid());
|
||||
printf("To terminate this program type \"exit\" or \"quit\" in this window,\n");
|
||||
printf("or press [ctrl-C] or [ctrl-D] twice in a row.\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
@@ -251,6 +281,11 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
|
||||
fprintf(stderr, "Stdin and stdout must be attached to a tty, redirection not allowed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
setup_and_process_keys(continuous_mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
62
tests/fkr.expect
Normal file
62
tests/fkr.expect
Normal file
@@ -0,0 +1,62 @@
|
||||
# vim: set filetype=expect:
|
||||
|
||||
spawn $fish_key_reader -c
|
||||
|
||||
# Do we get the expected startup prompt?
|
||||
expect -ex "Press a key" {
|
||||
puts "saw expected startup prompt"
|
||||
} unmatched {
|
||||
puts stderr "didn't see expected startup prompt"
|
||||
}
|
||||
|
||||
# Is a single control char echoed correctly?
|
||||
send "\x01"
|
||||
expect -ex "char: \\cA\r\n" {
|
||||
puts "ctrl-a handled"
|
||||
} unmatched {
|
||||
puts stderr "ctrl-a not handled"
|
||||
}
|
||||
|
||||
# Is a non-ASCII char echoed correctly? This looks a bit odd but \xE9
|
||||
# when using UTF-8 encoding becomes the two byte sequence \xC3\xA9 (or
|
||||
# \303\251).
|
||||
send "\xE9"
|
||||
expect -ex "char: \\303 (aka non-ASCII)\r\n" {
|
||||
puts "\\xE9, first byte, handled"
|
||||
} unmatched {
|
||||
puts stderr "\\xE9, first byte, not handled"
|
||||
}
|
||||
expect -ex "char: \\251 (aka non-ASCII)\r\n" {
|
||||
puts "\\xE9, second byte, handled"
|
||||
} unmatched {
|
||||
puts stderr "\\xE9, second byte, not handled"
|
||||
}
|
||||
|
||||
# Is a NULL char echoed correctly?
|
||||
send -null
|
||||
expect -ex "char: \\c@\r\n" {
|
||||
puts "\\c@ handled"
|
||||
} unmatched {
|
||||
puts stderr "\\c@ not handled"
|
||||
}
|
||||
|
||||
# Does it keep running if handed control sequences in the wrong order?
|
||||
send "\x03\x04"
|
||||
expect -ex "char: \\cD\r\n" {
|
||||
puts "invalid terminate sequence handled"
|
||||
} unmatched {
|
||||
puts stderr "invalid terminate sequence not handled"
|
||||
}
|
||||
|
||||
# Now send a second [ctrl-D]. Does that terminate the process like it should?
|
||||
send "\x04"
|
||||
expect -ex "char: \\cD\r\n" {
|
||||
puts "valid terminate sequence handled"
|
||||
} unmatched {
|
||||
puts stderr "valid terminate sequence not handled"
|
||||
}
|
||||
expect -ex "Exiting at your request.\r\n" {
|
||||
puts "exited on seeing valid terminate"
|
||||
} unmatched {
|
||||
puts stderr "did not exit on seeing valid terminate sequence"
|
||||
}
|
||||
0
tests/fkr.expect.err
Normal file
0
tests/fkr.expect.err
Normal file
8
tests/fkr.expect.out
Normal file
8
tests/fkr.expect.out
Normal file
@@ -0,0 +1,8 @@
|
||||
saw expected startup prompt
|
||||
ctrl-a handled
|
||||
\xE9, first byte, handled
|
||||
\xE9, second byte, handled
|
||||
\c@ handled
|
||||
invalid terminate sequence handled
|
||||
valid terminate sequence handled
|
||||
exited on seeing valid terminate
|
||||
1
tests/fkr.expect.status
Normal file
1
tests/fkr.expect.status
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -4,6 +4,7 @@ log_user 0
|
||||
log_file -noappend interactive.tmp.log
|
||||
|
||||
set fish ../test/root/bin/fish
|
||||
set fish_key_reader ../test/root/bin/fish_key_reader
|
||||
|
||||
set timeout 5
|
||||
|
||||
|
||||
Reference in New Issue
Block a user