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