#include "filecomplete.h" #include #include #ifdef _MSC_VER #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #endif #include #include #include #define MAX_OF(x, y) (((x) > (y)) ? (x) : (y)) #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN64) #ifndef OS_WINDOWS #define OS_WINDOWS #include #include #include #endif #elif defined(__APPLE__) || defined(__unix__) || defined(__unix) || \ defined(unix) || defined(__linux__) #ifndef OS_UNIX #define OS_UNIX #include #include #include #endif #else #error Unknown environment! #endif #if defined(OS_WINDOWS) #define ENTER 13 #define BACKSPACE 8 #define LEFT 75 #define RIGHT 77 #define UP 72 #define DOWN 80 #define DEL 83 #define CTRL_C 3 #define SPECIAL_SEQ_1 0 #define SPECIAL_SEQ_2 224 #define COLOR_TYPE uint16_t #define DEFAULT_TITLE_COLOR 160 #define DEFAULT_PREDICT_COLOR 8 #define DEFAULT_MAIN_COLOR 7 #elif defined(OS_UNIX) #define ENTER 10 #define BACKSPACE 127 #define LEFT 68 #define RIGHT 67 #define UP 65 #define DOWN 66 #define DEL 51 #define DEL_AFTER 126 #define SPECIAL_SEQ_1 27 #define SPECIAL_SEQ_2 91 #define COLOR_TYPE char* #define DEFAULT_TITLE_COLOR "0;30;102" #define DEFAULT_PREDICT_COLOR "90" #define DEFAULT_MAIN_COLOR "0" #endif #define SPACE 32 #define TAB 9 constexpr int buf_size = 1024 * 100; static std::vector> buf{}; static std::vector word{}; static size_t wo{}; static size_t len{}; static short cy{}; static short cx{}; void clear_line(); void set_cursor_x(short x); short get_cursor_y(); char* fc_readline(); void color_print(char* text, COLOR_TYPE color); std::pair get_wh() { HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE); if (h_console == NULL) { fprintf(stderr, "[ERROR] Couldn't handle terminal\n"); exit(1); } CONSOLE_SCREEN_BUFFER_INFO console_info; if (GetConsoleScreenBufferInfo(h_console, &console_info) == 0) { fprintf(stderr, "[ERROR] Couldn't get terminal info\n"); exit(1); } int cw = console_info.srWindow.Right - console_info.srWindow.Left + 1; int ch = console_info.srWindow.Bottom - console_info.srWindow.Top + 1; auto w = static_cast(cw); auto h = static_cast(ch); return std::make_pair(w, h); } void supply(std::vector& wch, char ch) { wch.push_back(ch); if (ch >= 0 && ch < 128) { return; } auto tch = _getch(); wch.push_back(tch); } void clear_line() { #if defined(OS_WINDOWS) // Get current terminal width short width = get_wh().first; if (width < 1) { fprintf(stderr, "[ERROR] Size of terminal is too small\n"); exit(1); } // Create long empty string char* empty = (char*)malloc(sizeof(char) * width); if (empty) { memset(empty, ' ', width); empty[width - 1] = '\0'; } HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cursorInfo; GetConsoleCursorInfo(hConsole, &cursorInfo); cursorInfo.bVisible = FALSE; SetConsoleCursorInfo(hConsole, &cursorInfo); // Clear line printf("\r%s\r", empty); // Free line free(empty); #elif defined(OS_UNIX) printf("\033[2K\r"); #endif } void set_cursor_x(short x) { #if defined(OS_WINDOWS) HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE); if (h_console == NULL) { fprintf(stderr, "[ERROR] Couldn't handle terminal\n"); exit(1); } auto wh = get_wh(); // Create position COORD xy; xy.Y = get_cursor_y(); short px = x % wh.first - 1; xy.X = px < 0 ? 0 : px; // Set cursor position if (SetConsoleCursorPosition(h_console, xy) == 0) { auto code = GetLastError(); fprintf(stderr, "[ERROR] Couldn't set terminal cursor position, err=%lu\n", code); exit(1); } #elif defined(OS_UNIX) printf("\033[%d;%dH", get_cursor_y(), x); #endif } short get_cursor_y() { #if defined(OS_WINDOWS) HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE); if (h_console == NULL) { fprintf(stderr, "[ERROR] Couldn't handle terminal\n"); exit(1); } // Get terminal info CONSOLE_SCREEN_BUFFER_INFO console_info; if (GetConsoleScreenBufferInfo(h_console, &console_info) == 0) { fprintf(stderr, "[ERROR] Couldn't get terminal Y position\n"); exit(1); } // Return Y position return console_info.dwCursorPosition.Y; #elif defined(OS_UNIX) struct termios old_attr, new_attr; char ch, buf[30] = {0}; int i = 0, pow = 1, y = 0; // Backup terminal attributes if (tcgetattr(STDIN_FILENO, &new_attr) == -1) { fprintf(stderr, "[ERROR] Couldn't get terminal attributes\n"); exit(1); } // Disable echo old_attr = new_attr; old_attr.c_lflag &= ~(ICANON | ECHO); if (tcsetattr(STDIN_FILENO, TCSANOW, &old_attr) == -1) { fprintf(stderr, "[ERROR] Couldn't set terminal attributes\n"); exit(1); } // Get info about cursor if (write(STDOUT_FILENO, "\033[6n", 4) != 4) { fprintf(stderr, "[ERROR] Couldn't get cursor information\n"); exit(1); } // Get ^[[{this};1R value for (ch = 0; ch != 'R'; i++) { if (read(STDIN_FILENO, &ch, 1) != 1) { fprintf(stderr, "[ERROR] Couldn't read cursor information"); exit(1); } buf[i] = ch; } i -= 2; while (buf[i] != ';') { i -= 1; } i -= 1; while (buf[i] != '[') { y = y + (buf[i] - '0') * pow; pow *= 10; i -= 1; } // Reset attributes if (tcsetattr(0, TCSANOW, &new_attr) == -1) { fprintf(stderr, "[ERROR] Couldn't reset terminal attributes\n"); exit(1); } return (short)y; #endif } char* fc_readline() { char* buffer = (char*)calloc((size_t)buf_size, sizeof(char)); if (buffer == NULL) { fprintf(stderr, "[ERROR] Couldn't allocate memory for buffer\n"); exit(1); } // Current hint number int hint_num = 0; while (1) { word.clear(); clear_line(); // Print current buffer color_print(buffer, DEFAULT_MAIN_COLOR); // Move cursor int cur_pos{}; for (int i = 0; i < buf.size() && i < wo; ++i) { cur_pos += static_cast(buf[i].size()); } set_cursor_x(cur_pos + 1); // Read character from console int ch = _getch(); switch (ch) { case ENTER: return nullptr; #if defined(OS_WINDWS) case CTRL_C: { exit(0); } #endif case BACKSPACE: { if (wo > 0) { for (size_t i = wo - 1; i < buf.size() - 1; ++i) { buf[i] = buf[i + 1]; } --wo; --len; } break; } case TAB: { break; } #if defined(OS_WINDOWS) case SPECIAL_SEQ_1: case SPECIAL_SEQ_2: #elif defined(OS_UNIX) case SPECIAL_SEQ_1: #endif { #if defined(OS_UNIX) if (_getch() != SPECIAL_SEQ_2) { continue; } #endif switch (_getch()) { case LEFT: wo = wo < 1 ? 0 : (--wo); break; case RIGHT: wo = wo >= len ? len : (++wo); break; case UP: break; case DOWN: break; case DEL: // Edit buffer like DELETE key #if defined(OS_UNIX) if (_getch() == DEL_AFTER) #endif { } break; default: break; } break; } default: { supply(word, ch); if (wo < buf.size()) { if (len >= buf.size()) { buf.resize(buf.size() * 2); } if (len > 0) { for (size_t i = len - 1; i >= wo; --i) { buf[i + 1] = buf[i]; } buf[wo] = word; } } else { buf.push_back(word); } ++wo; ++len; break; } } } return buffer; } void fc_free(char* str) { free(str); } void color_print(char* text, COLOR_TYPE color) { #if defined(OS_WINDOWS) HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE); if (h_console == NULL) { fprintf(stderr, "[ERROR] Couldn't handle terminal\n"); exit(1); } CONSOLE_SCREEN_BUFFER_INFO console_info; COLOR_TYPE backup; // Save current attributes if (GetConsoleScreenBufferInfo(h_console, &console_info) == 0) { fprintf(stderr, "[ERROR] Couldn't get terminal info\n"); exit(1); } auto wh = get_wh(); backup = console_info.wAttributes; // Print colored text if (SetConsoleTextAttribute(h_console, color) == 0) { fprintf(stderr, "[ERROR] Couldn't set terminal color\n"); exit(1); } std::string tbuf; for (size_t i = 0; i < buf.size() && i < len; ++i) { for (const auto& c : buf[i]) { tbuf.push_back(c); } } tbuf.push_back('\0'); printf("%s", tbuf.c_str()); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cursorInfo; GetConsoleCursorInfo(hConsole, &cursorInfo); cursorInfo.bVisible = TRUE; SetConsoleCursorInfo(hConsole, &cursorInfo); // Restore original color if (SetConsoleTextAttribute(h_console, backup) == 0) { fprintf(stderr, "[ERROR] Couldn't reset terminal color\n"); exit(1); } #elif defined(OS_UNIX) // Set new terminal color printf("\033["); printf("%s", color); printf("m"); // Print colored text printf("%s", text); // Resets the text to default color printf("\033[0m"); #endif }