RDB's patch: thanks to improvements in OEMANSI font mode, it can now
[sgt/putty] / telnet.c
index b6e4802..1cd4df4 100644 (file)
--- a/telnet.c
+++ b/telnet.c
@@ -171,10 +171,12 @@ static void s_write (void *buf, int len) {
 static void c_write (char *buf, int len) {
     while (len--) {
        int new_head = (inbuf_head + 1) & INBUF_MASK;
-       int c = (unsigned char) *buf;
        if (new_head != inbuf_reap) {
            inbuf[inbuf_head] = *buf++;
            inbuf_head = new_head;
+       } else {
+            term_out();
+            if( inbuf_head == inbuf_reap ) len++; else break;
        }
     }
 }
@@ -185,7 +187,7 @@ static void log_option (char *sender, int cmd, int option) {
            (cmd == WILL ? "WILL" : cmd == WONT ? "WONT" :
             cmd == DO ? "DO" : cmd == DONT ? "DONT" : "<??>"),
            telopt(option));
-    lognegot(buf);
+    logevent(buf);
 }
 
 static void send_opt (int cmd, int option) {
@@ -214,6 +216,11 @@ static void activate_option (struct Opt *o) {
         */
        deactivate_option (o->option==TELOPT_NEW_ENVIRON ? &o_oenv : &o_nenv);
     }
+    if (o->option == TELOPT_ECHO)
+    {
+       cfg.ldisc_term = FALSE;
+       ldisc = &ldisc_simple;
+    }
 }
 
 static void refused_option (struct Opt *o) {
@@ -222,6 +229,11 @@ static void refused_option (struct Opt *o) {
        send_opt (WILL, TELOPT_OLD_ENVIRON);
        o_oenv.state = REQUESTED;
     }
+    if (o->option == TELOPT_ECHO)
+    {
+       cfg.ldisc_term = TRUE;
+       ldisc = &ldisc_term;
+    }
 }
 
 static void proc_rec_opt (int cmd, int option) {
@@ -286,11 +298,11 @@ static void process_subneg (void) {
            n = 4 + strlen(cfg.termspeed);
            b[n] = IAC; b[n+1] = SE;
            s_write (b, n+2);
-           lognegot("server:\tSB TSPEED SEND");
+           logevent("server:\tSB TSPEED SEND");
            sprintf(logbuf, "client:\tSB TSPEED IS %s", cfg.termspeed);
-           lognegot (logbuf);
+           logevent (logbuf);
        } else
-           lognegot ("server:\tSB TSPEED <something weird>");
+           logevent ("server:\tSB TSPEED <something weird>");
        break;
       case TELOPT_TTYPE:
        if (sb_len == 1 && sb_buf[0] == TELQUAL_SEND) {
@@ -303,11 +315,11 @@ static void process_subneg (void) {
            b[n+4] = IAC; b[n+5] = SE;
            s_write (b, n+6);
            b[n+4] = 0;
-           lognegot("server:\tSB TTYPE SEND");
+           logevent("server:\tSB TTYPE SEND");
            sprintf(logbuf, "client:\tSB TTYPE IS %s", b+4);
-           lognegot(logbuf);
+           logevent(logbuf);
        } else
-           lognegot("server:\tSB TTYPE <something weird>\r\n");
+           logevent("server:\tSB TTYPE <something weird>\r\n");
        break;
       case TELOPT_OLD_ENVIRON:
       case TELOPT_NEW_ENVIRON: 
@@ -317,7 +329,7 @@ static void process_subneg (void) {
            char logbuf[50];
            p++;
            sprintf (logbuf, "server:\tSB %s SEND", telopt(sb_opt));
-           lognegot (logbuf);
+           logevent (logbuf);
            if (sb_opt == TELOPT_OLD_ENVIRON) {
                if (cfg.rfc_environ) {
                    value = RFC_VALUE;
@@ -369,7 +381,7 @@ static void process_subneg (void) {
            s_write (b, n);
            sprintf(logbuf, "client:\tSB %s IS %s", telopt(sb_opt),
                    n==6 ? "<nothing>" : "<stuff>");
-           lognegot (logbuf);
+           logevent (logbuf);
        }
        break;
     }
@@ -411,7 +423,14 @@ static void do_telnet_read (char *buf, int len) {
            else if (c == WILL) telnet_state = SEENWILL;
            else if (c == WONT) telnet_state = SEENWONT;
            else if (c == SB) telnet_state = SEENSB;
-           else telnet_state = TOPLEVEL;/* ignore _everything_ else! */
+           else {
+               /* ignore everything else; print it if it's IAC */
+               if (c == IAC) {
+                   b[0] = c;
+                   c_write(b,1);
+               }
+               telnet_state = TOPLEVEL;
+           }
            break;
          case SEENWILL:
            proc_rec_opt (WILL, c);
@@ -556,6 +575,15 @@ static char *telnet_init (HWND hwnd, 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;
 
@@ -582,14 +610,22 @@ static int telnet_msg (WPARAM wParam, LPARAM lParam) {
     int ret;
     char buf[256];
 
-    if (s == INVALID_SOCKET)          /* how the hell did we get here?! */
-       return -5000;
+    /*
+     * Because reading less than the whole of the available pending
+     * data can generate an FD_READ event, we need to allow for the
+     * possibility that FD_READ may arrive with FD_CLOSE already in
+     * the queue; so it's possible that we can get here even with s
+     * invalid. If so, we return 1 and don't worry about it.
+     */
+    if (s == INVALID_SOCKET)
+       return 1;
 
     if (WSAGETSELECTERROR(lParam) != 0)
        return -WSAGETSELECTERROR(lParam);
 
     switch (WSAGETSELECTEVENT(lParam)) {
       case FD_READ:
+      case FD_CLOSE:
        ret = recv(s, buf, sizeof(buf), 0);
        if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
            return 1;
@@ -597,7 +633,7 @@ static int telnet_msg (WPARAM wParam, LPARAM lParam) {
            return -10000-WSAGetLastError();
        if (ret == 0) {
            s = INVALID_SOCKET;
-           return 0;                  /* can't happen, in theory */
+           return 0;
        }
 #if 0
        if (in_synch) {
@@ -628,9 +664,6 @@ static int telnet_msg (WPARAM wParam, LPARAM lParam) {
        if (outbuf_head != outbuf_reap)
            try_write();
        return 1;
-      case FD_CLOSE:
-       s = INVALID_SOCKET;
-       return 0;
     }
     return 1;                         /* shouldn't happen, but WTF */
 }
@@ -642,19 +675,41 @@ static void telnet_send (char *buf, int len) {
     char *p;
     static unsigned char iac[2] = { IAC, IAC };
     static unsigned char cr[2] = { CR, NUL };
+    static unsigned char nl[2] = { CR, LF };
 
     if (s == INVALID_SOCKET)
        return;
 
     p = buf;
-    while (p < buf+len) {
+    if (cfg.ldisc_term) {
+       while (p < buf+len) {
+           char *q = p;
+           unsigned char * cstr = 0;
+           while (p < buf+len) {
+               if ((unsigned char)*p == IAC) {
+                   cstr = iac;
+                   break;
+               }
+               if (*p == '\r') {
+                   if( p+1 >= buf+len || ( p[1] != '\n' && p[1] != '\0'))
+                   {
+                       cstr = cr;
+                       break;
+                   }
+               }
+               p++;
+           }
+           if (p!=q) s_write (q, p-q);
+           if (cstr) s_write (cstr,2), p++;
+       }
+    } else while (p < buf+len) {
        char *q = p;
 
        while (iswritable((unsigned char)*p) && p < buf+len) p++;
        s_write (q, p-q);
 
        while (p < buf+len && !iswritable((unsigned char)*p)) {
-           s_write ((unsigned char)*p == IAC ? iac : cr, 2);
+           s_write ((unsigned char)*p == IAC ? iac : nl, 2);
            p++;
        }
     }
@@ -677,7 +732,7 @@ static void telnet_size(void) {
     sprintf(logbuf, "client:\tSB NAWS %d,%d",
            ((unsigned char)b[3] << 8) + (unsigned char)b[4],
            ((unsigned char)b[5] << 8) + (unsigned char)b[6]);
-    lognegot (logbuf);
+    logevent (logbuf);
 }
 
 /*
@@ -705,8 +760,20 @@ static void telnet_special (Telnet_Special code) {
       case TS_EOF: b[1] = xEOF; s_write (b, 2); break;
       case TS_SYNCH:
        outbuf_head = outbuf_reap = 0;
-       b[0] = DM;
-       send (s, b, 1, MSG_OOB);
+       b[1] = DM;
+       send (s, b, 2, MSG_OOB);
+       break;
+      case TS_RECHO:
+       if (o_echo.state == INACTIVE || o_echo.state == REALLY_INACTIVE) {
+           o_echo.state = REQUESTED;
+           send_opt (o_echo.send, o_echo.option);
+       }
+       break;
+      case TS_LECHO:
+       if (o_echo.state == ACTIVE) {
+           o_echo.state = REQUESTED;
+           send_opt (o_echo.nsend, o_echo.option);
+       }
        break;
     }
 }