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