enum { T_ENTER_CA, T_EXIT_CA, T_SHOW_CURSOR, T_HIDE_CURSOR, T_CLEAR_SCREEN, T_SGR0, T_UNDERLINE, T_BOLD, T_BLINK, T_REVERSE, T_ENTER_KEYPAD, T_EXIT_KEYPAD, T_ENTER_MOUSE, T_EXIT_MOUSE, T_ENTER_BRACKETED_PASTE, T_EXIT_BRACKETED_PASTE, T_FUNCS_NUM, }; #define EUNSUPPORTED_TERM -1 // rxvt-256color static const char *rxvt_256color_keys[] = { "\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 }; static const char *rxvt_256color_funcs[] = { "\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>", "\033[?1000h", "\033[?1000l", "\033[?2004h", "\033[?2004l", }; // Eterm static const char *eterm_keys[] = { "\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 }; static const char *eterm_funcs[] = { "\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", "", "", "", "", "", "", }; // screen static const char *screen_keys[] = { "\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 }; static const char *screen_funcs[] = { "\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>", "\033[?1000h", "\033[?1000l", "\033[?2004h", "\033[?2004l", }; // rxvt-unicode static const char *rxvt_unicode_keys[] = { "\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 }; static const char *rxvt_unicode_funcs[] = { "\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>", "\033[?1000h", "\033[?1000l", "\033[?2004h", "\033[?2004l", }; // linux static const char *linux_keys[] = { "\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 }; static const char *linux_funcs[] = { "", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J", "\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "", "", "", }; // xterm static const char *xterm_keys[] = { "\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 }; static const char *xterm_funcs[] = { "\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>", "\033[?1000h", "\033[?1000l", "\033[?2004h", "\033[?2004l", }; static struct term { const char *name; const char **keys; const char **funcs; } terms[] = { {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs}, {"Eterm", eterm_keys, eterm_funcs}, {"screen", screen_keys, screen_funcs}, {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs}, {"linux", linux_keys, linux_funcs}, {"xterm", xterm_keys, xterm_funcs}, {0, 0, 0}, }; static bool init_from_terminfo = false; static const char **keys; static const char **funcs; static int try_compatible(const char *term, const char *name, const char **tkeys, const char **tfuncs) { if (strstr(term, name)) { keys = tkeys; funcs = tfuncs; return 0; } return EUNSUPPORTED_TERM; } static int init_term_builtin(void) { int i; const char *term = getenv("TERM"); if (term) { for (i = 0; terms[i].name; i++) { if (!strcmp(terms[i].name, term)) { keys = terms[i].keys; funcs = terms[i].funcs; return 0; } } /* let's do some heuristic, maybe it's a compatible terminal */ if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0) return 0; if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0) return 0; if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0) return 0; if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0) return 0; if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0) return 0; /* let's assume that 'cygwin' is xterm compatible */ if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0) return 0; } return EUNSUPPORTED_TERM; } //---------------------------------------------------------------------- // terminfo //---------------------------------------------------------------------- static char *read_file(const char *file) { FILE *f = fopen(file, "rb"); if (!f) return 0; struct stat st; if (fstat(fileno(f), &st) != 0) { fclose(f); return 0; } char *data = malloc(st.st_size); if (!data) { fclose(f); return 0; } if (fread(data, 1, st.st_size, f) != (size_t)st.st_size) { fclose(f); free(data); return 0; } fclose(f); return data; } static char *terminfo_try_path(const char *path, const char *term) { char tmp[4096]; // snprintf guarantee for older compilers assert(sizeof(tmp) > sizeof(path)+sizeof("/x/")+sizeof(term)+1); snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term); char *data = read_file(tmp); if (data) { return data; } // fallback to darwin specific dirs structure // snprintf guarantee above still applies snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term); return read_file(tmp); } void string_copy(char* dest, const char* src, int dest_capacity) { strncpy(dest, src, dest_capacity); dest[dest_capacity-1] = '\0'; } void string_append(char* dest, const char* src, int dest_capacity) { strncat(dest, src, dest_capacity); dest[dest_capacity-1] = '\0'; } static char *load_terminfo(void) { char tmp[4096]; const char *term = getenv("TERM"); if (!term) { return 0; } // if TERMINFO is set, no other directory should be searched const char *terminfo = getenv("TERMINFO"); if (terminfo) { return terminfo_try_path(terminfo, term); } // next, consider ~/.terminfo const char *home = getenv("HOME"); if (home) { // snprintf guarantee for older compilers assert(sizeof(tmp) > sizeof(home)+sizeof("/.terminfo")+1); string_copy(tmp, home, sizeof(tmp)); string_append(tmp, "/.terminfo", sizeof(tmp)); char *data = terminfo_try_path(tmp, term);