]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - terminal.c
Terminal fix from RDB: probably didn't mean to compare a Y value
[PuTTY.git] / terminal.c
1 #include <windows.h>
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <ctype.h>
6
7 #include <time.h>
8 #include <assert.h>
9 #include "putty.h"
10 #include "tree234.h"
11
12 #define VT52_PLUS
13
14 #define CL_ANSIMIN      0x0001         /* Codes in all ANSI like terminals. */
15 #define CL_VT100        0x0002         /* VT100 */
16 #define CL_VT100AVO     0x0004         /* VT100 +AVO; 132x24 (not 132x14) & attrs */
17 #define CL_VT102        0x0008         /* VT102 */
18 #define CL_VT220        0x0010         /* VT220 */
19 #define CL_VT320        0x0020         /* VT320 */
20 #define CL_VT420        0x0040         /* VT420 */
21 #define CL_VT510        0x0080         /* VT510, NB VT510 includes ANSI */
22 #define CL_VT340TEXT    0x0100         /* VT340 extensions that appear in the VT420 */
23 #define CL_SCOANSI      0x1000         /* SCOANSI not in ANSIMIN. */
24 #define CL_ANSI         0x2000         /* ANSI ECMA-48 not in the VT100..VT420 */
25 #define CL_OTHER        0x4000         /* Others, Xterm, linux, putty, dunno, etc */
26
27 #define TM_VT100        (CL_ANSIMIN|CL_VT100)
28 #define TM_VT100AVO     (TM_VT100|CL_VT100AVO)
29 #define TM_VT102        (TM_VT100AVO|CL_VT102)
30 #define TM_VT220        (TM_VT102|CL_VT220)
31 #define TM_VTXXX        (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
32 #define TM_SCOANSI      (CL_ANSIMIN|CL_SCOANSI)
33
34 #define TM_PUTTY        (0xFFFF)
35
36 #define compatibility(x) \
37     if ( ((CL_##x)&compatibility_level) == 0 ) {        \
38        termstate=TOPLEVEL;                              \
39        break;                                           \
40     }
41 #define compatibility2(x,y) \
42     if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
43        termstate=TOPLEVEL;                              \
44        break;                                           \
45     }
46
47 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
48
49 static int compatibility_level = TM_PUTTY;
50
51 static tree234 *scrollback;            /* lines scrolled off top of screen */
52 static tree234 *screen;                /* lines on primary screen */
53 static tree234 *alt_screen;            /* lines on alternate screen */
54 static int disptop;                    /* distance scrolled back (0 or -ve) */
55
56 static unsigned long *cpos;            /* cursor position (convenience) */
57
58 static unsigned long *disptext;        /* buffer of text on real screen */
59 static unsigned long *dispcurs;        /* location of cursor on real screen */
60 static unsigned long curstype;         /* type of cursor on real screen */
61
62 #define VBELL_TIMEOUT 100              /* millisecond len of visual bell */
63
64 struct beeptime {
65     struct beeptime *next;
66     long ticks;
67 };
68 static struct beeptime *beephead, *beeptail;
69 int nbeeps;
70 int beep_overloaded;
71 long lastbeep;
72
73 #define TSIZE (sizeof(unsigned long))
74 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
75
76 static unsigned long curr_attr, save_attr;
77 static unsigned long erase_char = ERASE_CHAR;
78
79 typedef struct {
80     int y, x;
81 } pos;
82 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
83 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
84 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
85 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
86 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
87 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
88
89 static pos curs;                       /* cursor */
90 static pos savecurs;                   /* saved cursor position */
91 static int marg_t, marg_b;             /* scroll margins */
92 static int dec_om;                     /* DEC origin mode flag */
93 static int wrap, wrapnext;             /* wrap flags */
94 static int insert;                     /* insert-mode flag */
95 static int cset;                       /* 0 or 1: which char set */
96 static int save_cset, save_csattr;     /* saved with cursor position */
97 static int rvideo;                     /* global reverse video flag */
98 static int rvbell_timeout;             /* for ESC[?5hESC[?5l vbell */
99 static int cursor_on;                  /* cursor enabled flag */
100 static int reset_132;                  /* Flag ESC c resets to 80 cols */
101 static int use_bce;                    /* Use Background coloured erase */
102 static int blinker;                    /* When blinking is the cursor on ? */
103 static int tblinker;                   /* When the blinking text is on */
104 static int blink_is_real;              /* Actually blink blinking text */
105 static int term_echoing;               /* Does terminal want local echo? */
106 static int term_editing;               /* Does terminal want local edit? */
107 static int vt52_bold;                  /* Force bold on non-bold colours */
108 static int utf_state;                  /* Is there a pending UTF-8 character */
109 static int utf_char;                   /* and what is it so far. */
110 static int utf_size;                   /* The size of the UTF character. */
111
112 static int xterm_mouse;                /* send mouse messages to app */
113
114 static unsigned long cset_attr[2];
115
116 /*
117  * Saved settings on the alternate screen.
118  */
119 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
120 static int alt_t, alt_b;
121 static int alt_which;
122
123 #define ARGS_MAX 32                    /* max # of esc sequence arguments */
124 #define ARG_DEFAULT 0                  /* if an arg isn't specified */
125 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
126 static int esc_args[ARGS_MAX];
127 static int esc_nargs;
128 static int esc_query;
129 #define ANSI(x,y)       ((x)+((y)<<8))
130 #define ANSI_QUE(x)     ANSI(x,TRUE)
131
132 #define OSC_STR_MAX 2048
133 static int osc_strlen;
134 static char osc_string[OSC_STR_MAX + 1];
135 static int osc_w;
136
137 static char id_string[1024] = "\033[?6c";
138
139 static unsigned char *tabs;
140
141 static enum {
142     TOPLEVEL,
143     SEEN_ESC,
144     SEEN_CSI,
145     SEEN_OSC,
146     SEEN_OSC_W,
147
148     DO_CTRLS,
149
150     SEEN_OSC_P,
151     OSC_STRING, OSC_MAYBE_ST,
152     VT52_ESC,
153     VT52_Y1,
154     VT52_Y2,
155     VT52_FG,
156     VT52_BG
157 } termstate;
158
159 static enum {
160     NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
161 } selstate;
162 static enum {
163     SM_CHAR, SM_WORD, SM_LINE
164 } selmode;
165 static pos selstart, selend, selanchor;
166
167 static short wordness[256] = {
168     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169     0, 0, 0, 0, 0, 0, 0, 0,            /* 01 */
170     0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
171     2, 2, 1, 1, 1, 1, 1, 1,            /* 23 */
172     1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
173     2, 2, 2, 1, 1, 1, 1, 2,            /* 45 */
174     1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
175     2, 2, 2, 1, 1, 1, 1, 1,            /* 67 */
176     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
177     1, 1, 1, 1, 1, 1, 1, 1,            /* 89 */
178     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
179     1, 1, 1, 1, 1, 1, 1, 1,            /* AB */
180     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
181     2, 2, 2, 2, 2, 2, 2, 2,            /* CD */
182     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
183     2, 2, 2, 2, 2, 2, 2, 2,            /* EF */
184 };
185
186 #define sel_nl_sz  (sizeof(sel_nl)/sizeof(wchar_t))
187 static wchar_t sel_nl[] = SEL_NL;
188 static wchar_t *paste_buffer = 0;
189 static int paste_len, paste_pos, paste_hold;
190
191 /*
192  * Internal prototypes.
193  */
194 static void do_paint(Context, int);
195 static void erase_lots(int, int, int);
196 static void swap_screen(int);
197 static void update_sbar(void);
198 static void deselect(void);
199 /* log session to file stuff ... */
200 static FILE *lgfp = NULL;
201 static void logtraffic(unsigned char c, int logmode);
202
203 /*
204  * Resize a line to make it `cols' columns wide.
205  */
206 unsigned long *resizeline(unsigned long *line, int cols)
207 {
208     int i, oldlen;
209     unsigned long lineattrs;
210
211     if (line[0] != (unsigned long)cols) {
212         /*
213          * This line is the wrong length, which probably means it
214          * hasn't been accessed since a resize. Resize it now.
215          */
216         oldlen = line[0];
217         lineattrs = line[oldlen + 1];
218         line = srealloc(line, TSIZE * (2 + cols));
219         line[0] = cols;
220         for (i = oldlen; i < cols; i++)
221             line[i + 1] = ERASE_CHAR;
222         line[cols + 1] = lineattrs & LATTR_MODE;
223     }
224
225     return line;
226 }
227
228 /*
229  * Retrieve a line of the screen or of the scrollback, according to
230  * whether the y coordinate is non-negative or negative
231  * (respectively).
232  */
233 unsigned long *lineptr(int y, int lineno)
234 {
235     unsigned long *line, *newline;
236     tree234 *whichtree;
237     int treeindex;
238
239     if (y >= 0) {
240         whichtree = screen;
241         treeindex = y;
242     } else {
243         whichtree = scrollback;
244         treeindex = y + count234(scrollback);
245     }
246     line = index234(whichtree, treeindex);
247
248     /* We assume that we don't screw up and retrieve something out of range. */
249     assert(line != NULL);
250
251     newline = resizeline(line, cols);
252     if (newline != line) {
253         delpos234(whichtree, treeindex);
254         addpos234(whichtree, newline, treeindex);
255     }
256
257     return line + 1;
258 }
259
260 #define lineptr(x) lineptr(x,__LINE__)
261 /*
262  * Set up power-on settings for the terminal.
263  */
264 static void power_on(void)
265 {
266     curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
267     alt_t = marg_t = 0;
268     if (rows != -1)
269         alt_b = marg_b = rows - 1;
270     else
271         alt_b = marg_b = 0;
272     if (cols != -1) {
273         int i;
274         for (i = 0; i < cols; i++)
275             tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
276     }
277     alt_om = dec_om = cfg.dec_om;
278     alt_wnext = wrapnext = alt_ins = insert = FALSE;
279     alt_wrap = wrap = cfg.wrap_mode;
280     alt_cset = cset = 0;
281     cset_attr[0] = cset_attr[1] = ATTR_ASCII;
282     rvideo = 0;
283     in_vbell = FALSE;
284     cursor_on = 1;
285     big_cursor = 0;
286     save_attr = curr_attr = ATTR_DEFAULT;
287     term_editing = term_echoing = FALSE;
288     ldisc_send(NULL, 0);               /* cause ldisc to notice changes */
289     app_cursor_keys = cfg.app_cursor;
290     app_keypad_keys = cfg.app_keypad;
291     use_bce = cfg.bce;
292     blink_is_real = cfg.blinktext;
293     erase_char = ERASE_CHAR;
294     alt_which = 0;
295     {
296         int i;
297         for (i = 0; i < 256; i++)
298             wordness[i] = cfg.wordness[i];
299     }
300     if (screen) {
301         swap_screen(1);
302         erase_lots(FALSE, TRUE, TRUE);
303         swap_screen(0);
304         erase_lots(FALSE, TRUE, TRUE);
305     }
306 }
307
308 /*
309  * Force a screen update.
310  */
311 void term_update(void)
312 {
313     Context ctx;
314     ctx = get_ctx();
315     if (ctx) {
316         if (seen_disp_event)
317             update_sbar();
318         if ((seen_key_event && (cfg.scroll_on_key)) ||
319             (seen_disp_event && (cfg.scroll_on_disp))) {
320             disptop = 0;               /* return to main screen */
321             seen_disp_event = seen_key_event = 0;
322         }
323         do_paint(ctx, TRUE);
324         sys_cursor(curs.x, curs.y - disptop);
325         free_ctx(ctx);
326     }
327 }
328
329 /*
330  * Same as power_on(), but an external function.
331  */
332 void term_pwron(void)
333 {
334     power_on();
335     fix_cpos;
336     disptop = 0;
337     deselect();
338     term_update();
339 }
340
341 /*
342  * Clear the scrollback.
343  */
344 void term_clrsb(void)
345 {
346     unsigned long *line;
347     disptop = 0;
348     while ((line = delpos234(scrollback, 0)) != NULL) {
349         sfree(line);
350     }
351     update_sbar();
352 }
353
354 /*
355  * Initialise the terminal.
356  */
357 void term_init(void)
358 {
359     screen = alt_screen = scrollback = NULL;
360     disptop = 0;
361     disptext = dispcurs = NULL;
362     tabs = NULL;
363     deselect();
364     rows = cols = -1;
365     power_on();
366     beephead = beeptail = NULL;
367     nbeeps = 0;
368     lastbeep = FALSE;
369     beep_overloaded = FALSE;
370 }
371
372 /*
373  * Set up the terminal for a given size.
374  */
375 void term_size(int newrows, int newcols, int newsavelines)
376 {
377     tree234 *newalt;
378     unsigned long *newdisp, *line;
379     int i, j;
380     int sblen;
381     int save_alt_which = alt_which;
382
383     if (newrows == rows && newcols == cols && newsavelines == savelines)
384         return;                        /* nothing to do */
385
386     deselect();
387     swap_screen(0);
388
389     alt_t = marg_t = 0;
390     alt_b = marg_b = newrows - 1;
391
392     if (rows == -1) {
393         scrollback = newtree234(NULL);
394         screen = newtree234(NULL);
395         rows = 0;
396     }
397
398     /*
399      * Resize the screen and scrollback. We only need to shift
400      * lines around within our data structures, because lineptr()
401      * will take care of resizing each individual line if
402      * necessary. So:
403      * 
404      *  - If the new screen and the old screen differ in length, we
405      *    must shunt some lines in from the scrollback or out to
406      *    the scrollback.
407      * 
408      *  - If doing that fails to provide us with enough material to
409      *    fill the new screen (i.e. the number of rows needed in
410      *    the new screen exceeds the total number in the previous
411      *    screen+scrollback), we must invent some blank lines to
412      *    cover the gap.
413      * 
414      *  - Then, if the new scrollback length is less than the
415      *    amount of scrollback we actually have, we must throw some
416      *    away.
417      */
418     sblen = count234(scrollback);
419     /* Do this loop to expand the screen if newrows > rows */
420     for (i = rows; i < newrows; i++) {
421         if (sblen > 0) {
422             line = delpos234(scrollback, --sblen);
423         } else {
424             line = smalloc(TSIZE * (newcols + 2));
425             line[0] = newcols;
426             for (j = 0; j <= newcols; j++)
427                 line[j + 1] = ERASE_CHAR;
428         }
429         addpos234(screen, line, 0);
430     }
431     /* Do this loop to shrink the screen if newrows < rows */
432     for (i = newrows; i < rows; i++) {
433         line = delpos234(screen, 0);
434         addpos234(scrollback, line, sblen++);
435     }
436     assert(count234(screen) == newrows);
437     while (sblen > newsavelines) {
438         line = delpos234(scrollback, 0);
439         sfree(line);
440         sblen--;
441     }
442     assert(count234(scrollback) <= newsavelines);
443     disptop = 0;
444
445     newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
446     for (i = 0; i < newrows * (newcols + 1); i++)
447         newdisp[i] = ATTR_INVALID;
448     sfree(disptext);
449     disptext = newdisp;
450     dispcurs = NULL;
451
452     newalt = newtree234(NULL);
453     for (i = 0; i < newrows; i++) {
454         line = smalloc(TSIZE * (newcols + 2));
455         line[0] = newcols;
456         for (j = 0; j <= newcols; j++)
457             line[j + 1] = erase_char;
458         addpos234(newalt, line, i);
459     }
460     if (alt_screen) {
461         while (NULL != (line = delpos234(alt_screen, 0)))
462             sfree(line);
463         freetree234(alt_screen);
464     }
465     alt_screen = newalt;
466
467     tabs = srealloc(tabs, newcols * sizeof(*tabs));
468     {
469         int i;
470         for (i = (cols > 0 ? cols : 0); i < newcols; i++)
471             tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
472     }
473
474     if (rows > 0)
475         curs.y += newrows - rows;
476     if (curs.y < 0)
477         curs.y = 0;
478     if (curs.y >= newrows)
479         curs.y = newrows - 1;
480     if (curs.x >= newcols)
481         curs.x = newcols - 1;
482     alt_x = alt_y = 0;
483     wrapnext = alt_wnext = FALSE;
484
485     rows = newrows;
486     cols = newcols;
487     savelines = newsavelines;
488     fix_cpos;
489
490     swap_screen(save_alt_which);
491
492     update_sbar();
493     term_update();
494 }
495
496 /*
497  * Swap screens.
498  */
499 static void swap_screen(int which)
500 {
501     int t;
502     tree234 *ttr;
503
504     if (which == alt_which)
505         return;
506
507     alt_which = which;
508
509     ttr = alt_screen;
510     alt_screen = screen;
511     screen = ttr;
512     t = curs.x;
513     curs.x = alt_x;
514     alt_x = t;
515     t = curs.y;
516     curs.y = alt_y;
517     alt_y = t;
518     t = marg_t;
519     marg_t = alt_t;
520     alt_t = t;
521     t = marg_b;
522     marg_b = alt_b;
523     alt_b = t;
524     t = dec_om;
525     dec_om = alt_om;
526     alt_om = t;
527     t = wrap;
528     wrap = alt_wrap;
529     alt_wrap = t;
530     t = wrapnext;
531     wrapnext = alt_wnext;
532     alt_wnext = t;
533     t = insert;
534     insert = alt_ins;
535     alt_ins = t;
536     t = cset;
537     cset = alt_cset;
538     alt_cset = t;
539
540     fix_cpos;
541 }
542
543 /*
544  * Update the scroll bar.
545  */
546 static void update_sbar(void)
547 {
548     int nscroll;
549
550     nscroll = count234(scrollback);
551
552     set_sbar(nscroll + rows, nscroll + disptop, rows);
553 }
554
555 /*
556  * Check whether the region bounded by the two pointers intersects
557  * the scroll region, and de-select the on-screen selection if so.
558  */
559 static void check_selection(pos from, pos to)
560 {
561     if (poslt(from, selend) && poslt(selstart, to))
562         deselect();
563 }
564
565 /*
566  * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
567  * for backward.) `sb' is TRUE if the scrolling is permitted to
568  * affect the scrollback buffer.
569  * 
570  * NB this function invalidates all pointers into lines of the
571  * screen data structures. In particular, you MUST call fix_cpos
572  * after calling scroll() and before doing anything else that
573  * uses the cpos shortcut pointer.
574  */
575 static void scroll(int topline, int botline, int lines, int sb)
576 {
577     unsigned long *line, *line2;
578     int i;
579
580     if (topline != 0 || alt_which != 0)
581         sb = FALSE;
582
583     if (lines < 0) {
584         while (lines < 0) {
585             line = delpos234(screen, botline);
586             line = resizeline(line, cols);
587             for (i = 0; i < cols; i++)
588                 line[i + 1] = erase_char;
589             line[cols + 1] = 0;
590             addpos234(screen, line, topline);
591
592             if (selstart.y >= topline && selstart.y <= botline) {
593                 selstart.y++;
594                 if (selstart.y > botline) {
595                     selstart.y = botline;
596                     selstart.x = 0;
597                 }
598             }
599             if (selend.y >= topline && selend.y <= botline) {
600                 selend.y++;
601                 if (selend.y > botline) {
602                     selend.y = botline;
603                     selend.x = 0;
604                 }
605             }
606
607             lines++;
608         }
609     } else {
610         while (lines > 0) {
611             line = delpos234(screen, topline);
612             if (sb && savelines > 0) {
613                 int sblen = count234(scrollback);
614                 /*
615                  * We must add this line to the scrollback. We'll
616                  * remove a line from the top of the scrollback to
617                  * replace it, or allocate a new one if the
618                  * scrollback isn't full.
619                  */
620                 if (sblen == savelines) {
621                     sblen--, line2 = delpos234(scrollback, 0);
622                 } else {
623                     line2 = smalloc(TSIZE * (cols + 2));
624                     line2[0] = cols;
625                 }
626                 addpos234(scrollback, line, sblen);
627                 line = line2;
628             }
629             line = resizeline(line, cols);
630             for (i = 0; i < cols; i++)
631                 line[i + 1] = erase_char;
632             line[cols + 1] = 0;
633             addpos234(screen, line, botline);
634
635             if (selstart.y >= topline && selstart.y <= botline) {
636                 selstart.y--;
637                 if (selstart.y < topline) {
638                     selstart.y = topline;
639                     selstart.x = 0;
640                 }
641             }
642             if (selend.y >= topline && selend.y <= botline) {
643                 selend.y--;
644                 if (selend.y < topline) {
645                     selend.y = topline;
646                     selend.x = 0;
647                 }
648             }
649
650             lines--;
651         }
652     }
653 }
654
655 /*
656  * Move the cursor to a given position, clipping at boundaries. We
657  * may or may not want to clip at the scroll margin: marg_clip is 0
658  * not to, 1 to disallow _passing_ the margins, and 2 to disallow
659  * even _being_ outside the margins.
660  */
661 static void move(int x, int y, int marg_clip)
662 {
663     if (x < 0)
664         x = 0;
665     if (x >= cols)
666         x = cols - 1;
667     if (marg_clip) {
668         if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
669             y = marg_t;
670         if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
671             y = marg_b;
672     }
673     if (y < 0)
674         y = 0;
675     if (y >= rows)
676         y = rows - 1;
677     curs.x = x;
678     curs.y = y;
679     fix_cpos;
680     wrapnext = FALSE;
681 }
682
683 /*
684  * Save or restore the cursor and SGR mode.
685  */
686 static void save_cursor(int save)
687 {
688     if (save) {
689         savecurs = curs;
690         save_attr = curr_attr;
691         save_cset = cset;
692         save_csattr = cset_attr[cset];
693     } else {
694         curs = savecurs;
695         /* Make sure the window hasn't shrunk since the save */
696         if (curs.x >= cols)
697             curs.x = cols - 1;
698         if (curs.y >= rows)
699             curs.y = rows - 1;
700
701         curr_attr = save_attr;
702         cset = save_cset;
703         cset_attr[cset] = save_csattr;
704         fix_cpos;
705         if (use_bce)
706             erase_char = (' ' | (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
707     }
708 }
709
710 /*
711  * Erase a large portion of the screen: the whole screen, or the
712  * whole line, or parts thereof.
713  */
714 static void erase_lots(int line_only, int from_begin, int to_end)
715 {
716     pos start, end;
717     int erase_lattr;
718     unsigned long *ldata;
719
720     if (line_only) {
721         start.y = curs.y;
722         start.x = 0;
723         end.y = curs.y + 1;
724         end.x = 0;
725         erase_lattr = FALSE;
726     } else {
727         start.y = 0;
728         start.x = 0;
729         end.y = rows;
730         end.x = 0;
731         erase_lattr = TRUE;
732     }
733     if (!from_begin) {
734         start = curs;
735     }
736     if (!to_end) {
737         end = curs;
738         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                       case 'B':
1543                         move(curs.x, curs.y + def(esc_args[0], 1), 1);
1544                         seen_disp_event = TRUE;
1545                         break;
1546                       case 'a':       /* move right N cols */
1547                         compatibility(ANSI);
1548                       case ANSI('c', '>'):      /* report xterm version */
1549                         compatibility(OTHER);
1550                         /* this reports xterm version 136 so that VIM can
1551                            use the drag messages from the mouse reporting */
1552                         ldisc_send("\033[>0;136;0c", 11);
1553                         break;
1554                       case 'C':
1555                         move(curs.x + def(esc_args[0], 1), curs.y, 1);
1556                         seen_disp_event = TRUE;
1557                         break;
1558                       case 'D':       /* move left N cols */
1559                         move(curs.x - def(esc_args[0], 1), curs.y, 1);
1560                         seen_disp_event = TRUE;
1561                         break;
1562                       case 'E':       /* move down N lines and CR */
1563                         compatibility(ANSI);
1564                         move(0, curs.y + def(esc_args[0], 1), 1);
1565                         seen_disp_event = TRUE;
1566                         break;
1567                       case 'F':       /* move up N lines and CR */
1568                         compatibility(ANSI);
1569                         move(0, curs.y - def(esc_args[0], 1), 1);
1570                         seen_disp_event = TRUE;
1571                         break;
1572                       case 'G':
1573                       case '`':       /* set horizontal posn */
1574                         compatibility(ANSI);
1575                         move(def(esc_args[0], 1) - 1, curs.y, 0);
1576                         seen_disp_event = TRUE;
1577                         break;
1578                       case 'd':       /* set vertical posn */
1579                         compatibility(ANSI);
1580                         move(curs.x,
1581                              (dec_om ? marg_t : 0) + def(esc_args[0],
1582                                                          1) - 1,
1583                              (dec_om ? 2 : 0));
1584                         seen_disp_event = TRUE;
1585                         break;
1586                       case 'H':
1587                       case 'f':       /* set horz and vert posns at once */
1588                         if (esc_nargs < 2)
1589                             esc_args[1] = ARG_DEFAULT;
1590                         move(def(esc_args[1], 1) - 1,
1591                              (dec_om ? marg_t : 0) + def(esc_args[0],
1592                                                          1) - 1,
1593                              (dec_om ? 2 : 0));
1594                         seen_disp_event = TRUE;
1595                         break;
1596                       case 'J':       /* erase screen or parts of it */
1597                         {
1598                             unsigned int i = def(esc_args[0], 0) + 1;
1599                             if (i > 3)
1600                                 i = 0;
1601                             erase_lots(FALSE, !!(i & 2), !!(i & 1));
1602                         }
1603                         disptop = 0;
1604                         seen_disp_event = TRUE;
1605                         break;
1606                       case 'K':       /* erase line or parts of it */
1607                         {
1608                             unsigned int i = def(esc_args[0], 0) + 1;
1609                             if (i > 3)
1610                                 i = 0;
1611                             erase_lots(TRUE, !!(i & 2), !!(i & 1));
1612                         }
1613                         seen_disp_event = TRUE;
1614                         break;
1615                       case 'L':       /* insert lines */
1616                         compatibility(VT102);
1617                         if (curs.y <= marg_b)
1618                             scroll(curs.y, marg_b, -def(esc_args[0], 1),
1619                                    FALSE);
1620                         fix_cpos;
1621                         seen_disp_event = TRUE;
1622                         break;
1623                       case 'M':       /* delete lines */
1624                         compatibility(VT102);
1625                         if (curs.y <= marg_b)
1626                             scroll(curs.y, marg_b, def(esc_args[0], 1),
1627                                    TRUE);
1628                         fix_cpos;
1629                         seen_disp_event = TRUE;
1630                         break;
1631                       case '@':       /* insert chars */
1632                         /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1633                         compatibility(VT102);
1634                         insch(def(esc_args[0], 1));
1635                         seen_disp_event = TRUE;
1636                         break;
1637                       case 'P':       /* delete chars */
1638                         compatibility(VT102);
1639                         insch(-def(esc_args[0], 1));
1640                         seen_disp_event = TRUE;
1641                         break;
1642                       case 'c':       /* terminal type query */
1643                         compatibility(VT100);
1644                         /* This is the response for a VT102 */
1645                         ldisc_send(id_string, strlen(id_string));
1646                         break;
1647                       case 'n':       /* cursor position query */
1648                         if (esc_args[0] == 6) {
1649                             char buf[32];
1650                             sprintf(buf, "\033[%d;%dR", curs.y + 1,
1651                                     curs.x + 1);
1652                             ldisc_send(buf, strlen(buf));
1653                         } else if (esc_args[0] == 5) {
1654                             ldisc_send("\033[0n", 4);
1655                         }
1656                         break;
1657                       case 'h':       /* toggle modes to high */
1658                       case ANSI_QUE('h'):
1659                         compatibility(VT100);
1660                         {
1661                             int i;
1662                             for (i = 0; i < esc_nargs; i++)
1663                                 toggle_mode(esc_args[i], esc_query, TRUE);
1664                         }
1665                         break;
1666                       case 'l':       /* toggle modes to low */
1667                       case ANSI_QUE('l'):
1668                         compatibility(VT100);
1669                         {
1670                             int i;
1671                             for (i = 0; i < esc_nargs; i++)
1672                                 toggle_mode(esc_args[i], esc_query, FALSE);
1673                         }
1674                         break;
1675                       case 'g':       /* clear tabs */
1676                         compatibility(VT100);
1677                         if (esc_nargs == 1) {
1678                             if (esc_args[0] == 0) {
1679                                 tabs[curs.x] = FALSE;
1680                             } else if (esc_args[0] == 3) {
1681                                 int i;
1682                                 for (i = 0; i < cols; i++)
1683                                     tabs[i] = FALSE;
1684                             }
1685                         }
1686                         break;
1687                       case 'r':       /* set scroll margins */
1688                         compatibility(VT100);
1689                         if (esc_nargs <= 2) {
1690                             int top, bot;
1691                             top = def(esc_args[0], 1) - 1;
1692                             bot = (esc_nargs <= 1
1693                                    || esc_args[1] ==
1694                                    0 ? rows : def(esc_args[1], rows)) - 1;
1695                             if (bot >= rows)
1696                                 bot = rows - 1;
1697                             /* VTTEST Bug 9 - if region is less than 2 lines
1698                              * don't change region.
1699                              */
1700                             if (bot - top > 0) {
1701                                 marg_t = top;
1702                                 marg_b = bot;
1703                                 curs.x = 0;
1704                                 /*
1705                                  * I used to think the cursor should be
1706                                  * placed at the top of the newly marginned
1707                                  * area. Apparently not: VMS TPU falls over
1708                                  * if so.
1709                                  *
1710                                  * Well actually it should for Origin mode - RDB
1711                                  */
1712                                 curs.y = (dec_om ? marg_t : 0);
1713                                 fix_cpos;
1714                                 seen_disp_event = TRUE;
1715                             }
1716                         }
1717                         break;
1718                       case 'm':       /* set graphics rendition */
1719                         {
1720                             /* 
1721                              * A VT100 without the AVO only had one attribute, either
1722                              * underline or reverse video depending on the cursor type,
1723                              * this was selected by CSI 7m.
1724                              *
1725                              * case 2:
1726                              *  This is sometimes DIM, eg on the GIGI and Linux
1727                              * case 8:
1728                              *  This is sometimes INVIS various ANSI.
1729                              * case 21:
1730                              *  This like 22 disables BOLD, DIM and INVIS
1731                              *
1732                              * The ANSI colours appear on any terminal that has colour
1733                              * (obviously) but the interaction between sgr0 and the
1734                              * colours varies but is usually related to the background
1735                              * colour erase item.
1736                              * The interaction between colour attributes and the mono
1737                              * ones is also very implementation dependent.
1738                              *
1739                              * The 39 and 49 attributes are likely to be unimplemented.
1740                              */
1741                             int i;
1742                             for (i = 0; i < esc_nargs; i++) {
1743                                 switch (def(esc_args[i], 0)) {
1744                                   case 0:       /* restore defaults */
1745                                     curr_attr = ATTR_DEFAULT;
1746                                     break;
1747                                   case 1:       /* enable bold */
1748                                     compatibility(VT100AVO);
1749                                     curr_attr |= ATTR_BOLD;
1750                                     break;
1751                                   case 21:      /* (enable double underline) */
1752                                     compatibility(OTHER);
1753                                   case 4:       /* enable underline */
1754                                     compatibility(VT100AVO);
1755                                     curr_attr |= ATTR_UNDER;
1756                                     break;
1757                                   case 5:       /* enable blink */
1758                                     compatibility(VT100AVO);
1759                                     curr_attr |= ATTR_BLINK;
1760                                     break;
1761                                   case 7:       /* enable reverse video */
1762                                     curr_attr |= ATTR_REVERSE;
1763                                     break;
1764                                   case 22:      /* disable bold */
1765                                     compatibility2(OTHER, VT220);
1766                                     curr_attr &= ~ATTR_BOLD;
1767                                     break;
1768                                   case 24:      /* disable underline */
1769                                     compatibility2(OTHER, VT220);
1770                                     curr_attr &= ~ATTR_UNDER;
1771                                     break;
1772                                   case 25:      /* disable blink */
1773                                     compatibility2(OTHER, VT220);
1774                                     curr_attr &= ~ATTR_BLINK;
1775                                     break;
1776                                   case 27:      /* disable reverse video */
1777                                     compatibility2(OTHER, VT220);
1778                                     curr_attr &= ~ATTR_REVERSE;
1779                                     break;
1780                                   case 30:
1781                                   case 31:
1782                                   case 32:
1783                                   case 33:
1784                                   case 34:
1785                                   case 35:
1786                                   case 36:
1787                                   case 37:
1788                                     /* foreground */
1789                                     curr_attr &= ~ATTR_FGMASK;
1790                                     curr_attr |=
1791                                         (esc_args[i] - 30) << ATTR_FGSHIFT;
1792                                     break;
1793                                   case 39:      /* default-foreground */
1794                                     curr_attr &= ~ATTR_FGMASK;
1795                                     curr_attr |= ATTR_DEFFG;
1796                                     break;
1797                                   case 40:
1798                                   case 41:
1799                                   case 42:
1800                                   case 43:
1801                                   case 44:
1802                                   case 45:
1803                                   case 46:
1804                                   case 47:
1805                                     /* background */
1806                                     curr_attr &= ~ATTR_BGMASK;
1807                                     curr_attr |=
1808                                         (esc_args[i] - 40) << ATTR_BGSHIFT;
1809                                     break;
1810                                   case 49:      /* default-background */
1811                                     curr_attr &= ~ATTR_BGMASK;
1812                                     curr_attr |= ATTR_DEFBG;
1813                                     break;
1814                                 }
1815                             }
1816                             if (use_bce)
1817                                 erase_char =
1818                                     (' ' |
1819                                      (curr_attr &
1820                                       (ATTR_FGMASK | ATTR_BGMASK |
1821                                        ATTR_BLINK)));
1822                         }
1823                         break;
1824                       case 's':       /* save cursor */
1825                         save_cursor(TRUE);
1826                         break;
1827                       case 'u':       /* restore cursor */
1828                         save_cursor(FALSE);
1829                         seen_disp_event = TRUE;
1830                         break;
1831                       case 't':       /* set page size - ie window height */
1832                         /*
1833                          * VT340/VT420 sequence DECSLPP, DEC only allows values
1834                          *  24/25/36/48/72/144 other emulators (eg dtterm) use
1835                          * illegal values (eg first arg 1..9) for window changing 
1836                          * and reports.
1837                          */
1838                         compatibility(VT340TEXT);
1839                         if (esc_nargs <= 1
1840                             && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1841                             request_resize(cols, def(esc_args[0], 24), 0);
1842                             deselect();
1843                         }
1844                         break;
1845                       case 'S':
1846                         compatibility(SCOANSI);
1847                         scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1848                         fix_cpos;
1849                         wrapnext = FALSE;
1850                         seen_disp_event = TRUE;
1851                         break;
1852                       case 'T':
1853                         compatibility(SCOANSI);
1854                         scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1855                         fix_cpos;
1856                         wrapnext = FALSE;
1857                         seen_disp_event = TRUE;
1858                         break;
1859                       case ANSI('|', '*'):
1860                         /* VT420 sequence DECSNLS
1861                          * Set number of lines on screen
1862                          * VT420 uses VGA like hardware and can support any size in
1863                          * reasonable range (24..49 AIUI) with no default specified.
1864                          */
1865                         compatibility(VT420);
1866                         if (esc_nargs == 1 && esc_args[0] > 0) {
1867                             request_resize(cols,
1868                                            def(esc_args[0], cfg.height),
1869                                            0);
1870                             deselect();
1871                         }
1872                         break;
1873                       case ANSI('|', '$'):
1874                         /* VT340/VT420 sequence DECSCPP
1875                          * Set number of columns per page
1876                          * Docs imply range is only 80 or 132, but I'll allow any.
1877                          */
1878                         compatibility(VT340TEXT);
1879                         if (esc_nargs <= 1) {
1880                             request_resize(def(esc_args[0], cfg.width),
1881                                            rows, 0);
1882                             deselect();
1883                         }
1884                         break;
1885                       case 'X':       /* write N spaces w/o moving cursor */
1886                         /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1887                         compatibility(ANSIMIN);
1888                         {
1889                             int n = def(esc_args[0], 1);
1890                             pos cursplus;
1891                             unsigned long *p = cpos;
1892                             if (n > cols - curs.x)
1893                                 n = cols - curs.x;
1894                             cursplus = curs;
1895                             cursplus.x += n;
1896                             check_selection(curs, cursplus);
1897                             while (n--)
1898                                 *p++ = erase_char;
1899                             seen_disp_event = TRUE;
1900                         }
1901                         break;
1902                       case 'x':       /* report terminal characteristics */
1903                         compatibility(VT100);
1904                         {
1905                             char buf[32];
1906                             int i = def(esc_args[0], 0);
1907                             if (i == 0 || i == 1) {
1908                                 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1909                                 buf[2] += i;
1910                                 ldisc_send(buf, 20);
1911                             }
1912                         }
1913                         break;
1914                       case ANSI('L', '='):
1915                         compatibility(OTHER);
1916                         use_bce = (esc_args[0] <= 0);
1917                         erase_char = ERASE_CHAR;
1918                         if (use_bce)
1919                             erase_char =
1920                                 (' ' |
1921                                  (curr_attr &
1922                                   (ATTR_FGMASK | ATTR_BGMASK)));
1923                         break;
1924                       case ANSI('E', '='):
1925                         compatibility(OTHER);
1926                         blink_is_real = (esc_args[0] >= 1);
1927                         break;
1928                       case ANSI('p', '"'):
1929                         /* Allow the host to make this emulator a 'perfect' VT102.
1930                          * This first appeared in the VT220, but we do need to get 
1931                          * back to PuTTY mode so I won't check it.
1932                          *
1933                          * The arg in 40..42,50 are a PuTTY extension.
1934                          * The 2nd arg, 8bit vs 7bit is not checked.
1935                          *
1936                          * Setting VT102 mode should also change the Fkeys to
1937                          * generate PF* codes as a real VT102 has no Fkeys.
1938                          * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1939                          * send nothing.
1940                          *
1941                          * Note ESC c will NOT change this!
1942                          */
1943
1944                         switch (esc_args[0]) {
1945                           case 61:
1946                             compatibility_level &= ~TM_VTXXX;
1947                             compatibility_level |= TM_VT102;
1948                             break;
1949                           case 62:
1950                             compatibility_level &= ~TM_VTXXX;
1951                             compatibility_level |= TM_VT220;
1952                             break;
1953
1954                           default:
1955                             if (esc_args[0] > 60 && esc_args[0] < 70)
1956                                 compatibility_level |= TM_VTXXX;
1957                             break;
1958
1959                           case 40:
1960                             compatibility_level &= TM_VTXXX;
1961                             break;
1962                           case 41:
1963                             compatibility_level = TM_PUTTY;
1964                             break;
1965                           case 42:
1966                             compatibility_level = TM_SCOANSI;
1967                             break;
1968
1969                           case ARG_DEFAULT:
1970                             compatibility_level = TM_PUTTY;
1971                             break;
1972                           case 50:
1973                             break;
1974                         }
1975
1976                         /* Change the response to CSI c */
1977                         if (esc_args[0] == 50) {
1978                             int i;
1979                             char lbuf[64];
1980                             strcpy(id_string, "\033[?");
1981                             for (i = 1; i < esc_nargs; i++) {
1982                                 if (i != 1)
1983                                     strcat(id_string, ";");
1984                                 sprintf(lbuf, "%d", esc_args[i]);
1985                                 strcat(id_string, lbuf);
1986                             }
1987                             strcat(id_string, "c");
1988                         }
1989 #if 0
1990                         /* Is this a good idea ? 
1991                          * Well we should do a soft reset at this point ...
1992                          */
1993                         if (!has_compat(VT420) && has_compat(VT100)) {
1994                             if (reset_132)
1995                                 request_resize(132, 24, 1);
1996                             else
1997                                 request_resize(80, 24, 1);
1998                         }
1999 #endif
2000                         break;
2001                     }
2002                 break;
2003               case SEEN_OSC:
2004                 osc_w = FALSE;
2005                 switch (c) {
2006                   case 'P':            /* Linux palette sequence */
2007                     termstate = SEEN_OSC_P;
2008                     osc_strlen = 0;
2009                     break;
2010                   case 'R':            /* Linux palette reset */
2011                     palette_reset();
2012                     term_invalidate();
2013                     termstate = TOPLEVEL;
2014                     break;
2015                   case 'W':            /* word-set */
2016                     termstate = SEEN_OSC_W;
2017                     osc_w = TRUE;
2018                     break;
2019                   case '0':
2020                   case '1':
2021                   case '2':
2022                   case '3':
2023                   case '4':
2024                   case '5':
2025                   case '6':
2026                   case '7':
2027                   case '8':
2028                   case '9':
2029                     esc_args[0] = 10 * esc_args[0] + c - '0';
2030                     break;
2031                   case 'L':
2032                     /*
2033                      * Grotty hack to support xterm and DECterm title
2034                      * sequences concurrently.
2035                      */
2036                     if (esc_args[0] == 2) {
2037                         esc_args[0] = 1;
2038                         break;
2039                     }
2040                     /* else fall through */
2041                   default:
2042                     termstate = OSC_STRING;
2043                     osc_strlen = 0;
2044                 }
2045                 break;
2046               case OSC_STRING:
2047                 /*
2048                  * This OSC stuff is EVIL. It takes just one character to get into
2049                  * sysline mode and it's not initially obvious how to get out.
2050                  * So I've added CR and LF as string aborts.
2051                  * This shouldn't effect compatibility as I believe embedded 
2052                  * control characters are supposed to be interpreted (maybe?) 
2053                  * and they don't display anything useful anyway.
2054                  *
2055                  * -- RDB
2056                  */
2057                 if (c == '\n' || c == '\r') {
2058                     termstate = TOPLEVEL;
2059                 } else if (c == 0234 || c == '\007') {
2060                     /*
2061                      * These characters terminate the string; ST and BEL
2062                      * terminate the sequence and trigger instant
2063                      * processing of it, whereas ESC goes back to SEEN_ESC
2064                      * mode unless it is followed by \, in which case it is
2065                      * synonymous with ST in the first place.
2066                      */
2067                     do_osc();
2068                     termstate = TOPLEVEL;
2069                 } else if (c == '\033')
2070                     termstate = OSC_MAYBE_ST;
2071                 else if (osc_strlen < OSC_STR_MAX)
2072                     osc_string[osc_strlen++] = c;
2073                 break;
2074               case SEEN_OSC_P:
2075                 {
2076                     int max = (osc_strlen == 0 ? 21 : 16);
2077                     int val;
2078                     if (c >= '0' && c <= '9')
2079                         val = c - '0';
2080                     else if (c >= 'A' && c <= 'A' + max - 10)
2081                         val = c - 'A' + 10;
2082                     else if (c >= 'a' && c <= 'a' + max - 10)
2083                         val = c - 'a' + 10;
2084                     else {
2085                         termstate = TOPLEVEL;
2086                         break;
2087                     }
2088                     osc_string[osc_strlen++] = val;
2089                     if (osc_strlen >= 7) {
2090                         palette_set(osc_string[0],
2091                                     osc_string[1] * 16 + osc_string[2],
2092                                     osc_string[3] * 16 + osc_string[4],
2093                                     osc_string[5] * 16 + osc_string[6]);
2094                         term_invalidate();
2095                         termstate = TOPLEVEL;
2096                     }
2097                 }
2098                 break;
2099               case SEEN_OSC_W:
2100                 switch (c) {
2101                   case '0':
2102                   case '1':
2103                   case '2':
2104                   case '3':
2105                   case '4':
2106                   case '5':
2107                   case '6':
2108                   case '7':
2109                   case '8':
2110                   case '9':
2111                     esc_args[0] = 10 * esc_args[0] + c - '0';
2112                     break;
2113                   default:
2114                     termstate = OSC_STRING;
2115                     osc_strlen = 0;
2116                 }
2117                 break;
2118               case VT52_ESC:
2119                 termstate = TOPLEVEL;
2120                 seen_disp_event = TRUE;
2121                 switch (c) {
2122                   case 'A':
2123                     move(curs.x, curs.y - 1, 1);
2124                     break;
2125                   case 'B':
2126                     move(curs.x, curs.y + 1, 1);
2127                     break;
2128                   case 'C':
2129                     move(curs.x + 1, curs.y, 1);
2130                     break;
2131                   case 'D':
2132                     move(curs.x - 1, curs.y, 1);
2133                     break;
2134                     /*
2135                      * From the VT100 Manual
2136                      * NOTE: The special graphics characters in the VT100
2137                      *       are different from those in the VT52
2138                      *
2139                      * From VT102 manual:
2140                      *       137 _  Blank             - Same
2141                      *       140 `  Reserved          - Humm.
2142                      *       141 a  Solid rectangle   - Similar
2143                      *       142 b  1/                - Top half of fraction for the
2144                      *       143 c  3/                - subscript numbers below.
2145                      *       144 d  5/
2146                      *       145 e  7/
2147                      *       146 f  Degrees           - Same
2148                      *       147 g  Plus or minus     - Same
2149                      *       150 h  Right arrow
2150                      *       151 i  Ellipsis (dots)
2151                      *       152 j  Divide by
2152                      *       153 k  Down arrow
2153                      *       154 l  Bar at scan 0
2154                      *       155 m  Bar at scan 1
2155                      *       156 n  Bar at scan 2
2156                      *       157 o  Bar at scan 3     - Similar
2157                      *       160 p  Bar at scan 4     - Similar
2158                      *       161 q  Bar at scan 5     - Similar
2159                      *       162 r  Bar at scan 6     - Same
2160                      *       163 s  Bar at scan 7     - Similar
2161                      *       164 t  Subscript 0
2162                      *       165 u  Subscript 1
2163                      *       166 v  Subscript 2
2164                      *       167 w  Subscript 3
2165                      *       170 x  Subscript 4
2166                      *       171 y  Subscript 5
2167                      *       172 z  Subscript 6
2168                      *       173 {  Subscript 7
2169                      *       174 |  Subscript 8
2170                      *       175 }  Subscript 9
2171                      *       176 ~  Paragraph
2172                      *
2173                      */
2174                   case 'F':
2175                     cset_attr[cset = 0] = ATTR_LINEDRW;
2176                     break;
2177                   case 'G':
2178                     cset_attr[cset = 0] = ATTR_ASCII;
2179                     break;
2180                   case 'H':
2181                     move(0, 0, 0);
2182                     break;
2183                   case 'I':
2184                     if (curs.y == 0)
2185                         scroll(0, rows - 1, -1, TRUE);
2186                     else if (curs.y > 0)
2187                         curs.y--;
2188                     fix_cpos;
2189                     wrapnext = FALSE;
2190                     break;
2191                   case 'J':
2192                     erase_lots(FALSE, FALSE, TRUE);
2193                     disptop = 0;
2194                     break;
2195                   case 'K':
2196                     erase_lots(TRUE, FALSE, TRUE);
2197                     break;
2198 #if 0
2199                   case 'V':
2200                     /* XXX Print cursor line */
2201                     break;
2202                   case 'W':
2203                     /* XXX Start controller mode */
2204                     break;
2205                   case 'X':
2206                     /* XXX Stop controller mode */
2207                     break;
2208 #endif
2209                   case 'Y':
2210                     termstate = VT52_Y1;
2211                     break;
2212                   case 'Z':
2213                     ldisc_send("\033/Z", 3);
2214                     break;
2215                   case '=':
2216                     app_keypad_keys = TRUE;
2217                     break;
2218                   case '>':
2219                     app_keypad_keys = FALSE;
2220                     break;
2221                   case '<':
2222                     /* XXX This should switch to VT100 mode not current or default
2223                      *     VT mode. But this will only have effect in a VT220+
2224                      *     emulation.
2225                      */
2226                     vt52_mode = FALSE;
2227                     blink_is_real = cfg.blinktext;
2228                     break;
2229 #if 0
2230                   case '^':
2231                     /* XXX Enter auto print mode */
2232                     break;
2233                   case '_':
2234                     /* XXX Exit auto print mode */
2235                     break;
2236                   case ']':
2237                     /* XXX Print screen */
2238                     break;
2239 #endif
2240
2241 #ifdef VT52_PLUS
2242                   case 'E':
2243                     /* compatibility(ATARI) */
2244                     move(0, 0, 0);
2245                     erase_lots(FALSE, FALSE, TRUE);
2246                     disptop = 0;
2247                     break;
2248                   case 'L':
2249                     /* compatibility(ATARI) */
2250                     if (curs.y <= marg_b)
2251                         scroll(curs.y, marg_b, -1, FALSE);
2252                     break;
2253                   case 'M':
2254                     /* compatibility(ATARI) */
2255                     if (curs.y <= marg_b)
2256                         scroll(curs.y, marg_b, 1, TRUE);
2257                     break;
2258                   case 'b':
2259                     /* compatibility(ATARI) */
2260                     termstate = VT52_FG;
2261                     break;
2262                   case 'c':
2263                     /* compatibility(ATARI) */
2264                     termstate = VT52_BG;
2265                     break;
2266                   case 'd':
2267                     /* compatibility(ATARI) */
2268                     erase_lots(FALSE, TRUE, FALSE);
2269                     disptop = 0;
2270                     break;
2271                   case 'e':
2272                     /* compatibility(ATARI) */
2273                     cursor_on = TRUE;
2274                     break;
2275                   case 'f':
2276                     /* compatibility(ATARI) */
2277                     cursor_on = FALSE;
2278                     break;
2279                     /* case 'j': Save cursor position - broken on ST */
2280                     /* case 'k': Restore cursor position */
2281                   case 'l':
2282                     /* compatibility(ATARI) */
2283                     erase_lots(TRUE, TRUE, TRUE);
2284                     curs.x = 0;
2285                     wrapnext = FALSE;
2286                     fix_cpos;
2287                     break;
2288                   case 'o':
2289                     /* compatibility(ATARI) */
2290                     erase_lots(TRUE, TRUE, FALSE);
2291                     break;
2292                   case 'p':
2293                     /* compatibility(ATARI) */
2294                     curr_attr |= ATTR_REVERSE;
2295                     break;
2296                   case 'q':
2297                     /* compatibility(ATARI) */
2298                     curr_attr &= ~ATTR_REVERSE;
2299                     break;
2300                   case 'v':            /* wrap Autowrap on - Wyse style */
2301                     /* compatibility(ATARI) */
2302                     wrap = 1;
2303                     break;
2304                   case 'w':            /* Autowrap off */
2305                     /* compatibility(ATARI) */
2306                     wrap = 0;
2307                     break;
2308
2309                   case 'R':
2310                     /* compatibility(OTHER) */
2311                     vt52_bold = FALSE;
2312                     curr_attr = ATTR_DEFAULT;
2313                     if (use_bce)
2314                         erase_char = (' ' |
2315                                       (curr_attr &
2316                                        (ATTR_FGMASK | ATTR_BGMASK |
2317                                         ATTR_BLINK)));
2318                     break;
2319                   case 'S':
2320                     /* compatibility(VI50) */
2321                     curr_attr |= ATTR_UNDER;
2322                     break;
2323                   case 'W':
2324                     /* compatibility(VI50) */
2325                     curr_attr &= ~ATTR_UNDER;
2326                     break;
2327                   case 'U':
2328                     /* compatibility(VI50) */
2329                     vt52_bold = TRUE;
2330                     curr_attr |= ATTR_BOLD;
2331                     break;
2332                   case 'T':
2333                     /* compatibility(VI50) */
2334                     vt52_bold = FALSE;
2335                     curr_attr &= ~ATTR_BOLD;
2336                     break;
2337 #endif
2338                 }
2339                 break;
2340               case VT52_Y1:
2341                 termstate = VT52_Y2;
2342                 move(curs.x, c - ' ', 0);
2343                 break;
2344               case VT52_Y2:
2345                 termstate = TOPLEVEL;
2346                 move(c - ' ', curs.y, 0);
2347                 break;
2348
2349 #ifdef VT52_PLUS
2350               case VT52_FG:
2351                 termstate = TOPLEVEL;
2352                 curr_attr &= ~ATTR_FGMASK;
2353                 curr_attr &= ~ATTR_BOLD;
2354                 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2355                 if ((c & 0x8) || vt52_bold)
2356                     curr_attr |= ATTR_BOLD;
2357
2358                 if (use_bce)
2359                     erase_char = (' ' |
2360                                   (curr_attr &
2361                                    (ATTR_FGMASK | ATTR_BGMASK |
2362                                     ATTR_BLINK)));
2363                 break;
2364               case VT52_BG:
2365                 termstate = TOPLEVEL;
2366                 curr_attr &= ~ATTR_BGMASK;
2367                 curr_attr &= ~ATTR_BLINK;
2368                 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2369
2370                 /* Note: bold background */
2371                 if (c & 0x8)
2372                     curr_attr |= ATTR_BLINK;
2373
2374                 if (use_bce)
2375                     erase_char = (' ' |
2376                                   (curr_attr &
2377                                    (ATTR_FGMASK | ATTR_BGMASK |
2378                                     ATTR_BLINK)));
2379                 break;
2380 #endif
2381               default: break;          /* placate gcc warning about enum use */
2382             }
2383         if (selstate != NO_SELECTION) {
2384             pos cursplus = curs;
2385             incpos(cursplus);
2386             check_selection(curs, cursplus);
2387         }
2388     }
2389     inbuf_head = 0;
2390 }
2391
2392 #if 0
2393 /*
2394  * Compare two lines to determine whether they are sufficiently
2395  * alike to scroll-optimise one to the other. Return the degree of
2396  * similarity.
2397  */
2398 static int linecmp(unsigned long *a, unsigned long *b)
2399 {
2400     int i, n;
2401
2402     for (i = n = 0; i < cols; i++)
2403         n += (*a++ == *b++);
2404     return n;
2405 }
2406 #endif
2407
2408 /*
2409  * Given a context, update the window. Out of paranoia, we don't
2410  * allow WM_PAINT responses to do scrolling optimisations.
2411  */
2412 static void do_paint(Context ctx, int may_optimise)
2413 {
2414     int i, j, our_curs_y;
2415     unsigned long rv, cursor;
2416     pos scrpos;
2417     char ch[1024];
2418     long cursor_background = ERASE_CHAR;
2419     long ticks;
2420
2421     /*
2422      * Check the visual bell state.
2423      */
2424     if (in_vbell) {
2425         ticks = GetTickCount();
2426         if (ticks - vbell_timeout >= 0)
2427             in_vbell = FALSE;
2428     }
2429
2430     rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2431
2432     /* Depends on:
2433      * screen array, disptop, scrtop,
2434      * selection, rv, 
2435      * cfg.blinkpc, blink_is_real, tblinker, 
2436      * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2437      */
2438
2439     /* Has the cursor position or type changed ? */
2440     if (cursor_on) {
2441         if (has_focus) {
2442             if (blinker || !cfg.blink_cur)
2443                 cursor = TATTR_ACTCURS;
2444             else
2445                 cursor = 0;
2446         } else
2447             cursor = TATTR_PASCURS;
2448         if (wrapnext)
2449             cursor |= TATTR_RIGHTCURS;
2450     } else
2451         cursor = 0;
2452     our_curs_y = curs.y - disptop;
2453
2454     if (dispcurs && (curstype != cursor ||
2455                      dispcurs !=
2456                      disptext + our_curs_y * (cols + 1) + curs.x)) {
2457         if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2458             dispcurs[-1] |= ATTR_INVALID;
2459         if ((*dispcurs & ATTR_WIDE))
2460             dispcurs[1] |= ATTR_INVALID;
2461         *dispcurs |= ATTR_INVALID;
2462         curstype = 0;
2463     }
2464     dispcurs = NULL;
2465
2466     /* The normal screen data */
2467     for (i = 0; i < rows; i++) {
2468         unsigned long *ldata;
2469         int lattr;
2470         int idx, dirty_line, dirty_run;
2471         unsigned long attr = 0;
2472         int updated_line = 0;
2473         int start = 0;
2474         int ccount = 0;
2475         int last_run_dirty = 0;
2476
2477         scrpos.y = i + disptop;
2478         ldata = lineptr(scrpos.y);
2479         lattr = (ldata[cols] & LATTR_MODE);
2480
2481         idx = i * (cols + 1);
2482         dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2483         disptext[idx + cols] = ldata[cols];
2484
2485         for (j = 0; j < cols; j++, idx++) {
2486             unsigned long tattr, tchar;
2487             unsigned long *d = ldata + j;
2488             int break_run;
2489             scrpos.x = j;
2490
2491             tchar = (*d & (CHAR_MASK | CSET_MASK));
2492             tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2493             switch (tchar & CSET_MASK) {
2494               case ATTR_ASCII:
2495                 tchar = unitab_line[tchar & 0xFF];
2496                 break;
2497               case ATTR_LINEDRW:
2498                 tchar = unitab_xterm[tchar & 0xFF];
2499                 break;
2500             }
2501             tattr |= (tchar & CSET_MASK);
2502             tchar &= CHAR_MASK;
2503
2504             /* Video reversing things */
2505             tattr = (tattr ^ rv
2506                      ^ (posle(selstart, scrpos) &&
2507                         poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2508
2509             /* 'Real' blinking ? */
2510             if (blink_is_real && (tattr & ATTR_BLINK)) {
2511                 if (has_focus && tblinker) {
2512                     tchar = ' ';
2513                     tattr &= ~CSET_MASK;
2514                     tattr |= ATTR_ACP;
2515                 }
2516                 tattr &= ~ATTR_BLINK;
2517             }
2518
2519             /* Cursor here ? Save the 'background' */
2520             if (i == our_curs_y && j == curs.x) {
2521                 cursor_background = tattr | tchar;
2522                 dispcurs = disptext + idx;
2523             }
2524
2525             if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2526                 dirty_line = TRUE;
2527
2528             break_run = (tattr != attr || j - start >= sizeof(ch));
2529
2530             /* Special hack for VT100 Linedraw glyphs */
2531             if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2532                 && tchar <= 0xBD) break_run = TRUE;
2533
2534             if (!dbcs_screenfont && !dirty_line) {
2535                 if ((tchar | tattr) == disptext[idx])
2536                     break_run = TRUE;
2537                 else if (!dirty_run && ccount == 1)
2538                     break_run = TRUE;
2539             }
2540
2541             if (break_run) {
2542                 if ((dirty_run || last_run_dirty) && ccount > 0) {
2543                     do_text(ctx, start, i, ch, ccount, attr, lattr);
2544                     updated_line = 1;
2545                 }
2546                 start = j;
2547                 ccount = 0;
2548                 attr = tattr;
2549                 if (dbcs_screenfont)
2550                     last_run_dirty = dirty_run;
2551                 dirty_run = dirty_line;
2552             }
2553
2554             if ((tchar | tattr) != disptext[idx])
2555                 dirty_run = TRUE;
2556             ch[ccount++] = (char) tchar;
2557             disptext[idx] = tchar | tattr;
2558
2559             /* If it's a wide char step along to the next one. */
2560             if (tattr & ATTR_WIDE) {
2561                 if (++j < cols) {
2562                     idx++;
2563                     d++;
2564                     /* Cursor is here ? Ouch! */
2565                     if (i == our_curs_y && j == curs.x) {
2566                         cursor_background = *d;
2567                         dispcurs = disptext + idx;
2568                     }
2569                     if (disptext[idx] != *d)
2570                         dirty_run = TRUE;
2571                     disptext[idx] = *d;
2572                 }
2573             }
2574         }
2575         if (dirty_run && ccount > 0) {
2576             do_text(ctx, start, i, ch, ccount, attr, lattr);
2577             updated_line = 1;
2578         }
2579
2580         /* Cursor on this line ? (and changed) */
2581         if (i == our_curs_y && (curstype != cursor || updated_line)) {
2582             ch[0] = (char) (cursor_background & CHAR_MASK);
2583             attr = (cursor_background & ATTR_MASK) | cursor;
2584             do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2585             curstype = cursor;
2586         }
2587     }
2588 }
2589
2590 /*
2591  * Flick the switch that says if blinking things should be shown or hidden.
2592  */
2593
2594 void term_blink(int flg)
2595 {
2596     static long last_blink = 0;
2597     static long last_tblink = 0;
2598     long now, blink_diff;
2599
2600     now = GetTickCount();
2601     blink_diff = now - last_tblink;
2602
2603     /* Make sure the text blinks no more than 2Hz */
2604     if (blink_diff < 0 || blink_diff > 450) {
2605         last_tblink = now;
2606         tblinker = !tblinker;
2607     }
2608
2609     if (flg) {
2610         blinker = 1;
2611         last_blink = now;
2612         return;
2613     }
2614
2615     blink_diff = now - last_blink;
2616
2617     /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2618     if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2619         return;
2620
2621     last_blink = now;
2622     blinker = !blinker;
2623 }
2624
2625 /*
2626  * Invalidate the whole screen so it will be repainted in full.
2627  */
2628 void term_invalidate(void)
2629 {
2630     int i;
2631
2632     for (i = 0; i < rows * (cols + 1); i++)
2633         disptext[i] = ATTR_INVALID;
2634 }
2635
2636 /*
2637  * Paint the window in response to a WM_PAINT message.
2638  */
2639 void term_paint(Context ctx, int l, int t, int r, int b)
2640 {
2641     int i, j, left, top, right, bottom;
2642
2643     left = l / font_width;
2644     right = (r - 1) / font_width;
2645     top = t / font_height;
2646     bottom = (b - 1) / font_height;
2647     for (i = top; i <= bottom && i < rows; i++) {
2648         if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2649             for (j = left; j <= right && j < cols; j++)
2650                 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2651         else
2652             for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2653                 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2654     }
2655
2656     /* This should happen soon enough, also for some reason it sometimes 
2657      * fails to actually do anything when re-sizing ... painting the wrong
2658      * window perhaps ?
2659      do_paint (ctx, FALSE);
2660      */
2661 }
2662
2663 /*
2664  * Attempt to scroll the scrollback. The second parameter gives the
2665  * position we want to scroll to; the first is +1 to denote that
2666  * this position is relative to the beginning of the scrollback, -1
2667  * to denote it is relative to the end, and 0 to denote that it is
2668  * relative to the current position.
2669  */
2670 void term_scroll(int rel, int where)
2671 {
2672     int sbtop = -count234(scrollback);
2673
2674     disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2675     if (disptop < sbtop)
2676         disptop = sbtop;
2677     if (disptop > 0)
2678         disptop = 0;
2679     update_sbar();
2680     term_update();
2681 }
2682
2683 static void clipme(pos top, pos bottom)
2684 {
2685     wchar_t *workbuf;
2686     wchar_t *wbptr;                    /* where next char goes within workbuf */
2687     int wblen = 0;                     /* workbuf len */
2688     int buflen;                        /* amount of memory allocated to workbuf */
2689
2690     buflen = 5120;                     /* Default size */
2691     workbuf = smalloc(buflen * sizeof(wchar_t));
2692     wbptr = workbuf;                   /* start filling here */
2693
2694     while (poslt(top, bottom)) {
2695         int nl = FALSE;
2696         unsigned long *ldata = lineptr(top.y);
2697         pos nlpos;
2698
2699         nlpos.y = top.y;
2700         nlpos.x = cols;
2701
2702         if (!(ldata[cols] & LATTR_WRAPPED)) {
2703             while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2704                     (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2705                      (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2706                    && poslt(top, nlpos))
2707                 decpos(nlpos);
2708             if (poslt(nlpos, bottom))
2709                 nl = TRUE;
2710         }
2711         while (poslt(top, bottom) && poslt(top, nlpos)) {
2712 #if 0
2713             char cbuf[16], *p;
2714             sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2715 #else
2716             wchar_t cbuf[16], *p;
2717             int uc = (ldata[top.x] & 0xFFFF);
2718             int set, c;
2719
2720             if (uc == UCSWIDE) {
2721                 top.x++;
2722                 continue;
2723             }
2724
2725             switch (uc & CSET_MASK) {
2726               case ATTR_LINEDRW:
2727                 if (!cfg.rawcnp) {
2728                     uc = unitab_xterm[uc & 0xFF];
2729                     break;
2730                 }
2731               case ATTR_ASCII:
2732                 uc = unitab_line[uc & 0xFF];
2733                 break;
2734             }
2735             switch (uc & CSET_MASK) {
2736               case ATTR_ACP:
2737                 uc = unitab_font[uc & 0xFF];
2738                 break;
2739               case ATTR_OEMCP:
2740                 uc = unitab_oemcp[uc & 0xFF];
2741                 break;
2742             }
2743
2744             set = (uc & CSET_MASK);
2745             c = (uc & CHAR_MASK);
2746             cbuf[0] = uc;
2747             cbuf[1] = 0;
2748
2749             if (DIRECT_FONT(uc)) {
2750                 if (c >= ' ' && c != 0x7F) {
2751                     unsigned char buf[4];
2752                     WCHAR wbuf[4];
2753                     int rv;
2754                     if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2755                         buf[0] = c;
2756                         buf[1] = (unsigned char) ldata[top.x + 1];
2757                         rv = MultiByteToWideChar(font_codepage,
2758                                                  0, buf, 2, wbuf, 4);
2759                         top.x++;
2760                     } else {
2761                         buf[0] = c;
2762                         rv = MultiByteToWideChar(font_codepage,
2763                                                  0, buf, 1, wbuf, 4);
2764                     }
2765
2766                     if (rv > 0) {
2767                         memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2768                         cbuf[rv] = 0;
2769                     }
2770                 }
2771             }
2772 #endif
2773
2774             for (p = cbuf; *p; p++) {
2775                 /* Enough overhead for trailing NL and nul */
2776                 if (wblen >= buflen - 16) {
2777                     workbuf =
2778                         srealloc(workbuf,
2779                                  sizeof(wchar_t) * (buflen += 100));
2780                     wbptr = workbuf + wblen;
2781                 }
2782                 wblen++;
2783                 *wbptr++ = *p;
2784             }
2785             top.x++;
2786         }
2787         if (nl) {
2788             int i;
2789             for (i = 0; i < sel_nl_sz; i++) {
2790                 wblen++;
2791                 *wbptr++ = sel_nl[i];
2792             }
2793         }
2794         top.y++;
2795         top.x = 0;
2796     }
2797     wblen++;
2798     *wbptr++ = 0;
2799     write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2800     if (buflen > 0)                    /* indicates we allocated this buffer */
2801         sfree(workbuf);
2802 }
2803
2804 void term_copyall(void)
2805 {
2806     pos top;
2807     top.y = -count234(scrollback);
2808     top.x = 0;
2809     clipme(top, curs);
2810 }
2811
2812 /*
2813  * The wordness array is mainly for deciding the disposition of the US-ASCII 
2814  * characters.
2815  */
2816 static int wordtype(int uc)
2817 {
2818     static struct {
2819         int start, end, ctype;
2820     } *wptr, ucs_words[] = {
2821         {
2822         128, 160, 0}, {
2823         161, 191, 1}, {
2824         215, 215, 1}, {
2825         247, 247, 1}, {
2826         0x037e, 0x037e, 1},            /* Greek question mark */
2827         {
2828         0x0387, 0x0387, 1},            /* Greek ano teleia */
2829         {
2830         0x055a, 0x055f, 1},            /* Armenian punctuation */
2831         {
2832         0x0589, 0x0589, 1},            /* Armenian full stop */
2833         {
2834         0x0700, 0x070d, 1},            /* Syriac punctuation */
2835         {
2836         0x104a, 0x104f, 1},            /* Myanmar punctuation */
2837         {
2838         0x10fb, 0x10fb, 1},            /* Georgian punctuation */
2839         {
2840         0x1361, 0x1368, 1},            /* Ethiopic punctuation */
2841         {
2842         0x166d, 0x166e, 1},            /* Canadian Syl. punctuation */
2843         {
2844         0x17d4, 0x17dc, 1},            /* Khmer punctuation */
2845         {
2846         0x1800, 0x180a, 1},            /* Mongolian punctuation */
2847         {
2848         0x2000, 0x200a, 0},            /* Various spaces */
2849         {
2850         0x2070, 0x207f, 2},            /* superscript */
2851         {
2852         0x2080, 0x208f, 2},            /* subscript */
2853         {
2854         0x200b, 0x27ff, 1},            /* punctuation and symbols */
2855         {
2856         0x3000, 0x3000, 0},            /* ideographic space */
2857         {
2858         0x3001, 0x3020, 1},            /* ideographic punctuation */
2859         {
2860         0x303f, 0x309f, 3},            /* Hiragana */
2861         {
2862         0x30a0, 0x30ff, 3},            /* Katakana */
2863         {
2864         0x3300, 0x9fff, 3},            /* CJK Ideographs */
2865         {
2866         0xac00, 0xd7a3, 3},            /* Hangul Syllables */
2867         {
2868         0xf900, 0xfaff, 3},            /* CJK Ideographs */
2869         {
2870         0xfe30, 0xfe6b, 1},            /* punctuation forms */
2871         {
2872         0xff00, 0xff0f, 1},            /* half/fullwidth ASCII */
2873         {
2874         0xff1a, 0xff20, 1},            /* half/fullwidth ASCII */
2875         {
2876         0xff3b, 0xff40, 1},            /* half/fullwidth ASCII */
2877         {
2878         0xff5b, 0xff64, 1},            /* half/fullwidth ASCII */
2879         {
2880         0xfff0, 0xffff, 0},            /* half/fullwidth ASCII */
2881         {
2882         0, 0, 0}
2883     };
2884
2885     uc &= (CSET_MASK | CHAR_MASK);
2886
2887     switch (uc & CSET_MASK) {
2888       case ATTR_LINEDRW:
2889         uc = unitab_xterm[uc & 0xFF];
2890         break;
2891       case ATTR_ASCII:
2892         uc = unitab_line[uc & 0xFF];
2893         break;
2894     }
2895     switch (uc & CSET_MASK) {
2896       case ATTR_ACP:
2897         uc = unitab_font[uc & 0xFF];
2898         break;
2899       case ATTR_OEMCP:
2900         uc = unitab_oemcp[uc & 0xFF];
2901         break;
2902     }
2903
2904     if (uc < 0x80)
2905         return wordness[uc];
2906
2907     for (wptr = ucs_words; wptr->start; wptr++) {
2908         if (uc >= wptr->start && uc <= wptr->end)
2909             return wptr->ctype;
2910     }
2911
2912     return 2;
2913 }
2914
2915 /*
2916  * Spread the selection outwards according to the selection mode.
2917  */
2918 static pos sel_spread_half(pos p, int dir)
2919 {
2920     unsigned long *ldata;
2921     short wvalue;
2922
2923     ldata = lineptr(p.y);
2924
2925     switch (selmode) {
2926       case SM_CHAR:
2927         /*
2928          * In this mode, every character is a separate unit, except
2929          * for runs of spaces at the end of a non-wrapping line.
2930          */
2931         if (!(ldata[cols] & LATTR_WRAPPED)) {
2932             unsigned long *q = ldata + cols;
2933             while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2934                 q--;
2935             if (q == ldata + cols)
2936                 q--;
2937             if (p.x >= q - ldata)
2938                 p.x = (dir == -1 ? q - ldata : cols - 1);
2939         }
2940         break;
2941       case SM_WORD:
2942         /*
2943          * In this mode, the units are maximal runs of characters
2944          * whose `wordness' has the same value.
2945          */
2946         wvalue = wordtype(ldata[p.x]);
2947         if (dir == +1) {
2948             while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
2949                 p.x++;
2950         } else {
2951             while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
2952                 p.x--;
2953         }
2954         break;
2955       case SM_LINE:
2956         /*
2957          * In this mode, every line is a unit.
2958          */
2959         p.x = (dir == -1 ? 0 : cols - 1);
2960         break;
2961     }
2962     return p;
2963 }
2964
2965 static void sel_spread(void)
2966 {
2967     selstart = sel_spread_half(selstart, -1);
2968     decpos(selend);
2969     selend = sel_spread_half(selend, +1);
2970     incpos(selend);
2971 }
2972
2973 void term_do_paste(void)
2974 {
2975     wchar_t *data;
2976     int len;
2977
2978     get_clip(&data, &len);
2979     if (data) {
2980         wchar_t *p, *q;
2981
2982         if (paste_buffer)
2983             sfree(paste_buffer);
2984         paste_pos = paste_hold = paste_len = 0;
2985         paste_buffer = smalloc(len * sizeof(wchar_t));
2986
2987         p = q = data;
2988         while (p < data + len) {
2989             while (p < data + len &&
2990                    !(p <= data + len - sel_nl_sz &&
2991                      !memcmp(p, sel_nl, sizeof(sel_nl))))
2992                 p++;
2993
2994             {
2995                 int i;
2996                 for (i = 0; i < p - q; i++) {
2997                     paste_buffer[paste_len++] = q[i];
2998                 }
2999             }
3000
3001             if (p <= data + len - sel_nl_sz &&
3002                 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3003                 paste_buffer[paste_len++] = '\r';
3004                 p += sel_nl_sz;
3005             }
3006             q = p;
3007         }
3008
3009         /* Assume a small paste will be OK in one go. */
3010         if (paste_len < 256) {
3011             luni_send(paste_buffer, paste_len);
3012             if (paste_buffer)
3013                 sfree(paste_buffer);
3014             paste_buffer = 0;
3015             paste_pos = paste_hold = paste_len = 0;
3016         }
3017     }
3018     get_clip(NULL, NULL);
3019 }
3020
3021 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
3022                 int shift, int ctrl)
3023 {
3024     pos selpoint;
3025     unsigned long *ldata;
3026
3027     if (y < 0)
3028         y = 0;
3029     if (y >= rows)
3030         y = rows - 1;
3031     if (x < 0) {
3032         if (y > 0) {
3033             x = cols - 1;
3034             y--;
3035         } else
3036             x = 0;
3037     }
3038     if (x >= cols)
3039         x = cols - 1;
3040
3041     selpoint.y = y + disptop;
3042     selpoint.x = x;
3043     ldata = lineptr(selpoint.y);
3044     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
3045         selpoint.x /= 2;
3046
3047     if (xterm_mouse) {
3048         int encstate = 0, r, c;
3049         char abuf[16];
3050         static int is_down = 0;
3051
3052         switch (b) {
3053           case MBT_LEFT:
3054             encstate = 0x20;           /* left button down */
3055             break;
3056           case MBT_MIDDLE:
3057             encstate = 0x21;
3058             break;
3059           case MBT_RIGHT:
3060             encstate = 0x22;
3061             break;
3062           case MBT_WHEEL_UP:
3063             encstate = 0x60;
3064             break;
3065           case MBT_WHEEL_DOWN:
3066             encstate = 0x61;
3067             break;
3068           default: break;              /* placate gcc warning about enum use */
3069         }
3070         switch (a) {
3071           case MA_DRAG:
3072             if (xterm_mouse == 1)
3073                 return;
3074             encstate += 0x20;
3075             break;
3076           case MA_RELEASE:
3077             encstate = 0x23;
3078             is_down = 0;
3079             break;
3080           case MA_CLICK:
3081             if (is_down == b)
3082                 return;
3083             is_down = b;
3084             break;
3085           default: break;              /* placate gcc warning about enum use */
3086         }
3087         if (shift)
3088             encstate += 0x04;
3089         if (ctrl)
3090             encstate += 0x10;
3091         r = y + 33;
3092         c = x + 33;
3093
3094         sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3095         ldisc_send(abuf, 6);
3096         return;
3097     }
3098
3099     b = translate_button(b);
3100
3101     if (b == MBT_SELECT && a == MA_CLICK) {
3102         deselect();
3103         selstate = ABOUT_TO;
3104         selanchor = selpoint;
3105         selmode = SM_CHAR;
3106     } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3107         deselect();
3108         selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3109         selstate = DRAGGING;
3110         selstart = selanchor = selpoint;
3111         selend = selstart;
3112         incpos(selend);
3113         sel_spread();
3114     } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3115                (b == MBT_EXTEND && a != MA_RELEASE)) {
3116         if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3117             return;
3118         if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3119             if (posdiff(selpoint, selstart) <
3120                 posdiff(selend, selstart) / 2) {
3121                 selanchor = selend;
3122                 decpos(selanchor);
3123             } else {
3124                 selanchor = selstart;
3125             }
3126             selstate = DRAGGING;
3127         }
3128         if (selstate != ABOUT_TO && selstate != DRAGGING)
3129             selanchor = selpoint;
3130         selstate = DRAGGING;
3131         if (poslt(selpoint, selanchor)) {
3132             selstart = selpoint;
3133             selend = selanchor;
3134             incpos(selend);
3135         } else {
3136             selstart = selanchor;
3137             selend = selpoint;
3138             incpos(selend);
3139         }
3140         sel_spread();
3141     } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3142         if (selstate == DRAGGING) {
3143             /*
3144              * We've completed a selection. We now transfer the
3145              * data to the clipboard.
3146              */
3147             clipme(selstart, selend);
3148             selstate = SELECTED;
3149         } else
3150             selstate = NO_SELECTION;
3151     } else if (b == MBT_PASTE
3152                && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3153         term_do_paste();
3154     }
3155
3156     term_update();
3157 }
3158
3159 void term_nopaste()
3160 {
3161     if (paste_len == 0)
3162         return;
3163     sfree(paste_buffer);
3164     paste_buffer = 0;
3165     paste_len = 0;
3166 }
3167
3168 void term_paste()
3169 {
3170     static long last_paste = 0;
3171     long now, paste_diff;
3172
3173     if (paste_len == 0)
3174         return;
3175
3176     /* Don't wait forever to paste */
3177     if (paste_hold) {
3178         now = GetTickCount();
3179         paste_diff = now - last_paste;
3180         if (paste_diff >= 0 && paste_diff < 450)
3181             return;
3182     }
3183     paste_hold = 0;
3184
3185     while (paste_pos < paste_len) {
3186         int n = 0;
3187         while (n + paste_pos < paste_len) {
3188             if (paste_buffer[paste_pos + n++] == '\r')
3189                 break;
3190         }
3191         luni_send(paste_buffer + paste_pos, n);
3192         paste_pos += n;
3193
3194         if (paste_pos < paste_len) {
3195             paste_hold = 1;
3196             return;
3197         }
3198     }
3199     sfree(paste_buffer);
3200     paste_buffer = 0;
3201     paste_len = 0;
3202 }
3203
3204 static void deselect(void)
3205 {
3206     selstate = NO_SELECTION;
3207     selstart.x = selstart.y = selend.x = selend.y = 0;
3208 }
3209
3210 void term_deselect(void)
3211 {
3212     deselect();
3213     term_update();
3214 }
3215
3216 int term_ldisc(int option)
3217 {
3218     if (option == LD_ECHO)
3219         return term_echoing;
3220     if (option == LD_EDIT)
3221         return term_editing;
3222     return FALSE;
3223 }
3224
3225 /*
3226  * from_backend(), to get data from the backend for the terminal.
3227  */
3228 void from_backend(int is_stderr, char *data, int len)
3229 {
3230     while (len--) {
3231         if (inbuf_head >= INBUF_SIZE)
3232             term_out();
3233         inbuf[inbuf_head++] = *data++;
3234     }
3235 }
3236
3237 /*
3238  * Log session traffic.
3239  */
3240 void logtraffic(unsigned char c, int logmode)
3241 {
3242     if (cfg.logtype > 0) {
3243         if (cfg.logtype == logmode) {
3244             /* deferred open file from pgm start? */
3245             if (!lgfp)
3246                 logfopen();
3247             if (lgfp)
3248                 fputc(c, lgfp);
3249         }
3250     }
3251 }
3252
3253 /* open log file append/overwrite mode */
3254 void logfopen(void)
3255 {
3256     char buf[256];
3257     time_t t;
3258     struct tm *tm;
3259     char writemod[4];
3260
3261     if (!cfg.logtype)
3262         return;
3263     sprintf(writemod, "wb");           /* default to rewrite */
3264     lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3265     if (lgfp) {
3266         int i;
3267         fclose(lgfp);
3268         i = askappend(cfg.logfilename);
3269         if (i == 1)
3270             writemod[0] = 'a';         /* set append mode */
3271         else if (i == 0) {             /* cancelled */
3272             lgfp = NULL;
3273             cfg.logtype = 0;           /* disable logging */
3274             return;
3275         }
3276     }
3277
3278     lgfp = fopen(cfg.logfilename, writemod);
3279     if (lgfp) {                        /* enter into event log */
3280         sprintf(buf, "%s session log (%s mode) to file : ",
3281                 (writemod[0] == 'a') ? "Appending" : "Writing new",
3282                 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3283                  cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3284         /* Make sure we do not exceed the output buffer size */
3285         strncat(buf, cfg.logfilename, 128);
3286         buf[strlen(buf)] = '\0';
3287         logevent(buf);
3288
3289         /* --- write header line iinto log file */
3290         fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3291         time(&t);
3292         tm = localtime(&t);
3293         strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3294         fputs(buf, lgfp);
3295         fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3296     }
3297 }
3298
3299 void logfclose(void)
3300 {
3301     if (lgfp) {
3302         fclose(lgfp);
3303         lgfp = NULL;
3304     }
3305 }