1
0
Fork 0

added better readline support for control-C

This commit is contained in:
Frank Celler 2015-08-11 12:05:55 +02:00
parent 29b03e854d
commit 3029891405
19 changed files with 584 additions and 315 deletions

View File

@ -1,6 +1,8 @@
v2.7.0 (XXXX-XX-XX)
-------------------
* added better control-C support in arangosh
* increased default value collection-specific `indexBuckets` value from 1 to 16
Collections created from 2.7 on will use the new default if not overriden on

View File

@ -711,6 +711,10 @@ void ArangoServer::buildApplicationServer () {
// disable certain options in unittest or script mode
OperationMode::server_operation_mode_e mode = OperationMode::determineMode(_applicationServer->programOptions());
if (mode == OperationMode::MODE_CONSOLE) {
_applicationScheduler->disableControlCHandler();
}
if (mode == OperationMode::MODE_SCRIPT || mode == OperationMode::MODE_UNITTESTS) {
// testing disables authentication
_disableAuthentication = true;

View File

@ -71,8 +71,8 @@ ConsoleThread::ConsoleThread (ApplicationServer* applicationServer,
_vocbase(vocbase),
_done(0),
_userAborted(false) {
allowAsynchronousCancelation();
allowAsynchronousCancelation();
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -184,12 +184,6 @@ static vector<string> JsLint;
static uint64_t GcInterval = 10;
////////////////////////////////////////////////////////////////////////////////
/// @brief console object
////////////////////////////////////////////////////////////////////////////////
static triagens::V8LineEditor* Console = nullptr;
////////////////////////////////////////////////////////////////////////////////
/// @brief voice mode
////////////////////////////////////////////////////////////////////////////////
@ -1503,26 +1497,6 @@ static std::string BuildPrompt () {
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief signal handler for CTRL-C
////////////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
// TODO
#else
static void SignalHandler (int signal) {
if (Console != nullptr) {
Console->close();
Console = nullptr;
}
printf("\n");
TRI_EXIT_FUNCTION(EXIT_SUCCESS, nullptr);
}
#endif
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the shell
////////////////////////////////////////////////////////////////////////////////
@ -1531,25 +1505,8 @@ static void RunShell (v8::Isolate* isolate, v8::Handle<v8::Context> context, boo
v8::Context::Scope contextScope(context);
v8::Local<v8::String> name(TRI_V8_ASCII_STRING(TRI_V8_SHELL_COMMAND_NAME));
Console = new triagens::V8LineEditor(context, ".arangosh.history");
Console->open(BaseClient.autoComplete());
// install signal handler for CTRL-C
#ifdef _WIN32
// TODO
#else
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = &SignalHandler;
int res = sigaction(SIGINT, &sa, 0);
if (res != 0) {
LOG_ERROR("unable to install signal handler");
}
#endif
triagens::V8LineEditor console(context, ".arangosh.history");
console.open(BaseClient.autoComplete());
uint64_t nrCommands = 0;
@ -1573,17 +1530,7 @@ static void RunShell (v8::Isolate* isolate, v8::Handle<v8::Context> context, boo
string badPrompt;
#ifdef __APPLE__
// ........................................................................................
// MacOS uses libedit, which does not support ignoring of non-printable characters in the prompt
// using non-printable characters in the prompt will lead to wrong prompt lengths being calculated
// we will therefore disable colorful prompts for MacOS.
// ........................................................................................
goodPrompt = badPrompt = dynamicPrompt;
#elif _WIN32
#if _WIN32
// ........................................................................................
// Windows console is not coloured by escape sequences. So the method given below will not
@ -1637,7 +1584,7 @@ static void RunShell (v8::Isolate* isolate, v8::Handle<v8::Context> context, boo
}
#endif
char* input = Console->prompt(promptError ? badPrompt.c_str() : goodPrompt.c_str());
char* input = console.prompt(promptError ? badPrompt.c_str() : goodPrompt.c_str());
if (input == nullptr) {
break;
@ -1666,7 +1613,7 @@ static void RunShell (v8::Isolate* isolate, v8::Handle<v8::Context> context, boo
}
}
Console->addHistory(input);
console.addHistory(input);
v8::TryCatch tryCatch;
@ -1712,10 +1659,6 @@ static void RunShell (v8::Isolate* isolate, v8::Handle<v8::Context> context, boo
}
#endif
Console->close();
delete Console;
Console = nullptr;
BaseClient.printLine("");
BaseClient.printByeBye();

View File

@ -206,7 +206,165 @@ dnl ----------------------------------------------------------------------------
dnl READLINE
dnl ----------------------------------------------------------------------------
m4_include([m4/external.readline])
AC_MSG_NOTICE([--------------------------------------------------------------------------------])
AC_MSG_NOTICE([CHECKING FOR READLINE])
AC_MSG_NOTICE([--------------------------------------------------------------------------------])
AC_LANG(C)
AC_ARG_ENABLE(readline,
AS_HELP_STRING([--enable-readline], [enable readline support (default: yes)]),
tr_READLINE="$enableval",
tr_READLINE="maybe"
)
if test "x$tr_DARWIN" = xyes; then
READLINE=/usr/local/opt/readline
READLINE_CPPFLAGS="-I$READLINE/include"
READLINE_LDFLAGS="-L$READLINE/lib"
fi
AC_ARG_WITH(readline,
AS_HELP_STRING([--with-readline=DIR], [where the readline library and includes are located]),
[READLINE_CPPFLAGS="-I$withval/include"
READLINE_LDFLAGS="-L$withval/lib"
READLINE="$withval"]
)
AC_ARG_WITH(readline-lib,
AS_HELP_STRING([--with-readline-lib=DIR], [where the readline library is located]),
[READLINE_LDFLAGS="-L$withval"]
)
dnl checks for the READLINE library
if test "x$READLINE_CPPFLAGS" != x; then
TR_INCLUDE([READLINE_CPPFLAGS])
fi
dnl save flags
SAVE_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $READLINE_CPPFLAGS"
SAVE_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS $READLINE_LDFLAGS"
SAVE_LIBS="$LIBS"
dnl check for header and library
if test "x$tr_READLINE" = xyes -o "x$tr_READLINE" = xmaybe; then
ch_READLINE="$tr_READLINE"
AC_CHECK_HEADERS(readline/readline.h, [tr_READLINE="yes"], [tr_READLINE="no"])
if test "x$tr_READLINE" = xyes; then
AC_CHECK_LIB([readline], [readline], [READLINE_LIBS="-lreadline" tr_READLINE="yes"], [tr_READLINE="no"])
fi
AC_MSG_CHECKING([READLINE support])
if test "x$tr_READLINE" != xyes; then
AC_MSG_RESULT([not found])
if test "x$ch_READLINE" = xyes; then
AC_MSG_ERROR([Please install readline support])
fi
else
AC_MSG_RESULT([readline])
fi
else
AC_MSG_CHECKING([READLINE support])
AC_MSG_RESULT([disabled])
fi
dnl grep readline version number
if test "x$tr_READLINE" = xyes; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <readline/readline.h>
main () {
#if defined(RL_VERSION_MAJOR) && defined(RL_VERSION_MINOR)
long sdnhg36ed = RL_VERSION_MAJOR RL_VERSION_MINOR ;
#else
long sdnhg36ed = RL_READLINE_VERSION hex ;
#endif
}
_ACEOF
AC_MSG_CHECKING([READLINE version])
eval "$ac_cpp conftest.$ac_ext" | fgrep "long sdnhg36ed" | awk '{print $4 "." $5}' > conftest.output
READLINE_VERSION=`cat conftest.output`
if test -z "$READLINE_VERSION"; then
AC_MSG_ERROR([Readline support is not working. Please re-install readline support])
fi
AC_MSG_RESULT([$READLINE_VERSION])
rm -f conftest*
if test "x$tr_DARWIN" = xyes; then
case "$READLINE_VERSION" in
6.*|5.*)
;;
*)
AC_MSG_ERROR([Please install readline 6 from brew. If you have a non-standard brew location, use "--with-readline".])
;;
esac
fi
fi
dnl restore flags and set readline flags
LIBS="$SAVE_LIBS"
LDFLAGS="$SAVE_LDFLAGS"
CPPFLAGS="$SAVE_CPPFLAGS"
if test "x$tr_READLINE" = xyes; then
CPPFLAGS="$CPPFLAGS -DHAVE_READLINE=1"
READLINE_CPPFLAGS="${READLINE_CPPFLAGS} -DTRI_READLINE_VERSION='\"${READLINE_VERSION}\"'"
fi
AM_CONDITIONAL(ENABLE_READLINE, test "x$tr_READLINE" = xyes)
if test "x$tr_READLINE" = xyes; then
AC_DEFINE_UNQUOTED(TRI_HAVE_READLINE, 1, [true if readline is used])
fi
dnl add substitutions
AC_SUBST(READLINE_VERSION)
AC_SUBST(READLINE_CPPFLAGS)
AC_SUBST(READLINE_LDFLAGS)
AC_SUBST(READLINE_LIBS)
dnl informational output
if test "x$tr_READLINE" = xyes; then
LIB_INFO="$LIB_INFO|READLINE VERSION: ${READLINE_VERSION}"
LIB_INFO="$LIB_INFO|READLINE_CPPFLAGS: ${READLINE_CPPFLAGS}"
LIB_INFO="$LIB_INFO|READLINE_LDLIBS: ${READLINE_LDLIBS}"
LIB_INFO="$LIB_INFO|READLINE_LIBS: ${READLINE_LIBS}"
elif test "x$tr_READLINE" = xlinenoise; then
LIB_INFO="$LIB_INFO|LINENOISE VERSION: ${READLINE_VERSION}"
LIB_INFO="$LIB_INFO|LINENOISE_CPPFLAGS: ${READLINE_CPPFLAGS}"
LIB_INFO="$LIB_INFO|LINENOISE_LDLIBS: ${READLINE_LDLIBS}"
LIB_INFO="$LIB_INFO|LINENOISE_LIBS: ${READLINE_LIBS}"
else
LIB_INFO="$LIB_INFO|READLINE VERSION: disabled"
fi
LIB_INFO="$LIB_INFO|."
dnl ----------------------------------------------------------------------------
dnl OPENSSL

View File

@ -104,20 +104,11 @@ if ENABLE_READLINE
lib_libarango_a_SOURCES += \
lib/Utilities/ReadlineShell.cpp
else
if ENABLE_LINENOISE
lib_libarango_a_SOURCES += \
lib/Utilities/LinenoiseShell.cpp \
3rdParty/linenoise/linenoise.c \
3rdParty/linenoise/utf8.c
else
lib_libarango_a_SOURCES += \
lib/Utilities/DummyShell.cpp
endif
endif
################################################################################
### @brief library "libarango.a", client part

View File

@ -341,7 +341,8 @@ ApplicationScheduler::ApplicationScheduler (ApplicationServer* applicationServer
_multiSchedulerAllowed(true),
_nrSchedulerThreads(4),
_backend(0),
_descriptorMinimum(256) {
_descriptorMinimum(256),
_disableControlCHandler(false) {
}
////////////////////////////////////////////////////////////////////////////////
@ -416,6 +417,14 @@ void ApplicationScheduler::setProcessorAffinity (const vector<size_t>& cores) {
#endif
}
////////////////////////////////////////////////////////////////////////////////
/// @brief disables CTRL-C handling (because taken over by console input)
////////////////////////////////////////////////////////////////////////////////
void ApplicationScheduler::disableControlCHandler () {
_disableControlCHandler = true;
}
// -----------------------------------------------------------------------------
// --SECTION-- ApplicationFeature methods
// -----------------------------------------------------------------------------
@ -557,9 +566,7 @@ void ApplicationScheduler::stop () {
static size_t const MAX_TRIES = 10;
// remove all helper tasks
for (vector<Task*>::iterator i = _tasks.begin(); i != _tasks.end(); ++i) {
Task* task = *i;
for (auto& task : _tasks) {
_scheduler->destroyTask(task);
}
@ -610,7 +617,7 @@ void ApplicationScheduler::buildSchedulerReporter () {
Task* reporter = new SchedulerReporterTask(_scheduler, _reportInterval);
_scheduler->registerTask(reporter);
_tasks.push_back(reporter);
_tasks.emplace_back(reporter);
}
}
@ -623,23 +630,25 @@ void ApplicationScheduler::buildControlCHandler () {
LOG_FATAL_AND_EXIT("no scheduler is known, cannot create control-c handler");
}
if (! _disableControlCHandler) {
// control C handler
Task* controlC = new ControlCTask(_applicationServer);
_scheduler->registerTask(controlC);
_tasks.push_back(controlC);
_tasks.emplace_back(controlC);
}
// hangup handler
Task* hangup = new HangupTask();
_scheduler->registerTask(hangup);
_tasks.push_back(hangup);
_tasks.emplace_back(hangup);
// sigusr handler
Task* sigusr = new Sigusr1Task(this);
_scheduler->registerTask(sigusr);
_tasks.push_back(sigusr);
_tasks.emplace_back(sigusr);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -112,6 +112,12 @@ namespace triagens {
void setProcessorAffinity (const std::vector<size_t>& cores);
////////////////////////////////////////////////////////////////////////////////
/// @brief disables CTRL-C handling (because taken over by console input)
////////////////////////////////////////////////////////////////////////////////
void disableControlCHandler ();
// -----------------------------------------------------------------------------
// --SECTION-- ApplicationFeature methods
// -----------------------------------------------------------------------------
@ -257,6 +263,12 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
uint32_t _descriptorMinimum;
////////////////////////////////////////////////////////////////////////////////
/// @brief disables CTRL-C handling (because taken over by console input)
////////////////////////////////////////////////////////////////////////////////
bool _disableControlCHandler;
};
}
}

View File

@ -28,10 +28,8 @@
////////////////////////////////////////////////////////////////////////////////
#include "LineEditor.h"
#include "ShellImplementation.h"
#include "Completer.h"
#include "Basics/tri-strings.h"
#include "Utilities/ShellImplementation.h"
#include "Utilities/Completer.h"
using namespace std;
using namespace triagens;
@ -50,7 +48,7 @@ using namespace triagens;
LineEditor::LineEditor (std::string const& history)
: _history(history) {
_shellImpl = 0;
_shellImpl = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
@ -121,6 +119,14 @@ bool LineEditor::writeHistory () {
return _shellImpl->writeHistory();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief send a signal to the shell implementation
////////////////////////////////////////////////////////////////////////////////
void LineEditor::signal () {
_shellImpl->signal();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief sort the alternatives results vector
////////////////////////////////////////////////////////////////////////////////

View File

@ -41,6 +41,7 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief line editor
////////////////////////////////////////////////////////////////////////////////
namespace triagens {
class ShellImplementation;
@ -48,9 +49,9 @@ namespace triagens {
LineEditor(LineEditor const&);
LineEditor& operator= (LineEditor const&);
// -----------------------------------------------------------------------------
// --SECTION-- public constants
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- public constants
// -----------------------------------------------------------------------------
public:
@ -60,9 +61,9 @@ namespace triagens {
static const int MAX_HISTORY_ENTRIES = 1000;
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
public:
@ -78,9 +79,9 @@ namespace triagens {
virtual ~LineEditor ();
// -----------------------------------------------------------------------------
// --SECTION-- public methods
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- public methods
// -----------------------------------------------------------------------------
public:
@ -123,19 +124,21 @@ namespace triagens {
bool writeHistory ();
////////////////////////////////////////////////////////////////////////////////
/// @brief send a signal to the shell implementation
////////////////////////////////////////////////////////////////////////////////
void signal ();
////////////////////////////////////////////////////////////////////////////////
/// @brief sort the alternatives results vector
////////////////////////////////////////////////////////////////////////////////
static void sortAlternatives (std::vector<std::string>&);
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- protected methods
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- protected methods
// -----------------------------------------------------------------------------
protected:
@ -151,15 +154,16 @@ namespace triagens {
virtual void initializeShell () = 0;
// -----------------------------------------------------------------------------
// --SECTION-- protected variables
// -----------------------------------------------------------------------------
protected:
// -----------------------------------------------------------------------------
// --SECTION-- protected variables
// -----------------------------------------------------------------------------
ShellImplementation * _shellImpl;
ShellImplementation* _shellImpl;
std::string _history;
};
}
#endif

View File

@ -27,19 +27,13 @@
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include "ReadlineShell.h"
#include "Basics/tri-strings.h"
#include "Utilities/Completer.h"
#include "Utilities/LineEditor.h"
#include <iostream>
#include <readline/readline.h>
#include <readline/history.h>
#include "Basics/tri-strings.h"
#include <vector>
#include <v8.h>
using namespace std;
@ -97,17 +91,76 @@ namespace {
// --SECTION-- class ReadlineShell
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief callback function that readline calls periodically while waiting
/// for input and being idle
////////////////////////////////////////////////////////////////////////////////
static int ReadlineIdle () {
auto instance = ReadlineShell::instance();
if (instance != nullptr &&
instance->getLoopState() == 2) {
rl_done = 1;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief callback function that readline calls when the input is completed
////////////////////////////////////////////////////////////////////////////////
static void ReadlineInputCompleted (char* value) {
// if we don't clear the prompt here, readline will display it instantly
// the user pressed the return key. this is not desired because when we
// wait for input afterwards, readline will display the prompt again
rl_set_prompt("");
auto instance = ReadlineShell::instance();
if (instance == nullptr) {
return;
}
if (instance->getLoopState() == 2) {
// CTRL-C received
rl_done = 1;
// replace current input with nothing
rl_replace_line("", 0);
if (value != nullptr) {
// avoid memleak
TRI_SystemFree(value);
}
instance->setLastInput(nullptr);
}
else {
instance->setLoopState(1);
instance->setLastInput(value);
}
}
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief (sole) instance of a ReadlineShell
////////////////////////////////////////////////////////////////////////////////
std::atomic<ReadlineShell*> ReadlineShell::_instance(nullptr);
////////////////////////////////////////////////////////////////////////////////
/// @brief constructs a new editor
////////////////////////////////////////////////////////////////////////////////
ReadlineShell::ReadlineShell (std::string const& history,
Completer* completer)
: ShellImplementation(history, completer) {
: ShellImplementation(history, completer),
_loopState(0),
_lastInput(nullptr),
_lastInputWasEmpty(false) {
COMPLETER = completer;
@ -116,11 +169,19 @@ ReadlineShell::ReadlineShell (std::string const& history,
rl_attempted_completion_function = AttemptedCompletion;
rl_completer_word_break_characters = WordBreakCharacters;
#ifndef __APPLE__
rl_catch_signals = 0;
#endif
// register ourselves
TRI_ASSERT(_instance == nullptr);
_instance = this;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////
ReadlineShell::~ReadlineShell () {
// unregister ourselves
_instance = nullptr;
}
// -----------------------------------------------------------------------------
// --SECTION-- public methods
@ -188,11 +249,6 @@ bool ReadlineShell::close () {
}
#endif
#ifndef __APPLE__
// reset state of the terminal to what it was before readline()
rl_cleanup_after_signal();
#endif
return res;
}
@ -252,8 +308,46 @@ bool ReadlineShell::writeHistory () {
return (write_history(historyPath().c_str()) == 0);
}
char * ReadlineShell::getLine (char const * input) {
return readline(input);
////////////////////////////////////////////////////////////////////////////////
/// @brief read a line from the input
////////////////////////////////////////////////////////////////////////////////
char* ReadlineShell::getLine (char const* prompt) {
setLoopState(0);
rl_event_hook = ReadlineIdle;
rl_callback_handler_install(prompt, ReadlineInputCompleted);
int state;
do {
rl_callback_read_char();
state = getLoopState();
}
while (state == 0);
rl_callback_handler_remove();
if (state == 2) {
if (_lastInputWasEmpty) {
setLastInput(nullptr);
}
else {
setLastInput(strdup(""));
_lastInputWasEmpty = true;
}
}
else {
_lastInputWasEmpty = false;
}
return _lastInput;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief handle a signal
////////////////////////////////////////////////////////////////////////////////
void ReadlineShell::signal () {
// set the global state, so the readline input loop can react on it
setLoopState(2);
}
// -----------------------------------------------------------------------------

View File

@ -31,9 +31,8 @@
#define ARANGODB_UTILITIES_READLINE_SHELL_H 1
#include "Basics/Common.h"
#include "ShellImplementation.h"
#include "Completer.h"
#include "Utilities/Completer.h"
#include "Utilities/ShellImplementation.h"
namespace triagens {
@ -44,22 +43,29 @@ namespace triagens {
public:
////////////////////////////////////////////////////////////////////////////////
/// public constructor
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////
ReadlineShell (std::string const& history, Completer *);
ReadlineShell (std::string const& history,
Completer*);
////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
////////////////////////////////////////////////////////////////////////////////
~ReadlineShell ();
////////////////////////////////////////////////////////////////////////////////
/// @brief line editor open
////////////////////////////////////////////////////////////////////////////////
virtual bool open (bool autoComplete);
bool open (bool autoComplete) override final;
////////////////////////////////////////////////////////////////////////////////
/// @brief line editor shutdown
////////////////////////////////////////////////////////////////////////////////
virtual bool close ();
bool close () override final;
////////////////////////////////////////////////////////////////////////////////
/// @brief get the history file path
@ -68,26 +74,94 @@ namespace triagens {
/// the local file _historyFilename.
////////////////////////////////////////////////////////////////////////////////
virtual std::string historyPath ();
std::string historyPath () override final;
////////////////////////////////////////////////////////////////////////////////
/// @brief add to history
////////////////////////////////////////////////////////////////////////////////
virtual void addHistory (char const*);
void addHistory (char const*) override final;
////////////////////////////////////////////////////////////////////////////////
/// @brief save the history
////////////////////////////////////////////////////////////////////////////////
virtual bool writeHistory ();
bool writeHistory () override final;
////////////////////////////////////////////////////////////////////////////////
/// @brief todo!!
/// @brief read a line from the input
////////////////////////////////////////////////////////////////////////////////
virtual char * getLine (char const *);
char* getLine (char const*) override final;
////////////////////////////////////////////////////////////////////////////////
/// @brief handle a signal
////////////////////////////////////////////////////////////////////////////////
void signal () override final;
////////////////////////////////////////////////////////////////////////////////
/// @brief set the last input value
////////////////////////////////////////////////////////////////////////////////
void setLastInput (char* input) {
_lastInput = input;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the current input loop state
////////////////////////////////////////////////////////////////////////////////
int getLoopState () const {
return _loopState;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the current input loop state
////////////////////////////////////////////////////////////////////////////////
void setLoopState (int state) {
_loopState = state;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the currently active shell instance
////////////////////////////////////////////////////////////////////////////////
static ReadlineShell* instance () {
return _instance.load();
}
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
private:
////////////////////////////////////////////////////////////////////////////////
/// @brief current state of input loop (may be affected by out-of-band signals)
////////////////////////////////////////////////////////////////////////////////
std::atomic<int> _loopState;
////////////////////////////////////////////////////////////////////////////////
/// @brief last value entered by user. memory is allocated by readline
/// and must be freed using TRI_SystemFree()
////////////////////////////////////////////////////////////////////////////////
char* _lastInput;
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not the input from the previous invocation was a CTRL-C
////////////////////////////////////////////////////////////////////////////////
bool _lastInputWasEmpty;
////////////////////////////////////////////////////////////////////////////////
/// @brief system-wide instance of the ReadlineShell
////////////////////////////////////////////////////////////////////////////////
static std::atomic<ReadlineShell*> _instance;
};
}

View File

@ -41,13 +41,11 @@
#endif
using namespace triagens;
using namespace std;
ShellImplementation* ShellImplFactory::buildShell (string const & history,
ShellImplementation* ShellImplFactory::buildShell (std::string const& history,
Completer* completer) {
#ifdef _WIN32
//under windows the readline is not compilable
// under Windows the readline is not compilable
return new LinenoiseShell(history, completer);
#elif defined TRI_HAVE_LINENOISE
return new LinenoiseShell(history, completer);
@ -57,7 +55,19 @@ ShellImplementation* ShellImplFactory::buildShell (string const & history,
// last resort!
return new DummyShell(history, completer);
#endif
}
bool ShellImplFactory::hasCtrlCHandler () {
#ifdef _WIN32
// under Windows the readline is not compilable
return false;
#elif defined TRI_HAVE_LINENOISE
return false;
#elif defined TRI_HAVE_READLINE
return true;
#else
return false;
#endif
}
// -----------------------------------------------------------------------------

View File

@ -43,10 +43,16 @@ namespace triagens {
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief check if line is complete
/// @brief creates a shell
////////////////////////////////////////////////////////////////////////////////
ShellImplementation * buildShell (std::string const & history, Completer *);
static ShellImplementation* buildShell (std::string const& history, Completer*);
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not the shell will have a CTRL-C handler
////////////////////////////////////////////////////////////////////////////////
static bool hasCtrlCHandler ();
};
}

View File

@ -147,6 +147,14 @@ char* ShellImplementation::prompt (char const* the_prompt) {
return line;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief handle a signal
////////////////////////////////////////////////////////////////////////////////
void ShellImplementation::signal () {
// do nothing special
}
}
// -----------------------------------------------------------------------------

View File

@ -106,6 +106,12 @@ namespace triagens {
virtual char* getLine (const char*) = 0;
////////////////////////////////////////////////////////////////////////////////
/// @brief handle a signal
////////////////////////////////////////////////////////////////////////////////
virtual void signal ();
// -----------------------------------------------------------------------------
// --SECTION-- protected variables
// -----------------------------------------------------------------------------

View File

@ -28,16 +28,34 @@
////////////////////////////////////////////////////////////////////////////////
#include "V8LineEditor.h"
#include "Utilities/ShellImplFactory.h"
#include "Basics/tri-strings.h"
#include "V8/v8-utils.h"
#include "Basics/logging.h"
#include "Basics/StringUtils.h"
#include "Basics/tri-strings.h"
#include "Utilities/ShellImplFactory.h"
#include "V8/v8-utils.h"
using namespace std;
using namespace triagens;
// -----------------------------------------------------------------------------
// --SECTION-- helper functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief signal handler for CTRL-C
////////////////////////////////////////////////////////////////////////////////
#ifndef _WIN32
static void SignalHandler (int signal) {
// get the instance of the console
auto instance = triagens::V8LineEditor::instance();
if (instance != nullptr) {
instance->signal();
}
}
#endif
// -----------------------------------------------------------------------------
// --SECTION-- class V8Completer
// -----------------------------------------------------------------------------
@ -306,6 +324,8 @@ void V8Completer::getAlternatives (char const * text,
// --SECTION-- class V8LineEditor
// -----------------------------------------------------------------------------
std::atomic<V8LineEditor*> V8LineEditor::_instance(nullptr);
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
@ -314,8 +334,18 @@ void V8Completer::getAlternatives (char const * text,
/// @brief constructs a new editor
////////////////////////////////////////////////////////////////////////////////
V8LineEditor::V8LineEditor (v8::Handle<v8::Context> context, std::string const& history)
: LineEditor(history), _context(context), _completer(V8Completer()) {
V8LineEditor::V8LineEditor (v8::Handle<v8::Context> context,
std::string const& history)
: LineEditor(history),
_context(context),
_completer(V8Completer()) {
// register global instance
TRI_ASSERT(_instance.load() == nullptr);
_instance.store(this);
setupCtrlCHandler();
}
////////////////////////////////////////////////////////////////////////////////
@ -323,7 +353,30 @@ V8LineEditor::V8LineEditor (v8::Handle<v8::Context> context, std::string const&
////////////////////////////////////////////////////////////////////////////////
V8LineEditor::~V8LineEditor () {
// nothing
// unregister global instance
TRI_ASSERT(_instance.load() != nullptr);
_instance.store(nullptr);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief setup a signal handler for CTRL-C
////////////////////////////////////////////////////////////////////////////////
void V8LineEditor::setupCtrlCHandler () {
#ifndef _WIN32
if (ShellImplFactory::hasCtrlCHandler()) {
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = &SignalHandler;
int res = sigaction(SIGINT, &sa, 0);
if (res != 0) {
LOG_ERROR("unable to install signal handler");
}
}
#endif
}
// -----------------------------------------------------------------------------
@ -331,8 +384,7 @@ V8LineEditor::~V8LineEditor () {
// -----------------------------------------------------------------------------
void V8LineEditor::initializeShell () {
ShellImplFactory factory;
_shellImpl = factory.buildShell(_history, &_completer);
_shellImpl = ShellImplFactory::buildShell(_history, &_completer);
}
// -----------------------------------------------------------------------------

View File

@ -103,7 +103,8 @@ namespace triagens {
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////
V8LineEditor (v8::Handle<v8::Context>, std::string const& history);
V8LineEditor (v8::Handle<v8::Context>,
std::string const& history);
////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
@ -111,6 +112,14 @@ namespace triagens {
~V8LineEditor ();
////////////////////////////////////////////////////////////////////////////////
/// @brief the global instance of the editor
////////////////////////////////////////////////////////////////////////////////
static V8LineEditor* instance () {
return _instance.load();
}
// -----------------------------------------------------------------------------
// --SECTION-- protected methods
// -----------------------------------------------------------------------------
@ -123,6 +132,12 @@ namespace triagens {
void initializeShell () override;
////////////////////////////////////////////////////////////////////////////////
/// @brief setup a signal handler for CTRL-C
////////////////////////////////////////////////////////////////////////////////
static void setupCtrlCHandler ();
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
@ -135,7 +150,17 @@ namespace triagens {
v8::Handle<v8::Context> _context;
////////////////////////////////////////////////////////////////////////////////
/// @brief the completer
////////////////////////////////////////////////////////////////////////////////
V8Completer _completer;
////////////////////////////////////////////////////////////////////////////////
/// @brief the active instance of the editor
////////////////////////////////////////////////////////////////////////////////
static std::atomic<V8LineEditor*> _instance;
};
}

View File

@ -37,161 +37,26 @@ AC_ARG_WITH(readline-lib,
)
dnl ----------------------------------------------------------------------------
dnl checks for the READLINE library
dnl ----------------------------------------------------------------------------
if test "x$READLINE_CPPFLAGS" != x; then
TR_INCLUDE([READLINE_CPPFLAGS])
fi
dnl ----------------------------------------------------------------------------
dnl save flags
dnl ----------------------------------------------------------------------------
SAVE_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $READLINE_CPPFLAGS"
SAVE_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS $READLINE_LDFLAGS"
SAVE_LIBS="$LIBS"
dnl ----------------------------------------------------------------------------
dnl check for header and library
dnl ----------------------------------------------------------------------------
if test "x$tr_READLINE" = xyes -o "x$tr_READLINE" = xmaybe; then
ch_READLINE="$tr_READLINE"
AC_CHECK_HEADERS(readline/readline.h, [tr_READLINE="yes"], [tr_READLINE="no"])
if test "x$tr_READLINE" = xyes; then
AC_CHECK_LIB([readline], [readline], [READLINE_LIBS="-lreadline" tr_READLINE="yes"], [tr_READLINE="no"])
fi
AC_MSG_CHECKING([READLINE support])
if test "x$tr_READLINE" != xyes; then
AC_MSG_RESULT([not found])
if test "x$ch_READLINE" = xyes; then
AC_MSG_ERROR([Please install readline support])
fi
else
AC_MSG_RESULT([readline])
fi
elif test "x$tr_READLINE" = xlinenoise; then
READLINE_CPPFLAGS="-I${srcdir}/3rdParty/linenoise -DUSE_UTF8"
AC_MSG_CHECKING([READLINE support])
AC_MSG_RESULT([linenoise])
else
AC_MSG_CHECKING([READLINE support])
AC_MSG_RESULT([disabled])
fi
dnl ----------------------------------------------------------------------------
dnl grep readline version number
dnl ----------------------------------------------------------------------------
if test "x$tr_READLINE" = xyes; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <readline/readline.h>
main () {
#if defined(RL_VERSION_MAJOR) && defined(RL_VERSION_MINOR)
long sdnhg36ed = RL_VERSION_MAJOR RL_VERSION_MINOR ;
#else
long sdnhg36ed = RL_READLINE_VERSION hex ;
#endif
}
_ACEOF
AC_MSG_CHECKING([READLINE version])
eval "$ac_cpp conftest.$ac_ext" | fgrep "long sdnhg36ed" | awk '{print $4 "." $5}' > conftest.output
READLINE_VERSION=`cat conftest.output`
if test -z "$READLINE_VERSION"; then
AC_MSG_ERROR([Readline support is not working. Please re-install readline support])
fi
AC_MSG_RESULT([$READLINE_VERSION])
rm -f conftest*
elif test "x$tr_READLINE" = xlinenoise; then
READLINE_VERSION="linenoise"
AC_MSG_CHECKING([READLINE version])
AC_MSG_RESULT([$READLINE_VERSION])
fi
dnl ----------------------------------------------------------------------------
dnl restore flags
dnl ----------------------------------------------------------------------------
LIBS="$SAVE_LIBS"
LDFLAGS="$SAVE_LDFLAGS"
CPPFLAGS="$SAVE_CPPFLAGS"
if test "x$tr_READLINE" = xyes; then
CPPFLAGS="$CPPFLAGS -DHAVE_READLINE=1"
READLINE_CPPFLAGS="${READLINE_CPPFLAGS} -DTRI_READLINE_VERSION='\"${READLINE_VERSION}\"'"
elif test "x$tr_READLINE" = xlinenoise; then
READLINE_CPPFLAGS="${READLINE_CPPFLAGS} -DTRI_HAVE_LINENOISE -DTRI_READLINE_VERSION='\"${READLINE_VERSION}\"'"
fi
dnl ----------------------------------------------------------------------------
dnl add substitutions
dnl ----------------------------------------------------------------------------
AM_CONDITIONAL(ENABLE_READLINE, test "x$tr_READLINE" = xyes)
if test "x$tr_READLINE" = xyes; then
AC_DEFINE_UNQUOTED(TRI_HAVE_READLINE, 1, [true if readline is used])
fi
AM_CONDITIONAL(ENABLE_LINENOISE, test "x$tr_READLINE" = xlinenoise)
if test "x$tr_READLINE" = xlinenoise; then
AC_DEFINE_UNQUOTED(TRI_HAVE_LINENOISE, 1, [true if linenoise is used])
fi
AC_SUBST(READLINE_VERSION)
AC_SUBST(READLINE_CPPFLAGS)
AC_SUBST(READLINE_LDFLAGS)
AC_SUBST(READLINE_LIBS)
dnl ----------------------------------------------------------------------------
dnl informational output
dnl ----------------------------------------------------------------------------
if test "x$tr_READLINE" = xyes; then
LIB_INFO="$LIB_INFO|READLINE VERSION: ${READLINE_VERSION}"
LIB_INFO="$LIB_INFO|READLINE_CPPFLAGS: ${READLINE_CPPFLAGS}"
LIB_INFO="$LIB_INFO|READLINE_LDLIBS: ${READLINE_LDLIBS}"
LIB_INFO="$LIB_INFO|READLINE_LIBS: ${READLINE_LIBS}"
elif test "x$tr_READLINE" = xlinenoise; then
LIB_INFO="$LIB_INFO|LINENOISE VERSION: ${READLINE_VERSION}"
LIB_INFO="$LIB_INFO|LINENOISE_CPPFLAGS: ${READLINE_CPPFLAGS}"
LIB_INFO="$LIB_INFO|LINENOISE_LDLIBS: ${READLINE_LDLIBS}"
LIB_INFO="$LIB_INFO|LINENOISE_LIBS: ${READLINE_LIBS}"
else
LIB_INFO="$LIB_INFO|READLINE VERSION: disabled"
fi
LIB_INFO="$LIB_INFO|."
dnl ----------------------------------------------------------------------------
dnl --SECTION-- END-OF-FILE
dnl ----------------------------------------------------------------------------