termcap.c revision 1.22 1 /* $NetBSD: termcap.c,v 1.22 2017/05/04 09:42:23 roy Exp $ */
2
3 /*
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Roy Marples.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: termcap.c,v 1.22 2017/05/04 09:42:23 roy Exp $");
32
33 #include <assert.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdbool.h>
37 #include <stdint.h>
38 #include <string.h>
39 #include <term_private.h>
40 #include <term.h>
41 #include <termcap.h>
42 #include <unistd.h>
43 #include <stdio.h>
44
45 #include "termcap_map.c"
46 #include "termcap_hash.c"
47
48 char *UP;
49 char *BC;
50
51 /* ARGSUSED */
52 int
53 tgetent(__unused char *bp, const char *name)
54 {
55 int errret;
56 static TERMINAL *last = NULL;
57
58 _DIAGASSERT(name != NULL);
59
60 /* Free the old term */
61 if (cur_term != NULL) {
62 if (last != NULL && cur_term != last)
63 del_curterm(last);
64 last = cur_term;
65 }
66 errret = -1;
67 if (setupterm(name, STDOUT_FILENO, &errret) != 0)
68 return errret;
69
70 if (last == NULL)
71 last = cur_term;
72
73 if (pad_char != NULL)
74 PC = pad_char[0];
75 UP = __UNCONST(cursor_up);
76 BC = __UNCONST(cursor_left);
77 return 1;
78 }
79
80 int
81 tgetflag(const char *id2)
82 {
83 uint32_t ind;
84 size_t i;
85 TERMUSERDEF *ud;
86 const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
87
88 if (cur_term == NULL)
89 return 0;
90
91 ind = _t_flaghash((const unsigned char *)id, strlen(id));
92 if (ind < __arraycount(_ti_cap_flagids)) {
93 if (strcmp(id, _ti_cap_flagids[ind].id) == 0)
94 return cur_term->flags[_ti_cap_flagids[ind].ti];
95 }
96 for (i = 0; i < cur_term->_nuserdefs; i++) {
97 ud = &cur_term->_userdefs[i];
98 if (ud->type == 'f' && strcmp(ud->id, id) == 0)
99 return ud->flag;
100 }
101 return 0;
102 }
103
104 int
105 tgetnum(const char *id2)
106 {
107 uint32_t ind;
108 size_t i;
109 TERMUSERDEF *ud;
110 const TENTRY *te;
111 const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
112
113 if (cur_term == NULL)
114 return -1;
115
116 ind = _t_numhash((const unsigned char *)id, strlen(id));
117 if (ind < __arraycount(_ti_cap_numids)) {
118 te = &_ti_cap_numids[ind];
119 if (strcmp(id, te->id) == 0) {
120 if (!VALID_NUMERIC(cur_term->nums[te->ti]))
121 return ABSENT_NUMERIC;
122 return cur_term->nums[te->ti];
123 }
124 }
125 for (i = 0; i < cur_term->_nuserdefs; i++) {
126 ud = &cur_term->_userdefs[i];
127 if (ud->type == 'n' && strcmp(ud->id, id) == 0) {
128 if (!VALID_NUMERIC(ud->num))
129 return ABSENT_NUMERIC;
130 return ud->num;
131 }
132 }
133 return -1;
134 }
135
136 char *
137 tgetstr(const char *id2, char **area)
138 {
139 uint32_t ind;
140 size_t i;
141 TERMUSERDEF *ud;
142 const char *str;
143 const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
144
145 if (cur_term == NULL)
146 return NULL;
147
148 str = NULL;
149 ind = _t_strhash((const unsigned char *)id, strlen(id));
150 if (ind < __arraycount(_ti_cap_strids)) {
151 if (strcmp(id, _ti_cap_strids[ind].id) == 0) {
152 str = cur_term->strs[_ti_cap_strids[ind].ti];
153 if (str == NULL)
154 return NULL;
155 }
156 }
157 if (str != NULL)
158 for (i = 0; i < cur_term->_nuserdefs; i++) {
159 ud = &cur_term->_userdefs[i];
160 if (ud->type == 's' && strcmp(ud->id, id) == 0)
161 str = ud->str;
162 }
163
164 /* XXX: FXIXME
165 * We should fix sgr0(me) as it has a slightly different meaning
166 * for termcap. */
167
168 if (str != NULL && area != NULL && *area != NULL) {
169 char *s;
170 s = *area;
171 strcpy(*area, str);
172 *area += strlen(*area) + 1;
173 return s;
174 }
175
176 return __UNCONST(str);
177 }
178
179 char *
180 tgoto(const char *cm, int destcol, int destline)
181 {
182 _DIAGASSERT(cm != NULL);
183 return tiparm(cm, destline, destcol);
184 }
185
186 static const char *
187 flagname(const char *key)
188 {
189 uint32_t idx;
190
191 idx = _t_flaghash((const unsigned char *)key, strlen(key));
192 if (idx < __arraycount(_ti_cap_flagids) &&
193 strcmp(key, _ti_cap_flagids[idx].id) == 0)
194 return _ti_flagid(_ti_cap_flagids[idx].ti);
195 return key;
196 }
197
198 static const char *
199 numname(const char *key)
200 {
201 uint32_t idx;
202
203 idx = _t_numhash((const unsigned char *)key, strlen(key));
204 if (idx < __arraycount(_ti_cap_numids) &&
205 strcmp(key, _ti_cap_numids[idx].id) == 0)
206 return _ti_numid(_ti_cap_numids[idx].ti);
207 return key;
208 }
209
210 static const char *
211 strname(const char *key)
212 {
213 uint32_t idx;
214
215 idx = _t_strhash((const unsigned char *)key, strlen(key));
216 if (idx < __arraycount(_ti_cap_strids) &&
217 strcmp(key, _ti_cap_strids[idx].id) == 0)
218 return _ti_strid(_ti_cap_strids[idx].ti);
219
220 if (strcmp(key, "tc") == 0)
221 return "use";
222
223 return key;
224 }
225
226 /* Print a parameter if needed */
227 static size_t
228 printparam(char **dst, char p, bool *nop)
229 {
230 if (*nop) {
231 *nop = false;
232 return 0;
233 }
234
235 *(*dst)++ = '%';
236 *(*dst)++ = 'p';
237 *(*dst)++ = '0' + p;
238 return 3;
239 }
240
241 /* Convert a termcap character into terminfo equivalents */
242 static size_t
243 printchar(char **dst, const char **src)
244 {
245 char v;
246 size_t l;
247
248 l = 4;
249 v = *++(*src);
250 if (v == '\\') {
251 v = *++(*src);
252 switch (v) {
253 case '0':
254 case '1':
255 case '2':
256 case '3':
257 v = 0;
258 while (isdigit((unsigned char) **src))
259 v = 8 * v + (*(*src)++ - '0');
260 (*src)--;
261 break;
262 case '\0':
263 v = '\\';
264 break;
265 }
266 } else if (v == '^')
267 v = *++(*src) & 0x1f;
268 *(*dst)++ = '%';
269 if (isgraph((unsigned char )v) &&
270 v != ',' && v != '\'' && v != '\\' && v != ':')
271 {
272 *(*dst)++ = '\'';
273 *(*dst)++ = v;
274 *(*dst)++ = '\'';
275 } else {
276 *(*dst)++ = '{';
277 if (v > 99) {
278 *(*dst)++ = '0'+ v / 100;
279 l++;
280 }
281 if (v > 9) {
282 *(*dst)++ = '0' + ((int) (v / 10)) % 10;
283 l++;
284 }
285 *(*dst)++ = '0' + v % 10;
286 *(*dst)++ = '}';
287 }
288 return l;
289 }
290
291 /* Convert termcap commands into terminfo commands */
292 static const char fmtB[] = "%p0%{10}%/%{16}%*%p0%{10}%m%+";
293 static const char fmtD[] = "%p0%p0%{2}%*%-";
294 static const char fmtIf[] = "%p0%p0%?";
295 static const char fmtThen[] = "%>%t";
296 static const char fmtElse[] = "%+%;";
297
298 static char *
299 strval(const char *val)
300 {
301 char *info, *ip, c, p;
302 const char *ps, *pe;
303 bool nop;
304 size_t len, l;
305
306 len = 1024; /* no single string should be bigger */
307 info = ip = malloc(len);
308 if (info == NULL)
309 return 0;
310
311 /* Move the = */
312 *ip++ = *val++;
313
314 /* Set ps and pe to point to the start and end of the padding */
315 if (isdigit((unsigned char)*val)) {
316 for (ps = pe = val;
317 isdigit((unsigned char)*val) || *val == '.';
318 val++)
319 pe++;
320 if (*val == '*') {
321 val++;
322 pe++;
323 }
324 } else
325 ps = pe = NULL;
326
327 nop = false;
328 l = 0;
329 p = 1;
330 for (; *val != '\0'; val++) {
331 if (l + 2 > len)
332 goto elen;
333 if (*val != '%') {
334 if (*val == ',') {
335 if (l + 3 > len)
336 goto elen;
337 *ip++ = '\\';
338 l++;
339 }
340 *ip++ = *val;
341 l++;
342 continue;
343 }
344 switch (c = *++(val)) {
345 case 'B':
346 if (l + sizeof(fmtB) > len)
347 goto elen;
348 memcpy(ip, fmtB, sizeof(fmtB) - 1);
349 /* Replace the embedded parameters with real ones */
350 ip[2] += p;
351 ip[19] += p;
352 ip += sizeof(fmtB) - 1;
353 l += sizeof(fmtB) - 1;
354 nop = true;
355 continue;
356 case 'D':
357 if (l + sizeof(fmtD) > len)
358 goto elen;
359 memcpy(ip, fmtD, sizeof(fmtD) - 1);
360 /* Replace the embedded parameters with real ones */
361 ip[2] += p;
362 ip[5] += p;
363 ip += sizeof(fmtD) - 1;
364 l += sizeof(fmtD) - 1;
365 nop = true;
366 continue;
367 case 'r':
368 /* non op as switched below */
369 break;
370 case '2': /* FALLTHROUGH */
371 case '3': /* FALLTHROUGH */
372 case 'd':
373 if (l + 7 > len)
374 goto elen;
375 l += printparam(&ip, p, &nop);
376 *ip++ = '%';
377 if (c != 'd') {
378 *ip++ = c;
379 l++;
380 }
381 *ip++ = 'd';
382 l += 2;
383 break;
384 case '+':
385 if (l + 13 > len)
386 goto elen;
387 l += printparam(&ip, p, &nop);
388 l += printchar(&ip, &val);
389 *ip++ = '%';
390 *ip++ = c;
391 *ip++ = '%';
392 *ip++ = 'c';
393 l += 7;
394 break;
395 case '>':
396 if (l + sizeof(fmtIf) + sizeof(fmtThen) +
397 sizeof(fmtElse) + (6 * 2) > len)
398 goto elen;
399
400 memcpy(ip, fmtIf, sizeof(fmtIf) - 1);
401 /* Replace the embedded parameters with real ones */
402 ip[2] += p;
403 ip[5] += p;
404 ip += sizeof(fmtIf) - 1;
405 l += sizeof(fmtIf) - 1;
406 l += printchar(&ip, &val);
407 memcpy(ip, fmtThen, sizeof(fmtThen) - 1);
408 ip += sizeof(fmtThen) - 1;
409 l += sizeof(fmtThen) - 1;
410 l += printchar(&ip, &val);
411 memcpy(ip, fmtElse, sizeof(fmtElse) - 1);
412 ip += sizeof(fmtElse) - 1;
413 l += sizeof(fmtElse) - 1;
414 l += 16;
415 nop = true;
416 continue;
417 case '.':
418 if (l + 6 > len)
419 goto elen;
420 l += printparam(&ip, p, &nop);
421 *ip++ = '%';
422 *ip++ = 'c';
423 l += 2;
424 break;
425 default:
426 /* Hope it matches a terminfo command. */
427 *ip++ = '%';
428 *ip++ = c;
429 l += 2;
430 if (c == 'i')
431 continue;
432 break;
433 }
434 /* Swap p1 and p2 */
435 p = 3 - p;
436 }
437
438 /* \E\ is valid termcap.
439 * We need to escape the final \ for terminfo. */
440 if (l > 2 && info[l - 1] == '\\' &&
441 (info[l - 2] != '\\' && info[l - 2] != '^'))
442 {
443 if (l + 1 > len)
444 goto elen;
445 *ip++ = '\\';
446 }
447
448 /* Add our padding at the end. */
449 if (ps != NULL) {
450 size_t n = (size_t)(pe - ps);
451 if (l + n + 4 > len)
452 goto elen;
453 *ip++ = '$';
454 *ip++ = '<';
455 strncpy(ip, ps, n);
456 ip += n;
457 *ip++ = '/';
458 *ip++ = '>';
459 }
460
461 *ip = '\0';
462 return info;
463
464 elen:
465 free(info);
466 errno = ENOMEM;
467 return NULL;
468 }
469
470 typedef struct {
471 const char *name;
472 const char *cap;
473 } DEF_INFO;
474
475 static DEF_INFO def_infos[] = {
476 { "bel", "^G" },
477 { "cr", "^M" },
478 { "cud1", "^J" },
479 { "ht", "^I" },
480 { "ind", "^J" },
481 { "kbs", "^H" },
482 { "kcub1", "^H" },
483 { "kcud1", "^J" },
484 { "nel", "^M^J" }
485 };
486
487 char *
488 captoinfo(char *cap)
489 {
490 char *info, *ip, *token, *val, *p, tok[3];
491 const char *name;
492 size_t len, lp, nl, vl, rl;
493 int defs[__arraycount(def_infos)], fv;
494
495 _DIAGASSERT(cap != NULL);
496
497 len = strlen(cap) * 2;
498 len += __arraycount(def_infos) * (5 + 4 + 3); /* reserve for defs */
499 info = ip = malloc(len);
500 if (info == NULL)
501 return NULL;
502
503 memset(defs, 0, sizeof(defs));
504 lp = 0;
505 tok[2] = '\0';
506 for (token = _ti_get_token(&cap, ':');
507 token != NULL;
508 token = _ti_get_token(&cap, ':'))
509 {
510 if (token[0] == '\0')
511 continue;
512 name = token;
513 val = p = NULL;
514 fv = nl = 0;
515 if (token[1] != '\0') {
516 tok[0] = token[0];
517 tok[1] = token[1];
518 nl = 1;
519 if (token[2] == '\0') {
520 name = flagname(tok);
521 val = NULL;
522 } else if (token[2] == '#') {
523 name = numname(tok);
524 val = token + 2;
525 } else if (token[2] == '=') {
526 name = strname(tok);
527 val = strval(token + 2);
528 fv = 1;
529 } else
530 nl = 0;
531 }
532 /* If not matched we may need to convert padding still. */
533 if (nl == 0) {
534 p = strchr(name, '=');
535 if (p != NULL) {
536 val = strval(p);
537 *p = '\0';
538 fv = 1;
539 }
540 }
541
542 /* See if this sets a default. */
543 for (nl = 0; nl < __arraycount(def_infos); nl++) {
544 if (strcmp(name, def_infos[nl].name) == 0) {
545 defs[nl] = 1;
546 break;
547 }
548 }
549
550 nl = strlen(name);
551 if (val == NULL)
552 vl = 0;
553 else
554 vl = strlen(val);
555 rl = nl + vl + 3; /* , \0 */
556
557 if (lp + rl > len) {
558 if (rl < 256)
559 len += 256;
560 else
561 len += rl;
562 p = realloc(info, len);
563 if (p == NULL) {
564 if (fv == 1)
565 free(val);
566 return NULL;
567 }
568 info = p;
569 }
570
571 if (ip != info) {
572 *ip++ = ',';
573 *ip++ = ' ';
574 }
575
576 strcpy(ip, name);
577 ip += nl;
578 if (val != NULL) {
579 strcpy(ip, val);
580 ip += vl;
581 if (fv == 1)
582 free(val);
583 }
584 }
585
586 /* Add any defaults not set above. */
587 for (nl = 0; nl < __arraycount(def_infos); nl++) {
588 if (defs[nl] == 0) {
589 *ip++ = ',';
590 *ip++ = ' ';
591 strcpy(ip, def_infos[nl].name);
592 ip += strlen(def_infos[nl].name);
593 *ip++ = '=';
594 strcpy(ip, def_infos[nl].cap);
595 ip += strlen(def_infos[nl].cap);
596 }
597 }
598
599 *ip = '\0';
600 return info;
601 }
602
603