Begin destabilisation in the wake of 0.53! This checkin contains the
[u/mdw/putty] / terminal.c
index 87c151e..b286efe 100644 (file)
@@ -1,5 +1,3 @@
-#include <windows.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
@@ -59,11 +57,11 @@ static unsigned long *disptext;            /* buffer of text on real screen */
 static unsigned long *dispcurs;               /* location of cursor on real screen */
 static unsigned long curstype;        /* type of cursor on real screen */
 
-#define VBELL_TIMEOUT 100             /* millisecond len of visual bell */
+#define VBELL_TIMEOUT (TICKSPERSEC/10) /* visual bell lasts 1/10 sec */
 
 struct beeptime {
     struct beeptime *next;
-    long ticks;
+    unsigned long ticks;
 };
 static struct beeptime *beephead, *beeptail;
 int nbeeps;
@@ -86,6 +84,11 @@ typedef struct {
 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
 
+/* Product-order comparisons for rectangular block selection. */
+#define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
+#define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
+
+static bufchain inbuf;                /* terminal input buffer */
 static pos curs;                      /* cursor */
 static pos savecurs;                  /* saved cursor position */
 static int marg_t, marg_b;            /* scroll margins */
@@ -94,9 +97,9 @@ static int wrap, wrapnext;           /* wrap flags */
 static int insert;                    /* insert-mode flag */
 static int cset;                      /* 0 or 1: which char set */
 static int save_cset, save_csattr;     /* saved with cursor position */
-static int save_utf;                  /* saved with cursor position */
+static int save_utf, save_wnext;       /* saved with cursor position */
 static int rvideo;                    /* global reverse video flag */
-static int rvbell_timeout;            /* for ESC[?5hESC[?5l vbell */
+static unsigned long rvbell_startpoint;/* for ESC[?5hESC[?5l vbell */
 static int cursor_on;                 /* cursor enabled flag */
 static int reset_132;                 /* Flag ESC c resets to 80 cols */
 static int use_bce;                   /* Use Background coloured erase */
@@ -110,6 +113,10 @@ static int vt52_bold;                     /* Force bold on non-bold colours */
 static int utf_state;                 /* Is there a pending UTF-8 character */
 static int utf_char;                  /* and what is it so far. */
 static int utf_size;                  /* The size of the UTF character. */
+static int printing, only_printing;    /* Are we doing ANSI printing? */
+static int print_state;                       /* state of print-end-sequence scan */
+static bufchain printer_buf;          /* buffered data for printer */
+static printer_job *print_job;
 
 static int xterm_mouse;                       /* send mouse messages to app */
 
@@ -162,6 +169,9 @@ static enum {
     NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
 } selstate;
 static enum {
+    LEXICOGRAPHIC, RECTANGULAR
+} seltype;
+static enum {
     SM_CHAR, SM_WORD, SM_LINE
 } selmode;
 static pos selstart, selend, selanchor;
@@ -198,9 +208,7 @@ static void erase_lots(int, int, int);
 static void swap_screen(int);
 static void update_sbar(void);
 static void deselect(void);
-/* log session to file stuff ... */
-static FILE *lgfp = NULL;
-static void logtraffic(unsigned char c, int logmode);
+static void term_print_finish(void);
 
 /*
  * Resize a line to make it `cols' columns wide.
@@ -290,13 +298,14 @@ static void power_on(void)
     big_cursor = 0;
     save_attr = curr_attr = ATTR_DEFAULT;
     term_editing = term_echoing = FALSE;
-    ldisc_send(NULL, 0);              /* cause ldisc to notice changes */
+    ldisc_send(NULL, 0, 0);           /* cause ldisc to notice changes */
     app_cursor_keys = cfg.app_cursor;
     app_keypad_keys = cfg.app_keypad;
     use_bce = cfg.bce;
     blink_is_real = cfg.blinktext;
     erase_char = ERASE_CHAR;
     alt_which = 0;
+    term_print_finish();
     {
        int i;
        for (i = 0; i < 256; i++)
@@ -318,13 +327,15 @@ void term_update(void)
     Context ctx;
     ctx = get_ctx();
     if (ctx) {
-       if (seen_disp_event)
-           update_sbar();
+       int need_sbar_update = seen_disp_event;
        if ((seen_key_event && (cfg.scroll_on_key)) ||
            (seen_disp_event && (cfg.scroll_on_disp))) {
            disptop = 0;               /* return to main screen */
            seen_disp_event = seen_key_event = 0;
+           need_sbar_update = TRUE;
        }
+       if (need_sbar_update)
+           update_sbar();
        do_paint(ctx, TRUE);
        sys_cursor(curs.x, curs.y - disptop);
        free_ctx(ctx);
@@ -344,6 +355,30 @@ void term_pwron(void)
 }
 
 /*
+ * When the user reconfigures us, we need to check the forbidden-
+ * alternate-screen config option, disable raw mouse mode if the
+ * user has disabled mouse reporting, and abandon a print job if
+ * the user has disabled printing.
+ */
+void term_reconfig(void)
+{
+    if (cfg.no_alt_screen)
+       swap_screen(0);
+    if (cfg.no_mouse_rep) {
+       xterm_mouse = 0;
+       set_raw_mouse_mode(0);
+    }
+    if (cfg.no_remote_charset) {
+       cset_attr[0] = cset_attr[1] = ATTR_ASCII;
+       sco_acs = alt_sco_acs = 0;
+       utf = 0;
+    }
+    if (!*cfg.printer) {
+       term_print_finish();
+    }
+}
+
+/*
  * Clear the scrollback.
  */
 void term_clrsb(void)
@@ -496,6 +531,7 @@ void term_size(int newrows, int newcols, int newsavelines)
 
     update_sbar();
     term_update();
+    back->size();
 }
 
 /*
@@ -586,7 +622,7 @@ static void check_selection(pos from, pos to)
 static void scroll(int topline, int botline, int lines, int sb)
 {
     unsigned long *line, *line2;
-    int i;
+    int i, seltop;
 
     if (topline != 0 || alt_which != 0)
        sb = FALSE;
@@ -636,6 +672,23 @@ static void scroll(int topline, int botline, int lines, int sb)
                }
                addpos234(scrollback, line, sblen);
                line = line2;
+
+               /*
+                * If the user is currently looking at part of the
+                * scrollback, and they haven't enabled any options
+                * that are going to reset the scrollback as a
+                * result of this movement, then the chances are
+                * they'd like to keep looking at the same line. So
+                * we move their viewpoint at the same rate as the
+                * scroll, at least until their viewpoint hits the
+                * top end of the scrollback buffer, at which point
+                * we don't have the choice any more.
+                * 
+                * Thanks to Jan Holmen Holsten for the idea and
+                * initial implementation.
+                */
+               if (disptop > -savelines && disptop < 0)
+                   disptop--;
            }
             line = resizeline(line, cols);
            for (i = 0; i < cols; i++)
@@ -643,20 +696,40 @@ static void scroll(int topline, int botline, int lines, int sb)
            line[cols + 1] = 0;
            addpos234(screen, line, botline);
 
-           if (selstart.y >= topline && selstart.y <= botline) {
+           /*
+            * If the selection endpoints move into the scrollback,
+            * we keep them moving until they hit the top. However,
+            * of course, if the line _hasn't_ moved into the
+            * scrollback then we don't do this, and cut them off
+            * at the top of the scroll region.
+            * 
+            * This applies to selstart and selend (for an existing
+            * selection), and also selanchor (for one being
+            * selected as we speak).
+            */
+           seltop = sb ? -savelines : topline;
+
+           if (selstart.y >= seltop && selstart.y <= botline) {
                selstart.y--;
-               if (selstart.y < topline) {
-                   selstart.y = topline;
+               if (selstart.y < seltop) {
+                   selstart.y = seltop;
                    selstart.x = 0;
                }
            }
-           if (selend.y >= topline && selend.y <= botline) {
+           if (selend.y >= seltop && selend.y <= botline) {
                selend.y--;
-               if (selend.y < topline) {
-                   selend.y = topline;
+               if (selend.y < seltop) {
+                   selend.y = seltop;
                    selend.x = 0;
                }
            }
+           if (selanchor.y >= seltop && selanchor.y <= botline) {
+               selanchor.y--;
+               if (selanchor.y < seltop) {
+                   selanchor.y = seltop;
+                   selanchor.x = 0;
+               }
+           }
 
            lines--;
        }
@@ -701,6 +774,7 @@ static void save_cursor(int save)
        save_attr = curr_attr;
        save_cset = cset;
        save_utf = utf;
+       save_wnext = wrapnext;
        save_csattr = cset_attr[cset];
        save_sco_acs = sco_acs;
     } else {
@@ -714,6 +788,13 @@ static void save_cursor(int save)
        curr_attr = save_attr;
        cset = save_cset;
        utf = save_utf;
+       wrapnext = save_wnext;
+       /*
+        * wrapnext might reset to False if the x position is no
+        * longer at the rightmost edge.
+        */
+       if (wrapnext && curs.x < cols-1)
+           wrapnext = FALSE;
        cset_attr[cset] = save_csattr;
        sco_acs = save_sco_acs;
        fix_cpos;
@@ -806,7 +887,7 @@ static void insch(int n)
  */
 static void toggle_mode(int mode, int query, int state)
 {
-    long ticks;
+    unsigned long ticks;
 
     if (query)
        switch (mode) {
@@ -824,7 +905,8 @@ static void toggle_mode(int mode, int query, int state)
            break;
          case 3:                      /* 80/132 columns */
            deselect();
-           request_resize(state ? 132 : 80, rows, 1);
+           if (!cfg.no_remote_resize)
+               request_resize(state ? 132 : 80, rows);
            reset_132 = state;
            break;
          case 5:                      /* reverse video */
@@ -834,15 +916,21 @@ static void toggle_mode(int mode, int query, int state)
             * effective visual bell, so that ESC[?5hESC[?5l will
             * always be an actually _visible_ visual bell.
             */
-           ticks = GetTickCount();
-           if (rvideo && !state &&    /* we're turning it off */
-               ticks < rvbell_timeout) {       /* and it's not long since it was turned on */
+           ticks = GETTICKCOUNT();
+           /* turn off a previous vbell to avoid inconsistencies */
+           if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
+               in_vbell = FALSE;
+           if (rvideo && !state &&    /* we're turning it off... */
+               (ticks - rvbell_startpoint) < VBELL_TIMEOUT) {  /* ...soon */
+               /* If there's no vbell timeout already, or this one lasts
+                * longer, replace vbell_timeout with ours. */
+               if (!in_vbell ||
+                   (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
+                   vbell_startpoint = rvbell_startpoint;
                in_vbell = TRUE;       /* we may clear rvideo but we set in_vbell */
-               if (vbell_timeout < rvbell_timeout)     /* don't move vbell end forward */
-                   vbell_timeout = rvbell_timeout;     /* vbell end is at least then */
            } else if (!rvideo && state) {
                /* This is an ON, so we notice the time and save it. */
-               rvbell_timeout = ticks + VBELL_TIMEOUT;
+               rvbell_startpoint = ticks;
            }
            rvideo = state;
            seen_disp_event = TRUE;
@@ -860,7 +948,7 @@ static void toggle_mode(int mode, int query, int state)
            break;
          case 10:                     /* set local edit mode */
            term_editing = state;
-           ldisc_send(NULL, 0);       /* cause ldisc to notice changes */
+           ldisc_send(NULL, 0, 0);    /* cause ldisc to notice changes */
            break;
          case 25:                     /* enable/disable cursor */
            compatibility2(OTHER, VT220);
@@ -870,7 +958,7 @@ static void toggle_mode(int mode, int query, int state)
          case 47:                     /* alternate screen */
            compatibility(OTHER);
            deselect();
-           swap_screen(state);
+           swap_screen(cfg.no_alt_screen ? 0 : state);
            disptop = 0;
            break;
          case 1000:                   /* xterm mouse 1 */
@@ -889,7 +977,7 @@ static void toggle_mode(int mode, int query, int state)
            break;
          case 12:                     /* set echo mode */
            term_echoing = !state;
-           ldisc_send(NULL, 0);       /* cause ldisc to notice changes */
+           ldisc_send(NULL, 0, 0);    /* cause ldisc to notice changes */
            break;
          case 20:                     /* Return sends ... */
            cr_lf_return = state;
@@ -913,16 +1001,62 @@ static void do_osc(void)
        switch (esc_args[0]) {
          case 0:
          case 1:
-           set_icon(osc_string);
+           if (!cfg.no_remote_wintitle)
+               set_icon(osc_string);
            if (esc_args[0] == 1)
                break;
            /* fall through: parameter 0 means set both */
          case 2:
          case 21:
-           set_title(osc_string);
+           if (!cfg.no_remote_wintitle)
+               set_title(osc_string);
+           break;
+       }
+    }
+}
+
+/*
+ * ANSI printing routines.
+ */
+static void term_print_setup(void)
+{
+    bufchain_clear(&printer_buf);
+    print_job = printer_start_job(cfg.printer);
+}
+static void term_print_flush(void)
+{
+    void *data;
+    int len;
+    int size;
+    while ((size = bufchain_size(&printer_buf)) > 5) {
+       bufchain_prefix(&printer_buf, &data, &len);
+       if (len > size-5)
+           len = size-5;
+       printer_job_data(print_job, data, len);
+       bufchain_consume(&printer_buf, len);
+    }
+}
+static void term_print_finish(void)
+{
+    void *data;
+    int len, size;
+    char c;
+
+    term_print_flush();
+    while ((size = bufchain_size(&printer_buf)) > 0) {
+       bufchain_prefix(&printer_buf, &data, &len);
+       c = *(char *)data;
+       if (c == '\033' || c == '\233') {
+           bufchain_consume(&printer_buf, size);
            break;
+       } else {
+           printer_job_data(print_job, &c, 1);
+           bufchain_consume(&printer_buf, 1);
        }
     }
+    printer_finish_job(print_job);
+    print_job = NULL;
+    printing = only_printing = FALSE;
 }
 
 /*
@@ -932,22 +1066,76 @@ static void do_osc(void)
  */
 void term_out(void)
 {
-    int c, inbuf_reap;
-
-    for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
-       c = inbuf[inbuf_reap];
+    int c, unget;
+    unsigned char localbuf[256], *chars;
+    int nchars = 0;
+
+    unget = -1;
+
+    while (nchars > 0 || bufchain_size(&inbuf) > 0) {
+       if (unget == -1) {
+           if (nchars == 0) {
+               void *ret;
+               bufchain_prefix(&inbuf, &ret, &nchars);
+               if (nchars > sizeof(localbuf))
+                   nchars = sizeof(localbuf);
+               memcpy(localbuf, ret, nchars);
+               bufchain_consume(&inbuf, nchars);
+               chars = localbuf;
+               assert(chars != NULL);
+           }
+           c = *chars++;
+           nchars--;
 
-       /*
-        * Optionally log the session traffic to a file. Useful for
-        * debugging and possibly also useful for actual logging.
-        */
-       logtraffic((unsigned char) c, LGTYP_DEBUG);
+           /*
+            * Optionally log the session traffic to a file. Useful for
+            * debugging and possibly also useful for actual logging.
+            */
+           if (cfg.logtype == LGTYP_DEBUG)
+               logtraffic((unsigned char) c, LGTYP_DEBUG);
+       } else {
+           c = unget;
+           unget = -1;
+       }
 
        /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
         * be able to display 8-bit characters, but I'll let that go 'cause
         * of i18n.
         */
 
+       /*
+        * If we're printing, add the character to the printer
+        * buffer.
+        */
+       if (printing) {
+           bufchain_add(&printer_buf, &c, 1);
+
+           /*
+            * If we're in print-only mode, we use a much simpler
+            * state machine designed only to recognise the ESC[4i
+            * termination sequence.
+            */
+           if (only_printing) {
+               if (c == '\033')
+                   print_state = 1;
+               else if (c == (unsigned char)'\233')
+                   print_state = 2;
+               else if (c == '[' && print_state == 1)
+                   print_state = 2;
+               else if (c == '4' && print_state == 2)
+                   print_state = 3;
+               else if (c == 'i' && print_state == 3)
+                   print_state = 4;
+               else
+                   print_state = 0;
+               if (print_state == 4) {
+                   printing = only_printing = FALSE;
+                   term_print_finish();
+               }
+               continue;
+           }
+       }
+
        /* First see about all those translations. */
        if (termstate == TOPLEVEL) {
            if (in_utf)
@@ -985,9 +1173,9 @@ void term_out(void)
                  case 4:
                  case 5:
                    if ((c & 0xC0) != 0x80) {
-                       inbuf_reap--;  /* This causes the faulting character */
-                       c = UCSERR;    /* to be logged twice - not really a */
-                       utf_state = 0; /* serious problem. */
+                       unget = c;
+                       c = UCSERR;
+                       utf_state = 0;
                        break;
                    }
                    utf_char = (utf_char << 6) | (c & 0x3f);
@@ -1092,7 +1280,8 @@ void term_out(void)
                curs.x--;
            wrapnext = FALSE;
            fix_cpos;
-           *cpos = (' ' | curr_attr | ATTR_ASCII);
+           if (!cfg.no_dbackspace)    /* destructive bksp might be disabled */
+               *cpos = (' ' | curr_attr | ATTR_ASCII);
        } else
            /* Or normal C0 controls. */
        if ((c & -32) == 0 && termstate < DO_CTRLS) {
@@ -1125,15 +1314,15 @@ void term_out(void)
                        } else
                            *d++ = *s;
                    }
-                   lpage_send(CP_ACP, abuf, d - abuf);
+                   lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
                }
                break;
              case '\007':
                {
                    struct beeptime *newbeep;
-                   long ticks;
+                   unsigned long ticks;
 
-                   ticks = GetTickCount();
+                   ticks = GETTICKCOUNT();
 
                    if (!beep_overloaded) {
                        newbeep = smalloc(sizeof(struct beeptime));
@@ -1162,7 +1351,7 @@ void term_out(void)
                    }
 
                    if (cfg.bellovl && beep_overloaded &&
-                       ticks - lastbeep >= cfg.bellovl_s) {
+                       ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
                        /*
                         * If we're currently overloaded and the
                         * last beep was more than s seconds ago,
@@ -1187,7 +1376,7 @@ void term_out(void)
                        beep(cfg.beep);
                        if (cfg.beep == BELL_VISUAL) {
                            in_vbell = TRUE;
-                           vbell_timeout = ticks + VBELL_TIMEOUT;
+                           vbell_startpoint = ticks;
                            term_update();
                        }
                    }
@@ -1309,12 +1498,18 @@ void term_out(void)
                        width = wcwidth((wchar_t) c);
                    switch (width) {
                      case 2:
-                       if (curs.x + 1 != cols) {
-                           *cpos++ = c | ATTR_WIDE | curr_attr;
-                           *cpos++ = UCSWIDE | curr_attr;
-                           curs.x++;
-                           break;
+                       *cpos++ = c | curr_attr;
+                       if (++curs.x == cols) {
+                           *cpos |= LATTR_WRAPPED;
+                           if (curs.y == marg_b)
+                               scroll(marg_t, marg_b, 1, TRUE);
+                           else if (curs.y < rows - 1)
+                               curs.y++;
+                           curs.x = 0;
+                           fix_cpos;
                        }
+                       *cpos++ = UCSWIDE | curr_attr;
+                       break;
                      case 1:
                        *cpos++ = c | curr_attr;
                        break;
@@ -1425,13 +1620,14 @@ void term_out(void)
                    break;
                  case 'Z':            /* terminal type query */
                    compatibility(VT100);
-                   ldisc_send(id_string, strlen(id_string));
+                   ldisc_send(id_string, strlen(id_string), 0);
                    break;
                  case 'c':            /* restore power-on settings */
                    compatibility(VT100);
                    power_on();
                    if (reset_132) {
-                       request_resize(80, rows, 1);
+                       if (!cfg.no_remote_resize)
+                           request_resize(80, rows);
                        reset_132 = 0;
                    }
                    fix_cpos;
@@ -1495,46 +1691,56 @@ void term_out(void)
 
                  case ANSI('A', '('):
                    compatibility(VT100);
-                   cset_attr[0] = ATTR_GBCHR;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_GBCHR;
                    break;
                  case ANSI('B', '('):
                    compatibility(VT100);
-                   cset_attr[0] = ATTR_ASCII;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_ASCII;
                    break;
                  case ANSI('0', '('):
                    compatibility(VT100);
-                   cset_attr[0] = ATTR_LINEDRW;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_LINEDRW;
                    break;
                  case ANSI('U', '('): 
                    compatibility(OTHER);
-                   cset_attr[0] = ATTR_SCOACS; 
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_SCOACS; 
                    break;
 
                  case ANSI('A', ')'):
                    compatibility(VT100);
-                   cset_attr[1] = ATTR_GBCHR;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_GBCHR;
                    break;
                  case ANSI('B', ')'):
                    compatibility(VT100);
-                   cset_attr[1] = ATTR_ASCII;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_ASCII;
                    break;
                  case ANSI('0', ')'):
                    compatibility(VT100);
-                   cset_attr[1] = ATTR_LINEDRW;
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_LINEDRW;
                    break;
                  case ANSI('U', ')'): 
                    compatibility(OTHER);
-                   cset_attr[1] = ATTR_SCOACS; 
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_SCOACS; 
                    break;
 
                  case ANSI('8', '%'):  /* Old Linux code */
                  case ANSI('G', '%'):
                    compatibility(OTHER);
-                   utf = 1;
+                   if (!cfg.no_remote_charset)
+                       utf = 1;
                    break;
                  case ANSI('@', '%'):
                    compatibility(OTHER);
-                   utf = 0;
+                   if (!cfg.no_remote_charset)
+                       utf = 0;
                    break;
                }
                break;
@@ -1577,7 +1783,7 @@ void term_out(void)
                        compatibility(OTHER);
                        /* this reports xterm version 136 so that VIM can
                           use the drag messages from the mouse reporting */
-                       ldisc_send("\033[>0;136;0c", 11);
+                       ldisc_send("\033[>0;136;0c", 11, 0);
                        break;
                      case 'a':       /* move right N cols */
                        compatibility(ANSI);
@@ -1673,16 +1879,16 @@ void term_out(void)
                      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), 0);
                        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), 0);
                        } else if (esc_args[0] == 5) {
-                           ldisc_send("\033[0n", 4);
+                           ldisc_send("\033[0n", 4, 0);
                        }
                        break;
                      case 'h':       /* toggle modes to high */
@@ -1694,6 +1900,23 @@ void term_out(void)
                                toggle_mode(esc_args[i], esc_query, TRUE);
                        }
                        break;
+                     case 'i':
+                     case ANSI_QUE('i'):
+                       compatibility(VT100);
+                       {
+                           if (esc_nargs != 1) break;
+                           if (esc_args[0] == 5 && *cfg.printer) {
+                               printing = TRUE;
+                               only_printing = !esc_query;
+                               print_state = 0;
+                               term_print_setup();
+                           } else if (esc_args[0] == 4 && printing) {
+                               printing = FALSE;
+                               only_printing = FALSE;
+                               term_print_finish();
+                           }
+                       }
+                       break;                  
                      case 'l':       /* toggle modes to low */
                      case ANSI_QUE('l'):
                        compatibility(VT100);
@@ -1794,12 +2017,15 @@ void term_out(void)
                                    break;
                                  case 10:      /* SCO acs off */
                                    compatibility(SCOANSI);
+                                   if (cfg.no_remote_charset) break;
                                    sco_acs = 0; break;
                                  case 11:      /* SCO acs on */
                                    compatibility(SCOANSI);
+                                   if (cfg.no_remote_charset) break;
                                    sco_acs = 1; break;
                                  case 12:      /* SCO acs on flipped */
                                    compatibility(SCOANSI);
+                                   if (cfg.no_remote_charset) break;
                                    sco_acs = 2; break;
                                  case 22:      /* disable bold */
                                    compatibility2(OTHER, VT220);
@@ -1873,11 +2099,110 @@ void term_out(void)
                         * illegal values (eg first arg 1..9) for window changing 
                         * and reports.
                         */
-                       compatibility(VT340TEXT);
                        if (esc_nargs <= 1
                            && (esc_args[0] < 1 || esc_args[0] >= 24)) {
-                           request_resize(cols, def(esc_args[0], 24), 0);
+                           compatibility(VT340TEXT);
+                           if (!cfg.no_remote_resize)
+                               request_resize(cols, def(esc_args[0], 24));
                            deselect();
+                       } else if (esc_nargs >= 1 &&
+                                  esc_args[0] >= 1 &&
+                                  esc_args[0] < 24) {
+                           compatibility(OTHER);
+
+                           switch (esc_args[0]) {
+                               int x, y, len;
+                               char buf[80], *p;
+                             case 1:
+                               set_iconic(FALSE);
+                               break;
+                             case 2:
+                               set_iconic(TRUE);
+                               break;
+                             case 3:
+                               if (esc_nargs >= 3) {
+                                   if (!cfg.no_remote_resize)
+                                       move_window(def(esc_args[1], 0),
+                                                   def(esc_args[2], 0));
+                               }
+                               break;
+                             case 4:
+                               /* We should resize the window to a given
+                                * size in pixels here, but currently our
+                                * resizing code isn't healthy enough to
+                                * manage it. */
+                               break;
+                             case 5:
+                               set_zorder(TRUE);   /* move to top */
+                               break;
+                             case 6:
+                               set_zorder(FALSE);  /* move to bottom */
+                               break;
+                             case 7:
+                               refresh_window();
+                               break;
+                             case 8:
+                               if (esc_nargs >= 3) {
+                                   if (!cfg.no_remote_resize)
+                                       request_resize(def(esc_args[2], cfg.width),
+                                                      def(esc_args[1], cfg.height));
+                               }
+                               break;
+                             case 9:
+                               if (esc_nargs >= 2)
+                                   set_zoomed(esc_args[1] ? TRUE : FALSE);
+                               break;
+                             case 11:
+                               ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
+                                          4, 0);
+                               break;
+                             case 13:
+                               get_window_pos(&x, &y);
+                               len = sprintf(buf, "\033[3;%d;%dt", x, y);
+                               ldisc_send(buf, len, 0);
+                               break;
+                             case 14:
+                               get_window_pixels(&x, &y);
+                               len = sprintf(buf, "\033[4;%d;%dt", x, y);
+                               ldisc_send(buf, len, 0);
+                               break;
+                             case 18:
+                               len = sprintf(buf, "\033[8;%d;%dt",
+                                             rows, cols);
+                               ldisc_send(buf, len, 0);
+                               break;
+                             case 19:
+                               /*
+                                * Hmmm. Strictly speaking we
+                                * should return `the size of the
+                                * screen in characters', but
+                                * that's not easy: (a) window
+                                * furniture being what it is it's
+                                * hard to compute, and (b) in
+                                * resize-font mode maximising the
+                                * window wouldn't change the
+                                * number of characters. *shrug*. I
+                                * think we'll ignore it for the
+                                * moment and see if anyone
+                                * complains, and then ask them
+                                * what they would like it to do.
+                                */
+                               break;
+                             case 20:
+                               p = get_window_title(TRUE);
+                               len = strlen(p);
+                               ldisc_send("\033]L", 3, 0);
+                               ldisc_send(p, len, 0);
+                               ldisc_send("\033\\", 2, 0);
+                               break;
+                             case 21:
+                               p = get_window_title(FALSE);
+                               len = strlen(p);
+                               ldisc_send("\033]l", 3, 0);
+                               ldisc_send(p, len, 0);
+                               ldisc_send("\033\\", 2, 0);
+                               break;
+                           }
                        }
                        break;
                      case 'S':
@@ -1902,9 +2227,8 @@ void term_out(void)
                         */
                        compatibility(VT420);
                        if (esc_nargs == 1 && esc_args[0] > 0) {
-                           request_resize(cols,
-                                          def(esc_args[0], cfg.height),
-                                          0);
+                           if (!cfg.no_remote_resize)
+                               request_resize(cols, def(esc_args[0], cfg.height));
                            deselect();
                        }
                        break;
@@ -1915,8 +2239,8 @@ void term_out(void)
                         */
                        compatibility(VT340TEXT);
                        if (esc_nargs <= 1) {
-                           request_resize(def(esc_args[0], cfg.width),
-                                          rows, 0);
+                           if (!cfg.no_remote_resize)
+                               request_resize(def(esc_args[0], cfg.width), rows);
                            deselect();
                        }
                        break;
@@ -1945,7 +2269,7 @@ void term_out(void)
                            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, 0);
                            }
                        }
                        break;
@@ -2043,10 +2367,12 @@ void term_out(void)
                         * Well we should do a soft reset at this point ...
                         */
                        if (!has_compat(VT420) && has_compat(VT100)) {
-                           if (reset_132)
-                               request_resize(132, 24, 1);
-                           else
-                               request_resize(80, 24, 1);
+                           if (!cfg.no_remote_resize) {
+                               if (reset_132)
+                                   request_resize(132, 24);
+                               else
+                                   request_resize(80, 24);
+                           }
                        }
 #endif
                        break;
@@ -2262,7 +2588,7 @@ void term_out(void)
                    termstate = VT52_Y1;
                    break;
                  case 'Z':
-                   ldisc_send("\033/Z", 3);
+                   ldisc_send("\033/Z", 3, 0);
                    break;
                  case '=':
                    app_keypad_keys = TRUE;
@@ -2433,7 +2759,8 @@ void term_out(void)
            check_selection(curs, cursplus);
        }
     }
-    inbuf_head = 0;
+
+    term_print_flush();
 }
 
 #if 0
@@ -2463,16 +2790,16 @@ static void do_paint(Context ctx, int may_optimise)
     pos scrpos;
     char ch[1024];
     long cursor_background = ERASE_CHAR;
-    long ticks;
+    unsigned long ticks;
 
     /*
      * Check the visual bell state.
      */
     if (in_vbell) {
-       ticks = GetTickCount();
-       if (ticks - vbell_timeout >= 0)
-           in_vbell = FALSE;
-    }
+       ticks = GETTICKCOUNT();
+       if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
+           in_vbell = FALSE; 
+   }
 
     rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
 
@@ -2501,9 +2828,10 @@ static void do_paint(Context ctx, int may_optimise)
     if (dispcurs && (curstype != cursor ||
                     dispcurs !=
                     disptext + our_curs_y * (cols + 1) + curs.x)) {
-       if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
+       if (dispcurs > disptext && 
+               (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
            dispcurs[-1] |= ATTR_INVALID;
-       if ((*dispcurs & ATTR_WIDE))
+       if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
            dispcurs[1] |= ATTR_INVALID;
        *dispcurs |= ATTR_INVALID;
        curstype = 0;
@@ -2514,7 +2842,7 @@ static void do_paint(Context ctx, int may_optimise)
     for (i = 0; i < rows; i++) {
        unsigned long *ldata;
        int lattr;
-       int idx, dirty_line, dirty_run;
+       int idx, dirty_line, dirty_run, selected;
        unsigned long attr = 0;
        int updated_line = 0;
        int start = 0;
@@ -2550,11 +2878,16 @@ static void do_paint(Context ctx, int may_optimise)
            }
            tattr |= (tchar & CSET_MASK);
            tchar &= CHAR_MASK;
+           if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+                   tattr |= ATTR_WIDE;
 
            /* Video reversing things */
+           if (seltype == LEXICOGRAPHIC)
+               selected = posle(selstart, scrpos) && poslt(scrpos, selend);
+           else
+               selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
            tattr = (tattr ^ rv
-                    ^ (posle(selstart, scrpos) &&
-                       poslt(scrpos, selend) ? ATTR_REVERSE : 0));
+                    ^ (selected ? ATTR_REVERSE : 0));
 
            /* 'Real' blinking ? */
            if (blink_is_real && (tattr & ATTR_BLINK)) {
@@ -2566,6 +2899,17 @@ static void do_paint(Context ctx, int may_optimise)
                tattr &= ~ATTR_BLINK;
            }
 
+           /*
+            * Check the font we'll _probably_ be using to see if 
+            * the character is wide when we don't want it to be.
+            */
+           if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
+               if ((tattr & ATTR_WIDE) == 0 && 
+                   CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
+                   tattr |= ATTR_NARROW;
+           } else if (disptext[idx]&ATTR_NARROW)
+               tattr |= ATTR_NARROW;
+
            /* Cursor here ? Save the 'background' */
            if (i == our_curs_y && j == curs.x) {
                cursor_background = tattr | tchar;
@@ -2647,7 +2991,7 @@ void term_blink(int flg)
     static long last_tblink = 0;
     long now, blink_diff;
 
-    now = GetTickCount();
+    now = GETTICKCOUNT();
     blink_diff = now - last_tblink;
 
     /* Make sure the text blinks no more than 2Hz */
@@ -2664,8 +3008,8 @@ void term_blink(int flg)
 
     blink_diff = now - last_blink;
 
-    /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
-    if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
+    /* Make sure the cursor blinks no faster than system blink rate */
+    if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
        return;
 
     last_blink = now;
@@ -2686,14 +3030,14 @@ void term_invalidate(void)
 /*
  * Paint the window in response to a WM_PAINT message.
  */
-void term_paint(Context ctx, int l, int t, int r, int b)
+void term_paint(Context ctx, int left, int top, int right, int bottom)
 {
-    int i, j, left, top, right, bottom;
+    int i, j;
+    if (left < 0) left = 0;
+    if (top < 0) top = 0;
+    if (right >= cols) right = cols-1;
+    if (bottom >= rows) bottom = rows-1;
 
-    left = l / font_width;
-    right = (r - 1) / font_width;
-    top = t / font_height;
-    bottom = (b - 1) / font_height;
     for (i = top; i <= bottom && i < rows; i++) {
        if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
            for (j = left; j <= right && j < cols; j++)
@@ -2706,8 +3050,9 @@ void term_paint(Context ctx, int l, int t, int r, int b)
     /* This should happen soon enough, also for some reason it sometimes 
      * fails to actually do anything when re-sizing ... painting the wrong
      * window perhaps ?
-     do_paint (ctx, FALSE);
      */
+    if (alt_pressed)
+        do_paint (ctx, FALSE);
 }
 
 /*
@@ -2730,25 +3075,38 @@ void term_scroll(int rel, int where)
     term_update();
 }
 
-static void clipme(pos top, pos bottom)
+static void clipme(pos top, pos bottom, int rect)
 {
     wchar_t *workbuf;
     wchar_t *wbptr;                   /* where next char goes within workbuf */
+    int old_top_x;
     int wblen = 0;                    /* workbuf len */
     int buflen;                               /* amount of memory allocated to workbuf */
 
     buflen = 5120;                    /* Default size */
     workbuf = smalloc(buflen * sizeof(wchar_t));
     wbptr = workbuf;                  /* start filling here */
+    old_top_x = top.x;                /* needed for rect==1 */
 
     while (poslt(top, bottom)) {
        int nl = FALSE;
        unsigned long *ldata = lineptr(top.y);
        pos nlpos;
 
+       /*
+        * nlpos will point at the maximum position on this line we
+        * should copy up to. So we start it at the end of the
+        * line...
+        */
        nlpos.y = top.y;
        nlpos.x = cols;
 
+       /*
+        * ... move it backwards if there's unused space at the end
+        * of the line (and also set `nl' if this is the case,
+        * because in normal selection mode this means we need a
+        * newline at the end)...
+        */
        if (!(ldata[cols] & LATTR_WRAPPED)) {
            while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
                    (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
@@ -2758,6 +3116,20 @@ static void clipme(pos top, pos bottom)
            if (poslt(nlpos, bottom))
                nl = TRUE;
        }
+
+       /*
+        * ... and then clip it to the terminal x coordinate if
+        * we're doing rectangular selection. (In this case we
+        * still did the above, so that copying e.g. the right-hand
+        * column from a table doesn't fill with spaces on the
+        * right.)
+        */
+       if (rect) {
+           if (nlpos.x > bottom.x)
+               nlpos.x = bottom.x;
+           nl = (top.y < bottom.y);
+       }
+
        while (poslt(top, bottom) && poslt(top, nlpos)) {
 #if 0
            char cbuf[16], *p;
@@ -2845,7 +3217,7 @@ static void clipme(pos top, pos bottom)
            }
        }
        top.y++;
-       top.x = 0;
+       top.x = rect ? old_top_x : 0;
     }
     wblen++;
     *wbptr++ = 0;
@@ -2859,7 +3231,7 @@ void term_copyall(void)
     pos top;
     top.y = -count234(scrollback);
     top.x = 0;
-    clipme(top, curs);
+    clipme(top, curs, 0);
 }
 
 /*
@@ -2957,6 +3329,12 @@ static int wordtype(int uc)
        break;
     }
 
+    /* For DBCS font's I can't do anything usefull. Even this will sometimes
+     * fail as there's such a thing as a double width space. :-(
+     */
+    if (dbcs_screenfont && font_codepage == line_codepage)
+       return (uc != ' ');
+
     if (uc < 0x80)
        return wordness[uc];
 
@@ -2975,6 +3353,7 @@ static pos sel_spread_half(pos p, int dir)
 {
     unsigned long *ldata;
     short wvalue;
+    int topy = -count234(scrollback);
 
     ldata = lineptr(p.y);
 
@@ -3001,11 +3380,47 @@ static pos sel_spread_half(pos p, int dir)
         */
        wvalue = wordtype(ldata[p.x]);
        if (dir == +1) {
-           while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
-               p.x++;
+           while (1) {
+               if (p.x < cols-1) {
+                   if (wordtype(ldata[p.x + 1]) == wvalue)
+                       p.x++;
+                   else
+                       break;
+               } else {
+                   if (ldata[cols] & LATTR_WRAPPED) {
+                       unsigned long *ldata2;
+                       ldata2 = lineptr(p.y+1);
+                       if (wordtype(ldata2[0]) == wvalue) {
+                           p.x = 0;
+                           p.y++;
+                           ldata = ldata2;
+                       } else
+                           break;
+                   } else
+                       break;
+               }
+           }
        } else {
-           while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
-               p.x--;
+           while (1) {
+               if (p.x > 0) {
+                   if (wordtype(ldata[p.x - 1]) == wvalue)
+                       p.x--;
+                   else
+                       break;
+               } else {
+                   unsigned long *ldata2;
+                   if (p.y <= topy)
+                       break;
+                   ldata2 = lineptr(p.y-1);
+                   if ((ldata2[cols] & LATTR_WRAPPED) &&
+                       wordtype(ldata2[cols-1]) == wvalue) {
+                       p.x = cols-1;
+                       p.y--;
+                       ldata = ldata2;
+                   } else
+                       break;
+               }
+           }
        }
        break;
       case SM_LINE:
@@ -3020,10 +3435,12 @@ static pos sel_spread_half(pos p, int dir)
 
 static void sel_spread(void)
 {
-    selstart = sel_spread_half(selstart, -1);
-    decpos(selend);
-    selend = sel_spread_half(selend, +1);
-    incpos(selend);
+    if (seltype == LEXICOGRAPHIC) {
+       selstart = sel_spread_half(selstart, -1);
+       decpos(selend);
+       selend = sel_spread_half(selend, +1);
+       incpos(selend);
+    }
 }
 
 void term_do_paste(void)
@@ -3064,7 +3481,7 @@ void term_do_paste(void)
 
         /* Assume a small paste will be OK in one go. */
         if (paste_len < 256) {
-            luni_send(paste_buffer, paste_len);
+            luni_send(paste_buffer, paste_len, 0);
             if (paste_buffer)
                 sfree(paste_buffer);
             paste_buffer = 0;
@@ -3075,15 +3492,25 @@ void term_do_paste(void)
 }
 
 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
-               int shift, int ctrl)
+               int shift, int ctrl, int alt)
 {
     pos selpoint;
     unsigned long *ldata;
+    int raw_mouse = (xterm_mouse &&
+                    !cfg.no_mouse_rep &&
+                    !(cfg.mouse_override && shift));
+    int default_seltype;
 
-    if (y < 0)
+    if (y < 0) {
        y = 0;
-    if (y >= rows)
+       if (a == MA_DRAG && !raw_mouse)
+           term_scroll(0, -1);
+    }
+    if (y >= rows) {
        y = rows - 1;
+       if (a == MA_DRAG && !raw_mouse)
+           term_scroll(0, +1);
+    }
     if (x < 0) {
        if (y > 0) {
            x = cols - 1;
@@ -3100,7 +3527,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
        selpoint.x /= 2;
 
-    if (xterm_mouse) {
+    if (raw_mouse) {
        int encstate = 0, r, c;
        char abuf[16];
        static int is_down = 0;
@@ -3148,15 +3575,29 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
        c = x + 33;
 
        sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
-       ldisc_send(abuf, 6);
+       ldisc_send(abuf, 6, 0);
        return;
     }
 
     b = translate_button(b);
 
+    /*
+     * Set the selection type (rectangular or normal) at the start
+     * of a selection attempt, from the state of Alt.
+     */
+    if (!alt ^ !cfg.rect_select)
+       default_seltype = RECTANGULAR;
+    else
+       default_seltype = LEXICOGRAPHIC;
+       
+    if (selstate == NO_SELECTION) {
+       seltype = default_seltype;
+    }
+
     if (b == MBT_SELECT && a == MA_CLICK) {
        deselect();
        selstate = ABOUT_TO;
+       seltype = default_seltype;
        selanchor = selpoint;
        selmode = SM_CHAR;
     } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
@@ -3172,26 +3613,64 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
        if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
            return;
        if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
-           if (posdiff(selpoint, selstart) <
-               posdiff(selend, selstart) / 2) {
-               selanchor = selend;
-               decpos(selanchor);
+           if (seltype == LEXICOGRAPHIC) {
+               /*
+                * For normal selection, we extend by moving
+                * whichever end of the current selection is closer
+                * to the mouse.
+                */
+               if (posdiff(selpoint, selstart) <
+                   posdiff(selend, selstart) / 2) {
+                   selanchor = selend;
+                   decpos(selanchor);
+               } else {
+                   selanchor = selstart;
+               }
            } else {
-               selanchor = selstart;
+               /*
+                * For rectangular selection, we have a choice of
+                * _four_ places to put selanchor and selpoint: the
+                * four corners of the selection.
+                */
+               if (2*selpoint.x < selstart.x + selend.x)
+                   selanchor.x = selend.x-1;
+               else
+                   selanchor.x = selstart.x;
+
+               if (2*selpoint.y < selstart.y + selend.y)
+                   selanchor.y = selend.y;
+               else
+                   selanchor.y = selstart.y;
            }
            selstate = DRAGGING;
        }
        if (selstate != ABOUT_TO && selstate != DRAGGING)
            selanchor = selpoint;
        selstate = DRAGGING;
-       if (poslt(selpoint, selanchor)) {
-           selstart = selpoint;
-           selend = selanchor;
-           incpos(selend);
+       if (seltype == LEXICOGRAPHIC) {
+           /*
+            * For normal selection, we set (selstart,selend) to
+            * (selpoint,selanchor) in some order.
+            */
+           if (poslt(selpoint, selanchor)) {
+               selstart = selpoint;
+               selend = selanchor;
+               incpos(selend);
+           } else {
+               selstart = selanchor;
+               selend = selpoint;
+               incpos(selend);
+           }
        } else {
-           selstart = selanchor;
-           selend = selpoint;
-           incpos(selend);
+           /*
+            * For rectangular selection, we may need to
+            * interchange x and y coordinates (if the user has
+            * dragged in the -x and +y directions, or vice versa).
+            */
+           selstart.x = min(selanchor.x, selpoint.x);
+           selend.x = 1+max(selanchor.x, selpoint.x);
+           selstart.y = min(selanchor.y, selpoint.y);
+           selend.y =   max(selanchor.y, selpoint.y);
        }
        sel_spread();
     } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
@@ -3200,7 +3679,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
             * We've completed a selection. We now transfer the
             * data to the clipboard.
             */
-           clipme(selstart, selend);
+           clipme(selstart, selend, (seltype == RECTANGULAR));
            selstate = SELECTED;
        } else
            selstate = NO_SELECTION;
@@ -3231,7 +3710,7 @@ void term_paste()
 
     /* Don't wait forever to paste */
     if (paste_hold) {
-       now = GetTickCount();
+       now = GETTICKCOUNT();
        paste_diff = now - last_paste;
        if (paste_diff >= 0 && paste_diff < 450)
            return;
@@ -3244,7 +3723,7 @@ void term_paste()
            if (paste_buffer[paste_pos + n++] == '\r')
                break;
        }
-       luni_send(paste_buffer + paste_pos, n);
+       luni_send(paste_buffer + paste_pos, n, 0);
        paste_pos += n;
 
        if (paste_pos < paste_len) {
@@ -3283,18 +3762,17 @@ int term_ldisc(int option)
  */
 int from_backend(int is_stderr, char *data, int len)
 {
-    while (len--) {
-       if (inbuf_head >= INBUF_SIZE)
-           term_out();
-       inbuf[inbuf_head++] = *data++;
-    }
+    assert(len > 0);
+
+    bufchain_add(&inbuf, data, len);
 
     /*
-     * We process all stdout/stderr data immediately we receive it,
-     * and don't return until it's all gone. Therefore, there's no
-     * reason at all to return anything other than zero from this
-     * function.
-     * 
+     * term_out() always completely empties inbuf. Therefore,
+     * there's no reason at all to return anything other than zero
+     * from this function, because there _can't_ be a question of
+     * the remote side needing to wait until term_out() has cleared
+     * a backlog.
+     *
      * This is a slightly suboptimal way to deal with SSH2 - in
      * principle, the window mechanism would allow us to continue
      * to accept data on forwarded ports and X connections even
@@ -3304,78 +3782,8 @@ int from_backend(int is_stderr, char *data, int len)
      * portability. So we manage stdout buffering the old SSH1 way:
      * if the terminal processing goes slowly, the whole SSH
      * connection stops accepting data until it's ready.
-     * 
+     *
      * In practice, I can't imagine this causing serious trouble.
      */
     return 0;
 }
-
-/*
- * Log session traffic.
- */
-void logtraffic(unsigned char c, int logmode)
-{
-    if (cfg.logtype > 0) {
-       if (cfg.logtype == logmode) {
-           /* deferred open file from pgm start? */
-           if (!lgfp)
-               logfopen();
-           if (lgfp)
-               fputc(c, lgfp);
-       }
-    }
-}
-
-/* open log file append/overwrite mode */
-void logfopen(void)
-{
-    char buf[256];
-    time_t t;
-    struct tm *tm;
-    char writemod[4];
-
-    if (!cfg.logtype)
-       return;
-    sprintf(writemod, "wb");          /* default to rewrite */
-    lgfp = fopen(cfg.logfilename, "r");        /* file already present? */
-    if (lgfp) {
-       int i;
-       fclose(lgfp);
-       i = askappend(cfg.logfilename);
-       if (i == 1)
-           writemod[0] = 'a';         /* set append mode */
-       else if (i == 0) {             /* cancelled */
-           lgfp = NULL;
-           cfg.logtype = 0;           /* disable logging */
-           return;
-       }
-    }
-
-    lgfp = fopen(cfg.logfilename, writemod);
-    if (lgfp) {                               /* enter into event log */
-       sprintf(buf, "%s session log (%s mode) to file : ",
-               (writemod[0] == 'a') ? "Appending" : "Writing new",
-               (cfg.logtype == LGTYP_ASCII ? "ASCII" :
-                cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
-       /* Make sure we do not exceed the output buffer size */
-       strncat(buf, cfg.logfilename, 128);
-       buf[strlen(buf)] = '\0';
-       logevent(buf);
-
-       /* --- write header line iinto log file */
-       fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
-       time(&t);
-       tm = localtime(&t);
-       strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
-       fputs(buf, lgfp);
-       fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
-    }
-}
-
-void logfclose(void)
-{
-    if (lgfp) {
-       fclose(lgfp);
-       lgfp = NULL;
-    }
-}