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