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