Rethink the whole line discipline architecture. Instead of having
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Wed, 24 Jan 2001 14:08:20 +0000 (14:08 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Wed, 24 Jan 2001 14:08:20 +0000 (14:08 +0000)
multiple switchable line disciplines, we now have a single unified
one which changes its behaviour based on option settings. Each
option setting can be suggested by the back end and/or the terminal
handler, and can be forcibly overridden by the configuration. Local
echo and local line editing are separate, independently switchable,
options.

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

12 files changed:
ldisc.c
plink.c
putty.h
raw.c
rlogin.c
scp.c
settings.c
ssh.c
telnet.c
terminal.c
windlg.c
window.c

diff --git a/ldisc.c b/ldisc.c
index 80659cc..1ab832d 100644 (file)
--- a/ldisc.c
+++ b/ldisc.c
@@ -1,21 +1,27 @@
+/*
+ * ldisc.c: PuTTY line discipline. Sits between the input coming
+ * from keypresses in the window, and the output channel leading to
+ * the back end. Implements echo and/or local line editing,
+ * depending on what's currently configured.
+ */
+
 #include <windows.h>
 #include <stdio.h>
 #include <ctype.h>
 
 #include "putty.h"
 
-/*
- * ldisc.c: PuTTY line disciplines
- */
+#define ECHOING (cfg.localecho == LD_YES || \
+                 (cfg.localecho == LD_BACKEND && \
+                      (back->ldisc(LD_ECHO) || term_ldisc(LD_ECHO))))
+#define EDITING (cfg.localedit == LD_YES || \
+                 (cfg.localedit == LD_BACKEND && \
+                      (back->ldisc(LD_EDIT) || term_ldisc(LD_EDIT))))
 
 static void c_write (char *buf, int len) {
     from_backend(0, buf, len);
 }
 
-static void c_write1 (char c) {
-    c_write(&c, 1);
-}
-
 static char *term_buf = NULL;
 static int term_buflen = 0, term_bufsiz = 0, term_quotenext = 0;
 
@@ -32,7 +38,7 @@ static int plen(unsigned char c) {
 static void pwrite(unsigned char c) {
     if ((c >= 32 && c <= 126) ||
         (c >= 160)) {
-        c_write1(c);
+        c_write(&c, 1);
     } else if (c < 128) {
         char cc[2];
         cc[1] = (c == 127 ? '?' : c + 0x40);
@@ -52,108 +58,124 @@ static void bsb(int n) {
 
 #define CTRL(x) (x^'@')
 
-static void term_send(char *buf, int len) {
-    while (len--) {
-       char c;
-        c = *buf++;
-       switch (term_quotenext ? ' ' : c) {
-           /*
-            * ^h/^?: delete one char and output one BSB
-            * ^w: delete, and output BSBs, to return to last space/nonspace
-            * boundary
-            * ^u: delete, and output BSBs, to return to BOL
-            * ^c: Do a ^u then send a telnet IP
-            * ^z: Do a ^u then send a telnet SUSP
-            * ^\: Do a ^u then send a telnet ABORT
-            * ^r: echo "^R\n" and redraw line
-            * ^v: quote next char
-            * ^d: if at BOL, end of file and close connection, else send line
-            * and reset to BOL
-            * ^m: send line-plus-\r\n and reset to BOL
-            */
-         case CTRL('H'): case CTRL('?'):      /* backspace/delete */
-           if (term_buflen > 0) {
-               bsb(plen(term_buf[term_buflen-1]));
-               term_buflen--;
-           }
-           break;
-         case CTRL('W'):                      /* delete word */
-           while (term_buflen > 0) {
-               bsb(plen(term_buf[term_buflen-1]));
-               term_buflen--;
-               if (term_buflen > 0 &&
-                   isspace(term_buf[term_buflen-1]) &&
-                   !isspace(term_buf[term_buflen]))
-                   break;
-           }
-           break;
-         case CTRL('U'):              /* delete line */
-         case CTRL('C'):              /* Send IP */
-         case CTRL('\\'):             /* Quit */
-         case CTRL('Z'):              /* Suspend */
-           while (term_buflen > 0) {
-               bsb(plen(term_buf[term_buflen-1]));
-               term_buflen--;
-           }
-           back->special (TS_EL);
-           if( c == CTRL('C') )  back->special (TS_IP);
-           if( c == CTRL('Z') )  back->special (TS_SUSP);
-           if( c == CTRL('\\') ) back->special (TS_ABORT);
-            break;
-         case CTRL('R'):              /* redraw line */
-           c_write("^R\r\n", 4);
-           {
-               int i;
-               for (i = 0; i < term_buflen; i++)
-                   pwrite(term_buf[i]);
-           }
-           break;
-         case CTRL('V'):              /* quote next char */
-           term_quotenext = TRUE;
-           break;
-         case CTRL('D'):              /* logout or send */
-           if (term_buflen == 0) {
-               back->special (TS_EOF);
-           } else {
-               back->send(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);
-           c_write("\r\n", 2);
-           term_buflen = 0;
-           break;
-         default:                     /* get to this label from ^V handler */
-           if (term_buflen >= term_bufsiz) {
-               term_bufsiz = term_buflen + 256;
-               term_buf = saferealloc(term_buf, term_bufsiz);
-           }
-           term_buf[term_buflen++] = c;
-           pwrite(c);
-            term_quotenext = FALSE;
-           break;
-       }
+void ldisc_send(char *buf, int len) {
+    /*
+     * Called with len=0 when the options change. We must inform
+     * the front end in case it needs to know.
+     */
+    if (len == 0) {
+        void ldisc_update(int echo, int edit);
+        ldisc_update(ECHOING, EDITING);
     }
-}
-
-static void simple_send(char *buf, int len) {
-    if( term_buflen != 0 )
-    {
-       back->send(term_buf, term_buflen);
-       while (term_buflen > 0) {
-           bsb(plen(term_buf[term_buflen-1]));
-           term_buflen--;
-       }
+    /*
+     * Either perform local editing, or just send characters.
+     */
+    if (EDITING) {
+        while (len--) {
+            char c;
+            c = *buf++;
+            switch (term_quotenext ? ' ' : c) {
+                /*
+                 * ^h/^?: delete one char and output one BSB
+                 * ^w: delete, and output BSBs, to return to last
+                 * space/nonspace boundary
+                 * ^u: delete, and output BSBs, to return to BOL
+                 * ^c: Do a ^u then send a telnet IP
+                 * ^z: Do a ^u then send a telnet SUSP
+                 * ^\: Do a ^u then send a telnet ABORT
+                 * ^r: echo "^R\n" and redraw line
+                 * ^v: quote next char
+                 * ^d: if at BOL, end of file and close connection,
+                 * else send line and reset to BOL
+                 * ^m: send line-plus-\r\n and reset to BOL
+                 */
+              case CTRL('H'): case CTRL('?'):      /* backspace/delete */
+                if (term_buflen > 0) {
+                    if (ECHOING)
+                        bsb(plen(term_buf[term_buflen-1]));
+                    term_buflen--;
+                }
+                break;
+              case CTRL('W'):                 /* delete word */
+                while (term_buflen > 0) {
+                    if (ECHOING)
+                        bsb(plen(term_buf[term_buflen-1]));
+                    term_buflen--;
+                    if (term_buflen > 0 &&
+                        isspace(term_buf[term_buflen-1]) &&
+                        !isspace(term_buf[term_buflen]))
+                        break;
+                }
+                break;
+              case CTRL('U'):         /* delete line */
+              case CTRL('C'):         /* Send IP */
+              case CTRL('\\'):        /* Quit */
+              case CTRL('Z'):         /* Suspend */
+                while (term_buflen > 0) {
+                    if (ECHOING)
+                        bsb(plen(term_buf[term_buflen-1]));
+                    term_buflen--;
+                }
+                back->special (TS_EL);
+                if( c == CTRL('C') )  back->special (TS_IP);
+                if( c == CTRL('Z') )  back->special (TS_SUSP);
+                if( c == CTRL('\\') ) back->special (TS_ABORT);
+                break;
+              case CTRL('R'):         /* redraw line */
+                if (ECHOING) {
+                    int i;
+                    c_write("^R\r\n", 4);
+                    for (i = 0; i < term_buflen; i++)
+                        pwrite(term_buf[i]);
+                }
+                break;
+              case CTRL('V'):         /* quote next char */
+                term_quotenext = TRUE;
+                break;
+              case CTRL('D'):         /* logout or send */
+                if (term_buflen == 0) {
+                    back->special (TS_EOF);
+                } else {
+                    back->send(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;
+              default:                     /* get to this label from ^V handler */
+                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);
+                term_quotenext = FALSE;
+                break;
+            }
+        }
+    } else {
+        if( term_buflen != 0 )
+        {
+            back->send(term_buf, term_buflen);
+            while (term_buflen > 0) {
+                bsb(plen(term_buf[term_buflen-1]));
+                term_buflen--;
+            }
+        }
+        if (len > 0) {
+            if (ECHOING)
+                c_write(buf, len);
+            back->send(buf, len);
+        }
     }
-    if (len > 0)
-        back->send(buf, len);
 }
-
-Ldisc ldisc_term = { term_send };
-Ldisc ldisc_simple = { simple_send };
diff --git a/plink.c b/plink.c
index 61552fa..6cbc2f6 100644 (file)
--- a/plink.c
+++ b/plink.c
@@ -119,18 +119,11 @@ void verify_ssh_host_key(char *host, int port, char *keytype,
     }
 }
 
-HANDLE outhandle, errhandle;
+HANDLE inhandle, outhandle, errhandle;
 DWORD orig_console_mode;
 
 WSAEVENT netevent;
 
-void begin_session(void) {
-    if (!cfg.ldisc_term)
-        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
-    else
-        SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), orig_console_mode);
-}
-
 void from_backend(int is_stderr, char *data, int len) {
     int pos;
     DWORD ret;
@@ -144,6 +137,23 @@ void from_backend(int is_stderr, char *data, int len) {
     }
 }
 
+int term_ldisc(int mode) { return FALSE; }
+void ldisc_update(int echo, int edit) {
+    /* Update stdin read mode to reflect changes in line discipline. */
+    DWORD mode;
+
+    mode = ENABLE_PROCESSED_INPUT;
+    if (echo)
+        mode = mode | ENABLE_ECHO_INPUT;
+    else
+        mode = mode &~ ENABLE_ECHO_INPUT;
+    if (edit)
+        mode = mode | ENABLE_LINE_INPUT;
+    else
+        mode = mode &~ ENABLE_LINE_INPUT;
+    SetConsoleMode(inhandle, mode);
+}
+
 struct input_data {
     DWORD len;
     char buffer[4096];
@@ -403,7 +413,6 @@ int main(int argc, char **argv) {
                     len2 = strlen(cp); len -= len2; cp += len2;
                 }
                 cfg.nopty = TRUE;      /* command => no terminal */
-                cfg.ldisc_term = TRUE; /* use stdin like a line buffer */
                 break;                 /* done with cmdline */
             }
        }
@@ -475,10 +484,11 @@ int main(int argc, char **argv) {
 
     stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL);
 
-    GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &orig_console_mode);
-    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
+    inhandle = GetStdHandle(STD_INPUT_HANDLE);
     outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
     errhandle = GetStdHandle(STD_ERROR_HANDLE);
+    GetConsoleMode(inhandle, &orig_console_mode);
+    SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);
 
     /*
      * Turn off ECHO and LINE input modes. We don't care if this
diff --git a/putty.h b/putty.h
index 20eea8b..5423ae9 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -115,6 +115,21 @@ typedef enum {
     VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN
 } VT_Mode;
 
+enum {
+    /*
+     * Line discipline option states: off, on, up to the backend.
+     */
+    LD_YES, LD_NO, LD_BACKEND
+};
+
+enum {
+    /*
+     * Line discipline options which the backend might try to control.
+     */
+    LD_EDIT,                           /* local line editing */
+    LD_ECHO                            /* local echo */
+};
+
 typedef struct {
     char *(*init) (char *host, int port, char **realhost);
     void (*send) (char *buf, int len);
@@ -122,6 +137,7 @@ typedef struct {
     void (*special) (Telnet_Special code);
     Socket (*socket) (void);
     int (*sendok) (void);
+    int (*ldisc) (int);
     int default_port;
 } Backend;
 
@@ -134,12 +150,6 @@ extern struct backend_list {
 } backends[];
 
 typedef struct {
-    void (*send) (char *buf, int len);
-} Ldisc;
-
-GLOBAL Ldisc *ldisc;
-
-typedef struct {
     /* Basic options */
     char host[512];
     int port;
@@ -176,7 +186,8 @@ typedef struct {
     int alt_f4;                               /* is it special? */
     int alt_space;                    /* is it special? */
     int alt_only;                     /* is it special? */
-    int ldisc_term;
+    int localecho;
+    int localedit;
     int alwaysontop;
     int scroll_on_key;
     int scroll_on_disp;
@@ -333,6 +344,7 @@ void term_invalidate(void);
 void term_blink(int set_cursor);
 void term_paste(void);
 void term_nopaste(void);
+int telnet_ldisc(int option);
 void from_backend(int is_stderr, char *data, int len);
 void logfopen (void); 
 void logfclose (void);
@@ -367,7 +379,7 @@ extern Backend ssh_backend;
  * Exports from ldisc.c.
  */
 
-extern Ldisc ldisc_term, ldisc_simple;
+extern void ldisc_send(char *buf, int len);
 
 /*
  * Exports from sshrand.c.
diff --git a/raw.c b/raw.c
index c354580..2abc0a8 100644 (file)
--- a/raw.c
+++ b/raw.c
@@ -70,11 +70,6 @@ static char *raw_init (char *host, int port, char **realhost) {
 
     sk_addr_free(addr);
 
-    /*
-     * We have no pre-session phase.
-     */
-    begin_session();
-
     return NULL;
 }
 
@@ -109,6 +104,12 @@ static Socket raw_socket(void) { return s; }
 
 static int raw_sendok(void) { return 1; }
 
+static int raw_ldisc(int option) {
+    if (option == LD_EDIT || option == LD_ECHO)
+        return 1;
+    return 0;
+}
+
 Backend raw_backend = {
     raw_init,
     raw_send,
@@ -116,5 +117,6 @@ Backend raw_backend = {
     raw_special,
     raw_socket,
     raw_sendok,
+    raw_ldisc,
     1
 };
index c77f381..20296a0 100644 (file)
--- a/rlogin.c
+++ b/rlogin.c
@@ -102,8 +102,6 @@ static char *rlogin_init (char *host, int port, char **realhost) {
         sk_write(s, &z, 1);
     }
 
-    begin_session();
-
     return NULL;
 }
 
@@ -142,6 +140,10 @@ static Socket rlogin_socket(void) { return s; }
 
 static int rlogin_sendok(void) { return 1; }
 
+static int rlogin_ldisc(int option) {
+    return 0;
+}
+
 Backend rlogin_backend = {
     rlogin_init,
     rlogin_send,
@@ -149,5 +151,6 @@ Backend rlogin_backend = {
     rlogin_special,
     rlogin_socket,
     rlogin_sendok,
+    rlogin_ldisc,
     1
 };
diff --git a/scp.c b/scp.c
index 086c6be..c13be5e 100644 (file)
--- a/scp.c
+++ b/scp.c
@@ -74,7 +74,6 @@ static void send_str_msg(unsigned int msg_id, char *str);
 static void gui_update_stats(char *name, unsigned long size,
                              int percentage, unsigned long elapsed);
 
-void begin_session(void) { }
 void logevent(char *string) { }
 
 void verify_ssh_host_key(char *host, int port, char *keytype,
index 0a7f211..b8a7f79 100644 (file)
@@ -93,7 +93,8 @@ void save_settings (char *section, int do_host, Config *cfg) {
     write_setting_i (sesskey, "AltSpace", cfg->alt_space);
     write_setting_i (sesskey, "AltOnly", cfg->alt_only);
     write_setting_i (sesskey, "ComposeKey", cfg->compose_key);
-    write_setting_i (sesskey, "LdiscTerm", cfg->ldisc_term);
+    write_setting_i (sesskey, "LocalEcho", cfg->localecho);
+    write_setting_i (sesskey, "LocalEdit", cfg->localedit);
     write_setting_i (sesskey, "AlwaysOnTop", cfg->alwaysontop);
     write_setting_i (sesskey, "HideMousePtr", cfg->hide_mouseptr);
     write_setting_i (sesskey, "CurType", cfg->cursor_type);
@@ -237,7 +238,8 @@ void load_settings (char *section, int do_host, Config *cfg) {
     gppi (sesskey, "AltSpace", 0, &cfg->alt_space);
     gppi (sesskey, "AltOnly", 0, &cfg->alt_only);
     gppi (sesskey, "ComposeKey", 0, &cfg->compose_key);
-    gppi (sesskey, "LdiscTerm", 0, &cfg->ldisc_term);
+    gppi (sesskey, "LocalEcho", LD_BACKEND, &cfg->localecho);
+    gppi (sesskey, "LocalEdit", LD_BACKEND, &cfg->localedit);
     gppi (sesskey, "AlwaysOnTop", 0, &cfg->alwaysontop);
     gppi (sesskey, "HideMousePtr", 0, &cfg->hide_mouseptr);
     gppi (sesskey, "CurType", 0, &cfg->cursor_type);
diff --git a/ssh.c b/ssh.c
index 3845264..44075a7 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -272,6 +272,7 @@ int (*ssh_get_password)(const char *prompt, char *str, int maxlen) = NULL;
 static char *savedhost;
 static int savedport;
 static int ssh_send_ok;
+static int ssh_echoing, ssh_editing;
 
 static tree234 *ssh_channels;           /* indexed by local id */
 static struct ssh_channel *mainchan;   /* primary session channel */
@@ -1840,8 +1841,11 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
             crReturnV;
         } else if (pktin.type == SSH1_SMSG_FAILURE) {
             c_write("Server refused to allocate pty\r\n", 32);
+            ssh_editing = ssh_echoing = 1;
         }
        logevent("Allocated pty");
+    } else {
+        ssh_editing = ssh_echoing = 1;
     }
 
     if (cfg.compression) {
@@ -1871,9 +1875,9 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
     if (eof_needed)
         ssh_special(TS_EOF);
 
+    ldisc_send(NULL, 0);               /* cause ldisc to notice changes */
     ssh_send_ok = 1;
     ssh_channels = newtree234(ssh_channelcmp);
-    begin_session();
     while (1) {
        crReturnV;
        if (ispkt) {
@@ -2743,9 +2747,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                 crReturnV;
             }
             c_write("Server refused to allocate pty\r\n", 32);
+            ssh_editing = ssh_echoing = 1;
         } else {
             logevent("Allocated pty");
         }
+    } else {
+        ssh_editing = ssh_echoing = 1;
     }
 
     /*
@@ -2793,8 +2800,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     /*
      * Transfer data!
      */
+    ldisc_send(NULL, 0);               /* cause ldisc to notice changes */
     ssh_send_ok = 1;
-    begin_session();
     while (1) {
         static int try_send;
        crReturnV;
@@ -3005,6 +3012,8 @@ static char *ssh_init (char *host, int port, char **realhost) {
 #endif
 
     ssh_send_ok = 0;
+    ssh_editing = 0;
+    ssh_echoing = 0;
 
     p = connect_to_host(host, port, realhost);
     if (p != NULL)
@@ -3100,6 +3109,12 @@ static Socket ssh_socket(void) { return s; }
 
 static int ssh_sendok(void) { return ssh_send_ok; }
 
+static int ssh_ldisc(int option) {
+    if (option == LD_ECHO) return ssh_echoing;
+    if (option == LD_EDIT) return ssh_editing;
+    return FALSE;
+}
+
 Backend ssh_backend = {
     ssh_init,
     ssh_send,
@@ -3107,5 +3122,6 @@ Backend ssh_backend = {
     ssh_special,
     ssh_socket,
     ssh_sendok,
+    ssh_ldisc,
     22
 };
index f30902d..3e65cd4 100644 (file)
--- a/telnet.c
+++ b/telnet.c
@@ -132,6 +132,8 @@ static struct Opt *opts[] = {
     &o_we_sga, &o_they_sga, NULL
 };
 
+static int echoing = TRUE, editing = TRUE;
+
 static int in_synch;
 static int sb_opt, sb_len;
 static char *sb_buf = NULL;
@@ -170,8 +172,11 @@ static void deactivate_option (struct Opt *o) {
  * Generate side effects of enabling or disabling an option.
  */
 static void option_side_effects(struct Opt *o, int enabled) {
-    if (o->option == TELOPT_ECHO && cfg.ldisc_term)
-       ldisc = enabled ? &ldisc_simple : &ldisc_term;
+    if (o->option == TELOPT_ECHO && o->send == DO)
+        echoing = !enabled;
+    else if (o->option = TELOPT_SGA && o->send == DO)
+        editing = !enabled;
+    ldisc_send(NULL, 0);               /* cause ldisc to notice the change */
 }
 
 static void activate_option (struct Opt *o) {
@@ -509,15 +514,6 @@ static char *telnet_init (char *host, int port, char **realhost) {
     /*
      * Initialise option states.
      */
-    if( cfg.ldisc_term )
-    {
-       struct Opt **o;
-
-       for (o = opts; *o; o++)
-           if ((*o)->state == REQUESTED)
-               (*o)->state = INACTIVE;
-    }
-    else
     {
        struct Opt **o;
 
@@ -531,11 +527,6 @@ static char *telnet_init (char *host, int port, char **realhost) {
      */
     in_synch = FALSE;
 
-    /*
-     * We have no pre-session phase.
-     */
-    begin_session();
-
     return NULL;
 }
 
@@ -638,6 +629,12 @@ static Socket telnet_socket(void) { return s; }
 
 static int telnet_sendok(void) { return 1; }
 
+static int telnet_ldisc(int option) {
+    if (option == LD_ECHO) return echoing;
+    if (option == LD_EDIT) return editing;
+    return FALSE;
+}
+
 Backend telnet_backend = {
     telnet_init,
     telnet_send,
@@ -645,5 +642,6 @@ Backend telnet_backend = {
     telnet_special,
     telnet_socket,
     telnet_sendok,
+    telnet_ldisc,
     23
 };
index 49c0ddf..0f4504a 100644 (file)
@@ -78,6 +78,8 @@ static int use_bce;                  /* Use Background coloured erase */
 static int blinker;                   /* When blinking is the cursor on ? */
 static int tblinker;                  /* When the blinking text is on */
 static int blink_is_real;             /* Actually blink blinking text */
+static int term_echoing;               /* Does terminal want local echo? */
+static int term_editing;               /* Does terminal want local edit? */
 
 static unsigned long cset_attr[2];
 
@@ -183,6 +185,8 @@ static void power_on(void) {
     rvideo = 0;
     cursor_on = 1;
     save_attr = curr_attr = ATTR_DEFAULT;
+    term_editing = term_echoing = FALSE;
+    ldisc_send(NULL, 0);               /* cause ldisc to notice changes */
     app_cursor_keys = cfg.app_cursor;
     app_keypad_keys = cfg.app_keypad;
     use_bce = cfg.bce;
@@ -707,6 +711,10 @@ static void toggle_mode (int mode, int query, int state) {
       case 8:                         /* auto key repeat */
        repeat_off = !state;
        break;
+      case 10:                        /* set local edit mode */
+        term_editing = state;
+        ldisc_send(NULL, 0);           /* cause ldisc to notice changes */
+       break;
       case 25:                        /* enable/disable cursor */
        compatibility2(OTHER,VT220);
        cursor_on = state;
@@ -724,16 +732,8 @@ static void toggle_mode (int mode, int query, int state) {
        insert = state;
        break;
       case 12:                        /* set echo mode */
-       /* 
-        * This may be very good in smcup and rmcup (or smkx & rmkx) if you
-        * have a long RTT and the telnet client/daemon doesn't understand
-        * linemode.
-        *
-        * DONT send TS_RECHO/TS_LECHO; the telnet daemon tries to fix the
-        * tty and _really_ confuses some programs.
-        */
-       compatibility2(OTHER,VT220);
-        ldisc = (state? &ldisc_simple : &ldisc_term);
+        term_echoing = !state;
+        ldisc_send(NULL, 0);           /* cause ldisc to notice changes */
        break;
       case 20:                        /* Return sends ... */
        cr_lf_return = state;
@@ -804,7 +804,7 @@ static int beep_overload = 0;
                 * An xterm returns "xterm" (5 characters)
                 */
                compatibility(OTHER);
-               ldisc->send ("PuTTY", 5);
+               ldisc_send ("PuTTY", 5);
                break;
              case '\007':
                beep_count++; 
@@ -1056,7 +1056,7 @@ static int beep_overload = 0;
                break;
              case 'Z':                /* terminal type query */
                compatibility(VT100);
-               ldisc->send (id_string, strlen(id_string));
+               ldisc_send (id_string, strlen(id_string));
                break;
              case 'c':                /* restore power-on settings */
                compatibility(VT100);
@@ -1199,16 +1199,16 @@ static int beep_overload = 0;
              case 'c':                /* terminal type query */
                compatibility(VT100);
                /* This is the response for a VT102 */
-               ldisc->send (id_string, strlen(id_string));
+               ldisc_send (id_string, strlen(id_string));
                break;
              case 'n':                /* cursor position query */
                if (esc_args[0] == 6) {
                    char buf[32];
                    sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1);
-                   ldisc->send (buf, strlen(buf));
+                   ldisc_send (buf, strlen(buf));
                }
                else if (esc_args[0] == 5) {
-                   ldisc->send ("\033[0n", 4);
+                   ldisc_send ("\033[0n", 4);
                }
                break;
              case 'h':                /* toggle modes to high */
@@ -1420,7 +1420,7 @@ static int beep_overload = 0;
                    if (i == 0 || i == 1) {
                        strcpy (buf, "\033[2;1;1;112;112;1;0x");
                        buf[2] += i;
-                       ldisc->send (buf, 20);
+                       ldisc_send (buf, 20);
                    }
                }
                break;
@@ -1692,7 +1692,7 @@ static int beep_overload = 0;
                termstate = VT52_Y1;
                break;
              case 'Z':
-               ldisc->send ("\033/Z", 3);
+               ldisc_send ("\033/Z", 3);
                break;
              case '=':
                app_keypad_keys = TRUE;
@@ -2162,7 +2162,7 @@ void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) {
 
            /* Assume a small paste will be OK in one go. */
            if (paste_len<256) {
-               ldisc->send (paste_buffer, paste_len);
+               ldisc_send (paste_buffer, paste_len);
                if (paste_buffer) sfree(paste_buffer);
                paste_buffer = 0;
                paste_pos = paste_hold = paste_len = 0;
@@ -2203,7 +2203,7 @@ void term_paste() {
            if (paste_buffer[paste_pos + n++] == '\r')
                break;
        }
-       ldisc->send (paste_buffer+paste_pos, n);
+       ldisc_send (paste_buffer+paste_pos, n);
        paste_pos += n;
 
        if (paste_pos < paste_len) {
@@ -2226,6 +2226,12 @@ void term_deselect (void) {
     term_update();
 }
 
+int term_ldisc(int option) {
+    if (option == LD_ECHO) return term_echoing;
+    if (option == LD_EDIT) return term_editing;
+    return FALSE;
+}
+
 /*
  * from_backend(), to get data from the backend for the terminal.
  */
index 629e689..12bff5d 100644 (file)
--- a/windlg.c
+++ b/windlg.c
@@ -200,6 +200,17 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue,
     IDC_CLOSEEXIT,
     sessionpanelend,
 
+    loggingpanelstart,
+    IDC_BOX_LOGGING1,
+    IDC_LSTATSTATIC,
+    IDC_LSTATOFF,
+    IDC_LSTATASCII,
+    IDC_LSTATRAW,
+    IDC_LGFSTATIC,
+    IDC_LGFEDIT,
+    IDC_LGFBUTTON,
+    loggingpanelend,
+
     keyboardpanelstart,
     IDC_TITLE_KEYBOARD,
     IDC_BOX_KEYBOARD1,
@@ -238,14 +249,14 @@ enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, controlstartvalue,
     IDC_BEEP,
     IDC_BCE,
     IDC_BLINKTEXT,
-    IDC_LDISCTERM,
-    IDC_LSTATSTATIC,
-    IDC_LSTATOFF,
-    IDC_LSTATASCII,
-    IDC_LSTATRAW,
-    IDC_LGFSTATIC,
-    IDC_LGFEDIT,
-    IDC_LGFBUTTON,
+    IDC_ECHOSTATIC,
+    IDC_ECHOBACKEND,
+    IDC_ECHOYES,
+    IDC_ECHONO,
+    IDC_EDITSTATIC,
+    IDC_EDITBACKEND,
+    IDC_EDITYES,
+    IDC_EDITNO,
     terminalpanelend,
 
     windowpanelstart,
@@ -489,7 +500,12 @@ static void init_dlg_ctrls(HWND hwnd) {
     CheckDlgButton (hwnd, IDC_ALTSPACE, cfg.alt_space);
     CheckDlgButton (hwnd, IDC_ALTONLY, cfg.alt_only);
     CheckDlgButton (hwnd, IDC_COMPOSEKEY, cfg.compose_key);
-    CheckDlgButton (hwnd, IDC_LDISCTERM, cfg.ldisc_term);
+    CheckRadioButton (hwnd, IDC_ECHOBACKEND, IDC_ECHONO,
+                     cfg.localecho == LD_BACKEND ? IDC_ECHOBACKEND:
+                      cfg.localecho == LD_YES ? IDC_ECHOYES : IDC_ECHONO);
+    CheckRadioButton (hwnd, IDC_EDITBACKEND, IDC_EDITNO,
+                     cfg.localedit == LD_BACKEND ? IDC_EDITBACKEND:
+                      cfg.localedit == LD_YES ? IDC_EDITYES : IDC_EDITNO);
     CheckDlgButton (hwnd, IDC_ALWAYSONTOP, cfg.alwaysontop);
     CheckDlgButton (hwnd, IDC_SCROLLKEY, cfg.scroll_on_key);
     CheckDlgButton (hwnd, IDC_SCROLLDISP, cfg.scroll_on_disp);
@@ -681,8 +697,26 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) {
         endbox(&cp);
     }
 
+    if (panel == loggingpanelstart) {
+        /* The Logging panel. Accelerators used: [acgo] lpt */
+        struct ctlpos cp;
+        ctlposinit(&cp, hwnd, 80, 3, 13);
+        bartitle(&cp, "Options controlling session logging",
+                 IDC_TITLE_TERMINAL);
+        beginbox(&cp, NULL, IDC_BOX_LOGGING1);
+        radiobig(&cp,
+                 "Session logging:", IDC_LSTATSTATIC,
+                 "Logging &turned off completely", IDC_LSTATOFF,
+                 "Log &printable output only", IDC_LSTATASCII,
+                 "&Log all session output", IDC_LSTATRAW, NULL);
+        editbutton(&cp, "Log &file name:",
+                   IDC_LGFSTATIC, IDC_LGFEDIT, "Bro&wse...",
+                   IDC_LGFBUTTON);
+        endbox(&cp);
+    }
+
     if (panel == terminalpanelstart) {
-        /* The Terminal panel. Accelerators used: [acgo] &dflbenuw */
+        /* The Terminal panel. Accelerators used: [acgo] &dflbentuw */
         struct ctlpos cp;
         ctlposinit(&cp, hwnd, 80, 3, 13);
         bartitle(&cp, "Options controlling the terminal emulation",
@@ -695,19 +729,18 @@ static void create_controls(HWND hwnd, int dlgtype, int panel) {
         checkbox(&cp, "&Beep enabled", IDC_BEEP);
         checkbox(&cp, "Use background colour to &erase screen", IDC_BCE);
         checkbox(&cp, "Enable bli&nking text", IDC_BLINKTEXT);
-        checkbox(&cp, "&Use local terminal line discipline", IDC_LDISCTERM);
         endbox(&cp);
 
-        beginbox(&cp, "Control session logging",
+        beginbox(&cp, "Line discipline options",
                  IDC_BOX_TERMINAL2);
-        radiobig(&cp,
-                 "Session logging:", IDC_LSTATSTATIC,
-                 "Logging turned &off completely", IDC_LSTATOFF,
-                 "Log printable output only", IDC_LSTATASCII,
-                 "Log all session output", IDC_LSTATRAW, NULL);
-        editbutton(&cp, "Log &file name:",
-                   IDC_LGFSTATIC, IDC_LGFEDIT, "Bro&wse...",
-                   IDC_LGFBUTTON);
+        radioline(&cp, "Local echo:", IDC_ECHOSTATIC, 3,
+                  "A&uto", IDC_ECHOBACKEND,
+                  "Force on", IDC_ECHOYES,
+                  "Force off", IDC_ECHONO, NULL);
+        radioline(&cp, "Local line editing:", IDC_EDITSTATIC, 3,
+                  "Au&to", IDC_EDITBACKEND,
+                  "Force on", IDC_EDITYES,
+                  "Force off", IDC_EDITNO, NULL);
         endbox(&cp);
     }
 
@@ -1081,6 +1114,7 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg,
         * Set up the tree view contents.
         */
         hsession = treeview_insert(&tvfaff, 0, "Session");
+        treeview_insert(&tvfaff, 1, "Logging");
         treeview_insert(&tvfaff, 0, "Terminal");
         treeview_insert(&tvfaff, 1, "Keyboard");
         treeview_insert(&tvfaff, 0, "Window");
@@ -1144,6 +1178,8 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg,
             }
            if (!strcmp(buffer, "Session"))
                create_controls(hwnd, dlgtype, sessionpanelstart);
+           if (!strcmp(buffer, "Logging"))
+               create_controls(hwnd, dlgtype, loggingpanelstart);
            if (!strcmp(buffer, "Keyboard"))
                create_controls(hwnd, dlgtype, keyboardpanelstart);
            if (!strcmp(buffer, "Terminal"))
@@ -1398,10 +1434,25 @@ static int GenericMainDlgProc (HWND hwnd, UINT msg,
                HIWORD(wParam) == BN_DOUBLECLICKED)
                cfg.alt_only = IsDlgButtonChecked (hwnd, IDC_ALTONLY);
            break;
-         case IDC_LDISCTERM:
+         case IDC_ECHOBACKEND:
+         case IDC_ECHOYES:
+         case IDC_ECHONO:
            if (HIWORD(wParam) == BN_CLICKED ||
-               HIWORD(wParam) == BN_DOUBLECLICKED)
-               cfg.ldisc_term = IsDlgButtonChecked (hwnd, IDC_LDISCTERM);
+               HIWORD(wParam) == BN_DOUBLECLICKED) {
+               if (LOWORD(wParam)==IDC_ECHOBACKEND) cfg.localecho=LD_BACKEND;
+               if (LOWORD(wParam)==IDC_ECHOYES) cfg.localecho=LD_YES;
+               if (LOWORD(wParam)==IDC_ECHONO) cfg.localecho=LD_NO;
+            }
+           break;
+         case IDC_EDITBACKEND:
+         case IDC_EDITYES:
+         case IDC_EDITNO:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED) {
+               if (LOWORD(wParam)==IDC_EDITBACKEND) cfg.localedit=LD_BACKEND;
+               if (LOWORD(wParam)==IDC_EDITYES) cfg.localedit=LD_YES;
+               if (LOWORD(wParam)==IDC_EDITNO) cfg.localedit=LD_NO;
+            }
            break;
           case IDC_ALWAYSONTOP:
            if (HIWORD(wParam) == BN_CLICKED ||
index cd4d539..cef6825 100644 (file)
--- a/window.c
+++ b/window.c
@@ -101,13 +101,10 @@ static Mouse_Button lastbtn;
 
 static char *window_name, *icon_name;
 
-static Ldisc *real_ldisc;
-
 static int compose_state = 0;
 
-void begin_session(void) {
-    ldisc = real_ldisc;
-}
+/* Dummy routine, only required in plink. */
+void ldisc_update(int echo, int edit) {}
 
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     static char appname[] = "PuTTY";
@@ -318,11 +315,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         return 1;
     }
 
-    real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
-    /* To start with, we use the simple line discipline, so we can
-     * type passwords etc without fear of them being echoed... */
-    ldisc = &ldisc_simple;
-
     if (!prev) {
        wndclass.style         = 0;
        wndclass.lpfnWndProc   = WndProc;
@@ -1220,11 +1212,10 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                 init_fonts(0);
                 sfree(logpal);
                 /*
-                 * Telnet will change local echo -> remote if the
-                 * remote asks.
+                 * Flush the line discipline's edit buffer in the
+                 * case where local editing has just been disabled.
                  */
-                if (cfg.protocol != PROT_TELNET)
-                    ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
+                ldisc_send(NULL, 0);
                 if (pal)
                     DeleteObject(pal);
                 logpal = NULL;
@@ -1615,7 +1606,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                len = TranslateKey (message, wParam, lParam, buf);
                if (len == -1)
                    return DefWindowProc (hwnd, message, wParam, lParam);
-               ldisc->send (buf, len);
+               ldisc_send (buf, len);
 
                 if (len > 0)
                     show_mouseptr(0);
@@ -1628,7 +1619,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
 
            buf[1] = wParam;
            buf[0] = wParam >> 8;
-           ldisc->send (buf, 2);
+           ldisc_send (buf, 2);
        }
       case WM_CHAR:
       case WM_SYSCHAR:
@@ -1640,7 +1631,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
         */
        {
            char c = xlat_kbd2tty((unsigned char)wParam);
-           ldisc->send (&c, 1);
+           ldisc_send (&c, 1);
        }
        return 0;
     }