Skip to content

Commit ebdddb3

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

File tree

4 files changed

+65
-87
lines changed

4 files changed

+65
-87
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: 57 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -12,118 +12,95 @@ 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);
60+
61+
char *line = strdup(cinput);
62+
if (line == nullptr) {
63+
LOG_WARN("Failed to dup input string from replxx");
10564
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
11372
the beginning, then compare the result with 'exit', if they match, exit the obclient.
11473
*/
11574
bool is_exit_command(const char *cmd)
11675
{
117-
return 0 == strncasecmp("exit", cmd, 4)
118-
|| 0 == strncasecmp("bye", cmd, 3)
119-
|| 0 == strncasecmp("\\q", cmd, 2)
120-
|| 0 == strncasecmp("interrupted", cmd, 11);
76+
return 0 == strncasecmp("exit", cmd, 4) || 0 == strncasecmp("bye", cmd, 3) || 0 == strncasecmp("\\q", cmd, 2) ||
77+
0 == strncasecmp("interrupted", cmd, 11);
12178
}
12279

12380
char *read_command()
12481
{
125-
const char *prompt_str = "miniob > ";
126-
char *input_command = my_readline(prompt_str);
82+
const char *prompt_str = "miniob > ";
83+
84+
static bool is_first_call = true;
85+
if (is_first_call) {
86+
// rx.set_max_history_size(MAX_HISTORY_SIZE);
87+
// rx.set_unique_history(true);
88+
89+
rx.history_load(REPLXX_HISTORY_FILE);
90+
rx.install_window_change_handler();
91+
is_first_call = false;
92+
}
93+
94+
char *input_command = my_readline(prompt_str);
95+
96+
static time_t previous_history_save_time = 0;
97+
if (input_command != nullptr && input_command[0] != '\0') {
98+
if (time(NULL) - previous_history_save_time > 5) {
99+
rx.history_save(REPLXX_HISTORY_FILE);
100+
previous_history_save_time = time(NULL);
101+
}
102+
}
103+
127104
return input_command;
128105
}
129106

@@ -183,3 +160,9 @@ RC CliCommunicator::write_result(SessionEvent *event, bool &need_disconnect)
183160
need_disconnect = false;
184161
return rc;
185162
}
163+
164+
CliCommunicator::~CliCommunicator()
165+
{
166+
rx.history_save(REPLXX_HISTORY_FILE);
167+
LOG_INFO("Command history saved to %s", REPLXX_HISTORY_FILE.c_str());
168+
}

src/observer/net/cli_communicator.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ See the Mulan PSL v2 for more details. */
2525
class CliCommunicator : public PlainCommunicator
2626
{
2727
public:
28-
CliCommunicator() = default;
29-
virtual ~CliCommunicator() = default;
28+
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)