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 |
|
|
} |