Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/term.c
- This file was added.
| /* $NetBSD: term.c,v 1.29 2018/10/08 20:44:34 roy Exp $ */ | |||||
| /* | |||||
| * Copyright (c) 2009, 2010, 2011 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 <sys/stat.h> | |||||
| #include <ctype.h> | |||||
| #include <errno.h> | |||||
| #include <fcntl.h> | |||||
| #include <limits.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #include "cdbr.h" | |||||
| #include "term_private.h" | |||||
| #include "terminfo_term.h" | |||||
| #define _PATH_TERMINFO "/usr/share/misc/terminfo" | |||||
| #ifndef __arraycount | |||||
| #define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) | |||||
| #endif | |||||
| static char __ti_database[PATH_MAX]; | |||||
| const char *_ti_database; | |||||
| /* From NetBSD sys/arch/hpc/stand/include/machine/endian.h */ | |||||
| uint16_t | |||||
| le16dec(const void *buf) | |||||
| { | |||||
| const uint8_t *p = (const uint8_t *)buf; | |||||
| return ((p[1] << 8) | p[0]); | |||||
| } | |||||
| /* From NetBSD sys/external/bsd/libnv/dist/nv_compat.h */ | |||||
| static uint32_t | |||||
| le32dec(const void *buf) | |||||
| { | |||||
| uint8_t const *p = (uint8_t const *) buf; | |||||
| return (((unsigned) p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); | |||||
| } | |||||
| /* Include a generated list of pre-compiled terminfo descriptions. */ | |||||
| #include "compiled_terms.c" | |||||
| static int | |||||
| allocset(void *pp, int init, size_t nelem, size_t elemsize) | |||||
| { | |||||
| void **p = pp; | |||||
| if (*p) { | |||||
| memset(*p, init, nelem * elemsize); | |||||
| return 0; | |||||
| } | |||||
| if ((*p = calloc(nelem, elemsize)) == NULL) | |||||
| return -1; | |||||
| if (init != 0) | |||||
| memset(*p, init, nelem * elemsize); | |||||
| return 0; | |||||
| } | |||||
| static int | |||||
| _ti_readterm(TERMINAL *term, const char *cap, size_t caplen, int flags) | |||||
| { | |||||
| char ver; | |||||
| uint16_t ind, num; | |||||
| size_t len; | |||||
| TERMUSERDEF *ud; | |||||
| if (caplen == 0) | |||||
| goto out; | |||||
| ver = *cap++; | |||||
| caplen--; | |||||
| /* Only read version 1 structures */ | |||||
| if (ver != 1) | |||||
| goto out; | |||||
| if (allocset(&term->flags, 0, TIFLAGMAX+1, sizeof(*term->flags)) == -1) | |||||
| return -1; | |||||
| if (allocset(&term->nums, -1, TINUMMAX+1, sizeof(*term->nums)) == -1) | |||||
| return -1; | |||||
| if (allocset(&term->strs, 0, TISTRMAX+1, sizeof(*term->strs)) == -1) | |||||
| return -1; | |||||
| if (term->_arealen != caplen) { | |||||
| term->_arealen = caplen; | |||||
| term->_area = realloc(term->_area, term->_arealen); | |||||
| if (term->_area == NULL) | |||||
| return -1; | |||||
| } | |||||
| memcpy(term->_area, cap, term->_arealen); | |||||
| cap = term->_area; | |||||
| len = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| term->name = cap; | |||||
| cap += len; | |||||
| len = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| if (len == 0) | |||||
| term->_alias = NULL; | |||||
| else { | |||||
| term->_alias = cap; | |||||
| cap += len; | |||||
| } | |||||
| len = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| if (len == 0) | |||||
| term->desc = NULL; | |||||
| else { | |||||
| term->desc = cap; | |||||
| cap += len; | |||||
| } | |||||
| num = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| if (num != 0) { | |||||
| num = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| for (; num != 0; num--) { | |||||
| ind = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| term->flags[ind] = *cap++; | |||||
| if (flags == 0 && !VALID_BOOLEAN(term->flags[ind])) | |||||
| term->flags[ind] = 0; | |||||
| } | |||||
| } | |||||
| num = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| if (num != 0) { | |||||
| num = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| for (; num != 0; num--) { | |||||
| ind = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| term->nums[ind] = (short)le16dec(cap); | |||||
| if (flags == 0 && !VALID_NUMERIC(term->nums[ind])) | |||||
| term->nums[ind] = ABSENT_NUMERIC; | |||||
| cap += sizeof(uint16_t); | |||||
| } | |||||
| } | |||||
| num = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| if (num != 0) { | |||||
| num = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| for (; num != 0; num--) { | |||||
| ind = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| len = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| if (len > 0) | |||||
| term->strs[ind] = cap; | |||||
| else if (flags == 0) | |||||
| term->strs[ind] = ABSENT_STRING; | |||||
| else | |||||
| term->strs[ind] = CANCELLED_STRING; | |||||
| cap += len; | |||||
| } | |||||
| } | |||||
| num = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| if (num != 0) { | |||||
| num = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| if (num != term->_nuserdefs) { | |||||
| free(term->_userdefs); | |||||
| term->_userdefs = NULL; | |||||
| term->_nuserdefs = num; | |||||
| } | |||||
| if (allocset(&term->_userdefs, 0, term->_nuserdefs, | |||||
| sizeof(*term->_userdefs)) == -1) | |||||
| return -1; | |||||
| for (num = 0; num < term->_nuserdefs; num++) { | |||||
| ud = &term->_userdefs[num]; | |||||
| len = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| ud->id = cap; | |||||
| cap += len; | |||||
| ud->type = *cap++; | |||||
| switch (ud->type) { | |||||
| case 'f': | |||||
| ud->flag = *cap++; | |||||
| if (flags == 0 && | |||||
| !VALID_BOOLEAN(ud->flag)) | |||||
| ud->flag = 0; | |||||
| ud->num = ABSENT_NUMERIC; | |||||
| ud->str = ABSENT_STRING; | |||||
| break; | |||||
| case 'n': | |||||
| ud->flag = ABSENT_BOOLEAN; | |||||
| ud->num = (short)le16dec(cap); | |||||
| if (flags == 0 && | |||||
| !VALID_NUMERIC(ud->num)) | |||||
| ud->num = ABSENT_NUMERIC; | |||||
| ud->str = ABSENT_STRING; | |||||
| cap += sizeof(uint16_t); | |||||
| break; | |||||
| case 's': | |||||
| ud->flag = ABSENT_BOOLEAN; | |||||
| ud->num = ABSENT_NUMERIC; | |||||
| len = le16dec(cap); | |||||
| cap += sizeof(uint16_t); | |||||
| if (len > 0) | |||||
| ud->str = cap; | |||||
| else if (flags == 0) | |||||
| ud->str = ABSENT_STRING; | |||||
| else | |||||
| ud->str = CANCELLED_STRING; | |||||
| cap += len; | |||||
| break; | |||||
| default: | |||||
| goto out; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| term->_nuserdefs = 0; | |||||
| if (term->_userdefs) { | |||||
| free(term->_userdefs); | |||||
| term->_userdefs = NULL; | |||||
| } | |||||
| } | |||||
| return 1; | |||||
| out: | |||||
| errno = EINVAL; | |||||
| return -1; | |||||
| } | |||||
| static int | |||||
| _ti_checkname(const char *name, const char *termname, const char *termalias) | |||||
| { | |||||
| const char *alias, *s; | |||||
| size_t len, l; | |||||
| /* Check terminal name matches. */ | |||||
| if (strcmp(termname, name) == 0) | |||||
| return 1; | |||||
| /* Check terminal aliases match. */ | |||||
| if (termalias == NULL) | |||||
| return 0; | |||||
| len = strlen(name); | |||||
| alias = termalias; | |||||
| while (*alias != '\0') { | |||||
| s = strchr(alias, '|'); | |||||
| if (s == NULL) | |||||
| l = strlen(alias); | |||||
| else | |||||
| l = (size_t)(s - alias); | |||||
| if (len == l && memcmp(alias, name, l) == 0) | |||||
| return 1; | |||||
| if (s == NULL) | |||||
| break; | |||||
| alias = s + 1; | |||||
| } | |||||
| /* No match. */ | |||||
| return 0; | |||||
| } | |||||
| static int | |||||
| _ti_dbgetterm(TERMINAL *term, const char *path, const char *name, int flags) | |||||
| { | |||||
| struct cdbr *db; | |||||
| const void *data; | |||||
| const uint8_t *data8; | |||||
| size_t len, klen; | |||||
| int r; | |||||
| r = snprintf(__ti_database, sizeof(__ti_database), "%s.cdb", path); | |||||
| if (r < 0 || (size_t)r > sizeof(__ti_database)) { | |||||
| db = NULL; | |||||
| errno = ENOENT; /* To fall back to a non extension. */ | |||||
| } else | |||||
| db = cdbr_open(__ti_database, CDBR_DEFAULT); | |||||
| /* Target file *may* be a cdb file without the extension. */ | |||||
| if (db == NULL && errno == ENOENT) { | |||||
| len = strlcpy(__ti_database, path, sizeof(__ti_database)); | |||||
| if (len < sizeof(__ti_database)) | |||||
| db = cdbr_open(__ti_database, CDBR_DEFAULT); | |||||
| } | |||||
| if (db == NULL) | |||||
| return -1; | |||||
| r = 0; | |||||
| klen = strlen(name) + 1; | |||||
| if (cdbr_find(db, name, klen, &data, &len) == -1) | |||||
| goto out; | |||||
| data8 = data; | |||||
| if (len == 0) | |||||
| goto out; | |||||
| /* If the entry is an alias, load the indexed terminfo description. */ | |||||
| if (data8[0] == 2) { | |||||
| if (cdbr_get(db, le32dec(data8 + 1), &data, &len)) | |||||
| goto out; | |||||
| data8 = data; | |||||
| } | |||||
| r = _ti_readterm(term, data, len, flags); | |||||
| /* Ensure that this is the right terminfo description. */ | |||||
| if (r == 1) | |||||
| r = _ti_checkname(name, term->name, term->_alias); | |||||
| /* Remember the database we read. */ | |||||
| if (r == 1) | |||||
| _ti_database = __ti_database; | |||||
| out: | |||||
| cdbr_close(db); | |||||
| return r; | |||||
| } | |||||
| static int | |||||
| _ti_dbgettermp(TERMINAL *term, const char *path, const char *name, int flags) | |||||
| { | |||||
| const char *p; | |||||
| char pathbuf[PATH_MAX]; | |||||
| size_t l; | |||||
| int r, e; | |||||
| e = -1; | |||||
| r = 0; | |||||
| do { | |||||
| for (p = path; *path != '\0' && *path != ':'; path++) | |||||
| continue; | |||||
| l = (size_t)(path - p); | |||||
| if (l != 0 && l + 1 < sizeof(pathbuf)) { | |||||
| memcpy(pathbuf, p, l); | |||||
| pathbuf[l] = '\0'; | |||||
| r = _ti_dbgetterm(term, pathbuf, name, flags); | |||||
| if (r == 1) | |||||
| return 1; | |||||
| if (r == 0) | |||||
| e = 0; | |||||
| } | |||||
| } while (*path++ == ':'); | |||||
| return e; | |||||
| } | |||||
| static int | |||||
| _ti_findterm(TERMINAL *term, const char *name, int flags) | |||||
| { | |||||
| int r; | |||||
| char *c, *e; | |||||
| _ti_database = NULL; | |||||
| r = 0; | |||||
| if ((e = getenv("TERMINFO")) != NULL && *e != '\0') { | |||||
| if (e[0] == '/') | |||||
| return _ti_dbgetterm(term, e, name, flags); | |||||
| } | |||||
| c = NULL; | |||||
| if (e == NULL && (c = getenv("TERMCAP")) != NULL) { | |||||
| if (*c != '\0' && *c != '/') { | |||||
| c = strdup(c); | |||||
| if (c != NULL) { | |||||
| e = captoinfo(c); | |||||
| free(c); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (e != NULL) { | |||||
| TIC *tic; | |||||
| if (c == NULL) | |||||
| e = strdup(e); /* So we don't destroy env */ | |||||
| if (e == NULL) | |||||
| tic = NULL; | |||||
| else { | |||||
| tic = _ti_compile(e, TIC_WARNING | | |||||
| TIC_ALIAS | TIC_DESCRIPTION | TIC_EXTRA); | |||||
| free(e); | |||||
| } | |||||
| if (tic != NULL && | |||||
| _ti_checkname(name, tic->name, tic->alias) == 1) | |||||
| { | |||||
| uint8_t *f; | |||||
| ssize_t len; | |||||
| len = _ti_flatten(&f, tic); | |||||
| if (len != -1) { | |||||
| r = _ti_readterm(term, (char *)f, (size_t)len, | |||||
| flags); | |||||
| free(f); | |||||
| } | |||||
| } | |||||
| _ti_freetic(tic); | |||||
| if (r == 1) { | |||||
| if (c == NULL) | |||||
| _ti_database = "$TERMINFO"; | |||||
| else | |||||
| _ti_database = "$TERMCAP"; | |||||
| return r; | |||||
| } | |||||
| } | |||||
| if ((e = getenv("TERMINFO_DIRS")) != NULL) | |||||
| return _ti_dbgettermp(term, e, name, flags); | |||||
| if ((e = getenv("HOME")) != NULL) { | |||||
| char homepath[PATH_MAX]; | |||||
| if (snprintf(homepath, sizeof(homepath), "%s/.terminfo", e) > 0) | |||||
| r = _ti_dbgetterm(term, homepath, name, flags); | |||||
| } | |||||
| if (r != 1) | |||||
| r = _ti_dbgettermp(term, _PATH_TERMINFO, name, flags); | |||||
| return r; | |||||
| } | |||||
| int | |||||
| _ti_getterm(TERMINAL *term, const char *name, int flags) | |||||
| { | |||||
| int r; | |||||
| size_t i; | |||||
| const struct compiled_term *t; | |||||
| r = _ti_findterm(term, name, flags); | |||||
| if (r == 1) | |||||
| return r; | |||||
| for (i = 0; i < __arraycount(compiled_terms); i++) { | |||||
| t = &compiled_terms[i]; | |||||
| if (strcmp(name, t->name) == 0) { | |||||
| r = _ti_readterm(term, t->cap, t->caplen, flags); | |||||
| break; | |||||
| } | |||||
| } | |||||
| return r; | |||||
| } | |||||