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 * 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)
303 if ((seen_key_event && (cfg.scroll_on_key)) ||
304 (seen_disp_event && (cfg.scroll_on_disp))) {
305 disptop = 0; /* return to main screen */
306 seen_disp_event = seen_key_event = 0;
309 sys_cursor(curs.x, curs.y - disptop);
315 * Same as power_on(), but an external function.
317 void term_pwron(void)
327 * Clear the scrollback.
329 void term_clrsb(void)
333 while ((line = delpos234(scrollback, 0)) != NULL) {
340 * Initialise the terminal.
344 screen = alt_screen = scrollback = NULL;
346 disptext = dispcurs = NULL;
351 beephead = beeptail = NULL;
354 beep_overloaded = FALSE;
358 * Set up the terminal for a given size.
360 void term_size(int newrows, int newcols, int newsavelines)
363 unsigned long *newdisp, *line;
366 int save_alt_which = alt_which;
368 if (newrows == rows && newcols == cols && newsavelines == savelines)
369 return; /* nothing to do */
375 alt_b = marg_b = newrows - 1;
378 scrollback = newtree234(NULL);
379 screen = newtree234(NULL);
384 * Resize the screen and scrollback. We only need to shift
385 * lines around within our data structures, because lineptr()
386 * will take care of resizing each individual line if
389 * - If the new screen and the old screen differ in length, we
390 * must shunt some lines in from the scrollback or out to
393 * - If doing that fails to provide us with enough material to
394 * fill the new screen (i.e. the number of rows needed in
395 * the new screen exceeds the total number in the previous
396 * screen+scrollback), we must invent some blank lines to
399 * - Then, if the new scrollback length is less than the
400 * amount of scrollback we actually have, we must throw some
403 sblen = count234(scrollback);
404 /* Do this loop to expand the screen if newrows > rows */
405 for (i = rows; i < newrows; i++) {
407 line = delpos234(scrollback, --sblen);
409 line = smalloc(TSIZE * (newcols + 2));
411 for (j = 0; j <= newcols; j++)
412 line[j + 1] = ERASE_CHAR;
414 addpos234(screen, line, 0);
416 /* Do this loop to shrink the screen if newrows < rows */
417 for (i = newrows; i < rows; i++) {
418 line = delpos234(screen, 0);
419 addpos234(scrollback, line, sblen++);
421 assert(count234(screen) == newrows);
422 while (sblen > newsavelines) {
423 line = delpos234(scrollback, 0);
427 assert(count234(scrollback) <= newsavelines);
430 newdisp = smalloc(newrows * (newcols + 1) * TSIZE);
431 for (i = 0; i < newrows * (newcols + 1); i++)
432 newdisp[i] = ATTR_INVALID;
437 newalt = newtree234(NULL);
438 for (i = 0; i < newrows; i++) {
439 line = smalloc(TSIZE * (newcols + 2));
441 for (j = 0; j <= newcols; j++)
442 line[j + 1] = erase_char;
443 addpos234(newalt, line, i);
446 while (NULL != (line = delpos234(alt_screen, 0)))
448 freetree234(alt_screen);
452 tabs = srealloc(tabs, newcols * sizeof(*tabs));
455 for (i = (cols > 0 ? cols : 0); i < newcols; i++)
456 tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
460 curs.y += newrows - rows;
463 if (curs.y >= newrows)
464 curs.y = newrows - 1;
465 if (curs.x >= newcols)
466 curs.x = newcols - 1;
468 wrapnext = alt_wnext = FALSE;
472 savelines = newsavelines;
475 swap_screen(save_alt_which);
484 static void swap_screen(int which)
489 if (which == alt_which)
516 wrapnext = alt_wnext;
529 * Update the scroll bar.
531 static void update_sbar(void)
535 nscroll = count234(scrollback);
537 set_sbar(nscroll + rows, nscroll + disptop, rows);
541 * Check whether the region bounded by the two pointers intersects
542 * the scroll region, and de-select the on-screen selection if so.
544 static void check_selection(pos from, pos to)
546 if (poslt(from, selend) && poslt(selstart, to))
551 * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
552 * for backward.) `sb' is TRUE if the scrolling is permitted to
553 * affect the scrollback buffer.
555 * NB this function invalidates all pointers into lines of the
556 * screen data structures. In particular, you MUST call fix_cpos
557 * after calling scroll() and before doing anything else that
558 * uses the cpos shortcut pointer.
560 static void scroll(int topline, int botline, int lines, int sb)
562 unsigned long *line, *line2;
565 if (topline != 0 || alt_which != 0)
570 line = delpos234(screen, botline);
571 for (i = 0; i < cols; i++)
572 line[i + 1] = erase_char;
574 addpos234(screen, line, topline);
576 if (selstart.y >= topline && selstart.y <= botline) {
578 if (selstart.y > botline) {
579 selstart.y = botline;
583 if (selend.y >= topline && selend.y <= botline) {
585 if (selend.y > botline) {
595 line = delpos234(screen, topline);
596 if (sb && savelines > 0) {
597 int sblen = count234(scrollback);
599 * We must add this line to the scrollback. We'll
600 * remove a line from the top of the scrollback to
601 * replace it, or allocate a new one if the
602 * scrollback isn't full.
604 if (sblen == savelines) {
605 sblen--, line2 = delpos234(scrollback, 0);
607 line2 = smalloc(TSIZE * (cols + 2));
610 addpos234(scrollback, line, sblen);
613 for (i = 0; i < cols; i++)
614 line[i + 1] = erase_char;
616 addpos234(screen, line, botline);
618 if (selstart.y >= topline && selstart.y <= botline) {
620 if (selstart.y < topline) {
621 selstart.y = topline;
625 if (selend.y >= topline && selend.y <= botline) {
627 if (selend.y < topline) {
639 * Move the cursor to a given position, clipping at boundaries. We
640 * may or may not want to clip at the scroll margin: marg_clip is 0
641 * not to, 1 to disallow _passing_ the margins, and 2 to disallow
642 * even _being_ outside the margins.
644 static void move(int x, int y, int marg_clip)
651 if ((curs.y >= marg_t || marg_clip == 2) && y < marg_t)
653 if ((curs.y <= marg_b || marg_clip == 2) && y > marg_b)
667 * Save or restore the cursor and SGR mode.
669 static void save_cursor(int save)
673 save_attr = curr_attr;
675 save_csattr = cset_attr[cset];
678 /* Make sure the window hasn't shrunk since the save */
684 curr_attr = save_attr;
686 cset_attr[cset] = save_csattr;
689 erase_char = (' ' | (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
694 * Erase a large portion of the screen: the whole screen, or the
695 * whole line, or parts thereof.
697 static void erase_lots(int line_only, int from_begin, int to_end)
701 unsigned long *ldata;
722 check_selection(start, end);
724 /* Clear screen also forces a full window redraw, just in case. */
725 if (start.y == 0 && start.x == 0 && end.y == rows)
728 ldata = lineptr(start.y);
729 while (poslt(start, end)) {
730 if (start.y == cols && !erase_lattr)
731 ldata[start.x] &= ~LATTR_WRAPPED;
733 ldata[start.x] = erase_char;
734 if (incpos(start) && start.y < rows)
735 ldata = lineptr(start.y);
740 * Insert or delete characters within the current line. n is +ve if
741 * insertion is desired, and -ve for deletion.
743 static void insch(int n)
745 int dir = (n < 0 ? -1 : +1);
748 unsigned long *ldata;
750 n = (n < 0 ? -n : n);
751 if (n > cols - curs.x)
753 m = cols - curs.x - n;
755 cursplus.x = curs.x + n;
756 check_selection(curs, cursplus);
757 ldata = lineptr(curs.y);
759 memmove(ldata + curs.x, ldata + curs.x + n, m * TSIZE);
761 ldata[curs.x + m++] = erase_char;
763 memmove(ldata + curs.x + n, ldata + curs.x, m * TSIZE);
765 ldata[curs.x + n] = erase_char;
770 * Toggle terminal mode `mode' to state `state'. (`query' indicates
771 * whether the mode is a DEC private one or a normal one.)
773 static void toggle_mode(int mode, int query, int state)
779 case 1: /* application cursor keys */
780 app_cursor_keys = state;
782 case 2: /* VT52 mode */
785 blink_is_real = FALSE;
788 blink_is_real = cfg.blinktext;
791 case 3: /* 80/132 columns */
793 request_resize(state ? 132 : 80, rows, 1);
796 case 5: /* reverse video */
798 * Toggle reverse video. If we receive an OFF within the
799 * visual bell timeout period after an ON, we trigger an
800 * effective visual bell, so that ESC[?5hESC[?5l will
801 * always be an actually _visible_ visual bell.
803 ticks = GetTickCount();
804 if (rvideo && !state && /* we're turning it off */
805 ticks < rvbell_timeout) { /* and it's not long since it was turned on */
806 in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */
807 if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */
808 vbell_timeout = rvbell_timeout; /* vbell end is at least then */
809 } else if (!rvideo && state) {
810 /* This is an ON, so we notice the time and save it. */
811 rvbell_timeout = ticks + VBELL_TIMEOUT;
814 seen_disp_event = TRUE;
818 case 6: /* DEC origin mode */
821 case 7: /* auto wrap */
824 case 8: /* auto key repeat */
827 case 10: /* set local edit mode */
828 term_editing = state;
829 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
831 case 25: /* enable/disable cursor */
832 compatibility2(OTHER, VT220);
834 seen_disp_event = TRUE;
836 case 47: /* alternate screen */
837 compatibility(OTHER);
842 case 1000: /* xterm mouse 1 */
843 xterm_mouse = state ? 1 : 0;
844 set_raw_mouse_mode(state);
846 case 1002: /* xterm mouse 2 */
847 xterm_mouse = state ? 2 : 0;
848 set_raw_mouse_mode(state);
852 case 4: /* set insert mode */
853 compatibility(VT102);
856 case 12: /* set echo mode */
857 term_echoing = !state;
858 ldisc_send(NULL, 0); /* cause ldisc to notice changes */
860 case 20: /* Return sends ... */
861 cr_lf_return = state;
863 case 34: /* Make cursor BIG */
864 compatibility2(OTHER, VT220);
870 * Process an OSC sequence: set window title or icon name.
872 static void do_osc(void)
876 wordness[(unsigned char) osc_string[osc_strlen]] = esc_args[0];
878 osc_string[osc_strlen] = '\0';
879 switch (esc_args[0]) {
882 set_icon(osc_string);
883 if (esc_args[0] == 1)
885 /* fall through: parameter 0 means set both */
888 set_title(osc_string);
895 * Remove everything currently in `inbuf' and stick it up on the
896 * in-memory display. There's a big state machine in here to
897 * process escape sequences...
903 for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
904 c = inbuf[inbuf_reap];
907 * Optionally log the session traffic to a file. Useful for
908 * debugging and possibly also useful for actual logging.
910 logtraffic((unsigned char) c, LGTYP_DEBUG);
912 /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
913 * be able to display 8-bit characters, but I'll let that go 'cause
917 /* First see about all those translations. */
918 if (termstate == TOPLEVEL) {
923 /* I know; gotos are evil. This one is really bad!
924 * But before you try removing it follow the path of the
925 * sequence "0x5F 0xC0 0x71" with UTF and VTGraphics on.
928 if (cfg.no_vt_graph_with_utf8) break;
931 } else if ((c & 0xe0) == 0xc0) {
932 utf_size = utf_state = 1;
933 utf_char = (c & 0x1f);
934 } else if ((c & 0xf0) == 0xe0) {
935 utf_size = utf_state = 2;
936 utf_char = (c & 0x0f);
937 } else if ((c & 0xf8) == 0xf0) {
938 utf_size = utf_state = 3;
939 utf_char = (c & 0x07);
940 } else if ((c & 0xfc) == 0xf8) {
941 utf_size = utf_state = 4;
942 utf_char = (c & 0x03);
943 } else if ((c & 0xfe) == 0xfc) {
944 utf_size = utf_state = 5;
945 utf_char = (c & 0x01);
956 if ((c & 0xC0) != 0x80) {
957 inbuf_reap--; /* This causes the faulting character */
958 c = UCSERR; /* to be logged twice - not really a */
959 utf_state = 0; /* serious problem. */
962 utf_char = (utf_char << 6) | (c & 0x3f);
968 /* Is somebody trying to be evil! */
970 (c < 0x800 && utf_size >= 2) ||
971 (c < 0x10000 && utf_size >= 3) ||
972 (c < 0x200000 && utf_size >= 4) ||
973 (c < 0x4000000 && utf_size >= 5))
976 /* Unicode line separator and paragraph separator are CR-LF */
977 if (c == 0x2028 || c == 0x2029)
980 /* High controls are probably a Baaad idea too. */
984 /* The UTF-16 surrogates are not nice either. */
985 /* The standard give the option of decoding these:
986 * I don't want to! */
987 if (c >= 0xD800 && c < 0xE000)
990 /* ISO 10646 characters now limited to UTF-16 range. */
994 /* This is currently a TagPhobic application.. */
995 if (c >= 0xE0000 && c <= 0xE007F)
998 /* U+FEFF is best seen as a null. */
1001 /* But U+FFFE is an error. */
1002 if (c == 0xFFFE || c == 0xFFFF)
1005 /* Oops this is a 16bit implementation */
1011 switch (cset_attr[cset]) {
1013 * Linedraw characters are different from 'ESC ( B'
1014 * only for a small range. For ones outside that
1015 * range, make sure we use the same font as well as
1016 * the same encoding.
1019 if (unitab_ctrl[c] != 0xFF)
1022 c = ((unsigned char) c) | ATTR_LINEDRW;
1026 /* If UK-ASCII, make the '#' a LineDraw Pound */
1028 c = '}' | ATTR_LINEDRW;
1031 /*FALLTHROUGH*/ case ATTR_ASCII:
1032 if (unitab_ctrl[c] != 0xFF)
1035 c = ((unsigned char) c) | ATTR_ASCII;
1041 /* How about C1 controls ? */
1042 if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
1043 has_compat(VT220)) {
1044 termstate = SEEN_ESC;
1046 c = '@' + (c & 0x1F);
1049 /* Or the GL control. */
1050 if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
1051 if (curs.x && !wrapnext)
1055 *cpos = (' ' | curr_attr | ATTR_ASCII);
1057 /* Or normal C0 controls. */
1058 if ((c & -32) == 0 && termstate < DO_CTRLS) {
1060 case '\005': /* terminal type query */
1061 /* Strictly speaking this is VT100 but a VT100 defaults to
1062 * no response. Other terminals respond at their option.
1064 * Don't put a CR in the default string as this tends to
1065 * upset some weird software.
1067 * An xterm returns "xterm" (5 characters)
1069 compatibility(ANSIMIN);
1071 char abuf[256], *s, *d;
1073 for (s = cfg.answerback, d = abuf; *s; s++) {
1075 if (*s >= 'a' && *s <= 'z')
1076 *d++ = (*s - ('a' - 1));
1077 else if ((*s >= '@' && *s <= '_') ||
1078 *s == '?' || (*s & 0x80))
1083 } else if (*s == '^') {
1088 lpage_send(CP_ACP, abuf, d - abuf);
1093 struct beeptime *newbeep;
1096 ticks = GetTickCount();
1098 if (!beep_overloaded) {
1099 newbeep = smalloc(sizeof(struct beeptime));
1100 newbeep->ticks = ticks;
1101 newbeep->next = NULL;
1105 beeptail->next = newbeep;
1111 * Throw out any beeps that happened more than
1115 beephead->ticks < ticks - cfg.bellovl_t) {
1116 struct beeptime *tmp = beephead;
1117 beephead = tmp->next;
1124 if (cfg.bellovl && beep_overloaded &&
1125 ticks - lastbeep >= cfg.bellovl_s) {
1127 * If we're currently overloaded and the
1128 * last beep was more than s seconds ago,
1129 * leave overload mode.
1131 beep_overloaded = FALSE;
1132 } else if (cfg.bellovl && !beep_overloaded &&
1133 nbeeps >= cfg.bellovl_n) {
1135 * Now, if we have n or more beeps
1136 * remaining in the queue, go into overload
1139 beep_overloaded = TRUE;
1144 * Perform an actual beep if we're not overloaded.
1146 if (!cfg.bellovl || !beep_overloaded) {
1148 if (cfg.beep == BELL_VISUAL) {
1150 vbell_timeout = ticks + VBELL_TIMEOUT;
1158 if (curs.x == 0 && (curs.y == 0 || wrap == 0));
1159 else if (curs.x == 0 && curs.y > 0)
1160 curs.x = cols - 1, curs.y--;
1166 seen_disp_event = TRUE;
1169 compatibility(VT100);
1173 compatibility(VT100);
1178 termstate = VT52_ESC;
1180 compatibility(ANSIMIN);
1181 termstate = SEEN_ESC;
1189 seen_disp_event = TRUE;
1191 logtraffic((unsigned char) c, LGTYP_ASCII);
1194 if (has_compat(SCOANSI)) {
1196 erase_lots(FALSE, FALSE, TRUE);
1199 seen_disp_event = 1;
1203 compatibility(VT100);
1205 if (curs.y == marg_b)
1206 scroll(marg_t, marg_b, 1, TRUE);
1207 else if (curs.y < rows - 1)
1213 seen_disp_event = 1;
1215 logtraffic((unsigned char) c, LGTYP_ASCII);
1219 pos old_curs = curs;
1220 unsigned long *ldata = lineptr(curs.y);
1224 } while (curs.x < cols - 1 && !tabs[curs.x]);
1226 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) {
1227 if (curs.x >= cols / 2)
1228 curs.x = cols / 2 - 1;
1235 check_selection(old_curs, curs);
1237 seen_disp_event = TRUE;
1241 switch (termstate) {
1243 /* Only graphic characters get this far, ctrls are stripped above */
1244 if (wrapnext && wrap) {
1245 cpos[1] |= LATTR_WRAPPED;
1246 if (curs.y == marg_b)
1247 scroll(marg_t, marg_b, 1, TRUE);
1248 else if (curs.y < rows - 1)
1256 if (selstate != NO_SELECTION) {
1257 pos cursplus = curs;
1259 check_selection(curs, cursplus);
1261 if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
1262 logtraffic((unsigned char) c, LGTYP_ASCII);
1264 extern int wcwidth(wchar_t ucs);
1269 width = wcwidth((wchar_t) c);
1272 if (curs.x + 1 != cols) {
1273 *cpos++ = c | ATTR_WIDE | curr_attr;
1274 *cpos++ = UCSWIDE | curr_attr;
1279 *cpos++ = c | curr_attr;
1286 if (curs.x == cols) {
1290 if (wrap && vt52_mode) {
1291 cpos[1] |= LATTR_WRAPPED;
1292 if (curs.y == marg_b)
1293 scroll(marg_t, marg_b, 1, TRUE);
1294 else if (curs.y < rows - 1)
1301 seen_disp_event = 1;
1306 * This state is virtually identical to SEEN_ESC, with the
1307 * exception that we have an OSC sequence in the pipeline,
1308 * and _if_ we see a backslash, we process it.
1312 termstate = TOPLEVEL;
1315 /* else fall through */
1317 if (c >= ' ' && c <= '/') {
1324 termstate = TOPLEVEL;
1325 switch (ANSI(c, esc_query)) {
1326 case '[': /* enter CSI mode */
1327 termstate = SEEN_CSI;
1329 esc_args[0] = ARG_DEFAULT;
1332 case ']': /* xterm escape sequences */
1333 /* Compatibility is nasty here, xterm, linux, decterm yuk! */
1334 compatibility(OTHER);
1335 termstate = SEEN_OSC;
1338 case '7': /* save cursor */
1339 compatibility(VT100);
1342 case '8': /* restore cursor */
1343 compatibility(VT100);
1345 seen_disp_event = TRUE;
1348 compatibility(VT100);
1349 app_keypad_keys = TRUE;
1352 compatibility(VT100);
1353 app_keypad_keys = FALSE;
1355 case 'D': /* exactly equivalent to LF */
1356 compatibility(VT100);
1357 if (curs.y == marg_b)
1358 scroll(marg_t, marg_b, 1, TRUE);
1359 else if (curs.y < rows - 1)
1363 seen_disp_event = TRUE;
1365 case 'E': /* exactly equivalent to CR-LF */
1366 compatibility(VT100);
1368 if (curs.y == marg_b)
1369 scroll(marg_t, marg_b, 1, TRUE);
1370 else if (curs.y < rows - 1)
1374 seen_disp_event = TRUE;
1376 case 'M': /* reverse index - backwards LF */
1377 compatibility(VT100);
1378 if (curs.y == marg_t)
1379 scroll(marg_t, marg_b, -1, TRUE);
1380 else if (curs.y > 0)
1384 seen_disp_event = TRUE;
1386 case 'Z': /* terminal type query */
1387 compatibility(VT100);
1388 ldisc_send(id_string, strlen(id_string));
1390 case 'c': /* restore power-on settings */
1391 compatibility(VT100);
1394 request_resize(80, rows, 1);
1399 seen_disp_event = TRUE;
1401 case 'H': /* set a tab */
1402 compatibility(VT100);
1403 tabs[curs.x] = TRUE;
1406 case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */
1407 compatibility(VT100);
1409 unsigned long *ldata;
1413 for (i = 0; i < rows; i++) {
1415 for (j = 0; j < cols; j++)
1416 ldata[j] = ATTR_DEFAULT | 'E';
1420 seen_disp_event = TRUE;
1421 scrtop.x = scrtop.y = 0;
1424 check_selection(scrtop, scrbot);
1428 case ANSI('3', '#'):
1429 case ANSI('4', '#'):
1430 case ANSI('5', '#'):
1431 case ANSI('6', '#'):
1432 compatibility(VT100);
1434 unsigned long nlattr;
1435 unsigned long *ldata;
1436 switch (ANSI(c, esc_query)) {
1437 case ANSI('3', '#'):
1440 case ANSI('4', '#'):
1443 case ANSI('5', '#'):
1444 nlattr = LATTR_NORM;
1446 default: /* spiritually case ANSI('6', '#'): */
1447 nlattr = LATTR_WIDE;
1450 ldata = lineptr(curs.y);
1451 ldata[cols] &= ~LATTR_MODE;
1452 ldata[cols] |= nlattr;
1456 case ANSI('A', '('):
1457 compatibility(VT100);
1458 cset_attr[0] = ATTR_GBCHR;
1460 case ANSI('B', '('):
1461 compatibility(VT100);
1462 cset_attr[0] = ATTR_ASCII;
1464 case ANSI('0', '('):
1465 compatibility(VT100);
1466 cset_attr[0] = ATTR_LINEDRW;
1469 case ANSI('A', ')'):
1470 compatibility(VT100);
1471 cset_attr[1] = ATTR_GBCHR;
1473 case ANSI('B', ')'):
1474 compatibility(VT100);
1475 cset_attr[1] = ATTR_ASCII;
1477 case ANSI('0', ')'):
1478 compatibility(VT100);
1479 cset_attr[1] = ATTR_LINEDRW;
1482 case ANSI('8', '%'): /* Old Linux code */
1483 case ANSI('G', '%'):
1484 compatibility(OTHER);
1487 case ANSI('@', '%'):
1488 compatibility(OTHER);
1489 if (line_codepage != CP_UTF8)
1495 termstate = TOPLEVEL; /* default */
1497 if (esc_nargs <= ARGS_MAX) {
1498 if (esc_args[esc_nargs - 1] == ARG_DEFAULT)
1499 esc_args[esc_nargs - 1] = 0;
1500 esc_args[esc_nargs - 1] =
1501 10 * esc_args[esc_nargs - 1] + c - '0';
1503 termstate = SEEN_CSI;
1504 } else if (c == ';') {
1505 if (++esc_nargs <= ARGS_MAX)
1506 esc_args[esc_nargs - 1] = ARG_DEFAULT;
1507 termstate = SEEN_CSI;
1508 } else if (c < '@') {
1515 termstate = SEEN_CSI;
1517 switch (ANSI(c, esc_query)) {
1518 case 'A': /* move up N lines */
1519 move(curs.x, curs.y - def(esc_args[0], 1), 1);
1520 seen_disp_event = TRUE;
1522 case 'e': /* move down N lines */
1523 compatibility(ANSI);
1525 move(curs.x, curs.y + def(esc_args[0], 1), 1);
1526 seen_disp_event = TRUE;
1528 case 'a': /* move right N cols */
1529 compatibility(ANSI);
1530 case ANSI('c', '>'): /* report xterm version */
1531 compatibility(OTHER);
1532 /* this reports xterm version 136 so that VIM can
1533 use the drag messages from the mouse reporting */
1534 ldisc_send("\033[>0;136;0c", 11);
1537 move(curs.x + def(esc_args[0], 1), curs.y, 1);
1538 seen_disp_event = TRUE;
1540 case 'D': /* move left N cols */
1541 move(curs.x - def(esc_args[0], 1), curs.y, 1);
1542 seen_disp_event = TRUE;
1544 case 'E': /* move down N lines and CR */
1545 compatibility(ANSI);
1546 move(0, curs.y + def(esc_args[0], 1), 1);
1547 seen_disp_event = TRUE;
1549 case 'F': /* move up N lines and CR */
1550 compatibility(ANSI);
1551 move(0, curs.y - def(esc_args[0], 1), 1);
1552 seen_disp_event = TRUE;
1555 case '`': /* set horizontal posn */
1556 compatibility(ANSI);
1557 move(def(esc_args[0], 1) - 1, curs.y, 0);
1558 seen_disp_event = TRUE;
1560 case 'd': /* set vertical posn */
1561 compatibility(ANSI);
1563 (dec_om ? marg_t : 0) + def(esc_args[0],
1566 seen_disp_event = TRUE;
1569 case 'f': /* set horz and vert posns at once */
1571 esc_args[1] = ARG_DEFAULT;
1572 move(def(esc_args[1], 1) - 1,
1573 (dec_om ? marg_t : 0) + def(esc_args[0],
1576 seen_disp_event = TRUE;
1578 case 'J': /* erase screen or parts of it */
1580 unsigned int i = def(esc_args[0], 0) + 1;
1583 erase_lots(FALSE, !!(i & 2), !!(i & 1));
1586 seen_disp_event = TRUE;
1588 case 'K': /* erase line or parts of it */
1590 unsigned int i = def(esc_args[0], 0) + 1;
1593 erase_lots(TRUE, !!(i & 2), !!(i & 1));
1595 seen_disp_event = TRUE;
1597 case 'L': /* insert lines */
1598 compatibility(VT102);
1599 if (curs.y <= marg_b)
1600 scroll(curs.y, marg_b, -def(esc_args[0], 1),
1603 seen_disp_event = TRUE;
1605 case 'M': /* delete lines */
1606 compatibility(VT102);
1607 if (curs.y <= marg_b)
1608 scroll(curs.y, marg_b, def(esc_args[0], 1),
1611 seen_disp_event = TRUE;
1613 case '@': /* insert chars */
1614 /* XXX VTTEST says this is vt220, vt510 manual says vt102 */
1615 compatibility(VT102);
1616 insch(def(esc_args[0], 1));
1617 seen_disp_event = TRUE;
1619 case 'P': /* delete chars */
1620 compatibility(VT102);
1621 insch(-def(esc_args[0], 1));
1622 seen_disp_event = TRUE;
1624 case 'c': /* terminal type query */
1625 compatibility(VT100);
1626 /* This is the response for a VT102 */
1627 ldisc_send(id_string, strlen(id_string));
1629 case 'n': /* cursor position query */
1630 if (esc_args[0] == 6) {
1632 sprintf(buf, "\033[%d;%dR", curs.y + 1,
1634 ldisc_send(buf, strlen(buf));
1635 } else if (esc_args[0] == 5) {
1636 ldisc_send("\033[0n", 4);
1639 case 'h': /* toggle modes to high */
1641 compatibility(VT100);
1644 for (i = 0; i < esc_nargs; i++)
1645 toggle_mode(esc_args[i], esc_query, TRUE);
1648 case 'l': /* toggle modes to low */
1650 compatibility(VT100);
1653 for (i = 0; i < esc_nargs; i++)
1654 toggle_mode(esc_args[i], esc_query, FALSE);
1657 case 'g': /* clear tabs */
1658 compatibility(VT100);
1659 if (esc_nargs == 1) {
1660 if (esc_args[0] == 0) {
1661 tabs[curs.x] = FALSE;
1662 } else if (esc_args[0] == 3) {
1664 for (i = 0; i < cols; i++)
1669 case 'r': /* set scroll margins */
1670 compatibility(VT100);
1671 if (esc_nargs <= 2) {
1673 top = def(esc_args[0], 1) - 1;
1674 bot = (esc_nargs <= 1
1676 0 ? rows : def(esc_args[1], rows)) - 1;
1679 /* VTTEST Bug 9 - if region is less than 2 lines
1680 * don't change region.
1682 if (bot - top > 0) {
1687 * I used to think the cursor should be
1688 * placed at the top of the newly marginned
1689 * area. Apparently not: VMS TPU falls over
1692 * Well actually it should for Origin mode - RDB
1694 curs.y = (dec_om ? marg_t : 0);
1696 seen_disp_event = TRUE;
1700 case 'm': /* set graphics rendition */
1703 * A VT100 without the AVO only had one attribute, either
1704 * underline or reverse video depending on the cursor type,
1705 * this was selected by CSI 7m.
1708 * This is sometimes DIM, eg on the GIGI and Linux
1710 * This is sometimes INVIS various ANSI.
1712 * This like 22 disables BOLD, DIM and INVIS
1714 * The ANSI colours appear on any terminal that has colour
1715 * (obviously) but the interaction between sgr0 and the
1716 * colours varies but is usually related to the background
1717 * colour erase item.
1718 * The interaction between colour attributes and the mono
1719 * ones is also very implementation dependent.
1721 * The 39 and 49 attributes are likely to be unimplemented.
1724 for (i = 0; i < esc_nargs; i++) {
1725 switch (def(esc_args[i], 0)) {
1726 case 0: /* restore defaults */
1727 curr_attr = ATTR_DEFAULT;
1729 case 1: /* enable bold */
1730 compatibility(VT100AVO);
1731 curr_attr |= ATTR_BOLD;
1733 case 21: /* (enable double underline) */
1734 compatibility(OTHER);
1735 case 4: /* enable underline */
1736 compatibility(VT100AVO);
1737 curr_attr |= ATTR_UNDER;
1739 case 5: /* enable blink */
1740 compatibility(VT100AVO);
1741 curr_attr |= ATTR_BLINK;
1743 case 7: /* enable reverse video */
1744 curr_attr |= ATTR_REVERSE;
1746 case 22: /* disable bold */
1747 compatibility2(OTHER, VT220);
1748 curr_attr &= ~ATTR_BOLD;
1750 case 24: /* disable underline */
1751 compatibility2(OTHER, VT220);
1752 curr_attr &= ~ATTR_UNDER;
1754 case 25: /* disable blink */
1755 compatibility2(OTHER, VT220);
1756 curr_attr &= ~ATTR_BLINK;
1758 case 27: /* disable reverse video */
1759 compatibility2(OTHER, VT220);
1760 curr_attr &= ~ATTR_REVERSE;
1771 curr_attr &= ~ATTR_FGMASK;
1773 (esc_args[i] - 30) << ATTR_FGSHIFT;
1775 case 39: /* default-foreground */
1776 curr_attr &= ~ATTR_FGMASK;
1777 curr_attr |= ATTR_DEFFG;
1788 curr_attr &= ~ATTR_BGMASK;
1790 (esc_args[i] - 40) << ATTR_BGSHIFT;
1792 case 49: /* default-background */
1793 curr_attr &= ~ATTR_BGMASK;
1794 curr_attr |= ATTR_DEFBG;
1802 (ATTR_FGMASK | ATTR_BGMASK |
1806 case 's': /* save cursor */
1809 case 'u': /* restore cursor */
1811 seen_disp_event = TRUE;
1813 case 't': /* set page size - ie window height */
1815 * VT340/VT420 sequence DECSLPP, DEC only allows values
1816 * 24/25/36/48/72/144 other emulators (eg dtterm) use
1817 * illegal values (eg first arg 1..9) for window changing
1820 compatibility(VT340TEXT);
1822 && (esc_args[0] < 1 || esc_args[0] >= 24)) {
1823 request_resize(cols, def(esc_args[0], 24), 0);
1828 compatibility(SCOANSI);
1829 scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
1832 seen_disp_event = TRUE;
1835 compatibility(SCOANSI);
1836 scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
1839 seen_disp_event = TRUE;
1841 case ANSI('|', '*'):
1842 /* VT420 sequence DECSNLS
1843 * Set number of lines on screen
1844 * VT420 uses VGA like hardware and can support any size in
1845 * reasonable range (24..49 AIUI) with no default specified.
1847 compatibility(VT420);
1848 if (esc_nargs == 1 && esc_args[0] > 0) {
1849 request_resize(cols,
1850 def(esc_args[0], cfg.height),
1855 case ANSI('|', '$'):
1856 /* VT340/VT420 sequence DECSCPP
1857 * Set number of columns per page
1858 * Docs imply range is only 80 or 132, but I'll allow any.
1860 compatibility(VT340TEXT);
1861 if (esc_nargs <= 1) {
1862 request_resize(def(esc_args[0], cfg.width),
1867 case 'X': /* write N spaces w/o moving cursor */
1868 /* XXX VTTEST says this is vt220, vt510 manual says vt100 */
1869 compatibility(ANSIMIN);
1871 int n = def(esc_args[0], 1);
1873 unsigned long *p = cpos;
1874 if (n > cols - curs.x)
1878 check_selection(curs, cursplus);
1881 seen_disp_event = TRUE;
1884 case 'x': /* report terminal characteristics */
1885 compatibility(VT100);
1888 int i = def(esc_args[0], 0);
1889 if (i == 0 || i == 1) {
1890 strcpy(buf, "\033[2;1;1;112;112;1;0x");
1892 ldisc_send(buf, 20);
1896 case ANSI('L', '='):
1897 compatibility(OTHER);
1898 use_bce = (esc_args[0] <= 0);
1899 erase_char = ERASE_CHAR;
1904 (ATTR_FGMASK | ATTR_BGMASK)));
1906 case ANSI('E', '='):
1907 compatibility(OTHER);
1908 blink_is_real = (esc_args[0] >= 1);
1910 case ANSI('p', '"'):
1911 /* Allow the host to make this emulator a 'perfect' VT102.
1912 * This first appeared in the VT220, but we do need to get
1913 * back to PuTTY mode so I won't check it.
1915 * The arg in 40..42,50 are a PuTTY extension.
1916 * The 2nd arg, 8bit vs 7bit is not checked.
1918 * Setting VT102 mode should also change the Fkeys to
1919 * generate PF* codes as a real VT102 has no Fkeys.
1920 * The VT220 does this, F11..F13 become ESC,BS,LF other Fkeys
1923 * Note ESC c will NOT change this!
1926 switch (esc_args[0]) {
1928 compatibility_level &= ~TM_VTXXX;
1929 compatibility_level |= TM_VT102;
1932 compatibility_level &= ~TM_VTXXX;
1933 compatibility_level |= TM_VT220;
1937 if (esc_args[0] > 60 && esc_args[0] < 70)
1938 compatibility_level |= TM_VTXXX;
1942 compatibility_level &= TM_VTXXX;
1945 compatibility_level = TM_PUTTY;
1948 compatibility_level = TM_SCOANSI;
1952 compatibility_level = TM_PUTTY;
1958 /* Change the response to CSI c */
1959 if (esc_args[0] == 50) {
1962 strcpy(id_string, "\033[?");
1963 for (i = 1; i < esc_nargs; i++) {
1965 strcat(id_string, ";");
1966 sprintf(lbuf, "%d", esc_args[i]);
1967 strcat(id_string, lbuf);
1969 strcat(id_string, "c");
1972 /* Is this a good idea ?
1973 * Well we should do a soft reset at this point ...
1975 if (!has_compat(VT420) && has_compat(VT100)) {
1977 request_resize(132, 24, 1);
1979 request_resize(80, 24, 1);
1988 case 'P': /* Linux palette sequence */
1989 termstate = SEEN_OSC_P;
1992 case 'R': /* Linux palette reset */
1995 termstate = TOPLEVEL;
1997 case 'W': /* word-set */
1998 termstate = SEEN_OSC_W;
2011 esc_args[0] = 10 * esc_args[0] + c - '0';
2015 * Grotty hack to support xterm and DECterm title
2016 * sequences concurrently.
2018 if (esc_args[0] == 2) {
2022 /* else fall through */
2024 termstate = OSC_STRING;
2030 * This OSC stuff is EVIL. It takes just one character to get into
2031 * sysline mode and it's not initially obvious how to get out.
2032 * So I've added CR and LF as string aborts.
2033 * This shouldn't effect compatibility as I believe embedded
2034 * control characters are supposed to be interpreted (maybe?)
2035 * and they don't display anything useful anyway.
2039 if (c == '\n' || c == '\r') {
2040 termstate = TOPLEVEL;
2041 } else if (c == 0234 || c == '\007') {
2043 * These characters terminate the string; ST and BEL
2044 * terminate the sequence and trigger instant
2045 * processing of it, whereas ESC goes back to SEEN_ESC
2046 * mode unless it is followed by \, in which case it is
2047 * synonymous with ST in the first place.
2050 termstate = TOPLEVEL;
2051 } else if (c == '\033')
2052 termstate = OSC_MAYBE_ST;
2053 else if (osc_strlen < OSC_STR_MAX)
2054 osc_string[osc_strlen++] = c;
2058 int max = (osc_strlen == 0 ? 21 : 16);
2060 if (c >= '0' && c <= '9')
2062 else if (c >= 'A' && c <= 'A' + max - 10)
2064 else if (c >= 'a' && c <= 'a' + max - 10)
2067 termstate = TOPLEVEL;
2070 osc_string[osc_strlen++] = val;
2071 if (osc_strlen >= 7) {
2072 palette_set(osc_string[0],
2073 osc_string[1] * 16 + osc_string[2],
2074 osc_string[3] * 16 + osc_string[4],
2075 osc_string[5] * 16 + osc_string[6]);
2077 termstate = TOPLEVEL;
2093 esc_args[0] = 10 * esc_args[0] + c - '0';
2096 termstate = OSC_STRING;
2101 termstate = TOPLEVEL;
2102 seen_disp_event = TRUE;
2105 move(curs.x, curs.y - 1, 1);
2108 move(curs.x, curs.y + 1, 1);
2111 move(curs.x + 1, curs.y, 1);
2114 move(curs.x - 1, curs.y, 1);
2117 * From the VT100 Manual
2118 * NOTE: The special graphics characters in the VT100
2119 * are different from those in the VT52
2121 * From VT102 manual:
2122 * 137 _ Blank - Same
2123 * 140 ` Reserved - Humm.
2124 * 141 a Solid rectangle - Similar
2125 * 142 b 1/ - Top half of fraction for the
2126 * 143 c 3/ - subscript numbers below.
2129 * 146 f Degrees - Same
2130 * 147 g Plus or minus - Same
2132 * 151 i Ellipsis (dots)
2135 * 154 l Bar at scan 0
2136 * 155 m Bar at scan 1
2137 * 156 n Bar at scan 2
2138 * 157 o Bar at scan 3 - Similar
2139 * 160 p Bar at scan 4 - Similar
2140 * 161 q Bar at scan 5 - Similar
2141 * 162 r Bar at scan 6 - Same
2142 * 163 s Bar at scan 7 - Similar
2157 cset_attr[cset = 0] = ATTR_LINEDRW;
2160 cset_attr[cset = 0] = ATTR_ASCII;
2167 scroll(0, rows - 1, -1, TRUE);
2168 else if (curs.y > 0)
2174 erase_lots(FALSE, FALSE, TRUE);
2178 erase_lots(TRUE, FALSE, TRUE);
2182 /* XXX Print cursor line */
2185 /* XXX Start controller mode */
2188 /* XXX Stop controller mode */
2192 termstate = VT52_Y1;
2195 ldisc_send("\033/Z", 3);
2198 app_keypad_keys = TRUE;
2201 app_keypad_keys = FALSE;
2204 /* XXX This should switch to VT100 mode not current or default
2205 * VT mode. But this will only have effect in a VT220+
2209 blink_is_real = cfg.blinktext;
2213 /* XXX Enter auto print mode */
2216 /* XXX Exit auto print mode */
2219 /* XXX Print screen */
2225 /* compatibility(ATARI) */
2227 erase_lots(FALSE, FALSE, TRUE);
2231 /* compatibility(ATARI) */
2232 if (curs.y <= marg_b)
2233 scroll(curs.y, marg_b, -1, FALSE);
2236 /* compatibility(ATARI) */
2237 if (curs.y <= marg_b)
2238 scroll(curs.y, marg_b, 1, TRUE);
2241 /* compatibility(ATARI) */
2242 termstate = VT52_FG;
2245 /* compatibility(ATARI) */
2246 termstate = VT52_BG;
2249 /* compatibility(ATARI) */
2250 erase_lots(FALSE, TRUE, FALSE);
2254 /* compatibility(ATARI) */
2258 /* compatibility(ATARI) */
2261 /* case 'j': Save cursor position - broken on ST */
2262 /* case 'k': Restore cursor position */
2264 /* compatibility(ATARI) */
2265 erase_lots(TRUE, TRUE, TRUE);
2271 /* compatibility(ATARI) */
2272 erase_lots(TRUE, TRUE, FALSE);
2275 /* compatibility(ATARI) */
2276 curr_attr |= ATTR_REVERSE;
2279 /* compatibility(ATARI) */
2280 curr_attr &= ~ATTR_REVERSE;
2282 case 'v': /* wrap Autowrap on - Wyse style */
2283 /* compatibility(ATARI) */
2286 case 'w': /* Autowrap off */
2287 /* compatibility(ATARI) */
2292 /* compatibility(OTHER) */
2294 curr_attr = ATTR_DEFAULT;
2298 (ATTR_FGMASK | ATTR_BGMASK |
2302 /* compatibility(VI50) */
2303 curr_attr |= ATTR_UNDER;
2306 /* compatibility(VI50) */
2307 curr_attr &= ~ATTR_UNDER;
2310 /* compatibility(VI50) */
2312 curr_attr |= ATTR_BOLD;
2315 /* compatibility(VI50) */
2317 curr_attr &= ~ATTR_BOLD;
2323 termstate = VT52_Y2;
2324 move(curs.x, c - ' ', 0);
2327 termstate = TOPLEVEL;
2328 move(c - ' ', curs.y, 0);
2333 termstate = TOPLEVEL;
2334 curr_attr &= ~ATTR_FGMASK;
2335 curr_attr &= ~ATTR_BOLD;
2336 curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
2337 if ((c & 0x8) || vt52_bold)
2338 curr_attr |= ATTR_BOLD;
2343 (ATTR_FGMASK | ATTR_BGMASK |
2347 termstate = TOPLEVEL;
2348 curr_attr &= ~ATTR_BGMASK;
2349 curr_attr &= ~ATTR_BLINK;
2350 curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
2352 /* Note: bold background */
2354 curr_attr |= ATTR_BLINK;
2359 (ATTR_FGMASK | ATTR_BGMASK |
2363 default: break; /* placate gcc warning about enum use */
2365 if (selstate != NO_SELECTION) {
2366 pos cursplus = curs;
2368 check_selection(curs, cursplus);
2376 * Compare two lines to determine whether they are sufficiently
2377 * alike to scroll-optimise one to the other. Return the degree of
2380 static int linecmp(unsigned long *a, unsigned long *b)
2384 for (i = n = 0; i < cols; i++)
2385 n += (*a++ == *b++);
2391 * Given a context, update the window. Out of paranoia, we don't
2392 * allow WM_PAINT responses to do scrolling optimisations.
2394 static void do_paint(Context ctx, int may_optimise)
2396 int i, j, our_curs_y;
2397 unsigned long rv, cursor;
2400 long cursor_background = ERASE_CHAR;
2404 * Check the visual bell state.
2407 ticks = GetTickCount();
2408 if (ticks - vbell_timeout >= 0)
2412 rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
2415 * screen array, disptop, scrtop,
2417 * cfg.blinkpc, blink_is_real, tblinker,
2418 * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
2421 /* Has the cursor position or type changed ? */
2424 if (blinker || !cfg.blink_cur)
2425 cursor = TATTR_ACTCURS;
2429 cursor = TATTR_PASCURS;
2431 cursor |= TATTR_RIGHTCURS;
2434 our_curs_y = curs.y - disptop;
2436 if (dispcurs && (curstype != cursor ||
2438 disptext + our_curs_y * (cols + 1) + curs.x)) {
2439 if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
2440 dispcurs[-1] |= ATTR_INVALID;
2441 if ((*dispcurs & ATTR_WIDE))
2442 dispcurs[1] |= ATTR_INVALID;
2443 *dispcurs |= ATTR_INVALID;
2448 /* The normal screen data */
2449 for (i = 0; i < rows; i++) {
2450 unsigned long *ldata;
2452 int idx, dirty_line, dirty_run;
2453 unsigned long attr = 0;
2454 int updated_line = 0;
2457 int last_run_dirty = 0;
2459 scrpos.y = i + disptop;
2460 ldata = lineptr(scrpos.y);
2461 lattr = (ldata[cols] & LATTR_MODE);
2463 idx = i * (cols + 1);
2464 dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
2465 disptext[idx + cols] = ldata[cols];
2467 for (j = 0; j < cols; j++, idx++) {
2468 unsigned long tattr, tchar;
2469 unsigned long *d = ldata + j;
2473 tchar = (*d & (CHAR_MASK | CSET_MASK));
2474 tattr = (*d & (ATTR_MASK ^ CSET_MASK));
2475 switch (tchar & CSET_MASK) {
2477 tchar = unitab_line[tchar & 0xFF];
2480 tchar = unitab_xterm[tchar & 0xFF];
2483 tattr |= (tchar & CSET_MASK);
2486 /* Video reversing things */
2488 ^ (posle(selstart, scrpos) &&
2489 poslt(scrpos, selend) ? ATTR_REVERSE : 0));
2491 /* 'Real' blinking ? */
2492 if (blink_is_real && (tattr & ATTR_BLINK)) {
2493 if (has_focus && tblinker) {
2495 tattr &= ~CSET_MASK;
2498 tattr &= ~ATTR_BLINK;
2501 /* Cursor here ? Save the 'background' */
2502 if (i == our_curs_y && j == curs.x) {
2503 cursor_background = tattr | tchar;
2504 dispcurs = disptext + idx;
2507 if ((disptext[idx] ^ tattr) & ATTR_WIDE)
2510 break_run = (tattr != attr || j - start >= sizeof(ch));
2512 /* Special hack for VT100 Linedraw glyphs */
2513 if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
2514 && tchar <= 0xBD) break_run = TRUE;
2516 if (!dbcs_screenfont && !dirty_line) {
2517 if ((tchar | tattr) == disptext[idx])
2519 else if (!dirty_run && ccount == 1)
2524 if ((dirty_run || last_run_dirty) && ccount > 0) {
2525 do_text(ctx, start, i, ch, ccount, attr, lattr);
2531 if (dbcs_screenfont)
2532 last_run_dirty = dirty_run;
2533 dirty_run = dirty_line;
2536 if ((tchar | tattr) != disptext[idx])
2538 ch[ccount++] = (char) tchar;
2539 disptext[idx] = tchar | tattr;
2541 /* If it's a wide char step along to the next one. */
2542 if (tattr & ATTR_WIDE) {
2546 /* Cursor is here ? Ouch! */
2547 if (i == our_curs_y && j == curs.x) {
2548 cursor_background = *d;
2549 dispcurs = disptext + idx;
2551 if (disptext[idx] != *d)
2557 if (dirty_run && ccount > 0) {
2558 do_text(ctx, start, i, ch, ccount, attr, lattr);
2562 /* Cursor on this line ? (and changed) */
2563 if (i == our_curs_y && (curstype != cursor || updated_line)) {
2564 ch[0] = (char) (cursor_background & CHAR_MASK);
2565 attr = (cursor_background & ATTR_MASK) | cursor;
2566 do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
2573 * Flick the switch that says if blinking things should be shown or hidden.
2576 void term_blink(int flg)
2578 static long last_blink = 0;
2579 static long last_tblink = 0;
2580 long now, blink_diff;
2582 now = GetTickCount();
2583 blink_diff = now - last_tblink;
2585 /* Make sure the text blinks no more than 2Hz */
2586 if (blink_diff < 0 || blink_diff > 450) {
2588 tblinker = !tblinker;
2597 blink_diff = now - last_blink;
2599 /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
2600 if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
2608 * Invalidate the whole screen so it will be repainted in full.
2610 void term_invalidate(void)
2614 for (i = 0; i < rows * (cols + 1); i++)
2615 disptext[i] = ATTR_INVALID;
2619 * Paint the window in response to a WM_PAINT message.
2621 void term_paint(Context ctx, int l, int t, int r, int b)
2623 int i, j, left, top, right, bottom;
2625 left = l / font_width;
2626 right = (r - 1) / font_width;
2627 top = t / font_height;
2628 bottom = (b - 1) / font_height;
2629 for (i = top; i <= bottom && i < rows; i++) {
2630 if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
2631 for (j = left; j <= right && j < cols; j++)
2632 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2634 for (j = left / 2; j <= right / 2 + 1 && j < cols; j++)
2635 disptext[i * (cols + 1) + j] = ATTR_INVALID;
2638 /* This should happen soon enough, also for some reason it sometimes
2639 * fails to actually do anything when re-sizing ... painting the wrong
2641 do_paint (ctx, FALSE);
2646 * Attempt to scroll the scrollback. The second parameter gives the
2647 * position we want to scroll to; the first is +1 to denote that
2648 * this position is relative to the beginning of the scrollback, -1
2649 * to denote it is relative to the end, and 0 to denote that it is
2650 * relative to the current position.
2652 void term_scroll(int rel, int where)
2654 int sbtop = -count234(scrollback);
2656 disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : disptop) + where;
2657 if (disptop < sbtop)
2665 static void clipme(pos top, pos bottom)
2668 wchar_t *wbptr; /* where next char goes within workbuf */
2669 int wblen = 0; /* workbuf len */
2670 int buflen; /* amount of memory allocated to workbuf */
2672 buflen = 5120; /* Default size */
2673 workbuf = smalloc(buflen * sizeof(wchar_t));
2674 wbptr = workbuf; /* start filling here */
2676 while (poslt(top, bottom)) {
2678 unsigned long *ldata = lineptr(top.y);
2684 if (!(ldata[cols] & LATTR_WRAPPED)) {
2685 while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
2686 (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
2687 (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
2688 && poslt(top, nlpos))
2690 if (poslt(nlpos, bottom))
2693 while (poslt(top, bottom) && poslt(top, nlpos)) {
2696 sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
2698 wchar_t cbuf[16], *p;
2699 int uc = (ldata[top.x] & 0xFFFF);
2702 if (uc == UCSWIDE) {
2707 switch (uc & CSET_MASK) {
2710 uc = unitab_xterm[uc & 0xFF];
2714 uc = unitab_line[uc & 0xFF];
2717 switch (uc & CSET_MASK) {
2719 uc = unitab_font[uc & 0xFF];
2722 uc = unitab_oemcp[uc & 0xFF];
2726 set = (uc & CSET_MASK);
2727 c = (uc & CHAR_MASK);
2731 if (DIRECT_FONT(uc)) {
2732 if (c >= ' ' && c != 0x7F) {
2733 unsigned char buf[4];
2736 if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
2738 buf[1] = (unsigned char) ldata[top.x + 1];
2739 rv = MultiByteToWideChar(font_codepage,
2740 0, buf, 2, wbuf, 4);
2744 rv = MultiByteToWideChar(font_codepage,
2745 0, buf, 1, wbuf, 4);
2749 memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
2756 for (p = cbuf; *p; p++) {
2757 /* Enough overhead for trailing NL and nul */
2758 if (wblen >= buflen - 16) {
2761 sizeof(wchar_t) * (buflen += 100));
2762 wbptr = workbuf + wblen;
2771 for (i = 0; i < sel_nl_sz; i++) {
2773 *wbptr++ = sel_nl[i];
2781 write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
2782 if (buflen > 0) /* indicates we allocated this buffer */
2786 void term_copyall(void)
2789 top.y = -count234(scrollback);
2795 * The wordness array is mainly for deciding the disposition of the US-ASCII
2798 static int wordtype(int uc)
2801 int start, end, ctype;
2802 } *wptr, ucs_words[] = {
2808 0x037e, 0x037e, 1}, /* Greek question mark */
2810 0x0387, 0x0387, 1}, /* Greek ano teleia */
2812 0x055a, 0x055f, 1}, /* Armenian punctuation */
2814 0x0589, 0x0589, 1}, /* Armenian full stop */
2816 0x0700, 0x070d, 1}, /* Syriac punctuation */
2818 0x104a, 0x104f, 1}, /* Myanmar punctuation */
2820 0x10fb, 0x10fb, 1}, /* Georgian punctuation */
2822 0x1361, 0x1368, 1}, /* Ethiopic punctuation */
2824 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */
2826 0x17d4, 0x17dc, 1}, /* Khmer punctuation */
2828 0x1800, 0x180a, 1}, /* Mongolian punctuation */
2830 0x2000, 0x200a, 0}, /* Various spaces */
2832 0x2070, 0x207f, 2}, /* superscript */
2834 0x2080, 0x208f, 2}, /* subscript */
2836 0x200b, 0x27ff, 1}, /* punctuation and symbols */
2838 0x3000, 0x3000, 0}, /* ideographic space */
2840 0x3001, 0x3020, 1}, /* ideographic punctuation */
2842 0x303f, 0x309f, 3}, /* Hiragana */
2844 0x30a0, 0x30ff, 3}, /* Katakana */
2846 0x3300, 0x9fff, 3}, /* CJK Ideographs */
2848 0xac00, 0xd7a3, 3}, /* Hangul Syllables */
2850 0xf900, 0xfaff, 3}, /* CJK Ideographs */
2852 0xfe30, 0xfe6b, 1}, /* punctuation forms */
2854 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */
2856 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */
2858 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */
2860 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */
2862 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */
2867 uc &= (CSET_MASK | CHAR_MASK);
2869 switch (uc & CSET_MASK) {
2871 uc = unitab_xterm[uc & 0xFF];
2874 uc = unitab_line[uc & 0xFF];
2877 switch (uc & CSET_MASK) {
2879 uc = unitab_font[uc & 0xFF];
2882 uc = unitab_oemcp[uc & 0xFF];
2887 return wordness[uc];
2889 for (wptr = ucs_words; wptr->start; wptr++) {
2890 if (uc >= wptr->start && uc <= wptr->end)
2898 * Spread the selection outwards according to the selection mode.
2900 static pos sel_spread_half(pos p, int dir)
2902 unsigned long *ldata;
2905 ldata = lineptr(p.y);
2910 * In this mode, every character is a separate unit, except
2911 * for runs of spaces at the end of a non-wrapping line.
2913 if (!(ldata[cols] & LATTR_WRAPPED)) {
2914 unsigned long *q = ldata + cols;
2915 while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
2917 if (q == ldata + cols)
2919 if (p.x >= q - ldata)
2920 p.x = (dir == -1 ? q - ldata : cols - 1);
2925 * In this mode, the units are maximal runs of characters
2926 * whose `wordness' has the same value.
2928 wvalue = wordtype(ldata[p.x]);
2930 while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
2933 while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
2939 * In this mode, every line is a unit.
2941 p.x = (dir == -1 ? 0 : cols - 1);
2947 static void sel_spread(void)
2949 selstart = sel_spread_half(selstart, -1);
2951 selend = sel_spread_half(selend, +1);
2955 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
2956 int shift, int ctrl)
2959 unsigned long *ldata;
2975 selpoint.y = y + disptop;
2977 ldata = lineptr(selpoint.y);
2978 if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
2982 int encstate = 0, r, c;
2984 static int is_down = 0;
2988 encstate = 0x20; /* left button down */
2999 case MBT_WHEEL_DOWN:
3002 default: break; /* placate gcc warning about enum use */
3006 if (xterm_mouse == 1)
3019 default: break; /* placate gcc warning about enum use */
3028 sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
3029 ldisc_send(abuf, 6);
3033 b = translate_button(b);
3035 if (b == MBT_SELECT && a == MA_CLICK) {
3037 selstate = ABOUT_TO;
3038 selanchor = selpoint;
3040 } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
3042 selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
3043 selstate = DRAGGING;
3044 selstart = selanchor = selpoint;
3048 } else if ((b == MBT_SELECT && a == MA_DRAG) ||
3049 (b == MBT_EXTEND && a != MA_RELEASE)) {
3050 if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
3052 if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
3053 if (posdiff(selpoint, selstart) <
3054 posdiff(selend, selstart) / 2) {
3058 selanchor = selstart;
3060 selstate = DRAGGING;
3062 if (selstate != ABOUT_TO && selstate != DRAGGING)
3063 selanchor = selpoint;
3064 selstate = DRAGGING;
3065 if (poslt(selpoint, selanchor)) {
3066 selstart = selpoint;
3070 selstart = selanchor;
3075 } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
3076 if (selstate == DRAGGING) {
3078 * We've completed a selection. We now transfer the
3079 * data to the clipboard.
3081 clipme(selstart, selend);
3082 selstate = SELECTED;
3084 selstate = NO_SELECTION;
3085 } else if (b == MBT_PASTE
3086 && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
3090 get_clip(&data, &len);
3095 sfree(paste_buffer);
3096 paste_pos = paste_hold = paste_len = 0;
3097 paste_buffer = smalloc(len * sizeof(wchar_t));
3100 while (p < data + len) {
3101 while (p < data + len &&
3102 !(p <= data + len - sel_nl_sz &&
3103 !memcmp(p, sel_nl, sizeof(sel_nl))))
3108 for (i = 0; i < p - q; i++) {
3109 paste_buffer[paste_len++] = q[i];
3113 if (p <= data + len - sel_nl_sz &&
3114 !memcmp(p, sel_nl, sizeof(sel_nl))) {
3115 paste_buffer[paste_len++] = '\r';
3121 /* Assume a small paste will be OK in one go. */
3122 if (paste_len < 256) {
3123 luni_send(paste_buffer, paste_len);
3125 sfree(paste_buffer);
3127 paste_pos = paste_hold = paste_len = 0;
3130 get_clip(NULL, NULL);
3140 sfree(paste_buffer);
3147 static long last_paste = 0;
3148 long now, paste_diff;
3153 /* Don't wait forever to paste */
3155 now = GetTickCount();
3156 paste_diff = now - last_paste;
3157 if (paste_diff >= 0 && paste_diff < 450)
3162 while (paste_pos < paste_len) {
3164 while (n + paste_pos < paste_len) {
3165 if (paste_buffer[paste_pos + n++] == '\r')
3168 luni_send(paste_buffer + paste_pos, n);
3171 if (paste_pos < paste_len) {
3176 sfree(paste_buffer);
3181 static void deselect(void)
3183 selstate = NO_SELECTION;
3184 selstart.x = selstart.y = selend.x = selend.y = 0;
3187 void term_deselect(void)
3193 int term_ldisc(int option)
3195 if (option == LD_ECHO)
3196 return term_echoing;
3197 if (option == LD_EDIT)
3198 return term_editing;
3203 * from_backend(), to get data from the backend for the terminal.
3205 void from_backend(int is_stderr, char *data, int len)
3208 if (inbuf_head >= INBUF_SIZE)
3210 inbuf[inbuf_head++] = *data++;
3215 * Log session traffic.
3217 void logtraffic(unsigned char c, int logmode)
3219 if (cfg.logtype > 0) {
3220 if (cfg.logtype == logmode) {
3221 /* deferred open file from pgm start? */
3230 /* open log file append/overwrite mode */
3240 sprintf(writemod, "wb"); /* default to rewrite */
3241 lgfp = fopen(cfg.logfilename, "r"); /* file already present? */
3245 i = askappend(cfg.logfilename);
3247 writemod[0] = 'a'; /* set append mode */
3248 else if (i == 0) { /* cancelled */
3250 cfg.logtype = 0; /* disable logging */
3255 lgfp = fopen(cfg.logfilename, writemod);
3256 if (lgfp) { /* enter into event log */
3257 sprintf(buf, "%s session log (%s mode) to file : ",
3258 (writemod[0] == 'a') ? "Appending" : "Writing new",
3259 (cfg.logtype == LGTYP_ASCII ? "ASCII" :
3260 cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
3261 /* Make sure we do not exceed the output buffer size */
3262 strncat(buf, cfg.logfilename, 128);
3263 buf[strlen(buf)] = '\0';
3266 /* --- write header line iinto log file */
3267 fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
3270 strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
3272 fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
3276 void logfclose(void)