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