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 13270026Semastestatic const char sccsid[] = "$Id: cl_term.c,v 10.34 2013/12/07 16:21:14 wjenkner Exp $"; 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 <errno.h> 2319304Speter#include <limits.h> 2419304Speter#include <signal.h> 2519304Speter#include <stdio.h> 2619304Speter#include <stdlib.h> 2719304Speter#include <string.h> 28254225Speter#ifdef HAVE_TERM_H 29254225Speter#include <term.h> 30254225Speter#endif 3119304Speter#include <termios.h> 3219304Speter#include <unistd.h> 3319304Speter 3419304Speter#include "../common/common.h" 3519304Speter#include "cl.h" 3619304Speter 3719304Speterstatic int cl_pfmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t)); 3819304Speter 3919304Speter/* 4019304Speter * XXX 4119304Speter * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE. 4219304Speter */ 4319304Spetertypedef struct _tklist { 4419304Speter char *ts; /* Key's termcap string. */ 4519304Speter char *output; /* Corresponding vi command. */ 4619304Speter char *name; /* Name. */ 4719304Speter u_char value; /* Special value (for lookup). */ 4819304Speter} TKLIST; 4919304Speterstatic TKLIST const c_tklist[] = { /* Command mappings. */ 5019304Speter {"kil1", "O", "insert line"}, 5119304Speter {"kdch1", "x", "delete character"}, 5219304Speter {"kcud1", "j", "cursor down"}, 5319304Speter {"kel", "D", "delete to eol"}, 5419304Speter {"kind", "\004", "scroll down"}, /* ^D */ 5519304Speter {"kll", "$", "go to eol"}, 5669482Sru {"kend", "$", "go to eol"}, 5719304Speter {"khome", "^", "go to sol"}, 5819304Speter {"kich1", "i", "insert at cursor"}, 5919304Speter {"kdl1", "dd", "delete line"}, 6019304Speter {"kcub1", "h", "cursor left"}, 6119304Speter {"knp", "\006", "page down"}, /* ^F */ 6219304Speter {"kpp", "\002", "page up"}, /* ^B */ 6319304Speter {"kri", "\025", "scroll up"}, /* ^U */ 6419304Speter {"ked", "dG", "delete to end of screen"}, 6519304Speter {"kcuf1", "l", "cursor right"}, 6619304Speter {"kcuu1", "k", "cursor up"}, 6719304Speter {NULL}, 6819304Speter}; 6919304Speterstatic TKLIST const m1_tklist[] = { /* Input mappings (lookup). */ 7019304Speter {NULL}, 7119304Speter}; 7219304Speterstatic TKLIST const m2_tklist[] = { /* Input mappings (set or delete). */ 7319304Speter {"kcud1", "\033ja", "cursor down"}, /* ^[ja */ 7419304Speter {"kcub1", "\033ha", "cursor left"}, /* ^[ha */ 7519304Speter {"kcuu1", "\033ka", "cursor up"}, /* ^[ka */ 7619304Speter {"kcuf1", "\033la", "cursor right"}, /* ^[la */ 7719304Speter {NULL}, 7819304Speter}; 7919304Speter 8019304Speter/* 8119304Speter * cl_term_init -- 8219304Speter * Initialize the special keys defined by the termcap/terminfo entry. 8319304Speter * 8419304Speter * PUBLIC: int cl_term_init __P((SCR *)); 8519304Speter */ 8619304Speterint 87254225Spetercl_term_init(SCR *sp) 8819304Speter{ 8919304Speter KEYLIST *kp; 9019304Speter SEQ *qp; 9119304Speter TKLIST const *tkp; 9219304Speter char *t; 93254225Speter CHAR_T name[60]; 94254225Speter CHAR_T output[5]; 95254225Speter CHAR_T ts[20]; 96254225Speter CHAR_T *wp; 97254225Speter size_t wlen; 9819304Speter 9919304Speter /* Command mappings. */ 10019304Speter for (tkp = c_tklist; tkp->name != NULL; ++tkp) { 10119304Speter if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1) 10219304Speter continue; 103254225Speter CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen); 104254225Speter MEMCPY(name, wp, wlen); 105254225Speter CHAR2INT(sp, t, strlen(t), wp, wlen); 106254225Speter MEMCPY(ts, wp, wlen); 107254225Speter CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen); 108254225Speter MEMCPY(output, wp, wlen); 109254225Speter if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t), 110254225Speter output, strlen(tkp->output), SEQ_COMMAND, 11119304Speter SEQ_NOOVERWRITE | SEQ_SCREEN)) 11219304Speter return (1); 11319304Speter } 11419304Speter 11519304Speter /* Input mappings needing to be looked up. */ 11619304Speter for (tkp = m1_tklist; tkp->name != NULL; ++tkp) { 11719304Speter if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1) 11819304Speter continue; 11919304Speter for (kp = keylist;; ++kp) 12019304Speter if (kp->value == tkp->value) 12119304Speter break; 12219304Speter if (kp == NULL) 12319304Speter continue; 124254225Speter CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen); 125254225Speter MEMCPY(name, wp, wlen); 126254225Speter CHAR2INT(sp, t, strlen(t), wp, wlen); 127254225Speter MEMCPY(ts, wp, wlen); 128254225Speter output[0] = (UCHAR_T)kp->ch; 129254225Speter if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t), 130254225Speter output, 1, SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN)) 13119304Speter return (1); 13219304Speter } 13319304Speter 13419304Speter /* Input mappings that are already set or are text deletions. */ 13519304Speter for (tkp = m2_tklist; tkp->name != NULL; ++tkp) { 13619304Speter if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1) 13719304Speter continue; 13819304Speter /* 13919304Speter * !!! 14019304Speter * Some terminals' <cursor_left> keys send single <backspace> 14119304Speter * characters. This is okay in command mapping, but not okay 14219304Speter * in input mapping. That combination is the only one we'll 14319304Speter * ever see, hopefully, so kluge it here for now. 14419304Speter */ 14519304Speter if (!strcmp(t, "\b")) 14619304Speter continue; 14719304Speter if (tkp->output == NULL) { 148254225Speter CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen); 149254225Speter MEMCPY(name, wp, wlen); 150254225Speter CHAR2INT(sp, t, strlen(t), wp, wlen); 151254225Speter MEMCPY(ts, wp, wlen); 152254225Speter if (seq_set(sp, name, strlen(tkp->name), 153254225Speter ts, strlen(t), NULL, 0, 15419304Speter SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN)) 15519304Speter return (1); 156254225Speter } else { 157254225Speter CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen); 158254225Speter MEMCPY(name, wp, wlen); 159254225Speter CHAR2INT(sp, t, strlen(t), wp, wlen); 160254225Speter MEMCPY(ts, wp, wlen); 161254225Speter CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen); 162254225Speter MEMCPY(output, wp, wlen); 163254225Speter if (seq_set(sp, name, strlen(tkp->name), 164254225Speter ts, strlen(t), output, strlen(tkp->output), 16519304Speter SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN)) 16619304Speter return (1); 167254225Speter } 16819304Speter } 16919304Speter 17019304Speter /* 17119304Speter * Rework any function key mappings that were set before the 17219304Speter * screen was initialized. 17319304Speter */ 174254225Speter SLIST_FOREACH(qp, sp->gp->seqq, q) 17519304Speter if (F_ISSET(qp, SEQ_FUNCMAP)) 17619304Speter (void)cl_pfmap(sp, qp->stype, 17719304Speter qp->input, qp->ilen, qp->output, qp->olen); 17819304Speter return (0); 17919304Speter} 18019304Speter 18119304Speter/* 18219304Speter * cl_term_end -- 18319304Speter * End the special keys defined by the termcap/terminfo entry. 18419304Speter * 18519304Speter * PUBLIC: int cl_term_end __P((GS *)); 18619304Speter */ 18719304Speterint 188254225Spetercl_term_end(GS *gp) 18919304Speter{ 190270026Semaste SEQ *qp, *nqp, *pre_qp = NULL; 19119304Speter 19219304Speter /* Delete screen specific mappings. */ 193254225Speter SLIST_FOREACH_SAFE(qp, gp->seqq, q, nqp) 194254225Speter if (F_ISSET(qp, SEQ_SCREEN)) { 195270026Semaste if (qp == SLIST_FIRST(gp->seqq)) 196270026Semaste SLIST_REMOVE_HEAD(gp->seqq, q); 197270026Semaste else 198270026Semaste SLIST_REMOVE_AFTER(pre_qp, q); 199254225Speter (void)seq_free(qp); 200270026Semaste } else 201270026Semaste pre_qp = qp; 20219304Speter return (0); 20319304Speter} 20419304Speter 20519304Speter/* 20619304Speter * cl_fmap -- 20719304Speter * Map a function key. 20819304Speter * 20919304Speter * PUBLIC: int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t)); 21019304Speter */ 21119304Speterint 212254225Spetercl_fmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen) 21319304Speter{ 21419304Speter /* Ignore until the screen is running, do the real work then. */ 21519304Speter if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI)) 21619304Speter return (0); 21719304Speter if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX)) 21819304Speter return (0); 21919304Speter 22019304Speter return (cl_pfmap(sp, stype, from, flen, to, tlen)); 22119304Speter} 22219304Speter 22319304Speter/* 22419304Speter * cl_pfmap -- 22519304Speter * Map a function key (private version). 22619304Speter */ 22719304Speterstatic int 228254225Spetercl_pfmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen) 22919304Speter{ 23019304Speter size_t nlen; 231254225Speter char *p; 232254225Speter char name[64]; 233254225Speter CHAR_T keyname[64]; 234254225Speter CHAR_T ts[20]; 235254225Speter CHAR_T *wp; 236254225Speter size_t wlen; 23719304Speter 238254225Speter (void)snprintf(name, sizeof(name), "kf%d", 239254225Speter (int)STRTOL(from+1,NULL,10)); 240254225Speter if ((p = tigetstr(name)) == NULL || 24119304Speter p == (char *)-1 || strlen(p) == 0) 24219304Speter p = NULL; 24319304Speter if (p == NULL) { 244254225Speter msgq_wstr(sp, M_ERR, from, "233|This terminal has no %s key"); 24519304Speter return (1); 24619304Speter } 24719304Speter 248254225Speter nlen = SPRINTF(keyname, 249254225Speter SIZE(keyname), L("function key %d"), 250254225Speter (int)STRTOL(from+1,NULL,10)); 251254225Speter CHAR2INT(sp, p, strlen(p), wp, wlen); 252254225Speter MEMCPY(ts, wp, wlen); 25319304Speter return (seq_set(sp, keyname, nlen, 254254225Speter ts, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN)); 25519304Speter} 25619304Speter 25719304Speter/* 25819304Speter * cl_optchange -- 25919304Speter * Curses screen specific "option changed" routine. 26019304Speter * 26119304Speter * PUBLIC: int cl_optchange __P((SCR *, int, char *, u_long *)); 26219304Speter */ 26319304Speterint 264254225Spetercl_optchange(SCR *sp, int opt, char *str, u_long *valp) 26519304Speter{ 26619304Speter CL_PRIVATE *clp; 26719304Speter 26819304Speter clp = CLP(sp); 26919304Speter 27019304Speter switch (opt) { 27119304Speter case O_COLUMNS: 27219304Speter case O_LINES: 27319304Speter case O_TERM: 27419304Speter /* 27519304Speter * Changing the columns, lines or terminal require that 27619304Speter * we restart the screen. 27719304Speter */ 27819304Speter F_SET(sp->gp, G_SRESTART); 27919304Speter F_CLR(sp, SC_SCR_EX | SC_SCR_VI); 28019304Speter break; 28119304Speter case O_MESG: 282254225Speter (void)cl_omesg(sp, clp, *valp); 28319304Speter break; 28419304Speter case O_WINDOWNAME: 28519304Speter if (*valp) { 28619304Speter F_SET(clp, CL_RENAME_OK); 28719304Speter 28819304Speter /* 28919304Speter * If the screen is live, i.e. we're not reading the 29019304Speter * .exrc file, update the window. 29119304Speter */ 29219304Speter if (sp->frp != NULL && sp->frp->name != NULL) 29319304Speter (void)cl_rename(sp, sp->frp->name, 1); 294254225Speter } else { 295254225Speter F_CLR(clp, CL_RENAME_OK); 296254225Speter 297254225Speter (void)cl_rename(sp, NULL, 0); 29819304Speter } 29919304Speter break; 30019304Speter } 30119304Speter return (0); 30219304Speter} 30319304Speter 30419304Speter/* 30519304Speter * cl_omesg -- 30619304Speter * Turn the tty write permission on or off. 30719304Speter * 30819304Speter * PUBLIC: int cl_omesg __P((SCR *, CL_PRIVATE *, int)); 30919304Speter */ 31019304Speterint 311254225Spetercl_omesg(SCR *sp, CL_PRIVATE *clp, int on) 31219304Speter{ 31319304Speter struct stat sb; 31419304Speter char *tty; 31519304Speter 31619304Speter /* Find the tty, get the current permissions. */ 31719304Speter if ((tty = ttyname(STDERR_FILENO)) == NULL) { 31819304Speter if (sp != NULL) 31919304Speter msgq(sp, M_SYSERR, "stderr"); 32019304Speter return (1); 32119304Speter } 32219304Speter if (stat(tty, &sb) < 0) { 32319304Speter if (sp != NULL) 32419304Speter msgq(sp, M_SYSERR, "%s", tty); 32519304Speter return (1); 32619304Speter } 32719304Speter 32819304Speter /* Save the original status if it's unknown. */ 32919304Speter if (clp->tgw == TGW_UNKNOWN) 33019304Speter clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET; 33119304Speter 33219304Speter /* Toggle the permissions. */ 33319304Speter if (on) { 33419304Speter if (chmod(tty, sb.st_mode | S_IWGRP) < 0) { 33519304Speter if (sp != NULL) 33619304Speter msgq(sp, M_SYSERR, 33719304Speter "046|messages not turned on: %s", tty); 33819304Speter return (1); 33919304Speter } 34019304Speter } else 34119304Speter if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) { 34219304Speter if (sp != NULL) 34319304Speter msgq(sp, M_SYSERR, 34419304Speter "045|messages not turned off: %s", tty); 34519304Speter return (1); 34619304Speter } 34719304Speter return (0); 34819304Speter} 34919304Speter 35019304Speter/* 35119304Speter * cl_ssize -- 35219304Speter * Return the terminal size. 35319304Speter * 35419304Speter * PUBLIC: int cl_ssize __P((SCR *, int, size_t *, size_t *, int *)); 35519304Speter */ 35619304Speterint 357254225Spetercl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp) 35819304Speter{ 35919304Speter struct winsize win; 36019304Speter size_t col, row; 36119304Speter int rval; 36219304Speter char *p; 36319304Speter 36419304Speter /* Assume it's changed. */ 36519304Speter if (changedp != NULL) 36619304Speter *changedp = 1; 36719304Speter 36819304Speter /* 36919304Speter * !!! 37019304Speter * sp may be NULL. 37119304Speter * 37219304Speter * Get the screen rows and columns. If the values are wrong, it's 37319304Speter * not a big deal -- as soon as the user sets them explicitly the 37419304Speter * environment will be set and the screen package will use the new 37519304Speter * values. 37619304Speter * 37719304Speter * Try TIOCGWINSZ. 37819304Speter */ 37919304Speter row = col = 0; 38019304Speter if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) { 38119304Speter row = win.ws_row; 38219304Speter col = win.ws_col; 38319304Speter } 38419304Speter /* If here because of suspend or a signal, only trust TIOCGWINSZ. */ 38519304Speter if (sigwinch) { 38619304Speter /* 38719304Speter * Somebody didn't get TIOCGWINSZ right, or has suspend 38819304Speter * without window resizing support. The user just lost, 38919304Speter * but there's nothing we can do. 39019304Speter */ 39119304Speter if (row == 0 || col == 0) { 39219304Speter if (changedp != NULL) 39319304Speter *changedp = 0; 39419304Speter return (0); 39519304Speter } 39619304Speter 39719304Speter /* 39819304Speter * SunOS systems deliver SIGWINCH when windows are uncovered 39919304Speter * as well as when they change size. In addition, we call 40019304Speter * here when continuing after being suspended since the window 40119304Speter * may have changed size. Since we don't want to background 40219304Speter * all of the screens just because the window was uncovered, 40319304Speter * ignore the signal if there's no change. 40419304Speter */ 40519304Speter if (sp != NULL && 40619304Speter row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) { 40719304Speter if (changedp != NULL) 40819304Speter *changedp = 0; 40919304Speter return (0); 41019304Speter } 41119304Speter 41219304Speter if (rowp != NULL) 41319304Speter *rowp = row; 41419304Speter if (colp != NULL) 41519304Speter *colp = col; 41619304Speter return (0); 41719304Speter } 41819304Speter 41919304Speter /* 42019304Speter * !!! 42119304Speter * If TIOCGWINSZ failed, or had entries of 0, try termcap. This 42219304Speter * routine is called before any termcap or terminal information 42319304Speter * has been set up. If there's no TERM environmental variable set, 42419304Speter * let it go, at least ex can run. 42519304Speter */ 42619304Speter if (row == 0 || col == 0) { 42719304Speter if ((p = getenv("TERM")) == NULL) 42819304Speter goto noterm; 42919304Speter if (row == 0) 43019304Speter if ((rval = tigetnum("lines")) < 0) 43119304Speter msgq(sp, M_SYSERR, "tigetnum: lines"); 43219304Speter else 43319304Speter row = rval; 43419304Speter if (col == 0) 43519304Speter if ((rval = tigetnum("cols")) < 0) 43619304Speter msgq(sp, M_SYSERR, "tigetnum: cols"); 43719304Speter else 43819304Speter col = rval; 43919304Speter } 44019304Speter 44119304Speter /* If nothing else, well, it's probably a VT100. */ 44219304Speternoterm: if (row == 0) 44319304Speter row = 24; 44419304Speter if (col == 0) 44519304Speter col = 80; 44619304Speter 44719304Speter /* 44819304Speter * !!! 44919304Speter * POSIX 1003.2 requires the environment to override everything. 45019304Speter * Often, people can get nvi to stop messing up their screen by 45119304Speter * deleting the LINES and COLUMNS environment variables from their 45219304Speter * dot-files. 45319304Speter */ 45419304Speter if ((p = getenv("LINES")) != NULL) 45519304Speter row = strtol(p, NULL, 10); 45619304Speter if ((p = getenv("COLUMNS")) != NULL) 45719304Speter col = strtol(p, NULL, 10); 45819304Speter 45919304Speter if (rowp != NULL) 46019304Speter *rowp = row; 46119304Speter if (colp != NULL) 46219304Speter *colp = col; 46319304Speter return (0); 46419304Speter} 46519304Speter 46619304Speter/* 46719304Speter * cl_putchar -- 46819304Speter * Function version of putchar, for tputs. 46919304Speter * 47019304Speter * PUBLIC: int cl_putchar __P((int)); 47119304Speter */ 47219304Speterint 473254225Spetercl_putchar(int ch) 47419304Speter{ 47519304Speter return (putchar(ch)); 47619304Speter} 477