NetBSD/lib/libterminfo/termcap.c

605 lines
12 KiB
C

/* $NetBSD: termcap.c,v 1.25 2023/01/31 21:11:24 andvar 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>
__RCSID("$NetBSD: termcap.c,v 1.25 2023/01/31 21:11:24 andvar Exp $");
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <term_private.h>
#include <term.h>
#include <termcap.h>
#include <unistd.h>
#include <stdio.h>
#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;
_DIAGASSERT(name != 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: FIXME
* 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)
{
_DIAGASSERT(cm != NULL);
return tiparm(cm, destline, destcol);
}
#ifdef TERMINFO_COMPILE
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;
_DIAGASSERT(cap != NULL);
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 = 0;
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;
}
#endif