Fix error message: couldn't load _private_ key, not public.
[u/mdw/putty] / terminal.c
CommitLineData
374330e2 1#include <windows.h>
2
3#include <stdio.h>
4#include <stdlib.h>
49bad831 5#include <ctype.h>
374330e2 6
e1c8e0ed 7#include <time.h>
c83de303 8#include <assert.h>
374330e2 9#include "putty.h"
4facdf84 10#include "tree234.h"
374330e2 11
4eeb7d09 12#define VT52_PLUS
13
32874aea 14#define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */
15#define CL_VT100 0x0002 /* VT100 */
16#define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */
17#define CL_VT102 0x0008 /* VT102 */
18#define CL_VT220 0x0010 /* VT220 */
19#define CL_VT320 0x0020 /* VT320 */
20#define CL_VT420 0x0040 /* VT420 */
21#define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */
22#define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */
23#define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */
24#define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */
25#define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */
e14a5a13 26
c9def1b8 27#define TM_VT100 (CL_ANSIMIN|CL_VT100)
28#define TM_VT100AVO (TM_VT100|CL_VT100AVO)
29#define TM_VT102 (TM_VT100AVO|CL_VT102)
30#define TM_VT220 (TM_VT102|CL_VT220)
31#define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
32#define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)
33
34#define TM_PUTTY (0xFFFF)
e14a5a13 35
36#define compatibility(x) \
37 if ( ((CL_##x)&compatibility_level) == 0 ) { \
38 termstate=TOPLEVEL; \
39 break; \
40 }
c9def1b8 41#define compatibility2(x,y) \
42 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
43 termstate=TOPLEVEL; \
44 break; \
45 }
e14a5a13 46
47#define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
48
49static int compatibility_level = TM_PUTTY;
50
4facdf84 51static tree234 *scrollback; /* lines scrolled off top of screen */
52static tree234 *screen; /* lines on primary screen */
53static tree234 *alt_screen; /* lines on alternate screen */
c83de303 54static int disptop; /* distance scrolled back (0 or -ve) */
e14a5a13 55
374330e2 56static unsigned long *cpos; /* cursor position (convenience) */
4facdf84 57
374330e2 58static unsigned long *disptext; /* buffer of text on real screen */
4eeb7d09 59static unsigned long *dispcurs; /* location of cursor on real screen */
60static unsigned long curstype; /* type of cursor on real screen */
374330e2 61
c83de303 62#define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
156686ef 63
64struct beeptime {
65 struct beeptime *next;
66 long ticks;
67};
68static struct beeptime *beephead, *beeptail;
69int nbeeps;
70int beep_overloaded;
71long lastbeep;
72
4facdf84 73#define TSIZE (sizeof(unsigned long))
c83de303 74#define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
374330e2 75
76static unsigned long curr_attr, save_attr;
e14a5a13 77static unsigned long erase_char = ERASE_CHAR;
374330e2 78
4facdf84 79typedef struct {
80 int y, x;
81} pos;
82#define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
83#define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
84#define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
435aa044 85#define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
4facdf84 86#define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
87#define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
88
89static pos curs; /* cursor */
90static pos savecurs; /* saved cursor position */
374330e2 91static int marg_t, marg_b; /* scroll margins */
92static int dec_om; /* DEC origin mode flag */
93static int wrap, wrapnext; /* wrap flags */
94static int insert; /* insert-mode flag */
95static int cset; /* 0 or 1: which char set */
96static int save_cset, save_csattr; /* saved with cursor position */
8f22582c 97static int save_utf; /* saved with cursor position */
374330e2 98static int rvideo; /* global reverse video flag */
c83de303 99static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
e14a5a13 100static int cursor_on; /* cursor enabled flag */
101static int reset_132; /* Flag ESC c resets to 80 cols */
102static int use_bce; /* Use Background coloured erase */
103static int blinker; /* When blinking is the cursor on ? */
c9def1b8 104static int tblinker; /* When the blinking text is on */
105static int blink_is_real; /* Actually blink blinking text */
32874aea 106static int term_echoing; /* Does terminal want local echo? */
107static int term_editing; /* Does terminal want local edit? */
d3cb5465 108static int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */
4eeb7d09 109static int vt52_bold; /* Force bold on non-bold colours */
110static int utf_state; /* Is there a pending UTF-8 character */
111static int utf_char; /* and what is it so far. */
112static int utf_size; /* The size of the UTF character. */
374330e2 113
32874aea 114static int xterm_mouse; /* send mouse messages to app */
01c034ad 115
374330e2 116static unsigned long cset_attr[2];
117
118/*
119 * Saved settings on the alternate screen.
120 */
8f22582c 121static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
374330e2 122static int alt_t, alt_b;
123static int alt_which;
124
125#define ARGS_MAX 32 /* max # of esc sequence arguments */
064916ea 126#define ARG_DEFAULT 0 /* if an arg isn't specified */
374330e2 127#define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
128static int esc_args[ARGS_MAX];
129static int esc_nargs;
130static int esc_query;
8b811b12 131#define ANSI(x,y) ((x)+((y)<<8))
132#define ANSI_QUE(x) ANSI(x,TRUE)
374330e2 133
134#define OSC_STR_MAX 2048
135static int osc_strlen;
32874aea 136static char osc_string[OSC_STR_MAX + 1];
374330e2 137static int osc_w;
138
c9def1b8 139static char id_string[1024] = "\033[?6c";
140
374330e2 141static unsigned char *tabs;
142
374330e2 143static enum {
cabfd08c 144 TOPLEVEL,
145 SEEN_ESC,
146 SEEN_CSI,
147 SEEN_OSC,
148 SEEN_OSC_W,
149
150 DO_CTRLS,
151
cabfd08c 152 SEEN_OSC_P,
153 OSC_STRING, OSC_MAYBE_ST,
e14a5a13 154 VT52_ESC,
155 VT52_Y1,
4eeb7d09 156 VT52_Y2,
157 VT52_FG,
158 VT52_BG
374330e2 159} termstate;
160
161static enum {
162 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
163} selstate;
164static enum {
165 SM_CHAR, SM_WORD, SM_LINE
166} selmode;
4facdf84 167static pos selstart, selend, selanchor;
374330e2 168
169static short wordness[256] = {
32874aea 170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
172 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
173 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
174 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
175 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
176 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
177 2, 2, 2, 1, 1, 1, 1, 1, /* 67 */
178 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
179 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
180 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
181 1, 1, 1, 1, 1, 1, 1, 1, /* AB */
182 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
183 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
184 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
185 2, 2, 2, 2, 2, 2, 2, 2, /* EF */
374330e2 186};
187
4eeb7d09 188#define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
189static wchar_t sel_nl[] = SEL_NL;
190static wchar_t *paste_buffer = 0;
c9def1b8 191static int paste_len, paste_pos, paste_hold;
374330e2 192
193/*
194 * Internal prototypes.
195 */
32874aea 196static void do_paint(Context, int);
197static void erase_lots(int, int, int);
198static void swap_screen(int);
199static void update_sbar(void);
200static void deselect(void);
e1c8e0ed 201/* log session to file stuff ... */
202static FILE *lgfp = NULL;
203static void logtraffic(unsigned char c, int logmode);
374330e2 204
205/*
2371caac 206 * Resize a line to make it `cols' columns wide.
207 */
208unsigned long *resizeline(unsigned long *line, int cols)
209{
210 int i, oldlen;
211 unsigned long lineattrs;
212
213 if (line[0] != (unsigned long)cols) {
214 /*
215 * This line is the wrong length, which probably means it
216 * hasn't been accessed since a resize. Resize it now.
217 */
218 oldlen = line[0];
219 lineattrs = line[oldlen + 1];
220 line = srealloc(line, TSIZE * (2 + cols));
221 line[0] = cols;
222 for (i = oldlen; i < cols; i++)
223 line[i + 1] = ERASE_CHAR;
224 line[cols + 1] = lineattrs & LATTR_MODE;
225 }
226
227 return line;
228}
229
230/*
c83de303 231 * Retrieve a line of the screen or of the scrollback, according to
232 * whether the y coordinate is non-negative or negative
233 * (respectively).
234 */
32874aea 235unsigned long *lineptr(int y, int lineno)
236{
2371caac 237 unsigned long *line, *newline;
c83de303 238 tree234 *whichtree;
2371caac 239 int treeindex;
c83de303 240
241 if (y >= 0) {
242 whichtree = screen;
243 treeindex = y;
244 } else {
245 whichtree = scrollback;
246 treeindex = y + count234(scrollback);
247 }
248 line = index234(whichtree, treeindex);
249
250 /* We assume that we don't screw up and retrieve something out of range. */
c83de303 251 assert(line != NULL);
c83de303 252
2371caac 253 newline = resizeline(line, cols);
254 if (newline != line) {
c83de303 255 delpos234(whichtree, treeindex);
2371caac 256 addpos234(whichtree, newline, treeindex);
04dcfaa2 257 line = newline;
c83de303 258 }
259
32874aea 260 return line + 1;
c83de303 261}
32874aea 262
c83de303 263#define lineptr(x) lineptr(x,__LINE__)
264/*
374330e2 265 * Set up power-on settings for the terminal.
266 */
32874aea 267static void power_on(void)
268{
4facdf84 269 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
374330e2 270 alt_t = marg_t = 0;
271 if (rows != -1)
272 alt_b = marg_b = rows - 1;
273 else
274 alt_b = marg_b = 0;
275 if (cols != -1) {
276 int i;
277 for (i = 0; i < cols; i++)
278 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
279 }
280 alt_om = dec_om = cfg.dec_om;
281 alt_wnext = wrapnext = alt_ins = insert = FALSE;
282 alt_wrap = wrap = cfg.wrap_mode;
283 alt_cset = cset = 0;
8f22582c 284 alt_utf = utf = 0;
d3cb5465 285 alt_sco_acs = sco_acs = 0;
374330e2 286 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
287 rvideo = 0;
156686ef 288 in_vbell = FALSE;
e14a5a13 289 cursor_on = 1;
4eeb7d09 290 big_cursor = 0;
374330e2 291 save_attr = curr_attr = ATTR_DEFAULT;
0965bee0 292 term_editing = term_echoing = FALSE;
32874aea 293 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
374330e2 294 app_cursor_keys = cfg.app_cursor;
295 app_keypad_keys = cfg.app_keypad;
c9def1b8 296 use_bce = cfg.bce;
297 blink_is_real = cfg.blinktext;
e14a5a13 298 erase_char = ERASE_CHAR;
374330e2 299 alt_which = 0;
300 {
301 int i;
302 for (i = 0; i < 256; i++)
303 wordness[i] = cfg.wordness[i];
304 }
4facdf84 305 if (screen) {
32874aea 306 swap_screen(1);
307 erase_lots(FALSE, TRUE, TRUE);
308 swap_screen(0);
309 erase_lots(FALSE, TRUE, TRUE);
374330e2 310 }
311}
312
313/*
314 * Force a screen update.
315 */
32874aea 316void term_update(void)
317{
374330e2 318 Context ctx;
319 ctx = get_ctx();
320 if (ctx) {
421e689a 321 if (seen_disp_event)
322 update_sbar();
32874aea 323 if ((seen_key_event && (cfg.scroll_on_key)) ||
324 (seen_disp_event && (cfg.scroll_on_disp))) {
4facdf84 325 disptop = 0; /* return to main screen */
cabfd08c 326 seen_disp_event = seen_key_event = 0;
327 }
32874aea 328 do_paint(ctx, TRUE);
329 sys_cursor(curs.x, curs.y - disptop);
330 free_ctx(ctx);
374330e2 331 }
332}
333
334/*
335 * Same as power_on(), but an external function.
336 */
32874aea 337void term_pwron(void)
338{
374330e2 339 power_on();
340 fix_cpos;
4facdf84 341 disptop = 0;
374330e2 342 deselect();
343 term_update();
344}
345
346/*
347 * Clear the scrollback.
348 */
32874aea 349void term_clrsb(void)
350{
4facdf84 351 unsigned long *line;
352 disptop = 0;
353 while ((line = delpos234(scrollback, 0)) != NULL) {
354 sfree(line);
355 }
374330e2 356 update_sbar();
357}
358
359/*
360 * Initialise the terminal.
361 */
32874aea 362void term_init(void)
363{
4facdf84 364 screen = alt_screen = scrollback = NULL;
365 disptop = 0;
4eeb7d09 366 disptext = dispcurs = NULL;
374330e2 367 tabs = NULL;
374330e2 368 deselect();
369 rows = cols = -1;
374330e2 370 power_on();
156686ef 371 beephead = beeptail = NULL;
372 nbeeps = 0;
373 lastbeep = FALSE;
374 beep_overloaded = FALSE;
374330e2 375}
376
377/*
378 * Set up the terminal for a given size.
379 */
32874aea 380void term_size(int newrows, int newcols, int newsavelines)
381{
2d466ffd 382 tree234 *newalt;
383 unsigned long *newdisp, *line;
384 int i, j;
c83de303 385 int sblen;
d6832394 386 int save_alt_which = alt_which;
387
374330e2 388 if (newrows == rows && newcols == cols && newsavelines == savelines)
389 return; /* nothing to do */
390
d6832394 391 deselect();
392 swap_screen(0);
393
374330e2 394 alt_t = marg_t = 0;
395 alt_b = marg_b = newrows - 1;
396
c83de303 397 if (rows == -1) {
398 scrollback = newtree234(NULL);
399 screen = newtree234(NULL);
400 rows = 0;
401 }
402
403 /*
404 * Resize the screen and scrollback. We only need to shift
405 * lines around within our data structures, because lineptr()
406 * will take care of resizing each individual line if
407 * necessary. So:
408 *
409 * - If the new screen and the old screen differ in length, we
410 * must shunt some lines in from the scrollback or out to
411 * the scrollback.
412 *
413 * - If doing that fails to provide us with enough material to
414 * fill the new screen (i.e. the number of rows needed in
415 * the new screen exceeds the total number in the previous
416 * screen+scrollback), we must invent some blank lines to
417 * cover the gap.
418 *
419 * - Then, if the new scrollback length is less than the
420 * amount of scrollback we actually have, we must throw some
421 * away.
422 */
423 sblen = count234(scrollback);
cdd6c586 424 /* Do this loop to expand the screen if newrows > rows */
425 for (i = rows; i < newrows; i++) {
32874aea 426 if (sblen > 0) {
427 line = delpos234(scrollback, --sblen);
428 } else {
429 line = smalloc(TSIZE * (newcols + 2));
430 line[0] = newcols;
431 for (j = 0; j <= newcols; j++)
432 line[j + 1] = ERASE_CHAR;
433 }
434 addpos234(screen, line, 0);
cdd6c586 435 }
436 /* Do this loop to shrink the screen if newrows < rows */
437 for (i = newrows; i < rows; i++) {
32874aea 438 line = delpos234(screen, 0);
439 addpos234(scrollback, line, sblen++);
c83de303 440 }
441 assert(count234(screen) == newrows);
442 while (sblen > newsavelines) {
443 line = delpos234(scrollback, 0);
444 sfree(line);
445 sblen--;
c9def1b8 446 }
c83de303 447 assert(count234(scrollback) <= newsavelines);
4facdf84 448 disptop = 0;
374330e2 449
32874aea 450 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
451 for (i = 0; i < newrows * (newcols + 1); i++)
374330e2 452 newdisp[i] = ATTR_INVALID;
32874aea 453 sfree(disptext);
374330e2 454 disptext = newdisp;
4eeb7d09 455 dispcurs = NULL;
374330e2 456
4facdf84 457 newalt = newtree234(NULL);
32874aea 458 for (i = 0; i < newrows; i++) {
459 line = smalloc(TSIZE * (newcols + 2));
c83de303 460 line[0] = newcols;
4facdf84 461 for (j = 0; j <= newcols; j++)
32874aea 462 line[j + 1] = erase_char;
4facdf84 463 addpos234(newalt, line, i);
464 }
465 if (alt_screen) {
466 while (NULL != (line = delpos234(alt_screen, 0)))
467 sfree(line);
468 freetree234(alt_screen);
469 }
470 alt_screen = newalt;
374330e2 471
32874aea 472 tabs = srealloc(tabs, newcols * sizeof(*tabs));
374330e2 473 {
474 int i;
475 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
476 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
477 }
478
479 if (rows > 0)
4facdf84 480 curs.y += newrows - rows;
481 if (curs.y < 0)
482 curs.y = 0;
483 if (curs.y >= newrows)
32874aea 484 curs.y = newrows - 1;
4facdf84 485 if (curs.x >= newcols)
32874aea 486 curs.x = newcols - 1;
374330e2 487 alt_x = alt_y = 0;
488 wrapnext = alt_wnext = FALSE;
489
490 rows = newrows;
491 cols = newcols;
492 savelines = newsavelines;
493 fix_cpos;
494
d6832394 495 swap_screen(save_alt_which);
496
374330e2 497 update_sbar();
498 term_update();
499}
500
501/*
502 * Swap screens.
503 */
32874aea 504static void swap_screen(int which)
505{
374330e2 506 int t;
4facdf84 507 tree234 *ttr;
374330e2 508
509 if (which == alt_which)
510 return;
511
512 alt_which = which;
513
32874aea 514 ttr = alt_screen;
515 alt_screen = screen;
516 screen = ttr;
517 t = curs.x;
518 curs.x = alt_x;
519 alt_x = t;
520 t = curs.y;
521 curs.y = alt_y;
522 alt_y = t;
523 t = marg_t;
524 marg_t = alt_t;
525 alt_t = t;
526 t = marg_b;
527 marg_b = alt_b;
528 alt_b = t;
529 t = dec_om;
530 dec_om = alt_om;
531 alt_om = t;
532 t = wrap;
533 wrap = alt_wrap;
534 alt_wrap = t;
535 t = wrapnext;
536 wrapnext = alt_wnext;
537 alt_wnext = t;
538 t = insert;
539 insert = alt_ins;
540 alt_ins = t;
541 t = cset;
542 cset = alt_cset;
543 alt_cset = t;
8f22582c 544 t = utf;
545 utf = alt_utf;
546 alt_utf = t;
d3cb5465 547 t = sco_acs;
548 sco_acs = alt_sco_acs;
549 alt_sco_acs = t;
374330e2 550
551 fix_cpos;
552}
553
554/*
374330e2 555 * Update the scroll bar.
556 */
32874aea 557static void update_sbar(void)
558{
260f3dec 559 int nscroll;
374330e2 560
4facdf84 561 nscroll = count234(scrollback);
562
32874aea 563 set_sbar(nscroll + rows, nscroll + disptop, rows);
374330e2 564}
565
566/*
567 * Check whether the region bounded by the two pointers intersects
568 * the scroll region, and de-select the on-screen selection if so.
569 */
32874aea 570static void check_selection(pos from, pos to)
571{
4facdf84 572 if (poslt(from, selend) && poslt(selstart, to))
374330e2 573 deselect();
574}
575
576/*
577 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
578 * for backward.) `sb' is TRUE if the scrolling is permitted to
579 * affect the scrollback buffer.
a4450583 580 *
581 * NB this function invalidates all pointers into lines of the
582 * screen data structures. In particular, you MUST call fix_cpos
583 * after calling scroll() and before doing anything else that
584 * uses the cpos shortcut pointer.
374330e2 585 */
32874aea 586static void scroll(int topline, int botline, int lines, int sb)
587{
4facdf84 588 unsigned long *line, *line2;
589 int i;
c9def1b8 590
7af9fa7f 591 if (topline != 0 || alt_which != 0)
4facdf84 592 sb = FALSE;
593
594 if (lines < 0) {
595 while (lines < 0) {
596 line = delpos234(screen, botline);
2371caac 597 line = resizeline(line, cols);
4facdf84 598 for (i = 0; i < cols; i++)
32874aea 599 line[i + 1] = erase_char;
600 line[cols + 1] = 0;
4facdf84 601 addpos234(screen, line, topline);
602
603 if (selstart.y >= topline && selstart.y <= botline) {
604 selstart.y++;
605 if (selstart.y > botline) {
606 selstart.y = botline;
607 selstart.x = 0;
c9def1b8 608 }
c9def1b8 609 }
4facdf84 610 if (selend.y >= topline && selend.y <= botline) {
611 selend.y++;
612 if (selend.y > botline) {
613 selend.y = botline;
614 selend.x = 0;
c9def1b8 615 }
c9def1b8 616 }
c9def1b8 617
4facdf84 618 lines++;
c9def1b8 619 }
374330e2 620 } else {
4facdf84 621 while (lines > 0) {
622 line = delpos234(screen, topline);
c83de303 623 if (sb && savelines > 0) {
4facdf84 624 int sblen = count234(scrollback);
625 /*
626 * We must add this line to the scrollback. We'll
627 * remove a line from the top of the scrollback to
628 * replace it, or allocate a new one if the
629 * scrollback isn't full.
630 */
c83de303 631 if (sblen == savelines) {
4facdf84 632 sblen--, line2 = delpos234(scrollback, 0);
c83de303 633 } else {
32874aea 634 line2 = smalloc(TSIZE * (cols + 2));
c83de303 635 line2[0] = cols;
636 }
4facdf84 637 addpos234(scrollback, line, sblen);
638 line = line2;
639 }
2371caac 640 line = resizeline(line, cols);
4facdf84 641 for (i = 0; i < cols; i++)
32874aea 642 line[i + 1] = erase_char;
643 line[cols + 1] = 0;
4facdf84 644 addpos234(screen, line, botline);
645
646 if (selstart.y >= topline && selstart.y <= botline) {
647 selstart.y--;
648 if (selstart.y < topline) {
649 selstart.y = topline;
650 selstart.x = 0;
651 }
652 }
653 if (selend.y >= topline && selend.y <= botline) {
654 selend.y--;
655 if (selend.y < topline) {
656 selend.y = topline;
657 selend.x = 0;
658 }
659 }
660
661 lines--;
c9def1b8 662 }
374330e2 663 }
374330e2 664}
665
666/*
667 * Move the cursor to a given position, clipping at boundaries. We
668 * may or may not want to clip at the scroll margin: marg_clip is 0
669 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
670 * even _being_ outside the margins.
671 */
32874aea 672static void move(int x, int y, int marg_clip)
673{
374330e2 674 if (x < 0)
675 x = 0;
676 if (x >= cols)
32874aea 677 x = cols - 1;
374330e2 678 if (marg_clip) {
4facdf84 679 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
374330e2 680 y = marg_t;
4facdf84 681 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
374330e2 682 y = marg_b;
683 }
684 if (y < 0)
685 y = 0;
686 if (y >= rows)
32874aea 687 y = rows - 1;
4facdf84 688 curs.x = x;
689 curs.y = y;
374330e2 690 fix_cpos;
691 wrapnext = FALSE;
692}
693
694/*
695 * Save or restore the cursor and SGR mode.
696 */
32874aea 697static void save_cursor(int save)
698{
374330e2 699 if (save) {
4facdf84 700 savecurs = curs;
374330e2 701 save_attr = curr_attr;
702 save_cset = cset;
8f22582c 703 save_utf = utf;
374330e2 704 save_csattr = cset_attr[cset];
d3cb5465 705 save_sco_acs = sco_acs;
374330e2 706 } else {
4facdf84 707 curs = savecurs;
c9def1b8 708 /* Make sure the window hasn't shrunk since the save */
32874aea 709 if (curs.x >= cols)
710 curs.x = cols - 1;
711 if (curs.y >= rows)
712 curs.y = rows - 1;
c9def1b8 713
374330e2 714 curr_attr = save_attr;
715 cset = save_cset;
8f22582c 716 utf = save_utf;
374330e2 717 cset_attr[cset] = save_csattr;
d3cb5465 718 sco_acs = save_sco_acs;
374330e2 719 fix_cpos;
32874aea 720 if (use_bce)
8f22582c 721 erase_char = (' ' | ATTR_ASCII |
722 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
374330e2 723 }
724}
725
726/*
727 * Erase a large portion of the screen: the whole screen, or the
728 * whole line, or parts thereof.
729 */
32874aea 730static void erase_lots(int line_only, int from_begin, int to_end)
731{
260f3dec 732 pos start, end;
4facdf84 733 int erase_lattr;
734 unsigned long *ldata;
374330e2 735
736 if (line_only) {
4facdf84 737 start.y = curs.y;
738 start.x = 0;
739 end.y = curs.y + 1;
740 end.x = 0;
741 erase_lattr = FALSE;
374330e2 742 } else {
4facdf84 743 start.y = 0;
744 start.x = 0;
745 end.y = rows;
746 end.x = 0;
747 erase_lattr = TRUE;
748 }
749 if (!from_begin) {
750 start = curs;
374330e2 751 }
4facdf84 752 if (!to_end) {
753 end = curs;
2a6dfe0e 754 incpos(end);
4facdf84 755 }
32874aea 756 check_selection(start, end);
e14a5a13 757
758 /* Clear screen also forces a full window redraw, just in case. */
4facdf84 759 if (start.y == 0 && start.x == 0 && end.y == rows)
32874aea 760 term_invalidate();
e14a5a13 761
4facdf84 762 ldata = lineptr(start.y);
763 while (poslt(start, end)) {
cfbeddf3 764 if (start.x == cols && !erase_lattr)
4eeb7d09 765 ldata[start.x] &= ~LATTR_WRAPPED;
4facdf84 766 else
767 ldata[start.x] = erase_char;
c83de303 768 if (incpos(start) && start.y < rows)
4facdf84 769 ldata = lineptr(start.y);
770 }
374330e2 771}
772
773/*
774 * Insert or delete characters within the current line. n is +ve if
775 * insertion is desired, and -ve for deletion.
776 */
32874aea 777static void insch(int n)
778{
374330e2 779 int dir = (n < 0 ? -1 : +1);
780 int m;
4facdf84 781 pos cursplus;
782 unsigned long *ldata;
374330e2 783
784 n = (n < 0 ? -n : n);
4facdf84 785 if (n > cols - curs.x)
786 n = cols - curs.x;
787 m = cols - curs.x - n;
788 cursplus.y = curs.y;
789 cursplus.x = curs.x + n;
32874aea 790 check_selection(curs, cursplus);
4facdf84 791 ldata = lineptr(curs.y);
374330e2 792 if (dir < 0) {
32874aea 793 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
374330e2 794 while (n--)
4facdf84 795 ldata[curs.x + m++] = erase_char;
374330e2 796 } else {
32874aea 797 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
374330e2 798 while (n--)
4facdf84 799 ldata[curs.x + n] = erase_char;
374330e2 800 }
801}
802
803/*
804 * Toggle terminal mode `mode' to state `state'. (`query' indicates
805 * whether the mode is a DEC private one or a normal one.)
806 */
32874aea 807static void toggle_mode(int mode, int query, int state)
808{
7594731b 809 long ticks;
01c034ad 810
32874aea 811 if (query)
812 switch (mode) {
813 case 1: /* application cursor keys */
814 app_cursor_keys = state;
815 break;
816 case 2: /* VT52 mode */
817 vt52_mode = !state;
4eeb7d09 818 if (vt52_mode) {
819 blink_is_real = FALSE;
820 vt52_bold = FALSE;
821 } else {
822 blink_is_real = cfg.blinktext;
823 }
32874aea 824 break;
825 case 3: /* 80/132 columns */
826 deselect();
827 request_resize(state ? 132 : 80, rows, 1);
828 reset_132 = state;
829 break;
830 case 5: /* reverse video */
831 /*
832 * Toggle reverse video. If we receive an OFF within the
833 * visual bell timeout period after an ON, we trigger an
834 * effective visual bell, so that ESC[?5hESC[?5l will
835 * always be an actually _visible_ visual bell.
836 */
837 ticks = GetTickCount();
838 if (rvideo && !state && /* we're turning it off */
839 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
840 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
841 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
842 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
843 } else if (!rvideo && state) {
844 /* This is an ON, so we notice the time and save it. */
845 rvbell_timeout = ticks + VBELL_TIMEOUT;
846 }
847 rvideo = state;
848 seen_disp_event = TRUE;
849 if (state)
850 term_update();
851 break;
852 case 6: /* DEC origin mode */
853 dec_om = state;
854 break;
855 case 7: /* auto wrap */
856 wrap = state;
857 break;
858 case 8: /* auto key repeat */
859 repeat_off = !state;
860 break;
861 case 10: /* set local edit mode */
862 term_editing = state;
863 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
864 break;
865 case 25: /* enable/disable cursor */
866 compatibility2(OTHER, VT220);
867 cursor_on = state;
868 seen_disp_event = TRUE;
869 break;
870 case 47: /* alternate screen */
871 compatibility(OTHER);
872 deselect();
873 swap_screen(state);
874 disptop = 0;
875 break;
876 case 1000: /* xterm mouse 1 */
877 xterm_mouse = state ? 1 : 0;
878 set_raw_mouse_mode(state);
879 break;
880 case 1002: /* xterm mouse 2 */
881 xterm_mouse = state ? 2 : 0;
882 set_raw_mouse_mode(state);
883 break;
884 } else
885 switch (mode) {
886 case 4: /* set insert mode */
887 compatibility(VT102);
888 insert = state;
889 break;
890 case 12: /* set echo mode */
891 term_echoing = !state;
892 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
893 break;
894 case 20: /* Return sends ... */
895 cr_lf_return = state;
896 break;
4eeb7d09 897 case 34: /* Make cursor BIG */
898 compatibility2(OTHER, VT220);
899 big_cursor = !state;
7594731b 900 }
374330e2 901}
902
903/*
904 * Process an OSC sequence: set window title or icon name.
905 */
32874aea 906static void do_osc(void)
907{
374330e2 908 if (osc_w) {
909 while (osc_strlen--)
32874aea 910 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
374330e2 911 } else {
912 osc_string[osc_strlen] = '\0';
913 switch (esc_args[0]) {
914 case 0:
915 case 1:
32874aea 916 set_icon(osc_string);
374330e2 917 if (esc_args[0] == 1)
918 break;
919 /* fall through: parameter 0 means set both */
920 case 2:
921 case 21:
32874aea 922 set_title(osc_string);
374330e2 923 break;
924 }
925 }
926}
927
928/*
929 * Remove everything currently in `inbuf' and stick it up on the
930 * in-memory display. There's a big state machine in here to
931 * process escape sequences...
932 */
32874aea 933void term_out(void)
934{
c9def1b8 935 int c, inbuf_reap;
936
5a16642f 937 /*
938 * Optionally log the session traffic to a file. Useful for
939 * debugging and possibly also useful for actual logging.
940 */
941 if (cfg.logtype == LGTYP_DEBUG)
942 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
943 logtraffic((unsigned char) inbuf[inbuf_reap], LGTYP_DEBUG);
944 }
945
32874aea 946 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
947 c = inbuf[inbuf_reap];
374330e2 948
e14a5a13 949 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
950 * be able to display 8-bit characters, but I'll let that go 'cause
951 * of i18n.
952 */
4eeb7d09 953
954 /* First see about all those translations. */
955 if (termstate == TOPLEVEL) {
8f22582c 956 if (in_utf)
4eeb7d09 957 switch (utf_state) {
958 case 0:
959 if (c < 0x80) {
8f22582c 960 /* UTF-8 must be stateless so we ignore iso2022. */
961 if (unitab_ctrl[c] != 0xFF)
962 c = unitab_ctrl[c];
963 else c = ((unsigned char)c) | ATTR_ASCII;
964 break;
4eeb7d09 965 } else if ((c & 0xe0) == 0xc0) {
966 utf_size = utf_state = 1;
967 utf_char = (c & 0x1f);
968 } else if ((c & 0xf0) == 0xe0) {
969 utf_size = utf_state = 2;
970 utf_char = (c & 0x0f);
971 } else if ((c & 0xf8) == 0xf0) {
972 utf_size = utf_state = 3;
973 utf_char = (c & 0x07);
974 } else if ((c & 0xfc) == 0xf8) {
975 utf_size = utf_state = 4;
976 utf_char = (c & 0x03);
977 } else if ((c & 0xfe) == 0xfc) {
978 utf_size = utf_state = 5;
979 utf_char = (c & 0x01);
980 } else {
981 c = UCSERR;
982 break;
983 }
984 continue;
985 case 1:
986 case 2:
987 case 3:
988 case 4:
989 case 5:
990 if ((c & 0xC0) != 0x80) {
5a16642f 991 inbuf_reap--;
992 c = UCSERR;
993 utf_state = 0;
4eeb7d09 994 break;
995 }
996 utf_char = (utf_char << 6) | (c & 0x3f);
997 if (--utf_state)
998 continue;
999
1000 c = utf_char;
1001
1002 /* Is somebody trying to be evil! */
1003 if (c < 0x80 ||
1004 (c < 0x800 && utf_size >= 2) ||
1005 (c < 0x10000 && utf_size >= 3) ||
1006 (c < 0x200000 && utf_size >= 4) ||
1007 (c < 0x4000000 && utf_size >= 5))
1008 c = UCSERR;
1009
1010 /* Unicode line separator and paragraph separator are CR-LF */
1011 if (c == 0x2028 || c == 0x2029)
1012 c = 0x85;
1013
1014 /* High controls are probably a Baaad idea too. */
1015 if (c < 0xA0)
1016 c = 0xFFFD;
1017
1018 /* The UTF-16 surrogates are not nice either. */
1019 /* The standard give the option of decoding these:
1020 * I don't want to! */
1021 if (c >= 0xD800 && c < 0xE000)
1022 c = UCSERR;
1023
1024 /* ISO 10646 characters now limited to UTF-16 range. */
1025 if (c > 0x10FFFF)
1026 c = UCSERR;
1027
1028 /* This is currently a TagPhobic application.. */
1029 if (c >= 0xE0000 && c <= 0xE007F)
1030 continue;
1031
1032 /* U+FEFF is best seen as a null. */
1033 if (c == 0xFEFF)
1034 continue;
1035 /* But U+FFFE is an error. */
1036 if (c == 0xFFFE || c == 0xFFFF)
1037 c = UCSERR;
1038
1039 /* Oops this is a 16bit implementation */
1040 if (c >= 0x10000)
1041 c = 0xFFFD;
1042 break;
d3cb5465 1043 }
1044 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1045 else if(sco_acs &&
1046 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1047 {
1048 if (sco_acs == 2) c ^= 0x80;
1049 c |= ATTR_SCOACS;
4eeb7d09 1050 } else {
4eeb7d09 1051 switch (cset_attr[cset]) {
1052 /*
1053 * Linedraw characters are different from 'ESC ( B'
1054 * only for a small range. For ones outside that
1055 * range, make sure we use the same font as well as
1056 * the same encoding.
1057 */
1058 case ATTR_LINEDRW:
1059 if (unitab_ctrl[c] != 0xFF)
1060 c = unitab_ctrl[c];
1061 else
1062 c = ((unsigned char) c) | ATTR_LINEDRW;
1063 break;
1064
1065 case ATTR_GBCHR:
1066 /* If UK-ASCII, make the '#' a LineDraw Pound */
1067 if (c == '#') {
1068 c = '}' | ATTR_LINEDRW;
1069 break;
1070 }
1071 /*FALLTHROUGH*/ case ATTR_ASCII:
1072 if (unitab_ctrl[c] != 0xFF)
1073 c = unitab_ctrl[c];
1074 else
1075 c = ((unsigned char) c) | ATTR_ASCII;
1076 break;
d3cb5465 1077 case ATTR_SCOACS:
1078 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1079 break;
4eeb7d09 1080 }
1081 }
1082 }
1083
1084 /* How about C1 controls ? */
1085 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1086 has_compat(VT220)) {
1087 termstate = SEEN_ESC;
1088 esc_query = FALSE;
1089 c = '@' + (c & 0x1F);
1090 }
1091
1092 /* Or the GL control. */
1093 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1094 if (curs.x && !wrapnext)
1095 curs.x--;
1096 wrapnext = FALSE;
1097 fix_cpos;
1098 *cpos = (' ' | curr_attr | ATTR_ASCII);
1099 } else
1100 /* Or normal C0 controls. */
1101 if ((c & -32) == 0 && termstate < DO_CTRLS) {
374330e2 1102 switch (c) {
1103 case '\005': /* terminal type query */
e14a5a13 1104 /* Strictly speaking this is VT100 but a VT100 defaults to
1105 * no response. Other terminals respond at their option.
1106 *
1107 * Don't put a CR in the default string as this tends to
1108 * upset some weird software.
1109 *
1110 * An xterm returns "xterm" (5 characters)
1111 */
32874aea 1112 compatibility(ANSIMIN);
e7fbcdd8 1113 {
1114 char abuf[256], *s, *d;
32874aea 1115 int state = 0;
1116 for (s = cfg.answerback, d = abuf; *s; s++) {
1117 if (state) {
1118 if (*s >= 'a' && *s <= 'z')
1119 *d++ = (*s - ('a' - 1));
1120 else if ((*s >= '@' && *s <= '_') ||
1121 *s == '?' || (*s & 0x80))
1122 *d++ = ('@' ^ *s);
1123 else if (*s == '~')
1124 *d++ = '^';
1125 state = 0;
1126 } else if (*s == '^') {
1127 state = 1;
1128 } else
4eeb7d09 1129 *d++ = *s;
e7fbcdd8 1130 }
4eeb7d09 1131 lpage_send(CP_ACP, abuf, d - abuf);
e7fbcdd8 1132 }
374330e2 1133 break;
1134 case '\007':
156686ef 1135 {
1136 struct beeptime *newbeep;
1137 long ticks;
1138
1139 ticks = GetTickCount();
156686ef 1140
1141 if (!beep_overloaded) {
1142 newbeep = smalloc(sizeof(struct beeptime));
1143 newbeep->ticks = ticks;
1144 newbeep->next = NULL;
1145 if (!beephead)
1146 beephead = newbeep;
1147 else
1148 beeptail->next = newbeep;
1149 beeptail = newbeep;
1150 nbeeps++;
1151 }
1152
1153 /*
1154 * Throw out any beeps that happened more than
1155 * t seconds ago.
1156 */
1157 while (beephead &&
7f4968e6 1158 beephead->ticks < ticks - cfg.bellovl_t) {
156686ef 1159 struct beeptime *tmp = beephead;
1160 beephead = tmp->next;
156686ef 1161 sfree(tmp);
1162 if (!beephead)
1163 beeptail = NULL;
1164 nbeeps--;
1165 }
1166
1167 if (cfg.bellovl && beep_overloaded &&
32874aea 1168 ticks - lastbeep >= cfg.bellovl_s) {
156686ef 1169 /*
1170 * If we're currently overloaded and the
1171 * last beep was more than s seconds ago,
1172 * leave overload mode.
1173 */
156686ef 1174 beep_overloaded = FALSE;
1175 } else if (cfg.bellovl && !beep_overloaded &&
1176 nbeeps >= cfg.bellovl_n) {
1177 /*
1178 * Now, if we have n or more beeps
1179 * remaining in the queue, go into overload
1180 * mode.
1181 */
156686ef 1182 beep_overloaded = TRUE;
1183 }
1184 lastbeep = ticks;
1185
1186 /*
1187 * Perform an actual beep if we're not overloaded.
1188 */
f8a28d1f 1189 if (!cfg.bellovl || !beep_overloaded) {
1190 beep(cfg.beep);
1191 if (cfg.beep == BELL_VISUAL) {
156686ef 1192 in_vbell = TRUE;
1193 vbell_timeout = ticks + VBELL_TIMEOUT;
1194 term_update();
1195 }
1196 }
4facdf84 1197 disptop = 0;
156686ef 1198 }
374330e2 1199 break;
1200 case '\b':
967a161e 1201 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
4facdf84 1202 else if (curs.x == 0 && curs.y > 0)
32874aea 1203 curs.x = cols - 1, curs.y--;
374330e2 1204 else if (wrapnext)
1205 wrapnext = FALSE;
1206 else
4facdf84 1207 curs.x--;
374330e2 1208 fix_cpos;
cabfd08c 1209 seen_disp_event = TRUE;
374330e2 1210 break;
1211 case '\016':
32874aea 1212 compatibility(VT100);
374330e2 1213 cset = 1;
1214 break;
1215 case '\017':
32874aea 1216 compatibility(VT100);
374330e2 1217 cset = 0;
1218 break;
1219 case '\033':
32874aea 1220 if (vt52_mode)
1221 termstate = VT52_ESC;
e14a5a13 1222 else {
1223 compatibility(ANSIMIN);
1224 termstate = SEEN_ESC;
4eeb7d09 1225 esc_query = FALSE;
e14a5a13 1226 }
374330e2 1227 break;
374330e2 1228 case '\r':
4facdf84 1229 curs.x = 0;
374330e2 1230 wrapnext = FALSE;
1231 fix_cpos;
cabfd08c 1232 seen_disp_event = TRUE;
c9def1b8 1233 paste_hold = 0;
32874aea 1234 logtraffic((unsigned char) c, LGTYP_ASCII);
374330e2 1235 break;
374330e2 1236 case '\014':
1a837633 1237 if (has_compat(SCOANSI)) {
1238 move(0, 0, 0);
1239 erase_lots(FALSE, FALSE, TRUE);
1240 disptop = 0;
1241 wrapnext = FALSE;
1242 seen_disp_event = 1;
1243 break;
1244 }
1245 case '\013':
32874aea 1246 compatibility(VT100);
374330e2 1247 case '\n':
4facdf84 1248 if (curs.y == marg_b)
32874aea 1249 scroll(marg_t, marg_b, 1, TRUE);
1250 else if (curs.y < rows - 1)
4facdf84 1251 curs.y++;
cabfd08c 1252 if (cfg.lfhascr)
4facdf84 1253 curs.x = 0;
374330e2 1254 fix_cpos;
1255 wrapnext = FALSE;
cabfd08c 1256 seen_disp_event = 1;
c9def1b8 1257 paste_hold = 0;
32874aea 1258 logtraffic((unsigned char) c, LGTYP_ASCII);
374330e2 1259 break;
1260 case '\t':
374330e2 1261 {
4facdf84 1262 pos old_curs = curs;
1263 unsigned long *ldata = lineptr(curs.y);
c9def1b8 1264
1265 do {
4facdf84 1266 curs.x++;
32874aea 1267 } while (curs.x < cols - 1 && !tabs[curs.x]);
c9def1b8 1268
32874aea 1269 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1270 if (curs.x >= cols / 2)
1271 curs.x = cols / 2 - 1;
1272 } else {
4facdf84 1273 if (curs.x >= cols)
32874aea 1274 curs.x = cols - 1;
c9def1b8 1275 }
1276
374330e2 1277 fix_cpos;
32874aea 1278 check_selection(old_curs, curs);
374330e2 1279 }
cabfd08c 1280 seen_disp_event = TRUE;
374330e2 1281 break;
cabfd08c 1282 }
32874aea 1283 } else
1284 switch (termstate) {
1285 case TOPLEVEL:
1286 /* Only graphic characters get this far, ctrls are stripped above */
1287 if (wrapnext && wrap) {
4eeb7d09 1288 cpos[1] |= LATTR_WRAPPED;
32874aea 1289 if (curs.y == marg_b)
1290 scroll(marg_t, marg_b, 1, TRUE);
1291 else if (curs.y < rows - 1)
1292 curs.y++;
1293 curs.x = 0;
1294 fix_cpos;
1295 wrapnext = FALSE;
374330e2 1296 }
32874aea 1297 if (insert)
1298 insch(1);
1299 if (selstate != NO_SELECTION) {
1300 pos cursplus = curs;
1301 incpos(cursplus);
1302 check_selection(curs, cursplus);
374330e2 1303 }
4eeb7d09 1304 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1305 logtraffic((unsigned char) c, LGTYP_ASCII);
1306 {
1307 extern int wcwidth(wchar_t ucs);
1308 int width = 0;
1309 if (DIRECT_CHAR(c))
1310 width = 1;
1311 if (!width)
1312 width = wcwidth((wchar_t) c);
1313 switch (width) {
1314 case 2:
1315 if (curs.x + 1 != cols) {
1316 *cpos++ = c | ATTR_WIDE | curr_attr;
1317 *cpos++ = UCSWIDE | curr_attr;
1318 curs.x++;
1319 break;
1320 }
1321 case 1:
1322 *cpos++ = c | curr_attr;
32874aea 1323 break;
4eeb7d09 1324 default:
1325 continue;
32874aea 1326 }
374330e2 1327 }
32874aea 1328 curs.x++;
1329 if (curs.x == cols) {
1330 cpos--;
1331 curs.x--;
1332 wrapnext = TRUE;
4eeb7d09 1333 if (wrap && vt52_mode) {
1334 cpos[1] |= LATTR_WRAPPED;
1335 if (curs.y == marg_b)
1336 scroll(marg_t, marg_b, 1, TRUE);
1337 else if (curs.y < rows - 1)
1338 curs.y++;
1339 curs.x = 0;
1340 fix_cpos;
1341 wrapnext = FALSE;
1342 }
e14a5a13 1343 }
32874aea 1344 seen_disp_event = 1;
374330e2 1345 break;
32874aea 1346
32874aea 1347 case OSC_MAYBE_ST:
1348 /*
1349 * This state is virtually identical to SEEN_ESC, with the
1350 * exception that we have an OSC sequence in the pipeline,
1351 * and _if_ we see a backslash, we process it.
1352 */
1353 if (c == '\\') {
1354 do_osc();
1355 termstate = TOPLEVEL;
1356 break;
e14a5a13 1357 }
32874aea 1358 /* else fall through */
1359 case SEEN_ESC:
4eeb7d09 1360 if (c >= ' ' && c <= '/') {
1361 if (esc_query)
1362 esc_query = -1;
1363 else
1364 esc_query = c;
32874aea 1365 break;
4eeb7d09 1366 }
1367 termstate = TOPLEVEL;
1368 switch (ANSI(c, esc_query)) {
32874aea 1369 case '[': /* enter CSI mode */
1370 termstate = SEEN_CSI;
1371 esc_nargs = 1;
1372 esc_args[0] = ARG_DEFAULT;
1373 esc_query = FALSE;
1374 break;
1375 case ']': /* xterm escape sequences */
1376 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1377 compatibility(OTHER);
1378 termstate = SEEN_OSC;
1379 esc_args[0] = 0;
1380 break;
32874aea 1381 case '7': /* save cursor */
1382 compatibility(VT100);
1383 save_cursor(TRUE);
1384 break;
1385 case '8': /* restore cursor */
1386 compatibility(VT100);
1387 save_cursor(FALSE);
1388 seen_disp_event = TRUE;
1389 break;
1390 case '=':
1391 compatibility(VT100);
1392 app_keypad_keys = TRUE;
1393 break;
1394 case '>':
1395 compatibility(VT100);
1396 app_keypad_keys = FALSE;
1397 break;
1398 case 'D': /* exactly equivalent to LF */
1399 compatibility(VT100);
1400 if (curs.y == marg_b)
1401 scroll(marg_t, marg_b, 1, TRUE);
1402 else if (curs.y < rows - 1)
1403 curs.y++;
1404 fix_cpos;
1405 wrapnext = FALSE;
1406 seen_disp_event = TRUE;
1407 break;
1408 case 'E': /* exactly equivalent to CR-LF */
1409 compatibility(VT100);
1410 curs.x = 0;
1411 if (curs.y == marg_b)
1412 scroll(marg_t, marg_b, 1, TRUE);
1413 else if (curs.y < rows - 1)
1414 curs.y++;
1415 fix_cpos;
1416 wrapnext = FALSE;
1417 seen_disp_event = TRUE;
1418 break;
1419 case 'M': /* reverse index - backwards LF */
1420 compatibility(VT100);
1421 if (curs.y == marg_t)
1422 scroll(marg_t, marg_b, -1, TRUE);
1423 else if (curs.y > 0)
1424 curs.y--;
1425 fix_cpos;
1426 wrapnext = FALSE;
1427 seen_disp_event = TRUE;
1428 break;
1429 case 'Z': /* terminal type query */
1430 compatibility(VT100);
1431 ldisc_send(id_string, strlen(id_string));
1432 break;
1433 case 'c': /* restore power-on settings */
1434 compatibility(VT100);
1435 power_on();
1436 if (reset_132) {
1437 request_resize(80, rows, 1);
1438 reset_132 = 0;
374330e2 1439 }
32874aea 1440 fix_cpos;
1441 disptop = 0;
1442 seen_disp_event = TRUE;
1443 break;
32874aea 1444 case 'H': /* set a tab */
1445 compatibility(VT100);
1446 tabs[curs.x] = TRUE;
1447 break;
4eeb7d09 1448
1449 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1450 compatibility(VT100);
1451 {
1452 unsigned long *ldata;
1453 int i, j;
1454 pos scrtop, scrbot;
1455
1456 for (i = 0; i < rows; i++) {
1457 ldata = lineptr(i);
1458 for (j = 0; j < cols; j++)
1459 ldata[j] = ATTR_DEFAULT | 'E';
1460 ldata[cols] = 0;
1461 }
1462 disptop = 0;
1463 seen_disp_event = TRUE;
1464 scrtop.x = scrtop.y = 0;
1465 scrbot.x = 0;
1466 scrbot.y = rows;
1467 check_selection(scrtop, scrbot);
1468 }
1469 break;
1470
1471 case ANSI('3', '#'):
1472 case ANSI('4', '#'):
1473 case ANSI('5', '#'):
1474 case ANSI('6', '#'):
1475 compatibility(VT100);
1476 {
1477 unsigned long nlattr;
1478 unsigned long *ldata;
1479 switch (ANSI(c, esc_query)) {
1480 case ANSI('3', '#'):
1481 nlattr = LATTR_TOP;
1482 break;
1483 case ANSI('4', '#'):
1484 nlattr = LATTR_BOT;
1485 break;
1486 case ANSI('5', '#'):
1487 nlattr = LATTR_NORM;
1488 break;
2d466ffd 1489 default: /* spiritually case ANSI('6', '#'): */
4eeb7d09 1490 nlattr = LATTR_WIDE;
1491 break;
1492 }
1493 ldata = lineptr(curs.y);
1494 ldata[cols] &= ~LATTR_MODE;
1495 ldata[cols] |= nlattr;
1496 }
1497 break;
1498
1499 case ANSI('A', '('):
1500 compatibility(VT100);
1501 cset_attr[0] = ATTR_GBCHR;
1502 break;
1503 case ANSI('B', '('):
1504 compatibility(VT100);
1505 cset_attr[0] = ATTR_ASCII;
1506 break;
1507 case ANSI('0', '('):
1508 compatibility(VT100);
1509 cset_attr[0] = ATTR_LINEDRW;
1510 break;
d3cb5465 1511 case ANSI('U', '('):
1512 compatibility(OTHER);
1513 cset_attr[0] = ATTR_SCOACS;
1514 break;
4eeb7d09 1515
1516 case ANSI('A', ')'):
1517 compatibility(VT100);
1518 cset_attr[1] = ATTR_GBCHR;
1519 break;
1520 case ANSI('B', ')'):
1521 compatibility(VT100);
1522 cset_attr[1] = ATTR_ASCII;
1523 break;
1524 case ANSI('0', ')'):
1525 compatibility(VT100);
1526 cset_attr[1] = ATTR_LINEDRW;
1527 break;
d3cb5465 1528 case ANSI('U', ')'):
1529 compatibility(OTHER);
1530 cset_attr[1] = ATTR_SCOACS;
1531 break;
4eeb7d09 1532
1533 case ANSI('8', '%'): /* Old Linux code */
1534 case ANSI('G', '%'):
1535 compatibility(OTHER);
1536 utf = 1;
1537 break;
1538 case ANSI('@', '%'):
1539 compatibility(OTHER);
8f22582c 1540 utf = 0;
4eeb7d09 1541 break;
374330e2 1542 }
1543 break;
32874aea 1544 case SEEN_CSI:
1545 termstate = TOPLEVEL; /* default */
1546 if (isdigit(c)) {
1547 if (esc_nargs <= ARGS_MAX) {
1548 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1549 esc_args[esc_nargs - 1] = 0;
1550 esc_args[esc_nargs - 1] =
1551 10 * esc_args[esc_nargs - 1] + c - '0';
1552 }
1553 termstate = SEEN_CSI;
1554 } else if (c == ';') {
1555 if (++esc_nargs <= ARGS_MAX)
1556 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1557 termstate = SEEN_CSI;
1558 } else if (c < '@') {
1559 if (esc_query)
1560 esc_query = -1;
1561 else if (c == '?')
1562 esc_query = TRUE;
1563 else
1564 esc_query = c;
1565 termstate = SEEN_CSI;
1566 } else
1567 switch (ANSI(c, esc_query)) {
1568 case 'A': /* move up N lines */
1569 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1570 seen_disp_event = TRUE;
1571 break;
1572 case 'e': /* move down N lines */
1573 compatibility(ANSI);
5442e92f 1574 /* FALLTHROUGH */
32874aea 1575 case 'B':
1576 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1577 seen_disp_event = TRUE;
1578 break;
32874aea 1579 case ANSI('c', '>'): /* report xterm version */
1580 compatibility(OTHER);
1581 /* this reports xterm version 136 so that VIM can
1582 use the drag messages from the mouse reporting */
1583 ldisc_send("\033[>0;136;0c", 11);
1584 break;
5442e92f 1585 case 'a': /* move right N cols */
1586 compatibility(ANSI);
1587 /* FALLTHROUGH */
32874aea 1588 case 'C':
1589 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1590 seen_disp_event = TRUE;
1591 break;
1592 case 'D': /* move left N cols */
1593 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1594 seen_disp_event = TRUE;
1595 break;
1596 case 'E': /* move down N lines and CR */
1597 compatibility(ANSI);
1598 move(0, curs.y + def(esc_args[0], 1), 1);
1599 seen_disp_event = TRUE;
1600 break;
1601 case 'F': /* move up N lines and CR */
1602 compatibility(ANSI);
1603 move(0, curs.y - def(esc_args[0], 1), 1);
1604 seen_disp_event = TRUE;
1605 break;
1606 case 'G':
1607 case '`': /* set horizontal posn */
1608 compatibility(ANSI);
1609 move(def(esc_args[0], 1) - 1, curs.y, 0);
1610 seen_disp_event = TRUE;
1611 break;
1612 case 'd': /* set vertical posn */
1613 compatibility(ANSI);
1614 move(curs.x,
1615 (dec_om ? marg_t : 0) + def(esc_args[0],
1616 1) - 1,
1617 (dec_om ? 2 : 0));
1618 seen_disp_event = TRUE;
1619 break;
1620 case 'H':
1621 case 'f': /* set horz and vert posns at once */
1622 if (esc_nargs < 2)
1623 esc_args[1] = ARG_DEFAULT;
1624 move(def(esc_args[1], 1) - 1,
1625 (dec_om ? marg_t : 0) + def(esc_args[0],
1626 1) - 1,
1627 (dec_om ? 2 : 0));
1628 seen_disp_event = TRUE;
1629 break;
1630 case 'J': /* erase screen or parts of it */
1631 {
1632 unsigned int i = def(esc_args[0], 0) + 1;
1633 if (i > 3)
1634 i = 0;
1635 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1636 }
1637 disptop = 0;
1638 seen_disp_event = TRUE;
1639 break;
1640 case 'K': /* erase line or parts of it */
1641 {
1642 unsigned int i = def(esc_args[0], 0) + 1;
1643 if (i > 3)
1644 i = 0;
1645 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1646 }
1647 seen_disp_event = TRUE;
1648 break;
1649 case 'L': /* insert lines */
1650 compatibility(VT102);
1651 if (curs.y <= marg_b)
1652 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1653 FALSE);
1654 fix_cpos;
1655 seen_disp_event = TRUE;
1656 break;
1657 case 'M': /* delete lines */
1658 compatibility(VT102);
1659 if (curs.y <= marg_b)
1660 scroll(curs.y, marg_b, def(esc_args[0], 1),
1661 TRUE);
1662 fix_cpos;
1663 seen_disp_event = TRUE;
1664 break;
1665 case '@': /* insert chars */
1666 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1667 compatibility(VT102);
1668 insch(def(esc_args[0], 1));
1669 seen_disp_event = TRUE;
1670 break;
1671 case 'P': /* delete chars */
1672 compatibility(VT102);
1673 insch(-def(esc_args[0], 1));
1674 seen_disp_event = TRUE;
1675 break;
1676 case 'c': /* terminal type query */
1677 compatibility(VT100);
1678 /* This is the response for a VT102 */
1679 ldisc_send(id_string, strlen(id_string));
1680 break;
1681 case 'n': /* cursor position query */
1682 if (esc_args[0] == 6) {
1683 char buf[32];
1684 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1685 curs.x + 1);
1686 ldisc_send(buf, strlen(buf));
1687 } else if (esc_args[0] == 5) {
1688 ldisc_send("\033[0n", 4);
1689 }
1690 break;
1691 case 'h': /* toggle modes to high */
1692 case ANSI_QUE('h'):
1693 compatibility(VT100);
1694 {
1695 int i;
1696 for (i = 0; i < esc_nargs; i++)
1697 toggle_mode(esc_args[i], esc_query, TRUE);
1698 }
1699 break;
1700 case 'l': /* toggle modes to low */
1701 case ANSI_QUE('l'):
1702 compatibility(VT100);
1703 {
1704 int i;
1705 for (i = 0; i < esc_nargs; i++)
1706 toggle_mode(esc_args[i], esc_query, FALSE);
1707 }
1708 break;
1709 case 'g': /* clear tabs */
1710 compatibility(VT100);
1711 if (esc_nargs == 1) {
1712 if (esc_args[0] == 0) {
1713 tabs[curs.x] = FALSE;
1714 } else if (esc_args[0] == 3) {
1715 int i;
1716 for (i = 0; i < cols; i++)
1717 tabs[i] = FALSE;
1718 }
1719 }
1720 break;
1721 case 'r': /* set scroll margins */
1722 compatibility(VT100);
1723 if (esc_nargs <= 2) {
1724 int top, bot;
1725 top = def(esc_args[0], 1) - 1;
1726 bot = (esc_nargs <= 1
1727 || esc_args[1] ==
1728 0 ? rows : def(esc_args[1], rows)) - 1;
1729 if (bot >= rows)
1730 bot = rows - 1;
1731 /* VTTEST Bug 9 - if region is less than 2 lines
1732 * don't change region.
1733 */
1734 if (bot - top > 0) {
1735 marg_t = top;
1736 marg_b = bot;
1737 curs.x = 0;
1738 /*
1739 * I used to think the cursor should be
1740 * placed at the top of the newly marginned
1741 * area. Apparently not: VMS TPU falls over
1742 * if so.
1743 *
1744 * Well actually it should for Origin mode - RDB
1745 */
1746 curs.y = (dec_om ? marg_t : 0);
1747 fix_cpos;
1748 seen_disp_event = TRUE;
1749 }
1750 }
1751 break;
1752 case 'm': /* set graphics rendition */
1753 {
1754 /*
1755 * A VT100 without the AVO only had one attribute, either
1756 * underline or reverse video depending on the cursor type,
1757 * this was selected by CSI 7m.
1758 *
1759 * case 2:
4eeb7d09 1760 * This is sometimes DIM, eg on the GIGI and Linux
32874aea 1761 * case 8:
4eeb7d09 1762 * This is sometimes INVIS various ANSI.
32874aea 1763 * case 21:
1764 * This like 22 disables BOLD, DIM and INVIS
1765 *
1766 * The ANSI colours appear on any terminal that has colour
1767 * (obviously) but the interaction between sgr0 and the
1768 * colours varies but is usually related to the background
1769 * colour erase item.
1770 * The interaction between colour attributes and the mono
1771 * ones is also very implementation dependent.
1772 *
1773 * The 39 and 49 attributes are likely to be unimplemented.
1774 */
1775 int i;
1776 for (i = 0; i < esc_nargs; i++) {
1777 switch (def(esc_args[i], 0)) {
1778 case 0: /* restore defaults */
1779 curr_attr = ATTR_DEFAULT;
1780 break;
1781 case 1: /* enable bold */
1782 compatibility(VT100AVO);
1783 curr_attr |= ATTR_BOLD;
1784 break;
1785 case 21: /* (enable double underline) */
1786 compatibility(OTHER);
1787 case 4: /* enable underline */
1788 compatibility(VT100AVO);
1789 curr_attr |= ATTR_UNDER;
1790 break;
1791 case 5: /* enable blink */
1792 compatibility(VT100AVO);
1793 curr_attr |= ATTR_BLINK;
1794 break;
1795 case 7: /* enable reverse video */
1796 curr_attr |= ATTR_REVERSE;
1797 break;
d3cb5465 1798 case 10: /* SCO acs off */
1799 compatibility(SCOANSI);
1800 sco_acs = 0; break;
1801 case 11: /* SCO acs on */
1802 compatibility(SCOANSI);
1803 sco_acs = 1; break;
1804 case 12: /* SCO acs on flipped */
1805 compatibility(SCOANSI);
1806 sco_acs = 2; break;
32874aea 1807 case 22: /* disable bold */
1808 compatibility2(OTHER, VT220);
1809 curr_attr &= ~ATTR_BOLD;
1810 break;
1811 case 24: /* disable underline */
1812 compatibility2(OTHER, VT220);
1813 curr_attr &= ~ATTR_UNDER;
1814 break;
1815 case 25: /* disable blink */
1816 compatibility2(OTHER, VT220);
1817 curr_attr &= ~ATTR_BLINK;
1818 break;
1819 case 27: /* disable reverse video */
1820 compatibility2(OTHER, VT220);
1821 curr_attr &= ~ATTR_REVERSE;
1822 break;
1823 case 30:
1824 case 31:
1825 case 32:
1826 case 33:
1827 case 34:
1828 case 35:
1829 case 36:
1830 case 37:
1831 /* foreground */
1832 curr_attr &= ~ATTR_FGMASK;
1833 curr_attr |=
1834 (esc_args[i] - 30) << ATTR_FGSHIFT;
1835 break;
1836 case 39: /* default-foreground */
1837 curr_attr &= ~ATTR_FGMASK;
1838 curr_attr |= ATTR_DEFFG;
1839 break;
1840 case 40:
1841 case 41:
1842 case 42:
1843 case 43:
1844 case 44:
1845 case 45:
1846 case 46:
1847 case 47:
1848 /* background */
1849 curr_attr &= ~ATTR_BGMASK;
1850 curr_attr |=
1851 (esc_args[i] - 40) << ATTR_BGSHIFT;
1852 break;
1853 case 49: /* default-background */
1854 curr_attr &= ~ATTR_BGMASK;
1855 curr_attr |= ATTR_DEFBG;
1856 break;
1857 }
1858 }
1859 if (use_bce)
8f22582c 1860 erase_char = (' ' | ATTR_ASCII |
1861 (curr_attr &
1862 (ATTR_FGMASK | ATTR_BGMASK)));
32874aea 1863 }
1864 break;
1865 case 's': /* save cursor */
1866 save_cursor(TRUE);
1867 break;
1868 case 'u': /* restore cursor */
1869 save_cursor(FALSE);
1870 seen_disp_event = TRUE;
1871 break;
1872 case 't': /* set page size - ie window height */
374330e2 1873 /*
32874aea 1874 * VT340/VT420 sequence DECSLPP, DEC only allows values
1875 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1876 * illegal values (eg first arg 1..9) for window changing
1877 * and reports.
1878 */
1879 compatibility(VT340TEXT);
1880 if (esc_nargs <= 1
1881 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1882 request_resize(cols, def(esc_args[0], 24), 0);
1883 deselect();
1884 }
1885 break;
1a837633 1886 case 'S':
1887 compatibility(SCOANSI);
1888 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1889 fix_cpos;
1890 wrapnext = FALSE;
1891 seen_disp_event = TRUE;
1892 break;
1893 case 'T':
1894 compatibility(SCOANSI);
1895 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1896 fix_cpos;
1897 wrapnext = FALSE;
1898 seen_disp_event = TRUE;
1899 break;
32874aea 1900 case ANSI('|', '*'):
1901 /* VT420 sequence DECSNLS
1902 * Set number of lines on screen
1903 * VT420 uses VGA like hardware and can support any size in
1904 * reasonable range (24..49 AIUI) with no default specified.
1905 */
1906 compatibility(VT420);
1907 if (esc_nargs == 1 && esc_args[0] > 0) {
1908 request_resize(cols,
1909 def(esc_args[0], cfg.height),
1910 0);
1911 deselect();
1912 }
1913 break;
1914 case ANSI('|', '$'):
1915 /* VT340/VT420 sequence DECSCPP
1916 * Set number of columns per page
1917 * Docs imply range is only 80 or 132, but I'll allow any.
1918 */
1919 compatibility(VT340TEXT);
1920 if (esc_nargs <= 1) {
1921 request_resize(def(esc_args[0], cfg.width),
1922 rows, 0);
1923 deselect();
1924 }
1925 break;
1926 case 'X': /* write N spaces w/o moving cursor */
1927 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1928 compatibility(ANSIMIN);
1929 {
1930 int n = def(esc_args[0], 1);
1931 pos cursplus;
1932 unsigned long *p = cpos;
1933 if (n > cols - curs.x)
1934 n = cols - curs.x;
1935 cursplus = curs;
1936 cursplus.x += n;
1937 check_selection(curs, cursplus);
1938 while (n--)
1939 *p++ = erase_char;
1940 seen_disp_event = TRUE;
1941 }
1942 break;
1943 case 'x': /* report terminal characteristics */
1944 compatibility(VT100);
1945 {
1946 char buf[32];
1947 int i = def(esc_args[0], 0);
1948 if (i == 0 || i == 1) {
1949 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1950 buf[2] += i;
1951 ldisc_send(buf, 20);
1952 }
1953 }
1954 break;
979f6987 1955 case 'Z': /* BackTab for xterm */
1956 compatibility(OTHER);
1957 {
1958 int i = def(esc_args[0], 1);
1959 pos old_curs = curs;
1960
1961 for(;i>0 && curs.x>0; i--) {
1962 do {
1963 curs.x--;
1964 } while (curs.x >0 && !tabs[curs.x]);
1965 }
1966 fix_cpos;
1967 check_selection(old_curs, curs);
1968 }
1969 break;
32874aea 1970 case ANSI('L', '='):
1971 compatibility(OTHER);
1972 use_bce = (esc_args[0] <= 0);
1973 erase_char = ERASE_CHAR;
1974 if (use_bce)
8f22582c 1975 erase_char = (' ' | ATTR_ASCII |
1976 (curr_attr &
1977 (ATTR_FGMASK | ATTR_BGMASK)));
32874aea 1978 break;
1979 case ANSI('E', '='):
1980 compatibility(OTHER);
1981 blink_is_real = (esc_args[0] >= 1);
1982 break;
1983 case ANSI('p', '"'):
1984 /* Allow the host to make this emulator a 'perfect' VT102.
1985 * This first appeared in the VT220, but we do need to get
1986 * back to PuTTY mode so I won't check it.
e14a5a13 1987 *
4eeb7d09 1988 * The arg in 40..42,50 are a PuTTY extension.
32874aea 1989 * The 2nd arg, 8bit vs 7bit is not checked.
1990 *
1991 * Setting VT102 mode should also change the Fkeys to
1992 * generate PF* codes as a real VT102 has no Fkeys.
1993 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1994 * send nothing.
1995 *
1996 * Note ESC c will NOT change this!
374330e2 1997 */
32874aea 1998
1999 switch (esc_args[0]) {
2000 case 61:
2001 compatibility_level &= ~TM_VTXXX;
2002 compatibility_level |= TM_VT102;
374330e2 2003 break;
32874aea 2004 case 62:
2005 compatibility_level &= ~TM_VTXXX;
2006 compatibility_level |= TM_VT220;
2007 break;
2008
2009 default:
2010 if (esc_args[0] > 60 && esc_args[0] < 70)
2011 compatibility_level |= TM_VTXXX;
2012 break;
2013
2014 case 40:
2015 compatibility_level &= TM_VTXXX;
374330e2 2016 break;
32874aea 2017 case 41:
2018 compatibility_level = TM_PUTTY;
374330e2 2019 break;
32874aea 2020 case 42:
2021 compatibility_level = TM_SCOANSI;
374330e2 2022 break;
32874aea 2023
2024 case ARG_DEFAULT:
2025 compatibility_level = TM_PUTTY;
2026 break;
2027 case 50:
2028 break;
2029 }
2030
2031 /* Change the response to CSI c */
2032 if (esc_args[0] == 50) {
2033 int i;
2034 char lbuf[64];
2035 strcpy(id_string, "\033[?");
2036 for (i = 1; i < esc_nargs; i++) {
2037 if (i != 1)
2038 strcat(id_string, ";");
2039 sprintf(lbuf, "%d", esc_args[i]);
2040 strcat(id_string, lbuf);
2041 }
2042 strcat(id_string, "c");
2043 }
2044#if 0
2045 /* Is this a good idea ?
2046 * Well we should do a soft reset at this point ...
2047 */
2048 if (!has_compat(VT420) && has_compat(VT100)) {
2049 if (reset_132)
2050 request_resize(132, 24, 1);
2051 else
2052 request_resize(80, 24, 1);
374330e2 2053 }
32874aea 2054#endif
2055 break;
374330e2 2056 }
374330e2 2057 break;
32874aea 2058 case SEEN_OSC:
2059 osc_w = FALSE;
2060 switch (c) {
2061 case 'P': /* Linux palette sequence */
2062 termstate = SEEN_OSC_P;
2063 osc_strlen = 0;
2064 break;
2065 case 'R': /* Linux palette reset */
2066 palette_reset();
2067 term_invalidate();
2068 termstate = TOPLEVEL;
2069 break;
2070 case 'W': /* word-set */
2071 termstate = SEEN_OSC_W;
2072 osc_w = TRUE;
2073 break;
2074 case '0':
2075 case '1':
2076 case '2':
2077 case '3':
2078 case '4':
2079 case '5':
2080 case '6':
2081 case '7':
2082 case '8':
2083 case '9':
2084 esc_args[0] = 10 * esc_args[0] + c - '0';
2085 break;
2086 case 'L':
2087 /*
2088 * Grotty hack to support xterm and DECterm title
2089 * sequences concurrently.
2090 */
2091 if (esc_args[0] == 2) {
2092 esc_args[0] = 1;
2093 break;
2094 }
2095 /* else fall through */
2096 default:
2097 termstate = OSC_STRING;
2098 osc_strlen = 0;
e14a5a13 2099 }
2100 break;
32874aea 2101 case OSC_STRING:
2102 /*
2103 * This OSC stuff is EVIL. It takes just one character to get into
2104 * sysline mode and it's not initially obvious how to get out.
2105 * So I've added CR and LF as string aborts.
2106 * This shouldn't effect compatibility as I believe embedded
2107 * control characters are supposed to be interpreted (maybe?)
2108 * and they don't display anything useful anyway.
2109 *
2110 * -- RDB
e14a5a13 2111 */
32874aea 2112 if (c == '\n' || c == '\r') {
2113 termstate = TOPLEVEL;
2114 } else if (c == 0234 || c == '\007') {
2115 /*
2116 * These characters terminate the string; ST and BEL
2117 * terminate the sequence and trigger instant
2118 * processing of it, whereas ESC goes back to SEEN_ESC
2119 * mode unless it is followed by \, in which case it is
2120 * synonymous with ST in the first place.
2121 */
2122 do_osc();
2123 termstate = TOPLEVEL;
2124 } else if (c == '\033')
2125 termstate = OSC_MAYBE_ST;
2126 else if (osc_strlen < OSC_STR_MAX)
2127 osc_string[osc_strlen++] = c;
374330e2 2128 break;
32874aea 2129 case SEEN_OSC_P:
374330e2 2130 {
32874aea 2131 int max = (osc_strlen == 0 ? 21 : 16);
2132 int val;
2133 if (c >= '0' && c <= '9')
2134 val = c - '0';
2135 else if (c >= 'A' && c <= 'A' + max - 10)
2136 val = c - 'A' + 10;
2137 else if (c >= 'a' && c <= 'a' + max - 10)
2138 val = c - 'a' + 10;
2d466ffd 2139 else {
32874aea 2140 termstate = TOPLEVEL;
2d466ffd 2141 break;
2142 }
32874aea 2143 osc_string[osc_strlen++] = val;
2144 if (osc_strlen >= 7) {
2145 palette_set(osc_string[0],
2146 osc_string[1] * 16 + osc_string[2],
2147 osc_string[3] * 16 + osc_string[4],
2148 osc_string[5] * 16 + osc_string[6]);
2149 term_invalidate();
2150 termstate = TOPLEVEL;
374330e2 2151 }
2152 }
2153 break;
32874aea 2154 case SEEN_OSC_W:
2155 switch (c) {
2156 case '0':
2157 case '1':
2158 case '2':
2159 case '3':
2160 case '4':
2161 case '5':
2162 case '6':
2163 case '7':
2164 case '8':
2165 case '9':
2166 esc_args[0] = 10 * esc_args[0] + c - '0';
2167 break;
2168 default:
2169 termstate = OSC_STRING;
2170 osc_strlen = 0;
ec55b220 2171 }
32874aea 2172 break;
32874aea 2173 case VT52_ESC:
374330e2 2174 termstate = TOPLEVEL;
32874aea 2175 seen_disp_event = TRUE;
c9def1b8 2176 switch (c) {
32874aea 2177 case 'A':
2178 move(curs.x, curs.y - 1, 1);
2179 break;
2180 case 'B':
2181 move(curs.x, curs.y + 1, 1);
2182 break;
2183 case 'C':
2184 move(curs.x + 1, curs.y, 1);
2185 break;
2186 case 'D':
2187 move(curs.x - 1, curs.y, 1);
2188 break;
4eeb7d09 2189 /*
2190 * From the VT100 Manual
2191 * NOTE: The special graphics characters in the VT100
2192 * are different from those in the VT52
2193 *
2194 * From VT102 manual:
2195 * 137 _ Blank - Same
2196 * 140 ` Reserved - Humm.
2197 * 141 a Solid rectangle - Similar
2198 * 142 b 1/ - Top half of fraction for the
2199 * 143 c 3/ - subscript numbers below.
2200 * 144 d 5/
2201 * 145 e 7/
2202 * 146 f Degrees - Same
2203 * 147 g Plus or minus - Same
2204 * 150 h Right arrow
2205 * 151 i Ellipsis (dots)
2206 * 152 j Divide by
2207 * 153 k Down arrow
2208 * 154 l Bar at scan 0
2209 * 155 m Bar at scan 1
2210 * 156 n Bar at scan 2
2211 * 157 o Bar at scan 3 - Similar
2212 * 160 p Bar at scan 4 - Similar
2213 * 161 q Bar at scan 5 - Similar
2214 * 162 r Bar at scan 6 - Same
2215 * 163 s Bar at scan 7 - Similar
2216 * 164 t Subscript 0
2217 * 165 u Subscript 1
2218 * 166 v Subscript 2
2219 * 167 w Subscript 3
2220 * 170 x Subscript 4
2221 * 171 y Subscript 5
2222 * 172 z Subscript 6
2223 * 173 { Subscript 7
2224 * 174 | Subscript 8
2225 * 175 } Subscript 9
2226 * 176 ~ Paragraph
2227 *
2228 */
32874aea 2229 case 'F':
2230 cset_attr[cset = 0] = ATTR_LINEDRW;
2231 break;
2232 case 'G':
2233 cset_attr[cset = 0] = ATTR_ASCII;
2234 break;
2235 case 'H':
2236 move(0, 0, 0);
2237 break;
2238 case 'I':
2239 if (curs.y == 0)
2240 scroll(0, rows - 1, -1, TRUE);
2241 else if (curs.y > 0)
2242 curs.y--;
2243 fix_cpos;
2244 wrapnext = FALSE;
2245 break;
2246 case 'J':
2247 erase_lots(FALSE, FALSE, TRUE);
4facdf84 2248 disptop = 0;
c9def1b8 2249 break;
32874aea 2250 case 'K':
2251 erase_lots(TRUE, FALSE, TRUE);
2252 break;
4eeb7d09 2253#if 0
32874aea 2254 case 'V':
2255 /* XXX Print cursor line */
2256 break;
2257 case 'W':
2258 /* XXX Start controller mode */
2259 break;
2260 case 'X':
2261 /* XXX Stop controller mode */
2262 break;
4eeb7d09 2263#endif
32874aea 2264 case 'Y':
2265 termstate = VT52_Y1;
2266 break;
2267 case 'Z':
2268 ldisc_send("\033/Z", 3);
2269 break;
2270 case '=':
2271 app_keypad_keys = TRUE;
2272 break;
2273 case '>':
2274 app_keypad_keys = FALSE;
2275 break;
2276 case '<':
2277 /* XXX This should switch to VT100 mode not current or default
2278 * VT mode. But this will only have effect in a VT220+
2279 * emulation.
2280 */
2281 vt52_mode = FALSE;
4eeb7d09 2282 blink_is_real = cfg.blinktext;
32874aea 2283 break;
4eeb7d09 2284#if 0
32874aea 2285 case '^':
2286 /* XXX Enter auto print mode */
2287 break;
2288 case '_':
2289 /* XXX Exit auto print mode */
2290 break;
2291 case ']':
2292 /* XXX Print screen */
2293 break;
4eeb7d09 2294#endif
2295
2296#ifdef VT52_PLUS
2297 case 'E':
2298 /* compatibility(ATARI) */
2299 move(0, 0, 0);
2300 erase_lots(FALSE, FALSE, TRUE);
2301 disptop = 0;
2302 break;
2303 case 'L':
2304 /* compatibility(ATARI) */
2305 if (curs.y <= marg_b)
2306 scroll(curs.y, marg_b, -1, FALSE);
2307 break;
2308 case 'M':
2309 /* compatibility(ATARI) */
2310 if (curs.y <= marg_b)
2311 scroll(curs.y, marg_b, 1, TRUE);
2312 break;
2313 case 'b':
2314 /* compatibility(ATARI) */
2315 termstate = VT52_FG;
2316 break;
2317 case 'c':
2318 /* compatibility(ATARI) */
2319 termstate = VT52_BG;
2320 break;
2321 case 'd':
2322 /* compatibility(ATARI) */
2323 erase_lots(FALSE, TRUE, FALSE);
2324 disptop = 0;
2325 break;
2326 case 'e':
2327 /* compatibility(ATARI) */
2328 cursor_on = TRUE;
2329 break;
2330 case 'f':
2331 /* compatibility(ATARI) */
2332 cursor_on = FALSE;
2333 break;
2334 /* case 'j': Save cursor position - broken on ST */
2335 /* case 'k': Restore cursor position */
2336 case 'l':
2337 /* compatibility(ATARI) */
2338 erase_lots(TRUE, TRUE, TRUE);
2339 curs.x = 0;
2340 wrapnext = FALSE;
2341 fix_cpos;
2342 break;
2343 case 'o':
2344 /* compatibility(ATARI) */
2345 erase_lots(TRUE, TRUE, FALSE);
2346 break;
2347 case 'p':
2348 /* compatibility(ATARI) */
2349 curr_attr |= ATTR_REVERSE;
2350 break;
2351 case 'q':
2352 /* compatibility(ATARI) */
2353 curr_attr &= ~ATTR_REVERSE;
2354 break;
2355 case 'v': /* wrap Autowrap on - Wyse style */
2356 /* compatibility(ATARI) */
2357 wrap = 1;
2358 break;
2359 case 'w': /* Autowrap off */
2360 /* compatibility(ATARI) */
2361 wrap = 0;
2362 break;
2363
2364 case 'R':
2365 /* compatibility(OTHER) */
2366 vt52_bold = FALSE;
2367 curr_attr = ATTR_DEFAULT;
2368 if (use_bce)
8f22582c 2369 erase_char = (' ' | ATTR_ASCII |
2370 (curr_attr &
2371 (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2372 break;
2373 case 'S':
2374 /* compatibility(VI50) */
2375 curr_attr |= ATTR_UNDER;
2376 break;
2377 case 'W':
2378 /* compatibility(VI50) */
2379 curr_attr &= ~ATTR_UNDER;
2380 break;
2381 case 'U':
2382 /* compatibility(VI50) */
2383 vt52_bold = TRUE;
2384 curr_attr |= ATTR_BOLD;
2385 break;
2386 case 'T':
2387 /* compatibility(VI50) */
2388 vt52_bold = FALSE;
2389 curr_attr &= ~ATTR_BOLD;
2390 break;
2391#endif
c9def1b8 2392 }
e14a5a13 2393 break;
32874aea 2394 case VT52_Y1:
2395 termstate = VT52_Y2;
2396 move(curs.x, c - ' ', 0);
e14a5a13 2397 break;
32874aea 2398 case VT52_Y2:
2399 termstate = TOPLEVEL;
2400 move(c - ' ', curs.y, 0);
e14a5a13 2401 break;
4eeb7d09 2402
2403#ifdef VT52_PLUS
2404 case VT52_FG:
2405 termstate = TOPLEVEL;
2406 curr_attr &= ~ATTR_FGMASK;
2407 curr_attr &= ~ATTR_BOLD;
2408 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2409 if ((c & 0x8) || vt52_bold)
2410 curr_attr |= ATTR_BOLD;
2411
2412 if (use_bce)
8f22582c 2413 erase_char = (' ' | ATTR_ASCII |
2414 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2415 break;
2416 case VT52_BG:
2417 termstate = TOPLEVEL;
2418 curr_attr &= ~ATTR_BGMASK;
2419 curr_attr &= ~ATTR_BLINK;
2420 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2421
2422 /* Note: bold background */
2423 if (c & 0x8)
2424 curr_attr |= ATTR_BLINK;
2425
2426 if (use_bce)
8f22582c 2427 erase_char = (' ' | ATTR_ASCII |
2428 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2429 break;
2430#endif
2d466ffd 2431 default: break; /* placate gcc warning about enum use */
e14a5a13 2432 }
4facdf84 2433 if (selstate != NO_SELECTION) {
2434 pos cursplus = curs;
2435 incpos(cursplus);
32874aea 2436 check_selection(curs, cursplus);
4facdf84 2437 }
374330e2 2438 }
c9def1b8 2439 inbuf_head = 0;
374330e2 2440}
2441
4eeb7d09 2442#if 0
374330e2 2443/*
2444 * Compare two lines to determine whether they are sufficiently
2445 * alike to scroll-optimise one to the other. Return the degree of
2446 * similarity.
2447 */
32874aea 2448static int linecmp(unsigned long *a, unsigned long *b)
2449{
374330e2 2450 int i, n;
2451
32874aea 2452 for (i = n = 0; i < cols; i++)
374330e2 2453 n += (*a++ == *b++);
2454 return n;
2455}
4eeb7d09 2456#endif
374330e2 2457
2458/*
2459 * Given a context, update the window. Out of paranoia, we don't
2460 * allow WM_PAINT responses to do scrolling optimisations.
2461 */
32874aea 2462static void do_paint(Context ctx, int may_optimise)
2463{
4eeb7d09 2464 int i, j, our_curs_y;
2465 unsigned long rv, cursor;
4facdf84 2466 pos scrpos;
374330e2 2467 char ch[1024];
4eeb7d09 2468 long cursor_background = ERASE_CHAR;
156686ef 2469 long ticks;
374330e2 2470
156686ef 2471 /*
2472 * Check the visual bell state.
2473 */
2474 if (in_vbell) {
2475 ticks = GetTickCount();
2476 if (ticks - vbell_timeout >= 0)
2477 in_vbell = FALSE;
2478 }
2479
4eeb7d09 2480 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2481
156686ef 2482 /* Depends on:
2483 * screen array, disptop, scrtop,
2484 * selection, rv,
2485 * cfg.blinkpc, blink_is_real, tblinker,
4eeb7d09 2486 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
156686ef 2487 */
4eeb7d09 2488
2489 /* Has the cursor position or type changed ? */
e14a5a13 2490 if (cursor_on) {
32874aea 2491 if (has_focus) {
217dceef 2492 if (blinker || !cfg.blink_cur)
4eeb7d09 2493 cursor = TATTR_ACTCURS;
32874aea 2494 else
2495 cursor = 0;
2496 } else
4eeb7d09 2497 cursor = TATTR_PASCURS;
4e30ff69 2498 if (wrapnext)
4eeb7d09 2499 cursor |= TATTR_RIGHTCURS;
32874aea 2500 } else
156686ef 2501 cursor = 0;
4facdf84 2502 our_curs_y = curs.y - disptop;
374330e2 2503
4eeb7d09 2504 if (dispcurs && (curstype != cursor ||
2505 dispcurs !=
2506 disptext + our_curs_y * (cols + 1) + curs.x)) {
2507 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2508 dispcurs[-1] |= ATTR_INVALID;
2509 if ((*dispcurs & ATTR_WIDE))
2510 dispcurs[1] |= ATTR_INVALID;
2511 *dispcurs |= ATTR_INVALID;
2512 curstype = 0;
2513 }
2514 dispcurs = NULL;
2515
2516 /* The normal screen data */
32874aea 2517 for (i = 0; i < rows; i++) {
4facdf84 2518 unsigned long *ldata;
2519 int lattr;
4eeb7d09 2520 int idx, dirty_line, dirty_run;
2521 unsigned long attr = 0;
2522 int updated_line = 0;
2523 int start = 0;
2524 int ccount = 0;
2525 int last_run_dirty = 0;
2526
4facdf84 2527 scrpos.y = i + disptop;
2528 ldata = lineptr(scrpos.y);
2529 lattr = (ldata[cols] & LATTR_MODE);
4eeb7d09 2530
2531 idx = i * (cols + 1);
2532 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2533 disptext[idx + cols] = ldata[cols];
2534
2535 for (j = 0; j < cols; j++, idx++) {
2536 unsigned long tattr, tchar;
2537 unsigned long *d = ldata + j;
2538 int break_run;
4facdf84 2539 scrpos.x = j;
32874aea 2540
4eeb7d09 2541 tchar = (*d & (CHAR_MASK | CSET_MASK));
2542 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2543 switch (tchar & CSET_MASK) {
2544 case ATTR_ASCII:
2545 tchar = unitab_line[tchar & 0xFF];
2546 break;
2547 case ATTR_LINEDRW:
2548 tchar = unitab_xterm[tchar & 0xFF];
2549 break;
d3cb5465 2550 case ATTR_SCOACS:
2551 tchar = unitab_scoacs[tchar&0xFF];
2552 break;
4eeb7d09 2553 }
2554 tattr |= (tchar & CSET_MASK);
2555 tchar &= CHAR_MASK;
2556
2557 /* Video reversing things */
2558 tattr = (tattr ^ rv
2559 ^ (posle(selstart, scrpos) &&
2560 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2561
2562 /* 'Real' blinking ? */
2563 if (blink_is_real && (tattr & ATTR_BLINK)) {
2564 if (has_focus && tblinker) {
2565 tchar = ' ';
2566 tattr &= ~CSET_MASK;
2567 tattr |= ATTR_ACP;
c9def1b8 2568 }
4eeb7d09 2569 tattr &= ~ATTR_BLINK;
c9def1b8 2570 }
374330e2 2571
4eeb7d09 2572 /* Cursor here ? Save the 'background' */
2573 if (i == our_curs_y && j == curs.x) {
2574 cursor_background = tattr | tchar;
2575 dispcurs = disptext + idx;
2576 }
374330e2 2577
4eeb7d09 2578 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2579 dirty_line = TRUE;
2580
2581 break_run = (tattr != attr || j - start >= sizeof(ch));
2582
2583 /* Special hack for VT100 Linedraw glyphs */
2584 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2585 && tchar <= 0xBD) break_run = TRUE;
2586
2587 if (!dbcs_screenfont && !dirty_line) {
2588 if ((tchar | tattr) == disptext[idx])
2589 break_run = TRUE;
2590 else if (!dirty_run && ccount == 1)
2591 break_run = TRUE;
374330e2 2592 }
4eeb7d09 2593
2594 if (break_run) {
2595 if ((dirty_run || last_run_dirty) && ccount > 0) {
2596 do_text(ctx, start, i, ch, ccount, attr, lattr);
2597 updated_line = 1;
2598 }
2599 start = j;
2600 ccount = 0;
2601 attr = tattr;
2602 if (dbcs_screenfont)
2603 last_run_dirty = dirty_run;
2604 dirty_run = dirty_line;
2605 }
2606
2607 if ((tchar | tattr) != disptext[idx])
2608 dirty_run = TRUE;
2609 ch[ccount++] = (char) tchar;
2610 disptext[idx] = tchar | tattr;
2611
2612 /* If it's a wide char step along to the next one. */
2613 if (tattr & ATTR_WIDE) {
2614 if (++j < cols) {
2615 idx++;
2616 d++;
2617 /* Cursor is here ? Ouch! */
2618 if (i == our_curs_y && j == curs.x) {
2619 cursor_background = *d;
2620 dispcurs = disptext + idx;
2621 }
2622 if (disptext[idx] != *d)
2623 dirty_run = TRUE;
2624 disptext[idx] = *d;
374330e2 2625 }
374330e2 2626 }
4eeb7d09 2627 }
2628 if (dirty_run && ccount > 0) {
2629 do_text(ctx, start, i, ch, ccount, attr, lattr);
2630 updated_line = 1;
2631 }
2632
2633 /* Cursor on this line ? (and changed) */
2634 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2635 ch[0] = (char) (cursor_background & CHAR_MASK);
2636 attr = (cursor_background & ATTR_MASK) | cursor;
2637 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2638 curstype = cursor;
374330e2 2639 }
2640 }
2641}
2642
2643/*
e14a5a13 2644 * Flick the switch that says if blinking things should be shown or hidden.
2645 */
2646
32874aea 2647void term_blink(int flg)
2648{
22dcdc3b 2649 static long last_blink = 0;
2650 static long last_tblink = 0;
e14a5a13 2651 long now, blink_diff;
2652
c9def1b8 2653 now = GetTickCount();
32874aea 2654 blink_diff = now - last_tblink;
c9def1b8 2655
2656 /* Make sure the text blinks no more than 2Hz */
32874aea 2657 if (blink_diff < 0 || blink_diff > 450) {
2658 last_tblink = now;
c9def1b8 2659 tblinker = !tblinker;
2660 }
2661
e14a5a13 2662 if (flg) {
32874aea 2663 blinker = 1;
2664 last_blink = now;
e14a5a13 2665 return;
32874aea 2666 }
e14a5a13 2667
32874aea 2668 blink_diff = now - last_blink;
e14a5a13 2669
22dcdc3b 2670 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
32874aea 2671 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2672 return;
2673
e14a5a13 2674 last_blink = now;
2675 blinker = !blinker;
2676}
2677
2678/*
374330e2 2679 * Invalidate the whole screen so it will be repainted in full.
2680 */
32874aea 2681void term_invalidate(void)
2682{
374330e2 2683 int i;
2684
32874aea 2685 for (i = 0; i < rows * (cols + 1); i++)
374330e2 2686 disptext[i] = ATTR_INVALID;
2687}
2688
2689/*
2690 * Paint the window in response to a WM_PAINT message.
2691 */
32874aea 2692void term_paint(Context ctx, int l, int t, int r, int b)
2693{
374330e2 2694 int i, j, left, top, right, bottom;
2695
2696 left = l / font_width;
2697 right = (r - 1) / font_width;
2698 top = t / font_height;
2699 bottom = (b - 1) / font_height;
32874aea 2700 for (i = top; i <= bottom && i < rows; i++) {
2701 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2702 for (j = left; j <= right && j < cols; j++)
2703 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2704 else
32874aea 2705 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2706 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2707 }
374330e2 2708
e14a5a13 2709 /* This should happen soon enough, also for some reason it sometimes
2710 * fails to actually do anything when re-sizing ... painting the wrong
2711 * window perhaps ?
32874aea 2712 do_paint (ctx, FALSE);
2713 */
374330e2 2714}
2715
2716/*
2717 * Attempt to scroll the scrollback. The second parameter gives the
2718 * position we want to scroll to; the first is +1 to denote that
2719 * this position is relative to the beginning of the scrollback, -1
2720 * to denote it is relative to the end, and 0 to denote that it is
2721 * relative to the current position.
2722 */
32874aea 2723void term_scroll(int rel, int where)
2724{
4facdf84 2725 int sbtop = -count234(scrollback);
374330e2 2726
32874aea 2727 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
374330e2 2728 if (disptop < sbtop)
2729 disptop = sbtop;
4facdf84 2730 if (disptop > 0)
2731 disptop = 0;
374330e2 2732 update_sbar();
2733 term_update();
2734}
2735
4eeb7d09 2736static void clipme(pos top, pos bottom)
32874aea 2737{
4eeb7d09 2738 wchar_t *workbuf;
2739 wchar_t *wbptr; /* where next char goes within workbuf */
32874aea 2740 int wblen = 0; /* workbuf len */
2741 int buflen; /* amount of memory allocated to workbuf */
bc1235d4 2742
4eeb7d09 2743 buflen = 5120; /* Default size */
2744 workbuf = smalloc(buflen * sizeof(wchar_t));
2745 wbptr = workbuf; /* start filling here */
bc1235d4 2746
4facdf84 2747 while (poslt(top, bottom)) {
bc1235d4 2748 int nl = FALSE;
4facdf84 2749 unsigned long *ldata = lineptr(top.y);
260f3dec 2750 pos nlpos;
4facdf84 2751
2752 nlpos.y = top.y;
2753 nlpos.x = cols;
bc1235d4 2754
4eeb7d09 2755 if (!(ldata[cols] & LATTR_WRAPPED)) {
2756 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2757 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2758 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2759 && poslt(top, nlpos))
2760 decpos(nlpos);
4facdf84 2761 if (poslt(nlpos, bottom))
bc1235d4 2762 nl = TRUE;
2763 }
4facdf84 2764 while (poslt(top, bottom) && poslt(top, nlpos)) {
4eeb7d09 2765#if 0
2766 char cbuf[16], *p;
2767 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2768#else
2769 wchar_t cbuf[16], *p;
2770 int uc = (ldata[top.x] & 0xFFFF);
2771 int set, c;
2772
2773 if (uc == UCSWIDE) {
2774 top.x++;
2775 continue;
2776 }
2777
2778 switch (uc & CSET_MASK) {
2779 case ATTR_LINEDRW:
2780 if (!cfg.rawcnp) {
2781 uc = unitab_xterm[uc & 0xFF];
2782 break;
32874aea 2783 }
4eeb7d09 2784 case ATTR_ASCII:
2785 uc = unitab_line[uc & 0xFF];
2786 break;
d3cb5465 2787 case ATTR_SCOACS:
2788 uc = unitab_scoacs[uc&0xFF];
2789 break;
d3a22f79 2790 }
4eeb7d09 2791 switch (uc & CSET_MASK) {
2792 case ATTR_ACP:
2793 uc = unitab_font[uc & 0xFF];
2794 break;
2795 case ATTR_OEMCP:
2796 uc = unitab_oemcp[uc & 0xFF];
2797 break;
2798 }
2799
2800 set = (uc & CSET_MASK);
2801 c = (uc & CHAR_MASK);
2802 cbuf[0] = uc;
2803 cbuf[1] = 0;
2804
2805 if (DIRECT_FONT(uc)) {
2806 if (c >= ' ' && c != 0x7F) {
2807 unsigned char buf[4];
2808 WCHAR wbuf[4];
2809 int rv;
2810 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2811 buf[0] = c;
2812 buf[1] = (unsigned char) ldata[top.x + 1];
2813 rv = MultiByteToWideChar(font_codepage,
2814 0, buf, 2, wbuf, 4);
2815 top.x++;
2816 } else {
2817 buf[0] = c;
2818 rv = MultiByteToWideChar(font_codepage,
2819 0, buf, 1, wbuf, 4);
2820 }
bc1235d4 2821
4eeb7d09 2822 if (rv > 0) {
2823 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2824 cbuf[rv] = 0;
d3a22f79 2825 }
d3a22f79 2826 }
4eeb7d09 2827 }
2828#endif
2829
2830 for (p = cbuf; *p; p++) {
2831 /* Enough overhead for trailing NL and nul */
2832 if (wblen >= buflen - 16) {
2833 workbuf =
2834 srealloc(workbuf,
2835 sizeof(wchar_t) * (buflen += 100));
2836 wbptr = workbuf + wblen;
2837 }
2838 wblen++;
2839 *wbptr++ = *p;
bc1235d4 2840 }
4facdf84 2841 top.x++;
bc1235d4 2842 }
2843 if (nl) {
2844 int i;
4eeb7d09 2845 for (i = 0; i < sel_nl_sz; i++) {
32874aea 2846 wblen++;
bc1235d4 2847 *wbptr++ = sel_nl[i];
2848 }
2849 }
4facdf84 2850 top.y++;
2851 top.x = 0;
bc1235d4 2852 }
4eeb7d09 2853 wblen++;
2854 *wbptr++ = 0;
32874aea 2855 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2856 if (buflen > 0) /* indicates we allocated this buffer */
bc1235d4 2857 sfree(workbuf);
bc1235d4 2858}
4eeb7d09 2859
32874aea 2860void term_copyall(void)
2861{
4facdf84 2862 pos top;
2863 top.y = -count234(scrollback);
2864 top.x = 0;
4eeb7d09 2865 clipme(top, curs);
2866}
2867
2868/*
2869 * The wordness array is mainly for deciding the disposition of the US-ASCII
2870 * characters.
2871 */
2872static int wordtype(int uc)
2873{
2874 static struct {
2875 int start, end, ctype;
2876 } *wptr, ucs_words[] = {
2877 {
2878 128, 160, 0}, {
2879 161, 191, 1}, {
2880 215, 215, 1}, {
2881 247, 247, 1}, {
2882 0x037e, 0x037e, 1}, /* Greek question mark */
2883 {
2884 0x0387, 0x0387, 1}, /* Greek ano teleia */
2885 {
2886 0x055a, 0x055f, 1}, /* Armenian punctuation */
2887 {
2888 0x0589, 0x0589, 1}, /* Armenian full stop */
2889 {
2890 0x0700, 0x070d, 1}, /* Syriac punctuation */
2891 {
2892 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2893 {
2894 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2895 {
2896 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2897 {
2898 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2899 {
2900 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2901 {
2902 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2903 {
2904 0x2000, 0x200a, 0}, /* Various spaces */
2905 {
2906 0x2070, 0x207f, 2}, /* superscript */
2907 {
2908 0x2080, 0x208f, 2}, /* subscript */
2909 {
2910 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2911 {
2912 0x3000, 0x3000, 0}, /* ideographic space */
2913 {
2914 0x3001, 0x3020, 1}, /* ideographic punctuation */
2915 {
2916 0x303f, 0x309f, 3}, /* Hiragana */
2917 {
2918 0x30a0, 0x30ff, 3}, /* Katakana */
2919 {
2920 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2921 {
2922 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2923 {
2924 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2925 {
2926 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2927 {
2928 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2929 {
2930 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2931 {
2932 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2933 {
2934 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2935 {
2936 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2937 {
2938 0, 0, 0}
2939 };
2940
2941 uc &= (CSET_MASK | CHAR_MASK);
2942
2943 switch (uc & CSET_MASK) {
2944 case ATTR_LINEDRW:
2945 uc = unitab_xterm[uc & 0xFF];
2946 break;
2947 case ATTR_ASCII:
2948 uc = unitab_line[uc & 0xFF];
2949 break;
d3cb5465 2950 case ATTR_SCOACS:
2951 uc = unitab_scoacs[uc&0xFF];
2952 break;
4eeb7d09 2953 }
2954 switch (uc & CSET_MASK) {
2955 case ATTR_ACP:
2956 uc = unitab_font[uc & 0xFF];
2957 break;
2958 case ATTR_OEMCP:
2959 uc = unitab_oemcp[uc & 0xFF];
2960 break;
2961 }
2962
2963 if (uc < 0x80)
2964 return wordness[uc];
2965
2966 for (wptr = ucs_words; wptr->start; wptr++) {
2967 if (uc >= wptr->start && uc <= wptr->end)
2968 return wptr->ctype;
2969 }
2970
2971 return 2;
bc1235d4 2972}
2973
374330e2 2974/*
2975 * Spread the selection outwards according to the selection mode.
2976 */
32874aea 2977static pos sel_spread_half(pos p, int dir)
2978{
4facdf84 2979 unsigned long *ldata;
374330e2 2980 short wvalue;
2981
4facdf84 2982 ldata = lineptr(p.y);
374330e2 2983
2984 switch (selmode) {
2985 case SM_CHAR:
2986 /*
2987 * In this mode, every character is a separate unit, except
2988 * for runs of spaces at the end of a non-wrapping line.
2989 */
4eeb7d09 2990 if (!(ldata[cols] & LATTR_WRAPPED)) {
32874aea 2991 unsigned long *q = ldata + cols;
4facdf84 2992 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
374330e2 2993 q--;
32874aea 2994 if (q == ldata + cols)
374330e2 2995 q--;
32874aea 2996 if (p.x >= q - ldata)
2997 p.x = (dir == -1 ? q - ldata : cols - 1);
374330e2 2998 }
2999 break;
3000 case SM_WORD:
3001 /*
3002 * In this mode, the units are maximal runs of characters
3003 * whose `wordness' has the same value.
3004 */
4eeb7d09 3005 wvalue = wordtype(ldata[p.x]);
374330e2 3006 if (dir == +1) {
4eeb7d09 3007 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
3008 p.x++;
374330e2 3009 } else {
4eeb7d09 3010 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
3011 p.x--;
374330e2 3012 }
3013 break;
3014 case SM_LINE:
3015 /*
3016 * In this mode, every line is a unit.
3017 */
4facdf84 3018 p.x = (dir == -1 ? 0 : cols - 1);
374330e2 3019 break;
3020 }
3021 return p;
3022}
3023
32874aea 3024static void sel_spread(void)
3025{
3026 selstart = sel_spread_half(selstart, -1);
4facdf84 3027 decpos(selend);
32874aea 3028 selend = sel_spread_half(selend, +1);
4facdf84 3029 incpos(selend);
374330e2 3030}
3031
568dd02f 3032void term_do_paste(void)
3033{
3034 wchar_t *data;
3035 int len;
3036
3037 get_clip(&data, &len);
3038 if (data) {
3039 wchar_t *p, *q;
3040
3041 if (paste_buffer)
3042 sfree(paste_buffer);
3043 paste_pos = paste_hold = paste_len = 0;
3044 paste_buffer = smalloc(len * sizeof(wchar_t));
3045
3046 p = q = data;
3047 while (p < data + len) {
3048 while (p < data + len &&
3049 !(p <= data + len - sel_nl_sz &&
3050 !memcmp(p, sel_nl, sizeof(sel_nl))))
3051 p++;
3052
3053 {
3054 int i;
3055 for (i = 0; i < p - q; i++) {
3056 paste_buffer[paste_len++] = q[i];
3057 }
3058 }
3059
3060 if (p <= data + len - sel_nl_sz &&
3061 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3062 paste_buffer[paste_len++] = '\r';
3063 p += sel_nl_sz;
3064 }
3065 q = p;
3066 }
3067
3068 /* Assume a small paste will be OK in one go. */
3069 if (paste_len < 256) {
3070 luni_send(paste_buffer, paste_len);
3071 if (paste_buffer)
3072 sfree(paste_buffer);
3073 paste_buffer = 0;
3074 paste_pos = paste_hold = paste_len = 0;
3075 }
3076 }
3077 get_clip(NULL, NULL);
3078}
3079
32874aea 3080void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3081 int shift, int ctrl)
3082{
4facdf84 3083 pos selpoint;
3084 unsigned long *ldata;
32874aea 3085
3086 if (y < 0)
3087 y = 0;
3088 if (y >= rows)
3089 y = rows - 1;
3090 if (x < 0) {
3091 if (y > 0) {
3092 x = cols - 1;
3093 y--;
3094 } else
3095 x = 0;
094ed2a6 3096 }
32874aea 3097 if (x >= cols)
3098 x = cols - 1;
37508af4 3099
4facdf84 3100 selpoint.y = y + disptop;
3101 selpoint.x = x;
3102 ldata = lineptr(selpoint.y);
32874aea 3103 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
4facdf84 3104 selpoint.x /= 2;
374330e2 3105
b90840c3 3106 if (xterm_mouse && !(cfg.mouse_override && shift)) {
01c034ad 3107 int encstate = 0, r, c;
3108 char abuf[16];
3109 static int is_down = 0;
3110
32874aea 3111 switch (b) {
01c034ad 3112 case MBT_LEFT:
32874aea 3113 encstate = 0x20; /* left button down */
01c034ad 3114 break;
3115 case MBT_MIDDLE:
3116 encstate = 0x21;
3117 break;
3118 case MBT_RIGHT:
3119 encstate = 0x22;
3120 break;
3121 case MBT_WHEEL_UP:
3122 encstate = 0x60;
3123 break;
3124 case MBT_WHEEL_DOWN:
3125 encstate = 0x61;
3126 break;
2d466ffd 3127 default: break; /* placate gcc warning about enum use */
01c034ad 3128 }
32874aea 3129 switch (a) {
01c034ad 3130 case MA_DRAG:
3131 if (xterm_mouse == 1)
3132 return;
3133 encstate += 0x20;
3134 break;
3135 case MA_RELEASE:
3136 encstate = 0x23;
3137 is_down = 0;
3138 break;
3139 case MA_CLICK:
3140 if (is_down == b)
3141 return;
3142 is_down = b;
3143 break;
2d466ffd 3144 default: break; /* placate gcc warning about enum use */
01c034ad 3145 }
3146 if (shift)
3147 encstate += 0x04;
3148 if (ctrl)
3149 encstate += 0x10;
3150 r = y + 33;
3151 c = x + 33;
3152
3153 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3154 ldisc_send(abuf, 6);
3155 return;
3156 }
3157
3158 b = translate_button(b);
3159
3160 if (b == MBT_SELECT && a == MA_CLICK) {
374330e2 3161 deselect();
3162 selstate = ABOUT_TO;
3163 selanchor = selpoint;
3164 selmode = SM_CHAR;
01c034ad 3165 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
374330e2 3166 deselect();
3167 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3168 selstate = DRAGGING;
3169 selstart = selanchor = selpoint;
4facdf84 3170 selend = selstart;
3171 incpos(selend);
374330e2 3172 sel_spread();
01c034ad 3173 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3174 (b == MBT_EXTEND && a != MA_RELEASE)) {
4facdf84 3175 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
374330e2 3176 return;
01c034ad 3177 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
32874aea 3178 if (posdiff(selpoint, selstart) <
3179 posdiff(selend, selstart) / 2) {
4facdf84 3180 selanchor = selend;
3181 decpos(selanchor);
3182 } else {
374330e2 3183 selanchor = selstart;
4facdf84 3184 }
374330e2 3185 selstate = DRAGGING;
3186 }
3187 if (selstate != ABOUT_TO && selstate != DRAGGING)
3188 selanchor = selpoint;
3189 selstate = DRAGGING;
4facdf84 3190 if (poslt(selpoint, selanchor)) {
374330e2 3191 selstart = selpoint;
4facdf84 3192 selend = selanchor;
3193 incpos(selend);
374330e2 3194 } else {
3195 selstart = selanchor;
4facdf84 3196 selend = selpoint;
a4450583 3197 incpos(selend);
374330e2 3198 }
3199 sel_spread();
01c034ad 3200 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
374330e2 3201 if (selstate == DRAGGING) {
3202 /*
3203 * We've completed a selection. We now transfer the
3204 * data to the clipboard.
3205 */
4eeb7d09 3206 clipme(selstart, selend);
374330e2 3207 selstate = SELECTED;
3208 } else
3209 selstate = NO_SELECTION;
32874aea 3210 } else if (b == MBT_PASTE
3211 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
568dd02f 3212 term_do_paste();
374330e2 3213 }
3214
3215 term_update();
3216}
3217
32874aea 3218void term_nopaste()
3219{
3220 if (paste_len == 0)
3221 return;
c9def1b8 3222 sfree(paste_buffer);
3223 paste_buffer = 0;
3224 paste_len = 0;
3225}
3226
32874aea 3227void term_paste()
3228{
8df7a775 3229 static long last_paste = 0;
c9def1b8 3230 long now, paste_diff;
3231
32874aea 3232 if (paste_len == 0)
3233 return;
c9def1b8 3234
3235 /* Don't wait forever to paste */
32874aea 3236 if (paste_hold) {
3237 now = GetTickCount();
3238 paste_diff = now - last_paste;
3239 if (paste_diff >= 0 && paste_diff < 450)
c9def1b8 3240 return;
3241 }
3242 paste_hold = 0;
3243
32874aea 3244 while (paste_pos < paste_len) {
8df7a775 3245 int n = 0;
3246 while (n + paste_pos < paste_len) {
3247 if (paste_buffer[paste_pos + n++] == '\r')
3248 break;
3249 }
4eeb7d09 3250 luni_send(paste_buffer + paste_pos, n);
8df7a775 3251 paste_pos += n;
c9def1b8 3252
8df7a775 3253 if (paste_pos < paste_len) {
c9def1b8 3254 paste_hold = 1;
3255 return;
3256 }
3257 }
3258 sfree(paste_buffer);
3259 paste_buffer = 0;
3260 paste_len = 0;
3261}
3262
32874aea 3263static void deselect(void)
3264{
374330e2 3265 selstate = NO_SELECTION;
4facdf84 3266 selstart.x = selstart.y = selend.x = selend.y = 0;
374330e2 3267}
3268
32874aea 3269void term_deselect(void)
3270{
374330e2 3271 deselect();
3272 term_update();
3273}
fe50e814 3274
32874aea 3275int term_ldisc(int option)
3276{
3277 if (option == LD_ECHO)
3278 return term_echoing;
3279 if (option == LD_EDIT)
3280 return term_editing;
0965bee0 3281 return FALSE;
3282}
3283
fe50e814 3284/*
3285 * from_backend(), to get data from the backend for the terminal.
3286 */
5471d09a 3287int from_backend(int is_stderr, char *data, int len)
32874aea 3288{
fe50e814 3289 while (len--) {
3290 if (inbuf_head >= INBUF_SIZE)
3291 term_out();
3292 inbuf[inbuf_head++] = *data++;
3293 }
5471d09a 3294
3295 /*
3296 * We process all stdout/stderr data immediately we receive it,
3297 * and don't return until it's all gone. Therefore, there's no
3298 * reason at all to return anything other than zero from this
3299 * function.
3300 *
3301 * This is a slightly suboptimal way to deal with SSH2 - in
3302 * principle, the window mechanism would allow us to continue
3303 * to accept data on forwarded ports and X connections even
3304 * while the terminal processing was going slowly - but we
3305 * can't do the 100% right thing without moving the terminal
3306 * processing into a separate thread, and that might hurt
3307 * portability. So we manage stdout buffering the old SSH1 way:
3308 * if the terminal processing goes slowly, the whole SSH
3309 * connection stops accepting data until it's ready.
3310 *
3311 * In practice, I can't imagine this causing serious trouble.
3312 */
3313 return 0;
fe50e814 3314}
e1c8e0ed 3315
3316/*
3317 * Log session traffic.
3318 */
32874aea 3319void logtraffic(unsigned char c, int logmode)
3320{
e1c8e0ed 3321 if (cfg.logtype > 0) {
3322 if (cfg.logtype == logmode) {
3323 /* deferred open file from pgm start? */
32874aea 3324 if (!lgfp)
3325 logfopen();
3326 if (lgfp)
3327 fputc(c, lgfp);
3328 }
e1c8e0ed 3329 }
3330}
3331
3332/* open log file append/overwrite mode */
32874aea 3333void logfopen(void)
3334{
e1c8e0ed 3335 char buf[256];
3336 time_t t;
3337 struct tm *tm;
3338 char writemod[4];
3339
3340 if (!cfg.logtype)
3341 return;
32874aea 3342 sprintf(writemod, "wb"); /* default to rewrite */
3343 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
e1c8e0ed 3344 if (lgfp) {
3345 int i;
3346 fclose(lgfp);
3347 i = askappend(cfg.logfilename);
3348 if (i == 1)
3349 writemod[0] = 'a'; /* set append mode */
3350 else if (i == 0) { /* cancelled */
3351 lgfp = NULL;
32874aea 3352 cfg.logtype = 0; /* disable logging */
e1c8e0ed 3353 return;
3354 }
3355 }
3356
3357 lgfp = fopen(cfg.logfilename, writemod);
32874aea 3358 if (lgfp) { /* enter into event log */
e1c8e0ed 3359 sprintf(buf, "%s session log (%s mode) to file : ",
3360 (writemod[0] == 'a') ? "Appending" : "Writing new",
3361 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
32874aea 3362 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
e1c8e0ed 3363 /* Make sure we do not exceed the output buffer size */
32874aea 3364 strncat(buf, cfg.logfilename, 128);
e1c8e0ed 3365 buf[strlen(buf)] = '\0';
3366 logevent(buf);
3367
32874aea 3368 /* --- write header line iinto log file */
3369 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
e1c8e0ed 3370 time(&t);
3371 tm = localtime(&t);
3372 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
32874aea 3373 fputs(buf, lgfp);
3374 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
e1c8e0ed 3375 }
3376}
3377
32874aea 3378void logfclose(void)
3379{
3380 if (lgfp) {
3381 fclose(lgfp);
3382 lgfp = NULL;
3383 }
e1c8e0ed 3384}