From f4a5c0984e80dabcedabfbe5331fbdc09df31f83 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Wed, 10 Jul 2024 15:54:23 +0200 Subject: [PATCH] started the norminette --- line/include/line/_line_functions.h | 32 +-- line/include/line/_line_internal.h | 45 ++-- line/include/line/_line_structs.h | 106 ++++---- line/include/line/line.h | 6 +- line/src.list | 1 + line/src/line.c | 360 ++++++++++++---------------- line/src/line_globals.c | 63 +++++ 7 files changed, 318 insertions(+), 295 deletions(-) create mode 100644 line/src/line_globals.c diff --git a/line/include/line/_line_functions.h b/line/include/line/_line_functions.h index 85760e6b..15b240da 100644 --- a/line/include/line/_line_functions.h +++ b/line/include/line/_line_functions.h @@ -11,16 +11,16 @@ /* ************************************************************************** */ #ifndef _LINE_FUNCTIONS_H -#define _LINE_FUNCTIONS_H +# define _LINE_FUNCTIONS_H -#include "line/_line_structs.h" -#include "me/fs/fs.h" -#include "me/types.h" -#include "me/vec/vec_str.h" +# include "line/_line_structs.h" +# include "me/fs/fs.h" +# include "me/types.h" +# include "me/vec/vec_str.h" - -t_error line_edit_insert(t_line_state *state, char c); -t_error line_edit_start(t_line_state *state, t_fd *stdin_fd, t_fd *stdout_fd, t_const_str prompt); +t_error line_edit_insert(t_line_state *state, char c); +t_error line_edit_start(t_line_state *state, t_fd *stdin_fd, t_fd *stdout_fd, + t_const_str prompt); t_str line_edit_feed(t_line_state *state); void line_edit_backspace(t_line_state *state); void line_edit_delete(t_line_state *state); @@ -31,16 +31,16 @@ void line_edit_move_left(t_line_state *state); void line_edit_move_right(t_line_state *state); void line_edit_stop(t_line_state *state); -t_str linenoise(t_const_str prompt); -t_str line_blocking_edit(t_fd *stdin_fd, t_fd *stdout_fd, t_const_str prompt); +t_str linenoise(t_const_str prompt); +t_str line_blocking_edit(t_fd *stdin_fd, t_fd *stdout_fd, t_const_str prompt); bool line_history_add(t_const_str line); -t_error line_history_load(t_str name); -t_error line_history_save(t_str name); +t_error line_history_load(t_str name); +t_error line_history_save(t_str name); -void line_clear_screen(t_fd *output); -void line_refresh(t_line_state *state, t_line_flags flags); -void line_hide(t_line_state *state); -void line_show(t_line_state *state); +void line_clear_screen(t_fd *output); +void line_refresh(t_line_state *state, t_line_flags flags); +void line_hide(t_line_state *state); +void line_show(t_line_state *state); #endif /* _LINE_FUNCTIONS_H */ diff --git a/line/include/line/_line_internal.h b/line/include/line/_line_internal.h index 184b8014..902dfb19 100644 --- a/line/include/line/_line_internal.h +++ b/line/include/line/_line_internal.h @@ -1,38 +1,39 @@ /* ************************************************************************** */ /* */ /* ::: :::::::: */ -/* _line_interal.h :+: :+: :+: */ +/* _line_internal.h :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: maiboyer +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/07/10 15:37:40 by maiboyer #+# #+# */ -/* Updated: 2024/07/10 15:38:51 by maiboyer ### ########.fr */ +/* Updated: 2024/07/10 15:48:56 by maiboyer ### ########.fr */ /* */ /* ************************************************************************** */ -#ifndef _LINE_INTERAL_H -#define _LINE_INTERAL_H +#ifndef _LINE_INTERNAL_H +# define _LINE_INTERNAL_H -#include "line/_line_functions.h" -#include "me/fs/fs.h" -#include "me/string/string.h" -#include "me/types.h" -#include "me/vec/vec_str.h" +# include "line/_line_functions.h" +# include "me/fs/fs.h" +# include "me/string/string.h" +# include "me/types.h" +# include "me/vec/vec_str.h" -t_const_str get_unfinished_str(void); -t_raw_mode_state *get_raw_mode_state(void); -t_vec_str *get_history(void); -void free_history(void); +t_const_str get_unfinished_str(void); +t_raw_mode_state *get_raw_mode_state(void); +t_vec_str *get_history(void); +void free_history(void); -t_error gnl_wrapper(t_fd *fd, t_string *out); -void line_uninit_lib(void); -t_error line_get_cursor_position(t_fd *input, t_fd *output, t_u32 *column_out); -t_u32 line_get_columns(t_fd *input, t_fd *output); -t_usize line_get_prompt_len(t_const_str s); -t_str line_no_tty_impl(void); -void line_print_key_codes(void); +t_error gnl_wrapper(t_fd *fd, t_string *out); +void line_uninit_lib(void); +t_error line_get_cursor_position(t_fd *input, t_fd *output, + t_u32 *column_out); +t_u32 line_get_columns(t_fd *input, t_fd *output); +t_usize line_get_prompt_len(t_const_str s); +t_str line_no_tty_impl(void); +void line_print_key_codes(void); -t_error line_enable_raw_mode(t_fd *fd); -void line_disable_raw_mode(t_fd *fd); +t_error line_enable_raw_mode(t_fd *fd); +void line_disable_raw_mode(t_fd *fd); #endif /* _LINE_INTERAL_H */ diff --git a/line/include/line/_line_structs.h b/line/include/line/_line_structs.h index ae7fe3d5..c7929445 100644 --- a/line/include/line/_line_structs.h +++ b/line/include/line/_line_structs.h @@ -6,74 +6,82 @@ /* By: maiboyer +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/07/10 15:24:34 by maiboyer #+# #+# */ -/* Updated: 2024/07/10 15:32:35 by maiboyer ### ########.fr */ +/* Updated: 2024/07/10 15:53:54 by maiboyer ### ########.fr */ /* */ /* ************************************************************************** */ #ifndef _LINE_STRUCTS_H -#define _LINE_STRUCTS_H +# define _LINE_STRUCTS_H -#include "me/fs/fs.h" -#include "me/string/string.h" -#include "me/types.h" -#include +# include "me/fs/fs.h" +# include "me/string/string.h" +# include "me/types.h" +# include -typedef struct s_line_state t_line_state; -typedef struct s_raw_mode_state t_raw_mode_state; -typedef enum e_line_flags t_line_flags; -typedef enum e_key_action t_key_action; -typedef enum e_history_direction t_history_direction; +typedef struct s_line_state t_line_state; +typedef struct s_raw_mode_state t_raw_mode_state; +typedef enum e_line_flags t_line_flags; +typedef enum e_key_action t_key_action; +typedef enum e_history_direction t_history_direction; -struct s_line_state +/// @param input_fd Terminal stdin file descriptor. +/// @param output_fd Terminal stdout file descriptor. +/// @param buf Edited line buffer. +/// @param prompt_len Prompt to display. +/// @param pos Prompt length. +/// @param prompt_len Current cursor position. +/// @param columns Number of columns in terminal. +/// @param history_index The history index we are currently editing. +struct s_line_state { - t_fd *input_fd; /* Terminal stdin file descriptor. */ - t_fd *output_fd; /* Terminal stdout file descriptor. */ - t_string buf; /* Edited line buffer. */ - t_const_str prompt; /* Prompt to display. */ - t_usize prompt_len; /* Prompt length. */ - t_usize pos; /* Current cursor position. */ - t_usize columns; /* Number of columns in terminal. */ - t_i32 history_index; /* The history index we are currently editing. */ + t_fd *input_fd; + t_fd *output_fd; + t_string buf; + t_const_str prompt; + t_usize prompt_len; + t_usize pos; + t_usize columns; + t_i32 history_index; }; -struct s_raw_mode_state +struct s_raw_mode_state { - bool enabled; - struct termios state; + bool enabled; + struct termios state; }; -enum e_key_action +enum e_key_action { - K_KEY_NULL = 0, /* NULL */ - K_CTRL_A = 1, /* Ctrl+a */ - K_CTRL_B = 2, /* Ctrl-b */ - K_CTRL_C = 3, /* Ctrl-c */ - K_CTRL_D = 4, /* Ctrl-d */ - K_CTRL_E = 5, /* Ctrl-e */ - K_CTRL_F = 6, /* Ctrl-f */ - K_CTRL_H = 8, /* Ctrl-h */ - K_TAB = 9, /* Tab */ - K_NEWLINE = 10, /* Newline */ - K_CTRL_K = 11, /* Ctrl+k */ - K_CTRL_L = 12, /* Ctrl+l */ - K_ENTER = 13, /* Enter */ - K_CTRL_N = 14, /* Ctrl-n */ - K_CTRL_P = 16, /* Ctrl-p */ - K_CTRL_T = 20, /* Ctrl-t */ - K_CTRL_U = 21, /* Ctrl+u */ - K_CTRL_W = 23, /* Ctrl+w */ - K_ESC = 27, /* Escape */ - K_BACKSPACE = 127 /* Backspace */ + K_KEY_NULL = 0, + K_CTRL_A = 1, + K_CTRL_B = 2, + K_CTRL_C = 3, + K_CTRL_D = 4, + K_CTRL_E = 5, + K_CTRL_F = 6, + K_CTRL_H = 8, + K_TAB = 9, + K_NEWLINE = 10, + K_CTRL_K = 11, + K_CTRL_L = 12, + K_ENTER = 13, + K_CTRL_N = 14, + K_CTRL_P = 16, + K_CTRL_T = 20, + K_CTRL_U = 21, + K_CTRL_W = 23, + K_ESC = 27, + K_BACKSPACE = 127 }; -enum e_line_flags +enum e_line_flags { - REFRESH_CLEAN = 1 << 0, // Clean the old prompt from the screen - REFRESH_WRITE = 1 << 1, // Rewrite the prompt on the screen. - REFRESH_ALL = REFRESH_CLEAN | REFRESH_WRITE, // Do both. + REFRESH_CLEAN = 1 << 0, + REFRESH_WRITE = 1 << 1, + REFRESH_ALL = REFRESH_CLEAN | REFRESH_WRITE, }; -enum e_history_direction +enum e_history_direction { HIST_PREV, HIST_NEXT, diff --git a/line/include/line/line.h b/line/include/line/line.h index c323edab..5fefc5a7 100644 --- a/line/include/line/line.h +++ b/line/include/line/line.h @@ -14,9 +14,9 @@ /// https://github.com/antirez/linenoise #ifndef LINE_H -#define LINE_H +# define LINE_H -#include "line/_line_functions.h" -#include "line/_line_structs.h" +# include "line/_line_functions.h" +# include "line/_line_structs.h" #endif /* LINE_H */ diff --git a/line/src.list b/line/src.list index a999a0c2..cadaec57 100644 --- a/line/src.list +++ b/line/src.list @@ -1 +1,2 @@ line +line_globals diff --git a/line/src/line.c b/line/src/line.c index 7a3649fc..862dafd0 100644 --- a/line/src/line.c +++ b/line/src/line.c @@ -6,21 +6,10 @@ /* By: maiboyer +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/07/07 16:53:27 by maiboyer #+# #+# */ -/* Updated: 2024/07/10 15:41:07 by maiboyer ### ########.fr */ +/* Updated: 2024/07/10 15:47:55 by maiboyer ### ########.fr */ /* */ /* ************************************************************************** */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "line/_line_internal.h" #include "line/line.h" #include "me/fs/fs.h" @@ -31,47 +20,16 @@ #include "me/string/string.h" #include "me/types.h" #include "me/vec/vec_str.h" - -t_const_str get_unfinished_str(void) -{ - return ("If you see this, you are misusing the API: when linenoiseEditFeed() is called, if it returns get_unfinished_str() " - "the user is yet editing the line. See the README file for more information."); -} - -t_vec_str *get_history(void) -{ - static t_vec_str history = {}; - static bool init = false; - - if (!init) - { - history = vec_str_new(256, (void (*)())mem_free); - init = true; - } - return (&history); -} - -t_raw_mode_state *get_raw_mode_state(void) -{ - static t_raw_mode_state state = {}; - - return (&state); -} - -/* Free the history, but does not reset it. Only used when we have to - * exit() to avoid memory leaks are reported by valgrind & co. */ -void free_history(void) -{ - t_vec_str *history = get_history(); - vec_str_free(*history); -} - -/* At exit we'll try to fix the terminal to the initial conditions. */ -__attribute__((destructor)) void line_uninit_lib(void) -{ - line_disable_raw_mode(get_stdin()); - free_history(); -} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define lndebug(fmt, ...) @@ -80,27 +38,26 @@ __attribute__((destructor)) void line_uninit_lib(void) /* Use the ESC [6n escape sequence to query the horizontal cursor position * and return it. On error -1 is returned, on success the position of the * cursor. */ -t_error line_get_cursor_position(t_fd *input, t_fd *output, t_u32 *column_out) +t_error line_get_cursor_position(t_fd *input, t_fd *output, t_u32 *column_out) { - char buf[32]; - int cols, rows; - unsigned int i = 0; + char buf[32]; + unsigned int i; + int cols, rows; + i = 0; /* Report cursor location */ if (write_fd(output, (t_u8 *)"\x1b[6n", 4, NULL)) return (ERROR); - /* Read the response: ESC [ rows ; cols R */ while (i < sizeof(buf) - 1) { if (read_fd(input, (t_u8 *)(buf + i), 1, NULL)) - break; + break ; if (buf[i] == 'R') - break; + break ; i++; } buf[i] = '\0'; - /* Parse it. */ if (buf[0] != K_ESC && buf[1] != '[') return (ERROR); @@ -111,11 +68,11 @@ t_error line_get_cursor_position(t_fd *input, t_fd *output, t_u32 *column_out) /* Try to get the number of columns in the current terminal, or assume 80 * if it fails. */ -t_u32 line_get_columns(t_fd *input, t_fd *output) +t_u32 line_get_columns(t_fd *input, t_fd *output) { - struct winsize ws; - t_u32 cols; - t_u32 start; + struct winsize ws; + t_u32 cols; + t_u32 start; if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { @@ -123,12 +80,10 @@ t_u32 line_get_columns(t_fd *input, t_fd *output) /* Get the initial position so we can restore it later. */ if (line_get_cursor_position(input, output, &start)) return (80); - /* Go to right margin and get position. */ me_printf_fd(output, "\x1b[999C"); if (line_get_cursor_position(input, output, &cols)) return (80); - if (cols > start) { me_printf_fd(output, "\x1b[%dD", cols - start); @@ -140,40 +95,37 @@ t_u32 line_get_columns(t_fd *input, t_fd *output) } /* Clear the screen. Used to handle ctrl+l */ -void line_clear_screen(t_fd *output) +void line_clear_screen(t_fd *output) { me_printf_fd(output, "\x1b[H\x1b[2J"); } /* Raw mode: 1960 magic shit. */ -t_error line_enable_raw_mode(t_fd *fd) +t_error line_enable_raw_mode(t_fd *fd) { - struct termios raw; - t_raw_mode_state *raw_state; + struct termios raw; + t_raw_mode_state *raw_state; raw_state = get_raw_mode_state(); - if (!isatty(fd->fd)) return (errno = ENOTTY, ERROR); if (tcgetattr(fd->fd, &raw_state->state) == -1) return (errno = ENOTTY, ERROR); - raw = raw_state->state; /* modify the original mode */ /* input modes: no break, no CR to NL, no parity check, no strip char, - * no start/stop output control. */ + * no start/stop output control. */ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); /* output modes - disable post processing */ raw.c_oflag &= ~(OPOST); /* control modes - set 8 bit chars */ raw.c_cflag |= (CS8); /* local modes - choing off, canonical off, no extended functions, - * no signal chars (^Z,^C) */ + * no signal chars (^Z,^C) */ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* control chars - set return condition: min number of bytes and timer. - * We want read to return every single byte, without timeout. */ + * We want read to return every single byte, without timeout. */ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ - /* put terminal in raw mode after flushing */ if (tcsetattr(fd->fd, TCSAFLUSH, &raw) < 0) return (errno = ENOTTY, ERROR); @@ -181,9 +133,9 @@ t_error line_enable_raw_mode(t_fd *fd) return (NO_ERROR); } -void line_disable_raw_mode(t_fd *fd) +void line_disable_raw_mode(t_fd *fd) { - t_raw_mode_state *state; + t_raw_mode_state *state; state = get_raw_mode_state(); if (state->enabled && tcsetattr(fd->fd, TCSAFLUSH, &state->state) != -1) @@ -197,11 +149,11 @@ void line_disable_raw_mode(t_fd *fd) * write all the escape sequences in a buffer and flush them to the standard * output in a single call, to avoid flickering effects. */ -t_usize line_get_prompt_len(t_const_str s) +t_usize line_get_prompt_len(t_const_str s) { - t_usize i; - t_usize ret; - t_isize color; + t_usize i; + t_usize ret; + t_isize color; if (s == NULL) return (0); @@ -229,7 +181,7 @@ t_usize line_get_prompt_len(t_const_str s) else if (s[i] == 'm') { i++; - break; + break ; } } } @@ -248,32 +200,33 @@ t_usize line_get_prompt_len(t_const_str s) * * Flags is REFRESH_* macros. The function can just remove the old * prompt, just write it, or both. */ -void line_refresh(t_line_state *state, t_line_flags flags) +void line_refresh(t_line_state *state, t_line_flags flags) { - t_string str; + t_string str; str = string_new(64); string_push(&str, "\r\x1b[2K"); if (flags & REFRESH_WRITE) - me_printf_str(&str, "%s%s\x1b[0G\x1b[%uC", state->prompt, state->buf.buf, state->pos + line_get_prompt_len(state->prompt)); + me_printf_str(&str, "%s%s\x1b[0G\x1b[%uC", state->prompt, + state->buf.buf, state->pos + line_get_prompt_len(state->prompt)); me_printf_fd(state->output_fd, "%s", str.buf); string_free(str); } /* Utility function to avoid specifying REFRESH_ALL all the times. */ -void line_refresh_line(t_line_state *state) +void line_refresh_line(t_line_state *state) { line_refresh(state, REFRESH_ALL); } /* Hide the current line, when using the multiplexing API. */ -void line_hide(t_line_state *state) +void line_hide(t_line_state *state) { line_refresh(state, REFRESH_CLEAN); } /* Show the current line, when using the multiplexing API. */ -void line_show(t_line_state *state) +void line_show(t_line_state *state) { line_refresh(state, REFRESH_WRITE); } @@ -281,7 +234,7 @@ void line_show(t_line_state *state) /* Insert the character 'c' at cursor current position. * * On error writing to the terminal -1 is returned, otherwise 0. */ -t_error line_edit_insert(t_line_state *state, char c) +t_error line_edit_insert(t_line_state *state, char c) { if (state->pos == state->buf.len) { @@ -296,7 +249,7 @@ t_error line_edit_insert(t_line_state *state, char c) } /* Move cursor on the left. */ -void line_edit_move_left(t_line_state *state) +void line_edit_move_left(t_line_state *state) { if (state->pos > 0) { @@ -306,7 +259,7 @@ void line_edit_move_left(t_line_state *state) } /* Move cursor on the right. */ -void line_edit_move_right(t_line_state *state) +void line_edit_move_right(t_line_state *state) { if (state->pos != state->buf.len) { @@ -316,7 +269,7 @@ void line_edit_move_right(t_line_state *state) } /* Move cursor to the start of the line. */ -void line_edit_move_home(t_line_state *state) +void line_edit_move_home(t_line_state *state) { if (state->pos != 0) { @@ -326,7 +279,7 @@ void line_edit_move_home(t_line_state *state) } /* Move cursor to the end of the line. */ -void line_edit_move_end(t_line_state *state) +void line_edit_move_end(t_line_state *state) { if (state->pos != state->buf.len) { @@ -337,32 +290,34 @@ void line_edit_move_end(t_line_state *state) /* Substitute the currently edited line with the next or previous history * entry as specified by 'dir'. */ -void line_edit_history_next(t_line_state *state, t_history_direction direction) +void line_edit_history_next(t_line_state *state, + t_history_direction direction) { - t_vec_str *history; + t_vec_str *history; history = get_history(); - if (history->len > 1) { /* Update the current history entry before to - * overwrite it with the next one. */ + * overwrite it with the next one. */ mem_free(history->buffer[history->len - 1 - state->history_index]); - history->buffer[history->len - 1 - state->history_index] = str_clone(state->buf.buf); + history->buffer[history->len - 1 + - state->history_index] = str_clone(state->buf.buf); /* Show the new entry */ state->history_index += (direction == HIST_PREV) ? 1 : -1; if (state->history_index < 0) { state->history_index = 0; - return; + return ; } else if ((t_usize)state->history_index >= history->len) { state->history_index = history->len - 1; - return; + return ; } string_clear(&state->buf); - string_push(&state->buf, history->buffer[history->len - 1 - state->history_index]); + string_push(&state->buf, history->buffer[history->len - 1 + - state->history_index]); state->pos = state->buf.len; line_refresh_line(state); } @@ -370,14 +325,14 @@ void line_edit_history_next(t_line_state *state, t_history_direction direction) /* Delete the character at the right of the cursor without altering the cursor * position. Basically this is what happens with the "Delete" keyboard key. */ -void line_edit_delete(t_line_state *state) +void line_edit_delete(t_line_state *state) { string_remove(&state->buf, state->pos, NULL); line_refresh_line(state); } /* Backspace implementation. */ -void line_edit_backspace(t_line_state *state) +void line_edit_backspace(t_line_state *state) { if (state->pos > 0) { @@ -411,39 +366,34 @@ void line_edit_backspace(t_line_state *state) * fails. If stdin_fd or stdout_fd are set to -1, the default is to use * STDIN_FILENO and STDOUT_FILENO. */ -t_error line_edit_start(t_line_state *state, t_fd *stdin_fd, t_fd *stdout_fd, t_const_str prompt) +t_error line_edit_start(t_line_state *state, t_fd *stdin_fd, t_fd *stdout_fd, + t_const_str prompt) { if (stdin_fd == NULL) stdin_fd = get_stdin(); if (stdout_fd == NULL) stdout_fd = get_stdout(); - /* Populate the linenoise state that we pass to functions implementing - * specific editing functionalities. */ + * specific editing functionalities. */ state->input_fd = stdin_fd; state->output_fd = stdout_fd; state->buf = string_new(4096); state->prompt = prompt; state->prompt_len = str_len(state->prompt); state->pos = 0; - /* Enter raw mode. */ if (line_enable_raw_mode(state->input_fd)) return (ERROR); - state->columns = line_get_columns(stdin_fd, stdout_fd); state->history_index = 0; - /* If stdin is not a tty, stop here with the initialization. We - * will actually just read a line from standard input in blocking - * mode later, in linenoiseEditFeed(). */ + * will actually just read a line from standard input in blocking + * mode later, in linenoiseEditFeed(). */ if (!isatty(state->input_fd->fd)) return (NO_ERROR); - /* The latest history entry is always our current buffer, that - * initially is just an empty string. */ + * initially is just an empty string. */ line_history_add(""); - if (write_fd(state->output_fd, (t_u8 *)prompt, state->prompt_len, NULL)) return (ERROR); return (NO_ERROR); @@ -455,7 +405,8 @@ t_error line_edit_start(t_line_state *state, t_fd *stdin_fd, t_fd *stdout_fd, t_ * descriptor. In the case of blocking operations, this function can just be * called in a loop, and block. * - * The function returns get_unfinished_str() to signal that line editing is still + + * The function returns get_unfinished_str() to signal that line editing is still * in progress, that is, the user didn't yet pressed enter / CTRL-D. Otherwise * the function returns the pointer to the heap-allocated buffer with the * edited line, that the user should mem_free with linenoiseFree(). @@ -467,34 +418,34 @@ t_error line_edit_start(t_line_state *state, t_fd *stdin_fd, t_fd *stdout_fd, t_ * * Some other errno: I/O error. */ -t_str line_edit_feed(t_line_state *state) +t_str line_edit_feed(t_line_state *state) { - /* Not a TTY, pass control to line reading without character - * count limits. */ - if (!isatty(state->input_fd->fd)) - return line_no_tty_impl(); + char c; + t_isize nread; + char seq[3]; + t_vec_str *history; + t_str tmp; - char c; - t_isize nread; - char seq[3]; - t_vec_str *history = get_history(); - t_str tmp; + /* Not a TTY, pass control to line reading without character + * count limits. */ + if (!isatty(state->input_fd->fd)) + return (line_no_tty_impl()); + history = get_history(); if (read_fd(state->input_fd, (t_u8 *)&c, 1, &nread)) return (NULL); - switch (c) { case K_NEWLINE: /* enter */ - case K_ENTER: /* enter */ + case K_ENTER: /* enter */ if (!vec_str_pop(history, &tmp)) mem_free(tmp); - return str_clone(state->buf.buf); + return (str_clone(state->buf.buf)); case K_CTRL_C: /* ctrl-c */ return (errno = EAGAIN, NULL); case K_BACKSPACE: /* backspace */ - case K_CTRL_H: /* ctrl-h */ + case K_CTRL_H: /* ctrl-h */ line_edit_backspace(state); - break; + break ; case K_CTRL_D: /* ctrl-d, remove char at right of cursor, or if the line is empty, act as end-of-file. */ if (state->buf.len > 0) @@ -505,28 +456,27 @@ t_str line_edit_feed(t_line_state *state) mem_free(history->buffer[history->len]); return (errno = ENOENT, NULL); } - break; + break ; case K_CTRL_B: /* ctrl-b */ line_edit_move_left(state); - break; + break ; case K_CTRL_F: /* ctrl-f */ line_edit_move_right(state); - break; + break ; case K_CTRL_P: /* ctrl-p */ line_edit_history_next(state, HIST_PREV); - break; + break ; case K_CTRL_N: /* ctrl-n */ line_edit_history_next(state, HIST_NEXT); - break; + break ; case K_ESC: /* escape sequence */ /* Read the next two bytes representing the escape sequence. - * Use two calls to handle slow terminals returning the two - * chars at different times. */ + * Use two calls to handle slow terminals returning the two + * chars at different times. */ if (read_fd(state->input_fd, (t_u8 *)seq, 1, NULL)) - break; + break ; if (read_fd(state->input_fd, (t_u8 *)(seq + 1), 1, NULL)) - break; - + break ; /* ESC [ sequences. */ if (seq[0] == '[') { @@ -534,7 +484,7 @@ t_str line_edit_feed(t_line_state *state) { /* Extended escape, read additional byte. */ if (read_fd(state->input_fd, (t_u8 *)(seq + 2), 1, NULL)) - break; + break ; if (seq[1] == '3' && seq[2] == '~') line_edit_delete(state); } @@ -544,26 +494,25 @@ t_str line_edit_feed(t_line_state *state) { case 'A': /* Up */ line_edit_history_next(state, HIST_PREV); - break; + break ; case 'B': /* Down */ line_edit_history_next(state, HIST_NEXT); - break; + break ; case 'C': /* Right */ line_edit_move_right(state); - break; + break ; case 'D': /* Left */ line_edit_move_left(state); - break; + break ; case 'H': /* Home */ line_edit_move_home(state); - break; + break ; case 'F': /* End*/ line_edit_move_end(state); - break; + break ; } } } - /* ESC O sequences. */ else if (seq[0] == 'O') { @@ -571,36 +520,36 @@ t_str line_edit_feed(t_line_state *state) { case 'H': /* Home */ line_edit_move_home(state); - break; + break ; case 'F': /* End*/ line_edit_move_end(state); - break; + break ; } } - break; + break ; case K_CTRL_U: /* Ctrl+u, delete the whole line. */ string_clear(&state->buf); state->pos = 0; line_refresh_line(state); - break; + break ; case K_CTRL_K: /* Ctrl+k, delete from current to end of line. */ string_clear_after(&state->buf, state->pos); line_refresh_line(state); - break; + break ; case K_CTRL_A: /* Ctrl+a, go to the start of the line */ line_edit_move_home(state); - break; + break ; case K_CTRL_E: /* ctrl+e, go to the end of the line */ line_edit_move_end(state); - break; + break ; case K_CTRL_L: /* ctrl+l, clear screen */ line_clear_screen(state->output_fd); line_refresh_line(state); - break; + break ; default: if (line_edit_insert(state, c)) return (NULL); - break; + break ; } return ((t_str)get_unfinished_str()); } @@ -609,10 +558,10 @@ t_str line_edit_feed(t_line_state *state) * for more information. This function is called when linenoiseEditFeed() * returns something different than NULL. At this point the user input * is in the buffer, and we can restore the terminal in normal mode. */ -void line_edit_stop(t_line_state *state) +void line_edit_stop(t_line_state *state) { if (!isatty(state->input_fd->fd)) - return; + return ; line_disable_raw_mode(state->input_fd); me_printf_fd(state->output_fd, "\n"); string_free(state->buf); @@ -622,45 +571,45 @@ void line_edit_stop(t_line_state *state) * In many applications that are not event-drivern, we can just call * the blocking linenoise API, wait for the user to complete the editing * and return the buffer. */ -t_str line_blocking_edit(t_fd *stdin_fd, t_fd *stdout_fd, t_const_str prompt) +t_str line_blocking_edit(t_fd *stdin_fd, t_fd *stdout_fd, t_const_str prompt) { - t_line_state l; + t_line_state l; + t_str res; line_edit_start(&l, stdin_fd, stdout_fd, prompt); - t_str res; while ((res = line_edit_feed(&l)) == get_unfinished_str()) ; line_edit_stop(&l); - return res; + return (res); } /* This special mode is used by linenoise in order to print scan codes * on screen for debugging / development purposes. It is implemented * by the line_example program using the --keycodes option. */ -void line_print_key_codes(void) +void line_print_key_codes(void) { - char quit[4]; + char quit[4]; + char c; + t_isize nread; printf("Linenoise key codes debugging mode.\n" - "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); + "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); // if (enableRawMode(STDIN_FILENO) == -1) - // return; + // return ; mem_set(quit, ' ', 4); while (1) { - char c; - t_isize nread; - if (read_fd(get_stdin(), (void *)&c, 1, &nread)) - continue; + continue ; if (nread <= 0) - continue; + continue ; mem_move(quit, quit + 1, sizeof(quit) - 1); /* shift string to left. */ - quit[sizeof(quit) - 1] = c; /* Insert current char on the right. */ + quit[sizeof(quit) - 1] = c; + /* Insert current char on the right. */ if (mem_compare(quit, "quit", sizeof(quit))) - break; - - printf("'%c' %02x (%d) (type quit to exit)\n", isprint(c) ? c : '?', (int)c, (int)c); + break ; + printf("'%c' %02x (%d) (type quit to exit)\n", isprint(c) ? c : '?', + (int)c, (int)c); printf("\r"); /* Go left edge manually, we are in raw mode. */ fflush(stdout); } @@ -672,11 +621,11 @@ void line_print_key_codes(void) * program using linenoise is called in pipe or with a file redirected * to its standard input. In this case, we want to be able to return the * line regardless of its length (by default we are limited to 4k). */ -t_str line_no_tty_impl(void) +t_str line_no_tty_impl(void) { - t_string line; - t_isize ret; - char chr; + t_string line; + t_isize ret; + char chr; line = string_new(16); while (true) @@ -700,15 +649,15 @@ t_str line_no_tty_impl(void) * for a blacklist of stupid terminals, and later either calls the line * editing function or uses dummy fgets() so that you will be able to type * something even in the most desperate of the conditions. */ -t_str linenoise(t_const_str prompt) +t_str linenoise(t_const_str prompt) { - t_str retval; + t_str retval; if (!isatty(get_stdin()->fd)) { /* Not a tty: read from file / pipe. In this mode we don't want any - * limit to the line size, so we call a function to handle that. */ - return line_no_tty_impl(); + * limit to the line size, so we call a function to handle that. */ + return (line_no_tty_impl()); } retval = line_blocking_edit(get_stdin(), get_stdout(), prompt); return (retval); @@ -723,13 +672,12 @@ t_str linenoise(t_const_str prompt) * histories, but will work well for a few hundred of entries. * * Using a circular buffer is smarter, but a bit more complex to handle. */ -bool line_history_add(t_const_str line) +bool line_history_add(t_const_str line) { - t_str linecopy; - t_vec_str *history; + t_str linecopy; + t_vec_str *history; history = get_history(); - if (history->len != 0 && !strcmp(history->buffer[history->len - 1], line)) return (false); linecopy = str_clone(line); @@ -741,21 +689,22 @@ bool line_history_add(t_const_str line) /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ -t_error line_history_save(t_str name) +t_error line_history_save(t_str name) { - t_fd *fd; - t_usize j; - t_vec_str *history; + t_fd *fd; + t_usize j; + t_vec_str *history; history = get_history(); - - fd = open_fd(name, FD_READ, FD_CLOSE_ON_EXEC | FD_TRUNCATE | FD_CREATE, FP_OWRITE); + fd = open_fd(name, FD_READ, FD_CLOSE_ON_EXEC | FD_TRUNCATE | FD_CREATE, + FP_OWRITE); if (fd == NULL) return (ERROR); j = 0; while (j < history->len) { - write_fd(fd, (t_u8 *)history->buffer[j], str_len(history->buffer[j]), NULL); + write_fd(fd, (t_u8 *)history->buffer[j], str_len(history->buffer[j]), + NULL); write_fd(fd, (t_u8 *)"\n", 1, NULL); j++; } @@ -763,10 +712,10 @@ t_error line_history_save(t_str name) return (NO_ERROR); } -t_error gnl_wrapper(t_fd *fd, t_string *out) +t_error gnl_wrapper(t_fd *fd, t_string *out) { - bool error; - t_string value; + bool error; + t_string value; if (out == NULL || fd == NULL) return (ERROR); @@ -781,11 +730,11 @@ t_error gnl_wrapper(t_fd *fd, t_string *out) * * If the file exists and the operation succeeded 0 is returned, otherwise * on error -1 is returned. */ -t_error line_history_load(t_str name) +t_error line_history_load(t_str name) { - t_fd *fd; - t_string tmp; - t_vec_str *history; + t_fd *fd; + t_string tmp; + t_vec_str *history; fd = open_fd(name, FD_READ, FD_CLOSE_ON_EXEC, FP_ALL_READ); if (fd == NULL) @@ -793,7 +742,8 @@ t_error line_history_load(t_str name) history = get_history(); while (!gnl_wrapper(fd, &tmp)) { - while (tmp.len != 0 && (tmp.buf[tmp.len - 1] == '\n' || tmp.buf[tmp.len - 1] == '\r')) + while (tmp.len != 0 && (tmp.buf[tmp.len - 1] == '\n' || tmp.buf[tmp.len + - 1] == '\r')) string_pop(&tmp); vec_str_push(history, tmp.buf); } diff --git a/line/src/line_globals.c b/line/src/line_globals.c new file mode 100644 index 00000000..9770662b --- /dev/null +++ b/line/src/line_globals.c @@ -0,0 +1,63 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* line_globals.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: maiboyer +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/07/10 15:47:12 by maiboyer #+# #+# */ +/* Updated: 2024/07/10 15:48:09 by maiboyer ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "me/types.h" +#include "line/_line_internal.h" +#include "line/_line_structs.h" +#include "me/mem/mem.h" + +t_const_str get_unfinished_str(void) +{ + return ("If you see this," + " you are misusing the API" + " if it returns get_unfinished_str() " + " the user is yet editing the line. " + "See the README file for more information."); +} + +t_vec_str *get_history(void) +{ + static t_vec_str history = {}; + static bool init = false; + + if (!init) + { + history = vec_str_new(256, (void (*)())mem_free); + init = true; + } + return (&history); +} + +t_raw_mode_state *get_raw_mode_state(void) +{ + static t_raw_mode_state state = {}; + + return (&state); +} + +/* Free the history, but does not reset it. Only used when we have to + * exit() to avoid memory leaks are reported by valgrind & co. */ +void free_history(void) +{ + t_vec_str *history; + + history = get_history(); + vec_str_free(*history); +} + +/* At exit we'll try to fix the terminal to the initial conditions. */ +__attribute__((destructor)) void line_uninit_lib(void) +{ + line_disable_raw_mode(get_stdin()); + free_history(); +} +