Sebastian Kuschel reports that pfd_closing can be called for a socket
[u/mdw/putty] / ldisc.c
diff --git a/ldisc.c b/ldisc.c
index e572718..efbb871 100644 (file)
--- a/ldisc.c
+++ b/ldisc.c
 #include "terminal.h"
 #include "ldisc.h"
 
-#define ECHOING (ldisc->cfg->localecho == FORCE_ON || \
-                 (ldisc->cfg->localecho == AUTO && \
+#define ECHOING (ldisc->localecho == FORCE_ON || \
+                 (ldisc->localecho == AUTO && \
                       (ldisc->back->ldisc(ldisc->backhandle, LD_ECHO) || \
                           term_ldisc(ldisc->term, LD_ECHO))))
-#define EDITING (ldisc->cfg->localedit == FORCE_ON || \
-                 (ldisc->cfg->localedit == AUTO && \
+#define EDITING (ldisc->localedit == FORCE_ON || \
+                 (ldisc->localedit == AUTO && \
                       (ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \
                           term_ldisc(ldisc->term, LD_EDIT))))
 
@@ -32,13 +32,20 @@ static int plen(Ldisc ldisc, unsigned char c)
        return 1;
     else if (c < 128)
        return 2;                      /* ^x for some x */
+    else if (in_utf(ldisc->term) && c >= 0xC0)
+       return 1;                      /* UTF-8 introducer character
+                                       * (FIXME: combining / wide chars) */
+    else if (in_utf(ldisc->term) && c >= 0x80 && c < 0xC0)
+       return 0;                      /* UTF-8 followup character */
     else
-       return 4;                      /* <XY> for hex XY */
+       return 4;                      /* <XY> hex representation */
 }
 
 static void pwrite(Ldisc ldisc, unsigned char c)
 {
-    if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term))) {
+    if ((c >= 32 && c <= 126) ||
+       (!in_utf(ldisc->term) && c >= 0xA0) ||
+       (in_utf(ldisc->term) && c >= 0x80)) {
        c_write(ldisc, (char *)&c, 1);
     } else if (c < 128) {
        char cc[2];
@@ -52,6 +59,14 @@ static void pwrite(Ldisc ldisc, unsigned char c)
     }
 }
 
+static int char_start(Ldisc ldisc, unsigned char c)
+{
+    if (in_utf(ldisc->term))
+       return (c < 0x80 || c >= 0xC0);
+    else
+       return 1;
+}
+
 static void bsb(Ldisc ldisc, int n)
 {
     while (n--)
@@ -61,7 +76,7 @@ static void bsb(Ldisc ldisc, int n)
 #define CTRL(x) (x^'@')
 #define KCTRL(x) ((x^'@') | 0x100)
 
-void *ldisc_create(Config *mycfg, Terminal *term,
+void *ldisc_create(Conf *conf, Terminal *term,
                   Backend *back, void *backhandle,
                   void *frontend)
 {
@@ -72,12 +87,13 @@ void *ldisc_create(Config *mycfg, Terminal *term,
     ldisc->bufsiz = 0;
     ldisc->quotenext = 0;
 
-    ldisc->cfg = mycfg;
     ldisc->back = back;
     ldisc->backhandle = backhandle;
     ldisc->term = term;
     ldisc->frontend = frontend;
 
+    ldisc_configure(ldisc, conf);
+
     /* Link ourselves into the backend and the terminal */
     if (term)
        term->ldisc = ldisc;
@@ -87,6 +103,17 @@ void *ldisc_create(Config *mycfg, Terminal *term,
     return ldisc;
 }
 
+void ldisc_configure(void *handle, Conf *conf)
+{
+    Ldisc ldisc = (Ldisc) handle;
+
+    ldisc->telnet_keyboard = conf_get_int(conf, CONF_telnet_keyboard);
+    ldisc->telnet_newline = conf_get_int(conf, CONF_telnet_newline);
+    ldisc->protocol = conf_get_int(conf, CONF_protocol);
+    ldisc->localecho = conf_get_int(conf, CONF_localecho);
+    ldisc->localedit = conf_get_int(conf, CONF_localedit);
+}
+
 void ldisc_free(void *handle)
 {
     Ldisc ldisc = (Ldisc) handle;
@@ -132,12 +159,14 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
     if (EDITING) {
        while (len--) {
            int c;
-           c = *buf++ + keyflag;
+           c = (unsigned char)(*buf++) + keyflag;
            if (!interactive && c == '\r')
                c += KCTRL('@');
            switch (ldisc->quotenext ? ' ' : c) {
                /*
-                * ^h/^?: delete one char and output one BSB
+                * ^h/^?: delete, and output BSBs, to return to
+                * last character boundary (in UTF-8 mode this may
+                * be more than one byte)
                 * ^w: delete, and output BSBs, to return to last
                 * space/nonspace boundary
                 * ^u: delete, and output BSBs, to return to BOL
@@ -153,9 +182,11 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
              case KCTRL('H'):
              case KCTRL('?'):         /* backspace/delete */
                if (ldisc->buflen > 0) {
-                   if (ECHOING)
-                       bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
-                   ldisc->buflen--;
+                   do {
+                       if (ECHOING)
+                           bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
+                       ldisc->buflen--;
+                   } while (!char_start(ldisc, ldisc->buf[ldisc->buflen]));
                }
                break;
              case CTRL('W'):          /* delete word */
@@ -184,7 +215,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
                  * configured telnet specials off! This breaks
                  * talkers otherwise.
                  */
-                if (!ldisc->cfg->telnet_keyboard)
+                if (!ldisc->telnet_keyboard)
                     goto default_case;
                if (c == CTRL('C'))
                    ldisc->back->special(ldisc->backhandle, TS_IP);
@@ -236,7 +267,7 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
                 *    default clause because of the break.
                 */
              case CTRL('J'):
-               if (ldisc->cfg->protocol == PROT_RAW &&
+               if (ldisc->protocol == PROT_RAW &&
                    ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') {
                    if (ECHOING)
                        bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
@@ -245,9 +276,9 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
              case KCTRL('M'):         /* send with newline */
                    if (ldisc->buflen > 0)
                        ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);
-                   if (ldisc->cfg->protocol == PROT_RAW)
+                   if (ldisc->protocol == PROT_RAW)
                        ldisc->back->send(ldisc->backhandle, "\r\n", 2);
-                   else if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline)
+                   else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
                        ldisc->back->special(ldisc->backhandle, TS_EOL);
                    else
                        ldisc->back->send(ldisc->backhandle, "\r", 1);
@@ -281,27 +312,27 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
        if (len > 0) {
            if (ECHOING)
                c_write(ldisc, buf, len);
-           if (keyflag && ldisc->cfg->protocol == PROT_TELNET && len == 1) {
+           if (keyflag && ldisc->protocol == PROT_TELNET && len == 1) {
                switch (buf[0]) {
                  case CTRL('M'):
-                   if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline)
+                   if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)
                        ldisc->back->special(ldisc->backhandle, TS_EOL);
                    else
                        ldisc->back->send(ldisc->backhandle, "\r", 1);
                    break;
                  case CTRL('?'):
                  case CTRL('H'):
-                   if (ldisc->cfg->telnet_keyboard) {
+                   if (ldisc->telnet_keyboard) {
                        ldisc->back->special(ldisc->backhandle, TS_EC);
                        break;
                    }
                  case CTRL('C'):
-                   if (ldisc->cfg->telnet_keyboard) {
+                   if (ldisc->telnet_keyboard) {
                        ldisc->back->special(ldisc->backhandle, TS_IP);
                        break;
                    }
                  case CTRL('Z'):
-                   if (ldisc->cfg->telnet_keyboard) {
+                   if (ldisc->telnet_keyboard) {
                        ldisc->back->special(ldisc->backhandle, TS_SUSP);
                        break;
                    }