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