mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-25 12:23:09 +00:00
fix setting up and using the terminfo data
There should be just one place that calls `setupterm()`. While refactoring the code I also decided to not make initializing the curses subsystem a fatal error. We now try two fallback terminal names ("ansi" and "dumb") and if those can't be used we still end up with a usable shell. Fixes #3850
This commit is contained in:
parent
42a320064c
commit
6d02bec4c7
16 changed files with 282 additions and 237 deletions
|
@ -972,7 +972,7 @@ obj/builtin_set.o: config.h src/builtin.h src/common.h src/fallback.h
|
|||
obj/builtin_set.o: src/signal.h src/env.h src/expand.h src/parse_constants.h
|
||||
obj/builtin_set.o: src/io.h src/proc.h src/parse_tree.h src/tokenizer.h
|
||||
obj/builtin_set.o: src/wgetopt.h src/wutil.h
|
||||
obj/builtin_set_color.o: config.h src/builtin.h src/common.h src/fallback.h
|
||||
obj/builtin_set_color.o: config.h src/builtin.h src/common.h src/env.h src/fallback.h
|
||||
obj/builtin_set_color.o: src/signal.h src/color.h src/io.h src/output.h
|
||||
obj/builtin_set_color.o: src/proc.h src/parse_tree.h src/parse_constants.h
|
||||
obj/builtin_set_color.o: src/tokenizer.h src/wgetopt.h src/wutil.h
|
||||
|
|
|
@ -93,7 +93,9 @@
|
|||
{ symbol: ["uint32_t", "private", "<stdint.h>", "public"] },
|
||||
{ symbol: ["intptr_t", "private", "<stdint.h>", "public"] },
|
||||
{ symbol: ["tparm", "private", "<ncurses.h>", "public"] },
|
||||
{ symbol: ["tigetflag", "private", "<ncurses.h>", "public"] },
|
||||
{ symbol: ["ERR", "private", "<ncurses.h>", "public"] },
|
||||
{ symbol: ["OK", "private", "<ncurses.h>", "public"] },
|
||||
{ symbol: ["select", "private", "<sys/select.h>", "public"] },
|
||||
{ symbol: ["_LIBCPP_VERSION", "private", "<stddef.h>", "public"] },
|
||||
{ symbol: ["_LIBCPP_VERSION", "private", "<unistd.h>", "public"] },
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if HAVE_NCURSES_H
|
||||
#include <ncurses.h>
|
||||
|
@ -25,6 +24,7 @@
|
|||
#include "builtin.h"
|
||||
#include "color.h"
|
||||
#include "common.h"
|
||||
#include "env.h"
|
||||
#include "io.h"
|
||||
#include "output.h"
|
||||
#include "proc.h"
|
||||
|
@ -52,6 +52,9 @@ static int set_color_builtin_outputter(char c) {
|
|||
|
||||
/// set_color builtin.
|
||||
int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
||||
// By the time this is called we should have initialized the curses subsystem.
|
||||
assert(curses_initialized);
|
||||
|
||||
wgetopter_t w;
|
||||
// Variables used for parsing the argument list.
|
||||
const struct woption long_options[] = {{L"background", required_argument, 0, 'b'},
|
||||
|
@ -66,7 +69,6 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||
{0, 0, 0, 0}};
|
||||
|
||||
const wchar_t *short_options = L"b:hvoidrcu";
|
||||
|
||||
int argc = builtin_count_args(argv);
|
||||
|
||||
// Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a
|
||||
|
@ -77,7 +79,6 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||
|
||||
const wchar_t *bgcolor = NULL;
|
||||
bool bold = false, underline = false, italics = false, dim = false, reverse = false;
|
||||
int errret;
|
||||
|
||||
// Parse options to obtain the requested operation and the modifiers.
|
||||
w.woptind = 0;
|
||||
|
@ -162,12 +163,6 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
// Make sure that the term exists.
|
||||
if (cur_term == NULL && setupterm(0, STDOUT_FILENO, &errret) == ERR) {
|
||||
streams.err.append_format(_(L"%ls: Could not set up terminal\n"), argv[0]);
|
||||
return STATUS_BUILTIN_ERROR;
|
||||
}
|
||||
|
||||
// Test if we have at least basic support for setting fonts, colors and related bits - otherwise
|
||||
// just give up...
|
||||
if (cur_term == NULL || !exit_attribute_mode) {
|
||||
|
|
240
src/env.cpp
240
src/env.cpp
|
@ -6,6 +6,7 @@
|
|||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -14,6 +15,13 @@
|
|||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#if HAVE_NCURSES_H
|
||||
#include <ncurses.h>
|
||||
#elif HAVE_NCURSES_CURSES_H
|
||||
#include <ncurses/curses.h>
|
||||
#else
|
||||
#include <curses.h>
|
||||
#endif
|
||||
#if HAVE_TERM_H
|
||||
#include <term.h>
|
||||
#elif HAVE_NCURSES_TERM_H
|
||||
|
@ -37,6 +45,7 @@
|
|||
#include "history.h"
|
||||
#include "input.h"
|
||||
#include "input_common.h"
|
||||
#include "output.h"
|
||||
#include "path.h"
|
||||
#include "proc.h"
|
||||
#include "reader.h"
|
||||
|
@ -46,6 +55,8 @@
|
|||
|
||||
/// Value denoting a null string.
|
||||
#define ENV_NULL L"\x1d"
|
||||
#define DEFAULT_TERM1 "ansi"
|
||||
#define DEFAULT_TERM2 "dumb"
|
||||
|
||||
/// Some configuration path environment variables.
|
||||
#define FISH_DATADIR_VAR L"__fish_datadir"
|
||||
|
@ -62,20 +73,34 @@ extern char **environ;
|
|||
size_t read_byte_limit = READ_BYTE_LIMIT;
|
||||
|
||||
bool g_use_posix_spawn = false; // will usually be set to true
|
||||
bool curses_initialized = false;
|
||||
|
||||
/// Does the terminal have the "eat_newline_glitch".
|
||||
bool term_has_xn = false;
|
||||
|
||||
/// List of all locale environment variable names.
|
||||
/// This is used to ensure that we don't perform any callbacks from `react_to_variable_change()`
|
||||
/// when we're importing environment variables in `env_init()`. That's because we don't have any
|
||||
/// control over the order in which the vars are imported and some of them work in combination.
|
||||
/// For example, `TERMINFO_DIRS` and `TERM`. If the user has set `TERM` to a custom value that is
|
||||
/// found in `TERMINFO_DIRS` we don't to call `handle_curses()` before we've imported the latter.
|
||||
static bool env_initialized = false;
|
||||
|
||||
/// List of all locale environment variable names that might trigger (re)initializing the locale
|
||||
/// subsystem.
|
||||
static const wchar_t *const locale_variable[] = {
|
||||
L"LANG", L"LANGUAGE", L"LC_ALL", L"LC_ADDRESS", L"LC_COLLATE",
|
||||
L"LC_CTYPE", L"LC_IDENTIFICATION", L"LC_MEASUREMENT", L"LC_MESSAGES", L"LC_MONETARY",
|
||||
L"LC_NAME", L"LC_NUMERIC", L"LC_PAPER", L"LC_TELEPHONE", L"LC_TIME",
|
||||
NULL};
|
||||
|
||||
/// List of all curses environment variable names.
|
||||
/// List of all curses environment variable names that might trigger (re)initializing the curses
|
||||
/// subsystem.
|
||||
static const wchar_t *const curses_variable[] = {L"TERM", L"TERMINFO", L"TERMINFO_DIRS", NULL};
|
||||
|
||||
// Some forward declarations to make it easy to logically group the code.
|
||||
static void init_locale();
|
||||
static void init_curses();
|
||||
|
||||
// Struct representing one level in the function variable stack.
|
||||
// Only our variable stack should create and destroy these
|
||||
class env_node_t {
|
||||
|
@ -105,7 +130,6 @@ class variable_entry_t {
|
|||
static pthread_mutex_t env_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static bool local_scope_exports(const env_node_t *n);
|
||||
static void handle_locale(const wchar_t *env_var_name);
|
||||
|
||||
// A class wrapping up a variable stack
|
||||
// Currently there is only one variable stack in fish,
|
||||
|
@ -136,6 +160,8 @@ struct var_stack_t {
|
|||
// Pops the top node if it's not global
|
||||
void pop();
|
||||
|
||||
bool var_changed(wchar_t const *const vars[]);
|
||||
|
||||
// Returns the next scope to search for a given node, respecting the new_scope lag
|
||||
// Returns NULL if we're done
|
||||
env_node_t *next_scope_to_search(env_node_t *node);
|
||||
|
@ -156,23 +182,24 @@ void var_stack_t::push(bool new_scope) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return true if one of the vars in the passed list was changed in the current var scope.
|
||||
bool var_stack_t::var_changed(wchar_t const *const vars[]) {
|
||||
for (auto v = vars; *v; v++) {
|
||||
if (top->env.find(*v) != top->env.end()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void var_stack_t::pop() {
|
||||
// Don't pop the global
|
||||
if (this->top.get() == this->global_env) {
|
||||
// Don't pop the top-most, global, level.
|
||||
if (top.get() == this->global_env) {
|
||||
debug(0, _(L"Tried to pop empty environment stack."));
|
||||
sanity_lose();
|
||||
return;
|
||||
}
|
||||
|
||||
const wchar_t *locale_changed = NULL;
|
||||
|
||||
for (int i = 0; locale_variable[i]; i++) {
|
||||
var_table_t::iterator result = top->env.find(locale_variable[i]);
|
||||
if (result != top->env.end()) {
|
||||
locale_changed = locale_variable[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool locale_changed = this->var_changed(locale_variable);
|
||||
bool curses_changed = this->var_changed(curses_variable);
|
||||
|
||||
if (top->new_scope) { //!OCLINT(collapsible if statements)
|
||||
if (top->exportv || local_scope_exports(top->next.get())) {
|
||||
|
@ -196,8 +223,9 @@ void var_stack_t::pop() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
// TODO: Move this to something general.
|
||||
if (locale_changed) handle_locale(locale_changed);
|
||||
|
||||
if (locale_changed) init_locale();
|
||||
if (curses_changed) init_curses();
|
||||
}
|
||||
|
||||
const env_node_t *var_stack_t::next_scope_to_search(const env_node_t *node) const {
|
||||
|
@ -303,25 +331,27 @@ static bool var_is_locale(const wcstring &key) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// Properly sets all locale information.
|
||||
static void handle_locale(const wchar_t *env_var_name) {
|
||||
debug(2, L"handle_locale() called in response to '%ls' changing", env_var_name);
|
||||
/// Initialize the locale subsystem.
|
||||
static void init_locale() {
|
||||
// We have to make a copy because the subsequent setlocale() call to change the locale will
|
||||
// invalidate the pointer from the this setlocale() call.
|
||||
char *old_msg_locale = strdup(setlocale(LC_MESSAGES, NULL));
|
||||
const env_var_t val = env_get_string(env_var_name, ENV_EXPORT);
|
||||
|
||||
for (const wchar_t *const *var_name = locale_variable; *var_name; var_name++) {
|
||||
const env_var_t val = env_get_string(*var_name, ENV_EXPORT);
|
||||
const std::string &name = wcs2string(*var_name);
|
||||
const std::string &value = wcs2string(val);
|
||||
const std::string &name = wcs2string(env_var_name);
|
||||
debug(2, L"locale var %s='%s'", name.c_str(), value.c_str());
|
||||
if (val.empty()) {
|
||||
unsetenv(name.c_str());
|
||||
} else {
|
||||
setenv(name.c_str(), value.c_str(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
char *locale = setlocale(LC_ALL, "");
|
||||
fish_setlocale();
|
||||
debug(2, L"handle_locale() setlocale(): '%s'", locale);
|
||||
debug(2, L"init_locale() setlocale(): '%s'", locale);
|
||||
|
||||
const char *new_msg_locale = setlocale(LC_MESSAGES, NULL);
|
||||
debug(3, L"old LC_MESSAGES locale: '%s'", old_msg_locale);
|
||||
|
@ -380,15 +410,84 @@ static bool does_term_support_setting_title() {
|
|||
return true;
|
||||
}
|
||||
|
||||
/// Handle changes to the TERM env var that do not involves the curses subsystem.
|
||||
static void handle_term() { can_set_term_title = does_term_support_setting_title(); }
|
||||
/// Updates our idea of whether we support term256 and term24bit (see issue #10222).
|
||||
static void update_fish_color_support() {
|
||||
// Detect or infer term256 support. If fish_term256 is set, we respect it;
|
||||
// otherwise infer it from the TERM variable or use terminfo.
|
||||
env_var_t fish_term256 = env_get_string(L"fish_term256");
|
||||
env_var_t term = env_get_string(L"TERM");
|
||||
bool support_term256 = false; // default to no support
|
||||
if (!fish_term256.missing_or_empty()) {
|
||||
support_term256 = from_string<bool>(fish_term256);
|
||||
debug(2, L"256 color support determined by 'fish_term256'");
|
||||
} else if (term.find(L"256color") != wcstring::npos) {
|
||||
// TERM=*256color*: Explicitly supported.
|
||||
support_term256 = true;
|
||||
debug(2, L"256 color support enabled for '256color' in TERM");
|
||||
} else if (term.find(L"xterm") != wcstring::npos) {
|
||||
// Assume that all xterms are 256, except for OS X SnowLeopard
|
||||
const env_var_t prog = env_get_string(L"TERM_PROGRAM");
|
||||
const env_var_t progver = env_get_string(L"TERM_PROGRAM_VERSION");
|
||||
if (prog == L"Apple_Terminal" && !progver.missing_or_empty()) {
|
||||
// OS X Lion is version 300+, it has 256 color support
|
||||
if (strtod(wcs2str(progver), NULL) > 300) {
|
||||
support_term256 = true;
|
||||
debug(2, L"256 color support enabled for TERM=xterm + modern Terminal.app");
|
||||
}
|
||||
} else {
|
||||
support_term256 = true;
|
||||
debug(2, L"256 color support enabled for TERM=xterm");
|
||||
}
|
||||
} else if (cur_term != NULL) {
|
||||
// See if terminfo happens to identify 256 colors
|
||||
support_term256 = (max_colors >= 256);
|
||||
debug(2, L"256 color support: using %d colors per terminfo", max_colors);
|
||||
} else {
|
||||
debug(2, L"256 color support not enabled (yet)");
|
||||
}
|
||||
|
||||
/// Push all curses/terminfo env vars into the global environment where they can be found by those
|
||||
/// libraries.
|
||||
static void handle_curses(const wchar_t *env_var_name) {
|
||||
debug(2, L"handle_curses() called in response to '%ls' changing", env_var_name);
|
||||
const env_var_t val = env_get_string(env_var_name, ENV_EXPORT);
|
||||
const std::string &name = wcs2string(env_var_name);
|
||||
env_var_t fish_term24bit = env_get_string(L"fish_term24bit");
|
||||
bool support_term24bit;
|
||||
if (!fish_term24bit.missing_or_empty()) {
|
||||
support_term24bit = from_string<bool>(fish_term24bit);
|
||||
debug(2, L"'fish_term24bit' preference: 24-bit color %s",
|
||||
support_term24bit ? L"enabled" : L"disabled");
|
||||
} else {
|
||||
// We don't attempt to infer term24 bit support yet.
|
||||
support_term24bit = false;
|
||||
}
|
||||
|
||||
color_support_t support = (support_term256 ? color_support_term256 : 0) |
|
||||
(support_term24bit ? color_support_term24bit : 0);
|
||||
output_set_color_support(support);
|
||||
}
|
||||
|
||||
// Try to initialize the terminfo/curses subsystem using our fallback terminal name. Do not set
|
||||
// `TERM` to our fallback. We're only doing this in the hope of getting a minimally functional
|
||||
// shell. If we launch an external command that uses TERM it should get the same value we were
|
||||
// given, if any.
|
||||
static bool initialize_curses_using_fallback(const char *term) {
|
||||
// If $TERM is already set to the fallback name we're about to use there isn't any point in
|
||||
// seeing if the fallback name can be used.
|
||||
const char *term_env = wcs2str(env_get_string(L"TERM"));
|
||||
if (!strcmp(term_env, DEFAULT_TERM1) || !strcmp(term_env, DEFAULT_TERM2)) return false;
|
||||
|
||||
if (is_interactive_session) {
|
||||
debug(1, _(L"Using fallback terminal type '%s'."), term);
|
||||
}
|
||||
int err_ret;
|
||||
if (setupterm((char *)term, STDOUT_FILENO, &err_ret) == OK) return true;
|
||||
if (is_interactive_session) {
|
||||
debug(1, _(L"Could not set up terminal using the fallback terminal type '%s'."), term);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Initialize the curses subsystem.
|
||||
static void init_curses() {
|
||||
for (const wchar_t *const *var_name = curses_variable; *var_name; var_name++) {
|
||||
const env_var_t val = env_get_string(*var_name, ENV_EXPORT);
|
||||
const std::string &name = wcs2string(*var_name);
|
||||
const std::string &value = wcs2string(val);
|
||||
debug(2, L"curses var %s='%s'", name.c_str(), value.c_str());
|
||||
if (val.empty()) {
|
||||
|
@ -396,23 +495,45 @@ static void handle_curses(const wchar_t *env_var_name) {
|
|||
} else {
|
||||
setenv(name.c_str(), value.c_str(), 1);
|
||||
}
|
||||
// TODO: Modify input_init() to allow calling it when the terminfo env vars are dynamically
|
||||
// changed. At the present time it can be called just once. Also, we should really only do this
|
||||
// if the TERM var is set.
|
||||
// input_init();
|
||||
term_has_xn = tgetflag((char *)"xn") == 1; // does terminal have the eat_newline_glitch
|
||||
}
|
||||
|
||||
int err_ret;
|
||||
if (setupterm(NULL, STDOUT_FILENO, &err_ret) == ERR) {
|
||||
env_var_t term = env_get_string(L"TERM");
|
||||
if (is_interactive_session) {
|
||||
debug(1, _(L"Could not set up terminal."));
|
||||
if (term.missing_or_empty()) {
|
||||
debug(1, _(L"TERM environment variable not set."));
|
||||
} else {
|
||||
debug(1, _(L"TERM environment variable set to '%ls'."), term.c_str());
|
||||
debug(1, _(L"Check that this terminal type is supported on this system."));
|
||||
}
|
||||
}
|
||||
|
||||
if (!initialize_curses_using_fallback(DEFAULT_TERM1)) {
|
||||
initialize_curses_using_fallback(DEFAULT_TERM2);
|
||||
}
|
||||
}
|
||||
|
||||
can_set_term_title = does_term_support_setting_title();
|
||||
term_has_xn = tigetflag((char *)"xenl") == 1; // does terminal have the eat_newline_glitch
|
||||
update_fish_color_support();
|
||||
// Invalidate the cached escape sequences since they may no longer be valid.
|
||||
cached_esc_sequences.clear();
|
||||
curses_initialized = true;
|
||||
}
|
||||
|
||||
/// React to modifying the given variable.
|
||||
static void react_to_variable_change(const wcstring &key) {
|
||||
// Don't do any of this until `env_init()` has run. We only want to do this in response to
|
||||
// variables set by the user; e.g., in a script like *config.fish* or interactively.
|
||||
if (!env_initialized) return;
|
||||
|
||||
if (var_is_locale(key)) {
|
||||
handle_locale(key.c_str());
|
||||
init_locale();
|
||||
} else if (var_is_curses(key)) {
|
||||
handle_curses(key.c_str());
|
||||
if (key == L"TERM") handle_term();
|
||||
init_curses();
|
||||
init_input();
|
||||
} else if (var_is_timezone(key)) {
|
||||
handle_timezone(key.c_str());
|
||||
} else if (key == L"fish_term256" || key == L"fish_term24bit") {
|
||||
|
@ -468,7 +589,7 @@ static void setup_path() {
|
|||
}
|
||||
}
|
||||
|
||||
/// Initialize the `COLUMNS` and `LINES` env vars if they don't already exist to reasonable
|
||||
/// If they don't already exist initialize the `COLUMNS` and `LINES` env vars to reasonable
|
||||
/// defaults. They will be updated later by the `get_current_winsize()` function if they need to be
|
||||
/// adjusted.
|
||||
static void env_set_termsize() {
|
||||
|
@ -533,6 +654,42 @@ static void setup_user(bool force) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Various things we need to initialize at run-time that don't really fit any of the other init
|
||||
/// routines.
|
||||
void misc_init() {
|
||||
env_set_read_limit();
|
||||
|
||||
// If stdout is open on a tty ensure stdio is unbuffered. That's because those functions might
|
||||
// be intermixed with `write()` calls and we need to ensure the writes are not reordered. See
|
||||
// issue #3748.
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
fflush(stdout);
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
}
|
||||
|
||||
#ifdef OS_IS_CYGWIN
|
||||
// MS Windows tty devices do not currently have either a read or write timestamp. Those
|
||||
// respective fields of `struct stat` are always the current time. Which means we can't
|
||||
// use them. So we assume no external program has written to the terminal behind our
|
||||
// back. This makes multiline promptusable. See issue #2859 and
|
||||
// https://github.com/Microsoft/BashOnWindows/issues/545
|
||||
has_working_tty_timestamps = false;
|
||||
#else
|
||||
// This covers preview builds of Windows Subsystem for Linux (WSL).
|
||||
FILE *procsyskosrel;
|
||||
if ((procsyskosrel = wfopen(L"/proc/sys/kernel/osrelease", "r"))) {
|
||||
wcstring osrelease;
|
||||
fgetws2(&osrelease, procsyskosrel);
|
||||
if (osrelease.find(L"3.4.0-Microsoft") != wcstring::npos) {
|
||||
has_working_tty_timestamps = false;
|
||||
}
|
||||
}
|
||||
if (procsyskosrel) {
|
||||
fclose(procsyskosrel);
|
||||
}
|
||||
#endif // OS_IS_MS_WINDOWS
|
||||
}
|
||||
|
||||
void env_init(const struct config_paths_t *paths /* or NULL */) {
|
||||
// These variables can not be altered directly by the user.
|
||||
const wchar_t *const ro_keys[] = {
|
||||
|
@ -585,6 +742,10 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
|
|||
env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL);
|
||||
}
|
||||
|
||||
init_locale();
|
||||
init_curses();
|
||||
init_input();
|
||||
|
||||
// Set up the USER and PATH variables
|
||||
setup_path();
|
||||
setup_user(false);
|
||||
|
@ -647,6 +808,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) {
|
|||
// scope will persist throughout the lifetime of the fish process, and it will ensure that `set
|
||||
// -l` commands run at the command-line don't affect the global scope.
|
||||
env_push(false);
|
||||
env_initialized = true;
|
||||
}
|
||||
|
||||
/// Search all visible scopes in order for the specified key. Return the first scope in which it was
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "common.h"
|
||||
|
||||
extern size_t read_byte_limit;
|
||||
extern bool curses_initialized;
|
||||
|
||||
// Flags that may be passed as the 'mode' in env_set / env_get_string.
|
||||
enum {
|
||||
|
@ -54,6 +55,10 @@ struct config_paths_t {
|
|||
/// Initialize environment variable data.
|
||||
void env_init(const struct config_paths_t *paths = NULL);
|
||||
|
||||
/// Various things we need to initialize at run-time that don't really fit any of the other init
|
||||
/// routines.
|
||||
void misc_init();
|
||||
|
||||
int env_set(const wcstring &key, const wchar_t *val, env_mode_flags_t mode);
|
||||
|
||||
class env_var_t : public wcstring {
|
||||
|
|
44
src/fish.cpp
44
src/fish.cpp
|
@ -44,7 +44,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|||
#include "fish_version.h"
|
||||
#include "function.h"
|
||||
#include "history.h"
|
||||
#include "input.h"
|
||||
#include "io.h"
|
||||
#include "parser.h"
|
||||
#include "path.h"
|
||||
|
@ -318,42 +317,6 @@ static int fish_parse_opt(int argc, char **argv, std::vector<std::string> *cmds)
|
|||
return optind;
|
||||
}
|
||||
|
||||
/// Various things we need to initialize at run-time that don't really fit any of the other init
|
||||
/// routines.
|
||||
static void misc_init() {
|
||||
env_set_read_limit();
|
||||
|
||||
// If stdout is open on a tty ensure stdio is unbuffered. That's because those functions might
|
||||
// be intermixed with `write()` calls and we need to ensure the writes are not reordered. See
|
||||
// issue #3748.
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
fflush(stdout);
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
}
|
||||
|
||||
#ifdef OS_IS_CYGWIN
|
||||
// MS Windows tty devices do not currently have either a read or write timestamp. Those
|
||||
// respective fields of `struct stat` are always the current time. Which means we can't
|
||||
// use them. So we assume no external program has written to the terminal behind our
|
||||
// back. This makes multiline promptusable. See issue #2859 and
|
||||
// https://github.com/Microsoft/BashOnWindows/issues/545
|
||||
has_working_tty_timestamps = false;
|
||||
#else
|
||||
// This covers preview builds of Windows Subsystem for Linux (WSL).
|
||||
FILE *procsyskosrel;
|
||||
if ((procsyskosrel = wfopen(L"/proc/sys/kernel/osrelease", "r"))) {
|
||||
wcstring osrelease;
|
||||
fgetws2(&osrelease, procsyskosrel);
|
||||
if (osrelease.find(L"3.4.0-Microsoft") != wcstring::npos) {
|
||||
has_working_tty_timestamps = false;
|
||||
}
|
||||
}
|
||||
if (procsyskosrel) {
|
||||
fclose(procsyskosrel);
|
||||
}
|
||||
#endif // OS_IS_MS_WINDOWS
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int res = 1;
|
||||
int my_optind = 0;
|
||||
|
@ -389,17 +352,14 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
|
||||
const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
|
||||
|
||||
env_init(&paths);
|
||||
proc_init();
|
||||
event_init();
|
||||
builtin_init();
|
||||
function_init();
|
||||
env_init(&paths);
|
||||
misc_init();
|
||||
reader_init();
|
||||
history_init();
|
||||
// For set_color to support term256 in config.fish (issue #1022).
|
||||
update_fish_color_support();
|
||||
misc_init();
|
||||
|
||||
parser_t &parser = parser_t::principal_parser();
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|||
#include "env.h"
|
||||
#include "fish_version.h"
|
||||
#include "highlight.h"
|
||||
#include "input.h"
|
||||
#include "output.h"
|
||||
#include "parse_constants.h"
|
||||
#include "parse_tree.h"
|
||||
|
@ -347,7 +346,6 @@ int main(int argc, char *argv[]) {
|
|||
// (e.g., "# -*- coding: <encoding-name> -*-").
|
||||
setlocale(LC_ALL, "");
|
||||
env_init();
|
||||
input_init();
|
||||
|
||||
// Types of output we support.
|
||||
enum {
|
||||
|
|
|
@ -294,7 +294,6 @@ static void setup_and_process_keys(bool continuous_mode) {
|
|||
setup_fork_guards();
|
||||
env_init();
|
||||
reader_init();
|
||||
input_init();
|
||||
proc_push_interactive(1);
|
||||
install_our_signal_handlers();
|
||||
|
||||
|
|
|
@ -921,7 +921,7 @@ static void test_utf82wchar(const char *src, size_t slen, const wchar_t *dst, si
|
|||
size = utf8_to_wchar(src, slen, NULL, flags);
|
||||
} else {
|
||||
mem = (wchar_t *)malloc(dlen * sizeof(*mem));
|
||||
if (mem == NULL) {
|
||||
if (!mem) {
|
||||
err(L"u2w: %s: MALLOC FAILED\n", descr);
|
||||
return;
|
||||
}
|
||||
|
@ -969,7 +969,7 @@ static void test_wchar2utf8(const wchar_t *src, size_t slen, const char *dst, si
|
|||
|
||||
if (dst) {
|
||||
mem = (char *)malloc(dlen);
|
||||
if (mem == NULL) {
|
||||
if (!mem) {
|
||||
err(L"w2u: %s: MALLOC FAILED", descr);
|
||||
return;
|
||||
}
|
||||
|
@ -1673,8 +1673,13 @@ struct pager_layout_testcase_t {
|
|||
|
||||
wcstring text = sd.line(0).to_string();
|
||||
if (text != expected) {
|
||||
fwprintf(stderr, L"width %zu got <%ls>, expected <%ls>\n", this->width,
|
||||
text.c_str(), expected.c_str());
|
||||
fwprintf(stderr, L"width %zu got %d<%ls>, expected %d<%ls>\n", this->width,
|
||||
text.length(), text.c_str(), expected.length(), expected.c_str());
|
||||
for (size_t i = 0; i < std::max(text.length(), expected.length()); i++) {
|
||||
fwprintf(stderr, L"i %zu got <%lx> expected <%lx>\n", i,
|
||||
i >= text.length() ? 0xffff : text[i],
|
||||
i >= expected.length() ? 0xffff : expected[i]);
|
||||
}
|
||||
}
|
||||
do_test(text == expected);
|
||||
}
|
||||
|
@ -4178,7 +4183,7 @@ int main(int argc, char **argv) {
|
|||
function_init();
|
||||
builtin_init();
|
||||
env_init();
|
||||
|
||||
misc_init();
|
||||
reader_init();
|
||||
|
||||
// Set default signal handlers, so we can ctrl-C out of this.
|
||||
|
|
129
src/input.cpp
129
src/input.cpp
|
@ -2,19 +2,9 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
#if HAVE_NCURSES_H
|
||||
#include <ncurses.h>
|
||||
#elif HAVE_NCURSES_CURSES_H
|
||||
#include <ncurses/curses.h>
|
||||
#else
|
||||
#include <curses.h>
|
||||
#endif
|
||||
#if HAVE_TERM_H
|
||||
#include <term.h>
|
||||
#elif HAVE_NCURSES_TERM_H
|
||||
|
@ -33,14 +23,12 @@
|
|||
#include "input.h"
|
||||
#include "input_common.h"
|
||||
#include "io.h"
|
||||
#include "output.h"
|
||||
#include "parser.h"
|
||||
#include "proc.h"
|
||||
#include "reader.h"
|
||||
#include "signal.h" // IWYU pragma: keep
|
||||
#include "wutil.h" // IWYU pragma: keep
|
||||
|
||||
#define DEFAULT_TERM L"ansi"
|
||||
#define MAX_INPUT_FUNCTION_ARGS 20
|
||||
|
||||
/// Struct representing a keybinding. Returned by input_get_mappings.
|
||||
|
@ -201,11 +189,11 @@ static std::vector<terminfo_mapping_t> terminfo_mappings;
|
|||
/// List of all terminfo mappings.
|
||||
static std::vector<terminfo_mapping_t> mappings;
|
||||
|
||||
/// Set to one when the input subsytem has been initialized.
|
||||
static bool is_init = false;
|
||||
/// Set to true when the input subsytem has been initialized.
|
||||
bool input_initialized = false;
|
||||
|
||||
/// Initialize terminfo.
|
||||
static void input_terminfo_init();
|
||||
static void init_input_terminfo();
|
||||
|
||||
static wchar_t input_function_args[MAX_INPUT_FUNCTION_ARGS];
|
||||
static bool input_function_status;
|
||||
|
@ -300,87 +288,11 @@ static int interrupt_handler() {
|
|||
return R_NULL;
|
||||
}
|
||||
|
||||
void update_fish_color_support(void) {
|
||||
// Detect or infer term256 support. If fish_term256 is set, we respect it;
|
||||
// otherwise infer it from the TERM variable or use terminfo.
|
||||
env_var_t fish_term256 = env_get_string(L"fish_term256");
|
||||
env_var_t term = env_get_string(L"TERM");
|
||||
bool support_term256 = false; // default to no support
|
||||
if (!fish_term256.missing_or_empty()) {
|
||||
support_term256 = from_string<bool>(fish_term256);
|
||||
debug(2, L"256 color support determined by 'fish_term256'");
|
||||
} else if (term.find(L"256color") != wcstring::npos) {
|
||||
// TERM=*256color*: Explicitly supported.
|
||||
support_term256 = true;
|
||||
debug(2, L"256 color support enabled for '256color' in TERM");
|
||||
} else if (term.find(L"xterm") != wcstring::npos) {
|
||||
// Assume that all xterms are 256, except for OS X SnowLeopard
|
||||
const env_var_t prog = env_get_string(L"TERM_PROGRAM");
|
||||
const env_var_t progver = env_get_string(L"TERM_PROGRAM_VERSION");
|
||||
if (prog == L"Apple_Terminal" && !progver.missing_or_empty()) {
|
||||
// OS X Lion is version 300+, it has 256 color support
|
||||
if (strtod(wcs2str(progver), NULL) > 300) {
|
||||
support_term256 = true;
|
||||
debug(2, L"256 color support enabled for TERM=xterm + modern Terminal.app");
|
||||
}
|
||||
} else {
|
||||
support_term256 = true;
|
||||
debug(2, L"256 color support enabled for TERM=xterm");
|
||||
}
|
||||
} else if (cur_term != NULL) {
|
||||
// See if terminfo happens to identify 256 colors
|
||||
support_term256 = (max_colors >= 256);
|
||||
debug(2, L"256 color support: using %d colors per terminfo", max_colors);
|
||||
} else {
|
||||
debug(2, L"256 color support not enabled (yet)");
|
||||
}
|
||||
|
||||
env_var_t fish_term24bit = env_get_string(L"fish_term24bit");
|
||||
bool support_term24bit;
|
||||
if (!fish_term24bit.missing_or_empty()) {
|
||||
support_term24bit = from_string<bool>(fish_term24bit);
|
||||
debug(2, L"'fish_term24bit' preference: 24-bit color %s",
|
||||
support_term24bit ? L"enabled" : L"disabled");
|
||||
} else {
|
||||
// We don't attempt to infer term24 bit support yet.
|
||||
support_term24bit = false;
|
||||
}
|
||||
|
||||
color_support_t support = (support_term256 ? color_support_term256 : 0) |
|
||||
(support_term24bit ? color_support_term24bit : 0);
|
||||
output_set_color_support(support);
|
||||
}
|
||||
|
||||
int input_init() {
|
||||
if (is_init) return 1;
|
||||
is_init = true;
|
||||
/// Set up arrays used by readch to detect escape sequences for special keys and perform related
|
||||
/// initializations for our input subsystem.
|
||||
void init_input() {
|
||||
input_common_init(&interrupt_handler);
|
||||
|
||||
int err_ret;
|
||||
if (setupterm(NULL, STDOUT_FILENO, &err_ret) == ERR) {
|
||||
debug(0, _(L"Could not set up terminal"));
|
||||
env_var_t term = env_get_string(L"TERM");
|
||||
if (term.missing_or_empty()) {
|
||||
debug(0, _(L"TERM environment variable not set"));
|
||||
} else {
|
||||
debug(0, _(L"TERM environment variable set to '%ls'"), term.c_str());
|
||||
debug(0, _(L"Check that this terminal type is supported on this system"));
|
||||
}
|
||||
|
||||
env_set(L"TERM", DEFAULT_TERM, ENV_GLOBAL | ENV_EXPORT);
|
||||
if (setupterm(NULL, STDOUT_FILENO, &err_ret) == ERR) {
|
||||
debug(0,
|
||||
_(L"Could not set up terminal using the fallback terminal type '%ls' - exiting"),
|
||||
DEFAULT_TERM);
|
||||
exit_without_destructors(1);
|
||||
} else {
|
||||
debug(0, _(L"Using fallback terminal type '%ls'"), DEFAULT_TERM);
|
||||
}
|
||||
fputwc(L'\n', stderr);
|
||||
}
|
||||
|
||||
input_terminfo_init();
|
||||
update_fish_color_support();
|
||||
init_input_terminfo();
|
||||
|
||||
// If we have no keybindings, add a few simple defaults.
|
||||
if (mapping_list.empty()) {
|
||||
|
@ -393,17 +305,14 @@ int input_init() {
|
|||
input_mapping_add(L"\x5", L"bind");
|
||||
}
|
||||
|
||||
return 1;
|
||||
input_initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
void input_destroy() {
|
||||
if (!is_init) return;
|
||||
is_init = false;
|
||||
if (!input_initialized) return;
|
||||
input_initialized = false;
|
||||
input_common_destroy();
|
||||
|
||||
if (del_curterm(cur_term) == ERR) {
|
||||
debug(0, _(L"Error while closing terminfo"));
|
||||
}
|
||||
}
|
||||
|
||||
void input_function_push_arg(wchar_t arg) {
|
||||
|
@ -678,8 +587,10 @@ bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Add all terminfo mappings.
|
||||
static void input_terminfo_init() {
|
||||
/// Add all terminfo mappings and cache other terminfo facts we care about.
|
||||
static void init_input_terminfo() {
|
||||
assert(curses_initialized);
|
||||
if (!cur_term) return; // setupterm() failed so we can't referency any key definitions
|
||||
const terminfo_mapping_t tinfos[] = {
|
||||
TERMINFO_ADD(key_a1),
|
||||
TERMINFO_ADD(key_a3),
|
||||
|
@ -843,13 +754,12 @@ static void input_terminfo_init() {
|
|||
|
||||
bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
assert(input_initialized);
|
||||
CHECK(name, 0);
|
||||
|
||||
const char *res = 0;
|
||||
int err = ENOENT;
|
||||
|
||||
CHECK(name, 0);
|
||||
input_init();
|
||||
|
||||
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
||||
const terminfo_mapping_t &m = terminfo_mappings.at(i);
|
||||
if (!wcscmp(name, m.name)) {
|
||||
|
@ -869,7 +779,7 @@ bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) {
|
|||
}
|
||||
|
||||
bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name) {
|
||||
input_init();
|
||||
assert(input_initialized);
|
||||
|
||||
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
||||
terminfo_mapping_t &m = terminfo_mappings.at(i);
|
||||
|
@ -889,11 +799,10 @@ bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name) {
|
|||
}
|
||||
|
||||
wcstring_list_t input_terminfo_get_names(bool skip_null) {
|
||||
assert(input_initialized);
|
||||
wcstring_list_t result;
|
||||
result.reserve(terminfo_mappings.size());
|
||||
|
||||
input_init();
|
||||
|
||||
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
||||
terminfo_mapping_t &m = terminfo_mappings.at(i);
|
||||
|
||||
|
|
14
src/input.h
14
src/input.h
|
@ -15,11 +15,12 @@
|
|||
|
||||
wcstring describe_char(wint_t c);
|
||||
|
||||
/// Initialize the terminal by calling setupterm, and set up arrays used by readch to detect escape
|
||||
/// sequences for special keys.
|
||||
///
|
||||
/// Before calling input_init, terminfo is not initialized and MUST not be used.
|
||||
int input_init();
|
||||
/// Set to true when the input subsytem has been initialized.
|
||||
extern bool input_initialized;
|
||||
|
||||
/// Set up arrays used by readch to detect escape sequences for special keys and perform related
|
||||
/// initializations for our input subsystem.
|
||||
void init_input();
|
||||
|
||||
/// free up memory used by terminal functions.
|
||||
void input_destroy();
|
||||
|
@ -101,7 +102,4 @@ wchar_t input_function_get_code(const wcstring &name);
|
|||
/// Returns a list of all existing input function names.
|
||||
wcstring_list_t input_function_get_names(void);
|
||||
|
||||
/// Updates our idea of whether we support term256 and term24bit.
|
||||
void update_fish_color_support();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -96,6 +96,7 @@ static bool write_color_escape(char *todo, unsigned char idx, bool is_fg) {
|
|||
}
|
||||
|
||||
static bool write_foreground_color(unsigned char idx) {
|
||||
if (!cur_term) return false;
|
||||
if (set_a_foreground && set_a_foreground[0]) {
|
||||
return write_color_escape(set_a_foreground, idx, true);
|
||||
} else if (set_foreground && set_foreground[0]) {
|
||||
|
@ -105,6 +106,7 @@ static bool write_foreground_color(unsigned char idx) {
|
|||
}
|
||||
|
||||
static bool write_background_color(unsigned char idx) {
|
||||
if (!cur_term) return false;
|
||||
if (set_a_background && set_a_background[0]) {
|
||||
return write_color_escape(set_a_background, idx, false);
|
||||
} else if (set_background && set_background[0]) {
|
||||
|
@ -115,6 +117,7 @@ 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) {
|
||||
if (!cur_term) return false;
|
||||
bool supports_term24bit =
|
||||
static_cast<bool>(output_get_color_support() & color_support_term24bit);
|
||||
if (!supports_term24bit || !color.is_rgb()) {
|
||||
|
@ -168,6 +171,7 @@ void set_color(rgb_color_t c, rgb_color_t c2) {
|
|||
debug(3, "set_color %ls : %ls\n", tmp.c_str(), tmp2.c_str());
|
||||
#endif
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
if (!cur_term) return;
|
||||
|
||||
const rgb_color_t normal = rgb_color_t::normal();
|
||||
static rgb_color_t last_color = rgb_color_t::normal();
|
||||
|
|
|
@ -1539,10 +1539,10 @@ static bool check_for_orphaned_process(unsigned long loop_count, pid_t shell_pgi
|
|||
|
||||
/// Initialize data for interactive use.
|
||||
static void reader_interactive_init() {
|
||||
assert(input_initialized);
|
||||
// See if we are running interactively.
|
||||
pid_t shell_pgid;
|
||||
|
||||
input_init();
|
||||
kill_init();
|
||||
shell_pgid = getpgrp();
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
// IWYU pragma: no_include <cstddef>
|
||||
#include "config.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -552,7 +553,7 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) {
|
|||
// Note that this is required to avoid some visual glitches in iTerm (issue #1448).
|
||||
bool use_multi =
|
||||
multi_str != NULL && multi_str[0] != '\0' && abs(x_steps) * strlen(str) > strlen(multi_str);
|
||||
if (use_multi) {
|
||||
if (use_multi && cur_term) {
|
||||
char *multi_param = tparm(multi_str, abs(x_steps));
|
||||
writembs(multi_param);
|
||||
} else {
|
||||
|
@ -900,7 +901,10 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r
|
|||
}
|
||||
|
||||
/// Returns true if we are using a dumb terminal.
|
||||
static bool is_dumb(void) { return !cursor_up || !cursor_down || !cursor_left || !cursor_right; }
|
||||
static bool is_dumb(void) {
|
||||
if (!cur_term) return true;
|
||||
return !cursor_up || !cursor_down || !cursor_left || !cursor_right;
|
||||
}
|
||||
|
||||
struct screen_layout_t {
|
||||
// The left prompt that we're going to use.
|
||||
|
@ -1232,7 +1236,7 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) {
|
|||
// We do `>` rather than `>=` because the code below might require one extra space.
|
||||
if (screen_width > non_space_width) {
|
||||
bool justgrey = true;
|
||||
if (enter_dim_mode) {
|
||||
if (cur_term && enter_dim_mode) {
|
||||
std::string dim = tparm(enter_dim_mode);
|
||||
if (!dim.empty()) {
|
||||
// Use dim if they have it, so the color will be based on their actual normal
|
||||
|
@ -1241,7 +1245,7 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) {
|
|||
justgrey = false;
|
||||
}
|
||||
}
|
||||
if (justgrey && set_a_foreground) {
|
||||
if (cur_term && justgrey && set_a_foreground) {
|
||||
if (max_colors >= 238) {
|
||||
// draw the string in a particular grey
|
||||
abandon_line_string.append(str2wcstring(tparm(set_a_foreground, 237)));
|
||||
|
@ -1254,15 +1258,18 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) {
|
|||
abandon_line_string.append(str2wcstring(tparm(set_a_foreground, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
abandon_line_string.push_back(omitted_newline_char);
|
||||
|
||||
if (exit_attribute_mode) {
|
||||
if (cur_term && exit_attribute_mode) {
|
||||
abandon_line_string.append(
|
||||
str2wcstring(tparm(exit_attribute_mode))); // normal text ANSI escape sequence
|
||||
}
|
||||
|
||||
int newline_glitch_width = term_has_xn ? 0 : 1;
|
||||
abandon_line_string.append(screen_width - non_space_width - newline_glitch_width, L' ');
|
||||
}
|
||||
|
||||
abandon_line_string.push_back(L'\r');
|
||||
abandon_line_string.push_back(omitted_newline_char);
|
||||
// Now we are certainly on a new line. But we may have dropped the omitted newline char on
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <sys/stat.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
|
|
@ -220,7 +220,7 @@ static void handle_hup(int sig, siginfo_t *info, void *context) {
|
|||
}
|
||||
|
||||
/// Handle sigterm. The only thing we do is restore the front process ID, then die.
|
||||
static void handle_term(int sig, siginfo_t *info, void *context) {
|
||||
static void handle_sigterm(int sig, siginfo_t *info, void *context) {
|
||||
UNUSED(sig);
|
||||
UNUSED(info);
|
||||
UNUSED(context);
|
||||
|
@ -286,7 +286,7 @@ static void set_interactive_handlers() {
|
|||
sigaction(SIGINT, &act, 0);
|
||||
|
||||
// SIGTERM restores the terminal controlling process before dying.
|
||||
act.sa_sigaction = &handle_term;
|
||||
act.sa_sigaction = &handle_sigterm;
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGTERM, &act, 0);
|
||||
|
||||
|
|
Loading…
Reference in a new issue