|  | /* $Id: local.c,v 1.23 2007-11-26 20:36:30 nicm Exp $ */ | 
|  |  | 
|  | /* | 
|  | * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> | 
|  | * | 
|  | * Permission to use, copy, modify, and distribute this software for any | 
|  | * purpose with or without fee is hereby granted, provided that the above | 
|  | * copyright notice and this permission notice appear in all copies. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
|  | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
|  | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | 
|  | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
|  | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | 
|  | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | 
|  | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
|  | */ | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/ioctl.h> | 
|  | #include <sys/stat.h> | 
|  |  | 
|  | #include <curses.h> | 
|  | #include <fcntl.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #define TTYDEFCHARS | 
|  | /* glibc requires unistd.h before termios.h for TTYDEFCHARS. */ | 
|  | #include <unistd.h> | 
|  | #include <termios.h> | 
|  | #include <term.h> | 
|  |  | 
|  | #include "tmux.h" | 
|  |  | 
|  | /* | 
|  | * Functions to translate input and write output to the local client terminal. | 
|  | * This file should probably be called tty or terminal.c. | 
|  | */ | 
|  |  | 
|  | int	local_cmp(const void *, const void *); | 
|  | int	local_putc(int); | 
|  | void	local_putp(const char *); | 
|  | void	local_attributes(u_char, u_char); | 
|  | u_char	local_translate_acs(u_char); | 
|  |  | 
|  | /* Local key types and key codes. */ | 
|  | struct local_key { | 
|  | const char	*name; | 
|  | char		*string; | 
|  | size_t		 size; | 
|  | int	 	 code; | 
|  | }; | 
|  | struct local_key local_keys[] = { | 
|  | { "ka1",   NULL, 0, KEYC_A1 }, | 
|  | { "ka3",   NULL, 0, KEYC_A3 }, | 
|  | { "kb2",   NULL, 0, KEYC_B2 }, | 
|  | { "kbeg",  NULL, 0, KEYC_BEG }, | 
|  | { "kcbt",  NULL, 0, KEYC_BTAB }, | 
|  | { "kc1",   NULL, 0, KEYC_C1 }, | 
|  | { "kc3",   NULL, 0, KEYC_C3 }, | 
|  | { "kcan",  NULL, 0, KEYC_CANCEL }, | 
|  | { "ktbc",  NULL, 0, KEYC_CATAB }, | 
|  | { "kclr",  NULL, 0, KEYC_CLEAR }, | 
|  | { "kclo",  NULL, 0, KEYC_CLOSE }, | 
|  | { "kcmd",  NULL, 0, KEYC_COMMAND }, | 
|  | { "kcpy",  NULL, 0, KEYC_COPY }, | 
|  | { "kcrt",  NULL, 0, KEYC_CREATE }, | 
|  | { "kctab", NULL, 0, KEYC_CTAB }, | 
|  | { "kdch1", NULL, 0, KEYC_DC }, | 
|  | { "kdl1",  NULL, 0, KEYC_DL }, | 
|  | { "kcud1", NULL, 0, KEYC_DOWN }, | 
|  | { "krmir", NULL, 0, KEYC_EIC }, | 
|  | { "kend",  NULL, 0, KEYC_END }, | 
|  | { "kent",  NULL, 0, KEYC_ENTER }, | 
|  | { "kel",   NULL, 0, KEYC_EOL }, | 
|  | { "ked",   NULL, 0, KEYC_EOS }, | 
|  | { "kext",  NULL, 0, KEYC_EXIT }, | 
|  | { "kf0",   NULL, 0, KEYC_F0 }, | 
|  | { "kf1",   NULL, 0, KEYC_F1 }, | 
|  | { "kf10",  NULL, 0, KEYC_F10 }, | 
|  | { "kf11",  NULL, 0, KEYC_F11 }, | 
|  | { "kf12",  NULL, 0, KEYC_F12 }, | 
|  | { "kf13",  NULL, 0, KEYC_F13 }, | 
|  | { "kf14",  NULL, 0, KEYC_F14 }, | 
|  | { "kf15",  NULL, 0, KEYC_F15 }, | 
|  | { "kf16",  NULL, 0, KEYC_F16 }, | 
|  | { "kf17",  NULL, 0, KEYC_F17 }, | 
|  | { "kf18",  NULL, 0, KEYC_F18 }, | 
|  | { "kf19",  NULL, 0, KEYC_F19 }, | 
|  | { "kf2",   NULL, 0, KEYC_F2 }, | 
|  | { "kf20",  NULL, 0, KEYC_F20 }, | 
|  | { "kf21",  NULL, 0, KEYC_F21 }, | 
|  | { "kf22",  NULL, 0, KEYC_F22 }, | 
|  | { "kf23",  NULL, 0, KEYC_F23 }, | 
|  | { "kf24",  NULL, 0, KEYC_F24 }, | 
|  | { "kf25",  NULL, 0, KEYC_F25 }, | 
|  | { "kf26",  NULL, 0, KEYC_F26 }, | 
|  | { "kf27",  NULL, 0, KEYC_F27 }, | 
|  | { "kf28",  NULL, 0, KEYC_F28 }, | 
|  | { "kf29",  NULL, 0, KEYC_F29 }, | 
|  | { "kf3",   NULL, 0, KEYC_F3 }, | 
|  | { "kf30",  NULL, 0, KEYC_F30 }, | 
|  | { "kf31",  NULL, 0, KEYC_F31 }, | 
|  | { "kf32",  NULL, 0, KEYC_F32 }, | 
|  | { "kf33",  NULL, 0, KEYC_F33 }, | 
|  | { "kf34",  NULL, 0, KEYC_F34 }, | 
|  | { "kf35",  NULL, 0, KEYC_F35 }, | 
|  | { "kf36",  NULL, 0, KEYC_F36 }, | 
|  | { "kf37",  NULL, 0, KEYC_F37 }, | 
|  | { "kf38",  NULL, 0, KEYC_F38 }, | 
|  | { "kf39",  NULL, 0, KEYC_F39 }, | 
|  | { "kf4",   NULL, 0, KEYC_F4 }, | 
|  | { "kf40",  NULL, 0, KEYC_F40 }, | 
|  | { "kf41",  NULL, 0, KEYC_F41 }, | 
|  | { "kf42",  NULL, 0, KEYC_F42 }, | 
|  | { "kf43",  NULL, 0, KEYC_F43 }, | 
|  | { "kf44",  NULL, 0, KEYC_F44 }, | 
|  | { "kf45",  NULL, 0, KEYC_F45 }, | 
|  | { "kf46",  NULL, 0, KEYC_F46 }, | 
|  | { "kf47",  NULL, 0, KEYC_F47 }, | 
|  | { "kf48",  NULL, 0, KEYC_F48 }, | 
|  | { "kf49",  NULL, 0, KEYC_F49 }, | 
|  | { "kf5",   NULL, 0, KEYC_F5 }, | 
|  | { "kf50",  NULL, 0, KEYC_F50 }, | 
|  | { "kf51",  NULL, 0, KEYC_F51 }, | 
|  | { "kf52",  NULL, 0, KEYC_F52 }, | 
|  | { "kf53",  NULL, 0, KEYC_F53 }, | 
|  | { "kf54",  NULL, 0, KEYC_F54 }, | 
|  | { "kf55",  NULL, 0, KEYC_F55 }, | 
|  | { "kf56",  NULL, 0, KEYC_F56 }, | 
|  | { "kf57",  NULL, 0, KEYC_F57 }, | 
|  | { "kf58",  NULL, 0, KEYC_F58 }, | 
|  | { "kf59",  NULL, 0, KEYC_F59 }, | 
|  | { "kf6",   NULL, 0, KEYC_F6 }, | 
|  | { "kf60",  NULL, 0, KEYC_F60 }, | 
|  | { "kf61",  NULL, 0, KEYC_F61 }, | 
|  | { "kf62",  NULL, 0, KEYC_F62 }, | 
|  | { "kf63",  NULL, 0, KEYC_F63 }, | 
|  | { "kf7",   NULL, 0, KEYC_F7 }, | 
|  | { "kf8",   NULL, 0, KEYC_F8 }, | 
|  | { "kf9",   NULL, 0, KEYC_F9 }, | 
|  | { "kfnd",  NULL, 0, KEYC_FIND }, | 
|  | { "khlp",  NULL, 0, KEYC_HELP }, | 
|  | { "khome", NULL, 0, KEYC_HOME }, | 
|  | { "kich1", NULL, 0, KEYC_IC }, | 
|  | { "kil1",  NULL, 0, KEYC_IL }, | 
|  | { "kcub1", NULL, 0, KEYC_LEFT }, | 
|  | { "kll",   NULL, 0, KEYC_LL }, | 
|  | { "kmrk",  NULL, 0, KEYC_MARK }, | 
|  | { "kmsg",  NULL, 0, KEYC_MESSAGE }, | 
|  | { "kmov",  NULL, 0, KEYC_MOVE }, | 
|  | { "knxt",  NULL, 0, KEYC_NEXT }, | 
|  | { "knp",   NULL, 0, KEYC_NPAGE }, | 
|  | { "kopn",  NULL, 0, KEYC_OPEN }, | 
|  | { "kopt",  NULL, 0, KEYC_OPTIONS }, | 
|  | { "kpp",   NULL, 0, KEYC_PPAGE }, | 
|  | { "kprv",  NULL, 0, KEYC_PREVIOUS }, | 
|  | { "kprt",  NULL, 0, KEYC_PRINT }, | 
|  | { "krdo",  NULL, 0, KEYC_REDO }, | 
|  | { "kref",  NULL, 0, KEYC_REFERENCE }, | 
|  | { "krfr",  NULL, 0, KEYC_REFRESH }, | 
|  | { "krpl",  NULL, 0, KEYC_REPLACE }, | 
|  | { "krst",  NULL, 0, KEYC_RESTART }, | 
|  | { "kres",  NULL, 0, KEYC_RESUME }, | 
|  | { "kcuf1", NULL, 0, KEYC_RIGHT }, | 
|  | { "ksav",  NULL, 0, KEYC_SAVE }, | 
|  | { "kBEG",  NULL, 0, KEYC_SBEG }, | 
|  | { "kCAN",  NULL, 0, KEYC_SCANCEL }, | 
|  | { "kCMD",  NULL, 0, KEYC_SCOMMAND }, | 
|  | { "kCPY",  NULL, 0, KEYC_SCOPY }, | 
|  | { "kCRT",  NULL, 0, KEYC_SCREATE }, | 
|  | { "kDC",   NULL, 0, KEYC_SDC }, | 
|  | { "kDL",   NULL, 0, KEYC_SDL }, | 
|  | { "kslt",  NULL, 0, KEYC_SELECT }, | 
|  | { "kEND",  NULL, 0, KEYC_SEND }, | 
|  | { "kEOL",  NULL, 0, KEYC_SEOL }, | 
|  | { "kEXT",  NULL, 0, KEYC_SEXIT }, | 
|  | { "kind",  NULL, 0, KEYC_SF }, | 
|  | { "kFND",  NULL, 0, KEYC_SFIND }, | 
|  | { "kHLP",  NULL, 0, KEYC_SHELP }, | 
|  | { "kHOM",  NULL, 0, KEYC_SHOME }, | 
|  | { "kIC",   NULL, 0, KEYC_SIC }, | 
|  | { "kLFT",  NULL, 0, KEYC_SLEFT }, | 
|  | { "kMSG",  NULL, 0, KEYC_SMESSAGE }, | 
|  | { "kMOV",  NULL, 0, KEYC_SMOVE }, | 
|  | { "kNXT",  NULL, 0, KEYC_SNEXT }, | 
|  | { "kOPT",  NULL, 0, KEYC_SOPTIONS }, | 
|  | { "kPRV",  NULL, 0, KEYC_SPREVIOUS }, | 
|  | { "kPRT",  NULL, 0, KEYC_SPRINT }, | 
|  | { "kri",   NULL, 0, KEYC_SR }, | 
|  | { "kRDO",  NULL, 0, KEYC_SREDO }, | 
|  | { "kRPL",  NULL, 0, KEYC_SREPLACE }, | 
|  | { "kRIT",  NULL, 0, KEYC_SRIGHT }, | 
|  | { "kRES",  NULL, 0, KEYC_SRSUME }, | 
|  | { "kSAV",  NULL, 0, KEYC_SSAVE }, | 
|  | { "kSPD",  NULL, 0, KEYC_SSUSPEND }, | 
|  | { "khts",  NULL, 0, KEYC_STAB }, | 
|  | { "kUND",  NULL, 0, KEYC_SUNDO }, | 
|  | { "kspd",  NULL, 0, KEYC_SUSPEND }, | 
|  | { "kund",  NULL, 0, KEYC_UNDO }, | 
|  | { "kcuu1", NULL, 0, KEYC_UP }, | 
|  | { "pmous", NULL, 0, KEYC_MOUSE }, | 
|  | { NULL,    NULL, 0, KEYC_NONE } | 
|  | }; | 
|  |  | 
|  | /* tty file descriptor and local terminal buffers. */ | 
|  | int		 local_fd = -1; | 
|  | int		 local_log = -1; | 
|  | struct buffer	*local_in; | 
|  | struct buffer	*local_out; | 
|  | struct termios	 local_tio; | 
|  | u_char		 local_attr; | 
|  | u_char		 local_colr; | 
|  |  | 
|  | /* Initialise local terminal. */ | 
|  | int | 
|  | local_init(struct buffer **in, struct buffer **out) | 
|  | { | 
|  | char		       *tty, *path; | 
|  | const char	       *name; | 
|  | int		  	mode, error; | 
|  | struct termios	  	tio; | 
|  | struct local_key       *lk; | 
|  | u_int			i, j; | 
|  | static const char *const reqd[] = { | 
|  | "carriage_return", | 
|  | "change_scroll_region", | 
|  | "clear_screen", | 
|  | "clr_bol", | 
|  | "clr_eol", | 
|  | "cursor_address", | 
|  | "cursor_left", | 
|  | "cursor_down", | 
|  | "parm_dch", | 
|  | "parm_delete_line", | 
|  | "parm_down_cursor", | 
|  | "parm_ich", | 
|  | "parm_insert_line", | 
|  | "parm_left_cursor", | 
|  | "parm_right_cursor", | 
|  | "parm_up_cursor", | 
|  | "scroll_reverse", | 
|  | NULL | 
|  | }; | 
|  |  | 
|  | if ((tty = ttyname(STDIN_FILENO)) == NULL) | 
|  | fatal("ttyname failed"); | 
|  | if ((local_fd = open(tty, O_RDWR)) == -1) | 
|  | fatal("open failed"); | 
|  | if ((mode = fcntl(local_fd, F_GETFL)) == -1) | 
|  | fatal("fcntl failed"); | 
|  | if (fcntl(local_fd, F_SETFL, mode|O_NONBLOCK) == -1) | 
|  | fatal("fcntl failed"); | 
|  |  | 
|  | if ((name = getenv("TERM")) == NULL || *name == '\0') | 
|  | name = "unknown"; | 
|  | if (newterm(name, stdout, stdin) == NULL) { | 
|  | log_warnx("error opening terminal: %s", name); | 
|  | return (-1); | 
|  | } | 
|  | for (i = 0; reqd[i] != NULL; i++) { | 
|  | error = 0; | 
|  |  | 
|  | for (j = 0; strfnames[j] != NULL; j++) { | 
|  | if (strcmp(strfnames[j], reqd[i]) == 0) { | 
|  | if (strcodes[j] == NULL) | 
|  | error = -1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (error != -1) { | 
|  | for (j = 0; numfnames[j] != NULL; j++) { | 
|  | if (strcmp(numfnames[j], reqd[i]) == 0) { | 
|  | if (numcodes[j] == NULL) | 
|  | error = -1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (error != -1) { | 
|  | for (j = 0; boolfnames[j] != NULL; j++) { | 
|  | if (strcmp(boolfnames[j], reqd[i]) == 0) { | 
|  | if (boolcodes[j] == NULL) | 
|  | error = -1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (error == -1) { | 
|  | log_warnx("required capability missing: %s", reqd[i]); | 
|  | return (-1); | 
|  | } | 
|  | } | 
|  |  | 
|  | *in = local_in = buffer_create(BUFSIZ); | 
|  | *out = local_out = buffer_create(BUFSIZ); | 
|  |  | 
|  | if (tcgetattr(local_fd, &local_tio) != 0) | 
|  | fatal("tcgetattr failed"); | 
|  | memset(&tio, 0, sizeof tio); | 
|  | tio.c_iflag = TTYDEF_IFLAG & ~(IXON|IXOFF|ICRNL|INLCR); | 
|  | tio.c_oflag = TTYDEF_OFLAG & ~(OPOST|ONLCR|OCRNL|ONLRET); | 
|  | tio.c_lflag = | 
|  | TTYDEF_LFLAG & ~(IEXTEN|ICANON|ECHO|ECHOE|ECHOKE|ECHOCTL|ISIG); | 
|  | tio.c_cflag = TTYDEF_CFLAG; | 
|  | memcpy(&tio.c_cc, ttydefchars, sizeof tio.c_cc); | 
|  | cfsetspeed(&tio, TTYDEF_SPEED); | 
|  | if (tcsetattr(local_fd, TCSANOW, &tio) != 0) | 
|  | fatal("tcsetattr failed"); | 
|  |  | 
|  | if (enter_ca_mode != NULL) | 
|  | local_putp(enter_ca_mode); | 
|  | if (keypad_xmit != NULL) | 
|  | local_putp(keypad_xmit); | 
|  | if (ena_acs != NULL) | 
|  | local_putp(ena_acs); | 
|  | local_putp(clear_screen); | 
|  |  | 
|  | for (lk = local_keys; lk->name != NULL; lk++) { | 
|  | lk->string = tigetstr(lk->name); | 
|  | if (lk->string == (char *) -1 || lk->string == (char *) 0) | 
|  | lk->string = NULL; | 
|  | else { | 
|  | lk->size = strlen(lk->string); | 
|  | log_debug("string for %s (%d): \"%s\", length %zu", | 
|  | lk->name, lk->code, lk->string, lk->size); | 
|  | } | 
|  | } | 
|  | qsort(local_keys, sizeof local_keys / | 
|  | sizeof local_keys[0], sizeof local_keys[0], local_cmp); | 
|  |  | 
|  | local_attr = 0; | 
|  | local_colr = 0x88; | 
|  |  | 
|  | if (debug_level > 2) { | 
|  | xasprintf( | 
|  | &path, "%s-output-%ld.log", __progname,(long) getpid()); | 
|  | local_log = open( | 
|  | path, O_RDWR|O_APPEND|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); | 
|  | xfree(path); | 
|  | } | 
|  |  | 
|  | return (local_fd); | 
|  | } | 
|  |  | 
|  | /* Compare keys. */ | 
|  | int | 
|  | local_cmp(const void *ptr1, const void *ptr2) | 
|  | { | 
|  | const struct local_key *lk1 = ptr1, *lk2 = ptr2; | 
|  |  | 
|  | if (lk1->string == NULL && lk2->string == NULL) | 
|  | return (0); | 
|  | if (lk1->string == NULL) | 
|  | return (1); | 
|  | if (lk2->string == NULL) | 
|  | return (-1); | 
|  | return (lk2->size - lk1->size); | 
|  | } | 
|  |  | 
|  | /* Tidy up and reset local terminal. */ | 
|  | void | 
|  | local_done(void) | 
|  | { | 
|  | struct winsize	ws; | 
|  |  | 
|  | buffer_destroy(local_in); | 
|  | buffer_destroy(local_out); | 
|  |  | 
|  | if (tcsetattr(local_fd, TCSANOW, &local_tio) != 0) | 
|  | fatal("tcsetattr failed"); | 
|  | close(local_fd); | 
|  |  | 
|  | if (local_log != -1) | 
|  | close(local_log); | 
|  |  | 
|  | if (change_scroll_region != NULL) { | 
|  | if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) | 
|  | fatal("ioctl(TIOCGWINSZ)"); | 
|  | putp(tparm(change_scroll_region, 0, ws.ws_row - 1)); | 
|  | } | 
|  |  | 
|  | endwin(); | 
|  | if (keypad_local != NULL) | 
|  | putp(keypad_local);	/* not local_putp */ | 
|  | if (exit_ca_mode != NULL) | 
|  | putp(exit_ca_mode); | 
|  | putp(clear_screen); | 
|  | if (cursor_normal != NULL) | 
|  | putp(cursor_normal); | 
|  | if (exit_attribute_mode != NULL) | 
|  | putp(exit_attribute_mode); | 
|  | } | 
|  |  | 
|  | /* Put a character. Used as parameter to tputs. */ | 
|  | int | 
|  | local_putc(int c) | 
|  | { | 
|  | u_char	ch = c; | 
|  |  | 
|  | if (c < 0 || c > (int) UCHAR_MAX) | 
|  | fatalx("invalid character"); | 
|  |  | 
|  | if (local_log != -1) | 
|  | write(local_log, &ch, 1); | 
|  | if (acs_chars != NULL && local_attr & ATTR_DRAWING) { | 
|  | ch = local_translate_acs(ch); | 
|  | if (ch == '\0') | 
|  | ch = '?'; | 
|  | } | 
|  |  | 
|  | buffer_write(local_out, &ch, 1); | 
|  | return (c); | 
|  | } | 
|  |  | 
|  | /* Put terminfo string. */ | 
|  | void | 
|  | local_putp(const char *s) | 
|  | { | 
|  | if (s == NULL) | 
|  | fatalx("null pointer"); | 
|  |  | 
|  | tputs(s, 1, local_putc); | 
|  | } | 
|  |  | 
|  | /* Return waiting keys if any. */ | 
|  | int | 
|  | local_key(void) | 
|  | { | 
|  | struct local_key	*lk; | 
|  | u_int			 i; | 
|  | size_t			 size; | 
|  |  | 
|  | size = BUFFER_USED(local_in); | 
|  | if (size == 0) | 
|  | return (KEYC_NONE); | 
|  |  | 
|  | i = 0; | 
|  | lk = local_keys; | 
|  | while (lk->string != NULL) { | 
|  | if (strncmp(BUFFER_OUT(local_in), lk->string, size) == 0) { | 
|  | if (size < lk->size) | 
|  | return (KEYC_NONE); | 
|  | log_debug("got key: " | 
|  | "%s %d \"%s\"", lk->name, lk->code, lk->string); | 
|  | buffer_remove(local_in, lk->size); | 
|  | return (lk->code); | 
|  | } | 
|  |  | 
|  | i++; | 
|  | lk = local_keys + i; | 
|  | } | 
|  |  | 
|  | return (input_extract8(local_in)); | 
|  | } | 
|  |  | 
|  | /* Display output data. */ | 
|  | void | 
|  | local_output(struct buffer *b, size_t size) | 
|  | { | 
|  | u_char		 ch; | 
|  | uint16_t	 ua, ub; | 
|  |  | 
|  | while (size != 0) { | 
|  | if (size < 1) | 
|  | break; | 
|  | size--; | 
|  | ch = input_extract8(b); | 
|  | if (ch != '\e') { | 
|  | switch (ch) { | 
|  | case '\n':	/* LF */ | 
|  | local_putp(cursor_down); | 
|  | break; | 
|  | case '\r':	/* CR */ | 
|  | local_putp(carriage_return); | 
|  | break; | 
|  | case '\007':	/* BEL */ | 
|  | if (bell != NULL) | 
|  | local_putp(bell); | 
|  | break; | 
|  | case '\010':	/* BS */ | 
|  | local_putp(cursor_left); | 
|  | break; | 
|  | default: | 
|  | local_putc(ch); | 
|  | break; | 
|  | } | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (size < 1) | 
|  | fatalx("underflow"); | 
|  | size--; | 
|  | ch = input_extract8(b); | 
|  |  | 
|  | log_debug("received code %hhu", ch); | 
|  | switch (ch) { | 
|  | case CODE_CURSORUP: | 
|  | if (size < 2) | 
|  | fatalx("CODE_CURSORUP underflow"); | 
|  | size -= 2; | 
|  | ua = input_extract16(b); | 
|  | local_putp(tparm(parm_up_cursor, ua)); | 
|  | break; | 
|  | case CODE_CURSORDOWN: | 
|  | if (size < 2) | 
|  | fatalx("CODE_CURSORDOWN underflow"); | 
|  | size -= 2; | 
|  | ua = input_extract16(b); | 
|  | local_putp(tparm(parm_down_cursor, ua)); | 
|  | break; | 
|  | case CODE_CURSORRIGHT: | 
|  | if (size < 2) | 
|  | fatalx("CODE_CURSORRIGHT underflow"); | 
|  | size -= 2; | 
|  | ua = input_extract16(b); | 
|  | local_putp(tparm(parm_right_cursor, ua)); | 
|  | break; | 
|  | case CODE_CURSORLEFT: | 
|  | if (size < 2) | 
|  | fatalx("CODE_CURSORLEFT underflow"); | 
|  | size -= 2; | 
|  | ua = input_extract16(b); | 
|  | local_putp(tparm(parm_left_cursor, ua)); | 
|  | break; | 
|  | case CODE_CURSORMOVE: | 
|  | if (size < 4) | 
|  | fatalx("CODE_CURSORMOVE underflow"); | 
|  | size -= 4; | 
|  | ua = input_extract16(b); | 
|  | ub = input_extract16(b); | 
|  | local_putp(tparm(cursor_address, ua - 1, ub - 1)); | 
|  | break; | 
|  | case CODE_CLEARENDOFLINE: | 
|  | local_putp(clr_eol); | 
|  | break; | 
|  | case CODE_CLEARSTARTOFLINE: | 
|  | local_putp(clr_bol); | 
|  | break; | 
|  | case CODE_CLEARLINE: | 
|  | local_putp(clr_eol);	/* XXX */ | 
|  | break; | 
|  | case CODE_INSERTLINE: | 
|  | if (size < 2) | 
|  | fatalx("CODE_INSERTLINE underflow"); | 
|  | size -= 2; | 
|  | ua = input_extract16(b); | 
|  | local_putp(tparm(parm_insert_line, ua)); | 
|  | break; | 
|  | case CODE_DELETELINE: | 
|  | if (size < 2) | 
|  | fatalx("CODE_DELETELINE underflow"); | 
|  | size -= 2; | 
|  | ua = input_extract16(b); | 
|  | local_putp(tparm(parm_delete_line, ua)); | 
|  | break; | 
|  | case CODE_INSERTCHARACTER: | 
|  | if (size < 2) | 
|  | fatalx("CODE_INSERTCHARACTER underflow"); | 
|  | size -= 2; | 
|  | ua = input_extract16(b); | 
|  | local_putp(tparm(parm_ich, ua)); | 
|  | break; | 
|  | case CODE_DELETECHARACTER: | 
|  | if (size < 2) | 
|  | fatalx("CODE_DELETECHARACTER underflow"); | 
|  | size -= 2; | 
|  | ua = input_extract16(b); | 
|  | local_putp(tparm(parm_dch, ua)); | 
|  | break; | 
|  | case CODE_CURSORON: | 
|  | if (cursor_normal != NULL) | 
|  | local_putp(cursor_normal); | 
|  | break; | 
|  | case CODE_CURSOROFF: | 
|  | if (cursor_invisible != NULL) | 
|  | local_putp(cursor_invisible); | 
|  | break; | 
|  | case CODE_REVERSEINDEX: | 
|  | local_putp(scroll_reverse); | 
|  | break; | 
|  | case CODE_SCROLLREGION: | 
|  | if (size < 4) | 
|  | fatalx("CODE_SCROLLREGION underflow"); | 
|  | size -= 4; | 
|  | ua = input_extract16(b); | 
|  | ub = input_extract16(b); | 
|  | local_putp(tparm(change_scroll_region, ua - 1, ub - 1)); | 
|  | break; | 
|  | case CODE_INSERTON: | 
|  | if (enter_insert_mode != NULL) | 
|  | local_putp(enter_insert_mode); | 
|  | break; | 
|  | case CODE_INSERTOFF: | 
|  | if (exit_insert_mode != NULL) | 
|  | local_putp(exit_insert_mode); | 
|  | break; | 
|  | case CODE_KCURSOROFF: | 
|  | /* | 
|  | t = tigetstr("CE"); | 
|  | if (t != (char *) 0 && t != (char *) -1) | 
|  | local_putp(t); | 
|  | */ | 
|  | break; | 
|  | case CODE_KCURSORON: | 
|  | /* | 
|  | t = tigetstr("CS"); | 
|  | if (t != (char *) 0 && t != (char *) -1) | 
|  | local_putp(t); | 
|  | */ | 
|  | break; | 
|  | case CODE_KKEYPADOFF: | 
|  | /* | 
|  | if (keypad_local != NULL) | 
|  | local_putp(keypad_local); | 
|  | */ | 
|  | break; | 
|  | case CODE_KKEYPADON: | 
|  | /* | 
|  | if (keypad_xmit != NULL) | 
|  | local_putp(keypad_xmit); | 
|  | */ | 
|  | break; | 
|  | case CODE_TITLE: | 
|  | if (size < 2) | 
|  | fatalx("CODE_TITLE underflow"); | 
|  | size -= 2; | 
|  | ua = input_extract16(b); | 
|  |  | 
|  | if (size < ua) | 
|  | fatalx("CODE_TITLE underflow"); | 
|  | size -= ua; | 
|  | buffer_remove(b, ua); | 
|  | break; | 
|  | case CODE_ATTRIBUTES: | 
|  | if (size < 4) | 
|  | fatalx("CODE_ATTRIBUTES underflow"); | 
|  | size -= 4; | 
|  | ua = input_extract16(b); | 
|  | ub = input_extract16(b); | 
|  |  | 
|  | local_attributes(ua, ub); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | local_attributes(u_char attr, u_char colr) | 
|  | { | 
|  | u_char	fg, bg; | 
|  |  | 
|  | if (attr == local_attr && colr == local_colr) | 
|  | return; | 
|  |  | 
|  | /* If any bits are being cleared, reset everything. */ | 
|  | if (local_attr & ~attr) { | 
|  | if ((local_attr & ATTR_DRAWING) && | 
|  | exit_alt_charset_mode != NULL) | 
|  | local_putp(exit_alt_charset_mode); | 
|  | local_putp(exit_attribute_mode); | 
|  | local_colr = 0x88; | 
|  | local_attr = 0; | 
|  | } | 
|  |  | 
|  | /* Filter out bits already set. */ | 
|  | attr &= ~local_attr; | 
|  | local_attr |= attr; | 
|  |  | 
|  | if ((attr & ATTR_BRIGHT) && enter_bold_mode != NULL) | 
|  | local_putp(enter_bold_mode); | 
|  | if ((attr & ATTR_DIM) && enter_dim_mode != NULL) | 
|  | local_putp(enter_dim_mode); | 
|  | if ((attr & ATTR_ITALICS) && enter_standout_mode != NULL) | 
|  | local_putp(enter_standout_mode); | 
|  | if ((attr & ATTR_UNDERSCORE) && enter_underline_mode != NULL) | 
|  | local_putp(enter_underline_mode); | 
|  | if ((attr & ATTR_BLINK) && enter_blink_mode != NULL) | 
|  | local_putp(enter_blink_mode); | 
|  | if ((attr & ATTR_REVERSE) && enter_reverse_mode != NULL) | 
|  | local_putp(enter_reverse_mode); | 
|  | if ((attr & ATTR_HIDDEN) && enter_secure_mode != NULL) | 
|  | local_putp(enter_secure_mode); | 
|  | if ((attr & ATTR_DRAWING) && enter_alt_charset_mode != NULL) | 
|  | local_putp(enter_alt_charset_mode); | 
|  |  | 
|  | fg = (colr >> 4) & 0xf; | 
|  | if (fg != ((local_colr >> 4) & 0xf)) { | 
|  | if (tigetflag("AX") == TRUE) { | 
|  | if (fg == 7) | 
|  | fg = 8; | 
|  | } else { | 
|  | if (fg == 8) | 
|  | fg = 7; | 
|  | } | 
|  |  | 
|  | if (fg == 8) | 
|  | local_putp("\e[39m"); | 
|  | else if (set_a_foreground != NULL) | 
|  | local_putp(tparm(set_a_foreground, fg)); | 
|  | } | 
|  |  | 
|  | bg = colr & 0xf; | 
|  | if (bg != (local_colr & 0xf)) { | 
|  | if (tigetflag("AX") == TRUE) { | 
|  | if (bg == 0) | 
|  | bg = 8; | 
|  | } else { | 
|  | if (bg == 8) | 
|  | bg = 0; | 
|  | } | 
|  |  | 
|  | if (bg == 8) | 
|  | local_putp("\e[49m"); | 
|  | else if (set_a_background != NULL) | 
|  | local_putp(tparm(set_a_background, bg)); | 
|  | } | 
|  |  | 
|  | local_colr = colr; | 
|  | } | 
|  |  | 
|  | u_char | 
|  | local_translate_acs(u_char ch) | 
|  | { | 
|  | switch (ch) { | 
|  | case '~': | 
|  | return (ACS_BULLET); | 
|  | case '}': | 
|  | return (ACS_STERLING); | 
|  | case '|': | 
|  | return (ACS_NEQUAL); | 
|  | case '{': | 
|  | return (ACS_PI); | 
|  | case 'z': | 
|  | return (ACS_GEQUAL); | 
|  | case 'y': | 
|  | return (ACS_LEQUAL); | 
|  | case 'x': | 
|  | return (ACS_VLINE); | 
|  | case 'w': | 
|  | return (ACS_TTEE); | 
|  | case 'v': | 
|  | return (ACS_BTEE); | 
|  | case 'u': | 
|  | return (ACS_RTEE); | 
|  | case 't': | 
|  | return (ACS_LTEE); | 
|  | case 's': | 
|  | return (ACS_S9); | 
|  | case 'r': | 
|  | return (ACS_S7); | 
|  | case 'q': | 
|  | return (ACS_HLINE); | 
|  | case 'p': | 
|  | return (ACS_S3); | 
|  | case 'o': | 
|  | return (ACS_S1); | 
|  | case 'n': | 
|  | return (ACS_PLUS); | 
|  | case 'm': | 
|  | return (ACS_LLCORNER); | 
|  | case 'l': | 
|  | return (ACS_ULCORNER); | 
|  | case 'k': | 
|  | return (ACS_URCORNER); | 
|  | case 'j': | 
|  | return (ACS_LRCORNER); | 
|  | case 'i': | 
|  | return (ACS_LANTERN); | 
|  | case 'h': | 
|  | return (ACS_BOARD); | 
|  | case 'g': | 
|  | return (ACS_PLMINUS); | 
|  | case 'f': | 
|  | return (ACS_DEGREE); | 
|  | case 'a': | 
|  | return (ACS_CKBOARD); | 
|  | case '`': | 
|  | return (ACS_DIAMOND); | 
|  | case '0': | 
|  | return (ACS_BLOCK); | 
|  | case '.': | 
|  | return (ACS_DARROW); | 
|  | case '-': | 
|  | return (ACS_UARROW); | 
|  | case ',': | 
|  | return (ACS_LARROW); | 
|  | case '+': | 
|  | return (ACS_RARROW); | 
|  | } | 
|  | return (ch); | 
|  | } |