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