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