/*1* *****************************************************************************2*3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (c) 2018-2025 Gavin D. Howard and contributors.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions are met:9*10* * Redistributions of source code must retain the above copyright notice, this11* list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright notice,14* this list of conditions and the following disclaimer in the documentation15* and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"18* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE21* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR22* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF23* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS24* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN25* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)26* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE27* POSSIBILITY OF SUCH DAMAGE.28*29* *****************************************************************************30*31* Adapted from the following:32*33* linenoise.c -- guerrilla line editing library against the idea that a34* line editing lib needs to be 20,000 lines of C code.35*36* You can find the original source code at:37* http://github.com/antirez/linenoise38*39* You can find the fork that this code is based on at:40* https://github.com/rain-1/linenoise-mob41*42* ------------------------------------------------------------------------43*44* This code is also under the following license:45*46* Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com>47* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>48*49* Redistribution and use in source and binary forms, with or without50* modification, are permitted provided that the following conditions are51* met:52*53* * Redistributions of source code must retain the above copyright54* notice, this list of conditions and the following disclaimer.55*56* * Redistributions in binary form must reproduce the above copyright57* notice, this list of conditions and the following disclaimer in the58* documentation and/or other materials provided with the distribution.59*60* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS61* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT62* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR63* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT64* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,65* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT66* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,67* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY68* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT69* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE70* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.71*72* *****************************************************************************73*74* Definitions for line history.75*76*/7778#ifndef BC_HISTORY_H79#define BC_HISTORY_H8081// These must come before the #if BC_ENABLE_LINE_LIB below because status.h82// defines it.83#include <status.h>84#include <vector.h>8586#if BC_ENABLE_LINE_LIB8788#include <stdbool.h>89#include <setjmp.h>90#include <signal.h>9192extern sigjmp_buf bc_history_jmpbuf;93extern volatile sig_atomic_t bc_history_inlinelib;9495#endif // BC_ENABLE_LINE_LIB9697#if BC_ENABLE_EDITLINE9899#include <stdio.h>100#include <histedit.h>101102/**103* The history struct for editline.104*/105typedef struct BcHistory106{107/// A place to store the current line.108EditLine* el;109110/// The history.111History* hist;112113/// Whether the terminal is bad. This is more or less not used.114bool badTerm;115116} BcHistory;117118// The path to the editrc and its length.119extern const char bc_history_editrc[];120extern const size_t bc_history_editrc_len;121122#else // BC_ENABLE_EDITLINE123124#if BC_ENABLE_READLINE125126#include <stdio.h>127#include <readline/readline.h>128#include <readline/history.h>129130/**131* The history struct for readline.132*/133typedef struct BcHistory134{135/// A place to store the current line.136char* line;137138/// Whether the terminal is bad. This is more or less not used.139bool badTerm;140141} BcHistory;142143#else // BC_ENABLE_READLINE144145#if BC_ENABLE_HISTORY146147#include <stddef.h>148149#include <signal.h>150151#ifndef _WIN32152#include <termios.h>153#include <time.h>154#include <unistd.h>155#include <sys/select.h>156#else // _WIN32157158#ifndef WIN32_LEAN_AND_MEAN159#define WIN32_LEAN_AND_MEAN160#endif // WIN32_LEAN_AND_MEAN161162#include <Windows.h>163#include <io.h>164#include <conio.h>165166#define strncasecmp _strnicmp167#define strcasecmp _stricmp168169#endif // _WIN32170171#include <status.h>172#include <vector.h>173#include <read.h>174175/// Default columns.176#define BC_HIST_DEF_COLS (80)177178/// Max number of history entries.179#define BC_HIST_MAX_LEN (128)180181/// Max length of a line.182#define BC_HIST_MAX_LINE (4095)183184/// Max size for cursor position buffer.185#define BC_HIST_SEQ_SIZE (64)186187/**188* The number of entries in the history.189* @param h The history data.190*/191#define BC_HIST_BUF_LEN(h) ((h)->buf.len - 1)192193/**194* Read n characters into s and check the error.195* @param s The buffer to read into.196* @param n The number of bytes to read.197* @return True if there was an error, false otherwise.198*/199#define BC_HIST_READ(s, n) (bc_history_read((s), (n)) == -1)200201/// Markers for direction when using arrow keys.202#define BC_HIST_NEXT (false)203#define BC_HIST_PREV (true)204205#if BC_DEBUG_CODE206207// These are just for debugging.208209#define BC_HISTORY_DEBUG_BUF_SIZE (1024)210211// clang-format off212#define lndebug(...) \213do \214{ \215if (bc_history_debug_fp.fd == 0) \216{ \217bc_history_debug_buf = bc_vm_malloc(BC_HISTORY_DEBUG_BUF_SIZE); \218bc_file_init(&bc_history_debug_fp, \219open("/tmp/lndebug.txt", O_APPEND), \220BC_HISTORY_DEBUG_BUF_SIZE); \221bc_file_printf(&bc_history_debug_fp, \222"[%zu %zu %zu] p: %d, rows: %d, " \223"rpos: %d, max: %zu, oldmax: %d\n", \224l->len, l->pos, l->oldcolpos, plen, rows, rpos, \225l->maxrows, old_rows); \226} \227bc_file_printf(&bc_history_debug_fp, ", " __VA_ARGS__); \228bc_file_flush(&bc_history_debug_fp); \229} \230while (0)231#else // BC_DEBUG_CODE232#define lndebug(fmt, ...)233#endif // BC_DEBUG_CODE234// clang-format on235236/// An enum of useful actions. To understand what these mean, check terminal237/// emulators for their shortcuts or the VT100 codes.238typedef enum BcHistoryAction239{240BC_ACTION_NULL = 0,241BC_ACTION_CTRL_A = 1,242BC_ACTION_CTRL_B = 2,243BC_ACTION_CTRL_C = 3,244BC_ACTION_CTRL_D = 4,245BC_ACTION_CTRL_E = 5,246BC_ACTION_CTRL_F = 6,247BC_ACTION_CTRL_H = 8,248BC_ACTION_TAB = 9,249BC_ACTION_LINE_FEED = 10,250BC_ACTION_CTRL_K = 11,251BC_ACTION_CTRL_L = 12,252BC_ACTION_ENTER = 13,253BC_ACTION_CTRL_N = 14,254BC_ACTION_CTRL_P = 16,255BC_ACTION_CTRL_S = 19,256BC_ACTION_CTRL_T = 20,257BC_ACTION_CTRL_U = 21,258BC_ACTION_CTRL_W = 23,259BC_ACTION_CTRL_Z = 26,260BC_ACTION_ESC = 27,261BC_ACTION_CTRL_BSLASH = 28,262BC_ACTION_BACKSPACE = 127263264} BcHistoryAction;265266/**267* This represents the state during line editing. We pass this state268* to functions implementing specific editing functionalities.269*/270typedef struct BcHistory271{272/// Edited line buffer.273BcVec buf;274275/// The history.276BcVec history;277278/// Any material printed without a trailing newline.279BcVec extras;280281/// Prompt to display.282const char* prompt;283284/// Prompt length.285size_t plen;286287/// Prompt column length.288size_t pcol;289290/// Current cursor position.291size_t pos;292293/// Previous refresh cursor column position.294size_t oldcolpos;295296/// Number of columns in terminal.297size_t cols;298299/// The history index we are currently editing.300size_t idx;301302#ifndef _WIN32303/// The original terminal state.304struct termios orig_termios;305#else // _WIN32306/// The original input console mode.307DWORD orig_in;308309/// The original output console mode.310DWORD orig_out;311#endif // _WIN32312313/// These next two are here because pahole found a 4 byte hole here.314315/// Whether we are in rawmode.316bool rawMode;317318/// Whether the terminal is bad.319bool badTerm;320321#ifndef _WIN32322/// This is to check if stdin has more data.323fd_set rdset;324325/// This is to check if stdin has more data.326struct timespec ts;327328/// This is to check if stdin has more data.329sigset_t sigmask;330#endif // _WIN32331332} BcHistory;333334/**335* Frees strings used by history.336* @param str The string to free.337*/338void339bc_history_string_free(void* str);340341// A list of terminals that don't work.342extern const char* bc_history_bad_terms[];343344// A tab in history and its length.345extern const char bc_history_tab[];346extern const size_t bc_history_tab_len;347348// A ctrl+c string.349extern const char bc_history_ctrlc[];350351// UTF-8 data arrays.352extern const uint32_t bc_history_wchars[][2];353extern const size_t bc_history_wchars_len;354extern const uint32_t bc_history_combo_chars[];355extern const size_t bc_history_combo_chars_len;356357#if BC_DEBUG_CODE358359// Debug data.360extern BcFile bc_history_debug_fp;361extern char* bc_history_debug_buf;362363/**364* A function to print keycodes for debugging.365* @param h The history data.366*/367void368bc_history_printKeyCodes(BcHistory* h);369370#endif // BC_DEBUG_CODE371372#endif // BC_ENABLE_HISTORY373374#endif // BC_ENABLE_READLINE375376#endif // BC_ENABLE_EDITLINE377378#if BC_ENABLE_HISTORY379380/**381* Get a line from stdin using history. This returns a status because I don't382* want to throw errors while the terminal is in raw mode.383* @param h The history data.384* @param vec A vector to put the line into.385* @param prompt The prompt to display, if desired.386* @return A status indicating an error, if any. Returning a status here387* is better because if we throw an error out of history, we388* leave the terminal in raw mode or in some other half-baked389* state.390*/391BcStatus392bc_history_line(BcHistory* h, BcVec* vec, const char* prompt);393394/**395* Initialize history data.396* @param h The struct to initialize.397*/398void399bc_history_init(BcHistory* h);400401/**402* Free history data (and recook the terminal).403* @param h The struct to free.404*/405void406bc_history_free(BcHistory* h);407408#endif // BC_ENABLE_HISTORY409410#endif // BC_HISTORY_H411412413