tools/llvm: Do not build with symbols
[minix3.git] / lib / libterminfo / termcap.c
blobaed6080177d35ce8aa67d49c909f5d0580a6b767
1 /* $NetBSD: termcap.c,v 1.17 2011/11/13 15:24:04 christos Exp $ */
3 /*
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Roy Marples.
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.
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.
30 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: termcap.c,v 1.17 2011/11/13 15:24:04 christos Exp $");
33 #include <assert.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <stdint.h>
37 #include <string.h>
38 #include <term_private.h>
39 #include <term.h>
40 #include <termcap.h>
41 #include <unistd.h>
42 #include <stdio.h>
44 #include "termcap_map.c"
45 #include "termcap_hash.c"
47 char *UP;
48 char *BC;
50 /* ARGSUSED */
51 int
52 tgetent(__unused char *bp, const char *name)
54 int errret;
55 static TERMINAL *last = NULL;
57 _DIAGASSERT(name != NULL);
59 /* Free the old term */
60 if (last != NULL) {
61 del_curterm(last);
62 last = NULL;
64 errret = -1;
65 if (setupterm(name, STDOUT_FILENO, &errret) != 0)
66 return errret;
67 last = cur_term;
69 if (pad_char != NULL)
70 PC = pad_char[0];
71 UP = __UNCONST(cursor_up);
72 BC = __UNCONST(cursor_left);
73 return 1;
76 int
77 tgetflag(const char *id2)
79 uint32_t ind;
80 size_t i;
81 TERMUSERDEF *ud;
82 const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
84 if (cur_term == NULL)
85 return 0;
87 ind = _t_flaghash((const unsigned char *)id, strlen(id));
88 if (ind <= __arraycount(_ti_cap_flagids)) {
89 if (strcmp(id, _ti_cap_flagids[ind].id) == 0)
90 return cur_term->flags[_ti_cap_flagids[ind].ti];
92 for (i = 0; i < cur_term->_nuserdefs; i++) {
93 ud = &cur_term->_userdefs[i];
94 if (ud->type == 'f' && strcmp(ud->id, id) == 0)
95 return ud->flag;
97 return 0;
101 tgetnum(const char *id2)
103 uint32_t ind;
104 size_t i;
105 TERMUSERDEF *ud;
106 const TENTRY *te;
107 const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
109 if (cur_term == NULL)
110 return -1;
112 ind = _t_numhash((const unsigned char *)id, strlen(id));
113 if (ind <= __arraycount(_ti_cap_numids)) {
114 te = &_ti_cap_numids[ind];
115 if (strcmp(id, te->id) == 0) {
116 if (!VALID_NUMERIC(cur_term->nums[te->ti]))
117 return ABSENT_NUMERIC;
118 return cur_term->nums[te->ti];
121 for (i = 0; i < cur_term->_nuserdefs; i++) {
122 ud = &cur_term->_userdefs[i];
123 if (ud->type == 'n' && strcmp(ud->id, id) == 0) {
124 if (!VALID_NUMERIC(ud->num))
125 return ABSENT_NUMERIC;
126 return ud->num;
129 return -1;
132 char *
133 tgetstr(const char *id2, char **area)
135 uint32_t ind;
136 size_t i;
137 TERMUSERDEF *ud;
138 const char *str;
139 const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
141 if (cur_term == NULL)
142 return NULL;
144 str = NULL;
145 ind = _t_strhash((const unsigned char *)id, strlen(id));
146 if (ind <= __arraycount(_ti_cap_strids)) {
147 if (strcmp(id, _ti_cap_strids[ind].id) == 0) {
148 str = cur_term->strs[_ti_cap_strids[ind].ti];
149 if (str == NULL)
150 return NULL;
153 if (str != NULL)
154 for (i = 0; i < cur_term->_nuserdefs; i++) {
155 ud = &cur_term->_userdefs[i];
156 if (ud->type == 's' && strcmp(ud->id, id) == 0)
157 str = ud->str;
160 /* XXX: FXIXME
161 * We should fix sgr0(me) as it has a slightly different meaning
162 * for termcap. */
164 if (str != NULL && area != NULL && *area != NULL) {
165 char *s;
166 s = *area;
167 strcpy(*area, str);
168 *area += strlen(*area) + 1;
169 return s;
172 return __UNCONST(str);
175 char *
176 tgoto(const char *cm, int destcol, int destline)
178 _DIAGASSERT(cm != NULL);
179 return tiparm(cm, destline, destcol);
182 static const char *
183 flagname(const char *key)
185 uint32_t idx;
187 idx = _t_flaghash((const unsigned char *)key, strlen(key));
188 if (idx <= __arraycount(_ti_cap_flagids) &&
189 strcmp(key, _ti_cap_flagids[idx].id) == 0)
190 return _ti_flagid(_ti_cap_flagids[idx].ti);
191 return key;
194 static const char *
195 numname(const char *key)
197 uint32_t idx;
199 idx = _t_numhash((const unsigned char *)key, strlen(key));
200 if (idx <= __arraycount(_ti_cap_numids) &&
201 strcmp(key, _ti_cap_numids[idx].id) == 0)
202 return _ti_numid(_ti_cap_numids[idx].ti);
203 return key;
206 static const char *
207 strname(const char *key)
209 uint32_t idx;
211 idx = _t_strhash((const unsigned char *)key, strlen(key));
212 if (idx <= __arraycount(_ti_cap_strids) &&
213 strcmp(key, _ti_cap_strids[idx].id) == 0)
214 return _ti_strid(_ti_cap_strids[idx].ti);
216 if (strcmp(key, "tc") == 0)
217 return "use";
219 return key;
222 /* Print a parameter if needed */
223 static int
224 printparam(char **dst, char p, int *nop)
226 if (*nop != 0) {
227 *nop = 0;
228 return 0;
231 *(*dst)++ = '%';
232 *(*dst)++ = 'p';
233 *(*dst)++ = '0' + p;
234 return 3;
237 /* Convert a termcap character into terminfo equivalents */
238 static int
239 printchar(char **dst, const char **src)
241 unsigned char v;
242 int l;
244 l = 4;
245 v = (unsigned char) *++(*src);
246 if (v == '\\') {
247 v = (unsigned char) *++(*src);
248 switch (v) {
249 case '0':
250 case '1':
251 case '2':
252 case '3':
253 v = 0;
254 while (isdigit((unsigned char) **src))
255 v = 8 * v + ((unsigned char) *(*src)++ - '0');
256 (*src)--;
257 break;
258 case '\0':
259 v = '\\';
260 break;
262 } else if (v == '^')
263 v = (unsigned char) (*++(*src) & 0x1f);
264 *(*dst)++ = '%';
265 if (isgraph(v) && v != ',' && v != '\'' && v != '\\' && v != ':') {
266 *(*dst)++ = '\'';
267 *(*dst)++ = v;
268 *(*dst)++ = '\'';
269 } else {
270 *(*dst)++ = '{';
271 if (v > 99) {
272 *(*dst)++ = '0'+ v / 100;
273 l++;
275 if (v > 9) {
276 *(*dst)++ = '0' + ((int) (v / 10)) % 10;
277 l++;
279 *(*dst)++ = '0' + v % 10;
280 *(*dst)++ = '}';
282 return l;
285 /* Convert termcap commands into terminfo commands */
286 static const char fmtB[] = "%p0%{10}%/%{16}%*%p0%{10}%m%+";
287 static const char fmtD[] = "%p0%p0%{2}%*%-";
288 static const char fmtIf[] = "%p0%p0%?";
289 static const char fmtThen[] = "%>%t";
290 static const char fmtElse[] = "%+%;";
292 static char *
293 strval(const char *val)
295 char *info, *ip, c;
296 const char *ps, *pe;
297 int p, nop;
298 size_t len, l;
300 len = 1024; /* no single string should be bigger */
301 info = ip = malloc(len);
302 if (info == NULL)
303 return 0;
305 /* Move the = */
306 *ip++ = *val++;
308 /* Set ps and pe to point to the start and end of the padding */
309 if (isdigit((unsigned char)*val)) {
310 for (ps = pe = val;
311 isdigit((unsigned char)*val) || *val == '.';
312 val++)
313 pe++;
314 if (*val == '*') {
315 val++;
316 pe++;
318 } else
319 ps = pe = NULL;
321 l = nop = 0;
322 p = 1;
323 for (; *val != '\0'; val++) {
324 if (l + 2 > len)
325 goto elen;
326 if (*val != '%') {
327 if (*val == ',') {
328 if (l + 3 > len)
329 goto elen;
330 *ip++ = '\\';
331 l++;
333 *ip++ = *val;
334 l++;
335 continue;
337 switch (c = *++(val)) {
338 case 'B':
339 if (l + sizeof(fmtB) > len)
340 goto elen;
341 memcpy(ip, fmtB, sizeof(fmtB) - 1);
342 /* Replace the embedded parameters with real ones */
343 ip[2] += p;
344 ip[19] += p;
345 ip += sizeof(fmtB) - 1;
346 l += sizeof(fmtB) - 1;
347 nop = 1;
348 continue;
349 case 'D':
350 if (l + sizeof(fmtD) > len)
351 goto elen;
352 memcpy(ip, fmtD, sizeof(fmtD) - 1);
353 /* Replace the embedded parameters with real ones */
354 ip[2] += p;
355 ip[5] += p;
356 ip += sizeof(fmtD) - 1;
357 l += sizeof(fmtD) - 1;
358 nop = 1;
359 continue;
360 case 'r':
361 /* non op as switched below */
362 break;
363 case '2': /* FALLTHROUGH */
364 case '3': /* FALLTHROUGH */
365 case 'd':
366 if (l + 7 > len)
367 goto elen;
368 l += printparam(&ip, p, &nop);
369 *ip++ = '%';
370 if (c != 'd') {
371 *ip++ = c;
372 l++;
374 *ip++ = 'd';
375 l += 2;
376 break;
377 case '+':
378 if (l + 13 > len)
379 goto elen;
380 l += printparam(&ip, p, &nop);
381 l += printchar(&ip, &val);
382 *ip++ = '%';
383 *ip++ = c;
384 *ip++ = '%';
385 *ip++ = 'c';
386 l += 7;
387 break;
388 case '>':
389 if (l + sizeof(fmtIf) + sizeof(fmtThen) +
390 sizeof(fmtElse) + (6 * 2) > len)
391 goto elen;
393 memcpy(ip, fmtIf, sizeof(fmtIf) - 1);
394 /* Replace the embedded parameters with real ones */
395 ip[2] += p;
396 ip[5] += p;
397 ip += sizeof(fmtIf) - 1;
398 l += sizeof(fmtIf) - 1;
399 l += printchar(&ip, &val);
400 memcpy(ip, fmtThen, sizeof(fmtThen) - 1);
401 ip += sizeof(fmtThen) - 1;
402 l += sizeof(fmtThen) - 1;
403 l += printchar(&ip, &val);
404 memcpy(ip, fmtElse, sizeof(fmtElse) - 1);
405 ip += sizeof(fmtElse) - 1;
406 l += sizeof(fmtElse) - 1;
407 l += 16;
408 nop = 1;
409 continue;
410 case '.':
411 if (l + 6 > len)
412 goto elen;
413 l += printparam(&ip, p, &nop);
414 *ip++ = '%';
415 *ip++ = 'c';
416 l += 2;
417 break;
418 default:
419 /* Hope it matches a terminfo command. */
420 *ip++ = '%';
421 *ip++ = c;
422 l += 2;
423 if (c == 'i')
424 continue;
425 break;
427 /* Swap p1 and p2 */
428 p = 3 - p;
431 /* \E\ is valid termcap.
432 * We need to escape the final \ for terminfo. */
433 if (l > 2 && info[l - 1] == '\\' &&
434 (info[l - 2] != '\\' && info[l - 2] != '^'))
436 if (l + 1 > len)
437 goto elen;
438 *ip++ = '\\';
441 /* Add our padding at the end. */
442 if (ps != NULL) {
443 size_t n = pe - ps;
444 if (l + n + 4 > len)
445 goto elen;
446 *ip++ = '$';
447 *ip++ = '<';
448 strncpy(ip, ps, n);
449 ip += n;
450 *ip++ = '/';
451 *ip++ = '>';
454 *ip = '\0';
455 return info;
457 elen:
458 free(info);
459 errno = ENOMEM;
460 return NULL;
463 typedef struct {
464 const char *name;
465 const char *cap;
466 } DEF_INFO;
468 static DEF_INFO def_infos[] = {
469 { "bel", "^G" },
470 { "cr", "^M" },
471 { "cud1", "^J" },
472 { "ht", "^I" },
473 { "ind", "^J" },
474 { "kbs", "^H" },
475 { "kcub1", "^H" },
476 { "kcud1", "^J" },
477 { "nel", "^M^J" }
480 char *
481 captoinfo(char *cap)
483 char *info, *ip, *token, *val, *p, tok[3];
484 const char *name;
485 size_t len, lp, nl, vl, rl;
486 int defs[__arraycount(def_infos)], fv;
488 _DIAGASSERT(cap != NULL);
490 len = strlen(cap) * 2;
491 len += __arraycount(def_infos) * (5 + 4 + 3); /* reserve for defs */
492 info = ip = malloc(len);
493 if (info == NULL)
494 return NULL;
496 memset(defs, 0, sizeof(defs));
497 lp = 0;
498 tok[2] = '\0';
499 for (token = _ti_get_token(&cap, ':');
500 token != NULL;
501 token = _ti_get_token(&cap, ':'))
503 if (token[0] == '\0')
504 continue;
505 name = token;
506 val = p = NULL;
507 fv = nl = 0;
508 if (token[1] != '\0') {
509 tok[0] = token[0];
510 tok[1] = token[1];
511 nl = 1;
512 if (token[2] == '\0') {
513 name = flagname(tok);
514 val = NULL;
515 } else if (token[2] == '#') {
516 name = numname(tok);
517 val = token + 2;
518 } else if (token[2] == '=') {
519 name = strname(tok);
520 val = strval(token + 2);
521 fv = 1;
522 } else
523 nl = 0;
525 /* If not matched we may need to convert padding still. */
526 if (nl == 0) {
527 p = strchr(name, '=');
528 if (p != NULL) {
529 val = strval(p);
530 *p = '\0';
531 fv = 1;
535 /* See if this sets a default. */
536 for (nl = 0; nl < __arraycount(def_infos); nl++) {
537 if (strcmp(name, def_infos[nl].name) == 0) {
538 defs[nl] = 1;
539 break;
543 nl = strlen(name);
544 if (val == NULL)
545 vl = 0;
546 else
547 vl = strlen(val);
548 rl = nl + vl + 3; /* , \0 */
550 if (lp + rl > len) {
551 if (rl < 256)
552 len += 256;
553 else
554 len += rl;
555 p = realloc(info, len);
556 if (p == NULL)
557 return NULL;
558 info = p;
561 if (ip != info) {
562 *ip++ = ',';
563 *ip++ = ' ';
566 strcpy(ip, name);
567 ip += nl;
568 if (val != NULL) {
569 strcpy(ip, val);
570 ip += vl;
571 if (fv == 1)
572 free(val);
576 /* Add any defaults not set above. */
577 for (nl = 0; nl < __arraycount(def_infos); nl++) {
578 if (defs[nl] == 0) {
579 *ip++ = ',';
580 *ip++ = ' ';
581 strcpy(ip, def_infos[nl].name);
582 ip += strlen(def_infos[nl].name);
583 *ip++ = '=';
584 strcpy(ip, def_infos[nl].cap);
585 ip += strlen(def_infos[nl].cap);
589 *ip = '\0';
590 return info;