Reintroduce Cyrillic Caps Lock mode, which was in 0.51 but got
[u/mdw/putty] / terminal.c
index aeadc77..ab97e3c 100644 (file)
@@ -201,6 +201,7 @@ static void deselect(void);
 /* log session to file stuff ... */
 static FILE *lgfp = NULL;
 static void logtraffic(unsigned char c, int logmode);
+static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm);
 
 /*
  * Resize a line to make it `cols' columns wide.
@@ -254,6 +255,7 @@ unsigned long *lineptr(int y, int lineno)
     if (newline != line) {
        delpos234(whichtree, treeindex);
        addpos234(whichtree, newline, treeindex);
+        line = newline;
     }
 
     return line + 1;
@@ -495,6 +497,7 @@ void term_size(int newrows, int newcols, int newsavelines)
 
     update_sbar();
     term_update();
+    back->size();
 }
 
 /*
@@ -585,7 +588,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;
@@ -635,6 +638,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++)
@@ -642,17 +662,26 @@ 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.
+            */
+           seltop = sb ? -savelines : 0;
+
+           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;
                }
            }
@@ -823,7 +852,7 @@ static void toggle_mode(int mode, int query, int state)
            break;
          case 3:                      /* 80/132 columns */
            deselect();
-           request_resize(state ? 132 : 80, rows, 1);
+           request_resize(state ? 132 : 80, rows);
            reset_132 = state;
            break;
          case 5:                      /* reverse video */
@@ -933,15 +962,18 @@ void term_out(void)
 {
     int c, inbuf_reap;
 
+    /*
+     * Optionally log the session traffic to a file. Useful for
+     * debugging and possibly also useful for actual logging.
+     */
+    if (cfg.logtype == LGTYP_DEBUG)
+       for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
+           logtraffic((unsigned char) inbuf[inbuf_reap], LGTYP_DEBUG);
+       }
+
     for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
        c = inbuf[inbuf_reap];
 
-       /*
-        * Optionally log the session traffic to a file. Useful for
-        * debugging and possibly also useful for actual logging.
-        */
-       logtraffic((unsigned char) c, LGTYP_DEBUG);
-
        /* 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.
@@ -984,9 +1016,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. */
+                       inbuf_reap--;
+                       c = UCSERR;
+                       utf_state = 0;
                        break;
                    }
                    utf_char = (utf_char << 6) | (c & 0x3f);
@@ -1308,12 +1340,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;
@@ -1430,7 +1468,7 @@ void term_out(void)
                    compatibility(VT100);
                    power_on();
                    if (reset_132) {
-                       request_resize(80, rows, 1);
+                       request_resize(80, rows);
                        reset_132 = 0;
                    }
                    fix_cpos;
@@ -1875,7 +1913,7 @@ void term_out(void)
                        compatibility(VT340TEXT);
                        if (esc_nargs <= 1
                            && (esc_args[0] < 1 || esc_args[0] >= 24)) {
-                           request_resize(cols, def(esc_args[0], 24), 0);
+                           request_resize(cols, def(esc_args[0], 24));
                            deselect();
                        }
                        break;
@@ -1901,9 +1939,7 @@ void term_out(void)
                         */
                        compatibility(VT420);
                        if (esc_nargs == 1 && esc_args[0] > 0) {
-                           request_resize(cols,
-                                          def(esc_args[0], cfg.height),
-                                          0);
+                           request_resize(cols, def(esc_args[0], cfg.height));
                            deselect();
                        }
                        break;
@@ -1914,8 +1950,7 @@ void term_out(void)
                         */
                        compatibility(VT340TEXT);
                        if (esc_nargs <= 1) {
-                           request_resize(def(esc_args[0], cfg.width),
-                                          rows, 0);
+                           request_resize(def(esc_args[0], cfg.width), rows);
                            deselect();
                        }
                        break;
@@ -1948,6 +1983,21 @@ void term_out(void)
                            }
                        }
                        break;
+                     case 'Z':         /* BackTab for xterm */
+                       compatibility(OTHER);
+                       {
+                           int i = def(esc_args[0], 1);
+                           pos old_curs = curs;
+
+                           for(;i>0 && curs.x>0; i--) {
+                               do {
+                                   curs.x--;
+                               } while (curs.x >0 && !tabs[curs.x]);
+                           }
+                           fix_cpos;
+                           check_selection(old_curs, curs);
+                       }
+                       break;
                      case ANSI('L', '='):
                        compatibility(OTHER);
                        use_bce = (esc_args[0] <= 0);
@@ -2028,9 +2078,9 @@ void term_out(void)
                         */
                        if (!has_compat(VT420) && has_compat(VT100)) {
                            if (reset_132)
-                               request_resize(132, 24, 1);
+                               request_resize(132, 24);
                            else
-                               request_resize(80, 24, 1);
+                               request_resize(80, 24);
                        }
 #endif
                        break;
@@ -2485,9 +2535,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;
@@ -2534,6 +2585,8 @@ 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 */
            tattr = (tattr ^ rv
@@ -2550,6 +2603,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;
@@ -2670,14 +2734,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++)
@@ -2690,8 +2754,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);
 }
 
 /*
@@ -2941,6 +3006,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];
 
@@ -3063,11 +3134,18 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
 {
     pos selpoint;
     unsigned long *ldata;
+    int raw_mouse = xterm_mouse && !(cfg.mouse_override && shift);
 
-    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;
@@ -3084,7 +3162,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;
@@ -3265,13 +3343,33 @@ int term_ldisc(int option)
 /*
  * from_backend(), to get data from the backend for the terminal.
  */
-void from_backend(int is_stderr, char *data, int len)
+int from_backend(int is_stderr, char *data, int len)
 {
     while (len--) {
        if (inbuf_head >= INBUF_SIZE)
            term_out();
        inbuf[inbuf_head++] = *data++;
     }
+
+    /*
+     * 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.
+     * 
+     * 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
+     * while the terminal processing was going slowly - but we
+     * can't do the 100% right thing without moving the terminal
+     * processing into a separate thread, and that might hurt
+     * 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;
 }
 
 /*
@@ -3290,22 +3388,35 @@ void logtraffic(unsigned char c, int logmode)
     }
 }
 
+void settimstr(char *ta, int no_sec);
+char *subslfcode(char *dest, char *src, char *dstrt);
+char *stpncpy(char *dst, const char *src, size_t maxlen);
+char timdatbuf[20];
+char currlogfilename[FILENAME_MAX];
+
 /* open log file append/overwrite mode */
 void logfopen(void)
 {
     char buf[256];
     time_t t;
-    struct tm *tm;
+    struct tm tm;
     char writemod[4];
 
     if (!cfg.logtype)
        return;
     sprintf(writemod, "wb");          /* default to rewrite */
-    lgfp = fopen(cfg.logfilename, "r");        /* file already present? */
+
+    time(&t);
+    tm = *localtime(&t);
+
+    /* substitute special codes in file name */
+    xlatlognam(currlogfilename,cfg.logfilename,cfg.host, &tm);
+
+    lgfp = fopen(currlogfilename, "r");        /* file already present? */
     if (lgfp) {
        int i;
        fclose(lgfp);
-       i = askappend(cfg.logfilename);
+       i = askappend(currlogfilename);
        if (i == 1)
            writemod[0] = 'a';         /* set append mode */
        else if (i == 0) {             /* cancelled */
@@ -3315,22 +3426,20 @@ void logfopen(void)
        }
     }
 
-    lgfp = fopen(cfg.logfilename, writemod);
+    lgfp = fopen(currlogfilename, 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);
+       strncat(buf, currlogfilename, 128);
        buf[strlen(buf)] = '\0';
        logevent(buf);
 
-       /* --- write header line iinto log file */
+       /* --- write header line into log file */
        fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
-       time(&t);
-       tm = localtime(&t);
-       strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
+       strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);
        fputs(buf, lgfp);
        fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
     }
@@ -3343,3 +3452,57 @@ void logfclose(void)
        lgfp = NULL;
     }
 }
+
+/*
+ * translate format codes into time/date strings
+ * and insert them into log file name
+ *
+ * "&Y":YYYY   "&m":MM   "&d":DD   "&T":hhmm   "&h":<hostname>   "&&":&
+ */
+static void xlatlognam(char *d, char *s, char *hostname, struct tm *tm) {
+    char buf[10], *bufp;
+    int size;
+    char *ds = d; /* save start pos. */
+    int len = FILENAME_MAX-1;
+
+    while (*s) {
+       /* Let (bufp, len) be the string to append. */
+       bufp = buf;                    /* don't usually override this */
+       if (*s == '&') {
+           char c;
+           s++;
+           if (*s) switch (c = *s++, tolower(c)) {
+             case 'y':
+               size = strftime(buf, sizeof(buf), "%Y", tm);
+               break;
+             case 'm':
+               size = strftime(buf, sizeof(buf), "%m", tm);
+               break;
+             case 'd':
+               size = strftime(buf, sizeof(buf), "%d", tm);
+               break;
+             case 't':
+               size = strftime(buf, sizeof(buf), "%H%M%S", tm);
+               break;
+             case 'h':
+               bufp = hostname;
+               size = strlen(bufp);
+               break;
+             default:
+               buf[0] = '&';
+               size = 1;
+               if (c != '&')
+                   buf[size++] = c;
+           }
+       } else {
+           buf[0] = *s++;
+           size = 1;
+       }
+       if (size > len)
+           size = len;
+       memcpy(d, bufp, size);
+       d += size;
+       len -= size;
+    }
+    *d = '\0';
+}