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