Ping.

I've been using the patched the last ~6 months and didn't encounter any
problems with it. If there is no interested in bracketed paste mode or
if the design needs to be revised in general, please let me know. Below
is a slightly updated version of this patch which should fix the build
without -DEMACS.

Greetings,
Sören

diff --git edit.c edit.c
index 3089d195d20..cef7949b88d 100644
--- edit.c
+++ edit.c
@@ -150,12 +150,28 @@ x_puts(const char *s)
                shf_putc(*s++, shl_out);
 }
 
+#ifdef EMACS
+static void
+x_paste_mode(bool onoff)
+{
+       if (!Flag(FBBRACKETPASTE))
+               return;
+
+       printf((onoff) ? BRPASTE_INT : BRPASTE_DEINT);
+       fflush(stdout);
+}
+#endif
+
 bool
 x_mode(bool onoff)
 {
        static bool     x_cur_mode;
        bool            prev;
 
+#ifdef EMACS
+       x_paste_mode(onoff);
+#endif
+
        if (x_cur_mode == onoff)
                return x_cur_mode;
        prev = x_cur_mode;
diff --git edit.h edit.h
index 0b604cd64fb..8cc774f01dd 100644
--- edit.h
+++ edit.h
@@ -34,6 +34,12 @@ extern X_chars edchars;
 #define XCF_FULLPATH   BIT(2)  /* command completion: store full path */
 #define XCF_COMMAND_FILE (XCF_COMMAND|XCF_FILE)
 
+/* https://www.xfree86.org/4.7.0/ctlseqs.html#Bracketed%20Paste%20Mode */
+#define BRPASTE_INT    "\033[?2004h"
+#define BRPASTE_DEINT  "\033[?2004l"
+#define BRPASTE_PRE    kb_encode("^[[200~")
+#define BRPASTE_POST   kb_encode("^[[201~")
+
 /* edit.c */
 int    x_getc(void);
 void   x_flush(void);
diff --git emacs.c emacs.c
index 1a5ff6e9927..f4d369fbc38 100644
--- emacs.c
+++ emacs.c
@@ -118,6 +118,7 @@ static      char    *xmp;           /* mark pointer */
 static char    *killstack[KILLSIZE];
 static int     killsp, killtp;
 static int     x_literal_set;
+static int     x_brack_paste;
 static int     x_arg_set;
 static char    *macro_args;
 static int     prompt_skip;
@@ -203,6 +204,8 @@ static int  x_fold_lower(int);
 static int     x_fold_upper(int);
 static int     x_set_arg(int);
 static int     x_comment(int);
+static int     x_brack_paste_start(int);
+static int     x_brack_paste_end(int);
 #ifdef DEBUG
 static int     x_debug_info(int);
 #endif
@@ -260,6 +263,8 @@ static const struct x_ftab x_ftab[] = {
        { x_fold_upper,         "upcase-word",                  XF_ARG },
        { x_set_arg,            "set-arg",                      XF_NOBIND },
        { x_comment,            "comment",                      0 },
+       { x_brack_paste_start,  "bracketed-paste-start",        0 },
+       { x_brack_paste_end,    "bracketed-paste-end",          0 },
        { 0, 0, 0 },
 #ifdef DEBUG
        { x_debug_info,         "debug-info",                   0 },
@@ -316,6 +321,8 @@ x_emacs(char *buf, size_t len)
        }
 
        x_literal_set = 0;
+       x_brack_paste = 0;
+
        x_arg = -1;
        x_last_command = NULL;
        while (1) {
@@ -353,6 +360,13 @@ x_emacs(char *buf, size_t len)
                        }
                }
 
+               /* In bracketed paste mode only allow x_brack_paste_end,
+                * to quit this mode, for all other commands insert a literal. 
*/
+               if (x_brack_paste && (submatch == 1 && kmatch)) {
+                       if (kmatch->ftab->xf_func != x_brack_paste_end)
+                               submatch = 0;
+               }
+
                if (submatch == 1 && kmatch) {
                        if (kmatch->ftab->xf_func == x_ins_string &&
                            kmatch->args && !macro_args) {
@@ -1479,6 +1493,10 @@ x_init_emacs(void)
 
        TAILQ_INIT(&kblist);
 
+       /* bracketed paste mode */
+       kb_add_string(x_brack_paste_start,      NULL, BRPASTE_PRE);
+       kb_add_string(x_brack_paste_end,        NULL, BRPASTE_POST);
+
        /* man page order */
        kb_add(x_abort,                 CTRL('G'), 0);
        kb_add(x_mv_back,               CTRL('B'), 0);
@@ -1991,6 +2009,21 @@ x_comment(int c)
        return KSTD;
 }
 
+int
+x_brack_paste_start(int c)
+{
+       if (Flag(FBBRACKETPASTE))
+               x_brack_paste = 1;
+       return KSTD;
+}
+
+int
+x_brack_paste_end(int c)
+{
+       if (Flag(FBBRACKETPASTE))
+               x_brack_paste = 0;
+       return KSTD;
+}
 
 /* NAME:
  *      x_prev_histword - recover word from prev command
diff --git misc.c misc.c
index 672b5416419..392aa49b990 100644
--- misc.c
+++ misc.c
@@ -123,6 +123,9 @@ const struct option sh_options[] = {
         */
        { "allexport",  'a',            OF_ANY },
        { "braceexpand",  0,            OF_ANY }, /* non-standard */
+#ifdef EMACS
+       { "bracket-paste", 0,           OF_ANY }, /* non-standard */
+#endif
        { "bgnice",       0,            OF_ANY },
        { NULL, 'c',        OF_CMDLINE },
        { "csh-history",  0,            OF_ANY }, /* non-standard */
diff --git sh.h sh.h
index 93beef31d46..652a1f6dd06 100644
--- sh.h
+++ sh.h
@@ -134,6 +134,9 @@ extern const struct option sh_options[];
 enum sh_flag {
        FEXPORT = 0,    /* -a: export all */
        FBRACEEXPAND,   /* enable {} globbing */
+#ifdef EMACS
+       FBBRACKETPASTE, /* bracketed paste mode */
+#endif
        FBGNICE,        /* bgnice */
        FCOMMAND,       /* -c: (invocation) execute specified command */
        FCSHHISTORY,    /* csh-style history enabled */

Sören Tempel <soe...@soeren-tempel.net> wrote:
> Hello,
> 
> The diff below adds support for bracketed paste mode to ksh. Bracketed
> paste mode is a set of special escape sequences, which are employed by
> many terminal emulators to allow programs run inside of them to
> distinguish pasted text from typed-in text [0]. This is useful for
> preventing pasted text from accidentally executing commands in the
> application it was pasted to. Commonly this problem arises when copying
> text from a web browser to a shell since the user may have copied
> hidden text from the web page which may contain control characters such
> as \n [1].
> 
> Bracketed paste mode is supported by all mainstream terminal emulators
> including xterm, urxvt, and gnome-terminal. The implementation proposed
> here was tested with urxvt. However, since we cannot determine in
> advance whether the utilized terminal emulator supports these escape
> sequences the feature must be explicitly activated using the set
> builtin. I haven't tested the diff extensively yet but it is rather
> simple so I don't expect any breakages. One limitation of the proposed
> implementation is that in only works in emacs mode. Would be happy to
> address any feedback you might have.
> 
> Greetings,
> Sören
> 
> [0]: https://www.xfree86.org/4.7.0/ctlseqs.html#Bracketed%20Paste%20Mode
> [1]: https://thejh.net/misc/website-terminal-copy-paste
> 
> diff --git bin/ksh/edit.c bin/ksh/edit.c
> index 3089d195d..4b6d45050 100644
> --- bin/ksh/edit.c
> +++ bin/ksh/edit.c
> @@ -150,12 +150,23 @@ x_puts(const char *s)
>               shf_putc(*s++, shl_out);
>  }
>  
> +static void
> +x_paste_mode(bool onoff)
> +{
> +     if (!Flag(FBBRACKETPASTE))
> +             return;
> +
> +     printf((onoff) ? BRPASTE_INT : BRPASTE_DEINT);
> +     fflush(stdout);
> +}
> +
>  bool
>  x_mode(bool onoff)
>  {
>       static bool     x_cur_mode;
>       bool            prev;
>  
> +     x_paste_mode(onoff);
>       if (x_cur_mode == onoff)
>               return x_cur_mode;
>       prev = x_cur_mode;
> diff --git bin/ksh/edit.h bin/ksh/edit.h
> index 0b604cd64..8cc774f01 100644
> --- bin/ksh/edit.h
> +++ bin/ksh/edit.h
> @@ -34,6 +34,12 @@ extern X_chars edchars;
>  #define XCF_FULLPATH BIT(2)  /* command completion: store full path */
>  #define XCF_COMMAND_FILE (XCF_COMMAND|XCF_FILE)
>  
> +/* https://www.xfree86.org/4.7.0/ctlseqs.html#Bracketed%20Paste%20Mode */
> +#define BRPASTE_INT  "\033[?2004h"
> +#define BRPASTE_DEINT        "\033[?2004l"
> +#define BRPASTE_PRE  kb_encode("^[[200~")
> +#define BRPASTE_POST kb_encode("^[[201~")
> +
>  /* edit.c */
>  int  x_getc(void);
>  void x_flush(void);
> diff --git bin/ksh/emacs.c bin/ksh/emacs.c
> index 694c402ff..96136263e 100644
> --- bin/ksh/emacs.c
> +++ bin/ksh/emacs.c
> @@ -118,6 +118,7 @@ static    char    *xmp;           /* mark pointer */
>  static       char    *killstack[KILLSIZE];
>  static       int     killsp, killtp;
>  static       int     x_literal_set;
> +static       int     x_brack_paste;
>  static       int     x_arg_set;
>  static       char    *macro_args;
>  static       int     prompt_skip;
> @@ -203,6 +204,8 @@ static int        x_fold_lower(int);
>  static int   x_fold_upper(int);
>  static int   x_set_arg(int);
>  static int   x_comment(int);
> +static int   x_brack_paste_start(int);
> +static int   x_brack_paste_end(int);
>  #ifdef DEBUG
>  static int   x_debug_info(int);
>  #endif
> @@ -260,6 +263,8 @@ static const struct x_ftab x_ftab[] = {
>       { x_fold_upper,         "upcase-word",                  XF_ARG },
>       { x_set_arg,            "set-arg",                      XF_NOBIND },
>       { x_comment,            "comment",                      0 },
> +     { x_brack_paste_start,  "bracketed-paste-start",        0 },
> +     { x_brack_paste_end,    "bracketed-paste-end",          0 },
>       { 0, 0, 0 },
>  #ifdef DEBUG
>       { x_debug_info,         "debug-info",                   0 },
> @@ -316,6 +321,8 @@ x_emacs(char *buf, size_t len)
>       }
>  
>       x_literal_set = 0;
> +     x_brack_paste = 0;
> +
>       x_arg = -1;
>       x_last_command = NULL;
>       while (1) {
> @@ -353,6 +360,13 @@ x_emacs(char *buf, size_t len)
>                       }
>               }
>  
> +             /* In bracketed paste mode only allow x_brack_paste_end,
> +              * to quit this mode, for all other commands insert a literal. 
> */
> +             if (x_brack_paste && (submatch == 1 && kmatch)) {
> +                     if (kmatch->ftab->xf_func != x_brack_paste_end)
> +                             submatch = 0;
> +             }
> +
>               if (submatch == 1 && kmatch) {
>                       if (kmatch->ftab->xf_func == x_ins_string &&
>                           kmatch->args && !macro_args) {
> @@ -1479,6 +1493,10 @@ x_init_emacs(void)
>  
>       TAILQ_INIT(&kblist);
>  
> +     /* bracketed paste mode */
> +     kb_add_string(x_brack_paste_start,      NULL, BRPASTE_PRE);
> +     kb_add_string(x_brack_paste_end,        NULL, BRPASTE_POST);
> +
>       /* man page order */
>       kb_add(x_abort,                 CTRL('G'), 0);
>       kb_add(x_mv_back,               CTRL('B'), 0);
> @@ -1984,6 +2002,21 @@ x_comment(int c)
>       return KSTD;
>  }
>  
> +int
> +x_brack_paste_start(int c)
> +{
> +     if (Flag(FBBRACKETPASTE))
> +             x_brack_paste = 1;
> +     return KSTD;
> +}
> +
> +int
> +x_brack_paste_end(int c)
> +{
> +     if (Flag(FBBRACKETPASTE))
> +             x_brack_paste = 0;
> +     return KSTD;
> +}
>  
>  /* NAME:
>   *      x_prev_histword - recover word from prev command
> diff --git bin/ksh/ksh.1 bin/ksh/ksh.1
> index a707a8c57..4ec4bb104 100644
> --- bin/ksh/ksh.1
> +++ bin/ksh/ksh.1
> @@ -3595,6 +3595,9 @@ during file name generation.
>  Print commands and parameter assignments when they are executed, preceded by
>  the value of
>  .Ev PS4 .
> +.It Ic bracket-paste
> +Enables bracketed paste mode, requires the associated escape sequences to be
> +supported by the utilized terminal emulator.
>  .It Ic bgnice
>  Background jobs are run with lower priority.
>  .It Ic braceexpand
> diff --git bin/ksh/misc.c bin/ksh/misc.c
> index 672b54164..392aa49b9 100644
> --- bin/ksh/misc.c
> +++ bin/ksh/misc.c
> @@ -123,6 +123,9 @@ const struct option sh_options[] = {
>        */
>       { "allexport",  'a',            OF_ANY },
>       { "braceexpand",  0,            OF_ANY }, /* non-standard */
> +#ifdef EMACS
> +     { "bracket-paste", 0,           OF_ANY }, /* non-standard */
> +#endif
>       { "bgnice",       0,            OF_ANY },
>       { NULL, 'c',        OF_CMDLINE },
>       { "csh-history",  0,            OF_ANY }, /* non-standard */
> diff --git bin/ksh/sh.h bin/ksh/sh.h
> index 93beef31d..571e24955 100644
> --- bin/ksh/sh.h
> +++ bin/ksh/sh.h
> @@ -134,6 +134,7 @@ extern const struct option sh_options[];
>  enum sh_flag {
>       FEXPORT = 0,    /* -a: export all */
>       FBRACEEXPAND,   /* enable {} globbing */
> +     FBBRACKETPASTE, /* bracketed paste mode */
>       FBGNICE,        /* bgnice */
>       FCOMMAND,       /* -c: (invocation) execute specified command */
>       FCSHHISTORY,    /* csh-style history enabled */

Reply via email to