cl_term.c revision 281373
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
13static 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
37static 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 */
43typedef 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;
49static 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};
69static TKLIST const m1_tklist[] = {	/* Input mappings (lookup). */
70	{NULL},
71};
72static 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 */
86int
87cl_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 */
187int
188cl_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 */
211int
212cl_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 */
227static int
228cl_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 */
263int
264cl_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 */
311int
312cl_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 */
357int
358cl_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. */
443noterm:	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 */
473int
474cl_putchar(int ch)
475{
476	return (putchar(ch));
477}
478