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