Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/termcap.c
- This file was added.
| /* $NetBSD: termcap.c,v 1.22 2017/05/04 09:42:23 roy Exp $ */ | |||||
| /* | |||||
| * Copyright (c) 2009 The NetBSD Foundation, Inc. | |||||
| * | |||||
| * This code is derived from software contributed to The NetBSD Foundation | |||||
| * by Roy Marples. | |||||
| * | |||||
| * Redistribution and use in source and binary forms, with or without | |||||
| * modification, are permitted provided that the following conditions | |||||
| * are met: | |||||
| * 1. Redistributions of source code must retain the above copyright | |||||
| * notice, this list of conditions and the following disclaimer. | |||||
| * 2. Redistributions in binary form must reproduce the above copyright | |||||
| * notice, this list of conditions and the following disclaimer in the | |||||
| * documentation and/or other materials provided with the distribution. | |||||
| * | |||||
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||||
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||||
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |||||
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||||
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |||||
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
| */ | |||||
| #include <sys/cdefs.h> | |||||
| #include <ctype.h> | |||||
| #include <errno.h> | |||||
| #include <stdbool.h> | |||||
| #include <stdint.h> | |||||
| #include <string.h> | |||||
| #include <unistd.h> | |||||
| #include <stdio.h> | |||||
| #include "term_private.h" | |||||
| #include "terminfo_term.h" | |||||
| #ifndef __UNCONST | |||||
| #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) | |||||
| #endif | |||||
| #ifndef __arraycount | |||||
| #define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) | |||||
| #endif | |||||
| extern void mi_vector_hash(const void * __restrict, size_t, uint32_t, | |||||
| uint32_t[3]); | |||||
| #include "termcap_map.c" | |||||
| #include "termcap_hash.c" | |||||
| char *UP; | |||||
| char *BC; | |||||
| /* ARGSUSED */ | |||||
| int | |||||
| tgetent(__unused char *bp, const char *name) | |||||
| { | |||||
| int errret; | |||||
| static TERMINAL *last = NULL; | |||||
| /* Free the old term */ | |||||
| if (cur_term != NULL) { | |||||
| if (last != NULL && cur_term != last) | |||||
| del_curterm(last); | |||||
| last = cur_term; | |||||
| } | |||||
| errret = -1; | |||||
| if (setupterm(name, STDOUT_FILENO, &errret) != 0) | |||||
| return errret; | |||||
| if (last == NULL) | |||||
| last = cur_term; | |||||
| if (pad_char != NULL) | |||||
| PC = pad_char[0]; | |||||
| UP = __UNCONST(cursor_up); | |||||
| BC = __UNCONST(cursor_left); | |||||
| return 1; | |||||
| } | |||||
| int | |||||
| tgetflag(const char *id2) | |||||
| { | |||||
| uint32_t ind; | |||||
| size_t i; | |||||
| TERMUSERDEF *ud; | |||||
| const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' }; | |||||
| if (cur_term == NULL) | |||||
| return 0; | |||||
| ind = _t_flaghash((const unsigned char *)id, strlen(id)); | |||||
| if (ind < __arraycount(_ti_cap_flagids)) { | |||||
| if (strcmp(id, _ti_cap_flagids[ind].id) == 0) | |||||
| return cur_term->flags[_ti_cap_flagids[ind].ti]; | |||||
| } | |||||
| for (i = 0; i < cur_term->_nuserdefs; i++) { | |||||
| ud = &cur_term->_userdefs[i]; | |||||
| if (ud->type == 'f' && strcmp(ud->id, id) == 0) | |||||
| return ud->flag; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| int | |||||
| tgetnum(const char *id2) | |||||
| { | |||||
| uint32_t ind; | |||||
| size_t i; | |||||
| TERMUSERDEF *ud; | |||||
| const TENTRY *te; | |||||
| const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' }; | |||||
| if (cur_term == NULL) | |||||
| return -1; | |||||
| ind = _t_numhash((const unsigned char *)id, strlen(id)); | |||||
| if (ind < __arraycount(_ti_cap_numids)) { | |||||
| te = &_ti_cap_numids[ind]; | |||||
| if (strcmp(id, te->id) == 0) { | |||||
| if (!VALID_NUMERIC(cur_term->nums[te->ti])) | |||||
| return ABSENT_NUMERIC; | |||||
| return cur_term->nums[te->ti]; | |||||
| } | |||||
| } | |||||
| for (i = 0; i < cur_term->_nuserdefs; i++) { | |||||
| ud = &cur_term->_userdefs[i]; | |||||
| if (ud->type == 'n' && strcmp(ud->id, id) == 0) { | |||||
| if (!VALID_NUMERIC(ud->num)) | |||||
| return ABSENT_NUMERIC; | |||||
| return ud->num; | |||||
| } | |||||
| } | |||||
| return -1; | |||||
| } | |||||
| char * | |||||
| tgetstr(const char *id2, char **area) | |||||
| { | |||||
| uint32_t ind; | |||||
| size_t i; | |||||
| TERMUSERDEF *ud; | |||||
| const char *str; | |||||
| const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' }; | |||||
| if (cur_term == NULL) | |||||
| return NULL; | |||||
| str = NULL; | |||||
| ind = _t_strhash((const unsigned char *)id, strlen(id)); | |||||
| if (ind < __arraycount(_ti_cap_strids)) { | |||||
| if (strcmp(id, _ti_cap_strids[ind].id) == 0) { | |||||
| str = cur_term->strs[_ti_cap_strids[ind].ti]; | |||||
| if (str == NULL) | |||||
| return NULL; | |||||
| } | |||||
| } | |||||
| if (str != NULL) | |||||
| for (i = 0; i < cur_term->_nuserdefs; i++) { | |||||
| ud = &cur_term->_userdefs[i]; | |||||
| if (ud->type == 's' && strcmp(ud->id, id) == 0) | |||||
| str = ud->str; | |||||
| } | |||||
| /* XXX: FXIXME | |||||
| * We should fix sgr0(me) as it has a slightly different meaning | |||||
| * for termcap. */ | |||||
| if (str != NULL && area != NULL && *area != NULL) { | |||||
| char *s; | |||||
| s = *area; | |||||
| strcpy(*area, str); | |||||
| *area += strlen(*area) + 1; | |||||
| return s; | |||||
| } | |||||
| return __UNCONST(str); | |||||
| } | |||||
| char * | |||||
| tgoto(const char *cm, int destcol, int destline) | |||||
| { | |||||
| return tiparm(cm, destline, destcol); | |||||
| } | |||||
| static const char * | |||||
| flagname(const char *key) | |||||
| { | |||||
| uint32_t idx; | |||||
| idx = _t_flaghash((const unsigned char *)key, strlen(key)); | |||||
| if (idx < __arraycount(_ti_cap_flagids) && | |||||
| strcmp(key, _ti_cap_flagids[idx].id) == 0) | |||||
| return _ti_flagid(_ti_cap_flagids[idx].ti); | |||||
| return key; | |||||
| } | |||||
| static const char * | |||||
| numname(const char *key) | |||||
| { | |||||
| uint32_t idx; | |||||
| idx = _t_numhash((const unsigned char *)key, strlen(key)); | |||||
| if (idx < __arraycount(_ti_cap_numids) && | |||||
| strcmp(key, _ti_cap_numids[idx].id) == 0) | |||||
| return _ti_numid(_ti_cap_numids[idx].ti); | |||||
| return key; | |||||
| } | |||||
| static const char * | |||||
| strname(const char *key) | |||||
| { | |||||
| uint32_t idx; | |||||
| idx = _t_strhash((const unsigned char *)key, strlen(key)); | |||||
| if (idx < __arraycount(_ti_cap_strids) && | |||||
| strcmp(key, _ti_cap_strids[idx].id) == 0) | |||||
| return _ti_strid(_ti_cap_strids[idx].ti); | |||||
| if (strcmp(key, "tc") == 0) | |||||
| return "use"; | |||||
| return key; | |||||
| } | |||||
| /* Print a parameter if needed */ | |||||
| static size_t | |||||
| printparam(char **dst, char p, bool *nop) | |||||
| { | |||||
| if (*nop) { | |||||
| *nop = false; | |||||
| return 0; | |||||
| } | |||||
| *(*dst)++ = '%'; | |||||
| *(*dst)++ = 'p'; | |||||
| *(*dst)++ = '0' + p; | |||||
| return 3; | |||||
| } | |||||
| /* Convert a termcap character into terminfo equivalents */ | |||||
| static size_t | |||||
| printchar(char **dst, const char **src) | |||||
| { | |||||
| char v; | |||||
| size_t l; | |||||
| l = 4; | |||||
| v = *++(*src); | |||||
| if (v == '\\') { | |||||
| v = *++(*src); | |||||
| switch (v) { | |||||
| case '0': | |||||
| case '1': | |||||
| case '2': | |||||
| case '3': | |||||
| v = 0; | |||||
| while (isdigit((unsigned char) **src)) | |||||
| v = 8 * v + (*(*src)++ - '0'); | |||||
| (*src)--; | |||||
| break; | |||||
| case '\0': | |||||
| v = '\\'; | |||||
| break; | |||||
| } | |||||
| } else if (v == '^') | |||||
| v = *++(*src) & 0x1f; | |||||
| *(*dst)++ = '%'; | |||||
| if (isgraph((unsigned char )v) && | |||||
| v != ',' && v != '\'' && v != '\\' && v != ':') | |||||
| { | |||||
| *(*dst)++ = '\''; | |||||
| *(*dst)++ = v; | |||||
| *(*dst)++ = '\''; | |||||
| } else { | |||||
| *(*dst)++ = '{'; | |||||
| if (v > 99) { | |||||
| *(*dst)++ = '0'+ v / 100; | |||||
| l++; | |||||
| } | |||||
| if (v > 9) { | |||||
| *(*dst)++ = '0' + ((int) (v / 10)) % 10; | |||||
| l++; | |||||
| } | |||||
| *(*dst)++ = '0' + v % 10; | |||||
| *(*dst)++ = '}'; | |||||
| } | |||||
| return l; | |||||
| } | |||||
| /* Convert termcap commands into terminfo commands */ | |||||
| static const char fmtB[] = "%p0%{10}%/%{16}%*%p0%{10}%m%+"; | |||||
| static const char fmtD[] = "%p0%p0%{2}%*%-"; | |||||
| static const char fmtIf[] = "%p0%p0%?"; | |||||
| static const char fmtThen[] = "%>%t"; | |||||
| static const char fmtElse[] = "%+%;"; | |||||
| static char * | |||||
| strval(const char *val) | |||||
| { | |||||
| char *info, *ip, c, p; | |||||
| const char *ps, *pe; | |||||
| bool nop; | |||||
| size_t len, l; | |||||
| len = 1024; /* no single string should be bigger */ | |||||
| info = ip = malloc(len); | |||||
| if (info == NULL) | |||||
| return 0; | |||||
| /* Move the = */ | |||||
| *ip++ = *val++; | |||||
| /* Set ps and pe to point to the start and end of the padding */ | |||||
| if (isdigit((unsigned char)*val)) { | |||||
| for (ps = pe = val; | |||||
| isdigit((unsigned char)*val) || *val == '.'; | |||||
| val++) | |||||
| pe++; | |||||
| if (*val == '*') { | |||||
| val++; | |||||
| pe++; | |||||
| } | |||||
| } else | |||||
| ps = pe = NULL; | |||||
| nop = false; | |||||
| l = 0; | |||||
| p = 1; | |||||
| for (; *val != '\0'; val++) { | |||||
| if (l + 2 > len) | |||||
| goto elen; | |||||
| if (*val != '%') { | |||||
| if (*val == ',') { | |||||
| if (l + 3 > len) | |||||
| goto elen; | |||||
| *ip++ = '\\'; | |||||
| l++; | |||||
| } | |||||
| *ip++ = *val; | |||||
| l++; | |||||
| continue; | |||||
| } | |||||
| switch (c = *++(val)) { | |||||
| case 'B': | |||||
| if (l + sizeof(fmtB) > len) | |||||
| goto elen; | |||||
| memcpy(ip, fmtB, sizeof(fmtB) - 1); | |||||
| /* Replace the embedded parameters with real ones */ | |||||
| ip[2] += p; | |||||
| ip[19] += p; | |||||
| ip += sizeof(fmtB) - 1; | |||||
| l += sizeof(fmtB) - 1; | |||||
| nop = true; | |||||
| continue; | |||||
| case 'D': | |||||
| if (l + sizeof(fmtD) > len) | |||||
| goto elen; | |||||
| memcpy(ip, fmtD, sizeof(fmtD) - 1); | |||||
| /* Replace the embedded parameters with real ones */ | |||||
| ip[2] += p; | |||||
| ip[5] += p; | |||||
| ip += sizeof(fmtD) - 1; | |||||
| l += sizeof(fmtD) - 1; | |||||
| nop = true; | |||||
| continue; | |||||
| case 'r': | |||||
| /* non op as switched below */ | |||||
| break; | |||||
| case '2': /* FALLTHROUGH */ | |||||
| case '3': /* FALLTHROUGH */ | |||||
| case 'd': | |||||
| if (l + 7 > len) | |||||
| goto elen; | |||||
| l += printparam(&ip, p, &nop); | |||||
| *ip++ = '%'; | |||||
| if (c != 'd') { | |||||
| *ip++ = c; | |||||
| l++; | |||||
| } | |||||
| *ip++ = 'd'; | |||||
| l += 2; | |||||
| break; | |||||
| case '+': | |||||
| if (l + 13 > len) | |||||
| goto elen; | |||||
| l += printparam(&ip, p, &nop); | |||||
| l += printchar(&ip, &val); | |||||
| *ip++ = '%'; | |||||
| *ip++ = c; | |||||
| *ip++ = '%'; | |||||
| *ip++ = 'c'; | |||||
| l += 7; | |||||
| break; | |||||
| case '>': | |||||
| if (l + sizeof(fmtIf) + sizeof(fmtThen) + | |||||
| sizeof(fmtElse) + (6 * 2) > len) | |||||
| goto elen; | |||||
| memcpy(ip, fmtIf, sizeof(fmtIf) - 1); | |||||
| /* Replace the embedded parameters with real ones */ | |||||
| ip[2] += p; | |||||
| ip[5] += p; | |||||
| ip += sizeof(fmtIf) - 1; | |||||
| l += sizeof(fmtIf) - 1; | |||||
| l += printchar(&ip, &val); | |||||
| memcpy(ip, fmtThen, sizeof(fmtThen) - 1); | |||||
| ip += sizeof(fmtThen) - 1; | |||||
| l += sizeof(fmtThen) - 1; | |||||
| l += printchar(&ip, &val); | |||||
| memcpy(ip, fmtElse, sizeof(fmtElse) - 1); | |||||
| ip += sizeof(fmtElse) - 1; | |||||
| l += sizeof(fmtElse) - 1; | |||||
| l += 16; | |||||
| nop = true; | |||||
| continue; | |||||
| case '.': | |||||
| if (l + 6 > len) | |||||
| goto elen; | |||||
| l += printparam(&ip, p, &nop); | |||||
| *ip++ = '%'; | |||||
| *ip++ = 'c'; | |||||
| l += 2; | |||||
| break; | |||||
| default: | |||||
| /* Hope it matches a terminfo command. */ | |||||
| *ip++ = '%'; | |||||
| *ip++ = c; | |||||
| l += 2; | |||||
| if (c == 'i') | |||||
| continue; | |||||
| break; | |||||
| } | |||||
| /* Swap p1 and p2 */ | |||||
| p = 3 - p; | |||||
| } | |||||
| /* \E\ is valid termcap. | |||||
| * We need to escape the final \ for terminfo. */ | |||||
| if (l > 2 && info[l - 1] == '\\' && | |||||
| (info[l - 2] != '\\' && info[l - 2] != '^')) | |||||
| { | |||||
| if (l + 1 > len) | |||||
| goto elen; | |||||
| *ip++ = '\\'; | |||||
| } | |||||
| /* Add our padding at the end. */ | |||||
| if (ps != NULL) { | |||||
| size_t n = (size_t)(pe - ps); | |||||
| if (l + n + 4 > len) | |||||
| goto elen; | |||||
| *ip++ = '$'; | |||||
| *ip++ = '<'; | |||||
| strncpy(ip, ps, n); | |||||
| ip += n; | |||||
| *ip++ = '/'; | |||||
| *ip++ = '>'; | |||||
| } | |||||
| *ip = '\0'; | |||||
| return info; | |||||
| elen: | |||||
| free(info); | |||||
| errno = ENOMEM; | |||||
| return NULL; | |||||
| } | |||||
| typedef struct { | |||||
| const char *name; | |||||
| const char *cap; | |||||
| } DEF_INFO; | |||||
| static DEF_INFO def_infos[] = { | |||||
| { "bel", "^G" }, | |||||
| { "cr", "^M" }, | |||||
| { "cud1", "^J" }, | |||||
| { "ht", "^I" }, | |||||
| { "ind", "^J" }, | |||||
| { "kbs", "^H" }, | |||||
| { "kcub1", "^H" }, | |||||
| { "kcud1", "^J" }, | |||||
| { "nel", "^M^J" } | |||||
| }; | |||||
| char * | |||||
| captoinfo(char *cap) | |||||
| { | |||||
| char *info, *ip, *token, *val, *p, tok[3]; | |||||
| const char *name; | |||||
| size_t len, lp, nl, vl, rl; | |||||
| int defs[__arraycount(def_infos)], fv; | |||||
| len = strlen(cap) * 2; | |||||
| len += __arraycount(def_infos) * (5 + 4 + 3); /* reserve for defs */ | |||||
| info = ip = malloc(len); | |||||
| if (info == NULL) | |||||
| return NULL; | |||||
| memset(defs, 0, sizeof(defs)); | |||||
| lp = 0; | |||||
| tok[2] = '\0'; | |||||
| for (token = _ti_get_token(&cap, ':'); | |||||
| token != NULL; | |||||
| token = _ti_get_token(&cap, ':')) | |||||
| { | |||||
| if (token[0] == '\0') | |||||
| continue; | |||||
| name = token; | |||||
| val = p = NULL; | |||||
| fv = nl = 0; | |||||
| if (token[1] != '\0') { | |||||
| tok[0] = token[0]; | |||||
| tok[1] = token[1]; | |||||
| nl = 1; | |||||
| if (token[2] == '\0') { | |||||
| name = flagname(tok); | |||||
| val = NULL; | |||||
| } else if (token[2] == '#') { | |||||
| name = numname(tok); | |||||
| val = token + 2; | |||||
| } else if (token[2] == '=') { | |||||
| name = strname(tok); | |||||
| val = strval(token + 2); | |||||
| fv = 1; | |||||
| } else | |||||
| nl = 0; | |||||
| } | |||||
| /* If not matched we may need to convert padding still. */ | |||||
| if (nl == 0) { | |||||
| p = strchr(name, '='); | |||||
| if (p != NULL) { | |||||
| val = strval(p); | |||||
| *p = '\0'; | |||||
| fv = 1; | |||||
| } | |||||
| } | |||||
| /* See if this sets a default. */ | |||||
| for (nl = 0; nl < __arraycount(def_infos); nl++) { | |||||
| if (strcmp(name, def_infos[nl].name) == 0) { | |||||
| defs[nl] = 1; | |||||
| break; | |||||
| } | |||||
| } | |||||
| nl = strlen(name); | |||||
| if (val == NULL) | |||||
| vl = 0; | |||||
| else | |||||
| vl = strlen(val); | |||||
| rl = nl + vl + 3; /* , \0 */ | |||||
| if (lp + rl > len) { | |||||
| if (rl < 256) | |||||
| len += 256; | |||||
| else | |||||
| len += rl; | |||||
| p = realloc(info, len); | |||||
| if (p == NULL) { | |||||
| if (fv == 1) | |||||
| free(val); | |||||
| return NULL; | |||||
| } | |||||
| info = p; | |||||
| } | |||||
| if (ip != info) { | |||||
| *ip++ = ','; | |||||
| *ip++ = ' '; | |||||
| } | |||||
| strcpy(ip, name); | |||||
| ip += nl; | |||||
| if (val != NULL) { | |||||
| strcpy(ip, val); | |||||
| ip += vl; | |||||
| if (fv == 1) | |||||
| free(val); | |||||
| } | |||||
| } | |||||
| /* Add any defaults not set above. */ | |||||
| for (nl = 0; nl < __arraycount(def_infos); nl++) { | |||||
| if (defs[nl] == 0) { | |||||
| *ip++ = ','; | |||||
| *ip++ = ' '; | |||||
| strcpy(ip, def_infos[nl].name); | |||||
| ip += strlen(def_infos[nl].name); | |||||
| *ip++ = '='; | |||||
| strcpy(ip, def_infos[nl].cap); | |||||
| ip += strlen(def_infos[nl].cap); | |||||
| } | |||||
| } | |||||
| *ip = '\0'; | |||||
| return info; | |||||
| } | |||||