xdebug

next generation of mdebug (work in progress)
git clone http://frotz.net/git/xdebug.git
Log | Files | Refs | README

term.inl.c (9287B)


      1 enum {
      2 	T_ENTER_CA,
      3 	T_EXIT_CA,
      4 	T_SHOW_CURSOR,
      5 	T_HIDE_CURSOR,
      6 	T_CLEAR_SCREEN,
      7 	T_SGR0,
      8 	T_UNDERLINE,
      9 	T_BOLD,
     10 	T_BLINK,
     11 	T_REVERSE,
     12 	T_ENTER_KEYPAD,
     13 	T_EXIT_KEYPAD,
     14 	T_ENTER_MOUSE,
     15 	T_EXIT_MOUSE,
     16 	T_FUNCS_NUM,
     17 };
     18 
     19 #define ENTER_MOUSE_SEQ "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
     20 #define EXIT_MOUSE_SEQ "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
     21 
     22 #define EUNSUPPORTED_TERM -1
     23 
     24 // rxvt-256color
     25 static const char *rxvt_256color_keys[] = {
     26 	"\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
     27 };
     28 static const char *rxvt_256color_funcs[] = {
     29 	"\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
     30 };
     31 
     32 // Eterm
     33 static const char *eterm_keys[] = {
     34 	"\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
     35 };
     36 static const char *eterm_funcs[] = {
     37 	"\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "",
     38 };
     39 
     40 // screen
     41 static const char *screen_keys[] = {
     42 	"\033OP","\033OQ","\033OR","\033OS","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[1~","\033[4~","\033[5~","\033[6~","\033OA","\033OB","\033OD","\033OC", 0
     43 };
     44 static const char *screen_funcs[] = {
     45 	"\033[?1049h", "\033[?1049l", "\033[34h\033[?25h", "\033[?25l", "\033[H\033[J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
     46 };
     47 
     48 // rxvt-unicode
     49 static const char *rxvt_unicode_keys[] = {
     50 	"\033[11~","\033[12~","\033[13~","\033[14~","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[7~","\033[8~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
     51 };
     52 static const char *rxvt_unicode_funcs[] = {
     53 	"\033[?1049h", "\033[r\033[?1049l", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m\033(B", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
     54 };
     55 
     56 // linux
     57 static const char *linux_keys[] = {
     58 	"\033[[A","\033[[B","\033[[C","\033[[D","\033[[E","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033[1~","\033[4~","\033[5~","\033[6~","\033[A","\033[B","\033[D","\033[C", 0
     59 };
     60 static const char *linux_funcs[] = {
     61 	"", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J", "\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "",
     62 };
     63 
     64 // xterm
     65 static const char *xterm_keys[] = {
     66 	"\033OP","\033OQ","\033OR","\033OS","\033[15~","\033[17~","\033[18~","\033[19~","\033[20~","\033[21~","\033[23~","\033[24~","\033[2~","\033[3~","\033OH","\033OF","\033[5~","\033[6~","\033OA","\033OB","\033OD","\033OC", 0
     67 };
     68 static const char *xterm_funcs[] = {
     69 	"\033[?1049h", "\033[?1049l", "\033[?12l\033[?25h", "\033[?25l", "\033[H\033[2J", "\033(B\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
     70 };
     71 
     72 static struct term {
     73 	const char *name;
     74 	const char **keys;
     75 	const char **funcs;
     76 } terms[] = {
     77 	{"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs},
     78 	{"Eterm", eterm_keys, eterm_funcs},
     79 	{"screen", screen_keys, screen_funcs},
     80 	{"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs},
     81 	{"linux", linux_keys, linux_funcs},
     82 	{"xterm", xterm_keys, xterm_funcs},
     83 	{0, 0, 0},
     84 };
     85 
     86 static bool init_from_terminfo = false;
     87 static const char **keys;
     88 static const char **funcs;
     89 
     90 static int try_compatible(const char *term, const char *name,
     91 			  const char **tkeys, const char **tfuncs)
     92 {
     93 	if (strstr(term, name)) {
     94 		keys = tkeys;
     95 		funcs = tfuncs;
     96 		return 0;
     97 	}
     98 
     99 	return EUNSUPPORTED_TERM;
    100 }
    101 
    102 static int init_term_builtin(void)
    103 {
    104 	int i;
    105 	const char *term = getenv("TERM");
    106 
    107 	if (term) {
    108 		for (i = 0; terms[i].name; i++) {
    109 			if (!strcmp(terms[i].name, term)) {
    110 				keys = terms[i].keys;
    111 				funcs = terms[i].funcs;
    112 				return 0;
    113 			}
    114 		}
    115 
    116 		/* let's do some heuristic, maybe it's a compatible terminal */
    117 		if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0)
    118 			return 0;
    119 		if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0)
    120 			return 0;
    121 		if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0)
    122 			return 0;
    123 		if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0)
    124 			return 0;
    125 		if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0)
    126 			return 0;
    127 		if (try_compatible(term, "tmux", screen_keys, screen_funcs) == 0)
    128 			return 0;
    129 		/* let's assume that 'cygwin' is xterm compatible */
    130 		if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0)
    131 			return 0;
    132 	}
    133 
    134 	return EUNSUPPORTED_TERM;
    135 }
    136 
    137 //----------------------------------------------------------------------
    138 // terminfo
    139 //----------------------------------------------------------------------
    140 
    141 static char *read_file(const char *file) {
    142 	FILE *f = fopen(file, "rb");
    143 	if (!f)
    144 		return 0;
    145 
    146 	struct stat st;
    147 	if (fstat(fileno(f), &st) != 0) {
    148 		fclose(f);
    149 		return 0;
    150 	}
    151 
    152 	char *data = malloc(st.st_size);
    153 	if (!data) {
    154 		fclose(f);
    155 		return 0;
    156 	}
    157 
    158 	if (fread(data, 1, st.st_size, f) != (size_t)st.st_size) {
    159 		fclose(f);
    160 		free(data);
    161 		return 0;
    162 	}
    163 
    164 	fclose(f);
    165 	return data;
    166 }
    167 
    168 static char *terminfo_try_path(const char *path, const char *term) {
    169 	char tmp[4096];
    170 	snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
    171 	tmp[sizeof(tmp)-1] = '\0';
    172 	char *data = read_file(tmp);
    173 	if (data) {
    174 		return data;
    175 	}
    176 
    177 	// fallback to darwin specific dirs structure
    178 	snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
    179 	tmp[sizeof(tmp)-1] = '\0';
    180 	return read_file(tmp);
    181 }
    182 
    183 static char *load_terminfo(void) {
    184 	char tmp[4096];
    185 	const char *term = getenv("TERM");
    186 	if (!term) {
    187 		return 0;
    188 	}
    189 
    190 	// if TERMINFO is set, no other directory should be searched
    191 	const char *terminfo = getenv("TERMINFO");
    192 	if (terminfo) {
    193 		return terminfo_try_path(terminfo, term);
    194 	}
    195 
    196 	// next, consider ~/.terminfo
    197 	const char *home = getenv("HOME");
    198 	if (home) {
    199 		snprintf(tmp, sizeof(tmp), "%s/.terminfo", home);
    200 		tmp[sizeof(tmp)-1] = '\0';
    201 		char *data = terminfo_try_path(tmp, term);
    202 		if (data)
    203 			return data;
    204 	}
    205 
    206 	// next, TERMINFO_DIRS
    207 	const char *dirs = getenv("TERMINFO_DIRS");
    208 	if (dirs) {
    209 		snprintf(tmp, sizeof(tmp), "%s", dirs);
    210 		tmp[sizeof(tmp)-1] = '\0';
    211 		char *dir = strtok(tmp, ":");
    212 		while (dir) {
    213 			const char *cdir = dir;
    214 			if (strcmp(cdir, "") == 0) {
    215 				cdir = "/usr/share/terminfo";
    216 			}
    217 			char *data = terminfo_try_path(cdir, term);
    218 			if (data)
    219 				return data;
    220 			dir = strtok(0, ":");
    221 		}
    222 	}
    223 
    224 	// fallback to /usr/share/terminfo
    225 	return terminfo_try_path("/usr/share/terminfo", term);
    226 }
    227 
    228 #define TI_MAGIC 0432
    229 #define TI_ALT_MAGIC 542
    230 #define TI_HEADER_LENGTH 12
    231 #define TB_KEYS_NUM 22
    232 
    233 static const char *terminfo_copy_string(char *data, int str, int table) {
    234 	const int16_t off = *(int16_t*)(data + str);
    235 	const char *src = data + table + off;
    236 	int len = strlen(src);
    237 	char *dst = malloc(len+1);
    238 	strcpy(dst, src);
    239 	return dst;
    240 }
    241 
    242 static const int16_t ti_funcs[] = {
    243 	28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88,
    244 };
    245 
    246 static const int16_t ti_keys[] = {
    247 	66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69,
    248 	70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61,
    249 	79, 83,
    250 };
    251 
    252 static int init_term(void) {
    253 	int i;
    254 	char *data = load_terminfo();
    255 	if (!data) {
    256 		init_from_terminfo = false;
    257 		return init_term_builtin();
    258 	}
    259 
    260 	int16_t *header = (int16_t*)data;
    261 
    262 	const int number_sec_len = header[0] == TI_ALT_MAGIC ? 4 : 2;
    263 
    264 	if ((header[1] + header[2]) % 2) {
    265 		// old quirk to align everything on word boundaries
    266 		header[2] += 1;
    267 	}
    268 
    269 	const int str_offset = TI_HEADER_LENGTH +
    270 		header[1] + header[2] +	number_sec_len * header[3];
    271 	const int table_offset = str_offset + 2 * header[4];
    272 
    273 	keys = malloc(sizeof(const char*) * (TB_KEYS_NUM+1));
    274 	for (i = 0; i < TB_KEYS_NUM; i++) {
    275 		keys[i] = terminfo_copy_string(data,
    276 			str_offset + 2 * ti_keys[i], table_offset);
    277 	}
    278 	keys[TB_KEYS_NUM] = 0;
    279 
    280 	funcs = malloc(sizeof(const char*) * T_FUNCS_NUM);
    281 	// the last two entries are reserved for mouse. because the table offset is
    282 	// not there, the two entries have to fill in manually
    283 	for (i = 0; i < T_FUNCS_NUM-2; i++) {
    284 		funcs[i] = terminfo_copy_string(data,
    285 			str_offset + 2 * ti_funcs[i], table_offset);
    286 	}
    287 
    288 	funcs[T_FUNCS_NUM-2] = ENTER_MOUSE_SEQ;
    289 	funcs[T_FUNCS_NUM-1] = EXIT_MOUSE_SEQ;
    290 
    291 	init_from_terminfo = true;
    292 	free(data);
    293 	return 0;
    294 }
    295 
    296 static void shutdown_term(void) {
    297 	if (init_from_terminfo) {
    298 		int i;
    299 		for (i = 0; i < TB_KEYS_NUM; i++) {
    300 			free((void*)keys[i]);
    301 		}
    302 		// the last two entries are reserved for mouse. because the table offset
    303 		// is not there, the two entries have to fill in manually and do not
    304 		// need to be freed.
    305 		for (i = 0; i < T_FUNCS_NUM-2; i++) {
    306 			free((void*)funcs[i]);
    307 		}
    308 		free(keys);
    309 		free(funcs);
    310 	}
    311 }