1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "vt_parser.h"
4 
5 #include <stdio.h>    /* sprintf */
6 #include <string.h>   /* memmove */
7 #include <stdlib.h>   /* atoi */
8 #include <fcntl.h>    /* open */
9 #include <unistd.h>   /* write/getcwd */
10 #include <sys/time.h> /* gettimeofday */
11 #include <time.h>     /* clock */
12 #include <ctype.h>    /* iscntrl */
13 #ifdef DEBUG
14 #include <stdarg.h> /* va_list */
15 #endif
16 
17 #if defined(USE_LIBSSH2) && !defined(USE_WIN32API)
18 /*
19  * <wchar.h> has already been included without _XOPEN_SOURCE,
20  * so #define _XOPEN_SOURCE here doesn't work.
21  */
22 #if 0
23 #define _XOPEN_SOURCE
24 #include <wchar.h> /* wcwidth */
25 #else
26 int wcwidth(wchar_t c);
27 #endif
28 #endif
29 
30 #include <pobl/bl_debug.h>
31 #include <pobl/bl_mem.h>    /* malloc/free */
32 #include <pobl/bl_util.h>   /* DIGIT_STR_LEN */
33 #include <pobl/bl_conf_io.h>/* bl_get_user_rc_path */
34 #include <pobl/bl_str.h>    /* strdup */
35 #include <pobl/bl_args.h>
36 #include <pobl/bl_unistd.h>   /* bl_usleep */
37 #include <pobl/bl_locale.h>   /* bl_get_locale */
38 #include <mef/ef_ucs4_map.h> /* ef_map_to_ucs4 */
39 #include <mef/ef_ucs_property.h>
40 #include <mef/ef_locale_ucs4_map.h>
41 #include <mef/ef_ko_kr_map.h>
42 
43 #include "vt_iscii.h"
44 #include "vt_str_parser.h"
45 #include "vt_shape.h" /* vt_is_arabic_combining */
46 #include "vt_transfer.h"
47 
48 #if defined(__CYGWIN__) || defined(__MSYS__)
49 #include "cygfile.h"
50 #endif
51 
52 /*
53  * kterm BUF_SIZE in ptyx.h is 4096.
54  */
55 #define PTY_RD_BUFFER_SIZE 3072
56 
57 #define CTL_BEL 0x07
58 #define CTL_BS 0x08
59 #define CTL_TAB 0x09
60 #define CTL_LF 0x0a
61 #define CTL_VT 0x0b
62 #define CTL_FF 0x0c
63 #define CTL_CR 0x0d
64 #define CTL_SO 0x0e
65 #define CTL_SI 0x0f
66 #define CTL_ESC 0x1b
67 
68 #define CURRENT_STR_P(vt_parser) \
69   ((vt_parser)->r_buf.chars + (vt_parser)->r_buf.filled_len - (vt_parser)->r_buf.left)
70 
71 #define HAS_XTERM_LISTENER(vt_parser, method) \
72   ((vt_parser)->xterm_listener && ((vt_parser)->xterm_listener->method))
73 
74 #define HAS_CONFIG_LISTENER(vt_parser, method) \
75   ((vt_parser)->config_listener && ((vt_parser)->config_listener->method))
76 
77 #if 1
78 #define MAX_PS_DIGIT 0xffff
79 #endif
80 
81 #if 0
82 #define EDIT_DEBUG
83 #endif
84 
85 #if 0
86 #define EDIT_ROUGH_DEBUG
87 #endif
88 
89 #if 0
90 #define INPUT_DEBUG
91 #endif
92 
93 #if 0
94 #define ESCSEQ_DEBUG
95 #endif
96 
97 #if 0
98 #define OUTPUT_DEBUG
99 #endif
100 
101 #if 0
102 #define DUMP_HEX
103 #endif
104 
105 #if 0
106 #define SUPPORT_VTE_CJK_WIDTH
107 #endif
108 
109 #ifndef NO_IMAGE
110 #define SUPPORT_ITERM2_OSC1337
111 #endif
112 
113 /* mode must be less than 64 */
114 #define MOD32(mode) ((mode) >= 32 ? (mode) - 32 : (mode))
115 #define DIV32(mode) ((mode) >= 32) /* 1 or 0 */
116 #define SHIFT_FLAG_0(mode) ((mode) >= 32 ? 0 : (1 << (mode)))
117 #define SHIFT_FLAG_1(mode) ((mode) >= 32 ? (1 << ((mode) - 32)) : 0)
118 
119 /*
120  * The default of DECMODE_1010 is 'set' (scroll to bottom on tty output) on xterm,
121  * but is 'reset' on mlterm ("exit_backscroll_by_pty" option).
122  */
123 #define INITIAL_VTMODE_FLAGS_0 \
124   SHIFT_FLAG_0(DECMODE_2) | /* is_vt52_mode == 0 */ \
125   SHIFT_FLAG_0(DECMODE_7) | /* auto_wrap == 1 (compatible with xterm, not with VT220) */ \
126   SHIFT_FLAG_0(DECMODE_25) | /* is_visible_cursor == 1 */ \
127   SHIFT_FLAG_0(DECMODE_1034) | /* mod_meta_mode = 8bit (compatible with xterm) */ \
128   SHIFT_FLAG_0(VTMODE_12); /* local echo is false */
129 #define INITIAL_VTMODE_FLAGS_1 \
130   SHIFT_FLAG_1(DECMODE_2) | /* is_vt52_mode == 0 */ \
131   SHIFT_FLAG_1(DECMODE_7) | /* auto_wrap == 1 (compatible with xterm, not with VT220) */ \
132   SHIFT_FLAG_1(DECMODE_25) | /* is_visible_cursor == 1 */ \
133   SHIFT_FLAG_1(DECMODE_1034) | /* mod_meta_mode = 8bit (compatible with xterm) */ \
134   SHIFT_FLAG_1(VTMODE_12); /* local echo is false */
135 
136 #define SHIFT_FLAG(mode) (1 << MOD32(mode))
137 #define GET_VTMODE_FLAG(vt_parser, mode) \
138   ((vt_parser)->vtmode_flags[DIV32(mode)] & SHIFT_FLAG(mode))
139 /* returns 1 or 0 */
140 #define GET_VTMODE_FLAG2(vt_parser, mode) \
141   (((vt_parser)->vtmode_flags[DIV32(mode)] >> MOD32(mode)) & 1)
142 #define GET_SAVED_VTMODE_FLAG(vt_parser, mode) \
143   ((vt_parser)->saved_vtmode_flags[DIV32(mode)] & SHIFT_FLAG(mode))
144 /* returns 1 or 0 */
145 #define GET_SAVED_VTMODE_FLAG2(vt_parser, mode) \
146   (((vt_parser)->saved_vtmode_flags[DIV32(mode)] >> MOD32(mode)) & 1)
147 
148 #define IS_APP_CURSOR_KEYS(vt_parser) GET_VTMODE_FLAG(vt_parser, DECMODE_1)
149 #define ALLOW_DECCOLM(vt_parser) GET_VTMODE_FLAG(vt_parser, DECMODE_40)
150 #define KEEP_SCREEN_ON_DECCOLM(vt_parser) GET_VTMODE_FLAG(vt_parser, DECMODE_95)
151 #define BOLD_AFFECTS_BG(vt_parser) GET_VTMODE_FLAG(vt_parser, DECMODE_116)
152 #define IS_APP_ESCAPE(vt_parser) GET_VTMODE_FLAG(vt_parser, DECMODE_7727)
153 #define CURSOR_TO_RIGHT_OF_SIXEL(vt_parser) GET_VTMODE_FLAG(vt_parser, DECMODE_8452)
154 #define SEND_RECV_MODE(vt_parser) GET_VTMODE_FLAG(vt_parser, VTMODE_12)
155 #define AUTO_CR(vt_parser) GET_VTMODE_FLAG(vt_parser, VTMODE_20)
156 
157 #define VTMODE(mode) ((mode) + 10000)
158 
159 #define destroy_drcs(drcs) \
160   vt_drcs_destroy(drcs);   \
161   (drcs) = NULL;
162 
163 /*
164  * If VTMODE_NUM >= 64, enlarge the size of vt_parser_t::vtmode_flags.
165  * See get_initial_vtmode_flags() to check initial values of these modes.
166  */
167 typedef enum {
168   /* DECSET/DECRST */
169   /* vtmode_flags[0] */
170   DECMODE_1 = 0,
171   DECMODE_2,
172   DECMODE_3,
173   DECMODE_5,
174   DECMODE_6,
175   DECMODE_7,
176   DECMODE_25,
177   DECMODE_40,
178   DECMODE_47,
179   DECMODE_66,
180   DECMODE_67,
181   DECMODE_69,
182   DECMODE_80,
183   DECMODE_95,
184   DECMODE_116,
185   DECMODE_117,
186   DECMODE_1000,
187   DECMODE_1002, /* Don't add an entry between 1000 and 1002 (see set_vtmode() idx - DECMODE_1000) */
188   DECMODE_1003,
189   DECMODE_1004,
190   DECMODE_1005,
191   DECMODE_1006,
192   DECMODE_1015, /* Don't add an entry between 1005 and 1015 (see set_vtmode() idx - DECMODE_1005) */
193   DECMODE_1010,
194   DECMODE_1034,
195   DECMODE_1036,
196   DECMODE_1042,
197   DECMODE_1047,
198   DECMODE_1048,
199   DECMODE_1049,
200   DECMODE_2004,
201   DECMODE_7727,
202 
203   /* vtmode_flags[1] */
204   DECMODE_8428,
205   DECMODE_8452,
206   DECMODE_8800,
207 
208   /* SM/RM */
209   VTMODE_2,
210   VTMODE_4,
211   VTMODE_12,
212   VTMODE_20,
213   VTMODE_33,
214   VTMODE_34,
215 
216   VTMODE_NUM,
217 } vtmode_t;
218 
219 typedef struct area {
220   u_int32_t min;
221   u_int32_t max;
222 
223 } area_t;
224 
225 /* --- static variables --- */
226 
227 static u_int16_t vtmodes[] = {
228   /* DECSET/DECRST */
229   1, 2, 3, 5, 6, 7, 25, 40, 47, 66, 67, 69, 80, 95, 116, 117,
230   1000, 1002, /* Don't add an entry between 1000 and 1002 (see set_vtmode()) */
231   1003, 1004, 1005, 1006, 1015, /* Don't add an entry between 1005 and 1015 (see set_vtmode()) */
232   1010, 1034, 1036,
233   1042, 1047, 1048, 1049, 2004, 7727, 8428, 8452, 8800,
234 
235   /* SM/RM */
236   VTMODE(2), VTMODE(4), VTMODE(12), VTMODE(20), VTMODE(33), VTMODE(34),
237 };
238 
239 static int use_alt_buffer = 1;
240 
241 static area_t *unicode_noconv_areas;
242 static u_int num_unicode_noconv_areas;
243 
244 static area_t *full_width_areas;
245 static u_int num_full_width_areas;
246 
247 static area_t *half_width_areas;
248 static u_int num_half_width_areas;
249 
250 static char *auto_detect_encodings;
251 static struct {
252   vt_char_encoding_t encoding;
253   ef_parser_t *parser;
254 
255 } * auto_detect;
256 static u_int num_auto_detect_encodings;
257 
258 static int use_ttyrec_format;
259 
260 static clock_t timeout_read_pty = CLOCKS_PER_SEC / 100; /* 0.01 sec */
261 
262 static char *primary_da;
263 static char *secondary_da;
264 
265 static int is_broadcasting;
266 
267 static int old_drcs_sixel; /* Compatible behavior with RLogin 2.23.0 or before */
268 
269 static u_int local_echo_wait_msec = 250;
270 
271 #ifdef USE_LIBSSH2
272 static int use_scp_full;
273 #endif
274 
275 static u_int8_t alt_color_idxs[] = { 0, 1, 2, 4, 8, 3, 5, 9, 6, 10, 12, 7, 11, 13, 14, 15, } ;
276 
277 static char *send_file;
278 static char *recv_dir;
279 
280 /* --- static functions --- */
281 
282 #ifdef DEBUG
283 #if 0
284 #define debug_print_unknown bl_debug_printf
285 #else
debug_print_unknown(const char * format,...)286 static void debug_print_unknown(const char *format, ...) {
287   va_list arg_list;
288 
289   va_start(arg_list, format);
290 
291   fprintf(stderr, BL_DEBUG_TAG " received unknown sequence ");
292   vfprintf(stderr, format, arg_list);
293 }
294 #endif
295 #endif
296 
297 /* XXX This function should be moved to pobl */
str_replace(char * str,int c1,int c2)298 static void str_replace(char *str, int c1, int c2) {
299   while (*str) {
300     if (*str == c1) {
301       *str = c2;
302     }
303 
304     str++;
305   }
306 }
307 
set_area_to_table(area_t * area_table,u_int * num,char * areas)308 static area_t *set_area_to_table(area_t *area_table, u_int *num, char *areas) {
309   char *area;
310 
311 #ifdef __DEBUG
312   if (area_table == unicode_noconv_areas) {
313     bl_debug_printf("Unicode noconv area:");
314   } else if (area_table == full_width_areas) {
315     bl_debug_printf("Unicode full width area:");
316   } else {
317     bl_debug_printf("Unicode half width area:");
318   }
319   bl_msg_printf(" parsing %s\n", areas);
320 #endif
321 
322   if (areas == NULL || *areas == '\0') {
323     free(area_table);
324     *num = 0;
325 
326     return NULL;
327   } else {
328     void *p;
329 
330     if (!(p = realloc(area_table, sizeof(*area_table) * (bl_count_char_in_str(areas, ',') + 2)))) {
331       return area_table;
332     }
333 
334     area_table = p;
335   }
336 
337   *num = 0;
338 
339   while ((area = bl_str_sep(&areas, ","))) {
340     u_int min;
341     u_int max;
342 
343     if (vt_parse_unicode_area(area, &min, &max)) {
344       u_int count = 0;
345 
346       while (1) {
347         if (count == *num) {
348           area_table[*num].min = min;
349           area_table[(*num)++].max = max;
350           break;
351         }
352 
353         if (area_table[count].min <= min) {
354           if (area_table[count].max + 1 >= min) {
355             if (area_table[count].max < max) {
356               area_table[count].max = max;
357             }
358             break;
359           }
360         } else {
361           if (area_table[count].min <= max + 1) {
362             if (area_table[count].min > min) {
363               area_table[count].min = min;
364             }
365             if (area_table[count].max < max) {
366               area_table[count].max = max;
367             }
368           } else {
369             memmove(area_table + count + 1, area_table + count,
370                     (*num - count) * sizeof(*area_table));
371             area_table[count].max = max;
372             area_table[count].min = min;
373             (*num)++;
374           }
375           break;
376         }
377 
378         count++;
379       }
380     }
381   }
382 
383 #ifdef __DEBUG
384   {
385     u_int count;
386 
387     for (count = 0; count < *num; count++) {
388       bl_debug_printf("AREA %x-%x\n", area_table[count].min, area_table[count].max);
389     }
390   }
391 #endif
392 
393   return area_table;
394 }
395 
response_area_table(vt_pty_t * pty,u_char * key,area_t * area_table,u_int num,int to_menu)396 static void response_area_table(vt_pty_t *pty, u_char *key, area_t *area_table, u_int num,
397                                 int to_menu) {
398   u_char *value;
399 
400   /* 20: U+FFFFFFFF-FFFFFFFF, */
401   if (num > 0 && (value = alloca(20 * num))) {
402     u_int count;
403     u_char *p;
404 
405     p = value;
406     count = 0;
407     while (1) {
408       sprintf(p, area_table[count].min == area_table[count].max ? "U+%x" : "U+%x-%x",
409               area_table[count].min, area_table[count].max);
410       p += strlen(p);
411       if (++count < num) {
412         *(p++) = ',';
413       } else {
414         break;
415       }
416     }
417   } else {
418     value = "";
419   }
420 
421   vt_response_config(pty, key, value, to_menu);
422 }
423 
hit_area(area_t * areas,u_int num,u_int code)424 static inline int hit_area(area_t *areas, u_int num, u_int code) {
425   if (num > 0 && areas[0].min <= code && code <= areas[num - 1].max) {
426     u_int count;
427 
428     if (num == 1) {
429       return 1;
430     }
431     count = 0;
432     do {
433       if (areas[count].min <= code && code <= areas[count].max) {
434         return 1;
435       }
436     } while (++count < num);
437   }
438 
439   return 0;
440 }
441 
is_noconv_unicode(u_char * ch)442 static inline int is_noconv_unicode(u_char *ch) {
443   if (unicode_noconv_areas || ch[2] == 0x20) {
444     u_int32_t code = ef_bytes_to_int(ch, 4);
445 
446     if (hit_area(unicode_noconv_areas, num_unicode_noconv_areas, code)) {
447       return 1;
448     }
449 
450     /*
451      * Don't convert these characters in order not to show them.
452      * see vt_char_cols().
453      */
454     if ((0x200c <= code && code <= 0x200f) || (0x202a <= code && code <= 0x202e)) {
455       return 1;
456     }
457   }
458 
459   return 0;
460 }
461 
modify_ucs_property(u_int32_t code,int col_size_of_width_a,ef_property_t prop)462 static inline ef_property_t modify_ucs_property(u_int32_t code, int col_size_of_width_a,
463                                                 ef_property_t prop) {
464   if (prop & EF_AWIDTH) {
465 #ifdef SUPPORT_VTE_CJK_WIDTH
466     char *env;
467 #endif
468 
469     if (col_size_of_width_a == 2) {
470       prop |= EF_FULLWIDTH;
471     }
472 #ifdef SUPPORT_VTE_CJK_WIDTH
473     else if ((env = getenv("VTE_CJK_WIDTH")) &&
474              (strcmp(env, "wide") == 0 || strcmp(env, "1") == 0)) {
475       prop |= EF_FULLWIDTH;
476     }
477 #endif
478   }
479 
480   if (prop & EF_FULLWIDTH) {
481     if (half_width_areas && hit_area(half_width_areas, num_half_width_areas, code)) {
482       return prop & ~EF_FULLWIDTH;
483     }
484   } else {
485     if (full_width_areas && hit_area(full_width_areas, num_full_width_areas, code)) {
486       return prop | EF_FULLWIDTH;
487     }
488   }
489 
490   return prop;
491 }
492 
start_vt100_cmd(vt_parser_t * vt_parser,int trigger_xterm_event)493 static void start_vt100_cmd(vt_parser_t *vt_parser,
494                             int trigger_xterm_event /* dispatch to x_screen or not. */
495                             ) {
496   vt_set_use_multi_col_char(vt_parser->use_multi_col_char);
497 
498   if (trigger_xterm_event && HAS_XTERM_LISTENER(vt_parser, start)) {
499     /*
500      * XXX Adhoc implementation.
501      * Converting visual -> logical in xterm_listener->start.
502      */
503     (*vt_parser->xterm_listener->start)(vt_parser->xterm_listener->self);
504   } else {
505     vt_screen_logical(vt_parser->screen);
506   }
507 }
508 
stop_vt100_cmd(vt_parser_t * vt_parser,int trigger_xterm_event)509 static void stop_vt100_cmd(vt_parser_t *vt_parser,
510                            int trigger_xterm_event /* dispatch to x_screen or not. */
511                            ) {
512   vt_screen_render(vt_parser->screen);
513   vt_screen_visual(vt_parser->screen);
514 
515   if (trigger_xterm_event && HAS_XTERM_LISTENER(vt_parser, stop)) {
516     (*vt_parser->xterm_listener->stop)(vt_parser->xterm_listener->self);
517   }
518 }
519 
interrupt_vt100_cmd(vt_parser_t * vt_parser)520 static void interrupt_vt100_cmd(vt_parser_t *vt_parser) {
521   if (HAS_XTERM_LISTENER(vt_parser, interrupt)) {
522     vt_screen_render(vt_parser->screen);
523     vt_screen_visual(vt_parser->screen);
524 
525     (*vt_parser->xterm_listener->interrupt)(vt_parser->xterm_listener->self);
526 
527     vt_screen_logical(vt_parser->screen);
528   }
529 }
530 
change_read_buffer_size(vt_read_buffer_t * r_buf,size_t len)531 static int change_read_buffer_size(vt_read_buffer_t *r_buf, size_t len) {
532   void *p;
533 
534   if (!(p = realloc(r_buf->chars, len))) {
535     return 0;
536   }
537 
538   r_buf->chars = p;
539   r_buf->len = len;
540 
541   /*
542    * Not check if r_buf->left and r_buf->filled_len is larger than r_buf->len.
543    * It should be checked before calling this function.
544    */
545 
546   return 1;
547 }
548 
get_now_suffix(char * now)549 static char* get_now_suffix(char *now /* 16 bytes */) {
550   time_t t;
551   struct tm *tm;
552 
553   time(&t);
554   tm = localtime(&t);
555 
556   sprintf(now, "-%04d%02d%02d%02d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
557           tm->tm_hour, tm->tm_min, tm->tm_sec);
558 
559   return now;
560 }
561 
get_home_file_path(const char * prefix,const char * name,const char * suffix)562 static char *get_home_file_path(const char *prefix, const char *name, const char *suffix) {
563   char *file_name;
564 
565   if (!(file_name = alloca(7 + strlen(prefix) + 1 + strlen(name) + 1 + strlen(suffix) + 1))) {
566     return NULL;
567   }
568 
569   sprintf(file_name, "mlterm/%s%s.%s", prefix, name, suffix);
570   str_replace(file_name + 7, '/', '_');
571 
572   return bl_get_user_rc_path(file_name);
573 }
574 
575 /*
576  * 0: Error
577  * 1: No error
578  * >=2: Probable
579  */
parse_string(ef_parser_t * cc_parser,u_char * str,size_t len)580 static int parse_string(ef_parser_t *cc_parser, u_char *str, size_t len) {
581   ef_char_t ch;
582   int ret;
583   u_int nfull;
584   u_int nkana;
585 
586   ret = 1;
587   nfull = 0;
588   nkana = 0;
589   (*cc_parser->init)(cc_parser);
590   (*cc_parser->set_str)(cc_parser, str, len);
591 
592   while (1) {
593     if (!(*cc_parser->next_char)(cc_parser, &ch)) {
594       if (cc_parser->is_eos) {
595         if (nkana * 8 > nfull) {
596           /* kana is over 12.5%. */
597           ret = 2;
598         }
599 
600         return ret;
601       } else {
602         if (((str[len - cc_parser->left]) & 0x7f) <= 0x1f) {
603           /* skip C0 or C1 */
604           ef_parser_increment(cc_parser);
605         } else {
606           return 0;
607         }
608       }
609     } else if (ch.size > 1) {
610       if (ch.cs == ISO10646_UCS4_1) {
611         if (ret == 1 && (ch.property & EF_FULLWIDTH)) {
612           ret = 2;
613         }
614       } else {
615         if (IS_CS94MB(ch.cs)) {
616           if (ch.ch[0] <= 0x20 || ch.ch[0] == 0x7f || ch.ch[1] <= 0x20 || ch.ch[1] == 0x7f) {
617             /* mef can return illegal character code. */
618             return 0;
619           } else if (ret == 1 && (ch.cs == JISX0208_1983 || ch.cs == JISC6226_1978 ||
620                                   ch.cs == JISX0213_2000_1) &&
621                      (ch.ch[0] == 0x24 || ch.ch[0] == 0x25) && 0x21 <= ch.ch[1] &&
622                      ch.ch[1] <= 0x73) {
623             /* Hiragana/Katakana */
624             nkana++;
625           }
626         }
627 
628         nfull++;
629       }
630     }
631   }
632 }
633 
634 /* Check num_auto_detect_encodings > 0 before calling this function. */
detect_encoding(vt_parser_t * vt_parser)635 static void detect_encoding(vt_parser_t *vt_parser) {
636   u_char *str;
637   size_t len;
638   size_t count;
639   u_int idx;
640   int cur_idx;
641   int cand_idx;
642   int threshold;
643 
644   str = vt_parser->r_buf.chars;
645   len = vt_parser->r_buf.filled_len;
646 
647   for (count = 0; count < len - 1; count++) {
648     if (str[count] >= 0x80 && str[count + 1] >= 0x80) {
649       goto detect;
650     }
651   }
652 
653   return;
654 
655 detect:
656   cur_idx = -1;
657   threshold = 0;
658   for (idx = 0; idx < num_auto_detect_encodings; idx++) {
659     if (auto_detect[idx].encoding == vt_parser->encoding) {
660       if ((threshold = parse_string(auto_detect[idx].parser, str, len)) > 1) {
661         return;
662       }
663 
664       cur_idx = idx;
665       break;
666     }
667   }
668 
669   cand_idx = -1;
670   for (idx = 0; idx < num_auto_detect_encodings; idx++) {
671     int ret;
672 
673     if (idx != cur_idx && (ret = parse_string(auto_detect[idx].parser, str, len)) > threshold) {
674       cand_idx = idx;
675 
676       if (ret > 1) {
677         break;
678       }
679     }
680   }
681 
682   if (cand_idx >= 0) {
683 #ifdef DEBUG
684     bl_debug_printf(BL_DEBUG_TAG " Character encoding is changed to %s.\n",
685                     vt_get_char_encoding_name(auto_detect[cand_idx].encoding));
686 #endif
687 
688     vt_parser_change_encoding(vt_parser, auto_detect[cand_idx].encoding);
689   }
690 }
691 
is_dcs_or_osc(u_char * str)692 inline static int is_dcs_or_osc(u_char *str /* The length should be 2 or more. */
693                                 ) {
694   return *str == 0x90 || memcmp(str, "\x1bP", 2) == 0 || memcmp(str, "\x1b]", 2) == 0;
695 }
696 
write_ttyrec_header(int fd,size_t len,int keep_time)697 static void write_ttyrec_header(int fd, size_t len, int keep_time) {
698   u_int32_t buf[3];
699 
700 #ifdef HAVE_GETTIMEOFDAY
701   if (!keep_time) {
702     struct timeval tval;
703 
704     gettimeofday(&tval, NULL);
705 #ifdef WORDS_BIGENDIAN
706     buf[0] = LE32DEC(((u_char *)&tval.tv_sec));
707     buf[1] = LE32DEC(((u_char *)&tval.tv_usec));
708     buf[2] = LE32DEC(((u_char *)&len));
709 #else
710     buf[0] = tval.tv_sec;
711     buf[1] = tval.tv_usec;
712     buf[2] = len;
713 #endif
714 
715 #if __DEBUG
716     bl_debug_printf("write len %d at %d\n", len, lseek(fd, 0, SEEK_CUR));
717 #endif
718 
719     write(fd, buf, 12);
720   } else
721 #endif
722   {
723     lseek(fd, 8, SEEK_CUR);
724 
725 #ifdef WORDS_BIGENDIAN
726     buf[0] = LE32DEC(((u_char *)&len));
727 #else
728     buf[0] = len;
729 #endif
730 
731     write(fd, buf, 4);
732   }
733 }
734 
receive_bytes(vt_parser_t * vt_parser)735 static int receive_bytes(vt_parser_t *vt_parser) {
736   size_t len;
737 
738   if (vt_parser->r_buf.left == vt_parser->r_buf.len) {
739     /* Buffer is full => Expand buffer */
740 
741     len = vt_parser->r_buf.len >= PTY_RD_BUFFER_SIZE * 5 ? PTY_RD_BUFFER_SIZE * 10
742                                                             : PTY_RD_BUFFER_SIZE;
743 
744     if (!change_read_buffer_size(&vt_parser->r_buf, vt_parser->r_buf.len + len)) {
745       return 0;
746     }
747   } else {
748     if (0 < vt_parser->r_buf.left && vt_parser->r_buf.left < vt_parser->r_buf.filled_len) {
749       memmove(vt_parser->r_buf.chars, CURRENT_STR_P(vt_parser),
750               vt_parser->r_buf.left * sizeof(u_char));
751     }
752 
753     /* vt_parser->r_buf.left must be always less than vt_parser->r_buf.len
754      */
755     if ((len = vt_parser->r_buf.len - vt_parser->r_buf.left) > PTY_RD_BUFFER_SIZE &&
756         !is_dcs_or_osc(vt_parser->r_buf.chars)) {
757       len = PTY_RD_BUFFER_SIZE;
758     }
759   }
760 
761   if ((vt_parser->r_buf.new_len = vt_read_pty(
762            vt_parser->pty, vt_parser->r_buf.chars + vt_parser->r_buf.left, len)) == 0) {
763     vt_parser->r_buf.filled_len = vt_parser->r_buf.left;
764 
765     return 0;
766   }
767 
768   if (vt_parser->logging_vt_seq) {
769     if (vt_parser->log_file == -1) {
770       char *path;
771       char buf[16];
772 
773       if (!(path = get_home_file_path(vt_pty_get_slave_name(vt_parser->pty) + 5,
774                                       get_now_suffix(buf), "log"))) {
775         goto end;
776       }
777 
778       if ((vt_parser->log_file = open(path, O_CREAT | O_WRONLY, 0600)) == -1) {
779         free(path);
780 
781         goto end;
782       }
783 
784       free(path);
785 
786       /*
787        * O_APPEND in open() forces lseek(0,SEEK_END) in write()
788        * and disables lseek(pos,SEEK_SET) before calling write().
789        * So don't specify O_APPEND in open() and call lseek(0,SEEK_END)
790        * manually after open().
791        */
792       lseek(vt_parser->log_file, 0, SEEK_END);
793       bl_file_set_cloexec(vt_parser->log_file);
794 
795       if (use_ttyrec_format) {
796         char seq[6 + DIGIT_STR_LEN(int)*2 + 1];
797 
798         /* The height of "CSI 8 t" doesn't include status line. */
799         sprintf(seq, "\x1b[8;%d;%dt", vt_screen_get_logical_rows(vt_parser->screen),
800                 vt_screen_get_logical_cols(vt_parser->screen));
801         write_ttyrec_header(vt_parser->log_file, strlen(seq), 0);
802         write(vt_parser->log_file, seq, strlen(seq));
803       }
804     }
805 
806     if (use_ttyrec_format) {
807       if (vt_parser->r_buf.left > 0) {
808         lseek(vt_parser->log_file,
809               lseek(vt_parser->log_file, 0, SEEK_CUR) - vt_parser->r_buf.filled_len - 12,
810               SEEK_SET);
811 
812         if (vt_parser->r_buf.left < vt_parser->r_buf.filled_len) {
813           write_ttyrec_header(vt_parser->log_file,
814                               vt_parser->r_buf.filled_len - vt_parser->r_buf.left, 1);
815 
816           lseek(vt_parser->log_file,
817                 lseek(vt_parser->log_file, 0, SEEK_CUR) + vt_parser->r_buf.filled_len -
818                     vt_parser->r_buf.left,
819                 SEEK_SET);
820         }
821       }
822 
823       write_ttyrec_header(vt_parser->log_file,
824                           vt_parser->r_buf.left + vt_parser->r_buf.new_len, 0);
825 
826       write(vt_parser->log_file, vt_parser->r_buf.chars,
827             vt_parser->r_buf.left + vt_parser->r_buf.new_len);
828     } else {
829       write(vt_parser->log_file, vt_parser->r_buf.chars + vt_parser->r_buf.left,
830             vt_parser->r_buf.new_len);
831     }
832 
833 #ifndef USE_WIN32API
834     fsync(vt_parser->log_file);
835 #endif
836   } else {
837     if (vt_parser->log_file != -1) {
838       close(vt_parser->log_file);
839       vt_parser->log_file = -1;
840     }
841   }
842 
843 end:
844   vt_parser->r_buf.filled_len = (vt_parser->r_buf.left += vt_parser->r_buf.new_len);
845 
846   if (vt_parser->r_buf.filled_len <= PTY_RD_BUFFER_SIZE &&
847       vt_parser->r_buf.len > PTY_RD_BUFFER_SIZE) {
848     /* Shrink buffer */
849     change_read_buffer_size(&vt_parser->r_buf, PTY_RD_BUFFER_SIZE);
850   }
851 
852   if (vt_parser->use_auto_detect && num_auto_detect_encodings > 0) {
853     detect_encoding(vt_parser);
854   }
855 
856 #ifdef INPUT_DEBUG
857   {
858     size_t count;
859 
860     bl_debug_printf(BL_DEBUG_TAG " pty msg (len %d) is received:", vt_parser->r_buf.left);
861 
862     for (count = 0; count < vt_parser->r_buf.left; count++) {
863 #ifdef DUMP_HEX
864       if (isprint(vt_parser->r_buf.chars[count])) {
865         bl_msg_printf("%c ", vt_parser->r_buf.chars[count]);
866       } else {
867         bl_msg_printf("%.2x ", vt_parser->r_buf.chars[count]);
868       }
869 #else
870       bl_msg_printf("%c", vt_parser->r_buf.chars[count]);
871 #endif
872     }
873 
874     bl_msg_printf("[END]\n");
875   }
876 #endif
877 
878   return 1;
879 }
880 
881 /*
882  * If buffer exists, vt_parser->w_buf.last_ch is cached.
883  * If buffer doesn't exist, vt_parser->w_buf.last_ch is cleared.
884  */
flush_buffer(vt_parser_t * vt_parser)885 static int flush_buffer(vt_parser_t *vt_parser) {
886   vt_write_buffer_t *buffer;
887 
888   buffer = &vt_parser->w_buf;
889 
890   if (buffer->filled_len == 0) {
891     return 0;
892   }
893 
894 #ifdef OUTPUT_DEBUG
895   {
896     u_int count;
897 
898     bl_msg_printf("\nflushing chars(%d)...==>", buffer->filled_len);
899     for (count = 0; count < buffer->filled_len; count++) {
900       u_int code = vt_char_code(&buffer->chars[count]);
901 
902 #ifdef DUMP_HEX
903       bl_msg_printf("%x", code);
904 #else
905       bl_msg_printf("%c", code);
906 #endif
907     }
908 
909     bl_msg_printf("<===\n");
910   }
911 #endif
912 
913   (*buffer->output_func)(vt_parser->screen, buffer->chars, buffer->filled_len);
914 
915   /* last_ch which will be used & cleared in REP sequence is cached. */
916   buffer->last_ch = &buffer->chars[buffer->filled_len - 1];
917   /* buffer is cleared. */
918   buffer->filled_len = 0;
919 
920 #ifdef EDIT_DEBUG
921   vt_edit_dump(vt_parser->screen->edit);
922 #endif
923 
924   return 1;
925 }
926 
put_char(vt_parser_t * vt_parser,u_int32_t ch,ef_charset_t cs,ef_property_t prop)927 static void put_char(vt_parser_t *vt_parser, u_int32_t ch, ef_charset_t cs,
928                      ef_property_t prop) {
929   vt_color_t fg_color;
930   vt_color_t bg_color;
931   int is_fullwidth;
932   int is_awidth;
933   int is_comb;
934   int is_bold;
935   int is_italic;
936   int line_style;
937   int is_blinking;
938   int is_protected;
939   int is_reversed;
940 
941   if (vt_parser->w_buf.filled_len == PTY_WR_BUFFER_SIZE) {
942     flush_buffer(vt_parser);
943   }
944 
945   /*
946    * checking width property of the char.
947    */
948 
949   if (prop & EF_FULLWIDTH) {
950     is_fullwidth = 1;
951   } else {
952     is_fullwidth = 0;
953   }
954 
955   if (prop & EF_AWIDTH) {
956     is_awidth = 1;
957   } else {
958     is_awidth = 0;
959   }
960 
961 #ifdef __DEBUG
962   bl_debug_printf("%x %d %x => %s\n", ch, len, cs, is_fullwidth ? "Fullwidth" : "Single");
963 #endif
964 
965   if ((prop & EF_COMBINING)
966 #if !defined(NO_DYNAMIC_LOAD_CTL) || defined(USE_IND)
967       || (ch == '\xe9' && IS_ISCII(cs)) /* nukta is always combined. */
968 #endif
969       ) {
970     is_comb = 1;
971   } else {
972     is_comb = 0;
973   }
974 
975   fg_color = vt_parser->fg_color;
976   bg_color = vt_parser->bg_color;
977   is_italic = vt_parser->is_italic ? 1 : 0;
978   is_blinking = vt_parser->is_blinking ? 1 : 0;
979   is_protected = vt_parser->is_protected ? 1 : 0;
980   is_reversed = vt_parser->is_reversed ? 1 : 0;
981   line_style = vt_parser->line_style;
982   if (cs == ISO10646_UCS4_1 && 0x2580 <= ch && ch <= 0x259f) {
983     /* prevent these block characters from being drawn doubly. */
984     is_bold = 0;
985   } else {
986     is_bold = vt_parser->is_bold ? 1 : 0;
987   }
988 
989   if (fg_color == VT_FG_COLOR) {
990     if (is_italic && (vt_parser->alt_color_mode & ALT_COLOR_ITALIC)) {
991       is_italic = 0;
992       fg_color = VT_ITALIC_COLOR;
993     }
994 
995     if ((line_style & LS_CROSSED_OUT) && (vt_parser->alt_color_mode & ALT_COLOR_CROSSED_OUT)) {
996       line_style &= ~LS_CROSSED_OUT;
997       fg_color = VT_CROSSED_OUT_COLOR;
998     }
999 
1000     if (is_blinking && (vt_parser->alt_color_mode & ALT_COLOR_BLINKING)) {
1001       is_blinking = 0;
1002       fg_color = VT_BLINKING_COLOR;
1003     }
1004 
1005     if ((line_style & LS_UNDERLINE) && (vt_parser->alt_color_mode & ALT_COLOR_UNDERLINE)) {
1006       line_style &= ~LS_UNDERLINE;
1007       fg_color = VT_UNDERLINE_COLOR;
1008     }
1009 
1010     if (is_bold && (vt_parser->alt_color_mode & ALT_COLOR_BOLD)) {
1011       is_bold = 0;
1012       fg_color = VT_BOLD_COLOR;
1013     }
1014 
1015     if (is_reversed && (vt_parser->alt_color_mode & ALT_COLOR_REVERSE)) {
1016       is_reversed = 0;
1017       fg_color = VT_REVERSE_COLOR;
1018     }
1019   } else {
1020     if (is_bold) {
1021       if (IS_VTSYS_BASE_COLOR(fg_color)) {
1022         fg_color |= VT_BOLD_COLOR_MASK;
1023       }
1024       if (BOLD_AFFECTS_BG(vt_parser) && IS_VTSYS_BASE_COLOR(bg_color)) {
1025         bg_color |= VT_BOLD_COLOR_MASK;
1026       }
1027     }
1028   }
1029 
1030   if (is_reversed) {
1031     vt_color_t tmp = bg_color;
1032     bg_color = fg_color;
1033     fg_color = tmp;
1034   }
1035 
1036   if (vt_parser->alt_colors.flags) {
1037     int idx = is_bold | ((vt_parser->is_reversed ? 1 : 0) << 1) |
1038               ((line_style & LS_UNDERLINE) ? 4 : 0) | (is_blinking << 3);
1039 
1040     if (/* idx < 16 && */ (vt_parser->alt_colors.flags & (1 << idx))) {
1041       fg_color = vt_parser->alt_colors.fg[idx];
1042       bg_color = vt_parser->alt_colors.bg[idx];
1043     }
1044   }
1045 
1046   /*
1047    * 2.9.2 Alternate Text Colors in VT520-VT525ProgrammerInformation.pdf
1048    * "The foreground color of text with the invisible attribute becomes the background color."
1049    */
1050   if (vt_parser->is_invisible) {
1051     fg_color = bg_color;
1052   }
1053 
1054   if (vt_parser->use_char_combining && is_comb) {
1055     if (vt_parser->w_buf.filled_len == 0) {
1056       /*
1057        * vt_line_set_modified() is done in vt_screen_combine_with_prev_char()
1058        * internally.
1059        */
1060       if (vt_screen_combine_with_prev_char(vt_parser->screen, ch, cs, is_fullwidth, is_awidth,
1061                                            is_comb, fg_color, bg_color, is_bold, is_italic,
1062                                            line_style, is_blinking, is_protected)) {
1063         return;
1064       }
1065     } else {
1066       if (vt_char_combine(&vt_parser->w_buf.chars[vt_parser->w_buf.filled_len - 1], ch, cs,
1067                           is_fullwidth, is_awidth, is_comb, fg_color, bg_color, is_bold, is_italic,
1068                           line_style, is_blinking, is_protected)) {
1069         return;
1070       }
1071     }
1072 
1073     /*
1074      * if combining failed , char is normally appended.
1075      */
1076   }
1077 
1078 #ifndef NO_IMAGE
1079   {
1080     vt_drcs_font_t *font;
1081     int pic_id;
1082     int pic_pos;
1083 
1084     if (cs != US_ASCII &&
1085         (font = vt_drcs_get_font(vt_parser->drcs,
1086                                  cs == CS_REVISION_1(US_ASCII) ? US_ASCII : cs, 0)) &&
1087         /* XXX The width of pua is always regarded as 1. (is_fullwidth is ignored) */
1088         vt_drcs_get_picture(font, &pic_id, &pic_pos, ch)) {
1089       vt_char_copy(&vt_parser->w_buf.chars[vt_parser->w_buf.filled_len],
1090                    vt_screen_get_bce_ch(vt_parser->screen));
1091       vt_char_combine_picture(&vt_parser->w_buf.chars[vt_parser->w_buf.filled_len++],
1092                               pic_id, pic_pos);
1093 
1094       return;
1095     }
1096   }
1097 #endif
1098 
1099   vt_char_set(&vt_parser->w_buf.chars[vt_parser->w_buf.filled_len++], ch, cs, is_fullwidth,
1100               is_awidth, is_comb, fg_color, bg_color, is_bold, is_italic, line_style,
1101               is_blinking, is_protected);
1102 
1103   if (cs != ISO10646_UCS4_1) {
1104     return;
1105   }
1106 
1107   if (0x1f000 <= ch && ch <= 0x1f9ff && (ch <= 0x1f6ff || 0x1f900 <= ch) &&
1108       HAS_XTERM_LISTENER(vt_parser, get_emoji_data)) {
1109     /*
1110      * Emoji pictures (mostly U+1F000-1F6FF) provided by
1111      * https://github.com/github/gemoji
1112      */
1113 
1114     vt_char_t *emoji1;
1115     vt_char_t *emoji2;
1116 
1117     emoji1 = &vt_parser->w_buf.chars[vt_parser->w_buf.filled_len - 1];
1118     emoji2 = NULL;
1119 
1120     if (0x1f1e6 <= ch && ch <= 0x1f1ff /* REGIONAL INDICATOR SYMBOL LETTER A-Z */) {
1121       vt_char_t *prev_ch;
1122 
1123       if (vt_parser->w_buf.filled_len <= 1) {
1124         prev_ch = vt_screen_get_n_prev_char(vt_parser->screen, 1);
1125       } else {
1126         prev_ch = emoji1 - 1;
1127       }
1128 
1129       if (prev_ch) {
1130         if (0x1f1e6 <= vt_char_code(prev_ch) && vt_char_code(prev_ch) <= 0x1f1ff) {
1131           emoji2 = emoji1;
1132           emoji1 = prev_ch;
1133         }
1134       }
1135     } else if (0x1f3fb <= ch && ch <= 0x1f3ff) /* EMOJI MODIFIER FITZPATRIC TYPE 1-6 */ {
1136       vt_char_t *prev_ch;
1137 
1138       if (vt_parser->w_buf.filled_len <= 1) {
1139         prev_ch = vt_screen_get_n_prev_char(vt_parser->screen, 1);
1140       } else {
1141         prev_ch = emoji1 - 1;
1142       }
1143 
1144       if (prev_ch) {
1145         emoji2 = emoji1;
1146         emoji1 = prev_ch;
1147       }
1148     }
1149 
1150     if ((*vt_parser->xterm_listener->get_emoji_data)(vt_parser->xterm_listener->self, emoji1,
1151                                                      emoji2)) {
1152       if (emoji2) {
1153         /* Base char: emoji1, Comb1: picture, Comb2: emoji2 */
1154         if (emoji2 == emoji1 + 1) {
1155           vt_char_combine(emoji1, ch, cs, is_fullwidth, is_awidth, is_comb, fg_color, bg_color,
1156                           is_bold, is_italic, line_style, is_blinking, is_protected);
1157         } else {
1158           /*
1159            * vt_line_set_modified() is done in
1160            * vt_screen_combine_with_prev_char() internally.
1161            */
1162           vt_screen_combine_with_prev_char(vt_parser->screen, ch, cs, is_fullwidth, is_awidth,
1163                                            is_comb, fg_color, bg_color, is_bold, is_italic,
1164                                            line_style, is_blinking, is_protected);
1165         }
1166         vt_parser->w_buf.filled_len--;
1167       }
1168 
1169       /*
1170        * Flush buffer before searching and deleting unused pictures
1171        * in x_picture.c.
1172        */
1173       flush_buffer(vt_parser);
1174     }
1175   } else if (vt_parser->use_char_combining) {
1176     /*
1177      * Arabic combining
1178      */
1179 
1180     vt_char_t *prev2;
1181     vt_char_t *prev;
1182     vt_char_t *cur;
1183     int n;
1184 
1185     cur = &vt_parser->w_buf.chars[vt_parser->w_buf.filled_len - 1];
1186     n = 0;
1187 
1188     if (vt_parser->w_buf.filled_len >= 2) {
1189       prev = cur - 1;
1190     } else {
1191       if (!(prev = vt_screen_get_n_prev_char(vt_parser->screen, ++n))) {
1192         return;
1193       }
1194     }
1195 
1196     if (vt_parser->w_buf.filled_len >= 3) {
1197       prev2 = cur - 2;
1198     } else {
1199       /* possibly NULL */
1200       prev2 = vt_screen_get_n_prev_char(vt_parser->screen, ++n);
1201     }
1202 
1203     if (IS_ARABIC_CHAR(ch) && vt_is_arabic_combining(prev2, prev, cur)) {
1204       if (vt_parser->w_buf.filled_len >= 2) {
1205         if (vt_char_combine(prev, ch, cs, is_fullwidth, is_awidth, is_comb, fg_color, bg_color,
1206                             is_bold, is_italic, line_style, is_blinking, is_protected)) {
1207           vt_parser->w_buf.filled_len--;
1208         }
1209       } else {
1210         /*
1211          * vt_line_set_modified() is done in
1212          * vt_screen_combine_with_prev_char() internally.
1213          */
1214         if (vt_screen_combine_with_prev_char(vt_parser->screen, ch, cs, is_fullwidth, is_awidth,
1215                                              is_comb, fg_color, bg_color, is_bold, is_italic,
1216                                              line_style, is_blinking, is_protected)) {
1217           vt_parser->w_buf.filled_len--;
1218         }
1219       }
1220     }
1221   }
1222 }
1223 
push_to_saved_names(vt_saved_names_t * saved,char * name)1224 static void push_to_saved_names(vt_saved_names_t *saved, char *name) {
1225   void *p;
1226 
1227   if (!(p = realloc(saved->names, (saved->num + 1) * sizeof(name)))) {
1228     return;
1229   }
1230 
1231   saved->names = p;
1232   saved->names[saved->num++] = name ? strdup(name) : NULL;
1233 }
1234 
pop_from_saved_names(vt_saved_names_t * saved)1235 static char *pop_from_saved_names(vt_saved_names_t *saved) {
1236   char *name;
1237 
1238   name = saved->names[--saved->num];
1239 
1240   if (saved->num == 0) {
1241     free(saved->names);
1242     saved->names = NULL;
1243   }
1244 
1245   return name;
1246 }
1247 
1248 /*
1249  * VT100_PARSER Escape Sequence Commands.
1250  */
1251 
save_cursor(vt_parser_t * vt_parser)1252 static void save_cursor(vt_parser_t *vt_parser) {
1253   vt_storable_states_t *dest;
1254 
1255   dest = (vt_screen_is_alternative_edit(vt_parser->screen)) ? &(vt_parser->saved_alternate)
1256                                                                : &(vt_parser->saved_normal);
1257   dest->is_saved = 1;
1258   dest->fg_color = vt_parser->fg_color;
1259   dest->bg_color = vt_parser->bg_color;
1260   dest->is_bold = vt_parser->is_bold;
1261   dest->is_italic = vt_parser->is_italic;
1262   dest->line_style = vt_parser->line_style;
1263   dest->is_reversed = vt_parser->is_reversed;
1264   dest->is_blinking = vt_parser->is_blinking;
1265   dest->is_invisible = vt_parser->is_invisible;
1266   dest->is_protected = vt_parser->is_protected;
1267   dest->is_relative_origin = vt_screen_is_relative_origin(vt_parser->screen);
1268   dest->last_column_flag = vt_screen_get_last_column_flag(vt_parser->screen);
1269   dest->cs = vt_parser->cs;
1270 
1271   vt_screen_save_cursor(vt_parser->screen);
1272 }
1273 
init_encoding_parser(vt_parser_t * vt_parser)1274 static void init_encoding_parser(vt_parser_t *vt_parser) {
1275   (*vt_parser->cc_parser->init)(vt_parser->cc_parser);
1276   vt_parser->gl = US_ASCII;
1277   vt_parser->g0 = US_ASCII;
1278   vt_parser->g1 = US_ASCII;
1279   vt_parser->is_so = 0;
1280 }
1281 
1282 static void set_vtmode(vt_parser_t *vt_parser, int mode, int flag);
1283 static void change_char_attr(vt_parser_t *vt_parser, int flag);
1284 
restore_cursor(vt_parser_t * vt_parser)1285 static void restore_cursor(vt_parser_t *vt_parser) {
1286   vt_storable_states_t *src;
1287 
1288   src = (vt_screen_is_alternative_edit(vt_parser->screen)) ? &(vt_parser->saved_alternate)
1289                                                               : &(vt_parser->saved_normal);
1290   if (src->is_saved) {
1291     vt_parser->fg_color = src->fg_color;
1292     vt_screen_set_bce_fg_color(vt_parser->screen, src->fg_color);
1293     vt_parser->bg_color = src->bg_color;
1294     vt_screen_set_bce_bg_color(vt_parser->screen, src->bg_color);
1295     vt_parser->is_bold = src->is_bold;
1296     vt_parser->is_italic = src->is_italic;
1297     vt_parser->line_style = src->line_style;
1298     vt_parser->is_reversed = src->is_reversed;
1299     vt_parser->is_blinking = src->is_blinking;
1300     vt_parser->is_invisible = src->is_invisible;
1301     vt_parser->is_protected = src->is_protected;
1302     vt_screen_set_relative_origin(vt_parser->screen, src->is_relative_origin ? 1 : 0);
1303     /* See "Last Column Flag specifics" at https://github.com/mattiase/wraptest */
1304     vt_screen_set_last_column_flag(vt_parser->screen, src->last_column_flag ? 1 : 0);
1305     if (IS_ENCODING_BASED_ON_ISO2022(vt_parser->encoding)) {
1306       if ((src->cs == DEC_SPECIAL) && (src->cs != vt_parser->cs)) {
1307         /* force grapchics mode by sending \E(0 to current parser*/
1308         u_char DEC_SEQ[] = {CTL_ESC, '(', '0'};
1309         ef_char_t ch;
1310         ef_parser_t *parser;
1311 
1312         init_encoding_parser(vt_parser);
1313         parser = vt_parser->cc_parser;
1314         (*parser->set_str)(parser, DEC_SEQ, sizeof(DEC_SEQ));
1315         (*parser->next_char)(parser, &ch);
1316       }
1317     } else {
1318       /* XXX: what to do for g0/g1? */
1319       if (src->cs == DEC_SPECIAL) {
1320         vt_parser->gl = DEC_SPECIAL;
1321       } else {
1322         vt_parser->gl = US_ASCII;
1323       }
1324     }
1325 
1326     vt_screen_restore_cursor(vt_parser->screen);
1327   } else {
1328     /*
1329      * Moves the cursor to the home position (upper left of screen).
1330      * Resets origin mode (DECOM).
1331      * Turns all character attributes off (normal setting).
1332      * Maps the ASCII character set into GL, and the DEC Supplemental Graphic set into GR.
1333      * (see https://www.vt100.net/docs/vt510-rm/DECRC.html)
1334      */
1335     change_char_attr(vt_parser, 0);
1336     set_vtmode(vt_parser, 6, 0); /* goto(0, 0) internally */
1337   }
1338 }
1339 
set_maximize(vt_parser_t * vt_parser,int flag)1340 static void set_maximize(vt_parser_t *vt_parser, int flag) {
1341   if (HAS_XTERM_LISTENER(vt_parser, resize)) {
1342     stop_vt100_cmd(vt_parser, 0);
1343     (*vt_parser->xterm_listener->resize)(vt_parser->xterm_listener->self, 0, 0, flag, 0);
1344     start_vt100_cmd(vt_parser, 0);
1345   }
1346 }
1347 
get_cell_size(vt_parser_t * vt_parser,u_int * col_width,u_int * line_height)1348 static int get_cell_size(vt_parser_t *vt_parser, u_int *col_width, u_int *line_height) {
1349   if (HAS_XTERM_LISTENER(vt_parser, get_window_size)) {
1350     u_int width;
1351     u_int height;
1352 
1353     (*vt_parser->xterm_listener->get_window_size)(vt_parser->xterm_listener->self, &width, &height);
1354 
1355     /* XXX This doesn't work on vertical mode. */
1356     if ((*col_width = width / vt_screen_get_logical_cols(vt_parser->screen)) == 0 ||
1357         (*line_height = height / vt_screen_get_logical_rows(vt_parser->screen)) == 0) {
1358       return 0;
1359     }
1360 
1361     return 1;
1362   } else {
1363     return 0;
1364   }
1365 }
1366 
1367 /*
1368  * width|height == 0: maximize
1369  * width|height == -1: current value
1370  */
resize(vt_parser_t * vt_parser,int width,int height,int by_char)1371 static void resize(vt_parser_t *vt_parser, int width, int height, int by_char) {
1372   if (HAS_XTERM_LISTENER(vt_parser, resize)) {
1373     int flag = 0;
1374 
1375     if (by_char) {
1376       if (width == 0 || height == 0) {
1377         u_int col_width;
1378         u_int line_height;
1379 
1380         if (!HAS_XTERM_LISTENER(vt_parser, get_display_size) ||
1381             !get_cell_size(vt_parser, &col_width, &line_height)) {
1382           if (width == 0) {
1383             width = -1;
1384           }
1385           if (height == 0) {
1386             height = -1;
1387           }
1388         } else {
1389           u_int w;
1390           u_int h;
1391 
1392           (*vt_parser->xterm_listener->get_display_size)(vt_parser->xterm_listener->self, &w, &h);
1393           if (width == 0) {
1394             width = w / col_width;
1395           }
1396           if (height == 0) {
1397             height = h / line_height;
1398 
1399             goto do_resize;
1400           }
1401         }
1402       }
1403 
1404       if (width == -1) {
1405         width = vt_screen_get_logical_cols(vt_parser->screen);
1406       }
1407       if (height == -1) {
1408         height = vt_screen_get_logical_rows(vt_parser->screen);
1409       }
1410 
1411       /*
1412        * 'height' argument of this function should includes status line according to
1413        * the following document, but teraterm (4.95) excludes status line by CSI 8 t.
1414        * mlterm considers height of "CSI t", "OSC 5379;geometry", DECSNLS and DECSLPP
1415        * to be excluding status line.
1416        *
1417        * https://vt100.net/docs/vt510-rm/DECSNLS.html
1418        * The terminal supports three different font heights, which allows 26, 42, or 53 data
1419        * lines to be displayed on the screen or 25, 41, or 52 data lines to be displayed on
1420        * the screen, plus a status line.
1421        */
1422       if (vt_screen_has_status_line(vt_parser->screen)) {
1423         height ++;
1424       }
1425 
1426     do_resize:
1427       if (vt_screen_resize(vt_parser->screen, width, height) == 2) {
1428         flag = 1;
1429       }
1430 
1431       /*
1432        * xterm_listener::resize(0,0) means that screen should be
1433        * resized according to the size of pty.
1434        */
1435       width = 0;
1436       height = 0;
1437     } else {
1438       if ((width == 0 || height == 0) && HAS_XTERM_LISTENER(vt_parser, get_display_size)) {
1439         u_int w;
1440         u_int h;
1441 
1442         (*vt_parser->xterm_listener->get_display_size)(vt_parser->xterm_listener->self, &w, &h);
1443         if (width == 0) {
1444           width = w;
1445         }
1446         if (height == 0) {
1447           height = h;
1448         }
1449       }
1450 
1451       if (width == -1) {
1452         width = 0; /* xterm_listener->resize() doesn't resize width if width == 0. */
1453       }
1454       if (height == -1) {
1455         height = 0; /* xterm_listener->resize() doesn't resize height if height == 0. */
1456       }
1457     }
1458 
1459     stop_vt100_cmd(vt_parser, 0);
1460     (*vt_parser->xterm_listener->resize)(vt_parser->xterm_listener->self, width, height, 0, flag);
1461     start_vt100_cmd(vt_parser, 0);
1462   }
1463 }
1464 
reverse_video(vt_parser_t * vt_parser,int flag)1465 static void reverse_video(vt_parser_t *vt_parser, int flag) {
1466   if (HAS_XTERM_LISTENER(vt_parser, reverse_video)) {
1467     stop_vt100_cmd(vt_parser, 0);
1468     (*vt_parser->xterm_listener->reverse_video)(vt_parser->xterm_listener->self, flag);
1469     start_vt100_cmd(vt_parser, 0);
1470   }
1471 }
1472 
set_mouse_report(vt_parser_t * vt_parser,vt_mouse_report_mode_t mode)1473 static void set_mouse_report(vt_parser_t *vt_parser, vt_mouse_report_mode_t mode) {
1474   if (HAS_XTERM_LISTENER(vt_parser, set_mouse_report)) {
1475 /*
1476  * If xterm_set_mouse_report() calls x_stop_selecting() etc,
1477  * remove #if 0 - #end.
1478  *
1479  * If #if 0 - #end is removed, ctl_render() is at least twice
1480  * in one sequence packet from w3m because w3m sends CSI?1000h every time.
1481  */
1482 #if 0
1483     stop_vt100_cmd(vt_parser, 0);
1484 #endif
1485 
1486     vt_parser->mouse_mode = mode;
1487     (*vt_parser->xterm_listener->set_mouse_report)(vt_parser->xterm_listener->self);
1488 
1489 #if 0
1490     start_vt100_cmd(vt_parser, 0);
1491 #endif
1492   }
1493 }
1494 
request_locator(vt_parser_t * vt_parser)1495 static void request_locator(vt_parser_t *vt_parser) {
1496   if (HAS_XTERM_LISTENER(vt_parser, request_locator)) {
1497     vt_mouse_report_mode_t orig;
1498 
1499 #if 0
1500     stop_vt100_cmd(vt_parser, 0);
1501 #endif
1502 
1503     if (vt_parser->mouse_mode < LOCATOR_CHARCELL_REPORT) {
1504       orig = vt_parser->mouse_mode;
1505       vt_parser->mouse_mode = LOCATOR_CHARCELL_REPORT;
1506     } else {
1507       orig = 0;
1508     }
1509 
1510     vt_parser->locator_mode |= LOCATOR_REQUEST;
1511 
1512     (*vt_parser->xterm_listener->request_locator)(vt_parser->xterm_listener->self);
1513 
1514     vt_parser->locator_mode &= ~LOCATOR_REQUEST;
1515 
1516     if (orig) {
1517       vt_parser->mouse_mode = orig;
1518     }
1519 
1520 #if 0
1521     start_vt100_cmd(vt_parser, 0);
1522 #endif
1523   }
1524 }
1525 
parse_title(vt_parser_t * vt_parser,char * src)1526 static char *parse_title(vt_parser_t *vt_parser, char *src /* the caller should allocated */) {
1527   size_t len;
1528   ef_parser_t *parser;
1529   vt_char_encoding_t src_encoding;
1530   ef_char_t ch;
1531   u_int num_chars;
1532   vt_char_encoding_t dst_encoding;
1533   char *dst;
1534 
1535   if (src == NULL) {
1536     return NULL;
1537   }
1538 
1539   dst = NULL;
1540 
1541   len = strlen(src);
1542   if (vt_parser->set_title_using_hex) {
1543     if (!(len = bl_hex_decode(src, src, len))) {
1544 #ifdef DEBUG
1545       bl_debug_printf("Failed to decode %s as hex string.\n", src);
1546 #endif
1547       goto end;
1548     }
1549     src[len] = '\0';
1550   }
1551 
1552   if (vt_parser->set_title_using_utf8 ? vt_parser->encoding == VT_UTF8 : 1) {
1553     parser = vt_parser->cc_parser;
1554     src_encoding = vt_parser->encoding;
1555   } else {
1556     if (!(parser = vt_char_encoding_parser_new(VT_UTF8))) {
1557       goto end;
1558     }
1559     src_encoding = VT_UTF8;
1560   }
1561 
1562   (*parser->init)(parser);
1563   (*parser->set_str)(parser, src, len);
1564   num_chars = 0;
1565   while ((*parser->next_char)(parser, &ch)) {
1566     if ((ef_bytes_to_int(ch.ch, ch.size) & ~0x80) < 0x20) {
1567 #ifdef DEBUG
1568       bl_debug_printf("%s which contains control characters is ignored for window title.\n",
1569                       src);
1570 #endif
1571       goto end;
1572     }
1573     num_chars++;
1574   }
1575 
1576   /* locale encoding */
1577   dst_encoding = vt_get_char_encoding("auto");
1578 
1579   if (src_encoding == dst_encoding) {
1580     return src;
1581   }
1582 
1583   if ((dst = malloc(num_chars * UTF_MAX_SIZE + 1))) {
1584     (*parser->init)(parser);
1585     (*parser->set_str)(parser, src, len);
1586     len = vt_char_encoding_convert_with_parser(dst, num_chars * UTF_MAX_SIZE, dst_encoding,
1587                                                parser);
1588     dst[len] = '\0';
1589 
1590     if (parser != vt_parser->cc_parser) {
1591       (*parser->destroy)(parser);
1592     }
1593   }
1594 
1595 end:
1596   free(src);
1597 
1598   return dst;
1599 }
1600 
set_window_name(vt_parser_t * vt_parser,u_char * name)1601 static void set_window_name(vt_parser_t *vt_parser,
1602                             u_char *name /* should be malloc'ed or NULL. */
1603                             ) {
1604   free(vt_parser->win_name);
1605   vt_parser->win_name = name;
1606 
1607   if (HAS_XTERM_LISTENER(vt_parser, set_window_name)) {
1608 #if 0
1609     stop_vt100_cmd(vt_parser, 0);
1610 #endif
1611     (*vt_parser->xterm_listener->set_window_name)(vt_parser->xterm_listener->self, name);
1612 #if 0
1613     start_vt100_cmd(vt_parser, 0);
1614 #endif
1615   }
1616 }
1617 
set_icon_name(vt_parser_t * vt_parser,u_char * name)1618 static void set_icon_name(vt_parser_t *vt_parser,
1619                           u_char *name /* should be malloc'ed or NULL. */
1620                           ) {
1621   free(vt_parser->icon_name);
1622   vt_parser->icon_name = name;
1623 
1624   if (HAS_XTERM_LISTENER(vt_parser, set_icon_name)) {
1625 #if 0
1626     stop_vt100_cmd(vt_parser, 0);
1627 #endif
1628     (*vt_parser->xterm_listener->set_icon_name)(vt_parser->xterm_listener->self, name);
1629 #if 0
1630     start_vt100_cmd(vt_parser, 0);
1631 #endif
1632   }
1633 }
1634 
switch_im_mode(vt_parser_t * vt_parser)1635 static void switch_im_mode(vt_parser_t *vt_parser) {
1636   if (HAS_XTERM_LISTENER(vt_parser, switch_im_mode)) {
1637 #if 0
1638     stop_vt100_cmd(vt_parser, 0);
1639 #endif
1640     (*vt_parser->xterm_listener->switch_im_mode)(vt_parser->xterm_listener->self);
1641 #if 0
1642     start_vt100_cmd(vt_parser, 0);
1643 #endif
1644   }
1645 }
1646 
im_is_active(vt_parser_t * vt_parser)1647 static int im_is_active(vt_parser_t *vt_parser) {
1648   if (HAS_XTERM_LISTENER(vt_parser, im_is_active)) {
1649     return (*vt_parser->xterm_listener->im_is_active)(vt_parser->xterm_listener->self);
1650   } else {
1651     return 0;
1652   }
1653 }
1654 
set_modkey_mode(vt_parser_t * vt_parser,int key,int mode)1655 static void set_modkey_mode(vt_parser_t *vt_parser, int key, int mode) {
1656   if (key == 1) {
1657     if (-1 <= mode && mode <= 3) {
1658       vt_parser->modify_cursor_keys = mode;
1659     }
1660   } else if (key == 2) {
1661     if (-1 <= mode && mode <= 3) {
1662       vt_parser->modify_function_keys = mode;
1663     }
1664   } else if (key == 4) {
1665     if (0 <= mode && mode <= 2) {
1666       vt_parser->modify_other_keys = mode;
1667     }
1668   }
1669 }
1670 
report_window_size(vt_parser_t * vt_parser,int by_char)1671 static void report_window_size(vt_parser_t *vt_parser, int by_char) {
1672   if (HAS_XTERM_LISTENER(vt_parser, get_window_size)) {
1673     int ps;
1674     u_int width;
1675     u_int height;
1676     char seq[5 + 1 /* ps */ + DIGIT_STR_LEN(u_int) * 2 + 1];
1677 
1678     if (by_char) {
1679       width = vt_screen_get_logical_cols(vt_parser->screen);
1680       height = vt_screen_get_logical_rows(vt_parser->screen);
1681       ps = 8;
1682     } else {
1683       (*vt_parser->xterm_listener->get_window_size)(vt_parser->xterm_listener->self,
1684                                                     &width, &height);
1685       ps = 4;
1686     }
1687 
1688     sprintf(seq, "\x1b[%d;%d;%dt", ps, height, width);
1689     vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
1690   }
1691 }
1692 
report_display_size(vt_parser_t * vt_parser,int by_char)1693 static void report_display_size(vt_parser_t *vt_parser, int by_char) {
1694   if (HAS_XTERM_LISTENER(vt_parser, get_display_size)) {
1695     int ps;
1696     u_int width;
1697     u_int height;
1698     char seq[5 + 1 /* ps */ + DIGIT_STR_LEN(u_int) * 2 + 1];
1699 
1700     (*vt_parser->xterm_listener->get_display_size)(vt_parser->xterm_listener->self,
1701                                                    &width, &height);
1702 
1703     if (by_char) {
1704       u_int col_width;
1705       u_int line_height;
1706 
1707       if (!get_cell_size(vt_parser, &col_width, &line_height) ||
1708           (width /= col_width) == 0 || (height /= line_height) == 0) {
1709         return;
1710       }
1711 
1712       ps = 9;
1713     } else {
1714       ps = 5;
1715     }
1716 
1717     sprintf(seq, "\x1b[%d;%d;%dt", ps, height, width);
1718     vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
1719   }
1720 }
1721 
report_cell_size(vt_parser_t * vt_parser)1722 static void report_cell_size(vt_parser_t *vt_parser) {
1723   u_int col_width;
1724   u_int line_height;
1725 
1726   if (get_cell_size(vt_parser, &col_width, &line_height)) {
1727     char seq[6 + DIGIT_STR_LEN(u_int) * 2 + 1];
1728 
1729     sprintf(seq, "\x1b[6;%d;%dt", line_height, col_width);
1730     vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
1731   }
1732 }
1733 
report_window_or_icon_name(vt_parser_t * vt_parser,int is_window)1734 static void report_window_or_icon_name(vt_parser_t *vt_parser, int is_window) {
1735   char *seq;
1736   char *pre;
1737   char *title;
1738   size_t len;
1739   vt_char_encoding_t src_encoding;
1740   vt_char_encoding_t dst_encoding;
1741 
1742   if (is_window) {
1743     title = vt_parser->win_name;
1744     pre = "\x1b]l";
1745   } else {
1746     title = vt_parser->icon_name;
1747     pre = "\x1b]L";
1748   }
1749 
1750   if (!title) {
1751     title = "";
1752   }
1753 
1754   /* see parse_title() */
1755   src_encoding = vt_get_char_encoding("auto");
1756 
1757   if (vt_parser->get_title_using_utf8) {
1758     dst_encoding = VT_UTF8;
1759   } else {
1760     dst_encoding = vt_parser->encoding;
1761   }
1762 
1763   len = strlen(title);
1764 
1765   if (src_encoding != dst_encoding) {
1766     char *p;
1767 
1768     if (!(p = alloca(len * UTF_MAX_SIZE + 1))) {
1769       goto error;
1770     }
1771 
1772     len = vt_char_encoding_convert(p, len * UTF_MAX_SIZE, dst_encoding,
1773                                    title, len, src_encoding);
1774     title = p;
1775   }
1776 
1777   if (!(seq = alloca(3 + len * (vt_parser->get_title_using_hex ? 2 : 1) + 3))) {
1778     goto error;
1779   }
1780 
1781   strcpy(seq, pre);
1782 
1783   if (vt_parser->get_title_using_hex) {
1784     len = bl_hex_encode(seq + 3, title, len);
1785   } else {
1786     memcpy(seq + 3, title, len);
1787   }
1788   strcpy(seq + 3 + len, "\x1b\\");
1789   vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
1790 
1791   return;
1792 
1793 error:
1794   vt_write_to_pty(vt_parser->pty, pre, 3);
1795   vt_write_to_pty(vt_parser->pty, "\x1b\\", 2);
1796 }
1797 
set_presentation_state(vt_parser_t * vt_parser,char * seq)1798 static void set_presentation_state(vt_parser_t *vt_parser, char *seq) {
1799   int row;
1800   int col;
1801   int page;
1802   char rend;
1803   char attr;
1804   char flag;
1805 
1806   if (sscanf(seq, "%d;%d;%d;%c;%c;%c;0;2;@;BB%%5%%5\x1b\\",
1807              &row, &col, &page, &rend, &attr, &flag) == 6) {
1808     vt_screen_goto(vt_parser->screen, col - 1, row - 1);
1809 
1810     if (rend & 0x20) {
1811       vt_parser->is_reversed = ((rend & 0x8) == 0x8);
1812       vt_parser->is_blinking = ((rend & 0x4) == 0x4);
1813       vt_parser->line_style &= ~LS_UNDERLINE;
1814       vt_parser->line_style |= ((rend & 0x2) ? LS_UNDERLINE_SINGLE : 0);
1815       vt_parser->is_bold = ((rend & 0x1) == 0x1);
1816     }
1817 
1818     if (attr & 0x20) {
1819       vt_parser->is_protected = ((attr & 0x1) == 0x1);
1820     }
1821 
1822     if (flag & 0x20) {
1823       vt_screen_set_auto_wrap(vt_parser->screen, ((flag & 0x8) == 0x8));
1824       vt_screen_set_relative_origin(vt_parser->screen, ((flag & 0x1) == 0x1));
1825     }
1826   }
1827 }
1828 
report_presentation_state(vt_parser_t * vt_parser,int ps)1829 static void report_presentation_state(vt_parser_t *vt_parser, int ps) {
1830   if (ps == 1) {
1831     /* DECCIR */
1832     char seq[DIGIT_STR_LEN(u_int) * 3 + 31];
1833     int rend = 0x40;
1834     int attr = 0x40;
1835     int flag = 0x40;
1836 
1837     if (vt_parser->is_reversed) {
1838       rend |= 0x8;
1839     }
1840     if (vt_parser->is_blinking) {
1841       rend |= 0x4;
1842     }
1843     if (vt_parser->line_style & LS_UNDERLINE) {
1844       rend |= 0x2;
1845     }
1846     if (vt_parser->is_bold) {
1847       rend |= 0x1;
1848     }
1849 
1850     if (vt_parser->is_protected) {
1851       attr |= 0x1;
1852     }
1853 
1854     if (vt_screen_is_auto_wrap(vt_parser->screen)) {
1855       flag |= 0x8;
1856     }
1857     if (vt_screen_is_relative_origin(vt_parser->screen)) {
1858       flag |= 0x1;
1859     }
1860 
1861     sprintf(seq, "\x1bP1$u%d;%d;%d;%c;%c;%c;0;2;@;BB%%5%%5\x1b\\",
1862             vt_screen_cursor_logical_row(vt_parser->screen) + 1,
1863             vt_screen_cursor_logical_col(vt_parser->screen) + 1,
1864             vt_screen_get_page_id(vt_parser->screen) + 1, rend, attr, flag);
1865 
1866     vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
1867   } else if (ps == 2) {
1868     /* DECTABSR */
1869     char *seq;
1870     u_int max_digit_len = 1;
1871     u_int cols = vt_screen_get_logical_cols(vt_parser->screen);
1872     u_int tmp = cols;
1873 
1874     while ((tmp /= 10) > 0) {
1875       max_digit_len++;
1876     }
1877 
1878     if ((seq = alloca(5 + (max_digit_len + 1) * cols + 3))) {
1879       u_int count;
1880       char *p;
1881 
1882       p = strcpy(seq, "\x1bP2$u") + 5;
1883 
1884       for (count = 0; count < cols; count++) {
1885         if (vt_screen_is_tab_stop(vt_parser->screen, count)) {
1886           sprintf(p, "%d/", count + 1);
1887           p += strlen(p);
1888         }
1889       }
1890 
1891       if (p != seq + 5) {
1892         p--;
1893       }
1894 
1895       strcpy(p, "\x1b\\");
1896 
1897       vt_write_to_pty(vt_parser->pty, seq, p + 2 - seq);
1898     }
1899   }
1900 }
1901 
report_color_table(vt_parser_t * vt_parser,int pu)1902 static void report_color_table(vt_parser_t *vt_parser, int pu) {
1903   int color;
1904   u_int8_t r, g, b;
1905   char seq[5+(3*5+4)*256+255+3];
1906   char *p;
1907 
1908   p = strcpy(seq, "\x1bP2$s") + 5;
1909 
1910   if (pu == 2) {
1911     /* RGB */
1912     for (color = 0; color < 256; color++) {
1913       vt_get_color_rgba(color, &r, &g, &b, NULL);
1914       sprintf(p, "%d;2;%d;%d;%d/", color, r * 100 / 255, g * 100 / 255, b * 100 / 255);
1915       p += strlen(p);
1916     }
1917   } else if (pu == 1) {
1918     /* HLS */
1919     int h, l, s;
1920     for (color = 0; color < 256; color++) {
1921       vt_get_color_rgba(color, &r, &g, &b, NULL);
1922       bl_rgb_to_hls(&h, &l, &s, r, g, b);
1923       sprintf(p, "%d;1;%d;%d;%d/", color, h, l, s);
1924       p += strlen(p);
1925     }
1926   } else {
1927     return;
1928   }
1929 
1930   strcpy(p - 1, "\x1b\\");
1931 
1932   vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
1933 }
1934 
1935 #ifndef NO_IMAGE
cursor_char_is_picture_and_modified(vt_screen_t * screen)1936 static int cursor_char_is_picture_and_modified(vt_screen_t *screen) {
1937   vt_line_t *line;
1938   vt_char_t *ch;
1939 
1940   if ((line = vt_screen_get_cursor_line(screen)) && vt_line_is_modified(line) &&
1941       (ch = vt_char_at(line, vt_screen_cursor_char_index(screen))) && vt_get_picture_char(ch)) {
1942     return 1;
1943   } else {
1944     return 0;
1945   }
1946 }
1947 
1948 /* Don't call this if SIXEL_NO_SCROLLING is false. */
check_sixel_anim(vt_screen_t * screen,u_char * str,size_t left)1949 static int check_sixel_anim(vt_screen_t *screen, u_char *str, size_t left) {
1950   vt_line_t *line = vt_screen_get_line(screen, 0); /* Always non-NULL */
1951   vt_char_t *ch;
1952 
1953   if ((ch = vt_char_at(line, 0)) && vt_get_picture_char(ch)) {
1954     while (--left > 0) {
1955       if (*(++str) == '\x1b') {
1956         if (--left == 0) {
1957           break;
1958         }
1959 
1960         if (*(++str) == 'P') {
1961           /* It seems animation sixel. */
1962           return 1;
1963         }
1964       }
1965     }
1966   }
1967 
1968   return 0;
1969 }
1970 
show_picture(vt_parser_t * vt_parser,char * file_path,int clip_beg_col,int clip_beg_row,int clip_cols,int clip_rows,int img_cols,int img_rows,int keep_aspect,int is_sixel)1971 static void show_picture(vt_parser_t *vt_parser, char *file_path, int clip_beg_col,
1972                          int clip_beg_row, int clip_cols, int clip_rows,
1973                          int img_cols, int img_rows, int keep_aspect,
1974                          int is_sixel /* 0: not sixel, 1: sixel, 2: sixel anim, 3: macro */
1975                          ) {
1976 #ifndef DONT_OPTIMIZE_DRAWING_PICTURE
1977   if (is_sixel == 2) {
1978     (*vt_parser->xterm_listener->show_tmp_picture)(vt_parser->xterm_listener->self, file_path);
1979 
1980     vt_parser->yield = 1;
1981 
1982     return;
1983   }
1984 #endif /* DONT_OPTIMIZE_DRAWING_PICTURE */
1985 
1986   if (HAS_XTERM_LISTENER(vt_parser, get_picture_data)) {
1987     vt_char_t *data;
1988 
1989 #ifdef __DEBUG
1990     struct timeval tv1, tv2;
1991     gettimeofday(&tv1, NULL);
1992 #endif
1993 
1994     if ((data = (*vt_parser->xterm_listener->get_picture_data)(
1995                     vt_parser->xterm_listener->self, file_path, &img_cols, &img_rows, NULL, NULL,
1996                     is_sixel ? &vt_parser->sixel_palette : NULL, keep_aspect, 0)) &&
1997         clip_beg_row < img_rows && clip_beg_col < img_cols) {
1998       vt_char_t *p;
1999       int row;
2000       int cursor_col;
2001       int orig_auto_wrap;
2002 
2003       /* Flush buffer before vt_screen_overwrite_chars(picture data). */
2004       flush_buffer(vt_parser);
2005 
2006 #ifdef __DEBUG
2007       gettimeofday(&tv2, NULL);
2008       bl_debug_printf("Processing sixel time (msec) %lu - %lu = %lu\n",
2009                       tv2.tv_sec * 1000 + tv2.tv_usec / 1000,
2010                       tv1.tv_sec * 1000 + tv1.tv_usec / 1000,
2011                       tv2.tv_sec * 1000 + tv2.tv_usec / 1000 -
2012                       tv1.tv_sec * 1000 - tv1.tv_usec / 1000);
2013 #endif
2014 
2015       if (clip_cols == 0) {
2016         clip_cols = img_cols - clip_beg_col;
2017       }
2018 
2019       if (clip_rows == 0) {
2020         clip_rows = img_rows - clip_beg_row;
2021       }
2022 
2023       if (clip_beg_row + clip_rows > img_rows) {
2024         clip_rows = img_rows - clip_beg_row;
2025       }
2026 
2027       if (clip_beg_col + clip_cols > img_cols) {
2028         clip_cols = img_cols - clip_beg_col;
2029       }
2030 
2031       p = data + (img_cols * clip_beg_row);
2032       row = 0;
2033 
2034       if (is_sixel && !vt_parser->sixel_scrolling) {
2035         vt_screen_save_cursor(vt_parser->screen); /* XXX */
2036         vt_parser->is_visible_cursor = 0;
2037         vt_screen_goto_home(vt_parser->screen);
2038         vt_screen_goto_beg_of_line(vt_parser->screen);
2039       }
2040 
2041       if (cursor_char_is_picture_and_modified(vt_parser->screen)) {
2042         /* Perhaps it is animation. */
2043         interrupt_vt100_cmd(vt_parser);
2044         vt_parser->yield = 1;
2045       }
2046 
2047       orig_auto_wrap = vt_screen_is_auto_wrap(vt_parser->screen);
2048       vt_screen_set_auto_wrap(vt_parser->screen, 0);
2049       cursor_col = vt_screen_cursor_logical_col(vt_parser->screen);
2050 
2051       while (1) {
2052         vt_screen_overwrite_chars(vt_parser->screen, p + clip_beg_col, clip_cols);
2053 
2054         if (++row >= clip_rows) {
2055           break;
2056         }
2057 
2058         if (is_sixel && !vt_parser->sixel_scrolling) {
2059           if (!vt_screen_go_downward(vt_parser->screen, 1)) {
2060             break;
2061           }
2062         } else {
2063           vt_screen_line_feed(vt_parser->screen);
2064         }
2065 
2066         vt_screen_go_horizontally(vt_parser->screen, cursor_col);
2067 
2068         p += img_cols;
2069       }
2070 
2071       if (is_sixel) {
2072         if (vt_parser->sixel_scrolling) {
2073           if (!CURSOR_TO_RIGHT_OF_SIXEL(vt_parser) &&
2074               /* mlterm always enables DECSET 8452 in status line. */
2075               !vt_status_line_is_focused(vt_parser->screen)) {
2076             vt_screen_line_feed(vt_parser->screen);
2077             vt_screen_go_horizontally(vt_parser->screen, cursor_col);
2078           }
2079         } else {
2080           vt_screen_restore_cursor(vt_parser->screen); /* XXX */
2081           vt_parser->is_visible_cursor = 1;
2082         }
2083       }
2084 
2085       vt_str_destroy(data, img_cols * img_rows);
2086 
2087       vt_screen_set_auto_wrap(vt_parser->screen, orig_auto_wrap);
2088 
2089       if (strstr(file_path, "://")) {
2090         /* Showing remote image is very heavy. */
2091         vt_parser->yield = 1;
2092       }
2093     }
2094   }
2095 }
2096 
define_drcs_picture(vt_parser_t * vt_parser,char * path,ef_charset_t cs,int idx,u_int pix_width,u_int pix_height,u_int col_width,u_int line_height)2097 static void define_drcs_picture(vt_parser_t *vt_parser, char *path, ef_charset_t cs, int idx,
2098                                 u_int pix_width /* can be 0 */, u_int pix_height /* can be 0 */,
2099                                 u_int col_width, u_int line_height) {
2100   if (HAS_XTERM_LISTENER(vt_parser, get_picture_data)) {
2101     vt_char_t *data;
2102     int cols = 0;
2103     int rows = 0;
2104     int cols_small = 0;
2105     int rows_small = 0;
2106 
2107     if (pix_width > 0 && pix_height > 0) {
2108       if (old_drcs_sixel) {
2109         cols = pix_width / col_width;
2110         rows = pix_height / line_height;
2111       } else {
2112         cols = (pix_width + col_width - 1) / col_width;
2113         rows = (pix_height + line_height - 1) / line_height;
2114       }
2115     }
2116 
2117     if (idx <= 0x5f &&
2118         (data = (*vt_parser->xterm_listener->get_picture_data)(vt_parser->xterm_listener->self,
2119                                                                path, &cols, &rows, &cols_small,
2120                                                                &rows_small, NULL, 0, 1))) {
2121       u_int pages;
2122       u_int offset = 0;
2123       vt_drcs_font_t *font;
2124 
2125       if (!vt_parser->drcs) {
2126         vt_parser->drcs = vt_drcs_new();
2127       }
2128 
2129       if (!old_drcs_sixel) {
2130         cols_small = cols;
2131         rows_small = rows;
2132       }
2133 
2134       for (pages = (cols_small * rows_small + 95) / 96; pages > 0; pages--) {
2135         font = vt_drcs_get_font(vt_parser->drcs, cs, 1);
2136 
2137         vt_drcs_add_picture(font, vt_char_picture_id(vt_get_picture_char(data)),
2138                             offset, idx, cols, rows, cols_small, rows_small);
2139 
2140         offset += (96 - idx);
2141         idx = 0;
2142 
2143         if (cs == CS94SB_ID(0x7e)) {
2144           cs = CS96SB_ID(0x30);
2145         } else if (cs == CS96SB_ID(0x7e)) {
2146           cs = CS94SB_ID(0x30);
2147         } else {
2148           cs++;
2149         }
2150       }
2151 
2152       vt_str_destroy(data, cols * rows);
2153     }
2154   }
2155 }
2156 
2157 /*
2158  * The 2nd \x1bP...\x1b\\ in the following sequence (e.g. biplane.six)
2159  * overwrites the image of the 1st \x1bP...\x1b\\.
2160  * (\x1b[8k moves the cursor upward after drawing the 1st image.)
2161  * \x1bP...\x1b\\\x1b[8k\x1bP;1...\x1b\\
2162  *                            ^-> Pixel positions specified as 0 remain at their current color
2163  *
2164  * -> The 1st and 2nd sequences are saved in the same file and
2165  *    load_sixel_from_data() in c_sixel.c draws them at once.
2166  *
2167  * XXX
2168  * return -1 if len == 0, and wait for the next sequence.
2169  */
is_separated_sixel(u_char * str,size_t len)2170 static int is_separated_sixel(u_char *str, size_t len) {
2171   if (len >= 3) {
2172     if (*str == '\x1d') {
2173       str++;
2174       len--;
2175     } else if (*str == '\x1b' && *(str + 1) == ']') {
2176       str += 2;
2177       len -= 2;
2178     } else {
2179       return 0;
2180     }
2181 
2182     while ('0' <= *str && *str <= '9') {
2183       str++;
2184       if (--len == 0) {
2185         return 0;
2186       }
2187     }
2188 
2189     if (*str == 'A' || *str == 'k') {
2190       u_int count = 0;
2191 
2192       str++;
2193       len--;
2194       while (len >= 3 /* \x90;1 => 3 */) {
2195         if (*str == '\x1b' && *(str + 1) == 'P') {
2196           str += 2;
2197           len -= 2;
2198         } else {
2199           len--;
2200           if (*(str++) != 0x90) {
2201             goto next_loop;
2202           }
2203         }
2204 
2205         if ('0' <= *str && *str <= '9') {
2206           str++;
2207           len--;
2208         }
2209 
2210         if (len >= 2 && *str == ';') {
2211           if (*(++str) == '1') {
2212             return 1;
2213           }
2214           len--;
2215         }
2216 
2217       next_loop:
2218         /*
2219          * It is assumed that the distance between 1st and 2nd sequence is
2220          * less than about 10 bytes.
2221          */
2222         if (++count >= 10) {
2223           break;
2224         }
2225       }
2226     }
2227   }
2228 
2229   return 0;
2230 }
2231 
2232 static int increment_str(u_char **str, size_t *left);
2233 
save_sixel_or_regis(vt_parser_t * vt_parser,char * path,u_char * dcs_beg,u_char ** body,size_t * body_len)2234 static int save_sixel_or_regis(vt_parser_t *vt_parser, char *path, u_char *dcs_beg,
2235                                u_char **body /* q ... */ , size_t *body_len) {
2236   u_char *str_p = *body;
2237   size_t left = *body_len;
2238   int is_end;
2239   FILE *fp;
2240 
2241   if (left > 2 && *(str_p + 1) == '\0') {
2242     fp = fopen(path, "a");
2243     is_end = *(str_p + 2);
2244     /*
2245      * dcs_beg will equal to str_p after str_p is
2246      * incremented by the following increment_str().
2247      */
2248     dcs_beg = (str_p += 2) + 1;
2249     left -= 2;
2250   } else {
2251     char *format;
2252     vt_color_t color;
2253     u_int8_t red;
2254     u_int8_t green;
2255     u_int8_t blue;
2256 
2257     fp = fopen(path, "w");
2258     is_end = 0;
2259 
2260     if (strcmp(path + strlen(path) - 4, ".rgs") == 0) {
2261       /* Clear background by VT_BG_COLOR */
2262 
2263       /* 13 + 3*3 + 1 = 23 */
2264       format = "S(I(R%dG%dB%d))S(E)";
2265       color = VT_BG_COLOR;
2266     } else if (strcmp(path + strlen(path) - 4, ".six") == 0) {
2267       /*
2268        * Set VT_FG_COLOR to the default value of the first entry of the sixel palette,
2269        * because some sixel graphics data has not palette definitions (#0;2;r;g;b).
2270        * '9' is a mark which means that this definition is added by mlterm itself.
2271        * (see c_sixel.c)
2272        */
2273 
2274       /* 7 + 3*3 + 1 = 17 */
2275       format = "#0;9;%d;%d;%d";
2276       color = VT_FG_COLOR;
2277     } else {
2278       format = NULL;
2279     }
2280 
2281     if (format && HAS_XTERM_LISTENER(vt_parser, get_rgb) &&
2282         (*vt_parser->xterm_listener->get_rgb)(vt_parser->xterm_listener->self, &red,
2283                                               &green, &blue, color)) {
2284       char color_seq[23];
2285 
2286       if (color == VT_FG_COLOR) {
2287         /* sixel */
2288         red = red * 100 / 255;
2289         green = green * 100 / 255;
2290         blue = blue * 100 / 255;
2291       }
2292 
2293       fwrite(dcs_beg, 1, str_p - dcs_beg + 1, fp);
2294       sprintf(color_seq, format, red, green, blue);
2295       fwrite(color_seq, 1, strlen(color_seq), fp);
2296       dcs_beg = str_p + 1;
2297     }
2298 
2299     /*
2300      * +1 in case str_p[left - vt_parser->r_buf.new_len]
2301      * points "\\" of "\x1b\\".
2302      */
2303     if (left > vt_parser->r_buf.new_len + 1) {
2304       str_p += (left - vt_parser->r_buf.new_len - 1);
2305       left = vt_parser->r_buf.new_len + 1;
2306     }
2307   }
2308 
2309   while (1) {
2310     if (!increment_str(&str_p, &left)) {
2311       if (is_end == 2) {
2312         left++;
2313         break;
2314       }
2315 
2316       if (vt_parser->logging_vt_seq && use_ttyrec_format) {
2317         fclose(fp);
2318         free(path);
2319 
2320         *body = str_p;
2321         *body_len = left;
2322 
2323         return 0;
2324       }
2325 
2326       fwrite(dcs_beg, 1, str_p - dcs_beg + 1, fp);
2327 
2328       vt_parser->r_buf.left = 0;
2329       if (!receive_bytes(vt_parser)) {
2330         fclose(fp);
2331         memcpy(vt_parser->r_buf.chars,
2332                strcmp(path + strlen(path) - 4, ".six") == 0 ? "\x1bPq\0" : "\x1bPp\0", 4);
2333         free(path);
2334         vt_parser->r_buf.chars[4] = is_end;
2335         vt_parser->r_buf.filled_len = vt_parser->r_buf.left = 5;
2336 
2337         /* No more data in pty. */
2338         vt_parser->yield = 1;
2339 
2340         *body = str_p;
2341         *body_len = left;
2342 
2343         return 0;
2344       }
2345 
2346       dcs_beg = str_p = CURRENT_STR_P(vt_parser);
2347       left = vt_parser->r_buf.left;
2348     }
2349 
2350     if (is_end == 2) {
2351       if (is_separated_sixel(str_p, left)) {
2352         /* continued (XXX Hack for biplane.six) */
2353         is_end = 0;
2354       } else {
2355         str_p--;
2356         left++;
2357         break;
2358       }
2359     }
2360     /*
2361      * 0x9c is regarded as ST here, because sixel sequence
2362      * unuses it certainly.
2363      */
2364     else if (*str_p == 0x9c) {
2365       is_end = 2;
2366     } else if (*str_p == CTL_ESC) {
2367       is_end = 1;
2368     } else if (is_end == 1) {
2369       if (*str_p == '\\') {
2370         is_end = 2;
2371       } else {
2372         is_end = 0;
2373       }
2374     }
2375   }
2376 
2377   fwrite(dcs_beg, 1, str_p - dcs_beg + 1, fp);
2378   fclose(fp);
2379 
2380   *body = str_p;
2381   *body_len = left;
2382 
2383   return 1;
2384 }
2385 
check_cell_size(vt_parser_t * vt_parser,u_int col_width,u_int line_height)2386 static int check_cell_size(vt_parser_t *vt_parser, u_int col_width, u_int line_height) {
2387   u_int cw;
2388   u_int lh;
2389 
2390   /*
2391    * XXX
2392    * This works except vertical mode, but no problem because images are not
2393    * supported on vertical mode.
2394    */
2395   if (get_cell_size(vt_parser, &cw, &lh) && cw == col_width && lh == line_height) {
2396     return 1;
2397   } else {
2398     return 0;
2399   }
2400 }
2401 #endif
2402 
snapshot(vt_parser_t * vt_parser,vt_char_encoding_t encoding,char * file_name,vt_write_content_area_t area)2403 static void snapshot(vt_parser_t *vt_parser, vt_char_encoding_t encoding,
2404                      char *file_name, vt_write_content_area_t area) {
2405   char buf[16];
2406   char *path;
2407   int fd;
2408   ef_conv_t *conv;
2409 
2410   if (!(path = get_home_file_path(file_name, get_now_suffix(buf), "snp"))) {
2411     return;
2412   }
2413 
2414   fd = open(path, O_WRONLY | O_CREAT, 0600);
2415   free(path);
2416   if (fd == -1) {
2417 #ifdef DEBUG
2418     bl_debug_printf(BL_DEBUG_TAG " Failed to open %s\n", file_name);
2419 #endif
2420 
2421     return;
2422   }
2423 
2424   if (encoding == VT_UNKNOWN_ENCODING || (conv = vt_char_encoding_conv_new(encoding)) == NULL) {
2425     conv = vt_parser->cc_conv;
2426   }
2427 
2428   vt_screen_write_content(vt_parser->screen, fd, conv, 0, area);
2429 
2430   if (conv != vt_parser->cc_conv) {
2431     (*conv->destroy)(conv);
2432   }
2433 
2434   close(fd);
2435 }
2436 
set_col_size_of_width_a(vt_parser_t * vt_parser,u_int col_size_a)2437 static void set_col_size_of_width_a(vt_parser_t *vt_parser, u_int col_size_a) {
2438   if (col_size_a == 1 || col_size_a == 2) {
2439 #ifdef USE_LIBSSH2
2440     /* Don't change it after vt_parser_set_pty() on mosh. See vt_parser_set_pty(). */
2441     if (!vt_parser->pty || vt_pty_get_mode(vt_parser->pty) != PTY_MOSH)
2442 #endif
2443     {
2444       vt_parser->col_size_of_width_a = col_size_a;
2445     }
2446   } else {
2447 #ifdef DEBUG
2448     bl_warn_printf(BL_DEBUG_TAG " col size should be 1 or 2. default value 1 is used.\n");
2449 #endif
2450 
2451     vt_parser->col_size_of_width_a = 1;
2452   }
2453 }
2454 
2455 /*
2456  * This function will destroy the content of pt.
2457  */
config_protocol_set(vt_parser_t * vt_parser,char * pt,int save)2458 static void config_protocol_set(vt_parser_t *vt_parser, char *pt, int save) {
2459   int ret;
2460   char *dev;
2461 
2462   ret = vt_parse_proto_prefix(&dev, &pt, save);
2463   if (ret <= 0) {
2464     /*
2465      * ret == -1: do_challenge failed.
2466      * ret ==  0: illegal format.
2467      * to_menu is necessarily 0 because it is pty that msg should be returned
2468      * to.
2469      */
2470     vt_response_config(vt_parser->pty, ret < 0 ? "forbidden" : "error", NULL, 0);
2471 
2472     return;
2473   }
2474 
2475   if (dev && strlen(dev) <= 7 && strstr(dev, "font")) {
2476     char *key;
2477     char *val;
2478 
2479     if (vt_parse_proto(NULL, &key, &val, &pt, 0, 0) && val &&
2480         HAS_CONFIG_LISTENER(vt_parser, set_font)) {
2481 /*
2482  * Screen is redrawn not in vt_parser->config_listener->set_font
2483  * but in stop_vt100_cmd, so it is not necessary to hold
2484  * vt_parser->config_listener->set_font between stop_vt100_cmd and
2485  * start_vt100_cmd.
2486  */
2487 #if 0
2488       stop_vt100_cmd(vt_parser, 0);
2489 #endif
2490 
2491       (*vt_parser->config_listener->set_font)(vt_parser->config_listener->self, dev, key, val,
2492                                               save);
2493 
2494 #if 0
2495       start_vt100_cmd(vt_parser, 0);
2496 #endif
2497     }
2498   } else if (dev && strcmp(dev, "color") == 0) {
2499     char *key;
2500     char *val;
2501 
2502     if (vt_parse_proto(NULL, &key, &val, &pt, 0, 0) && val &&
2503         HAS_CONFIG_LISTENER(vt_parser, set_color)) {
2504 /*
2505  * Screen is redrawn not in vt_parser->config_listener->set_color
2506  * but in stop_vt100_cmd, so it is not necessary to hold
2507  * vt_parser->config_listener->set_font between stop_vt100_cmd and
2508  * start_vt100_cmd.
2509  */
2510 #if 0
2511       stop_vt100_cmd(vt_parser, 0);
2512 #endif
2513 
2514       (*vt_parser->config_listener->set_color)(vt_parser->config_listener->self, dev, key,
2515                                                val, save);
2516 
2517 #if 0
2518       start_vt100_cmd(vt_parser, 0);
2519 #endif
2520     }
2521   } else {
2522     stop_vt100_cmd(vt_parser, 0);
2523 
2524     if ((!HAS_CONFIG_LISTENER(vt_parser, exec) ||
2525          !(*vt_parser->config_listener->exec)(vt_parser->config_listener->self, pt))) {
2526       bl_conf_write_t *conf;
2527 
2528       if (save) {
2529         char *path;
2530 
2531         /* XXX */
2532         if ((path = bl_get_user_rc_path("mlterm/main")) == NULL) {
2533           return;
2534         }
2535 
2536         conf = bl_conf_write_open(path);
2537         free(path);
2538       } else {
2539         conf = NULL;
2540       }
2541 
2542       /* accept multiple key=value pairs. */
2543       while (pt) {
2544         char *key;
2545         char *val;
2546 
2547         if (!vt_parse_proto(dev ? NULL : &dev, &key, &val, &pt, 0, 1)) {
2548           break;
2549         }
2550 
2551         if (conf) {
2552           /* XXX */
2553           if (strcmp(key, "xim") != 0) {
2554             bl_conf_io_write(conf, key, val);
2555           }
2556         }
2557 
2558         if (val == NULL) {
2559           val = "";
2560         }
2561 
2562         if (HAS_CONFIG_LISTENER(vt_parser, set) &&
2563             (*vt_parser->config_listener->set)(vt_parser->config_listener->self, dev, key,
2564                                                   val)) {
2565           if (!vt_parser->config_listener) {
2566             /* pty changed. */
2567             break;
2568           }
2569         }
2570 
2571         dev = NULL;
2572       }
2573 
2574       if (conf) {
2575         bl_conf_write_close(conf);
2576 
2577         if (HAS_CONFIG_LISTENER(vt_parser, saved)) {
2578           (*vt_parser->config_listener->saved)();
2579         }
2580       }
2581     }
2582 
2583     start_vt100_cmd(vt_parser, 0);
2584   }
2585 }
2586 
config_protocol_set_simple(vt_parser_t * vt_parser,char * key,char * val,int visualize)2587 static void config_protocol_set_simple(vt_parser_t *vt_parser, char *key, char *val,
2588                                        int visualize) {
2589   if (HAS_CONFIG_LISTENER(vt_parser, set)) {
2590     if (visualize) {
2591       stop_vt100_cmd(vt_parser, 0);
2592     }
2593 
2594     (*vt_parser->config_listener->set)(vt_parser->config_listener->self, NULL, key, val);
2595 
2596     if (visualize) {
2597       start_vt100_cmd(vt_parser, 0);
2598     }
2599   }
2600 }
2601 
2602 /*
2603  * This function will destroy the content of pt.
2604  */
config_protocol_get(vt_parser_t * vt_parser,char * pt,int to_menu,int * flag)2605 static void config_protocol_get(vt_parser_t *vt_parser, char *pt, int to_menu, int *flag) {
2606   char *dev;
2607   char *key;
2608   int ret;
2609 
2610   if (to_menu == 0 && strchr(pt, ';') == NULL) {
2611     /* pt doesn't have challenge */
2612     to_menu = -1;
2613 
2614     stop_vt100_cmd(vt_parser, 0);
2615   }
2616 #if 0
2617   else {
2618     /*
2619      * It is assumed that screen is not redrawn not in
2620      * vt_parser->config_listener->get, so vt_parser->config_listener->get
2621      * is not held between stop_vt100_cmd and start_vt100_cmd.
2622      */
2623     stop_vt100_cmd(vt_parser, 0);
2624   }
2625 #endif
2626 
2627   ret = vt_parse_proto(&dev, &key, NULL, &pt, to_menu == 0, 0);
2628   if (ret <= 0) {
2629     /*
2630      * ret == -1: do_challenge failed.
2631      * ret ==  0: illegal format.
2632      * to_menu is necessarily 0 because it is pty that msg should be returned
2633      * to.
2634      */
2635     vt_response_config(vt_parser->pty, ret < 0 ? "forbidden" : "error", NULL, 0);
2636 
2637     goto end;
2638   }
2639 
2640   if (dev && strlen(dev) <= 7 && strstr(dev, "font")) {
2641     char *cs;
2642 
2643     /* Compat with old format (3.6.3 or before): <cs>,<fontsize> */
2644     cs = bl_str_sep(&key, ",");
2645 
2646     if (HAS_CONFIG_LISTENER(vt_parser, get_font)) {
2647       (*vt_parser->config_listener->get_font)(vt_parser->config_listener->self, dev, cs,
2648                                                  to_menu);
2649     }
2650   } else if (dev && strcmp(dev, "color") == 0) {
2651     if (HAS_CONFIG_LISTENER(vt_parser, get_color)) {
2652       (*vt_parser->config_listener->get_color)(vt_parser->config_listener->self, key,
2653                                                   to_menu);
2654     }
2655   } else if (HAS_CONFIG_LISTENER(vt_parser, get)) {
2656     (*vt_parser->config_listener->get)(vt_parser->config_listener->self, dev, key, to_menu);
2657   }
2658 
2659 end:
2660   if (to_menu == -1) {
2661     start_vt100_cmd(vt_parser, 0);
2662   }
2663 #if 0
2664   else {
2665     start_vt100_cmd(vt_parser, 0);
2666   }
2667 #endif
2668 }
2669 
2670 #ifdef SUPPORT_ITERM2_OSC1337
2671 
2672 #include <pobl/bl_path.h> /* bl_basename */
2673 
2674 /*
2675  * This function will destroy the content of pt.
2676  */
iterm2_proprietary_set(vt_parser_t * vt_parser,char * pt)2677 static void iterm2_proprietary_set(vt_parser_t *vt_parser, char *pt) {
2678   if (strncmp(pt, "File=", 5) == 0) {
2679     /* See https://www.iterm2.com/images.html (2014/03/20) */
2680 
2681     char *args;
2682     char *encoded;
2683     char *decoded;
2684     size_t e_len;
2685     u_int width;
2686     u_int height;
2687     char *path;
2688     int keep_aspect;
2689 
2690     args = pt + 5;
2691     width = height = 0;
2692     keep_aspect = 1; /* default value is 1. */
2693 
2694     if ((encoded = strchr(args, ':'))) {
2695       char *beg;
2696       char *end;
2697 
2698       *(encoded++) = '\0';
2699 
2700       if ((beg = strstr(args, "name=")) &&
2701           ((end = strchr((beg += 5), ';')) || (end = beg + strlen(beg))) &&
2702           (path = malloc(7 + (end - beg) + 1))) {
2703         char *file;
2704         char *new_path;
2705         size_t d_len;
2706 
2707         strcpy(path, "mlterm/");
2708 
2709         d_len = bl_base64_decode(path + 7, beg, end - beg);
2710         path[7 + d_len] = '\0';
2711         file = bl_basename(path);
2712         memmove(path + 7, file, strlen(file) + 1);
2713         new_path = bl_get_user_rc_path(path);
2714         free(path);
2715         path = new_path;
2716       } else {
2717         path = get_home_file_path("", vt_pty_get_slave_name(vt_parser->pty) + 5, "img");
2718       }
2719 
2720       if ((beg = strstr(args, "width=")) &&
2721           ((end = strchr((beg += 6), ';')) || (end = beg + strlen(beg)))) {
2722         *(end--) = '\0';
2723         if ('0' <= *end && *end <= '9') {
2724           width = atoi(beg);
2725         } else if (*end == '%') {
2726           /* XXX vertical mode is not considered */
2727           width = atoi(beg) * vt_screen_get_logical_cols(vt_parser->screen) / 100;
2728         } else {
2729           /* XXX Npx is not supported */
2730         }
2731         *(end + 1) = ';'; /* For next strstr() */
2732       }
2733 
2734       if ((beg = strstr(args, "height=")) &&
2735           ((end = strchr((beg += 7), ';')) || (end = beg + strlen(beg)))) {
2736         *(end--) = '\0';
2737         if ('0' <= *end && *end <= '9') {
2738           height = atoi(beg);
2739         } else if (*end == '%') {
2740           /* XXX vertical mode is not considered */
2741           height = atoi(beg) * vt_screen_get_logical_rows(vt_parser->screen) / 100;
2742         } else {
2743           /* XXX Npx is not supported */
2744         }
2745         /* *(end + 1) = ';'; */
2746       }
2747 
2748       if ((beg = strstr(args, "preserveAspectRatio=")) && beg[20] == '0') {
2749         keep_aspect = 0;
2750       }
2751     } else {
2752       encoded = args;
2753       path = get_home_file_path("", vt_pty_get_slave_name(vt_parser->pty) + 5, "img");
2754     }
2755 
2756     if (!path) {
2757       return;
2758     }
2759 #ifdef DEBUG
2760     else {
2761       bl_debug_printf(BL_DEBUG_TAG " OSC 1337 file is stored at %s.\n", path);
2762     }
2763 #endif
2764 
2765     if ((e_len = strlen(encoded)) > 0 && (decoded = malloc(e_len))) {
2766       size_t d_len;
2767       FILE *fp;
2768 
2769       if ((d_len = bl_base64_decode(decoded, encoded, e_len)) > 0 && (fp = fopen(path, "w"))) {
2770         fwrite(decoded, 1, d_len, fp);
2771         fclose(fp);
2772 
2773         show_picture(vt_parser, path, 0, 0, 0, 0, width, height, keep_aspect, 0);
2774 
2775         remove(path);
2776       }
2777 
2778       free(decoded);
2779     }
2780 
2781     free(path);
2782   }
2783 }
2784 #endif
2785 
change_char_fine_color(vt_parser_t * vt_parser,int * ps,int num)2786 static int change_char_fine_color(vt_parser_t *vt_parser, int *ps, int num) {
2787   int proceed;
2788   vt_color_t color;
2789 
2790   if (ps[0] != 38 && ps[0] != 48) {
2791     return 0;
2792   }
2793 
2794   if (num >= 3 && ps[1] == 5) {
2795     proceed = 3;
2796     color = (ps[2] <= 0 ? 0 : ps[2]);
2797   } else if (num >= 5 && ps[1] == 2) {
2798     proceed = 5;
2799     color = vt_get_closest_color(ps[2] <= 0 ? 0 : ps[2], ps[3] <= 0 ? 0 : ps[3],
2800                                  ps[4] <= 0 ? 0 : ps[4]);
2801   } else {
2802     return 1;
2803   }
2804 
2805   if (vt_parser->use_ansi_colors) {
2806     if (ps[0] == 38) {
2807       vt_parser->fg_color = color;
2808       vt_screen_set_bce_fg_color(vt_parser->screen, color);
2809     } else /* if( ps[0] == 48) */
2810     {
2811       vt_parser->bg_color = color;
2812       vt_screen_set_bce_bg_color(vt_parser->screen, color);
2813     }
2814   }
2815 
2816   return proceed;
2817 }
2818 
change_char_attr(vt_parser_t * vt_parser,int flag)2819 static void change_char_attr(vt_parser_t *vt_parser, int flag) {
2820   vt_color_t fg_color;
2821   vt_color_t bg_color;
2822 
2823   fg_color = vt_parser->fg_color;
2824   bg_color = vt_parser->bg_color;
2825 
2826   if (flag == 0) {
2827     /* Normal */
2828     fg_color = VT_FG_COLOR;
2829     bg_color = VT_BG_COLOR;
2830     vt_parser->is_bold = 0;
2831     vt_parser->is_italic = 0;
2832     vt_parser->line_style = 0;
2833     vt_parser->is_reversed = 0;
2834     vt_parser->is_blinking = 0;
2835     vt_parser->is_invisible = 0;
2836   } else if (flag == 1) {
2837     /* Bold */
2838     vt_parser->is_bold = 1;
2839   } else if (flag == 2) {
2840     /* XXX Faint */
2841     vt_parser->is_bold = 0;
2842   } else if (flag == 3) {
2843     /* Italic */
2844     vt_parser->is_italic = 1;
2845   } else if (flag == 4) {
2846     /* Underscore */
2847     vt_parser->line_style = (vt_parser->line_style & ~LS_UNDERLINE) | LS_UNDERLINE_SINGLE;
2848   } else if (flag == 5 || flag == 6) {
2849     /* Blink (6 is repidly blinking) */
2850     vt_parser->is_blinking = 1;
2851     vt_screen_enable_blinking(vt_parser->screen);
2852   } else if (flag == 7) {
2853     /* Inverse */
2854     vt_parser->is_reversed = 1;
2855   } else if (flag == 8) {
2856     vt_parser->is_invisible = 1;
2857   } else if (flag == 9) {
2858     vt_parser->line_style |= LS_CROSSED_OUT;
2859   } else if (flag == 21) {
2860     /* Double underscore */
2861     vt_parser->line_style = (vt_parser->line_style & ~LS_UNDERLINE) | LS_UNDERLINE_DOUBLE;
2862   } else if (flag == 22) {
2863     /* Bold */
2864     vt_parser->is_bold = 0;
2865   } else if (flag == 23) {
2866     /* Italic */
2867     vt_parser->is_italic = 0;
2868   } else if (flag == 24) {
2869     /* Underline */
2870     vt_parser->line_style &= ~LS_UNDERLINE;
2871   } else if (flag == 25) {
2872     /* blink */
2873     vt_parser->is_blinking = 0;
2874   } else if (flag == 27) {
2875     vt_parser->is_reversed = 0;
2876   } else if (flag == 28) {
2877     vt_parser->is_invisible = 0;
2878   } else if (flag == 29) {
2879     vt_parser->line_style &= ~LS_CROSSED_OUT;
2880   } else if (flag == 39) {
2881     /* default fg */
2882     fg_color = VT_FG_COLOR;
2883   } else if (flag == 49) {
2884     bg_color = VT_BG_COLOR;
2885   } else if (flag == 53) {
2886     vt_parser->line_style |= LS_OVERLINE;
2887   } else if (flag == 55) {
2888     vt_parser->line_style &= ~LS_OVERLINE;
2889   } else if (vt_parser->use_ansi_colors) {
2890     /* Color attributes */
2891 
2892     if (30 <= flag && flag <= 37) {
2893       /* 30=VT_BLACK(0) ... 37=VT_WHITE(7) */
2894       fg_color = flag - 30;
2895     } else if (40 <= flag && flag <= 47) {
2896       /* 40=VT_BLACK(0) ... 47=VT_WHITE(7) */
2897       bg_color = flag - 40;
2898     } else if (90 <= flag && flag <= 97) {
2899       fg_color = (flag - 90) | VT_BOLD_COLOR_MASK;
2900     } else if (100 <= flag && flag <= 107) {
2901       bg_color = (flag - 100) | VT_BOLD_COLOR_MASK;
2902     }
2903 #ifdef DEBUG
2904     else {
2905       bl_warn_printf(BL_DEBUG_TAG " unknown char attr flag(%d).\n", flag);
2906     }
2907 #endif
2908   }
2909 
2910   if (fg_color != vt_parser->fg_color) {
2911     vt_parser->fg_color = fg_color;
2912     vt_screen_set_bce_fg_color(vt_parser->screen, fg_color);
2913   }
2914 
2915   if (bg_color != vt_parser->bg_color) {
2916     vt_parser->bg_color = bg_color;
2917     vt_screen_set_bce_bg_color(vt_parser->screen, bg_color);
2918   }
2919 }
2920 
get_rgb(vt_parser_t * vt_parser,vt_color_t color)2921 static void get_rgb(vt_parser_t *vt_parser, vt_color_t color) {
2922   u_int8_t red;
2923   u_int8_t green;
2924   u_int8_t blue;
2925   int ret;
2926 
2927   if (IS_ALT_COLOR(color)) {
2928     ret = (HAS_XTERM_LISTENER(vt_parser, get_rgb) &&
2929            (*vt_parser->xterm_listener->get_rgb)(vt_parser->xterm_listener->self, &red, &green,
2930                                                  &blue, color));
2931     color = color + 0x100 - VT_BOLD_COLOR;
2932   } else {
2933     ret = vt_get_color_rgba(color, &red, &green, &blue, NULL);
2934   }
2935 
2936   if (ret) {
2937     char seq[4 + DIGIT_STR_LEN(int) + 1 + 18 + 2 + 1];
2938 
2939     sprintf(seq, "\x1b]4;%d;rgb:%.2x%.2x/%.2x%.2x/%.2x%.2x\x1b\\",
2940             color, red, red, green, green, blue, blue);
2941     vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
2942   }
2943 }
2944 
get_fgbg_rgb(vt_parser_t * vt_parser,int ps)2945 static void get_fgbg_rgb(vt_parser_t *vt_parser, int ps /* 10, 11 */) {
2946   u_int8_t red;
2947   u_int8_t green;
2948   u_int8_t blue;
2949 
2950   if (HAS_XTERM_LISTENER(vt_parser, get_rgb) &&
2951       (*vt_parser->xterm_listener->get_rgb)(vt_parser->xterm_listener->self, &red, &green,
2952                                             &blue, ps == 10 ? VT_FG_COLOR : VT_BG_COLOR)) {
2953     char seq[2 + DIGIT_STR_LEN(int) + 1 + 18 + 2 + 1];
2954 
2955     sprintf(seq, "\x1b]%d;rgb:%.2x%.2x/%.2x%.2x/%.2x%.2x\x1b\\",
2956             ps, red, red, green, green, blue, blue);
2957 
2958     vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
2959   }
2960 }
2961 
2962 /*
2963  * This function will destroy the content of pt.
2964  */
change_color_rgb(vt_parser_t * vt_parser,u_char * pt)2965 static void change_color_rgb(vt_parser_t *vt_parser, u_char *pt) {
2966   char *p;
2967 
2968   while ((p = strchr(pt, ';'))) {
2969     char *next;
2970 
2971     if ((next = strchr(p + 1, ';'))) {
2972       *(next++) = '\0';
2973     }
2974 
2975     if (strcmp(p + 1, "?") == 0) {
2976       vt_color_t color;
2977 
2978       *p = '\0';
2979 
2980       if ((color = vt_get_color(pt)) != VT_UNKNOWN_COLOR) {
2981         get_rgb(vt_parser, color);
2982       }
2983     } else {
2984       *p = '=';
2985 
2986       if ((p = alloca(6 + strlen(pt) + 1))) {
2987         sprintf(p, "color:%s", pt);
2988         config_protocol_set(vt_parser, p, 0);
2989       }
2990     }
2991 
2992     if (!next) {
2993       break;
2994     }
2995 
2996     pt = next;
2997   }
2998 }
2999 
get_special_color_name(const char c)3000 static char *get_special_color_name(const char c /* '0' - '4' */) {
3001   if (c == '0') {
3002     return "bd_color";
3003   } else if (c == '1') {
3004     return "ul_color";
3005   } else if (c == '2') {
3006     return "bl_color";
3007   } else if (c == '3') {
3008     return "rv_color"; /* XXX Not supported for now */
3009   } else /* if (c == '4') */ {
3010     return "it_color";
3011   }
3012 }
3013 
change_special_color(vt_parser_t * vt_parser,u_char * pt)3014 static void change_special_color(vt_parser_t *vt_parser, u_char *pt) {
3015   char *p;
3016 
3017   while ((p = strchr(pt, ';'))) {
3018     char *next;
3019 
3020     if ((next = strchr(p + 1, ';'))) {
3021       *(next++) = '\0';
3022     }
3023 
3024     if ('0' <= *pt && *pt <= '4') {
3025       if (strcmp(p + 1, "?") == 0) {
3026         get_rgb(vt_parser, *pt - '0' + VT_BOLD_COLOR);
3027       } else {
3028         config_protocol_set_simple(vt_parser, get_special_color_name(*pt), p + 1, 0);
3029       }
3030     }
3031 
3032     if (!next) {
3033       break;
3034     }
3035 
3036     pt = next;
3037   }
3038 }
3039 
change_fgbg_color(vt_parser_t * vt_parser,int ps,u_char * pt)3040 static void change_fgbg_color(vt_parser_t *vt_parser, int ps /* 10, 11 */, u_char *pt) {
3041   while (1) {
3042     char *next;
3043 
3044     if ((next = strchr(pt, ';'))) {
3045       *(next++) = '\0';
3046     }
3047 
3048     if (10 <= ps && ps <= 11) {
3049       if (strcmp(pt, "?") == 0) {
3050         get_fgbg_rgb(vt_parser, ps);
3051       } else {
3052         char *key;
3053 
3054         if (ps == 10) {
3055           key = "fg_color";
3056         } else /* if (ps == 11) */ {
3057           key = "bg_color";
3058         }
3059 
3060         config_protocol_set_simple(vt_parser, key, pt, 0);
3061       }
3062     }
3063 
3064     if (!next) {
3065       break;
3066     }
3067 
3068     pt = next;
3069     ps ++;
3070   }
3071 }
3072 
reset_color_rgb(vt_parser_t * vt_parser,u_char * pt,int is_spcolor)3073 static void reset_color_rgb(vt_parser_t *vt_parser, u_char *pt, int is_spcolor) {
3074   u_char *p;
3075   char seq[11];
3076   int color;
3077 
3078   if (*pt == '\0') {
3079     if (is_spcolor) {
3080       for (color = '0'; color <= '4'; color++) {
3081         config_protocol_set_simple(vt_parser, get_special_color_name(color), "", 0);
3082       }
3083     } else {
3084       for (color = 0; color < 256; color++) {
3085         sprintf(seq, "color:%d=", color);
3086         config_protocol_set(vt_parser, seq, 0);
3087       }
3088     }
3089   } else {
3090     while ((p = bl_str_sep(&pt, ";"))) {
3091       if (is_spcolor) {
3092         if ('0' <= *p && *p <= '4') {
3093           config_protocol_set_simple(vt_parser, get_special_color_name(*p), "", 0);
3094         }
3095       } else {
3096         color = atoi(p);
3097         sprintf(seq, "color:%d=", color);
3098         config_protocol_set(vt_parser, seq, 0);
3099       }
3100     }
3101   }
3102 }
3103 
set_selection(vt_parser_t * vt_parser,u_char * encoded)3104 static void set_selection(vt_parser_t *vt_parser, u_char *encoded) {
3105   if (HAS_XTERM_LISTENER(vt_parser, set_selection)) {
3106     u_char *p;
3107     u_char *targets;
3108     size_t e_len;
3109     size_t d_len;
3110     u_char *decoded;
3111     ef_char_t ch;
3112     vt_char_t *str;
3113     u_int str_len;
3114 
3115     if ((p = strchr(encoded, ';'))) {
3116       *p = '\0';
3117       targets = encoded;
3118       encoded = p + 1;
3119     } else {
3120       targets = "s0";
3121     }
3122 
3123     if ((e_len = strlen(encoded)) < 4 || !(decoded = alloca(e_len)) ||
3124         (d_len = bl_base64_decode(decoded, encoded, e_len)) == 0 || !(str = vt_str_new(d_len))) {
3125       return;
3126     }
3127 
3128     str_len = 0;
3129     (*vt_parser->cc_parser->set_str)(vt_parser->cc_parser, decoded, d_len);
3130     while ((*vt_parser->cc_parser->next_char)(vt_parser->cc_parser, &ch)) {
3131       vt_char_set(&str[str_len++], ef_char_to_int(&ch), ch.cs, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3132     }
3133 
3134 /*
3135  * It is assumed that screen is not redrawn not in
3136  * vt_parser->config_listener->get, so vt_parser->config_listener->get
3137  * is not held between stop_vt100_cmd and start_vt100_cmd.
3138  */
3139 #if 0
3140     stop_vt100_cmd(vt_parser, 0);
3141 #endif
3142 
3143     (*vt_parser->xterm_listener->set_selection)(vt_parser->xterm_listener->self, str, str_len,
3144                                                    targets);
3145 
3146 #if 0
3147     start_vt100_cmd(vt_parser, 0);
3148 #endif
3149   }
3150 }
3151 
destroy_macro(vt_parser_t * vt_parser,int id)3152 static void destroy_macro(vt_parser_t *vt_parser,
3153                          int id /* should be less than vt_parser->num_macros */
3154                          ) {
3155   if (vt_parser->macros[id].is_sixel) {
3156     unlink(vt_parser->macros[id].str);
3157     vt_parser->macros[id].is_sixel = 0;
3158     vt_parser->macros[id].sixel_num++;
3159   }
3160 
3161   free(vt_parser->macros[id].str);
3162   vt_parser->macros[id].str = NULL;
3163 }
3164 
destroy_all_macros(vt_parser_t * vt_parser)3165 static void destroy_all_macros(vt_parser_t *vt_parser) {
3166   u_int count;
3167 
3168   for (count = 0; count < vt_parser->num_macros; count++) {
3169     destroy_macro(vt_parser, count);
3170   }
3171 
3172   free(vt_parser->macros);
3173   vt_parser->macros = NULL;
3174   vt_parser->num_macros = 0;
3175 }
3176 
hex_to_text(u_char * hex)3177 static u_char *hex_to_text(u_char *hex) {
3178   u_char *text;
3179   u_char *p;
3180   size_t len;
3181   size_t count;
3182   int rep_count;
3183   u_char *rep_beg;
3184   int d[2];
3185   int sub_count;
3186 
3187   len = strlen(hex) / 2 + 1;
3188   if (!(text = malloc(len))) {
3189     return NULL;
3190   }
3191 
3192   count = 0;
3193   rep_count = 1;
3194   rep_beg = NULL;
3195   sub_count = 0;
3196 
3197   /* Don't use sscanf() here because it works too slow. */
3198   while (1) {
3199     if ('0' <= *hex && *hex <= '9') {
3200       d[sub_count++] = *hex - '0';
3201     } else {
3202       u_char masked_hex;
3203 
3204       masked_hex = (*hex) & 0xcf;
3205       if ('A' <= masked_hex && masked_hex <= 'F') {
3206         d[sub_count++] = masked_hex - 'A' + 10;
3207       } else {
3208         sub_count = 0;
3209 
3210         if (*hex == '!') {
3211           rep_beg = NULL;
3212 
3213           if ((p = strchr(hex + 1, ';'))) {
3214             *p = '\0';
3215             if ((rep_count = atoi(hex + 1)) > 1) {
3216               rep_beg = text + count;
3217             }
3218             hex = p;
3219           }
3220         } else if (*hex == ';' || *hex == '\0') {
3221           if (rep_beg) {
3222             size_t rep_len;
3223 
3224             if ((rep_len = text + count - rep_beg) > 0) {
3225               len += rep_len * (rep_count - 1);
3226               if (!(p = realloc(text, len))) {
3227                 free(text);
3228 
3229                 return NULL;
3230               }
3231               rep_beg += (p - text);
3232               text = p;
3233 
3234               while (--rep_count > 0) {
3235                 strncpy(text + count, rep_beg, rep_len);
3236                 count += rep_len;
3237               }
3238             }
3239 
3240             rep_beg = NULL;
3241           }
3242 
3243           if (*hex == '\0') {
3244             goto end;
3245           }
3246         }
3247       }
3248     }
3249 
3250     if (sub_count == 2) {
3251       text[count++] = (d[0] << 4) + d[1];
3252       sub_count = 0;
3253     }
3254 
3255     hex++;
3256   }
3257 
3258 end:
3259   text[count] = '\0';
3260 
3261   return text;
3262 }
3263 
3264 #define MAX_NUM_OF_MACRO 1024
3265 #define MAX_DIGIT_OF_MACRO 4
3266 
define_macro(vt_parser_t * vt_parser,u_char * param,u_char * data)3267 static void define_macro(vt_parser_t *vt_parser, u_char *param, u_char *data) {
3268   u_char *p;
3269   int ps[3] = {0, 0, 0};
3270   int num = 0;
3271 
3272   for (p = param; *p != 'z'; p++) {
3273     if ((*p == ';' || *p == '!') && num < 3) {
3274       *p = '\0';
3275       ps[num++] = atoi(param);
3276       param = p + 1;
3277     }
3278   }
3279 
3280   if (ps[0] >= MAX_NUM_OF_MACRO) {
3281     return;
3282   }
3283 
3284   if (ps[1] == 1) {
3285     destroy_all_macros(vt_parser);
3286   }
3287 
3288   if (ps[0] >= vt_parser->num_macros) {
3289     void *p;
3290 
3291     if (*data == '\0' ||
3292         !(p = realloc(vt_parser->macros, (ps[0] + 1) * sizeof(*vt_parser->macros)))) {
3293       return;
3294     }
3295 
3296     memset((vt_parser->macros = p) + vt_parser->num_macros, 0,
3297            (ps[0] + 1 - vt_parser->num_macros) * sizeof(*vt_parser->macros));
3298     vt_parser->num_macros = ps[0] + 1;
3299   } else {
3300     destroy_macro(vt_parser, ps[0]);
3301 
3302     if (*data == '\0') {
3303       return;
3304     }
3305   }
3306 
3307   if (ps[2] == 1) {
3308     p = vt_parser->macros[ps[0]].str = hex_to_text(data);
3309 
3310 #ifndef NO_IMAGE
3311     if (p && (*p == 0x90 || (*(p++) == '\x1b' && *p == 'P'))) {
3312       for (p++; *p == ';' || ('0' <= *p && *p <= '9'); p++)
3313         ;
3314 
3315       if (*p == 'q' && (strrchr(p, 0x9c) || ((p = strrchr(p, '\\')) && *(p - 1) == '\x1b'))) {
3316         char prefix[5 + MAX_DIGIT_OF_MACRO + 1 + DIGIT_STR_LEN(vt_parser->macros[0].sixel_num) +
3317                     2];
3318         char *path;
3319 
3320         sprintf(prefix, "macro%d_%d_", ps[0], vt_parser->macros[ps[0]].sixel_num);
3321 
3322         if ((path =
3323                  get_home_file_path(prefix, vt_pty_get_slave_name(vt_parser->pty) + 5, "six"))) {
3324           FILE *fp;
3325 
3326           if ((fp = fopen(path, "w"))) {
3327             fwrite(vt_parser->macros[ps[0]].str, 1, strlen(vt_parser->macros[ps[0]].str), fp);
3328             fclose(fp);
3329 
3330             free(vt_parser->macros[ps[0]].str);
3331             vt_parser->macros[ps[0]].str = path;
3332             vt_parser->macros[ps[0]].is_sixel = 1;
3333 
3334 #ifdef DEBUG
3335             bl_debug_printf(BL_DEBUG_TAG " Register %s to macro %d\n", path, ps[0]);
3336 #endif
3337           }
3338         }
3339       }
3340     }
3341 #endif
3342   } else {
3343     vt_parser->macros[ps[0]].str = strdup(data);
3344   }
3345 }
3346 
3347 static int write_loopback(vt_parser_t *vt_parser, const u_char *buf, size_t len,
3348                           int enable_local_echo, int is_visual);
3349 
invoke_macro(vt_parser_t * vt_parser,int id)3350 static void invoke_macro(vt_parser_t *vt_parser, int id) {
3351   if (id < vt_parser->num_macros && vt_parser->macros[id].str) {
3352 #ifndef NO_IMAGE
3353     if (vt_parser->macros[id].is_sixel) {
3354       show_picture(vt_parser, vt_parser->macros[id].str, 0, 0, 0, 0, 0, 0, 0, 3);
3355     } else
3356 #endif
3357     {
3358       write_loopback(vt_parser, vt_parser->macros[id].str,
3359                      strlen(vt_parser->macros[id].str), 0, 0);
3360     }
3361   }
3362 }
3363 
response_termcap(vt_pty_t * pty,u_char * key,u_char * value)3364 static int response_termcap(vt_pty_t *pty, u_char *key, u_char *value) {
3365   u_char *response;
3366 
3367   if ((response = alloca(5 + strlen(key) + 1 + strlen(value) * 2 + 3))) {
3368     u_char *dst;
3369 
3370     sprintf(response, "\x1bP1+r%s=", key);
3371     for (dst = response + strlen(response); *value; value++, dst += 2) {
3372       dst[0] = (value[0] >> 4) & 0xf;
3373       dst[0] = (dst[0] > 9) ? (dst[0] + 'A' - 10) : (dst[0] + '0');
3374       dst[1] = value[0] & 0xf;
3375       dst[1] = (dst[1] > 9) ? (dst[1] + 'A' - 10) : (dst[1] + '0');
3376     }
3377     strcpy(dst, "\x1b\\");
3378 
3379     vt_write_to_pty(pty, response, strlen(response));
3380 
3381     return 1;
3382   } else {
3383     return 0;
3384   }
3385 }
3386 
3387 #define TO_INT(a) (((a) | 0x20) - ((a) <= '9' ? '0' : ('a' - 10)))
3388 
report_termcap(vt_parser_t * vt_parser,u_char * key)3389 static void report_termcap(vt_parser_t *vt_parser, u_char *key) {
3390   u_char *deckey;
3391   u_char *src;
3392   u_char *dst;
3393 
3394   if ((deckey = alloca(strlen(key) / 2 + 1))) {
3395     struct {
3396       u_char *tckey;
3397       u_char *tikey;
3398       int16_t spkey; /* vt_special_key_t */
3399       int16_t modcode;
3400 
3401     } db[] = {
3402         {"%1", "khlp", SPKEY_F15, 0},
3403         {"#1", "kHLP", SPKEY_F15, 2},
3404         {"@0", "kfnd", SPKEY_FIND, 0},
3405         {"*0", "kFND", SPKEY_FIND, 2},
3406         {"*6", "kslt", SPKEY_SELECT, 0},
3407         {"#6", "kSLT", SPKEY_SELECT, 2},
3408 
3409         {"kh", "khome", SPKEY_HOME, 0},
3410         {"#2", "kHOM", SPKEY_HOME, 2},
3411         {"@7", "kend", SPKEY_END, 0},
3412         {"*7", "kEND", SPKEY_END, 2},
3413 
3414         {"kl", "kcub1", SPKEY_LEFT, 0},
3415         {"kr", "kcuf1", SPKEY_RIGHT, 0},
3416         {"ku", "kcuu1", SPKEY_UP, 0},
3417         {"kd", "kcud1", SPKEY_DOWN, 0},
3418 
3419         {"#4", "kLFT", SPKEY_LEFT, 2},
3420         {"%i", "kRIT", SPKEY_RIGHT, 2},
3421         {"kF", "kind", SPKEY_DOWN, 2},
3422         {"kR", "kri", SPKEY_UP, 2},
3423 
3424         {"k1", "kf1", SPKEY_F1, 0},
3425         {"k2", "kf2", SPKEY_F2, 0},
3426         {"k3", "kf3", SPKEY_F3, 0},
3427         {"k4", "kf4", SPKEY_F4, 0},
3428         {"k5", "kf5", SPKEY_F5, 0},
3429         {"k6", "kf6", SPKEY_F6, 0},
3430         {"k7", "kf7", SPKEY_F7, 0},
3431         {"k8", "kf8", SPKEY_F8, 0},
3432         {"k9", "kf9", SPKEY_F9, 0},
3433         {"k;", "kf10", SPKEY_F10, 0},
3434 
3435         {"F1", "kf11", SPKEY_F11, 0},
3436         {"F2", "kf12", SPKEY_F12, 0},
3437         {"F3", "kf13", SPKEY_F13, 0},
3438         {"F4", "kf14", SPKEY_F14, 0},
3439         {"F5", "kf15", SPKEY_F15, 0},
3440         {"F6", "kf16", SPKEY_F16, 0},
3441         {"F7", "kf17", SPKEY_F17, 0},
3442         {"F8", "kf18", SPKEY_F18, 0},
3443         {"F9", "kf19", SPKEY_F19, 0},
3444         {"FA", "kf20", SPKEY_F20, 0},
3445         {"FB", "kf21", SPKEY_F21, 0},
3446         {"FC", "kf22", SPKEY_F22, 0},
3447         {"FD", "kf23", SPKEY_F23, 0},
3448         {"FE", "kf24", SPKEY_F24, 0},
3449         {"FF", "kf25", SPKEY_F25, 0},
3450         {"FG", "kf26", SPKEY_F26, 0},
3451         {"FH", "kf27", SPKEY_F27, 0},
3452         {"FI", "kf28", SPKEY_F28, 0},
3453         {"FJ", "kf29", SPKEY_F29, 0},
3454         {"FK", "kf30", SPKEY_F30, 0},
3455         {"FL", "kf31", SPKEY_F31, 0},
3456         {"FM", "kf32", SPKEY_F32, 0},
3457         {"FN", "kf33", SPKEY_F33, 0},
3458         {"FO", "kf34", SPKEY_F34, 0},
3459         {"FP", "kf35", SPKEY_F35, 0},
3460         {"FQ", "kf36", SPKEY_F36, 0},
3461         {"FR", "kf37", SPKEY_F37, 0},
3462 
3463         {"K1", "ka1", SPKEY_KP_HOME, 0},
3464         {"K4", "kc1", SPKEY_KP_END, 0},
3465         {"K3", "ka3", SPKEY_KP_PRIOR, 0},
3466         {"K5", "kc3", SPKEY_KP_NEXT, 0},
3467         {"kB", "kcbt", SPKEY_ISO_LEFT_TAB, 0},
3468         /* { "kC" , "kclr" , SPKEY_CLEAR , 0 } , */
3469         {"kD", "kdch1", SPKEY_DELETE, 0},
3470         {"kI", "kich1", SPKEY_INSERT, 0},
3471 
3472         {"kN", "knp", SPKEY_NEXT, 0},
3473         {"kP", "kpp", SPKEY_PRIOR, 0},
3474         {"%c", "kNXT", SPKEY_NEXT, 2},
3475         {"%e", "kPRV", SPKEY_PRIOR, 2},
3476 
3477         /* { "&8" , "kund" , SPKEY_UNDO , 0 } , */
3478         {"kb", "kbs", SPKEY_BACKSPACE, 0},
3479 
3480         {"Co", "colors", -1, 0},
3481         {"TN", "name", -2, 0},
3482     };
3483     int idx;
3484     u_char *value;
3485 
3486     for (src = key, dst = deckey; src[0] && src[1]; src += 2) {
3487       *(dst++) = TO_INT(src[0]) * 16 + TO_INT(src[1]);
3488     }
3489     *dst = '\0';
3490 
3491     value = NULL;
3492 
3493     for (idx = 0; idx < sizeof(db) / sizeof(db[0]); idx++) {
3494       if (strcmp(deckey, db[idx].tckey) == 0 || strcmp(deckey, db[idx].tikey) == 0) {
3495         if (db[idx].spkey == -1) {
3496           value = "256";
3497         } else if (db[idx].spkey == -2) {
3498           value = "mlterm";
3499         } else {
3500           value = vt_termcap_special_key_to_seq(
3501               vt_parser->termcap, db[idx].spkey, db[idx].modcode,
3502               /* vt_parser->is_app_keypad */ 0, IS_APP_CURSOR_KEYS(vt_parser),
3503               /* IS_APP_ESCAPE(vt_parser) */ 0,
3504               vt_parser->modify_cursor_keys, vt_parser->modify_function_keys);
3505         }
3506 
3507         break;
3508       }
3509     }
3510 
3511     if (value && response_termcap(vt_parser->pty, key, value)) {
3512       return;
3513     }
3514   }
3515 
3516   vt_write_to_pty(vt_parser->pty, "\x1bP0+r\x1b\\", 7);
3517 }
3518 
report_char_attr_status(vt_parser_t * vt_parser)3519 static void report_char_attr_status(vt_parser_t *vt_parser) {
3520   char color[10]; /* ";38;5;XXX" (256 color) */
3521 
3522   vt_write_to_pty(vt_parser->pty, "\x1bP1$r0", 6);
3523 
3524   if (vt_parser->is_bold) {
3525     vt_write_to_pty(vt_parser->pty, ";1", 2);
3526   }
3527 
3528   if (vt_parser->is_italic) {
3529     vt_write_to_pty(vt_parser->pty, ";3", 2);
3530   }
3531 
3532   if (vt_parser->line_style & LS_UNDERLINE_SINGLE) {
3533     vt_write_to_pty(vt_parser->pty, ";4", 2);
3534   }
3535 
3536   if (vt_parser->is_blinking) {
3537     vt_write_to_pty(vt_parser->pty, ";5", 2);
3538   }
3539 
3540   if (vt_parser->is_reversed) {
3541     vt_write_to_pty(vt_parser->pty, ";7", 2);
3542   }
3543 
3544   if (vt_parser->is_invisible) {
3545     vt_write_to_pty(vt_parser->pty, ":8", 2);
3546   }
3547 
3548   if (vt_parser->line_style & LS_CROSSED_OUT) {
3549     vt_write_to_pty(vt_parser->pty, ";9", 2);
3550   }
3551 
3552   if (vt_parser->line_style & LS_UNDERLINE_DOUBLE) {
3553     vt_write_to_pty(vt_parser->pty, ";21", 3);
3554   }
3555 
3556   if (vt_parser->line_style & LS_OVERLINE) {
3557     vt_write_to_pty(vt_parser->pty, ":53", 3);
3558   }
3559 
3560   color[0] = ';';
3561   if (IS_VTSYS_BASE_COLOR(vt_parser->fg_color)) {
3562     color[1] = '3';
3563     color[2] = '0' + vt_parser->fg_color;
3564   } else if (IS_VTSYS_COLOR(vt_parser->fg_color)) {
3565     color[1] = '9';
3566     color[2] = '0' + (vt_parser->fg_color - 8);
3567   } else if (IS_VTSYS256_COLOR(vt_parser->fg_color)) {
3568     sprintf(color + 1, "38;5;%d", vt_parser->fg_color);
3569   } else {
3570     goto bg_color;
3571   }
3572   vt_write_to_pty(vt_parser->pty, color, strlen(color));
3573 
3574 bg_color:
3575   if (IS_VTSYS_BASE_COLOR(vt_parser->bg_color)) {
3576     color[1] = '4';
3577     color[2] = '0' + vt_parser->bg_color;
3578   } else if (IS_VTSYS_COLOR(vt_parser->bg_color)) {
3579     color[1] = '1';
3580     color[2] = '0';
3581     color[3] = '0' + (vt_parser->bg_color - 8);
3582   } else if (IS_VTSYS256_COLOR(vt_parser->bg_color)) {
3583     sprintf(color, ";48;5;%d", vt_parser->bg_color);
3584   } else {
3585     goto end;
3586   }
3587   vt_write_to_pty(vt_parser->pty, color, strlen(color));
3588 
3589 end:
3590   vt_write_to_pty(vt_parser->pty, "m\x1b\\", 3);
3591 }
3592 
report_status(vt_parser_t * vt_parser,u_char * key)3593 static void report_status(vt_parser_t *vt_parser, u_char *key) {
3594   u_char *val;
3595   u_char digits[DIGIT_STR_LEN(int)*3 + 3];
3596   u_char *seq;
3597 
3598   if (strcmp(key, "\"q") == 0) {
3599     /* DECSCA */
3600     val = vt_parser->is_protected ? "1" : "0"; /* Both 0 and 2 => is_protected = 0 */
3601   } else if (strcmp(key, "\"p") == 0) {
3602     /* DECSCL */
3603     val = "63;1";
3604   } else if (strcmp(key, "r") == 0) {
3605     /* DECSTBM */
3606     val = digits;
3607     sprintf(val, "%d;%d", vt_parser->screen->edit->vmargin_beg + 1,
3608             vt_parser->screen->edit->vmargin_end + 1);
3609   } else if (strcmp(key, "s") == 0) {
3610     /* DECSLRM */
3611     val = digits;
3612     sprintf(val, "%d;%d", vt_parser->screen->edit->hmargin_beg + 1,
3613             vt_parser->screen->edit->hmargin_end + 1);
3614   } else if (strcmp(key, " q") == 0) {
3615     /* DECSCUR */
3616     char vals[] = {'2', '4', '6', '\0', '1', '3', '5'};
3617     digits[0] = vals[vt_parser->cursor_style];
3618     digits[1] = '\0';
3619     val = digits;
3620   } else if (strcmp(key, "$}") == 0) {
3621     /* DECSASD */
3622     val = vt_status_line_is_focused(vt_parser->screen) ? "1" : "0";
3623   } else if (strcmp(key, "$~") == 0) {
3624     /* DECSSDT */
3625     val = vt_screen_has_status_line(vt_parser->screen) ? "2" : "0";
3626   } else if (strcmp(key, "*x") == 0) {
3627     /* DECSACE */
3628     val = vt_screen_is_using_rect_attr_select(vt_parser->screen) ? "2" : "1";
3629   } else if (strcmp(key, "){") == 0) {
3630     /* DECSTGLT */
3631     if (vt_parser->use_ansi_colors) {
3632       val = "0";
3633     } else if (vt_parser->alt_colors.flags) {
3634       val = "1";
3635     } else {
3636       val = "2";
3637     }
3638   } else if (strcmp(key, "$|") == 0) {
3639     /* DECSCPP */
3640     val = digits;
3641     sprintf(val, "%d", vt_screen_get_logical_cols(vt_parser->screen));
3642   } else if (strcmp(key, "t") == 0 || strcmp(key, "*|") == 0) {
3643     /* DECSLPP/DECSNLS */
3644     val = digits;
3645     sprintf(val, "%d", vt_screen_get_logical_rows(vt_parser->screen));
3646   } else if (strcmp(key, "m") == 0) {
3647     /* SGR */
3648     report_char_attr_status(vt_parser);
3649     return;
3650   } else {
3651     u_int ps = atoi(key);
3652 
3653     key += (strlen(key) - 2);
3654 
3655     if (strcmp(key, ",|") == 0) {
3656       /* DECAC */
3657       u_int8_t red;
3658       u_int8_t green;
3659       u_int8_t blue;
3660       vt_color_t colors[] = { VT_FG_COLOR, VT_BG_COLOR } ;
3661       u_int count;
3662 
3663       if (ps == 2) {
3664         /* Window Frame */
3665         goto error_validreq;
3666       }
3667 
3668       vt_color_force_linear_search(1);
3669       for (count = 0; count < sizeof(colors) / sizeof(colors[0]); count++) {
3670         if (!HAS_XTERM_LISTENER(vt_parser, get_rgb) ||
3671             !(*vt_parser->xterm_listener->get_rgb)(vt_parser->xterm_listener->self, &red, &green,
3672                                                    &blue, colors[count])) {
3673           goto error_validreq;
3674         }
3675         colors[count] = vt_get_closest_color(red, green, blue);
3676       }
3677       vt_color_force_linear_search(0);
3678 
3679       val = digits;
3680       sprintf(val, "1;%d;%d", colors[0], colors[1]);
3681     } else if (strcmp(key, ",}") == 0) {
3682       /* DECATC */
3683       int idx;
3684       if (ps <= 15 && (vt_parser->alt_colors.flags & (1 << (idx = alt_color_idxs[ps])))) {
3685         val = digits;
3686         sprintf(val, "%d;%d;%d", ps, vt_parser->alt_colors.fg[idx], vt_parser->alt_colors.bg[idx]);
3687       } else {
3688         goto error_validreq;
3689       }
3690     } else {
3691       goto error_invalidreq;
3692     }
3693   }
3694 
3695   /*
3696    * XXX
3697    * xterm returns 1 for vaild request while 0 for invalid request.
3698    * VT520/525 manual says 0 for vaild request while 1 for invalid request,
3699    * but it's wrong. (https://twitter.com/ttdoda/status/911125737242992645)
3700    */
3701 
3702   if ((seq = alloca(7 + strlen(val) + strlen(key) + 1))) {
3703     sprintf(seq, "\x1bP1$r%s%s\x1b\\", val, key);
3704     vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
3705   }
3706 
3707   return;
3708 
3709 error_validreq:
3710   vt_write_to_pty(vt_parser->pty, "\x1bP1$r\x1b\\", 7);
3711   return;
3712 
3713 error_invalidreq:
3714   vt_write_to_pty(vt_parser->pty, "\x1bP0$r\x1b\\", 7);
3715 }
3716 
clear_line_all(vt_parser_t * vt_parser)3717 static void clear_line_all(vt_parser_t *vt_parser) {
3718   vt_screen_clear_line_to_left(vt_parser->screen);
3719   vt_screen_clear_line_to_right(vt_parser->screen);
3720 }
3721 
clear_display_all(vt_parser_t * vt_parser)3722 static void clear_display_all(vt_parser_t *vt_parser) {
3723   vt_screen_clear_below(vt_parser->screen);
3724   vt_screen_clear_above(vt_parser->screen);
3725   vt_screen_clear_size_attr(vt_parser->screen);
3726 }
3727 
vtmode_num_to_idx(int mode)3728 static int vtmode_num_to_idx(int mode) {
3729   int idx;
3730 
3731   for (idx = 0; idx < VTMODE_NUM; idx++) {
3732     if (vtmodes[idx] == mode) {
3733       return idx;
3734     }
3735   }
3736 
3737   return -1;
3738 }
3739 
get_vtmode(vt_parser_t * vt_parser,int mode)3740 static int get_vtmode(vt_parser_t *vt_parser, int mode) {
3741   int idx;
3742 
3743   /* XXX These can be changed via OSC 5379 instead of DEC Private Mode Set. */
3744   if (mode == 8428) {
3745     return (vt_parser->col_size_of_width_a == 1) ? 1 : 2;
3746   } else if (mode == 117) {
3747     return vt_screen_is_using_bce(vt_parser->screen) ? 1 : 2;
3748   } else if (mode == VTMODE(33)) {
3749     return (vt_parser->cursor_style & CS_BLINK) ? 2 : 1;
3750   } else
3751   /* XXX DECBKM affects *all* terms sharing vt_termcap, so vtmode_flags is not reliable. */
3752   if (mode == 67) {
3753     return strcmp(vt_termcap_special_key_to_seq(vt_parser->termcap, SPKEY_BACKSPACE,
3754                                                 0, 0, 0, 0, 0, 0), "\x08") == 0 ? 1 : 2;
3755   }
3756 
3757   if ((idx = vtmode_num_to_idx(mode)) != -1) {
3758     if (GET_VTMODE_FLAG(vt_parser, idx)) {
3759       return 1; /* set */
3760     } else {
3761       return 2; /* reset */
3762     }
3763   }
3764 
3765   if (mode == 8 /* DECARM (auto repeat) */ ||
3766       mode == 64 /* DECPCCM (page cursor-coupling mode) */ ||
3767       mode == 98 /* DECARSM (change lines per screen automatically by DECSLPP) */ ||
3768       mode == 102 /* DECNULM (always dicard NUL character) */ ||
3769       mode == 114 /* DECATCUM (alternate text color underline mode) */ ||
3770       mode == 115 /* DECATCBM (alternate text color blink mode) */ ||
3771       mode == 19 /* DECPEX (Ignore DECSTBM in MC and DECMC) */) {
3772     return 3; /* permanently set */
3773   } else if (mode == 4 /* DECSCLM (smooth scroll) */ || mode == 9 /* X10 mouse report */ ||
3774              mode == 38 /* Tek */ || mode == 45 /* reverse wraparound */ ||
3775              mode == 1001 /* highlight mouse tracking */ ||
3776              mode == 1011 /* scroll to bottom on key press */ ||
3777              /*
3778               * GATM, SRTM, VEM, HEM, PUM, FEAM, FETM, MATM, TTM, SATM, TSM, EBM are
3779               * permanently reset on VT510.
3780               * (https://vt100.net/docs/vt510-rm/DECRQM.html)
3781               *
3782               * ERM => http://nanno.dip.jp/softlib/man/rlogin/ctrlcode.html#ERM
3783               */
3784              mode == VTMODE(1) /* GATM */ ||
3785              (VTMODE(5) <= mode && mode <= VTMODE(7)) /* SRTM, ERM, VEM */||
3786              mode == VTMODE(10) /* HEM */ ||
3787              mode == VTMODE(11) /* PUM */ ||
3788              /* FEAM, FETM, MATM, TTM, SATM, TSM, EBM */
3789              (VTMODE(13) <= mode && mode <= VTMODE(19)) ||
3790              mode == VTMODE(18) /* ISM */) {
3791     return 4; /* permanently reset */
3792   }
3793 
3794   return 0; /* not recognized */
3795 }
3796 
set_vtmode(vt_parser_t * vt_parser,int mode,int flag)3797 static void set_vtmode(vt_parser_t *vt_parser, int mode, int flag) {
3798   int idx;
3799 
3800 #ifdef DEBUG
3801   if (VTMODE_NUM != sizeof(vtmodes) / sizeof(vtmodes[0])) {
3802     bl_debug_printf(BL_DEBUG_TAG "vtmode table is broken.\n");
3803   }
3804 #endif
3805 
3806   if ((idx = vtmode_num_to_idx(mode)) == -1) {
3807 #ifdef DEBUG
3808     debug_print_unknown("ESC [%s %d l\n", mode >= VTMODE(0) ? "" : " ?",
3809                         mode >= VTMODE(0) ? mode - VTMODE(0) : mode);
3810 #endif
3811 
3812     return;
3813   }
3814 
3815   if (flag) {
3816     vt_parser->vtmode_flags[DIV32(idx)] |= (SHIFT_FLAG(idx));
3817   } else {
3818     vt_parser->vtmode_flags[DIV32(idx)] &= ~(SHIFT_FLAG(idx));
3819   }
3820 
3821   switch (idx) {
3822   case DECMODE_1:
3823     /* DECCKM */
3824     break;
3825 
3826   case DECMODE_2:
3827 #ifdef USE_VT52
3828     if (!(vt_parser->is_vt52_mode = (flag ? 0 : 1))) {
3829       /*
3830        * USASCII should be designated for G0-G3 here, but it is temporized by
3831        * using init_encoding_parser() etc for now.
3832        */
3833       init_encoding_parser(vt_parser);
3834     }
3835 #endif
3836     break;
3837 
3838   case DECMODE_3:
3839     /* DECCOLM */
3840     if (ALLOW_DECCOLM(vt_parser)) {
3841       /* XTERM compatibility [#1048321] */
3842       if (!KEEP_SCREEN_ON_DECCOLM(vt_parser)) {
3843         clear_display_all(vt_parser);
3844         vt_screen_goto(vt_parser->screen, 0, 0);
3845       }
3846       resize(vt_parser, flag ? 132 : 80, -1, 1);
3847     }
3848     break;
3849 
3850 #if 0
3851   case DECMODE_4:
3852     /* DECSCLM smooth scrolling */
3853     break;
3854 #endif
3855 
3856   case DECMODE_5:
3857     /* DECSCNM */
3858     reverse_video(vt_parser, flag);
3859     break;
3860 
3861   case DECMODE_6:
3862     /* DECOM */
3863     if (flag) {
3864       vt_screen_set_relative_origin(vt_parser->screen, 1);
3865     } else {
3866       vt_screen_set_relative_origin(vt_parser->screen, 0);
3867     }
3868 
3869     /*
3870      * cursor position is reset
3871      * (the same behavior of xterm 4.0.3, kterm 6.2.0 or so)
3872      */
3873     vt_screen_goto(vt_parser->screen, 0, 0);
3874     break;
3875 
3876   case DECMODE_7:
3877     /* DECAWM */
3878     vt_screen_set_auto_wrap(vt_parser->screen, flag);
3879     break;
3880 
3881 #if 0
3882   case DECMODE_8:
3883     /* DECARM auto repeat */
3884     break;
3885 
3886   case DECMODE_9:
3887     /* X10 mouse reporting */
3888     break;
3889 
3890   case DECMODE_12:
3891     /*
3892      * XT_CBLINK
3893      * If cursor blinking is enabled, restart blinking.
3894      */
3895     break;
3896 #endif
3897 
3898   case DECMODE_25:
3899     /* DECTCEM */
3900     vt_parser->is_visible_cursor = flag;
3901     break;
3902 
3903 #if 0
3904   case DECMODE_35:
3905     /* shift keys */
3906     break;
3907 #endif
3908 
3909   case DECMODE_40:
3910     if (flag) {
3911       /* Compatible behavior with rlogin. (Not compatible with xterm) */
3912       set_vtmode(vt_parser, 3, GET_VTMODE_FLAG2(vt_parser, DECMODE_3));
3913     }
3914     break;
3915 
3916   case DECMODE_47:
3917     if (use_alt_buffer) {
3918       if (flag) {
3919         vt_screen_use_alternative_edit(vt_parser->screen);
3920       } else {
3921         vt_screen_use_normal_edit(vt_parser->screen);
3922       }
3923     }
3924     break;
3925 
3926   case DECMODE_66:
3927     /* DECNKM */
3928     vt_parser->is_app_keypad = flag;
3929     break;
3930 
3931   case DECMODE_67:
3932     /* DECBKM have back space */
3933     /* XXX Affects all terms sharing vt_termcap */
3934     vt_termcap_set_key_seq(vt_parser->termcap, SPKEY_BACKSPACE, flag ? "\x08" : "\x7f");
3935     break;
3936 
3937   case DECMODE_69:
3938     /* DECLRMM */
3939     vt_screen_set_use_hmargin(vt_parser->screen, flag);
3940     break;
3941 
3942   case DECMODE_80:
3943     /* DECSDM */
3944     vt_parser->sixel_scrolling = (flag ? 0 : 1);
3945     break;
3946 
3947   case DECMODE_95:
3948     /* DECNCSM */
3949     break;
3950 
3951   case DECMODE_116:
3952     /* DECBBSM */
3953     break;
3954 
3955   case DECMODE_117:
3956     /* DECECM */
3957     vt_screen_set_use_bce(vt_parser->screen, (flag ? 0 : 1));
3958     break;
3959 
3960   case DECMODE_1000: /* MOUSE_REPORT */
3961   case DECMODE_1002: /* BUTTON_EVENT */
3962   case DECMODE_1003: /* ANY_EVENT */
3963     set_mouse_report(vt_parser, flag ? (MOUSE_REPORT + idx - DECMODE_1000) : 0);
3964     break;
3965 
3966 #if 0
3967   case DECMODE_1001:
3968     /* X11 mouse highlighting */
3969     break;
3970 #endif
3971 
3972   case DECMODE_1004:
3973     vt_parser->want_focus_event = flag;
3974     break;
3975 
3976   case DECMODE_1005: /* UTF8 */
3977   case DECMODE_1006: /* SGR */
3978   case DECMODE_1015: /* URXVT */
3979     if (flag) {
3980       vt_parser->ext_mouse_mode = EXTENDED_MOUSE_REPORT_UTF8 + idx - DECMODE_1005;
3981     } else {
3982       vt_parser->ext_mouse_mode = 0;
3983     }
3984     break;
3985 
3986   case DECMODE_1010:
3987     /* scroll to bottom on tty output */
3988     config_protocol_set_simple(vt_parser, "exit_backscroll_by_pty", flag ? "true" : "false", 0);
3989     break;
3990 
3991 #if 0
3992   case DECMODE_1011:
3993     /* scroll to bottom on key press (always enabled) */
3994     break;
3995 #endif
3996 
3997   case DECMODE_1034:
3998     if (get_vtmode(vt_parser, 1036) == 2 /* reset */) {
3999       config_protocol_set_simple(vt_parser, "mod_meta_mode", flag ? "8bit" : "none", 0);
4000     }
4001     break;
4002 
4003   case DECMODE_1036:
4004     config_protocol_set_simple(vt_parser, "mod_meta_mode",
4005                                flag ? "esc" :
4006                                       (get_vtmode(vt_parser, 1034) == 1 ? "8bit" : "none"),
4007                                0);
4008     break;
4009 
4010   case DECMODE_1042:
4011     config_protocol_set_simple(vt_parser, "use_urgent_bell", flag ? "true" : "false", 0);
4012     break;
4013 
4014   case DECMODE_1047:
4015     if (use_alt_buffer) {
4016       if (flag) {
4017         vt_screen_use_alternative_edit(vt_parser->screen);
4018       } else {
4019         if (vt_screen_is_alternative_edit(vt_parser->screen)) {
4020           clear_display_all(vt_parser);
4021           vt_screen_use_normal_edit(vt_parser->screen);
4022         }
4023       }
4024     }
4025     break;
4026 
4027   case DECMODE_1048:
4028     if (use_alt_buffer) {
4029       if (flag) {
4030         save_cursor(vt_parser);
4031       } else {
4032         restore_cursor(vt_parser);
4033       }
4034     }
4035     break;
4036 
4037   case DECMODE_1049:
4038     if (use_alt_buffer) {
4039       if (flag) {
4040         if (!vt_screen_is_alternative_edit(vt_parser->screen)) {
4041           save_cursor(vt_parser);
4042           vt_screen_use_alternative_edit(vt_parser->screen);
4043           clear_display_all(vt_parser);
4044         }
4045       } else {
4046         if (vt_screen_is_alternative_edit(vt_parser->screen)) {
4047           vt_screen_use_normal_edit(vt_parser->screen);
4048           restore_cursor(vt_parser);
4049         }
4050       }
4051     }
4052     break;
4053 
4054   case DECMODE_2004:
4055     vt_parser->is_bracketed_paste_mode = flag;
4056     break;
4057 
4058   case DECMODE_7727:
4059     break;
4060 
4061   case DECMODE_8428:
4062     /* RLogin original */
4063     vt_parser->col_size_of_width_a = (flag ? 1 : 2);
4064     break;
4065 
4066   case DECMODE_8452:
4067     /* RLogin original */
4068     break;
4069 
4070   case DECMODE_8800:
4071     if (flag) {
4072       vt_parser->unicode_policy |= USE_UNICODE_DRCS;
4073     } else {
4074       vt_parser->unicode_policy &= ~USE_UNICODE_DRCS;
4075     }
4076     break;
4077 
4078   case VTMODE_2:
4079     /* KAM */
4080     if (!HAS_XTERM_LISTENER(vt_parser, lock_keyboard)) {
4081       if (flag) {
4082         vt_parser->vtmode_flags[DIV32(VTMODE_2)] &= ~(SHIFT_FLAG(VTMODE_2)); /* unset flag */
4083       }
4084     } else {
4085       (*vt_parser->xterm_listener->lock_keyboard)(vt_parser->xterm_listener->self, flag);
4086     }
4087     break;
4088 
4089   case VTMODE_4:
4090     /* IRM */
4091     if (flag) {
4092       /* insert mode */
4093       vt_parser->w_buf.output_func = vt_screen_insert_chars;
4094     } else {
4095       /* replace mode */
4096       vt_parser->w_buf.output_func = vt_screen_overwrite_chars;
4097     }
4098     break;
4099 
4100   case VTMODE_12:
4101     /* SRM */
4102     break;
4103 
4104   case VTMODE_20:
4105     /* LNM */
4106     break;
4107 
4108   case VTMODE_33:
4109     /* WYSTCURM */
4110     if (flag) {
4111       vt_parser->cursor_style &= ~CS_BLINK;
4112     } else {
4113       vt_parser->cursor_style |= CS_BLINK;
4114     }
4115     break;
4116 
4117   case VTMODE_34:
4118     /* WYULCURM */
4119     if (flag) {
4120       vt_parser->cursor_style |= CS_UNDERLINE;
4121     } else {
4122       vt_parser->cursor_style &= ~CS_UNDERLINE;
4123     }
4124     break;
4125   }
4126 }
4127 
4128 /* Options configurable by ~/.mlterm/main are not reset. */
soft_reset(vt_parser_t * vt_parser)4129 static void soft_reset(vt_parser_t *vt_parser) {
4130   /*
4131    * XXX
4132    * Following options is not reset for now.
4133    * DECNRCM, DECAUPSS, DECKPM, DECRLM, DECPCTERM
4134    * (see https://vt100.net/docs/vt510-rm/DECSTR.html)
4135    */
4136 
4137   /* "CSI m" (SGR) */
4138   change_char_attr(vt_parser, 0);
4139 
4140   init_encoding_parser(vt_parser);
4141 
4142   /* DECSC */
4143   (vt_screen_is_alternative_edit(vt_parser->screen) ? &vt_parser->saved_alternate
4144                                                        : &vt_parser->saved_normal)->is_saved = 0;
4145 
4146   /* DECSCA */
4147   vt_parser->is_protected = 0;
4148 
4149   /* CSI < r, CSI < s, CSI < t (compatible with TeraTerm) */
4150   vt_parser->im_is_active = 0;
4151 
4152   set_vtmode(vt_parser, 1, 0); /* DECCKM */
4153   /* auto_wrap == 1 (compatible with xterm, not with VT220) */
4154   set_vtmode(vt_parser, 7, 1); /* DECAWM */
4155   set_vtmode(vt_parser, 25, 1); /* DECTCEM */
4156   set_vtmode(vt_parser, 66, 0); /* DECNKM */
4157   set_vtmode(vt_parser, 1000, 0); /* compatible with xterm */
4158   set_vtmode(vt_parser, 1002, 0); /* compatible with xterm */
4159   set_vtmode(vt_parser, 1003, 0); /* compatible with xterm */
4160   set_vtmode(vt_parser, 1005, 0); /* compatible with xterm */
4161   set_vtmode(vt_parser, 1006, 0); /* compatible with xterm */
4162   set_vtmode(vt_parser, 1015, 0); /* compatible with xterm */
4163   set_vtmode(vt_parser, 2004, 0); /* compatible with xterm */
4164   set_vtmode(vt_parser, 7727, 0); /* compatible with xterm */
4165   set_vtmode(vt_parser, VTMODE(2), 0); /* KAM */
4166   set_vtmode(vt_parser, VTMODE(4), 0); /* IRM */
4167 
4168   /* DECOM */
4169 #if 0
4170   set_vtmode(vt_parser, 6, 0);
4171 #else
4172   vt_screen_set_relative_origin(vt_parser->screen, 0);
4173   vt_parser->vtmode_flags[DIV32(DECMODE_6)] &= ~(SHIFT_FLAG(DECMODE_6));
4174 #endif
4175 
4176   /*
4177    * DECSLRM is reset but DECLRMM is as it is.
4178    * (Compatible with xterm, not described in vt510 manual.)
4179    */
4180   vt_screen_set_hmargin(vt_parser->screen, -1, -1);
4181 
4182   /* "CSI r" (DECSTBM) */
4183   vt_screen_set_vmargin(vt_parser->screen, -1, -1);
4184 
4185   /* DECSC => Home position */
4186   vt_screen_saved_cursor_to_home(vt_parser->screen);
4187 
4188   /* DECSASD */
4189   vt_focus_main_screen(vt_parser->screen);
4190 }
4191 
full_reset(vt_parser_t * vt_parser)4192 static void full_reset(vt_parser_t *vt_parser) {
4193   vt_screen_use_normal_edit(vt_parser->screen);
4194   vt_screen_goto_page(vt_parser->screen, 0);
4195   set_vtmode(vt_parser, 3, 0); /* DECCOLM */
4196   soft_reset(vt_parser); /* XXX insufficient */
4197   vt_screen_goto(vt_parser->screen, 0, 0);
4198   vt_screen_set_tab_size(vt_parser->screen, 8);
4199   clear_display_all(vt_parser); /* XXX off-screen pages are not cleared */
4200   destroy_all_macros(vt_parser);
4201   destroy_drcs(vt_parser->drcs);
4202 
4203   /* Compatible with xterm (CSI > T / CSI > t) */
4204   vt_parser->set_title_using_hex = vt_parser->get_title_using_hex =
4205     vt_parser->set_title_using_utf8 = vt_parser->get_title_using_utf8 = 0;
4206 }
4207 
send_device_status(vt_parser_t * vt_parser,int num,int id)4208 static void send_device_status(vt_parser_t *vt_parser, int num, int id) {
4209   char *seq;
4210   char amb[] = "\x1b[?884Xn";
4211 
4212   if (num == 6) {
4213     /* XCPR */
4214     if ((seq = alloca(6 + DIGIT_STR_LEN(int)+1))) {
4215       sprintf(seq, "\x1b[?%d;%d;%dR", vt_screen_cursor_logical_row(vt_parser->screen) + 1,
4216               vt_screen_cursor_logical_col(vt_parser->screen) + 1,
4217               vt_screen_get_page_id(vt_parser->screen) + 1);
4218     } else {
4219       return;
4220     }
4221   } else if (num == 15) {
4222     seq = "\x1b[?13n"; /* No printer */
4223   } else if (num == 25) {
4224     seq = "\x1b[?21n"; /* UDKs are locked */
4225   } else if (num == 26) {
4226     seq = "\x1b[?27;1;0;0n"; /* North American, Keyboard ready */
4227   } else if (num == 53 || num == 55) {
4228     seq = "\x1b[?50n"; /* Locator ready */
4229   } else if (num == 56) {
4230     seq = "\x1b[?57;1n"; /* Locator exists */
4231   } else if (num == 62) {
4232     seq = "\x1b[0*{"; /* Macro Space (Pn = [num of bytes] / 16 (rounded down) */
4233   } else if (num == 63) {
4234     /* checksum = 0 */
4235     if ((seq = alloca(10+DIGIT_STR_LEN(int)+1))) {
4236       sprintf(seq, "\x1bP%d!~0000\x1b\\", id);
4237     } else {
4238       seq = "\x1bP0!~0000\x1b\\";
4239     }
4240   } else if (num == 75) {
4241     seq = "\x1b[?70n"; /* Ready */
4242   } else if (num == 85) {
4243     seq = "\x1b[?83n"; /* Not multiple-session */
4244   } else if (num == 8840) {
4245     /* "CSI ? 8840 n" (TNREPTAMB) */
4246 
4247     amb[6] = vt_parser->col_size_of_width_a + 0x30;
4248     seq = amb;
4249   } else {
4250     return;
4251   }
4252 
4253   vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
4254 }
4255 
send_device_attributes(vt_pty_t * pty,int rank)4256 static void send_device_attributes(vt_pty_t *pty, int rank) {
4257   char *seq;
4258 
4259   if (rank == 1) {
4260     if (primary_da && (seq = alloca(4 + strlen(primary_da) + 1))) {
4261       sprintf(seq, "\x1b[?%sc", primary_da);
4262     } else {
4263       /*
4264        * 1  132-columns
4265        * 2  Printer
4266        * 3  ReGIS graphics
4267        * 4  Sixel graphics
4268        * 6  Selective erase
4269        * 7  Soft character set (DRCS)
4270        * 15 Technical character set
4271        * 18 Windowing capability
4272        * 22 Color
4273        * 29 ANSI text locator (i.e., DEC Locator mode)
4274        */
4275 #ifndef NO_IMAGE
4276       seq = "\x1b[?63;1;2;3;4;6;7;15;18;22;29c";
4277 #else
4278       seq = "\x1b[?63;1;2;6;7;15;18;22;29c";
4279 #endif
4280     }
4281   } else if (rank == 2) {
4282     if (secondary_da && (seq = alloca(4 + strlen(secondary_da) + 1))) {
4283       sprintf(seq, "\x1b[>%sc", secondary_da);
4284     } else {
4285       /*
4286        * >=96: vim sets ttymouse=xterm2
4287        * >=141: vim uses tcap-query.
4288        * >=277: vim uses sgr mouse tracking.
4289        * >=279: xterm supports DECSLRM/DECLRMM.
4290        */
4291       seq = "\x1b[>24;279;0c";
4292     }
4293   } else if (rank == 3) {
4294     seq = "\x1bP!|000000\x1b\\";
4295   } else {
4296     return;
4297   }
4298 
4299   vt_write_to_pty(pty, seq, strlen(seq));
4300 }
4301 
send_display_extent(vt_pty_t * pty,u_int cols,u_int rows,int vmargin,int hmargin,int page)4302 static void send_display_extent(vt_pty_t *pty, u_int cols, u_int rows, int vmargin,
4303                                 int hmargin, int page) {
4304   char seq[DIGIT_STR_LEN(int) * 5 + 1];
4305 
4306   sprintf(seq, "\x1b[%d;%d;%d;%d;%d\"w", rows, cols, hmargin, vmargin, page);
4307 
4308   vt_write_to_pty(pty, seq, strlen(seq));
4309 }
4310 
set_use_status_line(vt_parser_t * vt_parser,int ps)4311 static void set_use_status_line(vt_parser_t *vt_parser, int ps) {
4312   u_int width;
4313   u_int height;
4314 
4315   if (ps <= 1) {
4316     vt_screen_set_use_status_line(vt_parser->screen, 0);
4317   } else if (ps == 2) {
4318     vt_screen_set_use_status_line(vt_parser->screen, 1);
4319   } else {
4320     return;
4321   }
4322 
4323   if (!HAS_XTERM_LISTENER(vt_parser, get_window_size)) {
4324     width = height = 0;
4325   } else {
4326     (*vt_parser->xterm_listener->get_window_size)(vt_parser->xterm_listener->self,
4327                                                   &width, &height);
4328   }
4329 
4330   vt_set_pty_winsize(vt_parser->pty, vt_screen_get_logical_cols(vt_parser->screen),
4331                      vt_screen_get_logical_rows(vt_parser->screen), width, height);
4332 }
4333 
4334 /*
4335  * For string outside escape sequences.
4336  */
increment_str(u_char ** str,size_t * left)4337 static int increment_str(u_char **str, size_t *left) {
4338   if (--(*left) == 0) {
4339     return 0;
4340   }
4341 
4342   (*str)++;
4343 
4344   return 1;
4345 }
4346 
4347 /*
4348  * For string inside escape sequences.
4349  */
inc_str_in_esc_seq(vt_screen_t * screen,u_char ** str_p,size_t * left,int want_ctrl)4350 static int inc_str_in_esc_seq(vt_screen_t *screen, u_char **str_p, size_t *left, int want_ctrl) {
4351   while (1) {
4352     if (increment_str(str_p, left) == 0) {
4353       return 0;
4354     }
4355 
4356     if (**str_p < 0x20 || 0x7e < **str_p) {
4357       /*
4358        * cursor-control characters inside ESC sequences
4359        */
4360       if (CTL_LF <= **str_p && **str_p <= CTL_FF) {
4361         vt_screen_line_feed(screen);
4362         /* XXX AUTO_CR(vt_parser) is ignored for now. */
4363       } else if (**str_p == CTL_CR) {
4364         vt_screen_goto_beg_of_line(screen);
4365       } else if (**str_p == CTL_BS) {
4366         vt_screen_go_back(screen, 1, 0);
4367       } else if (want_ctrl) {
4368         return 1;
4369       } else {
4370 #ifdef DEBUG
4371         bl_warn_printf(BL_DEBUG_TAG " Ignored 0x%x inside escape sequences.\n", **str_p);
4372 #endif
4373       }
4374     } else {
4375       return 1;
4376     }
4377   }
4378 }
4379 
4380 /*
4381  * Set use_c1=0 for 0x9c not to be regarded as ST if str can be encoded by utf8.
4382  * (UTF-8 uses 0x80-0x9f as printable characters.)
4383  */
get_pt_in_esc_seq(u_char ** str,size_t * left,int use_c1,int bel_terminate)4384 static char *get_pt_in_esc_seq(
4385     u_char **str, size_t *left,
4386     int use_c1,       /* OSC is terminated by not only ST(ESC \) but also 0x9c. */
4387     int bel_terminate /* OSC is terminated by not only ST(ESC \) but also BEL. */
4388     ) {
4389   u_char *pt;
4390 
4391   pt = *str;
4392 
4393   while (1) {
4394     if ((bel_terminate && **str == CTL_BEL) || (use_c1 && **str == 0x9c)) {
4395       **str = '\0';
4396 
4397       break;
4398     }
4399 
4400     if (**str == CTL_ESC) {
4401       if (!increment_str(str, left)) {
4402         return NULL;
4403       }
4404 
4405       if (**str == '\\') {
4406         *((*str) - 1) = '\0';
4407 
4408         break;
4409       }
4410 
4411       /*
4412        * Reset position ahead of unprintable character for compat with xterm.
4413        * e.g.) "\x1bP\x1b[A" is parsed as "\x1b[A"
4414        */
4415       (*str) -= 2;
4416       (*left) += 2;
4417 
4418       return NULL;
4419     }
4420 
4421     if (!increment_str(str, left)) {
4422       return NULL;
4423     }
4424   }
4425 
4426   return pt;
4427 }
4428 
4429 #ifdef USE_VT52
parse_vt52_escape_sequence(vt_parser_t * vt_parser)4430 inline static int parse_vt52_escape_sequence(
4431     vt_parser_t *vt_parser /* vt_parser->r_buf.left must be more than 0 */
4432     ) {
4433   u_char *str_p;
4434   size_t left;
4435 
4436   str_p = CURRENT_STR_P(vt_parser);
4437   left = vt_parser->r_buf.left;
4438 
4439   if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
4440     return 0;
4441   }
4442 
4443   if (*str_p == 'A') {
4444     vt_screen_go_upward(vt_parser->screen, 1);
4445   } else if (*str_p == 'B') {
4446     vt_screen_go_downward(vt_parser->screen, 1);
4447   } else if (*str_p == 'C') {
4448     vt_screen_go_forward(vt_parser->screen, 1, 0);
4449   } else if (*str_p == 'D') {
4450     vt_screen_go_back(vt_parser->screen, 1, 0);
4451   } else if (*str_p == 'F') {
4452     /* Enter graphics mode */
4453   } else if (*str_p == 'G') {
4454     /* Exit graphics mode */
4455   } else if (*str_p == 'H') {
4456     vt_screen_goto(vt_parser->screen, 0, 0);
4457   } else if (*str_p == 'I') {
4458     if (vt_screen_cursor_logical_row(vt_parser->screen) == 0) {
4459       vt_screen_scroll_downward(vt_parser->screen, 1);
4460     } else {
4461       vt_screen_go_upward(vt_parser->screen, 1);
4462     }
4463   } else if (*str_p == 'J') {
4464     vt_screen_clear_below(vt_parser->screen);
4465   } else if (*str_p == 'K') {
4466     vt_screen_clear_line_to_right(vt_parser->screen);
4467   } else if (*str_p == 'Y') {
4468     int row;
4469     int col;
4470 
4471     if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
4472       return 0;
4473     }
4474 
4475     if (*str_p < ' ') {
4476       goto end;
4477     }
4478 
4479     row = *str_p - ' ';
4480 
4481     if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
4482       return 0;
4483     }
4484 
4485     if (*str_p < ' ') {
4486       goto end;
4487     }
4488 
4489     col = *str_p - ' ';
4490 
4491     vt_screen_goto(vt_parser->screen, col, row);
4492   } else if (*str_p == 'Z') {
4493     char msg[] = "\x1b/Z";
4494 
4495     vt_write_to_pty(vt_parser->pty, msg, sizeof(msg) - 1);
4496   } else if (*str_p == '=') {
4497     /* Enter altenate keypad mode */
4498   } else if (*str_p == '>') {
4499     /* Exit altenate keypad mode */
4500   } else if (*str_p == '<') {
4501     vt_parser->is_vt52_mode = 0;
4502   } else {
4503     /* not VT52 control sequence */
4504 
4505     return 1;
4506   }
4507 
4508 end:
4509   vt_parser->r_buf.left = left - 1;
4510 
4511   return 1;
4512 }
4513 #endif
4514 
4515 /*
4516  * Parse escape/control sequence. Note that *any* valid format sequence
4517  * is parsed even if mlterm doesn't support it.
4518  *
4519  * Return value:
4520  * 0 => vt_parser->r_buf.left == 0
4521  * 1 => Finished parsing. (If current vt_parser->r_buf.chars is not escape
4522  *sequence,
4523  *      return without doing anthing.)
4524  */
parse_vt100_escape_sequence(vt_parser_t * vt_parser)4525 inline static int parse_vt100_escape_sequence(
4526     vt_parser_t *vt_parser /* vt_parser->r_buf.left must be more than 0 */
4527     ) {
4528   u_char *str_p;
4529   size_t left;
4530 
4531 #if 0
4532   if (vt_parser->r_buf.left == 0) {
4533     /* end of string */
4534 
4535     return 1;
4536   }
4537 #endif
4538 
4539   str_p = CURRENT_STR_P(vt_parser);
4540   left = vt_parser->r_buf.left;
4541 
4542   if (*str_p == CTL_ESC) {
4543 #ifdef ESCSEQ_DEBUG
4544     bl_msg_printf("RECEIVED ESCAPE SEQUENCE(current left = %d: ESC", left);
4545 #endif
4546 
4547 #ifdef USE_VT52
4548     if (vt_parser->is_vt52_mode) {
4549       return parse_vt52_escape_sequence(vt_parser);
4550     }
4551 #endif
4552 
4553     if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
4554       return 0;
4555     }
4556 
4557     if (*str_p == '2') {
4558       /* "ESC 2" DECCAHT */
4559       vt_screen_clear_all_tab_stops(vt_parser->screen);
4560     } else if (*str_p == '6') {
4561       /* "ESC 6" DECBI */
4562 
4563       vt_screen_go_back(vt_parser->screen, 1, 1);
4564     } else if (*str_p == '7') {
4565       /* "ESC 7" save cursor (DECSC) */
4566 
4567       save_cursor(vt_parser);
4568     } else if (*str_p == '8') {
4569       /* "ESC 8" restore cursor (DECRC) */
4570 
4571       restore_cursor(vt_parser);
4572     } else if (*str_p == '9') {
4573       /* "ESC 9" DECFI */
4574 
4575       vt_screen_go_forward(vt_parser->screen, 1, 1);
4576     } else if (*str_p == '=') {
4577       /* "ESC =" application keypad (DECKPAM) */
4578 
4579       vt_parser->is_app_keypad = 1;
4580     } else if (*str_p == '>') {
4581       /* "ESC >" normal keypad (DECKPNM) */
4582 
4583       vt_parser->is_app_keypad = 0;
4584     } else if (*str_p == 'D') {
4585       /* "ESC D" index(scroll up) (IND) */
4586 
4587       vt_screen_index(vt_parser->screen);
4588     } else if (*str_p == 'E') {
4589       /* "ESC E" next line (NEL) */
4590 
4591       vt_screen_line_feed(vt_parser->screen);
4592       vt_screen_goto_beg_of_line(vt_parser->screen);
4593     }
4594 #if 0
4595     else if (*str_p == 'F') {
4596       /* "ESC F" cursor to lower left corner of screen */
4597     }
4598 #endif
4599     else if (*str_p == 'H' || *str_p == '1') {
4600       /* "ESC H" (HTS) / "ESC 1" (DECHTS) set tab */
4601 
4602       vt_screen_set_tab_stop(vt_parser->screen);
4603     } else if (*str_p == 'I') {
4604       /* "ESC I" (HTJ) */
4605       vt_screen_forward_tabs(vt_parser->screen, 1);
4606     }
4607 #if 0
4608     else if (*str_p == 'K') {
4609       /* "ESC K" (PLD) Partial Line Down */
4610     } else if (*str_p == 'L') {
4611       /* "ESC L" (PLU) Partial Line Up */
4612     }
4613 #endif
4614     else if (*str_p == 'M') {
4615       /* "ESC M" reverse index(scroll down) */
4616 
4617       vt_screen_reverse_index(vt_parser->screen);
4618     } else if (*str_p == 'Z') {
4619       /* "ESC Z" return terminal id (Obsolete form of CSI c) */
4620 
4621       send_device_attributes(vt_parser->pty, 1);
4622     } else if (*str_p == 'c') {
4623       /* "ESC c" full reset (RIS) */
4624 
4625       full_reset(vt_parser);
4626     }
4627 #if 0
4628     else if (*str_p == 'l') {
4629       /* "ESC l" memory lock */
4630     }
4631 #endif
4632 #if 0
4633     else if (*str_p == 'm') {
4634       /* "ESC m" memory unlock */
4635     }
4636 #endif
4637     else if (*str_p == '[') {
4638 /*
4639  * "ESC [" (CSI)
4640  * CSI P.....P I.....I F
4641  *     060-077 040-057 100-176
4642  */
4643 
4644 #define MAX_NUM_OF_PS 10
4645 
4646       u_char para_ch = '\0';
4647       u_char intmed_ch = '\0';
4648       int ps[MAX_NUM_OF_PS];
4649       int num;
4650 
4651       num = 0;
4652       while (1) {
4653         if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
4654           return 0;
4655         }
4656 
4657         if (*str_p == ';') {
4658           /*
4659            * "CSI ; n" is regarded as "CSI -1 ; n"
4660            */
4661           if (num < MAX_NUM_OF_PS) {
4662             ps[num++] = -1;
4663           }
4664         } else if (0x3c <= *str_p && *str_p <= 0x3f) {
4665           /* parameter character except numeric, ':' and ';' (< = > ?). */
4666           if (para_ch == '\0') {
4667             para_ch = *str_p;
4668           }
4669         } else {
4670           if ('0' <= *str_p && *str_p <= '9') {
4671             u_char digit[DIGIT_STR_LEN(int)+1];
4672             int count;
4673 
4674             if (*str_p == '0') {
4675               /* 000000001 -> 01 */
4676               while (left > 1 && *(str_p + 1) == '0') {
4677                 str_p++;
4678                 left--;
4679               }
4680             }
4681 
4682             digit[0] = *str_p;
4683 
4684             for (count = 1; count < DIGIT_STR_LEN(int); count++) {
4685               if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
4686                 return 0;
4687               }
4688 
4689               if (*str_p < '0' || '9' < *str_p) {
4690                 break;
4691               }
4692 
4693               digit[count] = *str_p;
4694             }
4695 
4696             digit[count] = '\0';
4697             if (num < MAX_NUM_OF_PS) {
4698               ps[num++] = atoi(digit);
4699 #ifdef MAX_PS_DIGIT
4700               if (ps[num - 1] > MAX_PS_DIGIT) {
4701                 ps[num - 1] = MAX_PS_DIGIT;
4702               }
4703 #endif
4704             }
4705           }
4706 
4707           if (*str_p < 0x30 || 0x3f < *str_p) {
4708             /* Is not a parameter character(0x30-0x3f). */
4709 
4710             /*
4711              * "CSI 0 n" is *not* regarded as "CSI 0 ; -1 n"
4712              * "CSI n" is regarded as "CSI -1 n"
4713              * "CSI 0 ; n" is regarded as "CSI 0 ; -1 n"
4714              * "CSI ; n" which has been already regarded as
4715              * "CSI -1 ; n" is regarded as "CSI -1 ; -1 n"
4716              */
4717             if (num == 0 || (*(str_p - 1) == ';' && num < MAX_NUM_OF_PS)) {
4718               ps[num++] = -1;
4719             }
4720 
4721             /* num is always greater than 0 */
4722 
4723             break;
4724           }
4725         }
4726       }
4727 
4728       /*
4729        * Intermediate(0x20-0x2f) characters.
4730        * (Unexpected paremeter(0x30-0x3f) characters are also ignored.)
4731        */
4732       while (0x20 <= *str_p && *str_p <= 0x3f) {
4733         if (*str_p <= 0x2f && intmed_ch == '\0') {
4734           intmed_ch = *str_p;
4735         }
4736 
4737         if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
4738           return 0;
4739         }
4740       }
4741 
4742       if (para_ch == '?') {
4743         if (intmed_ch == '$') {
4744           if (*str_p == 'p' && num > 0) {
4745             /* "CSI ? $ p" DECRQM */
4746 
4747             char seq[3 + DIGIT_STR_LEN(u_int) + 5];
4748             sprintf(seq, "\x1b[?%d;%d$y", ps[0],
4749                     (ps[0] >= VTMODE(0)) ? 0 : get_vtmode(vt_parser, ps[0]));
4750             vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
4751           }
4752         } else if (*str_p == 'h') {
4753           /* "CSI ? h" DEC Private Mode Set (DECSET) */
4754           int count;
4755 
4756           for (count = 0; count < num; count++) {
4757             if (ps[count] < VTMODE(0)) {
4758               set_vtmode(vt_parser, ps[count], 1);
4759             }
4760           }
4761         } else if (*str_p == 'i') {
4762           /* "CSI ? i" (DECMC) */
4763 
4764           if (ps[0] <= 0 || ps[0] == 10) {
4765             snapshot(vt_parser, VT_UTF8, vt_pty_get_slave_name(vt_parser->pty) + 5 /* skip /dev/ */,
4766                      WCA_SCREEN);
4767           } else if (ps[0] == 1) {
4768             snapshot(vt_parser, VT_UTF8, vt_pty_get_slave_name(vt_parser->pty) + 5 /* skip /dev/ */,
4769                      WCA_CURSOR_LINE);
4770           } else if (ps[0] == 11) {
4771             snapshot(vt_parser, VT_UTF8, vt_pty_get_slave_name(vt_parser->pty) + 5 /* skip /dev/ */,
4772                      WCA_ALL);
4773           }
4774         } else if (*str_p == 'l') {
4775           /* "CSI ? l" DEC Private Mode Reset (DECRST) */
4776           int count;
4777 
4778           for (count = 0; count < num; count++) {
4779             if (ps[count] < VTMODE(0)) {
4780               set_vtmode(vt_parser, ps[count], 0);
4781             }
4782           }
4783         } else if (*str_p == 'r') {
4784           /* "CSI ? r" Restore DEC Private Mode (xterm) */
4785           int count;
4786 
4787           for (count = 0; count < num; count++) {
4788             if (ps[count] < VTMODE(0)) {
4789               int idx;
4790               if ((idx = vtmode_num_to_idx(ps[count])) != -1) {
4791                 set_vtmode(vt_parser, ps[count], GET_SAVED_VTMODE_FLAG2(vt_parser, idx));
4792               }
4793             }
4794           }
4795         } else if (*str_p == 's') {
4796           /* "CSI ? s" Save DEC Private Mode (xterm) */
4797           int count;
4798 
4799           for (count = 0; count < num; count++) {
4800             if (ps[count] < VTMODE(0)) {
4801               int idx;
4802               if ((idx = vtmode_num_to_idx(ps[count])) != -1) {
4803                 if (GET_VTMODE_FLAG(vt_parser, idx)) {
4804                   vt_parser->saved_vtmode_flags[DIV32(idx)] |= (SHIFT_FLAG(idx));
4805                 } else {
4806                   vt_parser->saved_vtmode_flags[DIV32(idx)] &= ~(SHIFT_FLAG(idx));
4807                 }
4808               }
4809             }
4810           }
4811         } else if (*str_p == 'n') {
4812           /* "CSI ? n" Device Status Report (DSR) */
4813 
4814           send_device_status(vt_parser, ps[0], num == 2 ? ps[1] : 0);
4815         } else if (*str_p == 'J') {
4816           /* "CSI ? J" DECSED (Selective Erase Display) */
4817           vt_protect_store_t *save;
4818 
4819           if (ps[0] <= 0) {
4820             save = vt_screen_save_protected_chars(vt_parser->screen,
4821                                                   -1 /* cursor row */,
4822                                                   vt_screen_get_logical_rows(vt_parser->screen) - 1,
4823                                                   0);
4824             vt_screen_clear_below(vt_parser->screen);
4825             vt_screen_restore_protected_chars(vt_parser->screen, save);
4826           } else if (ps[0] == 1) {
4827             save = vt_screen_save_protected_chars(vt_parser->screen, 0,
4828                                                   -1 /* cursor row */, 0);
4829             vt_screen_clear_above(vt_parser->screen);
4830             vt_screen_restore_protected_chars(vt_parser->screen, save);
4831           } else if (ps[0] == 2) {
4832             save = vt_screen_save_protected_chars(vt_parser->screen, 0,
4833                                                   vt_screen_get_logical_rows(vt_parser->screen) - 1,
4834                                                   0);
4835             clear_display_all(vt_parser);
4836             vt_screen_restore_protected_chars(vt_parser->screen, save);
4837           }
4838         } else if (*str_p == 'K') {
4839           /* "CSI ? K" DECSEL (Selective Erase Line) */
4840           vt_protect_store_t *save;
4841 
4842           save = vt_screen_save_protected_chars(vt_parser->screen,
4843                                                 -1 /* cursor row */,
4844                                                 -1 /* cursor row */, 0);
4845           if (ps[0] <= 0) {
4846             vt_screen_clear_line_to_right(vt_parser->screen);
4847           } else if (ps[0] == 1) {
4848             vt_screen_clear_line_to_left(vt_parser->screen);
4849           } else if (ps[0] == 2) {
4850             clear_line_all(vt_parser);
4851           }
4852           vt_screen_restore_protected_chars(vt_parser->screen, save);
4853         } else if (*str_p == 'S') {
4854           /*
4855            * "CSI ? Pi;Pa;Pv S" (Xterm original)
4856            * The number of palettes mlterm supports is 1024 alone.
4857            */
4858           char *seq;
4859 
4860           if (num == 3 && ps[0] == 1 &&
4861               (ps[1] == 1 /* read */ || ps[1] == 2 /* reset to default */ ||
4862                ps[1] == 3 /* set (ps[2] is ignored) */ ||
4863                ps[1] == 4 /* read the max allowd value */)) {
4864             seq = "\x1b[?1;0;1024S";
4865           } else {
4866             seq = "\x1b[?1;3;0S";
4867           }
4868           vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
4869         } else if (*str_p == 'W') {
4870           /* "CSI ? W"  DECST8C */
4871 
4872           vt_screen_set_tab_size(vt_parser->screen, 8);
4873         }
4874 #if 0
4875         else if (*str_p == 'r') {
4876           /* "CSI ? r"  Restore DEC Private Mode */
4877         }
4878 #endif
4879 #if 0
4880         else if (*str_p == 's') {
4881           /* "CSI ? s" Save DEC Private Mode */
4882         }
4883 #endif
4884         /* Other final character */
4885         else if (0x40 <= *str_p && *str_p <= 0x7e) {
4886 #ifdef DEBUG
4887           debug_print_unknown("ESC [ ? %c\n", *str_p);
4888 #endif
4889         } else {
4890 /* not VT100 control sequence. */
4891 
4892 #ifdef ESCSEQ_DEBUG
4893           bl_msg_printf("=> not VT100 control sequence.\n");
4894 #endif
4895 
4896           return 1;
4897         }
4898       } else if (para_ch == '<') {
4899         /* Teraterm compatible IME control sequence */
4900 
4901         if (*str_p == 'r') {
4902           /* Restore IME state */
4903           if (vt_parser->im_is_active != im_is_active(vt_parser)) {
4904             switch_im_mode(vt_parser);
4905           }
4906         } else if (*str_p == 's') {
4907           /* Save IME state */
4908 
4909           vt_parser->im_is_active = im_is_active(vt_parser);
4910         } else if (*str_p == 't') {
4911           /* ps[0] = 0 (Close), ps[0] = 1 (Open) */
4912 
4913           if (ps[0] != im_is_active(vt_parser)) {
4914             switch_im_mode(vt_parser);
4915           }
4916         }
4917       } else if (para_ch == '>') {
4918         if (*str_p == 'c') {
4919           /* "CSI > c" Secondary DA (DA2) */
4920 
4921           send_device_attributes(vt_parser->pty, 2);
4922         } else if (*str_p == 'm') {
4923           /* "CSI > m" */
4924 
4925           if (ps[0] == -1) {
4926             /* reset to initial value. */
4927             set_modkey_mode(vt_parser, 1, 2);
4928             set_modkey_mode(vt_parser, 2, 2);
4929             set_modkey_mode(vt_parser, 4, 0);
4930           } else {
4931             if (num == 1 || ps[1] == -1) {
4932               if (ps[0] == 1 || /* modifyCursorKeys */
4933                   ps[0] == 2)   /* modifyFunctionKeys */
4934               {
4935                 /* The default is 2. */
4936                 ps[1] = 2;
4937               } else /* if( ps[0] == 4) */
4938               {
4939                 /*
4940                  * modifyOtherKeys
4941                  * The default is 0.
4942                  */
4943                 ps[1] = 0;
4944               }
4945             }
4946 
4947             set_modkey_mode(vt_parser, ps[0], ps[1]);
4948           }
4949         } else if (*str_p == 'n') {
4950           /* "CSI > n" */
4951 
4952           if (num == 1) {
4953             if (ps[0] == -1) {
4954               ps[0] = 2;
4955             }
4956 
4957             /* -1: don't send modifier key code. */
4958             set_modkey_mode(vt_parser, ps[0], -1);
4959           }
4960         } else if (*str_p == 'p') {
4961           /* "CSI > p" pointer mode */
4962 
4963           vt_parser->hide_pointer_mode = ps[0];
4964         } else if (*str_p == 't') {
4965           /* "CSI > t" */
4966 
4967           int count;
4968           for (count = 0; count < num; count++) {
4969             if (ps[count] == 0) {
4970               vt_parser->set_title_using_hex = 1;
4971             } else if (ps[count] == 1) {
4972               vt_parser->get_title_using_hex = 1;
4973             } else if (ps[count] == 2) {
4974               vt_parser->set_title_using_utf8 = 1;
4975             } else if (ps[count] == 3) {
4976               vt_parser->get_title_using_utf8 = 1;
4977             }
4978           }
4979         } else if (*str_p == 'T') {
4980           /* "CSI > T" */
4981 
4982           int count;
4983           for (count = 0; count < num; count++) {
4984             if (ps[count] == 0) {
4985               vt_parser->set_title_using_hex = 0;
4986             } else if (ps[count] == 1) {
4987               vt_parser->get_title_using_hex = 0;
4988             } else if (ps[count] == 2) {
4989               vt_parser->set_title_using_utf8 = 0;
4990             } else if (ps[count] == 3) {
4991               vt_parser->get_title_using_utf8 = 0;
4992             }
4993           }
4994         } else {
4995           /*
4996            * "CSI > c"
4997            */
4998         }
4999       } else if (para_ch == '=') {
5000         if (*str_p == 'c') {
5001           /* "CSI = c" Tertiary DA (DA3) */
5002 
5003           send_device_attributes(vt_parser->pty, 3);
5004         }
5005       } else if (intmed_ch == '!') {
5006         if (*str_p == 'p') {
5007           /* "CSI ! p" Soft Terminal Reset (DECSTR) */
5008 
5009           soft_reset(vt_parser);
5010         }
5011       } else if (intmed_ch == '$') {
5012         if (*str_p == 'p') {
5013           /* "CSI $ p" DECRQM */
5014 
5015           if (num > 0) {
5016             char seq[2 + DIGIT_STR_LEN(u_int) + 5];
5017             sprintf(seq, "\x1b[%d;%d$y", ps[0], get_vtmode(vt_parser, VTMODE(ps[0])));
5018             vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
5019           }
5020         } else if (*str_p == '|') {
5021           /* "CSI $ |" DECSCPP */
5022 
5023           /*
5024            * DECSCPP doesns't clear screen, reset scroll regions or moves the cursor
5025            * as does DECCOLM.
5026            */
5027 
5028           if (ps[0] <= 0 || ps[0] == 80) {
5029             resize(vt_parser, 80, -1, 1);
5030           } else if (ps[0] == 132) {
5031             resize(vt_parser, 132, -1, 1);
5032           }
5033         } else if (*str_p == '}') {
5034           /* "CSI $ }" DECSASD */
5035 
5036           if (ps[0] <= 0) {
5037             vt_focus_main_screen(vt_parser->screen);
5038           } else if (ps[0] == 1) {
5039             vt_focus_status_line(vt_parser->screen);
5040           }
5041         } else if (*str_p == '~') {
5042           /* "CSI $ ~" DECSSDT */
5043 
5044           set_use_status_line(vt_parser, ps[0]);
5045         } else if (*str_p == 'u') {
5046           if (ps[0] == 1) {
5047             /* "CSI 1 $ u" DECRQTSR */
5048             vt_write_to_pty(vt_parser->pty, "\x1bP0$s\x1b\\", 7);
5049           } else if (ps[0] == 2) {
5050             if (num == 2) {
5051               /* "CSI 2;Pu $ u" DECCTR */
5052               report_color_table(vt_parser, ps[1]);
5053             }
5054           }
5055         } else if (*str_p == 'w') {
5056           /* "CSI $ w" DECRQPSR */
5057           if (num == 1) {
5058             report_presentation_state(vt_parser, ps[0]);
5059           }
5060         } else {
5061           int count;
5062 
5063           if (*str_p == 'x') {
5064             /* DECFRA: Move Pc to the end */
5065             int tmp;
5066 
5067             tmp = ps[0];
5068             memmove(ps, ps + 1, sizeof(ps[0]) * 4);
5069             ps[4] = tmp;
5070             num--;
5071           }
5072 
5073           /* Set the default values to the 0-3 parameters. */
5074           for (count = 0; count < 4; count++) {
5075             if (count >= num || ps[count] <= 0) {
5076               if (count == 2) {
5077                 ps[count] = vt_screen_get_logical_rows(vt_parser->screen);
5078               } else if (count == 3) {
5079                 ps[count] = vt_screen_get_logical_cols(vt_parser->screen);
5080               } else {
5081                 ps[count] = 1;
5082               }
5083             }
5084           }
5085 
5086           if (ps[3] >= ps[1] && ps[2] >= ps[0]) {
5087             if (*str_p == 'z') {
5088               /* "CSI ... $ z" DECERA */
5089               vt_screen_erase_area(vt_parser->screen, ps[1] - 1, ps[0] - 1, ps[3] - ps[1] + 1,
5090                                    ps[2] - ps[0] + 1);
5091             } else if (*str_p == '{') {
5092               /* "CSI ... $ {" DECSERA */
5093               vt_protect_store_t *save;
5094               save = vt_screen_save_protected_chars(vt_parser->screen, ps[0] - 1, ps[2] - 1, 1);
5095               vt_screen_erase_area(vt_parser->screen, ps[1] - 1, ps[0] - 1, ps[3] - ps[1] + 1,
5096                                    ps[2] - ps[0] + 1);
5097               vt_screen_restore_protected_chars(vt_parser->screen, save);
5098             } else if (*str_p == 'v' && (num == 5 || num >= 8)) {
5099               /* "CSI ... $ v" DECCRA */
5100               if (num == 5) {
5101                 ps[5] = ps[6] = ps[7] = 1; /* default values of xterm */
5102               } else {
5103                 for (count = 4; count < 8; count++) {
5104                   if (ps[count] <= 0) {
5105                     ps[count] = 1;
5106                   }
5107                 }
5108               }
5109               vt_screen_copy_area(vt_parser->screen, ps[1] - 1, ps[0] - 1, ps[3] - ps[1] + 1,
5110                                   ps[2] - ps[0] + 1, ps[4] - 1, ps[6] - 1, ps[5] - 1, ps[7] - 1);
5111             } else if (*str_p == 'x') {
5112               /* "CSI ... $ x" DECFRA */
5113               if (!iscntrl(ps[4])) {
5114                 vt_screen_fill_area(vt_parser->screen, ps[4], vt_parser->is_protected,
5115                                     ps[1] - 1, ps[0] - 1, ps[3] - ps[1] + 1, ps[2] - ps[0] + 1);
5116               }
5117             } else if (*str_p == 'r') {
5118               /* "CSI Pt;Pl;Pb;Pr;...$r" DECCARA */
5119 
5120               for (count = 4; count < num; count++) {
5121                 vt_screen_change_attr_area(vt_parser->screen, ps[1] - 1, ps[0] - 1,
5122                                            ps[3] - ps[1] + 1, ps[2] - ps[0] + 1, ps[count]);
5123               }
5124             } else if (*str_p == 't') {
5125               /* "CSI Pt;Pl;Pb;Pr;...$t" DECRARA */
5126 
5127               for (count = 4; count < num; count++) {
5128                 vt_screen_reverse_attr_area(vt_parser->screen, ps[1] - 1, ps[0] - 1,
5129                                             ps[3] - ps[1] + 1, ps[2] - ps[0] + 1, ps[count]);
5130               }
5131             }
5132           }
5133         }
5134       } else if (intmed_ch == ' ') {
5135         if (*str_p == 'q') {
5136           /* "CSI SP q" DECSCUSR */
5137 
5138           if (ps[0] < 2) {
5139             vt_parser->cursor_style = CS_BLOCK|CS_BLINK;
5140           } else if (ps[0] == 2) {
5141             vt_parser->cursor_style = CS_BLOCK;
5142           } else if (ps[0] == 3) {
5143             vt_parser->cursor_style = CS_UNDERLINE|CS_BLINK;
5144           } else if (ps[0] == 4) {
5145             vt_parser->cursor_style = CS_UNDERLINE;
5146           } else if (ps[0] == 5) {
5147             vt_parser->cursor_style = CS_BAR|CS_BLINK;
5148           } else if (ps[0] == 6) {
5149             vt_parser->cursor_style = CS_BAR;
5150           }
5151         } else {
5152           if (ps[0] <= 0) {
5153             ps[0] = 1;
5154           }
5155 
5156           if (*str_p == '@') {
5157             /* CSI SP @ (SL) */
5158             vt_screen_scroll_leftward(vt_parser->screen, ps[0]);
5159           } else if (*str_p == 'A') {
5160             /* CSI SP A (SR) */
5161             vt_screen_scroll_rightward(vt_parser->screen, ps[0]);
5162           } else if (*str_p == 'P') {
5163             /* CSI SP P (PPA) */
5164             vt_screen_goto_page(vt_parser->screen, ps[0] - 1);
5165           } else if (*str_p == 'Q') {
5166             /* CSI SP Q (PPR) */
5167             vt_screen_goto_next_page(vt_parser->screen, ps[0]);
5168           } else if (*str_p == 'R') {
5169             /* CSI SP R (PPB) */
5170             vt_screen_goto_prev_page(vt_parser->screen, ps[0]);
5171           } else {
5172             /*
5173              * "CSI SP t"(DECSWBV), "CSI SP u"(DECSMBV)
5174              */
5175           }
5176         }
5177       } else if (intmed_ch == '*') {
5178         if (ps[0] == -1) {
5179           ps[0] = 0;
5180         }
5181 
5182         if (*str_p == 'z') {
5183           /* "CSI Pn * z" DECINVM */
5184 
5185           invoke_macro(vt_parser, ps[0]);
5186         } else if (*str_p == 'x') {
5187           /* "CSI Pn * x " DECSACE */
5188 
5189           vt_screen_set_use_rect_attr_select(vt_parser->screen, ps[0] == 2);
5190         } else if (*str_p == '|') {
5191           /* "CSI Pn * |" DECSNLS */
5192           resize(vt_parser, -1, ps[0], 1);
5193         } else if (*str_p == 'y') {
5194           /* "CSI Pn * y" DECRQCRA */
5195           if (num >= 1) {
5196             u_int16_t checksum;
5197             char seq[6+DIGIT_STR_LEN(ps[0])+4+1];
5198 
5199             if (ps[1] == 0) {
5200 #if 0
5201               /* Ignore following parameters (https://vt100.net/docs/vt510-rm/DECRQCRA.html) */
5202               num = 1;
5203 #else
5204               /* Compatible with xterm */
5205               ps[1] = 1;
5206 #endif
5207             }
5208 
5209             switch(num) {
5210             case 1:
5211             case 2:
5212               ps[2] = 1;
5213 
5214             case 3:
5215               ps[3] = 1;
5216 
5217             case 4:
5218               ps[4] = vt_screen_get_rows(vt_parser->screen);
5219 
5220             case 5:
5221               ps[5] = vt_screen_get_cols(vt_parser->screen);
5222             }
5223 
5224             if (num == 1) {
5225               int page;
5226 
5227               checksum = 0;
5228               for (page = 0; page <= MAX_PAGE_ID; page++) {
5229                 int i = vt_screen_get_checksum(vt_parser->screen, ps[3] - 1, ps[2] - 1,
5230                                                   ps[5] - ps[3] + 1, ps[4] - ps[2] + 1, page);
5231                 checksum += i;
5232               }
5233             } else {
5234               checksum = vt_screen_get_checksum(vt_parser->screen, ps[3] - 1, ps[2] - 1,
5235                                                 ps[5] - ps[3] + 1, ps[4] - ps[2] + 1, ps[1] - 1);
5236             }
5237 
5238             sprintf(seq, "\x1bP%d!~%.4x\x1b\\", ps[0], checksum);
5239             vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
5240           }
5241         }
5242       } else if (intmed_ch == '\'') {
5243         if (*str_p == '|') {
5244           /* "CSI ' |" DECRQLP */
5245 
5246           request_locator(vt_parser);
5247         } else if (*str_p == '{') {
5248           /* "CSI Pn ' {" DECSLE */
5249           int count;
5250 
5251           for (count = 0; count < num; count++) {
5252             if (ps[count] == 1) {
5253               vt_parser->locator_mode |= LOCATOR_BUTTON_DOWN;
5254             } else if (ps[count] == 2) {
5255               vt_parser->locator_mode &= ~LOCATOR_BUTTON_DOWN;
5256             } else if (ps[count] == 3) {
5257               vt_parser->locator_mode |= LOCATOR_BUTTON_UP;
5258             } else if (ps[count] == 4) {
5259               vt_parser->locator_mode &= ~LOCATOR_BUTTON_UP;
5260             } else {
5261               vt_parser->locator_mode &= ~(LOCATOR_BUTTON_UP | LOCATOR_BUTTON_DOWN);
5262             }
5263           }
5264         } else if (*str_p == '}') {
5265           /* "CSI ' }" DECIC */
5266 
5267           if (ps[0] <= 0) {
5268             ps[0] = 1;
5269           }
5270 
5271           vt_screen_scroll_rightward_from_cursor(vt_parser->screen, ps[0]);
5272         } else if (*str_p == '~') {
5273           /* "CSI ' ~" DECDC */
5274 
5275           if (ps[0] <= 0) {
5276             ps[0] = 1;
5277           }
5278 
5279           vt_screen_scroll_leftward_from_cursor(vt_parser->screen, ps[0]);
5280         } else if (*str_p == 'w') {
5281           /* "CSI ' w" DECEFR Filter Rectangle */
5282 
5283           if (num == 4) {
5284 #if 0
5285             if (top > ps[0] || left > ps[1] || bottom < ps[2] || right < ps[3]) {
5286               /*
5287                * XXX
5288                * An outside rectangle event should
5289                * be sent immediately.
5290                */
5291             }
5292 #endif
5293 
5294             vt_parser->loc_filter.top = ps[0];
5295             vt_parser->loc_filter.left = ps[1];
5296             vt_parser->loc_filter.bottom = ps[2];
5297             vt_parser->loc_filter.right = ps[3];
5298           }
5299 
5300           vt_parser->locator_mode |= LOCATOR_FILTER_RECT;
5301         } else if (*str_p == 'z') {
5302           /* "CSI ' z" DECELR */
5303 
5304           vt_parser->locator_mode &= ~LOCATOR_FILTER_RECT;
5305           memset(&vt_parser->loc_filter, 0, sizeof(vt_parser->loc_filter));
5306           if (ps[0] <= 0) {
5307             if (vt_parser->mouse_mode >= LOCATOR_CHARCELL_REPORT) {
5308               set_mouse_report(vt_parser, 0);
5309             }
5310           } else {
5311             vt_parser->locator_mode |= ps[0] == 2 ? LOCATOR_ONESHOT : 0;
5312 
5313             set_mouse_report(vt_parser, (num == 1 || ps[1] <= 0 || ps[1] == 2)
5314                                                ? LOCATOR_CHARCELL_REPORT
5315                                                : LOCATOR_PIXEL_REPORT);
5316           }
5317         }
5318       } else if (intmed_ch == '\"') {
5319         if (*str_p == 'v') {
5320           /* "CSI " v" DECRQDE */
5321 
5322           send_display_extent(vt_parser->pty, vt_screen_get_logical_cols(vt_parser->screen),
5323                               vt_screen_get_logical_rows(vt_parser->screen),
5324                               vt_parser->screen->edit->hmargin_beg + 1,
5325                               vt_parser->screen->edit->vmargin_beg + 1,
5326                               vt_screen_get_page_id(vt_parser->screen) + 1);
5327         } else if (*str_p == 'q') {
5328           /* "CSI " q" DECSCA */
5329 
5330           if (ps[0] <= 0 || ps[0] == 2) {
5331             vt_parser->is_protected = 0;
5332           } else if (ps[0] == 1) {
5333             vt_parser->is_protected = 1;
5334           }
5335         } else {
5336           /* "CSI " p"(DECSCL) etc */
5337         }
5338       } else if (intmed_ch == ',') {
5339         if (*str_p == '|') {
5340           /* "CSI Ps1;Ps2;Ps3,|" (DECAC) */
5341 
5342           if (num == 3 && ps[0] == 1 && ((u_int)ps[1]) <= 255 && ((u_int)ps[2]) <= 255) {
5343             config_protocol_set_simple(vt_parser, "fg_color", vt_get_color_name(ps[1]), 0);
5344             config_protocol_set_simple(vt_parser, "bg_color", vt_get_color_name(ps[2]), 1);
5345           }
5346         } else if (*str_p == '}') {
5347           /* "CSI Ps1;Ps2;Ps3,}" (DECATC) */
5348 
5349           if (num == 3 && ((u_int)ps[0]) <= 15 && ((u_int)ps[1]) <= 255 && ((u_int)ps[2] <= 255)) {
5350             int idx = alt_color_idxs[ps[0]];
5351             vt_parser->alt_colors.flags |= (1 << idx);
5352             vt_parser->alt_colors.fg[idx] = ps[1];
5353             vt_parser->alt_colors.bg[idx] = ps[2];
5354           }
5355         }
5356       } else if (intmed_ch == ')') {
5357         if (*str_p == '{') {
5358           /* "CSI ) {" DECSTGLT */
5359           if (ps[0] <= 0) {
5360             vt_parser->use_ansi_colors = 0;
5361           } else if (ps[0] <= 3) {
5362             /*
5363              * ps[0] == 1 or 2 enables "Alternate color", but mlterm always enables it
5364              * if DECATC specifies alternate colors.
5365              */
5366             vt_parser->use_ansi_colors = 1;
5367           }
5368         }
5369       } else if (intmed_ch == '+') {
5370         if (*str_p == 'p') {
5371           /* "CSI + p" DECSR */
5372 
5373           full_reset(vt_parser);
5374 
5375           if (ps[0] >= 0) {
5376             char seq[2+DIGIT_STR_LEN(ps[0])+3];
5377 
5378             sprintf(seq, "\x1b[%d*q", ps[0]);
5379             vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
5380           }
5381         }
5382       }
5383       /* Other para_ch(0x3a-0x3f) or intmed_ch(0x20-0x2f) */
5384       else if (para_ch || intmed_ch) {
5385 #ifdef DEBUG
5386         debug_print_unknown("ESC [ %c %c\n", para_ch, intmed_ch, *str_p);
5387 #endif
5388       } else if (*str_p == '@') {
5389         /* "CSI @" insert blank chars (ICH) */
5390 
5391         if (ps[0] <= 0) {
5392           ps[0] = 1;
5393         }
5394 
5395         /* inserting ps[0] blank characters. */
5396         vt_screen_insert_blank_chars(vt_parser->screen, ps[0]);
5397       } else if (*str_p == 'A' || *str_p == 'k') {
5398         /* "CSI A" = CUU , "CSI k" = VPB */
5399 
5400         if (ps[0] <= 0) {
5401           ps[0] = 1;
5402         }
5403 
5404         vt_screen_go_upward(vt_parser->screen, ps[0]);
5405       } else if (*str_p == 'B' || *str_p == 'e') {
5406         /* "CSI B" = CUD , "CSI e" = VPR */
5407 
5408         if (ps[0] <= 0) {
5409           ps[0] = 1;
5410         }
5411 
5412         vt_screen_go_downward(vt_parser->screen, ps[0]);
5413       } else if (*str_p == 'C' || *str_p == 'a') {
5414         /* "CSI C" = CUF , "CSI a" = HPR */
5415 
5416         if (ps[0] <= 0) {
5417           ps[0] = 1;
5418         }
5419 
5420         vt_screen_go_forward(vt_parser->screen, ps[0], 0);
5421       } else if (*str_p == 'D' || *str_p == 'j') {
5422         /* "CSI D" = CUB , "CSI j" = HPB */
5423 
5424         if (ps[0] <= 0) {
5425           ps[0] = 1;
5426         }
5427 
5428         vt_screen_go_back(vt_parser->screen, ps[0], 0);
5429       } else if (*str_p == 'E') {
5430         /* "CSI E" down and goto first column (CNL) */
5431 
5432         if (ps[0] <= 0) {
5433           ps[0] = 1;
5434         }
5435 
5436         vt_screen_go_downward(vt_parser->screen, ps[0]);
5437         vt_screen_goto_beg_of_line(vt_parser->screen);
5438       } else if (*str_p == 'F') {
5439         /* "CSI F" up and goto first column (CPL) */
5440 
5441         if (ps[0] <= 0) {
5442           ps[0] = 1;
5443         }
5444 
5445         vt_screen_go_upward(vt_parser->screen, ps[0]);
5446         vt_screen_goto_beg_of_line(vt_parser->screen);
5447       } else if (*str_p == 'G' || *str_p == '`') {
5448         /*
5449          * "CSI G" (CHA), "CSI `" (HPA)
5450          * cursor position absolute.
5451          */
5452 
5453         if (ps[0] <= 0) {
5454           ps[0] = 1;
5455         }
5456 
5457         vt_screen_go_horizontally(vt_parser->screen, ps[0] - 1);
5458       } else if (*str_p == 'H' || *str_p == 'f') {
5459         /* "CSI H" (CUP) "CSI f" (HVP) */
5460 
5461         if (ps[0] <= 0) {
5462           ps[0] = 1;
5463         }
5464 
5465         if (num <= 1 || ps[1] <= 0) {
5466           ps[1] = 1;
5467         }
5468 
5469         vt_screen_goto(vt_parser->screen, ps[1] - 1, ps[0] - 1);
5470       } else if (*str_p == 'I') {
5471         /* "CSI I" cursor forward tabulation (CHT) */
5472 
5473         if (ps[0] == -1) {
5474           /*
5475            * "CSI 0 I" => No tabulation.
5476            * "CSI I" => 1 taburation.
5477            */
5478           ps[0] = 1;
5479         }
5480 
5481         vt_screen_forward_tabs(vt_parser->screen, ps[0]);
5482       } else if (*str_p == 'J') {
5483         /* "CSI J" Erase in Display (ED) */
5484 
5485         if (ps[0] <= 0) {
5486           vt_screen_clear_below(vt_parser->screen);
5487         } else if (ps[0] == 1) {
5488           vt_screen_clear_above(vt_parser->screen);
5489         } else if (ps[0] == 2) {
5490           clear_display_all(vt_parser);
5491         }
5492 #if 1
5493         /* xterm compatible (Tera Term 4.105 or later also supports) */
5494         else if (ps[0] == 3) {
5495           u_int size = vt_screen_get_log_size(vt_parser->screen);
5496           int unlimited = vt_screen_log_size_is_unlimited(vt_parser->screen);
5497 
5498           vt_screen_change_log_size(vt_parser->screen, 0);
5499           if (unlimited) {
5500             vt_screen_unlimit_log_size(vt_parser->screen);
5501           } else {
5502             vt_screen_change_log_size(vt_parser->screen, size);
5503           }
5504         }
5505 #endif
5506       } else if (*str_p == 'K') {
5507         /* "CSI K" Erase in Line (EL) */
5508 
5509         if (ps[0] <= 0) {
5510           vt_screen_clear_line_to_right(vt_parser->screen);
5511         } else if (ps[0] == 1) {
5512           vt_screen_clear_line_to_left(vt_parser->screen);
5513         } else if (ps[0] == 2) {
5514           clear_line_all(vt_parser);
5515         }
5516       } else if (*str_p == 'L') {
5517         /* "CSI L" IL */
5518 
5519         if (ps[0] <= 0) {
5520           ps[0] = 1;
5521         }
5522 
5523         vt_screen_insert_new_lines(vt_parser->screen, ps[0]);
5524       } else if (*str_p == 'M') {
5525         /* "CSI M" DL */
5526 
5527         if (ps[0] <= 0) {
5528           ps[0] = 1;
5529         }
5530 
5531         vt_screen_delete_lines(vt_parser->screen, ps[0]);
5532       } else if (*str_p == 'P') {
5533         /* "CSI P" delete chars (DCH) */
5534 
5535         if (ps[0] <= 0) {
5536           ps[0] = 1;
5537         }
5538 
5539         vt_screen_delete_cols(vt_parser->screen, ps[0]);
5540       } else if (*str_p == 'S') {
5541         /* "CSI S" scroll up (SU) */
5542 
5543         if (ps[0] <= 0) {
5544           ps[0] = 1;
5545         }
5546 
5547         vt_screen_scroll_upward(vt_parser->screen, ps[0]);
5548       } else if (*str_p == 'T') {
5549         /* "CSI T" scroll down (SD) */
5550 
5551         if (ps[0] <= 0) {
5552           ps[0] = 1;
5553         }
5554 
5555         vt_screen_scroll_downward(vt_parser->screen, ps[0]);
5556       } else if (*str_p == 'U') {
5557         /* "CSI U" (NP) */
5558 
5559         if (ps[0] <= 0) {
5560           ps[0] = 1;
5561         }
5562 
5563         vt_screen_goto_next_page(vt_parser->screen, ps[0]);
5564         vt_screen_goto_home(vt_parser->screen);
5565       } else if (*str_p == 'V') {
5566         /* "CSI V" (PP) */
5567 
5568         if (ps[0] <= 0) {
5569           ps[0] = 1;
5570         }
5571 
5572         vt_screen_goto_prev_page(vt_parser->screen, ps[0]);
5573         vt_screen_goto_home(vt_parser->screen);
5574       } else if (*str_p == 'W') {
5575         /* "CSI W" Cursor Tabluation Control (CTC) */
5576 
5577         if (ps[0] <= 0) {
5578           vt_screen_set_tab_stop(vt_parser->screen);
5579         } else if (ps[0] == 2) {
5580           vt_screen_clear_tab_stop(vt_parser->screen);
5581         } else if (ps[0] == 4 || ps[0] == 5) {
5582           vt_screen_clear_all_tab_stops(vt_parser->screen);
5583         }
5584       } else if (*str_p == 'X') {
5585         /* "CSI X" erase characters (ECH) */
5586 
5587         if (ps[0] <= 0) {
5588           ps[0] = 1;
5589         }
5590 
5591         vt_screen_clear_cols(vt_parser->screen, ps[0]);
5592       } else if (*str_p == 'Z') {
5593         /* "CSI Z" cursor backward tabulation (CBT) */
5594 
5595         if (ps[0] == -1) {
5596           /*
5597            * "CSI 0 Z" => No tabulation.
5598            * "CSI Z" => 1 taburation.
5599            */
5600           ps[0] = 1;
5601         }
5602 
5603         vt_screen_backward_tabs(vt_parser->screen, ps[0]);
5604       } else if (*str_p == 'b') {
5605         /* "CSI b" repeat the preceding graphic character(REP) */
5606 
5607         if (vt_parser->w_buf.last_ch) {
5608           int count;
5609 
5610           if (ps[0] <= 0) {
5611             ps[0] = 1;
5612           }
5613 
5614           for (count = 0; count < ps[0]; count++) {
5615             (*vt_parser->w_buf.output_func)(vt_parser->screen, vt_parser->w_buf.last_ch,
5616                                                1);
5617           }
5618 
5619           vt_parser->w_buf.last_ch = NULL;
5620         }
5621       } else if (*str_p == 'c') {
5622         /* "CSI c" Primary DA (DA1) */
5623 
5624         send_device_attributes(vt_parser->pty, 1);
5625       } else if (*str_p == 'd') {
5626         /* "CSI d" line position absolute(VPA) */
5627 
5628         if (ps[0] <= 0) {
5629           ps[0] = 1;
5630         }
5631 
5632         vt_screen_go_vertically(vt_parser->screen, ps[0] - 1);
5633       } else if (*str_p == 'g') {
5634         /* "CSI g" tab clear (TBC) */
5635 
5636         if (ps[0] <= 0) {
5637           vt_screen_clear_tab_stop(vt_parser->screen);
5638         } else if (ps[0] == 3) {
5639           vt_screen_clear_all_tab_stops(vt_parser->screen);
5640         }
5641       } else if (*str_p == 'h') {
5642         /* "CSI h" (SM) */
5643         int count;
5644 
5645         for (count = 0; count < num; count++) {
5646           set_vtmode(vt_parser, VTMODE(ps[count]), 1);
5647         }
5648       } else if (*str_p == 'i') {
5649         /* "CSI i" (MC) */
5650 
5651         if (ps[0] <= 0) {
5652           snapshot(vt_parser, VT_UTF8, vt_pty_get_slave_name(vt_parser->pty) + 5 /* skip /dev/ */,
5653                    WCA_SCREEN);
5654         } else if (ps[0] == 1) {
5655           snapshot(vt_parser, VT_UTF8, vt_pty_get_slave_name(vt_parser->pty) + 5 /* skip /dev/ */,
5656                    WCA_CURSOR_LINE);
5657         }
5658       } else if (*str_p == 'l') {
5659         /* "CSI l" (RM) */
5660         int count;
5661 
5662         for (count = 0; count < num; count++) {
5663           set_vtmode(vt_parser, VTMODE(ps[count]), 0);
5664         }
5665       } else if (*str_p == 'm') {
5666         /* "CSI m" (SGR) */
5667         int count;
5668 
5669         for (count = 0; count < num;) {
5670           int proceed;
5671 
5672           if ((proceed = change_char_fine_color(vt_parser, ps + count, num - count))) {
5673             count += proceed;
5674           } else {
5675             if (ps[count] < 0) {
5676               ps[count] = 0;
5677             }
5678 
5679             change_char_attr(vt_parser, ps[count++]);
5680           }
5681         }
5682       } else if (*str_p == 'n') {
5683         /* "CSI n" Device Status Report (DSR) */
5684 
5685         if (ps[0] == 5) {
5686           /* Operating Status */
5687           vt_write_to_pty(vt_parser->pty, "\x1b[0n", 4);
5688         } else if (ps[0] == 6) {
5689           /* CPR */
5690           char seq[4 + DIGIT_STR_LEN(u_int) * 2 + 1];
5691 
5692           sprintf(seq, "\x1b[%d;%dR", vt_screen_cursor_logical_row(vt_parser->screen) + 1,
5693                   vt_screen_cursor_logical_col(vt_parser->screen) + 1);
5694 
5695           vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
5696         }
5697       } else if (*str_p == 'r') {
5698         /* "CSI r" set scroll region (DECSTBM) */
5699 
5700         if (ps[0] < 0) {
5701           ps[0] = 0;
5702         }
5703 
5704         if (num <= 1 || ps[1] < 0) {
5705           ps[1] = 0;
5706         }
5707 
5708         vt_screen_set_vmargin(vt_parser->screen, ps[0] - 1, ps[1] - 1);
5709         vt_screen_goto(vt_parser->screen, 0, 0);
5710       } else if (*str_p == 's') {
5711         /* "CSI s" SCOSC or DECSLRM */
5712 
5713         if (num <= 1 || ps[1] <= 0) {
5714           ps[1] = vt_screen_get_logical_cols(vt_parser->screen);
5715         }
5716 
5717         if (vt_screen_set_hmargin(vt_parser->screen, ps[0] <= 0 ? 0 : ps[0] - 1, ps[1] - 1)) {
5718           vt_screen_goto(vt_parser->screen, 0, 0);
5719         } else if (num == 1 && ps[0] == -1) {
5720           save_cursor(vt_parser);
5721         }
5722       } else if (*str_p == 't') {
5723         /* "CSI t" */
5724 
5725         if (ps[0] == 4 || ps[0] == 8) {
5726           if (num == 2) {
5727             ps[2] = -1;
5728             num = 3;
5729           }
5730 
5731           if (num == 3) {
5732             resize(vt_parser, ps[2], ps[1], ps[0] == 8);
5733           }
5734         } else if (ps[0] == 9) {
5735           if (num == 2 && 0 <= ps[1] && ps[1] <= 3) {
5736             int flag;
5737 
5738             if (ps[1] >= 2) {
5739               flag = ps[1]; /* MAXIMIZE VERTICALLY or HORIZONTALLY */
5740             } else {
5741               flag = (ps[1] == 0 ? 1 /* UNMAXIMIZE */ : 4 /* MAXIMIZE FULL */);
5742             }
5743 
5744             set_maximize(vt_parser, flag);
5745           }
5746         } else if (ps[0] == 10) {
5747           /* XXX full screen is not supported for now. */
5748         } else if (ps[0] == 7) {
5749           char cmd[] = "update_all";
5750           config_protocol_set(vt_parser, cmd, 0);
5751         } else if (ps[0] == 11) {
5752           vt_write_to_pty(vt_parser->pty, "\x1b[1t", 4); /* XXX always non-iconified */
5753         } else if (ps[0] == 13) {
5754           vt_write_to_pty(vt_parser->pty, "\x1b[3;0;0t", 8);
5755         } else if (ps[0] == 14) {
5756           report_window_size(vt_parser, 0);
5757         } else if (ps[0] == 15) {
5758           report_display_size(vt_parser, 0);
5759         } else if (ps[0] == 16) {
5760           report_cell_size(vt_parser);
5761         } else if (ps[0] == 18) {
5762           report_window_size(vt_parser, 1);
5763         } else if (ps[0] == 19) {
5764           report_display_size(vt_parser, 1);
5765         } else if (ps[0] == 20) {
5766           report_window_or_icon_name(vt_parser, 0);
5767         } else if (ps[0] == 21) {
5768           report_window_or_icon_name(vt_parser, 1);
5769         } else if (ps[0] >= 24) {
5770           /*
5771            * "CSI Pn t" DECSLPP
5772            * This changes not only the number of lines but also
5773            * the number of pages, but mlterm doesn't change the latter.
5774            */
5775           resize(vt_parser, -1, ps[0], 1);
5776         } else if (num == 2) {
5777           if (ps[0] == 22) {
5778             /*
5779              * XXX
5780              * If Icon and window title are pushed by ps[1] = 0 and either of them is poped,
5781              * the other should be poped.
5782              * But it is not supported for now.
5783              * esctest: XtermWinopsTests.test_XtermWinops_PushIconAndWindow_PopIcon and
5784              * XtermWinopsTests.test_XtermWinops_PushIconAndWindow_PopWindow fails by this.
5785              */
5786             if (ps[1] == 0 || ps[1] == 1) {
5787               push_to_saved_names(&vt_parser->saved_icon_names, vt_parser->icon_name);
5788             }
5789 
5790             if (ps[1] == 0 || ps[1] == 2) {
5791               push_to_saved_names(&vt_parser->saved_win_names, vt_parser->win_name);
5792             }
5793           } else if (ps[0] == 23) {
5794             if ((ps[1] == 0 || ps[1] == 1) && vt_parser->saved_icon_names.num > 0) {
5795               set_icon_name(vt_parser, pop_from_saved_names(&vt_parser->saved_icon_names));
5796             }
5797 
5798             if ((ps[1] == 0 || ps[1] == 2) && vt_parser->saved_win_names.num > 0) {
5799               set_window_name(vt_parser, pop_from_saved_names(&vt_parser->saved_win_names));
5800             }
5801           }
5802         }
5803       } else if (*str_p == 'u') {
5804         /* "CSI u" (SCORC) */
5805 
5806         restore_cursor(vt_parser);
5807       } else if (*str_p == 'x') {
5808         /* "CSI x" request terminal parameters (DECREQTPARM) */
5809 
5810         /* XXX the same as rxvt */
5811 
5812         if (ps[0] < 0) {
5813           ps[0] = 0;
5814         }
5815 
5816         if (ps[0] == 0 || ps[0] == 1) {
5817           char seq[] = "\x1b[X;1;1;112;112;1;0x";
5818 
5819           /* '+ 0x30' lets int to char */
5820           seq[2] = ps[0] + 2 + 0x30;
5821 
5822           vt_write_to_pty(vt_parser->pty, seq, sizeof(seq) - 1);
5823         }
5824       }
5825 #if 0
5826       else if (*str_p == '^') {
5827         /* "CSI ^" initiate hilite mouse tracking. */
5828       }
5829 #endif
5830       /* Other final character */
5831       else if (0x40 <= *str_p && *str_p <= 0x7e) {
5832 #ifdef DEBUG
5833         debug_print_unknown("ESC [ %c\n", *str_p);
5834 #endif
5835       } else {
5836 /* not VT100 control sequence. */
5837 
5838 #ifdef ESCSEQ_DEBUG
5839         bl_msg_printf("=> not VT100 control sequence.\n");
5840 #endif
5841 
5842         return 1;
5843       }
5844     } else if (*str_p == ']') {
5845       /* "ESC ]" (OSC) */
5846 
5847       char digit[DIGIT_STR_LEN(int)+1];
5848       int count;
5849       int ps;
5850       u_char *pt;
5851 
5852       if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
5853         return 0;
5854       }
5855 
5856       for (count = 0; count < DIGIT_STR_LEN(int); count++) {
5857         if ('0' <= *str_p && *str_p <= '9') {
5858           digit[count] = *str_p;
5859 
5860           if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
5861             return 0;
5862           }
5863         } else {
5864           break;
5865         }
5866       }
5867 
5868       if (count > 0 && *str_p == ';') {
5869         digit[count] = '\0';
5870 
5871         /* if digit is illegal , ps is set 0. */
5872         ps = atoi(digit);
5873 #ifdef MAX_PS_DIGIT
5874         if (ps > MAX_PS_DIGIT) {
5875           ps = MAX_PS_DIGIT;
5876         }
5877 #endif
5878 
5879         if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 1)) {
5880           return 0;
5881         }
5882       } else {
5883         /* Illegal OSC format */
5884         ps = -1;
5885       }
5886 
5887       pt = str_p;
5888 
5889       /*
5890        * Skip string which was already parsed.
5891        * +1 in case str_p[left - vt_parser->r_buf.new_len] points
5892        * "\\" of "\x1b\\".
5893        */
5894       if (left > vt_parser->r_buf.new_len + 1) {
5895         str_p += (left - vt_parser->r_buf.new_len - 1);
5896         left = vt_parser->r_buf.new_len + 1;
5897       }
5898 
5899       if (!get_pt_in_esc_seq(&str_p, &left, 0, 1)) {
5900         if (left == 0) {
5901           return 0;
5902         }
5903 #ifdef DEBUG
5904         else {
5905           debug_print_unknown("ESC ] %d ; ???\n", ps);
5906         }
5907 #endif
5908       } else if (ps == 0) {
5909         /* "OSC 0" change icon name and window title */
5910 
5911         if (*pt != '\0') {
5912           if ((pt = parse_title(vt_parser, strdup(pt)))) {
5913             set_window_name(vt_parser, pt);
5914             set_icon_name(vt_parser, strdup(pt));
5915           }
5916         }
5917       } else if (ps == 1) {
5918         /* "OSC 1" change icon name */
5919 
5920         if (*pt != '\0') {
5921           if ((pt = parse_title(vt_parser, strdup(pt)))) {
5922             set_icon_name(vt_parser, pt);
5923           }
5924         }
5925       } else if (ps == 2) {
5926         /* "OSC 2" change window title */
5927 
5928         if (*pt != '\0') {
5929           if ((pt = parse_title(vt_parser, strdup(pt)))) {
5930             set_window_name(vt_parser, pt);
5931           }
5932         }
5933       } else if (ps == 4) {
5934         /* "OSC 4" change 256 color */
5935 
5936         change_color_rgb(vt_parser, pt);
5937       } else if (ps == 5) {
5938         /* "OSC 5" change colorBD/colorUL */
5939 
5940         change_special_color(vt_parser, pt);
5941       } else if (ps == 10) {
5942         /* "OSC 10" fg color */
5943 
5944         change_fgbg_color(vt_parser, 10, pt);
5945       } else if (ps == 11) {
5946         /* "OSC 11" bg color */
5947 
5948         change_fgbg_color(vt_parser, 11, pt);
5949       } else if (ps == 12) {
5950         /* "OSC 12" cursor bg color */
5951 
5952         if (strcmp(pt, "?") != 0) /* ?:query rgb */ {
5953           /* XXX do nothing for now .*/
5954         } else {
5955           config_protocol_set_simple(vt_parser, "cursor_bg_color", pt, 1);
5956         }
5957       } else if (ps == 20) {
5958         /* "OSC 20" (Eterm compatible) */
5959 
5960         /* edit commands */
5961         char *p;
5962 
5963         /* XXX discard all adjust./op. settings.*/
5964         /* XXX may break multi-byte char string. */
5965         if ((p = strchr(pt, ';'))) {
5966           *p = '\0';
5967         }
5968         if ((p = strchr(pt, ':'))) {
5969           *p = '\0';
5970         }
5971 
5972         if (*pt == '\0') {
5973           /*
5974            * Do not change current edit but
5975            * alter diaplay setting.
5976            * XXX nothing can be done for now.
5977            */
5978 
5979           return 0;
5980         }
5981 
5982         config_protocol_set_simple(vt_parser, "wall_picture", pt, 1);
5983       }
5984 #if 0
5985       else if (ps == 46) {
5986         /* "OSC 46" change log file */
5987       } else if (ps == 50) {
5988         /* "OSC 50" set font */
5989       }
5990 #endif
5991       else if (ps == 52) {
5992         set_selection(vt_parser, pt);
5993       } else if (ps == 104) {
5994         reset_color_rgb(vt_parser, pt, 0);
5995       } else if (ps == 105) {
5996         reset_color_rgb(vt_parser, pt, 1);
5997       }
5998 #if 0
5999       else if (ps == 110) {
6000         config_protocol_set_simple(vt_parser, "fg_color", "black", 1);
6001       } else if (ps == 111) {
6002         config_protocol_set_simple(vt_parser, "bg_color", "white", 1);
6003       } else if (ps == 112) {
6004         config_protocol_set_simple(vt_parser, "cursor_bg_color", "black", 1);
6005       }
6006 #endif
6007 #ifdef SUPPORT_ITERM2_OSC1337
6008       else if (ps == 1337) {
6009         if (strcmp(pt, "SetMark") == 0) {
6010           vt_line_t *line;
6011 
6012           if ((line = vt_screen_get_cursor_line(vt_parser->screen))) {
6013             line->mark = 1;
6014           }
6015         } else {
6016           iterm2_proprietary_set(vt_parser, pt);
6017         }
6018       }
6019 #endif
6020       else if (ps == 5379) {
6021         /* "OSC 5379" set */
6022 
6023         config_protocol_set(vt_parser, pt, 0);
6024       } else if (ps == 5380) {
6025         /* "OSC 5380" get */
6026 
6027         config_protocol_get(vt_parser, pt, 0, NULL);
6028       } else if (ps == 5381) {
6029         /* "OSC 5381" get(menu) */
6030 
6031         config_protocol_get(vt_parser, pt, 1, NULL);
6032       } else if (ps == 5383) {
6033         /* "OSC 5383" set&save */
6034 
6035         config_protocol_set(vt_parser, pt, 1);
6036       }
6037 #ifdef DEBUG
6038       else if (ps == -1) {
6039         debug_print_unknown("ESC ] %s\n", pt);
6040       } else {
6041         debug_print_unknown("ESC ] %d ; %s\n", ps, pt);
6042       }
6043 #endif
6044     } else if (*str_p == 'P') {
6045       /* "ESC P" DCS */
6046 
6047       u_char *dcs_beg;
6048 #ifndef NO_IMAGE
6049       char *path;
6050 #endif
6051 
6052       while (1) {
6053         /* ESC P ... */
6054         dcs_beg = str_p - 1;
6055         break;
6056 
6057       parse_dcs:
6058         /* 0x90 ... */
6059         dcs_beg = str_p;
6060         break;
6061       }
6062 
6063       do {
6064         if (!increment_str(&str_p, &left)) {
6065           return 0;
6066         }
6067       } while (*str_p == ';' || ('0' <= *str_p && *str_p <= '9'));
6068 
6069 #ifndef NO_IMAGE
6070       if (/* sixel */
6071           (*str_p == 'q' &&
6072            (path = get_home_file_path("", vt_pty_get_slave_name(vt_parser->pty) + 5, "six"))) ||
6073           /* ReGIS */
6074           (*str_p == 'p' &&
6075            (path = get_home_file_path("", vt_pty_get_slave_name(vt_parser->pty) + 5, "rgs")))) {
6076         if (!save_sixel_or_regis(vt_parser, path, dcs_beg, &str_p, &left)) {
6077           return 0;
6078         }
6079 
6080         if (strcmp(path + strlen(path) - 4, ".six") == 0) {
6081           show_picture(vt_parser, path, 0, 0, 0, 0, 0, 0, 0,
6082                        (!vt_parser->sixel_scrolling &&
6083                         check_sixel_anim(vt_parser->screen, str_p, left)) ? 2 : 1);
6084         } else {
6085           /* ReGIS */
6086           int orig_flag;
6087 
6088           orig_flag = vt_parser->sixel_scrolling;
6089           vt_parser->sixel_scrolling = 0;
6090           show_picture(vt_parser, path, 0, 0, 0, 0, 0, 0, 0,
6091                        1 /* is_sixel (vt_parser->sixel_scrolling is applied) */);
6092           vt_parser->sixel_scrolling = orig_flag;
6093         }
6094 
6095         free(path);
6096       } else
6097 #endif /* NO_IMAGE */
6098       if (*str_p == '{') {
6099         /* DECDLD */
6100 
6101         u_char *param;
6102         ef_charset_t cs;
6103         int num;
6104         u_char *p;
6105         int ps[9];
6106         int idx;
6107         int is_end;
6108         u_int col_width;
6109         u_int line_height;
6110 
6111         if (*dcs_beg == '\x1b') {
6112           param = dcs_beg + 2;
6113         } else /* if( *dcs_beg == '\x90') */ {
6114           param = dcs_beg + 1;
6115         }
6116 
6117         for (num = 0; num < 9; num++) {
6118           u_char c;
6119 
6120           p = param;
6121 
6122           while ('0' <= *param && *param <= '9') {
6123             param++;
6124           }
6125 
6126           c = *param;
6127           if (c != ';' && c != '{') {
6128             break;
6129           }
6130           *param = '\0';
6131           ps[num] = *p ? atoi(p) : 0;
6132           *(param++) = c; /* restore in case of restarting to parse from the begining. */
6133         }
6134 
6135         if (num != 8) {
6136           if (!get_pt_in_esc_seq(&str_p, &left, 1, 0)) {
6137             return 0;
6138           }
6139         } else {
6140           char *path;
6141 
6142           if (!increment_str(&str_p, &left)) {
6143             return 0;
6144           }
6145 
6146           if (*str_p == ' ') {
6147             /* ESC ( SP Ft */
6148             if (!increment_str(&str_p, &left)) {
6149               return 0;
6150             }
6151           }
6152 
6153           idx = ps[1];
6154 
6155           if (0x30 <= *str_p && *str_p <= 0x7e) {
6156             /* Ft */
6157             if (ps[7] == 0) {
6158               cs = CS94SB_ID(*str_p);
6159             } else {
6160               cs = CS96SB_ID(*str_p);
6161             }
6162 
6163             if (ps[3] <= 4 || ps[3] >= 255) {
6164               col_width = 15;
6165             } else {
6166               col_width = ps[3];
6167             }
6168 
6169             if (ps[6] == 0 || ps[6] >= 255) {
6170               line_height = 12;
6171             } else {
6172               line_height = ps[6];
6173             }
6174           } else {
6175             cs = UNKNOWN_CS;
6176             col_width = line_height = 0;
6177           }
6178 
6179 #ifndef NO_IMAGE
6180           if (ps[5] == 3 && cs != UNKNOWN_CS &&
6181               (path = get_home_file_path("", vt_pty_get_slave_name(vt_parser->pty) + 5,
6182                                          "six"))) {
6183             /* DRCS Sixel */
6184             u_char *orig_drcs_header;
6185             size_t drcs_header_len = str_p - dcs_beg + 1;
6186             u_char *orig_sixel_size = NULL;
6187             size_t sixel_size_len = 0;
6188             int tmp;
6189             int pix_width = 0;
6190             int pix_height = 0;
6191 
6192             orig_drcs_header = alloca(drcs_header_len);
6193             memcpy(orig_drcs_header, dcs_beg, drcs_header_len);
6194 
6195             dcs_beg = str_p - 1;
6196 
6197             /* skip sixel header parameters */
6198             do {
6199               if (!increment_str(&str_p, &left)) {
6200                 free(path);
6201 
6202                 return 0;
6203               }
6204             } while (*str_p == ';' || ('0' <= *str_p && *str_p <= '9'));
6205 
6206             if (*str_p != 'q') {
6207               dcs_beg--;
6208               dcs_beg[0] = '\x1b';
6209               dcs_beg[1] = 'P';
6210               dcs_beg[2] = 'q';
6211               str_p--; /* str_p points 'q' */
6212               left++;
6213             } else {
6214               dcs_beg[0] = '\x1b';
6215               dcs_beg[1] = 'P';
6216 
6217               if (left == 0) {
6218                 return 0;
6219               }
6220             }
6221 
6222             /*
6223              * Read width and height of sixel graphics from "Pan;Pad;Ph;Pv.
6224              * If failed, it is impossible to scale image pieces according to Pcmw and Pcmh.
6225              */
6226             if (str_p[1] == '"' && !check_cell_size(vt_parser, col_width, line_height)) {
6227               if (left < 2) { /* XXX */
6228                 return 0;
6229               }
6230 
6231               if (sscanf(str_p + 2, "%d;%d;%d;%d", &tmp, &tmp, &pix_width, &pix_height) == 4 &&
6232                   pix_width > 0 && pix_height > 0) {
6233                 sixel_size_len = 1; /* q */
6234                 while (left > ++sixel_size_len &&
6235                        '0' <= str_p[sixel_size_len] && str_p[sixel_size_len] <= ';');
6236                 orig_sixel_size = alloca(sixel_size_len);
6237                 memcpy(orig_sixel_size, str_p, sixel_size_len);
6238 
6239                 if (str_p[sixel_size_len] == 'q') {
6240                   /*
6241                    * Starting DRCS Sixel:   q"Pan;Pad;Ph;Pv#...
6242                    * Continuing DRCS Sixel: q"Pan;Pad;Ph;Pv\0q...
6243                    */
6244                   str_p += sixel_size_len;
6245                   left -= sixel_size_len;
6246                 }
6247               }
6248             }
6249 
6250             if (!save_sixel_or_regis(vt_parser, path, dcs_beg, &str_p, &left)) {
6251               /*
6252                * q"Pan;Pad;Ph;Pvq\0...
6253                *                ^^^^^^
6254                */
6255               memmove(vt_parser->r_buf.chars + drcs_header_len + sixel_size_len,
6256                       vt_parser->r_buf.chars + 2 /* ESC P */,
6257                       vt_parser->r_buf.filled_len - 2);
6258               /*
6259                * q"Pan;Pad;Ph;Pvq\0...
6260                * ^^^^^^^^^^^^^^^
6261                */
6262               memcpy(vt_parser->r_buf.chars + drcs_header_len, orig_sixel_size, sixel_size_len);
6263               memcpy(vt_parser->r_buf.chars, orig_drcs_header, drcs_header_len);
6264               vt_parser->r_buf.filled_len += (drcs_header_len - 2 + sixel_size_len);
6265               vt_parser->r_buf.left += (drcs_header_len - 2 + sixel_size_len);
6266 
6267               return 0;
6268             }
6269 
6270             define_drcs_picture(vt_parser, path, cs, idx, pix_width, pix_height,
6271                                 col_width, line_height);
6272 
6273             free(path);
6274           } else
6275 #endif
6276           {
6277             u_char *pt = str_p;
6278             vt_drcs_font_t *font;
6279 
6280             if (!get_pt_in_esc_seq(&str_p, &left, 1, 0)) {
6281               return 0;
6282             }
6283 
6284             if (cs == UNKNOWN_CS) {
6285               if (ps[2] == 2) {
6286                 destroy_drcs(vt_parser->drcs);
6287               }
6288             } else {
6289               if (ps[2] == 0) {
6290                 vt_drcs_final(vt_parser->drcs, cs);
6291               } else if (ps[2] == 2) {
6292                 vt_drcs_final_full(vt_parser->drcs);
6293               }
6294 
6295               if (!vt_parser->drcs) {
6296                 vt_parser->drcs = vt_drcs_new();
6297               }
6298 
6299               font = vt_drcs_get_font(vt_parser->drcs, cs, 1);
6300 
6301               while (1) {
6302                 p = ++pt;
6303 
6304                 while (*pt == '/' || ('?' <= *pt && *pt <= '~')) {
6305                   pt++;
6306                 }
6307 
6308                 if (*pt) {
6309                   *pt = '\0';
6310                   is_end = 0;
6311                 } else {
6312                   is_end = 1;
6313                 }
6314 
6315                 if (*p) {
6316                   if (strlen(p) == (col_width + 1) * ((line_height + 5) / 6) - 1) {
6317                     vt_drcs_add_glyph(font, idx, p, col_width, line_height);
6318                   }
6319 #ifdef DEBUG
6320                   else {
6321                     bl_debug_printf(BL_DEBUG_TAG "DRCS illegal size %s\n", p);
6322                   }
6323 #endif
6324 
6325                   idx++;
6326                 }
6327 
6328                 if (is_end) {
6329                   break;
6330                 }
6331               }
6332             }
6333           }
6334         }
6335       } else {
6336         u_char *macro;
6337         u_char *tckey;
6338         u_char *status;
6339         u_char *present;
6340 
6341         macro = tckey = status = present = NULL;
6342 
6343         if ((*str_p == '!' && *(str_p + 1) == 'z') ||
6344             ((*str_p == '+' || *str_p == '$') && *(str_p + 1) == 'q') ||
6345             (*str_p == '$' && *(str_p + 1) == 't')) {
6346           if (left <= 2) {
6347             left = 0;
6348 
6349             return 0;
6350           }
6351 
6352           str_p += 2;
6353           left -= 2;
6354 
6355           if (*(str_p - 1) == 'z' /* && *(str_p - 2) == '!' */) {
6356             /* DECDMAC */
6357             macro = str_p;
6358           } else if (*(str_p - 1) == 'q') {
6359             if (*(str_p - 2) == '$') {
6360               /* DECRQSS */
6361               status = str_p;
6362             } else {
6363               /* Termcap query */
6364               tckey = str_p;
6365             }
6366           } else /* if (*(str_p - 1) == t && *(str_p - 2) == '$') */ {
6367             /* DECRSPS */
6368             present = str_p;
6369           }
6370         } else {
6371           if (!increment_str(&str_p, &left)) {
6372             return 0;
6373           }
6374         }
6375 
6376         /*
6377          * +1 in case str_p[left - vt_parser->r_buf.new_len] points
6378          * "\\" of "\x1b\\".
6379          */
6380         if (left > vt_parser->r_buf.new_len + 1) {
6381           str_p += (left - vt_parser->r_buf.new_len - 1);
6382           left = vt_parser->r_buf.new_len + 1;
6383         }
6384 
6385         if (get_pt_in_esc_seq(&str_p, &left, 1, 0)) {
6386           if (macro) {
6387             define_macro(vt_parser, dcs_beg + (*dcs_beg == '\x1b' ? 2 : 1), macro);
6388           } else if (status) {
6389             report_status(vt_parser, status);
6390           } else if (tckey) {
6391             report_termcap(vt_parser, tckey);
6392           } else if (present) {
6393             set_presentation_state(vt_parser, present);
6394           }
6395         } else if (left == 0) {
6396           return 0;
6397         }
6398       }
6399     } else if (*str_p == 'X' || *str_p == '^' || *str_p == '_') {
6400       /*
6401        * "ESC X" SOS
6402        * "ESC ^" PM
6403        * "ESC _" APC
6404        */
6405       if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
6406         return 0;
6407       }
6408 
6409       /* +1 in case str_p[left - new_len] points "\\" of "\x1b\\". */
6410       if (left > vt_parser->r_buf.new_len + 1) {
6411         str_p += (left - vt_parser->r_buf.new_len - 1);
6412         left = vt_parser->r_buf.new_len + 1;
6413       }
6414 
6415       if (!get_pt_in_esc_seq(&str_p, &left, 1, 0) && left == 0) {
6416         return 0;
6417       }
6418     }
6419     /* Other final character */
6420     else if (0x30 <= *str_p && *str_p <= 0x7e) {
6421 #ifdef DEBUG
6422       debug_print_unknown("ESC %c\n", *str_p);
6423 #endif
6424     }
6425     /* intermediate character */
6426     else if (0x20 <= *str_p && *str_p <= 0x2f) {
6427       /*
6428        * ESC I.....I  F
6429        * 033 040-057  060-176
6430        */
6431       u_int ic_num;
6432 
6433       ic_num = 0;
6434 
6435       /* In case more than one intermediate(0x20-0x2f) chars. */
6436       do {
6437         ic_num++;
6438 
6439         if (!inc_str_in_esc_seq(vt_parser->screen, &str_p, &left, 0)) {
6440           return 0;
6441         }
6442       } while (0x20 <= *str_p && *str_p <= 0x2f);
6443 
6444       if (ic_num == 1 || ic_num == 2) {
6445         if (ic_num == 1 && *(str_p - 1) == '#') {
6446           if ('3' <= *str_p && *str_p <= '6') {
6447             vt_line_t *line;
6448 
6449             line = vt_screen_get_cursor_line(vt_parser->screen);
6450             if (*str_p == '3') {
6451               /*
6452                * "ESC # 3" DEC double-height line,
6453                * top half (DECDHL)
6454                */
6455               vt_line_set_size_attr(line, DOUBLE_HEIGHT_TOP);
6456             } else if (*str_p == '4') {
6457               /*
6458                * "ESC # 4" DEC double-height line,
6459                * bottom half (DECDHL)
6460                */
6461               vt_line_set_size_attr(line, DOUBLE_HEIGHT_BOTTOM);
6462             } else if (*str_p == '5') {
6463               /*
6464                * "ESC # 5" DEC single-with line (DECSWL)
6465                */
6466               vt_line_set_size_attr(line, 0);
6467             } else /* if( *str_p == '6') */
6468             {
6469               /*
6470                * "ESC # 6" DEC double-with line (DECDWL)
6471                */
6472               vt_line_set_size_attr(line, DOUBLE_WIDTH);
6473             }
6474           } else if (*str_p == '8') {
6475             /* "ESC # 8" DEC screen alignment test (DECALN) */
6476 
6477             vt_screen_set_vmargin(vt_parser->screen, -1, -1);
6478             vt_screen_set_use_hmargin(vt_parser->screen, 0);
6479             vt_screen_goto(vt_parser->screen, 0, 0);
6480 
6481             vt_screen_fill_area(vt_parser->screen, 'E', vt_parser->is_protected, 0, 0,
6482                                 vt_screen_get_logical_cols(vt_parser->screen),
6483                                 vt_screen_get_logical_rows(vt_parser->screen));
6484           }
6485         } else if (*(str_p - ic_num) == '(' || *(str_p - ic_num) == '$') {
6486           /*
6487            * "ESC ("(Registered CS),
6488            * "ESC ( SP"(DRCS) or "ESC $"
6489            * See vt_convert_to_internal_ch() about CS94MB_ID.
6490            */
6491 
6492           if (IS_ENCODING_BASED_ON_ISO2022(vt_parser->encoding)) {
6493             /* ESC ( will be processed in mef. */
6494             return 1;
6495           }
6496 
6497           vt_parser->g0 = (*(str_p - ic_num) == '$') ? CS94MB_ID(*str_p) : CS94SB_ID(*str_p);
6498 
6499           if (!vt_parser->is_so) {
6500             vt_parser->gl = vt_parser->g0;
6501           }
6502         } else if (*(str_p - ic_num) == ')') {
6503           /* "ESC )"(Registered CS) or "ESC ( SP"(DRCS) */
6504 
6505           if (IS_ENCODING_BASED_ON_ISO2022(vt_parser->encoding)) {
6506             /* ESC ) will be processed in mef. */
6507             return 1;
6508           }
6509 
6510           vt_parser->g1 = (*(str_p - ic_num) == '$') ? CS94MB_ID(*str_p) : CS94SB_ID(*str_p);
6511 
6512           if (vt_parser->is_so) {
6513             vt_parser->gl = vt_parser->g1;
6514           }
6515         } else if (*(str_p - ic_num) == '-') {
6516           /* "ESC -"(Registered CS) or "ESC - SP"(DRCS) */
6517 
6518           if (IS_ENCODING_BASED_ON_ISO2022(vt_parser->encoding)) {
6519             /* ESC ) will be processed in mef. */
6520             return 1;
6521           }
6522 
6523           vt_parser->g1 = CS96SB_ID(*str_p);
6524 
6525           if (vt_parser->is_so) {
6526             vt_parser->gl = vt_parser->g1;
6527           }
6528         } else {
6529           /*
6530            * "ESC SP F", "ESC SP G", "ESC SP L", "ESC SP M",
6531            * "ESC SP N" etc ...
6532            */
6533         }
6534       }
6535     } else {
6536 /* not VT100 control sequence. */
6537 
6538 #ifdef ESCSEQ_DEBUG
6539       bl_msg_printf("=> not VT100 control sequence.\n");
6540 #endif
6541 
6542       return 1;
6543     }
6544 
6545 #ifdef ESCSEQ_DEBUG
6546     bl_msg_printf("\n");
6547 #endif
6548   } else if (*str_p == CTL_SI) {
6549     if (IS_ENCODING_BASED_ON_ISO2022(vt_parser->encoding)) {
6550       /* SI will be processed in mef. */
6551       return 1;
6552     }
6553 
6554 #ifdef ESCSEQ_DEBUG
6555     bl_debug_printf(BL_DEBUG_TAG " receiving SI\n");
6556 #endif
6557 
6558     vt_parser->gl = vt_parser->g0;
6559     vt_parser->is_so = 0;
6560   } else if (*str_p == CTL_SO) {
6561     if (IS_ENCODING_BASED_ON_ISO2022(vt_parser->encoding)) {
6562       /* SO will be processed in mef. */
6563       return 1;
6564     }
6565 
6566 #ifdef ESCSEQ_DEBUG
6567     bl_debug_printf(BL_DEBUG_TAG " receiving SO\n");
6568 #endif
6569 
6570     vt_parser->gl = vt_parser->g1;
6571     vt_parser->is_so = 1;
6572   } else if (CTL_LF <= *str_p && *str_p <= CTL_FF) {
6573 #ifdef ESCSEQ_DEBUG
6574     bl_debug_printf(BL_DEBUG_TAG " receiving LF\n");
6575 #endif
6576 
6577     vt_screen_line_feed(vt_parser->screen);
6578     if (AUTO_CR(vt_parser)) {
6579       vt_screen_goto_beg_of_line(vt_parser->screen);
6580     }
6581   } else if (*str_p == CTL_CR) {
6582 #ifdef ESCSEQ_DEBUG
6583     bl_debug_printf(BL_DEBUG_TAG " receiving CR\n");
6584 #endif
6585 
6586     vt_screen_goto_beg_of_line(vt_parser->screen);
6587   } else if (*str_p == CTL_TAB) {
6588 #ifdef ESCSEQ_DEBUG
6589     bl_debug_printf(BL_DEBUG_TAG " receiving TAB\n");
6590 #endif
6591 
6592     vt_screen_forward_tabs(vt_parser->screen, 1);
6593   } else if (*str_p == CTL_BS) {
6594 #ifdef ESCSEQ_DEBUG
6595     bl_debug_printf(BL_DEBUG_TAG " receiving BS\n");
6596 #endif
6597 
6598     vt_screen_go_back(vt_parser->screen, 1, 0);
6599   } else if (*str_p == CTL_BEL) {
6600 #ifdef ESCSEQ_DEBUG
6601     bl_debug_printf(BL_DEBUG_TAG " receiving BEL\n");
6602 #endif
6603 
6604     if (HAS_XTERM_LISTENER(vt_parser, bel)) {
6605       stop_vt100_cmd(vt_parser, 0);
6606       (*vt_parser->xterm_listener->bel)(vt_parser->xterm_listener->self);
6607       /*
6608        * XXX
6609        * start_vt100_cmd( ... , *1*) erases cursor which
6610        * xterm_listener::bell drew if bell mode is visual.
6611        */
6612       start_vt100_cmd(vt_parser, 1);
6613     }
6614   } else if (*str_p == 0x90) {
6615     goto parse_dcs;
6616   } else if (*str_p == 0x18) {
6617     if (vt_parser->r_buf.left < 3) {
6618       return 0;
6619     }
6620 
6621     if (memcmp(str_p + 1, "B01", 3) == 0) {
6622       vt_parser->is_zmodem_ready = 1; /* rz */
6623     }
6624 #if 0
6625     else if (memcmp(str_p + 1, "B00", 3) == 0) {
6626       vt_parser->is_zmodem_ready = 2; /* sz */
6627     }
6628 #endif
6629   } else {
6630     /* not VT100 control sequence */
6631 
6632     return 1;
6633   }
6634 
6635 #ifdef EDIT_DEBUG
6636   vt_edit_dump(vt_parser->screen->edit);
6637 #endif
6638 
6639   vt_parser->r_buf.left = left - 1;
6640 
6641   return 1;
6642 }
6643 
parse_vt100_sequence(vt_parser_t * vt_parser)6644 static int parse_vt100_sequence(vt_parser_t *vt_parser) {
6645   ef_char_t ch;
6646   size_t prev_left;
6647 
6648   while (1) {
6649     prev_left = vt_parser->r_buf.left;
6650 
6651     /*
6652      * parsing character encoding.
6653      */
6654     (*vt_parser->cc_parser->set_str)(vt_parser->cc_parser, CURRENT_STR_P(vt_parser),
6655                                         vt_parser->r_buf.left);
6656     while ((*vt_parser->cc_parser->next_char)(vt_parser->cc_parser, &ch)) {
6657       int ret;
6658       ef_charset_t orig_cs;
6659 
6660       orig_cs = ch.cs;
6661       ret = vt_convert_to_internal_ch(vt_parser, &ch);
6662 
6663       if (ret == 1) {
6664         if (vt_parser->gl != US_ASCII && orig_cs == US_ASCII) {
6665           orig_cs = vt_parser->gl;
6666         }
6667 
6668         if (vt_parser->cs != orig_cs) {
6669           vt_parser->cs = orig_cs;
6670         }
6671 
6672 #if !defined(NO_DYNAMIC_LOAD_CTL) || defined(USE_IND)
6673         if (IS_ISCII(ch.cs) && ch.size == 2) {
6674           ch.size = 1;
6675           put_char(vt_parser, ef_char_to_int(&ch), ch.cs, ch.property);
6676           ch.ch[0] = ch.ch[1];
6677           /* nukta is always combined. */
6678           ch.property |= EF_COMBINING;
6679         }
6680 #endif
6681 
6682         put_char(vt_parser, ef_char_to_int(&ch), ch.cs, ch.property);
6683 
6684         vt_parser->r_buf.left = vt_parser->cc_parser->left;
6685       } else if (ret == -1) {
6686         /*
6687          * This is a control sequence (C0 or C1), so
6688          * reparsing this char in vt100_escape_sequence() ...
6689          */
6690 
6691         vt_parser->cc_parser->left++;
6692         vt_parser->cc_parser->is_eos = 0;
6693 
6694         break;
6695       }
6696     }
6697 
6698     vt_parser->r_buf.left = vt_parser->cc_parser->left;
6699 
6700     flush_buffer(vt_parser);
6701 
6702     if (vt_parser->cc_parser->is_eos) {
6703       /* expect more input */
6704       break;
6705     }
6706 
6707     /*
6708      * parsing other vt100 sequences.
6709      * (vt_parser->w_buf is always flushed here.)
6710      */
6711 
6712     if (!parse_vt100_escape_sequence(vt_parser)) {
6713       /* expect more input */
6714       break;
6715     }
6716 
6717 #ifdef EDIT_ROUGH_DEBUG
6718     vt_edit_dump(vt_parser->screen->edit);
6719 #endif
6720 
6721     if (vt_parser->r_buf.left == prev_left) {
6722 #ifdef DEBUG
6723       bl_debug_printf(BL_DEBUG_TAG " unrecognized sequence[%.2x] is received , ignored...\n",
6724                       *CURRENT_STR_P(vt_parser));
6725 #endif
6726 
6727       vt_parser->r_buf.left--;
6728     }
6729 
6730     if (vt_parser->r_buf.left == 0) {
6731       break;
6732     }
6733   }
6734 
6735 /*
6736  * If only one window is shown on screen, it is not necessary to process
6737  * pending events for other windows.
6738  * But multiple windows can be shown even on framebuffer now, so it is
6739  * commented out.
6740  */
6741 #if 0
6742   if (vt_parser->yield) {
6743     vt_parser->yield = 0;
6744 
6745     return 0;
6746   }
6747 #endif
6748 
6749   return 1;
6750 }
6751 
write_loopback(vt_parser_t * vt_parser,const u_char * buf,size_t len,int enable_local_echo,int is_visual)6752 static int write_loopback(vt_parser_t *vt_parser, const u_char *buf, size_t len,
6753                           int enable_local_echo,
6754                           int is_visual /* 1: stop_vt100_cmd(1), -1: stop_vt100_cmd(0) */
6755                           ) {
6756   char *orig_buf;
6757   size_t orig_left;
6758 
6759   if (!vt_parser->pty || /* vt_term_write_loopback() in open_pty_intern() in ui_screen_manager.c can
6760                           * be called when vt_parser->pty is NULL */
6761       vt_pty_get_master_fd(vt_parser->pty) != -1) {
6762     if (vt_parser->r_buf.len < len && !change_read_buffer_size(&vt_parser->r_buf, len)) {
6763       return 0;
6764     }
6765 
6766     if ((orig_left = vt_parser->r_buf.left) > 0) {
6767       if (!(orig_buf = alloca(orig_left))) {
6768         return 0;
6769       }
6770 
6771       memcpy(orig_buf, CURRENT_STR_P(vt_parser), orig_left);
6772     }
6773 
6774     memcpy(vt_parser->r_buf.chars, buf, len);
6775     vt_parser->r_buf.filled_len = vt_parser->r_buf.left = vt_parser->r_buf.new_len = len;
6776   } else {
6777     /* for vterm compatible library */
6778 
6779     if (vt_parser->r_buf.len < len + vt_parser->r_buf.left &&
6780         !change_read_buffer_size(&vt_parser->r_buf, len + vt_parser->r_buf.left)) {
6781       return 0;
6782     }
6783 
6784     memmove(vt_parser->r_buf.chars,
6785             vt_parser->r_buf.chars + vt_parser->r_buf.filled_len - vt_parser->r_buf.left,
6786             vt_parser->r_buf.left);
6787     memcpy(vt_parser->r_buf.chars + vt_parser->r_buf.left, buf, len);
6788     vt_parser->r_buf.filled_len = (vt_parser->r_buf.left += len);
6789     vt_parser->r_buf.new_len = len;
6790     orig_left = 0;
6791   }
6792 
6793   if (is_visual) {
6794     start_vt100_cmd(vt_parser, 1);
6795   }
6796 
6797   if (enable_local_echo) {
6798     vt_screen_enable_local_echo(vt_parser->screen);
6799   }
6800 
6801   /*
6802    * bidi and visual-indian is always stopped from here.
6803    * If you want to call {start|stop}_vt100_cmd (where vt_xterm_event_listener
6804    * is called),
6805    * the second argument of it shoule be 0.
6806    */
6807   parse_vt100_sequence(vt_parser);
6808 
6809   if (is_visual) {
6810     stop_vt100_cmd(vt_parser, is_visual > 0);
6811   }
6812 
6813   if (orig_left > 0) {
6814     memcpy(vt_parser->r_buf.chars, orig_buf, orig_left);
6815     vt_parser->r_buf.filled_len = vt_parser->r_buf.left = orig_left;
6816   }
6817 
6818   return 1;
6819 }
6820 
local_echo(vt_parser_t * vt_parser,const u_char * buf,size_t len)6821 static void local_echo(vt_parser_t *vt_parser, const u_char *buf, size_t len) {
6822   size_t count;
6823 
6824   if (len == 1) {
6825     if (vt_parser->prev_local_echo_char == buf[0]) {
6826       vt_screen_local_echo_wait(vt_parser->screen, 0);
6827       vt_parse_vt100_sequence(vt_parser);
6828 
6829       return;
6830     } else {
6831       vt_parser->prev_local_echo_char = buf[0];
6832     }
6833   } else {
6834     vt_parser->prev_local_echo_char = 0;
6835   }
6836 
6837   for (count = 0; count < len; count++) {
6838     if (buf[count] < 0x20) {
6839       vt_screen_local_echo_wait(vt_parser->screen, 0);
6840       vt_parse_vt100_sequence(vt_parser);
6841 
6842       return;
6843     }
6844   }
6845 
6846   vt_parse_vt100_sequence(vt_parser);
6847 
6848   if (!(vt_parser->line_style & LS_UNDERLINE)) {
6849     char *new_buf;
6850     size_t new_len;
6851 
6852     if ((new_buf = alloca((new_len = 4 + len + 5)))) {
6853       memcpy(new_buf, "\x1b[4m", 4);
6854       memcpy(new_buf + 4, buf, len);
6855       memcpy(new_buf + 4 + len, "\x1b[24m", 5);
6856       buf = new_buf;
6857       len = new_len;
6858     }
6859   }
6860 
6861   write_loopback(vt_parser, buf, len, 1, 1);
6862 }
6863 
is_transferring_data(vt_parser_t * vt_parser)6864 static int is_transferring_data(vt_parser_t *vt_parser) {
6865   int progress_cur;
6866   int progress_len;
6867   int state;
6868   vt_char_t *bar;
6869 
6870   if ((state = vt_transfer_get_state(&progress_cur, &progress_len)) >= 0 &&
6871       (bar = alloca(sizeof(vt_char_t) * (progress_len + 2)))) {
6872     int count;
6873 
6874     start_vt100_cmd(vt_parser, 1);
6875 
6876     if (progress_cur == 0) {
6877       char *msg;
6878       char *p1;
6879       char *p2;
6880       size_t len;
6881       size_t pre_len;
6882 
6883       if (vt_parser->is_transferring_data == 0x1) {
6884         pre_len = 5;
6885         p1 = "Send ";
6886         p2 = send_file ? send_file : "";
6887       } else {
6888         pre_len = 8;
6889         p1 = "Save in ";
6890         p2 = recv_dir ? recv_dir : "~/.mlterm/recv";
6891       }
6892 
6893       len = 10 + pre_len + strlen(p2);
6894       if ((msg = alloca(len + 1))) {
6895         memcpy(msg, "\r\nZMODEM: ", 10);
6896         memcpy(msg + 10, p1, pre_len);
6897         strcpy(msg + 10 + pre_len, p2);
6898       }
6899       write_loopback(vt_parser, msg, len, 0, 0);
6900       vt_screen_line_feed(vt_parser->screen);
6901     }
6902 
6903     vt_screen_goto_beg_of_line(vt_parser->screen);
6904     vt_screen_clear_line_to_right(vt_parser->screen);
6905 
6906     vt_str_init(bar, progress_len + 2);
6907     vt_char_set(bar, '|', US_ASCII, 0, 0, 0, VT_FG_COLOR, VT_BG_COLOR, 0, 0, 0, 0, 0);
6908 
6909     for (count = 0; count < progress_cur; count++) {
6910       vt_char_set(bar + 1 + count, '*', US_ASCII, 0, 0, 0, VT_FG_COLOR, VT_BG_COLOR, 0, 0, 0, 0, 0);
6911     }
6912     for (; count < progress_len; count++) {
6913       vt_char_set(bar + 1 + count, ' ', US_ASCII, 0, 0, 0, VT_FG_COLOR, VT_BG_COLOR, 0, 0, 0, 0, 0);
6914     }
6915     vt_char_set(bar + 1 + progress_len, '|', US_ASCII, 0, 0, 0, VT_FG_COLOR, VT_BG_COLOR,
6916                 0, 0, 0, 0, 0);
6917 
6918     vt_screen_overwrite_chars(vt_parser->screen, bar, progress_len + 2);
6919 
6920     if (state == 0) {
6921       vt_screen_line_feed(vt_parser->screen);
6922       vt_screen_goto_beg_of_line(vt_parser->screen);
6923 
6924       stop_vt100_cmd(vt_parser, 1);
6925 
6926       vt_parser->is_transferring_data = 0;
6927 
6928       return 0;
6929     }
6930 
6931     stop_vt100_cmd(vt_parser, 1);
6932   }
6933 
6934   return 1;
6935 }
6936 
6937 /* sending or receiving data via zmodem */
transfer_data(vt_parser_t * vt_parser)6938 static void transfer_data(vt_parser_t *vt_parser) {
6939   receive_bytes(vt_parser);
6940 
6941   do {
6942     u_char input[4096];
6943     u_char output[(2 * (1024 + 4 + 1)) + 1];
6944     u_int len = 0;
6945     size_t copy_len;
6946 
6947     if ((copy_len = vt_parser->r_buf.left) > sizeof(input) - 1) {
6948       copy_len = sizeof(input) - 1;
6949     }
6950 
6951     memcpy(input, CURRENT_STR_P(vt_parser), copy_len);
6952     input[copy_len] = '\0';
6953 
6954     if ((vt_parser->r_buf.left -= copy_len) > 0) {
6955       memmove(vt_parser->r_buf.chars, CURRENT_STR_P(vt_parser), vt_parser->r_buf.left);
6956       vt_parser->r_buf.filled_len = vt_parser->r_buf.left;
6957     }
6958 
6959     vt_transfer_data(input, copy_len, output, &len, sizeof(output));
6960 
6961     if (len > 0) {
6962       vt_write_to_pty(vt_parser->pty, output, len);
6963     }
6964   } while (is_transferring_data(vt_parser) && receive_bytes(vt_parser) > 0);
6965 }
6966 
6967 /* This function assumes vt_pty_is_loopback(vt_parser->pty) is false if do_zcan is true. */
transfer_cancel(vt_parser_t * vt_parser,int do_zcan)6968 static void transfer_cancel(vt_parser_t *vt_parser, int do_zcan) {
6969   if (vt_parser->is_transferring_data) {
6970     vt_parser->is_transferring_data = 0;
6971     vt_parser->r_buf.left = 0;
6972     vt_transfer_cancel();
6973   }
6974 #if 0
6975   else if (vt_parser->is_zmodem_ready == 0) {
6976     return;
6977   }
6978 #endif
6979 
6980   vt_parser->is_zmodem_ready = 0;
6981 
6982   if (do_zcan) {
6983     vt_write_to_pty(vt_parser->pty,
6984                         "**\x18\x18\x18\x18\x18\x18\x18\x18"
6985                         "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08", 20);
6986   }
6987 }
6988 
convert_to_locale_encoding(char * str,ef_parser_t * parser,vt_char_encoding_t encoding)6989 static char *convert_to_locale_encoding(char *str, ef_parser_t *parser,
6990                                         vt_char_encoding_t encoding) {
6991   vt_char_encoding_t locale_encoding;
6992   char *new_str;
6993   size_t len = strlen(str);
6994 
6995   if ((locale_encoding = vt_get_char_encoding(bl_get_codeset())) == VT_UNKNOWN_ENCODING) {
6996     locale_encoding = encoding;
6997   }
6998 
6999   if (locale_encoding == encoding || (new_str = alloca(len * 2 + 1)) == NULL) {
7000     new_str = str;
7001   } else {
7002     if (parser) {
7003       (*parser->init)(parser);
7004       (*parser->set_str)(parser, str, len);
7005       new_str[vt_char_encoding_convert_with_parser(new_str, len * 2, locale_encoding,
7006                                                    parser)] = '\0';
7007     } else {
7008       new_str[vt_char_encoding_convert(new_str, len * 2, locale_encoding,
7009                                        str, len - 5, encoding)] = '\0';
7010     }
7011   }
7012 
7013   return strdup(new_str);
7014 }
7015 
7016 /* --- global functions --- */
7017 
vt_set_use_alt_buffer(int use)7018 void vt_set_use_alt_buffer(int use) { use_alt_buffer = use; }
7019 
vt_set_unicode_noconv_areas(char * areas)7020 void vt_set_unicode_noconv_areas(char *areas) {
7021   unicode_noconv_areas =
7022       set_area_to_table(unicode_noconv_areas, &num_unicode_noconv_areas, areas);
7023 }
7024 
vt_set_full_width_areas(char * areas)7025 void vt_set_full_width_areas(char *areas) {
7026   full_width_areas = set_area_to_table(full_width_areas, &num_full_width_areas, areas);
7027 }
7028 
vt_set_half_width_areas(char * areas)7029 void vt_set_half_width_areas(char *areas) {
7030   half_width_areas = set_area_to_table(half_width_areas, &num_half_width_areas, areas);
7031 }
7032 
vt_set_use_ttyrec_format(int use)7033 void vt_set_use_ttyrec_format(int use) { use_ttyrec_format = use; }
7034 
7035 #ifdef USE_LIBSSH2
vt_set_use_scp_full(int use)7036 void vt_set_use_scp_full(int use) {
7037   if (use >= 0) {
7038     use_scp_full = use;
7039   } else if (use == -1) {
7040     if (use_scp_full == 0) {
7041       use_scp_full = -1;
7042     } else if (use_scp_full == -1) {
7043       use_scp_full = 0;
7044     }
7045   }
7046 }
7047 #endif
7048 
vt_set_recv_dir(const char * dir)7049 void vt_set_recv_dir(const char *dir) {
7050   if (strstr(dir, "..")) {
7051     /* insecure dir name */
7052     bl_msg_printf("%s is insecure dir name.\n", dir);
7053   } else {
7054     free(recv_dir);
7055     recv_dir = strdup(dir);
7056   }
7057 }
7058 
vt_set_timeout_read_pty(u_long timeout)7059 void vt_set_timeout_read_pty(u_long timeout) { timeout_read_pty = timeout; }
7060 
vt_set_primary_da(char * da)7061 void vt_set_primary_da(char *da) {
7062   free(primary_da);
7063   primary_da = strdup(da);
7064 }
7065 
vt_set_secondary_da(char * da)7066 void vt_set_secondary_da(char *da) {
7067   free(secondary_da);
7068   secondary_da = strdup(da);
7069 }
7070 
vt_set_local_echo_wait(u_int msec)7071 void vt_set_local_echo_wait(u_int msec) {
7072   local_echo_wait_msec = msec;
7073 }
7074 
vt_parser_final(void)7075 void vt_parser_final(void) {
7076   vt_config_proto_final();
7077 
7078   free(unicode_noconv_areas);
7079   num_unicode_noconv_areas = 0;
7080 
7081   free(full_width_areas);
7082   num_full_width_areas = 0;
7083 
7084   free(half_width_areas);
7085   num_half_width_areas = 0;
7086 
7087   vt_set_auto_detect_encodings("");
7088 }
7089 
vt_parser_new(vt_screen_t * screen,vt_termcap_ptr_t termcap,vt_char_encoding_t encoding,int is_auto_encoding,int use_auto_detect,int logging_vt_seq,vt_unicode_policy_t policy,u_int col_size_a,int use_char_combining,int use_multi_col_char,const char * win_name,const char * icon_name,int use_ansi_colors,vt_alt_color_mode_t alt_color_mode,vt_cursor_style_t cursor_style,int ignore_broadcasted_chars,int use_local_echo)7090 vt_parser_t *vt_parser_new(vt_screen_t *screen, vt_termcap_ptr_t termcap, vt_char_encoding_t encoding,
7091                            int is_auto_encoding, int use_auto_detect, int logging_vt_seq,
7092                            vt_unicode_policy_t policy, u_int col_size_a, int use_char_combining,
7093                            int use_multi_col_char, const char *win_name, const char *icon_name,
7094                            int use_ansi_colors, vt_alt_color_mode_t alt_color_mode,
7095                            vt_cursor_style_t cursor_style, int ignore_broadcasted_chars, int use_local_echo) {
7096   vt_parser_t *vt_parser;
7097 
7098   if ((vt_parser = calloc(1, sizeof(vt_parser_t))) == NULL) {
7099     return NULL;
7100   }
7101 
7102   vt_str_init(vt_parser->w_buf.chars, PTY_WR_BUFFER_SIZE);
7103   vt_parser->w_buf.output_func = vt_screen_overwrite_chars;
7104 
7105   vt_parser->screen = screen;
7106   vt_parser->termcap = termcap;
7107 
7108   vt_parser->log_file = -1;
7109 
7110   vt_parser->cs = UNKNOWN_CS;
7111   vt_parser->fg_color = VT_FG_COLOR;
7112   vt_parser->bg_color = VT_BG_COLOR;
7113   vt_parser->use_char_combining = use_char_combining;
7114   vt_parser->use_multi_col_char = use_multi_col_char;
7115   vt_parser->is_auto_encoding = is_auto_encoding;
7116   vt_parser->use_auto_detect = use_auto_detect;
7117   vt_parser->logging_vt_seq = logging_vt_seq;
7118   vt_parser->unicode_policy = policy;
7119   vt_parser->cursor_style = cursor_style;
7120   vt_parser->is_visible_cursor = 1;
7121   vt_parser->hide_pointer_mode = 2; /* Compatible with xterm 344 */
7122 
7123   if ((vt_parser->cc_conv = vt_char_encoding_conv_new(encoding)) == NULL) {
7124     goto error;
7125   }
7126 
7127   if ((vt_parser->cc_parser = vt_char_encoding_parser_new(encoding)) == NULL) {
7128     goto error;
7129   }
7130 
7131   vt_parser->encoding = encoding;
7132 
7133   if (win_name) {
7134     vt_parser->win_name = strdup(win_name);
7135   }
7136 
7137   if (icon_name) {
7138     vt_parser->icon_name = strdup(icon_name);
7139   }
7140 
7141   vt_parser->gl = US_ASCII;
7142   vt_parser->g0 = US_ASCII;
7143   vt_parser->g1 = US_ASCII;
7144 
7145   set_col_size_of_width_a(vt_parser, col_size_a);
7146 
7147   /* Default value of modify_*_keys except modify_other_keys is 2. */
7148   vt_parser->modify_cursor_keys = 2;
7149   vt_parser->modify_function_keys = 2;
7150 
7151   vt_parser->sixel_scrolling = 1;
7152   vt_parser->use_ansi_colors = use_ansi_colors;
7153   vt_parser->alt_color_mode = alt_color_mode;
7154   vt_parser->saved_vtmode_flags[0] = vt_parser->vtmode_flags[0] = INITIAL_VTMODE_FLAGS_0;
7155   vt_parser->saved_vtmode_flags[1] = vt_parser->vtmode_flags[1] = INITIAL_VTMODE_FLAGS_1;
7156   vt_parser->ignore_broadcasted_chars = ignore_broadcasted_chars;
7157   vt_parser->use_local_echo = use_local_echo;
7158 
7159   return vt_parser;
7160 
7161 error:
7162   if (vt_parser->cc_conv) {
7163     (*vt_parser->cc_conv->destroy)(vt_parser->cc_conv);
7164   }
7165 
7166   if (vt_parser->cc_parser) {
7167     (*vt_parser->cc_parser->destroy)(vt_parser->cc_parser);
7168   }
7169 
7170   free(vt_parser);
7171 
7172   return NULL;
7173 }
7174 
vt_parser_destroy(vt_parser_t * vt_parser)7175 int vt_parser_destroy(vt_parser_t *vt_parser) {
7176   vt_str_final(vt_parser->w_buf.chars, PTY_WR_BUFFER_SIZE);
7177   (*vt_parser->cc_parser->destroy)(vt_parser->cc_parser);
7178   (*vt_parser->cc_conv->destroy)(vt_parser->cc_conv);
7179   destroy_drcs(vt_parser->drcs);
7180   destroy_all_macros(vt_parser);
7181   free(vt_parser->sixel_palette);
7182 
7183   if (vt_parser->log_file != -1) {
7184     close(vt_parser->log_file);
7185   }
7186 
7187   free(vt_parser->r_buf.chars);
7188 
7189   free(vt_parser->win_name);
7190   free(vt_parser->icon_name);
7191   free(vt_parser->saved_win_names.names);
7192   free(vt_parser->saved_icon_names.names);
7193 
7194   free(vt_parser);
7195 
7196   return 1;
7197 }
7198 
vt_parser_set_pty(vt_parser_t * vt_parser,vt_pty_t * pty)7199 void vt_parser_set_pty(vt_parser_t *vt_parser, vt_pty_t *pty) {
7200 #ifdef USE_LIBSSH2
7201   /* See set_col_size_of_width_a() */
7202   if (!vt_parser->pty && vt_pty_get_mode(pty) == PTY_MOSH) {
7203 #ifdef USE_WIN32API
7204     char *env = getenv("MOSH_AWIDTH");
7205 
7206     if (!env) {
7207       putenv(vt_parser->col_size_of_width_a == 2 ? "MOSH_AWIDTH=2" : "MOSH_AWIDTH=1");
7208     } else {
7209       vt_parser->col_size_of_width_a = (*env == '2' ? 2 : 1);
7210     }
7211 #else
7212     vt_parser->col_size_of_width_a = (wcwidth(0x25a0) == 2) ? 2 : 1;
7213 #endif
7214   }
7215 #endif
7216 
7217   vt_parser->pty = pty;
7218 }
7219 
vt_parser_set_xterm_listener(vt_parser_t * vt_parser,vt_xterm_event_listener_t * xterm_listener)7220 void vt_parser_set_xterm_listener(vt_parser_t *vt_parser,
7221                                   vt_xterm_event_listener_t *xterm_listener) {
7222   vt_parser->xterm_listener = xterm_listener;
7223 }
7224 
vt_parser_set_config_listener(vt_parser_t * vt_parser,vt_config_event_listener_t * config_listener)7225 void vt_parser_set_config_listener(vt_parser_t *vt_parser,
7226                                    vt_config_event_listener_t *config_listener) {
7227   vt_parser->config_listener = config_listener;
7228 }
7229 
vt_parse_vt100_sequence(vt_parser_t * vt_parser)7230 int vt_parse_vt100_sequence(vt_parser_t *vt_parser) {
7231   clock_t beg;
7232 
7233   if (vt_parser->is_transferring_data) {
7234     /* vt_parser->pty is always non-NULL value. */
7235     transfer_data(vt_parser);
7236 
7237     return 1;
7238   }
7239 
7240   if (vt_screen_local_echo_wait(vt_parser->screen, local_echo_wait_msec)) {
7241     return 1;
7242   }
7243 
7244   if (!vt_parser->pty || receive_bytes(vt_parser) == 0) {
7245     return 0;
7246   }
7247 
7248   beg = clock();
7249 
7250   start_vt100_cmd(vt_parser, 1);
7251 
7252   vt_screen_disable_local_echo(vt_parser->screen);
7253 
7254   /*
7255    * bidi and visual-indian is always stopped from here.
7256    * If you want to call {start|stop}_vt100_cmd (where vt_xterm_event_listener
7257    * is called),
7258    * the second argument of it shoule be 0.
7259    */
7260 
7261   while (parse_vt100_sequence(vt_parser) &&
7262          !vt_parser->is_transferring_data &&
7263          /* (PTY_RD_BUFFER_SIZE / 2) is baseless. */
7264          vt_parser->r_buf.filled_len >= (PTY_RD_BUFFER_SIZE / 2) &&
7265          clock() - beg < timeout_read_pty && receive_bytes(vt_parser))
7266     ;
7267 
7268   stop_vt100_cmd(vt_parser, 1);
7269 
7270   return 1;
7271 }
7272 
vt_parser_write(vt_parser_t * vt_parser,u_char * buf,size_t len)7273 size_t vt_parser_write(vt_parser_t *vt_parser, u_char *buf, size_t len) {
7274   if (vt_parser->is_transferring_data) {
7275     return 0;
7276   }
7277 
7278   if (vt_parser->use_local_echo) {
7279     local_echo(vt_parser, buf, len);
7280   }
7281 
7282   if (!SEND_RECV_MODE(vt_parser)) {
7283     write_loopback(vt_parser, buf, len, 0, 1);
7284   }
7285 
7286   return vt_write_to_pty(vt_parser->pty, buf, len);
7287 }
7288 
vt_parser_write_modified_key(vt_parser_t * vt_parser,int key,int modcode)7289 int vt_parser_write_modified_key(vt_parser_t *vt_parser,
7290                                  int key, /* 0 < key < 0x80 */
7291                                  int modcode) {
7292   if (vt_parser->is_transferring_data) {
7293     return 0;
7294   }
7295 
7296   if (vt_parser->modify_other_keys == 1) {
7297     if ((modcode == 5 /* Control */ || modcode == 6 /* Shift+Control */) &&
7298         (('@' <= key && key <= 0x7e) || ('2' <= key && key <= '8') || key == '/' || key == ' ')) {
7299       return 0;
7300     }
7301   } else if (vt_parser->modify_other_keys != 2) {
7302     return 0;
7303   }
7304 
7305   if (!((modcode - 1) == 1 /* is shift */ &&
7306         (('!' <= key && key < 'A') || ('Z' < key && key < 'a') || ('z' < key && key <= '~')))) {
7307     size_t len;
7308 
7309 #if 1
7310     char buf[10];
7311 
7312     /* formatOtherKeys = 1 */
7313     sprintf(buf, "\x1b[%d;%du", key, modcode);
7314 #else
7315     char buf[12];
7316 
7317     /* formatOtherKeys = 0 */
7318     sprintf(buf, "\x1b[27;%d;%d", modcode, key);
7319 #endif
7320 
7321     len = strlen(buf);
7322 
7323     if (!SEND_RECV_MODE(vt_parser)) {
7324       write_loopback(vt_parser, buf, len, 0, 1);
7325     }
7326 
7327     vt_write_to_pty(vt_parser->pty, buf, len);
7328 
7329     return 1;
7330   }
7331 
7332   return 0;
7333 }
7334 
vt_parser_write_special_key(vt_parser_t * vt_parser,vt_special_key_t key,int modcode,int is_numlock)7335 int vt_parser_write_special_key(vt_parser_t *vt_parser, vt_special_key_t key,
7336                                 int modcode, int is_numlock) {
7337   char *buf;
7338 
7339   if (vt_parser->is_transferring_data) {
7340     return 0;
7341   }
7342 
7343   if ((buf = vt_termcap_special_key_to_seq(vt_parser->termcap, key, modcode,
7344                                            (vt_parser->is_app_keypad && !is_numlock),
7345                                            IS_APP_CURSOR_KEYS(vt_parser), IS_APP_ESCAPE(vt_parser),
7346                                            vt_parser->modify_cursor_keys, vt_parser->modify_function_keys))) {
7347     size_t len = strlen(buf);
7348 
7349     if (!SEND_RECV_MODE(vt_parser)) {
7350       write_loopback(vt_parser, buf, len, 0, 1);
7351     }
7352 
7353     vt_write_to_pty(vt_parser->pty, buf, len);
7354 
7355     return 1;
7356   } else {
7357     return 0;
7358   }
7359 }
7360 
vt_parser_write_loopback(vt_parser_t * vt_parser,const u_char * buf,size_t len)7361 int vt_parser_write_loopback(vt_parser_t *vt_parser, const u_char *buf, size_t len) {
7362   return write_loopback(vt_parser, buf, len, 0, 1);
7363 }
7364 
vt_parser_show_message(vt_parser_t * vt_parser,char * msg)7365 int vt_parser_show_message(vt_parser_t *vt_parser, char *msg) {
7366   char *buf;
7367   size_t len;
7368 
7369   if (!(buf = alloca((len = 3 + strlen(msg) + 4)))) {
7370     return 0;
7371   }
7372 
7373   if (vt_screen_is_local_echo_mode(vt_parser->screen)) {
7374     sprintf(buf, "\r\n%s\x1b[K", msg);
7375 
7376     return write_loopback(vt_parser, buf, len - 2, 0, -1);
7377   } else {
7378     sprintf(buf, "\x1b[H%s\x1b[K", msg);
7379 
7380     return write_loopback(vt_parser, buf, len - 1, 1, -1);
7381   }
7382 }
7383 
7384 #if 1 /* defined(__ANDROID__) || defined(__APPLE__) || defined(USE_SDL2) */
vt_parser_preedit(vt_parser_t * vt_parser,const u_char * buf,size_t len)7385 int vt_parser_preedit(vt_parser_t *vt_parser, const u_char *buf, size_t len) {
7386   if (!(vt_parser->line_style & LS_UNDERLINE)) {
7387     char *new_buf;
7388     size_t new_len;
7389 
7390     if ((new_buf = alloca((new_len = 4 + len + 5)))) {
7391       memcpy(new_buf, "\x1b[4m", 4);
7392       memcpy(new_buf + 4, buf, len);
7393       memcpy(new_buf + 4 + len, "\x1b[24m", 5);
7394       buf = new_buf;
7395       len = new_len;
7396     }
7397   }
7398 
7399   return write_loopback(vt_parser, buf, len, 1, 1);
7400 }
7401 #endif
7402 
vt_parser_change_encoding(vt_parser_t * vt_parser,vt_char_encoding_t encoding)7403 int vt_parser_change_encoding(vt_parser_t *vt_parser, vt_char_encoding_t encoding) {
7404   ef_parser_t *cc_parser;
7405   ef_conv_t *cc_conv;
7406 
7407   cc_conv = vt_char_encoding_conv_new(encoding);
7408   cc_parser = vt_char_encoding_parser_new(encoding);
7409 
7410   if (cc_parser == NULL || cc_conv == NULL) {
7411 #ifdef DEBUG
7412     bl_warn_printf(BL_DEBUG_TAG " encoding not changed.\n");
7413 #endif
7414     if (cc_parser) {
7415       (*cc_parser->destroy)(cc_parser);
7416     }
7417 
7418     if (cc_conv) {
7419       (*cc_conv->destroy)(cc_conv);
7420     }
7421 
7422     return 0;
7423   }
7424 
7425 #ifdef DEBUG
7426   bl_warn_printf(BL_DEBUG_TAG " encoding changed.\n");
7427 #endif
7428 
7429   (*vt_parser->cc_parser->destroy)(vt_parser->cc_parser);
7430   (*vt_parser->cc_conv->destroy)(vt_parser->cc_conv);
7431 
7432   vt_parser->encoding = encoding;
7433   vt_parser->cc_parser = cc_parser;
7434   vt_parser->cc_conv = cc_conv;
7435 
7436   /* reset */
7437   vt_parser->gl = US_ASCII;
7438   vt_parser->g0 = US_ASCII;
7439   vt_parser->g1 = US_ASCII;
7440   vt_parser->is_so = 0;
7441 
7442   vt_parser->is_auto_encoding = 0;
7443 
7444   return 1;
7445 }
7446 
vt_parser_convert_to(vt_parser_t * vt_parser,u_char * dst,size_t len,ef_parser_t * parser)7447 size_t vt_parser_convert_to(vt_parser_t *vt_parser, u_char *dst, size_t len,
7448                             ef_parser_t *parser) {
7449   return (*vt_parser->cc_conv->convert)(vt_parser->cc_conv, dst, len, parser);
7450 }
7451 
vt_init_encoding_conv(vt_parser_t * vt_parser)7452 void vt_init_encoding_conv(vt_parser_t *vt_parser) {
7453   (*vt_parser->cc_conv->init)(vt_parser->cc_conv);
7454 
7455   /*
7456    * XXX
7457    * this causes unexpected behaviors in some applications(e.g. biew) ,
7458    * but this is necessary , since 0x00 - 0x7f is not necessarily US-ASCII
7459    * in these encodings but key input or selection paste assumes that
7460    * 0x00 - 0x7f should be US-ASCII at the initial state.
7461    */
7462   if (IS_STATEFUL_ENCODING(vt_parser->encoding)) {
7463     init_encoding_parser(vt_parser);
7464   }
7465 }
7466 
vt_set_auto_detect_encodings(char * encodings)7467 int vt_set_auto_detect_encodings(char *encodings) {
7468   char *p;
7469   u_int count;
7470 
7471   if (num_auto_detect_encodings > 0) {
7472     for (count = 0; count < num_auto_detect_encodings; count++) {
7473       (*auto_detect[count].parser->destroy)(auto_detect[count].parser);
7474     }
7475 
7476     free(auto_detect);
7477     num_auto_detect_encodings = 0;
7478   }
7479 
7480   free(auto_detect_encodings);
7481 
7482   if (*encodings == '\0') {
7483     auto_detect_encodings = NULL;
7484 
7485     return 1;
7486   } else {
7487     auto_detect_encodings = strdup(encodings);
7488   }
7489 
7490   if (!(auto_detect = malloc(sizeof(*auto_detect) * (bl_count_char_in_str(encodings, ',') + 1)))) {
7491     return 0;
7492   }
7493 
7494   while ((p = bl_str_sep(&encodings, ","))) {
7495     if ((auto_detect[num_auto_detect_encodings].encoding = vt_get_char_encoding(p)) !=
7496         VT_UNKNOWN_ENCODING) {
7497       num_auto_detect_encodings++;
7498     }
7499   }
7500 
7501   if (num_auto_detect_encodings == 0) {
7502     free(auto_detect);
7503 
7504     return 0;
7505   }
7506 
7507   for (count = 0; count < num_auto_detect_encodings; count++) {
7508     auto_detect[count].parser = vt_char_encoding_parser_new(auto_detect[count].encoding);
7509   }
7510 
7511   return 1;
7512 }
7513 
7514 /*
7515  * XXX
7516  * ef_map_ucs4_to_iscii() in ef_ucs4_iscii.h is used directly in
7517  * vt_convert_to_internal_ch(), though it should be used internally in mef
7518  * library
7519  */
7520 int ef_map_ucs4_to_iscii(ef_char_t *non_ucs, u_int32_t ucs4_code);
7521 
7522 /*
7523  * Return value
7524  *  1:  Succeed
7525  *  0:  Error
7526  *  -1: Control sequence
7527  */
vt_convert_to_internal_ch(vt_parser_t * vt_parser,ef_char_t * orig_ch)7528 int vt_convert_to_internal_ch(vt_parser_t *vt_parser, ef_char_t *orig_ch) {
7529   ef_char_t ch;
7530 
7531   ch = *orig_ch;
7532 
7533   /*
7534    * UCS <-> OTHER CS
7535    */
7536   if (ch.cs == ISO10646_UCS4_1) {
7537     u_char decsp;
7538 
7539     if ((vt_parser->unicode_policy & NOT_USE_UNICODE_BOXDRAW_FONT) &&
7540         (decsp = vt_convert_ucs_to_decsp(ef_char_to_int(&ch)))) {
7541       ch.ch[0] = decsp;
7542       ch.size = 1;
7543       ch.cs = DEC_SPECIAL;
7544       ch.property = 0;
7545     }
7546 #if 1
7547     /* See https://github.com/saitoha/drcsterm/ */
7548     else if ((vt_parser->unicode_policy & USE_UNICODE_DRCS) &&
7549              vt_convert_unicode_pua_to_drcs(&ch)) {
7550       if (ch.cs == US_ASCII) {
7551         vt_drcs_font_t *font;
7552 
7553         if ((font = vt_drcs_get_font(vt_parser->drcs, US_ASCII, 0)) &&
7554             vt_drcs_is_picture(font, ch.ch[0])) {
7555           ch.cs = CS_REVISION_1(US_ASCII);
7556         }
7557       }
7558 
7559       /*
7560        * Go to end to skip 'if (ch.ch[0] == 0x7f) { return 0; }' in next block.
7561        * Otherwise, 0x10XX7f doesn't work.
7562        */
7563       goto end_func;
7564     }
7565 #endif
7566     else {
7567       ef_char_t non_ucs;
7568       u_int32_t code = ef_char_to_int(&ch);
7569 
7570       ch.property = modify_ucs_property(code, vt_parser->col_size_of_width_a, ch.property);
7571 
7572       if (vt_parser->unicode_policy & NOT_USE_UNICODE_FONT) {
7573         /* convert ucs4 to appropriate charset */
7574 
7575         if (!is_noconv_unicode(ch.ch) && ef_map_locale_ucs4_to(&non_ucs, &ch) &&
7576             non_ucs.cs != ISO8859_6_R && /* ARABIC */
7577             non_ucs.cs != ISO8859_8_R)   /* HEBREW */
7578         {
7579           /* Use width property of unicode (the original charset). */
7580           non_ucs.property = ch.property;
7581           ch = non_ucs;
7582 
7583           goto end_block;
7584         }
7585       }
7586 
7587 #if !defined(NO_DYNAMIC_LOAD_CTL) || defined(USE_IND)
7588       if ((vt_parser->unicode_policy & CONVERT_UNICODE_TO_ISCII) &&
7589           0x900 <= code && code <= 0xd7f) {
7590         int ret = ef_map_ucs4_to_iscii(&non_ucs, code);
7591 
7592         if (!HAS_XTERM_LISTENER(vt_parser,check_iscii_font) ||
7593             /* non_ucs.cs is set if ef_map_ucs4_to_iscii() fails. */
7594             !(*vt_parser->xterm_listener->check_iscii_font)(vt_parser->xterm_listener->self,
7595                                                             non_ucs.cs)) {
7596           goto end_block;
7597         }
7598 
7599         if (ret) {
7600           ch.ch[0] = non_ucs.ch[0];
7601           ch.cs = non_ucs.cs;
7602           ch.size = 1;
7603           /* ch.property is not changed. */
7604         } else {
7605           switch (code & 0x07f) {
7606             case 0x0c:
7607               ch.ch[0] = '\xa6';
7608               break;
7609             case 0x3d:
7610               ch.ch[0] = '\xea';
7611               break;
7612             case 0x44:
7613               ch.ch[0] = '\xdf';
7614               break;
7615             case 0x50:
7616               ch.ch[0] = '\xa1';
7617               break;
7618             case 0x58:
7619               ch.ch[0] = '\xb3';
7620               break;
7621             case 0x59:
7622               ch.ch[0] = '\xb4';
7623               break;
7624             case 0x5a:
7625               ch.ch[0] = '\xb5';
7626               break;
7627             case 0x5b:
7628               ch.ch[0] = '\xba';
7629               break;
7630             case 0x5c:
7631               ch.ch[0] = '\xbf';
7632               break;
7633             case 0x5d:
7634               ch.ch[0] = '\xc0';
7635               break;
7636             case 0x5e:
7637               ch.ch[0] = '\xc9';
7638               break;
7639             case 0x60:
7640               ch.ch[0] = '\xaa';
7641               break;
7642             case 0x61:
7643               ch.ch[0] = '\xa7';
7644               break;
7645             case 0x62:
7646               ch.ch[0] = '\xdb';
7647               break;
7648             case 0x63:
7649               ch.ch[0] = '\xdc';
7650               break;
7651             default:
7652               goto end_block;
7653           }
7654 
7655           ch.ch[1] = '\xe9';
7656           /* non_ucs.cs is set if ef_map_ucs4_to_iscii() fails. */
7657           ch.cs = non_ucs.cs;
7658           ch.size = 2;
7659           /* ch.property is not changed. */
7660         }
7661       }
7662 #endif
7663 
7664     end_block:
7665       ;
7666     }
7667   } else if (ch.cs != US_ASCII) {
7668     if ((vt_parser->unicode_policy & ONLY_USE_UNICODE_FONT) ||
7669         /* XXX converting japanese gaiji to ucs. */
7670         IS_JIS_EXT(ch.cs) ||
7671         /* XXX converting RTL characters to ucs. */
7672         ch.cs == ISO8859_6_R || /* Arabic */
7673         ch.cs == ISO8859_8_R    /* Hebrew */
7674 #if 0
7675         /* GB18030_2000 2-byte chars(==GBK) are converted to UCS */
7676         || (encoding == VT_GB18030 && ch.cs == GBK)
7677 #endif
7678         ) {
7679       ef_char_t ucs;
7680 
7681       if (ef_map_to_ucs4(&ucs, &ch)) {
7682         u_int32_t code = ef_char_to_int(&ucs);
7683         ef_charset_t orig_cs = ch.cs;
7684 
7685         ch = ucs;
7686 
7687         /* Use width property of the original charset. */
7688         ch.property = ef_get_ucs_property(code) & ~(EF_FULLWIDTH|EF_AWIDTH);
7689         if (IS_FULLWIDTH_CS(orig_cs)) {
7690           ch.property |= EF_FULLWIDTH;
7691         }
7692         ch.property = modify_ucs_property(code, vt_parser->col_size_of_width_a, ch.property);
7693       }
7694     } else if (IS_FULLWIDTH_CS(ch.cs)) {
7695       ch.property |= EF_FULLWIDTH;
7696     }
7697   }
7698 
7699   if (ch.size == 1) {
7700     /* single byte cs */
7701     vt_drcs_font_t *font;
7702 
7703     if ((ch.ch[0] == 0x0 || ch.ch[0] == 0x7f) &&
7704         (!(font = vt_drcs_get_font(vt_parser->drcs, US_ASCII, 0)) ||
7705          !(vt_drcs_is_picture(font, ch.ch[0])))) {
7706       /* DECNULM is always set => discarding 0x0 */
7707 #ifdef DEBUG
7708       bl_warn_printf(BL_DEBUG_TAG " 0x0/0x7f sequence is received , ignored...\n");
7709 #endif
7710       return 0;
7711     } else if ((ch.ch[0] & 0x7f) <= 0x1f && ch.cs == US_ASCII) {
7712       /* Control sequence (C0 or C1) */
7713       return -1;
7714     }
7715 
7716     if (vt_is_msb_set(ch.cs)) {
7717       SET_MSB(ch.ch[0]);
7718     } else {
7719       u_int16_t ucs;
7720 
7721       if (ch.cs == US_ASCII) {
7722         /* XXX prev_ch should not be static. */
7723         static u_char prev_ch;
7724         static ef_charset_t prev_gl = US_ASCII;
7725 
7726         if (vt_parser->gl == US_ASCII) {
7727           goto end_func;
7728         }
7729 
7730         if (IS_CS94MB(vt_parser->gl)) {
7731           /* iso2022 multi byte characters can be parsed in utf-8 encoding */
7732           if (vt_parser->gl == prev_gl && prev_ch) {
7733             ch.ch[1] = ch.ch[0];
7734             ch.ch[0] = prev_ch;
7735             ch.size = 2;
7736             ch.property = EF_FULLWIDTH;
7737             prev_ch = 0;
7738             prev_gl = US_ASCII;
7739 
7740             ch.cs = vt_parser->gl;
7741 
7742             goto end_func;
7743           } else {
7744             prev_ch = ch.ch[0];
7745             prev_gl = vt_parser->gl;
7746 
7747             return 0;
7748           }
7749         } else {
7750           ch.cs = vt_parser->gl;
7751         }
7752       }
7753 
7754       if (ch.cs == DEC_SPECIAL) {
7755         if ((vt_parser->unicode_policy & ONLY_USE_UNICODE_BOXDRAW_FONT)) {
7756           ucs = vt_convert_decsp_to_ucs(ch.ch[0]);
7757         } else {
7758           goto end_func;
7759         }
7760       } else if (ch.cs == DEC_TECHNICAL) {
7761         ucs = vt_convert_dectech_to_ucs(ch.ch[0]);
7762       } else {
7763         goto end_func;
7764       }
7765 
7766       if (ucs) {
7767         ef_int_to_bytes(ch.ch, 4, ucs);
7768         ch.size = 4;
7769         ch.cs = ISO10646_UCS4_1;
7770         /* Use modify_ucs_property() to make it possible to aplly full_width_areas setting. */
7771         ch.property = modify_ucs_property(ucs, 0 /* not used */, 0 /* Use original property */);
7772       }
7773     }
7774   } else {
7775     /*
7776      * NON UCS <-> NON UCS
7777      */
7778 
7779     /* multi byte cs */
7780 
7781     /*
7782      * XXX hack
7783      * how to deal with johab 10-4-4(8-4-4) font ?
7784      * is there any uhc font ?
7785      */
7786 
7787     if (ch.cs == JOHAB) {
7788       ef_char_t uhc;
7789 
7790       if (ef_map_johab_to_uhc(&uhc, &ch) == 0) {
7791         return 0;
7792       }
7793 
7794       ch = uhc;
7795     }
7796 
7797     /*
7798      * XXX
7799      * switching option whether this conversion is done should
7800      * be introduced.
7801      */
7802     if (ch.cs == UHC) {
7803       ef_char_t ksc;
7804 
7805       if (ef_map_uhc_to_ksc5601_1987(&ksc, &ch) == 0) {
7806         return 0;
7807       }
7808 
7809       ch = ksc;
7810     }
7811   }
7812 
7813 end_func:
7814   *orig_ch = ch;
7815 
7816   return 1;
7817 }
7818 
vt_parser_set_alt_color_mode(vt_parser_t * vt_parser,vt_alt_color_mode_t mode)7819 void vt_parser_set_alt_color_mode(vt_parser_t *vt_parser, vt_alt_color_mode_t mode) {
7820   vt_parser->alt_color_mode = mode;
7821 }
7822 
vt_set_broadcasting(int flag)7823 void vt_set_broadcasting(int flag) {
7824   is_broadcasting = flag;
7825 }
7826 
vt_parser_is_broadcasting(vt_parser_t * vt_parser)7827 int vt_parser_is_broadcasting(vt_parser_t *vt_parser) {
7828   return (is_broadcasting && !vt_parser->ignore_broadcasted_chars);
7829 }
7830 
true_or_false(const char * str)7831 int true_or_false(const char *str) {
7832   if (strcmp(str, "true") == 0) {
7833     return 1;
7834   } else if (strcmp(str, "false") == 0) {
7835     return 0;
7836   } else {
7837     return -1;
7838   }
7839 }
7840 
vt_parser_get_config(vt_parser_t * vt_parser,vt_pty_t * output,char * key,int to_menu,int * flag)7841 int vt_parser_get_config(
7842     vt_parser_t *vt_parser,
7843     vt_pty_t *output, /* if vt_parser->pty == output, NULL is set */
7844     char *key, int to_menu, int *flag) {
7845   char *value;
7846   char digit[DIGIT_STR_LEN(u_int) + 1];
7847   char cwd[PATH_MAX];
7848 
7849   if (strcmp(key, "encoding") == 0) {
7850     value = vt_get_char_encoding_name(vt_parser->encoding);
7851   } else if (strcmp(key, "is_auto_encoding") == 0) {
7852     if (vt_parser->is_auto_encoding) {
7853       value = "true";
7854     } else {
7855       value = "false";
7856     }
7857   } else if (strcmp(key, "word_separators") == 0) {
7858     value = vt_get_word_separators();
7859   } else if (strcmp(key, "regard_uri_as_word") == 0) {
7860     if (vt_get_regard_uri_as_word()) {
7861       value = "true";
7862     } else {
7863       value = "false";
7864     }
7865   } else if (strcmp(key, "use_alt_buffer") == 0) {
7866     if (use_alt_buffer) {
7867       value = "true";
7868     } else {
7869       value = "false";
7870     }
7871   } else if (strcmp(key, "vt_color_mode") == 0) {
7872     value = vt_get_color_mode();
7873   } else if (strcmp(key, "use_ansi_colors") == 0) {
7874     if (vt_parser->use_ansi_colors) {
7875       value = "true";
7876     } else {
7877       value = "false";
7878     }
7879   } else if (strcmp(key, "tabsize") == 0) {
7880     sprintf(digit, "%d", vt_screen_get_tab_size(vt_parser->screen));
7881     value = digit;
7882   } else if (strcmp(key, "logsize") == 0) {
7883     if (vt_screen_log_size_is_unlimited(vt_parser->screen)) {
7884       value = "unlimited";
7885     } else {
7886       sprintf(digit, "%d", vt_screen_get_log_size(vt_parser->screen));
7887       value = digit;
7888     }
7889   } else if (strcmp(key, "static_backscroll_mode") == 0) {
7890     if (vt_get_backscroll_mode(vt_parser->screen) == BSM_STATIC) {
7891       value = "true";
7892     } else {
7893       value = "false";
7894     }
7895   } else if (strcmp(key, "use_combining") == 0) {
7896     if (vt_parser->use_char_combining) {
7897       value = "true";
7898     } else {
7899       value = "false";
7900     }
7901   } else if (strcmp(key, "col_size_of_width_a") == 0) {
7902     if (vt_parser->col_size_of_width_a == 2) {
7903       value = "2";
7904     } else {
7905       value = "1";
7906     }
7907   } else if (strcmp(key, "locale") == 0) {
7908     value = bl_get_locale();
7909   } else if (strcmp(key, "pwd") == 0) {
7910     value = getcwd(cwd, sizeof(cwd));
7911   } else if (strcmp(key, "logging_vt_seq") == 0) {
7912     if (vt_parser->logging_vt_seq) {
7913       value = "true";
7914     } else {
7915       value = "false";
7916     }
7917   } else if (strcmp(key, "vt_seq_format") == 0) {
7918     if (use_ttyrec_format) {
7919       value = "ttyrec";
7920     } else {
7921       value = "raw";
7922     }
7923   } else if (strcmp(key, "rows") == 0) {
7924     sprintf(digit, "%d", vt_screen_get_logical_rows(vt_parser->screen));
7925     value = digit;
7926   } else if (strcmp(key, "cols") == 0) {
7927     sprintf(digit, "%d", vt_screen_get_logical_cols(vt_parser->screen));
7928     value = digit;
7929   } else if (strcmp(key, "not_use_unicode_font") == 0) {
7930     if (vt_parser->unicode_policy & NOT_USE_UNICODE_FONT) {
7931       value = "true";
7932     } else {
7933       value = "false";
7934     }
7935   } else if (strcmp(key, "only_use_unicode_font") == 0) {
7936     if (vt_parser->unicode_policy & ONLY_USE_UNICODE_FONT) {
7937       value = "true";
7938     } else {
7939       value = "false";
7940     }
7941   } else if (strcmp(key, "box_drawing_font") == 0) {
7942     if (vt_parser->unicode_policy & NOT_USE_UNICODE_BOXDRAW_FONT) {
7943       value = "decsp";
7944     } else if (vt_parser->unicode_policy & ONLY_USE_UNICODE_BOXDRAW_FONT) {
7945       value = "unicode";
7946     } else {
7947       value = "noconv";
7948     }
7949   } else if (strcmp(key, "auto_detect_encodings") == 0) {
7950     if ((value = auto_detect_encodings) == NULL) {
7951       value = "";
7952     }
7953   } else if (strcmp(key, "use_auto_detect") == 0) {
7954     if (vt_parser->use_auto_detect) {
7955       value = "true";
7956     } else {
7957       value = "false";
7958     }
7959   } else if (strcmp(key, "allow_scp") == 0) {
7960 #ifdef USE_LIBSSH2
7961     if (use_scp_full) {
7962       value = "true";
7963     } else
7964 #endif
7965     {
7966       value = "false";
7967     }
7968   } else if (strcmp(key, "unicode_noconv_areas") == 0) {
7969     response_area_table(vt_parser->pty, key, unicode_noconv_areas, num_unicode_noconv_areas,
7970                         to_menu);
7971 
7972     return 1;
7973   } else if (strcmp(key, "unicode_full_width_areas") == 0) {
7974     response_area_table(vt_parser->pty, key, full_width_areas, num_full_width_areas, to_menu);
7975 
7976     return 1;
7977   } else if (strcmp(key, "unicode_half_width_areas") == 0) {
7978     response_area_table(vt_parser->pty, key, half_width_areas, num_half_width_areas, to_menu);
7979 
7980     return 1;
7981   } else if (strcmp(key, "blink_cursor") == 0) {
7982     if (vt_parser->cursor_style & CS_BLINK) {
7983       value = "true";
7984     } else {
7985       value = "false";
7986     }
7987   } else if (strcmp(key, "ignore_broadcasted_chars") == 0) {
7988     if (vt_parser->ignore_broadcasted_chars) {
7989       value = "true";
7990     } else {
7991       value = "false";
7992     }
7993   } else if (strcmp(key, "broadcast") == 0) {
7994     if (is_broadcasting) {
7995       value = "true";
7996     } else {
7997       value = "false";
7998     }
7999   } else if (strcmp(key, "use_multi_column_char") == 0) {
8000     if (vt_parser->use_multi_col_char) {
8001       value = "true";
8002     } else {
8003       value = "false";
8004     }
8005   } else if (strcmp(key, "old_drcs_sixel") == 0) {
8006     if (old_drcs_sixel) {
8007       value = "true";
8008     } else {
8009       value = "false";
8010     }
8011   } else if (strcmp(key, "send_file") == 0) {
8012     if (send_file) {
8013       value = send_file;
8014     } else {
8015       value = "";
8016     }
8017   } else if (strcmp(key, "receive_directory") == 0) {
8018     if (recv_dir) {
8019       value = recv_dir;
8020     } else {
8021       value = "";
8022     }
8023   } else if (strcmp(key, "local_echo_wait") == 0) {
8024     sprintf(digit, "%d", local_echo_wait_msec);
8025     value = digit;
8026   } else if (strcmp(key, "use_local_echo") == 0) {
8027     if (vt_parser->use_local_echo) {
8028       value = "true";
8029     } else {
8030       value = "false";
8031     }
8032   } else if (strcmp(key, "challenge") == 0) {
8033     value = vt_get_proto_challenge();
8034     if (to_menu < 0) {
8035       to_menu = 0;
8036     }
8037   } else {
8038     /* Continue to process it in x_screen.c */
8039     return 0;
8040   }
8041 
8042   if (!output) {
8043     output = vt_parser->pty;
8044   }
8045 
8046 /* value is never set NULL above. */
8047 #if 0
8048   if (!value) {
8049     vt_response_config(output, "error", NULL, to_menu);
8050   }
8051 #endif
8052 
8053   if (flag) {
8054     *flag = value ? true_or_false(value) : -1;
8055   } else {
8056     vt_response_config(output, key, value, to_menu);
8057   }
8058 
8059   return 1;
8060 }
8061 
8062 /* Called in visual context */
vt_parser_set_config(vt_parser_t * vt_parser,char * key,char * value)8063 int vt_parser_set_config(vt_parser_t *vt_parser, char *key, char *value) {
8064   if (strcmp(key, "encoding") == 0) {
8065     if (strcmp(value, "auto") == 0) {
8066       vt_parser->is_auto_encoding = strcasecmp(value, "auto") == 0 ? 1 : 0;
8067     }
8068 
8069     return 0; /* Continue to process it in x_screen.c */
8070   } else if (strcmp(key, "logging_msg") == 0) {
8071     if (true_or_false(value) > 0) {
8072       bl_set_msg_log_file_name("mlterm/msg.log");
8073     } else {
8074       bl_set_msg_log_file_name(NULL);
8075     }
8076   } else if (strcmp(key, "word_separators") == 0) {
8077     vt_set_word_separators(value);
8078   } else if (strcmp(key, "regard_uri_as_word") == 0) {
8079     int flag;
8080 
8081     if ((flag = true_or_false(value)) != -1) {
8082       vt_set_regard_uri_as_word(flag);
8083     }
8084   } else if (strcmp(key, "vt_color_mode") == 0) {
8085     vt_set_color_mode(value);
8086   } else if (strcmp(key, "use_alt_buffer") == 0) {
8087     int flag;
8088 
8089     if ((flag = true_or_false(value)) != -1) {
8090       use_alt_buffer = flag;
8091     }
8092   } else if (strcmp(key, "use_ansi_colors") == 0) {
8093     int flag;
8094 
8095     if ((flag = true_or_false(value)) != -1) {
8096       vt_parser->use_ansi_colors = flag;
8097     }
8098   } else if (strcmp(key, "unicode_noconv_areas") == 0) {
8099     vt_set_unicode_noconv_areas(value);
8100   } else if (strcmp(key, "unicode_full_width_areas") == 0) {
8101     vt_set_full_width_areas(value);
8102   } else if (strcmp(key, "unicode_half_width_areas") == 0) {
8103     vt_set_half_width_areas(value);
8104   } else if (strcmp(key, "tabsize") == 0) {
8105     u_int tab_size;
8106 
8107     if (bl_str_to_uint(&tab_size, value)) {
8108       vt_screen_set_tab_size(vt_parser->screen, tab_size);
8109     }
8110   } else if (strcmp(key, "static_backscroll_mode") == 0) {
8111     vt_bs_mode_t mode;
8112 
8113     if (strcmp(value, "true") == 0) {
8114       mode = BSM_STATIC;
8115     } else if (strcmp(value, "false") == 0) {
8116       mode = BSM_DEFAULT;
8117     } else {
8118       return 1;
8119     }
8120 
8121     vt_set_backscroll_mode(vt_parser->screen, mode);
8122   } else if (strcmp(key, "use_combining") == 0) {
8123     int flag;
8124 
8125     if ((flag = true_or_false(value)) != -1) {
8126       vt_parser->use_char_combining = flag;
8127     }
8128   } else if (strcmp(key, "col_size_of_width_a") == 0) {
8129     u_int size;
8130 
8131     if (strcmp(value, "switch") == 0) {
8132       size = vt_parser->col_size_of_width_a == 1 ? 2 : 1;
8133     } else if (!bl_str_to_uint(&size, value)) {
8134       goto end;
8135     }
8136 
8137     set_col_size_of_width_a(vt_parser, size);
8138 
8139   end:
8140     ;
8141   } else if (strcmp(key, "locale") == 0) {
8142     bl_locale_init(value);
8143   } else if (strcmp(key, "logging_vt_seq") == 0) {
8144     int flag;
8145 
8146     if ((flag = true_or_false(value)) != -1) {
8147       vt_parser->logging_vt_seq = flag;
8148     }
8149   } else if (strcmp(key, "vt_seq_format") == 0) {
8150     use_ttyrec_format = (strcmp(value, "ttyrec") == 0);
8151   } else if (strcmp(key, "geometry") == 0) {
8152     u_int cols;
8153     u_int rows;
8154 
8155     if (sscanf(value, "%ux%u", &cols, &rows) == 2) {
8156       resize(vt_parser, cols, rows, 1);
8157     }
8158   } else if (strcmp(key, "box_drawing_font") == 0) {
8159     if (strcmp(value, "unicode") == 0) {
8160       vt_parser->unicode_policy &= ~NOT_USE_UNICODE_BOXDRAW_FONT;
8161       vt_parser->unicode_policy |= ONLY_USE_UNICODE_BOXDRAW_FONT;
8162     } else if (strcmp(value, "decsp") == 0) {
8163       vt_parser->unicode_policy &= ~ONLY_USE_UNICODE_BOXDRAW_FONT;
8164       vt_parser->unicode_policy |= NOT_USE_UNICODE_BOXDRAW_FONT;
8165     } else {
8166       vt_parser->unicode_policy &=
8167           (~NOT_USE_UNICODE_BOXDRAW_FONT & ~ONLY_USE_UNICODE_BOXDRAW_FONT);
8168     }
8169   } else if (strcmp(key, "auto_detect_encodings") == 0) {
8170     vt_set_auto_detect_encodings(value);
8171   } else if (strcmp(key, "use_auto_detect") == 0) {
8172     int flag;
8173 
8174     if ((flag = true_or_false(value)) != -1) {
8175       vt_parser->use_auto_detect = flag;
8176     }
8177   } else if (strcmp(key, "blink_cursor") == 0) {
8178     if (strcmp(value, "true") == 0) {
8179       vt_parser->cursor_style |= CS_BLINK;
8180     } else {
8181       vt_parser->cursor_style &= ~CS_BLINK;
8182     }
8183   } else if (strcmp(key, "ignore_broadcasted_chars") == 0) {
8184     int flag;
8185 
8186     if ((flag = true_or_false(value)) != -1) {
8187       vt_parser->ignore_broadcasted_chars = flag;
8188     }
8189   } else if (strcmp(key, "broadcast") == 0) {
8190     int flag;
8191 
8192     if ((flag = true_or_false(value)) != -1) {
8193       is_broadcasting = flag;
8194     }
8195   } else if (strcmp(key, "use_multi_column_char") == 0) {
8196     int flag;
8197 
8198     if ((flag = true_or_false(value)) != -1) {
8199       vt_parser->use_multi_col_char = flag;
8200     }
8201   } else if (strcmp(key, "old_drcs_sixel") == 0) {
8202     int flag;
8203 
8204     if ((flag = true_or_false(value)) != -1) {
8205       old_drcs_sixel = flag;
8206     }
8207   } else if (strcmp(key, "local_echo_wait") == 0) {
8208     u_int msec;
8209 
8210     if (bl_str_to_uint(&msec, value)) {
8211       local_echo_wait_msec = msec;
8212     }
8213   } else if (strcmp(key, "use_local_echo") == 0) {
8214     int flag;
8215 
8216     if ((flag = true_or_false(value)) != -1) {
8217       if ((vt_parser->use_local_echo ? 1 : 0) != flag && !(vt_parser->use_local_echo = flag)) {
8218         vt_screen_logical(vt_parser->screen);
8219         vt_screen_disable_local_echo(vt_parser->screen);
8220         vt_screen_visual(vt_parser->screen);
8221       }
8222     }
8223   } else if (strncmp(key, "send_file", 9) == 0) {
8224     if (strstr(value, "..")) {
8225       /* insecure file name */
8226       bl_msg_printf("%s is insecure file name.\n", value);
8227     } else {
8228       if (key[9] == '\0') {
8229         free(send_file);
8230         send_file = convert_to_locale_encoding(value, vt_parser->cc_parser, vt_parser->encoding);
8231       } else if (strcmp(key + 9, "_utf8") == 0) {
8232         /* Hack for set_xdnd_config() in ui_screen.c */
8233         free(send_file);
8234         send_file = convert_to_locale_encoding(value, NULL, VT_UTF8);
8235       }
8236     }
8237   } else {
8238     /* Continue to process it in x_screen.c */
8239     return 0;
8240   }
8241 
8242   return 1;
8243 }
8244 
8245 /* Called in visual context */
vt_parser_exec_cmd(vt_parser_t * vt_parser,char * cmd)8246 int vt_parser_exec_cmd(vt_parser_t *vt_parser, char *cmd) {
8247   if (strcmp(cmd, "gen_proto_challenge") == 0) {
8248     vt_gen_proto_challenge();
8249   } else if (strcmp(cmd, "full_reset") == 0) {
8250     vt_parser_reset(vt_parser, 0);
8251   } else if (strncmp(cmd, "snapshot", 8) == 0) {
8252     char **argv;
8253     int argc;
8254 
8255     if ((argv = bl_argv_alloca(cmd)) && bl_arg_str_to_array(argv, &argc, cmd)) {
8256       vt_char_encoding_t encoding;
8257       char *file;
8258 
8259       if (argc >= 3) {
8260         encoding = vt_get_char_encoding(argv[2]);
8261       } else {
8262         encoding = VT_UNKNOWN_ENCODING;
8263       }
8264 
8265       if (argc >= 2) {
8266         file = argv[1];
8267       } else {
8268         /* skip /dev/ */
8269         file = vt_pty_get_slave_name(vt_parser->pty) + 5;
8270       }
8271 
8272       if (strstr(file, "..")) {
8273         /* insecure file name */
8274         bl_msg_printf("%s is insecure file name.\n", file);
8275       } else {
8276         snapshot(vt_parser, encoding, file, WCA_ALL);
8277       }
8278     }
8279   } else if (strcmp(cmd, "zmodem_start") == 0) {
8280     int count = 0;
8281 
8282     while (1) {
8283       if (!vt_pty_is_loopback(vt_parser->pty)) {
8284         if ((send_file || recv_dir || (recv_dir = bl_get_user_rc_path("mlterm/recv"))) &&
8285             vt_transfer_start(send_file, recv_dir, 0,
8286                               vt_screen_get_cols(vt_parser->screen) / 2 + 1)) {
8287           vt_parser->is_zmodem_ready = 0;
8288           vt_parser->is_transferring_data = (send_file ? 0x1 : 0x2);
8289           vt_parser->r_buf.left = 0;
8290           transfer_data(vt_parser);
8291         } else {
8292           transfer_cancel(vt_parser, 1);
8293           /* send_file is freed in vt_transfer_start() */
8294         }
8295         send_file = NULL;
8296 
8297         break;
8298       }
8299 
8300       if (++count == 10) {
8301         bl_msg_printf("Retry zmodem_start.\n");
8302 
8303         break;
8304       }
8305 
8306       bl_usleep(100000);
8307     }
8308   }
8309 #if !defined(NO_IMAGE) && defined(ENABLE_OSC5379PICTURE)
8310   else if (strncmp(cmd, "show_picture ", 13) == 0 || strncmp(cmd, "add_frame ", 10) == 0) {
8311     int clip_beg_col = 0;
8312     int clip_beg_row = 0;
8313     int clip_cols = 0;
8314     int clip_rows = 0;
8315     int img_cols = 0;
8316     int img_rows = 0;
8317     char **argv;
8318     int argc;
8319 
8320     if (!(argv = bl_argv_alloca(cmd)) || !bl_arg_str_to_array(argv, &argc, cmd) || argc == 1) {
8321       return 1;
8322     }
8323 
8324     if (argc >= 3) {
8325       int has_img_size;
8326 
8327       if (strchr(argv[argc - 1], '+')) {
8328         sscanf(argv[argc - 1], "%dx%d+%d+%d", &clip_cols, &clip_rows, &clip_beg_col, &clip_beg_row);
8329 
8330         has_img_size = (argc >= 4);
8331       } else {
8332         has_img_size = 1;
8333       }
8334 
8335       if (has_img_size) {
8336         sscanf(argv[2], "%dx%d", &img_cols, &img_rows);
8337       }
8338     }
8339 
8340     if (*argv[0] == 's') {
8341       show_picture(vt_parser, argv[1], clip_beg_col, clip_beg_row, clip_cols, clip_rows,
8342                    img_cols, img_rows, 0, 0);
8343     } else if (HAS_XTERM_LISTENER(vt_parser, add_frame_to_animation)) {
8344       (*vt_parser->xterm_listener->add_frame_to_animation)(vt_parser->xterm_listener->self,
8345                                                               argv[1], &img_cols, &img_rows);
8346     }
8347   }
8348 #endif
8349 #ifdef USE_LIBSSH2
8350   else if (strncmp(cmd, "scp ", 4) == 0) {
8351     char **argv;
8352     int argc;
8353 
8354     if ((argv = bl_argv_alloca(cmd)) && bl_arg_str_to_array(argv, &argc, cmd) &&
8355         (argc == 3 || argc == 4)) {
8356       vt_char_encoding_t encoding;
8357 
8358       if (!argv[3] || (encoding = vt_get_char_encoding(argv[3])) == VT_UNKNOWN_ENCODING) {
8359         encoding = vt_parser->encoding;
8360       }
8361 
8362       vt_pty_ssh_scp(vt_parser->pty, vt_parser->encoding, encoding, argv[2], argv[1],
8363                      use_scp_full, recv_dir, vt_screen_get_cols(vt_parser->screen) / 2 + 1);
8364     }
8365   }
8366 #endif
8367   else {
8368     return 0;
8369   }
8370 
8371   return 1;
8372 }
8373 
8374 /*
8375  * level=1: Unuse loopback, Output ZCAN.
8376  */
vt_parser_reset(vt_parser_t * vt_parser,int level)8377 void vt_parser_reset(vt_parser_t *vt_parser, int level) {
8378 #ifdef USE_LIBSSH2
8379   if (level >= 1) {
8380     if (vt_pty_is_loopback(vt_parser->pty)) {
8381       if (vt_pty_get_mode(vt_parser->pty) == PTY_MOSH) {
8382         vt_pty_mosh_set_use_loopback(vt_parser->pty, 0);
8383       } else {
8384         vt_pty_ssh_set_use_loopback(vt_parser->pty, 0);
8385       }
8386     }
8387   }
8388 #endif
8389 
8390   soft_reset(vt_parser);
8391   vt_parser->r_buf.left = 0; /* Reset DCS or OSC etc sequence */
8392   vt_parser->sixel_scrolling = 1;
8393   transfer_cancel(vt_parser, level >= 1);
8394 }
8395 
8396 #define MOUSE_POS_LIMIT (0xff - 0x20)
8397 #define EXT_MOUSE_POS_LIMIT (0x7ff - 0x20)
8398 
vt_parser_report_mouse_tracking(vt_parser_t * vt_parser,int col,int row,int button,int is_released,int key_state,int button_state)8399 void vt_parser_report_mouse_tracking(vt_parser_t *vt_parser, int col, int row,
8400                                            int button,
8401                                            int is_released, /* is_released is 0 if PointerMotion */
8402                                            int key_state, int button_state) {
8403   if (vt_parser->mouse_mode >= LOCATOR_CHARCELL_REPORT) {
8404     char seq[10 + DIGIT_STR_LEN(int)*4 + 1];
8405     int ev;
8406     int is_outside_filter_rect;
8407 
8408     is_outside_filter_rect = 0;
8409     if (vt_parser->loc_filter.top > row || vt_parser->loc_filter.left > col ||
8410         vt_parser->loc_filter.bottom < row || vt_parser->loc_filter.right < col) {
8411       vt_parser->loc_filter.top = vt_parser->loc_filter.bottom = row;
8412       vt_parser->loc_filter.left = vt_parser->loc_filter.right = col;
8413 
8414       if (vt_parser->locator_mode & LOCATOR_FILTER_RECT) {
8415         vt_parser->locator_mode &= ~LOCATOR_FILTER_RECT;
8416         is_outside_filter_rect = 1;
8417       }
8418     }
8419 
8420     if (button == 0) {
8421       if (is_outside_filter_rect) {
8422         ev = 10;
8423       } else if (vt_parser->locator_mode & LOCATOR_REQUEST) {
8424         ev = 1;
8425       } else {
8426         return;
8427       }
8428     } else {
8429       if ((is_released && !(vt_parser->locator_mode & LOCATOR_BUTTON_UP)) ||
8430           (!is_released && !(vt_parser->locator_mode & LOCATOR_BUTTON_DOWN))) {
8431         return;
8432       }
8433 
8434       if (button == 1) {
8435         ev = is_released ? 3 : 2;
8436       } else if (button == 2) {
8437         ev = is_released ? 5 : 4;
8438       } else if (button == 3) {
8439         ev = is_released ? 7 : 6;
8440       } else {
8441         ev = 1;
8442       }
8443     }
8444 
8445     sprintf(seq, "\x1b[%d;%d;%d;%d;%d&w", ev, button_state, row, col,
8446             vt_screen_get_page_id(vt_parser->screen) + 1);
8447 
8448     vt_write_to_pty(vt_parser->pty, seq, strlen(seq));
8449 
8450     if (vt_parser->locator_mode & LOCATOR_ONESHOT) {
8451       set_mouse_report(vt_parser, 0);
8452       vt_parser->locator_mode = 0;
8453     }
8454   } else {
8455     /*
8456      * Max length is SGR style => ESC [ < %d ; %d(col) ; %d(row) ; %c('M' or
8457      * 'm') NULL
8458      *                            1   1 1 3  1 3       1  3      1 1 1
8459      */
8460     u_char seq[17];
8461     size_t seq_len;
8462 
8463     if (is_released && vt_parser->ext_mouse_mode != EXTENDED_MOUSE_REPORT_SGR) {
8464       key_state = 0;
8465       button = 3;
8466     } else if (button == 0) {
8467       /* PointerMotion */
8468       button = 3 + 32;
8469     } else {
8470       if (is_released) {
8471         /* for EXTENDED_MOUSE_REPORT_SGR */
8472         key_state += 0x80;
8473       }
8474 
8475       button--; /* button1 == 0, button2 == 1, button3 == 2 */
8476 
8477       while (button >= 3) {
8478         /* Wheel mouse */
8479         key_state += 64;
8480         button -= 3;
8481       }
8482     }
8483 
8484     if (vt_parser->ext_mouse_mode == EXTENDED_MOUSE_REPORT_SGR) {
8485       sprintf(seq, "\x1b[<%d;%d;%d%c", (button + key_state) & 0x7f, col, row,
8486               ((button + key_state) & 0x80) ? 'm' : 'M');
8487       seq_len = strlen(seq);
8488     } else if (vt_parser->ext_mouse_mode == EXTENDED_MOUSE_REPORT_URXVT) {
8489       sprintf(seq, "\x1b[%d;%d;%dM", 0x20 + button + key_state, col, row);
8490       seq_len = strlen(seq);
8491     } else {
8492       memcpy(seq, "\x1b[M", 3);
8493       seq[3] = 0x20 + button + key_state;
8494 
8495       if (vt_parser->ext_mouse_mode == EXTENDED_MOUSE_REPORT_UTF8) {
8496         int ch;
8497         u_char *p;
8498 
8499         p = seq + 4;
8500 
8501         if (col > EXT_MOUSE_POS_LIMIT) {
8502           col = EXT_MOUSE_POS_LIMIT;
8503         }
8504 
8505         if ((ch = 0x20 + col) >= 0x80) {
8506           *(p++) = ((ch >> 6) & 0x1f) | 0xc0;
8507           *(p++) = (ch & 0x3f) | 0x80;
8508         } else {
8509           *(p++) = ch;
8510         }
8511 
8512         if (row > EXT_MOUSE_POS_LIMIT) {
8513           row = EXT_MOUSE_POS_LIMIT;
8514         }
8515 
8516         if ((ch = 0x20 + row) >= 0x80) {
8517           *(p++) = ((ch >> 6) & 0x1f) | 0xc0;
8518           *p = (ch & 0x3f) | 0x80;
8519         } else {
8520           *p = ch;
8521         }
8522 
8523         seq_len = p - seq + 1;
8524       } else {
8525         seq[4] = 0x20 + (col < MOUSE_POS_LIMIT ? col : MOUSE_POS_LIMIT);
8526         seq[5] = 0x20 + (row < MOUSE_POS_LIMIT ? row : MOUSE_POS_LIMIT);
8527         seq_len = 6;
8528       }
8529     }
8530 
8531     vt_write_to_pty(vt_parser->pty, seq, seq_len);
8532 
8533 #ifdef __DEBUG
8534     bl_debug_printf(BL_DEBUG_TAG " [reported cursor pos] %d %d\n", col, row);
8535 #endif
8536   }
8537 }
8538 
8539 #ifdef BL_DEBUG
8540 
8541 #include <assert.h>
8542 
TEST_vt_parser(void)8543 void TEST_vt_parser(void) {
8544 #ifndef NO_IMAGE
8545   u_char a[] = "\x1b]8k @\x1f\x1bP;1";
8546   u_char b[] = "\x1b]k @\x1f\x90;1";
8547   u_char c[] = "\x1b]16k @@@@\x1f\x1bP0;1";
8548   u_char d[] = "\x1b]A @\x90;2";
8549   u_char e[] = "\x1b]A hogefuga\x1f\x1bP;1";
8550   u_char f[] = "\x1b]A hogefug\x1f\x1bP;1";
8551 
8552   assert(is_separated_sixel(a, sizeof(a) - 1) == 1);
8553   assert(is_separated_sixel(b, sizeof(b) - 1) == 1);
8554   assert(is_separated_sixel(c, sizeof(c) - 1) == 1);
8555   assert(is_separated_sixel(d, sizeof(d) - 1) == 0);
8556   assert(is_separated_sixel(e, sizeof(e) - 1) == 0);
8557   assert(is_separated_sixel(f, sizeof(f) - 1) == 1);
8558 
8559   bl_msg_printf("PASS vt_parser test.\n");
8560 #endif
8561 }
8562 
8563 #endif
8564