X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/568dd02f4cd326537f5b71db44a06242a9311280..2ed9fb8a1161813bd21e396475d4810f27fac7eb:/terminal.c diff --git a/terminal.c b/terminal.c index d84f57ee..9fc1787b 100644 --- a/terminal.c +++ b/terminal.c @@ -94,6 +94,7 @@ 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 rvideo; /* global reverse video flag */ static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */ static int cursor_on; /* cursor enabled flag */ @@ -104,6 +105,7 @@ static int tblinker; /* When the blinking text is on */ static int blink_is_real; /* Actually blink blinking text */ static int term_echoing; /* Does terminal want local echo? */ static int term_editing; /* Does terminal want local edit? */ +static int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */ 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. */ @@ -116,7 +118,7 @@ static unsigned long cset_attr[2]; /* * Saved settings on the alternate screen. */ -static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset; +static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf; static int alt_t, alt_b; static int alt_which; @@ -199,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. @@ -252,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; @@ -278,6 +282,8 @@ static void power_on(void) alt_wnext = wrapnext = alt_ins = insert = FALSE; alt_wrap = wrap = cfg.wrap_mode; alt_cset = cset = 0; + alt_utf = utf = 0; + alt_sco_acs = sco_acs = 0; cset_attr[0] = cset_attr[1] = ATTR_ASCII; rvideo = 0; in_vbell = FALSE; @@ -536,6 +542,12 @@ static void swap_screen(int which) t = cset; cset = alt_cset; alt_cset = t; + t = utf; + utf = alt_utf; + alt_utf = t; + t = sco_acs; + sco_acs = alt_sco_acs; + alt_sco_acs = t; fix_cpos; } @@ -689,7 +701,9 @@ static void save_cursor(int save) savecurs = curs; save_attr = curr_attr; save_cset = cset; + save_utf = utf; save_csattr = cset_attr[cset]; + save_sco_acs = sco_acs; } else { curs = savecurs; /* Make sure the window hasn't shrunk since the save */ @@ -700,10 +714,13 @@ static void save_cursor(int save) curr_attr = save_attr; cset = save_cset; + utf = save_utf; cset_attr[cset] = save_csattr; + sco_acs = save_sco_acs; fix_cpos; if (use_bce) - erase_char = (' ' | (curr_attr & (ATTR_FGMASK | ATTR_BGMASK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & (ATTR_FGMASK | ATTR_BGMASK))); } } @@ -735,6 +752,7 @@ static void erase_lots(int line_only, int from_begin, int to_end) } if (!to_end) { end = curs; + incpos(end); } check_selection(start, end); @@ -744,7 +762,7 @@ static void erase_lots(int line_only, int from_begin, int to_end) ldata = lineptr(start.y); while (poslt(start, end)) { - if (start.y == cols && !erase_lattr) + if (start.x == cols && !erase_lattr) ldata[start.x] &= ~LATTR_WRAPPED; else ldata[start.x] = erase_char; @@ -917,15 +935,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. @@ -933,18 +954,15 @@ void term_out(void) /* First see about all those translations. */ if (termstate == TOPLEVEL) { - if (utf) + if (in_utf) switch (utf_state) { case 0: if (c < 0x80) { - /* I know; gotos are evil. This one is really bad! - * But before you try removing it follow the path of the - * sequence "0x5F 0xC0 0x71" with UTF and VTGraphics on. - */ - /* - if (cfg.no_vt_graph_with_utf8) break; - */ - goto evil_jump; + /* UTF-8 must be stateless so we ignore iso2022. */ + if (unitab_ctrl[c] != 0xFF) + c = unitab_ctrl[c]; + else c = ((unsigned char)c) | ATTR_ASCII; + break; } else if ((c & 0xe0) == 0xc0) { utf_size = utf_state = 1; utf_char = (c & 0x1f); @@ -971,9 +989,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); @@ -1023,8 +1041,14 @@ void term_out(void) if (c >= 0x10000) c = 0xFFFD; break; + } + /* Are we in the nasty ACS mode? Note: no sco in utf mode. */ + else if(sco_acs && + (c!='\033' && c!='\n' && c!='\r' && c!='\b')) + { + if (sco_acs == 2) c ^= 0x80; + c |= ATTR_SCOACS; } else { - evil_jump:; switch (cset_attr[cset]) { /* * Linedraw characters are different from 'ESC ( B' @@ -1051,6 +1075,9 @@ void term_out(void) else c = ((unsigned char) c) | ATTR_ASCII; break; + case ATTR_SCOACS: + if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS; + break; } } } @@ -1482,6 +1509,10 @@ void term_out(void) compatibility(VT100); cset_attr[0] = ATTR_LINEDRW; break; + case ANSI('U', '('): + compatibility(OTHER); + cset_attr[0] = ATTR_SCOACS; + break; case ANSI('A', ')'): compatibility(VT100); @@ -1495,6 +1526,10 @@ void term_out(void) compatibility(VT100); cset_attr[1] = ATTR_LINEDRW; break; + case ANSI('U', ')'): + compatibility(OTHER); + cset_attr[1] = ATTR_SCOACS; + break; case ANSI('8', '%'): /* Old Linux code */ case ANSI('G', '%'): @@ -1503,8 +1538,7 @@ void term_out(void) break; case ANSI('@', '%'): compatibility(OTHER); - if (line_codepage != CP_UTF8) - utf = 0; + utf = 0; break; } break; @@ -1538,18 +1572,20 @@ void term_out(void) break; case 'e': /* move down N lines */ compatibility(ANSI); + /* FALLTHROUGH */ case 'B': move(curs.x, curs.y + def(esc_args[0], 1), 1); seen_disp_event = TRUE; break; - case 'a': /* move right N cols */ - compatibility(ANSI); case ANSI('c', '>'): /* report xterm version */ 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); break; + case 'a': /* move right N cols */ + compatibility(ANSI); + /* FALLTHROUGH */ case 'C': move(curs.x + def(esc_args[0], 1), curs.y, 1); seen_disp_event = TRUE; @@ -1760,6 +1796,15 @@ void term_out(void) case 7: /* enable reverse video */ curr_attr |= ATTR_REVERSE; break; + case 10: /* SCO acs off */ + compatibility(SCOANSI); + sco_acs = 0; break; + case 11: /* SCO acs on */ + compatibility(SCOANSI); + sco_acs = 1; break; + case 12: /* SCO acs on flipped */ + compatibility(SCOANSI); + sco_acs = 2; break; case 22: /* disable bold */ compatibility2(OTHER, VT220); curr_attr &= ~ATTR_BOLD; @@ -1813,11 +1858,9 @@ void term_out(void) } } if (use_bce) - erase_char = - (' ' | - (curr_attr & - (ATTR_FGMASK | ATTR_BGMASK | - ATTR_BLINK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & + (ATTR_FGMASK | ATTR_BGMASK))); } break; case 's': /* save cursor */ @@ -1910,15 +1953,29 @@ 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); erase_char = ERASE_CHAR; if (use_bce) - erase_char = - (' ' | - (curr_attr & - (ATTR_FGMASK | ATTR_BGMASK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & + (ATTR_FGMASK | ATTR_BGMASK))); break; case ANSI('E', '='): compatibility(OTHER); @@ -2310,10 +2367,9 @@ void term_out(void) vt52_bold = FALSE; curr_attr = ATTR_DEFAULT; if (use_bce) - erase_char = (' ' | - (curr_attr & - (ATTR_FGMASK | ATTR_BGMASK | - ATTR_BLINK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & + (ATTR_FGMASK | ATTR_BGMASK))); break; case 'S': /* compatibility(VI50) */ @@ -2355,10 +2411,8 @@ void term_out(void) curr_attr |= ATTR_BOLD; if (use_bce) - erase_char = (' ' | - (curr_attr & - (ATTR_FGMASK | ATTR_BGMASK | - ATTR_BLINK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & (ATTR_FGMASK | ATTR_BGMASK))); break; case VT52_BG: termstate = TOPLEVEL; @@ -2371,10 +2425,8 @@ void term_out(void) curr_attr |= ATTR_BLINK; if (use_bce) - erase_char = (' ' | - (curr_attr & - (ATTR_FGMASK | ATTR_BGMASK | - ATTR_BLINK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & (ATTR_FGMASK | ATTR_BGMASK))); break; #endif default: break; /* placate gcc warning about enum use */ @@ -2496,6 +2548,9 @@ static void do_paint(Context ctx, int may_optimise) case ATTR_LINEDRW: tchar = unitab_xterm[tchar & 0xFF]; break; + case ATTR_SCOACS: + tchar = unitab_scoacs[tchar&0xFF]; + break; } tattr |= (tchar & CSET_MASK); tchar &= CHAR_MASK; @@ -2730,6 +2785,9 @@ static void clipme(pos top, pos bottom) case ATTR_ASCII: uc = unitab_line[uc & 0xFF]; break; + case ATTR_SCOACS: + uc = unitab_scoacs[uc&0xFF]; + break; } switch (uc & CSET_MASK) { case ATTR_ACP: @@ -2890,6 +2948,9 @@ static int wordtype(int uc) case ATTR_ASCII: uc = unitab_line[uc & 0xFF]; break; + case ATTR_SCOACS: + uc = unitab_scoacs[uc&0xFF]; + break; } switch (uc & CSET_MASK) { case ATTR_ACP: @@ -3022,11 +3083,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; @@ -3043,7 +3111,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; @@ -3224,13 +3292,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; } /* @@ -3249,22 +3337,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 */ @@ -3274,22 +3375,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" : "")); /* 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); } @@ -3302,3 +3401,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": "&&":& + */ +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'; +}