]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/nvi/cl/cl_term.c
Merge libc++ trunk r366426, resolve conflicts, and add FREEBSD-Xlist.
[FreeBSD/FreeBSD.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.35 2015/04/08 02:12:11 zy 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(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(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(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(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(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_TERM:
272                 F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
273                 /* FALLTHROUGH */
274         case O_COLUMNS:
275         case O_LINES:
276                 /*
277                  * Changing the terminal type requires that we reinitialize
278                  * curses, while resizing does not.
279                  */
280                 F_SET(sp->gp, G_SRESTART);
281                 break;
282         case O_MESG:
283                 (void)cl_omesg(sp, clp, *valp);
284                 break;
285         case O_WINDOWNAME:
286                 if (*valp) {
287                         F_SET(clp, CL_RENAME_OK);
288
289                         /*
290                          * If the screen is live, i.e. we're not reading the
291                          * .exrc file, update the window.
292                          */
293                         if (sp->frp != NULL && sp->frp->name != NULL)
294                                 (void)cl_rename(sp, sp->frp->name, 1);
295                 } else {
296                         F_CLR(clp, CL_RENAME_OK);
297
298                         (void)cl_rename(sp, NULL, 0);
299                 }
300                 break;
301         }
302         return (0);
303 }
304
305 /*
306  * cl_omesg --
307  *      Turn the tty write permission on or off.
308  *
309  * PUBLIC: int cl_omesg(SCR *, CL_PRIVATE *, int);
310  */
311 int
312 cl_omesg(SCR *sp, CL_PRIVATE *clp, int on)
313 {
314         struct stat sb;
315         char *tty;
316
317         /* Find the tty, get the current permissions. */
318         if ((tty = ttyname(STDERR_FILENO)) == NULL) {
319                 if (sp != NULL)
320                         msgq(sp, M_SYSERR, "stderr");
321                 return (1);
322         }
323         if (stat(tty, &sb) < 0) {
324                 if (sp != NULL)
325                         msgq(sp, M_SYSERR, "%s", tty);
326                 return (1);
327         }
328
329         /* Save the original status if it's unknown. */
330         if (clp->tgw == TGW_UNKNOWN)
331                 clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET;
332
333         /* Toggle the permissions. */
334         if (on) {
335                 if (chmod(tty, sb.st_mode | S_IWGRP) < 0) {
336                         if (sp != NULL)
337                                 msgq(sp, M_SYSERR,
338                                     "046|messages not turned on: %s", tty);
339                         return (1);
340                 }
341         } else
342                 if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) {
343                         if (sp != NULL)
344                                 msgq(sp, M_SYSERR,
345                                     "045|messages not turned off: %s", tty);
346                         return (1);
347                 }
348         return (0);
349 }
350
351 /*
352  * cl_ssize --
353  *      Return the terminal size.
354  *
355  * PUBLIC: int cl_ssize(SCR *, int, size_t *, size_t *, int *);
356  */
357 int
358 cl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp)
359 {
360         struct winsize win;
361         size_t col, row;
362         int rval;
363         char *p;
364
365         /* Assume it's changed. */
366         if (changedp != NULL)
367                 *changedp = 1;
368
369         /*
370          * !!!
371          * sp may be NULL.
372          *
373          * Get the screen rows and columns.  If the values are wrong, it's
374          * not a big deal -- as soon as the user sets them explicitly the
375          * environment will be set and the screen package will use the new
376          * values.
377          *
378          * Try TIOCGWINSZ.
379          */
380         row = col = 0;
381         if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
382                 row = win.ws_row;
383                 col = win.ws_col;
384         }
385         /* If here because of suspend or a signal, only trust TIOCGWINSZ. */
386         if (sigwinch) {
387                 /*
388                  * Somebody didn't get TIOCGWINSZ right, or has suspend
389                  * without window resizing support.  The user just lost,
390                  * but there's nothing we can do.
391                  */
392                 if (row == 0 || col == 0) {
393                         if (changedp != NULL)
394                                 *changedp = 0;
395                         return (0);
396                 }
397
398                 /*
399                  * SunOS systems deliver SIGWINCH when windows are uncovered
400                  * as well as when they change size.  In addition, we call
401                  * here when continuing after being suspended since the window
402                  * may have changed size.  Since we don't want to background
403                  * all of the screens just because the window was uncovered,
404                  * ignore the signal if there's no change.
405                  */
406                 if (sp != NULL &&
407                     row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) {
408                         if (changedp != NULL)
409                                 *changedp = 0;
410                         return (0);
411                 }
412
413                 if (rowp != NULL)
414                         *rowp = row;
415                 if (colp != NULL)
416                         *colp = col;
417                 return (0);
418         }
419
420         /*
421          * !!!
422          * If TIOCGWINSZ failed, or had entries of 0, try termcap.  This
423          * routine is called before any termcap or terminal information
424          * has been set up.  If there's no TERM environmental variable set,
425          * let it go, at least ex can run.
426          */
427         if (row == 0 || col == 0) {
428                 if ((p = getenv("TERM")) == NULL)
429                         goto noterm;
430                 if (row == 0)
431                         if ((rval = tigetnum("lines")) < 0)
432                                 msgq(sp, M_SYSERR, "tigetnum: lines");
433                         else
434                                 row = rval;
435                 if (col == 0)
436                         if ((rval = tigetnum("cols")) < 0)
437                                 msgq(sp, M_SYSERR, "tigetnum: cols");
438                         else
439                                 col = rval;
440         }
441
442         /* If nothing else, well, it's probably a VT100. */
443 noterm: if (row == 0)
444                 row = 24;
445         if (col == 0)
446                 col = 80;
447
448         /*
449          * !!!
450          * POSIX 1003.2 requires the environment to override everything.
451          * Often, people can get nvi to stop messing up their screen by
452          * deleting the LINES and COLUMNS environment variables from their
453          * dot-files.
454          */
455         if ((p = getenv("LINES")) != NULL)
456                 row = strtol(p, NULL, 10);
457         if ((p = getenv("COLUMNS")) != NULL)
458                 col = strtol(p, NULL, 10);
459
460         if (rowp != NULL)
461                 *rowp = row;
462         if (colp != NULL)
463                 *colp = col;
464         return (0);
465 }
466
467 /*
468  * cl_putchar --
469  *      Function version of putchar, for tputs.
470  *
471  * PUBLIC: int cl_putchar(int);
472  */
473 int
474 cl_putchar(int ch)
475 {
476         return (putchar(ch));
477 }