]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - terminal.c
Using plink with CVS - need to make sure the saved session uses SSH
[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 "putty.h"
9 #include "tree234.h"
10
11 #define CL_ANSIMIN      0x0001  /* Codes in all ANSI like terminals. */
12 #define CL_VT100        0x0002  /* VT100 */
13 #define CL_VT100AVO     0x0004  /* VT100 +AVO; 132x24 (not 132x14) & attrs */
14 #define CL_VT102        0x0008  /* VT102 */
15 #define CL_VT220        0x0010  /* VT220 */
16 #define CL_VT320        0x0020  /* VT320 */
17 #define CL_VT420        0x0040  /* VT420 */
18 #define CL_VT510        0x0080  /* VT510, NB VT510 includes ANSI */
19 #define CL_VT340TEXT    0x0100  /* VT340 extensions that appear in the VT420 */
20 #define CL_SCOANSI      0x1000  /* SCOANSI not in ANSIMIN. */
21 #define CL_ANSI         0x2000  /* ANSI ECMA-48 not in the VT100..VT420 */
22 #define CL_OTHER        0x4000  /* Others, Xterm, linux, putty, dunno, etc */
23
24 #define TM_VT100        (CL_ANSIMIN|CL_VT100)
25 #define TM_VT100AVO     (TM_VT100|CL_VT100AVO)
26 #define TM_VT102        (TM_VT100AVO|CL_VT102)
27 #define TM_VT220        (TM_VT102|CL_VT220)
28 #define TM_VTXXX        (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)
29 #define TM_SCOANSI      (CL_ANSIMIN|CL_SCOANSI)
30
31 #define TM_PUTTY        (0xFFFF)
32
33 #define compatibility(x) \
34     if ( ((CL_##x)&compatibility_level) == 0 ) {        \
35        termstate=TOPLEVEL;                              \
36        break;                                           \
37     }
38 #define compatibility2(x,y) \
39     if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
40        termstate=TOPLEVEL;                              \
41        break;                                           \
42     }
43
44 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
45
46 static int compatibility_level = TM_PUTTY;
47
48 static tree234 *scrollback;            /* lines scrolled off top of screen */
49 static tree234 *screen;                /* lines on primary screen */
50 static tree234 *alt_screen;            /* lines on alternate screen */
51 static int disptop;                    /* distance scrolled back (0 or negative) */
52
53 static unsigned long *cpos;            /* cursor position (convenience) */
54
55 static unsigned long *disptext;        /* buffer of text on real screen */
56 static unsigned long *wanttext;        /* buffer of text we want on screen */
57
58 #define VBELL_TIMEOUT 100              /* millisecond duration of visual bell */
59
60 struct beeptime {
61     struct beeptime *next;
62     long ticks;
63 };
64 static struct beeptime *beephead, *beeptail;
65 int nbeeps;
66 int beep_overloaded;
67 long lastbeep;
68
69 static unsigned char *selspace;        /* buffer for building selections in */
70
71 #define TSIZE (sizeof(unsigned long))
72 #define lineptr(x) ( (unsigned long *) \
73                         ((x) >= 0 ? index234(screen, x) : \
74                              index234(scrollback, (x)+count234(scrollback)) ) )
75 #define fix_cpos  do { cpos = lineptr(curs.y) + curs.x; } while(0)
76
77 static unsigned long curr_attr, save_attr;
78 static unsigned long erase_char = ERASE_CHAR;
79
80 typedef struct {
81     int y, x;
82 } pos;
83 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
84 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
85 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
86 #define posdiff(p1,p2) ( ((p2).y - (p1).y) * (cols+1) + (p2).x - (p1).x )
87 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
88 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
89
90 static pos curs;                       /* cursor */
91 static pos savecurs;                   /* saved cursor position */
92 static int marg_t, marg_b;             /* scroll margins */
93 static int dec_om;                     /* DEC origin mode flag */
94 static int wrap, wrapnext;             /* wrap flags */
95 static int insert;                     /* insert-mode flag */
96 static int cset;                       /* 0 or 1: which char set */
97 static int save_cset, save_csattr;     /* saved with cursor position */
98 static int rvideo;                     /* global reverse video flag */
99 static int cursor_on;                  /* cursor enabled flag */
100 static int reset_132;                  /* Flag ESC c resets to 80 cols */
101 static int use_bce;                    /* Use Background coloured erase */
102 static int blinker;                    /* When blinking is the cursor on ? */
103 static int tblinker;                   /* When the blinking text is on */
104 static int blink_is_real;              /* Actually blink blinking text */
105 static int term_echoing;               /* Does terminal want local echo? */
106 static int term_editing;               /* Does terminal want local edit? */
107
108 static unsigned long cset_attr[2];
109
110 /*
111  * Saved settings on the alternate screen.
112  */
113 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
114 static int alt_t, alt_b;
115 static int alt_which;
116
117 #define ARGS_MAX 32                    /* max # of esc sequence arguments */
118 #define ARG_DEFAULT 0                  /* if an arg isn't specified */
119 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
120 static int esc_args[ARGS_MAX];
121 static int esc_nargs;
122 static int esc_query;
123 #define ANSI(x,y)       ((x)+((y)<<8))
124 #define ANSI_QUE(x)     ANSI(x,TRUE)
125
126 #define OSC_STR_MAX 2048
127 static int osc_strlen;
128 static char osc_string[OSC_STR_MAX+1];
129 static int osc_w;
130
131 static char id_string[1024] = "\033[?6c";
132
133 static unsigned char *tabs;
134
135 static enum {
136     TOPLEVEL,
137     SEEN_ESC,
138     SEEN_CSI,
139     SEEN_OSC,
140     SEEN_OSC_W,
141
142     DO_CTRLS,
143
144     IGNORE_NEXT,
145     SET_GL, SET_GR,
146     SEEN_OSC_P,
147     OSC_STRING, OSC_MAYBE_ST,
148     SEEN_ESCHASH,
149     VT52_ESC,
150     VT52_Y1,
151     VT52_Y2
152 } termstate;
153
154 static enum {
155     NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
156 } selstate;
157 static enum {
158     SM_CHAR, SM_WORD, SM_LINE
159 } selmode;
160 static pos selstart, selend, selanchor;
161
162 static short wordness[256] = {
163     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 01 */
164     0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2, 2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1, /* 23 */
165     1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2, /* 45 */
166     1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1, /* 67 */
167     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 89 */
168     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* AB */
169     2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2, /* CD */
170     2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2, /* EF */
171 };
172
173 static unsigned char sel_nl[] = SEL_NL;
174 static char * paste_buffer = 0;
175 static int paste_len, paste_pos, paste_hold;
176
177 /*
178  * Internal prototypes.
179  */
180 static void do_paint (Context, int);
181 static void erase_lots (int, int, int);
182 static void swap_screen (int);
183 static void update_sbar (void);
184 static void deselect (void);
185 /* log session to file stuff ... */
186 static FILE *lgfp = NULL;
187 static void logtraffic(unsigned char c, int logmode);
188
189 /*
190  * Set up power-on settings for the terminal.
191  */
192 static void power_on(void) {
193     curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
194     alt_t = marg_t = 0;
195     if (rows != -1)
196         alt_b = marg_b = rows - 1;
197     else
198         alt_b = marg_b = 0;
199     if (cols != -1) {
200         int i;
201         for (i = 0; i < cols; i++)
202             tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
203     }
204     alt_om = dec_om = cfg.dec_om;
205     alt_wnext = wrapnext = alt_ins = insert = FALSE;
206     alt_wrap = wrap = cfg.wrap_mode;
207     alt_cset = cset = 0;
208     cset_attr[0] = cset_attr[1] = ATTR_ASCII;
209     rvideo = 0;
210     in_vbell = FALSE;
211     cursor_on = 1;
212     save_attr = curr_attr = ATTR_DEFAULT;
213     term_editing = term_echoing = FALSE;
214     ldisc_send(NULL, 0);               /* cause ldisc to notice changes */
215     app_cursor_keys = cfg.app_cursor;
216     app_keypad_keys = cfg.app_keypad;
217     use_bce = cfg.bce;
218     blink_is_real = cfg.blinktext;
219     erase_char = ERASE_CHAR;
220     alt_which = 0;
221     {
222         int i;
223         for (i = 0; i < 256; i++)
224             wordness[i] = cfg.wordness[i];
225     }
226     if (screen) {
227         swap_screen (1);
228         erase_lots (FALSE, TRUE, TRUE);
229         swap_screen (0);
230         erase_lots (FALSE, TRUE, TRUE);
231     }
232 }
233
234 /*
235  * Force a screen update.
236  */
237 void term_update(void) {
238     Context ctx;
239     ctx = get_ctx();
240     if (ctx) {
241         if ( (seen_key_event && (cfg.scroll_on_key)) ||
242              (seen_disp_event && (cfg.scroll_on_disp)) ) {
243             disptop = 0;               /* return to main screen */
244             seen_disp_event = seen_key_event = 0;
245             update_sbar();
246         }
247         do_paint (ctx, TRUE);
248         sys_cursor(curs.x, curs.y - disptop);
249         free_ctx (ctx);
250     }
251 }
252
253 /*
254  * Same as power_on(), but an external function.
255  */
256 void term_pwron(void) {
257     power_on();
258     fix_cpos;
259     disptop = 0;
260     deselect();
261     term_update();
262 }
263
264 /*
265  * Clear the scrollback.
266  */
267 void term_clrsb(void) {
268     unsigned long *line;
269     disptop = 0;
270     while ((line = delpos234(scrollback, 0)) != NULL) {
271         sfree(line);
272     }
273     update_sbar();
274 }
275
276 /*
277  * Initialise the terminal.
278  */
279 void term_init(void) {
280     screen = alt_screen = scrollback = NULL;
281     disptop = 0;
282     disptext = wanttext = NULL;
283     tabs = NULL;
284     selspace = NULL;
285     deselect();
286     rows = cols = -1;
287     power_on();
288     beephead = beeptail = NULL;
289     nbeeps = 0;
290     lastbeep = FALSE;
291     beep_overloaded = FALSE;
292 }
293
294 /*
295  * Set up the terminal for a given size.
296  */
297 void term_size(int newrows, int newcols, int newsavelines) {
298     tree234 *newsb, *newscreen, *newalt;
299     unsigned long *newdisp, *newwant, *oldline, *line;
300     int i, j, ccols;
301     int posn, oldposn, furthest_back, oldsbsize;
302     int save_alt_which = alt_which;
303
304     if (newrows == rows && newcols == cols && newsavelines == savelines)
305         return;                        /* nothing to do */
306
307     deselect();
308     swap_screen(0);
309
310     alt_t = marg_t = 0;
311     alt_b = marg_b = newrows - 1;
312
313     newsb = newtree234(NULL);
314     newscreen = newtree234(NULL);
315     ccols = (cols < newcols ? cols : newcols);
316     oldsbsize = scrollback ? count234(scrollback) : 0;
317     furthest_back = newrows - rows - oldsbsize;
318     if (furthest_back > 0)
319         furthest_back = 0;
320     if (furthest_back < -newsavelines)
321         furthest_back = -newsavelines;
322     for (posn = newrows; posn-- > furthest_back ;) {
323         oldposn = posn - newrows + rows;
324         if (rows == -1 || oldposn < -oldsbsize) {
325             line = smalloc(TSIZE * (newcols + 1));
326             for (j = 0; j < newcols; j++)
327                 line[j] = erase_char;
328             line[newcols] = 0;
329         } else {
330             oldline = (oldposn >= 0 ?
331                        delpos234(screen, count234(screen)-1) :
332                        delpos234(scrollback, count234(scrollback)-1));
333             if (newcols != cols) {
334                 line = smalloc(TSIZE * (newcols + 1));
335                 for (j = 0; j < ccols; j++)
336                     line[j] = oldline[j];
337                 for (j = ccols; j < newcols; j++)
338                     line[j] = erase_char;
339                 line[newcols] = oldline[cols] & LATTR_MODE;
340                 sfree(oldline);
341             } else {
342                 line = oldline;
343             }
344         }
345         if (posn >= 0)
346             addpos234(newscreen, line, 0);
347         else
348             addpos234(newsb, line, 0);
349     }
350     disptop = 0;
351     if (scrollback) freetree234(scrollback);
352     if (screen) freetree234(screen);
353     scrollback = newsb;
354     screen = newscreen;
355
356     newdisp = smalloc (newrows*(newcols+1)*TSIZE);
357     for (i=0; i<newrows*(newcols+1); i++)
358         newdisp[i] = ATTR_INVALID;
359     sfree (disptext);
360     disptext = newdisp;
361
362     newwant = smalloc (newrows*(newcols+1)*TSIZE);
363     for (i=0; i<newrows*(newcols+1); i++)
364         newwant[i] = ATTR_INVALID;
365     sfree (wanttext);
366     wanttext = newwant;
367
368     newalt = newtree234(NULL);
369     for (i=0; i<newrows; i++) {
370         line = smalloc(TSIZE * (newcols+1));
371         for (j = 0; j <= newcols; j++)
372             line[j] = erase_char;
373         addpos234(newalt, line, i);
374     }
375     if (alt_screen) {
376         while (NULL != (line = delpos234(alt_screen, 0)))
377             sfree(line);
378         freetree234(alt_screen);
379     }
380     alt_screen = newalt;
381
382     sfree (selspace);
383     selspace = smalloc ( (newrows+newsavelines) * (newcols+sizeof(sel_nl)) );
384
385     tabs = srealloc (tabs, newcols*sizeof(*tabs));
386     {
387         int i;
388         for (i = (cols > 0 ? cols : 0); i < newcols; i++)
389             tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
390     }
391
392     if (rows > 0)
393         curs.y += newrows - rows;
394     if (curs.y < 0)
395         curs.y = 0;
396     if (curs.y >= newrows)
397         curs.y = newrows-1;
398     if (curs.x >= newcols)
399         curs.x = newcols-1;
400     alt_x = alt_y = 0;
401     wrapnext = alt_wnext = FALSE;
402
403     rows = newrows;
404     cols = newcols;
405     savelines = newsavelines;
406     fix_cpos;
407
408     swap_screen(save_alt_which);
409
410     update_sbar();
411     term_update();
412 }
413
414 /*
415  * Swap screens.
416  */
417 static void swap_screen (int which) {
418     int t;
419     tree234 *ttr;
420
421     if (which == alt_which)
422         return;
423
424     alt_which = which;
425
426     ttr = alt_screen; alt_screen = screen; screen = ttr;
427     t = curs.x; curs.x = alt_x; alt_x = t;
428     t = curs.y; curs.y = alt_y; alt_y = t;
429     t = marg_t; marg_t = alt_t; alt_t = t;
430     t = marg_b; marg_b = alt_b; alt_b = t;
431     t = dec_om; dec_om = alt_om; alt_om = t;
432     t = wrap; wrap = alt_wrap; alt_wrap = t;
433     t = wrapnext; wrapnext = alt_wnext; alt_wnext = t;
434     t = insert; insert = alt_ins; alt_ins = t;
435     t = cset; cset = alt_cset; alt_cset = t;
436
437     fix_cpos;
438 }
439
440 /*
441  * Update the scroll bar.
442  */
443 static void update_sbar(void) {
444     int nscroll;
445
446     nscroll = count234(scrollback);
447
448     set_sbar (nscroll + rows, nscroll + disptop, rows);
449 }
450
451 /*
452  * Check whether the region bounded by the two pointers intersects
453  * the scroll region, and de-select the on-screen selection if so.
454  */
455 static void check_selection (pos from, pos to) {
456     if (poslt(from, selend) && poslt(selstart, to))
457         deselect();
458 }
459
460 /*
461  * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
462  * for backward.) `sb' is TRUE if the scrolling is permitted to
463  * affect the scrollback buffer.
464  * 
465  * NB this function invalidates all pointers into lines of the
466  * screen data structures. In particular, you MUST call fix_cpos
467  * after calling scroll() and before doing anything else that
468  * uses the cpos shortcut pointer.
469  */
470 static void scroll (int topline, int botline, int lines, int sb) {
471     unsigned long *line, *line2;
472     int i;
473
474     if (topline != 0 || alt_which != 0)
475         sb = FALSE;
476
477     if (lines < 0) {
478         while (lines < 0) {
479             line = delpos234(screen, botline);
480             for (i = 0; i < cols; i++)
481                 line[i] = erase_char;
482             line[cols] = 0;
483             addpos234(screen, line, topline);
484
485             if (selstart.y >= topline && selstart.y <= botline) {
486                 selstart.y++;
487                 if (selstart.y > botline) {
488                     selstart.y = botline;
489                     selstart.x = 0;
490                 }
491             }
492             if (selend.y >= topline && selend.y <= botline) {
493                 selend.y++;
494                 if (selend.y > botline) {
495                     selend.y = botline;
496                     selend.x = 0;
497                 }
498             }
499
500             lines++;
501         }
502     } else {
503         while (lines > 0) {
504             line = delpos234(screen, topline);
505             if (sb) {
506                 int sblen = count234(scrollback);
507                 /*
508                  * We must add this line to the scrollback. We'll
509                  * remove a line from the top of the scrollback to
510                  * replace it, or allocate a new one if the
511                  * scrollback isn't full.
512                  */
513                 if (sblen == savelines)
514                     sblen--, line2 = delpos234(scrollback, 0);
515                 else
516                     line2 = smalloc(TSIZE * (cols+1));
517                 addpos234(scrollback, line, sblen);
518                 line = line2;
519             }
520             for (i = 0; i < cols; i++)
521                 line[i] = erase_char;
522             line[cols] = 0;
523             addpos234(screen, line, botline);
524
525             if (selstart.y >= topline && selstart.y <= botline) {
526                 selstart.y--;
527                 if (selstart.y < topline) {
528                     selstart.y = topline;
529                     selstart.x = 0;
530                 }
531             }
532             if (selend.y >= topline && selend.y <= botline) {
533                 selend.y--;
534                 if (selend.y < topline) {
535                     selend.y = topline;
536                     selend.x = 0;
537                 }
538             }
539
540             lines--;
541         }
542     }
543 }
544
545 /*
546  * Move the cursor to a given position, clipping at boundaries. We
547  * may or may not want to clip at the scroll margin: marg_clip is 0
548  * not to, 1 to disallow _passing_ the margins, and 2 to disallow
549  * even _being_ outside the margins.
550  */
551 static void move (int x, int y, int marg_clip) {
552     if (x < 0)
553         x = 0;
554     if (x >= cols)
555         x = cols-1;
556     if (marg_clip) {
557         if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
558             y = marg_t;
559         if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
560             y = marg_b;
561     }
562     if (y < 0)
563         y = 0;
564     if (y >= rows)
565         y = rows-1;
566     curs.x = x;
567     curs.y = y;
568     fix_cpos;
569     wrapnext = FALSE;
570 }
571
572 /*
573  * Save or restore the cursor and SGR mode.
574  */
575 static void save_cursor(int save) {
576     if (save) {
577         savecurs = curs;
578         save_attr = curr_attr;
579         save_cset = cset;
580         save_csattr = cset_attr[cset];
581     } else {
582         curs = savecurs;
583         /* Make sure the window hasn't shrunk since the save */
584         if (curs.x >= cols) curs.x = cols-1;
585         if (curs.y >= rows) curs.y = rows-1;
586
587         curr_attr = save_attr;
588         cset = save_cset;
589         cset_attr[cset] = save_csattr;
590         fix_cpos;
591         if (use_bce) erase_char = (' ' |(curr_attr&(ATTR_FGMASK|ATTR_BGMASK)));
592     }
593 }
594
595 /*
596  * Erase a large portion of the screen: the whole screen, or the
597  * whole line, or parts thereof.
598  */
599 static void erase_lots (int line_only, int from_begin, int to_end) {
600     pos start, end;
601     int erase_lattr;
602     unsigned long *ldata;
603
604     if (line_only) {
605         start.y = curs.y;
606         start.x = 0;
607         end.y = curs.y + 1;
608         end.x = 0;
609         erase_lattr = FALSE;
610     } else {
611         start.y = 0;
612         start.x = 0;
613         end.y = rows;
614         end.x = 0;
615         erase_lattr = TRUE;
616     }
617     if (!from_begin) {
618         start = curs;
619     }
620     if (!to_end) {
621         end = curs;
622     }
623     check_selection (start, end);
624
625     /* Clear screen also forces a full window redraw, just in case. */
626     if (start.y == 0 && start.x == 0 && end.y == rows)
627        term_invalidate();
628
629     ldata = lineptr(start.y);
630     while (poslt(start, end)) {
631         if (start.y == cols && !erase_lattr)
632             ldata[start.x] &= ~ATTR_WRAPPED;
633         else
634             ldata[start.x] = erase_char;
635         if (incpos(start))
636             ldata = lineptr(start.y);
637     }
638 }
639
640 /*
641  * Insert or delete characters within the current line. n is +ve if
642  * insertion is desired, and -ve for deletion.
643  */
644 static void insch (int n) {
645     int dir = (n < 0 ? -1 : +1);
646     int m;
647     pos cursplus;
648     unsigned long *ldata;
649
650     n = (n < 0 ? -n : n);
651     if (n > cols - curs.x)
652         n = cols - curs.x;
653     m = cols - curs.x - n;
654     cursplus.y = curs.y;
655     cursplus.x = curs.x + n;
656     check_selection (curs, cursplus);
657     ldata = lineptr(curs.y);
658     if (dir < 0) {
659         memmove (ldata + curs.x, ldata + curs.x + n, m*TSIZE);
660         while (n--)
661             ldata[curs.x + m++] = erase_char;
662     } else {
663         memmove (ldata + curs.x + n, ldata + curs.x, m*TSIZE);
664         while (n--)
665             ldata[curs.x + n] = erase_char;
666     }
667 }
668
669 /*
670  * Toggle terminal mode `mode' to state `state'. (`query' indicates
671  * whether the mode is a DEC private one or a normal one.)
672  */
673 static void toggle_mode (int mode, int query, int state) {
674     if (query) switch (mode) {
675       case 1:                          /* application cursor keys */
676         app_cursor_keys = state;
677         break;
678       case 2:                          /* VT52 mode */
679         vt52_mode = !state;
680         break;
681       case 3:                          /* 80/132 columns */
682         deselect();
683         request_resize (state ? 132 : 80, rows, 1);
684         reset_132 = state;
685         break;
686       case 5:                          /* reverse video */
687         rvideo = state;
688         seen_disp_event = TRUE;
689         if (state) term_update();
690         break;
691       case 6:                          /* DEC origin mode */
692         dec_om = state;
693         break;
694       case 7:                          /* auto wrap */
695         wrap = state;
696         break;
697       case 8:                          /* auto key repeat */
698         repeat_off = !state;
699         break;
700       case 10:                         /* set local edit mode */
701         term_editing = state;
702         ldisc_send(NULL, 0);           /* cause ldisc to notice changes */
703         break;
704       case 25:                         /* enable/disable cursor */
705         compatibility2(OTHER,VT220);
706         cursor_on = state;
707         seen_disp_event = TRUE;
708         break;
709       case 47:                         /* alternate screen */
710         compatibility(OTHER);
711         deselect();
712         swap_screen (state);
713         disptop = 0;
714         break;
715     } else switch (mode) {
716       case 4:                          /* set insert mode */
717         compatibility(VT102);
718         insert = state;
719         break;
720       case 12:                         /* set echo mode */
721         term_echoing = !state;
722         ldisc_send(NULL, 0);           /* cause ldisc to notice changes */
723         break;
724       case 20:                         /* Return sends ... */
725         cr_lf_return = state;
726         break;
727     }
728 }
729
730 /*
731  * Process an OSC sequence: set window title or icon name.
732  */
733 static void do_osc(void) {
734     if (osc_w) {
735         while (osc_strlen--)
736             wordness[(unsigned char)osc_string[osc_strlen]] = esc_args[0];
737     } else {
738         osc_string[osc_strlen] = '\0';
739         switch (esc_args[0]) {
740           case 0:
741           case 1:
742             set_icon (osc_string);
743             if (esc_args[0] == 1)
744                 break;
745             /* fall through: parameter 0 means set both */
746           case 2:
747           case 21:
748             set_title (osc_string);
749             break;
750         }
751     }
752 }
753
754 /*
755  * Remove everything currently in `inbuf' and stick it up on the
756  * in-memory display. There's a big state machine in here to
757  * process escape sequences...
758  */
759 void term_out(void) {
760     int c, inbuf_reap;
761
762     for(inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++)
763     {
764         c = inbuf[inbuf_reap];
765
766         /*
767          * Optionally log the session traffic to a file. Useful for
768          * debugging and possibly also useful for actual logging.
769          */
770         logtraffic((unsigned char)c, LGTYP_DEBUG);
771
772         /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
773          * be able to display 8-bit characters, but I'll let that go 'cause
774          * of i18n.
775          */
776         if( ( (c&0x60) == 0 || c == '\177') && 
777              termstate < DO_CTRLS &&
778             ( (c&0x80) == 0 || has_compat(VT220))) {
779             switch (c) {
780               case '\005':             /* terminal type query */
781                 /* Strictly speaking this is VT100 but a VT100 defaults to
782                  * no response. Other terminals respond at their option.
783                  *
784                  * Don't put a CR in the default string as this tends to
785                  * upset some weird software.
786                  *
787                  * An xterm returns "xterm" (5 characters)
788                  */
789                 compatibility(ANSIMIN);
790                 {
791                     char abuf[256], *s, *d;
792                     int state=0;
793                     for(s=cfg.answerback, d=abuf; *s; s++) {
794                         if (state)
795                         {
796                             if (*s >= 'a' && *s <= 'z')
797                                 *d++ = (*s - ('a'-1));
798                             else if ((*s >='@' && *s<='_') ||
799                                      *s == '?' || (*s&0x80))
800                                 *d++ = ('@'^*s);
801                             else if (*s == '~')
802                                 *d++ = '^';
803                             state = 0;
804                         }
805                         else if (*s == '^') {
806                             state = 1;
807                         }
808                         else
809                             *d++ = xlat_kbd2tty((unsigned char)*s);
810                     }
811                     ldisc_send (abuf, d-abuf);
812                 }
813                 break;
814               case '\007':
815                 {
816                     struct beeptime *newbeep;
817                     long ticks;
818
819                     ticks = GetTickCount();
820
821                     if (!beep_overloaded) {
822                         newbeep = smalloc(sizeof(struct beeptime));
823                         newbeep->ticks = ticks;
824                         newbeep->next = NULL;
825                         if (!beephead)
826                             beephead = newbeep;
827                         else
828                             beeptail->next = newbeep;
829                         beeptail = newbeep;
830                         nbeeps++;
831                     }
832
833                     /*
834                      * Throw out any beeps that happened more than
835                      * t seconds ago.
836                      */
837                     while (beephead &&
838                            beephead->ticks < ticks - cfg.bellovl_t*1000) {
839                         struct beeptime *tmp = beephead;
840                         beephead = tmp->next;
841                         sfree(tmp);
842                         if (!beephead)
843                             beeptail = NULL;
844                         nbeeps--;
845                     }
846
847                     if (cfg.bellovl && beep_overloaded &&
848                         ticks-lastbeep >= cfg.bellovl_s * 1000) {
849                         /*
850                          * If we're currently overloaded and the
851                          * last beep was more than s seconds ago,
852                          * leave overload mode.
853                          */
854                         beep_overloaded = FALSE;
855                     } else if (cfg.bellovl && !beep_overloaded &&
856                                nbeeps >= cfg.bellovl_n) {
857                         /*
858                          * Now, if we have n or more beeps
859                          * remaining in the queue, go into overload
860                          * mode.
861                          */
862                         beep_overloaded = TRUE;
863                     }
864                     lastbeep = ticks;
865
866                     /*
867                      * Perform an actual beep if we're not overloaded.
868                      */
869                     if ((!cfg.bellovl || !beep_overloaded) && cfg.beep != 0) {
870                         if (cfg.beep != 2)
871                             beep(cfg.beep);
872                         else if(cfg.beep == 2) {
873                             in_vbell = TRUE;
874                             vbell_timeout = ticks + VBELL_TIMEOUT;
875                             term_update();
876                         }
877                     }
878                     disptop = 0;
879                 }
880                 break;
881               case '\b':
882                 if (curs.x == 0 && curs.y == 0)
883                     ;
884                 else if (curs.x == 0 && curs.y > 0)
885                     curs.x = cols-1, curs.y--;
886                 else if (wrapnext)
887                     wrapnext = FALSE;
888                 else
889                     curs.x--;
890                 fix_cpos;
891                 seen_disp_event = TRUE;
892                 break;
893               case '\016':
894                 compatibility(VT100);
895                 cset = 1;
896                 break;
897               case '\017':
898                 compatibility(VT100);
899                 cset = 0;
900                 break;
901               case '\033':
902                 if (vt52_mode) 
903                    termstate = VT52_ESC;
904                 else {
905                     compatibility(ANSIMIN);
906                     termstate = SEEN_ESC;
907                 }
908                 break;
909               case 0233:
910                 compatibility(VT220);
911                 termstate = SEEN_CSI;
912                 esc_nargs = 1;
913                 esc_args[0] = ARG_DEFAULT;
914                 esc_query = FALSE;
915                 break;
916               case 0235:
917                 compatibility(VT220);
918                 termstate = SEEN_OSC;
919                 esc_args[0] = 0;
920                 break;
921               case '\r':
922                 curs.x = 0;
923                 wrapnext = FALSE;
924                 fix_cpos;
925                 seen_disp_event = TRUE;
926                 paste_hold = 0;
927                 logtraffic((unsigned char)c,LGTYP_ASCII);
928                 break;
929               case '\013':
930               case '\014':
931                 compatibility(VT100);
932               case '\n':
933                 if (curs.y == marg_b)
934                     scroll (marg_t, marg_b, 1, TRUE);
935                 else if (curs.y < rows-1)
936                     curs.y++;
937                 if (cfg.lfhascr)
938                     curs.x = 0;
939                 fix_cpos;
940                 wrapnext = FALSE;
941                 seen_disp_event = 1;
942                 paste_hold = 0;
943                 logtraffic((unsigned char)c,LGTYP_ASCII);
944                 break;
945               case '\t':
946                 {
947                     pos old_curs = curs;
948                     unsigned long *ldata = lineptr(curs.y);
949
950                     do {
951                         curs.x++;
952                     } while (curs.x < cols-1 && !tabs[curs.x]);
953
954                     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
955                     {
956                         if (curs.x >= cols/2)
957                             curs.x = cols/2-1;
958                     }
959                     else
960                     {
961                         if (curs.x >= cols)
962                             curs.x = cols-1;
963                     }
964
965                     fix_cpos;
966                     check_selection (old_curs, curs);
967                 }
968                 seen_disp_event = TRUE;
969                 break;
970               case '\177': /* Destructive backspace
971                               This does nothing on a real VT100 */
972                 compatibility(OTHER);
973                 if (curs.x && !wrapnext) curs.x--;
974                 wrapnext = FALSE;
975                 fix_cpos;
976                 *cpos = (' ' | curr_attr | ATTR_ASCII);
977                 break;
978             }
979         }
980         else switch (termstate) {
981           case TOPLEVEL:
982             /* Only graphic characters get this far, ctrls are stripped above */
983             if (wrapnext && wrap) {
984                 cpos[1] |= ATTR_WRAPPED;
985                 if (curs.y == marg_b)
986                     scroll (marg_t, marg_b, 1, TRUE);
987                 else if (curs.y < rows-1)
988                     curs.y++;
989                 curs.x = 0;
990                 fix_cpos;
991                 wrapnext = FALSE;
992             }
993             if (insert)
994                 insch (1);
995             if (selstate != NO_SELECTION) {
996                 pos cursplus = curs;
997                 incpos(cursplus);
998                 check_selection (curs, cursplus);
999             }
1000             switch (cset_attr[cset]) {
1001                 /*
1002                  * Linedraw characters are different from 'ESC ( B'
1003                  * only for a small range. For ones outside that
1004                  * range, make sure we use the same font as well as
1005                  * the same encoding.
1006                  */
1007             case ATTR_LINEDRW:
1008                 if (c<0x5f || c>0x7F)
1009                     *cpos++ = xlat_tty2scr((unsigned char)c) | curr_attr |
1010                               ATTR_ASCII;
1011                 else if (c==0x5F)
1012                     *cpos++ = ' ' | curr_attr | ATTR_ASCII;
1013                 else
1014                     *cpos++ = ((unsigned char)c) | curr_attr | ATTR_LINEDRW;
1015                 break;
1016             case ATTR_GBCHR:
1017                 /* If UK-ASCII, make the '#' a LineDraw Pound */
1018                 if (c == '#') {
1019                     *cpos++ = '}' | curr_attr | ATTR_LINEDRW;
1020                     break;
1021                 }
1022                 /*FALLTHROUGH*/
1023             default:
1024                 *cpos = xlat_tty2scr((unsigned char)c) | curr_attr |
1025                     (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
1026                 logtraffic((unsigned char)c, LGTYP_ASCII);
1027                 cpos++;
1028                 break;
1029             }
1030             curs.x++;
1031             if (curs.x == cols) {
1032                 cpos--;
1033                 curs.x--;
1034                 wrapnext = TRUE;
1035             }
1036             seen_disp_event = 1;
1037             break;
1038
1039           case IGNORE_NEXT:
1040             termstate = TOPLEVEL;
1041             break;
1042           case OSC_MAYBE_ST:
1043             /*
1044              * This state is virtually identical to SEEN_ESC, with the
1045              * exception that we have an OSC sequence in the pipeline,
1046              * and _if_ we see a backslash, we process it.
1047              */
1048             if (c == '\\') {
1049                 do_osc();
1050                 termstate = TOPLEVEL;
1051                 break;
1052             }
1053             /* else fall through */
1054           case SEEN_ESC:
1055             termstate = TOPLEVEL;
1056             switch (c) {
1057               case ' ':                /* some weird sequence? */
1058                 compatibility(VT220);
1059                 termstate = IGNORE_NEXT;
1060                 break;
1061               case '[':                /* enter CSI mode */
1062                 termstate = SEEN_CSI;
1063                 esc_nargs = 1;
1064                 esc_args[0] = ARG_DEFAULT;
1065                 esc_query = FALSE;
1066                 break;
1067               case ']':                /* xterm escape sequences */
1068                 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1069                 compatibility(OTHER);
1070                 termstate = SEEN_OSC;
1071                 esc_args[0] = 0;
1072                 break;
1073               case '(':                /* should set GL */
1074                 compatibility(VT100);
1075                 termstate = SET_GL;
1076                 break;
1077               case ')':                /* should set GR */
1078                 compatibility(VT100);
1079                 termstate = SET_GR;
1080                 break;
1081               case '7':                /* save cursor */
1082                 compatibility(VT100);
1083                 save_cursor (TRUE);
1084                 break;
1085               case '8':                /* restore cursor */
1086                 compatibility(VT100);
1087                 save_cursor (FALSE);
1088                 seen_disp_event = TRUE;
1089                 break;
1090               case '=':
1091                 compatibility(VT100);
1092                 app_keypad_keys = TRUE;
1093                 break;
1094               case '>':
1095                 compatibility(VT100);
1096                 app_keypad_keys = FALSE;
1097                 break;
1098               case 'D':                /* exactly equivalent to LF */
1099                 compatibility(VT100);
1100                 if (curs.y == marg_b)
1101                     scroll (marg_t, marg_b, 1, TRUE);
1102                 else if (curs.y < rows-1)
1103                     curs.y++;
1104                 fix_cpos;
1105                 wrapnext = FALSE;
1106                 seen_disp_event = TRUE;
1107                 break;
1108               case 'E':                /* exactly equivalent to CR-LF */
1109                 compatibility(VT100);
1110                 curs.x = 0;
1111                 if (curs.y == marg_b)
1112                     scroll (marg_t, marg_b, 1, TRUE);
1113                 else if (curs.y < rows-1)
1114                     curs.y++;
1115                 fix_cpos;
1116                 wrapnext = FALSE;
1117                 seen_disp_event = TRUE;
1118                 break;
1119               case 'M':                /* reverse index - backwards LF */
1120                 compatibility(VT100);
1121                 if (curs.y == marg_t)
1122                     scroll (marg_t, marg_b, -1, TRUE);
1123                 else if (curs.y > 0)
1124                     curs.y--;
1125                 fix_cpos;
1126                 wrapnext = FALSE;
1127                 seen_disp_event = TRUE;
1128                 break;
1129               case 'Z':                /* terminal type query */
1130                 compatibility(VT100);
1131                 ldisc_send (id_string, strlen(id_string));
1132                 break;
1133               case 'c':                /* restore power-on settings */
1134                 compatibility(VT100);
1135                 power_on();
1136                 if (reset_132) {
1137                     request_resize (80, rows, 1);
1138                     reset_132 = 0;
1139                 }
1140                 fix_cpos;
1141                 disptop = 0;
1142                 seen_disp_event = TRUE;
1143                 break;
1144               case '#':                /* ESC # 8 fills screen with Es :-) */
1145                 compatibility(VT100);
1146                 termstate = SEEN_ESCHASH;
1147                 break;
1148               case 'H':                /* set a tab */
1149                 compatibility(VT100);
1150                 tabs[curs.x] = TRUE;
1151                 break;
1152             }
1153             break;
1154           case SEEN_CSI:
1155             termstate = TOPLEVEL;      /* default */
1156             if( isdigit(c) )
1157             {
1158                 if (esc_nargs <= ARGS_MAX) {
1159                     if (esc_args[esc_nargs-1] == ARG_DEFAULT)
1160                         esc_args[esc_nargs-1] = 0;
1161                     esc_args[esc_nargs-1] =
1162                         10 * esc_args[esc_nargs-1] + c - '0';
1163                 }
1164                 termstate = SEEN_CSI;
1165             }
1166             else if( c == ';' )
1167             {
1168                 if (++esc_nargs <= ARGS_MAX)
1169                     esc_args[esc_nargs-1] = ARG_DEFAULT;
1170                 termstate = SEEN_CSI;
1171             }
1172             else if( c < '@' )
1173             {
1174                 if( esc_query )     esc_query = -1;
1175                 else if( c == '?' ) esc_query = TRUE;
1176                 else                esc_query = c;
1177                 termstate = SEEN_CSI;
1178             }
1179             else switch (ANSI(c,esc_query)) {
1180               case 'A':                /* move up N lines */
1181                 move (curs.x, curs.y - def(esc_args[0], 1), 1);
1182                 seen_disp_event = TRUE;
1183                 break;
1184               case 'e':      /* move down N lines */
1185                 compatibility(ANSI);
1186               case 'B':
1187                 move (curs.x, curs.y + def(esc_args[0], 1), 1);
1188                 seen_disp_event = TRUE;
1189                 break;
1190               case 'a':      /* move right N cols */
1191                 compatibility(ANSI);
1192               case 'C':
1193                 move (curs.x + def(esc_args[0], 1), curs.y, 1);
1194                 seen_disp_event = TRUE;
1195                 break;
1196               case 'D':                /* move left N cols */
1197                 move (curs.x - def(esc_args[0], 1), curs.y, 1);
1198                 seen_disp_event = TRUE;
1199                 break;
1200               case 'E':                /* move down N lines and CR */
1201                 compatibility(ANSI);
1202                 move (0, curs.y + def(esc_args[0], 1), 1);
1203                 seen_disp_event = TRUE;
1204                 break;
1205               case 'F':                /* move up N lines and CR */
1206                 compatibility(ANSI);
1207                 move (0, curs.y - def(esc_args[0], 1), 1);
1208                 seen_disp_event = TRUE;
1209                 break;
1210               case 'G': case '`':      /* set horizontal posn */
1211                 compatibility(ANSI);
1212                 move (def(esc_args[0], 1) - 1, curs.y, 0);
1213                 seen_disp_event = TRUE;
1214                 break;
1215               case 'd':                /* set vertical posn */
1216                 compatibility(ANSI);
1217                 move (curs.x, (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
1218                       (dec_om ? 2 : 0));
1219                 seen_disp_event = TRUE;
1220                 break;
1221               case 'H': case 'f':      /* set horz and vert posns at once */
1222                 if (esc_nargs < 2)
1223                     esc_args[1] = ARG_DEFAULT;
1224                 move (def(esc_args[1], 1) - 1,
1225                       (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
1226                       (dec_om ? 2 : 0));
1227                 seen_disp_event = TRUE;
1228                 break;
1229               case 'J':                /* erase screen or parts of it */
1230                 {
1231                     unsigned int i = def(esc_args[0], 0) + 1;
1232                     if (i > 3)
1233                         i = 0;
1234                     erase_lots(FALSE, !!(i & 2), !!(i & 1));
1235                 }
1236                 disptop = 0;
1237                 seen_disp_event = TRUE;
1238                 break;
1239               case 'K':                /* erase line or parts of it */
1240                 {
1241                     unsigned int i = def(esc_args[0], 0) + 1;
1242                     if (i > 3)
1243                         i = 0;
1244                     erase_lots(TRUE, !!(i & 2), !!(i & 1));
1245                 }
1246                 seen_disp_event = TRUE;
1247                 break;
1248               case 'L':                /* insert lines */
1249                 compatibility(VT102);
1250                 if (curs.y <= marg_b)
1251                     scroll (curs.y, marg_b, -def(esc_args[0], 1), FALSE);
1252                 fix_cpos;
1253                 seen_disp_event = TRUE;
1254                 break;
1255               case 'M':                /* delete lines */
1256                 compatibility(VT102);
1257                 if (curs.y <= marg_b)
1258                     scroll (curs.y, marg_b, def(esc_args[0], 1), TRUE);
1259                 fix_cpos;
1260                 seen_disp_event = TRUE;
1261                 break;
1262               case '@':                /* insert chars */
1263                 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1264                 compatibility(VT102);   
1265                 insch (def(esc_args[0], 1));
1266                 seen_disp_event = TRUE;
1267                 break;
1268               case 'P':                /* delete chars */
1269                 compatibility(VT102);   
1270                 insch (-def(esc_args[0], 1));
1271                 seen_disp_event = TRUE;
1272                 break;
1273               case 'c':                /* terminal type query */
1274                 compatibility(VT100);
1275                 /* This is the response for a VT102 */
1276                 ldisc_send (id_string, strlen(id_string));
1277                 break;
1278               case 'n':                /* cursor position query */
1279                 if (esc_args[0] == 6) {
1280                     char buf[32];
1281                     sprintf (buf, "\033[%d;%dR", curs.y + 1, curs.x + 1);
1282                     ldisc_send (buf, strlen(buf));
1283                 }
1284                 else if (esc_args[0] == 5) {
1285                     ldisc_send ("\033[0n", 4);
1286                 }
1287                 break;
1288               case 'h':                /* toggle modes to high */
1289               case ANSI_QUE('h'):
1290                 compatibility(VT100);
1291                 {
1292                     int i;
1293                     for (i=0; i<esc_nargs; i++)
1294                         toggle_mode (esc_args[i], esc_query, TRUE);
1295                 }
1296                 break;
1297               case 'l':                /* toggle modes to low */
1298               case ANSI_QUE('l'):
1299                 compatibility(VT100);
1300                 {
1301                     int i;
1302                     for (i=0; i<esc_nargs; i++)
1303                         toggle_mode (esc_args[i], esc_query, FALSE);
1304                 }
1305                 break;
1306               case 'g':                /* clear tabs */
1307                 compatibility(VT100);
1308                 if (esc_nargs == 1) {
1309                     if (esc_args[0] == 0) {
1310                         tabs[curs.x] = FALSE;
1311                     } else if (esc_args[0] == 3) {
1312                         int i;
1313                         for (i = 0; i < cols; i++)
1314                             tabs[i] = FALSE;
1315                     }
1316                 }
1317                 break;
1318               case 'r':                /* set scroll margins */
1319                 compatibility(VT100);
1320                 if (esc_nargs <= 2) {
1321                     int top, bot;
1322                     top = def(esc_args[0], 1) - 1;
1323                     bot = (esc_nargs <= 1 || esc_args[1] == 0 ? rows :
1324                            def(esc_args[1], rows)) - 1;
1325                     if (bot >= rows)
1326                         bot = rows-1;
1327                     /* VTTEST Bug 9 - if region is less than 2 lines
1328                      * don't change region.
1329                      */
1330                     if (bot-top > 0) {
1331                         marg_t = top;
1332                         marg_b = bot;
1333                         curs.x = 0;
1334                         /*
1335                          * I used to think the cursor should be
1336                          * placed at the top of the newly marginned
1337                          * area. Apparently not: VMS TPU falls over
1338                          * if so.
1339                          *
1340                          * Well actually it should for Origin mode - RDB
1341                          */
1342                         curs.y = (dec_om ? marg_t : 0);
1343                         fix_cpos;
1344                         seen_disp_event = TRUE;
1345                     }
1346                 }
1347                 break;
1348               case 'm':                /* set graphics rendition */
1349                 {
1350                     /* 
1351                      * A VT100 without the AVO only had one attribute, either
1352                      * underline or reverse video depending on the cursor type,
1353                      * this was selected by CSI 7m.
1354                      *
1355                      * case 2:
1356                      *  This is DIM on the VT100-AVO and VT102
1357                      * case 5:
1358                      *  This is BLINK on the VT100-AVO and VT102+
1359                      * case 8:
1360                      *  This is INVIS on the VT100-AVO and VT102
1361                      * case 21:
1362                      *  This like 22 disables BOLD, DIM and INVIS
1363                      *
1364                      * The ANSI colours appear on any terminal that has colour
1365                      * (obviously) but the interaction between sgr0 and the
1366                      * colours varies but is usually related to the background
1367                      * colour erase item.
1368                      * The interaction between colour attributes and the mono
1369                      * ones is also very implementation dependent.
1370                      *
1371                      * The 39 and 49 attributes are likely to be unimplemented.
1372                      */
1373                     int i;
1374                     for (i=0; i<esc_nargs; i++) {
1375                         switch (def(esc_args[i], 0)) {
1376                           case 0:      /* restore defaults */
1377                             curr_attr = ATTR_DEFAULT; break;
1378                           case 1:      /* enable bold */
1379                             compatibility(VT100AVO);
1380                             curr_attr |= ATTR_BOLD; break;
1381                           case 21:     /* (enable double underline) */
1382                             compatibility(OTHER);
1383                           case 4:      /* enable underline */
1384                             compatibility(VT100AVO);
1385                             curr_attr |= ATTR_UNDER; break;
1386                           case 5:      /* enable blink */
1387                             compatibility(VT100AVO);
1388                             curr_attr |= ATTR_BLINK; break;
1389                           case 7:      /* enable reverse video */
1390                             curr_attr |= ATTR_REVERSE; break;
1391                           case 22:     /* disable bold */
1392                             compatibility2(OTHER,VT220);
1393                             curr_attr &= ~ATTR_BOLD; break;
1394                           case 24:     /* disable underline */
1395                             compatibility2(OTHER,VT220);
1396                             curr_attr &= ~ATTR_UNDER; break;
1397                           case 25:     /* disable blink */
1398                             compatibility2(OTHER,VT220);
1399                             curr_attr &= ~ATTR_BLINK; break;
1400                           case 27:     /* disable reverse video */
1401                             compatibility2(OTHER,VT220);
1402                             curr_attr &= ~ATTR_REVERSE; break;
1403                           case 30: case 31: case 32: case 33:
1404                           case 34: case 35: case 36: case 37:
1405                             /* foreground */
1406                             curr_attr &= ~ATTR_FGMASK;
1407                             curr_attr |= (esc_args[i] - 30) << ATTR_FGSHIFT;
1408                             break;
1409                           case 39:     /* default-foreground */
1410                             curr_attr &= ~ATTR_FGMASK;
1411                             curr_attr |= ATTR_DEFFG;
1412                             break;
1413                           case 40: case 41: case 42: case 43:
1414                           case 44: case 45: case 46: case 47:
1415                             /* background */
1416                             curr_attr &= ~ATTR_BGMASK;
1417                             curr_attr |= (esc_args[i] - 40) << ATTR_BGSHIFT;
1418                             break;
1419                           case 49:     /* default-background */
1420                             curr_attr &= ~ATTR_BGMASK;
1421                             curr_attr |= ATTR_DEFBG;
1422                             break;
1423                         }
1424                     }
1425                     if (use_bce) 
1426                        erase_char = 
1427                             (' '|
1428                               (curr_attr&(ATTR_FGMASK|ATTR_BGMASK|ATTR_BLINK))
1429                             );
1430                 }
1431                 break;
1432               case 's':                /* save cursor */
1433                 save_cursor (TRUE);
1434                 break;
1435               case 'u':                /* restore cursor */
1436                 save_cursor (FALSE);
1437                 seen_disp_event = TRUE;
1438                 break;
1439               case 't':                /* set page size - ie window height */
1440                 /*
1441                  * VT340/VT420 sequence DECSLPP, DEC only allows values
1442                  *  24/25/36/48/72/144 other emulators (eg dtterm) use
1443                  * illegal values (eg first arg 1..9) for window changing 
1444                  * and reports.
1445                  */
1446                 compatibility(VT340TEXT);
1447                 if (esc_nargs<=1 && (esc_args[0]<1 || esc_args[0]>=24)) {
1448                     request_resize (cols, def(esc_args[0], 24), 0);
1449                     deselect();
1450                 }
1451                 break;
1452               case ANSI('|', '*'):
1453                 /* VT420 sequence DECSNLS
1454                  * Set number of lines on screen
1455                  * VT420 uses VGA like hardware and can support any size in
1456                  * reasonable range (24..49 AIUI) with no default specified.
1457                  */
1458                 compatibility(VT420);
1459                 if (esc_nargs==1 && esc_args[0]>0) {
1460                     request_resize (cols, def(esc_args[0], cfg.height), 0);
1461                     deselect();
1462                 }
1463                 break;
1464               case ANSI('|', '$'):
1465                 /* VT340/VT420 sequence DECSCPP
1466                  * Set number of columns per page
1467                  * Docs imply range is only 80 or 132, but I'll allow any.
1468                  */
1469                 compatibility(VT340TEXT);
1470                 if (esc_nargs<=1) {
1471                     request_resize (def(esc_args[0], cfg.width), rows, 0);
1472                     deselect();
1473                 }
1474                 break;
1475               case 'X':                /* write N spaces w/o moving cursor */
1476                 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1477                 compatibility(ANSIMIN);
1478                 {
1479                     int n = def(esc_args[0], 1);
1480                     pos cursplus;
1481                     unsigned long *p = cpos;
1482                     if (n > cols - curs.x)
1483                         n = cols - curs.x;
1484                     cursplus = curs;
1485                     cursplus.x += n;
1486                     check_selection (curs, cursplus);
1487                     while (n--)
1488                         *p++ = erase_char;
1489                     seen_disp_event = TRUE;
1490                 }
1491                 break;
1492               case 'x':                /* report terminal characteristics */
1493                 compatibility(VT100);
1494                 {
1495                     char buf[32];
1496                     int i = def(esc_args[0], 0);
1497                     if (i == 0 || i == 1) {
1498                         strcpy (buf, "\033[2;1;1;112;112;1;0x");
1499                         buf[2] += i;
1500                         ldisc_send (buf, 20);
1501                     }
1502                 }
1503                 break;
1504               case ANSI('L','='):
1505                 compatibility(OTHER);
1506                 use_bce = (esc_args[0]<=0);
1507                 erase_char = ERASE_CHAR;
1508                 if (use_bce)
1509                     erase_char = (' '|(curr_attr&(ATTR_FGMASK|ATTR_BGMASK)));
1510                 break;
1511               case ANSI('E','='):
1512                 compatibility(OTHER);
1513                 blink_is_real = (esc_args[0]>=1);
1514                 break;
1515               case ANSI('p','"'):
1516                 /* Allow the host to make this emulator a 'perfect' VT102.
1517                  * This first appeared in the VT220, but we do need to get 
1518                  * back to PuTTY mode so I won't check it.
1519                  *
1520                  * The arg in 40..42 are a PuTTY extension.
1521                  * The 2nd arg, 8bit vs 7bit is not checked.
1522                  *
1523                  * Setting VT102 mode should also change the Fkeys to
1524                  * generate PF* codes as a real VT102 has no Fkeys.
1525                  * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1526                  * send nothing.
1527                  *
1528                  * Note ESC c will NOT change this!
1529                  */
1530
1531                 switch (esc_args[0]) {
1532                 case 61: compatibility_level &= ~TM_VTXXX;
1533                          compatibility_level |=  TM_VT102;      break;
1534                 case 62: compatibility_level &= ~TM_VTXXX;
1535                          compatibility_level |=  TM_VT220;      break;
1536
1537                 default: if( esc_args[0] > 60 && esc_args[0] < 70 )
1538                             compatibility_level |= TM_VTXXX;    
1539                          break;
1540
1541                 case 40: compatibility_level &=  TM_VTXXX;      break;
1542                 case 41: compatibility_level  =  TM_PUTTY;      break;
1543                 case 42: compatibility_level  =  TM_SCOANSI;    break;
1544
1545                 case ARG_DEFAULT: 
1546                          compatibility_level  =  TM_PUTTY;      break;
1547                 case 50: break;
1548                 }
1549
1550                 /* Change the response to CSI c */
1551                 if (esc_args[0] == 50) {
1552                    int i;
1553                    char lbuf[64];
1554                    strcpy(id_string, "\033[?");
1555                    for (i=1; i<esc_nargs; i++) {
1556                       if (i!=1) strcat(id_string, ";");
1557                       sprintf(lbuf, "%d", esc_args[i]);
1558                       strcat(id_string, lbuf);
1559                    }
1560                    strcat(id_string, "c");
1561                 }
1562
1563 #if 0
1564                 /* Is this a good idea ? 
1565                  * Well we should do a soft reset at this point ...
1566                  */
1567                 if (!has_compat(VT420) && has_compat(VT100)) {
1568                     if (reset_132) request_resize (132, 24, 1);
1569                     else           request_resize ( 80, 24, 1);
1570                 }
1571 #endif
1572                 break;
1573             }
1574             break;
1575           case SET_GL:
1576           case SET_GR:
1577             /* VT100 only here, checked above */
1578             switch (c) {
1579               case 'A':
1580                 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
1581                 break;
1582               case '0':
1583                 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
1584                 break;
1585               case 'B':
1586               default:                 /* specifically, 'B' */
1587                 cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
1588                 break;
1589             }
1590             if( !has_compat(VT220) || c != '%' )
1591                 termstate = TOPLEVEL;
1592             break;
1593           case SEEN_OSC:
1594             osc_w = FALSE;
1595             switch (c) {
1596               case 'P':                /* Linux palette sequence */
1597                 termstate = SEEN_OSC_P;
1598                 osc_strlen = 0;
1599                 break;
1600               case 'R':                /* Linux palette reset */
1601                 palette_reset();
1602                 term_invalidate();
1603                 termstate = TOPLEVEL;
1604                 break;
1605               case 'W':                /* word-set */
1606                 termstate = SEEN_OSC_W;
1607                 osc_w = TRUE;
1608                 break;
1609               case '0': case '1': case '2': case '3': case '4':
1610               case '5': case '6': case '7': case '8': case '9':
1611                 esc_args[0] = 10 * esc_args[0] + c - '0';
1612                 break;
1613               case 'L':
1614                 /*
1615                  * Grotty hack to support xterm and DECterm title
1616                  * sequences concurrently.
1617                  */
1618                 if (esc_args[0] == 2) {
1619                     esc_args[0] = 1;
1620                     break;
1621                 }
1622                 /* else fall through */
1623               default:
1624                 termstate = OSC_STRING;
1625                 osc_strlen = 0;
1626             }
1627             break;
1628           case OSC_STRING:
1629             /*
1630              * This OSC stuff is EVIL. It takes just one character to get into
1631              * sysline mode and it's not initially obvious how to get out.
1632              * So I've added CR and LF as string aborts.
1633              * This shouldn't effect compatibility as I believe embedded 
1634              * control characters are supposed to be interpreted (maybe?) 
1635              * and they don't display anything useful anyway.
1636              *
1637              * -- RDB
1638              */
1639             if (c == '\n' || c == '\r') {
1640                 termstate = TOPLEVEL;
1641             } else if (c == 0234 || c == '\007' ) {
1642                 /*
1643                  * These characters terminate the string; ST and BEL
1644                  * terminate the sequence and trigger instant
1645                  * processing of it, whereas ESC goes back to SEEN_ESC
1646                  * mode unless it is followed by \, in which case it is
1647                  * synonymous with ST in the first place.
1648                  */
1649                 do_osc();
1650                 termstate = TOPLEVEL;
1651             } else if (c == '\033')
1652                     termstate = OSC_MAYBE_ST;
1653             else if (osc_strlen < OSC_STR_MAX)
1654                 osc_string[osc_strlen++] = c;
1655             break;
1656           case SEEN_OSC_P:
1657             {
1658                 int max = (osc_strlen == 0 ? 21 : 16);
1659                 int val;
1660                 if (c >= '0' && c <= '9')
1661                     val = c - '0';
1662                 else if (c >= 'A' && c <= 'A'+max-10)
1663                     val = c - 'A' + 10;
1664                 else if (c >= 'a' && c <= 'a'+max-10)
1665                     val = c - 'a' + 10;
1666                 else
1667                     termstate = TOPLEVEL;
1668                 osc_string[osc_strlen++] = val;
1669                 if (osc_strlen >= 7) {
1670                     palette_set (osc_string[0],
1671                                  osc_string[1] * 16 + osc_string[2],
1672                                  osc_string[3] * 16 + osc_string[4],
1673                                  osc_string[5] * 16 + osc_string[6]);
1674                     term_invalidate();
1675                     termstate = TOPLEVEL;
1676                 }
1677             }
1678             break;
1679           case SEEN_OSC_W:
1680             switch (c) {
1681               case '0': case '1': case '2': case '3': case '4':
1682               case '5': case '6': case '7': case '8': case '9':
1683                 esc_args[0] = 10 * esc_args[0] + c - '0';
1684                 break;
1685               default:
1686                 termstate = OSC_STRING;
1687                 osc_strlen = 0;
1688             }
1689             break;
1690           case SEEN_ESCHASH:
1691             {
1692                 unsigned long nlattr;
1693                 unsigned long *ldata;
1694                 int i, j;
1695                 pos scrtop, scrbot;
1696
1697                 switch (c) {
1698                 case '8':
1699                     for (i = 0; i < rows; i++) {
1700                         ldata = lineptr(i);
1701                         for (j = 0; j < cols; j++)
1702                             ldata[j] = ATTR_DEFAULT | 'E';
1703                         ldata[cols] = 0;
1704                     }
1705                     disptop = 0;
1706                     seen_disp_event = TRUE;
1707                     scrtop.x = scrtop.y = 0;
1708                     scrbot.x = 0; scrbot.y = rows;
1709                     check_selection (scrtop, scrbot);
1710                     break;
1711
1712                 case '3': nlattr = LATTR_TOP; goto lattr_common;
1713                 case '4': nlattr = LATTR_BOT; goto lattr_common;
1714                 case '5': nlattr = LATTR_NORM; goto lattr_common;
1715                 case '6': nlattr = LATTR_WIDE;
1716                     lattr_common:
1717
1718                     ldata = lineptr(curs.y);
1719                     ldata[cols] &= ~LATTR_MODE;
1720                     ldata[cols] |=  nlattr;
1721                 }
1722             }
1723             termstate = TOPLEVEL;
1724             break;
1725           case VT52_ESC:
1726             termstate = TOPLEVEL;
1727             seen_disp_event = TRUE;
1728             switch (c) {
1729               case 'A':
1730                 move (curs.x, curs.y - 1, 1);
1731                 break;
1732               case 'B':
1733                 move (curs.x, curs.y + 1, 1);
1734                 break;
1735               case 'C':
1736                 move (curs.x + 1, curs.y, 1);
1737                 break;
1738               case 'D':
1739                 move (curs.x - 1, curs.y, 1);
1740                 break;
1741               case 'F':
1742                 cset_attr[cset=0] = ATTR_LINEDRW;
1743                 break;
1744               case 'G':
1745                 cset_attr[cset=0] = ATTR_ASCII;
1746                 break;
1747               case 'H':
1748                 move (0, 0, 0);
1749                 break;
1750               case 'I':
1751                 if (curs.y == 0)
1752                     scroll (0, rows-1, -1, TRUE);
1753                 else if (curs.y > 0)
1754                     curs.y--;
1755                 fix_cpos;
1756                 wrapnext = FALSE;
1757                 break;
1758               case 'J':
1759                 erase_lots(FALSE, FALSE, TRUE);
1760                 disptop = 0;
1761                 break;
1762               case 'K':
1763                 erase_lots(TRUE, FALSE, TRUE);
1764                 break;
1765               case 'V':
1766                 /* XXX Print cursor line */
1767                 break;
1768               case 'W':
1769                 /* XXX Start controller mode */
1770                 break;
1771               case 'X':
1772                 /* XXX Stop controller mode */
1773                 break;
1774               case 'Y':
1775                 termstate = VT52_Y1;
1776                 break;
1777               case 'Z':
1778                 ldisc_send ("\033/Z", 3);
1779                 break;
1780               case '=':
1781                 app_keypad_keys = TRUE;
1782                 break;
1783               case '>':
1784                 app_keypad_keys = FALSE;
1785                 break;
1786               case '<':
1787                 /* XXX This should switch to VT100 mode not current or default
1788                  *     VT mode. But this will only have effect in a VT220+
1789                  *     emulation.
1790                  */
1791                 vt52_mode = FALSE;
1792                 break;
1793               case '^':
1794                 /* XXX Enter auto print mode */
1795                 break;
1796               case '_':
1797                 /* XXX Exit auto print mode */
1798                 break;
1799               case ']':
1800                 /* XXX Print screen */
1801                 break;
1802             }
1803             break;
1804           case VT52_Y1:
1805             termstate = VT52_Y2;
1806             move(curs.x, c-' ', 0);
1807             break;
1808           case VT52_Y2:
1809             termstate = TOPLEVEL;
1810             move(c-' ', curs.y, 0);
1811             break;
1812         }
1813         if (selstate != NO_SELECTION) {
1814             pos cursplus = curs;
1815             incpos(cursplus);
1816             check_selection (curs, cursplus);
1817         }
1818     }
1819     inbuf_head = 0;
1820 }
1821
1822 /*
1823  * Compare two lines to determine whether they are sufficiently
1824  * alike to scroll-optimise one to the other. Return the degree of
1825  * similarity.
1826  */
1827 static int linecmp (unsigned long *a, unsigned long *b) {
1828     int i, n;
1829
1830     for (i=n=0; i < cols; i++)
1831         n += (*a++ == *b++);
1832     return n;
1833 }
1834
1835 /*
1836  * Given a context, update the window. Out of paranoia, we don't
1837  * allow WM_PAINT responses to do scrolling optimisations.
1838  */
1839 static void do_paint (Context ctx, int may_optimise) {
1840     int i, j, start, our_curs_y;
1841     unsigned long attr, rv, cursor;
1842     pos scrpos;
1843     char ch[1024];
1844     long ticks;
1845
1846     /*
1847      * Check the visual bell state.
1848      */
1849     if (in_vbell) {
1850         ticks = GetTickCount();
1851         if (ticks - vbell_timeout >= 0)
1852             in_vbell = FALSE;
1853     }
1854
1855     /* Depends on:
1856      * screen array, disptop, scrtop,
1857      * selection, rv, 
1858      * cfg.blinkpc, blink_is_real, tblinker, 
1859      * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus
1860      */
1861     if (cursor_on) {
1862         if (has_focus) {
1863             if (blinker || !cfg.blink_cur)
1864                 cursor = ATTR_ACTCURS;
1865             else
1866                 cursor = 0;
1867         }
1868         else
1869             cursor = ATTR_PASCURS;
1870         if (wrapnext)
1871             cursor |= ATTR_RIGHTCURS;
1872     }
1873     else
1874         cursor = 0;
1875     rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
1876     our_curs_y = curs.y - disptop;
1877
1878     for (i=0; i<rows; i++) {
1879         unsigned long *ldata;
1880         int lattr;
1881         scrpos.y = i + disptop;
1882         ldata = lineptr(scrpos.y);
1883         lattr = (ldata[cols] & LATTR_MODE);
1884         for (j=0; j<=cols; j++) {
1885             unsigned long d = ldata[j];
1886             int idx = i*(cols+1)+j;
1887             scrpos.x = j;
1888             
1889             wanttext[idx] = lattr | (((d &~ ATTR_WRAPPED) ^ rv
1890                                       ^ (posle(selstart, scrpos) &&
1891                                          poslt(scrpos, selend) ?
1892                                          ATTR_REVERSE : 0)) |
1893                                      (i==our_curs_y && j==curs.x ? cursor : 0));
1894             if (blink_is_real) {
1895                 if (has_focus && tblinker && (wanttext[idx]&ATTR_BLINK) )
1896                 {
1897                     wanttext[idx] &= ATTR_MASK;
1898                     wanttext[idx] += ' ';
1899                 }
1900                 wanttext[idx] &= ~ATTR_BLINK;
1901             }
1902         }
1903     }
1904
1905     /*
1906      * We would perform scrolling optimisations in here, if they
1907      * didn't have a nasty tendency to cause the whole sodding
1908      * program to hang for a second at speed-critical moments.
1909      * We'll leave it well alone...
1910      */
1911
1912     for (i=0; i<rows; i++) {
1913         int idx = i*(cols+1);
1914         int lattr = (wanttext[idx+cols] & LATTR_MODE);
1915         start = -1;
1916         for (j=0; j<=cols; j++,idx++) {
1917             unsigned long t = wanttext[idx];
1918             int needs_update = (j < cols && t != disptext[idx]);
1919             int keep_going = (start != -1 && needs_update &&
1920                               (t & ATTR_MASK) == attr &&
1921                               j-start < sizeof(ch));
1922             if (start != -1 && !keep_going) {
1923                 do_text (ctx, start, i, ch, j-start, attr, lattr);
1924                 start = -1;
1925             }
1926             if (needs_update) {
1927                 if (start == -1) {
1928                     start = j;
1929                     attr = t & ATTR_MASK;
1930                 }
1931                 ch[j-start] = (char) (t & CHAR_MASK);
1932             }
1933             disptext[idx] = t;
1934         }
1935     }
1936 }
1937
1938 /*
1939  * Flick the switch that says if blinking things should be shown or hidden.
1940  */
1941
1942 void term_blink(int flg) {
1943     static long last_blink = 0;
1944     static long last_tblink = 0;
1945     long now, blink_diff;
1946
1947     now = GetTickCount();
1948     blink_diff = now-last_tblink;
1949
1950     /* Make sure the text blinks no more than 2Hz */
1951     if (blink_diff<0 || blink_diff>450)
1952     {
1953         last_tblink = now;
1954         tblinker = !tblinker;
1955     }
1956
1957     if (flg) {
1958         blinker = 1;
1959         last_blink = now;
1960         return;
1961     } 
1962
1963     blink_diff = now-last_blink;
1964
1965     /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
1966     if (blink_diff>=0 && blink_diff<(long)GetCaretBlinkTime())
1967        return;
1968  
1969     last_blink = now;
1970     blinker = !blinker;
1971 }
1972
1973 /*
1974  * Invalidate the whole screen so it will be repainted in full.
1975  */
1976 void term_invalidate(void) {
1977     int i;
1978
1979     for (i=0; i<rows*(cols+1); i++)
1980         disptext[i] = ATTR_INVALID;
1981 }
1982
1983 /*
1984  * Paint the window in response to a WM_PAINT message.
1985  */
1986 void term_paint (Context ctx, int l, int t, int r, int b) {
1987     int i, j, left, top, right, bottom;
1988
1989     left = l / font_width;
1990     right = (r - 1) / font_width;
1991     top = t / font_height;
1992     bottom = (b - 1) / font_height;
1993     for (i = top; i <= bottom && i < rows ; i++)
1994     {
1995         if ( (disptext[i*(cols+1)+cols]&LATTR_MODE) == LATTR_NORM)
1996             for (j = left; j <= right && j < cols ; j++)
1997                 disptext[i*(cols+1)+j] = ATTR_INVALID;
1998         else
1999             for (j = left/2; j <= right/2+1 && j < cols ; j++)
2000                 disptext[i*(cols+1)+j] = ATTR_INVALID;
2001     }
2002
2003     /* This should happen soon enough, also for some reason it sometimes 
2004      * fails to actually do anything when re-sizing ... painting the wrong
2005      * window perhaps ?
2006     do_paint (ctx, FALSE);
2007     */
2008 }
2009
2010 /*
2011  * Attempt to scroll the scrollback. The second parameter gives the
2012  * position we want to scroll to; the first is +1 to denote that
2013  * this position is relative to the beginning of the scrollback, -1
2014  * to denote it is relative to the end, and 0 to denote that it is
2015  * relative to the current position.
2016  */
2017 void term_scroll (int rel, int where) {
2018     int sbtop = -count234(scrollback);
2019
2020     disptop = (rel < 0 ? 0 :
2021                rel > 0 ? sbtop : disptop) + where;
2022     if (disptop < sbtop)
2023         disptop = sbtop;
2024     if (disptop > 0)
2025         disptop = 0;
2026     update_sbar();
2027     term_update();
2028 }
2029
2030 static void clipme(pos top, pos bottom, char *workbuf) {
2031     char *wbptr;                /* where next char goes within workbuf */
2032     int wblen = 0;              /* workbuf len */
2033     int buflen;                 /* amount of memory allocated to workbuf */
2034
2035     if ( workbuf != NULL ) {    /* user supplied buffer? */
2036         buflen = -1;            /* assume buffer passed in is big enough */
2037         wbptr = workbuf;        /* start filling here */
2038     }
2039     else
2040         buflen = 0;             /* No data is available yet */
2041
2042     while (poslt(top, bottom)) {
2043         int nl = FALSE;
2044         unsigned long *ldata = lineptr(top.y);
2045         pos nlpos;
2046
2047         nlpos.y = top.y;
2048         nlpos.x = cols;
2049
2050         if (!(ldata[cols] & ATTR_WRAPPED)) {
2051             while ((ldata[nlpos.x-1] & CHAR_MASK) == 0x20 && poslt(top, nlpos))
2052                 decpos(nlpos);
2053             if (poslt(nlpos, bottom))
2054                 nl = TRUE;
2055         }
2056         while (poslt(top, bottom) && poslt(top, nlpos)) {
2057             int ch = (ldata[top.x] & CHAR_MASK);
2058             int set = (ldata[top.x] & CSET_MASK);
2059
2060             /* VT Specials -> ISO8859-1 for Cut&Paste */
2061             static const unsigned char poorman2[] =
2062 "* # HTFFCRLF\xB0 \xB1 NLVT+ + + + + - - - - - + + + + | <=>=PI!=\xA3 \xB7 ";
2063
2064             if (set && !cfg.rawcnp) {
2065                 if (set == ATTR_LINEDRW && ch >= 0x60 && ch < 0x7F) {
2066                     int x;
2067                     if ((x = poorman2[2*(ch-0x60)+1]) == ' ')
2068                         x = 0;
2069                     ch = (x<<8) + poorman2[2*(ch-0x60)];
2070                 }
2071             }
2072
2073             while(ch != 0) {
2074                 if (cfg.rawcnp || !!(ch&0xE0)) {
2075                     if ( wblen == buflen )
2076                     {
2077                         workbuf = srealloc(workbuf, buflen += 100);
2078                         wbptr = workbuf + wblen;
2079                     }
2080                     wblen++;
2081                     *wbptr++ = (unsigned char) ch;
2082                 }
2083                 ch>>=8;
2084             }
2085             top.x++;
2086         }
2087         if (nl) {
2088             int i;
2089             for (i=0; i<sizeof(sel_nl); i++)
2090             {
2091                 if ( wblen == buflen )
2092                 {
2093                     workbuf = srealloc(workbuf, buflen += 100);
2094                     wbptr = workbuf + wblen;
2095                 }
2096                 wblen++;
2097                 *wbptr++ = sel_nl[i];
2098             }
2099         }
2100         top.y++;
2101         top.x = 0;
2102     }
2103     write_clip (workbuf, wblen, FALSE); /* transfer to clipboard */
2104     if ( buflen > 0 )   /* indicates we allocated this buffer */
2105         sfree(workbuf);
2106
2107 }
2108 void term_copyall (void) {
2109     pos top;
2110     top.y = -count234(scrollback);
2111     top.x = 0;
2112     clipme(top, curs, NULL /* dynamic allocation */);
2113 }
2114
2115 /*
2116  * Spread the selection outwards according to the selection mode.
2117  */
2118 static pos sel_spread_half (pos p, int dir) {
2119     unsigned long *ldata;
2120     short wvalue;
2121
2122     ldata = lineptr(p.y);
2123
2124     switch (selmode) {
2125       case SM_CHAR:
2126         /*
2127          * In this mode, every character is a separate unit, except
2128          * for runs of spaces at the end of a non-wrapping line.
2129          */
2130         if (!(ldata[cols] & ATTR_WRAPPED)) {
2131             unsigned long *q = ldata+cols;
2132             while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2133                 q--;
2134             if (q == ldata+cols)
2135                 q--;
2136             if (p.x >= q-ldata)
2137                 p.x = (dir == -1 ? q-ldata : cols - 1);
2138         }
2139         break;
2140       case SM_WORD:
2141         /*
2142          * In this mode, the units are maximal runs of characters
2143          * whose `wordness' has the same value.
2144          */
2145         wvalue = wordness[ldata[p.x] & CHAR_MASK];
2146         if (dir == +1) {
2147             while (p.x < cols && wordness[ldata[p.x+1] & CHAR_MASK] == wvalue)
2148                 p.x++;
2149         } else {
2150             while (p.x > 0 && wordness[ldata[p.x-1] & CHAR_MASK] == wvalue)
2151                 p.x--;
2152         }
2153         break;
2154       case SM_LINE:
2155         /*
2156          * In this mode, every line is a unit.
2157          */
2158         p.x = (dir == -1 ? 0 : cols - 1);
2159         break;
2160     }
2161     return p;
2162 }
2163
2164 static void sel_spread (void) {
2165     selstart = sel_spread_half (selstart, -1);
2166     decpos(selend);
2167     selend = sel_spread_half (selend, +1);
2168     incpos(selend);
2169 }
2170
2171 void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) {
2172     pos selpoint;
2173     unsigned long *ldata;
2174     
2175     if (y<0) y = 0;
2176     if (y>=rows) y = rows-1;
2177     if (x<0) {
2178         if (y > 0) {
2179             x = cols-1;
2180             y--;
2181         } else
2182             x = 0;
2183     }
2184     if (x>=cols) x = cols-1;
2185
2186     selpoint.y = y + disptop;
2187     selpoint.x = x;
2188     ldata = lineptr(selpoint.y);
2189     if ((ldata[cols]&LATTR_MODE) != LATTR_NORM)
2190         selpoint.x /= 2;
2191
2192     if (b == MB_SELECT && a == MA_CLICK) {
2193         deselect();
2194         selstate = ABOUT_TO;
2195         selanchor = selpoint;
2196         selmode = SM_CHAR;
2197     } else if (b == MB_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
2198         deselect();
2199         selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
2200         selstate = DRAGGING;
2201         selstart = selanchor = selpoint;
2202         selend = selstart;
2203         incpos(selend);
2204         sel_spread();
2205     } else if ((b == MB_SELECT && a == MA_DRAG) ||
2206                (b == MB_EXTEND && a != MA_RELEASE)) {
2207         if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
2208             return;
2209         if (b == MB_EXTEND && a != MA_DRAG && selstate == SELECTED) {
2210             if (posdiff(selpoint,selstart) < posdiff(selend,selstart)/2) {
2211                 selanchor = selend;
2212                 decpos(selanchor);
2213             } else {
2214                 selanchor = selstart;
2215             }
2216             selstate = DRAGGING;
2217         }
2218         if (selstate != ABOUT_TO && selstate != DRAGGING)
2219             selanchor = selpoint;
2220         selstate = DRAGGING;
2221         if (poslt(selpoint, selanchor)) {
2222             selstart = selpoint;
2223             selend = selanchor;
2224             incpos(selend);
2225         } else {
2226             selstart = selanchor;
2227             selend = selpoint;
2228             incpos(selend);
2229         }
2230         sel_spread();
2231     } else if ((b == MB_SELECT || b == MB_EXTEND) && a == MA_RELEASE) {
2232         if (selstate == DRAGGING) {
2233             /*
2234              * We've completed a selection. We now transfer the
2235              * data to the clipboard.
2236              */
2237             clipme(selstart, selend, selspace);
2238             selstate = SELECTED;
2239         } else
2240             selstate = NO_SELECTION;
2241     } else if (b == MB_PASTE && (a==MA_CLICK || a==MA_2CLK || a==MA_3CLK)) {
2242         char *data;
2243         int len;
2244
2245         get_clip((void **) &data, &len);
2246         if (data) {
2247             char *p, *q;
2248
2249             if (paste_buffer) sfree(paste_buffer);
2250             paste_pos = paste_hold = paste_len = 0;
2251             paste_buffer = smalloc(len);
2252
2253             p = q = data;
2254             while (p < data+len) {
2255                 while (p < data+len &&
2256                        !(p <= data+len-sizeof(sel_nl) &&
2257                          !memcmp(p, sel_nl, sizeof(sel_nl))))
2258                     p++;
2259
2260                 {
2261                     int i;
2262                     unsigned char c;
2263                     for(i=0;i<p-q;i++)
2264                     {
2265                         c=xlat_kbd2tty(q[i]);
2266                         paste_buffer[paste_len++] = c;
2267                     }
2268                 }
2269
2270                 if (p <= data+len-sizeof(sel_nl) &&
2271                     !memcmp(p, sel_nl, sizeof(sel_nl))) {
2272                     paste_buffer[paste_len++] = '\r';
2273                     p += sizeof(sel_nl);
2274                 }
2275                 q = p;
2276             }
2277
2278             /* Assume a small paste will be OK in one go. */
2279             if (paste_len<256) {
2280                 ldisc_send (paste_buffer, paste_len);
2281                 if (paste_buffer) sfree(paste_buffer);
2282                 paste_buffer = 0;
2283                 paste_pos = paste_hold = paste_len = 0;
2284             }
2285         }
2286         get_clip(NULL, NULL);
2287     }
2288
2289     term_update();
2290 }
2291
2292 void term_nopaste() {
2293     if(paste_len == 0) return;
2294     sfree(paste_buffer);
2295     paste_buffer = 0;
2296     paste_len = 0;
2297 }
2298
2299 void term_paste() {
2300     static long last_paste = 0;
2301     long now, paste_diff;
2302
2303     if(paste_len == 0) return;
2304
2305     /* Don't wait forever to paste */
2306     if(paste_hold) {
2307         now = GetTickCount();
2308         paste_diff = now-last_paste;
2309         if (paste_diff>=0 && paste_diff<450)
2310             return;
2311     }
2312     paste_hold = 0;
2313
2314     while(paste_pos<paste_len)
2315     {
2316         int n = 0;
2317         while (n + paste_pos < paste_len) {
2318             if (paste_buffer[paste_pos + n++] == '\r')
2319                 break;
2320         }
2321         ldisc_send (paste_buffer+paste_pos, n);
2322         paste_pos += n;
2323
2324         if (paste_pos < paste_len) {
2325             paste_hold = 1;
2326             return;
2327         }
2328     }
2329     sfree(paste_buffer);
2330     paste_buffer = 0;
2331     paste_len = 0;
2332 }
2333
2334 static void deselect (void) {
2335     selstate = NO_SELECTION;
2336     selstart.x = selstart.y = selend.x = selend.y = 0;
2337 }
2338
2339 void term_deselect (void) {
2340     deselect();
2341     term_update();
2342 }
2343
2344 int term_ldisc(int option) {
2345     if (option == LD_ECHO) return term_echoing;
2346     if (option == LD_EDIT) return term_editing;
2347     return FALSE;
2348 }
2349
2350 /*
2351  * from_backend(), to get data from the backend for the terminal.
2352  */
2353 void from_backend(int is_stderr, char *data, int len) {
2354     while (len--) {
2355         if (inbuf_head >= INBUF_SIZE)
2356             term_out();
2357         inbuf[inbuf_head++] = *data++;
2358     }
2359 }
2360
2361 /*
2362  * Log session traffic.
2363  */
2364 void logtraffic(unsigned char c, int logmode) {
2365     if (cfg.logtype > 0) {
2366         if (cfg.logtype == logmode) {
2367             /* deferred open file from pgm start? */
2368             if (!lgfp) logfopen();
2369             if (lgfp) fputc (c, lgfp);
2370         }
2371     }
2372 }
2373
2374 /* open log file append/overwrite mode */
2375 void logfopen(void) {
2376     char buf[256];
2377     time_t t;
2378     struct tm *tm;
2379     char writemod[4];
2380
2381     if (!cfg.logtype)
2382         return;
2383     sprintf (writemod, "wb");          /* default to rewrite */
2384     lgfp = fopen(cfg.logfilename, "r");  /* file already present? */
2385     if (lgfp) {
2386         int i;
2387         fclose(lgfp);
2388         i = askappend(cfg.logfilename);
2389         if (i == 1)
2390             writemod[0] = 'a';         /* set append mode */
2391         else if (i == 0) {             /* cancelled */
2392             lgfp = NULL;
2393             cfg.logtype = 0;           /* disable logging */
2394             return;
2395         }
2396     }
2397
2398     lgfp = fopen(cfg.logfilename, writemod);
2399     if (lgfp) { /* enter into event log */
2400         sprintf(buf, "%s session log (%s mode) to file : ",
2401                 (writemod[0] == 'a') ? "Appending" : "Writing new",
2402                 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
2403                  cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>")  );
2404         /* Make sure we do not exceed the output buffer size */
2405         strncat (buf, cfg.logfilename, 128);
2406         buf[strlen(buf)] = '\0';
2407         logevent(buf);
2408
2409         /* --- write header line iinto log file */
2410         fputs ("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
2411         time(&t);
2412         tm = localtime(&t);
2413         strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
2414         fputs (buf, lgfp);
2415         fputs (" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
2416     }
2417 }
2418
2419 void logfclose (void) {
2420     if (lgfp) { fclose(lgfp); lgfp = NULL; }
2421 }