119304Speter/*- 219304Speter * Copyright (c) 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 1319304Speterstatic const char sccsid[] = "@(#)cl_term.c 10.22 (Berkeley) 9/15/96"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/ioctl.h> 1819304Speter#include <sys/queue.h> 1919304Speter#include <sys/stat.h> 2019304Speter 2119304Speter#include <bitstring.h> 2219304Speter#include <curses.h> 2319304Speter#include <errno.h> 2419304Speter#include <limits.h> 2519304Speter#include <signal.h> 2619304Speter#include <stdio.h> 2719304Speter#include <stdlib.h> 2819304Speter#include <string.h> 2919304Speter#include <termios.h> 3019304Speter#include <unistd.h> 3119304Speter 3219304Speter#include "../common/common.h" 3319304Speter#include "cl.h" 3419304Speter 3519304Speterstatic int cl_pfmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t)); 3619304Speter 3719304Speter/* 3819304Speter * XXX 3919304Speter * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE. 4019304Speter */ 4119304Spetertypedef struct _tklist { 4219304Speter char *ts; /* Key's termcap string. */ 4319304Speter char *output; /* Corresponding vi command. */ 4419304Speter char *name; /* Name. */ 4519304Speter u_char value; /* Special value (for lookup). */ 4619304Speter} TKLIST; 4719304Speterstatic TKLIST const c_tklist[] = { /* Command mappings. */ 4819304Speter {"kil1", "O", "insert line"}, 4919304Speter {"kdch1", "x", "delete character"}, 5019304Speter {"kcud1", "j", "cursor down"}, 5119304Speter {"kel", "D", "delete to eol"}, 5219304Speter {"kind", "\004", "scroll down"}, /* ^D */ 5319304Speter {"kll", "$", "go to eol"}, 5469482Sru {"kend", "$", "go to eol"}, 5519304Speter {"khome", "^", "go to sol"}, 5619304Speter {"kich1", "i", "insert at cursor"}, 5719304Speter {"kdl1", "dd", "delete line"}, 5819304Speter {"kcub1", "h", "cursor left"}, 5919304Speter {"knp", "\006", "page down"}, /* ^F */ 6019304Speter {"kpp", "\002", "page up"}, /* ^B */ 6119304Speter {"kri", "\025", "scroll up"}, /* ^U */ 6219304Speter {"ked", "dG", "delete to end of screen"}, 6319304Speter {"kcuf1", "l", "cursor right"}, 6419304Speter {"kcuu1", "k", "cursor up"}, 6519304Speter {NULL}, 6619304Speter}; 6719304Speterstatic TKLIST const m1_tklist[] = { /* Input mappings (lookup). */ 6819304Speter {NULL}, 6919304Speter}; 7019304Speterstatic TKLIST const m2_tklist[] = { /* Input mappings (set or delete). */ 7119304Speter {"kcud1", "\033ja", "cursor down"}, /* ^[ja */ 7219304Speter {"kcub1", "\033ha", "cursor left"}, /* ^[ha */ 7319304Speter {"kcuu1", "\033ka", "cursor up"}, /* ^[ka */ 7419304Speter {"kcuf1", "\033la", "cursor right"}, /* ^[la */ 7519304Speter {NULL}, 7619304Speter}; 7719304Speter 7819304Speter/* 7919304Speter * cl_term_init -- 8019304Speter * Initialize the special keys defined by the termcap/terminfo entry. 8119304Speter * 8219304Speter * PUBLIC: int cl_term_init __P((SCR *)); 8319304Speter */ 8419304Speterint 8519304Spetercl_term_init(sp) 8619304Speter SCR *sp; 8719304Speter{ 8819304Speter KEYLIST *kp; 8919304Speter SEQ *qp; 9019304Speter TKLIST const *tkp; 9119304Speter char *t; 9219304Speter 9319304Speter /* Command mappings. */ 9419304Speter for (tkp = c_tklist; tkp->name != NULL; ++tkp) { 9519304Speter if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1) 9619304Speter continue; 9719304Speter if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t), 9819304Speter tkp->output, strlen(tkp->output), SEQ_COMMAND, 9919304Speter SEQ_NOOVERWRITE | SEQ_SCREEN)) 10019304Speter return (1); 10119304Speter } 10219304Speter 10319304Speter /* Input mappings needing to be looked up. */ 10419304Speter for (tkp = m1_tklist; tkp->name != NULL; ++tkp) { 10519304Speter if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1) 10619304Speter continue; 10719304Speter for (kp = keylist;; ++kp) 10819304Speter if (kp->value == tkp->value) 10919304Speter break; 11019304Speter if (kp == NULL) 11119304Speter continue; 11219304Speter if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t), 11319304Speter &kp->ch, 1, SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN)) 11419304Speter return (1); 11519304Speter } 11619304Speter 11719304Speter /* Input mappings that are already set or are text deletions. */ 11819304Speter for (tkp = m2_tklist; tkp->name != NULL; ++tkp) { 11919304Speter if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1) 12019304Speter continue; 12119304Speter /* 12219304Speter * !!! 12319304Speter * Some terminals' <cursor_left> keys send single <backspace> 12419304Speter * characters. This is okay in command mapping, but not okay 12519304Speter * in input mapping. That combination is the only one we'll 12619304Speter * ever see, hopefully, so kluge it here for now. 12719304Speter */ 12819304Speter if (!strcmp(t, "\b")) 12919304Speter continue; 13019304Speter if (tkp->output == NULL) { 13119304Speter if (seq_set(sp, tkp->name, strlen(tkp->name), 13219304Speter t, strlen(t), NULL, 0, 13319304Speter SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN)) 13419304Speter return (1); 13519304Speter } else 13619304Speter if (seq_set(sp, tkp->name, strlen(tkp->name), 13719304Speter t, strlen(t), tkp->output, strlen(tkp->output), 13819304Speter SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN)) 13919304Speter return (1); 14019304Speter } 14119304Speter 14219304Speter /* 14319304Speter * Rework any function key mappings that were set before the 14419304Speter * screen was initialized. 14519304Speter */ 14619304Speter for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) 14719304Speter if (F_ISSET(qp, SEQ_FUNCMAP)) 14819304Speter (void)cl_pfmap(sp, qp->stype, 14919304Speter qp->input, qp->ilen, qp->output, qp->olen); 15019304Speter return (0); 15119304Speter} 15219304Speter 15319304Speter/* 15419304Speter * cl_term_end -- 15519304Speter * End the special keys defined by the termcap/terminfo entry. 15619304Speter * 15719304Speter * PUBLIC: int cl_term_end __P((GS *)); 15819304Speter */ 15919304Speterint 16019304Spetercl_term_end(gp) 16119304Speter GS *gp; 16219304Speter{ 16319304Speter SEQ *qp, *nqp; 16419304Speter 16519304Speter /* Delete screen specific mappings. */ 16619304Speter for (qp = gp->seqq.lh_first; qp != NULL; qp = nqp) { 16719304Speter nqp = qp->q.le_next; 16819304Speter if (F_ISSET(qp, SEQ_SCREEN)) 16919304Speter (void)seq_mdel(qp); 17019304Speter } 17119304Speter return (0); 17219304Speter} 17319304Speter 17419304Speter/* 17519304Speter * cl_fmap -- 17619304Speter * Map a function key. 17719304Speter * 17819304Speter * PUBLIC: int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t)); 17919304Speter */ 18019304Speterint 18119304Spetercl_fmap(sp, stype, from, flen, to, tlen) 18219304Speter SCR *sp; 18319304Speter seq_t stype; 18419304Speter CHAR_T *from, *to; 18519304Speter size_t flen, tlen; 18619304Speter{ 18719304Speter /* Ignore until the screen is running, do the real work then. */ 18819304Speter if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI)) 18919304Speter return (0); 19019304Speter if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX)) 19119304Speter return (0); 19219304Speter 19319304Speter return (cl_pfmap(sp, stype, from, flen, to, tlen)); 19419304Speter} 19519304Speter 19619304Speter/* 19719304Speter * cl_pfmap -- 19819304Speter * Map a function key (private version). 19919304Speter */ 20019304Speterstatic int 20119304Spetercl_pfmap(sp, stype, from, flen, to, tlen) 20219304Speter SCR *sp; 20319304Speter seq_t stype; 20419304Speter CHAR_T *from, *to; 20519304Speter size_t flen, tlen; 20619304Speter{ 20719304Speter size_t nlen; 20819304Speter char *p, keyname[64]; 20919304Speter 21019304Speter (void)snprintf(keyname, sizeof(keyname), "kf%d", atoi(from + 1)); 21119304Speter if ((p = tigetstr(keyname)) == NULL || 21219304Speter p == (char *)-1 || strlen(p) == 0) 21319304Speter p = NULL; 21419304Speter if (p == NULL) { 21519304Speter msgq_str(sp, M_ERR, from, "233|This terminal has no %s key"); 21619304Speter return (1); 21719304Speter } 21819304Speter 21919304Speter nlen = snprintf(keyname, 22019304Speter sizeof(keyname), "function key %d", atoi(from + 1)); 22119304Speter return (seq_set(sp, keyname, nlen, 22219304Speter p, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN)); 22319304Speter} 22419304Speter 22519304Speter/* 22619304Speter * cl_optchange -- 22719304Speter * Curses screen specific "option changed" routine. 22819304Speter * 22919304Speter * PUBLIC: int cl_optchange __P((SCR *, int, char *, u_long *)); 23019304Speter */ 23119304Speterint 23219304Spetercl_optchange(sp, opt, str, valp) 23319304Speter SCR *sp; 23419304Speter int opt; 23519304Speter char *str; 23619304Speter u_long *valp; 23719304Speter{ 23819304Speter CL_PRIVATE *clp; 23919304Speter 24019304Speter clp = CLP(sp); 24119304Speter 24219304Speter switch (opt) { 24319304Speter case O_COLUMNS: 24419304Speter case O_LINES: 24519304Speter case O_TERM: 24619304Speter /* 24719304Speter * Changing the columns, lines or terminal require that 24819304Speter * we restart the screen. 24919304Speter */ 25019304Speter F_SET(sp->gp, G_SRESTART); 25119304Speter F_CLR(sp, SC_SCR_EX | SC_SCR_VI); 25219304Speter break; 25319304Speter case O_MESG: 25419304Speter (void)cl_omesg(sp, clp, !*valp); 25519304Speter break; 25619304Speter case O_WINDOWNAME: 25719304Speter if (*valp) { 25819304Speter F_CLR(clp, CL_RENAME_OK); 25919304Speter 26019304Speter (void)cl_rename(sp, NULL, 0); 26119304Speter } else { 26219304Speter F_SET(clp, CL_RENAME_OK); 26319304Speter 26419304Speter /* 26519304Speter * If the screen is live, i.e. we're not reading the 26619304Speter * .exrc file, update the window. 26719304Speter */ 26819304Speter if (sp->frp != NULL && sp->frp->name != NULL) 26919304Speter (void)cl_rename(sp, sp->frp->name, 1); 27019304Speter } 27119304Speter break; 27219304Speter } 27319304Speter return (0); 27419304Speter} 27519304Speter 27619304Speter/* 27719304Speter * cl_omesg -- 27819304Speter * Turn the tty write permission on or off. 27919304Speter * 28019304Speter * PUBLIC: int cl_omesg __P((SCR *, CL_PRIVATE *, int)); 28119304Speter */ 28219304Speterint 28319304Spetercl_omesg(sp, clp, on) 28419304Speter SCR *sp; 28519304Speter CL_PRIVATE *clp; 28619304Speter int on; 28719304Speter{ 28819304Speter struct stat sb; 28919304Speter char *tty; 29019304Speter 29119304Speter /* Find the tty, get the current permissions. */ 29219304Speter if ((tty = ttyname(STDERR_FILENO)) == NULL) { 29319304Speter if (sp != NULL) 29419304Speter msgq(sp, M_SYSERR, "stderr"); 29519304Speter return (1); 29619304Speter } 29719304Speter if (stat(tty, &sb) < 0) { 29819304Speter if (sp != NULL) 29919304Speter msgq(sp, M_SYSERR, "%s", tty); 30019304Speter return (1); 30119304Speter } 30219304Speter 30319304Speter /* Save the original status if it's unknown. */ 30419304Speter if (clp->tgw == TGW_UNKNOWN) 30519304Speter clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET; 30619304Speter 30719304Speter /* Toggle the permissions. */ 30819304Speter if (on) { 30919304Speter if (chmod(tty, sb.st_mode | S_IWGRP) < 0) { 31019304Speter if (sp != NULL) 31119304Speter msgq(sp, M_SYSERR, 31219304Speter "046|messages not turned on: %s", tty); 31319304Speter return (1); 31419304Speter } 31519304Speter } else 31619304Speter if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) { 31719304Speter if (sp != NULL) 31819304Speter msgq(sp, M_SYSERR, 31919304Speter "045|messages not turned off: %s", tty); 32019304Speter return (1); 32119304Speter } 32219304Speter return (0); 32319304Speter} 32419304Speter 32519304Speter/* 32619304Speter * cl_ssize -- 32719304Speter * Return the terminal size. 32819304Speter * 32919304Speter * PUBLIC: int cl_ssize __P((SCR *, int, size_t *, size_t *, int *)); 33019304Speter */ 33119304Speterint 33219304Spetercl_ssize(sp, sigwinch, rowp, colp, changedp) 33319304Speter SCR *sp; 33419304Speter int sigwinch; 33519304Speter size_t *rowp, *colp; 33619304Speter int *changedp; 33719304Speter{ 33819304Speter#ifdef TIOCGWINSZ 33919304Speter struct winsize win; 34019304Speter#endif 34119304Speter size_t col, row; 34219304Speter int rval; 34319304Speter char *p; 34419304Speter 34519304Speter /* Assume it's changed. */ 34619304Speter if (changedp != NULL) 34719304Speter *changedp = 1; 34819304Speter 34919304Speter /* 35019304Speter * !!! 35119304Speter * sp may be NULL. 35219304Speter * 35319304Speter * Get the screen rows and columns. If the values are wrong, it's 35419304Speter * not a big deal -- as soon as the user sets them explicitly the 35519304Speter * environment will be set and the screen package will use the new 35619304Speter * values. 35719304Speter * 35819304Speter * Try TIOCGWINSZ. 35919304Speter */ 36019304Speter row = col = 0; 36119304Speter#ifdef TIOCGWINSZ 36219304Speter if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) { 36319304Speter row = win.ws_row; 36419304Speter col = win.ws_col; 36519304Speter } 36619304Speter#endif 36719304Speter /* If here because of suspend or a signal, only trust TIOCGWINSZ. */ 36819304Speter if (sigwinch) { 36919304Speter /* 37019304Speter * Somebody didn't get TIOCGWINSZ right, or has suspend 37119304Speter * without window resizing support. The user just lost, 37219304Speter * but there's nothing we can do. 37319304Speter */ 37419304Speter if (row == 0 || col == 0) { 37519304Speter if (changedp != NULL) 37619304Speter *changedp = 0; 37719304Speter return (0); 37819304Speter } 37919304Speter 38019304Speter /* 38119304Speter * SunOS systems deliver SIGWINCH when windows are uncovered 38219304Speter * as well as when they change size. In addition, we call 38319304Speter * here when continuing after being suspended since the window 38419304Speter * may have changed size. Since we don't want to background 38519304Speter * all of the screens just because the window was uncovered, 38619304Speter * ignore the signal if there's no change. 38719304Speter */ 38819304Speter if (sp != NULL && 38919304Speter row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) { 39019304Speter if (changedp != NULL) 39119304Speter *changedp = 0; 39219304Speter return (0); 39319304Speter } 39419304Speter 39519304Speter if (rowp != NULL) 39619304Speter *rowp = row; 39719304Speter if (colp != NULL) 39819304Speter *colp = col; 39919304Speter return (0); 40019304Speter } 40119304Speter 40219304Speter /* 40319304Speter * !!! 40419304Speter * If TIOCGWINSZ failed, or had entries of 0, try termcap. This 40519304Speter * routine is called before any termcap or terminal information 40619304Speter * has been set up. If there's no TERM environmental variable set, 40719304Speter * let it go, at least ex can run. 40819304Speter */ 40919304Speter if (row == 0 || col == 0) { 41019304Speter if ((p = getenv("TERM")) == NULL) 41119304Speter goto noterm; 41219304Speter if (row == 0) 41319304Speter if ((rval = tigetnum("lines")) < 0) 41419304Speter msgq(sp, M_SYSERR, "tigetnum: lines"); 41519304Speter else 41619304Speter row = rval; 41719304Speter if (col == 0) 41819304Speter if ((rval = tigetnum("cols")) < 0) 41919304Speter msgq(sp, M_SYSERR, "tigetnum: cols"); 42019304Speter else 42119304Speter col = rval; 42219304Speter } 42319304Speter 42419304Speter /* If nothing else, well, it's probably a VT100. */ 42519304Speternoterm: if (row == 0) 42619304Speter row = 24; 42719304Speter if (col == 0) 42819304Speter col = 80; 42919304Speter 43019304Speter /* 43119304Speter * !!! 43219304Speter * POSIX 1003.2 requires the environment to override everything. 43319304Speter * Often, people can get nvi to stop messing up their screen by 43419304Speter * deleting the LINES and COLUMNS environment variables from their 43519304Speter * dot-files. 43619304Speter */ 43719304Speter if ((p = getenv("LINES")) != NULL) 43819304Speter row = strtol(p, NULL, 10); 43919304Speter if ((p = getenv("COLUMNS")) != NULL) 44019304Speter col = strtol(p, NULL, 10); 44119304Speter 44219304Speter if (rowp != NULL) 44319304Speter *rowp = row; 44419304Speter if (colp != NULL) 44519304Speter *colp = col; 44619304Speter return (0); 44719304Speter} 44819304Speter 44919304Speter/* 45019304Speter * cl_putchar -- 45119304Speter * Function version of putchar, for tputs. 45219304Speter * 45319304Speter * PUBLIC: int cl_putchar __P((int)); 45419304Speter */ 45519304Speterint 45619304Spetercl_putchar(ch) 45719304Speter int ch; 45819304Speter{ 45919304Speter return (putchar(ch)); 46019304Speter} 461