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