]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/nvi/cl/cl_term.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / nvi / cl / cl_term.c
1 /*-
2  * Copyright (c) 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1993, 1994, 1995, 1996
5  *      Keith Bostic.  All rights reserved.
6  *
7  * See the LICENSE file for redistribution information.
8  */
9
10 #include "config.h"
11
12 #ifndef lint
13 static const char sccsid[] = "$Id: cl_term.c,v 10.34 2013/12/07 16:21:14 wjenkner Exp $";
14 #endif /* not lint */
15
16 #include <sys/types.h>
17 #include <sys/ioctl.h>
18 #include <sys/queue.h>
19 #include <sys/stat.h>
20
21 #include <bitstring.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <signal.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef HAVE_TERM_H
29 #include <term.h>
30 #endif
31 #include <termios.h>
32 #include <unistd.h>
33
34 #include "../common/common.h"
35 #include "cl.h"
36
37 static int cl_pfmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
38
39 /*
40  * XXX
41  * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
42  */
43 typedef struct _tklist {
44         char    *ts;                    /* Key's termcap string. */
45         char    *output;                /* Corresponding vi command. */
46         char    *name;                  /* Name. */
47         u_char   value;                 /* Special value (for lookup). */
48 } TKLIST;
49 static TKLIST const c_tklist[] = {      /* Command mappings. */
50         {"kil1",        "O",    "insert line"},
51         {"kdch1",       "x",    "delete character"},
52         {"kcud1",       "j",    "cursor down"},
53         {"kel",         "D",    "delete to eol"},
54         {"kind",     "\004",    "scroll down"},                 /* ^D */
55         {"kll",         "$",    "go to eol"},
56         {"kend",        "$",    "go to eol"},
57         {"khome",       "^",    "go to sol"},
58         {"kich1",       "i",    "insert at cursor"},
59         {"kdl1",       "dd",    "delete line"},
60         {"kcub1",       "h",    "cursor left"},
61         {"knp",      "\006",    "page down"},                   /* ^F */
62         {"kpp",      "\002",    "page up"},                     /* ^B */
63         {"kri",      "\025",    "scroll up"},                   /* ^U */
64         {"ked",        "dG",    "delete to end of screen"},
65         {"kcuf1",       "l",    "cursor right"},
66         {"kcuu1",       "k",    "cursor up"},
67         {NULL},
68 };
69 static TKLIST const m1_tklist[] = {     /* Input mappings (lookup). */
70         {NULL},
71 };
72 static TKLIST const m2_tklist[] = {     /* Input mappings (set or delete). */
73         {"kcud1",  "\033ja",    "cursor down"},                 /* ^[ja */
74         {"kcub1",  "\033ha",    "cursor left"},                 /* ^[ha */
75         {"kcuu1",  "\033ka",    "cursor up"},                   /* ^[ka */
76         {"kcuf1",  "\033la",    "cursor right"},                /* ^[la */
77         {NULL},
78 };
79
80 /*
81  * cl_term_init --
82  *      Initialize the special keys defined by the termcap/terminfo entry.
83  *
84  * PUBLIC: int cl_term_init __P((SCR *));
85  */
86 int
87 cl_term_init(SCR *sp)
88 {
89         KEYLIST *kp;
90         SEQ *qp;
91         TKLIST const *tkp;
92         char *t;
93         CHAR_T name[60];
94         CHAR_T output[5];
95         CHAR_T ts[20];
96         CHAR_T *wp;
97         size_t wlen;
98
99         /* Command mappings. */
100         for (tkp = c_tklist; tkp->name != NULL; ++tkp) {
101                 if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
102                         continue;
103                 CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
104                 MEMCPY(name, wp, wlen);
105                 CHAR2INT(sp, t, strlen(t), wp, wlen);
106                 MEMCPY(ts, wp, wlen);
107                 CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen);
108                 MEMCPY(output, wp, wlen);
109                 if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t),
110                     output, strlen(tkp->output), SEQ_COMMAND,
111                     SEQ_NOOVERWRITE | SEQ_SCREEN))
112                         return (1);
113         }
114
115         /* Input mappings needing to be looked up. */
116         for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {
117                 if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
118                         continue;
119                 for (kp = keylist;; ++kp)
120                         if (kp->value == tkp->value)
121                                 break;
122                 if (kp == NULL)
123                         continue;
124                 CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
125                 MEMCPY(name, wp, wlen);
126                 CHAR2INT(sp, t, strlen(t), wp, wlen);
127                 MEMCPY(ts, wp, wlen);
128                 output[0] = (UCHAR_T)kp->ch;
129                 if (seq_set(sp, name, strlen(tkp->name), ts, strlen(t),
130                     output, 1, SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
131                         return (1);
132         }
133
134         /* Input mappings that are already set or are text deletions. */
135         for (tkp = m2_tklist; tkp->name != NULL; ++tkp) {
136                 if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
137                         continue;
138                 /*
139                  * !!!
140                  * Some terminals' <cursor_left> keys send single <backspace>
141                  * characters.  This is okay in command mapping, but not okay
142                  * in input mapping.  That combination is the only one we'll
143                  * ever see, hopefully, so kluge it here for now.
144                  */
145                 if (!strcmp(t, "\b"))
146                         continue;
147                 if (tkp->output == NULL) {
148                         CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
149                         MEMCPY(name, wp, wlen);
150                         CHAR2INT(sp, t, strlen(t), wp, wlen);
151                         MEMCPY(ts, wp, wlen);
152                         if (seq_set(sp, name, strlen(tkp->name),
153                             ts, strlen(t), NULL, 0,
154                             SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
155                                 return (1);
156                 } else {
157                         CHAR2INT(sp, tkp->name, strlen(tkp->name), wp, wlen);
158                         MEMCPY(name, wp, wlen);
159                         CHAR2INT(sp, t, strlen(t), wp, wlen);
160                         MEMCPY(ts, wp, wlen);
161                         CHAR2INT(sp, tkp->output, strlen(tkp->output), wp, wlen);
162                         MEMCPY(output, wp, wlen);
163                         if (seq_set(sp, name, strlen(tkp->name),
164                             ts, strlen(t), output, strlen(tkp->output),
165                             SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
166                                 return (1);
167                 }
168         }
169
170         /*
171          * Rework any function key mappings that were set before the
172          * screen was initialized.
173          */
174         SLIST_FOREACH(qp, sp->gp->seqq, q)
175                 if (F_ISSET(qp, SEQ_FUNCMAP))
176                         (void)cl_pfmap(sp, qp->stype,
177                             qp->input, qp->ilen, qp->output, qp->olen);
178         return (0);
179 }
180
181 /*
182  * cl_term_end --
183  *      End the special keys defined by the termcap/terminfo entry.
184  *
185  * PUBLIC: int cl_term_end __P((GS *));
186  */
187 int
188 cl_term_end(GS *gp)
189 {
190         SEQ *qp, *nqp, *pre_qp = NULL;
191
192         /* Delete screen specific mappings. */
193         SLIST_FOREACH_SAFE(qp, gp->seqq, q, nqp)
194                 if (F_ISSET(qp, SEQ_SCREEN)) {
195                         if (qp == SLIST_FIRST(gp->seqq))
196                                 SLIST_REMOVE_HEAD(gp->seqq, q);
197                         else
198                                 SLIST_REMOVE_AFTER(pre_qp, q);
199                         (void)seq_free(qp);
200                 } else
201                         pre_qp = qp;
202         return (0);
203 }
204
205 /*
206  * cl_fmap --
207  *      Map a function key.
208  *
209  * PUBLIC: int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
210  */
211 int
212 cl_fmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen)
213 {
214         /* Ignore until the screen is running, do the real work then. */
215         if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI))
216                 return (0);
217         if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX))
218                 return (0);
219
220         return (cl_pfmap(sp, stype, from, flen, to, tlen));
221 }
222
223 /*
224  * cl_pfmap --
225  *      Map a function key (private version).
226  */
227 static int
228 cl_pfmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen)
229 {
230         size_t nlen;
231         char *p;
232         char name[64];
233         CHAR_T keyname[64];
234         CHAR_T ts[20];
235         CHAR_T *wp;
236         size_t wlen;
237
238         (void)snprintf(name, sizeof(name), "kf%d", 
239                         (int)STRTOL(from+1,NULL,10));
240         if ((p = tigetstr(name)) == NULL ||
241             p == (char *)-1 || strlen(p) == 0)
242                 p = NULL;
243         if (p == NULL) {
244                 msgq_wstr(sp, M_ERR, from, "233|This terminal has no %s key");
245                 return (1);
246         }
247
248         nlen = SPRINTF(keyname,
249             SIZE(keyname), L("function key %d"), 
250                         (int)STRTOL(from+1,NULL,10));
251         CHAR2INT(sp, p, strlen(p), wp, wlen);
252         MEMCPY(ts, wp, wlen);
253         return (seq_set(sp, keyname, nlen,
254             ts, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN));
255 }
256
257 /*
258  * cl_optchange --
259  *      Curses screen specific "option changed" routine.
260  *
261  * PUBLIC: int cl_optchange __P((SCR *, int, char *, u_long *));
262  */
263 int
264 cl_optchange(SCR *sp, int opt, char *str, u_long *valp)
265 {
266         CL_PRIVATE *clp;
267
268         clp = CLP(sp);
269
270         switch (opt) {
271         case O_COLUMNS:
272         case O_LINES:
273         case O_TERM:
274                 /*
275                  * Changing the columns, lines or terminal require that
276                  * we restart the screen.
277                  */
278                 F_SET(sp->gp, G_SRESTART);
279                 F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
280                 break;
281         case O_MESG:
282                 (void)cl_omesg(sp, clp, *valp);
283                 break;
284         case O_WINDOWNAME:
285                 if (*valp) {
286                         F_SET(clp, CL_RENAME_OK);
287
288                         /*
289                          * If the screen is live, i.e. we're not reading the
290                          * .exrc file, update the window.
291                          */
292                         if (sp->frp != NULL && sp->frp->name != NULL)
293                                 (void)cl_rename(sp, sp->frp->name, 1);
294                 } else {
295                         F_CLR(clp, CL_RENAME_OK);
296
297                         (void)cl_rename(sp, NULL, 0);
298                 }
299                 break;
300         }
301         return (0);
302 }
303
304 /*
305  * cl_omesg --
306  *      Turn the tty write permission on or off.
307  *
308  * PUBLIC: int cl_omesg __P((SCR *, CL_PRIVATE *, int));
309  */
310 int
311 cl_omesg(SCR *sp, CL_PRIVATE *clp, int on)
312 {
313         struct stat sb;
314         char *tty;
315
316         /* Find the tty, get the current permissions. */
317         if ((tty = ttyname(STDERR_FILENO)) == NULL) {
318                 if (sp != NULL)
319                         msgq(sp, M_SYSERR, "stderr");
320                 return (1);
321         }
322         if (stat(tty, &sb) < 0) {
323                 if (sp != NULL)
324                         msgq(sp, M_SYSERR, "%s", tty);
325                 return (1);
326         }
327
328         /* Save the original status if it's unknown. */
329         if (clp->tgw == TGW_UNKNOWN)
330                 clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET;
331
332         /* Toggle the permissions. */
333         if (on) {
334                 if (chmod(tty, sb.st_mode | S_IWGRP) < 0) {
335                         if (sp != NULL)
336                                 msgq(sp, M_SYSERR,
337                                     "046|messages not turned on: %s", tty);
338                         return (1);
339                 }
340         } else
341                 if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) {
342                         if (sp != NULL)
343                                 msgq(sp, M_SYSERR,
344                                     "045|messages not turned off: %s", tty);
345                         return (1);
346                 }
347         return (0);
348 }
349
350 /*
351  * cl_ssize --
352  *      Return the terminal size.
353  *
354  * PUBLIC: int cl_ssize __P((SCR *, int, size_t *, size_t *, int *));
355  */
356 int
357 cl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp)
358 {
359         struct winsize win;
360         size_t col, row;
361         int rval;
362         char *p;
363
364         /* Assume it's changed. */
365         if (changedp != NULL)
366                 *changedp = 1;
367
368         /*
369          * !!!
370          * sp may be NULL.
371          *
372          * Get the screen rows and columns.  If the values are wrong, it's
373          * not a big deal -- as soon as the user sets them explicitly the
374          * environment will be set and the screen package will use the new
375          * values.
376          *
377          * Try TIOCGWINSZ.
378          */
379         row = col = 0;
380         if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
381                 row = win.ws_row;
382                 col = win.ws_col;
383         }
384         /* If here because of suspend or a signal, only trust TIOCGWINSZ. */
385         if (sigwinch) {
386                 /*
387                  * Somebody didn't get TIOCGWINSZ right, or has suspend
388                  * without window resizing support.  The user just lost,
389                  * but there's nothing we can do.
390                  */
391                 if (row == 0 || col == 0) {
392                         if (changedp != NULL)
393                                 *changedp = 0;
394                         return (0);
395                 }
396
397                 /*
398                  * SunOS systems deliver SIGWINCH when windows are uncovered
399                  * as well as when they change size.  In addition, we call
400                  * here when continuing after being suspended since the window
401                  * may have changed size.  Since we don't want to background
402                  * all of the screens just because the window was uncovered,
403                  * ignore the signal if there's no change.
404                  */
405                 if (sp != NULL &&
406                     row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) {
407                         if (changedp != NULL)
408                                 *changedp = 0;
409                         return (0);
410                 }
411
412                 if (rowp != NULL)
413                         *rowp = row;
414                 if (colp != NULL)
415                         *colp = col;
416                 return (0);
417         }
418
419         /*
420          * !!!
421          * If TIOCGWINSZ failed, or had entries of 0, try termcap.  This
422          * routine is called before any termcap or terminal information
423          * has been set up.  If there's no TERM environmental variable set,
424          * let it go, at least ex can run.
425          */
426         if (row == 0 || col == 0) {
427                 if ((p = getenv("TERM")) == NULL)
428                         goto noterm;
429                 if (row == 0)
430                         if ((rval = tigetnum("lines")) < 0)
431                                 msgq(sp, M_SYSERR, "tigetnum: lines");
432                         else
433                                 row = rval;
434                 if (col == 0)
435                         if ((rval = tigetnum("cols")) < 0)
436                                 msgq(sp, M_SYSERR, "tigetnum: cols");
437                         else
438                                 col = rval;
439         }
440
441         /* If nothing else, well, it's probably a VT100. */
442 noterm: if (row == 0)
443                 row = 24;
444         if (col == 0)
445                 col = 80;
446
447         /*
448          * !!!
449          * POSIX 1003.2 requires the environment to override everything.
450          * Often, people can get nvi to stop messing up their screen by
451          * deleting the LINES and COLUMNS environment variables from their
452          * dot-files.
453          */
454         if ((p = getenv("LINES")) != NULL)
455                 row = strtol(p, NULL, 10);
456         if ((p = getenv("COLUMNS")) != NULL)
457                 col = strtol(p, NULL, 10);
458
459         if (rowp != NULL)
460                 *rowp = row;
461         if (colp != NULL)
462                 *colp = col;
463         return (0);
464 }
465
466 /*
467  * cl_putchar --
468  *      Function version of putchar, for tputs.
469  *
470  * PUBLIC: int cl_putchar __P((int));
471  */
472 int
473 cl_putchar(int ch)
474 {
475         return (putchar(ch));
476 }