Skip to content

Commit e7a17d1

Browse files
committed
feat(cli): enable cli history via replxx
1 parent 437d751 commit e7a17d1

File tree

4 files changed

+62
-82
lines changed

4 files changed

+62
-82
lines changed

build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ function do_init
115115
cd ${TOPDIR}/deps/3rd/replxx && \
116116
mkdir -p build && \
117117
cd build && \
118-
${CMAKE_COMMAND_THIRD_PARTY} .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DREPLXX_BUILD_EXAMPLES=OFF -DREPLXX_BUILD_PACKAGE=OFF && \
118+
${CMAKE_COMMAND_THIRD_PARTY} .. -DCMAKE_BUILD_TYPE=Release -DREPLXX_BUILD_EXAMPLES=OFF -DREPLXX_BUILD_PACKAGE=OFF && \
119119
${MAKE_COMMAND} -j4 && \
120120
${MAKE_COMMAND} install
121121

src/observer/CMakeLists.txt

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,11 @@ ADD_EXECUTABLE(observer ${MAIN_SRC})
4242
TARGET_LINK_LIBRARIES(observer observer_static)
4343

4444
ADD_LIBRARY(observer_static STATIC ${LIB_SRC})
45-
INCLUDE (readline)
46-
MINIOB_FIND_READLINE()
47-
IF (HAVE_READLINE)
48-
TARGET_LINK_LIBRARIES(observer_static ${READLINE_LIBRARY})
49-
TARGET_INCLUDE_DIRECTORIES(observer_static PRIVATE ${READLINE_INCLUDE_DIR})
50-
ADD_DEFINITIONS(-DUSE_READLINE)
51-
MESSAGE ("observer_static use readline ${READLINE_INCLUDE_DIR}")
52-
ELSE ()
53-
MESSAGE ("readline is not found")
54-
ENDIF()
45+
46+
find_package(Threads REQUIRED)
47+
find_package(replxx REQUIRED)
48+
TARGET_LINK_LIBRARIES(observer_static replxx::replxx)
49+
MESSAGE ("observer_static use replxx")
5550

5651
SET_TARGET_PROPERTIES(observer_static PROPERTIES OUTPUT_NAME observer)
5752
TARGET_LINK_LIBRARIES(observer_static ${LIBRARIES} oblsm)

src/observer/net/cli_communicator.cpp

Lines changed: 55 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -12,101 +12,60 @@ See the Mulan PSL v2 for more details. */
1212
// Created by Wangyunlai on 2023/06/25.
1313
//
1414

15-
#include <setjmp.h>
16-
#include <signal.h>
15+
#include <string>
1716

1817
#include "net/cli_communicator.h"
1918
#include "common/lang/string.h"
2019
#include "common/log/log.h"
21-
#include "common/os/signal.h"
2220
#include "event/session_event.h"
2321
#include "net/buffered_writer.h"
2422
#include "session/session.h"
25-
26-
#ifdef USE_READLINE
27-
#include "readline/history.h"
28-
#include "readline/readline.h"
29-
#endif
23+
#include "replxx.hxx"
3024

3125
#define MAX_MEM_BUFFER_SIZE 8192
3226
#define PORT_DEFAULT 6789
3327

3428
using namespace common;
3529

36-
#ifdef USE_READLINE
37-
const string HISTORY_FILE = string(getenv("HOME")) + "/.miniob.history";
38-
time_t last_history_write_time = 0;
39-
sigjmp_buf ctrlc_buf;
40-
bool ctrlc_flag = false;
41-
42-
void handle_signals(int signo) {
43-
if (signo == SIGINT) {
44-
ctrlc_flag = true;
45-
siglongjmp(ctrlc_buf, 1);
46-
}
47-
}
30+
static replxx::Replxx rx;
31+
const std::string REPLXX_HISTORY_FILE = "./.miniob.history";
32+
// const int MAX_HISTORY_SIZE = 500; // default: 1000
4833

4934
char *my_readline(const char *prompt)
5035
{
51-
static sighandler_t setup_signal_handler = signal(SIGINT, handle_signals);
52-
(void)setup_signal_handler;
36+
char const* cinput = nullptr;
5337

54-
int size = history_length;
55-
if (size == 0) {
56-
read_history(HISTORY_FILE.c_str());
57-
58-
FILE *fp = fopen(HISTORY_FILE.c_str(), "a");
59-
if (fp != nullptr) {
60-
fclose(fp);
61-
}
38+
try {
39+
cinput = rx.input(prompt);
40+
} catch (std::exception const& e) {
41+
LOG_WARN("replxx input error: %s", e.what());
42+
return nullptr;
6243
}
6344

64-
while ( sigsetjmp( ctrlc_buf, 1 ) != 0 );
65-
66-
if (ctrlc_flag) {
67-
char *line = (char *)malloc(strlen("exit") + 1);
68-
strcpy(line, "exit");
69-
printf("\n");
70-
return line;
45+
if (cinput == nullptr) {
46+
return nullptr;
7147
}
7248

73-
char *line = readline(prompt);
74-
if (line != nullptr && line[0] != 0) {
75-
add_history(line);
76-
if (time(NULL) - last_history_write_time > 5) {
77-
write_history(HISTORY_FILE.c_str());
49+
bool is_valid_input = false;
50+
for (auto c = cinput; *c != '\0'; ++c) {
51+
if (!isspace(*c)) {
52+
is_valid_input = true;
53+
break;
7854
}
79-
// append_history doesn't work on some readlines
80-
// append_history(1, HISTORY_FILE.c_str());
8155
}
82-
return line;
83-
}
84-
#else // USE_READLINE
85-
char *my_readline(const char *prompt)
86-
{
87-
char *buffer = (char *)malloc(MAX_MEM_BUFFER_SIZE);
88-
if (nullptr == buffer) {
89-
LOG_WARN("failed to alloc line buffer");
90-
return nullptr;
56+
57+
if (is_valid_input) {
58+
rx.history_add(cinput);
9159
}
92-
fprintf(stdout, "%s", prompt);
93-
char *s = fgets(buffer, MAX_MEM_BUFFER_SIZE, stdin);
94-
if (nullptr == s) {
95-
if (ferror(stdin) || feof(stdin)) {
96-
LOG_WARN("failed to read line: %s", strerror(errno));
97-
}
98-
/* EINTR(4):Interrupted system call */
99-
if (errno == EINTR) {
100-
strncpy(buffer, "interrupted", MAX_MEM_BUFFER_SIZE);
101-
fprintf(stdout, "\n");
102-
return buffer;
103-
}
104-
free(buffer);
105-
return nullptr;
60+
61+
char *line = strdup(cinput);
62+
if (line == nullptr) {
63+
LOG_WARN("Failed to dup input string from replxx");
64+
return nullptr;
10665
}
107-
return buffer;
66+
67+
return line;
10868
}
109-
#endif // USE_READLINE
11069

11170
/* this function config a exit-cmd list, strncasecmp func truncate the command from terminal according to the number,
11271
'strncasecmp("exit", cmd, 4)' means that obclient read command string from terminal, truncate it to 4 chars from
@@ -123,7 +82,27 @@ bool is_exit_command(const char *cmd)
12382
char *read_command()
12483
{
12584
const char *prompt_str = "miniob > ";
126-
char *input_command = my_readline(prompt_str);
85+
86+
static bool is_first_call = true;
87+
if (is_first_call) {
88+
// rx.set_max_history_size(MAX_HISTORY_SIZE);
89+
// rx.set_unique_history(true);
90+
91+
rx.history_load(REPLXX_HISTORY_FILE);
92+
rx.install_window_change_handler();
93+
is_first_call = false;
94+
}
95+
96+
char *input_command = my_readline(prompt_str);
97+
98+
static time_t previous_history_save_time = 0;
99+
if (input_command != nullptr && input_command[0] != '\0') {
100+
if (time(NULL) - previous_history_save_time > 5) {
101+
rx.history_save(REPLXX_HISTORY_FILE);
102+
previous_history_save_time = time(NULL);
103+
}
104+
}
105+
127106
return input_command;
128107
}
129108

@@ -183,3 +162,9 @@ RC CliCommunicator::write_result(SessionEvent *event, bool &need_disconnect)
183162
need_disconnect = false;
184163
return rc;
185164
}
165+
166+
CliCommunicator::~CliCommunicator()
167+
{
168+
rx.history_save(REPLXX_HISTORY_FILE);
169+
LOG_INFO("Command history saved to %s", REPLXX_HISTORY_FILE.c_str());
170+
}

src/observer/net/cli_communicator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class CliCommunicator : public PlainCommunicator
2626
{
2727
public:
2828
CliCommunicator() = default;
29-
virtual ~CliCommunicator() = default;
29+
virtual ~CliCommunicator();
3030

3131
RC init(int fd, unique_ptr<Session> session, const string &addr) override;
3232
RC read_event(SessionEvent *&event) override;

0 commit comments

Comments
 (0)