/* TTY interface file Copyright (c) Tudor Hulubei & Andrei Pitis, May 1994 This file is part of UIT (UNIX Interactive Tools) UIT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. UIT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details . You should have received a copy of the GNU General Public License along with UIT; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include "tty.h" #include "termcap.h" #define INPUT 0 #define OUTPUT 1 int tty_kbdmode = 0; /* If tty_mode == 1 single characters are inserted in the linked list. This feature is used by uitps (no command line ... ) */ static struct termios oldterm; static struct termios newterm; static int tty_perm; static int rows, columns; static int fg_color, bg_color, br_status, rv_status; extern int LinuxConsole; struct key_struct *key_list_head = NULL; struct key_struct *current_key; struct key_struct default_key; void fatal(char *); static char colors[10][10] = { "BLACK", "RED", "GREEN", "YELLOW", "BLUE", "MAGENTA", "CYAN", "WHITE", "OFF", "ON" }; char key_ctrl_tbl[0x5f] = { 0x20, /* 0x20 ( ) */ 0x21, /* 0x21 (!) */ 0x22, /* 0x22 (") */ 0x23, /* 0x23 (#) */ 0x24, /* 0x24 ($) */ 0x25, /* 0x25 (%) */ 0x26, /* 0x26 (&) */ 0x07, /* 0x27 (') */ 0x28, /* 0x28 (() */ 0x29, /* 0x29 ()) */ 0x2a, /* 0x2a (*) */ 0x2b, /* 0x2b (+) */ 0x2c, /* 0x2c (,) */ 0x2d, /* 0x2d (-) */ 0x2e, /* 0x2e (.) */ 0x2f, /* 0x2f (/) */ 0x20, /* 0x30 (0) */ 0x20, /* 0x31 (1) */ 0x20, /* 0x32 (2) */ 0x1b, /* 0x33 (3) */ 0x1c, /* 0x34 (4) */ 0x1d, /* 0x35 (5) */ 0x1e, /* 0x36 (6) */ 0x1f, /* 0x37 (7) */ 0x7f, /* 0x38 (8) */ 0x39, /* 0x39 (9) */ 0x3a, /* 0x3a (:) */ 0x3b, /* 0x3b (;) */ 0x3c, /* 0x3c (<) */ 0x20, /* 0x3d (=) */ 0x3e, /* 0x3e (>) */ 0x20, /* 0x3f (?) */ 0x20, /* 0x40 (@) */ 0x01, /* 0x41 (A) */ 0x02, /* 0x42 (B) */ 0x03, /* 0x43 (C) */ 0x04, /* 0x44 (D) */ 0x05, /* 0x45 (E) */ 0x06, /* 0x46 (F) */ 0x07, /* 0x47 (G) */ 0x08, /* 0x48 (H) */ 0x09, /* 0x49 (I) */ 0x0a, /* 0x4a (J) */ 0x0b, /* 0x4b (K) */ 0x0c, /* 0x4c (L) */ 0x0d, /* 0x4d (M) */ 0x0e, /* 0x4e (N) */ 0x0f, /* 0x4f (O) */ 0x10, /* 0x50 (P) */ 0x11, /* 0x51 (Q) */ 0x12, /* 0x52 (R) */ 0x13, /* 0x53 (S) */ 0x14, /* 0x54 (T) */ 0x15, /* 0x55 (U) */ 0x16, /* 0x56 (V) */ 0x17, /* 0x57 (W) */ 0x18, /* 0x58 (X) */ 0x19, /* 0x59 (Y) */ 0x1a, /* 0x5a (Z) */ 0x1b, /* 0x5b ([) */ 0x1c, /* 0x5c (\) */ 0x1d, /* 0x5d (]) */ 0x5e, /* 0x5e (^) */ 0x1f, /* 0x5f (_) */ 0x20, /* 0x60 (`) */ 0x01, /* 0x61 (a) */ 0x02, /* 0x62 (b) */ 0x03, /* 0x63 (c) */ 0x04, /* 0x64 (d) */ 0x05, /* 0x65 (e) */ 0x06, /* 0x66 (f) */ 0x07, /* 0x67 (g) */ 0x08, /* 0x68 (h) */ 0x09, /* 0x69 (i) */ 0x0a, /* 0x6a (j) */ 0x0b, /* 0x6b (k) */ 0x0c, /* 0x6c (l) */ 0x0d, /* 0x6d (m) */ 0x0e, /* 0x6e (n) */ 0x0f, /* 0x6f (o) */ 0x10, /* 0x70 (p) */ 0x11, /* 0x71 (q) */ 0x12, /* 0x72 (r) */ 0x13, /* 0x73 (s) */ 0x14, /* 0x74 (t) */ 0x15, /* 0x75 (u) */ 0x16, /* 0x76 (v) */ 0x17, /* 0x77 (w) */ 0x18, /* 0x78 (x) */ 0x19, /* 0x79 (y) */ 0x1a, /* 0x7a (z) */ 0x20, /* 0x7b ({) */ 0x20, /* 0x7c (|) */ 0x20, /* 0x7d (}) */ 0x20 /* 0x7e (~) */ }; extern char termcap[TTY_MAXCAP][16]; extern char termcap_len[TTY_MAXCAP]; #define NO 0 #define YES 1 static int keyno = 0; static int keyindex = 0; static char keybuf[1024]; void tty_init(void) { tty_defaults(); tcgetattr(OUTPUT, &oldterm); newterm = oldterm; newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IGNBRK | BRKINT); newterm.c_oflag &= ~OPOST; newterm.c_lflag |= ISIG | NOFLSH; newterm.c_lflag &= ~(ICANON | ECHO); newterm.c_cc[VMIN] = 1; newterm.c_cc[VTIME] = 0; tcsetattr(OUTPUT, TCSADRAIN, &newterm); } void tty_end(void) { tcsetattr(OUTPUT, TCSADRAIN, &oldterm); tty_defaults(); } void tty_kbdinit(int mode) { default_key.key_seq = (char *)malloc(16); default_key.aux_data = NULL; default_key.next = NULL; tty_kbdmode = mode; } char *tty_key_convert(char *key_seq) { char *first, *second; first = second = key_seq; if (tty_kbdmode == 0 && *key_seq != '^') return NULL; while (*second) { if (*second == '^') if (*++second) *first++ = key_ctrl_tbl[(*second++ & 0x7F) - ' ']; else return NULL; else *first++ = *second++; } *first = 0; return key_seq; } void tty_clrscr(void) { write(OUTPUT, termcap[TTY_CLRSCR], termcap_len[TTY_CLRSCR]); } void tty_cursormove(int y, int x) { char msg[10]; sprintf(msg, "\33[%d;%dH", y + 1, x + 1); write(OUTPUT, msg, strlen(msg)); } void tty_foreground(int color) { #ifdef HAVE_LINUX char str[] = "\x1b\x5b\x33\x30\x6d"; if (LinuxConsole) { str[3] += (fg_color = color); write(OUTPUT, str, 5); } else tty_reverse((fg_color = color) != WHITE); #else tty_reverse((fg_color = color) != WHITE); #endif } void tty_background(int color) { #ifdef HAVE_LINUX char str[] = "\x1b\x5b\x34\x30\x6d"; if (LinuxConsole) { str[3] += (bg_color = color); write(OUTPUT, str, 5); } else tty_reverse((bg_color = color) != BLACK); #else tty_reverse((bg_color = color) != BLACK); #endif } void tty_bright(int status) { if (status == ON) write(OUTPUT, termcap[TTY_BRIGHT_ON], termcap_len[TTY_BRIGHT_ON]); else { #ifdef HAVE_LINUX if (LinuxConsole) write(OUTPUT, "\x1b\x5b\x32\x32\x6d", 5); else #endif { write(OUTPUT, termcap[TTY_BRIGHT_OFF],termcap_len[TTY_BRIGHT_OFF]); br_status = OFF; if (rv_status) tty_reverse(rv_status); } } br_status = status; } void tty_reverse(int status) { if (status == ON) write(OUTPUT, termcap[TTY_REVERSE_ON], termcap_len[TTY_REVERSE_ON]); else { write(OUTPUT, termcap[TTY_REVERSE_OFF], termcap_len[TTY_REVERSE_OFF]); rv_status = OFF; if (br_status) tty_bright(br_status); } rv_status = status; } void tty_beep(void) { char c = 7; write(OUTPUT, &c, 1); } void tty_save(tty_status *status) { status->fg_color = fg_color; status->bg_color = bg_color; status->br_status = br_status; status->rv_status = rv_status; } void tty_restore(tty_status *status) { tty_foreground(status->fg_color); tty_background(status->bg_color); tty_bright(status->br_status); tty_reverse(status->rv_status); } void tty_defaults(void) { #ifdef HAVE_LINUX if (LinuxConsole) write(OUTPUT, "\x1b\x5b\x30\x6d", 4); /* Linux console defaults */ else { write(OUTPUT, termcap[TTY_BRIGHT_OFF], termcap_len[TTY_BRIGHT_OFF]); write(OUTPUT, termcap[TTY_REVERSE_OFF], termcap_len[TTY_REVERSE_OFF]); } #else write(OUTPUT, termcap[TTY_BRIGHT_OFF], termcap_len[TTY_BRIGHT_OFF]); write(OUTPUT, termcap[TTY_REVERSE_OFF], termcap_len[TTY_REVERSE_OFF]); #endif fg_color = WHITE; bg_color = BLACK; br_status = rv_status = OFF; } int tty_write(char *buf, int length) { return write(OUTPUT, buf, length); } int tty_read(char *buf, int length) { return read(INPUT, buf, length); } char tty_getch(void) { if (keyno) return keybuf[keyno--, keyindex++]; for (keyindex = 0; (keyno = read(INPUT, keybuf, 1024)) < 0;); return keyno ? keybuf[keyno--, keyindex++] : 0; } void tty_putch(int c) { write(OUTPUT, &c, 1); } void tty_key_list_insert_sequence(struct key_struct **key, char *key_seq, void *aux_data) { struct key_struct *new_key; new_key = (struct key_struct *)malloc(sizeof(struct key_struct)); if (new_key == NULL) { fprintf(stderr, "not enough memory\n"); return; } new_key->key_seq = (char *)malloc(strlen(key_seq) + 1); if (new_key->key_seq == NULL) { fprintf(stderr, "not enough memory\n"); free(new_key); return; } strcpy(new_key->key_seq, key_seq); new_key->aux_data = aux_data; new_key->next = *key; *key = new_key; } void tty_key_list_insert(char *key_seq, void *aux_data) { struct key_struct **key; if (*key_seq == 0) return; /* bad key sequence ! */ for (key = &key_list_head; *key; key = &(*key)->next) if (strcmp(key_seq, (*key)->key_seq) <= 0) { tty_key_list_insert_sequence(key, key_seq, aux_data); return; } tty_key_list_insert_sequence(key, key_seq, aux_data); } struct key_struct *tty_key_search(char *key_seq) { int cmp; if (current_key == NULL) return NULL; for (; current_key; current_key = current_key->next) { cmp = strcmp(key_seq, current_key->key_seq); if (cmp == 0) return current_key; if (cmp < 0) break; } if (current_key == NULL || strncmp(key_seq, current_key->key_seq, strlen(key_seq))) return (struct key_struct*) - 1; else return NULL; } void tty_key_search_restart(void) { current_key = key_list_head; } void tty_key_list_delete(void) { struct key_struct *key, *next_key; for (key = key_list_head; key; key = next_key) { next_key = key->next; free(key->key_seq); free(key); } } struct key_struct *tty_getkey(int *repeat_count) { int i, j; struct key_struct *key; char c, key_seq[16]; restart: while ((c = tty_getch()) == 0); if (repeat_count) *repeat_count = 1; if (!tty_kbdmode) { if (c == '\n' || c == '\r') { default_key.key_seq[0] = KEY_ENTER; default_key.key_seq[1] = 0; return &default_key; } if (is_print(c) || c == KEY_TAB || c == KEY_BACKSPACE) { default_key.key_seq[0] = c; default_key.key_seq[1] = 0; return &default_key; } } tty_key_search_restart(); for (j = 0; j < 15; j++) { key_seq[j ] = c; key_seq[j + 1] = 0; key = tty_key_search(key_seq); if (key == (struct key_struct *)-1) goto restart; if (key) break; while ((c = tty_getch()) == 0); } if (repeat_count) while (keyno > j && (memcmp(key_seq, &keybuf[keyindex], j + 1) == 0)) { keyindex += j + 1; keyno -= j + 1; (*repeat_count)++; } return key; } void tty_getsize(int *_columns, int *_rows) { char *env; struct winsize winsz; int temp_rows, temp_columns; columns = rows = temp_columns = temp_rows = 0; ioctl(OUTPUT, TIOCGWINSZ, &winsz); if (winsz.ws_col && winsz.ws_row) { temp_columns = winsz.ws_col; temp_rows = winsz.ws_row; } if (env = getenv("COLUMNS")) sscanf(env, "%d", &columns); if (env = getenv("LINES")) sscanf(env, "%d", &rows); if (columns < 80) columns = temp_columns; if (rows < 10) rows = temp_rows; if (columns < 80 || columns > 200) columns = 80; if (rows < 10 || rows > 128) rows = 24; *_columns = columns; *_rows = rows; } void tty_getscreen(char *buf) { #ifdef HAVE_LINUX int x, y, index, len; char *tty_name; if (LinuxConsole) { tty_name = ttyname(1); len = strlen(tty_name); if (tty_name[len - 4] != 't' || tty_name[len - 3] != 't' || tty_name[len - 2] != 'y' || !isdigit(tty_name[len - 1])) { tty_perm = 0; return; } buf[0] = 0; buf[1] = tty_name[len - 1] - '0'; if (ioctl(OUTPUT, TIOCLINUX, buf) == -1) { buf[0] = buf[1] = 0; /* Linux bug: bad ioctl on console 8 */ if (ioctl(OUTPUT, TIOCLINUX, buf) == -1) { tty_perm = 0; return; } } tty_perm = 1; index = 2 + rows * columns; for (y = rows - 1; y >= 0; y--) for (x = columns - 1; x >= 0; x--) if (buf[--index] != ' ') goto found; found: buf[0] = y + 1; buf[1] = 0; tty_cursormove(y + 1, 0); } #endif } void tty_putscreen(char *buf) { tty_defaults(); tty_cursormove(0, 0); #ifdef HAVE_LINUX if (LinuxConsole) { if (tty_perm) { write(OUTPUT, buf + 2, rows * columns); tty_cursormove(buf[0], buf[1]); } else { tty_defaults(); tty_clrscr(); } } else { tty_defaults(); tty_clrscr(); } #else tty_defaults(); tty_clrscr(); #endif } int tty_getcolorindex(char *colorname) { int i; for (i = 0; i < 10; i++) if (strcmp(colors[i], colorname) == 0) return (i < 8) ? i : (i - 8); return -1; }