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