GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../cl/cl_term.c Lines: 0 127 0.0 %
Date: 2017-11-07 Branches: 0 128 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: cl_term.c,v 1.28 2017/07/20 08:37:48 anton Exp $	*/
2
3
/*-
4
 * Copyright (c) 1993, 1994
5
 *	The Regents of the University of California.  All rights reserved.
6
 * Copyright (c) 1993, 1994, 1995, 1996
7
 *	Keith Bostic.  All rights reserved.
8
 *
9
 * See the LICENSE file for redistribution information.
10
 */
11
12
#include "config.h"
13
14
#include <sys/types.h>
15
#include <sys/ioctl.h>
16
#include <sys/queue.h>
17
#include <sys/stat.h>
18
19
#include <bitstring.h>
20
#include <curses.h>
21
#include <limits.h>
22
#include <signal.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <termios.h>
27
#include <unistd.h>
28
29
#include "../common/common.h"
30
#include "cl.h"
31
32
static int cl_pfmap(SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t);
33
34
/*
35
 * XXX
36
 * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
37
 */
38
typedef struct _tklist {
39
	char	*ts;			/* Key's termcap string. */
40
	char	*output;		/* Corresponding vi command. */
41
	char	*name;			/* Name. */
42
} TKLIST;
43
static TKLIST const c_tklist[] = {	/* Command mappings. */
44
	{"kil1",	"O",	"insert line"},
45
	{"kdch1",	"x",	"delete character"},
46
	{"kcud1",	"j",	"cursor down"},
47
	{"kel",		"D",	"delete to eol"},
48
	{"kind",     "\004",	"scroll down"},			/* ^D */
49
	{"kll",		"$",	"go to eol"},
50
	{"khome",	"^",	"go to sol"},
51
	{"kich1",	"i",	"insert at cursor"},
52
	{"kdl1",       "dd",	"delete line"},
53
	{"kcub1",	"h",	"cursor left"},
54
	{"knp",	     "\006",	"page down"},			/* ^F */
55
	{"kpp",	     "\002",	"page up"},			/* ^B */
56
	{"kri",	     "\025",	"scroll up"},			/* ^U */
57
	{"ked",	       "dG",	"delete to end of screen"},
58
	{"kcuf1",	"l",	"cursor right"},
59
	{"kcuu1",	"k",	"cursor up"},
60
	{NULL, NULL, NULL},
61
};
62
static TKLIST const m1_tklist[] = {	/* Input mappings (set or delete). */
63
	{"kcud1",  "\033ja",	"cursor down"},			/* ^[ja */
64
	{"kcub1",  "\033ha",	"cursor left"},			/* ^[ha */
65
	{"kcuu1",  "\033ka",	"cursor up"},			/* ^[ka */
66
	{"kcuf1",  "\033la",	"cursor right"},		/* ^[la */
67
	{NULL, NULL, NULL},
68
};
69
70
/*
71
 * cl_term_init --
72
 *	Initialize the special keys defined by the termcap/terminfo entry.
73
 *
74
 * PUBLIC: int cl_term_init(SCR *);
75
 */
76
int
77
cl_term_init(SCR *sp)
78
{
79
	SEQ *qp;
80
	TKLIST const *tkp;
81
	char *t;
82
83
	/* Command mappings. */
84
	for (tkp = c_tklist; tkp->name != NULL; ++tkp) {
85
		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
86
			continue;
87
		if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t),
88
		    tkp->output, strlen(tkp->output), SEQ_COMMAND,
89
		    SEQ_NOOVERWRITE | SEQ_SCREEN))
90
			return (1);
91
	}
92
93
	/* Input mappings that are already set or are text deletions. */
94
	for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {
95
		if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
96
			continue;
97
		/*
98
		 * !!!
99
		 * Some terminals' <cursor_left> keys send single <backspace>
100
		 * characters.  This is okay in command mapping, but not okay
101
		 * in input mapping.  That combination is the only one we'll
102
		 * ever see, hopefully, so kluge it here for now.
103
		 */
104
		if (!strcmp(t, "\b"))
105
			continue;
106
		if (tkp->output == NULL) {
107
			if (seq_set(sp, tkp->name, strlen(tkp->name),
108
			    t, strlen(t), NULL, 0,
109
			    SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
110
				return (1);
111
		} else
112
			if (seq_set(sp, tkp->name, strlen(tkp->name),
113
			    t, strlen(t), tkp->output, strlen(tkp->output),
114
			    SEQ_INPUT, SEQ_NOOVERWRITE | SEQ_SCREEN))
115
				return (1);
116
	}
117
118
	/*
119
	 * Rework any function key mappings that were set before the
120
	 * screen was initialized.
121
	 */
122
	LIST_FOREACH(qp, & sp->gp->seqq, q)
123
		if (F_ISSET(qp, SEQ_FUNCMAP))
124
			(void)cl_pfmap(sp, qp->stype,
125
			    qp->input, qp->ilen, qp->output, qp->olen);
126
	return (0);
127
}
128
129
/*
130
 * cl_term_end --
131
 *	End the special keys defined by the termcap/terminfo entry.
132
 *
133
 * PUBLIC: int cl_term_end(GS *);
134
 */
135
int
136
cl_term_end(GS *gp)
137
{
138
	SEQ *qp, *nqp;
139
140
	/* Delete screen specific mappings. */
141
	for (qp = LIST_FIRST(&gp->seqq); qp != NULL; qp = nqp) {
142
		nqp = LIST_NEXT(qp, q);
143
		if (F_ISSET(qp, SEQ_SCREEN))
144
			(void)seq_mdel(qp);
145
	}
146
	return (0);
147
}
148
149
/*
150
 * cl_fmap --
151
 *	Map a function key.
152
 *
153
 * PUBLIC: int cl_fmap(SCR *, seq_t, CHAR_T *, size_t, CHAR_T *, size_t);
154
 */
155
int
156
cl_fmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to,
157
    size_t tlen)
158
{
159
	/* Ignore until the screen is running, do the real work then. */
160
	if (F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_SCR_VI))
161
		return (0);
162
	if (F_ISSET(sp, SC_EX) && !F_ISSET(sp, SC_SCR_EX))
163
		return (0);
164
165
	return (cl_pfmap(sp, stype, from, flen, to, tlen));
166
}
167
168
/*
169
 * cl_pfmap --
170
 *	Map a function key (private version).
171
 */
172
static int
173
cl_pfmap(SCR *sp, seq_t stype, CHAR_T *from, size_t flen, CHAR_T *to,
174
    size_t tlen)
175
{
176
	size_t nlen;
177
	char *p, key_name[64];
178
179
	(void)snprintf(key_name, sizeof(key_name), "kf%d", atoi(from + 1));
180
	if ((p = tigetstr(key_name)) == NULL ||
181
	    p == (char *)-1 || strlen(p) == 0)
182
		p = NULL;
183
	if (p == NULL) {
184
		msgq_str(sp, M_ERR, from, "This terminal has no %s key");
185
		return (1);
186
	}
187
188
	nlen = snprintf(key_name,
189
	    sizeof(key_name), "function key %d", atoi(from + 1));
190
	if (nlen >= sizeof(key_name))
191
		nlen = sizeof(key_name) - 1;
192
	return (seq_set(sp, key_name, nlen,
193
	    p, strlen(p), to, tlen, stype, SEQ_NOOVERWRITE | SEQ_SCREEN));
194
}
195
196
/*
197
 * cl_optchange --
198
 *	Curses screen specific "option changed" routine.
199
 *
200
 * PUBLIC: int cl_optchange(SCR *, int, char *, u_long *);
201
 */
202
int
203
cl_optchange(SCR *sp, int opt, char *str, u_long *valp)
204
{
205
	CL_PRIVATE *clp;
206
207
	clp = CLP(sp);
208
209
	switch (opt) {
210
	case O_TERM:
211
		F_CLR(sp, SC_SCR_EX | SC_SCR_VI);
212
		/* FALLTHROUGH */
213
	case O_COLUMNS:
214
	case O_LINES:
215
		/*
216
		 * Changing the terminal type requires that we reinitialize
217
		 * curses, while resizing does not.
218
		 */
219
		F_SET(sp->gp, G_SRESTART);
220
		break;
221
	case O_MESG:
222
		(void)cl_omesg(sp, clp, !*valp);
223
		break;
224
	case O_WINDOWNAME:
225
		if (*valp) {
226
			F_CLR(clp, CL_RENAME_OK);
227
228
			(void)cl_rename(sp, NULL, 0);
229
		} else {
230
			F_SET(clp, CL_RENAME_OK);
231
232
			/*
233
			 * If the screen is live, i.e. we're not reading the
234
			 * .exrc file, update the window.
235
			 */
236
			if (sp->frp != NULL && sp->frp->name != NULL)
237
				(void)cl_rename(sp, sp->frp->name, 1);
238
		}
239
		break;
240
	}
241
	return (0);
242
}
243
244
/*
245
 * cl_omesg --
246
 *	Turn the tty write permission on or off.
247
 *
248
 * PUBLIC: int cl_omesg(SCR *, CL_PRIVATE *, int);
249
 */
250
int
251
cl_omesg(SCR *sp, CL_PRIVATE *clp, int on)
252
{
253
	struct stat sb;
254
	char *tty;
255
256
	/* Find the tty, get the current permissions. */
257
	if ((tty = ttyname(STDERR_FILENO)) == NULL) {
258
		if (sp != NULL)
259
			msgq(sp, M_SYSERR, "stderr");
260
		return (1);
261
	}
262
	if (stat(tty, &sb) < 0) {
263
		if (sp != NULL)
264
			msgq(sp, M_SYSERR, "%s", tty);
265
		return (1);
266
	}
267
	sb.st_mode &= ACCESSPERMS;
268
269
	/* Save the original status if it's unknown. */
270
	if (clp->tgw == TGW_UNKNOWN)
271
		clp->tgw = sb.st_mode & S_IWGRP ? TGW_SET : TGW_UNSET;
272
273
	/* Toggle the permissions. */
274
	if (on) {
275
		if (chmod(tty, sb.st_mode | S_IWGRP) < 0) {
276
			if (sp != NULL)
277
				msgq(sp, M_SYSERR,
278
				    "messages not turned on: %s", tty);
279
			return (1);
280
		}
281
	} else
282
		if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0) {
283
			if (sp != NULL)
284
				msgq(sp, M_SYSERR,
285
				    "messages not turned off: %s", tty);
286
			return (1);
287
		}
288
	return (0);
289
}
290
291
/*
292
 * cl_ssize --
293
 *	Return the terminal size.
294
 *
295
 * PUBLIC: int cl_ssize(SCR *, int, size_t *, size_t *, int *);
296
 */
297
int
298
cl_ssize(SCR *sp, int sigwinch, size_t *rowp, size_t *colp, int *changedp)
299
{
300
	struct winsize win;
301
	size_t col, row;
302
	int rval;
303
	char *p;
304
305
	/* Assume it's changed. */
306
	if (changedp != NULL)
307
		*changedp = 1;
308
309
	/*
310
	 * !!!
311
	 * sp may be NULL.
312
	 *
313
	 * Get the screen rows and columns.  If the values are wrong, it's
314
	 * not a big deal -- as soon as the user sets them explicitly the
315
	 * environment will be set and the screen package will use the new
316
	 * values.
317
	 *
318
	 * Try TIOCGWINSZ.
319
	 */
320
	row = col = 0;
321
	if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
322
		row = win.ws_row;
323
		col = win.ws_col;
324
	}
325
	/* If here because of suspend or a signal, only trust TIOCGWINSZ. */
326
	if (sigwinch) {
327
		/*
328
		 * Somebody didn't get TIOCGWINSZ right, or has suspend
329
		 * without window resizing support.  The user just lost,
330
		 * but there's nothing we can do.
331
		 */
332
		if (row == 0 || col == 0) {
333
			if (changedp != NULL)
334
				*changedp = 0;
335
			return (0);
336
		}
337
338
		/*
339
		 * SunOS systems deliver SIGWINCH when windows are uncovered
340
		 * as well as when they change size.  In addition, we call
341
		 * here when continuing after being suspended since the window
342
		 * may have changed size.  Since we don't want to background
343
		 * all of the screens just because the window was uncovered,
344
		 * ignore the signal if there's no change.
345
		 */
346
		if (sp != NULL &&
347
		    row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS)) {
348
			if (changedp != NULL)
349
				*changedp = 0;
350
			return (0);
351
		}
352
353
		if (rowp != NULL)
354
			*rowp = row;
355
		if (colp != NULL)
356
			*colp = col;
357
		return (0);
358
	}
359
360
	/*
361
	 * !!!
362
	 * If TIOCGWINSZ failed, or had entries of 0, try termcap.  This
363
	 * routine is called before any termcap or terminal information
364
	 * has been set up.  If there's no TERM environmental variable set,
365
	 * let it go, at least ex can run.
366
	 */
367
	if (row == 0 || col == 0) {
368
		if ((p = getenv("TERM")) == NULL)
369
			goto noterm;
370
		if (row == 0) {
371
			if ((rval = tigetnum("lines")) < 0)
372
				msgq(sp, M_SYSERR, "tigetnum: lines");
373
			else
374
				row = rval;
375
		}
376
		if (col == 0) {
377
			if ((rval = tigetnum("cols")) < 0)
378
				msgq(sp, M_SYSERR, "tigetnum: cols");
379
			else
380
				col = rval;
381
		}
382
	}
383
384
	/* If nothing else, well, it's probably a VT100. */
385
noterm:	if (row == 0)
386
		row = 24;
387
	if (col == 0)
388
		col = 80;
389
390
	/*
391
	 * !!!
392
	 * POSIX 1003.2 requires the environment to override everything.
393
	 * Often, people can get nvi to stop messing up their screen by
394
	 * deleting the LINES and COLUMNS environment variables from their
395
	 * dot-files.
396
	 */
397
	if ((p = getenv("LINES")) != NULL &&
398
	    (rval = strtonum(p, 1, INT_MAX, NULL)) > 0)
399
		row = rval;
400
	if ((p = getenv("COLUMNS")) != NULL &&
401
	    (rval = strtonum(p, 1, INT_MAX, NULL)) > 0)
402
		col = rval;
403
404
	if (rowp != NULL)
405
		*rowp = row;
406
	if (colp != NULL)
407
		*colp = col;
408
	return (0);
409
}
410
411
/*
412
 * cl_putchar --
413
 *	Function version of putchar, for tputs.
414
 *
415
 * PUBLIC: int cl_putchar(int);
416
 */
417
int
418
cl_putchar(int ch)
419
{
420
	return (putchar(ch));
421
}