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