Re: ksh: add support for bracketed paste mode

2021-09-07 Thread Theo de Raadt
bracketed paste mode is complete garbage which should never have been
invented.  It creates as many problems as it solves.

It is way too easy for a tty to get into the mode, and get stuck.  Yes,
getting stuck happens, especially when programs crash while in the mode.

At that point, the user experience is terrible, and no user knows how to
fix it.

I despise the thing, and will raise objection everytime.

Sören Tempel  wrote:

> 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;
>   boolprev;
>  
> +#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 @@ staticchar*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 intx_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();
>  
> + /* 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 

Re: ksh: add support for bracketed paste mode

2021-09-04 Thread Sören Tempel
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;
boolprev;
 
+#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_PREkb_encode("^[[200~")
+#define BRPASTE_POST   kb_encode("^[[201~")
+
 /* edit.c */
 intx_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();
 
+   /* 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 

ksh: add support for bracketed paste mode

2021-04-02 Thread Sören Tempel
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;
boolprev;
 
+   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_PREkb_encode("^[[200~")
+#define BRPASTE_POST   kb_encode("^[[201~")
+
 /* edit.c */
 intx_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();
 
+   /* 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