diff --git a/README.md b/README.md index 1841f7e..5253d59 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,12 @@ A simple CLI project that has a command history, an easy function to register co Personally i'm using this project for my auth backend as a local control panel because I personally hate working with websites. In the future I'll maybe add in a scrolling feature since right now it only display's the last 20ish command responses/logs but everything gets logged to a file so I don't see the need for it right now. To use the command history, it's just like any other cli you just use your arrow keys to cycle through your history. -This is only for windows. I'm sure it's fairly easy to port to linux but I havent tried to. Only reason it won' work on linux is the fact that I use windows functions for getting console size and console cursor position. +You can compile it on almost any Linux using: +``` +cd src +g++ -std=c++17 -I../includes -o LittleCLI main.cpp command.cpp console.cpp inputhandler.cpp +``` Registering commands is very simple either with a lambda funciton or just another function present. diff --git a/includes/command.hpp b/includes/command.hpp index ec235df..edbbb09 100644 --- a/includes/command.hpp +++ b/includes/command.hpp @@ -6,8 +6,11 @@ #define LITTLECLI_COMMAND_HPP #include "console.hpp" -#include #include +#include +#include +#include +#include namespace command { inline std::unordered_map)> commands; diff --git a/includes/console.hpp b/includes/console.hpp index c87aab3..1b1ff5a 100644 --- a/includes/console.hpp +++ b/includes/console.hpp @@ -8,11 +8,12 @@ #include #include #include -#include +#include +#include namespace console { - inline HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); inline int console_height = 20; + inline int console_width = 80; inline std::deque log_buffer; inline std::string current_input = ""; @@ -24,4 +25,5 @@ namespace console { } + #endif //LITTLECLI_CONSOLE_HPP diff --git a/includes/inputhandler.hpp b/includes/inputhandler.hpp index 95ff572..da3dcb0 100644 --- a/includes/inputhandler.hpp +++ b/includes/inputhandler.hpp @@ -6,7 +6,6 @@ #define LITTLECLI_INPUTHANDLER_HPP #include "command.hpp" -#include namespace input { std::string read_input(); diff --git a/src/command.cpp b/src/command.cpp index 1e202f5..f889a6b 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -3,6 +3,10 @@ // #include "command.hpp" +#include +#include +#include +#include void command::handle_input(std::string input) { std::istringstream stream(input); @@ -26,4 +30,4 @@ void command::handle_input(std::string input) { void command::register_command(std::string command, void(*function)(std::vector)) { commands[command] = function; -} \ No newline at end of file +} diff --git a/src/console.cpp b/src/console.cpp index 2da5597..9af8fda 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -5,40 +5,34 @@ #include "console.hpp" void console::get_console_size() { - CONSOLE_SCREEN_BUFFER_INFO csbi; - if (GetConsoleScreenBufferInfo(console_handle, &csbi)) { - console_height = csbi.srWindow.Bottom - csbi.srWindow.Top - 2; + struct winsize w; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) { + console_height = w.ws_row - 2; + console_width = w.ws_col; } } void console::clear_log_area() { - COORD cursorPos = {0, 0}; - DWORD written; - CONSOLE_SCREEN_BUFFER_INFO csbi; - GetConsoleScreenBufferInfo(console_handle, &csbi); - int logAreaSize = csbi.dwSize.X * console_height; - - FillConsoleOutputCharacterA(console_handle, ' ', logAreaSize, cursorPos, &written); - FillConsoleOutputAttribute(console_handle, csbi.wAttributes, logAreaSize, cursorPos, &written); + get_console_size(); + std::cout << "\033[H"; + std::cout << "\033[J"; + std::cout.flush(); } void console::print_log() { get_console_size(); clear_log_area(); - - COORD cursorPos = {0, 0}; - SetConsoleCursorPosition(console_handle, cursorPos); + std::cout << "\033[H"; for (const auto& log : log_buffer) { std::cout << log << std::endl; } - - cursorPos.Y = console_height; - cursorPos.X = 2; - SetConsoleCursorPosition(console_handle, cursorPos); + std::cout << "\033[" << (console_height + 1) << ";3H"; std::cout << current_input; + std::cout.flush(); } + void console::add_log(const std::string& message) { if (log_buffer.size() >= console_height - 1) log_buffer.pop_front(); @@ -46,15 +40,8 @@ void console::add_log(const std::string& message) { } void console::clear_input_line() { - COORD cursorPos; - CONSOLE_SCREEN_BUFFER_INFO csbi; - GetConsoleScreenBufferInfo(console_handle, &csbi); - cursorPos.X = 0; - cursorPos.Y = console_height; - - SetConsoleCursorPosition(console_handle, cursorPos); - std::cout << std::string(csbi.dwSize.X, ' '); // Overwrite input line - - cursorPos.X = 2; - SetConsoleCursorPosition(console_handle, cursorPos); -} \ No newline at end of file + std::cout << "\033[" << (console_height + 1) << ";0H"; + std::cout << "\033[2K"; + std::cout << "\033[" << (console_height + 1) << ";3H"; + std::cout.flush(); +} diff --git a/src/inputhandler.cpp b/src/inputhandler.cpp index 7456a21..2f948d0 100644 --- a/src/inputhandler.cpp +++ b/src/inputhandler.cpp @@ -1,48 +1,75 @@ // // Created by littleegg on 3/28/2025. +// Adapted for Linux terminal input handling. // #include "inputhandler.hpp" +#include "console.hpp" +#include "command.hpp" +#include +#include +#include +#include -std::string input::read_input() { - console::current_input.clear(); - char ch; +static int getch() { + struct termios oldt, newt; + int ch; + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); // I still dunno why it works + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + ch = getchar(); + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + return ch; +} - while (true) { - ch = _getch(); +namespace input { - if (ch == '\r') { - std::cout << std::endl; - return console::current_input; - } else if (ch == '\b') { - if (!console::current_input.empty()) { - console::current_input.pop_back(); - std::cout << "\b \b"; - } - } else if (ch == -32) { - ch = _getch(); + std::string read_input() { + console::current_input.clear(); + + while (true) { + int ch = getch(); - if (ch == 72) { - if (command::history_index < (int)command::command_history.size() - 1) { - command::history_index++; - console::current_input = command::command_history[command::command_history.size() - 1 - command::history_index]; - console::clear_input_line(); - std::cout << ">> " << console::current_input; + if (ch == '\n' || ch == '\r') { + std::cout << std::endl; + return console::current_input; + } else if (ch == 127) { // back + if (!console::current_input.empty()) { + console::current_input.pop_back(); + std::cout << "\b \b"; + std::cout.flush(); } - } else if (ch == 80) { - if (command::history_index > 0) { - command::history_index--; - console::current_input = command::command_history[command::command_history.size() - 1 - command::history_index]; - } else { - command::history_index = -1; - console::current_input.clear(); + } else if (ch == 27) { + int ch1 = getch(); + if (ch1 == '[') { + int ch2 = getch(); + if (ch2 == 'A') { // up + if (command::history_index < (int)command::command_history.size() - 1) { + command::history_index++; + console::current_input = command::command_history[command::command_history.size() - 1 - command::history_index]; + console::clear_input_line(); + std::cout << ">> " << console::current_input; + std::cout.flush(); + } + } else if (ch2 == 'B') { // down + if (command::history_index > 0) { + command::history_index--; + console::current_input = command::command_history[command::command_history.size() - 1 - command::history_index]; + } else { + command::history_index = -1; + console::current_input.clear(); + } + console::clear_input_line(); + std::cout << ">> " << console::current_input; + std::cout.flush(); + } } - console::clear_input_line(); - std::cout << ">> " << console::current_input; + } else if (isprint(ch)) { + console::current_input += (char)ch; + std::cout << (char)ch; + std::cout.flush(); } - } else if (isprint(ch)) { // Printable characters - console::current_input += ch; - std::cout << ch; } } -} \ No newline at end of file +}