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) ( ((p2).y - (p1).y) * (cols+1) + (p2).x - (p1).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 * Retrieve a line of the screen or of the scrollback, according to
205 * whether the y coordinate is non-negative or negative
208 unsigned long *lineptr(int y, int lineno)
210 unsigned long *line, lineattrs;
212 int i, treeindex, oldlen;
218 whichtree = scrollback;
219 treeindex = y + count234(scrollback);
221 line = index234(whichtree, treeindex);
223 /* We assume that we don't screw up and retrieve something out of range. */
224 assert(line != NULL);
226 if (line[0] != cols) {
228 * This line is the wrong length, which probably means it
229 * hasn't been accessed since a resize. Resize it now.
232 lineattrs = line[oldlen + 1];
233 delpos234(whichtree, treeindex);
234 line = srealloc(line, TSIZE * (2 + cols));
236 for (i = oldlen; i < cols; i++)
237 line[i + 1] = ERASE_CHAR;
238 line[cols + 1] = lineattrs & LATTR_MODE;
239 addpos234(whichtree, line, treeindex);
245 #define lineptr(x) lineptr(x,__LINE__)
247 * Set up power-on settings for the terminal.
249 static void power_on(void)
251 curs.x = curs.y = alt_x = alt_y = savecurs.x = savecurs.y = 0;
254 alt_b = marg_b = rows - 1;
259 for (i = 0; i < cols; i++)
260 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
262 alt_om = dec_om = cfg.dec_om;
263 alt_wnext = wrapnext = alt_ins = insert = FALSE;
264 alt_wrap = wrap = cfg.wrap_mode;
266 cset_attr[0] = cset_attr[1] = ATTR_ASCII;
271 save_attr = curr_attr = ATTR_DEFAULT;
272 term_editing = term_echoing = FALSE;
273 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
274 app_cursor_keys = cfg.app_cursor;
275 app_keypad_keys = cfg.app_keypad;
277 blink_is_real = cfg.blinktext;
278 erase_char = ERASE_CHAR;
282 for (i = 0; i < 256; i++)
283 wordness[i] = cfg.wordness[i];
287 erase_lots(FALSE, TRUE, TRUE);
289 erase_lots(FALSE, TRUE, TRUE);
294 * Force a screen update.
296 void term_update(void)
301 if ((seen_key_event && (cfg.scroll_on_key)) ||
302 (seen_disp_event && (cfg.scroll_on_disp))) {
303 disptop = 0; /* return to main screen */
304 seen_disp_event = seen_key_event = 0;
308 sys_cursor(curs.x, curs.y - disptop);
314 * Same as power_on(), but an external function.
316 void term_pwron(void)
326 * Clear the scrollback.
328 void term_clrsb(void)
332 while ((line = delpos234(scrollback, 0)) != NULL) {
339 * Initialise the terminal.
343 screen = alt_screen = scrollback = NULL;
345 disptext = dispcurs = NULL;
350 beephead = beeptail = NULL;
353 beep_overloaded = FALSE;
357 * Set up the terminal for a given size.
359 void term_size(int newrows, int newcols, int newsavelines)
362 unsigned long *newdisp, *line;
365 int save_alt_which = alt_which;
367 if (newrows == rows && newcols == cols && newsavelines == savelines)
368 return; /* nothing to do */
374 alt_b = marg_b = newrows - 1;
377 scrollback = newtree234(NULL);
378 screen = newtree234(NULL);
383 * Resize the screen and scrollback. We only need to shift
384 * lines around within our data structures, because lineptr()
385 * will take care of resizing each individual line if
388 * - If the new screen and the old screen differ in length, we
389 * must shunt some lines in from the scrollback or out to
392 * - If doing that fails to provide us with enough material to
393 * fill the new screen (i.e. the number of rows needed in
394 * the new screen exceeds the total number in the previous
395 * screen+scrollback), we must invent some blank lines to
398 * - Then, if the new scrollback length is less than the
399 * amount of scrollback we actually have, we must throw some
402 sblen = count234(scrollback);
403 /* Do this loop to expand the screen if newrows > rows */
404 for (i = rows; i < newrows; i++) {
406 line = delpos234(scrollback, --sblen);
408 line = smalloc(TSIZE * (newcols + 2));
410 for (j = 0; j <= newcols; j++)
411 line[j + 1] = ERASE_CHAR;
413 addpos234(screen, line, 0);
415 /* Do this loop to shrink the screen if newrows < rows */
416 for (i = newrows; i < rows; i++) {
417 line = delpos234(screen, 0);
418 addpos234(scrollback, line, sblen++);
420 assert(count234(screen) == newrows);
421 while (sblen > newsavelines) {
422 line = delpos234(scrollback, 0);
426 assert(count234(scrollback) <= newsavelines);
429 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
430 for (i = 0; i < newrows * (newcols + 1); i++)
431 newdisp[i] = ATTR_INVALID;
436 newalt = newtree234(NULL);
437 for (i = 0; i < newrows; i++) {
438 line = smalloc(TSIZE * (newcols + 2));
440 for (j = 0; j <= newcols; j++)
441 line[j + 1] = erase_char;
442 addpos234(newalt, line, i);
445 while (NULL != (line = delpos234(alt_screen, 0)))
447 freetree234(alt_screen);
451 tabs = srealloc(tabs, newcols * sizeof(*tabs));
454 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
455 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
459 curs.y += newrows - rows;
462 if (curs.y >= newrows)
463 curs.y = newrows - 1;
464 if (curs.x >= newcols)
465 curs.x = newcols - 1;
467 wrapnext = alt_wnext = FALSE;
471 savelines = newsavelines;
474 swap_screen(save_alt_which);
483 static void swap_screen(int which)
488 if (which == alt_which)
515 wrapnext = alt_wnext;
528 * Update the scroll bar.
530 static void update_sbar(void)
534 nscroll = count234(scrollback);
536 set_sbar(nscroll + rows, nscroll + disptop, rows);
540 * Check whether the region bounded by the two pointers intersects
541 * the scroll region, and de-select the on-screen selection if so.
543 static void check_selection(pos from, pos to)
545 if (poslt(from, selend) && poslt(selstart, to))
550 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
551 * for backward.) `sb' is TRUE if the scrolling is permitted to
552 * affect the scrollback buffer.
554 * NB this function invalidates all pointers into lines of the
555 * screen data structures. In particular, you MUST call fix_cpos
556 * after calling scroll() and before doing anything else that
557 * uses the cpos shortcut pointer.
559 static void scroll(int topline, int botline, int lines, int sb)
561 unsigned long *line, *line2;
564 if (topline != 0 || alt_which != 0)
569 line = delpos234(screen, botline);
570 for (i = 0; i < cols; i++)
571 line[i + 1] = erase_char;
573 addpos234(screen, line, topline);
575 if (selstart.y >= topline && selstart.y <= botline) {
577 if (selstart.y > botline) {
578 selstart.y = botline;
582 if (selend.y >= topline && selend.y <= botline) {
584 if (selend.y > botline) {
594 line = delpos234(screen, topline);
595 if (sb && savelines > 0) {
596 int sblen = count234(scrollback);
598 * We must add this line to the scrollback. We'll
599 * remove a line from the top of the scrollback to
600 * replace it, or allocate a new one if the
601 * scrollback isn't full.
603 if (sblen == savelines) {
604 sblen--, line2 = delpos234(scrollback, 0);
606 line2 = smalloc(TSIZE * (cols + 2));
609 addpos234(scrollback, line, sblen);
612 for (i = 0; i < cols; i++)
613 line[i + 1] = erase_char;
615 addpos234(screen, line, botline);
617 if (selstart.y >= topline && selstart.y <= botline) {
619 if (selstart.y < topline) {
620 selstart.y = topline;
624 if (selend.y >= topline && selend.y <= botline) {
626 if (selend.y < topline) {
638 * Move the cursor to a given position, clipping at boundaries. We
639 * may or may not want to clip at the scroll margin: marg_clip is 0
640 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
641 * even _being_ outside the margins.
643 static void move(int x, int y, int marg_clip)
650 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
652 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
666 * Save or restore the cursor and SGR mode.
668 static void save_cursor(int save)
672 save_attr = curr_attr;
674 save_csattr = cset_attr[cset];
677 /* Make sure the window hasn't shrunk since the save */
683 curr_attr = save_attr;
685 cset_attr[cset] = save_csattr;
688 erase_char = (' ' | (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
693 * Erase a large portion of the screen: the whole screen, or the
694 * whole line, or parts thereof.
696 static void erase_lots(int line_only, int from_begin, int to_end)
700 unsigned long *ldata;
721 check_selection(start, end);
723 /* Clear screen also forces a full window redraw, just in case. */
724 if (start.y == 0 && start.x == 0 && end.y == rows)
727 ldata = lineptr(start.y);
728 while (poslt(start, end)) {
729 if (start.y == cols && !erase_lattr)
730 ldata[start.x] &= ~LATTR_WRAPPED;
732 ldata[start.x] = erase_char;
733 if (incpos(start) && start.y < rows)
734 ldata = lineptr(start.y);
739 * Insert or delete characters within the current line. n is +ve if
740 * insertion is desired, and -ve for deletion.
742 static void insch(int n)
744 int dir = (n < 0 ? -1 : +1);
747 unsigned long *ldata;
749 n = (n < 0 ? -n : n);
750 if (n > cols - curs.x)
752 m = cols - curs.x - n;
754 cursplus.x = curs.x + n;
755 check_selection(curs, cursplus);
756 ldata = lineptr(curs.y);
758 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
760 ldata[curs.x + m++] = erase_char;
762 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
764 ldata[curs.x + n] = erase_char;
769 * Toggle terminal mode `mode' to state `state'. (`query' indicates
770 * whether the mode is a DEC private one or a normal one.)
772 static void toggle_mode(int mode, int query, int state)
778 case 1: /* application cursor keys */
779 app_cursor_keys = state;
781 case 2: /* VT52 mode */
784 blink_is_real = FALSE;
787 blink_is_real = cfg.blinktext;
790 case 3: /* 80/132 columns */
792 request_resize(state ? 132 : 80, rows, 1);
795 case 5: /* reverse video */
797 * Toggle reverse video. If we receive an OFF within the
798 * visual bell timeout period after an ON, we trigger an
799 * effective visual bell, so that ESC[?5hESC[?5l will
800 * always be an actually _visible_ visual bell.
802 ticks = GetTickCount();
803 if (rvideo && !state && /* we're turning it off */
804 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
805 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
806 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
807 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
808 } else if (!rvideo && state) {
809 /* This is an ON, so we notice the time and save it. */
810 rvbell_timeout = ticks + VBELL_TIMEOUT;
813 seen_disp_event = TRUE;
817 case 6: /* DEC origin mode */
820 case 7: /* auto wrap */
823 case 8: /* auto key repeat */
826 case 10: /* set local edit mode */
827 term_editing = state;
828 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
830 case 25: /* enable/disable cursor */
831 compatibility2(OTHER, VT220);
833 seen_disp_event = TRUE;
835 case 47: /* alternate screen */
836 compatibility(OTHER);
841 case 1000: /* xterm mouse 1 */
842 xterm_mouse = state ? 1 : 0;
843 set_raw_mouse_mode(state);
845 case 1002: /* xterm mouse 2 */
846 xterm_mouse = state ? 2 : 0;
847 set_raw_mouse_mode(state);
851 case 4: /* set insert mode */
852 compatibility(VT102);
855 case 12: /* set echo mode */
856 term_echoing = !state;
857 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
859 case 20: /* Return sends ... */
860 cr_lf_return = state;
862 case 34: /* Make cursor BIG */
863 compatibility2(OTHER, VT220);
869 * Process an OSC sequence: set window title or icon name.
871 static void do_osc(void)
875 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
877 osc_string[osc_strlen] = '\0';
878 switch (esc_args[0]) {
881 set_icon(osc_string);
882 if (esc_args[0] == 1)
884 /* fall through: parameter 0 means set both */
887 set_title(osc_string);
894 * Remove everything currently in `inbuf' and stick it up on the
895 * in-memory display. There's a big state machine in here to
896 * process escape sequences...
902 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
903 c = inbuf[inbuf_reap];
906 * Optionally log the session traffic to a file. Useful for
907 * debugging and possibly also useful for actual logging.
909 logtraffic((unsigned char) c, LGTYP_DEBUG);
911 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
912 * be able to display 8-bit characters, but I'll let that go 'cause
916 /* First see about all those translations. */
917 if (termstate == TOPLEVEL) {
922 /* I know; gotos are evil. This one is really bad!
923 * But before you try removing it follow the path of the
924 * sequence "0x5F 0xC0 0x71" with UTF and VTGraphics on.
927 if (cfg.no_vt_graph_with_utf8) break;
930 } else if ((c & 0xe0) == 0xc0) {
931 utf_size = utf_state = 1;
932 utf_char = (c & 0x1f);
933 } else if ((c & 0xf0) == 0xe0) {
934 utf_size = utf_state = 2;
935 utf_char = (c & 0x0f);
936 } else if ((c & 0xf8) == 0xf0) {
937 utf_size = utf_state = 3;
938 utf_char = (c & 0x07);
939 } else if ((c & 0xfc) == 0xf8) {
940 utf_size = utf_state = 4;
941 utf_char = (c & 0x03);
942 } else if ((c & 0xfe) == 0xfc) {
943 utf_size = utf_state = 5;
944 utf_char = (c & 0x01);
955 if ((c & 0xC0) != 0x80) {
956 inbuf_reap--; /* This causes the faulting character */
957 c = UCSERR; /* to be logged twice - not really a */
958 utf_state = 0; /* serious problem. */
961 utf_char = (utf_char << 6) | (c & 0x3f);
967 /* Is somebody trying to be evil! */
969 (c < 0x800 && utf_size >= 2) ||
970 (c < 0x10000 && utf_size >= 3) ||
971 (c < 0x200000 && utf_size >= 4) ||
972 (c < 0x4000000 && utf_size >= 5))
975 /* Unicode line separator and paragraph separator are CR-LF */
976 if (c == 0x2028 || c == 0x2029)
979 /* High controls are probably a Baaad idea too. */
983 /* The UTF-16 surrogates are not nice either. */
984 /* The standard give the option of decoding these:
985 * I don't want to! */
986 if (c >= 0xD800 && c < 0xE000)
989 /* ISO 10646 characters now limited to UTF-16 range. */
993 /* This is currently a TagPhobic application.. */
994 if (c >= 0xE0000 && c <= 0xE007F)
997 /* U+FEFF is best seen as a null. */
1000 /* But U+FFFE is an error. */
1001 if (c == 0xFFFE || c == 0xFFFF)
1004 /* Oops this is a 16bit implementation */
1010 switch (cset_attr[cset]) {
1012 * Linedraw characters are different from 'ESC ( B'
1013 * only for a small range. For ones outside that
1014 * range, make sure we use the same font as well as
1015 * the same encoding.
1018 if (unitab_ctrl[c] != 0xFF)
1021 c = ((unsigned char) c) | ATTR_LINEDRW;
1025 /* If UK-ASCII, make the '#' a LineDraw Pound */
1027 c = '}' | ATTR_LINEDRW;
1030 /*FALLTHROUGH*/ case ATTR_ASCII:
1031 if (unitab_ctrl[c] != 0xFF)
1034 c = ((unsigned char) c) | ATTR_ASCII;
1040 /* How about C1 controls ? */
1041 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1042 has_compat(VT220)) {
1043 termstate = SEEN_ESC;
1045 c = '@' + (c & 0x1F);
1048 /* Or the GL control. */
1049 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1050 if (curs.x && !wrapnext)
1054 *cpos = (' ' | curr_attr | ATTR_ASCII);
1056 /* Or normal C0 controls. */
1057 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1059 case '\005': /* terminal type query */
1060 /* Strictly speaking this is VT100 but a VT100 defaults to
1061 * no response. Other terminals respond at their option.
1063 * Don't put a CR in the default string as this tends to
1064 * upset some weird software.
1066 * An xterm returns "xterm" (5 characters)
1068 compatibility(ANSIMIN);
1070 char abuf[256], *s, *d;
1072 for (s = cfg.answerback, d = abuf; *s; s++) {
1074 if (*s >= 'a' && *s <= 'z')
1075 *d++ = (*s - ('a' - 1));
1076 else if ((*s >= '@' && *s <= '_') ||
1077 *s == '?' || (*s & 0x80))
1082 } else if (*s == '^') {
1087 lpage_send(CP_ACP, abuf, d - abuf);
1092 struct beeptime *newbeep;
1095 ticks = GetTickCount();
1097 if (!beep_overloaded) {
1098 newbeep = smalloc(sizeof(struct beeptime));
1099 newbeep->ticks = ticks;
1100 newbeep->next = NULL;
1104 beeptail->next = newbeep;
1110 * Throw out any beeps that happened more than
1114 beephead->ticks < ticks - cfg.bellovl_t) {
1115 struct beeptime *tmp = beephead;
1116 beephead = tmp->next;
1123 if (cfg.bellovl && beep_overloaded &&
1124 ticks - lastbeep >= cfg.bellovl_s) {
1126 * If we're currently overloaded and the
1127 * last beep was more than s seconds ago,
1128 * leave overload mode.
1130 beep_overloaded = FALSE;
1131 } else if (cfg.bellovl && !beep_overloaded &&
1132 nbeeps >= cfg.bellovl_n) {
1134 * Now, if we have n or more beeps
1135 * remaining in the queue, go into overload
1138 beep_overloaded = TRUE;
1143 * Perform an actual beep if we're not overloaded.
1145 if (!cfg.bellovl || !beep_overloaded) {
1147 if (cfg.beep == BELL_VISUAL) {
1149 vbell_timeout = ticks + VBELL_TIMEOUT;
1157 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1158 else if (curs.x == 0 && curs.y > 0)
1159 curs.x = cols - 1, curs.y--;
1165 seen_disp_event = TRUE;
1168 compatibility(VT100);
1172 compatibility(VT100);
1177 termstate = VT52_ESC;
1179 compatibility(ANSIMIN);
1180 termstate = SEEN_ESC;
1188 seen_disp_event = TRUE;
1190 logtraffic((unsigned char) c, LGTYP_ASCII);
1193 if (has_compat(SCOANSI)) {
1195 erase_lots(FALSE, FALSE, TRUE);
1198 seen_disp_event = 1;
1202 compatibility(VT100);
1204 if (curs.y == marg_b)
1205 scroll(marg_t, marg_b, 1, TRUE);
1206 else if (curs.y < rows - 1)
1212 seen_disp_event = 1;
1214 logtraffic((unsigned char) c, LGTYP_ASCII);
1218 pos old_curs = curs;
1219 unsigned long *ldata = lineptr(curs.y);
1223 } while (curs.x < cols - 1 && !tabs[curs.x]);
1225 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1226 if (curs.x >= cols / 2)
1227 curs.x = cols / 2 - 1;
1234 check_selection(old_curs, curs);
1236 seen_disp_event = TRUE;
1240 switch (termstate) {
1242 /* Only graphic characters get this far, ctrls are stripped above */
1243 if (wrapnext && wrap) {
1244 cpos[1] |= LATTR_WRAPPED;
1245 if (curs.y == marg_b)
1246 scroll(marg_t, marg_b, 1, TRUE);
1247 else if (curs.y < rows - 1)
1255 if (selstate != NO_SELECTION) {
1256 pos cursplus = curs;
1258 check_selection(curs, cursplus);
1260 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1261 logtraffic((unsigned char) c, LGTYP_ASCII);
1263 extern int wcwidth(wchar_t ucs);
1268 width = wcwidth((wchar_t) c);
1271 if (curs.x + 1 != cols) {
1272 *cpos++ = c | ATTR_WIDE | curr_attr;
1273 *cpos++ = UCSWIDE | curr_attr;
1278 *cpos++ = c | curr_attr;
1285 if (curs.x == cols) {
1289 if (wrap && vt52_mode) {
1290 cpos[1] |= LATTR_WRAPPED;
1291 if (curs.y == marg_b)
1292 scroll(marg_t, marg_b, 1, TRUE);
1293 else if (curs.y < rows - 1)
1300 seen_disp_event = 1;
1305 * This state is virtually identical to SEEN_ESC, with the
1306 * exception that we have an OSC sequence in the pipeline,
1307 * and _if_ we see a backslash, we process it.
1311 termstate = TOPLEVEL;
1314 /* else fall through */
1316 if (c >= ' ' && c <= '/') {
1323 termstate = TOPLEVEL;
1324 switch (ANSI(c, esc_query)) {
1325 case '[': /* enter CSI mode */
1326 termstate = SEEN_CSI;
1328 esc_args[0] = ARG_DEFAULT;
1331 case ']': /* xterm escape sequences */
1332 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1333 compatibility(OTHER);
1334 termstate = SEEN_OSC;
1337 case '7': /* save cursor */
1338 compatibility(VT100);
1341 case '8': /* restore cursor */
1342 compatibility(VT100);
1344 seen_disp_event = TRUE;
1347 compatibility(VT100);
1348 app_keypad_keys = TRUE;
1351 compatibility(VT100);
1352 app_keypad_keys = FALSE;
1354 case 'D': /* exactly equivalent to LF */
1355 compatibility(VT100);
1356 if (curs.y == marg_b)
1357 scroll(marg_t, marg_b, 1, TRUE);
1358 else if (curs.y < rows - 1)
1362 seen_disp_event = TRUE;
1364 case 'E': /* exactly equivalent to CR-LF */
1365 compatibility(VT100);
1367 if (curs.y == marg_b)
1368 scroll(marg_t, marg_b, 1, TRUE);
1369 else if (curs.y < rows - 1)
1373 seen_disp_event = TRUE;
1375 case 'M': /* reverse index - backwards LF */
1376 compatibility(VT100);
1377 if (curs.y == marg_t)
1378 scroll(marg_t, marg_b, -1, TRUE);
1379 else if (curs.y > 0)
1383 seen_disp_event = TRUE;
1385 case 'Z': /* terminal type query */
1386 compatibility(VT100);
1387 ldisc_send(id_string, strlen(id_string));
1389 case 'c': /* restore power-on settings */
1390 compatibility(VT100);
1393 request_resize(80, rows, 1);
1398 seen_disp_event = TRUE;
1400 case 'H': /* set a tab */
1401 compatibility(VT100);
1402 tabs[curs.x] = TRUE;
1405 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1406 compatibility(VT100);
1408 unsigned long *ldata;
1412 for (i = 0; i < rows; i++) {
1414 for (j = 0; j < cols; j++)
1415 ldata[j] = ATTR_DEFAULT | 'E';
1419 seen_disp_event = TRUE;
1420 scrtop.x = scrtop.y = 0;
1423 check_selection(scrtop, scrbot);
1427 case ANSI('3', '#'):
1428 case ANSI('4', '#'):
1429 case ANSI('5', '#'):
1430 case ANSI('6', '#'):
1431 compatibility(VT100);
1433 unsigned long nlattr;
1434 unsigned long *ldata;
1435 switch (ANSI(c, esc_query)) {
1436 case ANSI('3', '#'):
1439 case ANSI('4', '#'):
1442 case ANSI('5', '#'):
1443 nlattr = LATTR_NORM;
1445 default: /* spiritually case ANSI('6', '#'): */
1446 nlattr = LATTR_WIDE;
1449 ldata = lineptr(curs.y);
1450 ldata[cols] &= ~LATTR_MODE;
1451 ldata[cols] |= nlattr;
1455 case ANSI('A', '('):
1456 compatibility(VT100);
1457 cset_attr[0] = ATTR_GBCHR;
1459 case ANSI('B', '('):
1460 compatibility(VT100);
1461 cset_attr[0] = ATTR_ASCII;
1463 case ANSI('0', '('):
1464 compatibility(VT100);
1465 cset_attr[0] = ATTR_LINEDRW;
1468 case ANSI('A', ')'):
1469 compatibility(VT100);
1470 cset_attr[1] = ATTR_GBCHR;
1472 case ANSI('B', ')'):
1473 compatibility(VT100);
1474 cset_attr[1] = ATTR_ASCII;
1476 case ANSI('0', ')'):
1477 compatibility(VT100);
1478 cset_attr[1] = ATTR_LINEDRW;
1481 case ANSI('8', '%'): /* Old Linux code */
1482 case ANSI('G', '%'):
1483 compatibility(OTHER);
1486 case ANSI('@', '%'):
1487 compatibility(OTHER);
1488 if (line_codepage != CP_UTF8)
1494 termstate = TOPLEVEL; /* default */
1496 if (esc_nargs <= ARGS_MAX) {
1497 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1498 esc_args[esc_nargs - 1] = 0;
1499 esc_args[esc_nargs - 1] =
1500 10 * esc_args[esc_nargs - 1] + c - '0';
1502 termstate = SEEN_CSI;
1503 } else if (c == ';') {
1504 if (++esc_nargs <= ARGS_MAX)
1505 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1506 termstate = SEEN_CSI;
1507 } else if (c < '@') {
1514 termstate = SEEN_CSI;
1516 switch (ANSI(c, esc_query)) {
1517 case 'A': /* move up N lines */
1518 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1519 seen_disp_event = TRUE;
1521 case 'e': /* move down N lines */
1522 compatibility(ANSI);
1524 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1525 seen_disp_event = TRUE;
1527 case 'a': /* move right N cols */
1528 compatibility(ANSI);
1529 case ANSI('c', '>'): /* report xterm version */
1530 compatibility(OTHER);
1531 /* this reports xterm version 136 so that VIM can
1532 use the drag messages from the mouse reporting */
1533 ldisc_send("\033[>0;136;0c", 11);
1536 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1537 seen_disp_event = TRUE;
1539 case 'D': /* move left N cols */
1540 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1541 seen_disp_event = TRUE;
1543 case 'E': /* move down N lines and CR */
1544 compatibility(ANSI);
1545 move(0, curs.y + def(esc_args[0], 1), 1);
1546 seen_disp_event = TRUE;
1548 case 'F': /* move up N lines and CR */
1549 compatibility(ANSI);
1550 move(0, curs.y - def(esc_args[0], 1), 1);
1551 seen_disp_event = TRUE;
1554 case '`': /* set horizontal posn */
1555 compatibility(ANSI);
1556 move(def(esc_args[0], 1) - 1, curs.y, 0);
1557 seen_disp_event = TRUE;
1559 case 'd': /* set vertical posn */
1560 compatibility(ANSI);
1562 (dec_om ? marg_t : 0) + def(esc_args[0],
1565 seen_disp_event = TRUE;
1568 case 'f': /* set horz and vert posns at once */
1570 esc_args[1] = ARG_DEFAULT;
1571 move(def(esc_args[1], 1) - 1,
1572 (dec_om ? marg_t : 0) + def(esc_args[0],
1575 seen_disp_event = TRUE;
1577 case 'J': /* erase screen or parts of it */
1579 unsigned int i = def(esc_args[0], 0) + 1;
1582 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1585 seen_disp_event = TRUE;
1587 case 'K': /* erase line or parts of it */
1589 unsigned int i = def(esc_args[0], 0) + 1;
1592 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1594 seen_disp_event = TRUE;
1596 case 'L': /* insert lines */
1597 compatibility(VT102);
1598 if (curs.y <= marg_b)
1599 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1602 seen_disp_event = TRUE;
1604 case 'M': /* delete lines */
1605 compatibility(VT102);
1606 if (curs.y <= marg_b)
1607 scroll(curs.y, marg_b, def(esc_args[0], 1),
1610 seen_disp_event = TRUE;
1612 case '@': /* insert chars */
1613 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1614 compatibility(VT102);
1615 insch(def(esc_args[0], 1));
1616 seen_disp_event = TRUE;
1618 case 'P': /* delete chars */
1619 compatibility(VT102);
1620 insch(-def(esc_args[0], 1));
1621 seen_disp_event = TRUE;
1623 case 'c': /* terminal type query */
1624 compatibility(VT100);
1625 /* This is the response for a VT102 */
1626 ldisc_send(id_string, strlen(id_string));
1628 case 'n': /* cursor position query */
1629 if (esc_args[0] == 6) {
1631 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1633 ldisc_send(buf, strlen(buf));
1634 } else if (esc_args[0] == 5) {
1635 ldisc_send("\033[0n", 4);
1638 case 'h': /* toggle modes to high */
1640 compatibility(VT100);
1643 for (i = 0; i < esc_nargs; i++)
1644 toggle_mode(esc_args[i], esc_query, TRUE);
1647 case 'l': /* toggle modes to low */
1649 compatibility(VT100);
1652 for (i = 0; i < esc_nargs; i++)
1653 toggle_mode(esc_args[i], esc_query, FALSE);
1656 case 'g': /* clear tabs */
1657 compatibility(VT100);
1658 if (esc_nargs == 1) {
1659 if (esc_args[0] == 0) {
1660 tabs[curs.x] = FALSE;
1661 } else if (esc_args[0] == 3) {
1663 for (i = 0; i < cols; i++)
1668 case 'r': /* set scroll margins */
1669 compatibility(VT100);
1670 if (esc_nargs <= 2) {
1672 top = def(esc_args[0], 1) - 1;
1673 bot = (esc_nargs <= 1
1675 0 ? rows : def(esc_args[1], rows)) - 1;
1678 /* VTTEST Bug 9 - if region is less than 2 lines
1679 * don't change region.
1681 if (bot - top > 0) {
1686 * I used to think the cursor should be
1687 * placed at the top of the newly marginned
1688 * area. Apparently not: VMS TPU falls over
1691 * Well actually it should for Origin mode - RDB
1693 curs.y = (dec_om ? marg_t : 0);
1695 seen_disp_event = TRUE;
1699 case 'm': /* set graphics rendition */
1702 * A VT100 without the AVO only had one attribute, either
1703 * underline or reverse video depending on the cursor type,
1704 * this was selected by CSI 7m.
1707 * This is sometimes DIM, eg on the GIGI and Linux
1709 * This is sometimes INVIS various ANSI.
1711 * This like 22 disables BOLD, DIM and INVIS
1713 * The ANSI colours appear on any terminal that has colour
1714 * (obviously) but the interaction between sgr0 and the
1715 * colours varies but is usually related to the background
1716 * colour erase item.
1717 * The interaction between colour attributes and the mono
1718 * ones is also very implementation dependent.
1720 * The 39 and 49 attributes are likely to be unimplemented.
1723 for (i = 0; i < esc_nargs; i++) {
1724 switch (def(esc_args[i], 0)) {
1725 case 0: /* restore defaults */
1726 curr_attr = ATTR_DEFAULT;
1728 case 1: /* enable bold */
1729 compatibility(VT100AVO);
1730 curr_attr |= ATTR_BOLD;
1732 case 21: /* (enable double underline) */
1733 compatibility(OTHER);
1734 case 4: /* enable underline */
1735 compatibility(VT100AVO);
1736 curr_attr |= ATTR_UNDER;
1738 case 5: /* enable blink */
1739 compatibility(VT100AVO);
1740 curr_attr |= ATTR_BLINK;
1742 case 7: /* enable reverse video */
1743 curr_attr |= ATTR_REVERSE;
1745 case 22: /* disable bold */
1746 compatibility2(OTHER, VT220);
1747 curr_attr &= ~ATTR_BOLD;
1749 case 24: /* disable underline */
1750 compatibility2(OTHER, VT220);
1751 curr_attr &= ~ATTR_UNDER;
1753 case 25: /* disable blink */
1754 compatibility2(OTHER, VT220);
1755 curr_attr &= ~ATTR_BLINK;
1757 case 27: /* disable reverse video */
1758 compatibility2(OTHER, VT220);
1759 curr_attr &= ~ATTR_REVERSE;
1770 curr_attr &= ~ATTR_FGMASK;
1772 (esc_args[i] - 30) << ATTR_FGSHIFT;
1774 case 39: /* default-foreground */
1775 curr_attr &= ~ATTR_FGMASK;
1776 curr_attr |= ATTR_DEFFG;
1787 curr_attr &= ~ATTR_BGMASK;
1789 (esc_args[i] - 40) << ATTR_BGSHIFT;
1791 case 49: /* default-background */
1792 curr_attr &= ~ATTR_BGMASK;
1793 curr_attr |= ATTR_DEFBG;
1801 (ATTR_FGMASK | ATTR_BGMASK |
1805 case 's': /* save cursor */
1808 case 'u': /* restore cursor */
1810 seen_disp_event = TRUE;
1812 case 't': /* set page size - ie window height */
1814 * VT340/VT420 sequence DECSLPP, DEC only allows values
1815 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1816 * illegal values (eg first arg 1..9) for window changing
1819 compatibility(VT340TEXT);
1821 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1822 request_resize(cols, def(esc_args[0], 24), 0);
1827 compatibility(SCOANSI);
1828 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1831 seen_disp_event = TRUE;
1834 compatibility(SCOANSI);
1835 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1838 seen_disp_event = TRUE;
1840 case ANSI('|', '*'):
1841 /* VT420 sequence DECSNLS
1842 * Set number of lines on screen
1843 * VT420 uses VGA like hardware and can support any size in
1844 * reasonable range (24..49 AIUI) with no default specified.
1846 compatibility(VT420);
1847 if (esc_nargs == 1 && esc_args[0] > 0) {
1848 request_resize(cols,
1849 def(esc_args[0], cfg.height),
1854 case ANSI('|', '$'):
1855 /* VT340/VT420 sequence DECSCPP
1856 * Set number of columns per page
1857 * Docs imply range is only 80 or 132, but I'll allow any.
1859 compatibility(VT340TEXT);
1860 if (esc_nargs <= 1) {
1861 request_resize(def(esc_args[0], cfg.width),
1866 case 'X': /* write N spaces w/o moving cursor */
1867 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1868 compatibility(ANSIMIN);
1870 int n = def(esc_args[0], 1);
1872 unsigned long *p = cpos;
1873 if (n > cols - curs.x)
1877 check_selection(curs, cursplus);
1880 seen_disp_event = TRUE;
1883 case 'x': /* report terminal characteristics */
1884 compatibility(VT100);
1887 int i = def(esc_args[0], 0);
1888 if (i == 0 || i == 1) {
1889 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1891 ldisc_send(buf, 20);
1895 case ANSI('L', '='):
1896 compatibility(OTHER);
1897 use_bce = (esc_args[0] <= 0);
1898 erase_char = ERASE_CHAR;
1903 (ATTR_FGMASK | ATTR_BGMASK)));
1905 case ANSI('E', '='):
1906 compatibility(OTHER);
1907 blink_is_real = (esc_args[0] >= 1);
1909 case ANSI('p', '"'):
1910 /* Allow the host to make this emulator a 'perfect' VT102.
1911 * This first appeared in the VT220, but we do need to get
1912 * back to PuTTY mode so I won't check it.
1914 * The arg in 40..42,50 are a PuTTY extension.
1915 * The 2nd arg, 8bit vs 7bit is not checked.
1917 * Setting VT102 mode should also change the Fkeys to
1918 * generate PF* codes as a real VT102 has no Fkeys.
1919 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1922 * Note ESC c will NOT change this!
1925 switch (esc_args[0]) {
1927 compatibility_level &= ~TM_VTXXX;
1928 compatibility_level |= TM_VT102;
1931 compatibility_level &= ~TM_VTXXX;
1932 compatibility_level |= TM_VT220;
1936 if (esc_args[0] > 60 && esc_args[0] < 70)
1937 compatibility_level |= TM_VTXXX;
1941 compatibility_level &= TM_VTXXX;
1944 compatibility_level = TM_PUTTY;
1947 compatibility_level = TM_SCOANSI;
1951 compatibility_level = TM_PUTTY;
1957 /* Change the response to CSI c */
1958 if (esc_args[0] == 50) {
1961 strcpy(id_string, "\033[?");
1962 for (i = 1; i < esc_nargs; i++) {
1964 strcat(id_string, ";");
1965 sprintf(lbuf, "%d", esc_args[i]);
1966 strcat(id_string, lbuf);
1968 strcat(id_string, "c");
1971 /* Is this a good idea ?
1972 * Well we should do a soft reset at this point ...
1974 if (!has_compat(VT420) && has_compat(VT100)) {
1976 request_resize(132, 24, 1);
1978 request_resize(80, 24, 1);
1987 case 'P': /* Linux palette sequence */
1988 termstate = SEEN_OSC_P;
1991 case 'R': /* Linux palette reset */
1994 termstate = TOPLEVEL;
1996 case 'W': /* word-set */
1997 termstate = SEEN_OSC_W;
2010 esc_args[0] = 10 * esc_args[0] + c - '0';
2014 * Grotty hack to support xterm and DECterm title
2015 * sequences concurrently.
2017 if (esc_args[0] == 2) {
2021 /* else fall through */
2023 termstate = OSC_STRING;
2029 * This OSC stuff is EVIL. It takes just one character to get into
2030 * sysline mode and it's not initially obvious how to get out.
2031 * So I've added CR and LF as string aborts.
2032 * This shouldn't effect compatibility as I believe embedded
2033 * control characters are supposed to be interpreted (maybe?)
2034 * and they don't display anything useful anyway.
2038 if (c == '\n' || c == '\r') {
2039 termstate = TOPLEVEL;
2040 } else if (c == 0234 || c == '\007') {
2042 * These characters terminate the string; ST and BEL
2043 * terminate the sequence and trigger instant
2044 * processing of it, whereas ESC goes back to SEEN_ESC
2045 * mode unless it is followed by \, in which case it is
2046 * synonymous with ST in the first place.
2049 termstate = TOPLEVEL;
2050 } else if (c == '\033')
2051 termstate = OSC_MAYBE_ST;
2052 else if (osc_strlen < OSC_STR_MAX)
2053 osc_string[osc_strlen++] = c;
2057 int max = (osc_strlen == 0 ? 21 : 16);
2059 if (c >= '0' && c <= '9')
2061 else if (c >= 'A' && c <= 'A' + max - 10)
2063 else if (c >= 'a' && c <= 'a' + max - 10)
2066 termstate = TOPLEVEL;
2069 osc_string[osc_strlen++] = val;
2070 if (osc_strlen >= 7) {
2071 palette_set(osc_string[0],
2072 osc_string[1] * 16 + osc_string[2],
2073 osc_string[3] * 16 + osc_string[4],
2074 osc_string[5] * 16 + osc_string[6]);
2076 termstate = TOPLEVEL;
2092 esc_args[0] = 10 * esc_args[0] + c - '0';
2095 termstate = OSC_STRING;
2100 termstate = TOPLEVEL;
2101 seen_disp_event = TRUE;
2104 move(curs.x, curs.y - 1, 1);
2107 move(curs.x, curs.y + 1, 1);
2110 move(curs.x + 1, curs.y, 1);
2113 move(curs.x - 1, curs.y, 1);
2116 * From the VT100 Manual
2117 * NOTE: The special graphics characters in the VT100
2118 * are different from those in the VT52
2120 * From VT102 manual:
2121 * 137 _ Blank - Same
2122 * 140 ` Reserved - Humm.
2123 * 141 a Solid rectangle - Similar
2124 * 142 b 1/ - Top half of fraction for the
2125 * 143 c 3/ - subscript numbers below.
2128 * 146 f Degrees - Same
2129 * 147 g Plus or minus - Same
2131 * 151 i Ellipsis (dots)
2134 * 154 l Bar at scan 0
2135 * 155 m Bar at scan 1
2136 * 156 n Bar at scan 2
2137 * 157 o Bar at scan 3 - Similar
2138 * 160 p Bar at scan 4 - Similar
2139 * 161 q Bar at scan 5 - Similar
2140 * 162 r Bar at scan 6 - Same
2141 * 163 s Bar at scan 7 - Similar
2156 cset_attr[cset = 0] = ATTR_LINEDRW;
2159 cset_attr[cset = 0] = ATTR_ASCII;
2166 scroll(0, rows - 1, -1, TRUE);
2167 else if (curs.y > 0)
2173 erase_lots(FALSE, FALSE, TRUE);
2177 erase_lots(TRUE, FALSE, TRUE);
2181 /* XXX Print cursor line */
2184 /* XXX Start controller mode */
2187 /* XXX Stop controller mode */
2191 termstate = VT52_Y1;
2194 ldisc_send("\033/Z", 3);
2197 app_keypad_keys = TRUE;
2200 app_keypad_keys = FALSE;
2203 /* XXX This should switch to VT100 mode not current or default
2204 * VT mode. But this will only have effect in a VT220+
2208 blink_is_real = cfg.blinktext;
2212 /* XXX Enter auto print mode */
2215 /* XXX Exit auto print mode */
2218 /* XXX Print screen */
2224 /* compatibility(ATARI) */
2226 erase_lots(FALSE, FALSE, TRUE);
2230 /* compatibility(ATARI) */
2231 if (curs.y <= marg_b)
2232 scroll(curs.y, marg_b, -1, FALSE);
2235 /* compatibility(ATARI) */
2236 if (curs.y <= marg_b)
2237 scroll(curs.y, marg_b, 1, TRUE);
2240 /* compatibility(ATARI) */
2241 termstate = VT52_FG;
2244 /* compatibility(ATARI) */
2245 termstate = VT52_BG;
2248 /* compatibility(ATARI) */
2249 erase_lots(FALSE, TRUE, FALSE);
2253 /* compatibility(ATARI) */
2257 /* compatibility(ATARI) */
2260 /* case 'j': Save cursor position - broken on ST */
2261 /* case 'k': Restore cursor position */
2263 /* compatibility(ATARI) */
2264 erase_lots(TRUE, TRUE, TRUE);
2270 /* compatibility(ATARI) */
2271 erase_lots(TRUE, TRUE, FALSE);
2274 /* compatibility(ATARI) */
2275 curr_attr |= ATTR_REVERSE;
2278 /* compatibility(ATARI) */
2279 curr_attr &= ~ATTR_REVERSE;
2281 case 'v': /* wrap Autowrap on - Wyse style */
2282 /* compatibility(ATARI) */
2285 case 'w': /* Autowrap off */
2286 /* compatibility(ATARI) */
2291 /* compatibility(OTHER) */
2293 curr_attr = ATTR_DEFAULT;
2297 (ATTR_FGMASK | ATTR_BGMASK |
2301 /* compatibility(VI50) */
2302 curr_attr |= ATTR_UNDER;
2305 /* compatibility(VI50) */
2306 curr_attr &= ~ATTR_UNDER;
2309 /* compatibility(VI50) */
2311 curr_attr |= ATTR_BOLD;
2314 /* compatibility(VI50) */
2316 curr_attr &= ~ATTR_BOLD;
2322 termstate = VT52_Y2;
2323 move(curs.x, c - ' ', 0);
2326 termstate = TOPLEVEL;
2327 move(c - ' ', curs.y, 0);
2332 termstate = TOPLEVEL;
2333 curr_attr &= ~ATTR_FGMASK;
2334 curr_attr &= ~ATTR_BOLD;
2335 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2336 if ((c & 0x8) || vt52_bold)
2337 curr_attr |= ATTR_BOLD;
2342 (ATTR_FGMASK | ATTR_BGMASK |
2346 termstate = TOPLEVEL;
2347 curr_attr &= ~ATTR_BGMASK;
2348 curr_attr &= ~ATTR_BLINK;
2349 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2351 /* Note: bold background */
2353 curr_attr |= ATTR_BLINK;
2358 (ATTR_FGMASK | ATTR_BGMASK |
2362 default: break; /* placate gcc warning about enum use */
2364 if (selstate != NO_SELECTION) {
2365 pos cursplus = curs;
2367 check_selection(curs, cursplus);
2375 * Compare two lines to determine whether they are sufficiently
2376 * alike to scroll-optimise one to the other. Return the degree of
2379 static int linecmp(unsigned long *a, unsigned long *b)
2383 for (i = n = 0; i < cols; i++)
2384 n += (*a++ == *b++);
2390 * Given a context, update the window. Out of paranoia, we don't
2391 * allow WM_PAINT responses to do scrolling optimisations.
2393 static void do_paint(Context ctx, int may_optimise)
2395 int i, j, our_curs_y;
2396 unsigned long rv, cursor;
2399 long cursor_background = ERASE_CHAR;
2403 * Check the visual bell state.
2406 ticks = GetTickCount();
2407 if (ticks - vbell_timeout >= 0)
2411 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2414 * screen array, disptop, scrtop,
2416 * cfg.blinkpc, blink_is_real, tblinker,
2417 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2420 /* Has the cursor position or type changed ? */
2423 if (blinker || !cfg.blink_cur)
2424 cursor = TATTR_ACTCURS;
2428 cursor = TATTR_PASCURS;
2430 cursor |= TATTR_RIGHTCURS;
2433 our_curs_y = curs.y - disptop;
2435 if (dispcurs && (curstype != cursor ||
2437 disptext + our_curs_y * (cols + 1) + curs.x)) {
2438 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2439 dispcurs[-1] |= ATTR_INVALID;
2440 if ((*dispcurs & ATTR_WIDE))
2441 dispcurs[1] |= ATTR_INVALID;
2442 *dispcurs |= ATTR_INVALID;
2447 /* The normal screen data */
2448 for (i = 0; i < rows; i++) {
2449 unsigned long *ldata;
2451 int idx, dirty_line, dirty_run;
2452 unsigned long attr = 0;
2453 int updated_line = 0;
2456 int last_run_dirty = 0;
2458 scrpos.y = i + disptop;
2459 ldata = lineptr(scrpos.y);
2460 lattr = (ldata[cols] & LATTR_MODE);
2462 idx = i * (cols + 1);
2463 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2464 disptext[idx + cols] = ldata[cols];
2466 for (j = 0; j < cols; j++, idx++) {
2467 unsigned long tattr, tchar;
2468 unsigned long *d = ldata + j;
2472 tchar = (*d & (CHAR_MASK | CSET_MASK));
2473 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2474 switch (tchar & CSET_MASK) {
2476 tchar = unitab_line[tchar & 0xFF];
2479 tchar = unitab_xterm[tchar & 0xFF];
2482 tattr |= (tchar & CSET_MASK);
2485 /* Video reversing things */
2487 ^ (posle(selstart, scrpos) &&
2488 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2490 /* 'Real' blinking ? */
2491 if (blink_is_real && (tattr & ATTR_BLINK)) {
2492 if (has_focus && tblinker) {
2494 tattr &= ~CSET_MASK;
2497 tattr &= ~ATTR_BLINK;
2500 /* Cursor here ? Save the 'background' */
2501 if (i == our_curs_y && j == curs.x) {
2502 cursor_background = tattr | tchar;
2503 dispcurs = disptext + idx;
2506 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2509 break_run = (tattr != attr || j - start >= sizeof(ch));
2511 /* Special hack for VT100 Linedraw glyphs */
2512 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2513 && tchar <= 0xBD) break_run = TRUE;
2515 if (!dbcs_screenfont && !dirty_line) {
2516 if ((tchar | tattr) == disptext[idx])
2518 else if (!dirty_run && ccount == 1)
2523 if ((dirty_run || last_run_dirty) && ccount > 0) {
2524 do_text(ctx, start, i, ch, ccount, attr, lattr);
2530 if (dbcs_screenfont)
2531 last_run_dirty = dirty_run;
2532 dirty_run = dirty_line;
2535 if ((tchar | tattr) != disptext[idx])
2537 ch[ccount++] = (char) tchar;
2538 disptext[idx] = tchar | tattr;
2540 /* If it's a wide char step along to the next one. */
2541 if (tattr & ATTR_WIDE) {
2545 /* Cursor is here ? Ouch! */
2546 if (i == our_curs_y && j == curs.x) {
2547 cursor_background = *d;
2548 dispcurs = disptext + idx;
2550 if (disptext[idx] != *d)
2556 if (dirty_run && ccount > 0) {
2557 do_text(ctx, start, i, ch, ccount, attr, lattr);
2561 /* Cursor on this line ? (and changed) */
2562 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2563 ch[0] = (char) (cursor_background & CHAR_MASK);
2564 attr = (cursor_background & ATTR_MASK) | cursor;
2565 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2572 * Flick the switch that says if blinking things should be shown or hidden.
2575 void term_blink(int flg)
2577 static long last_blink = 0;
2578 static long last_tblink = 0;
2579 long now, blink_diff;
2581 now = GetTickCount();
2582 blink_diff = now - last_tblink;
2584 /* Make sure the text blinks no more than 2Hz */
2585 if (blink_diff < 0 || blink_diff > 450) {
2587 tblinker = !tblinker;
2596 blink_diff = now - last_blink;
2598 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2599 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2607 * Invalidate the whole screen so it will be repainted in full.
2609 void term_invalidate(void)
2613 for (i = 0; i < rows * (cols + 1); i++)
2614 disptext[i] = ATTR_INVALID;
2618 * Paint the window in response to a WM_PAINT message.
2620 void term_paint(Context ctx, int l, int t, int r, int b)
2622 int i, j, left, top, right, bottom;
2624 left = l / font_width;
2625 right = (r - 1) / font_width;
2626 top = t / font_height;
2627 bottom = (b - 1) / font_height;
2628 for (i = top; i <= bottom && i < rows; i++) {
2629 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2630 for (j = left; j <= right && j < cols; j++)
2631 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2633 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2634 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2637 /* This should happen soon enough, also for some reason it sometimes
2638 * fails to actually do anything when re-sizing ... painting the wrong
2640 do_paint (ctx, FALSE);
2645 * Attempt to scroll the scrollback. The second parameter gives the
2646 * position we want to scroll to; the first is +1 to denote that
2647 * this position is relative to the beginning of the scrollback, -1
2648 * to denote it is relative to the end, and 0 to denote that it is
2649 * relative to the current position.
2651 void term_scroll(int rel, int where)
2653 int sbtop = -count234(scrollback);
2655 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2656 if (disptop < sbtop)
2664 static void clipme(pos top, pos bottom)
2667 wchar_t *wbptr; /* where next char goes within workbuf */
2668 int wblen = 0; /* workbuf len */
2669 int buflen; /* amount of memory allocated to workbuf */
2671 buflen = 5120; /* Default size */
2672 workbuf = smalloc(buflen * sizeof(wchar_t));
2673 wbptr = workbuf; /* start filling here */
2675 while (poslt(top, bottom)) {
2677 unsigned long *ldata = lineptr(top.y);
2683 if (!(ldata[cols] & LATTR_WRAPPED)) {
2684 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2685 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2686 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2687 && poslt(top, nlpos))
2689 if (poslt(nlpos, bottom))
2692 while (poslt(top, bottom) && poslt(top, nlpos)) {
2695 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2697 wchar_t cbuf[16], *p;
2698 int uc = (ldata[top.x] & 0xFFFF);
2701 if (uc == UCSWIDE) {
2706 switch (uc & CSET_MASK) {
2709 uc = unitab_xterm[uc & 0xFF];
2713 uc = unitab_line[uc & 0xFF];
2716 switch (uc & CSET_MASK) {
2718 uc = unitab_font[uc & 0xFF];
2721 uc = unitab_oemcp[uc & 0xFF];
2725 set = (uc & CSET_MASK);
2726 c = (uc & CHAR_MASK);
2730 if (DIRECT_FONT(uc)) {
2731 if (c >= ' ' && c != 0x7F) {
2732 unsigned char buf[4];
2735 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2737 buf[1] = (unsigned char) ldata[top.x + 1];
2738 rv = MultiByteToWideChar(font_codepage,
2739 0, buf, 2, wbuf, 4);
2743 rv = MultiByteToWideChar(font_codepage,
2744 0, buf, 1, wbuf, 4);
2748 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2755 for (p = cbuf; *p; p++) {
2756 /* Enough overhead for trailing NL and nul */
2757 if (wblen >= buflen - 16) {
2760 sizeof(wchar_t) * (buflen += 100));
2761 wbptr = workbuf + wblen;
2770 for (i = 0; i < sel_nl_sz; i++) {
2772 *wbptr++ = sel_nl[i];
2780 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2781 if (buflen > 0) /* indicates we allocated this buffer */
2785 void term_copyall(void)
2788 top.y = -count234(scrollback);
2794 * The wordness array is mainly for deciding the disposition of the US-ASCII
2797 static int wordtype(int uc)
2800 int start, end, ctype;
2801 } *wptr, ucs_words[] = {
2807 0x037e, 0x037e, 1}, /* Greek question mark */
2809 0x0387, 0x0387, 1}, /* Greek ano teleia */
2811 0x055a, 0x055f, 1}, /* Armenian punctuation */
2813 0x0589, 0x0589, 1}, /* Armenian full stop */
2815 0x0700, 0x070d, 1}, /* Syriac punctuation */
2817 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2819 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2821 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2823 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2825 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2827 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2829 0x2000, 0x200a, 0}, /* Various spaces */
2831 0x2070, 0x207f, 2}, /* superscript */
2833 0x2080, 0x208f, 2}, /* subscript */
2835 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2837 0x3000, 0x3000, 0}, /* ideographic space */
2839 0x3001, 0x3020, 1}, /* ideographic punctuation */
2841 0x303f, 0x309f, 3}, /* Hiragana */
2843 0x30a0, 0x30ff, 3}, /* Katakana */
2845 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2847 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2849 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2851 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2853 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2855 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2857 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2859 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2861 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2866 uc &= (CSET_MASK | CHAR_MASK);
2868 switch (uc & CSET_MASK) {
2870 uc = unitab_xterm[uc & 0xFF];
2873 uc = unitab_line[uc & 0xFF];
2876 switch (uc & CSET_MASK) {
2878 uc = unitab_font[uc & 0xFF];
2881 uc = unitab_oemcp[uc & 0xFF];
2886 return wordness[uc];
2888 for (wptr = ucs_words; wptr->start; wptr++) {
2889 if (uc >= wptr->start && uc <= wptr->end)
2897 * Spread the selection outwards according to the selection mode.
2899 static pos sel_spread_half(pos p, int dir)
2901 unsigned long *ldata;
2904 ldata = lineptr(p.y);
2909 * In this mode, every character is a separate unit, except
2910 * for runs of spaces at the end of a non-wrapping line.
2912 if (!(ldata[cols] & LATTR_WRAPPED)) {
2913 unsigned long *q = ldata + cols;
2914 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2916 if (q == ldata + cols)
2918 if (p.x >= q - ldata)
2919 p.x = (dir == -1 ? q - ldata : cols - 1);
2924 * In this mode, the units are maximal runs of characters
2925 * whose `wordness' has the same value.
2927 wvalue = wordtype(ldata[p.x]);
2929 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
2932 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
2938 * In this mode, every line is a unit.
2940 p.x = (dir == -1 ? 0 : cols - 1);
2946 static void sel_spread(void)
2948 selstart = sel_spread_half(selstart, -1);
2950 selend = sel_spread_half(selend, +1);
2954 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
2955 int shift, int ctrl)
2958 unsigned long *ldata;
2974 selpoint.y = y + disptop;
2976 ldata = lineptr(selpoint.y);
2977 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
2981 int encstate = 0, r, c;
2983 static int is_down = 0;
2987 encstate = 0x20; /* left button down */
2998 case MBT_WHEEL_DOWN:
3001 default: break; /* placate gcc warning about enum use */
3005 if (xterm_mouse == 1)
3018 default: break; /* placate gcc warning about enum use */
3027 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3028 ldisc_send(abuf, 6);
3032 b = translate_button(b);
3034 if (b == MBT_SELECT && a == MA_CLICK) {
3036 selstate = ABOUT_TO;
3037 selanchor = selpoint;
3039 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3041 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3042 selstate = DRAGGING;
3043 selstart = selanchor = selpoint;
3047 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3048 (b == MBT_EXTEND && a != MA_RELEASE)) {
3049 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3051 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3052 if (posdiff(selpoint, selstart) <
3053 posdiff(selend, selstart) / 2) {
3057 selanchor = selstart;
3059 selstate = DRAGGING;
3061 if (selstate != ABOUT_TO && selstate != DRAGGING)
3062 selanchor = selpoint;
3063 selstate = DRAGGING;
3064 if (poslt(selpoint, selanchor)) {
3065 selstart = selpoint;
3069 selstart = selanchor;
3074 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3075 if (selstate == DRAGGING) {
3077 * We've completed a selection. We now transfer the
3078 * data to the clipboard.
3080 clipme(selstart, selend);
3081 selstate = SELECTED;
3083 selstate = NO_SELECTION;
3084 } else if (b == MBT_PASTE
3085 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3089 get_clip(&data, &len);
3094 sfree(paste_buffer);
3095 paste_pos = paste_hold = paste_len = 0;
3096 paste_buffer = smalloc(len * sizeof(wchar_t));
3099 while (p < data + len) {
3100 while (p < data + len &&
3101 !(p <= data + len - sel_nl_sz &&
3102 !memcmp(p, sel_nl, sizeof(sel_nl))))
3107 for (i = 0; i < p - q; i++) {
3108 paste_buffer[paste_len++] = q[i];
3112 if (p <= data + len - sel_nl_sz &&
3113 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3114 paste_buffer[paste_len++] = '\r';
3120 /* Assume a small paste will be OK in one go. */
3121 if (paste_len < 256) {
3122 luni_send(paste_buffer, paste_len);
3124 sfree(paste_buffer);
3126 paste_pos = paste_hold = paste_len = 0;
3129 get_clip(NULL, NULL);
3139 sfree(paste_buffer);
3146 static long last_paste = 0;
3147 long now, paste_diff;
3152 /* Don't wait forever to paste */
3154 now = GetTickCount();
3155 paste_diff = now - last_paste;
3156 if (paste_diff >= 0 && paste_diff < 450)
3161 while (paste_pos < paste_len) {
3163 while (n + paste_pos < paste_len) {
3164 if (paste_buffer[paste_pos + n++] == '\r')
3167 luni_send(paste_buffer + paste_pos, n);
3170 if (paste_pos < paste_len) {
3175 sfree(paste_buffer);
3180 static void deselect(void)
3182 selstate = NO_SELECTION;
3183 selstart.x = selstart.y = selend.x = selend.y = 0;
3186 void term_deselect(void)
3192 int term_ldisc(int option)
3194 if (option == LD_ECHO)
3195 return term_echoing;
3196 if (option == LD_EDIT)
3197 return term_editing;
3202 * from_backend(), to get data from the backend for the terminal.
3204 void from_backend(int is_stderr, char *data, int len)
3207 if (inbuf_head >= INBUF_SIZE)
3209 inbuf[inbuf_head++] = *data++;
3214 * Log session traffic.
3216 void logtraffic(unsigned char c, int logmode)
3218 if (cfg.logtype > 0) {
3219 if (cfg.logtype == logmode) {
3220 /* deferred open file from pgm start? */
3229 /* open log file append/overwrite mode */
3239 sprintf(writemod, "wb"); /* default to rewrite */
3240 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3244 i = askappend(cfg.logfilename);
3246 writemod[0] = 'a'; /* set append mode */
3247 else if (i == 0) { /* cancelled */
3249 cfg.logtype = 0; /* disable logging */
3254 lgfp = fopen(cfg.logfilename, writemod);
3255 if (lgfp) { /* enter into event log */
3256 sprintf(buf, "%s session log (%s mode) to file : ",
3257 (writemod[0] == 'a') ? "Appending" : "Writing new",
3258 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3259 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3260 /* Make sure we do not exceed the output buffer size */
3261 strncat(buf, cfg.logfilename, 128);
3262 buf[strlen(buf)] = '\0';
3265 /* --- write header line iinto log file */
3266 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3269 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3271 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3275 void logfclose(void)