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