1/* 2 * ***************************************************************************** 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 2018-2023 Gavin D. Howard and contributors. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are met: 10 * 11 * * Redistributions of source code must retain the above copyright notice, this 12 * list of conditions and the following disclaimer. 13 * 14 * * Redistributions in binary form must reproduce the above copyright notice, 15 * this list of conditions and the following disclaimer in the documentation 16 * and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 * 30 * ***************************************************************************** 31 * 32 * Adapted from the following: 33 * 34 * linenoise.c -- guerrilla line editing library against the idea that a 35 * line editing lib needs to be 20,000 lines of C code. 36 * 37 * You can find the original source code at: 38 * http://github.com/antirez/linenoise 39 * 40 * You can find the fork that this code is based on at: 41 * https://github.com/rain-1/linenoise-mob 42 * 43 * ------------------------------------------------------------------------ 44 * 45 * This code is also under the following license: 46 * 47 * Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com> 48 * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> 49 * 50 * Redistribution and use in source and binary forms, with or without 51 * modification, are permitted provided that the following conditions are 52 * met: 53 * 54 * * Redistributions of source code must retain the above copyright 55 * notice, this list of conditions and the following disclaimer. 56 * 57 * * Redistributions in binary form must reproduce the above copyright 58 * notice, this list of conditions and the following disclaimer in the 59 * documentation and/or other materials provided with the distribution. 60 * 61 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 62 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 63 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 64 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 65 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 66 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 67 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 68 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 69 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 70 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 71 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 72 * 73 * ***************************************************************************** 74 * 75 * Definitions for line history. 76 * 77 */ 78 79#ifndef BC_HISTORY_H 80#define BC_HISTORY_H 81 82// These must come before the #if BC_ENABLE_LINE_LIB below because status.h 83// defines it. 84#include <status.h> 85#include <vector.h> 86 87#if BC_ENABLE_LINE_LIB 88 89#include <stdbool.h> 90#include <setjmp.h> 91#include <signal.h> 92 93extern sigjmp_buf bc_history_jmpbuf; 94extern volatile sig_atomic_t bc_history_inlinelib; 95 96#endif // BC_ENABLE_LINE_LIB 97 98#if BC_ENABLE_EDITLINE 99 100#include <stdio.h> 101#include <histedit.h> 102 103/** 104 * The history struct for editline. 105 */ 106typedef struct BcHistory 107{ 108 /// A place to store the current line. 109 EditLine* el; 110 111 /// The history. 112 History* hist; 113 114 /// Whether the terminal is bad. This is more or less not used. 115 bool badTerm; 116 117} BcHistory; 118 119// The path to the editrc and its length. 120extern const char bc_history_editrc[]; 121extern const size_t bc_history_editrc_len; 122 123#else // BC_ENABLE_EDITLINE 124 125#if BC_ENABLE_READLINE 126 127#include <stdio.h> 128#include <readline/readline.h> 129#include <readline/history.h> 130 131/** 132 * The history struct for readline. 133 */ 134typedef struct BcHistory 135{ 136 /// A place to store the current line. 137 char* line; 138 139 /// Whether the terminal is bad. This is more or less not used. 140 bool badTerm; 141 142} BcHistory; 143 144#else // BC_ENABLE_READLINE 145 146#if BC_ENABLE_HISTORY 147 148#include <stddef.h> 149 150#include <signal.h> 151 152#ifndef _WIN32 153#include <termios.h> 154#include <time.h> 155#include <unistd.h> 156#include <sys/select.h> 157#else // _WIN32 158 159#ifndef WIN32_LEAN_AND_MEAN 160#define WIN32_LEAN_AND_MEAN 161#endif // WIN32_LEAN_AND_MEAN 162 163#include <Windows.h> 164#include <io.h> 165#include <conio.h> 166 167#define strncasecmp _strnicmp 168#define strcasecmp _stricmp 169 170#endif // _WIN32 171 172#include <status.h> 173#include <vector.h> 174#include <read.h> 175 176/// Default columns. 177#define BC_HIST_DEF_COLS (80) 178 179/// Max number of history entries. 180#define BC_HIST_MAX_LEN (128) 181 182/// Max length of a line. 183#define BC_HIST_MAX_LINE (4095) 184 185/// Max size for cursor position buffer. 186#define BC_HIST_SEQ_SIZE (64) 187 188/** 189 * The number of entries in the history. 190 * @param h The history data. 191 */ 192#define BC_HIST_BUF_LEN(h) ((h)->buf.len - 1) 193 194/** 195 * Read n characters into s and check the error. 196 * @param s The buffer to read into. 197 * @param n The number of bytes to read. 198 * @return True if there was an error, false otherwise. 199 */ 200#define BC_HIST_READ(s, n) (bc_history_read((s), (n)) == -1) 201 202/// Markers for direction when using arrow keys. 203#define BC_HIST_NEXT (false) 204#define BC_HIST_PREV (true) 205 206#if BC_DEBUG_CODE 207 208// These are just for debugging. 209 210#define BC_HISTORY_DEBUG_BUF_SIZE (1024) 211 212// clang-format off 213#define lndebug(...) \ 214 do \ 215 { \ 216 if (bc_history_debug_fp.fd == 0) \ 217 { \ 218 bc_history_debug_buf = bc_vm_malloc(BC_HISTORY_DEBUG_BUF_SIZE); \ 219 bc_file_init(&bc_history_debug_fp, \ 220 open("/tmp/lndebug.txt", O_APPEND), \ 221 BC_HISTORY_DEBUG_BUF_SIZE); \ 222 bc_file_printf(&bc_history_debug_fp, \ 223 "[%zu %zu %zu] p: %d, rows: %d, " \ 224 "rpos: %d, max: %zu, oldmax: %d\n", \ 225 l->len, l->pos, l->oldcolpos, plen, rows, rpos, \ 226 l->maxrows, old_rows); \ 227 } \ 228 bc_file_printf(&bc_history_debug_fp, ", " __VA_ARGS__); \ 229 bc_file_flush(&bc_history_debug_fp); \ 230 } \ 231 while (0) 232#else // BC_DEBUG_CODE 233#define lndebug(fmt, ...) 234#endif // BC_DEBUG_CODE 235// clang-format on 236 237/// An enum of useful actions. To understand what these mean, check terminal 238/// emulators for their shortcuts or the VT100 codes. 239typedef enum BcHistoryAction 240{ 241 BC_ACTION_NULL = 0, 242 BC_ACTION_CTRL_A = 1, 243 BC_ACTION_CTRL_B = 2, 244 BC_ACTION_CTRL_C = 3, 245 BC_ACTION_CTRL_D = 4, 246 BC_ACTION_CTRL_E = 5, 247 BC_ACTION_CTRL_F = 6, 248 BC_ACTION_CTRL_H = 8, 249 BC_ACTION_TAB = 9, 250 BC_ACTION_LINE_FEED = 10, 251 BC_ACTION_CTRL_K = 11, 252 BC_ACTION_CTRL_L = 12, 253 BC_ACTION_ENTER = 13, 254 BC_ACTION_CTRL_N = 14, 255 BC_ACTION_CTRL_P = 16, 256 BC_ACTION_CTRL_S = 19, 257 BC_ACTION_CTRL_T = 20, 258 BC_ACTION_CTRL_U = 21, 259 BC_ACTION_CTRL_W = 23, 260 BC_ACTION_CTRL_Z = 26, 261 BC_ACTION_ESC = 27, 262 BC_ACTION_CTRL_BSLASH = 28, 263 BC_ACTION_BACKSPACE = 127 264 265} BcHistoryAction; 266 267/** 268 * This represents the state during line editing. We pass this state 269 * to functions implementing specific editing functionalities. 270 */ 271typedef struct BcHistory 272{ 273 /// Edited line buffer. 274 BcVec buf; 275 276 /// The history. 277 BcVec history; 278 279 /// Any material printed without a trailing newline. 280 BcVec extras; 281 282 /// Prompt to display. 283 const char* prompt; 284 285 /// Prompt length. 286 size_t plen; 287 288 /// Prompt column length. 289 size_t pcol; 290 291 /// Current cursor position. 292 size_t pos; 293 294 /// Previous refresh cursor column position. 295 size_t oldcolpos; 296 297 /// Number of columns in terminal. 298 size_t cols; 299 300 /// The history index we are currently editing. 301 size_t idx; 302 303#ifndef _WIN32 304 /// The original terminal state. 305 struct termios orig_termios; 306#else // _WIN32 307 /// The original input console mode. 308 DWORD orig_in; 309 310 /// The original output console mode. 311 DWORD orig_out; 312#endif // _WIN32 313 314 /// These next two are here because pahole found a 4 byte hole here. 315 316 /// Whether we are in rawmode. 317 bool rawMode; 318 319 /// Whether the terminal is bad. 320 bool badTerm; 321 322#ifndef _WIN32 323 /// This is to check if stdin has more data. 324 fd_set rdset; 325 326 /// This is to check if stdin has more data. 327 struct timespec ts; 328 329 /// This is to check if stdin has more data. 330 sigset_t sigmask; 331#endif // _WIN32 332 333} BcHistory; 334 335/** 336 * Frees strings used by history. 337 * @param str The string to free. 338 */ 339void 340bc_history_string_free(void* str); 341 342// A list of terminals that don't work. 343extern const char* bc_history_bad_terms[]; 344 345// A tab in history and its length. 346extern const char bc_history_tab[]; 347extern const size_t bc_history_tab_len; 348 349// A ctrl+c string. 350extern const char bc_history_ctrlc[]; 351 352// UTF-8 data arrays. 353extern const uint32_t bc_history_wchars[][2]; 354extern const size_t bc_history_wchars_len; 355extern const uint32_t bc_history_combo_chars[]; 356extern const size_t bc_history_combo_chars_len; 357 358#if BC_DEBUG_CODE 359 360// Debug data. 361extern BcFile bc_history_debug_fp; 362extern char* bc_history_debug_buf; 363 364/** 365 * A function to print keycodes for debugging. 366 * @param h The history data. 367 */ 368void 369bc_history_printKeyCodes(BcHistory* h); 370 371#endif // BC_DEBUG_CODE 372 373#endif // BC_ENABLE_HISTORY 374 375#endif // BC_ENABLE_READLINE 376 377#endif // BC_ENABLE_EDITLINE 378 379#if BC_ENABLE_HISTORY 380 381/** 382 * Get a line from stdin using history. This returns a status because I don't 383 * want to throw errors while the terminal is in raw mode. 384 * @param h The history data. 385 * @param vec A vector to put the line into. 386 * @param prompt The prompt to display, if desired. 387 * @return A status indicating an error, if any. Returning a status here 388 * is better because if we throw an error out of history, we 389 * leave the terminal in raw mode or in some other half-baked 390 * state. 391 */ 392BcStatus 393bc_history_line(BcHistory* h, BcVec* vec, const char* prompt); 394 395/** 396 * Initialize history data. 397 * @param h The struct to initialize. 398 */ 399void 400bc_history_init(BcHistory* h); 401 402/** 403 * Free history data (and recook the terminal). 404 * @param h The struct to free. 405 */ 406void 407bc_history_free(BcHistory* h); 408 409#endif // BC_ENABLE_HISTORY 410 411#endif // BC_HISTORY_H 412