Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
5 changes: 4 additions & 1 deletion includes/command.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
#define LITTLECLI_COMMAND_HPP

#include "console.hpp"
#include <unordered_map>
#include <sstream>
#include <vector>
#include <string>
#include <unordered_map>
#include <deque>

namespace command {
inline std::unordered_map<std::string, void(*)(std::vector<std::string>)> commands;
Expand Down
6 changes: 4 additions & 2 deletions includes/console.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
#include <iostream>
#include <string>
#include <deque>
#include <windows.h>
#include <sys/ioctl.h>
#include <unistd.h>

namespace console {
inline HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
inline int console_height = 20;
inline int console_width = 80;
inline std::deque<std::string> log_buffer;
inline std::string current_input = "";

Expand All @@ -24,4 +25,5 @@ namespace console {
}



#endif //LITTLECLI_CONSOLE_HPP
1 change: 0 additions & 1 deletion includes/inputhandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#define LITTLECLI_INPUTHANDLER_HPP

#include "command.hpp"
#include <conio.h>

namespace input {
std::string read_input();
Expand Down
6 changes: 5 additions & 1 deletion src/command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
//

#include "command.hpp"
#include <vector>
#include <string>
#include <unordered_map>
#include <deque>

void command::handle_input(std::string input) {
std::istringstream stream(input);
Expand All @@ -26,4 +30,4 @@ void command::handle_input(std::string input) {

void command::register_command(std::string command, void(*function)(std::vector<std::string>)) {
commands[command] = function;
}
}
47 changes: 17 additions & 30 deletions src/console.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,43 @@
#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();
log_buffer.push_back(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);
}
std::cout << "\033[" << (console_height + 1) << ";0H";
std::cout << "\033[2K";
std::cout << "\033[" << (console_height + 1) << ";3H";
std::cout.flush();
}
95 changes: 61 additions & 34 deletions src/inputhandler.cpp
Original file line number Diff line number Diff line change
@@ -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 <termios.h>
#include <unistd.h>
#include <cstdio>
#include <cctype>

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;
}
}
}
}