Oops - fix that fix :-/
[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
32874aea 937 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
938 c = inbuf[inbuf_reap];
374330e2 939
c9def1b8 940 /*
32874aea 941 * Optionally log the session traffic to a file. Useful for
942 * debugging and possibly also useful for actual logging.
943 */
944 logtraffic((unsigned char) c, LGTYP_DEBUG);
e1c8e0ed 945
e14a5a13 946 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
947 * be able to display 8-bit characters, but I'll let that go 'cause
948 * of i18n.
949 */
4eeb7d09 950
951 /* First see about all those translations. */
952 if (termstate == TOPLEVEL) {
8f22582c 953 if (in_utf)
4eeb7d09 954 switch (utf_state) {
955 case 0:
956 if (c < 0x80) {
8f22582c 957 /* UTF-8 must be stateless so we ignore iso2022. */
958 if (unitab_ctrl[c] != 0xFF)
959 c = unitab_ctrl[c];
960 else c = ((unsigned char)c) | ATTR_ASCII;
961 break;
4eeb7d09 962 } else if ((c & 0xe0) == 0xc0) {
963 utf_size = utf_state = 1;
964 utf_char = (c & 0x1f);
965 } else if ((c & 0xf0) == 0xe0) {
966 utf_size = utf_state = 2;
967 utf_char = (c & 0x0f);
968 } else if ((c & 0xf8) == 0xf0) {
969 utf_size = utf_state = 3;
970 utf_char = (c & 0x07);
971 } else if ((c & 0xfc) == 0xf8) {
972 utf_size = utf_state = 4;
973 utf_char = (c & 0x03);
974 } else if ((c & 0xfe) == 0xfc) {
975 utf_size = utf_state = 5;
976 utf_char = (c & 0x01);
977 } else {
978 c = UCSERR;
979 break;
980 }
981 continue;
982 case 1:
983 case 2:
984 case 3:
985 case 4:
986 case 5:
987 if ((c & 0xC0) != 0x80) {
988 inbuf_reap--; /* This causes the faulting character */
989 c = UCSERR; /* to be logged twice - not really a */
990 utf_state = 0; /* serious problem. */
991 break;
992 }
993 utf_char = (utf_char << 6) | (c & 0x3f);
994 if (--utf_state)
995 continue;
996
997 c = utf_char;
998
999 /* Is somebody trying to be evil! */
1000 if (c < 0x80 ||
1001 (c < 0x800 && utf_size >= 2) ||
1002 (c < 0x10000 && utf_size >= 3) ||
1003 (c < 0x200000 && utf_size >= 4) ||
1004 (c < 0x4000000 && utf_size >= 5))
1005 c = UCSERR;
1006
1007 /* Unicode line separator and paragraph separator are CR-LF */
1008 if (c == 0x2028 || c == 0x2029)
1009 c = 0x85;
1010
1011 /* High controls are probably a Baaad idea too. */
1012 if (c < 0xA0)
1013 c = 0xFFFD;
1014
1015 /* The UTF-16 surrogates are not nice either. */
1016 /* The standard give the option of decoding these:
1017 * I don't want to! */
1018 if (c >= 0xD800 && c < 0xE000)
1019 c = UCSERR;
1020
1021 /* ISO 10646 characters now limited to UTF-16 range. */
1022 if (c > 0x10FFFF)
1023 c = UCSERR;
1024
1025 /* This is currently a TagPhobic application.. */
1026 if (c >= 0xE0000 && c <= 0xE007F)
1027 continue;
1028
1029 /* U+FEFF is best seen as a null. */
1030 if (c == 0xFEFF)
1031 continue;
1032 /* But U+FFFE is an error. */
1033 if (c == 0xFFFE || c == 0xFFFF)
1034 c = UCSERR;
1035
1036 /* Oops this is a 16bit implementation */
1037 if (c >= 0x10000)
1038 c = 0xFFFD;
1039 break;
d3cb5465 1040 }
1041 /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
1042 else if(sco_acs &&
1043 (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
1044 {
1045 if (sco_acs == 2) c ^= 0x80;
1046 c |= ATTR_SCOACS;
4eeb7d09 1047 } else {
4eeb7d09 1048 switch (cset_attr[cset]) {
1049 /*
1050 * Linedraw characters are different from 'ESC ( B'
1051 * only for a small range. For ones outside that
1052 * range, make sure we use the same font as well as
1053 * the same encoding.
1054 */
1055 case ATTR_LINEDRW:
1056 if (unitab_ctrl[c] != 0xFF)
1057 c = unitab_ctrl[c];
1058 else
1059 c = ((unsigned char) c) | ATTR_LINEDRW;
1060 break;
1061
1062 case ATTR_GBCHR:
1063 /* If UK-ASCII, make the '#' a LineDraw Pound */
1064 if (c == '#') {
1065 c = '}' | ATTR_LINEDRW;
1066 break;
1067 }
1068 /*FALLTHROUGH*/ case ATTR_ASCII:
1069 if (unitab_ctrl[c] != 0xFF)
1070 c = unitab_ctrl[c];
1071 else
1072 c = ((unsigned char) c) | ATTR_ASCII;
1073 break;
d3cb5465 1074 case ATTR_SCOACS:
1075 if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
1076 break;
4eeb7d09 1077 }
1078 }
1079 }
1080
1081 /* How about C1 controls ? */
1082 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1083 has_compat(VT220)) {
1084 termstate = SEEN_ESC;
1085 esc_query = FALSE;
1086 c = '@' + (c & 0x1F);
1087 }
1088
1089 /* Or the GL control. */
1090 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1091 if (curs.x && !wrapnext)
1092 curs.x--;
1093 wrapnext = FALSE;
1094 fix_cpos;
1095 *cpos = (' ' | curr_attr | ATTR_ASCII);
1096 } else
1097 /* Or normal C0 controls. */
1098 if ((c & -32) == 0 && termstate < DO_CTRLS) {
374330e2 1099 switch (c) {
1100 case '\005': /* terminal type query */
e14a5a13 1101 /* Strictly speaking this is VT100 but a VT100 defaults to
1102 * no response. Other terminals respond at their option.
1103 *
1104 * Don't put a CR in the default string as this tends to
1105 * upset some weird software.
1106 *
1107 * An xterm returns "xterm" (5 characters)
1108 */
32874aea 1109 compatibility(ANSIMIN);
e7fbcdd8 1110 {
1111 char abuf[256], *s, *d;
32874aea 1112 int state = 0;
1113 for (s = cfg.answerback, d = abuf; *s; s++) {
1114 if (state) {
1115 if (*s >= 'a' && *s <= 'z')
1116 *d++ = (*s - ('a' - 1));
1117 else if ((*s >= '@' && *s <= '_') ||
1118 *s == '?' || (*s & 0x80))
1119 *d++ = ('@' ^ *s);
1120 else if (*s == '~')
1121 *d++ = '^';
1122 state = 0;
1123 } else if (*s == '^') {
1124 state = 1;
1125 } else
4eeb7d09 1126 *d++ = *s;
e7fbcdd8 1127 }
4eeb7d09 1128 lpage_send(CP_ACP, abuf, d - abuf);
e7fbcdd8 1129 }
374330e2 1130 break;
1131 case '\007':
156686ef 1132 {
1133 struct beeptime *newbeep;
1134 long ticks;
1135
1136 ticks = GetTickCount();
156686ef 1137
1138 if (!beep_overloaded) {
1139 newbeep = smalloc(sizeof(struct beeptime));
1140 newbeep->ticks = ticks;
1141 newbeep->next = NULL;
1142 if (!beephead)
1143 beephead = newbeep;
1144 else
1145 beeptail->next = newbeep;
1146 beeptail = newbeep;
1147 nbeeps++;
1148 }
1149
1150 /*
1151 * Throw out any beeps that happened more than
1152 * t seconds ago.
1153 */
1154 while (beephead &&
7f4968e6 1155 beephead->ticks < ticks - cfg.bellovl_t) {
156686ef 1156 struct beeptime *tmp = beephead;
1157 beephead = tmp->next;
156686ef 1158 sfree(tmp);
1159 if (!beephead)
1160 beeptail = NULL;
1161 nbeeps--;
1162 }
1163
1164 if (cfg.bellovl && beep_overloaded &&
32874aea 1165 ticks - lastbeep >= cfg.bellovl_s) {
156686ef 1166 /*
1167 * If we're currently overloaded and the
1168 * last beep was more than s seconds ago,
1169 * leave overload mode.
1170 */
156686ef 1171 beep_overloaded = FALSE;
1172 } else if (cfg.bellovl && !beep_overloaded &&
1173 nbeeps >= cfg.bellovl_n) {
1174 /*
1175 * Now, if we have n or more beeps
1176 * remaining in the queue, go into overload
1177 * mode.
1178 */
156686ef 1179 beep_overloaded = TRUE;
1180 }
1181 lastbeep = ticks;
1182
1183 /*
1184 * Perform an actual beep if we're not overloaded.
1185 */
f8a28d1f 1186 if (!cfg.bellovl || !beep_overloaded) {
1187 beep(cfg.beep);
1188 if (cfg.beep == BELL_VISUAL) {
156686ef 1189 in_vbell = TRUE;
1190 vbell_timeout = ticks + VBELL_TIMEOUT;
1191 term_update();
1192 }
1193 }
4facdf84 1194 disptop = 0;
156686ef 1195 }
374330e2 1196 break;
1197 case '\b':
967a161e 1198 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
4facdf84 1199 else if (curs.x == 0 && curs.y > 0)
32874aea 1200 curs.x = cols - 1, curs.y--;
374330e2 1201 else if (wrapnext)
1202 wrapnext = FALSE;
1203 else
4facdf84 1204 curs.x--;
374330e2 1205 fix_cpos;
cabfd08c 1206 seen_disp_event = TRUE;
374330e2 1207 break;
1208 case '\016':
32874aea 1209 compatibility(VT100);
374330e2 1210 cset = 1;
1211 break;
1212 case '\017':
32874aea 1213 compatibility(VT100);
374330e2 1214 cset = 0;
1215 break;
1216 case '\033':
32874aea 1217 if (vt52_mode)
1218 termstate = VT52_ESC;
e14a5a13 1219 else {
1220 compatibility(ANSIMIN);
1221 termstate = SEEN_ESC;
4eeb7d09 1222 esc_query = FALSE;
e14a5a13 1223 }
374330e2 1224 break;
374330e2 1225 case '\r':
4facdf84 1226 curs.x = 0;
374330e2 1227 wrapnext = FALSE;
1228 fix_cpos;
cabfd08c 1229 seen_disp_event = TRUE;
c9def1b8 1230 paste_hold = 0;
32874aea 1231 logtraffic((unsigned char) c, LGTYP_ASCII);
374330e2 1232 break;
374330e2 1233 case '\014':
1a837633 1234 if (has_compat(SCOANSI)) {
1235 move(0, 0, 0);
1236 erase_lots(FALSE, FALSE, TRUE);
1237 disptop = 0;
1238 wrapnext = FALSE;
1239 seen_disp_event = 1;
1240 break;
1241 }
1242 case '\013':
32874aea 1243 compatibility(VT100);
374330e2 1244 case '\n':
4facdf84 1245 if (curs.y == marg_b)
32874aea 1246 scroll(marg_t, marg_b, 1, TRUE);
1247 else if (curs.y < rows - 1)
4facdf84 1248 curs.y++;
cabfd08c 1249 if (cfg.lfhascr)
4facdf84 1250 curs.x = 0;
374330e2 1251 fix_cpos;
1252 wrapnext = FALSE;
cabfd08c 1253 seen_disp_event = 1;
c9def1b8 1254 paste_hold = 0;
32874aea 1255 logtraffic((unsigned char) c, LGTYP_ASCII);
374330e2 1256 break;
1257 case '\t':
374330e2 1258 {
4facdf84 1259 pos old_curs = curs;
1260 unsigned long *ldata = lineptr(curs.y);
c9def1b8 1261
1262 do {
4facdf84 1263 curs.x++;
32874aea 1264 } while (curs.x < cols - 1 && !tabs[curs.x]);
c9def1b8 1265
32874aea 1266 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1267 if (curs.x >= cols / 2)
1268 curs.x = cols / 2 - 1;
1269 } else {
4facdf84 1270 if (curs.x >= cols)
32874aea 1271 curs.x = cols - 1;
c9def1b8 1272 }
1273
374330e2 1274 fix_cpos;
32874aea 1275 check_selection(old_curs, curs);
374330e2 1276 }
cabfd08c 1277 seen_disp_event = TRUE;
374330e2 1278 break;
cabfd08c 1279 }
32874aea 1280 } else
1281 switch (termstate) {
1282 case TOPLEVEL:
1283 /* Only graphic characters get this far, ctrls are stripped above */
1284 if (wrapnext && wrap) {
4eeb7d09 1285 cpos[1] |= LATTR_WRAPPED;
32874aea 1286 if (curs.y == marg_b)
1287 scroll(marg_t, marg_b, 1, TRUE);
1288 else if (curs.y < rows - 1)
1289 curs.y++;
1290 curs.x = 0;
1291 fix_cpos;
1292 wrapnext = FALSE;
374330e2 1293 }
32874aea 1294 if (insert)
1295 insch(1);
1296 if (selstate != NO_SELECTION) {
1297 pos cursplus = curs;
1298 incpos(cursplus);
1299 check_selection(curs, cursplus);
374330e2 1300 }
4eeb7d09 1301 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1302 logtraffic((unsigned char) c, LGTYP_ASCII);
1303 {
1304 extern int wcwidth(wchar_t ucs);
1305 int width = 0;
1306 if (DIRECT_CHAR(c))
1307 width = 1;
1308 if (!width)
1309 width = wcwidth((wchar_t) c);
1310 switch (width) {
1311 case 2:
1312 if (curs.x + 1 != cols) {
1313 *cpos++ = c | ATTR_WIDE | curr_attr;
1314 *cpos++ = UCSWIDE | curr_attr;
1315 curs.x++;
1316 break;
1317 }
1318 case 1:
1319 *cpos++ = c | curr_attr;
32874aea 1320 break;
4eeb7d09 1321 default:
1322 continue;
32874aea 1323 }
374330e2 1324 }
32874aea 1325 curs.x++;
1326 if (curs.x == cols) {
1327 cpos--;
1328 curs.x--;
1329 wrapnext = TRUE;
4eeb7d09 1330 if (wrap && vt52_mode) {
1331 cpos[1] |= LATTR_WRAPPED;
1332 if (curs.y == marg_b)
1333 scroll(marg_t, marg_b, 1, TRUE);
1334 else if (curs.y < rows - 1)
1335 curs.y++;
1336 curs.x = 0;
1337 fix_cpos;
1338 wrapnext = FALSE;
1339 }
e14a5a13 1340 }
32874aea 1341 seen_disp_event = 1;
374330e2 1342 break;
32874aea 1343
32874aea 1344 case OSC_MAYBE_ST:
1345 /*
1346 * This state is virtually identical to SEEN_ESC, with the
1347 * exception that we have an OSC sequence in the pipeline,
1348 * and _if_ we see a backslash, we process it.
1349 */
1350 if (c == '\\') {
1351 do_osc();
1352 termstate = TOPLEVEL;
1353 break;
e14a5a13 1354 }
32874aea 1355 /* else fall through */
1356 case SEEN_ESC:
4eeb7d09 1357 if (c >= ' ' && c <= '/') {
1358 if (esc_query)
1359 esc_query = -1;
1360 else
1361 esc_query = c;
32874aea 1362 break;
4eeb7d09 1363 }
1364 termstate = TOPLEVEL;
1365 switch (ANSI(c, esc_query)) {
32874aea 1366 case '[': /* enter CSI mode */
1367 termstate = SEEN_CSI;
1368 esc_nargs = 1;
1369 esc_args[0] = ARG_DEFAULT;
1370 esc_query = FALSE;
1371 break;
1372 case ']': /* xterm escape sequences */
1373 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1374 compatibility(OTHER);
1375 termstate = SEEN_OSC;
1376 esc_args[0] = 0;
1377 break;
32874aea 1378 case '7': /* save cursor */
1379 compatibility(VT100);
1380 save_cursor(TRUE);
1381 break;
1382 case '8': /* restore cursor */
1383 compatibility(VT100);
1384 save_cursor(FALSE);
1385 seen_disp_event = TRUE;
1386 break;
1387 case '=':
1388 compatibility(VT100);
1389 app_keypad_keys = TRUE;
1390 break;
1391 case '>':
1392 compatibility(VT100);
1393 app_keypad_keys = FALSE;
1394 break;
1395 case 'D': /* exactly equivalent to LF */
1396 compatibility(VT100);
1397 if (curs.y == marg_b)
1398 scroll(marg_t, marg_b, 1, TRUE);
1399 else if (curs.y < rows - 1)
1400 curs.y++;
1401 fix_cpos;
1402 wrapnext = FALSE;
1403 seen_disp_event = TRUE;
1404 break;
1405 case 'E': /* exactly equivalent to CR-LF */
1406 compatibility(VT100);
1407 curs.x = 0;
1408 if (curs.y == marg_b)
1409 scroll(marg_t, marg_b, 1, TRUE);
1410 else if (curs.y < rows - 1)
1411 curs.y++;
1412 fix_cpos;
1413 wrapnext = FALSE;
1414 seen_disp_event = TRUE;
1415 break;
1416 case 'M': /* reverse index - backwards LF */
1417 compatibility(VT100);
1418 if (curs.y == marg_t)
1419 scroll(marg_t, marg_b, -1, TRUE);
1420 else if (curs.y > 0)
1421 curs.y--;
1422 fix_cpos;
1423 wrapnext = FALSE;
1424 seen_disp_event = TRUE;
1425 break;
1426 case 'Z': /* terminal type query */
1427 compatibility(VT100);
1428 ldisc_send(id_string, strlen(id_string));
1429 break;
1430 case 'c': /* restore power-on settings */
1431 compatibility(VT100);
1432 power_on();
1433 if (reset_132) {
1434 request_resize(80, rows, 1);
1435 reset_132 = 0;
374330e2 1436 }
32874aea 1437 fix_cpos;
1438 disptop = 0;
1439 seen_disp_event = TRUE;
1440 break;
32874aea 1441 case 'H': /* set a tab */
1442 compatibility(VT100);
1443 tabs[curs.x] = TRUE;
1444 break;
4eeb7d09 1445
1446 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1447 compatibility(VT100);
1448 {
1449 unsigned long *ldata;
1450 int i, j;
1451 pos scrtop, scrbot;
1452
1453 for (i = 0; i < rows; i++) {
1454 ldata = lineptr(i);
1455 for (j = 0; j < cols; j++)
1456 ldata[j] = ATTR_DEFAULT | 'E';
1457 ldata[cols] = 0;
1458 }
1459 disptop = 0;
1460 seen_disp_event = TRUE;
1461 scrtop.x = scrtop.y = 0;
1462 scrbot.x = 0;
1463 scrbot.y = rows;
1464 check_selection(scrtop, scrbot);
1465 }
1466 break;
1467
1468 case ANSI('3', '#'):
1469 case ANSI('4', '#'):
1470 case ANSI('5', '#'):
1471 case ANSI('6', '#'):
1472 compatibility(VT100);
1473 {
1474 unsigned long nlattr;
1475 unsigned long *ldata;
1476 switch (ANSI(c, esc_query)) {
1477 case ANSI('3', '#'):
1478 nlattr = LATTR_TOP;
1479 break;
1480 case ANSI('4', '#'):
1481 nlattr = LATTR_BOT;
1482 break;
1483 case ANSI('5', '#'):
1484 nlattr = LATTR_NORM;
1485 break;
2d466ffd 1486 default: /* spiritually case ANSI('6', '#'): */
4eeb7d09 1487 nlattr = LATTR_WIDE;
1488 break;
1489 }
1490 ldata = lineptr(curs.y);
1491 ldata[cols] &= ~LATTR_MODE;
1492 ldata[cols] |= nlattr;
1493 }
1494 break;
1495
1496 case ANSI('A', '('):
1497 compatibility(VT100);
1498 cset_attr[0] = ATTR_GBCHR;
1499 break;
1500 case ANSI('B', '('):
1501 compatibility(VT100);
1502 cset_attr[0] = ATTR_ASCII;
1503 break;
1504 case ANSI('0', '('):
1505 compatibility(VT100);
1506 cset_attr[0] = ATTR_LINEDRW;
1507 break;
d3cb5465 1508 case ANSI('U', '('):
1509 compatibility(OTHER);
1510 cset_attr[0] = ATTR_SCOACS;
1511 break;
4eeb7d09 1512
1513 case ANSI('A', ')'):
1514 compatibility(VT100);
1515 cset_attr[1] = ATTR_GBCHR;
1516 break;
1517 case ANSI('B', ')'):
1518 compatibility(VT100);
1519 cset_attr[1] = ATTR_ASCII;
1520 break;
1521 case ANSI('0', ')'):
1522 compatibility(VT100);
1523 cset_attr[1] = ATTR_LINEDRW;
1524 break;
d3cb5465 1525 case ANSI('U', ')'):
1526 compatibility(OTHER);
1527 cset_attr[1] = ATTR_SCOACS;
1528 break;
4eeb7d09 1529
1530 case ANSI('8', '%'): /* Old Linux code */
1531 case ANSI('G', '%'):
1532 compatibility(OTHER);
1533 utf = 1;
1534 break;
1535 case ANSI('@', '%'):
1536 compatibility(OTHER);
8f22582c 1537 utf = 0;
4eeb7d09 1538 break;
374330e2 1539 }
1540 break;
32874aea 1541 case SEEN_CSI:
1542 termstate = TOPLEVEL; /* default */
1543 if (isdigit(c)) {
1544 if (esc_nargs <= ARGS_MAX) {
1545 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1546 esc_args[esc_nargs - 1] = 0;
1547 esc_args[esc_nargs - 1] =
1548 10 * esc_args[esc_nargs - 1] + c - '0';
1549 }
1550 termstate = SEEN_CSI;
1551 } else if (c == ';') {
1552 if (++esc_nargs <= ARGS_MAX)
1553 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1554 termstate = SEEN_CSI;
1555 } else if (c < '@') {
1556 if (esc_query)
1557 esc_query = -1;
1558 else if (c == '?')
1559 esc_query = TRUE;
1560 else
1561 esc_query = c;
1562 termstate = SEEN_CSI;
1563 } else
1564 switch (ANSI(c, esc_query)) {
1565 case 'A': /* move up N lines */
1566 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1567 seen_disp_event = TRUE;
1568 break;
1569 case 'e': /* move down N lines */
1570 compatibility(ANSI);
5442e92f 1571 /* FALLTHROUGH */
32874aea 1572 case 'B':
1573 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1574 seen_disp_event = TRUE;
1575 break;
32874aea 1576 case ANSI('c', '>'): /* report xterm version */
1577 compatibility(OTHER);
1578 /* this reports xterm version 136 so that VIM can
1579 use the drag messages from the mouse reporting */
1580 ldisc_send("\033[>0;136;0c", 11);
1581 break;
5442e92f 1582 case 'a': /* move right N cols */
1583 compatibility(ANSI);
1584 /* FALLTHROUGH */
32874aea 1585 case 'C':
1586 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1587 seen_disp_event = TRUE;
1588 break;
1589 case 'D': /* move left N cols */
1590 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1591 seen_disp_event = TRUE;
1592 break;
1593 case 'E': /* move down N lines and CR */
1594 compatibility(ANSI);
1595 move(0, curs.y + def(esc_args[0], 1), 1);
1596 seen_disp_event = TRUE;
1597 break;
1598 case 'F': /* move up N lines and CR */
1599 compatibility(ANSI);
1600 move(0, curs.y - def(esc_args[0], 1), 1);
1601 seen_disp_event = TRUE;
1602 break;
1603 case 'G':
1604 case '`': /* set horizontal posn */
1605 compatibility(ANSI);
1606 move(def(esc_args[0], 1) - 1, curs.y, 0);
1607 seen_disp_event = TRUE;
1608 break;
1609 case 'd': /* set vertical posn */
1610 compatibility(ANSI);
1611 move(curs.x,
1612 (dec_om ? marg_t : 0) + def(esc_args[0],
1613 1) - 1,
1614 (dec_om ? 2 : 0));
1615 seen_disp_event = TRUE;
1616 break;
1617 case 'H':
1618 case 'f': /* set horz and vert posns at once */
1619 if (esc_nargs < 2)
1620 esc_args[1] = ARG_DEFAULT;
1621 move(def(esc_args[1], 1) - 1,
1622 (dec_om ? marg_t : 0) + def(esc_args[0],
1623 1) - 1,
1624 (dec_om ? 2 : 0));
1625 seen_disp_event = TRUE;
1626 break;
1627 case 'J': /* erase screen or parts of it */
1628 {
1629 unsigned int i = def(esc_args[0], 0) + 1;
1630 if (i > 3)
1631 i = 0;
1632 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1633 }
1634 disptop = 0;
1635 seen_disp_event = TRUE;
1636 break;
1637 case 'K': /* erase line or parts of it */
1638 {
1639 unsigned int i = def(esc_args[0], 0) + 1;
1640 if (i > 3)
1641 i = 0;
1642 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1643 }
1644 seen_disp_event = TRUE;
1645 break;
1646 case 'L': /* insert lines */
1647 compatibility(VT102);
1648 if (curs.y <= marg_b)
1649 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1650 FALSE);
1651 fix_cpos;
1652 seen_disp_event = TRUE;
1653 break;
1654 case 'M': /* delete lines */
1655 compatibility(VT102);
1656 if (curs.y <= marg_b)
1657 scroll(curs.y, marg_b, def(esc_args[0], 1),
1658 TRUE);
1659 fix_cpos;
1660 seen_disp_event = TRUE;
1661 break;
1662 case '@': /* insert chars */
1663 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1664 compatibility(VT102);
1665 insch(def(esc_args[0], 1));
1666 seen_disp_event = TRUE;
1667 break;
1668 case 'P': /* delete chars */
1669 compatibility(VT102);
1670 insch(-def(esc_args[0], 1));
1671 seen_disp_event = TRUE;
1672 break;
1673 case 'c': /* terminal type query */
1674 compatibility(VT100);
1675 /* This is the response for a VT102 */
1676 ldisc_send(id_string, strlen(id_string));
1677 break;
1678 case 'n': /* cursor position query */
1679 if (esc_args[0] == 6) {
1680 char buf[32];
1681 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1682 curs.x + 1);
1683 ldisc_send(buf, strlen(buf));
1684 } else if (esc_args[0] == 5) {
1685 ldisc_send("\033[0n", 4);
1686 }
1687 break;
1688 case 'h': /* toggle modes to high */
1689 case ANSI_QUE('h'):
1690 compatibility(VT100);
1691 {
1692 int i;
1693 for (i = 0; i < esc_nargs; i++)
1694 toggle_mode(esc_args[i], esc_query, TRUE);
1695 }
1696 break;
1697 case 'l': /* toggle modes to low */
1698 case ANSI_QUE('l'):
1699 compatibility(VT100);
1700 {
1701 int i;
1702 for (i = 0; i < esc_nargs; i++)
1703 toggle_mode(esc_args[i], esc_query, FALSE);
1704 }
1705 break;
1706 case 'g': /* clear tabs */
1707 compatibility(VT100);
1708 if (esc_nargs == 1) {
1709 if (esc_args[0] == 0) {
1710 tabs[curs.x] = FALSE;
1711 } else if (esc_args[0] == 3) {
1712 int i;
1713 for (i = 0; i < cols; i++)
1714 tabs[i] = FALSE;
1715 }
1716 }
1717 break;
1718 case 'r': /* set scroll margins */
1719 compatibility(VT100);
1720 if (esc_nargs <= 2) {
1721 int top, bot;
1722 top = def(esc_args[0], 1) - 1;
1723 bot = (esc_nargs <= 1
1724 || esc_args[1] ==
1725 0 ? rows : def(esc_args[1], rows)) - 1;
1726 if (bot >= rows)
1727 bot = rows - 1;
1728 /* VTTEST Bug 9 - if region is less than 2 lines
1729 * don't change region.
1730 */
1731 if (bot - top > 0) {
1732 marg_t = top;
1733 marg_b = bot;
1734 curs.x = 0;
1735 /*
1736 * I used to think the cursor should be
1737 * placed at the top of the newly marginned
1738 * area. Apparently not: VMS TPU falls over
1739 * if so.
1740 *
1741 * Well actually it should for Origin mode - RDB
1742 */
1743 curs.y = (dec_om ? marg_t : 0);
1744 fix_cpos;
1745 seen_disp_event = TRUE;
1746 }
1747 }
1748 break;
1749 case 'm': /* set graphics rendition */
1750 {
1751 /*
1752 * A VT100 without the AVO only had one attribute, either
1753 * underline or reverse video depending on the cursor type,
1754 * this was selected by CSI 7m.
1755 *
1756 * case 2:
4eeb7d09 1757 * This is sometimes DIM, eg on the GIGI and Linux
32874aea 1758 * case 8:
4eeb7d09 1759 * This is sometimes INVIS various ANSI.
32874aea 1760 * case 21:
1761 * This like 22 disables BOLD, DIM and INVIS
1762 *
1763 * The ANSI colours appear on any terminal that has colour
1764 * (obviously) but the interaction between sgr0 and the
1765 * colours varies but is usually related to the background
1766 * colour erase item.
1767 * The interaction between colour attributes and the mono
1768 * ones is also very implementation dependent.
1769 *
1770 * The 39 and 49 attributes are likely to be unimplemented.
1771 */
1772 int i;
1773 for (i = 0; i < esc_nargs; i++) {
1774 switch (def(esc_args[i], 0)) {
1775 case 0: /* restore defaults */
1776 curr_attr = ATTR_DEFAULT;
1777 break;
1778 case 1: /* enable bold */
1779 compatibility(VT100AVO);
1780 curr_attr |= ATTR_BOLD;
1781 break;
1782 case 21: /* (enable double underline) */
1783 compatibility(OTHER);
1784 case 4: /* enable underline */
1785 compatibility(VT100AVO);
1786 curr_attr |= ATTR_UNDER;
1787 break;
1788 case 5: /* enable blink */
1789 compatibility(VT100AVO);
1790 curr_attr |= ATTR_BLINK;
1791 break;
1792 case 7: /* enable reverse video */
1793 curr_attr |= ATTR_REVERSE;
1794 break;
d3cb5465 1795 case 10: /* SCO acs off */
1796 compatibility(SCOANSI);
1797 sco_acs = 0; break;
1798 case 11: /* SCO acs on */
1799 compatibility(SCOANSI);
1800 sco_acs = 1; break;
1801 case 12: /* SCO acs on flipped */
1802 compatibility(SCOANSI);
1803 sco_acs = 2; break;
32874aea 1804 case 22: /* disable bold */
1805 compatibility2(OTHER, VT220);
1806 curr_attr &= ~ATTR_BOLD;
1807 break;
1808 case 24: /* disable underline */
1809 compatibility2(OTHER, VT220);
1810 curr_attr &= ~ATTR_UNDER;
1811 break;
1812 case 25: /* disable blink */
1813 compatibility2(OTHER, VT220);
1814 curr_attr &= ~ATTR_BLINK;
1815 break;
1816 case 27: /* disable reverse video */
1817 compatibility2(OTHER, VT220);
1818 curr_attr &= ~ATTR_REVERSE;
1819 break;
1820 case 30:
1821 case 31:
1822 case 32:
1823 case 33:
1824 case 34:
1825 case 35:
1826 case 36:
1827 case 37:
1828 /* foreground */
1829 curr_attr &= ~ATTR_FGMASK;
1830 curr_attr |=
1831 (esc_args[i] - 30) << ATTR_FGSHIFT;
1832 break;
1833 case 39: /* default-foreground */
1834 curr_attr &= ~ATTR_FGMASK;
1835 curr_attr |= ATTR_DEFFG;
1836 break;
1837 case 40:
1838 case 41:
1839 case 42:
1840 case 43:
1841 case 44:
1842 case 45:
1843 case 46:
1844 case 47:
1845 /* background */
1846 curr_attr &= ~ATTR_BGMASK;
1847 curr_attr |=
1848 (esc_args[i] - 40) << ATTR_BGSHIFT;
1849 break;
1850 case 49: /* default-background */
1851 curr_attr &= ~ATTR_BGMASK;
1852 curr_attr |= ATTR_DEFBG;
1853 break;
1854 }
1855 }
1856 if (use_bce)
8f22582c 1857 erase_char = (' ' | ATTR_ASCII |
1858 (curr_attr &
1859 (ATTR_FGMASK | ATTR_BGMASK)));
32874aea 1860 }
1861 break;
1862 case 's': /* save cursor */
1863 save_cursor(TRUE);
1864 break;
1865 case 'u': /* restore cursor */
1866 save_cursor(FALSE);
1867 seen_disp_event = TRUE;
1868 break;
1869 case 't': /* set page size - ie window height */
374330e2 1870 /*
32874aea 1871 * VT340/VT420 sequence DECSLPP, DEC only allows values
1872 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1873 * illegal values (eg first arg 1..9) for window changing
1874 * and reports.
1875 */
1876 compatibility(VT340TEXT);
1877 if (esc_nargs <= 1
1878 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1879 request_resize(cols, def(esc_args[0], 24), 0);
1880 deselect();
1881 }
1882 break;
1a837633 1883 case 'S':
1884 compatibility(SCOANSI);
1885 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1886 fix_cpos;
1887 wrapnext = FALSE;
1888 seen_disp_event = TRUE;
1889 break;
1890 case 'T':
1891 compatibility(SCOANSI);
1892 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1893 fix_cpos;
1894 wrapnext = FALSE;
1895 seen_disp_event = TRUE;
1896 break;
32874aea 1897 case ANSI('|', '*'):
1898 /* VT420 sequence DECSNLS
1899 * Set number of lines on screen
1900 * VT420 uses VGA like hardware and can support any size in
1901 * reasonable range (24..49 AIUI) with no default specified.
1902 */
1903 compatibility(VT420);
1904 if (esc_nargs == 1 && esc_args[0] > 0) {
1905 request_resize(cols,
1906 def(esc_args[0], cfg.height),
1907 0);
1908 deselect();
1909 }
1910 break;
1911 case ANSI('|', '$'):
1912 /* VT340/VT420 sequence DECSCPP
1913 * Set number of columns per page
1914 * Docs imply range is only 80 or 132, but I'll allow any.
1915 */
1916 compatibility(VT340TEXT);
1917 if (esc_nargs <= 1) {
1918 request_resize(def(esc_args[0], cfg.width),
1919 rows, 0);
1920 deselect();
1921 }
1922 break;
1923 case 'X': /* write N spaces w/o moving cursor */
1924 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1925 compatibility(ANSIMIN);
1926 {
1927 int n = def(esc_args[0], 1);
1928 pos cursplus;
1929 unsigned long *p = cpos;
1930 if (n > cols - curs.x)
1931 n = cols - curs.x;
1932 cursplus = curs;
1933 cursplus.x += n;
1934 check_selection(curs, cursplus);
1935 while (n--)
1936 *p++ = erase_char;
1937 seen_disp_event = TRUE;
1938 }
1939 break;
1940 case 'x': /* report terminal characteristics */
1941 compatibility(VT100);
1942 {
1943 char buf[32];
1944 int i = def(esc_args[0], 0);
1945 if (i == 0 || i == 1) {
1946 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1947 buf[2] += i;
1948 ldisc_send(buf, 20);
1949 }
1950 }
1951 break;
979f6987 1952 case 'Z': /* BackTab for xterm */
1953 compatibility(OTHER);
1954 {
1955 int i = def(esc_args[0], 1);
1956 pos old_curs = curs;
1957
1958 for(;i>0 && curs.x>0; i--) {
1959 do {
1960 curs.x--;
1961 } while (curs.x >0 && !tabs[curs.x]);
1962 }
1963 fix_cpos;
1964 check_selection(old_curs, curs);
1965 }
1966 break;
32874aea 1967 case ANSI('L', '='):
1968 compatibility(OTHER);
1969 use_bce = (esc_args[0] <= 0);
1970 erase_char = ERASE_CHAR;
1971 if (use_bce)
8f22582c 1972 erase_char = (' ' | ATTR_ASCII |
1973 (curr_attr &
1974 (ATTR_FGMASK | ATTR_BGMASK)));
32874aea 1975 break;
1976 case ANSI('E', '='):
1977 compatibility(OTHER);
1978 blink_is_real = (esc_args[0] >= 1);
1979 break;
1980 case ANSI('p', '"'):
1981 /* Allow the host to make this emulator a 'perfect' VT102.
1982 * This first appeared in the VT220, but we do need to get
1983 * back to PuTTY mode so I won't check it.
e14a5a13 1984 *
4eeb7d09 1985 * The arg in 40..42,50 are a PuTTY extension.
32874aea 1986 * The 2nd arg, 8bit vs 7bit is not checked.
1987 *
1988 * Setting VT102 mode should also change the Fkeys to
1989 * generate PF* codes as a real VT102 has no Fkeys.
1990 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1991 * send nothing.
1992 *
1993 * Note ESC c will NOT change this!
374330e2 1994 */
32874aea 1995
1996 switch (esc_args[0]) {
1997 case 61:
1998 compatibility_level &= ~TM_VTXXX;
1999 compatibility_level |= TM_VT102;
374330e2 2000 break;
32874aea 2001 case 62:
2002 compatibility_level &= ~TM_VTXXX;
2003 compatibility_level |= TM_VT220;
2004 break;
2005
2006 default:
2007 if (esc_args[0] > 60 && esc_args[0] < 70)
2008 compatibility_level |= TM_VTXXX;
2009 break;
2010
2011 case 40:
2012 compatibility_level &= TM_VTXXX;
374330e2 2013 break;
32874aea 2014 case 41:
2015 compatibility_level = TM_PUTTY;
374330e2 2016 break;
32874aea 2017 case 42:
2018 compatibility_level = TM_SCOANSI;
374330e2 2019 break;
32874aea 2020
2021 case ARG_DEFAULT:
2022 compatibility_level = TM_PUTTY;
2023 break;
2024 case 50:
2025 break;
2026 }
2027
2028 /* Change the response to CSI c */
2029 if (esc_args[0] == 50) {
2030 int i;
2031 char lbuf[64];
2032 strcpy(id_string, "\033[?");
2033 for (i = 1; i < esc_nargs; i++) {
2034 if (i != 1)
2035 strcat(id_string, ";");
2036 sprintf(lbuf, "%d", esc_args[i]);
2037 strcat(id_string, lbuf);
2038 }
2039 strcat(id_string, "c");
2040 }
2041#if 0
2042 /* Is this a good idea ?
2043 * Well we should do a soft reset at this point ...
2044 */
2045 if (!has_compat(VT420) && has_compat(VT100)) {
2046 if (reset_132)
2047 request_resize(132, 24, 1);
2048 else
2049 request_resize(80, 24, 1);
374330e2 2050 }
32874aea 2051#endif
2052 break;
374330e2 2053 }
374330e2 2054 break;
32874aea 2055 case SEEN_OSC:
2056 osc_w = FALSE;
2057 switch (c) {
2058 case 'P': /* Linux palette sequence */
2059 termstate = SEEN_OSC_P;
2060 osc_strlen = 0;
2061 break;
2062 case 'R': /* Linux palette reset */
2063 palette_reset();
2064 term_invalidate();
2065 termstate = TOPLEVEL;
2066 break;
2067 case 'W': /* word-set */
2068 termstate = SEEN_OSC_W;
2069 osc_w = TRUE;
2070 break;
2071 case '0':
2072 case '1':
2073 case '2':
2074 case '3':
2075 case '4':
2076 case '5':
2077 case '6':
2078 case '7':
2079 case '8':
2080 case '9':
2081 esc_args[0] = 10 * esc_args[0] + c - '0';
2082 break;
2083 case 'L':
2084 /*
2085 * Grotty hack to support xterm and DECterm title
2086 * sequences concurrently.
2087 */
2088 if (esc_args[0] == 2) {
2089 esc_args[0] = 1;
2090 break;
2091 }
2092 /* else fall through */
2093 default:
2094 termstate = OSC_STRING;
2095 osc_strlen = 0;
e14a5a13 2096 }
2097 break;
32874aea 2098 case OSC_STRING:
2099 /*
2100 * This OSC stuff is EVIL. It takes just one character to get into
2101 * sysline mode and it's not initially obvious how to get out.
2102 * So I've added CR and LF as string aborts.
2103 * This shouldn't effect compatibility as I believe embedded
2104 * control characters are supposed to be interpreted (maybe?)
2105 * and they don't display anything useful anyway.
2106 *
2107 * -- RDB
e14a5a13 2108 */
32874aea 2109 if (c == '\n' || c == '\r') {
2110 termstate = TOPLEVEL;
2111 } else if (c == 0234 || c == '\007') {
2112 /*
2113 * These characters terminate the string; ST and BEL
2114 * terminate the sequence and trigger instant
2115 * processing of it, whereas ESC goes back to SEEN_ESC
2116 * mode unless it is followed by \, in which case it is
2117 * synonymous with ST in the first place.
2118 */
2119 do_osc();
2120 termstate = TOPLEVEL;
2121 } else if (c == '\033')
2122 termstate = OSC_MAYBE_ST;
2123 else if (osc_strlen < OSC_STR_MAX)
2124 osc_string[osc_strlen++] = c;
374330e2 2125 break;
32874aea 2126 case SEEN_OSC_P:
374330e2 2127 {
32874aea 2128 int max = (osc_strlen == 0 ? 21 : 16);
2129 int val;
2130 if (c >= '0' && c <= '9')
2131 val = c - '0';
2132 else if (c >= 'A' && c <= 'A' + max - 10)
2133 val = c - 'A' + 10;
2134 else if (c >= 'a' && c <= 'a' + max - 10)
2135 val = c - 'a' + 10;
2d466ffd 2136 else {
32874aea 2137 termstate = TOPLEVEL;
2d466ffd 2138 break;
2139 }
32874aea 2140 osc_string[osc_strlen++] = val;
2141 if (osc_strlen >= 7) {
2142 palette_set(osc_string[0],
2143 osc_string[1] * 16 + osc_string[2],
2144 osc_string[3] * 16 + osc_string[4],
2145 osc_string[5] * 16 + osc_string[6]);
2146 term_invalidate();
2147 termstate = TOPLEVEL;
374330e2 2148 }
2149 }
2150 break;
32874aea 2151 case SEEN_OSC_W:
2152 switch (c) {
2153 case '0':
2154 case '1':
2155 case '2':
2156 case '3':
2157 case '4':
2158 case '5':
2159 case '6':
2160 case '7':
2161 case '8':
2162 case '9':
2163 esc_args[0] = 10 * esc_args[0] + c - '0';
2164 break;
2165 default:
2166 termstate = OSC_STRING;
2167 osc_strlen = 0;
ec55b220 2168 }
32874aea 2169 break;
32874aea 2170 case VT52_ESC:
374330e2 2171 termstate = TOPLEVEL;
32874aea 2172 seen_disp_event = TRUE;
c9def1b8 2173 switch (c) {
32874aea 2174 case 'A':
2175 move(curs.x, curs.y - 1, 1);
2176 break;
2177 case 'B':
2178 move(curs.x, curs.y + 1, 1);
2179 break;
2180 case 'C':
2181 move(curs.x + 1, curs.y, 1);
2182 break;
2183 case 'D':
2184 move(curs.x - 1, curs.y, 1);
2185 break;
4eeb7d09 2186 /*
2187 * From the VT100 Manual
2188 * NOTE: The special graphics characters in the VT100
2189 * are different from those in the VT52
2190 *
2191 * From VT102 manual:
2192 * 137 _ Blank - Same
2193 * 140 ` Reserved - Humm.
2194 * 141 a Solid rectangle - Similar
2195 * 142 b 1/ - Top half of fraction for the
2196 * 143 c 3/ - subscript numbers below.
2197 * 144 d 5/
2198 * 145 e 7/
2199 * 146 f Degrees - Same
2200 * 147 g Plus or minus - Same
2201 * 150 h Right arrow
2202 * 151 i Ellipsis (dots)
2203 * 152 j Divide by
2204 * 153 k Down arrow
2205 * 154 l Bar at scan 0
2206 * 155 m Bar at scan 1
2207 * 156 n Bar at scan 2
2208 * 157 o Bar at scan 3 - Similar
2209 * 160 p Bar at scan 4 - Similar
2210 * 161 q Bar at scan 5 - Similar
2211 * 162 r Bar at scan 6 - Same
2212 * 163 s Bar at scan 7 - Similar
2213 * 164 t Subscript 0
2214 * 165 u Subscript 1
2215 * 166 v Subscript 2
2216 * 167 w Subscript 3
2217 * 170 x Subscript 4
2218 * 171 y Subscript 5
2219 * 172 z Subscript 6
2220 * 173 { Subscript 7
2221 * 174 | Subscript 8
2222 * 175 } Subscript 9
2223 * 176 ~ Paragraph
2224 *
2225 */
32874aea 2226 case 'F':
2227 cset_attr[cset = 0] = ATTR_LINEDRW;
2228 break;
2229 case 'G':
2230 cset_attr[cset = 0] = ATTR_ASCII;
2231 break;
2232 case 'H':
2233 move(0, 0, 0);
2234 break;
2235 case 'I':
2236 if (curs.y == 0)
2237 scroll(0, rows - 1, -1, TRUE);
2238 else if (curs.y > 0)
2239 curs.y--;
2240 fix_cpos;
2241 wrapnext = FALSE;
2242 break;
2243 case 'J':
2244 erase_lots(FALSE, FALSE, TRUE);
4facdf84 2245 disptop = 0;
c9def1b8 2246 break;
32874aea 2247 case 'K':
2248 erase_lots(TRUE, FALSE, TRUE);
2249 break;
4eeb7d09 2250#if 0
32874aea 2251 case 'V':
2252 /* XXX Print cursor line */
2253 break;
2254 case 'W':
2255 /* XXX Start controller mode */
2256 break;
2257 case 'X':
2258 /* XXX Stop controller mode */
2259 break;
4eeb7d09 2260#endif
32874aea 2261 case 'Y':
2262 termstate = VT52_Y1;
2263 break;
2264 case 'Z':
2265 ldisc_send("\033/Z", 3);
2266 break;
2267 case '=':
2268 app_keypad_keys = TRUE;
2269 break;
2270 case '>':
2271 app_keypad_keys = FALSE;
2272 break;
2273 case '<':
2274 /* XXX This should switch to VT100 mode not current or default
2275 * VT mode. But this will only have effect in a VT220+
2276 * emulation.
2277 */
2278 vt52_mode = FALSE;
4eeb7d09 2279 blink_is_real = cfg.blinktext;
32874aea 2280 break;
4eeb7d09 2281#if 0
32874aea 2282 case '^':
2283 /* XXX Enter auto print mode */
2284 break;
2285 case '_':
2286 /* XXX Exit auto print mode */
2287 break;
2288 case ']':
2289 /* XXX Print screen */
2290 break;
4eeb7d09 2291#endif
2292
2293#ifdef VT52_PLUS
2294 case 'E':
2295 /* compatibility(ATARI) */
2296 move(0, 0, 0);
2297 erase_lots(FALSE, FALSE, TRUE);
2298 disptop = 0;
2299 break;
2300 case 'L':
2301 /* compatibility(ATARI) */
2302 if (curs.y <= marg_b)
2303 scroll(curs.y, marg_b, -1, FALSE);
2304 break;
2305 case 'M':
2306 /* compatibility(ATARI) */
2307 if (curs.y <= marg_b)
2308 scroll(curs.y, marg_b, 1, TRUE);
2309 break;
2310 case 'b':
2311 /* compatibility(ATARI) */
2312 termstate = VT52_FG;
2313 break;
2314 case 'c':
2315 /* compatibility(ATARI) */
2316 termstate = VT52_BG;
2317 break;
2318 case 'd':
2319 /* compatibility(ATARI) */
2320 erase_lots(FALSE, TRUE, FALSE);
2321 disptop = 0;
2322 break;
2323 case 'e':
2324 /* compatibility(ATARI) */
2325 cursor_on = TRUE;
2326 break;
2327 case 'f':
2328 /* compatibility(ATARI) */
2329 cursor_on = FALSE;
2330 break;
2331 /* case 'j': Save cursor position - broken on ST */
2332 /* case 'k': Restore cursor position */
2333 case 'l':
2334 /* compatibility(ATARI) */
2335 erase_lots(TRUE, TRUE, TRUE);
2336 curs.x = 0;
2337 wrapnext = FALSE;
2338 fix_cpos;
2339 break;
2340 case 'o':
2341 /* compatibility(ATARI) */
2342 erase_lots(TRUE, TRUE, FALSE);
2343 break;
2344 case 'p':
2345 /* compatibility(ATARI) */
2346 curr_attr |= ATTR_REVERSE;
2347 break;
2348 case 'q':
2349 /* compatibility(ATARI) */
2350 curr_attr &= ~ATTR_REVERSE;
2351 break;
2352 case 'v': /* wrap Autowrap on - Wyse style */
2353 /* compatibility(ATARI) */
2354 wrap = 1;
2355 break;
2356 case 'w': /* Autowrap off */
2357 /* compatibility(ATARI) */
2358 wrap = 0;
2359 break;
2360
2361 case 'R':
2362 /* compatibility(OTHER) */
2363 vt52_bold = FALSE;
2364 curr_attr = ATTR_DEFAULT;
2365 if (use_bce)
8f22582c 2366 erase_char = (' ' | ATTR_ASCII |
2367 (curr_attr &
2368 (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2369 break;
2370 case 'S':
2371 /* compatibility(VI50) */
2372 curr_attr |= ATTR_UNDER;
2373 break;
2374 case 'W':
2375 /* compatibility(VI50) */
2376 curr_attr &= ~ATTR_UNDER;
2377 break;
2378 case 'U':
2379 /* compatibility(VI50) */
2380 vt52_bold = TRUE;
2381 curr_attr |= ATTR_BOLD;
2382 break;
2383 case 'T':
2384 /* compatibility(VI50) */
2385 vt52_bold = FALSE;
2386 curr_attr &= ~ATTR_BOLD;
2387 break;
2388#endif
c9def1b8 2389 }
e14a5a13 2390 break;
32874aea 2391 case VT52_Y1:
2392 termstate = VT52_Y2;
2393 move(curs.x, c - ' ', 0);
e14a5a13 2394 break;
32874aea 2395 case VT52_Y2:
2396 termstate = TOPLEVEL;
2397 move(c - ' ', curs.y, 0);
e14a5a13 2398 break;
4eeb7d09 2399
2400#ifdef VT52_PLUS
2401 case VT52_FG:
2402 termstate = TOPLEVEL;
2403 curr_attr &= ~ATTR_FGMASK;
2404 curr_attr &= ~ATTR_BOLD;
2405 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2406 if ((c & 0x8) || vt52_bold)
2407 curr_attr |= ATTR_BOLD;
2408
2409 if (use_bce)
8f22582c 2410 erase_char = (' ' | ATTR_ASCII |
2411 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2412 break;
2413 case VT52_BG:
2414 termstate = TOPLEVEL;
2415 curr_attr &= ~ATTR_BGMASK;
2416 curr_attr &= ~ATTR_BLINK;
2417 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2418
2419 /* Note: bold background */
2420 if (c & 0x8)
2421 curr_attr |= ATTR_BLINK;
2422
2423 if (use_bce)
8f22582c 2424 erase_char = (' ' | ATTR_ASCII |
2425 (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
4eeb7d09 2426 break;
2427#endif
2d466ffd 2428 default: break; /* placate gcc warning about enum use */
e14a5a13 2429 }
4facdf84 2430 if (selstate != NO_SELECTION) {
2431 pos cursplus = curs;
2432 incpos(cursplus);
32874aea 2433 check_selection(curs, cursplus);
4facdf84 2434 }
374330e2 2435 }
c9def1b8 2436 inbuf_head = 0;
374330e2 2437}
2438
4eeb7d09 2439#if 0
374330e2 2440/*
2441 * Compare two lines to determine whether they are sufficiently
2442 * alike to scroll-optimise one to the other. Return the degree of
2443 * similarity.
2444 */
32874aea 2445static int linecmp(unsigned long *a, unsigned long *b)
2446{
374330e2 2447 int i, n;
2448
32874aea 2449 for (i = n = 0; i < cols; i++)
374330e2 2450 n += (*a++ == *b++);
2451 return n;
2452}
4eeb7d09 2453#endif
374330e2 2454
2455/*
2456 * Given a context, update the window. Out of paranoia, we don't
2457 * allow WM_PAINT responses to do scrolling optimisations.
2458 */
32874aea 2459static void do_paint(Context ctx, int may_optimise)
2460{
4eeb7d09 2461 int i, j, our_curs_y;
2462 unsigned long rv, cursor;
4facdf84 2463 pos scrpos;
374330e2 2464 char ch[1024];
4eeb7d09 2465 long cursor_background = ERASE_CHAR;
156686ef 2466 long ticks;
374330e2 2467
156686ef 2468 /*
2469 * Check the visual bell state.
2470 */
2471 if (in_vbell) {
2472 ticks = GetTickCount();
2473 if (ticks - vbell_timeout >= 0)
2474 in_vbell = FALSE;
2475 }
2476
4eeb7d09 2477 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2478
156686ef 2479 /* Depends on:
2480 * screen array, disptop, scrtop,
2481 * selection, rv,
2482 * cfg.blinkpc, blink_is_real, tblinker,
4eeb7d09 2483 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
156686ef 2484 */
4eeb7d09 2485
2486 /* Has the cursor position or type changed ? */
e14a5a13 2487 if (cursor_on) {
32874aea 2488 if (has_focus) {
217dceef 2489 if (blinker || !cfg.blink_cur)
4eeb7d09 2490 cursor = TATTR_ACTCURS;
32874aea 2491 else
2492 cursor = 0;
2493 } else
4eeb7d09 2494 cursor = TATTR_PASCURS;
4e30ff69 2495 if (wrapnext)
4eeb7d09 2496 cursor |= TATTR_RIGHTCURS;
32874aea 2497 } else
156686ef 2498 cursor = 0;
4facdf84 2499 our_curs_y = curs.y - disptop;
374330e2 2500
4eeb7d09 2501 if (dispcurs && (curstype != cursor ||
2502 dispcurs !=
2503 disptext + our_curs_y * (cols + 1) + curs.x)) {
2504 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2505 dispcurs[-1] |= ATTR_INVALID;
2506 if ((*dispcurs & ATTR_WIDE))
2507 dispcurs[1] |= ATTR_INVALID;
2508 *dispcurs |= ATTR_INVALID;
2509 curstype = 0;
2510 }
2511 dispcurs = NULL;
2512
2513 /* The normal screen data */
32874aea 2514 for (i = 0; i < rows; i++) {
4facdf84 2515 unsigned long *ldata;
2516 int lattr;
4eeb7d09 2517 int idx, dirty_line, dirty_run;
2518 unsigned long attr = 0;
2519 int updated_line = 0;
2520 int start = 0;
2521 int ccount = 0;
2522 int last_run_dirty = 0;
2523
4facdf84 2524 scrpos.y = i + disptop;
2525 ldata = lineptr(scrpos.y);
2526 lattr = (ldata[cols] & LATTR_MODE);
4eeb7d09 2527
2528 idx = i * (cols + 1);
2529 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2530 disptext[idx + cols] = ldata[cols];
2531
2532 for (j = 0; j < cols; j++, idx++) {
2533 unsigned long tattr, tchar;
2534 unsigned long *d = ldata + j;
2535 int break_run;
4facdf84 2536 scrpos.x = j;
32874aea 2537
4eeb7d09 2538 tchar = (*d & (CHAR_MASK | CSET_MASK));
2539 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2540 switch (tchar & CSET_MASK) {
2541 case ATTR_ASCII:
2542 tchar = unitab_line[tchar & 0xFF];
2543 break;
2544 case ATTR_LINEDRW:
2545 tchar = unitab_xterm[tchar & 0xFF];
2546 break;
d3cb5465 2547 case ATTR_SCOACS:
2548 tchar = unitab_scoacs[tchar&0xFF];
2549 break;
4eeb7d09 2550 }
2551 tattr |= (tchar & CSET_MASK);
2552 tchar &= CHAR_MASK;
2553
2554 /* Video reversing things */
2555 tattr = (tattr ^ rv
2556 ^ (posle(selstart, scrpos) &&
2557 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2558
2559 /* 'Real' blinking ? */
2560 if (blink_is_real && (tattr & ATTR_BLINK)) {
2561 if (has_focus && tblinker) {
2562 tchar = ' ';
2563 tattr &= ~CSET_MASK;
2564 tattr |= ATTR_ACP;
c9def1b8 2565 }
4eeb7d09 2566 tattr &= ~ATTR_BLINK;
c9def1b8 2567 }
374330e2 2568
4eeb7d09 2569 /* Cursor here ? Save the 'background' */
2570 if (i == our_curs_y && j == curs.x) {
2571 cursor_background = tattr | tchar;
2572 dispcurs = disptext + idx;
2573 }
374330e2 2574
4eeb7d09 2575 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2576 dirty_line = TRUE;
2577
2578 break_run = (tattr != attr || j - start >= sizeof(ch));
2579
2580 /* Special hack for VT100 Linedraw glyphs */
2581 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2582 && tchar <= 0xBD) break_run = TRUE;
2583
2584 if (!dbcs_screenfont && !dirty_line) {
2585 if ((tchar | tattr) == disptext[idx])
2586 break_run = TRUE;
2587 else if (!dirty_run && ccount == 1)
2588 break_run = TRUE;
374330e2 2589 }
4eeb7d09 2590
2591 if (break_run) {
2592 if ((dirty_run || last_run_dirty) && ccount > 0) {
2593 do_text(ctx, start, i, ch, ccount, attr, lattr);
2594 updated_line = 1;
2595 }
2596 start = j;
2597 ccount = 0;
2598 attr = tattr;
2599 if (dbcs_screenfont)
2600 last_run_dirty = dirty_run;
2601 dirty_run = dirty_line;
2602 }
2603
2604 if ((tchar | tattr) != disptext[idx])
2605 dirty_run = TRUE;
2606 ch[ccount++] = (char) tchar;
2607 disptext[idx] = tchar | tattr;
2608
2609 /* If it's a wide char step along to the next one. */
2610 if (tattr & ATTR_WIDE) {
2611 if (++j < cols) {
2612 idx++;
2613 d++;
2614 /* Cursor is here ? Ouch! */
2615 if (i == our_curs_y && j == curs.x) {
2616 cursor_background = *d;
2617 dispcurs = disptext + idx;
2618 }
2619 if (disptext[idx] != *d)
2620 dirty_run = TRUE;
2621 disptext[idx] = *d;
374330e2 2622 }
374330e2 2623 }
4eeb7d09 2624 }
2625 if (dirty_run && ccount > 0) {
2626 do_text(ctx, start, i, ch, ccount, attr, lattr);
2627 updated_line = 1;
2628 }
2629
2630 /* Cursor on this line ? (and changed) */
2631 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2632 ch[0] = (char) (cursor_background & CHAR_MASK);
2633 attr = (cursor_background & ATTR_MASK) | cursor;
2634 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2635 curstype = cursor;
374330e2 2636 }
2637 }
2638}
2639
2640/*
e14a5a13 2641 * Flick the switch that says if blinking things should be shown or hidden.
2642 */
2643
32874aea 2644void term_blink(int flg)
2645{
22dcdc3b 2646 static long last_blink = 0;
2647 static long last_tblink = 0;
e14a5a13 2648 long now, blink_diff;
2649
c9def1b8 2650 now = GetTickCount();
32874aea 2651 blink_diff = now - last_tblink;
c9def1b8 2652
2653 /* Make sure the text blinks no more than 2Hz */
32874aea 2654 if (blink_diff < 0 || blink_diff > 450) {
2655 last_tblink = now;
c9def1b8 2656 tblinker = !tblinker;
2657 }
2658
e14a5a13 2659 if (flg) {
32874aea 2660 blinker = 1;
2661 last_blink = now;
e14a5a13 2662 return;
32874aea 2663 }
e14a5a13 2664
32874aea 2665 blink_diff = now - last_blink;
e14a5a13 2666
22dcdc3b 2667 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
32874aea 2668 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2669 return;
2670
e14a5a13 2671 last_blink = now;
2672 blinker = !blinker;
2673}
2674
2675/*
374330e2 2676 * Invalidate the whole screen so it will be repainted in full.
2677 */
32874aea 2678void term_invalidate(void)
2679{
374330e2 2680 int i;
2681
32874aea 2682 for (i = 0; i < rows * (cols + 1); i++)
374330e2 2683 disptext[i] = ATTR_INVALID;
2684}
2685
2686/*
2687 * Paint the window in response to a WM_PAINT message.
2688 */
32874aea 2689void term_paint(Context ctx, int l, int t, int r, int b)
2690{
374330e2 2691 int i, j, left, top, right, bottom;
2692
2693 left = l / font_width;
2694 right = (r - 1) / font_width;
2695 top = t / font_height;
2696 bottom = (b - 1) / font_height;
32874aea 2697 for (i = top; i <= bottom && i < rows; i++) {
2698 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2699 for (j = left; j <= right && j < cols; j++)
2700 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2701 else
32874aea 2702 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2703 disptext[i * (cols + 1) + j] = ATTR_INVALID;
c9def1b8 2704 }
374330e2 2705
e14a5a13 2706 /* This should happen soon enough, also for some reason it sometimes
2707 * fails to actually do anything when re-sizing ... painting the wrong
2708 * window perhaps ?
32874aea 2709 do_paint (ctx, FALSE);
2710 */
374330e2 2711}
2712
2713/*
2714 * Attempt to scroll the scrollback. The second parameter gives the
2715 * position we want to scroll to; the first is +1 to denote that
2716 * this position is relative to the beginning of the scrollback, -1
2717 * to denote it is relative to the end, and 0 to denote that it is
2718 * relative to the current position.
2719 */
32874aea 2720void term_scroll(int rel, int where)
2721{
4facdf84 2722 int sbtop = -count234(scrollback);
374330e2 2723
32874aea 2724 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
374330e2 2725 if (disptop < sbtop)
2726 disptop = sbtop;
4facdf84 2727 if (disptop > 0)
2728 disptop = 0;
374330e2 2729 update_sbar();
2730 term_update();
2731}
2732
4eeb7d09 2733static void clipme(pos top, pos bottom)
32874aea 2734{
4eeb7d09 2735 wchar_t *workbuf;
2736 wchar_t *wbptr; /* where next char goes within workbuf */
32874aea 2737 int wblen = 0; /* workbuf len */
2738 int buflen; /* amount of memory allocated to workbuf */
bc1235d4 2739
4eeb7d09 2740 buflen = 5120; /* Default size */
2741 workbuf = smalloc(buflen * sizeof(wchar_t));
2742 wbptr = workbuf; /* start filling here */
bc1235d4 2743
4facdf84 2744 while (poslt(top, bottom)) {
bc1235d4 2745 int nl = FALSE;
4facdf84 2746 unsigned long *ldata = lineptr(top.y);
260f3dec 2747 pos nlpos;
4facdf84 2748
2749 nlpos.y = top.y;
2750 nlpos.x = cols;
bc1235d4 2751
4eeb7d09 2752 if (!(ldata[cols] & LATTR_WRAPPED)) {
2753 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2754 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2755 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2756 && poslt(top, nlpos))
2757 decpos(nlpos);
4facdf84 2758 if (poslt(nlpos, bottom))
bc1235d4 2759 nl = TRUE;
2760 }
4facdf84 2761 while (poslt(top, bottom) && poslt(top, nlpos)) {
4eeb7d09 2762#if 0
2763 char cbuf[16], *p;
2764 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2765#else
2766 wchar_t cbuf[16], *p;
2767 int uc = (ldata[top.x] & 0xFFFF);
2768 int set, c;
2769
2770 if (uc == UCSWIDE) {
2771 top.x++;
2772 continue;
2773 }
2774
2775 switch (uc & CSET_MASK) {
2776 case ATTR_LINEDRW:
2777 if (!cfg.rawcnp) {
2778 uc = unitab_xterm[uc & 0xFF];
2779 break;
32874aea 2780 }
4eeb7d09 2781 case ATTR_ASCII:
2782 uc = unitab_line[uc & 0xFF];
2783 break;
d3cb5465 2784 case ATTR_SCOACS:
2785 uc = unitab_scoacs[uc&0xFF];
2786 break;
d3a22f79 2787 }
4eeb7d09 2788 switch (uc & CSET_MASK) {
2789 case ATTR_ACP:
2790 uc = unitab_font[uc & 0xFF];
2791 break;
2792 case ATTR_OEMCP:
2793 uc = unitab_oemcp[uc & 0xFF];
2794 break;
2795 }
2796
2797 set = (uc & CSET_MASK);
2798 c = (uc & CHAR_MASK);
2799 cbuf[0] = uc;
2800 cbuf[1] = 0;
2801
2802 if (DIRECT_FONT(uc)) {
2803 if (c >= ' ' && c != 0x7F) {
2804 unsigned char buf[4];
2805 WCHAR wbuf[4];
2806 int rv;
2807 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2808 buf[0] = c;
2809 buf[1] = (unsigned char) ldata[top.x + 1];
2810 rv = MultiByteToWideChar(font_codepage,
2811 0, buf, 2, wbuf, 4);
2812 top.x++;
2813 } else {
2814 buf[0] = c;
2815 rv = MultiByteToWideChar(font_codepage,
2816 0, buf, 1, wbuf, 4);
2817 }
bc1235d4 2818
4eeb7d09 2819 if (rv > 0) {
2820 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2821 cbuf[rv] = 0;
d3a22f79 2822 }
d3a22f79 2823 }
4eeb7d09 2824 }
2825#endif
2826
2827 for (p = cbuf; *p; p++) {
2828 /* Enough overhead for trailing NL and nul */
2829 if (wblen >= buflen - 16) {
2830 workbuf =
2831 srealloc(workbuf,
2832 sizeof(wchar_t) * (buflen += 100));
2833 wbptr = workbuf + wblen;
2834 }
2835 wblen++;
2836 *wbptr++ = *p;
bc1235d4 2837 }
4facdf84 2838 top.x++;
bc1235d4 2839 }
2840 if (nl) {
2841 int i;
4eeb7d09 2842 for (i = 0; i < sel_nl_sz; i++) {
32874aea 2843 wblen++;
bc1235d4 2844 *wbptr++ = sel_nl[i];
2845 }
2846 }
4facdf84 2847 top.y++;
2848 top.x = 0;
bc1235d4 2849 }
4eeb7d09 2850 wblen++;
2851 *wbptr++ = 0;
32874aea 2852 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2853 if (buflen > 0) /* indicates we allocated this buffer */
bc1235d4 2854 sfree(workbuf);
bc1235d4 2855}
4eeb7d09 2856
32874aea 2857void term_copyall(void)
2858{
4facdf84 2859 pos top;
2860 top.y = -count234(scrollback);
2861 top.x = 0;
4eeb7d09 2862 clipme(top, curs);
2863}
2864
2865/*
2866 * The wordness array is mainly for deciding the disposition of the US-ASCII
2867 * characters.
2868 */
2869static int wordtype(int uc)
2870{
2871 static struct {
2872 int start, end, ctype;
2873 } *wptr, ucs_words[] = {
2874 {
2875 128, 160, 0}, {
2876 161, 191, 1}, {
2877 215, 215, 1}, {
2878 247, 247, 1}, {
2879 0x037e, 0x037e, 1}, /* Greek question mark */
2880 {
2881 0x0387, 0x0387, 1}, /* Greek ano teleia */
2882 {
2883 0x055a, 0x055f, 1}, /* Armenian punctuation */
2884 {
2885 0x0589, 0x0589, 1}, /* Armenian full stop */
2886 {
2887 0x0700, 0x070d, 1}, /* Syriac punctuation */
2888 {
2889 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2890 {
2891 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2892 {
2893 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2894 {
2895 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2896 {
2897 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2898 {
2899 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2900 {
2901 0x2000, 0x200a, 0}, /* Various spaces */
2902 {
2903 0x2070, 0x207f, 2}, /* superscript */
2904 {
2905 0x2080, 0x208f, 2}, /* subscript */
2906 {
2907 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2908 {
2909 0x3000, 0x3000, 0}, /* ideographic space */
2910 {
2911 0x3001, 0x3020, 1}, /* ideographic punctuation */
2912 {
2913 0x303f, 0x309f, 3}, /* Hiragana */
2914 {
2915 0x30a0, 0x30ff, 3}, /* Katakana */
2916 {
2917 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2918 {
2919 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2920 {
2921 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2922 {
2923 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2924 {
2925 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2926 {
2927 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2928 {
2929 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2930 {
2931 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2932 {
2933 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2934 {
2935 0, 0, 0}
2936 };
2937
2938 uc &= (CSET_MASK | CHAR_MASK);
2939
2940 switch (uc & CSET_MASK) {
2941 case ATTR_LINEDRW:
2942 uc = unitab_xterm[uc & 0xFF];
2943 break;
2944 case ATTR_ASCII:
2945 uc = unitab_line[uc & 0xFF];
2946 break;
d3cb5465 2947 case ATTR_SCOACS:
2948 uc = unitab_scoacs[uc&0xFF];
2949 break;
4eeb7d09 2950 }
2951 switch (uc & CSET_MASK) {
2952 case ATTR_ACP:
2953 uc = unitab_font[uc & 0xFF];
2954 break;
2955 case ATTR_OEMCP:
2956 uc = unitab_oemcp[uc & 0xFF];
2957 break;
2958 }
2959
2960 if (uc < 0x80)
2961 return wordness[uc];
2962
2963 for (wptr = ucs_words; wptr->start; wptr++) {
2964 if (uc >= wptr->start && uc <= wptr->end)
2965 return wptr->ctype;
2966 }
2967
2968 return 2;
bc1235d4 2969}
2970
374330e2 2971/*
2972 * Spread the selection outwards according to the selection mode.
2973 */
32874aea 2974static pos sel_spread_half(pos p, int dir)
2975{
4facdf84 2976 unsigned long *ldata;
374330e2 2977 short wvalue;
2978
4facdf84 2979 ldata = lineptr(p.y);
374330e2 2980
2981 switch (selmode) {
2982 case SM_CHAR:
2983 /*
2984 * In this mode, every character is a separate unit, except
2985 * for runs of spaces at the end of a non-wrapping line.
2986 */
4eeb7d09 2987 if (!(ldata[cols] & LATTR_WRAPPED)) {
32874aea 2988 unsigned long *q = ldata + cols;
4facdf84 2989 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
374330e2 2990 q--;
32874aea 2991 if (q == ldata + cols)
374330e2 2992 q--;
32874aea 2993 if (p.x >= q - ldata)
2994 p.x = (dir == -1 ? q - ldata : cols - 1);
374330e2 2995 }
2996 break;
2997 case SM_WORD:
2998 /*
2999 * In this mode, the units are maximal runs of characters
3000 * whose `wordness' has the same value.
3001 */
4eeb7d09 3002 wvalue = wordtype(ldata[p.x]);
374330e2 3003 if (dir == +1) {
4eeb7d09 3004 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
3005 p.x++;
374330e2 3006 } else {
4eeb7d09 3007 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
3008 p.x--;
374330e2 3009 }
3010 break;
3011 case SM_LINE:
3012 /*
3013 * In this mode, every line is a unit.
3014 */
4facdf84 3015 p.x = (dir == -1 ? 0 : cols - 1);
374330e2 3016 break;
3017 }
3018 return p;
3019}
3020
32874aea 3021static void sel_spread(void)
3022{
3023 selstart = sel_spread_half(selstart, -1);
4facdf84 3024 decpos(selend);
32874aea 3025 selend = sel_spread_half(selend, +1);
4facdf84 3026 incpos(selend);
374330e2 3027}
3028
568dd02f 3029void term_do_paste(void)
3030{
3031 wchar_t *data;
3032 int len;
3033
3034 get_clip(&data, &len);
3035 if (data) {
3036 wchar_t *p, *q;
3037
3038 if (paste_buffer)
3039 sfree(paste_buffer);
3040 paste_pos = paste_hold = paste_len = 0;
3041 paste_buffer = smalloc(len * sizeof(wchar_t));
3042
3043 p = q = data;
3044 while (p < data + len) {
3045 while (p < data + len &&
3046 !(p <= data + len - sel_nl_sz &&
3047 !memcmp(p, sel_nl, sizeof(sel_nl))))
3048 p++;
3049
3050 {
3051 int i;
3052 for (i = 0; i < p - q; i++) {
3053 paste_buffer[paste_len++] = q[i];
3054 }
3055 }
3056
3057 if (p <= data + len - sel_nl_sz &&
3058 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3059 paste_buffer[paste_len++] = '\r';
3060 p += sel_nl_sz;
3061 }
3062 q = p;
3063 }
3064
3065 /* Assume a small paste will be OK in one go. */
3066 if (paste_len < 256) {
3067 luni_send(paste_buffer, paste_len);
3068 if (paste_buffer)
3069 sfree(paste_buffer);
3070 paste_buffer = 0;
3071 paste_pos = paste_hold = paste_len = 0;
3072 }
3073 }
3074 get_clip(NULL, NULL);
3075}
3076
32874aea 3077void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3078 int shift, int ctrl)
3079{
4facdf84 3080 pos selpoint;
3081 unsigned long *ldata;
32874aea 3082
3083 if (y < 0)
3084 y = 0;
3085 if (y >= rows)
3086 y = rows - 1;
3087 if (x < 0) {
3088 if (y > 0) {
3089 x = cols - 1;
3090 y--;
3091 } else
3092 x = 0;
094ed2a6 3093 }
32874aea 3094 if (x >= cols)
3095 x = cols - 1;
37508af4 3096
4facdf84 3097 selpoint.y = y + disptop;
3098 selpoint.x = x;
3099 ldata = lineptr(selpoint.y);
32874aea 3100 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
4facdf84 3101 selpoint.x /= 2;
374330e2 3102
01c034ad 3103 if (xterm_mouse) {
3104 int encstate = 0, r, c;
3105 char abuf[16];
3106 static int is_down = 0;
3107
32874aea 3108 switch (b) {
01c034ad 3109 case MBT_LEFT:
32874aea 3110 encstate = 0x20; /* left button down */
01c034ad 3111 break;
3112 case MBT_MIDDLE:
3113 encstate = 0x21;
3114 break;
3115 case MBT_RIGHT:
3116 encstate = 0x22;
3117 break;
3118 case MBT_WHEEL_UP:
3119 encstate = 0x60;
3120 break;
3121 case MBT_WHEEL_DOWN:
3122 encstate = 0x61;
3123 break;
2d466ffd 3124 default: break; /* placate gcc warning about enum use */
01c034ad 3125 }
32874aea 3126 switch (a) {
01c034ad 3127 case MA_DRAG:
3128 if (xterm_mouse == 1)
3129 return;
3130 encstate += 0x20;
3131 break;
3132 case MA_RELEASE:
3133 encstate = 0x23;
3134 is_down = 0;
3135 break;
3136 case MA_CLICK:
3137 if (is_down == b)
3138 return;
3139 is_down = b;
3140 break;
2d466ffd 3141 default: break; /* placate gcc warning about enum use */
01c034ad 3142 }
3143 if (shift)
3144 encstate += 0x04;
3145 if (ctrl)
3146 encstate += 0x10;
3147 r = y + 33;
3148 c = x + 33;
3149
3150 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3151 ldisc_send(abuf, 6);
3152 return;
3153 }
3154
3155 b = translate_button(b);
3156
3157 if (b == MBT_SELECT && a == MA_CLICK) {
374330e2 3158 deselect();
3159 selstate = ABOUT_TO;
3160 selanchor = selpoint;
3161 selmode = SM_CHAR;
01c034ad 3162 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
374330e2 3163 deselect();
3164 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3165 selstate = DRAGGING;
3166 selstart = selanchor = selpoint;
4facdf84 3167 selend = selstart;
3168 incpos(selend);
374330e2 3169 sel_spread();
01c034ad 3170 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3171 (b == MBT_EXTEND && a != MA_RELEASE)) {
4facdf84 3172 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
374330e2 3173 return;
01c034ad 3174 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
32874aea 3175 if (posdiff(selpoint, selstart) <
3176 posdiff(selend, selstart) / 2) {
4facdf84 3177 selanchor = selend;
3178 decpos(selanchor);
3179 } else {
374330e2 3180 selanchor = selstart;
4facdf84 3181 }
374330e2 3182 selstate = DRAGGING;
3183 }
3184 if (selstate != ABOUT_TO && selstate != DRAGGING)
3185 selanchor = selpoint;
3186 selstate = DRAGGING;
4facdf84 3187 if (poslt(selpoint, selanchor)) {
374330e2 3188 selstart = selpoint;
4facdf84 3189 selend = selanchor;
3190 incpos(selend);
374330e2 3191 } else {
3192 selstart = selanchor;
4facdf84 3193 selend = selpoint;
a4450583 3194 incpos(selend);
374330e2 3195 }
3196 sel_spread();
01c034ad 3197 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
374330e2 3198 if (selstate == DRAGGING) {
3199 /*
3200 * We've completed a selection. We now transfer the
3201 * data to the clipboard.
3202 */
4eeb7d09 3203 clipme(selstart, selend);
374330e2 3204 selstate = SELECTED;
3205 } else
3206 selstate = NO_SELECTION;
32874aea 3207 } else if (b == MBT_PASTE
3208 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
568dd02f 3209 term_do_paste();
374330e2 3210 }
3211
3212 term_update();
3213}
3214
32874aea 3215void term_nopaste()
3216{
3217 if (paste_len == 0)
3218 return;
c9def1b8 3219 sfree(paste_buffer);
3220 paste_buffer = 0;
3221 paste_len = 0;
3222}
3223
32874aea 3224void term_paste()
3225{
8df7a775 3226 static long last_paste = 0;
c9def1b8 3227 long now, paste_diff;
3228
32874aea 3229 if (paste_len == 0)
3230 return;
c9def1b8 3231
3232 /* Don't wait forever to paste */
32874aea 3233 if (paste_hold) {
3234 now = GetTickCount();
3235 paste_diff = now - last_paste;
3236 if (paste_diff >= 0 && paste_diff < 450)
c9def1b8 3237 return;
3238 }
3239 paste_hold = 0;
3240
32874aea 3241 while (paste_pos < paste_len) {
8df7a775 3242 int n = 0;
3243 while (n + paste_pos < paste_len) {
3244 if (paste_buffer[paste_pos + n++] == '\r')
3245 break;
3246 }
4eeb7d09 3247 luni_send(paste_buffer + paste_pos, n);
8df7a775 3248 paste_pos += n;
c9def1b8 3249
8df7a775 3250 if (paste_pos < paste_len) {
c9def1b8 3251 paste_hold = 1;
3252 return;
3253 }
3254 }
3255 sfree(paste_buffer);
3256 paste_buffer = 0;
3257 paste_len = 0;
3258}
3259
32874aea 3260static void deselect(void)
3261{
374330e2 3262 selstate = NO_SELECTION;
4facdf84 3263 selstart.x = selstart.y = selend.x = selend.y = 0;
374330e2 3264}
3265
32874aea 3266void term_deselect(void)
3267{
374330e2 3268 deselect();
3269 term_update();
3270}
fe50e814 3271
32874aea 3272int term_ldisc(int option)
3273{
3274 if (option == LD_ECHO)
3275 return term_echoing;
3276 if (option == LD_EDIT)
3277 return term_editing;
0965bee0 3278 return FALSE;
3279}
3280
fe50e814 3281/*
3282 * from_backend(), to get data from the backend for the terminal.
3283 */
32874aea 3284void from_backend(int is_stderr, char *data, int len)
3285{
fe50e814 3286 while (len--) {
3287 if (inbuf_head >= INBUF_SIZE)
3288 term_out();
3289 inbuf[inbuf_head++] = *data++;
3290 }
3291}
e1c8e0ed 3292
3293/*
3294 * Log session traffic.
3295 */
32874aea 3296void logtraffic(unsigned char c, int logmode)
3297{
e1c8e0ed 3298 if (cfg.logtype > 0) {
3299 if (cfg.logtype == logmode) {
3300 /* deferred open file from pgm start? */
32874aea 3301 if (!lgfp)
3302 logfopen();
3303 if (lgfp)
3304 fputc(c, lgfp);
3305 }
e1c8e0ed 3306 }
3307}
3308
3309/* open log file append/overwrite mode */
32874aea 3310void logfopen(void)
3311{
e1c8e0ed 3312 char buf[256];
3313 time_t t;
3314 struct tm *tm;
3315 char writemod[4];
3316
3317 if (!cfg.logtype)
3318 return;
32874aea 3319 sprintf(writemod, "wb"); /* default to rewrite */
3320 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
e1c8e0ed 3321 if (lgfp) {
3322 int i;
3323 fclose(lgfp);
3324 i = askappend(cfg.logfilename);
3325 if (i == 1)
3326 writemod[0] = 'a'; /* set append mode */
3327 else if (i == 0) { /* cancelled */
3328 lgfp = NULL;
32874aea 3329 cfg.logtype = 0; /* disable logging */
e1c8e0ed 3330 return;
3331 }
3332 }
3333
3334 lgfp = fopen(cfg.logfilename, writemod);
32874aea 3335 if (lgfp) { /* enter into event log */
e1c8e0ed 3336 sprintf(buf, "%s session log (%s mode) to file : ",
3337 (writemod[0] == 'a') ? "Appending" : "Writing new",
3338 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
32874aea 3339 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
e1c8e0ed 3340 /* Make sure we do not exceed the output buffer size */
32874aea 3341 strncat(buf, cfg.logfilename, 128);
e1c8e0ed 3342 buf[strlen(buf)] = '\0';
3343 logevent(buf);
3344
32874aea 3345 /* --- write header line iinto log file */
3346 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
e1c8e0ed 3347 time(&t);
3348 tm = localtime(&t);
3349 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
32874aea 3350 fputs(buf, lgfp);
3351 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
e1c8e0ed 3352 }
3353}
3354
32874aea 3355void logfclose(void)
3356{
3357 if (lgfp) {
3358 fclose(lgfp);
3359 lgfp = NULL;
3360 }
e1c8e0ed 3361}