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