Major destabilisation, phase 2. This time it's the backends' turn:
[u/mdw/putty] / ldisc.c
diff --git a/ldisc.c b/ldisc.c
index a42db93..d7b6e40 100644 (file)
--- a/ldisc.c
+++ b/ldisc.c
@@ -5,22 +5,24 @@
  * depending on what's currently configured.
  */
 
-#include <windows.h>
 #include <stdio.h>
 #include <ctype.h>
 
 #include "putty.h"
+#include "terminal.h"
 
 #define ECHOING (cfg.localecho == LD_YES || \
                  (cfg.localecho == LD_BACKEND && \
-                      (back->ldisc(LD_ECHO) || term_ldisc(LD_ECHO))))
+                      (back->ldisc(backhandle, LD_ECHO) || \
+                          term_ldisc(term, LD_ECHO))))
 #define EDITING (cfg.localedit == LD_YES || \
                  (cfg.localedit == LD_BACKEND && \
-                      (back->ldisc(LD_EDIT) || term_ldisc(LD_EDIT))))
+                      (back->ldisc(backhandle, LD_EDIT) || \
+                          term_ldisc(term, LD_EDIT))))
 
 static void c_write(char *buf, int len)
 {
-    from_backend(0, buf, len);
+    from_backend(term, 0, buf, len);
 }
 
 static char *term_buf = NULL;
@@ -28,7 +30,7 @@ static int term_buflen = 0, term_bufsiz = 0, term_quotenext = 0;
 
 static int plen(unsigned char c)
 {
-    if ((c >= 32 && c <= 126) || (c >= 160))
+    if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(term)))
        return 1;
     else if (c < 128)
        return 2;                      /* ^x for some x */
@@ -38,7 +40,7 @@ static int plen(unsigned char c)
 
 static void pwrite(unsigned char c)
 {
-    if ((c >= 32 && c <= 126) || (c >= 160)) {
+    if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(term))) {
        c_write(&c, 1);
     } else if (c < 128) {
        char cc[2];
@@ -59,9 +61,11 @@ static void bsb(int n)
 }
 
 #define CTRL(x) (x^'@')
+#define KCTRL(x) ((x^'@') | 0x100)
 
-void ldisc_send(char *buf, int len)
+void ldisc_send(char *buf, int len, int interactive)
 {
+    int keyflag = 0;
     /*
      * Called with len=0 when the options change. We must inform
      * the front end in case it needs to know.
@@ -69,14 +73,30 @@ void ldisc_send(char *buf, int len)
     if (len == 0) {
        void ldisc_update(int echo, int edit);
        ldisc_update(ECHOING, EDITING);
+       return;
+    }
+    /*
+     * Notify the front end that something was pressed, in case
+     * it's depending on finding out (e.g. keypress termination for
+     * Close On Exit). 
+     */
+    frontend_keypress();
+    /*
+     * Less than zero means null terminated special string.
+     */
+    if (len < 0) {
+       len = strlen(buf);
+       keyflag = KCTRL('@');
     }
     /*
      * Either perform local editing, or just send characters.
      */
     if (EDITING) {
        while (len--) {
-           char c;
-           c = *buf++;
+           int c;
+           c = *buf++ + keyflag;
+           if (!interactive && c == '\r')
+               c += KCTRL('@');
            switch (term_quotenext ? ' ' : c) {
                /*
                 * ^h/^?: delete one char and output one BSB
@@ -92,8 +112,8 @@ void ldisc_send(char *buf, int len)
                 * else send line and reset to BOL
                 * ^m: send line-plus-\r\n and reset to BOL
                 */
-             case CTRL('H'):
-             case CTRL('?'):          /* backspace/delete */
+             case KCTRL('H'):
+             case KCTRL('?'):         /* backspace/delete */
                if (term_buflen > 0) {
                    if (ECHOING)
                        bsb(plen(term_buf[term_buflen - 1]));
@@ -120,13 +140,20 @@ void ldisc_send(char *buf, int len)
                        bsb(plen(term_buf[term_buflen - 1]));
                    term_buflen--;
                }
-               back->special(TS_EL);
+               back->special(backhandle, TS_EL);
+                /*
+                 * We don't send IP, SUSP or ABORT if the user has
+                 * configured telnet specials off! This breaks
+                 * talkers otherwise.
+                 */
+                if (!cfg.telnet_keyboard)
+                    goto default_case;
                if (c == CTRL('C'))
-                   back->special(TS_IP);
+                   back->special(backhandle, TS_IP);
                if (c == CTRL('Z'))
-                   back->special(TS_SUSP);
+                   back->special(backhandle, TS_SUSP);
                if (c == CTRL('\\'))
-                   back->special(TS_ABORT);
+                   back->special(backhandle, TS_ABORT);
                break;
              case CTRL('R'):          /* redraw line */
                if (ECHOING) {
@@ -141,38 +168,73 @@ void ldisc_send(char *buf, int len)
                break;
              case CTRL('D'):          /* logout or send */
                if (term_buflen == 0) {
-                   back->special(TS_EOF);
+                   back->special(backhandle, TS_EOF);
                } else {
-                   back->send(term_buf, term_buflen);
+                   back->send(backhandle, term_buf, term_buflen);
                    term_buflen = 0;
                }
                break;
-             case CTRL('M'):          /* send with newline */
-               if (term_buflen > 0)
-                   back->send(term_buf, term_buflen);
-               if (cfg.protocol == PROT_RAW)
-                   back->send("\r\n", 2);
-               else
-                   back->send("\r", 1);
-               if (ECHOING)
-                   c_write("\r\n", 2);
-               term_buflen = 0;
-               break;
+               /*
+                * This particularly hideous bit of code from RDB
+                * allows ordinary ^M^J to do the same thing as
+                * magic-^M when in Raw protocol. The line `case
+                * KCTRL('M'):' is _inside_ the if block. Thus:
+                * 
+                *  - receiving regular ^M goes straight to the
+                *    default clause and inserts as a literal ^M.
+                *  - receiving regular ^J _not_ directly after a
+                *    literal ^M (or not in Raw protocol) fails the
+                *    if condition, leaps to the bottom of the if,
+                *    and falls through into the default clause
+                *    again.
+                *  - receiving regular ^J just after a literal ^M
+                *    in Raw protocol passes the if condition,
+                *    deletes the literal ^M, and falls through
+                *    into the magic-^M code
+                *  - receiving a magic-^M empties the line buffer,
+                *    signals end-of-line in one of the various
+                *    entertaining ways, and _doesn't_ fall out of
+                *    the bottom of the if and through to the
+                *    default clause because of the break.
+                */
+             case CTRL('J'):
+               if (cfg.protocol == PROT_RAW &&
+                   term_buflen > 0 && term_buf[term_buflen - 1] == '\r') {
+                   if (ECHOING)
+                       bsb(plen(term_buf[term_buflen - 1]));
+                   term_buflen--;
+                   /* FALLTHROUGH */
+             case KCTRL('M'):         /* send with newline */
+                   if (term_buflen > 0)
+                       back->send(backhandle, term_buf, term_buflen);
+                   if (cfg.protocol == PROT_RAW)
+                       back->send(backhandle, "\r\n", 2);
+                   else if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
+                       back->special(backhandle, TS_EOL);
+                   else
+                       back->send(backhandle, "\r", 1);
+                   if (ECHOING)
+                       c_write("\r\n", 2);
+                   term_buflen = 0;
+                   break;
+               }
+               /* FALLTHROUGH */
              default:                 /* get to this label from ^V handler */
+                default_case:
                if (term_buflen >= term_bufsiz) {
                    term_bufsiz = term_buflen + 256;
                    term_buf = saferealloc(term_buf, term_bufsiz);
                }
                term_buf[term_buflen++] = c;
                if (ECHOING)
-                   pwrite(c);
+                   pwrite((unsigned char) c);
                term_quotenext = FALSE;
                break;
            }
        }
     } else {
        if (term_buflen != 0) {
-           back->send(term_buf, term_buflen);
+           back->send(backhandle, term_buf, term_buflen);
            while (term_buflen > 0) {
                bsb(plen(term_buf[term_buflen - 1]));
                term_buflen--;
@@ -181,7 +243,37 @@ void ldisc_send(char *buf, int len)
        if (len > 0) {
            if (ECHOING)
                c_write(buf, len);
-           back->send(buf, len);
+           if (keyflag && cfg.protocol == PROT_TELNET && len == 1) {
+               switch (buf[0]) {
+                 case CTRL('M'):
+                   if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
+                       back->special(backhandle, TS_EOL);
+                   else
+                       back->send(backhandle, "\r", 1);
+                   break;
+                 case CTRL('?'):
+                 case CTRL('H'):
+                   if (cfg.telnet_keyboard) {
+                       back->special(backhandle, TS_EC);
+                       break;
+                   }
+                 case CTRL('C'):
+                   if (cfg.telnet_keyboard) {
+                       back->special(backhandle, TS_IP);
+                       break;
+                   }
+                 case CTRL('Z'):
+                   if (cfg.telnet_keyboard) {
+                       back->special(backhandle, TS_SUSP);
+                       break;
+                   }
+
+                 default:
+                   back->send(backhandle, buf, len);
+                   break;
+               }
+           } else
+               back->send(backhandle, buf, len);
        }
     }
 }