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