cl_term.c revision 254225
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.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
37static 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 */
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 __P((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 __P((GS *));
186 */
187int
188cl_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 */
207int
208cl_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 */
223static int
224cl_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 */
259int
260cl_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 */
306int
307cl_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 */
352int
353cl_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. */
438noterm:	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 */
468int
469cl_putchar(int ch)
470{
471	return (putchar(ch));
472}
473