Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "deps/3rd/benchmark"]
path = deps/3rd/benchmark
url = https://github.com/google/benchmark
[submodule "deps/3rd/replxx"]
path = deps/3rd/replxx
url = https://github.com/AmokHuginnsson/replxx
9 changes: 6 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ OPTION(USE_SIMD "Use SIMD" OFF)
OPTION(USE_MUSL_LIBC "Use musl libc" OFF)
OPTION(WITH_CPPLINGS "Compile cpplings" ON)


MESSAGE(STATUS "HOME dir: $ENV{HOME}")
#SET(ENV{变量名} 值)
IF(WIN32)
Expand Down Expand Up @@ -158,7 +157,7 @@ IF (EXISTS /usr/local/lib64)
LINK_DIRECTORIES (/usr/local/lib64)
ENDIF ()

INCLUDE_DIRECTORIES(. ${PROJECT_SOURCE_DIR}/deps ${PROJECT_SOURCE_DIR}/src /usr/local/include)
INCLUDE_DIRECTORIES(. ${PROJECT_SOURCE_DIR}/deps ${PROJECT_SOURCE_DIR}/deps/3rd/usr/local/include ${PROJECT_SOURCE_DIR}/src /usr/local/include)

IF(WITH_UNIT_TESTS)
IF (ENABLE_COVERAGE)
Expand All @@ -172,6 +171,11 @@ SET(CMAKE_C_FLAGS ${CMAKE_COMMON_FLAGS})

MESSAGE(STATUS "CMAKE_CXX_FLAGS is " ${CMAKE_CXX_FLAGS})

# Line Reading configs
MESSAGE(STATUS "Using replxx for line reading")
FIND_PACKAGE(Threads REQUIRED)
FIND_PACKAGE(replxx REQUIRED)

# ADD_SUBDIRECTORY(src bin) bin 为目标目录, 可以省略
# ADD_SUBDIRECTORY(deps)
ADD_SUBDIRECTORY(src)
Expand All @@ -184,4 +188,3 @@ ENDIF(WITH_BENCHMARK)
IF(WITH_UNIT_TESTS)
ADD_SUBDIRECTORY(unittest)
ENDIF(WITH_UNIT_TESTS)

10 changes: 9 additions & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,15 @@ function do_init
mkdir -p build && \
cd build && \
${CMAKE_COMMAND_THIRD_PARTY} -DJSONCPP_WITH_TESTS=OFF -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF .. && \
${MAKE_COMMAND} && \
${MAKE_COMMAND} -j4 && \
${MAKE_COMMAND} install

# build replxx
cd ${TOPDIR}/deps/3rd/replxx && \
mkdir -p build && \
cd build && \
${CMAKE_COMMAND_THIRD_PARTY} .. -DCMAKE_BUILD_TYPE=Release -DREPLXX_BUILD_EXAMPLES=OFF -DREPLXX_BUILD_PACKAGE=OFF && \
${MAKE_COMMAND} -j4 && \
${MAKE_COMMAND} install

cd $current_dir
Expand Down
12 changes: 0 additions & 12 deletions cmake/readline.cmake

This file was deleted.

1 change: 1 addition & 0 deletions deps/3rd/replxx
Submodule replxx added at 1f149b
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ RUN apt-get update && apt-get install -y locales apt-utils && rm -rf /var/lib/ap
# && apt-get update

RUN apt-get update \
&& apt-get install -y build-essential gdb cmake git wget flex texinfo libreadline-dev diffutils bison \
&& apt-get install -y build-essential gdb cmake git wget flex texinfo diffutils bison \
&& apt-get install -y clang-format vim sudo

RUN apt-get install -y openssh-server
Expand Down
3 changes: 3 additions & 0 deletions src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ IF(USE_MUSL_LIBC)
TARGET_LINK_LIBRARIES(common execinfo)
ENDIF(USE_MUSL_LIBC)

TARGET_LINK_LIBRARIES(common replxx::replxx)
MESSAGE(STATUS "common uses replxx")

# 编译静态库时,自动会把同名的动态库给删除, 因此需要临时设置一下
SET_TARGET_PROPERTIES(common PROPERTIES CLEAN_DIRECT_OUTPUT 1)

Expand Down
87 changes: 87 additions & 0 deletions src/common/linereader/line_reader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

//
// Created by Willaaaaaaa in 2025
//

#include "common/linereader/line_reader.h"
#include "common/lang/string.h"

namespace common {
LineReader MiniobLineReader::reader_;
bool MiniobLineReader::is_first_call_ = true;
time_t MiniobLineReader::previous_history_save_time_ = 0;
int MiniobLineReader::history_save_interval_ = 5;

std::string MiniobLineReader::my_readline(const std::string &prompt, const std::string &history_file)
{
if (is_first_call_) {
reader_.history_load(history_file);
is_first_call_ = false;
}

const char *cinput = nullptr;
cinput = reader_.input(prompt);
if (cinput == nullptr) {
return "";
}

std::string line = cinput;
cinput = nullptr;

if (line.empty()) {
return "";
}

bool is_valid_input = false;
for (auto c : line) {
if (!isspace(c)) {
is_valid_input = true;
break;
}
}

if (is_valid_input) {
reader_.history_add(line);
check_and_save_history(history_file);
}

return line;
}

bool MiniobLineReader::is_exit_command(const std::string &cmd, const std::string &history_file)
{
std::string lower_cmd = cmd;
common::str_to_lower(lower_cmd);

bool is_exit = lower_cmd.compare(0, 4, "exit") == 0 || lower_cmd.compare(0, 3, "bye") == 0 ||
lower_cmd.compare(0, 2, "\\q") == 0 || lower_cmd.compare(0, 11, "interrupted") == 0;

if (is_exit) {
reader_.history_save(history_file);
}

return is_exit;
}

bool MiniobLineReader::save_history(const std::string &history_file) { return reader_.history_save(history_file); }

bool MiniobLineReader::check_and_save_history(const std::string &history_file)
{
time_t current_time = time(nullptr);
if (current_time - previous_history_save_time_ > history_save_interval_) {
reader_.history_save(history_file);
previous_history_save_time_ = current_time;
return true;
}
return false;
}
} // namespace common
64 changes: 64 additions & 0 deletions src/common/linereader/line_reader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

//
// Created by Willaaaaaaa in 2025
//

#ifndef COMMON_LINE_READER_H
#define COMMON_LINE_READER_H

#include "replxx.hxx"
using LineReader = replxx::Replxx;

namespace common {
class MiniobLineReader
{
public:
/**
* @brief Read a line from input
* @param prompt The prompt to display
* @param history_file path/to/file
* @return char* to input string or nullptr
*/
static std::string my_readline(const std::string &prompt, const std::string &history_file);

/**
* @brief Check if the command is an exit command
* @param cmd The input command
* @param history_file path/to/file
* @return True if the command is an exit command
*/
static bool is_exit_command(const std::string &cmd, const std::string &history_file);

/**
* @brief Save history to file
* @param history_file path/to/file
* @return True if save succeed
*/
static bool save_history(const std::string &history_file);

private:
/**
* @brief Check if history should be auto-saved
* @param history_file path/to/file
* @return True if history should be saved due to time interval
*/
static bool check_and_save_history(const std::string &history_file);

private:
static LineReader reader_;
static bool is_first_call_;
static time_t previous_history_save_time_;
static int history_save_interval_;
};
} // namespace common

#endif // COMMON_LINE_READER_H
12 changes: 0 additions & 12 deletions src/obclient/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
ADD_EXECUTABLE(obclient)
MESSAGE("Begin to build " obclient)

INCLUDE(readline)
MINIOB_FIND_READLINE()

IF (HAVE_READLINE)
TARGET_LINK_LIBRARIES(obclient ${READLINE_LIBRARY})
TARGET_INCLUDE_DIRECTORIES(obclient PRIVATE ${READLINE_INCLUDE_DIR})
ADD_DEFINITIONS(-DUSE_READLINE)
MESSAGE ("obclient use readline")
ELSE ()
MESSAGE ("readline is not found")
ENDIF()

FILE(GLOB_RECURSE ALL_SRC *.cpp)
FOREACH (F ${ALL_SRC})

Expand Down
87 changes: 13 additions & 74 deletions src/obclient/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,72 +29,15 @@ See the Mulan PSL v2 for more details. */

#include "common/defs.h"
#include "common/lang/string.h"

#ifdef USE_READLINE
#include "readline/history.h"
#include "readline/readline.h"
#endif
#include "common/linereader/line_reader.h"

#define MAX_MEM_BUFFER_SIZE 8192
#define PORT_DEFAULT 6789

using namespace std;
using namespace common;

#ifdef USE_READLINE
const string HISTORY_FILE = string(getenv("HOME")) + "/.miniob.history";
time_t last_history_write_time = 0;

char *my_readline(const char *prompt)
{
int size = history_length;
if (size == 0) {
read_history(HISTORY_FILE.c_str());

FILE *fp = fopen(HISTORY_FILE.c_str(), "a");
if (fp != nullptr) {
fclose(fp);
}
}

char *line = readline(prompt);
if (line != nullptr && line[0] != 0) {
add_history(line);
if (time(NULL) - last_history_write_time > 5) {
write_history(HISTORY_FILE.c_str());
}
// append_history doesn't work on some readlines
// append_history(1, HISTORY_FILE.c_str());
}
return line;
}
#else // USE_READLINE
char *my_readline(const char *prompt)
{
char *buffer = (char *)malloc(MAX_MEM_BUFFER_SIZE);
if (nullptr == buffer) {
fprintf(stderr, "failed to alloc line buffer");
return nullptr;
}
fprintf(stdout, "%s", prompt);
char *s = fgets(buffer, MAX_MEM_BUFFER_SIZE, stdin);
if (nullptr == s) {
fprintf(stderr, "failed to read message from console");
free(buffer);
return nullptr;
}
return buffer;
}
#endif // USE_READLINE

/* this function config a exit-cmd list, strncasecmp func truncate the command from terminal according to the number,
'strncasecmp("exit", cmd, 4)' means that obclient read command string from terminal, truncate it to 4 chars from
the beginning, then compare the result with 'exit', if they match, exit the obclient.
*/
bool is_exit_command(const char *cmd)
{
return 0 == strncasecmp("exit", cmd, 4) || 0 == strncasecmp("bye", cmd, 3) || 0 == strncasecmp("\\q", cmd, 2);
}
const std::string LINE_HISTORY_FILE = "./.obclient.history";

int init_unix_sock(const char *unix_sock_path)
{
Expand Down Expand Up @@ -188,26 +131,23 @@ int main(int argc, char *argv[])

char send_buf[MAX_MEM_BUFFER_SIZE];

char *input_command = nullptr;
while ((input_command = my_readline(prompt_str)) != nullptr) {
if (common::is_blank(input_command)) {
free(input_command);
input_command = nullptr;
std::string input_command = "";

while (true) {
input_command = MiniobLineReader::my_readline(prompt_str, LINE_HISTORY_FILE);

if (input_command.empty() || common::is_blank(input_command.c_str())) {
continue;
}

if (is_exit_command(input_command)) {
free(input_command);
input_command = nullptr;
if (MiniobLineReader::is_exit_command(input_command, LINE_HISTORY_FILE)) {
break;
}

if ((send_bytes = write(sockfd, input_command, strlen(input_command) + 1)) == -1) { // TODO writen
if ((send_bytes = write(sockfd, input_command.c_str(), input_command.length() + 1)) == -1) { // TODO writen
fprintf(stderr, "send error: %d:%s \n", errno, strerror(errno));
exit(1);
}
free(input_command);
input_command = nullptr;

memset(send_buf, 0, sizeof(send_buf));

Expand Down Expand Up @@ -237,11 +177,10 @@ int main(int argc, char *argv[])
}
}

if (input_command != nullptr) {
free(input_command);
input_command = nullptr;
}
close(sockfd);

MiniobLineReader::save_history(LINE_HISTORY_FILE);
printf("Command history saved to: %s\n", LINE_HISTORY_FILE.c_str());

return 0;
}
Loading