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 */
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)
34 #define TM_PUTTY (0xFFFF)
36 #define compatibility(x) \
37 if ( ((CL_##x)&compatibility_level) == 0 ) { \
41 #define compatibility2(x,y) \
42 if ( ((CL_##x|CL_##y)&compatibility_level) == 0 ) { \
47 #define has_compat(x) ( ((CL_##x)&compatibility_level) != 0 )
49 static int compatibility_level = TM_PUTTY;
51 static tree234 *scrollback; /* lines scrolled off top of screen */
52 static tree234 *screen; /* lines on primary screen */
53 static tree234 *alt_screen; /* lines on alternate screen */
54 static int disptop; /* distance scrolled back (0 or -ve) */
56 static unsigned long *cpos; /* cursor position (convenience) */
58 static unsigned long *disptext; /* buffer of text on real screen */
59 static unsigned long *dispcurs; /* location of cursor on real screen */
60 static unsigned long curstype; /* type of cursor on real screen */
62 #define VBELL_TIMEOUT 100 /* millisecond len of visual bell */
65 struct beeptime *next;
68 static struct beeptime *beephead, *beeptail;
73 #define TSIZE (sizeof(unsigned long))
74 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
76 static unsigned long curr_attr, save_attr;
77 static unsigned long erase_char = ERASE_CHAR;
82 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
83 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
84 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
85 #define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
86 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
87 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
89 static pos curs; /* cursor */
90 static pos savecurs; /* saved cursor position */
91 static int marg_t, marg_b; /* scroll margins */
92 static int dec_om; /* DEC origin mode flag */
93 static int wrap, wrapnext; /* wrap flags */
94 static int insert; /* insert-mode flag */
95 static int cset; /* 0 or 1: which char set */
96 static int save_cset, save_csattr; /* saved with cursor position */
97 static int rvideo; /* global reverse video flag */
98 static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */
99 static int cursor_on; /* cursor enabled flag */
100 static int reset_132; /* Flag ESC c resets to 80 cols */
101 static int use_bce; /* Use Background coloured erase */
102 static int blinker; /* When blinking is the cursor on ? */
103 static int tblinker; /* When the blinking text is on */
104 static int blink_is_real; /* Actually blink blinking text */
105 static int term_echoing; /* Does terminal want local echo? */
106 static int term_editing; /* Does terminal want local edit? */
107 static int vt52_bold; /* Force bold on non-bold colours */
108 static int utf_state; /* Is there a pending UTF-8 character */
109 static int utf_char; /* and what is it so far. */
110 static int utf_size; /* The size of the UTF character. */
112 static int xterm_mouse; /* send mouse messages to app */
114 static unsigned long cset_attr[2];
117 * Saved settings on the alternate screen.
119 static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
120 static int alt_t, alt_b;
121 static int alt_which;
123 #define ARGS_MAX 32 /* max # of esc sequence arguments */
124 #define ARG_DEFAULT 0 /* if an arg isn't specified */
125 #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
126 static int esc_args[ARGS_MAX];
127 static int esc_nargs;
128 static int esc_query;
129 #define ANSI(x,y) ((x)+((y)<<8))
130 #define ANSI_QUE(x) ANSI(x,TRUE)
132 #define OSC_STR_MAX 2048
133 static int osc_strlen;
134 static char osc_string[OSC_STR_MAX + 1];
137 static char id_string[1024] = "\033[?6c";
139 static unsigned char *tabs;
151 OSC_STRING, OSC_MAYBE_ST,
160 NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
163 SM_CHAR, SM_WORD, SM_LINE
165 static pos selstart, selend, selanchor;
167 static short wordness[256] = {
168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169 0, 0, 0, 0, 0, 0, 0, 0, /* 01 */
170 0, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
171 2, 2, 1, 1, 1, 1, 1, 1, /* 23 */
172 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
173 2, 2, 2, 1, 1, 1, 1, 2, /* 45 */
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, 1, /* 67 */
176 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
177 1, 1, 1, 1, 1, 1, 1, 1, /* 89 */
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, /* AB */
180 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1,
181 2, 2, 2, 2, 2, 2, 2, 2, /* CD */
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, /* EF */
186 #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
187 static wchar_t sel_nl[] = SEL_NL;
188 static wchar_t *paste_buffer = 0;
189 static int paste_len, paste_pos, paste_hold;
192 * Internal prototypes.
194 static void do_paint(Context, int);
195 static void erase_lots(int, int, int);
196 static void swap_screen(int);
197 static void update_sbar(void);
198 static void deselect(void);
199 /* log session to file stuff ... */
200 static FILE *lgfp = NULL;
201 static void logtraffic(unsigned char c, int logmode);
204 * Resize a line to make it `cols' columns wide.
206 unsigned long *resizeline(unsigned long *line, int cols)
209 unsigned long lineattrs;
211 if (line[0] != (unsigned long)cols) {
213 * This line is the wrong length, which probably means it
214 * hasn't been accessed since a resize. Resize it now.
217 lineattrs = line[oldlen + 1];
218 line = srealloc(line, TSIZE * (2 + cols));
220 for (i = oldlen; i < cols; i++)
221 line[i + 1] = ERASE_CHAR;
222 line[cols + 1] = lineattrs & LATTR_MODE;
229 * Retrieve a line of the screen or of the scrollback, according to
230 * whether the y coordinate is non-negative or negative
233 unsigned long *lineptr(int y, int lineno)
235 unsigned long *line, *newline;
243 whichtree = scrollback;
244 treeindex = y + count234(scrollback);
246 line = index234(whichtree, treeindex);
248 /* We assume that we don't screw up and retrieve something out of range. */
249 assert(line != NULL);
251 newline = resizeline(line, cols);
252 if (newline != line) {
253 delpos234(whichtree, treeindex);
254 addpos234(whichtree, newline, treeindex);
260 #define lineptr(x) lineptr(x,__LINE__)
262 * Set up power-on settings for the terminal.
264 static void power_on(void)
266 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
269 alt_b = marg_b = rows - 1;
274 for (i = 0; i < cols; i++)
275 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
277 alt_om = dec_om = cfg.dec_om;
278 alt_wnext = wrapnext = alt_ins = insert = FALSE;
279 alt_wrap = wrap = cfg.wrap_mode;
281 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
286 save_attr = curr_attr = ATTR_DEFAULT;
287 term_editing = term_echoing = FALSE;
288 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
289 app_cursor_keys = cfg.app_cursor;
290 app_keypad_keys = cfg.app_keypad;
292 blink_is_real = cfg.blinktext;
293 erase_char = ERASE_CHAR;
297 for (i = 0; i < 256; i++)
298 wordness[i] = cfg.wordness[i];
302 erase_lots(FALSE, TRUE, TRUE);
304 erase_lots(FALSE, TRUE, TRUE);
309 * Force a screen update.
311 void term_update(void)
318 if ((seen_key_event && (cfg.scroll_on_key)) ||
319 (seen_disp_event && (cfg.scroll_on_disp))) {
320 disptop = 0; /* return to main screen */
321 seen_disp_event = seen_key_event = 0;
324 sys_cursor(curs.x, curs.y - disptop);
330 * Same as power_on(), but an external function.
332 void term_pwron(void)
342 * Clear the scrollback.
344 void term_clrsb(void)
348 while ((line = delpos234(scrollback, 0)) != NULL) {
355 * Initialise the terminal.
359 screen = alt_screen = scrollback = NULL;
361 disptext = dispcurs = NULL;
366 beephead = beeptail = NULL;
369 beep_overloaded = FALSE;
373 * Set up the terminal for a given size.
375 void term_size(int newrows, int newcols, int newsavelines)
378 unsigned long *newdisp, *line;
381 int save_alt_which = alt_which;
383 if (newrows == rows && newcols == cols && newsavelines == savelines)
384 return; /* nothing to do */
390 alt_b = marg_b = newrows - 1;
393 scrollback = newtree234(NULL);
394 screen = newtree234(NULL);
399 * Resize the screen and scrollback. We only need to shift
400 * lines around within our data structures, because lineptr()
401 * will take care of resizing each individual line if
404 * - If the new screen and the old screen differ in length, we
405 * must shunt some lines in from the scrollback or out to
408 * - If doing that fails to provide us with enough material to
409 * fill the new screen (i.e. the number of rows needed in
410 * the new screen exceeds the total number in the previous
411 * screen+scrollback), we must invent some blank lines to
414 * - Then, if the new scrollback length is less than the
415 * amount of scrollback we actually have, we must throw some
418 sblen = count234(scrollback);
419 /* Do this loop to expand the screen if newrows > rows */
420 for (i = rows; i < newrows; i++) {
422 line = delpos234(scrollback, --sblen);
424 line = smalloc(TSIZE * (newcols + 2));
426 for (j = 0; j <= newcols; j++)
427 line[j + 1] = ERASE_CHAR;
429 addpos234(screen, line, 0);
431 /* Do this loop to shrink the screen if newrows < rows */
432 for (i = newrows; i < rows; i++) {
433 line = delpos234(screen, 0);
434 addpos234(scrollback, line, sblen++);
436 assert(count234(screen) == newrows);
437 while (sblen > newsavelines) {
438 line = delpos234(scrollback, 0);
442 assert(count234(scrollback) <= newsavelines);
445 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
446 for (i = 0; i < newrows * (newcols + 1); i++)
447 newdisp[i] = ATTR_INVALID;
452 newalt = newtree234(NULL);
453 for (i = 0; i < newrows; i++) {
454 line = smalloc(TSIZE * (newcols + 2));
456 for (j = 0; j <= newcols; j++)
457 line[j + 1] = erase_char;
458 addpos234(newalt, line, i);
461 while (NULL != (line = delpos234(alt_screen, 0)))
463 freetree234(alt_screen);
467 tabs = srealloc(tabs, newcols * sizeof(*tabs));
470 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
471 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
475 curs.y += newrows - rows;
478 if (curs.y >= newrows)
479 curs.y = newrows - 1;
480 if (curs.x >= newcols)
481 curs.x = newcols - 1;
483 wrapnext = alt_wnext = FALSE;
487 savelines = newsavelines;
490 swap_screen(save_alt_which);
499 static void swap_screen(int which)
504 if (which == alt_which)
531 wrapnext = alt_wnext;
544 * Update the scroll bar.
546 static void update_sbar(void)
550 nscroll = count234(scrollback);
552 set_sbar(nscroll + rows, nscroll + disptop, rows);
556 * Check whether the region bounded by the two pointers intersects
557 * the scroll region, and de-select the on-screen selection if so.
559 static void check_selection(pos from, pos to)
561 if (poslt(from, selend) && poslt(selstart, to))
566 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
567 * for backward.) `sb' is TRUE if the scrolling is permitted to
568 * affect the scrollback buffer.
570 * NB this function invalidates all pointers into lines of the
571 * screen data structures. In particular, you MUST call fix_cpos
572 * after calling scroll() and before doing anything else that
573 * uses the cpos shortcut pointer.
575 static void scroll(int topline, int botline, int lines, int sb)
577 unsigned long *line, *line2;
580 if (topline != 0 || alt_which != 0)
585 line = delpos234(screen, botline);
586 line = resizeline(line, cols);
587 for (i = 0; i < cols; i++)
588 line[i + 1] = erase_char;
590 addpos234(screen, line, topline);
592 if (selstart.y >= topline && selstart.y <= botline) {
594 if (selstart.y > botline) {
595 selstart.y = botline;
599 if (selend.y >= topline && selend.y <= botline) {
601 if (selend.y > botline) {
611 line = delpos234(screen, topline);
612 if (sb && savelines > 0) {
613 int sblen = count234(scrollback);
615 * We must add this line to the scrollback. We'll
616 * remove a line from the top of the scrollback to
617 * replace it, or allocate a new one if the
618 * scrollback isn't full.
620 if (sblen == savelines) {
621 sblen--, line2 = delpos234(scrollback, 0);
623 line2 = smalloc(TSIZE * (cols + 2));
626 addpos234(scrollback, line, sblen);
629 line = resizeline(line, cols);
630 for (i = 0; i < cols; i++)
631 line[i + 1] = erase_char;
633 addpos234(screen, line, botline);
635 if (selstart.y >= topline && selstart.y <= botline) {
637 if (selstart.y < topline) {
638 selstart.y = topline;
642 if (selend.y >= topline && selend.y <= botline) {
644 if (selend.y < topline) {
656 * Move the cursor to a given position, clipping at boundaries. We
657 * may or may not want to clip at the scroll margin: marg_clip is 0
658 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
659 * even _being_ outside the margins.
661 static void move(int x, int y, int marg_clip)
668 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
670 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
684 * Save or restore the cursor and SGR mode.
686 static void save_cursor(int save)
690 save_attr = curr_attr;
692 save_csattr = cset_attr[cset];
695 /* Make sure the window hasn't shrunk since the save */
701 curr_attr = save_attr;
703 cset_attr[cset] = save_csattr;
706 erase_char = (' ' | (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
711 * Erase a large portion of the screen: the whole screen, or the
712 * whole line, or parts thereof.
714 static void erase_lots(int line_only, int from_begin, int to_end)
718 unsigned long *ldata;
739 check_selection(start, end);
741 /* Clear screen also forces a full window redraw, just in case. */
742 if (start.y == 0 && start.x == 0 && end.y == rows)
745 ldata = lineptr(start.y);
746 while (poslt(start, end)) {
747 if (start.y == cols && !erase_lattr)
748 ldata[start.x] &= ~LATTR_WRAPPED;
750 ldata[start.x] = erase_char;
751 if (incpos(start) && start.y < rows)
752 ldata = lineptr(start.y);
757 * Insert or delete characters within the current line. n is +ve if
758 * insertion is desired, and -ve for deletion.
760 static void insch(int n)
762 int dir = (n < 0 ? -1 : +1);
765 unsigned long *ldata;
767 n = (n < 0 ? -n : n);
768 if (n > cols - curs.x)
770 m = cols - curs.x - n;
772 cursplus.x = curs.x + n;
773 check_selection(curs, cursplus);
774 ldata = lineptr(curs.y);
776 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
778 ldata[curs.x + m++] = erase_char;
780 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
782 ldata[curs.x + n] = erase_char;
787 * Toggle terminal mode `mode' to state `state'. (`query' indicates
788 * whether the mode is a DEC private one or a normal one.)
790 static void toggle_mode(int mode, int query, int state)
796 case 1: /* application cursor keys */
797 app_cursor_keys = state;
799 case 2: /* VT52 mode */
802 blink_is_real = FALSE;
805 blink_is_real = cfg.blinktext;
808 case 3: /* 80/132 columns */
810 request_resize(state ? 132 : 80, rows, 1);
813 case 5: /* reverse video */
815 * Toggle reverse video. If we receive an OFF within the
816 * visual bell timeout period after an ON, we trigger an
817 * effective visual bell, so that ESC[?5hESC[?5l will
818 * always be an actually _visible_ visual bell.
820 ticks = GetTickCount();
821 if (rvideo && !state && /* we're turning it off */
822 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
823 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
824 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
825 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
826 } else if (!rvideo && state) {
827 /* This is an ON, so we notice the time and save it. */
828 rvbell_timeout = ticks + VBELL_TIMEOUT;
831 seen_disp_event = TRUE;
835 case 6: /* DEC origin mode */
838 case 7: /* auto wrap */
841 case 8: /* auto key repeat */
844 case 10: /* set local edit mode */
845 term_editing = state;
846 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
848 case 25: /* enable/disable cursor */
849 compatibility2(OTHER, VT220);
851 seen_disp_event = TRUE;
853 case 47: /* alternate screen */
854 compatibility(OTHER);
859 case 1000: /* xterm mouse 1 */
860 xterm_mouse = state ? 1 : 0;
861 set_raw_mouse_mode(state);
863 case 1002: /* xterm mouse 2 */
864 xterm_mouse = state ? 2 : 0;
865 set_raw_mouse_mode(state);
869 case 4: /* set insert mode */
870 compatibility(VT102);
873 case 12: /* set echo mode */
874 term_echoing = !state;
875 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
877 case 20: /* Return sends ... */
878 cr_lf_return = state;
880 case 34: /* Make cursor BIG */
881 compatibility2(OTHER, VT220);
887 * Process an OSC sequence: set window title or icon name.
889 static void do_osc(void)
893 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
895 osc_string[osc_strlen] = '\0';
896 switch (esc_args[0]) {
899 set_icon(osc_string);
900 if (esc_args[0] == 1)
902 /* fall through: parameter 0 means set both */
905 set_title(osc_string);
912 * Remove everything currently in `inbuf' and stick it up on the
913 * in-memory display. There's a big state machine in here to
914 * process escape sequences...
920 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
921 c = inbuf[inbuf_reap];
924 * Optionally log the session traffic to a file. Useful for
925 * debugging and possibly also useful for actual logging.
927 logtraffic((unsigned char) c, LGTYP_DEBUG);
929 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
930 * be able to display 8-bit characters, but I'll let that go 'cause
934 /* First see about all those translations. */
935 if (termstate == TOPLEVEL) {
940 /* I know; gotos are evil. This one is really bad!
941 * But before you try removing it follow the path of the
942 * sequence "0x5F 0xC0 0x71" with UTF and VTGraphics on.
945 if (cfg.no_vt_graph_with_utf8) break;
948 } else if ((c & 0xe0) == 0xc0) {
949 utf_size = utf_state = 1;
950 utf_char = (c & 0x1f);
951 } else if ((c & 0xf0) == 0xe0) {
952 utf_size = utf_state = 2;
953 utf_char = (c & 0x0f);
954 } else if ((c & 0xf8) == 0xf0) {
955 utf_size = utf_state = 3;
956 utf_char = (c & 0x07);
957 } else if ((c & 0xfc) == 0xf8) {
958 utf_size = utf_state = 4;
959 utf_char = (c & 0x03);
960 } else if ((c & 0xfe) == 0xfc) {
961 utf_size = utf_state = 5;
962 utf_char = (c & 0x01);
973 if ((c & 0xC0) != 0x80) {
974 inbuf_reap--; /* This causes the faulting character */
975 c = UCSERR; /* to be logged twice - not really a */
976 utf_state = 0; /* serious problem. */
979 utf_char = (utf_char << 6) | (c & 0x3f);
985 /* Is somebody trying to be evil! */
987 (c < 0x800 && utf_size >= 2) ||
988 (c < 0x10000 && utf_size >= 3) ||
989 (c < 0x200000 && utf_size >= 4) ||
990 (c < 0x4000000 && utf_size >= 5))
993 /* Unicode line separator and paragraph separator are CR-LF */
994 if (c == 0x2028 || c == 0x2029)
997 /* High controls are probably a Baaad idea too. */
1001 /* The UTF-16 surrogates are not nice either. */
1002 /* The standard give the option of decoding these:
1003 * I don't want to! */
1004 if (c >= 0xD800 && c < 0xE000)
1007 /* ISO 10646 characters now limited to UTF-16 range. */
1011 /* This is currently a TagPhobic application.. */
1012 if (c >= 0xE0000 && c <= 0xE007F)
1015 /* U+FEFF is best seen as a null. */
1018 /* But U+FFFE is an error. */
1019 if (c == 0xFFFE || c == 0xFFFF)
1022 /* Oops this is a 16bit implementation */
1028 switch (cset_attr[cset]) {
1030 * Linedraw characters are different from 'ESC ( B'
1031 * only for a small range. For ones outside that
1032 * range, make sure we use the same font as well as
1033 * the same encoding.
1036 if (unitab_ctrl[c] != 0xFF)
1039 c = ((unsigned char) c) | ATTR_LINEDRW;
1043 /* If UK-ASCII, make the '#' a LineDraw Pound */
1045 c = '}' | ATTR_LINEDRW;
1048 /*FALLTHROUGH*/ case ATTR_ASCII:
1049 if (unitab_ctrl[c] != 0xFF)
1052 c = ((unsigned char) c) | ATTR_ASCII;
1058 /* How about C1 controls ? */
1059 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1060 has_compat(VT220)) {
1061 termstate = SEEN_ESC;
1063 c = '@' + (c & 0x1F);
1066 /* Or the GL control. */
1067 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1068 if (curs.x && !wrapnext)
1072 *cpos = (' ' | curr_attr | ATTR_ASCII);
1074 /* Or normal C0 controls. */
1075 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1077 case '\005': /* terminal type query */
1078 /* Strictly speaking this is VT100 but a VT100 defaults to
1079 * no response. Other terminals respond at their option.
1081 * Don't put a CR in the default string as this tends to
1082 * upset some weird software.
1084 * An xterm returns "xterm" (5 characters)
1086 compatibility(ANSIMIN);
1088 char abuf[256], *s, *d;
1090 for (s = cfg.answerback, d = abuf; *s; s++) {
1092 if (*s >= 'a' && *s <= 'z')
1093 *d++ = (*s - ('a' - 1));
1094 else if ((*s >= '@' && *s <= '_') ||
1095 *s == '?' || (*s & 0x80))
1100 } else if (*s == '^') {
1105 lpage_send(CP_ACP, abuf, d - abuf);
1110 struct beeptime *newbeep;
1113 ticks = GetTickCount();
1115 if (!beep_overloaded) {
1116 newbeep = smalloc(sizeof(struct beeptime));
1117 newbeep->ticks = ticks;
1118 newbeep->next = NULL;
1122 beeptail->next = newbeep;
1128 * Throw out any beeps that happened more than
1132 beephead->ticks < ticks - cfg.bellovl_t) {
1133 struct beeptime *tmp = beephead;
1134 beephead = tmp->next;
1141 if (cfg.bellovl && beep_overloaded &&
1142 ticks - lastbeep >= cfg.bellovl_s) {
1144 * If we're currently overloaded and the
1145 * last beep was more than s seconds ago,
1146 * leave overload mode.
1148 beep_overloaded = FALSE;
1149 } else if (cfg.bellovl && !beep_overloaded &&
1150 nbeeps >= cfg.bellovl_n) {
1152 * Now, if we have n or more beeps
1153 * remaining in the queue, go into overload
1156 beep_overloaded = TRUE;
1161 * Perform an actual beep if we're not overloaded.
1163 if (!cfg.bellovl || !beep_overloaded) {
1165 if (cfg.beep == BELL_VISUAL) {
1167 vbell_timeout = ticks + VBELL_TIMEOUT;
1175 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1176 else if (curs.x == 0 && curs.y > 0)
1177 curs.x = cols - 1, curs.y--;
1183 seen_disp_event = TRUE;
1186 compatibility(VT100);
1190 compatibility(VT100);
1195 termstate = VT52_ESC;
1197 compatibility(ANSIMIN);
1198 termstate = SEEN_ESC;
1206 seen_disp_event = TRUE;
1208 logtraffic((unsigned char) c, LGTYP_ASCII);
1211 if (has_compat(SCOANSI)) {
1213 erase_lots(FALSE, FALSE, TRUE);
1216 seen_disp_event = 1;
1220 compatibility(VT100);
1222 if (curs.y == marg_b)
1223 scroll(marg_t, marg_b, 1, TRUE);
1224 else if (curs.y < rows - 1)
1230 seen_disp_event = 1;
1232 logtraffic((unsigned char) c, LGTYP_ASCII);
1236 pos old_curs = curs;
1237 unsigned long *ldata = lineptr(curs.y);
1241 } while (curs.x < cols - 1 && !tabs[curs.x]);
1243 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1244 if (curs.x >= cols / 2)
1245 curs.x = cols / 2 - 1;
1252 check_selection(old_curs, curs);
1254 seen_disp_event = TRUE;
1258 switch (termstate) {
1260 /* Only graphic characters get this far, ctrls are stripped above */
1261 if (wrapnext && wrap) {
1262 cpos[1] |= LATTR_WRAPPED;
1263 if (curs.y == marg_b)
1264 scroll(marg_t, marg_b, 1, TRUE);
1265 else if (curs.y < rows - 1)
1273 if (selstate != NO_SELECTION) {
1274 pos cursplus = curs;
1276 check_selection(curs, cursplus);
1278 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1279 logtraffic((unsigned char) c, LGTYP_ASCII);
1281 extern int wcwidth(wchar_t ucs);
1286 width = wcwidth((wchar_t) c);
1289 if (curs.x + 1 != cols) {
1290 *cpos++ = c | ATTR_WIDE | curr_attr;
1291 *cpos++ = UCSWIDE | curr_attr;
1296 *cpos++ = c | curr_attr;
1303 if (curs.x == cols) {
1307 if (wrap && vt52_mode) {
1308 cpos[1] |= LATTR_WRAPPED;
1309 if (curs.y == marg_b)
1310 scroll(marg_t, marg_b, 1, TRUE);
1311 else if (curs.y < rows - 1)
1318 seen_disp_event = 1;
1323 * This state is virtually identical to SEEN_ESC, with the
1324 * exception that we have an OSC sequence in the pipeline,
1325 * and _if_ we see a backslash, we process it.
1329 termstate = TOPLEVEL;
1332 /* else fall through */
1334 if (c >= ' ' && c <= '/') {
1341 termstate = TOPLEVEL;
1342 switch (ANSI(c, esc_query)) {
1343 case '[': /* enter CSI mode */
1344 termstate = SEEN_CSI;
1346 esc_args[0] = ARG_DEFAULT;
1349 case ']': /* xterm escape sequences */
1350 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1351 compatibility(OTHER);
1352 termstate = SEEN_OSC;
1355 case '7': /* save cursor */
1356 compatibility(VT100);
1359 case '8': /* restore cursor */
1360 compatibility(VT100);
1362 seen_disp_event = TRUE;
1365 compatibility(VT100);
1366 app_keypad_keys = TRUE;
1369 compatibility(VT100);
1370 app_keypad_keys = FALSE;
1372 case 'D': /* exactly equivalent to LF */
1373 compatibility(VT100);
1374 if (curs.y == marg_b)
1375 scroll(marg_t, marg_b, 1, TRUE);
1376 else if (curs.y < rows - 1)
1380 seen_disp_event = TRUE;
1382 case 'E': /* exactly equivalent to CR-LF */
1383 compatibility(VT100);
1385 if (curs.y == marg_b)
1386 scroll(marg_t, marg_b, 1, TRUE);
1387 else if (curs.y < rows - 1)
1391 seen_disp_event = TRUE;
1393 case 'M': /* reverse index - backwards LF */
1394 compatibility(VT100);
1395 if (curs.y == marg_t)
1396 scroll(marg_t, marg_b, -1, TRUE);
1397 else if (curs.y > 0)
1401 seen_disp_event = TRUE;
1403 case 'Z': /* terminal type query */
1404 compatibility(VT100);
1405 ldisc_send(id_string, strlen(id_string));
1407 case 'c': /* restore power-on settings */
1408 compatibility(VT100);
1411 request_resize(80, rows, 1);
1416 seen_disp_event = TRUE;
1418 case 'H': /* set a tab */
1419 compatibility(VT100);
1420 tabs[curs.x] = TRUE;
1423 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1424 compatibility(VT100);
1426 unsigned long *ldata;
1430 for (i = 0; i < rows; i++) {
1432 for (j = 0; j < cols; j++)
1433 ldata[j] = ATTR_DEFAULT | 'E';
1437 seen_disp_event = TRUE;
1438 scrtop.x = scrtop.y = 0;
1441 check_selection(scrtop, scrbot);
1445 case ANSI('3', '#'):
1446 case ANSI('4', '#'):
1447 case ANSI('5', '#'):
1448 case ANSI('6', '#'):
1449 compatibility(VT100);
1451 unsigned long nlattr;
1452 unsigned long *ldata;
1453 switch (ANSI(c, esc_query)) {
1454 case ANSI('3', '#'):
1457 case ANSI('4', '#'):
1460 case ANSI('5', '#'):
1461 nlattr = LATTR_NORM;
1463 default: /* spiritually case ANSI('6', '#'): */
1464 nlattr = LATTR_WIDE;
1467 ldata = lineptr(curs.y);
1468 ldata[cols] &= ~LATTR_MODE;
1469 ldata[cols] |= nlattr;
1473 case ANSI('A', '('):
1474 compatibility(VT100);
1475 cset_attr[0] = ATTR_GBCHR;
1477 case ANSI('B', '('):
1478 compatibility(VT100);
1479 cset_attr[0] = ATTR_ASCII;
1481 case ANSI('0', '('):
1482 compatibility(VT100);
1483 cset_attr[0] = ATTR_LINEDRW;
1486 case ANSI('A', ')'):
1487 compatibility(VT100);
1488 cset_attr[1] = ATTR_GBCHR;
1490 case ANSI('B', ')'):
1491 compatibility(VT100);
1492 cset_attr[1] = ATTR_ASCII;
1494 case ANSI('0', ')'):
1495 compatibility(VT100);
1496 cset_attr[1] = ATTR_LINEDRW;
1499 case ANSI('8', '%'): /* Old Linux code */
1500 case ANSI('G', '%'):
1501 compatibility(OTHER);
1504 case ANSI('@', '%'):
1505 compatibility(OTHER);
1506 if (line_codepage != CP_UTF8)
1512 termstate = TOPLEVEL; /* default */
1514 if (esc_nargs <= ARGS_MAX) {
1515 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1516 esc_args[esc_nargs - 1] = 0;
1517 esc_args[esc_nargs - 1] =
1518 10 * esc_args[esc_nargs - 1] + c - '0';
1520 termstate = SEEN_CSI;
1521 } else if (c == ';') {
1522 if (++esc_nargs <= ARGS_MAX)
1523 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1524 termstate = SEEN_CSI;
1525 } else if (c < '@') {
1532 termstate = SEEN_CSI;
1534 switch (ANSI(c, esc_query)) {
1535 case 'A': /* move up N lines */
1536 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1537 seen_disp_event = TRUE;
1539 case 'e': /* move down N lines */
1540 compatibility(ANSI);
1542 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1543 seen_disp_event = TRUE;
1545 case 'a': /* move right N cols */
1546 compatibility(ANSI);
1547 case ANSI('c', '>'): /* report xterm version */
1548 compatibility(OTHER);
1549 /* this reports xterm version 136 so that VIM can
1550 use the drag messages from the mouse reporting */
1551 ldisc_send("\033[>0;136;0c", 11);
1554 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1555 seen_disp_event = TRUE;
1557 case 'D': /* move left N cols */
1558 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1559 seen_disp_event = TRUE;
1561 case 'E': /* move down N lines and CR */
1562 compatibility(ANSI);
1563 move(0, curs.y + def(esc_args[0], 1), 1);
1564 seen_disp_event = TRUE;
1566 case 'F': /* move up N lines and CR */
1567 compatibility(ANSI);
1568 move(0, curs.y - def(esc_args[0], 1), 1);
1569 seen_disp_event = TRUE;
1572 case '`': /* set horizontal posn */
1573 compatibility(ANSI);
1574 move(def(esc_args[0], 1) - 1, curs.y, 0);
1575 seen_disp_event = TRUE;
1577 case 'd': /* set vertical posn */
1578 compatibility(ANSI);
1580 (dec_om ? marg_t : 0) + def(esc_args[0],
1583 seen_disp_event = TRUE;
1586 case 'f': /* set horz and vert posns at once */
1588 esc_args[1] = ARG_DEFAULT;
1589 move(def(esc_args[1], 1) - 1,
1590 (dec_om ? marg_t : 0) + def(esc_args[0],
1593 seen_disp_event = TRUE;
1595 case 'J': /* erase screen or parts of it */
1597 unsigned int i = def(esc_args[0], 0) + 1;
1600 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1603 seen_disp_event = TRUE;
1605 case 'K': /* erase line or parts of it */
1607 unsigned int i = def(esc_args[0], 0) + 1;
1610 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1612 seen_disp_event = TRUE;
1614 case 'L': /* insert lines */
1615 compatibility(VT102);
1616 if (curs.y <= marg_b)
1617 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1620 seen_disp_event = TRUE;
1622 case 'M': /* delete lines */
1623 compatibility(VT102);
1624 if (curs.y <= marg_b)
1625 scroll(curs.y, marg_b, def(esc_args[0], 1),
1628 seen_disp_event = TRUE;
1630 case '@': /* insert chars */
1631 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1632 compatibility(VT102);
1633 insch(def(esc_args[0], 1));
1634 seen_disp_event = TRUE;
1636 case 'P': /* delete chars */
1637 compatibility(VT102);
1638 insch(-def(esc_args[0], 1));
1639 seen_disp_event = TRUE;
1641 case 'c': /* terminal type query */
1642 compatibility(VT100);
1643 /* This is the response for a VT102 */
1644 ldisc_send(id_string, strlen(id_string));
1646 case 'n': /* cursor position query */
1647 if (esc_args[0] == 6) {
1649 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1651 ldisc_send(buf, strlen(buf));
1652 } else if (esc_args[0] == 5) {
1653 ldisc_send("\033[0n", 4);
1656 case 'h': /* toggle modes to high */
1658 compatibility(VT100);
1661 for (i = 0; i < esc_nargs; i++)
1662 toggle_mode(esc_args[i], esc_query, TRUE);
1665 case 'l': /* toggle modes to low */
1667 compatibility(VT100);
1670 for (i = 0; i < esc_nargs; i++)
1671 toggle_mode(esc_args[i], esc_query, FALSE);
1674 case 'g': /* clear tabs */
1675 compatibility(VT100);
1676 if (esc_nargs == 1) {
1677 if (esc_args[0] == 0) {
1678 tabs[curs.x] = FALSE;
1679 } else if (esc_args[0] == 3) {
1681 for (i = 0; i < cols; i++)
1686 case 'r': /* set scroll margins */
1687 compatibility(VT100);
1688 if (esc_nargs <= 2) {
1690 top = def(esc_args[0], 1) - 1;
1691 bot = (esc_nargs <= 1
1693 0 ? rows : def(esc_args[1], rows)) - 1;
1696 /* VTTEST Bug 9 - if region is less than 2 lines
1697 * don't change region.
1699 if (bot - top > 0) {
1704 * I used to think the cursor should be
1705 * placed at the top of the newly marginned
1706 * area. Apparently not: VMS TPU falls over
1709 * Well actually it should for Origin mode - RDB
1711 curs.y = (dec_om ? marg_t : 0);
1713 seen_disp_event = TRUE;
1717 case 'm': /* set graphics rendition */
1720 * A VT100 without the AVO only had one attribute, either
1721 * underline or reverse video depending on the cursor type,
1722 * this was selected by CSI 7m.
1725 * This is sometimes DIM, eg on the GIGI and Linux
1727 * This is sometimes INVIS various ANSI.
1729 * This like 22 disables BOLD, DIM and INVIS
1731 * The ANSI colours appear on any terminal that has colour
1732 * (obviously) but the interaction between sgr0 and the
1733 * colours varies but is usually related to the background
1734 * colour erase item.
1735 * The interaction between colour attributes and the mono
1736 * ones is also very implementation dependent.
1738 * The 39 and 49 attributes are likely to be unimplemented.
1741 for (i = 0; i < esc_nargs; i++) {
1742 switch (def(esc_args[i], 0)) {
1743 case 0: /* restore defaults */
1744 curr_attr = ATTR_DEFAULT;
1746 case 1: /* enable bold */
1747 compatibility(VT100AVO);
1748 curr_attr |= ATTR_BOLD;
1750 case 21: /* (enable double underline) */
1751 compatibility(OTHER);
1752 case 4: /* enable underline */
1753 compatibility(VT100AVO);
1754 curr_attr |= ATTR_UNDER;
1756 case 5: /* enable blink */
1757 compatibility(VT100AVO);
1758 curr_attr |= ATTR_BLINK;
1760 case 7: /* enable reverse video */
1761 curr_attr |= ATTR_REVERSE;
1763 case 22: /* disable bold */
1764 compatibility2(OTHER, VT220);
1765 curr_attr &= ~ATTR_BOLD;
1767 case 24: /* disable underline */
1768 compatibility2(OTHER, VT220);
1769 curr_attr &= ~ATTR_UNDER;
1771 case 25: /* disable blink */
1772 compatibility2(OTHER, VT220);
1773 curr_attr &= ~ATTR_BLINK;
1775 case 27: /* disable reverse video */
1776 compatibility2(OTHER, VT220);
1777 curr_attr &= ~ATTR_REVERSE;
1788 curr_attr &= ~ATTR_FGMASK;
1790 (esc_args[i] - 30) << ATTR_FGSHIFT;
1792 case 39: /* default-foreground */
1793 curr_attr &= ~ATTR_FGMASK;
1794 curr_attr |= ATTR_DEFFG;
1805 curr_attr &= ~ATTR_BGMASK;
1807 (esc_args[i] - 40) << ATTR_BGSHIFT;
1809 case 49: /* default-background */
1810 curr_attr &= ~ATTR_BGMASK;
1811 curr_attr |= ATTR_DEFBG;
1819 (ATTR_FGMASK | ATTR_BGMASK |
1823 case 's': /* save cursor */
1826 case 'u': /* restore cursor */
1828 seen_disp_event = TRUE;
1830 case 't': /* set page size - ie window height */
1832 * VT340/VT420 sequence DECSLPP, DEC only allows values
1833 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1834 * illegal values (eg first arg 1..9) for window changing
1837 compatibility(VT340TEXT);
1839 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1840 request_resize(cols, def(esc_args[0], 24), 0);
1845 compatibility(SCOANSI);
1846 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1849 seen_disp_event = TRUE;
1852 compatibility(SCOANSI);
1853 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1856 seen_disp_event = TRUE;
1858 case ANSI('|', '*'):
1859 /* VT420 sequence DECSNLS
1860 * Set number of lines on screen
1861 * VT420 uses VGA like hardware and can support any size in
1862 * reasonable range (24..49 AIUI) with no default specified.
1864 compatibility(VT420);
1865 if (esc_nargs == 1 && esc_args[0] > 0) {
1866 request_resize(cols,
1867 def(esc_args[0], cfg.height),
1872 case ANSI('|', '$'):
1873 /* VT340/VT420 sequence DECSCPP
1874 * Set number of columns per page
1875 * Docs imply range is only 80 or 132, but I'll allow any.
1877 compatibility(VT340TEXT);
1878 if (esc_nargs <= 1) {
1879 request_resize(def(esc_args[0], cfg.width),
1884 case 'X': /* write N spaces w/o moving cursor */
1885 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1886 compatibility(ANSIMIN);
1888 int n = def(esc_args[0], 1);
1890 unsigned long *p = cpos;
1891 if (n > cols - curs.x)
1895 check_selection(curs, cursplus);
1898 seen_disp_event = TRUE;
1901 case 'x': /* report terminal characteristics */
1902 compatibility(VT100);
1905 int i = def(esc_args[0], 0);
1906 if (i == 0 || i == 1) {
1907 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1909 ldisc_send(buf, 20);
1913 case ANSI('L', '='):
1914 compatibility(OTHER);
1915 use_bce = (esc_args[0] <= 0);
1916 erase_char = ERASE_CHAR;
1921 (ATTR_FGMASK | ATTR_BGMASK)));
1923 case ANSI('E', '='):
1924 compatibility(OTHER);
1925 blink_is_real = (esc_args[0] >= 1);
1927 case ANSI('p', '"'):
1928 /* Allow the host to make this emulator a 'perfect' VT102.
1929 * This first appeared in the VT220, but we do need to get
1930 * back to PuTTY mode so I won't check it.
1932 * The arg in 40..42,50 are a PuTTY extension.
1933 * The 2nd arg, 8bit vs 7bit is not checked.
1935 * Setting VT102 mode should also change the Fkeys to
1936 * generate PF* codes as a real VT102 has no Fkeys.
1937 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1940 * Note ESC c will NOT change this!
1943 switch (esc_args[0]) {
1945 compatibility_level &= ~TM_VTXXX;
1946 compatibility_level |= TM_VT102;
1949 compatibility_level &= ~TM_VTXXX;
1950 compatibility_level |= TM_VT220;
1954 if (esc_args[0] > 60 && esc_args[0] < 70)
1955 compatibility_level |= TM_VTXXX;
1959 compatibility_level &= TM_VTXXX;
1962 compatibility_level = TM_PUTTY;
1965 compatibility_level = TM_SCOANSI;
1969 compatibility_level = TM_PUTTY;
1975 /* Change the response to CSI c */
1976 if (esc_args[0] == 50) {
1979 strcpy(id_string, "\033[?");
1980 for (i = 1; i < esc_nargs; i++) {
1982 strcat(id_string, ";");
1983 sprintf(lbuf, "%d", esc_args[i]);
1984 strcat(id_string, lbuf);
1986 strcat(id_string, "c");
1989 /* Is this a good idea ?
1990 * Well we should do a soft reset at this point ...
1992 if (!has_compat(VT420) && has_compat(VT100)) {
1994 request_resize(132, 24, 1);
1996 request_resize(80, 24, 1);
2005 case 'P': /* Linux palette sequence */
2006 termstate = SEEN_OSC_P;
2009 case 'R': /* Linux palette reset */
2012 termstate = TOPLEVEL;
2014 case 'W': /* word-set */
2015 termstate = SEEN_OSC_W;
2028 esc_args[0] = 10 * esc_args[0] + c - '0';
2032 * Grotty hack to support xterm and DECterm title
2033 * sequences concurrently.
2035 if (esc_args[0] == 2) {
2039 /* else fall through */
2041 termstate = OSC_STRING;
2047 * This OSC stuff is EVIL. It takes just one character to get into
2048 * sysline mode and it's not initially obvious how to get out.
2049 * So I've added CR and LF as string aborts.
2050 * This shouldn't effect compatibility as I believe embedded
2051 * control characters are supposed to be interpreted (maybe?)
2052 * and they don't display anything useful anyway.
2056 if (c == '\n' || c == '\r') {
2057 termstate = TOPLEVEL;
2058 } else if (c == 0234 || c == '\007') {
2060 * These characters terminate the string; ST and BEL
2061 * terminate the sequence and trigger instant
2062 * processing of it, whereas ESC goes back to SEEN_ESC
2063 * mode unless it is followed by \, in which case it is
2064 * synonymous with ST in the first place.
2067 termstate = TOPLEVEL;
2068 } else if (c == '\033')
2069 termstate = OSC_MAYBE_ST;
2070 else if (osc_strlen < OSC_STR_MAX)
2071 osc_string[osc_strlen++] = c;
2075 int max = (osc_strlen == 0 ? 21 : 16);
2077 if (c >= '0' && c <= '9')
2079 else if (c >= 'A' && c <= 'A' + max - 10)
2081 else if (c >= 'a' && c <= 'a' + max - 10)
2084 termstate = TOPLEVEL;
2087 osc_string[osc_strlen++] = val;
2088 if (osc_strlen >= 7) {
2089 palette_set(osc_string[0],
2090 osc_string[1] * 16 + osc_string[2],
2091 osc_string[3] * 16 + osc_string[4],
2092 osc_string[5] * 16 + osc_string[6]);
2094 termstate = TOPLEVEL;
2110 esc_args[0] = 10 * esc_args[0] + c - '0';
2113 termstate = OSC_STRING;
2118 termstate = TOPLEVEL;
2119 seen_disp_event = TRUE;
2122 move(curs.x, curs.y - 1, 1);
2125 move(curs.x, curs.y + 1, 1);
2128 move(curs.x + 1, curs.y, 1);
2131 move(curs.x - 1, curs.y, 1);
2134 * From the VT100 Manual
2135 * NOTE: The special graphics characters in the VT100
2136 * are different from those in the VT52
2138 * From VT102 manual:
2139 * 137 _ Blank - Same
2140 * 140 ` Reserved - Humm.
2141 * 141 a Solid rectangle - Similar
2142 * 142 b 1/ - Top half of fraction for the
2143 * 143 c 3/ - subscript numbers below.
2146 * 146 f Degrees - Same
2147 * 147 g Plus or minus - Same
2149 * 151 i Ellipsis (dots)
2152 * 154 l Bar at scan 0
2153 * 155 m Bar at scan 1
2154 * 156 n Bar at scan 2
2155 * 157 o Bar at scan 3 - Similar
2156 * 160 p Bar at scan 4 - Similar
2157 * 161 q Bar at scan 5 - Similar
2158 * 162 r Bar at scan 6 - Same
2159 * 163 s Bar at scan 7 - Similar
2174 cset_attr[cset = 0] = ATTR_LINEDRW;
2177 cset_attr[cset = 0] = ATTR_ASCII;
2184 scroll(0, rows - 1, -1, TRUE);
2185 else if (curs.y > 0)
2191 erase_lots(FALSE, FALSE, TRUE);
2195 erase_lots(TRUE, FALSE, TRUE);
2199 /* XXX Print cursor line */
2202 /* XXX Start controller mode */
2205 /* XXX Stop controller mode */
2209 termstate = VT52_Y1;
2212 ldisc_send("\033/Z", 3);
2215 app_keypad_keys = TRUE;
2218 app_keypad_keys = FALSE;
2221 /* XXX This should switch to VT100 mode not current or default
2222 * VT mode. But this will only have effect in a VT220+
2226 blink_is_real = cfg.blinktext;
2230 /* XXX Enter auto print mode */
2233 /* XXX Exit auto print mode */
2236 /* XXX Print screen */
2242 /* compatibility(ATARI) */
2244 erase_lots(FALSE, FALSE, TRUE);
2248 /* compatibility(ATARI) */
2249 if (curs.y <= marg_b)
2250 scroll(curs.y, marg_b, -1, FALSE);
2253 /* compatibility(ATARI) */
2254 if (curs.y <= marg_b)
2255 scroll(curs.y, marg_b, 1, TRUE);
2258 /* compatibility(ATARI) */
2259 termstate = VT52_FG;
2262 /* compatibility(ATARI) */
2263 termstate = VT52_BG;
2266 /* compatibility(ATARI) */
2267 erase_lots(FALSE, TRUE, FALSE);
2271 /* compatibility(ATARI) */
2275 /* compatibility(ATARI) */
2278 /* case 'j': Save cursor position - broken on ST */
2279 /* case 'k': Restore cursor position */
2281 /* compatibility(ATARI) */
2282 erase_lots(TRUE, TRUE, TRUE);
2288 /* compatibility(ATARI) */
2289 erase_lots(TRUE, TRUE, FALSE);
2292 /* compatibility(ATARI) */
2293 curr_attr |= ATTR_REVERSE;
2296 /* compatibility(ATARI) */
2297 curr_attr &= ~ATTR_REVERSE;
2299 case 'v': /* wrap Autowrap on - Wyse style */
2300 /* compatibility(ATARI) */
2303 case 'w': /* Autowrap off */
2304 /* compatibility(ATARI) */
2309 /* compatibility(OTHER) */
2311 curr_attr = ATTR_DEFAULT;
2315 (ATTR_FGMASK | ATTR_BGMASK |
2319 /* compatibility(VI50) */
2320 curr_attr |= ATTR_UNDER;
2323 /* compatibility(VI50) */
2324 curr_attr &= ~ATTR_UNDER;
2327 /* compatibility(VI50) */
2329 curr_attr |= ATTR_BOLD;
2332 /* compatibility(VI50) */
2334 curr_attr &= ~ATTR_BOLD;
2340 termstate = VT52_Y2;
2341 move(curs.x, c - ' ', 0);
2344 termstate = TOPLEVEL;
2345 move(c - ' ', curs.y, 0);
2350 termstate = TOPLEVEL;
2351 curr_attr &= ~ATTR_FGMASK;
2352 curr_attr &= ~ATTR_BOLD;
2353 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2354 if ((c & 0x8) || vt52_bold)
2355 curr_attr |= ATTR_BOLD;
2360 (ATTR_FGMASK | ATTR_BGMASK |
2364 termstate = TOPLEVEL;
2365 curr_attr &= ~ATTR_BGMASK;
2366 curr_attr &= ~ATTR_BLINK;
2367 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2369 /* Note: bold background */
2371 curr_attr |= ATTR_BLINK;
2376 (ATTR_FGMASK | ATTR_BGMASK |
2380 default: break; /* placate gcc warning about enum use */
2382 if (selstate != NO_SELECTION) {
2383 pos cursplus = curs;
2385 check_selection(curs, cursplus);
2393 * Compare two lines to determine whether they are sufficiently
2394 * alike to scroll-optimise one to the other. Return the degree of
2397 static int linecmp(unsigned long *a, unsigned long *b)
2401 for (i = n = 0; i < cols; i++)
2402 n += (*a++ == *b++);
2408 * Given a context, update the window. Out of paranoia, we don't
2409 * allow WM_PAINT responses to do scrolling optimisations.
2411 static void do_paint(Context ctx, int may_optimise)
2413 int i, j, our_curs_y;
2414 unsigned long rv, cursor;
2417 long cursor_background = ERASE_CHAR;
2421 * Check the visual bell state.
2424 ticks = GetTickCount();
2425 if (ticks - vbell_timeout >= 0)
2429 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2432 * screen array, disptop, scrtop,
2434 * cfg.blinkpc, blink_is_real, tblinker,
2435 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2438 /* Has the cursor position or type changed ? */
2441 if (blinker || !cfg.blink_cur)
2442 cursor = TATTR_ACTCURS;
2446 cursor = TATTR_PASCURS;
2448 cursor |= TATTR_RIGHTCURS;
2451 our_curs_y = curs.y - disptop;
2453 if (dispcurs && (curstype != cursor ||
2455 disptext + our_curs_y * (cols + 1) + curs.x)) {
2456 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2457 dispcurs[-1] |= ATTR_INVALID;
2458 if ((*dispcurs & ATTR_WIDE))
2459 dispcurs[1] |= ATTR_INVALID;
2460 *dispcurs |= ATTR_INVALID;
2465 /* The normal screen data */
2466 for (i = 0; i < rows; i++) {
2467 unsigned long *ldata;
2469 int idx, dirty_line, dirty_run;
2470 unsigned long attr = 0;
2471 int updated_line = 0;
2474 int last_run_dirty = 0;
2476 scrpos.y = i + disptop;
2477 ldata = lineptr(scrpos.y);
2478 lattr = (ldata[cols] & LATTR_MODE);
2480 idx = i * (cols + 1);
2481 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2482 disptext[idx + cols] = ldata[cols];
2484 for (j = 0; j < cols; j++, idx++) {
2485 unsigned long tattr, tchar;
2486 unsigned long *d = ldata + j;
2490 tchar = (*d & (CHAR_MASK | CSET_MASK));
2491 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2492 switch (tchar & CSET_MASK) {
2494 tchar = unitab_line[tchar & 0xFF];
2497 tchar = unitab_xterm[tchar & 0xFF];
2500 tattr |= (tchar & CSET_MASK);
2503 /* Video reversing things */
2505 ^ (posle(selstart, scrpos) &&
2506 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2508 /* 'Real' blinking ? */
2509 if (blink_is_real && (tattr & ATTR_BLINK)) {
2510 if (has_focus && tblinker) {
2512 tattr &= ~CSET_MASK;
2515 tattr &= ~ATTR_BLINK;
2518 /* Cursor here ? Save the 'background' */
2519 if (i == our_curs_y && j == curs.x) {
2520 cursor_background = tattr | tchar;
2521 dispcurs = disptext + idx;
2524 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2527 break_run = (tattr != attr || j - start >= sizeof(ch));
2529 /* Special hack for VT100 Linedraw glyphs */
2530 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2531 && tchar <= 0xBD) break_run = TRUE;
2533 if (!dbcs_screenfont && !dirty_line) {
2534 if ((tchar | tattr) == disptext[idx])
2536 else if (!dirty_run && ccount == 1)
2541 if ((dirty_run || last_run_dirty) && ccount > 0) {
2542 do_text(ctx, start, i, ch, ccount, attr, lattr);
2548 if (dbcs_screenfont)
2549 last_run_dirty = dirty_run;
2550 dirty_run = dirty_line;
2553 if ((tchar | tattr) != disptext[idx])
2555 ch[ccount++] = (char) tchar;
2556 disptext[idx] = tchar | tattr;
2558 /* If it's a wide char step along to the next one. */
2559 if (tattr & ATTR_WIDE) {
2563 /* Cursor is here ? Ouch! */
2564 if (i == our_curs_y && j == curs.x) {
2565 cursor_background = *d;
2566 dispcurs = disptext + idx;
2568 if (disptext[idx] != *d)
2574 if (dirty_run && ccount > 0) {
2575 do_text(ctx, start, i, ch, ccount, attr, lattr);
2579 /* Cursor on this line ? (and changed) */
2580 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2581 ch[0] = (char) (cursor_background & CHAR_MASK);
2582 attr = (cursor_background & ATTR_MASK) | cursor;
2583 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2590 * Flick the switch that says if blinking things should be shown or hidden.
2593 void term_blink(int flg)
2595 static long last_blink = 0;
2596 static long last_tblink = 0;
2597 long now, blink_diff;
2599 now = GetTickCount();
2600 blink_diff = now - last_tblink;
2602 /* Make sure the text blinks no more than 2Hz */
2603 if (blink_diff < 0 || blink_diff > 450) {
2605 tblinker = !tblinker;
2614 blink_diff = now - last_blink;
2616 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2617 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2625 * Invalidate the whole screen so it will be repainted in full.
2627 void term_invalidate(void)
2631 for (i = 0; i < rows * (cols + 1); i++)
2632 disptext[i] = ATTR_INVALID;
2636 * Paint the window in response to a WM_PAINT message.
2638 void term_paint(Context ctx, int l, int t, int r, int b)
2640 int i, j, left, top, right, bottom;
2642 left = l / font_width;
2643 right = (r - 1) / font_width;
2644 top = t / font_height;
2645 bottom = (b - 1) / font_height;
2646 for (i = top; i <= bottom && i < rows; i++) {
2647 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2648 for (j = left; j <= right && j < cols; j++)
2649 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2651 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2652 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2655 /* This should happen soon enough, also for some reason it sometimes
2656 * fails to actually do anything when re-sizing ... painting the wrong
2658 do_paint (ctx, FALSE);
2663 * Attempt to scroll the scrollback. The second parameter gives the
2664 * position we want to scroll to; the first is +1 to denote that
2665 * this position is relative to the beginning of the scrollback, -1
2666 * to denote it is relative to the end, and 0 to denote that it is
2667 * relative to the current position.
2669 void term_scroll(int rel, int where)
2671 int sbtop = -count234(scrollback);
2673 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2674 if (disptop < sbtop)
2682 static void clipme(pos top, pos bottom)
2685 wchar_t *wbptr; /* where next char goes within workbuf */
2686 int wblen = 0; /* workbuf len */
2687 int buflen; /* amount of memory allocated to workbuf */
2689 buflen = 5120; /* Default size */
2690 workbuf = smalloc(buflen * sizeof(wchar_t));
2691 wbptr = workbuf; /* start filling here */
2693 while (poslt(top, bottom)) {
2695 unsigned long *ldata = lineptr(top.y);
2701 if (!(ldata[cols] & LATTR_WRAPPED)) {
2702 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2703 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2704 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2705 && poslt(top, nlpos))
2707 if (poslt(nlpos, bottom))
2710 while (poslt(top, bottom) && poslt(top, nlpos)) {
2713 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2715 wchar_t cbuf[16], *p;
2716 int uc = (ldata[top.x] & 0xFFFF);
2719 if (uc == UCSWIDE) {
2724 switch (uc & CSET_MASK) {
2727 uc = unitab_xterm[uc & 0xFF];
2731 uc = unitab_line[uc & 0xFF];
2734 switch (uc & CSET_MASK) {
2736 uc = unitab_font[uc & 0xFF];
2739 uc = unitab_oemcp[uc & 0xFF];
2743 set = (uc & CSET_MASK);
2744 c = (uc & CHAR_MASK);
2748 if (DIRECT_FONT(uc)) {
2749 if (c >= ' ' && c != 0x7F) {
2750 unsigned char buf[4];
2753 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2755 buf[1] = (unsigned char) ldata[top.x + 1];
2756 rv = MultiByteToWideChar(font_codepage,
2757 0, buf, 2, wbuf, 4);
2761 rv = MultiByteToWideChar(font_codepage,
2762 0, buf, 1, wbuf, 4);
2766 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2773 for (p = cbuf; *p; p++) {
2774 /* Enough overhead for trailing NL and nul */
2775 if (wblen >= buflen - 16) {
2778 sizeof(wchar_t) * (buflen += 100));
2779 wbptr = workbuf + wblen;
2788 for (i = 0; i < sel_nl_sz; i++) {
2790 *wbptr++ = sel_nl[i];
2798 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2799 if (buflen > 0) /* indicates we allocated this buffer */
2803 void term_copyall(void)
2806 top.y = -count234(scrollback);
2812 * The wordness array is mainly for deciding the disposition of the US-ASCII
2815 static int wordtype(int uc)
2818 int start, end, ctype;
2819 } *wptr, ucs_words[] = {
2825 0x037e, 0x037e, 1}, /* Greek question mark */
2827 0x0387, 0x0387, 1}, /* Greek ano teleia */
2829 0x055a, 0x055f, 1}, /* Armenian punctuation */
2831 0x0589, 0x0589, 1}, /* Armenian full stop */
2833 0x0700, 0x070d, 1}, /* Syriac punctuation */
2835 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2837 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2839 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2841 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2843 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2845 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2847 0x2000, 0x200a, 0}, /* Various spaces */
2849 0x2070, 0x207f, 2}, /* superscript */
2851 0x2080, 0x208f, 2}, /* subscript */
2853 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2855 0x3000, 0x3000, 0}, /* ideographic space */
2857 0x3001, 0x3020, 1}, /* ideographic punctuation */
2859 0x303f, 0x309f, 3}, /* Hiragana */
2861 0x30a0, 0x30ff, 3}, /* Katakana */
2863 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2865 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2867 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2869 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2871 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2873 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2875 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2877 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2879 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2884 uc &= (CSET_MASK | CHAR_MASK);
2886 switch (uc & CSET_MASK) {
2888 uc = unitab_xterm[uc & 0xFF];
2891 uc = unitab_line[uc & 0xFF];
2894 switch (uc & CSET_MASK) {
2896 uc = unitab_font[uc & 0xFF];
2899 uc = unitab_oemcp[uc & 0xFF];
2904 return wordness[uc];
2906 for (wptr = ucs_words; wptr->start; wptr++) {
2907 if (uc >= wptr->start && uc <= wptr->end)
2915 * Spread the selection outwards according to the selection mode.
2917 static pos sel_spread_half(pos p, int dir)
2919 unsigned long *ldata;
2922 ldata = lineptr(p.y);
2927 * In this mode, every character is a separate unit, except
2928 * for runs of spaces at the end of a non-wrapping line.
2930 if (!(ldata[cols] & LATTR_WRAPPED)) {
2931 unsigned long *q = ldata + cols;
2932 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2934 if (q == ldata + cols)
2936 if (p.x >= q - ldata)
2937 p.x = (dir == -1 ? q - ldata : cols - 1);
2942 * In this mode, the units are maximal runs of characters
2943 * whose `wordness' has the same value.
2945 wvalue = wordtype(ldata[p.x]);
2947 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
2950 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
2956 * In this mode, every line is a unit.
2958 p.x = (dir == -1 ? 0 : cols - 1);
2964 static void sel_spread(void)
2966 selstart = sel_spread_half(selstart, -1);
2968 selend = sel_spread_half(selend, +1);
2972 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
2973 int shift, int ctrl)
2976 unsigned long *ldata;
2992 selpoint.y = y + disptop;
2994 ldata = lineptr(selpoint.y);
2995 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
2999 int encstate = 0, r, c;
3001 static int is_down = 0;
3005 encstate = 0x20; /* left button down */
3016 case MBT_WHEEL_DOWN:
3019 default: break; /* placate gcc warning about enum use */
3023 if (xterm_mouse == 1)
3036 default: break; /* placate gcc warning about enum use */
3045 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3046 ldisc_send(abuf, 6);
3050 b = translate_button(b);
3052 if (b == MBT_SELECT && a == MA_CLICK) {
3054 selstate = ABOUT_TO;
3055 selanchor = selpoint;
3057 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3059 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3060 selstate = DRAGGING;
3061 selstart = selanchor = selpoint;
3065 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3066 (b == MBT_EXTEND && a != MA_RELEASE)) {
3067 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3069 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3070 if (posdiff(selpoint, selstart) <
3071 posdiff(selend, selstart) / 2) {
3075 selanchor = selstart;
3077 selstate = DRAGGING;
3079 if (selstate != ABOUT_TO && selstate != DRAGGING)
3080 selanchor = selpoint;
3081 selstate = DRAGGING;
3082 if (poslt(selpoint, selanchor)) {
3083 selstart = selpoint;
3087 selstart = selanchor;
3092 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3093 if (selstate == DRAGGING) {
3095 * We've completed a selection. We now transfer the
3096 * data to the clipboard.
3098 clipme(selstart, selend);
3099 selstate = SELECTED;
3101 selstate = NO_SELECTION;
3102 } else if (b == MBT_PASTE
3103 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3107 get_clip(&data, &len);
3112 sfree(paste_buffer);
3113 paste_pos = paste_hold = paste_len = 0;
3114 paste_buffer = smalloc(len * sizeof(wchar_t));
3117 while (p < data + len) {
3118 while (p < data + len &&
3119 !(p <= data + len - sel_nl_sz &&
3120 !memcmp(p, sel_nl, sizeof(sel_nl))))
3125 for (i = 0; i < p - q; i++) {
3126 paste_buffer[paste_len++] = q[i];
3130 if (p <= data + len - sel_nl_sz &&
3131 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3132 paste_buffer[paste_len++] = '\r';
3138 /* Assume a small paste will be OK in one go. */
3139 if (paste_len < 256) {
3140 luni_send(paste_buffer, paste_len);
3142 sfree(paste_buffer);
3144 paste_pos = paste_hold = paste_len = 0;
3147 get_clip(NULL, NULL);
3157 sfree(paste_buffer);
3164 static long last_paste = 0;
3165 long now, paste_diff;
3170 /* Don't wait forever to paste */
3172 now = GetTickCount();
3173 paste_diff = now - last_paste;
3174 if (paste_diff >= 0 && paste_diff < 450)
3179 while (paste_pos < paste_len) {
3181 while (n + paste_pos < paste_len) {
3182 if (paste_buffer[paste_pos + n++] == '\r')
3185 luni_send(paste_buffer + paste_pos, n);
3188 if (paste_pos < paste_len) {
3193 sfree(paste_buffer);
3198 static void deselect(void)
3200 selstate = NO_SELECTION;
3201 selstart.x = selstart.y = selend.x = selend.y = 0;
3204 void term_deselect(void)
3210 int term_ldisc(int option)
3212 if (option == LD_ECHO)
3213 return term_echoing;
3214 if (option == LD_EDIT)
3215 return term_editing;
3220 * from_backend(), to get data from the backend for the terminal.
3222 void from_backend(int is_stderr, char *data, int len)
3225 if (inbuf_head >= INBUF_SIZE)
3227 inbuf[inbuf_head++] = *data++;
3232 * Log session traffic.
3234 void logtraffic(unsigned char c, int logmode)
3236 if (cfg.logtype > 0) {
3237 if (cfg.logtype == logmode) {
3238 /* deferred open file from pgm start? */
3247 /* open log file append/overwrite mode */
3257 sprintf(writemod, "wb"); /* default to rewrite */
3258 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3262 i = askappend(cfg.logfilename);
3264 writemod[0] = 'a'; /* set append mode */
3265 else if (i == 0) { /* cancelled */
3267 cfg.logtype = 0; /* disable logging */
3272 lgfp = fopen(cfg.logfilename, writemod);
3273 if (lgfp) { /* enter into event log */
3274 sprintf(buf, "%s session log (%s mode) to file : ",
3275 (writemod[0] == 'a') ? "Appending" : "Writing new",
3276 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3277 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3278 /* Make sure we do not exceed the output buffer size */
3279 strncat(buf, cfg.logfilename, 128);
3280 buf[strlen(buf)] = '\0';
3283 /* --- write header line iinto log file */
3284 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3287 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3289 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3293 void logfclose(void)