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