2009-09-14 01:07:32 +00:00
|
|
|
/**
|
2013-05-03 06:02:03 +00:00
|
|
|
* Copyright (c) 2007-2012, Timothy Stack
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
2013-05-28 04:35:00 +00:00
|
|
|
*
|
2013-05-03 06:02:03 +00:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
2013-05-28 04:35:00 +00:00
|
|
|
*
|
2013-05-03 06:02:03 +00:00
|
|
|
* * Redistributions of source code must retain the above copyright notice, this
|
|
|
|
* list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of Timothy Stack nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
2013-05-28 04:35:00 +00:00
|
|
|
*
|
2013-05-03 06:02:03 +00:00
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
2022-03-16 22:38:08 +00:00
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
2013-05-03 06:02:03 +00:00
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
2009-09-14 01:07:32 +00:00
|
|
|
* @file readline_curses.cc
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdlib.h>
|
2022-03-16 22:38:08 +00:00
|
|
|
#include <string.h>
|
2009-09-14 01:07:32 +00:00
|
|
|
#include <sys/socket.h>
|
2022-03-16 22:38:08 +00:00
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
2009-09-14 01:07:32 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_PTY_H
|
2022-03-16 22:38:08 +00:00
|
|
|
# include <pty.h>
|
2009-09-14 01:07:32 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_UTIL_H
|
2022-03-16 22:38:08 +00:00
|
|
|
# include <util.h>
|
2009-09-14 01:07:32 +00:00
|
|
|
#endif
|
|
|
|
|
2009-12-24 18:36:01 +00:00
|
|
|
#ifdef HAVE_LIBUTIL_H
|
2022-03-16 22:38:08 +00:00
|
|
|
# include <libutil.h>
|
2009-12-24 18:36:01 +00:00
|
|
|
#endif
|
|
|
|
|
2009-09-14 01:07:32 +00:00
|
|
|
#include <string>
|
2021-02-03 05:58:42 +00:00
|
|
|
#include <utility>
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2022-04-12 23:07:13 +00:00
|
|
|
#include "base/ansi_scrubber.hh"
|
|
|
|
#include "base/auto_mem.hh"
|
2022-03-16 22:38:08 +00:00
|
|
|
#include "base/lnav_log.hh"
|
2021-05-16 05:39:18 +00:00
|
|
|
#include "base/paths.hh"
|
2020-10-29 04:23:46 +00:00
|
|
|
#include "base/string_util.hh"
|
2020-11-10 06:17:17 +00:00
|
|
|
#include "fmt/format.h"
|
2022-03-16 22:38:08 +00:00
|
|
|
#include "fts_fuzzy_match.hh"
|
2015-04-09 03:36:45 +00:00
|
|
|
#include "lnav_util.hh"
|
2009-09-14 01:07:32 +00:00
|
|
|
#include "readline_curses.hh"
|
2022-03-16 22:38:08 +00:00
|
|
|
#include "shlex.hh"
|
2017-03-26 13:02:53 +00:00
|
|
|
#include "spookyhash/SpookyV2.h"
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static int got_line = 0;
|
2022-07-16 04:41:47 +00:00
|
|
|
static int got_abort = 0;
|
2022-03-16 22:38:08 +00:00
|
|
|
static bool alt_done = 0;
|
|
|
|
static sig_atomic_t got_timeout = 0;
|
|
|
|
static sig_atomic_t got_winch = 0;
|
|
|
|
static readline_curses* child_this;
|
|
|
|
static sig_atomic_t looping = 1;
|
|
|
|
static const int HISTORY_SIZE = 256;
|
|
|
|
static int completion_start;
|
|
|
|
static const int FUZZY_PEER_THRESHOLD = 30;
|
|
|
|
|
|
|
|
static const char* RL_INIT[] = {
|
2014-02-10 02:41:32 +00:00
|
|
|
/*
|
|
|
|
* XXX Need to keep the input on a single line since the display screws
|
|
|
|
* up if it wraps around.
|
|
|
|
*/
|
|
|
|
"set horizontal-scroll-mode on",
|
2017-10-19 05:34:55 +00:00
|
|
|
"set bell-style none",
|
2018-09-13 21:27:49 +00:00
|
|
|
"set show-all-if-ambiguous on",
|
|
|
|
"set show-all-if-unmodified on",
|
|
|
|
"set menu-complete-display-prefix on",
|
|
|
|
"TAB: menu-complete",
|
2018-12-02 17:34:36 +00:00
|
|
|
"\"\\e[Z\": menu-complete-backward",
|
2014-02-10 02:41:32 +00:00
|
|
|
};
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
readline_context* readline_context::loaded_context;
|
2022-03-31 15:59:19 +00:00
|
|
|
std::set<std::string>* readline_context::arg_possibilities;
|
|
|
|
static std::string last_match_str;
|
2014-03-09 09:29:28 +00:00
|
|
|
static bool last_match_str_valid;
|
2021-07-14 18:16:51 +00:00
|
|
|
static bool arg_needs_shlex;
|
2021-03-23 16:35:02 +00:00
|
|
|
static nonstd::optional<std::string> rewrite_line_start;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static void
|
|
|
|
sigalrm(int sig)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
|
|
|
got_timeout = 1;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static void
|
|
|
|
sigwinch(int sig)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
|
|
|
got_winch = 1;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static void
|
|
|
|
sigterm(int sig)
|
2010-02-24 04:35:52 +00:00
|
|
|
{
|
|
|
|
looping = 0;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static void
|
|
|
|
line_ready_tramp(char* line)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
|
|
|
child_this->line_ready(line);
|
|
|
|
got_line = 1;
|
|
|
|
rl_callback_handler_remove();
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static int
|
|
|
|
sendall(int sock, const char* buf, size_t len)
|
2011-05-14 19:06:32 +00:00
|
|
|
{
|
2014-03-09 09:29:28 +00:00
|
|
|
off_t offset = 0;
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2014-03-09 09:29:28 +00:00
|
|
|
while (len > 0) {
|
|
|
|
int rc = send(sock, &buf[offset], len, 0);
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2014-03-09 09:29:28 +00:00
|
|
|
if (rc == -1) {
|
|
|
|
switch (errno) {
|
2022-03-16 22:38:08 +00:00
|
|
|
case EAGAIN:
|
|
|
|
case EINTR:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
2014-03-09 09:29:28 +00:00
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
} else {
|
2014-03-09 09:29:28 +00:00
|
|
|
len -= rc;
|
|
|
|
offset += rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static int
|
|
|
|
sendstring(int sock, const char* buf, size_t len)
|
2014-03-09 09:29:28 +00:00
|
|
|
{
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendall(sock, (char*) &len, sizeof(len)) == -1) {
|
2014-03-09 09:29:28 +00:00
|
|
|
return -1;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (sendall(sock, buf, len) == -1) {
|
2014-03-09 09:29:28 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static int
|
|
|
|
sendcmd(int sock, char cmd, const char* buf, size_t len)
|
2015-08-03 13:49:07 +00:00
|
|
|
{
|
|
|
|
size_t total_len = len + 2;
|
2022-03-16 22:38:08 +00:00
|
|
|
char prefix[2] = {cmd, ':'};
|
2015-08-03 13:49:07 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendall(sock, (char*) &total_len, sizeof(total_len)) == -1) {
|
2015-08-03 13:49:07 +00:00
|
|
|
return -1;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (sendall(sock, prefix, sizeof(prefix)) == -1
|
|
|
|
|| sendall(sock, buf, len) == -1)
|
|
|
|
{
|
2015-08-03 13:49:07 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static int
|
|
|
|
recvall(int sock, char* buf, size_t len)
|
2014-03-09 09:29:28 +00:00
|
|
|
{
|
|
|
|
off_t offset = 0;
|
|
|
|
|
|
|
|
while (len > 0) {
|
2018-11-22 01:00:28 +00:00
|
|
|
ssize_t rc = recv(sock, &buf[offset], len, 0);
|
2014-03-09 09:29:28 +00:00
|
|
|
|
|
|
|
if (rc == -1) {
|
|
|
|
switch (errno) {
|
2022-03-16 22:38:08 +00:00
|
|
|
case EAGAIN:
|
|
|
|
case EINTR:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
2014-03-09 09:29:28 +00:00
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (rc == 0) {
|
2014-03-10 06:38:30 +00:00
|
|
|
errno = EIO;
|
|
|
|
return -1;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else {
|
2014-03-09 09:29:28 +00:00
|
|
|
len -= rc;
|
|
|
|
offset += rc;
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
2011-05-14 19:06:32 +00:00
|
|
|
}
|
|
|
|
|
2014-03-09 09:29:28 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static ssize_t
|
|
|
|
recvstring(int sock, char* buf, size_t len)
|
2014-03-09 09:29:28 +00:00
|
|
|
{
|
|
|
|
ssize_t retval;
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
if (recvall(sock, (char*) &retval, sizeof(retval)) == -1) {
|
2014-03-09 09:29:28 +00:00
|
|
|
return -1;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (retval > (ssize_t) len) {
|
2014-03-09 09:29:28 +00:00
|
|
|
return -1;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (recvall(sock, buf, retval) == -1) {
|
2014-03-09 09:29:28 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-05-14 19:06:32 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
char*
|
|
|
|
readline_context::completion_generator(const char* text_in, int state)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
2022-03-31 15:59:19 +00:00
|
|
|
static std::vector<std::string> matches;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2022-03-31 15:59:19 +00:00
|
|
|
std::vector<std::string> long_matches;
|
2022-03-16 22:38:08 +00:00
|
|
|
char* retval = nullptr;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
|
|
|
if (state == 0) {
|
2021-07-14 18:16:51 +00:00
|
|
|
auto text_str = std::string(text_in);
|
|
|
|
|
|
|
|
if (arg_needs_shlex) {
|
|
|
|
shlex arg_lexer(text_str);
|
2022-03-31 15:59:19 +00:00
|
|
|
std::map<std::string, std::string> scope;
|
2021-07-14 18:16:51 +00:00
|
|
|
std::string result;
|
|
|
|
|
|
|
|
if (arg_lexer.eval(result, scope)) {
|
|
|
|
text_str = result;
|
|
|
|
}
|
|
|
|
}
|
2013-05-28 04:35:00 +00:00
|
|
|
|
|
|
|
matches.clear();
|
2019-01-21 22:15:00 +00:00
|
|
|
if (arg_possibilities != nullptr) {
|
2022-03-16 22:38:08 +00:00
|
|
|
for (const auto& poss : (*arg_possibilities)) {
|
|
|
|
auto cmpfunc
|
|
|
|
= (loaded_context->is_case_sensitive() ? strncmp
|
|
|
|
: strncasecmp);
|
2019-01-21 22:15:00 +00:00
|
|
|
auto poss_str = poss.c_str();
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2015-04-25 19:15:51 +00:00
|
|
|
// Check for an exact match and for the quoted version.
|
2022-03-16 22:38:08 +00:00
|
|
|
if (cmpfunc(text_str.c_str(), poss_str, text_str.length()) == 0
|
|
|
|
|| ((strchr(loaded_context->rc_quote_chars, poss_str[0])
|
|
|
|
!= nullptr)
|
|
|
|
&& cmpfunc(text_str.c_str(),
|
|
|
|
&poss_str[1],
|
|
|
|
text_str.length())
|
|
|
|
== 0))
|
|
|
|
{
|
|
|
|
auto poss_slash_count
|
|
|
|
= std::count(poss.begin(), poss.end(), '/');
|
2020-09-13 04:02:20 +00:00
|
|
|
|
2020-10-29 04:23:46 +00:00
|
|
|
if (endswith(poss, "/")) {
|
2020-09-13 04:02:20 +00:00
|
|
|
poss_slash_count -= 1;
|
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
if (std::count(text_str.begin(), text_str.end(), '/')
|
2022-07-29 06:31:58 +00:00
|
|
|
== poss_slash_count)
|
|
|
|
{
|
2020-09-13 04:02:20 +00:00
|
|
|
matches.emplace_back(poss);
|
|
|
|
} else {
|
|
|
|
long_matches.emplace_back(poss);
|
|
|
|
}
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
|
|
|
}
|
2018-09-13 21:27:49 +00:00
|
|
|
|
2020-09-13 04:02:20 +00:00
|
|
|
if (matches.empty()) {
|
|
|
|
matches = std::move(long_matches);
|
|
|
|
}
|
|
|
|
|
2018-09-13 21:27:49 +00:00
|
|
|
if (matches.empty()) {
|
2022-03-31 15:59:19 +00:00
|
|
|
std::vector<std::pair<int, std::string>> fuzzy_matches;
|
|
|
|
std::vector<std::pair<int, std::string>> fuzzy_long_matches;
|
2018-09-13 21:27:49 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
for (const auto& poss : (*arg_possibilities)) {
|
2022-03-31 15:59:19 +00:00
|
|
|
std::string poss_str = tolower(poss);
|
2022-04-30 20:05:42 +00:00
|
|
|
int score = 0;
|
2018-09-13 21:27:49 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
if (fts::fuzzy_match(
|
|
|
|
text_str.c_str(), poss_str.c_str(), score)
|
2022-07-29 06:31:58 +00:00
|
|
|
&& score > 0)
|
|
|
|
{
|
2018-09-29 07:20:45 +00:00
|
|
|
if (score <= 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-09-13 04:02:20 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
auto poss_slash_count
|
|
|
|
= std::count(poss_str.begin(), poss_str.end(), '/');
|
2020-10-29 04:23:46 +00:00
|
|
|
if (endswith(poss, "/")) {
|
2020-09-13 04:02:20 +00:00
|
|
|
poss_slash_count -= 1;
|
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
if (std::count(text_str.begin(), text_str.end(), '/')
|
2022-07-29 06:31:58 +00:00
|
|
|
== poss_slash_count)
|
|
|
|
{
|
2020-09-13 04:02:20 +00:00
|
|
|
fuzzy_matches.emplace_back(score, poss);
|
|
|
|
} else {
|
|
|
|
fuzzy_long_matches.emplace_back(score, poss);
|
|
|
|
}
|
2018-09-13 21:27:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-13 04:02:20 +00:00
|
|
|
if (fuzzy_matches.empty()) {
|
|
|
|
fuzzy_matches = std::move(fuzzy_long_matches);
|
|
|
|
}
|
|
|
|
|
2018-09-13 21:27:49 +00:00
|
|
|
if (!fuzzy_matches.empty()) {
|
2022-03-16 22:38:08 +00:00
|
|
|
stable_sort(
|
|
|
|
begin(fuzzy_matches),
|
|
|
|
end(fuzzy_matches),
|
2018-09-13 21:27:49 +00:00
|
|
|
[](auto l, auto r) { return r.first < l.first; });
|
|
|
|
|
|
|
|
int highest = fuzzy_matches[0].first;
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
for (const auto& pair : fuzzy_matches) {
|
2018-09-29 07:20:45 +00:00
|
|
|
if (highest - pair.first < FUZZY_PEER_THRESHOLD) {
|
2018-09-13 21:27:49 +00:00
|
|
|
matches.push_back(pair.second);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2014-03-09 15:54:55 +00:00
|
|
|
if (matches.size() == 1) {
|
2021-07-14 18:16:51 +00:00
|
|
|
if (text_str == matches[0]) {
|
2014-03-09 15:54:55 +00:00
|
|
|
matches.pop_back();
|
|
|
|
}
|
2014-03-09 09:29:28 +00:00
|
|
|
|
2014-03-09 15:54:55 +00:00
|
|
|
last_match_str_valid = false;
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendstring(
|
|
|
|
child_this->rc_command_pipe[readline_curses::RCF_SLAVE],
|
|
|
|
"m:0:0:0",
|
|
|
|
7)
|
|
|
|
== -1)
|
|
|
|
{
|
2014-03-09 15:54:55 +00:00
|
|
|
_exit(1);
|
|
|
|
}
|
2014-03-09 09:29:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-14 01:07:32 +00:00
|
|
|
if (!matches.empty()) {
|
2013-05-28 04:35:00 +00:00
|
|
|
retval = strdup(matches.back().c_str());
|
|
|
|
matches.pop_back();
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
char**
|
|
|
|
readline_context::attempted_completion(const char* text, int start, int end)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
2022-03-16 22:38:08 +00:00
|
|
|
char** retval = nullptr;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2018-09-13 21:27:49 +00:00
|
|
|
completion_start = start;
|
2022-03-16 22:38:08 +00:00
|
|
|
if (start == 0
|
|
|
|
&& loaded_context->rc_possibilities.find("__command")
|
|
|
|
!= loaded_context->rc_possibilities.end())
|
|
|
|
{
|
2015-12-29 04:30:04 +00:00
|
|
|
arg_possibilities = &loaded_context->rc_possibilities["__command"];
|
2021-07-14 18:16:51 +00:00
|
|
|
arg_needs_shlex = false;
|
2014-02-10 02:41:32 +00:00
|
|
|
rl_completion_append_character = loaded_context->rc_append_character;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else {
|
|
|
|
char* space;
|
2022-03-31 15:59:19 +00:00
|
|
|
std::string cmd;
|
|
|
|
std::vector<std::string> prefix;
|
2019-05-03 20:50:19 +00:00
|
|
|
int point = rl_point;
|
|
|
|
while (point > 0 && rl_line_buffer[point] != ' ') {
|
|
|
|
point -= 1;
|
|
|
|
}
|
|
|
|
shlex lexer(rl_line_buffer, point);
|
2022-03-31 15:59:19 +00:00
|
|
|
std::map<std::string, std::string> scope;
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2019-05-03 20:50:19 +00:00
|
|
|
arg_possibilities = nullptr;
|
2013-05-28 04:35:00 +00:00
|
|
|
rl_completion_append_character = 0;
|
2019-05-03 20:50:19 +00:00
|
|
|
if (lexer.split(prefix, scope)) {
|
2022-03-29 05:00:49 +00:00
|
|
|
auto prefix2
|
|
|
|
= fmt::format(FMT_STRING("{}"), fmt::join(prefix, "\x1f"));
|
2019-05-03 20:50:19 +00:00
|
|
|
auto prefix_iter = loaded_context->rc_prefixes.find(prefix2);
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2019-05-03 20:50:19 +00:00
|
|
|
if (prefix_iter != loaded_context->rc_prefixes.end()) {
|
2022-03-16 22:38:08 +00:00
|
|
|
arg_possibilities
|
|
|
|
= &(loaded_context->rc_possibilities[prefix_iter->second]);
|
2021-07-14 18:16:51 +00:00
|
|
|
arg_needs_shlex = false;
|
2019-05-03 20:50:19 +00:00
|
|
|
}
|
|
|
|
}
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2019-05-03 20:50:19 +00:00
|
|
|
if (arg_possibilities == nullptr) {
|
|
|
|
space = strchr(rl_line_buffer, ' ');
|
|
|
|
if (space == nullptr) {
|
|
|
|
space = rl_line_buffer + strlen(rl_line_buffer);
|
2017-04-06 06:20:55 +00:00
|
|
|
}
|
2022-03-31 15:59:19 +00:00
|
|
|
cmd = std::string(rl_line_buffer, space - rl_line_buffer);
|
2017-04-06 06:20:55 +00:00
|
|
|
|
2019-05-03 20:50:19 +00:00
|
|
|
auto iter = loaded_context->rc_prototypes.find(cmd);
|
|
|
|
|
|
|
|
if (iter == loaded_context->rc_prototypes.end()) {
|
2022-03-16 22:38:08 +00:00
|
|
|
if (loaded_context->rc_possibilities.find("*")
|
|
|
|
!= loaded_context->rc_possibilities.end())
|
|
|
|
{
|
2019-05-03 20:50:19 +00:00
|
|
|
arg_possibilities = &loaded_context->rc_possibilities["*"];
|
2021-07-14 18:16:51 +00:00
|
|
|
arg_needs_shlex = false;
|
2022-03-16 22:38:08 +00:00
|
|
|
rl_completion_append_character
|
|
|
|
= loaded_context->rc_append_character;
|
2019-05-03 20:50:19 +00:00
|
|
|
}
|
2017-04-06 06:20:55 +00:00
|
|
|
} else {
|
2022-03-31 15:59:19 +00:00
|
|
|
std::vector<std::string>& proto
|
|
|
|
= loaded_context->rc_prototypes[cmd];
|
2019-05-03 20:50:19 +00:00
|
|
|
|
|
|
|
if (proto.empty()) {
|
|
|
|
arg_possibilities = nullptr;
|
|
|
|
} else if (proto[0] == "filename") {
|
2021-05-12 05:37:21 +00:00
|
|
|
shlex fn_lexer(rl_line_buffer, rl_point);
|
|
|
|
std::vector<std::string> fn_list;
|
|
|
|
int found = 0;
|
|
|
|
|
2021-05-14 02:50:04 +00:00
|
|
|
fn_lexer.split(fn_list, scope);
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
const auto& last_fn = fn_list.size() <= 1 ? ""
|
|
|
|
: fn_list.back();
|
2021-05-14 02:50:04 +00:00
|
|
|
|
2022-03-31 15:59:19 +00:00
|
|
|
if (last_fn.find(':') != std::string::npos) {
|
2022-03-16 22:38:08 +00:00
|
|
|
auto rp_iter = loaded_context->rc_possibilities.find(
|
|
|
|
"remote-path");
|
2021-05-14 02:50:04 +00:00
|
|
|
if (rp_iter != loaded_context->rc_possibilities.end()) {
|
|
|
|
for (const auto& poss : rp_iter->second) {
|
|
|
|
if (startswith(poss, last_fn.c_str())) {
|
|
|
|
found += 1;
|
2021-05-12 05:37:21 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-14 02:50:04 +00:00
|
|
|
if (found) {
|
|
|
|
arg_possibilities = &rp_iter->second;
|
2021-07-14 18:16:51 +00:00
|
|
|
arg_needs_shlex = false;
|
2021-05-12 05:37:21 +00:00
|
|
|
}
|
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
if (!found || (endswith(last_fn, "/") && found == 1)) {
|
2021-05-14 02:50:04 +00:00
|
|
|
char msg[2048];
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
snprintf(
|
|
|
|
msg, sizeof(msg), "\t:%s", last_fn.c_str());
|
2021-05-14 02:50:04 +00:00
|
|
|
sendstring(child_this->rc_command_pipe[1],
|
2022-03-16 22:38:08 +00:00
|
|
|
msg,
|
|
|
|
strlen(msg));
|
2021-05-14 02:50:04 +00:00
|
|
|
}
|
2021-05-12 05:37:21 +00:00
|
|
|
}
|
|
|
|
if (!found) {
|
2021-05-14 02:50:04 +00:00
|
|
|
static std::set<std::string> file_name_set;
|
|
|
|
|
|
|
|
file_name_set.clear();
|
|
|
|
auto_mem<char> completed_fn;
|
|
|
|
int fn_state = 0;
|
2022-03-16 22:38:08 +00:00
|
|
|
auto recent_netlocs_iter
|
|
|
|
= loaded_context->rc_possibilities.find(
|
|
|
|
"recent-netlocs");
|
2021-05-14 02:50:04 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
if (recent_netlocs_iter
|
2022-07-29 06:31:58 +00:00
|
|
|
!= loaded_context->rc_possibilities.end())
|
|
|
|
{
|
2021-05-14 02:50:04 +00:00
|
|
|
file_name_set.insert(
|
|
|
|
recent_netlocs_iter->second.begin(),
|
|
|
|
recent_netlocs_iter->second.end());
|
|
|
|
}
|
|
|
|
while ((completed_fn = rl_filename_completion_function(
|
2022-03-16 22:38:08 +00:00
|
|
|
last_fn.c_str(), fn_state))
|
|
|
|
!= nullptr)
|
|
|
|
{
|
2021-05-14 02:50:04 +00:00
|
|
|
file_name_set.insert(completed_fn.in());
|
|
|
|
fn_state += 1;
|
|
|
|
}
|
|
|
|
arg_possibilities = &file_name_set;
|
2021-07-14 18:16:51 +00:00
|
|
|
arg_needs_shlex = true;
|
2021-05-12 05:37:21 +00:00
|
|
|
}
|
2019-05-03 20:50:19 +00:00
|
|
|
} else {
|
2022-03-16 22:38:08 +00:00
|
|
|
arg_possibilities
|
|
|
|
= &(loaded_context->rc_possibilities[proto[0]]);
|
2021-07-14 18:16:51 +00:00
|
|
|
arg_needs_shlex = false;
|
2019-05-03 20:50:19 +00:00
|
|
|
}
|
2017-04-06 06:20:55 +00:00
|
|
|
}
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
retval = rl_completion_matches(text, completion_generator);
|
2021-02-03 05:58:42 +00:00
|
|
|
if (retval == nullptr) {
|
2013-05-28 04:35:00 +00:00
|
|
|
rl_attempted_completion_over = 1;
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static int
|
|
|
|
rubout_char_or_abort(int count, int key)
|
2017-11-06 05:19:46 +00:00
|
|
|
{
|
|
|
|
if (rl_line_buffer[0] == '\0') {
|
|
|
|
rl_done = true;
|
2022-07-16 04:41:47 +00:00
|
|
|
got_abort = 1;
|
|
|
|
got_line = 0;
|
2017-11-06 05:19:46 +00:00
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return rl_rubout(count, '\b');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
static int
|
|
|
|
alt_done_func(int count, int key)
|
2020-10-01 04:55:37 +00:00
|
|
|
{
|
|
|
|
alt_done = true;
|
|
|
|
rl_newline(count, key);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
int
|
|
|
|
readline_context::command_complete(int count, int key)
|
2018-09-13 21:27:49 +00:00
|
|
|
{
|
2022-03-16 22:38:08 +00:00
|
|
|
if (loaded_context->rc_possibilities.find("__command")
|
|
|
|
!= loaded_context->rc_possibilities.end())
|
|
|
|
{
|
|
|
|
char* space = strchr(rl_line_buffer, ' ');
|
2018-09-13 21:27:49 +00:00
|
|
|
|
|
|
|
if (space == nullptr) {
|
|
|
|
return rl_menu_complete(count, key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rl_insert(count, key);
|
|
|
|
}
|
|
|
|
|
2021-02-03 05:58:42 +00:00
|
|
|
readline_context::readline_context(std::string name,
|
2022-03-16 22:38:08 +00:00
|
|
|
readline_context::command_map_t* commands,
|
2019-08-01 14:14:47 +00:00
|
|
|
bool case_sensitive)
|
2022-03-16 22:38:08 +00:00
|
|
|
: rc_name(std::move(name)), rc_case_sensitive(case_sensitive),
|
|
|
|
rc_quote_chars("\"'"), rc_highlighter(nullptr)
|
2019-08-01 14:14:47 +00:00
|
|
|
{
|
|
|
|
if (commands != nullptr) {
|
|
|
|
command_map_t::iterator iter;
|
|
|
|
|
|
|
|
for (iter = commands->begin(); iter != commands->end(); ++iter) {
|
|
|
|
std::string cmd = iter->first;
|
|
|
|
|
|
|
|
this->rc_possibilities["__command"].insert(cmd);
|
2022-03-16 22:38:08 +00:00
|
|
|
iter->second->c_func(
|
|
|
|
INIT_EXEC_CONTEXT, cmd, this->rc_prototypes[cmd]);
|
2019-08-01 14:14:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&this->rc_history, 0, sizeof(this->rc_history));
|
|
|
|
history_set_history_state(&this->rc_history);
|
|
|
|
|
2021-05-16 05:39:18 +00:00
|
|
|
auto config_dir = lnav::paths::dotlnav();
|
2020-10-21 05:55:46 +00:00
|
|
|
auto hpath = (config_dir / this->rc_name).string() + ".history";
|
|
|
|
read_history(hpath.c_str());
|
2019-08-01 14:14:47 +00:00
|
|
|
this->save();
|
|
|
|
|
|
|
|
this->rc_append_character = ' ';
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_context::load()
|
2020-12-06 05:51:46 +00:00
|
|
|
{
|
|
|
|
char buffer[128];
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
rl_completer_word_break_characters = (char*) " \t\n|()"; /* XXX */
|
2020-12-06 05:51:46 +00:00
|
|
|
/*
|
|
|
|
* XXX Need to keep the input on a single line since the display screws
|
|
|
|
* up if it wraps around.
|
|
|
|
*/
|
2022-03-16 22:38:08 +00:00
|
|
|
snprintf(buffer,
|
|
|
|
sizeof(buffer),
|
2020-12-06 05:51:46 +00:00
|
|
|
"set completion-ignore-case %s",
|
|
|
|
this->rc_case_sensitive ? "off" : "on");
|
|
|
|
rl_parse_and_bind(buffer); /* NOTE: buffer is modified */
|
|
|
|
|
|
|
|
loaded_context = this;
|
|
|
|
rl_attempted_completion_function = attempted_completion;
|
|
|
|
history_set_history_state(&this->rc_history);
|
2022-03-16 22:38:08 +00:00
|
|
|
for (auto& rc_var : this->rc_vars) {
|
|
|
|
*(rc_var.rv_dst.ch) = (char*) rc_var.rv_val.ch;
|
2020-12-06 05:51:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_context::save()
|
2020-12-06 05:51:46 +00:00
|
|
|
{
|
2022-03-16 22:38:08 +00:00
|
|
|
HISTORY_STATE* hs = history_get_history_state();
|
2020-12-06 05:51:46 +00:00
|
|
|
|
|
|
|
this->rc_history = *hs;
|
|
|
|
free(hs);
|
|
|
|
hs = nullptr;
|
|
|
|
}
|
|
|
|
|
2022-07-05 18:06:37 +00:00
|
|
|
readline_curses::readline_curses(
|
|
|
|
std::shared_ptr<pollable_supervisor> supervisor)
|
|
|
|
: pollable(supervisor, pollable::category::interactive),
|
|
|
|
rc_focus(noop_func{}), rc_change(noop_func{}), rc_perform(noop_func{}),
|
2022-03-16 22:38:08 +00:00
|
|
|
rc_alt_perform(noop_func{}), rc_timeout(noop_func{}),
|
|
|
|
rc_abort(noop_func{}), rc_display_match(noop_func{}),
|
|
|
|
rc_display_next(noop_func{}), rc_blur(noop_func{}),
|
2021-05-12 05:37:21 +00:00
|
|
|
rc_completion_request(noop_func{})
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
readline_curses::~readline_curses()
|
|
|
|
{
|
2021-03-28 20:50:26 +00:00
|
|
|
this->rc_pty[RCF_MASTER].reset();
|
|
|
|
this->rc_command_pipe[RCF_MASTER].reset();
|
2009-09-14 01:07:32 +00:00
|
|
|
if (this->rc_child == 0) {
|
2014-03-02 00:35:30 +00:00
|
|
|
_exit(0);
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (this->rc_child > 0) {
|
2013-05-28 04:35:00 +00:00
|
|
|
int status;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2021-09-25 22:35:44 +00:00
|
|
|
log_debug("terminating readline child %d", this->rc_child);
|
2021-03-28 20:50:26 +00:00
|
|
|
log_perror(kill(this->rc_child, SIGTERM));
|
2013-05-28 04:35:00 +00:00
|
|
|
this->rc_child = -1;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2013-05-28 04:35:00 +00:00
|
|
|
while (wait(&status) < 0 && (errno == EINTR)) {
|
|
|
|
;
|
|
|
|
}
|
2021-09-25 22:35:44 +00:00
|
|
|
log_debug(" child %d has exited", this->rc_child);
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::store_matches(char** matches, int num_matches, int max_len)
|
2014-03-09 09:29:28 +00:00
|
|
|
{
|
2019-01-21 22:15:00 +00:00
|
|
|
static int match_index = 0;
|
2014-03-09 09:29:28 +00:00
|
|
|
char msg[64];
|
|
|
|
int rc;
|
|
|
|
|
2014-03-12 15:32:56 +00:00
|
|
|
max_len = 0;
|
2014-03-13 03:02:59 +00:00
|
|
|
for (int lpc = 0; lpc <= num_matches; lpc++) {
|
2022-03-31 15:59:19 +00:00
|
|
|
max_len = std::max(max_len, (int) strlen(matches[lpc]));
|
2014-03-12 15:32:56 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
if (last_match_str_valid && strcmp(last_match_str.c_str(), matches[0]) == 0)
|
|
|
|
{
|
2019-01-21 22:15:00 +00:00
|
|
|
match_index += 1;
|
|
|
|
rc = snprintf(msg, sizeof(msg), "n:%d", match_index);
|
|
|
|
|
|
|
|
if (sendstring(child_this->rc_command_pipe[RCF_SLAVE], msg, rc) == -1) {
|
2014-03-09 09:29:28 +00:00
|
|
|
_exit(1);
|
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
} else {
|
2019-01-21 22:15:00 +00:00
|
|
|
match_index = 0;
|
2022-03-16 22:38:08 +00:00
|
|
|
rc = snprintf(msg,
|
|
|
|
sizeof(msg),
|
2018-09-13 21:27:49 +00:00
|
|
|
"m:%d:%d:%d",
|
2022-03-16 22:38:08 +00:00
|
|
|
completion_start,
|
|
|
|
num_matches,
|
|
|
|
max_len);
|
2014-03-09 09:29:28 +00:00
|
|
|
if (sendstring(child_this->rc_command_pipe[RCF_SLAVE], msg, rc) == -1) {
|
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
for (int lpc = 1; lpc <= num_matches; lpc++) {
|
|
|
|
if (sendstring(child_this->rc_command_pipe[RCF_SLAVE],
|
|
|
|
matches[lpc],
|
2022-03-16 22:38:08 +00:00
|
|
|
strlen(matches[lpc]))
|
|
|
|
== -1)
|
|
|
|
{
|
2014-03-09 09:29:28 +00:00
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
last_match_str = matches[0];
|
|
|
|
last_match_str_valid = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::start()
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
2018-11-09 17:45:19 +00:00
|
|
|
if (this->rc_child > 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct winsize ws;
|
2022-03-16 22:38:08 +00:00
|
|
|
int sp[2];
|
2018-11-09 17:45:19 +00:00
|
|
|
|
|
|
|
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) < 0) {
|
|
|
|
throw error(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
this->rc_command_pipe[RCF_MASTER] = sp[RCF_MASTER];
|
2022-03-16 22:38:08 +00:00
|
|
|
this->rc_command_pipe[RCF_SLAVE] = sp[RCF_SLAVE];
|
2018-11-09 17:45:19 +00:00
|
|
|
|
|
|
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) {
|
|
|
|
throw error(errno);
|
|
|
|
}
|
|
|
|
|
2023-06-17 15:44:01 +00:00
|
|
|
if (this->vc_width > 0) {
|
|
|
|
ws.ws_col = this->vc_width;
|
|
|
|
} else if (this->vc_width < 0) {
|
|
|
|
ws.ws_col -= this->vc_left;
|
|
|
|
ws.ws_col += this->vc_width;
|
|
|
|
}
|
2018-11-09 17:45:19 +00:00
|
|
|
if (openpty(this->rc_pty[RCF_MASTER].out(),
|
|
|
|
this->rc_pty[RCF_SLAVE].out(),
|
2021-02-03 05:58:42 +00:00
|
|
|
nullptr,
|
|
|
|
nullptr,
|
2022-03-16 22:38:08 +00:00
|
|
|
&ws)
|
|
|
|
< 0)
|
|
|
|
{
|
2018-11-09 17:45:19 +00:00
|
|
|
perror("error: failed to open terminal(openpty)");
|
|
|
|
throw error(errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((this->rc_child = fork()) == -1) {
|
|
|
|
throw error(errno);
|
|
|
|
}
|
|
|
|
|
2009-09-14 01:07:32 +00:00
|
|
|
if (this->rc_child != 0) {
|
2018-11-09 17:45:19 +00:00
|
|
|
this->rc_command_pipe[RCF_SLAVE].reset();
|
|
|
|
this->rc_pty[RCF_SLAVE].reset();
|
2013-05-28 04:35:00 +00:00
|
|
|
return;
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
|
2018-11-09 17:45:19 +00:00
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
|
|
|
this->rc_command_pipe[RCF_MASTER].reset();
|
|
|
|
this->rc_pty[RCF_MASTER].reset();
|
|
|
|
|
|
|
|
signal(SIGALRM, sigalrm);
|
|
|
|
signal(SIGWINCH, sigwinch);
|
|
|
|
signal(SIGINT, sigterm);
|
|
|
|
signal(SIGTERM, sigterm);
|
|
|
|
|
|
|
|
dup2(this->rc_pty[RCF_SLAVE], STDIN_FILENO);
|
|
|
|
dup2(this->rc_pty[RCF_SLAVE], STDOUT_FILENO);
|
|
|
|
|
|
|
|
setenv("TERM", "vt52", 1);
|
|
|
|
|
|
|
|
rl_initialize();
|
|
|
|
using_history();
|
|
|
|
stifle_history(HISTORY_SIZE);
|
|
|
|
|
|
|
|
rl_add_defun("rubout-char-or-abort", rubout_char_or_abort, '\b');
|
2020-10-01 04:55:37 +00:00
|
|
|
rl_add_defun("alt-done", alt_done_func, '\x0a');
|
2022-03-16 22:38:08 +00:00
|
|
|
// rl_add_defun("command-complete", readline_context::command_complete,
|
|
|
|
// ' ');
|
2018-11-09 17:45:19 +00:00
|
|
|
|
2021-02-03 05:58:42 +00:00
|
|
|
for (const auto* init_cmd : RL_INIT) {
|
|
|
|
snprintf(buffer, sizeof(buffer), "%s", init_cmd);
|
2018-11-09 17:45:19 +00:00
|
|
|
rl_parse_and_bind(buffer); /* NOTE: buffer is modified */
|
|
|
|
}
|
|
|
|
|
|
|
|
child_this = this;
|
|
|
|
}
|
|
|
|
|
2022-03-31 15:59:19 +00:00
|
|
|
std::map<int, readline_context*>::iterator current_context;
|
2022-03-16 22:38:08 +00:00
|
|
|
int maxfd;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2014-03-06 14:58:49 +00:00
|
|
|
require(!this->rc_contexts.empty());
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2014-03-09 09:29:28 +00:00
|
|
|
rl_completion_display_matches_hook = store_matches;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
|
|
|
current_context = this->rc_contexts.end();
|
|
|
|
|
2022-03-31 15:59:19 +00:00
|
|
|
maxfd = std::max(STDIN_FILENO, this->rc_command_pipe[RCF_SLAVE].get());
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2010-02-24 04:35:52 +00:00
|
|
|
while (looping) {
|
2018-09-30 05:24:55 +00:00
|
|
|
fd_set ready_rfds;
|
2022-03-16 22:38:08 +00:00
|
|
|
int rc;
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2018-09-30 05:24:55 +00:00
|
|
|
FD_ZERO(&ready_rfds);
|
|
|
|
if (current_context != this->rc_contexts.end()) {
|
|
|
|
FD_SET(STDIN_FILENO, &ready_rfds);
|
|
|
|
}
|
|
|
|
FD_SET(this->rc_command_pipe[RCF_SLAVE], &ready_rfds);
|
|
|
|
|
|
|
|
rc = select(maxfd + 1, &ready_rfds, nullptr, nullptr, nullptr);
|
2013-05-28 04:35:00 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
switch (errno) {
|
2022-03-16 22:38:08 +00:00
|
|
|
case EINTR:
|
|
|
|
break;
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
} else {
|
2013-05-28 04:35:00 +00:00
|
|
|
if (FD_ISSET(STDIN_FILENO, &ready_rfds)) {
|
2017-03-26 13:02:53 +00:00
|
|
|
static uint64_t last_h1, last_h2;
|
|
|
|
|
2013-05-28 04:35:00 +00:00
|
|
|
struct itimerval itv;
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
itv.it_value.tv_sec = 0;
|
|
|
|
itv.it_value.tv_usec = KEY_TIMEOUT;
|
|
|
|
itv.it_interval.tv_sec = 0;
|
2013-05-28 04:35:00 +00:00
|
|
|
itv.it_interval.tv_usec = 0;
|
2021-02-03 05:58:42 +00:00
|
|
|
setitimer(ITIMER_REAL, &itv, nullptr);
|
2013-05-28 04:35:00 +00:00
|
|
|
|
|
|
|
rl_callback_read_char();
|
|
|
|
if (RL_ISSTATE(RL_STATE_DONE) && !got_line) {
|
|
|
|
got_line = 1;
|
|
|
|
this->line_ready("");
|
|
|
|
rl_callback_handler_remove();
|
2022-03-16 22:38:08 +00:00
|
|
|
} else {
|
2017-03-26 13:02:53 +00:00
|
|
|
uint64_t h1 = 1, h2 = 2;
|
|
|
|
|
2018-09-13 21:27:49 +00:00
|
|
|
if (rl_last_func == readline_context::command_complete) {
|
|
|
|
rl_last_func = rl_menu_complete;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
bool complete_done
|
|
|
|
= (rl_last_func != rl_menu_complete
|
|
|
|
&& rl_last_func != rl_backward_menu_complete);
|
2018-09-13 21:27:49 +00:00
|
|
|
|
2018-11-09 17:45:19 +00:00
|
|
|
if (complete_done) {
|
|
|
|
last_match_str_valid = false;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (rewrite_line_start
|
|
|
|
&& !startswith(rl_line_buffer,
|
|
|
|
rewrite_line_start->c_str()))
|
|
|
|
{
|
2021-03-23 16:35:02 +00:00
|
|
|
// If the line was rewritten, the extra text stays on
|
|
|
|
// the screen, so we need to delete it, make sure the
|
|
|
|
// append character is there, and redisplay. For
|
|
|
|
// example, ':co<TAB>' will complete ':comment' and
|
|
|
|
// append the current comment. Pressing '<TAB>' again
|
|
|
|
// would switch to ':config' and the comment text would
|
|
|
|
// be left on the display.
|
|
|
|
rl_delete_text(rl_point, rl_end);
|
2022-03-16 22:38:08 +00:00
|
|
|
if (rl_completion_append_character
|
|
|
|
&& rl_line_buffer[rl_point]
|
|
|
|
!= rl_completion_append_character)
|
|
|
|
{
|
|
|
|
char buf[2]
|
|
|
|
= {(char) rl_completion_append_character, '\0'};
|
2021-03-23 16:35:02 +00:00
|
|
|
|
|
|
|
rl_insert_text(buf);
|
|
|
|
}
|
|
|
|
rl_redisplay();
|
2018-11-09 17:45:19 +00:00
|
|
|
}
|
2021-03-23 16:35:02 +00:00
|
|
|
rewrite_line_start = nonstd::nullopt;
|
|
|
|
|
|
|
|
SpookyHash::Hash128(rl_line_buffer, rl_end, &h1, &h2);
|
2018-11-09 17:45:19 +00:00
|
|
|
|
2017-03-26 13:02:53 +00:00
|
|
|
if (h1 == last_h1 && h2 == last_h2) {
|
|
|
|
// do nothing
|
|
|
|
} else if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
|
2022-03-16 22:38:08 +00:00
|
|
|
complete_done ? 'l' : 'c',
|
2017-03-26 13:02:53 +00:00
|
|
|
rl_line_buffer,
|
2022-03-16 22:38:08 +00:00
|
|
|
rl_end)
|
|
|
|
!= 0)
|
|
|
|
{
|
2015-08-03 13:49:07 +00:00
|
|
|
perror("line: write failed");
|
|
|
|
_exit(1);
|
|
|
|
}
|
2017-03-26 13:02:53 +00:00
|
|
|
last_h1 = h1;
|
|
|
|
last_h2 = h2;
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendcmd(this->rc_command_pipe[RCF_SLAVE], 'w', "", 0)
|
2022-07-29 06:31:58 +00:00
|
|
|
!= 0)
|
|
|
|
{
|
2021-02-03 05:58:42 +00:00
|
|
|
perror("line: write failed");
|
|
|
|
_exit(1);
|
|
|
|
}
|
2015-08-03 13:49:07 +00:00
|
|
|
}
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
|
|
|
if (FD_ISSET(this->rc_command_pipe[RCF_SLAVE], &ready_rfds)) {
|
|
|
|
char msg[1024 + 1];
|
|
|
|
|
2014-03-09 09:29:28 +00:00
|
|
|
if ((rc = recvstring(this->rc_command_pipe[RCF_SLAVE],
|
|
|
|
msg,
|
2022-03-16 22:38:08 +00:00
|
|
|
sizeof(msg) - 1))
|
|
|
|
< 0)
|
|
|
|
{
|
2018-09-30 05:24:55 +00:00
|
|
|
looping = false;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else {
|
|
|
|
int context, prompt_start = 0;
|
2019-05-03 20:50:19 +00:00
|
|
|
char type[1024];
|
2013-05-28 04:35:00 +00:00
|
|
|
|
|
|
|
msg[rc] = '\0';
|
2018-11-22 01:00:28 +00:00
|
|
|
if (sscanf(msg, "i:%d:%n", &rl_point, &prompt_start) == 1) {
|
2022-03-16 22:38:08 +00:00
|
|
|
const char* initial = &msg[prompt_start];
|
2018-11-09 17:45:19 +00:00
|
|
|
|
|
|
|
rl_extend_line_buffer(strlen(initial) + 1);
|
|
|
|
strcpy(rl_line_buffer, initial);
|
|
|
|
rl_end = strlen(initial);
|
2022-03-16 22:38:08 +00:00
|
|
|
rewrite_line_start
|
|
|
|
= std::string(rl_line_buffer, rl_point);
|
2018-11-09 17:45:19 +00:00
|
|
|
rl_redisplay();
|
2020-11-29 21:20:07 +00:00
|
|
|
if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
|
2021-03-23 16:35:02 +00:00
|
|
|
'c',
|
2020-11-29 21:20:07 +00:00
|
|
|
rl_line_buffer,
|
2022-03-16 22:38:08 +00:00
|
|
|
rl_end)
|
2022-07-29 06:31:58 +00:00
|
|
|
!= 0)
|
|
|
|
{
|
2020-11-29 21:20:07 +00:00
|
|
|
perror("line: write failed");
|
|
|
|
_exit(1);
|
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (sscanf(msg, "f:%d:%n", &context, &prompt_start)
|
|
|
|
== 1
|
|
|
|
&& prompt_start != 0
|
|
|
|
&& (current_context
|
|
|
|
= this->rc_contexts.find(context))
|
|
|
|
!= this->rc_contexts.end())
|
|
|
|
{
|
2022-07-16 04:41:47 +00:00
|
|
|
got_abort = 0;
|
2013-05-28 04:35:00 +00:00
|
|
|
current_context->second->load();
|
|
|
|
rl_callback_handler_install(&msg[prompt_start],
|
|
|
|
line_ready_tramp);
|
2014-03-09 09:29:28 +00:00
|
|
|
last_match_str_valid = false;
|
2015-08-03 13:49:07 +00:00
|
|
|
if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
|
|
|
|
'l',
|
|
|
|
rl_line_buffer,
|
2022-03-16 22:38:08 +00:00
|
|
|
rl_end)
|
2022-07-29 06:31:58 +00:00
|
|
|
!= 0)
|
|
|
|
{
|
2015-08-03 13:49:07 +00:00
|
|
|
perror("line: write failed");
|
|
|
|
_exit(1);
|
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendcmd(
|
|
|
|
this->rc_command_pipe[RCF_SLAVE], 'w', "", 0)
|
2022-07-29 06:31:58 +00:00
|
|
|
!= 0)
|
|
|
|
{
|
2021-02-03 05:58:42 +00:00
|
|
|
perror("line: write failed");
|
|
|
|
_exit(1);
|
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (strcmp(msg, "a") == 0) {
|
2013-07-24 14:42:16 +00:00
|
|
|
char reply[4];
|
|
|
|
|
|
|
|
rl_done = 1;
|
|
|
|
got_timeout = 0;
|
|
|
|
got_line = 1;
|
|
|
|
rl_callback_handler_remove();
|
|
|
|
|
|
|
|
snprintf(reply, sizeof(reply), "a");
|
|
|
|
|
2014-03-09 09:29:28 +00:00
|
|
|
if (sendstring(this->rc_command_pipe[RCF_SLAVE],
|
|
|
|
reply,
|
2022-03-16 22:38:08 +00:00
|
|
|
strlen(reply))
|
2022-07-29 06:31:58 +00:00
|
|
|
== -1)
|
|
|
|
{
|
2013-07-24 14:42:16 +00:00
|
|
|
perror("abort: write failed");
|
2014-03-09 09:29:28 +00:00
|
|
|
_exit(1);
|
2013-07-24 14:42:16 +00:00
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (sscanf(msg,
|
|
|
|
"apre:%d:%1023[^\x1d]\x1d%n",
|
|
|
|
&context,
|
|
|
|
type,
|
|
|
|
&prompt_start)
|
|
|
|
== 2)
|
|
|
|
{
|
2021-02-03 05:58:42 +00:00
|
|
|
require(this->rc_contexts[context] != nullptr);
|
2019-05-03 20:50:19 +00:00
|
|
|
|
2022-03-31 15:59:19 +00:00
|
|
|
this->rc_contexts[context]
|
|
|
|
->rc_prefixes[std::string(type)]
|
|
|
|
= std::string(&msg[prompt_start]);
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (sscanf(msg,
|
|
|
|
"ap:%d:%31[^:]:%n",
|
|
|
|
&context,
|
|
|
|
type,
|
|
|
|
&prompt_start)
|
2022-07-29 06:31:58 +00:00
|
|
|
== 2)
|
|
|
|
{
|
2021-02-03 05:58:42 +00:00
|
|
|
require(this->rc_contexts[context] != nullptr);
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
this->rc_contexts[context]->add_possibility(
|
2022-03-31 15:59:19 +00:00
|
|
|
std::string(type), std::string(&msg[prompt_start]));
|
2022-03-16 22:38:08 +00:00
|
|
|
if (rl_last_func == rl_complete
|
2022-07-29 06:31:58 +00:00
|
|
|
|| rl_last_func == rl_menu_complete)
|
|
|
|
{
|
2021-05-12 05:37:21 +00:00
|
|
|
rl_last_func = NULL;
|
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (sscanf(msg,
|
|
|
|
"rp:%d:%31[^:]:%n",
|
|
|
|
&context,
|
|
|
|
type,
|
|
|
|
&prompt_start)
|
2022-07-29 06:31:58 +00:00
|
|
|
== 2)
|
|
|
|
{
|
2021-02-03 05:58:42 +00:00
|
|
|
require(this->rc_contexts[context] != nullptr);
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
this->rc_contexts[context]->rem_possibility(
|
2022-03-31 15:59:19 +00:00
|
|
|
std::string(type), std::string(&msg[prompt_start]));
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (sscanf(msg, "cpre:%d", &context) == 1) {
|
2019-05-03 20:50:19 +00:00
|
|
|
this->rc_contexts[context]->rc_prefixes.clear();
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (sscanf(msg, "cp:%d:%s", &context, type)) {
|
2013-05-28 04:35:00 +00:00
|
|
|
this->rc_contexts[context]->clear_possibilities(type);
|
2022-03-16 22:38:08 +00:00
|
|
|
} else {
|
2014-03-02 00:35:30 +00:00
|
|
|
log_error("unhandled message: %s", msg);
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (got_timeout) {
|
|
|
|
got_timeout = 0;
|
2015-08-03 13:49:07 +00:00
|
|
|
if (sendcmd(this->rc_command_pipe[RCF_SLAVE],
|
|
|
|
't',
|
|
|
|
rl_line_buffer,
|
2022-03-16 22:38:08 +00:00
|
|
|
rl_end)
|
|
|
|
== -1)
|
|
|
|
{
|
2014-03-09 09:29:28 +00:00
|
|
|
_exit(1);
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (got_line) {
|
|
|
|
struct itimerval itv;
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
got_line = 0;
|
|
|
|
itv.it_value.tv_sec = 0;
|
|
|
|
itv.it_value.tv_usec = 0;
|
|
|
|
itv.it_interval.tv_sec = 0;
|
2013-05-28 04:35:00 +00:00
|
|
|
itv.it_interval.tv_usec = 0;
|
2021-02-03 05:58:42 +00:00
|
|
|
if (setitimer(ITIMER_REAL, &itv, nullptr) < 0) {
|
2014-03-02 00:35:30 +00:00
|
|
|
log_error("setitimer: %s", strerror(errno));
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
2021-03-23 16:35:02 +00:00
|
|
|
if (current_context != this->rc_contexts.end()) {
|
|
|
|
current_context->second->save();
|
|
|
|
current_context = this->rc_contexts.end();
|
|
|
|
}
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
|
|
|
if (got_winch) {
|
2021-02-03 05:58:42 +00:00
|
|
|
struct winsize new_ws;
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2021-02-03 05:58:42 +00:00
|
|
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &new_ws) == -1) {
|
2013-05-28 04:35:00 +00:00
|
|
|
throw error(errno);
|
|
|
|
}
|
|
|
|
got_winch = 0;
|
2021-02-03 05:58:42 +00:00
|
|
|
rl_set_screen_size(new_ws.ws_row, new_ws.ws_col);
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
2010-02-24 04:35:52 +00:00
|
|
|
|
2022-08-30 20:23:15 +00:00
|
|
|
if (this->rc_save_history) {
|
|
|
|
auto config_dir = lnav::paths::dotlnav();
|
|
|
|
for (auto& pair : this->rc_contexts) {
|
|
|
|
pair.second->load();
|
|
|
|
|
|
|
|
auto hpath
|
|
|
|
= (config_dir / pair.second->get_name()).string() + ".history";
|
|
|
|
write_history(hpath.c_str());
|
|
|
|
pair.second->save();
|
|
|
|
}
|
2010-02-24 04:35:52 +00:00
|
|
|
}
|
|
|
|
|
2014-03-09 09:29:28 +00:00
|
|
|
_exit(0);
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::line_ready(const char* line)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
|
|
|
auto_mem<char> expanded;
|
2022-03-16 22:38:08 +00:00
|
|
|
char msg[1024] = {0};
|
|
|
|
int rc;
|
|
|
|
const char* cmd_ch = alt_done ? "D" : "d";
|
2009-09-14 01:07:32 +00:00
|
|
|
|
2020-10-01 04:55:37 +00:00
|
|
|
alt_done = false;
|
2022-07-16 04:41:47 +00:00
|
|
|
if (got_abort || line == nullptr) {
|
2018-11-09 17:45:19 +00:00
|
|
|
snprintf(msg, sizeof(msg), "a");
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendstring(this->rc_command_pipe[RCF_SLAVE], msg, strlen(msg))
|
2022-07-29 06:31:58 +00:00
|
|
|
== -1)
|
|
|
|
{
|
2018-11-09 17:45:19 +00:00
|
|
|
perror("abort: write failed");
|
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-18 04:29:42 +00:00
|
|
|
if (rl_line_buffer[0] == '^') {
|
|
|
|
rc = -1;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else {
|
2014-06-18 04:29:42 +00:00
|
|
|
rc = history_expand(rl_line_buffer, expanded.out());
|
|
|
|
}
|
2013-07-25 13:22:54 +00:00
|
|
|
switch (rc) {
|
2009-09-14 01:07:32 +00:00
|
|
|
#if 0
|
2013-07-24 14:42:16 +00:00
|
|
|
/* TODO: fix clash between history and pcre metacharacters */
|
|
|
|
case -1:
|
|
|
|
/* XXX */
|
|
|
|
snprintf(msg, sizeof(msg),
|
|
|
|
"e:unable to expand history -- %s",
|
|
|
|
expanded.in());
|
|
|
|
break;
|
2009-09-14 01:07:32 +00:00
|
|
|
#endif
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
case -1:
|
|
|
|
snprintf(msg, sizeof(msg), "%s:%s", cmd_ch, line);
|
|
|
|
break;
|
2013-05-28 04:35:00 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
case 2: /* XXX */
|
|
|
|
snprintf(msg, sizeof(msg), "%s:%s", cmd_ch, expanded.in());
|
|
|
|
break;
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
2013-07-24 14:42:16 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendstring(this->rc_command_pipe[RCF_SLAVE], msg, strlen(msg)) == -1) {
|
2013-05-28 04:35:00 +00:00
|
|
|
perror("line_ready: write failed");
|
2014-03-09 09:29:28 +00:00
|
|
|
_exit(1);
|
2009-10-14 19:42:58 +00:00
|
|
|
}
|
2013-07-06 21:37:07 +00:00
|
|
|
|
|
|
|
{
|
2022-03-16 22:38:08 +00:00
|
|
|
HIST_ENTRY* entry;
|
|
|
|
|
|
|
|
if (line[0] != '\0'
|
|
|
|
&& (history_length == 0
|
|
|
|
|| (entry = history_get(history_base + history_length - 1))
|
|
|
|
== nullptr
|
|
|
|
|| strcmp(entry->line, line) != 0))
|
|
|
|
{
|
2013-07-06 21:37:07 +00:00
|
|
|
add_history(line);
|
|
|
|
}
|
|
|
|
}
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
2022-03-31 15:59:19 +00:00
|
|
|
readline_curses::check_poll_set(const std::vector<struct pollfd>& pollfds)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2015-04-09 03:36:45 +00:00
|
|
|
if (pollfd_ready(pollfds, this->rc_pty[RCF_MASTER])) {
|
2013-05-28 04:35:00 +00:00
|
|
|
char buffer[128];
|
|
|
|
|
|
|
|
rc = read(this->rc_pty[RCF_MASTER], buffer, sizeof(buffer));
|
|
|
|
if (rc > 0) {
|
2017-03-31 14:01:11 +00:00
|
|
|
int old_x = this->vc_x;
|
|
|
|
|
2013-05-28 04:35:00 +00:00
|
|
|
this->map_output(buffer, rc);
|
2017-03-31 14:01:11 +00:00
|
|
|
if (this->vc_x != old_x) {
|
2020-11-15 05:39:12 +00:00
|
|
|
this->rc_change(this);
|
2017-03-31 14:01:11 +00:00
|
|
|
}
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
2015-04-09 03:36:45 +00:00
|
|
|
if (pollfd_ready(pollfds, this->rc_command_pipe[RCF_MASTER])) {
|
2013-05-28 04:35:00 +00:00
|
|
|
char msg[1024 + 1];
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
rc = recvstring(
|
|
|
|
this->rc_command_pipe[RCF_MASTER], msg, sizeof(msg) - 1);
|
2014-03-09 09:29:28 +00:00
|
|
|
if (rc >= 0) {
|
2013-05-28 04:35:00 +00:00
|
|
|
msg[rc] = '\0';
|
2014-03-09 09:29:28 +00:00
|
|
|
if (this->rc_matches_remaining > 0) {
|
2018-11-09 17:45:19 +00:00
|
|
|
this->rc_matches.emplace_back(msg);
|
2014-03-09 09:29:28 +00:00
|
|
|
this->rc_matches_remaining -= 1;
|
|
|
|
if (this->rc_matches_remaining == 0) {
|
2020-11-15 05:39:12 +00:00
|
|
|
this->rc_display_match(this);
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (msg[0] == 'm') {
|
|
|
|
if (sscanf(msg,
|
|
|
|
"m:%d:%d:%d",
|
2018-09-13 21:27:49 +00:00
|
|
|
&this->rc_match_start,
|
|
|
|
&this->rc_matches_remaining,
|
2022-03-16 22:38:08 +00:00
|
|
|
&this->rc_max_match_length)
|
|
|
|
!= 3)
|
|
|
|
{
|
2014-03-09 19:55:02 +00:00
|
|
|
require(0);
|
|
|
|
}
|
2014-03-09 09:29:28 +00:00
|
|
|
this->rc_matches.clear();
|
|
|
|
if (this->rc_matches_remaining == 0) {
|
2020-11-15 05:39:12 +00:00
|
|
|
this->rc_display_match(this);
|
2014-03-09 09:29:28 +00:00
|
|
|
}
|
2019-01-21 22:15:00 +00:00
|
|
|
this->rc_match_index = 0;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (msg[0] == '\t') {
|
2021-05-12 05:37:21 +00:00
|
|
|
char path[2048];
|
|
|
|
|
|
|
|
if (sscanf(msg, "\t:%s", path) != 1) {
|
|
|
|
require(0);
|
|
|
|
}
|
|
|
|
this->rc_remote_complete_path = path;
|
|
|
|
this->rc_completion_request(this);
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (msg[0] == 'n') {
|
2019-01-21 22:15:00 +00:00
|
|
|
if (sscanf(msg, "n:%d", &this->rc_match_index) != 1) {
|
|
|
|
require(0);
|
|
|
|
}
|
2020-11-15 05:39:12 +00:00
|
|
|
this->rc_display_next(this);
|
2022-03-16 22:38:08 +00:00
|
|
|
} else {
|
2014-03-13 06:30:11 +00:00
|
|
|
switch (msg[0]) {
|
2022-03-16 22:38:08 +00:00
|
|
|
case 't':
|
|
|
|
case 'd':
|
|
|
|
case 'D':
|
2022-03-31 15:59:19 +00:00
|
|
|
this->rc_value = std::string(&msg[2]);
|
2022-03-16 22:38:08 +00:00
|
|
|
break;
|
2014-03-13 06:30:11 +00:00
|
|
|
}
|
2014-03-09 09:29:28 +00:00
|
|
|
switch (msg[0]) {
|
2022-03-16 22:38:08 +00:00
|
|
|
case 'a':
|
|
|
|
curs_set(0);
|
|
|
|
this->vc_line.clear();
|
|
|
|
this->rc_active_context = -1;
|
|
|
|
this->rc_matches.clear();
|
|
|
|
this->rc_abort(this);
|
|
|
|
this->rc_display_match(this);
|
|
|
|
this->rc_blur(this);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
this->rc_timeout(this);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'd':
|
|
|
|
case 'D':
|
|
|
|
curs_set(0);
|
|
|
|
this->rc_active_context = -1;
|
|
|
|
this->rc_matches.clear();
|
|
|
|
if (msg[0] == 'D' || this->rc_is_alt_focus) {
|
|
|
|
this->rc_alt_perform(this);
|
|
|
|
} else {
|
|
|
|
this->rc_perform(this);
|
|
|
|
}
|
|
|
|
this->rc_display_match(this);
|
|
|
|
this->rc_blur(this);
|
|
|
|
break;
|
2014-03-09 09:29:28 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
case 'l':
|
|
|
|
this->rc_line_buffer = &msg[2];
|
|
|
|
if (this->rc_active_context != -1) {
|
|
|
|
this->rc_change(this);
|
|
|
|
}
|
|
|
|
this->rc_matches.clear();
|
|
|
|
if (this->rc_active_context != -1) {
|
|
|
|
this->rc_display_match(this);
|
|
|
|
}
|
|
|
|
break;
|
2014-03-09 09:29:28 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
case 'c':
|
|
|
|
this->rc_line_buffer = &msg[2];
|
2020-11-29 21:20:07 +00:00
|
|
|
this->rc_change(this);
|
|
|
|
this->rc_display_match(this);
|
2022-03-16 22:38:08 +00:00
|
|
|
break;
|
|
|
|
case 'w':
|
|
|
|
this->rc_ready_for_input = true;
|
|
|
|
break;
|
2014-03-09 09:29:28 +00:00
|
|
|
}
|
2013-05-28 04:35:00 +00:00
|
|
|
}
|
|
|
|
}
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::handle_key(int ch)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
2022-03-16 22:38:08 +00:00
|
|
|
const char* bch;
|
|
|
|
int len;
|
2009-09-14 01:07:32 +00:00
|
|
|
|
|
|
|
bch = this->map_input(ch, len);
|
2009-10-14 19:42:58 +00:00
|
|
|
if (write(this->rc_pty[RCF_MASTER], bch, len) == -1) {
|
2013-05-28 04:35:00 +00:00
|
|
|
perror("handle_key: write failed");
|
2009-10-14 19:42:58 +00:00
|
|
|
}
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::focus(int context,
|
|
|
|
const std::string& prompt,
|
|
|
|
const std::string& initial)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
2011-06-26 01:54:46 +00:00
|
|
|
curs_set(1);
|
|
|
|
|
2009-09-14 01:07:32 +00:00
|
|
|
this->rc_active_context = context;
|
|
|
|
|
2020-10-01 04:55:37 +00:00
|
|
|
snprintf(buffer, sizeof(buffer), "f:%d:%s", context, prompt.c_str());
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendstring(
|
|
|
|
this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
|
|
|
|
== -1)
|
|
|
|
{
|
2013-05-28 04:35:00 +00:00
|
|
|
perror("focus: write failed");
|
2009-10-14 19:42:58 +00:00
|
|
|
}
|
2018-11-09 17:45:19 +00:00
|
|
|
wmove(this->vc_window, this->get_actual_y(), this->vc_left);
|
2009-09-14 01:07:32 +00:00
|
|
|
wclrtoeol(this->vc_window);
|
2020-10-01 04:55:37 +00:00
|
|
|
if (!initial.empty()) {
|
|
|
|
this->rewrite_line(initial.size(), initial);
|
2018-11-09 17:45:19 +00:00
|
|
|
}
|
2020-10-01 04:55:37 +00:00
|
|
|
this->rc_is_alt_focus = false;
|
2021-05-14 05:00:26 +00:00
|
|
|
this->rc_focus(this);
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::rewrite_line(int pos, const std::string& value)
|
2018-11-22 01:00:28 +00:00
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
|
|
|
snprintf(buffer, sizeof(buffer), "i:%d:%s", pos, value.c_str());
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendstring(
|
|
|
|
this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
|
|
|
|
== -1)
|
|
|
|
{
|
2020-10-01 04:55:37 +00:00
|
|
|
perror("rewrite_line: write failed");
|
2018-11-22 01:00:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::abort()
|
2013-07-24 14:42:16 +00:00
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
2019-01-21 22:15:00 +00:00
|
|
|
this->vc_x = 0;
|
2013-07-24 14:42:16 +00:00
|
|
|
snprintf(buffer, sizeof(buffer), "a");
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendstring(this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer))
|
2022-07-29 06:31:58 +00:00
|
|
|
== -1)
|
|
|
|
{
|
2013-07-24 14:42:16 +00:00
|
|
|
perror("abort: write failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::add_prefix(int context,
|
2022-03-31 15:59:19 +00:00
|
|
|
const std::vector<std::string>& prefix,
|
|
|
|
const std::string& value)
|
2019-05-03 20:50:19 +00:00
|
|
|
{
|
|
|
|
char buffer[1024];
|
2022-03-29 05:00:49 +00:00
|
|
|
auto prefix_wire = fmt::format(FMT_STRING("{}"), fmt::join(prefix, "\x1f"));
|
2019-05-03 20:50:19 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
snprintf(buffer,
|
|
|
|
sizeof(buffer),
|
2019-05-03 20:50:19 +00:00
|
|
|
"apre:%d:%s\x1d%s",
|
|
|
|
context,
|
|
|
|
prefix_wire.c_str(),
|
|
|
|
value.c_str());
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendstring(
|
|
|
|
this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
|
|
|
|
== -1)
|
|
|
|
{
|
2020-09-13 04:02:20 +00:00
|
|
|
perror("add_prefix: write failed");
|
2019-05-03 20:50:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::clear_prefixes(int context)
|
2019-05-03 20:50:19 +00:00
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
|
|
|
snprintf(buffer, sizeof(buffer), "cpre:%d", context);
|
2022-03-16 22:38:08 +00:00
|
|
|
if (sendstring(
|
|
|
|
this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
|
|
|
|
== -1)
|
|
|
|
{
|
2019-05-03 20:50:19 +00:00
|
|
|
perror("add_possibility: write failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::add_possibility(int context,
|
2022-03-31 15:59:19 +00:00
|
|
|
const std::string& type,
|
|
|
|
const std::string& value)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
2014-03-09 09:29:28 +00:00
|
|
|
if (value.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
snprintf(buffer,
|
|
|
|
sizeof(buffer),
|
2013-05-28 04:35:00 +00:00
|
|
|
"ap:%d:%s:%s",
|
2022-03-16 22:38:08 +00:00
|
|
|
context,
|
|
|
|
type.c_str(),
|
|
|
|
value.c_str());
|
|
|
|
if (sendstring(
|
|
|
|
this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
|
|
|
|
== -1)
|
|
|
|
{
|
2013-05-28 04:35:00 +00:00
|
|
|
perror("add_possibility: write failed");
|
2009-10-14 19:42:58 +00:00
|
|
|
}
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::rem_possibility(int context,
|
2022-03-31 15:59:19 +00:00
|
|
|
const std::string& type,
|
|
|
|
const std::string& value)
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
snprintf(buffer,
|
|
|
|
sizeof(buffer),
|
2013-05-28 04:35:00 +00:00
|
|
|
"rp:%d:%s:%s",
|
2022-03-16 22:38:08 +00:00
|
|
|
context,
|
|
|
|
type.c_str(),
|
|
|
|
value.c_str());
|
|
|
|
if (sendstring(
|
|
|
|
this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
|
|
|
|
== -1)
|
|
|
|
{
|
2013-05-28 04:35:00 +00:00
|
|
|
perror("rem_possiblity: write failed");
|
2009-10-14 19:42:58 +00:00
|
|
|
}
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
2022-03-31 15:59:19 +00:00
|
|
|
readline_curses::clear_possibilities(int context, std::string type)
|
2013-05-24 14:55:56 +00:00
|
|
|
{
|
|
|
|
char buffer[1024];
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
snprintf(buffer, sizeof(buffer), "cp:%d:%s", context, type.c_str());
|
|
|
|
if (sendstring(
|
|
|
|
this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
|
|
|
|
== -1)
|
|
|
|
{
|
2013-05-28 04:35:00 +00:00
|
|
|
perror("clear_possiblity: write failed");
|
2013-05-24 14:55:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
void
|
|
|
|
readline_curses::do_update()
|
2009-09-14 01:07:32 +00:00
|
|
|
{
|
2022-07-29 06:31:58 +00:00
|
|
|
if (!this->vc_visible || this->vc_window == nullptr) {
|
2018-11-09 17:45:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-17 15:44:01 +00:00
|
|
|
auto actual_width = this->get_actual_width();
|
2009-09-14 01:07:32 +00:00
|
|
|
if (this->rc_active_context == -1) {
|
2022-03-16 22:38:08 +00:00
|
|
|
int alt_start = -1;
|
2013-10-11 13:27:36 +00:00
|
|
|
struct line_range lr(0, 0);
|
2022-04-30 20:05:42 +00:00
|
|
|
attr_line_t alt_al;
|
2023-06-17 15:44:01 +00:00
|
|
|
auto& vc = view_colors::singleton();
|
2013-06-01 03:45:40 +00:00
|
|
|
|
2018-11-09 17:45:19 +00:00
|
|
|
wmove(this->vc_window, this->get_actual_y(), this->vc_left);
|
2022-07-28 04:49:14 +00:00
|
|
|
auto attrs = vc.attrs_for_role(role_t::VCR_TEXT);
|
|
|
|
wattr_set(this->vc_window,
|
|
|
|
attrs.ta_attrs,
|
|
|
|
vc.ensure_color_pair(attrs.ta_fg_color, attrs.ta_bg_color),
|
|
|
|
nullptr);
|
2023-06-17 15:44:01 +00:00
|
|
|
whline(this->vc_window, ' ', actual_width);
|
2013-06-14 13:49:00 +00:00
|
|
|
|
2018-11-09 17:45:19 +00:00
|
|
|
if (time(nullptr) > this->rc_value_expiration) {
|
2013-06-15 14:11:45 +00:00
|
|
|
this->rc_value.clear();
|
|
|
|
}
|
|
|
|
|
2013-06-14 13:49:00 +00:00
|
|
|
if (!this->rc_alt_value.empty()) {
|
|
|
|
alt_al.get_string() = this->rc_alt_value;
|
2022-08-09 06:14:02 +00:00
|
|
|
scrub_ansi_string(alt_al.get_string(), &alt_al.get_attrs());
|
2013-06-14 13:49:00 +00:00
|
|
|
|
|
|
|
alt_start = getmaxx(this->vc_window) - alt_al.get_string().size();
|
2013-06-04 13:53:25 +00:00
|
|
|
}
|
2013-06-14 13:49:00 +00:00
|
|
|
|
2022-04-30 20:05:42 +00:00
|
|
|
if (alt_start >= (int) (this->rc_value.length() + 5)) {
|
2013-06-14 13:49:00 +00:00
|
|
|
lr.lr_end = alt_al.get_string().length();
|
2022-03-16 22:38:08 +00:00
|
|
|
view_curses::mvwattrline(
|
|
|
|
this->vc_window, this->get_actual_y(), alt_start, alt_al, lr);
|
2013-06-14 13:49:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-30 20:05:42 +00:00
|
|
|
lr.lr_end = this->rc_value.length();
|
|
|
|
view_curses::mvwattrline(this->vc_window,
|
|
|
|
this->get_actual_y(),
|
|
|
|
this->vc_left,
|
|
|
|
this->rc_value,
|
|
|
|
lr);
|
2013-05-28 04:35:00 +00:00
|
|
|
this->set_x(0);
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
2014-03-04 15:38:33 +00:00
|
|
|
|
|
|
|
if (this->rc_active_context != -1) {
|
2022-03-16 22:38:08 +00:00
|
|
|
readline_context* rc = this->rc_contexts[this->rc_active_context];
|
2014-03-04 15:38:33 +00:00
|
|
|
readline_highlighter_t hl = rc->get_highlighter();
|
2018-11-09 17:45:19 +00:00
|
|
|
attr_line_t al = this->vc_line;
|
2014-03-04 15:38:33 +00:00
|
|
|
|
2018-11-09 17:45:19 +00:00
|
|
|
if (hl != nullptr) {
|
|
|
|
hl(al, this->vc_left + this->vc_x);
|
2014-03-04 15:38:33 +00:00
|
|
|
}
|
2018-11-09 17:45:19 +00:00
|
|
|
view_curses::mvwattrline(this->vc_window,
|
2022-03-16 22:38:08 +00:00
|
|
|
this->get_actual_y(),
|
|
|
|
this->vc_left,
|
2018-11-09 17:45:19 +00:00
|
|
|
al,
|
2023-06-17 15:44:01 +00:00
|
|
|
line_range{0, (int) actual_width});
|
2018-11-09 17:45:19 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
wmove(
|
|
|
|
this->vc_window, this->get_actual_y(), this->vc_left + this->vc_x);
|
2014-03-04 15:38:33 +00:00
|
|
|
}
|
2009-09-14 01:07:32 +00:00
|
|
|
}
|
2019-01-29 17:58:34 +00:00
|
|
|
|
2022-03-16 22:38:08 +00:00
|
|
|
std::string
|
|
|
|
readline_curses::get_match_string() const
|
2019-01-29 17:58:34 +00:00
|
|
|
{
|
2022-03-31 15:59:19 +00:00
|
|
|
auto len = std::min((size_t) this->vc_x, this->rc_line_buffer.size())
|
2022-03-16 22:38:08 +00:00
|
|
|
- this->rc_match_start;
|
2022-03-31 15:59:19 +00:00
|
|
|
auto* context = this->get_active_context();
|
2019-01-29 17:58:34 +00:00
|
|
|
|
2021-03-23 16:35:02 +00:00
|
|
|
if (context->get_append_character() != 0) {
|
2022-03-16 22:38:08 +00:00
|
|
|
if (this->rc_line_buffer.length() > (this->rc_match_start + len - 1)
|
|
|
|
&& this->rc_line_buffer[this->rc_match_start + len - 1]
|
|
|
|
== context->get_append_character())
|
|
|
|
{
|
2021-03-23 16:35:02 +00:00
|
|
|
len -= 1;
|
2022-03-16 22:38:08 +00:00
|
|
|
} else if (this->rc_line_buffer.length()
|
|
|
|
> (this->rc_match_start + len - 2)
|
|
|
|
&& this->rc_line_buffer[this->rc_match_start + len - 2]
|
|
|
|
== context->get_append_character())
|
|
|
|
{
|
2021-03-23 16:35:02 +00:00
|
|
|
len -= 2;
|
|
|
|
}
|
2019-01-29 17:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return this->rc_line_buffer.substr(this->rc_match_start, len);
|
|
|
|
}
|
2022-04-30 20:05:42 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
readline_curses::set_value(const std::string& value)
|
|
|
|
{
|
|
|
|
this->set_attr_value(attr_line_t::from_ansi_str(value.c_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
readline_curses::set_attr_value(const attr_line_t& value)
|
|
|
|
{
|
|
|
|
this->rc_value = value;
|
|
|
|
if (this->rc_value.length() > 1024) {
|
|
|
|
this->rc_value = this->rc_value.subline(0, 1024);
|
|
|
|
}
|
|
|
|
this->rc_value_expiration = time(nullptr) + VALUE_EXPIRATION;
|
|
|
|
this->set_needs_update();
|
|
|
|
}
|
2022-07-05 18:06:37 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
readline_curses::update_poll_set(std::vector<struct pollfd>& pollfds)
|
|
|
|
{
|
|
|
|
if (this->rc_pty[RCF_MASTER] != -1) {
|
|
|
|
pollfds.push_back((struct pollfd){this->rc_pty[RCF_MASTER], POLLIN, 0});
|
|
|
|
}
|
|
|
|
if (this->rc_command_pipe[RCF_MASTER] != -1) {
|
|
|
|
pollfds.push_back(
|
|
|
|
(struct pollfd){this->rc_command_pipe[RCF_MASTER], POLLIN, 0});
|
|
|
|
}
|
|
|
|
}
|
2023-06-17 15:44:01 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
readline_curses::window_change()
|
|
|
|
{
|
|
|
|
struct winsize ws;
|
|
|
|
|
|
|
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) {
|
|
|
|
throw error(errno);
|
|
|
|
}
|
|
|
|
if (this->vc_width > 0) {
|
|
|
|
ws.ws_col = this->vc_width;
|
|
|
|
} else if (this->vc_width < 0) {
|
|
|
|
ws.ws_col -= this->vc_left;
|
|
|
|
ws.ws_col += this->vc_width;
|
|
|
|
}
|
|
|
|
if (ioctl(this->rc_pty[RCF_MASTER], TIOCSWINSZ, &ws) == -1) {
|
|
|
|
throw error(errno);
|
|
|
|
}
|
|
|
|
kill(this->rc_child, SIGWINCH);
|
|
|
|
}
|