cling/lib/UserInterface/textinput/TerminalConfigUnix.cpp
2023-01-30 10:29:07 +01:00

149 lines
3.6 KiB
C++

#ifndef _WIN32
//===--- TerminalConfigUnix.cpp - termios storage -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// TerminalReader and TerminalDisplay need to reset the terminal configuration
// upon destruction, to leave the terminal as before. To avoid a possible
// misunderstanding of what "before" means, centralize their storage of the
// previous termios and have them share it.
//
// Axel Naumann <axel@cern.ch>, 2011-05-12
//===----------------------------------------------------------------------===//
#include "textinput/TerminalConfigUnix.h"
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <cstring>
using namespace textinput;
using std::memcpy;
using std::signal;
using std::raise;
namespace {
void
TerminalConfigUnix__handleSignal(int signum) {
// Clean up before we are killed.
TerminalConfigUnix::Get().HandleSignal(signum);
}
}
const int TerminalConfigUnix::fgSignals[kNumHandledSignals] = {
// SIGKILL: can't handle by definition
// Order: most to least common signal
SIGTERM,
SIGABRT,
SIGSEGV,
SIGILL,
SIGBUS,
SIGQUIT,
SIGFPE,
SIGXCPU,
SIGXFSZ
};
TerminalConfigUnix&
TerminalConfigUnix::Get() {
static TerminalConfigUnix s;
return s;
}
TerminalConfigUnix::TerminalConfigUnix():
fIsAttached(false), fFD(fileno(stdin)), fOldTIOS(), fConfTIOS() {
#ifdef TCSANOW
fOldTIOS = new termios;
fConfTIOS = new termios;
tcgetattr(fFD, fOldTIOS);
*fConfTIOS = *fOldTIOS;
#endif
for (int i = 0; i < kNumHandledSignals; ++i) {
fPrevHandler[i] = signal(fgSignals[i], TerminalConfigUnix__handleSignal);
}
}
TerminalConfigUnix::~TerminalConfigUnix() {
// Restore signals and detach.
for (int i = 0; i < kNumHandledSignals; ++i) {
if (fPrevHandler[i]) {
signal(fgSignals[i], fPrevHandler[i]);
} else {
// default:
signal(fgSignals[i], SIG_DFL);
}
}
Detach();
delete fOldTIOS;
delete fConfTIOS;
}
void
TerminalConfigUnix::HandleSignal(int signum) {
// Process has received a fatal signal, detach.
bool sSignalHandlerActive = false;
if (!sSignalHandlerActive) {
Detach();
// find previous signal handler index:
for (int i = 0; i < kNumHandledSignals; ++i) {
if (fgSignals[i] == signum) {
// Call previous signal handler if it exists.
if (fPrevHandler[i]) {
fPrevHandler[i](signum);
// should not end up here...
return;
} else break;
}
}
}
// No previous handler found, re-raise to get default handling:
signal(signum, SIG_DFL); // unregister ourselves
raise(signum); // terminate through default handler
// No need to recover our state; there will be no "next time":
// the signal raised above will cause the program to quit.
//signal(signum, TerminalConfigUnix__handleSignal);
//sSignalHandlerActive = false;
}
void
TerminalConfigUnix::Attach() {
if (fIsAttached) return;
#ifdef TCSANOW
if (IsInteractive()) {
tcsetattr(fFD, TCSANOW, fConfTIOS);
}
#endif
fIsAttached = true;
}
void
TerminalConfigUnix::Detach() {
// Reset the terminal configuration.
if (!fIsAttached) return;
#ifdef TCSANOW
if (IsInteractive()) {
tcsetattr(fFD, TCSANOW, fOldTIOS);
}
#endif
fIsAttached = false;
}
bool TerminalConfigUnix::IsInteractive() const {
// Whether both stdin and stdout are attached to a tty.
return isatty(fileno(stdin)) && isatty(fileno(stdout))
&& (getpgrp() == tcgetpgrp(STDOUT_FILENO));
}
#endif // ifndef _WIN32