mirror of
https://github.com/Aigor44/ncursesw-morphos.git
synced 2025-01-24 15:13:56 +08:00
684ea7a157
+ modify scripts to generate ncurses*-config and pc-files to add dependency for tinfo library (patch by Charles Wilson). + improve comparison of program-names when checking for linked flavors such as "reset" by ignoring the executable suffix (reports by Charles Wilson, Samuel Thibault and Cedric Bretaudeau on Cygwin mailing list). + suppress configure check for static/dynamic linker flags for gcc on Solaris 10, since gcc is confused by absence of static libc, and does not switch back to dynamic mode before finishing the libraries (reports by Joel Bertrand, Alan Pae). + minor fixes to Intel compiler warning checks in configure script. + modify _nc_leaks_tinfo() so leak-checking in test/railroad.c works. + modify set_curterm() to make broken-linker configuration work with changes from 20090228 (report by Charles Wilson).
1522 lines
37 KiB
C
1522 lines
37 KiB
C
/****************************************************************************
|
|
* Copyright (c) 1998-2008,2009 Free Software Foundation, Inc. *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining a *
|
|
* copy of this software and associated documentation files (the *
|
|
* "Software"), to deal in the Software without restriction, including *
|
|
* without limitation the rights to use, copy, modify, merge, publish, *
|
|
* distribute, distribute with modifications, sublicense, and/or sell *
|
|
* copies of the Software, and to permit persons to whom the Software is *
|
|
* furnished to do so, subject to the following conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included *
|
|
* in all copies or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
|
|
* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
|
|
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
* *
|
|
* Except as contained in this notice, the name(s) of the above copyright *
|
|
* holders shall not be used in advertising or otherwise to promote the *
|
|
* sale, use or other dealings in this Software without prior written *
|
|
* authorization. *
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
|
|
* and: Eric S. Raymond <esr@snark.thyrsus.com> *
|
|
* and: Thomas E. Dickey 1996 on *
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* tic.c --- Main program for terminfo compiler
|
|
* by Eric S. Raymond
|
|
*
|
|
*/
|
|
|
|
#include <progs.priv.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <dump_entry.h>
|
|
#include <transform.h>
|
|
|
|
MODULE_ID("$Id: tic.c,v 1.138 2009/03/14 18:45:55 tom Exp $")
|
|
|
|
const char *_nc_progname = "tic";
|
|
|
|
static FILE *log_fp;
|
|
static FILE *tmp_fp;
|
|
static bool capdump = FALSE; /* running as infotocap? */
|
|
static bool infodump = FALSE; /* running as captoinfo? */
|
|
static bool showsummary = FALSE;
|
|
static const char *to_remove;
|
|
|
|
static void (*save_check_termtype) (TERMTYPE *, bool);
|
|
static void check_termtype(TERMTYPE *tt, bool);
|
|
|
|
static const char usage_string[] = "\
|
|
[-e names] \
|
|
[-o dir] \
|
|
[-R name] \
|
|
[-v[n]] \
|
|
[-V] \
|
|
[-w[n]] \
|
|
[-\
|
|
1\
|
|
a\
|
|
C\
|
|
c\
|
|
f\
|
|
G\
|
|
g\
|
|
I\
|
|
L\
|
|
N\
|
|
r\
|
|
s\
|
|
T\
|
|
t\
|
|
U\
|
|
x\
|
|
] \
|
|
source-file\n";
|
|
|
|
#if NO_LEAKS
|
|
static void
|
|
free_namelist(char **src)
|
|
{
|
|
if (src != 0) {
|
|
int n;
|
|
for (n = 0; src[n] != 0; ++n)
|
|
free(src[n]);
|
|
free(src);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
cleanup(char **namelst GCC_UNUSED)
|
|
{
|
|
#if NO_LEAKS
|
|
free_namelist(namelst);
|
|
#endif
|
|
if (tmp_fp != 0)
|
|
fclose(tmp_fp);
|
|
if (to_remove != 0) {
|
|
#if HAVE_REMOVE
|
|
remove(to_remove);
|
|
#else
|
|
unlink(to_remove);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void
|
|
failed(const char *msg)
|
|
{
|
|
perror(msg);
|
|
cleanup((char **) 0);
|
|
ExitProgram(EXIT_FAILURE);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
static const char *const tbl[] =
|
|
{
|
|
"Options:",
|
|
" -1 format translation output one capability per line",
|
|
#if NCURSES_XNAMES
|
|
" -a retain commented-out capabilities (sets -x also)",
|
|
#endif
|
|
" -C translate entries to termcap source form",
|
|
" -c check only, validate input without compiling or translating",
|
|
" -e<names> translate/compile only entries named by comma-separated list",
|
|
" -f format complex strings for readability",
|
|
" -G format %{number} to %'char'",
|
|
" -g format %'char' to %{number}",
|
|
" -I translate entries to terminfo source form",
|
|
" -L translate entries to full terminfo source form",
|
|
" -N disable smart defaults for source translation",
|
|
" -o<dir> set output directory for compiled entry writes",
|
|
" -R<name> restrict translation to given terminfo/termcap version",
|
|
" -r force resolution of all use entries in source translation",
|
|
" -s print summary statistics",
|
|
" -T remove size-restrictions on compiled description",
|
|
#if NCURSES_XNAMES
|
|
" -t suppress commented-out capabilities",
|
|
#endif
|
|
" -U suppress post-processing of entries",
|
|
" -V print version",
|
|
" -v[n] set verbosity level",
|
|
" -w[n] set format width for translation output",
|
|
#if NCURSES_XNAMES
|
|
" -x treat unknown capabilities as user-defined",
|
|
#endif
|
|
"",
|
|
"Parameters:",
|
|
" <file> file to translate or compile"
|
|
};
|
|
size_t j;
|
|
|
|
fprintf(stderr, "Usage: %s %s\n", _nc_progname, usage_string);
|
|
for (j = 0; j < SIZEOF(tbl); j++) {
|
|
fputs(tbl[j], stderr);
|
|
putc('\n', stderr);
|
|
}
|
|
ExitProgram(EXIT_FAILURE);
|
|
}
|
|
|
|
#define L_BRACE '{'
|
|
#define R_BRACE '}'
|
|
#define S_QUOTE '\'';
|
|
|
|
static void
|
|
write_it(ENTRY * ep)
|
|
{
|
|
unsigned n;
|
|
int ch;
|
|
char *s, *d, *t;
|
|
char result[MAX_ENTRY_SIZE];
|
|
|
|
/*
|
|
* Look for strings that contain %{number}, convert them to %'char',
|
|
* which is shorter and runs a little faster.
|
|
*/
|
|
for (n = 0; n < STRCOUNT; n++) {
|
|
s = ep->tterm.Strings[n];
|
|
if (VALID_STRING(s)
|
|
&& strchr(s, L_BRACE) != 0) {
|
|
d = result;
|
|
t = s;
|
|
while ((ch = *t++) != 0) {
|
|
*d++ = (char) ch;
|
|
if (ch == '\\') {
|
|
*d++ = *t++;
|
|
} else if ((ch == '%')
|
|
&& (*t == L_BRACE)) {
|
|
char *v = 0;
|
|
long value = strtol(t + 1, &v, 0);
|
|
if (v != 0
|
|
&& *v == R_BRACE
|
|
&& value > 0
|
|
&& value != '\\' /* FIXME */
|
|
&& value < 127
|
|
&& isprint((int) value)) {
|
|
*d++ = S_QUOTE;
|
|
*d++ = (char) value;
|
|
*d++ = S_QUOTE;
|
|
t = (v + 1);
|
|
}
|
|
}
|
|
}
|
|
*d = 0;
|
|
if (strlen(result) < strlen(s))
|
|
strcpy(s, result);
|
|
}
|
|
}
|
|
|
|
_nc_set_type(_nc_first_name(ep->tterm.term_names));
|
|
_nc_curr_line = ep->startline;
|
|
_nc_write_entry(&ep->tterm);
|
|
}
|
|
|
|
static bool
|
|
immedhook(ENTRY * ep GCC_UNUSED)
|
|
/* write out entries with no use capabilities immediately to save storage */
|
|
{
|
|
#if !HAVE_BIG_CORE
|
|
/*
|
|
* This is strictly a core-economy kluge. The really clean way to handle
|
|
* compilation is to slurp the whole file into core and then do all the
|
|
* name-collision checks and entry writes in one swell foop. But the
|
|
* terminfo master file is large enough that some core-poor systems swap
|
|
* like crazy when you compile it this way...there have been reports of
|
|
* this process taking *three hours*, rather than the twenty seconds or
|
|
* less typical on my development box.
|
|
*
|
|
* So. This hook *immediately* writes out the referenced entry if it
|
|
* has no use capabilities. The compiler main loop refrains from
|
|
* adding the entry to the in-core list when this hook fires. If some
|
|
* other entry later needs to reference an entry that got written
|
|
* immediately, that's OK; the resolution code will fetch it off disk
|
|
* when it can't find it in core.
|
|
*
|
|
* Name collisions will still be detected, just not as cleanly. The
|
|
* write_entry() code complains before overwriting an entry that
|
|
* postdates the time of tic's first call to write_entry(). Thus
|
|
* it will complain about overwriting entries newly made during the
|
|
* tic run, but not about overwriting ones that predate it.
|
|
*
|
|
* The reason this is a hook, and not in line with the rest of the
|
|
* compiler code, is that the support for termcap fallback cannot assume
|
|
* it has anywhere to spool out these entries!
|
|
*
|
|
* The _nc_set_type() call here requires a compensating one in
|
|
* _nc_parse_entry().
|
|
*
|
|
* If you define HAVE_BIG_CORE, you'll disable this kluge. This will
|
|
* make tic a bit faster (because the resolution code won't have to do
|
|
* disk I/O nearly as often).
|
|
*/
|
|
if (ep->nuses == 0) {
|
|
int oldline = _nc_curr_line;
|
|
|
|
write_it(ep);
|
|
_nc_curr_line = oldline;
|
|
free(ep->tterm.str_table);
|
|
return (TRUE);
|
|
}
|
|
#endif /* HAVE_BIG_CORE */
|
|
return (FALSE);
|
|
}
|
|
|
|
static void
|
|
put_translate(int c)
|
|
/* emit a comment char, translating terminfo names to termcap names */
|
|
{
|
|
static bool in_name = FALSE;
|
|
static size_t have, used;
|
|
static char *namebuf, *suffix;
|
|
|
|
if (in_name) {
|
|
if (used + 1 >= have) {
|
|
have += 132;
|
|
namebuf = typeRealloc(char, have, namebuf);
|
|
suffix = typeRealloc(char, have, suffix);
|
|
}
|
|
if (c == '\n' || c == '@') {
|
|
namebuf[used++] = '\0';
|
|
(void) putchar('<');
|
|
(void) fputs(namebuf, stdout);
|
|
putchar(c);
|
|
in_name = FALSE;
|
|
} else if (c != '>') {
|
|
namebuf[used++] = (char) c;
|
|
} else { /* ah! candidate name! */
|
|
char *up;
|
|
NCURSES_CONST char *tp;
|
|
|
|
namebuf[used++] = '\0';
|
|
in_name = FALSE;
|
|
|
|
suffix[0] = '\0';
|
|
if ((up = strchr(namebuf, '#')) != 0
|
|
|| (up = strchr(namebuf, '=')) != 0
|
|
|| ((up = strchr(namebuf, '@')) != 0 && up[1] == '>')) {
|
|
(void) strcpy(suffix, up);
|
|
*up = '\0';
|
|
}
|
|
|
|
if ((tp = nametrans(namebuf)) != 0) {
|
|
(void) putchar(':');
|
|
(void) fputs(tp, stdout);
|
|
(void) fputs(suffix, stdout);
|
|
(void) putchar(':');
|
|
} else {
|
|
/* couldn't find a translation, just dump the name */
|
|
(void) putchar('<');
|
|
(void) fputs(namebuf, stdout);
|
|
(void) fputs(suffix, stdout);
|
|
(void) putchar('>');
|
|
}
|
|
}
|
|
} else {
|
|
used = 0;
|
|
if (c == '<') {
|
|
in_name = TRUE;
|
|
} else {
|
|
putchar(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Returns a string, stripped of leading/trailing whitespace */
|
|
static char *
|
|
stripped(char *src)
|
|
{
|
|
while (isspace(UChar(*src)))
|
|
src++;
|
|
if (*src != '\0') {
|
|
char *dst = strcpy((char *) malloc(strlen(src) + 1), src);
|
|
size_t len = strlen(dst);
|
|
while (--len != 0 && isspace(UChar(dst[len])))
|
|
dst[len] = '\0';
|
|
return dst;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static FILE *
|
|
open_input(const char *filename)
|
|
{
|
|
FILE *fp = fopen(filename, "r");
|
|
struct stat sb;
|
|
|
|
if (fp == 0) {
|
|
fprintf(stderr, "%s: Can't open %s\n", _nc_progname, filename);
|
|
ExitProgram(EXIT_FAILURE);
|
|
}
|
|
if (fstat(fileno(fp), &sb) < 0
|
|
|| (sb.st_mode & S_IFMT) != S_IFREG) {
|
|
fprintf(stderr, "%s: %s is not a file\n", _nc_progname, filename);
|
|
ExitProgram(EXIT_FAILURE);
|
|
}
|
|
return fp;
|
|
}
|
|
|
|
/* Parse the "-e" option-value into a list of names */
|
|
static char **
|
|
make_namelist(char *src)
|
|
{
|
|
char **dst = 0;
|
|
|
|
char *s, *base;
|
|
unsigned pass, n, nn;
|
|
char buffer[BUFSIZ];
|
|
|
|
if (src == 0) {
|
|
/* EMPTY */ ;
|
|
} else if (strchr(src, '/') != 0) { /* a filename */
|
|
FILE *fp = open_input(src);
|
|
|
|
for (pass = 1; pass <= 2; pass++) {
|
|
nn = 0;
|
|
while (fgets(buffer, sizeof(buffer), fp) != 0) {
|
|
if ((s = stripped(buffer)) != 0) {
|
|
if (dst != 0)
|
|
dst[nn] = s;
|
|
else
|
|
free(s);
|
|
nn++;
|
|
}
|
|
}
|
|
if (pass == 1) {
|
|
dst = typeCalloc(char *, nn + 1);
|
|
rewind(fp);
|
|
}
|
|
}
|
|
fclose(fp);
|
|
} else { /* literal list of names */
|
|
for (pass = 1; pass <= 2; pass++) {
|
|
for (n = nn = 0, base = src;; n++) {
|
|
int mark = src[n];
|
|
if (mark == ',' || mark == '\0') {
|
|
if (pass == 1) {
|
|
nn++;
|
|
} else {
|
|
src[n] = '\0';
|
|
if ((s = stripped(base)) != 0)
|
|
dst[nn++] = s;
|
|
base = &src[n + 1];
|
|
}
|
|
}
|
|
if (mark == '\0')
|
|
break;
|
|
}
|
|
if (pass == 1)
|
|
dst = typeCalloc(char *, nn + 1);
|
|
}
|
|
}
|
|
if (showsummary && (dst != 0)) {
|
|
fprintf(log_fp, "Entries that will be compiled:\n");
|
|
for (n = 0; dst[n] != 0; n++)
|
|
fprintf(log_fp, "%u:%s\n", n + 1, dst[n]);
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static bool
|
|
matches(char **needle, const char *haystack)
|
|
/* does entry in needle list match |-separated field in haystack? */
|
|
{
|
|
bool code = FALSE;
|
|
size_t n;
|
|
|
|
if (needle != 0) {
|
|
for (n = 0; needle[n] != 0; n++) {
|
|
if (_nc_name_match(haystack, needle[n], "|")) {
|
|
code = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
code = TRUE;
|
|
return (code);
|
|
}
|
|
|
|
static FILE *
|
|
open_tempfile(char *name)
|
|
{
|
|
FILE *result = 0;
|
|
#if HAVE_MKSTEMP
|
|
int fd = mkstemp(name);
|
|
if (fd >= 0)
|
|
result = fdopen(fd, "w");
|
|
#else
|
|
if (tmpnam(name) != 0)
|
|
result = fopen(name, "w");
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char my_tmpname[PATH_MAX];
|
|
int v_opt = -1, debug_level;
|
|
int smart_defaults = TRUE;
|
|
char *termcap;
|
|
ENTRY *qp;
|
|
|
|
int this_opt, last_opt = '?';
|
|
|
|
int outform = F_TERMINFO; /* output format */
|
|
int sortmode = S_TERMINFO; /* sort_mode */
|
|
|
|
int width = 60;
|
|
bool formatted = FALSE; /* reformat complex strings? */
|
|
bool literal = FALSE; /* suppress post-processing? */
|
|
int numbers = 0; /* format "%'char'" to/from "%{number}" */
|
|
bool forceresolve = FALSE; /* force resolution */
|
|
bool limited = TRUE;
|
|
char *tversion = (char *) NULL;
|
|
const char *source_file = "terminfo";
|
|
char **namelst = 0;
|
|
char *outdir = (char *) NULL;
|
|
bool check_only = FALSE;
|
|
bool suppress_untranslatable = FALSE;
|
|
|
|
log_fp = stderr;
|
|
|
|
_nc_progname = _nc_rootname(argv[0]);
|
|
|
|
if ((infodump = same_program(_nc_progname, PROG_CAPTOINFO)) != FALSE) {
|
|
outform = F_TERMINFO;
|
|
sortmode = S_TERMINFO;
|
|
}
|
|
if ((capdump = same_program(_nc_progname, PROG_INFOTOCAP)) != FALSE) {
|
|
outform = F_TERMCAP;
|
|
sortmode = S_TERMCAP;
|
|
}
|
|
#if NCURSES_XNAMES
|
|
use_extended_names(FALSE);
|
|
#endif
|
|
|
|
/*
|
|
* Processing arguments is a little complicated, since someone made a
|
|
* design decision to allow the numeric values for -w, -v options to
|
|
* be optional.
|
|
*/
|
|
while ((this_opt = getopt(argc, argv,
|
|
"0123456789CILNR:TUVace:fGgo:rstvwx")) != -1) {
|
|
if (isdigit(this_opt)) {
|
|
switch (last_opt) {
|
|
case 'v':
|
|
v_opt = (v_opt * 10) + (this_opt - '0');
|
|
break;
|
|
case 'w':
|
|
width = (width * 10) + (this_opt - '0');
|
|
break;
|
|
default:
|
|
if (this_opt != '1')
|
|
usage();
|
|
last_opt = this_opt;
|
|
width = 0;
|
|
}
|
|
continue;
|
|
}
|
|
switch (this_opt) {
|
|
case 'C':
|
|
capdump = TRUE;
|
|
outform = F_TERMCAP;
|
|
sortmode = S_TERMCAP;
|
|
break;
|
|
case 'I':
|
|
infodump = TRUE;
|
|
outform = F_TERMINFO;
|
|
sortmode = S_TERMINFO;
|
|
break;
|
|
case 'L':
|
|
infodump = TRUE;
|
|
outform = F_VARIABLE;
|
|
sortmode = S_VARIABLE;
|
|
break;
|
|
case 'N':
|
|
smart_defaults = FALSE;
|
|
literal = TRUE;
|
|
break;
|
|
case 'R':
|
|
tversion = optarg;
|
|
break;
|
|
case 'T':
|
|
limited = FALSE;
|
|
break;
|
|
case 'U':
|
|
literal = TRUE;
|
|
break;
|
|
case 'V':
|
|
puts(curses_version());
|
|
cleanup(namelst);
|
|
ExitProgram(EXIT_SUCCESS);
|
|
case 'c':
|
|
check_only = TRUE;
|
|
break;
|
|
case 'e':
|
|
namelst = make_namelist(optarg);
|
|
break;
|
|
case 'f':
|
|
formatted = TRUE;
|
|
break;
|
|
case 'G':
|
|
numbers = 1;
|
|
break;
|
|
case 'g':
|
|
numbers = -1;
|
|
break;
|
|
case 'o':
|
|
outdir = optarg;
|
|
break;
|
|
case 'r':
|
|
forceresolve = TRUE;
|
|
break;
|
|
case 's':
|
|
showsummary = TRUE;
|
|
break;
|
|
case 'v':
|
|
v_opt = 0;
|
|
break;
|
|
case 'w':
|
|
width = 0;
|
|
break;
|
|
#if NCURSES_XNAMES
|
|
case 't':
|
|
_nc_disable_period = FALSE;
|
|
suppress_untranslatable = TRUE;
|
|
break;
|
|
case 'a':
|
|
_nc_disable_period = TRUE;
|
|
/* FALLTHRU */
|
|
case 'x':
|
|
use_extended_names(TRUE);
|
|
break;
|
|
#endif
|
|
default:
|
|
usage();
|
|
}
|
|
last_opt = this_opt;
|
|
}
|
|
|
|
debug_level = (v_opt > 0) ? v_opt : (v_opt == 0);
|
|
set_trace_level(debug_level);
|
|
|
|
if (_nc_tracing) {
|
|
save_check_termtype = _nc_check_termtype2;
|
|
_nc_check_termtype2 = check_termtype;
|
|
}
|
|
#if !HAVE_BIG_CORE
|
|
/*
|
|
* Aaargh! immedhook seriously hoses us!
|
|
*
|
|
* One problem with immedhook is it means we can't do -e. Problem
|
|
* is that we can't guarantee that for each terminal listed, all the
|
|
* terminals it depends on will have been kept in core for reference
|
|
* resolution -- in fact it's certain the primitive types at the end
|
|
* of reference chains *won't* be in core unless they were explicitly
|
|
* in the select list themselves.
|
|
*/
|
|
if (namelst && (!infodump && !capdump)) {
|
|
(void) fprintf(stderr,
|
|
"Sorry, -e can't be used without -I or -C\n");
|
|
cleanup(namelst);
|
|
ExitProgram(EXIT_FAILURE);
|
|
}
|
|
#endif /* HAVE_BIG_CORE */
|
|
|
|
if (optind < argc) {
|
|
source_file = argv[optind++];
|
|
if (optind < argc) {
|
|
fprintf(stderr,
|
|
"%s: Too many file names. Usage:\n\t%s %s",
|
|
_nc_progname,
|
|
_nc_progname,
|
|
usage_string);
|
|
ExitProgram(EXIT_FAILURE);
|
|
}
|
|
} else {
|
|
if (infodump == TRUE) {
|
|
/* captoinfo's no-argument case */
|
|
source_file = "/etc/termcap";
|
|
if ((termcap = getenv("TERMCAP")) != 0
|
|
&& (namelst = make_namelist(getenv("TERM"))) != 0) {
|
|
if (access(termcap, F_OK) == 0) {
|
|
/* file exists */
|
|
source_file = termcap;
|
|
} else if ((tmp_fp = open_tempfile(strcpy(my_tmpname,
|
|
"/tmp/XXXXXX")))
|
|
!= 0) {
|
|
source_file = my_tmpname;
|
|
fprintf(tmp_fp, "%s\n", termcap);
|
|
fclose(tmp_fp);
|
|
tmp_fp = open_input(source_file);
|
|
to_remove = source_file;
|
|
} else {
|
|
failed("tmpnam");
|
|
}
|
|
}
|
|
} else {
|
|
/* tic */
|
|
fprintf(stderr,
|
|
"%s: File name needed. Usage:\n\t%s %s",
|
|
_nc_progname,
|
|
_nc_progname,
|
|
usage_string);
|
|
cleanup(namelst);
|
|
ExitProgram(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if (tmp_fp == 0)
|
|
tmp_fp = open_input(source_file);
|
|
|
|
if (infodump)
|
|
dump_init(tversion,
|
|
smart_defaults
|
|
? outform
|
|
: F_LITERAL,
|
|
sortmode, width, debug_level, formatted);
|
|
else if (capdump)
|
|
dump_init(tversion,
|
|
outform,
|
|
sortmode, width, debug_level, FALSE);
|
|
|
|
/* parse entries out of the source file */
|
|
_nc_set_source(source_file);
|
|
#if !HAVE_BIG_CORE
|
|
if (!(check_only || infodump || capdump))
|
|
_nc_set_writedir(outdir);
|
|
#endif /* HAVE_BIG_CORE */
|
|
_nc_read_entry_source(tmp_fp, (char *) NULL,
|
|
!smart_defaults || literal, FALSE,
|
|
((check_only || infodump || capdump)
|
|
? NULLHOOK
|
|
: immedhook));
|
|
|
|
/* do use resolution */
|
|
if (check_only || (!infodump && !capdump) || forceresolve) {
|
|
if (!_nc_resolve_uses2(TRUE, literal) && !check_only) {
|
|
cleanup(namelst);
|
|
ExitProgram(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* length check */
|
|
if (check_only && (capdump || infodump)) {
|
|
for_entry_list(qp) {
|
|
if (matches(namelst, qp->tterm.term_names)) {
|
|
int len = fmt_entry(&qp->tterm, NULL, FALSE, TRUE, infodump, numbers);
|
|
|
|
if (len > (infodump ? MAX_TERMINFO_LENGTH : MAX_TERMCAP_LENGTH))
|
|
(void) fprintf(stderr,
|
|
"warning: resolved %s entry is %d bytes long\n",
|
|
_nc_first_name(qp->tterm.term_names),
|
|
len);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* write or dump all entries */
|
|
if (!check_only) {
|
|
if (!infodump && !capdump) {
|
|
_nc_set_writedir(outdir);
|
|
for_entry_list(qp) {
|
|
if (matches(namelst, qp->tterm.term_names))
|
|
write_it(qp);
|
|
}
|
|
} else {
|
|
/* this is in case infotocap() generates warnings */
|
|
_nc_curr_col = _nc_curr_line = -1;
|
|
|
|
for_entry_list(qp) {
|
|
if (matches(namelst, qp->tterm.term_names)) {
|
|
int j = qp->cend - qp->cstart;
|
|
int len = 0;
|
|
|
|
/* this is in case infotocap() generates warnings */
|
|
_nc_set_type(_nc_first_name(qp->tterm.term_names));
|
|
|
|
(void) fseek(tmp_fp, qp->cstart, SEEK_SET);
|
|
while (j-- > 0) {
|
|
if (infodump)
|
|
(void) putchar(fgetc(tmp_fp));
|
|
else
|
|
put_translate(fgetc(tmp_fp));
|
|
}
|
|
|
|
dump_entry(&qp->tterm, suppress_untranslatable,
|
|
limited, numbers, NULL);
|
|
for (j = 0; j < (int) qp->nuses; j++)
|
|
dump_uses(qp->uses[j].name, !capdump);
|
|
len = show_entry();
|
|
if (debug_level != 0 && !limited)
|
|
printf("# length=%d\n", len);
|
|
}
|
|
}
|
|
if (!namelst && _nc_tail) {
|
|
int c, oldc = '\0';
|
|
bool in_comment = FALSE;
|
|
bool trailing_comment = FALSE;
|
|
|
|
(void) fseek(tmp_fp, _nc_tail->cend, SEEK_SET);
|
|
while ((c = fgetc(tmp_fp)) != EOF) {
|
|
if (oldc == '\n') {
|
|
if (c == '#') {
|
|
trailing_comment = TRUE;
|
|
in_comment = TRUE;
|
|
} else {
|
|
in_comment = FALSE;
|
|
}
|
|
}
|
|
if (trailing_comment
|
|
&& (in_comment || (oldc == '\n' && c == '\n')))
|
|
putchar(c);
|
|
oldc = c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Show the directory into which entries were written, and the total
|
|
* number of entries
|
|
*/
|
|
if (showsummary
|
|
&& (!(check_only || infodump || capdump))) {
|
|
int total = _nc_tic_written();
|
|
if (total != 0)
|
|
fprintf(log_fp, "%d entries written to %s\n",
|
|
total,
|
|
_nc_tic_dir((char *) 0));
|
|
else
|
|
fprintf(log_fp, "No entries written\n");
|
|
}
|
|
cleanup(namelst);
|
|
ExitProgram(EXIT_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* This bit of legerdemain turns all the terminfo variable names into
|
|
* references to locations in the arrays Booleans, Numbers, and Strings ---
|
|
* precisely what's needed (see comp_parse.c).
|
|
*/
|
|
#undef CUR
|
|
#define CUR tp->
|
|
|
|
/*
|
|
* Check if the alternate character-set capabilities are consistent.
|
|
*/
|
|
static void
|
|
check_acs(TERMTYPE *tp)
|
|
{
|
|
if (VALID_STRING(acs_chars)) {
|
|
const char *boxes = "lmkjtuvwqxn";
|
|
char mapped[256];
|
|
char missing[256];
|
|
const char *p;
|
|
char *q;
|
|
|
|
memset(mapped, 0, sizeof(mapped));
|
|
for (p = acs_chars; *p != '\0'; p += 2) {
|
|
if (p[1] == '\0') {
|
|
_nc_warning("acsc has odd number of characters");
|
|
break;
|
|
}
|
|
mapped[UChar(p[0])] = p[1];
|
|
}
|
|
|
|
if (mapped[UChar('I')] && !mapped[UChar('i')]) {
|
|
_nc_warning("acsc refers to 'I', which is probably an error");
|
|
}
|
|
|
|
for (p = boxes, q = missing; *p != '\0'; ++p) {
|
|
if (!mapped[UChar(p[0])]) {
|
|
*q++ = p[0];
|
|
}
|
|
}
|
|
*q = '\0';
|
|
|
|
assert(strlen(missing) <= strlen(boxes));
|
|
if (*missing != '\0' && strcmp(missing, boxes)) {
|
|
_nc_warning("acsc is missing some line-drawing mapping: %s", missing);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if the color capabilities are consistent
|
|
*/
|
|
static void
|
|
check_colors(TERMTYPE *tp)
|
|
{
|
|
if ((max_colors > 0) != (max_pairs > 0)
|
|
|| ((max_colors > max_pairs) && (initialize_pair == 0)))
|
|
_nc_warning("inconsistent values for max_colors (%d) and max_pairs (%d)",
|
|
max_colors, max_pairs);
|
|
|
|
PAIRED(set_foreground, set_background);
|
|
PAIRED(set_a_foreground, set_a_background);
|
|
PAIRED(set_color_pair, initialize_pair);
|
|
|
|
if (VALID_STRING(set_foreground)
|
|
&& VALID_STRING(set_a_foreground)
|
|
&& !_nc_capcmp(set_foreground, set_a_foreground))
|
|
_nc_warning("expected setf/setaf to be different");
|
|
|
|
if (VALID_STRING(set_background)
|
|
&& VALID_STRING(set_a_background)
|
|
&& !_nc_capcmp(set_background, set_a_background))
|
|
_nc_warning("expected setb/setab to be different");
|
|
|
|
/* see: has_colors() */
|
|
if (VALID_NUMERIC(max_colors) && VALID_NUMERIC(max_pairs)
|
|
&& (((set_foreground != NULL)
|
|
&& (set_background != NULL))
|
|
|| ((set_a_foreground != NULL)
|
|
&& (set_a_background != NULL))
|
|
|| set_color_pair)) {
|
|
if (!VALID_STRING(orig_pair) && !VALID_STRING(orig_colors))
|
|
_nc_warning("expected either op/oc string for resetting colors");
|
|
}
|
|
}
|
|
|
|
static char
|
|
keypad_final(const char *string)
|
|
{
|
|
char result = '\0';
|
|
|
|
if (VALID_STRING(string)
|
|
&& *string++ == '\033'
|
|
&& *string++ == 'O'
|
|
&& strlen(string) == 1) {
|
|
result = *string;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
keypad_index(const char *string)
|
|
{
|
|
char *test;
|
|
const char *list = "PQRSwxymtuvlqrsPpn"; /* app-keypad except "Enter" */
|
|
int ch;
|
|
int result = -1;
|
|
|
|
if ((ch = keypad_final(string)) != '\0') {
|
|
test = strchr(list, ch);
|
|
if (test != 0)
|
|
result = (test - list);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#define MAX_KP 5
|
|
/*
|
|
* Do a quick sanity-check for vt100-style keypads to see if the 5-key keypad
|
|
* is mapped inconsistently.
|
|
*/
|
|
static void
|
|
check_keypad(TERMTYPE *tp)
|
|
{
|
|
char show[80];
|
|
|
|
if (VALID_STRING(key_a1) &&
|
|
VALID_STRING(key_a3) &&
|
|
VALID_STRING(key_b2) &&
|
|
VALID_STRING(key_c1) &&
|
|
VALID_STRING(key_c3)) {
|
|
char final[MAX_KP + 1];
|
|
int list[MAX_KP];
|
|
int increase = 0;
|
|
int j, k, kk;
|
|
int last;
|
|
int test;
|
|
|
|
final[0] = keypad_final(key_a1);
|
|
final[1] = keypad_final(key_a3);
|
|
final[2] = keypad_final(key_b2);
|
|
final[3] = keypad_final(key_c1);
|
|
final[4] = keypad_final(key_c3);
|
|
final[5] = '\0';
|
|
|
|
/* special case: legacy coding using 1,2,3,0,. on the bottom */
|
|
assert(strlen(final) <= MAX_KP);
|
|
if (!strcmp(final, "qsrpn"))
|
|
return;
|
|
|
|
list[0] = keypad_index(key_a1);
|
|
list[1] = keypad_index(key_a3);
|
|
list[2] = keypad_index(key_b2);
|
|
list[3] = keypad_index(key_c1);
|
|
list[4] = keypad_index(key_c3);
|
|
|
|
/* check that they're all vt100 keys */
|
|
for (j = 0; j < MAX_KP; ++j) {
|
|
if (list[j] < 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* check if they're all in increasing order */
|
|
for (j = 1; j < MAX_KP; ++j) {
|
|
if (list[j] > list[j - 1]) {
|
|
++increase;
|
|
}
|
|
}
|
|
if (increase != (MAX_KP - 1)) {
|
|
show[0] = '\0';
|
|
|
|
for (j = 0, last = -1; j < MAX_KP; ++j) {
|
|
for (k = 0, kk = -1, test = 100; k < 5; ++k) {
|
|
if (list[k] > last &&
|
|
list[k] < test) {
|
|
test = list[k];
|
|
kk = k;
|
|
}
|
|
}
|
|
last = test;
|
|
assert(strlen(show) < (MAX_KP * 4));
|
|
switch (kk) {
|
|
case 0:
|
|
strcat(show, " ka1");
|
|
break;
|
|
case 1:
|
|
strcat(show, " ka3");
|
|
break;
|
|
case 2:
|
|
strcat(show, " kb2");
|
|
break;
|
|
case 3:
|
|
strcat(show, " kc1");
|
|
break;
|
|
case 4:
|
|
strcat(show, " kc3");
|
|
break;
|
|
}
|
|
}
|
|
|
|
_nc_warning("vt100 keypad order inconsistent: %s", show);
|
|
}
|
|
|
|
} else if (VALID_STRING(key_a1) ||
|
|
VALID_STRING(key_a3) ||
|
|
VALID_STRING(key_b2) ||
|
|
VALID_STRING(key_c1) ||
|
|
VALID_STRING(key_c3)) {
|
|
show[0] = '\0';
|
|
if (keypad_index(key_a1) >= 0)
|
|
strcat(show, " ka1");
|
|
if (keypad_index(key_a3) >= 0)
|
|
strcat(show, " ka3");
|
|
if (keypad_index(key_b2) >= 0)
|
|
strcat(show, " kb2");
|
|
if (keypad_index(key_c1) >= 0)
|
|
strcat(show, " kc1");
|
|
if (keypad_index(key_c3) >= 0)
|
|
strcat(show, " kc3");
|
|
if (*show != '\0')
|
|
_nc_warning("vt100 keypad map incomplete:%s", show);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns the expected number of parameters for the given capability.
|
|
*/
|
|
static int
|
|
expected_params(const char *name)
|
|
{
|
|
/* *INDENT-OFF* */
|
|
static const struct {
|
|
const char *name;
|
|
int count;
|
|
} table[] = {
|
|
{ "S0", 1 }, /* 'screen' extension */
|
|
{ "birep", 2 },
|
|
{ "chr", 1 },
|
|
{ "colornm", 1 },
|
|
{ "cpi", 1 },
|
|
{ "csnm", 1 },
|
|
{ "csr", 2 },
|
|
{ "cub", 1 },
|
|
{ "cud", 1 },
|
|
{ "cuf", 1 },
|
|
{ "cup", 2 },
|
|
{ "cuu", 1 },
|
|
{ "cvr", 1 },
|
|
{ "cwin", 5 },
|
|
{ "dch", 1 },
|
|
{ "defc", 3 },
|
|
{ "dial", 1 },
|
|
{ "dispc", 1 },
|
|
{ "dl", 1 },
|
|
{ "ech", 1 },
|
|
{ "getm", 1 },
|
|
{ "hpa", 1 },
|
|
{ "ich", 1 },
|
|
{ "il", 1 },
|
|
{ "indn", 1 },
|
|
{ "initc", 4 },
|
|
{ "initp", 7 },
|
|
{ "lpi", 1 },
|
|
{ "mc5p", 1 },
|
|
{ "mrcup", 2 },
|
|
{ "mvpa", 1 },
|
|
{ "pfkey", 2 },
|
|
{ "pfloc", 2 },
|
|
{ "pfx", 2 },
|
|
{ "pfxl", 3 },
|
|
{ "pln", 2 },
|
|
{ "qdial", 1 },
|
|
{ "rcsd", 1 },
|
|
{ "rep", 2 },
|
|
{ "rin", 1 },
|
|
{ "sclk", 3 },
|
|
{ "scp", 1 },
|
|
{ "scs", 1 },
|
|
{ "scsd", 2 },
|
|
{ "setab", 1 },
|
|
{ "setaf", 1 },
|
|
{ "setb", 1 },
|
|
{ "setcolor", 1 },
|
|
{ "setf", 1 },
|
|
{ "sgr", 9 },
|
|
{ "sgr1", 6 },
|
|
{ "slength", 1 },
|
|
{ "slines", 1 },
|
|
{ "smgbp", 1 }, /* 2 if smgtp is not given */
|
|
{ "smglp", 1 },
|
|
{ "smglr", 2 },
|
|
{ "smgrp", 1 },
|
|
{ "smgtb", 2 },
|
|
{ "smgtp", 1 },
|
|
{ "tsl", 1 },
|
|
{ "u6", -1 },
|
|
{ "vpa", 1 },
|
|
{ "wind", 4 },
|
|
{ "wingo", 1 },
|
|
};
|
|
/* *INDENT-ON* */
|
|
|
|
unsigned n;
|
|
int result = 0; /* function-keys, etc., use none */
|
|
|
|
for (n = 0; n < SIZEOF(table); n++) {
|
|
if (!strcmp(name, table[n].name)) {
|
|
result = table[n].count;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Make a quick sanity check for the parameters which are used in the given
|
|
* strings. If there are no "%p" tokens, then there should be no other "%"
|
|
* markers.
|
|
*/
|
|
static void
|
|
check_params(TERMTYPE *tp, const char *name, char *value)
|
|
{
|
|
int expected = expected_params(name);
|
|
int actual = 0;
|
|
int n;
|
|
bool params[10];
|
|
char *s = value;
|
|
|
|
#ifdef set_top_margin_parm
|
|
if (!strcmp(name, "smgbp")
|
|
&& set_top_margin_parm == 0)
|
|
expected = 2;
|
|
#endif
|
|
|
|
for (n = 0; n < 10; n++)
|
|
params[n] = FALSE;
|
|
|
|
while (*s != 0) {
|
|
if (*s == '%') {
|
|
if (*++s == '\0') {
|
|
_nc_warning("expected character after %% in %s", name);
|
|
break;
|
|
} else if (*s == 'p') {
|
|
if (*++s == '\0' || !isdigit((int) *s)) {
|
|
_nc_warning("expected digit after %%p in %s", name);
|
|
return;
|
|
} else {
|
|
n = (*s - '0');
|
|
if (n > actual)
|
|
actual = n;
|
|
params[n] = TRUE;
|
|
}
|
|
}
|
|
}
|
|
s++;
|
|
}
|
|
|
|
if (params[0]) {
|
|
_nc_warning("%s refers to parameter 0 (%%p0), which is not allowed", name);
|
|
}
|
|
if (value == set_attributes || expected < 0) {
|
|
;
|
|
} else if (expected != actual) {
|
|
_nc_warning("%s uses %d parameters, expected %d", name,
|
|
actual, expected);
|
|
for (n = 1; n < actual; n++) {
|
|
if (!params[n])
|
|
_nc_warning("%s omits parameter %d", name, n);
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *
|
|
skip_delay(char *s)
|
|
{
|
|
while (*s == '/' || isdigit(UChar(*s)))
|
|
++s;
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* Skip a delay altogether, e.g., when comparing a simple string to sgr,
|
|
* the latter may have a worst-case delay on the end.
|
|
*/
|
|
static char *
|
|
ignore_delays(char *s)
|
|
{
|
|
int delaying = 0;
|
|
|
|
do {
|
|
switch (*s) {
|
|
case '$':
|
|
if (delaying == 0)
|
|
delaying = 1;
|
|
break;
|
|
case '<':
|
|
if (delaying == 1)
|
|
delaying = 2;
|
|
break;
|
|
case '\0':
|
|
delaying = 0;
|
|
break;
|
|
default:
|
|
if (delaying) {
|
|
s = skip_delay(s);
|
|
if (*s == '>')
|
|
++s;
|
|
delaying = 0;
|
|
}
|
|
break;
|
|
}
|
|
if (delaying)
|
|
++s;
|
|
} while (delaying);
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* An sgr string may contain several settings other than the one we're
|
|
* interested in, essentially sgr0 + rmacs + whatever. As long as the
|
|
* "whatever" is contained in the sgr string, that is close enough for our
|
|
* sanity check.
|
|
*/
|
|
static bool
|
|
similar_sgr(int num, char *a, char *b)
|
|
{
|
|
static const char *names[] =
|
|
{
|
|
"none"
|
|
,"standout"
|
|
,"underline"
|
|
,"reverse"
|
|
,"blink"
|
|
,"dim"
|
|
,"bold"
|
|
,"invis"
|
|
,"protect"
|
|
,"altcharset"
|
|
};
|
|
char *base_a = a;
|
|
char *base_b = b;
|
|
int delaying = 0;
|
|
|
|
while (*b != 0) {
|
|
while (*a != *b) {
|
|
if (*a == 0) {
|
|
if (b[0] == '$'
|
|
&& b[1] == '<') {
|
|
_nc_warning("Did not find delay %s", _nc_visbuf(b));
|
|
} else {
|
|
_nc_warning("checking sgr(%s) %s\n\tcompare to %s\n\tunmatched %s",
|
|
names[num], _nc_visbuf2(1, base_a),
|
|
_nc_visbuf2(2, base_b),
|
|
_nc_visbuf2(3, b));
|
|
}
|
|
return FALSE;
|
|
} else if (delaying) {
|
|
a = skip_delay(a);
|
|
b = skip_delay(b);
|
|
} else {
|
|
a++;
|
|
}
|
|
}
|
|
switch (*a) {
|
|
case '$':
|
|
if (delaying == 0)
|
|
delaying = 1;
|
|
break;
|
|
case '<':
|
|
if (delaying == 1)
|
|
delaying = 2;
|
|
break;
|
|
default:
|
|
delaying = 0;
|
|
break;
|
|
}
|
|
a++;
|
|
b++;
|
|
}
|
|
/* ignore delays on the end of the string */
|
|
a = ignore_delays(a);
|
|
return ((num != 0) || (*a == 0));
|
|
}
|
|
|
|
static char *
|
|
check_sgr(TERMTYPE *tp, char *zero, int num, char *cap, const char *name)
|
|
{
|
|
char *test;
|
|
|
|
_nc_tparm_err = 0;
|
|
test = TPARM_9(set_attributes,
|
|
num == 1,
|
|
num == 2,
|
|
num == 3,
|
|
num == 4,
|
|
num == 5,
|
|
num == 6,
|
|
num == 7,
|
|
num == 8,
|
|
num == 9);
|
|
if (test != 0) {
|
|
if (PRESENT(cap)) {
|
|
if (!similar_sgr(num, test, cap)) {
|
|
_nc_warning("%s differs from sgr(%d)\n\t%s=%s\n\tsgr(%d)=%s",
|
|
name, num,
|
|
name, _nc_visbuf2(1, cap),
|
|
num, _nc_visbuf2(2, test));
|
|
}
|
|
} else if (_nc_capcmp(test, zero)) {
|
|
_nc_warning("sgr(%d) present, but not %s", num, name);
|
|
}
|
|
} else if (PRESENT(cap)) {
|
|
_nc_warning("sgr(%d) missing, but %s present", num, name);
|
|
}
|
|
if (_nc_tparm_err)
|
|
_nc_warning("stack error in sgr(%d) string", num);
|
|
return test;
|
|
}
|
|
|
|
#define CHECK_SGR(num,name) check_sgr(tp, zero, num, name, #name)
|
|
|
|
#ifdef TRACE
|
|
/*
|
|
* If tic is compiled with TRACE, we'll be able to see the output from the
|
|
* DEBUG() macro. But since it doesn't use traceon(), it always goes to
|
|
* the standard error. Use this function to make it simpler to follow the
|
|
* resulting debug traces.
|
|
*/
|
|
static void
|
|
show_where(unsigned level)
|
|
{
|
|
if (_nc_tracing >= DEBUG_LEVEL(level)) {
|
|
char my_name[256];
|
|
_nc_get_type(my_name);
|
|
fprintf(stderr, "\"%s\", line %d, '%s' ",
|
|
_nc_get_source(),
|
|
_nc_curr_line, my_name);
|
|
}
|
|
}
|
|
|
|
#else
|
|
#define show_where(level) /* nothing */
|
|
#endif
|
|
|
|
/* other sanity-checks (things that we don't want in the normal
|
|
* logic that reads a terminfo entry)
|
|
*/
|
|
static void
|
|
check_termtype(TERMTYPE *tp, bool literal)
|
|
{
|
|
bool conflict = FALSE;
|
|
unsigned j, k;
|
|
char fkeys[STRCOUNT];
|
|
|
|
/*
|
|
* A terminal entry may contain more than one keycode assigned to
|
|
* a given string (e.g., KEY_END and KEY_LL). But curses will only
|
|
* return one (the last one assigned).
|
|
*/
|
|
if (!(_nc_syntax == SYN_TERMCAP && capdump)) {
|
|
memset(fkeys, 0, sizeof(fkeys));
|
|
for (j = 0; _nc_tinfo_fkeys[j].code; j++) {
|
|
char *a = tp->Strings[_nc_tinfo_fkeys[j].offset];
|
|
bool first = TRUE;
|
|
if (!VALID_STRING(a))
|
|
continue;
|
|
for (k = j + 1; _nc_tinfo_fkeys[k].code; k++) {
|
|
char *b = tp->Strings[_nc_tinfo_fkeys[k].offset];
|
|
if (!VALID_STRING(b)
|
|
|| fkeys[k])
|
|
continue;
|
|
if (!_nc_capcmp(a, b)) {
|
|
fkeys[j] = 1;
|
|
fkeys[k] = 1;
|
|
if (first) {
|
|
if (!conflict) {
|
|
_nc_warning("Conflicting key definitions (using the last)");
|
|
conflict = TRUE;
|
|
}
|
|
fprintf(stderr, "... %s is the same as %s",
|
|
keyname((int) _nc_tinfo_fkeys[j].code),
|
|
keyname((int) _nc_tinfo_fkeys[k].code));
|
|
first = FALSE;
|
|
} else {
|
|
fprintf(stderr, ", %s",
|
|
keyname((int) _nc_tinfo_fkeys[k].code));
|
|
}
|
|
}
|
|
}
|
|
if (!first)
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < NUM_STRINGS(tp); j++) {
|
|
char *a = tp->Strings[j];
|
|
if (VALID_STRING(a))
|
|
check_params(tp, ExtStrname(tp, j, strnames), a);
|
|
}
|
|
|
|
check_acs(tp);
|
|
check_colors(tp);
|
|
check_keypad(tp);
|
|
|
|
/*
|
|
* These may be mismatched because the terminal description relies on
|
|
* restoring the cursor visibility by resetting it.
|
|
*/
|
|
ANDMISSING(cursor_invisible, cursor_normal);
|
|
ANDMISSING(cursor_visible, cursor_normal);
|
|
|
|
if (PRESENT(cursor_visible) && PRESENT(cursor_normal)
|
|
&& !_nc_capcmp(cursor_visible, cursor_normal))
|
|
_nc_warning("cursor_visible is same as cursor_normal");
|
|
|
|
/*
|
|
* From XSI & O'Reilly, we gather that sc/rc are required if csr is
|
|
* given, because the cursor position after the scrolling operation is
|
|
* performed is undefined.
|
|
*/
|
|
ANDMISSING(change_scroll_region, save_cursor);
|
|
ANDMISSING(change_scroll_region, restore_cursor);
|
|
|
|
if (PRESENT(set_attributes)) {
|
|
char *zero = 0;
|
|
|
|
_nc_tparm_err = 0;
|
|
if (PRESENT(exit_attribute_mode)) {
|
|
zero = strdup(CHECK_SGR(0, exit_attribute_mode));
|
|
} else {
|
|
zero = strdup(TPARM_9(set_attributes, 0, 0, 0, 0, 0, 0, 0, 0, 0));
|
|
}
|
|
if (_nc_tparm_err)
|
|
_nc_warning("stack error in sgr(0) string");
|
|
|
|
if (zero != 0) {
|
|
CHECK_SGR(1, enter_standout_mode);
|
|
CHECK_SGR(2, enter_underline_mode);
|
|
CHECK_SGR(3, enter_reverse_mode);
|
|
CHECK_SGR(4, enter_blink_mode);
|
|
CHECK_SGR(5, enter_dim_mode);
|
|
CHECK_SGR(6, enter_bold_mode);
|
|
CHECK_SGR(7, enter_secure_mode);
|
|
CHECK_SGR(8, enter_protected_mode);
|
|
CHECK_SGR(9, enter_alt_charset_mode);
|
|
free(zero);
|
|
} else {
|
|
_nc_warning("sgr(0) did not return a value");
|
|
}
|
|
} else if (PRESENT(exit_attribute_mode) &&
|
|
set_attributes != CANCELLED_STRING) {
|
|
if (_nc_syntax == SYN_TERMINFO)
|
|
_nc_warning("missing sgr string");
|
|
}
|
|
|
|
if (PRESENT(exit_attribute_mode)) {
|
|
char *check_sgr0 = _nc_trim_sgr0(tp);
|
|
|
|
if (check_sgr0 == 0 || *check_sgr0 == '\0') {
|
|
_nc_warning("trimmed sgr0 is empty");
|
|
} else {
|
|
show_where(2);
|
|
if (check_sgr0 != exit_attribute_mode) {
|
|
DEBUG(2,
|
|
("will trim sgr0\n\toriginal sgr0=%s\n\ttrimmed sgr0=%s",
|
|
_nc_visbuf2(1, exit_attribute_mode),
|
|
_nc_visbuf2(2, check_sgr0)));
|
|
free(check_sgr0);
|
|
} else {
|
|
DEBUG(2,
|
|
("will not trim sgr0\n\toriginal sgr0=%s",
|
|
_nc_visbuf(exit_attribute_mode)));
|
|
}
|
|
}
|
|
}
|
|
#ifdef TRACE
|
|
show_where(2);
|
|
if (!auto_right_margin) {
|
|
DEBUG(2,
|
|
("can write to lower-right directly"));
|
|
} else if (PRESENT(enter_am_mode) && PRESENT(exit_am_mode)) {
|
|
DEBUG(2,
|
|
("can write to lower-right by suppressing automargin"));
|
|
} else if ((PRESENT(enter_insert_mode) && PRESENT(exit_insert_mode))
|
|
|| PRESENT(insert_character) || PRESENT(parm_ich)) {
|
|
DEBUG(2,
|
|
("can write to lower-right by using inserts"));
|
|
} else {
|
|
DEBUG(2,
|
|
("cannot write to lower-right"));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Some standard applications (e.g., vi) and some non-curses
|
|
* applications (e.g., jove) get confused if we have both ich1 and
|
|
* smir/rmir. Let's be nice and warn about that, too, even though
|
|
* ncurses handles it.
|
|
*/
|
|
if ((PRESENT(enter_insert_mode) || PRESENT(exit_insert_mode))
|
|
&& PRESENT(parm_ich)) {
|
|
_nc_warning("non-curses applications may be confused by ich1 with smir/rmir");
|
|
}
|
|
|
|
/*
|
|
* Finally, do the non-verbose checks
|
|
*/
|
|
if (save_check_termtype != 0)
|
|
save_check_termtype(tp, literal);
|
|
}
|