1252884aeSStefan Eßer /* 2252884aeSStefan Eßer * ***************************************************************************** 3252884aeSStefan Eßer * 43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 5252884aeSStefan Eßer * 610328f8bSStefan Eßer * Copyright (c) 2018-2021 Gavin D. Howard and contributors. 7252884aeSStefan Eßer * 8252884aeSStefan Eßer * Redistribution and use in source and binary forms, with or without 9252884aeSStefan Eßer * modification, are permitted provided that the following conditions are met: 10252884aeSStefan Eßer * 11252884aeSStefan Eßer * * Redistributions of source code must retain the above copyright notice, this 12252884aeSStefan Eßer * list of conditions and the following disclaimer. 13252884aeSStefan Eßer * 14252884aeSStefan Eßer * * Redistributions in binary form must reproduce the above copyright notice, 15252884aeSStefan Eßer * this list of conditions and the following disclaimer in the documentation 16252884aeSStefan Eßer * and/or other materials provided with the distribution. 17252884aeSStefan Eßer * 18252884aeSStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19252884aeSStefan Eßer * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20252884aeSStefan Eßer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21252884aeSStefan Eßer * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22252884aeSStefan Eßer * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23252884aeSStefan Eßer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24252884aeSStefan Eßer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25252884aeSStefan Eßer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26252884aeSStefan Eßer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27252884aeSStefan Eßer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28252884aeSStefan Eßer * POSSIBILITY OF SUCH DAMAGE. 29252884aeSStefan Eßer * 30252884aeSStefan Eßer * ***************************************************************************** 31252884aeSStefan Eßer * 32252884aeSStefan Eßer * Adapted from the following: 33252884aeSStefan Eßer * 34252884aeSStefan Eßer * linenoise.c -- guerrilla line editing library against the idea that a 35252884aeSStefan Eßer * line editing lib needs to be 20,000 lines of C code. 36252884aeSStefan Eßer * 37252884aeSStefan Eßer * You can find the original source code at: 38252884aeSStefan Eßer * http://github.com/antirez/linenoise 39252884aeSStefan Eßer * 40252884aeSStefan Eßer * You can find the fork that this code is based on at: 41252884aeSStefan Eßer * https://github.com/rain-1/linenoise-mob 42252884aeSStefan Eßer * 43252884aeSStefan Eßer * ------------------------------------------------------------------------ 44252884aeSStefan Eßer * 45252884aeSStefan Eßer * This code is also under the following license: 46252884aeSStefan Eßer * 47252884aeSStefan Eßer * Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com> 48252884aeSStefan Eßer * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> 49252884aeSStefan Eßer * 50252884aeSStefan Eßer * Redistribution and use in source and binary forms, with or without 51252884aeSStefan Eßer * modification, are permitted provided that the following conditions are 52252884aeSStefan Eßer * met: 53252884aeSStefan Eßer * 54252884aeSStefan Eßer * * Redistributions of source code must retain the above copyright 55252884aeSStefan Eßer * notice, this list of conditions and the following disclaimer. 56252884aeSStefan Eßer * 57252884aeSStefan Eßer * * Redistributions in binary form must reproduce the above copyright 58252884aeSStefan Eßer * notice, this list of conditions and the following disclaimer in the 59252884aeSStefan Eßer * documentation and/or other materials provided with the distribution. 60252884aeSStefan Eßer * 61252884aeSStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 62252884aeSStefan Eßer * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 63252884aeSStefan Eßer * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 64252884aeSStefan Eßer * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 65252884aeSStefan Eßer * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 66252884aeSStefan Eßer * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 67252884aeSStefan Eßer * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 68252884aeSStefan Eßer * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 69252884aeSStefan Eßer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 70252884aeSStefan Eßer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 71252884aeSStefan Eßer * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 72252884aeSStefan Eßer * 73252884aeSStefan Eßer * ***************************************************************************** 74252884aeSStefan Eßer * 75252884aeSStefan Eßer * Definitions for line history. 76252884aeSStefan Eßer * 77252884aeSStefan Eßer */ 78252884aeSStefan Eßer 79252884aeSStefan Eßer #ifndef BC_HISTORY_H 80252884aeSStefan Eßer #define BC_HISTORY_H 81252884aeSStefan Eßer 82252884aeSStefan Eßer #ifndef BC_ENABLE_HISTORY 83252884aeSStefan Eßer #define BC_ENABLE_HISTORY (1) 84252884aeSStefan Eßer #endif // BC_ENABLE_HISTORY 85252884aeSStefan Eßer 86252884aeSStefan Eßer #if BC_ENABLE_HISTORY 87252884aeSStefan Eßer 88252884aeSStefan Eßer #include <stdbool.h> 89252884aeSStefan Eßer #include <stddef.h> 90252884aeSStefan Eßer 91252884aeSStefan Eßer #include <signal.h> 92252884aeSStefan Eßer 93*44d4804dSStefan Eßer #ifndef _WIN32 94252884aeSStefan Eßer #include <termios.h> 95252884aeSStefan Eßer #include <time.h> 96252884aeSStefan Eßer #include <unistd.h> 97252884aeSStefan Eßer #include <sys/select.h> 98*44d4804dSStefan Eßer #else // _WIN32 99*44d4804dSStefan Eßer 100*44d4804dSStefan Eßer #ifndef WIN32_LEAN_AND_MEAN 101*44d4804dSStefan Eßer #define WIN32_LEAN_AND_MEAN 102*44d4804dSStefan Eßer #endif // WIN32_LEAN_AND_MEAN 103*44d4804dSStefan Eßer 104*44d4804dSStefan Eßer #include <Windows.h> 105*44d4804dSStefan Eßer #include <io.h> 106*44d4804dSStefan Eßer #include <conio.h> 107*44d4804dSStefan Eßer 108*44d4804dSStefan Eßer #define strncasecmp _strnicmp 109*44d4804dSStefan Eßer #define strcasecmp _stricmp 110*44d4804dSStefan Eßer 111*44d4804dSStefan Eßer #endif // _WIN32 112252884aeSStefan Eßer 113252884aeSStefan Eßer #include <status.h> 114252884aeSStefan Eßer #include <vector.h> 115252884aeSStefan Eßer #include <read.h> 116252884aeSStefan Eßer 117252884aeSStefan Eßer #if BC_DEBUG_CODE 118252884aeSStefan Eßer #include <file.h> 119252884aeSStefan Eßer #endif // BC_DEBUG_CODE 120252884aeSStefan Eßer 121*44d4804dSStefan Eßer /// Default columns. 122252884aeSStefan Eßer #define BC_HIST_DEF_COLS (80) 123*44d4804dSStefan Eßer 124*44d4804dSStefan Eßer /// Max number of history entries. 125252884aeSStefan Eßer #define BC_HIST_MAX_LEN (128) 126*44d4804dSStefan Eßer 127*44d4804dSStefan Eßer /// Max length of a line. 128252884aeSStefan Eßer #define BC_HIST_MAX_LINE (4095) 129*44d4804dSStefan Eßer 130*44d4804dSStefan Eßer /// Max size for cursor position buffer. 131252884aeSStefan Eßer #define BC_HIST_SEQ_SIZE (64) 132252884aeSStefan Eßer 133*44d4804dSStefan Eßer /** 134*44d4804dSStefan Eßer * The number of entries in the history. 135*44d4804dSStefan Eßer * @param h The history data. 136*44d4804dSStefan Eßer */ 137252884aeSStefan Eßer #define BC_HIST_BUF_LEN(h) ((h)->buf.len - 1) 138*44d4804dSStefan Eßer 139*44d4804dSStefan Eßer /** 140*44d4804dSStefan Eßer * Read n characters into s and check the error. 141*44d4804dSStefan Eßer * @param s The buffer to read into. 142*44d4804dSStefan Eßer * @param n The number of bytes to read. 143*44d4804dSStefan Eßer * @return True if there was an error, false otherwise. 144*44d4804dSStefan Eßer */ 145252884aeSStefan Eßer #define BC_HIST_READ(s, n) (bc_history_read((s), (n)) == -1) 146252884aeSStefan Eßer 147*44d4804dSStefan Eßer /// Markers for direction when using arrow keys. 148252884aeSStefan Eßer #define BC_HIST_NEXT (false) 149252884aeSStefan Eßer #define BC_HIST_PREV (true) 150252884aeSStefan Eßer 151252884aeSStefan Eßer #if BC_DEBUG_CODE 152252884aeSStefan Eßer 153*44d4804dSStefan Eßer // These are just for debugging. 154*44d4804dSStefan Eßer 155252884aeSStefan Eßer #define BC_HISTORY_DEBUG_BUF_SIZE (1024) 156252884aeSStefan Eßer 157252884aeSStefan Eßer #define lndebug(...) \ 158252884aeSStefan Eßer do { \ 159252884aeSStefan Eßer if (bc_history_debug_fp.fd == 0) { \ 160252884aeSStefan Eßer bc_history_debug_buf = bc_vm_malloc(BC_HISTORY_DEBUG_BUF_SIZE); \ 161252884aeSStefan Eßer bc_file_init(&bc_history_debug_fp, \ 162252884aeSStefan Eßer open("/tmp/lndebug.txt", O_APPEND), \ 163252884aeSStefan Eßer BC_HISTORY_DEBUG_BUF_SIZE); \ 164252884aeSStefan Eßer bc_file_printf(&bc_history_debug_fp, \ 165252884aeSStefan Eßer "[%zu %zu %zu] p: %d, rows: %d, " \ 166252884aeSStefan Eßer "rpos: %d, max: %zu, oldmax: %d\n", \ 167252884aeSStefan Eßer l->len, l->pos, l->oldcolpos, plen, rows, rpos, \ 168252884aeSStefan Eßer l->maxrows, old_rows); \ 169252884aeSStefan Eßer } \ 170252884aeSStefan Eßer bc_file_printf(&bc_history_debug_fp, ", " __VA_ARGS__); \ 171252884aeSStefan Eßer bc_file_flush(&bc_history_debug_fp); \ 172252884aeSStefan Eßer } while (0) 173252884aeSStefan Eßer #else // BC_DEBUG_CODE 174252884aeSStefan Eßer #define lndebug(fmt, ...) 175252884aeSStefan Eßer #endif // BC_DEBUG_CODE 176252884aeSStefan Eßer 177*44d4804dSStefan Eßer /// An enum of useful actions. To understand what these mean, check terminal 178*44d4804dSStefan Eßer /// emulators for their shortcuts or the VT100 codes. 179252884aeSStefan Eßer typedef enum BcHistoryAction { 180252884aeSStefan Eßer 181252884aeSStefan Eßer BC_ACTION_NULL = 0, 182252884aeSStefan Eßer BC_ACTION_CTRL_A = 1, 183252884aeSStefan Eßer BC_ACTION_CTRL_B = 2, 184252884aeSStefan Eßer BC_ACTION_CTRL_C = 3, 185252884aeSStefan Eßer BC_ACTION_CTRL_D = 4, 186252884aeSStefan Eßer BC_ACTION_CTRL_E = 5, 187252884aeSStefan Eßer BC_ACTION_CTRL_F = 6, 188252884aeSStefan Eßer BC_ACTION_CTRL_H = 8, 189252884aeSStefan Eßer BC_ACTION_TAB = 9, 190252884aeSStefan Eßer BC_ACTION_LINE_FEED = 10, 191252884aeSStefan Eßer BC_ACTION_CTRL_K = 11, 192252884aeSStefan Eßer BC_ACTION_CTRL_L = 12, 193252884aeSStefan Eßer BC_ACTION_ENTER = 13, 194252884aeSStefan Eßer BC_ACTION_CTRL_N = 14, 195252884aeSStefan Eßer BC_ACTION_CTRL_P = 16, 1968c39e252SStefan Eßer BC_ACTION_CTRL_S = 19, 197252884aeSStefan Eßer BC_ACTION_CTRL_T = 20, 198252884aeSStefan Eßer BC_ACTION_CTRL_U = 21, 199252884aeSStefan Eßer BC_ACTION_CTRL_W = 23, 200252884aeSStefan Eßer BC_ACTION_CTRL_Z = 26, 201252884aeSStefan Eßer BC_ACTION_ESC = 27, 202*44d4804dSStefan Eßer BC_ACTION_CTRL_BSLASH = 28, 203252884aeSStefan Eßer BC_ACTION_BACKSPACE = 127 204252884aeSStefan Eßer 205252884aeSStefan Eßer } BcHistoryAction; 206252884aeSStefan Eßer 207252884aeSStefan Eßer /** 208252884aeSStefan Eßer * This represents the state during line editing. We pass this state 209252884aeSStefan Eßer * to functions implementing specific editing functionalities. 210252884aeSStefan Eßer */ 211252884aeSStefan Eßer typedef struct BcHistory { 212252884aeSStefan Eßer 213252884aeSStefan Eßer /// Edited line buffer. 214252884aeSStefan Eßer BcVec buf; 215252884aeSStefan Eßer 216252884aeSStefan Eßer /// The history. 217252884aeSStefan Eßer BcVec history; 218252884aeSStefan Eßer 2197e5c51e5SStefan Eßer /// Any material printed without a trailing newline. 2207e5c51e5SStefan Eßer BcVec extras; 2217e5c51e5SStefan Eßer 222252884aeSStefan Eßer /// Prompt to display. 223252884aeSStefan Eßer const char *prompt; 224252884aeSStefan Eßer 225252884aeSStefan Eßer /// Prompt length. 226252884aeSStefan Eßer size_t plen; 227252884aeSStefan Eßer 228252884aeSStefan Eßer /// Prompt column length. 229252884aeSStefan Eßer size_t pcol; 230252884aeSStefan Eßer 231252884aeSStefan Eßer /// Current cursor position. 232252884aeSStefan Eßer size_t pos; 233252884aeSStefan Eßer 234252884aeSStefan Eßer /// Previous refresh cursor column position. 235252884aeSStefan Eßer size_t oldcolpos; 236252884aeSStefan Eßer 237252884aeSStefan Eßer /// Number of columns in terminal. 238252884aeSStefan Eßer size_t cols; 239252884aeSStefan Eßer 240252884aeSStefan Eßer /// The history index we are currently editing. 241252884aeSStefan Eßer size_t idx; 242252884aeSStefan Eßer 243*44d4804dSStefan Eßer #ifndef _WIN32 244252884aeSStefan Eßer /// The original terminal state. 245252884aeSStefan Eßer struct termios orig_termios; 246*44d4804dSStefan Eßer #else // _WIN32 247*44d4804dSStefan Eßer DWORD orig_console_mode; 248*44d4804dSStefan Eßer #endif // _WIN32 249252884aeSStefan Eßer 250*44d4804dSStefan Eßer /// These next two are here because pahole found a 4 byte hole here. 251252884aeSStefan Eßer 252252884aeSStefan Eßer /// Whether we are in rawmode. 253252884aeSStefan Eßer bool rawMode; 254252884aeSStefan Eßer 255252884aeSStefan Eßer /// Whether the terminal is bad. 256252884aeSStefan Eßer bool badTerm; 257252884aeSStefan Eßer 258*44d4804dSStefan Eßer #ifndef _WIN32 259252884aeSStefan Eßer /// This is to check if stdin has more data. 260252884aeSStefan Eßer fd_set rdset; 261252884aeSStefan Eßer 262252884aeSStefan Eßer /// This is to check if stdin has more data. 263252884aeSStefan Eßer struct timespec ts; 264252884aeSStefan Eßer 265252884aeSStefan Eßer /// This is to check if stdin has more data. 266252884aeSStefan Eßer sigset_t sigmask; 267*44d4804dSStefan Eßer #endif // _WIN32 268252884aeSStefan Eßer 269252884aeSStefan Eßer } BcHistory; 270252884aeSStefan Eßer 271*44d4804dSStefan Eßer /** 272*44d4804dSStefan Eßer * Get a line from stdin using history. This returns a status because I don't 273*44d4804dSStefan Eßer * want to throw errors while the terminal is in raw mode. 274*44d4804dSStefan Eßer * @param h The history data. 275*44d4804dSStefan Eßer * @param vec A vector to put the line into. 276*44d4804dSStefan Eßer * @param prompt The prompt to display, if desired. 277*44d4804dSStefan Eßer * @return A status indicating an error, if any. Returning a status here 278*44d4804dSStefan Eßer * is better because if we throw an error out of history, we 279*44d4804dSStefan Eßer * leave the terminal in raw mode or in some other half-baked 280*44d4804dSStefan Eßer * state. 281*44d4804dSStefan Eßer */ 282252884aeSStefan Eßer BcStatus bc_history_line(BcHistory *h, BcVec *vec, const char *prompt); 283252884aeSStefan Eßer 284*44d4804dSStefan Eßer /** 285*44d4804dSStefan Eßer * Initialize history data. 286*44d4804dSStefan Eßer * @param h The struct to initialize. 287*44d4804dSStefan Eßer */ 288252884aeSStefan Eßer void bc_history_init(BcHistory *h); 289*44d4804dSStefan Eßer 290*44d4804dSStefan Eßer /** 291*44d4804dSStefan Eßer * Free history data (and recook the terminal). 292*44d4804dSStefan Eßer * @param h The struct to free. 293*44d4804dSStefan Eßer */ 294252884aeSStefan Eßer void bc_history_free(BcHistory *h); 295252884aeSStefan Eßer 296*44d4804dSStefan Eßer /** 297*44d4804dSStefan Eßer * Frees strings used by history. 298*44d4804dSStefan Eßer * @param str The string to free. 299*44d4804dSStefan Eßer */ 300*44d4804dSStefan Eßer void bc_history_string_free(void *str); 301*44d4804dSStefan Eßer 302*44d4804dSStefan Eßer // A list of terminals that don't work. 303252884aeSStefan Eßer extern const char *bc_history_bad_terms[]; 304*44d4804dSStefan Eßer 305*44d4804dSStefan Eßer // A tab in history and its length. 306252884aeSStefan Eßer extern const char bc_history_tab[]; 307252884aeSStefan Eßer extern const size_t bc_history_tab_len; 308*44d4804dSStefan Eßer 309*44d4804dSStefan Eßer // A ctrl+c string. 310252884aeSStefan Eßer extern const char bc_history_ctrlc[]; 311*44d4804dSStefan Eßer 312*44d4804dSStefan Eßer // UTF-8 data arrays. 313252884aeSStefan Eßer extern const uint32_t bc_history_wchars[][2]; 314252884aeSStefan Eßer extern const size_t bc_history_wchars_len; 315252884aeSStefan Eßer extern const uint32_t bc_history_combo_chars[]; 316252884aeSStefan Eßer extern const size_t bc_history_combo_chars_len; 317*44d4804dSStefan Eßer 318252884aeSStefan Eßer #if BC_DEBUG_CODE 319*44d4804dSStefan Eßer 320*44d4804dSStefan Eßer // Debug data. 321252884aeSStefan Eßer extern BcFile bc_history_debug_fp; 322252884aeSStefan Eßer extern char *bc_history_debug_buf; 323*44d4804dSStefan Eßer 324*44d4804dSStefan Eßer /** 325*44d4804dSStefan Eßer * A function to print keycodes for debugging. 326*44d4804dSStefan Eßer * @param h The history data. 327*44d4804dSStefan Eßer */ 328*44d4804dSStefan Eßer void bc_history_printKeyCodes(BcHistory* h); 329*44d4804dSStefan Eßer 330252884aeSStefan Eßer #endif // BC_DEBUG_CODE 331252884aeSStefan Eßer 332252884aeSStefan Eßer #endif // BC_ENABLE_HISTORY 333252884aeSStefan Eßer 334252884aeSStefan Eßer #endif // BC_HISTORY_H 335