From RDB: a patch to allow special keys (^C, ^Z, Delete, Return) to
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Wed, 9 May 2001 15:12:26 +0000 (15:12 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Wed, 9 May 2001 15:12:26 +0000 (15:12 +0000)
send Telnet special sequences (Interrupt Process, Suspend, Erase
Char, End Of Line) instead of their ASCII equivalents. In particular
Return -> Telnet End Of Line is _always_ enabled irrespective of the
configuration, while the others are optional. Also in this patch, an
entertainingly ghastly use of `switch' to allow literal ^M^J to do
the same thing as magic-^M (the Return key) when in Raw protocol.

git-svn-id: svn://svn.tartarus.org/sgt/putty@1109 cda61777-01e9-0310-a592-d414129be87e

ldisc.c
putty.h
settings.c
telnet.c
windlg.c
window.c

diff --git a/ldisc.c b/ldisc.c
index a42db93..b307d4e 100644 (file)
--- a/ldisc.c
+++ b/ldisc.c
@@ -59,9 +59,11 @@ static void bsb(int n)
 }
 
 #define CTRL(x) (x^'@')
+#define KCTRL(x) ((x^'@') | 0x100)
 
 void ldisc_send(char *buf, int len)
 {
+    int keyflag = 0;
     /*
      * Called with len=0 when the options change. We must inform
      * the front end in case it needs to know.
@@ -71,12 +73,19 @@ void ldisc_send(char *buf, int len)
        ldisc_update(ECHOING, EDITING);
     }
     /*
+     * 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;
            switch (term_quotenext ? ' ' : c) {
                /*
                 * ^h/^?: delete one char and output one BSB
@@ -92,8 +101,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]));
@@ -147,17 +156,51 @@ void ldisc_send(char *buf, int len)
                    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(term_buf, term_buflen);
+                   if (cfg.protocol == PROT_RAW)
+                       back->send("\r\n", 2);
+                   else if (cfg.protocol == PROT_TELNET)
+                       back->special(TS_EOL);
+                   else
+                       back->send("\r", 1);
+                   if (ECHOING)
+                       c_write("\r\n", 2);
+                   term_buflen = 0;
+                   break;
+               }
+               /* FALLTHROUGH */
              default:                 /* get to this label from ^V handler */
                if (term_buflen >= term_bufsiz) {
                    term_bufsiz = term_buflen + 256;
@@ -165,7 +208,7 @@ void ldisc_send(char *buf, int len)
                }
                term_buf[term_buflen++] = c;
                if (ECHOING)
-                   pwrite(c);
+                   pwrite((unsigned char) c);
                term_quotenext = FALSE;
                break;
            }
@@ -181,7 +224,34 @@ 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'):
+                   back->special(TS_EOL);
+                   break;
+                 case CTRL('?'):
+                 case CTRL('H'):
+                   if (cfg.telnet_keyboard) {
+                       back->special(TS_EC);
+                       break;
+                   }
+                 case CTRL('C'):
+                   if (cfg.telnet_keyboard) {
+                       back->special(TS_IP);
+                       break;
+                   }
+                 case CTRL('Z'):
+                   if (cfg.telnet_keyboard) {
+                       back->special(TS_SUSP);
+                       break;
+                   }
+
+                 default:
+                   back->send(buf, len);
+                   break;
+               }
+           } else
+               back->send(buf, len);
        }
     }
 }
diff --git a/putty.h b/putty.h
index cd7262f..ee6a2b7 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -107,7 +107,8 @@ GLOBAL HWND logbox;
 
 typedef enum {
     TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT,
-    TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING
+    TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING,
+    TS_EOL
 } Telnet_Special;
 
 typedef enum {
@@ -206,6 +207,7 @@ typedef struct {
     int app_cursor;
     int app_keypad;
     int nethack_keypad;
+    int telnet_keyboard;
     int alt_f4;                               /* is it special? */
     int alt_space;                    /* is it special? */
     int alt_only;                     /* is it special? */
index 141e9b8..4493966 100644 (file)
@@ -100,6 +100,7 @@ void save_settings(char *section, int do_host, Config * cfg)
     write_setting_i(sesskey, "AltOnly", cfg->alt_only);
     write_setting_i(sesskey, "ComposeKey", cfg->compose_key);
     write_setting_i(sesskey, "CtrlAltKeys", cfg->ctrlaltkeys);
+    write_setting_i(sesskey, "TelnetKey", cfg->telnet_keyboard);
     write_setting_i(sesskey, "LocalEcho", cfg->localecho);
     write_setting_i(sesskey, "LocalEdit", cfg->localedit);
     write_setting_s(sesskey, "Answerback", cfg->answerback);
@@ -263,6 +264,7 @@ void load_settings(char *section, int do_host, Config * cfg)
     gppi(sesskey, "AltOnly", 0, &cfg->alt_only);
     gppi(sesskey, "ComposeKey", 0, &cfg->compose_key);
     gppi(sesskey, "CtrlAltKeys", 1, &cfg->ctrlaltkeys);
+    gppi(sesskey, "TelnetKey", 0, &cfg->telnet_keyboard);
     gppi(sesskey, "LocalEcho", LD_BACKEND, &cfg->localecho);
     gppi(sesskey, "LocalEdit", LD_BACKEND, &cfg->localedit);
     gpps(sesskey, "Answerback", "PuTTY", cfg->answerback,
index 41de319..b00a6da 100644 (file)
--- a/telnet.c
+++ b/telnet.c
@@ -653,7 +653,9 @@ static void telnet_send(char *buf, int len)
     char *p;
     static unsigned char iac[2] = { IAC, IAC };
     static unsigned char cr[2] = { CR, NUL };
+#if 0
     static unsigned char nl[2] = { CR, LF };
+#endif
 
     if (s == NULL)
        return;
@@ -667,7 +669,7 @@ static void telnet_send(char *buf, int len)
        sk_write(s, q, p - q);
 
        while (p < buf + len && !iswritable((unsigned char) *p)) {
-           sk_write(s, (unsigned char) *p == IAC ? iac : nl, 2);
+           sk_write(s, (unsigned char) *p == IAC ? iac : cr, 2);
            p++;
        }
     }
@@ -759,6 +761,9 @@ static void telnet_special(Telnet_Special code)
        b[1] = xEOF;
        sk_write(s, b, 2);
        break;
+      case TS_EOL:
+       sk_write(s, "\r\n", 2);
+       break;
       case TS_SYNCH:
        b[1] = DM;
        sk_write(s, b, 1);
index eb01fc2..2f9ca56 100644 (file)
--- a/windlg.c
+++ b/windlg.c
@@ -291,6 +291,7 @@ enum { IDCX_ABOUT =
     IDC_CURAPPLIC,
     IDC_COMPOSEKEY,
     IDC_CTRLALTKEYS,
+    IDC_TELNETKEY,
     keyboardpanelend,
 
     terminalpanelstart,
@@ -588,6 +589,7 @@ static void init_dlg_ctrls(HWND hwnd)
     CheckDlgButton(hwnd, IDC_ALTONLY, cfg.alt_only);
     CheckDlgButton(hwnd, IDC_COMPOSEKEY, cfg.compose_key);
     CheckDlgButton(hwnd, IDC_CTRLALTKEYS, cfg.ctrlaltkeys);
+    CheckDlgButton(hwnd, IDC_TELNETKEY, cfg.telnet_keyboard);
     CheckRadioButton(hwnd, IDC_ECHOBACKEND, IDC_ECHONO,
                     cfg.localecho == LD_BACKEND ? IDC_ECHOBACKEND :
                     cfg.localecho == LD_YES ? IDC_ECHOYES : IDC_ECHONO);
@@ -1098,6 +1100,11 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
            staticedit(&cp, "Auto-login &username", IDC_LOGSTATIC,
                       IDC_LOGEDIT, 50);
            endbox(&cp);
+       } else {
+           beginbox(&cp, "Adjust telnet session.", IDC_BOX_CONNECTION1);
+           checkbox(&cp, "Keyboard sends telnet Backspace and Interrupt",
+                    IDC_TELNETKEY);
+           endbox(&cp);
        }
        beginbox(&cp, "Sending of null packets to keep session active",
                 IDC_BOX_CONNECTION2);
@@ -1107,7 +1114,7 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
     }
 
     if (panel == telnetpanelstart) {
-       /* The Telnet panel. Accelerators used: [acgo] svldr bft */
+       /* The Telnet panel. Accelerators used: [acgo] svldr bftk */
        struct ctlpos cp;
        ctlposinit(&cp, hwnd, 80, 3, 13);
        if (dlgtype == 0) {
@@ -1128,6 +1135,8 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
            radioline(&cp, "&Telnet negotiation mode:", IDC_ACTSTATIC, 2,
                      "Passive", IDC_TPASSIVE, "Active",
                      IDC_TACTIVE, NULL);
+           checkbox(&cp, "&Keyboard sends telnet Backspace and Interrupt",
+                    IDC_TELNETKEY);
            endbox(&cp);
        }
     }
@@ -1698,6 +1707,12 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
                        cfg.ctrlaltkeys =
                        IsDlgButtonChecked(hwnd, IDC_CTRLALTKEYS);
                break;
+             case IDC_TELNETKEY:
+               if (HIWORD(wParam) == BN_CLICKED ||
+                   HIWORD(wParam) == BN_DOUBLECLICKED)
+                       cfg.telnet_keyboard =
+                       IsDlgButtonChecked(hwnd, IDC_TELNETKEY);
+               break;
              case IDC_WRAPMODE:
                if (HIWORD(wParam) == BN_CLICKED ||
                    HIWORD(wParam) == BN_DOUBLECLICKED)
index e89a309..48ba35b 100644 (file)
--- a/window.c
+++ b/window.c
@@ -2554,7 +2554,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
        if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
            *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
-           return p - output;
+           *p++ = 0;
+           return -2;
        }
        if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
            *p++ = 0x1B;
@@ -2572,7 +2573,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        }
        if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
            *p++ = 3;
-           return p - output;
+           *p++ = 0;
+           return -2;
        }
        if (wParam == VK_PAUSE) {      /* Break/Pause */
            *p++ = 26;
@@ -2786,7 +2788,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
         */
        if (wParam == VK_RETURN) {     /* Return */
            *p++ = 0x0D;
-           return p - output;
+           *p++ = 0;
+           return -2;
        }
     }