Currently, there are four separate workarounds for pseudo console
output in pty_master_fwd_thread. Each workaround has its own 'for'
loop that iterates over the entire output buffer, which is not
efficient. This patch consolidates these loops and introduces a
single state machine to handle all worarounds at once. In addition,
the workarouds are moved into a dedicated function,
'workarounds_for_pseudo_console_output()' to improve readability.

Suggested-by: Johannes Schindelin <[email protected]>
Reviewed-by:
Signed-off-by: Takashi Yano <[email protected]>
---
 winsup/cygwin/fhandler/pty.cc | 283 ++++++++++++++++------------------
 1 file changed, 129 insertions(+), 154 deletions(-)

diff --git a/winsup/cygwin/fhandler/pty.cc b/winsup/cygwin/fhandler/pty.cc
index 32e50540e..7fa747e0a 100644
--- a/winsup/cygwin/fhandler/pty.cc
+++ b/winsup/cygwin/fhandler/pty.cc
@@ -2642,6 +2642,134 @@ pty_master_thread (VOID *arg)
   return fhandler_pty_master::pty_master_thread (&p);
 }
 
+static DWORD
+workarounds_for_pseudo_console_output (char *outbuf, DWORD rlen)
+{
+  int state = 0;
+  int start_at = 0;
+  bool is_csi = false;
+  bool is_osc = false;
+  int arg = 0;
+  bool saw_greater_than_sign = false;
+  for (DWORD i=0; i<rlen; i++)
+    if (state == 0 && outbuf[i] == '\033')
+      {
+       start_at = i;
+       state = 1;
+       is_csi = false;
+       is_osc = false;
+       arg = 0;
+       continue;
+      }
+    else if (state == 1)
+      {
+       switch (outbuf[i])
+         {
+         case '[':
+           is_csi = true;
+           state = 2;
+           break;
+         case ']':
+           is_osc = true;
+           state = 2;
+           break;
+         case '\033':
+           start_at = i;
+           state = 1;
+           break;
+         default:
+           state = 0;
+         }
+       continue;
+      }
+    else if (is_csi)
+      {
+       if (state == 2 && outbuf[i] == '>')
+         saw_greater_than_sign = true;
+       else if (state == 2 && (isdigit (outbuf[i]) || outbuf[i] == ';'))
+         continue;
+       else if (state == 2)
+         {
+           if (saw_greater_than_sign && outbuf[i] == 'm')
+             {
+               /* Remove CSI > Pm m */
+               memmove (&outbuf[start_at], &outbuf[i+1], rlen-i-1);
+               rlen = start_at + rlen - i - 1;
+               i = start_at - 1;
+               state = 0;
+             }
+           else if (wincap.has_pcon_omit_nl_before_cursor_move ()
+                    && !saw_greater_than_sign && outbuf[i] == 'H')
+             /* Workaround for rlwrap in Win11. rlwrap treats text between
+                NLs as a line, however, pseudo console in Win11 somtimes
+                omits NL before "CSIm;nH". This does not happen in Win10. */
+             {
+               /* Add omitted CR NL before "CSIm;nH". However, when the
+                  cusor is on the bottom-most line, adding NL might cause
+                  unexpected scrolling. To avoid this, add "CSI H" before
+                  CR NL. */
+               const char *ins = "\033[H\r\n";
+               const int ins_len = strlen (ins);
+               if (rlen + ins_len <= NT_MAX_PATH)
+                 {
+                   memmove (&outbuf[start_at + ins_len], &outbuf[start_at],
+                            rlen - start_at);
+                   memcpy (&outbuf[start_at], ins, ins_len);
+                   rlen += ins_len;
+                   i += ins_len;
+                 }
+             }
+           state = 0;
+         }
+       else if (outbuf[i] == '\033')
+         {
+           start_at = i;
+           is_csi = false;
+           state = 1;
+         }
+       else
+         state = 0;
+      }
+    else if (is_osc)
+      {
+       if (state == 2 && isdigit (outbuf[i]))
+         arg = arg * 10 + (outbuf[i] - '0');
+       else if (state == 2 && outbuf[i] == ';')
+         state = 3;
+       else if (state == 3 && outbuf[i] == '\033')
+         state = 4;
+       else if ((state == 3 && outbuf[i] == '\a')
+                || (state == 4 && outbuf[i] == '\\'))
+         {
+           const char *helper_str = "\\bin\\cygwin-console-helper.exe";
+           if (outbuf[start_at + 4] == '?' /* OSC Ps; ? BEL/ST */
+               /* Stray set title at the start up of pcon */
+               || (arg == 0 && memmem (&outbuf[start_at], i + 1 - start_at,
+                                       helper_str, strlen (helper_str))))
+             {
+               /* Remove this ESC sequence */
+               memmove (&outbuf[start_at], &outbuf[i+1], rlen-i-1);
+               rlen = start_at + rlen - i - 1;
+               i = start_at - 1;
+             }
+           state = 0;
+         }
+       else if (state == 3)
+         continue;
+       else if (outbuf[i] == '\033')
+         {
+           start_at = i;
+           is_osc = false;
+           state = 1;
+         }
+       else
+         state = 0;
+      }
+    else
+      state = 0; /* Do not reach */
+  return rlen;
+}
+
 /* The function pty_master_fwd_thread() should be static because the
    instance is deleted if the master is dup()'ed and the original is
    closed. In this case, dup()'ed instance still exists, therefore,
@@ -2676,160 +2804,7 @@ fhandler_pty_master::pty_master_fwd_thread (const 
master_fwd_thread_param_t *p)
       char *ptr = outbuf;
       if (p->ttyp->pcon_activated)
        {
-         /* Avoid setting window title to "cygwin-console-helper.exe" */
-         int state = 0;
-         int start_at = 0;
-         for (DWORD i=0; i<rlen; i++)
-           if (state == 0 && outbuf[i] == '\033')
-             {
-               start_at = i;
-               state = 1;
-               continue;
-             }
-           else if ((state == 1 && outbuf[i] == ']') ||
-                    (state == 2 && outbuf[i] == '0') ||
-                    (state == 3 && outbuf[i] == ';') ||
-                    (state == 4 && outbuf[i] == '\033'))
-             {
-               state ++;
-               continue;
-             }
-           else if ((state == 4 && outbuf[i] == '\a')
-                    || (state == 5 && outbuf[i] == '\\'))
-             {
-               const char *helper_str = "\\bin\\cygwin-console-helper.exe";
-               if (memmem (&outbuf[start_at], i + 1 - start_at,
-                           helper_str, strlen (helper_str)))
-                 {
-                   memmove (&outbuf[start_at], &outbuf[i+1], rlen-i-1);
-                   rlen = wlen = start_at + rlen - i - 1;
-                   i = start_at - 1;
-                 }
-               state = 0;
-               continue;
-             }
-           else if (state == 4)
-             continue;
-           else if (outbuf[i] == '\033')
-             {
-               start_at = i;
-               state = 1;
-               continue;
-             }
-           else
-             {
-               state = 0;
-               continue;
-             }
-
-         /* Remove CSI > Pm m */
-         state = 0;
-         for (DWORD i = 0; i < rlen; i++)
-           if (outbuf[i] == '\033')
-             {
-               start_at = i;
-               state = 1;
-               continue;
-             }
-           else if ((state == 1 && outbuf[i] == '[')
-                    || (state == 2 && outbuf[i] == '>'))
-             {
-               state ++;
-               continue;
-             }
-           else if (state == 3 && (isdigit (outbuf[i]) || outbuf[i] == ';'))
-             continue;
-           else if (state == 3 && outbuf[i] == 'm')
-             {
-               memmove (&outbuf[start_at], &outbuf[i+1], rlen-i-1);
-               rlen = wlen = start_at + rlen - i - 1;
-               state = 0;
-               i = start_at - 1;
-               continue;
-             }
-           else
-             state = 0;
-
-         /* Remove OSC Ps ; ? BEL/ST */
-         state = 0;
-         for (DWORD i = 0; i < rlen; i++)
-           if (state == 0 && outbuf[i] == '\033')
-             {
-               start_at = i;
-               state = 1;
-               continue;
-             }
-           else if ((state == 1 && outbuf[i] == ']')
-                    || (state == 2 && outbuf[i] == ';')
-                    || (state == 3 && outbuf[i] == '?')
-                    || (state == 4 && outbuf[i] == '\033'))
-             {
-               state ++;
-               continue;
-             }
-           else if (state == 2 && isdigit (outbuf[i]))
-             continue;
-           else if ((state == 4 && outbuf[i] == '\a')
-                    || (state == 5 && outbuf[i] == '\\'))
-             {
-               memmove (&outbuf[start_at], &outbuf[i+1], rlen-i-1);
-               rlen = wlen = start_at + rlen - i - 1;
-               state = 0;
-               i = start_at - 1;
-               continue;
-             }
-           else if (outbuf[i] == '\033')
-             {
-               start_at = i;
-               state = 1;
-               continue;
-             }
-           else
-             state = 0;
-
-         /* Workaround for rlwrap in Win11. rlwrap treats text between
-            NLs as a line, however, pseudo console in Win11 somtimes
-            omits NL before "CSIm;nH". This does not happen in Win10. */
-         if (wincap.has_pcon_omit_nl_before_cursor_move ())
-           {
-             state = 0;
-             for (DWORD i = 0; i < rlen; i++)
-               if (state == 0 && outbuf[i] == '\033')
-                 {
-                   start_at = i;
-                   state++;
-                   continue;
-                 }
-               else if (state == 1 && outbuf[i] == '[')
-                 {
-                   state++;
-                   continue;
-                 }
-               else if (state == 2
-                        && (isdigit (outbuf[i]) || outbuf[i] == ';'))
-                 continue;
-               else if (state == 2 && outbuf[i] == 'H')
-                 {
-                   /* Add omitted CR NL before "CSIm;nH". However, when the
-                      cusor is on the bottom-most line, adding NL might cause
-                      unexpected scrolling. To avoid this, add "CSI H" before
-                      CR NL. */
-                   const char *ins = "\033[H\r\n";
-                   const int ins_len = strlen (ins);
-                   if (rlen + ins_len <= NT_MAX_PATH)
-                     {
-                       memmove (&outbuf[start_at + ins_len],
-                                &outbuf[start_at], rlen - start_at);
-                       memcpy (&outbuf[start_at], ins, ins_len);
-                       rlen += ins_len;
-                       i += ins_len;
-                     }
-                   state = 0;
-                   continue;
-                 }
-               else
-                 state = 0;
-           }
+         wlen = rlen = workarounds_for_pseudo_console_output (outbuf, rlen);
 
          if (p->ttyp->term_code_page != CP_UTF8)
            {
-- 
2.51.0

Reply via email to