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:
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;