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