]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/nvi/cl/cl_term.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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.33 2012/04/21 23:51:46 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 __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;
191
192         /* Delete screen specific mappings. */
193         SLIST_FOREACH_SAFE(qp, gp->seqq, q, nqp)
194                 if (F_ISSET(qp, SEQ_SCREEN)) {
195                         SLIST_REMOVE_HEAD(gp->seqq, q);
196                         (void)seq_free(qp);
197                 }
198         return (0);
199 }
200
201 /*
202  * cl_fmap --
203  *      Map a function key.
204  *
205  * PUBLIC: int cl_fmap __P((SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t));
206  */
207 int
208 cl_fmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen)
209 {
210         /* Ignore until the screen is running, do the real work then. */
211         if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI))
212                 return (0);
213         if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX))
214                 return (0);
215
216         return (cl_pfmap(sp, stype, from, flen, to, tlen));
217 }
218
219 /*
220  * cl_pfmap --
221  *      Map a function key (private version).
222  */
223 static int
224 cl_pfmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to, size_t tlen)
225 {
226         size_t nlen;
227         char *p;
228         char name[64];
229         CHAR_T keyname[64];
230         CHAR_T ts[20];
231         CHAR_T *wp;
232         size_t wlen;
233
234         (void)snprintf(name, sizeof(name), "kf%d", 
235                         (int)STRTOL(from+1,NULL,10));
236         if ((p = tigetstr(name)) == NULL ||
237             p == (char *)-1 || strlen(p) == 0)
238                 p = NULL;
239         if (p == NULL) {
240                 msgq_wstr(sp, M_ERR, from, "233|This terminal has no %s key");
241                 return (1);
242         }
243
244         nlen = SPRINTF(keyname,
245             SIZE(keyname), L("function key %d"), 
246                         (int)STRTOL(from+1,NULL,10));
247         CHAR2INT(sp, p, strlen(p), wp, wlen);
248         MEMCPY(ts, wp, wlen);
249         return (seq_set(sp, keyname, nlen,
250             ts, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN));
251 }
252
253 /*
254  * cl_optchange --
255  *      Curses screen specific "option changed" routine.
256  *
257  * PUBLIC: int cl_optchange __P((SCR *, int, char *, u_long *));
258  */
259 int
260 cl_optchange(SCR *sp, int opt, char *str, u_long *valp)
261 {
262         CL_PRIVATE *clp;
263
264         clp = CLP(sp);
265
266         switch (opt) {
267         case O_COLUMNS:
268         case O_LINES:
269         case O_TERM:
270                 /*
271                  * Changing the columns, lines or terminal require that
272                  * we restart the screen.
273                  */
274                 F_SET(sp->gp, G_SRESTART);
275                 F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
276                 break;
277         case O_MESG:
278                 (void)cl_omesg(sp, clp, *valp);
279                 break;
280         case O_WINDOWNAME:
281                 if (*valp) {
282                         F_SET(clp, CL_RENAME_OK);
283
284                         /*
285                          * If the screen is live, i.e. we're not reading the
286                          * .exrc file, update the window.
287                          */
288                         if (sp->frp != NULL && sp->frp->name != NULL)
289                                 (void)cl_rename(sp, sp->frp->name, 1);
290                 } else {
291                         F_CLR(clp, CL_RENAME_OK);
292
293                         (void)cl_rename(sp, NULL, 0);
294                 }
295                 break;
296         }
297         return (0);
298 }
299
300 /*
301  * cl_omesg --
302  *      Turn the tty write permission on or off.
303  *
304  * PUBLIC: int cl_omesg __P((SCR *, CL_PRIVATE *, int));
305  */
306 int
307 cl_omesg(SCR *sp, CL_PRIVATE *clp, int on)
308 {
309         struct stat sb;
310         char *tty;
311
312         /* Find the tty, get the current permissions. */
313         if ((tty = ttyname(STDERR_FILENO)) == NULL) {
314                 if (sp != NULL)
315                         msgq(sp, M_SYSERR, "stderr");
316                 return (1);
317         }
318         if (stat(tty, &sb) < 0) {
319                 if (sp != NULL)
320                         msgq(sp, M_SYSERR, "%s", tty);
321                 return (1);
322         }
323
324         /* Save the original status if it's unknown. */
325         if (clp->tgw == TGW_UNKNOWN)
326                 clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET;
327
328         /* Toggle the permissions. */
329         if (on) {
330                 if (chmod(tty, sb.st_mode | S_IWGRP) < 0) {
331                         if (sp != NULL)
332                                 msgq(sp, M_SYSERR,
333                                     "046|messages not turned on: %s", tty);
334                         return (1);
335                 }
336         } else
337                 if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) {
338                         if (sp != NULL)
339                                 msgq(sp, M_SYSERR,
340                                     "045|messages not turned off: %s", tty);
341                         return (1);
342                 }
343         return (0);
344 }
345
346 /*
347  * cl_ssize --
348  *      Return the terminal size.
349  *
350  * PUBLIC: int cl_ssize __P((SCR *, int, size_t *, size_t *, int *));
351  */
352 int
353 cl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp)
354 {
355         struct winsize win;
356         size_t col, row;
357         int rval;
358         char *p;
359
360         /* Assume it's changed. */
361         if (changedp != NULL)
362                 *changedp = 1;
363
364         /*
365          * !!!
366          * sp may be NULL.
367          *
368          * Get the screen rows and columns.  If the values are wrong, it's
369          * not a big deal -- as soon as the user sets them explicitly the
370          * environment will be set and the screen package will use the new
371          * values.
372          *
373          * Try TIOCGWINSZ.
374          */
375         row = col = 0;
376         if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
377                 row = win.ws_row;
378                 col = win.ws_col;
379         }
380         /* If here because of suspend or a signal, only trust TIOCGWINSZ. */
381         if (sigwinch) {
382                 /*
383                  * Somebody didn't get TIOCGWINSZ right, or has suspend
384                  * without window resizing support.  The user just lost,
385                  * but there's nothing we can do.
386                  */
387                 if (row == 0 || col == 0) {
388                         if (changedp != NULL)
389                                 *changedp = 0;
390                         return (0);
391                 }
392
393                 /*
394                  * SunOS systems deliver SIGWINCH when windows are uncovered
395                  * as well as when they change size.  In addition, we call
396                  * here when continuing after being suspended since the window
397                  * may have changed size.  Since we don't want to background
398                  * all of the screens just because the window was uncovered,
399                  * ignore the signal if there's no change.
400                  */
401                 if (sp != NULL &&
402                     row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) {
403                         if (changedp != NULL)
404                                 *changedp = 0;
405                         return (0);
406                 }
407
408                 if (rowp != NULL)
409                         *rowp = row;
410                 if (colp != NULL)
411                         *colp = col;
412                 return (0);
413         }
414
415         /*
416          * !!!
417          * If TIOCGWINSZ failed, or had entries of 0, try termcap.  This
418          * routine is called before any termcap or terminal information
419          * has been set up.  If there's no TERM environmental variable set,
420          * let it go, at least ex can run.
421          */
422         if (row == 0 || col == 0) {
423                 if ((p = getenv("TERM")) == NULL)
424                         goto noterm;
425                 if (row == 0)
426                         if ((rval = tigetnum("lines")) < 0)
427                                 msgq(sp, M_SYSERR, "tigetnum: lines");
428                         else
429                                 row = rval;
430                 if (col == 0)
431                         if ((rval = tigetnum("cols")) < 0)
432                                 msgq(sp, M_SYSERR, "tigetnum: cols");
433                         else
434                                 col = rval;
435         }
436
437         /* If nothing else, well, it's probably a VT100. */
438 noterm: if (row == 0)
439                 row = 24;
440         if (col == 0)
441                 col = 80;
442
443         /*
444          * !!!
445          * POSIX 1003.2 requires the environment to override everything.
446          * Often, people can get nvi to stop messing up their screen by
447          * deleting the LINES and COLUMNS environment variables from their
448          * dot-files.
449          */
450         if ((p = getenv("LINES")) != NULL)
451                 row = strtol(p, NULL, 10);
452         if ((p = getenv("COLUMNS")) != NULL)
453                 col = strtol(p, NULL, 10);
454
455         if (rowp != NULL)
456                 *rowp = row;
457         if (colp != NULL)
458                 *colp = col;
459         return (0);
460 }
461
462 /*
463  * cl_putchar --
464  *      Function version of putchar, for tputs.
465  *
466  * PUBLIC: int cl_putchar __P((int));
467  */
468 int
469 cl_putchar(int ch)
470 {
471         return (putchar(ch));
472 }