nano

nano with my custom patches
git clone git://bsandro.tech/nano
Log | Files | Refs | README | LICENSE

commit bf12c90b076a424d2a399c543de24dac906f0a21
parent 615570cf5e37e808cf8fb5f0b34dd07bae7ddc89
Author: Benno Schulenberg <bensberg@telfort.nl>
Date:   Sun,  9 Mar 2025 12:04:41 +0100

input: implement bracketed pastes in a different manner

Ask ncurses to recognize the escape sequences that mark the start
and end of a bracketed paste, and thus benefit from the waiting that
ncurses does for an escape sequence to complete.  This helps prevent
nano from failing to recognize an end-of-paste sequence (resulting
in hanging or inserting some sequence characters into the buffer).

This addresses https://savannah.gnu.org/bugs/?66858 in a better way.
Reported-by: Doug Smythies <dsmythies@telus.net>

Diffstat:
Msrc/browser.c | 10+++++-----
Msrc/definitions.h | 7++++---
Msrc/global.c | 12+++---------
Msrc/help.c | 8++++----
Msrc/nano.c | 17++++++++---------
Msrc/prompt.c | 18+++++++++++++++---
Msrc/prototypes.h | 2+-
Msrc/winio.c | 3+--
8 files changed, 41 insertions(+), 36 deletions(-)

diff --git a/src/browser.c b/src/browser.c @@ -483,11 +483,7 @@ char *browse(char *path) continue; } #endif /* ENABLE_MOUSE */ -#ifndef NANO_TINY - while (bracketed_paste && kbinput != FOREIGN_SEQUENCE) - kbinput = get_kbinput(midwin, BLIND); - bracketed_paste = FALSE; -#endif + function = interpret(kbinput); if (function == do_help || function == full_refresh) { @@ -638,6 +634,10 @@ char *browse(char *path) implant(first_sc_for(MBROWSER, function)->expansion); #endif #ifndef NANO_TINY + } else if (kbinput == START_OF_PASTE) { + while (get_kbinput(midwin, BLIND) != END_OF_PASTE) + ; + statusline(AHEM, _("Paste is ignored")); } else if (kbinput == THE_WINDOW_RESIZED) { ; /* Gets handled below. */ #endif diff --git a/src/definitions.h b/src/definitions.h @@ -217,6 +217,10 @@ #define FOCUS_IN 0x491 #define FOCUS_OUT 0x499 +/* Custom keycodes for signaling the start and end of a bracketed paste. */ +#define START_OF_PASTE 0x4B5 +#define END_OF_PASTE 0x4BE + /* Special keycodes for when a string bind has been partially implanted * or has an unpaired opening brace, or when a function in a string bind * needs execution or a specified function name is invalid. */ @@ -233,9 +237,6 @@ #define THE_WINDOW_RESIZED 0x4F7 #endif -/* A special keycode to signal the beginning and end of a bracketed paste. */ -#define BRACKETED_PASTE_MARKER 0x4FB - /* A special keycode for when a key produces an unknown escape sequence. */ #define FOREIGN_SEQUENCE 0x4FC diff --git a/src/global.c b/src/global.c @@ -43,8 +43,6 @@ bool shift_held; /* Whether Shift was being held together with a movement key. */ bool mute_modifiers = FALSE; /* Whether to ignore modifier keys while running a macro or string bind. */ -bool bracketed_paste = FALSE; - /* Whether text is being pasted into nano from outside. */ bool we_are_running = FALSE; /* Becomes TRUE as soon as all options and files have been read. */ @@ -480,11 +478,6 @@ const keystruct *get_shortcut(const int keycode) if (meta_key && keycode < 0x20) return NULL; -#ifndef NANO_TINY - /* During a paste at a prompt, ignore all command keycodes. */ - if (bracketed_paste && keycode != BRACKETED_PASTE_MARKER) - return NULL; -#endif #ifdef ENABLE_NANORC if (keycode == PLANTED_A_COMMAND) return planted_shortcut; @@ -1583,8 +1576,9 @@ void shortcut_init(void) add_to_sclist((MMOST & ~MMAIN) | MYESNO, "", KEY_CANCEL, do_cancel, 0); add_to_sclist(MMAIN, "", KEY_CENTER, do_center, 0); add_to_sclist(MMAIN, "", KEY_SIC, do_insertfile, 0); - /* Catch and ignore bracketed paste marker keys. */ - add_to_sclist(MMOST|MBROWSER|MHELP|MYESNO, "", BRACKETED_PASTE_MARKER, do_nothing, 0); + add_to_sclist(MMAIN, "", START_OF_PASTE, suck_up_input_and_paste_it, 0); + add_to_sclist(MMOST, "", START_OF_PASTE, do_nothing, 0); + add_to_sclist(MMOST, "", END_OF_PASTE, do_nothing, 0); #else add_to_sclist(MMOST|MBROWSER|MHELP|MYESNO, "", KEY_FRESH, full_refresh, 0); #endif diff --git a/src/help.c b/src/help.c @@ -476,10 +476,6 @@ void show_help(void) #ifndef NANO_TINY spotlighted = FALSE; - - while (bracketed_paste && kbinput != FOREIGN_SEQUENCE) - kbinput = get_kbinput(midwin, BLIND); - bracketed_paste = FALSE; #endif function = interpret(kbinput); @@ -508,6 +504,10 @@ void show_help(void) get_mouseinput(&dummy_row, &dummy_col, TRUE); #endif #ifndef NANO_TINY + } else if (kbinput == START_OF_PASTE) { + while (get_kbinput(midwin, BLIND) != END_OF_PASTE) + ; + statusline(AHEM, _("Paste is ignored")); } else if (kbinput == THE_WINDOW_RESIZED) { ; /* Nothing to do. */ #endif diff --git a/src/nano.c b/src/nano.c @@ -1307,8 +1307,6 @@ void unbound_key(int code) statusline(AHEM, _("Missing }")); #endif #ifndef NANO_TINY - else if (code == BRACKETED_PASTE_MARKER) - statusline(AHEM, _("Paste is ignored")); else if (code > KEY_F0 && code < KEY_F0 + 25) /* TRANSLATORS: This refers to an unbound function key. */ statusline(AHEM, _("Unbound key: F%i"), code - KEY_F0); @@ -1446,7 +1444,7 @@ void suck_up_input_and_paste_it(void) line->data = copy_of(""); cutbuffer = line; - while (bracketed_paste) { + while (TRUE) { input = get_kbinput(midwin, BLIND); if ((0x20 <= input && input <= 0xFF && input != DEL_CODE) || input == '\t') { @@ -1458,8 +1456,8 @@ void suck_up_input_and_paste_it(void) line = line->next; line->data = copy_of(""); index = 0; - } else if (input != BRACKETED_PASTE_MARKER) - bracketed_paste = FALSE; + } else + break; } if (ISSET(VIEW_MODE)) @@ -1467,7 +1465,7 @@ void suck_up_input_and_paste_it(void) else paste_text(); - if (input == FOREIGN_SEQUENCE) + if (input != END_OF_PASTE) statusline(ALERT, _("Flawed paste")); free_lines(cutbuffer); @@ -1725,9 +1723,6 @@ void process_a_keystroke(void) } else if (openfile->current != was_current) also_the_last = FALSE; - if (bracketed_paste) - suck_up_input_and_paste_it(); - if (ISSET(STATEFLAGS) && openfile->mark != was_mark) titlebar(NULL); #endif @@ -2483,6 +2478,10 @@ int main(int argc, char **argv) shiftaltright = get_keycode("kRIT4", SHIFT_ALT_RIGHT); shiftaltup = get_keycode("kUP4", SHIFT_ALT_UP); shiftaltdown = get_keycode("kDN4", SHIFT_ALT_DOWN); + + /* Tell ncurses to transform bracketed-paste sequences into keycodes. */ + define_key("\e[200~", START_OF_PASTE); + define_key("\e[201~", END_OF_PASTE); #endif mousefocusin = get_keycode("kxIN", FOCUS_IN); mousefocusout = get_keycode("kxOUT", FOCUS_OUT); diff --git a/src/prompt.c b/src/prompt.c @@ -427,6 +427,9 @@ functionptrtype acquire_an_answer(int *actual, bool *listed, /* The length of the fragment that the user tries to tab complete. */ #endif #endif +#ifndef NANO_TINY + bool bracketed_paste = FALSE; +#endif const keystruct *shortcut; functionptrtype function; int input; @@ -450,6 +453,8 @@ functionptrtype acquire_an_answer(int *actual, bool *listed, #endif return NULL; } + if (input == START_OF_PASTE || input == END_OF_PASTE) + bracketed_paste = (input == START_OF_PASTE); #endif #ifdef ENABLE_MOUSE /* For a click on a shortcut, read in the resulting keycode. */ @@ -466,6 +471,12 @@ functionptrtype acquire_an_answer(int *actual, bool *listed, /* When it's a normal character, add it to the answer. */ absorb_character(input, function); +#ifndef NANO_TINY + /* Ignore any commands inside an external paste. */ + if (bracketed_paste) + continue; +#endif + if (function == do_cancel || function == do_enter) break; @@ -711,10 +722,11 @@ int ask_user(bool withall, const char *question) continue; /* Accept first character of an external paste and ignore the rest. */ - if (bracketed_paste) + if (kbinput == START_OF_PASTE) { kbinput = get_kbinput(footwin, BLIND); - while (bracketed_paste) - get_kbinput(footwin, BLIND); + while (get_kbinput(footwin, BLIND) != END_OF_PASTE) + ; + } #endif #ifdef ENABLE_NLS diff --git a/src/prototypes.h b/src/prototypes.h @@ -32,7 +32,6 @@ extern bool shifted_metas; extern bool meta_key; extern bool shift_held; extern bool mute_modifiers; -extern bool bracketed_paste; extern bool we_are_running; extern bool more_than_one; @@ -441,6 +440,7 @@ void confirm_margin(void); #endif void unbound_key(int code); bool changes_something(functionptrtype f); +void suck_up_input_and_paste_it(void); void inject(char *burst, size_t count); /* Most functions in prompt.c. */ diff --git a/src/winio.c b/src/winio.c @@ -728,8 +728,7 @@ int convert_CSI_sequence(const int *seq, size_t length, int *consumed) /* Esc [ 2 0 0 ~ == start of a bracketed paste, * Esc [ 2 0 1 ~ == end of a bracketed paste. */ *consumed = 4; - bracketed_paste = (seq[2] == '0'); - return BRACKETED_PASTE_MARKER; + return (seq[2] == '0') ? START_OF_PASTE : END_OF_PASTE; } else { *consumed = length; return FOREIGN_SEQUENCE;