150276Speter/****************************************************************************
2184989Srafan * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
350276Speter *                                                                          *
450276Speter * Permission is hereby granted, free of charge, to any person obtaining a  *
550276Speter * copy of this software and associated documentation files (the            *
650276Speter * "Software"), to deal in the Software without restriction, including      *
750276Speter * without limitation the rights to use, copy, modify, merge, publish,      *
850276Speter * distribute, distribute with modifications, sublicense, and/or sell       *
950276Speter * copies of the Software, and to permit persons to whom the Software is    *
1050276Speter * furnished to do so, subject to the following conditions:                 *
1150276Speter *                                                                          *
1250276Speter * The above copyright notice and this permission notice shall be included  *
1350276Speter * in all copies or substantial portions of the Software.                   *
1450276Speter *                                                                          *
1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2250276Speter *                                                                          *
2350276Speter * Except as contained in this notice, the name(s) of the above copyright   *
2450276Speter * holders shall not be used in advertising or otherwise to promote the     *
2550276Speter * sale, use or other dealings in this Software without prior written       *
2650276Speter * authorization.                                                           *
2750276Speter ****************************************************************************/
2850276Speter
2950276Speter/****************************************************************************
3050276Speter *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3150276Speter *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32184989Srafan *     and: Thomas E. Dickey                        1996 on                 *
3350276Speter ****************************************************************************/
3450276Speter
3550276Speter/*
3650276Speter *	tic.c --- Main program for terminfo compiler
3750276Speter *			by Eric S. Raymond
3850276Speter *
3950276Speter */
4050276Speter
4150276Speter#include <progs.priv.h>
4266963Speter#include <sys/stat.h>
4350276Speter
4450276Speter#include <dump_entry.h>
4566963Speter#include <transform.h>
4650276Speter
47184989SrafanMODULE_ID("$Id: tic.c,v 1.137 2008/09/13 16:59:24 tom Exp $")
4850276Speter
4950276Speterconst char *_nc_progname = "tic";
5050276Speter
5162449Speterstatic FILE *log_fp;
5262449Speterstatic FILE *tmp_fp;
53166124Srafanstatic bool capdump = FALSE;	/* running as infotocap? */
54166124Srafanstatic bool infodump = FALSE;	/* running as captoinfo? */
5562449Speterstatic bool showsummary = FALSE;
5662449Speterstatic const char *to_remove;
5750276Speter
58166124Srafanstatic void (*save_check_termtype) (TERMTYPE *, bool);
59166124Srafanstatic void check_termtype(TERMTYPE *tt, bool);
6050276Speter
61166124Srafanstatic const char usage_string[] = "\
62166124Srafan[-e names] \
63166124Srafan[-o dir] \
64166124Srafan[-R name] \
65166124Srafan[-v[n]] \
66166124Srafan[-V] \
67166124Srafan[-w[n]] \
68166124Srafan[-\
69166124Srafan1\
70166124Srafana\
71166124SrafanC\
72166124Srafanc\
73166124Srafanf\
74166124SrafanG\
75166124Srafang\
76166124SrafanI\
77166124SrafanL\
78166124SrafanN\
79166124Srafanr\
80166124Srafans\
81166124SrafanT\
82166124Srafant\
83166124SrafanU\
84166124Srafanx\
85166124Srafan] \
86166124Srafansource-file\n";
8750276Speter
88184989Srafan#if NO_LEAKS
8962449Speterstatic void
90184989Srafanfree_namelist(char **src)
9150276Speter{
92184989Srafan    if (src != 0) {
93184989Srafan	int n;
94184989Srafan	for (n = 0; src[n] != 0; ++n)
95184989Srafan	    free(src[n]);
96184989Srafan	free(src);
97184989Srafan    }
98184989Srafan}
99184989Srafan#endif
100184989Srafan
101184989Srafanstatic void
102184989Srafancleanup(char **namelst GCC_UNUSED)
103184989Srafan{
104184989Srafan#if NO_LEAKS
105184989Srafan    free_namelist(namelst);
106184989Srafan#endif
10762449Speter    if (tmp_fp != 0)
10862449Speter	fclose(tmp_fp);
10962449Speter    if (to_remove != 0) {
11050276Speter#if HAVE_REMOVE
11162449Speter	remove(to_remove);
11250276Speter#else
11362449Speter	unlink(to_remove);
11450276Speter#endif
11562449Speter    }
11650276Speter}
11750276Speter
11862449Speterstatic void
11962449Speterfailed(const char *msg)
12050276Speter{
12162449Speter    perror(msg);
122184989Srafan    cleanup((char **) 0);
123166124Srafan    ExitProgram(EXIT_FAILURE);
12450276Speter}
12550276Speter
12662449Speterstatic void
12762449Speterusage(void)
12850276Speter{
12962449Speter    static const char *const tbl[] =
13062449Speter    {
13150276Speter	"Options:",
13250276Speter	"  -1         format translation output one capability per line",
13362449Speter#if NCURSES_XNAMES
13462449Speter	"  -a         retain commented-out capabilities (sets -x also)",
13562449Speter#endif
136166124Srafan	"  -C         translate entries to termcap source form",
13750276Speter	"  -c         check only, validate input without compiling or translating",
138166124Srafan	"  -e<names>  translate/compile only entries named by comma-separated list",
13950276Speter	"  -f         format complex strings for readability",
14050276Speter	"  -G         format %{number} to %'char'",
14150276Speter	"  -g         format %'char' to %{number}",
142166124Srafan	"  -I         translate entries to terminfo source form",
143166124Srafan	"  -L         translate entries to full terminfo source form",
144166124Srafan	"  -N         disable smart defaults for source translation",
14550276Speter	"  -o<dir>    set output directory for compiled entry writes",
146166124Srafan	"  -R<name>   restrict translation to given terminfo/termcap version",
14750276Speter	"  -r         force resolution of all use entries in source translation",
14850276Speter	"  -s         print summary statistics",
149166124Srafan	"  -T         remove size-restrictions on compiled description",
150166124Srafan#if NCURSES_XNAMES
151166124Srafan	"  -t         suppress commented-out capabilities",
152166124Srafan#endif
153166124Srafan	"  -U         suppress post-processing of entries",
154166124Srafan	"  -V         print version",
15550276Speter	"  -v[n]      set verbosity level",
15650276Speter	"  -w[n]      set format width for translation output",
15750276Speter#if NCURSES_XNAMES
15850276Speter	"  -x         treat unknown capabilities as user-defined",
15950276Speter#endif
16050276Speter	"",
16150276Speter	"Parameters:",
16250276Speter	"  <file>     file to translate or compile"
16362449Speter    };
16462449Speter    size_t j;
16550276Speter
16662449Speter    fprintf(stderr, "Usage: %s %s\n", _nc_progname, usage_string);
16766963Speter    for (j = 0; j < SIZEOF(tbl); j++) {
16862449Speter	fputs(tbl[j], stderr);
16962449Speter	putc('\n', stderr);
17062449Speter    }
171166124Srafan    ExitProgram(EXIT_FAILURE);
17250276Speter}
17350276Speter
17450276Speter#define L_BRACE '{'
17550276Speter#define R_BRACE '}'
17650276Speter#define S_QUOTE '\'';
17750276Speter
17862449Speterstatic void
17962449Speterwrite_it(ENTRY * ep)
18050276Speter{
18162449Speter    unsigned n;
18262449Speter    int ch;
18362449Speter    char *s, *d, *t;
18462449Speter    char result[MAX_ENTRY_SIZE];
18550276Speter
18662449Speter    /*
18762449Speter     * Look for strings that contain %{number}, convert them to %'char',
18862449Speter     * which is shorter and runs a little faster.
18962449Speter     */
19062449Speter    for (n = 0; n < STRCOUNT; n++) {
19162449Speter	s = ep->tterm.Strings[n];
19262449Speter	if (VALID_STRING(s)
19362449Speter	    && strchr(s, L_BRACE) != 0) {
19462449Speter	    d = result;
19562449Speter	    t = s;
19662449Speter	    while ((ch = *t++) != 0) {
197184989Srafan		*d++ = (char) ch;
19862449Speter		if (ch == '\\') {
19962449Speter		    *d++ = *t++;
20062449Speter		} else if ((ch == '%')
20166963Speter			   && (*t == L_BRACE)) {
20262449Speter		    char *v = 0;
20362449Speter		    long value = strtol(t + 1, &v, 0);
20462449Speter		    if (v != 0
20562449Speter			&& *v == R_BRACE
20662449Speter			&& value > 0
20762449Speter			&& value != '\\'	/* FIXME */
20862449Speter			&& value < 127
20962449Speter			&& isprint((int) value)) {
21062449Speter			*d++ = S_QUOTE;
211184989Srafan			*d++ = (char) value;
21262449Speter			*d++ = S_QUOTE;
21362449Speter			t = (v + 1);
21462449Speter		    }
21550276Speter		}
21662449Speter	    }
21762449Speter	    *d = 0;
21862449Speter	    if (strlen(result) < strlen(s))
21962449Speter		strcpy(s, result);
22050276Speter	}
22162449Speter    }
22250276Speter
22362449Speter    _nc_set_type(_nc_first_name(ep->tterm.term_names));
22462449Speter    _nc_curr_line = ep->startline;
22562449Speter    _nc_write_entry(&ep->tterm);
22650276Speter}
22750276Speter
22862449Speterstatic bool
22962449Speterimmedhook(ENTRY * ep GCC_UNUSED)
23050276Speter/* write out entries with no use capabilities immediately to save storage */
23150276Speter{
23266963Speter#if !HAVE_BIG_CORE
23350276Speter    /*
23450276Speter     * This is strictly a core-economy kluge.  The really clean way to handle
23550276Speter     * compilation is to slurp the whole file into core and then do all the
23650276Speter     * name-collision checks and entry writes in one swell foop.  But the
23750276Speter     * terminfo master file is large enough that some core-poor systems swap
23850276Speter     * like crazy when you compile it this way...there have been reports of
23950276Speter     * this process taking *three hours*, rather than the twenty seconds or
24050276Speter     * less typical on my development box.
24150276Speter     *
24250276Speter     * So.  This hook *immediately* writes out the referenced entry if it
24350276Speter     * has no use capabilities.  The compiler main loop refrains from
24450276Speter     * adding the entry to the in-core list when this hook fires.  If some
24550276Speter     * other entry later needs to reference an entry that got written
24650276Speter     * immediately, that's OK; the resolution code will fetch it off disk
24750276Speter     * when it can't find it in core.
24850276Speter     *
24950276Speter     * Name collisions will still be detected, just not as cleanly.  The
25050276Speter     * write_entry() code complains before overwriting an entry that
25150276Speter     * postdates the time of tic's first call to write_entry().  Thus
25250276Speter     * it will complain about overwriting entries newly made during the
25350276Speter     * tic run, but not about overwriting ones that predate it.
25450276Speter     *
25550276Speter     * The reason this is a hook, and not in line with the rest of the
25650276Speter     * compiler code, is that the support for termcap fallback cannot assume
25750276Speter     * it has anywhere to spool out these entries!
25850276Speter     *
25950276Speter     * The _nc_set_type() call here requires a compensating one in
26050276Speter     * _nc_parse_entry().
26150276Speter     *
26250276Speter     * If you define HAVE_BIG_CORE, you'll disable this kluge.  This will
26350276Speter     * make tic a bit faster (because the resolution code won't have to do
26450276Speter     * disk I/O nearly as often).
26550276Speter     */
26662449Speter    if (ep->nuses == 0) {
26762449Speter	int oldline = _nc_curr_line;
26850276Speter
26950276Speter	write_it(ep);
27050276Speter	_nc_curr_line = oldline;
27150276Speter	free(ep->tterm.str_table);
27262449Speter	return (TRUE);
27350276Speter    }
27450276Speter#endif /* HAVE_BIG_CORE */
27562449Speter    return (FALSE);
27650276Speter}
27750276Speter
27862449Speterstatic void
27962449Speterput_translate(int c)
28050276Speter/* emit a comment char, translating terminfo names to termcap names */
28150276Speter{
28250276Speter    static bool in_name = FALSE;
28362449Speter    static size_t have, used;
28462449Speter    static char *namebuf, *suffix;
28550276Speter
28662449Speter    if (in_name) {
28762449Speter	if (used + 1 >= have) {
28862449Speter	    have += 132;
28962449Speter	    namebuf = typeRealloc(char, have, namebuf);
29062449Speter	    suffix = typeRealloc(char, have, suffix);
29150276Speter	}
29262449Speter	if (c == '\n' || c == '@') {
29362449Speter	    namebuf[used++] = '\0';
29462449Speter	    (void) putchar('<');
29562449Speter	    (void) fputs(namebuf, stdout);
29650276Speter	    putchar(c);
29762449Speter	    in_name = FALSE;
29862449Speter	} else if (c != '>') {
299184989Srafan	    namebuf[used++] = (char) c;
30062449Speter	} else {		/* ah! candidate name! */
30162449Speter	    char *up;
30262449Speter	    NCURSES_CONST char *tp;
30350276Speter
30462449Speter	    namebuf[used++] = '\0';
30562449Speter	    in_name = FALSE;
30650276Speter
30762449Speter	    suffix[0] = '\0';
30862449Speter	    if ((up = strchr(namebuf, '#')) != 0
30962449Speter		|| (up = strchr(namebuf, '=')) != 0
31062449Speter		|| ((up = strchr(namebuf, '@')) != 0 && up[1] == '>')) {
31162449Speter		(void) strcpy(suffix, up);
31262449Speter		*up = '\0';
31362449Speter	    }
31450276Speter
31562449Speter	    if ((tp = nametrans(namebuf)) != 0) {
31662449Speter		(void) putchar(':');
31762449Speter		(void) fputs(tp, stdout);
31862449Speter		(void) fputs(suffix, stdout);
31962449Speter		(void) putchar(':');
32062449Speter	    } else {
32162449Speter		/* couldn't find a translation, just dump the name */
32262449Speter		(void) putchar('<');
32362449Speter		(void) fputs(namebuf, stdout);
32462449Speter		(void) fputs(suffix, stdout);
32562449Speter		(void) putchar('>');
32662449Speter	    }
32750276Speter	}
32862449Speter    } else {
32962449Speter	used = 0;
33062449Speter	if (c == '<') {
33162449Speter	    in_name = TRUE;
33262449Speter	} else {
33362449Speter	    putchar(c);
33450276Speter	}
33550276Speter    }
33650276Speter}
33750276Speter
33850276Speter/* Returns a string, stripped of leading/trailing whitespace */
33962449Speterstatic char *
34062449Speterstripped(char *src)
34150276Speter{
34297049Speter    while (isspace(UChar(*src)))
34362449Speter	src++;
34462449Speter    if (*src != '\0') {
345166124Srafan	char *dst = strcpy((char *) malloc(strlen(src) + 1), src);
34662449Speter	size_t len = strlen(dst);
34797049Speter	while (--len != 0 && isspace(UChar(dst[len])))
34862449Speter	    dst[len] = '\0';
34962449Speter	return dst;
35062449Speter    }
35162449Speter    return 0;
35250276Speter}
35350276Speter
35466963Speterstatic FILE *
35566963Speteropen_input(const char *filename)
35666963Speter{
35766963Speter    FILE *fp = fopen(filename, "r");
35866963Speter    struct stat sb;
35966963Speter
36066963Speter    if (fp == 0) {
36166963Speter	fprintf(stderr, "%s: Can't open %s\n", _nc_progname, filename);
362166124Srafan	ExitProgram(EXIT_FAILURE);
36366963Speter    }
36466963Speter    if (fstat(fileno(fp), &sb) < 0
36566963Speter	|| (sb.st_mode & S_IFMT) != S_IFREG) {
36666963Speter	fprintf(stderr, "%s: %s is not a file\n", _nc_progname, filename);
367166124Srafan	ExitProgram(EXIT_FAILURE);
36866963Speter    }
36966963Speter    return fp;
37066963Speter}
37166963Speter
37250276Speter/* Parse the "-e" option-value into a list of names */
373174993Srafanstatic char **
37462449Spetermake_namelist(char *src)
37550276Speter{
376174993Srafan    char **dst = 0;
37750276Speter
37862449Speter    char *s, *base;
37962449Speter    unsigned pass, n, nn;
38062449Speter    char buffer[BUFSIZ];
38150276Speter
38262449Speter    if (src == 0) {
38362449Speter	/* EMPTY */ ;
38462449Speter    } else if (strchr(src, '/') != 0) {		/* a filename */
38566963Speter	FILE *fp = open_input(src);
38650276Speter
38762449Speter	for (pass = 1; pass <= 2; pass++) {
38862449Speter	    nn = 0;
38962449Speter	    while (fgets(buffer, sizeof(buffer), fp) != 0) {
39062449Speter		if ((s = stripped(buffer)) != 0) {
39162449Speter		    if (dst != 0)
39262449Speter			dst[nn] = s;
393174993Srafan		    else
394174993Srafan			free(s);
39562449Speter		    nn++;
39650276Speter		}
39762449Speter	    }
39862449Speter	    if (pass == 1) {
399174993Srafan		dst = typeCalloc(char *, nn + 1);
40062449Speter		rewind(fp);
40162449Speter	    }
40262449Speter	}
40362449Speter	fclose(fp);
40462449Speter    } else {			/* literal list of names */
40562449Speter	for (pass = 1; pass <= 2; pass++) {
40662449Speter	    for (n = nn = 0, base = src;; n++) {
40762449Speter		int mark = src[n];
40862449Speter		if (mark == ',' || mark == '\0') {
40962449Speter		    if (pass == 1) {
41062449Speter			nn++;
41162449Speter		    } else {
41262449Speter			src[n] = '\0';
41362449Speter			if ((s = stripped(base)) != 0)
41462449Speter			    dst[nn++] = s;
41562449Speter			base = &src[n + 1];
41662449Speter		    }
41750276Speter		}
41862449Speter		if (mark == '\0')
41962449Speter		    break;
42062449Speter	    }
42162449Speter	    if (pass == 1)
422174993Srafan		dst = typeCalloc(char *, nn + 1);
42350276Speter	}
42462449Speter    }
425174993Srafan    if (showsummary && (dst != 0)) {
42662449Speter	fprintf(log_fp, "Entries that will be compiled:\n");
42762449Speter	for (n = 0; dst[n] != 0; n++)
428166124Srafan	    fprintf(log_fp, "%u:%s\n", n + 1, dst[n]);
42962449Speter    }
43062449Speter    return dst;
43150276Speter}
43250276Speter
43362449Speterstatic bool
434174993Srafanmatches(char **needle, const char *haystack)
43550276Speter/* does entry in needle list match |-separated field in haystack? */
43650276Speter{
43762449Speter    bool code = FALSE;
43862449Speter    size_t n;
43950276Speter
44062449Speter    if (needle != 0) {
44162449Speter	for (n = 0; needle[n] != 0; n++) {
44262449Speter	    if (_nc_name_match(haystack, needle[n], "|")) {
44362449Speter		code = TRUE;
44462449Speter		break;
44562449Speter	    }
44650276Speter	}
44762449Speter    } else
44862449Speter	code = TRUE;
44962449Speter    return (code);
45050276Speter}
45150276Speter
45262449Speterstatic FILE *
45362449Speteropen_tempfile(char *name)
45450276Speter{
45562449Speter    FILE *result = 0;
45662449Speter#if HAVE_MKSTEMP
45762449Speter    int fd = mkstemp(name);
45862449Speter    if (fd >= 0)
45962449Speter	result = fdopen(fd, "w");
46062449Speter#else
46162449Speter    if (tmpnam(name) != 0)
46262449Speter	result = fopen(name, "w");
46362449Speter#endif
46462449Speter    return result;
46562449Speter}
46650276Speter
46762449Speterint
46862449Spetermain(int argc, char *argv[])
46962449Speter{
47062449Speter    char my_tmpname[PATH_MAX];
47162449Speter    int v_opt = -1, debug_level;
47262449Speter    int smart_defaults = TRUE;
47362449Speter    char *termcap;
47462449Speter    ENTRY *qp;
47550276Speter
47662449Speter    int this_opt, last_opt = '?';
47750276Speter
47862449Speter    int outform = F_TERMINFO;	/* output format */
47962449Speter    int sortmode = S_TERMINFO;	/* sort_mode */
48050276Speter
48162449Speter    int width = 60;
48262449Speter    bool formatted = FALSE;	/* reformat complex strings? */
483166124Srafan    bool literal = FALSE;	/* suppress post-processing? */
48462449Speter    int numbers = 0;		/* format "%'char'" to/from "%{number}" */
48562449Speter    bool forceresolve = FALSE;	/* force resolution */
48662449Speter    bool limited = TRUE;
48762449Speter    char *tversion = (char *) NULL;
48862449Speter    const char *source_file = "terminfo";
489174993Srafan    char **namelst = 0;
49062449Speter    char *outdir = (char *) NULL;
49162449Speter    bool check_only = FALSE;
492166124Srafan    bool suppress_untranslatable = FALSE;
49350276Speter
49462449Speter    log_fp = stderr;
49550276Speter
49697049Speter    _nc_progname = _nc_rootname(argv[0]);
49762449Speter
49866963Speter    if ((infodump = (strcmp(_nc_progname, PROG_CAPTOINFO) == 0)) != FALSE) {
49962449Speter	outform = F_TERMINFO;
50062449Speter	sortmode = S_TERMINFO;
50162449Speter    }
50266963Speter    if ((capdump = (strcmp(_nc_progname, PROG_INFOTOCAP) == 0)) != FALSE) {
50362449Speter	outform = F_TERMCAP;
50462449Speter	sortmode = S_TERMCAP;
50562449Speter    }
50650276Speter#if NCURSES_XNAMES
50762449Speter    use_extended_names(FALSE);
50850276Speter#endif
50950276Speter
51062449Speter    /*
51162449Speter     * Processing arguments is a little complicated, since someone made a
51262449Speter     * design decision to allow the numeric values for -w, -v options to
51362449Speter     * be optional.
51462449Speter     */
51562449Speter    while ((this_opt = getopt(argc, argv,
516174993Srafan			      "0123456789CILNR:TUVace:fGgo:rstvwx")) != -1) {
51762449Speter	if (isdigit(this_opt)) {
51862449Speter	    switch (last_opt) {
51962449Speter	    case 'v':
52062449Speter		v_opt = (v_opt * 10) + (this_opt - '0');
52162449Speter		break;
52262449Speter	    case 'w':
52362449Speter		width = (width * 10) + (this_opt - '0');
52462449Speter		break;
52562449Speter	    default:
52662449Speter		if (this_opt != '1')
52762449Speter		    usage();
52862449Speter		last_opt = this_opt;
52962449Speter		width = 0;
53062449Speter	    }
53162449Speter	    continue;
53262449Speter	}
53362449Speter	switch (this_opt) {
53462449Speter	case 'C':
53562449Speter	    capdump = TRUE;
53662449Speter	    outform = F_TERMCAP;
53762449Speter	    sortmode = S_TERMCAP;
53862449Speter	    break;
53962449Speter	case 'I':
54062449Speter	    infodump = TRUE;
54162449Speter	    outform = F_TERMINFO;
54262449Speter	    sortmode = S_TERMINFO;
54362449Speter	    break;
54462449Speter	case 'L':
54562449Speter	    infodump = TRUE;
54662449Speter	    outform = F_VARIABLE;
54762449Speter	    sortmode = S_VARIABLE;
54862449Speter	    break;
54962449Speter	case 'N':
55062449Speter	    smart_defaults = FALSE;
551166124Srafan	    literal = TRUE;
55262449Speter	    break;
55362449Speter	case 'R':
55462449Speter	    tversion = optarg;
55562449Speter	    break;
55662449Speter	case 'T':
55762449Speter	    limited = FALSE;
55862449Speter	    break;
559166124Srafan	case 'U':
560166124Srafan	    literal = TRUE;
561166124Srafan	    break;
56262449Speter	case 'V':
56366963Speter	    puts(curses_version());
564184989Srafan	    cleanup(namelst);
565184989Srafan	    ExitProgram(EXIT_SUCCESS);
56662449Speter	case 'c':
56762449Speter	    check_only = TRUE;
56862449Speter	    break;
56962449Speter	case 'e':
57062449Speter	    namelst = make_namelist(optarg);
57162449Speter	    break;
57262449Speter	case 'f':
57362449Speter	    formatted = TRUE;
57462449Speter	    break;
57562449Speter	case 'G':
57662449Speter	    numbers = 1;
57762449Speter	    break;
57862449Speter	case 'g':
57962449Speter	    numbers = -1;
58062449Speter	    break;
58162449Speter	case 'o':
58262449Speter	    outdir = optarg;
58362449Speter	    break;
58462449Speter	case 'r':
58562449Speter	    forceresolve = TRUE;
58662449Speter	    break;
58762449Speter	case 's':
58862449Speter	    showsummary = TRUE;
58962449Speter	    break;
59062449Speter	case 'v':
59162449Speter	    v_opt = 0;
59262449Speter	    break;
59362449Speter	case 'w':
59462449Speter	    width = 0;
59562449Speter	    break;
59650276Speter#if NCURSES_XNAMES
597166124Srafan	case 't':
598166124Srafan	    _nc_disable_period = FALSE;
599166124Srafan	    suppress_untranslatable = TRUE;
600166124Srafan	    break;
60162449Speter	case 'a':
60262449Speter	    _nc_disable_period = TRUE;
60362449Speter	    /* FALLTHRU */
60462449Speter	case 'x':
60562449Speter	    use_extended_names(TRUE);
60662449Speter	    break;
60750276Speter#endif
60862449Speter	default:
60962449Speter	    usage();
61050276Speter	}
61162449Speter	last_opt = this_opt;
61262449Speter    }
61350276Speter
61462449Speter    debug_level = (v_opt > 0) ? v_opt : (v_opt == 0);
61562449Speter    set_trace_level(debug_level);
61650276Speter
61762449Speter    if (_nc_tracing) {
618166124Srafan	save_check_termtype = _nc_check_termtype2;
619166124Srafan	_nc_check_termtype2 = check_termtype;
62062449Speter    }
62166963Speter#if !HAVE_BIG_CORE
62262449Speter    /*
62362449Speter     * Aaargh! immedhook seriously hoses us!
62462449Speter     *
62562449Speter     * One problem with immedhook is it means we can't do -e.  Problem
62662449Speter     * is that we can't guarantee that for each terminal listed, all the
62762449Speter     * terminals it depends on will have been kept in core for reference
62862449Speter     * resolution -- in fact it's certain the primitive types at the end
62962449Speter     * of reference chains *won't* be in core unless they were explicitly
63062449Speter     * in the select list themselves.
63162449Speter     */
63262449Speter    if (namelst && (!infodump && !capdump)) {
63362449Speter	(void) fprintf(stderr,
63466963Speter		       "Sorry, -e can't be used without -I or -C\n");
635184989Srafan	cleanup(namelst);
636166124Srafan	ExitProgram(EXIT_FAILURE);
63762449Speter    }
63850276Speter#endif /* HAVE_BIG_CORE */
63950276Speter
64062449Speter    if (optind < argc) {
64162449Speter	source_file = argv[optind++];
64250276Speter	if (optind < argc) {
64362449Speter	    fprintf(stderr,
64466963Speter		    "%s: Too many file names.  Usage:\n\t%s %s",
64566963Speter		    _nc_progname,
64666963Speter		    _nc_progname,
64766963Speter		    usage_string);
648166124Srafan	    ExitProgram(EXIT_FAILURE);
64962449Speter	}
65062449Speter    } else {
65162449Speter	if (infodump == TRUE) {
65262449Speter	    /* captoinfo's no-argument case */
65362449Speter	    source_file = "/etc/termcap";
65462449Speter	    if ((termcap = getenv("TERMCAP")) != 0
65562449Speter		&& (namelst = make_namelist(getenv("TERM"))) != 0) {
65662449Speter		if (access(termcap, F_OK) == 0) {
65762449Speter		    /* file exists */
65862449Speter		    source_file = termcap;
65966963Speter		} else if ((tmp_fp = open_tempfile(strcpy(my_tmpname,
66066963Speter							  "/tmp/XXXXXX")))
66166963Speter			   != 0) {
66262449Speter		    source_file = my_tmpname;
66362449Speter		    fprintf(tmp_fp, "%s\n", termcap);
66462449Speter		    fclose(tmp_fp);
66566963Speter		    tmp_fp = open_input(source_file);
66662449Speter		    to_remove = source_file;
66762449Speter		} else {
66862449Speter		    failed("tmpnam");
66950276Speter		}
67062449Speter	    }
67150276Speter	} else {
67262449Speter	    /* tic */
67362449Speter	    fprintf(stderr,
67466963Speter		    "%s: File name needed.  Usage:\n\t%s %s",
67566963Speter		    _nc_progname,
67666963Speter		    _nc_progname,
67766963Speter		    usage_string);
678184989Srafan	    cleanup(namelst);
679166124Srafan	    ExitProgram(EXIT_FAILURE);
68050276Speter	}
68162449Speter    }
68250276Speter
68366963Speter    if (tmp_fp == 0)
68466963Speter	tmp_fp = open_input(source_file);
68550276Speter
68662449Speter    if (infodump)
68762449Speter	dump_init(tversion,
68866963Speter		  smart_defaults
68966963Speter		  ? outform
69066963Speter		  : F_LITERAL,
69166963Speter		  sortmode, width, debug_level, formatted);
69262449Speter    else if (capdump)
69362449Speter	dump_init(tversion,
69466963Speter		  outform,
69566963Speter		  sortmode, width, debug_level, FALSE);
69650276Speter
69762449Speter    /* parse entries out of the source file */
69862449Speter    _nc_set_source(source_file);
69966963Speter#if !HAVE_BIG_CORE
70062449Speter    if (!(check_only || infodump || capdump))
70162449Speter	_nc_set_writedir(outdir);
70250276Speter#endif /* HAVE_BIG_CORE */
70362449Speter    _nc_read_entry_source(tmp_fp, (char *) NULL,
704166124Srafan			  !smart_defaults || literal, FALSE,
705166124Srafan			  ((check_only || infodump || capdump)
706166124Srafan			   ? NULLHOOK
707166124Srafan			   : immedhook));
70850276Speter
70962449Speter    /* do use resolution */
71062449Speter    if (check_only || (!infodump && !capdump) || forceresolve) {
711166124Srafan	if (!_nc_resolve_uses2(TRUE, literal) && !check_only) {
712184989Srafan	    cleanup(namelst);
713166124Srafan	    ExitProgram(EXIT_FAILURE);
71450276Speter	}
71562449Speter    }
71650276Speter
71762449Speter    /* length check */
71862449Speter    if (check_only && (capdump || infodump)) {
71962449Speter	for_entry_list(qp) {
72062449Speter	    if (matches(namelst, qp->tterm.term_names)) {
721166124Srafan		int len = fmt_entry(&qp->tterm, NULL, FALSE, TRUE, infodump, numbers);
72250276Speter
72362449Speter		if (len > (infodump ? MAX_TERMINFO_LENGTH : MAX_TERMCAP_LENGTH))
72462449Speter		    (void) fprintf(stderr,
72566963Speter				   "warning: resolved %s entry is %d bytes long\n",
72666963Speter				   _nc_first_name(qp->tterm.term_names),
72766963Speter				   len);
72850276Speter	    }
72950276Speter	}
73062449Speter    }
73150276Speter
73262449Speter    /* write or dump all entries */
73362449Speter    if (!check_only) {
73462449Speter	if (!infodump && !capdump) {
73562449Speter	    _nc_set_writedir(outdir);
73662449Speter	    for_entry_list(qp) {
73762449Speter		if (matches(namelst, qp->tterm.term_names))
73862449Speter		    write_it(qp);
73950276Speter	    }
74062449Speter	} else {
74162449Speter	    /* this is in case infotocap() generates warnings */
74262449Speter	    _nc_curr_col = _nc_curr_line = -1;
74350276Speter
74462449Speter	    for_entry_list(qp) {
74562449Speter		if (matches(namelst, qp->tterm.term_names)) {
74662449Speter		    int j = qp->cend - qp->cstart;
74762449Speter		    int len = 0;
74850276Speter
74962449Speter		    /* this is in case infotocap() generates warnings */
75062449Speter		    _nc_set_type(_nc_first_name(qp->tterm.term_names));
75150276Speter
75262449Speter		    (void) fseek(tmp_fp, qp->cstart, SEEK_SET);
753166124Srafan		    while (j-- > 0) {
75462449Speter			if (infodump)
75562449Speter			    (void) putchar(fgetc(tmp_fp));
75662449Speter			else
75762449Speter			    put_translate(fgetc(tmp_fp));
75850276Speter		    }
75950276Speter
760166124Srafan		    dump_entry(&qp->tterm, suppress_untranslatable,
761166124Srafan			       limited, numbers, NULL);
762184989Srafan		    for (j = 0; j < (int) qp->nuses; j++)
763166124Srafan			dump_uses(qp->uses[j].name, !capdump);
764166124Srafan		    len = show_entry();
76562449Speter		    if (debug_level != 0 && !limited)
76662449Speter			printf("# length=%d\n", len);
76762449Speter		}
76862449Speter	    }
76976726Speter	    if (!namelst && _nc_tail) {
77062449Speter		int c, oldc = '\0';
77162449Speter		bool in_comment = FALSE;
77262449Speter		bool trailing_comment = FALSE;
77362449Speter
77462449Speter		(void) fseek(tmp_fp, _nc_tail->cend, SEEK_SET);
77562449Speter		while ((c = fgetc(tmp_fp)) != EOF) {
77662449Speter		    if (oldc == '\n') {
77762449Speter			if (c == '#') {
77862449Speter			    trailing_comment = TRUE;
77962449Speter			    in_comment = TRUE;
78062449Speter			} else {
78162449Speter			    in_comment = FALSE;
78250276Speter			}
78350276Speter		    }
78462449Speter		    if (trailing_comment
78562449Speter			&& (in_comment || (oldc == '\n' && c == '\n')))
78662449Speter			putchar(c);
78762449Speter		    oldc = c;
78850276Speter		}
78950276Speter	    }
79050276Speter	}
79162449Speter    }
79250276Speter
79362449Speter    /* Show the directory into which entries were written, and the total
79462449Speter     * number of entries
79562449Speter     */
79662449Speter    if (showsummary
79762449Speter	&& (!(check_only || infodump || capdump))) {
79862449Speter	int total = _nc_tic_written();
79962449Speter	if (total != 0)
80062449Speter	    fprintf(log_fp, "%d entries written to %s\n",
80166963Speter		    total,
80266963Speter		    _nc_tic_dir((char *) 0));
80362449Speter	else
80462449Speter	    fprintf(log_fp, "No entries written\n");
80562449Speter    }
806184989Srafan    cleanup(namelst);
807166124Srafan    ExitProgram(EXIT_SUCCESS);
80850276Speter}
80950276Speter
81050276Speter/*
81150276Speter * This bit of legerdemain turns all the terminfo variable names into
81250276Speter * references to locations in the arrays Booleans, Numbers, and Strings ---
81350276Speter * precisely what's needed (see comp_parse.c).
81450276Speter */
81550276Speter#undef CUR
81650276Speter#define CUR tp->
81750276Speter
81862449Speter/*
819166124Srafan * Check if the alternate character-set capabilities are consistent.
820166124Srafan */
821166124Srafanstatic void
822166124Srafancheck_acs(TERMTYPE *tp)
823166124Srafan{
824166124Srafan    if (VALID_STRING(acs_chars)) {
825166124Srafan	const char *boxes = "lmkjtuvwqxn";
826166124Srafan	char mapped[256];
827166124Srafan	char missing[256];
828166124Srafan	const char *p;
829166124Srafan	char *q;
830166124Srafan
831166124Srafan	memset(mapped, 0, sizeof(mapped));
832166124Srafan	for (p = acs_chars; *p != '\0'; p += 2) {
833166124Srafan	    if (p[1] == '\0') {
834166124Srafan		_nc_warning("acsc has odd number of characters");
835166124Srafan		break;
836166124Srafan	    }
837166124Srafan	    mapped[UChar(p[0])] = p[1];
838166124Srafan	}
839184989Srafan
840166124Srafan	if (mapped[UChar('I')] && !mapped[UChar('i')]) {
841166124Srafan	    _nc_warning("acsc refers to 'I', which is probably an error");
842166124Srafan	}
843184989Srafan
844166124Srafan	for (p = boxes, q = missing; *p != '\0'; ++p) {
845166124Srafan	    if (!mapped[UChar(p[0])]) {
846166124Srafan		*q++ = p[0];
847166124Srafan	    }
848166124Srafan	}
849184989Srafan	*q = '\0';
850184989Srafan
851184989Srafan	assert(strlen(missing) <= strlen(boxes));
852166124Srafan	if (*missing != '\0' && strcmp(missing, boxes)) {
853166124Srafan	    _nc_warning("acsc is missing some line-drawing mapping: %s", missing);
854166124Srafan	}
855166124Srafan    }
856166124Srafan}
857166124Srafan
858166124Srafan/*
859166124Srafan * Check if the color capabilities are consistent
860166124Srafan */
861166124Srafanstatic void
862166124Srafancheck_colors(TERMTYPE *tp)
863166124Srafan{
864166124Srafan    if ((max_colors > 0) != (max_pairs > 0)
865166124Srafan	|| ((max_colors > max_pairs) && (initialize_pair == 0)))
866166124Srafan	_nc_warning("inconsistent values for max_colors (%d) and max_pairs (%d)",
867166124Srafan		    max_colors, max_pairs);
868166124Srafan
869166124Srafan    PAIRED(set_foreground, set_background);
870166124Srafan    PAIRED(set_a_foreground, set_a_background);
871166124Srafan    PAIRED(set_color_pair, initialize_pair);
872166124Srafan
873166124Srafan    if (VALID_STRING(set_foreground)
874166124Srafan	&& VALID_STRING(set_a_foreground)
875166124Srafan	&& !_nc_capcmp(set_foreground, set_a_foreground))
876166124Srafan	_nc_warning("expected setf/setaf to be different");
877166124Srafan
878166124Srafan    if (VALID_STRING(set_background)
879166124Srafan	&& VALID_STRING(set_a_background)
880166124Srafan	&& !_nc_capcmp(set_background, set_a_background))
881166124Srafan	_nc_warning("expected setb/setab to be different");
882166124Srafan
883166124Srafan    /* see: has_colors() */
884166124Srafan    if (VALID_NUMERIC(max_colors) && VALID_NUMERIC(max_pairs)
885166124Srafan	&& (((set_foreground != NULL)
886166124Srafan	     && (set_background != NULL))
887166124Srafan	    || ((set_a_foreground != NULL)
888166124Srafan		&& (set_a_background != NULL))
889166124Srafan	    || set_color_pair)) {
890166124Srafan	if (!VALID_STRING(orig_pair) && !VALID_STRING(orig_colors))
891166124Srafan	    _nc_warning("expected either op/oc string for resetting colors");
892166124Srafan    }
893166124Srafan}
894166124Srafan
895184989Srafanstatic char
896166124Srafankeypad_final(const char *string)
897166124Srafan{
898184989Srafan    char result = '\0';
899166124Srafan
900166124Srafan    if (VALID_STRING(string)
901166124Srafan	&& *string++ == '\033'
902166124Srafan	&& *string++ == 'O'
903166124Srafan	&& strlen(string) == 1) {
904166124Srafan	result = *string;
905166124Srafan    }
906166124Srafan
907166124Srafan    return result;
908166124Srafan}
909166124Srafan
910166124Srafanstatic int
911166124Srafankeypad_index(const char *string)
912166124Srafan{
913166124Srafan    char *test;
914166124Srafan    const char *list = "PQRSwxymtuvlqrsPpn";	/* app-keypad except "Enter" */
915166124Srafan    int ch;
916166124Srafan    int result = -1;
917166124Srafan
918166124Srafan    if ((ch = keypad_final(string)) != '\0') {
919166124Srafan	test = strchr(list, ch);
920166124Srafan	if (test != 0)
921166124Srafan	    result = (test - list);
922166124Srafan    }
923166124Srafan    return result;
924166124Srafan}
925166124Srafan
926184989Srafan#define MAX_KP 5
927166124Srafan/*
928166124Srafan * Do a quick sanity-check for vt100-style keypads to see if the 5-key keypad
929166124Srafan * is mapped inconsistently.
930166124Srafan */
931166124Srafanstatic void
932166124Srafancheck_keypad(TERMTYPE *tp)
933166124Srafan{
934166124Srafan    char show[80];
935166124Srafan
936166124Srafan    if (VALID_STRING(key_a1) &&
937166124Srafan	VALID_STRING(key_a3) &&
938166124Srafan	VALID_STRING(key_b2) &&
939166124Srafan	VALID_STRING(key_c1) &&
940166124Srafan	VALID_STRING(key_c3)) {
941184989Srafan	char final[MAX_KP + 1];
942184989Srafan	int list[MAX_KP];
943166124Srafan	int increase = 0;
944166124Srafan	int j, k, kk;
945166124Srafan	int last;
946166124Srafan	int test;
947166124Srafan
948166124Srafan	final[0] = keypad_final(key_a1);
949166124Srafan	final[1] = keypad_final(key_a3);
950166124Srafan	final[2] = keypad_final(key_b2);
951166124Srafan	final[3] = keypad_final(key_c1);
952166124Srafan	final[4] = keypad_final(key_c3);
953166124Srafan	final[5] = '\0';
954166124Srafan
955166124Srafan	/* special case: legacy coding using 1,2,3,0,. on the bottom */
956184989Srafan	assert(strlen(final) <= MAX_KP);
957166124Srafan	if (!strcmp(final, "qsrpn"))
958166124Srafan	    return;
959166124Srafan
960166124Srafan	list[0] = keypad_index(key_a1);
961166124Srafan	list[1] = keypad_index(key_a3);
962166124Srafan	list[2] = keypad_index(key_b2);
963166124Srafan	list[3] = keypad_index(key_c1);
964166124Srafan	list[4] = keypad_index(key_c3);
965166124Srafan
966166124Srafan	/* check that they're all vt100 keys */
967184989Srafan	for (j = 0; j < MAX_KP; ++j) {
968166124Srafan	    if (list[j] < 0) {
969166124Srafan		return;
970166124Srafan	    }
971166124Srafan	}
972166124Srafan
973166124Srafan	/* check if they're all in increasing order */
974184989Srafan	for (j = 1; j < MAX_KP; ++j) {
975166124Srafan	    if (list[j] > list[j - 1]) {
976166124Srafan		++increase;
977166124Srafan	    }
978166124Srafan	}
979184989Srafan	if (increase != (MAX_KP - 1)) {
980166124Srafan	    show[0] = '\0';
981166124Srafan
982184989Srafan	    for (j = 0, last = -1; j < MAX_KP; ++j) {
983166124Srafan		for (k = 0, kk = -1, test = 100; k < 5; ++k) {
984166124Srafan		    if (list[k] > last &&
985166124Srafan			list[k] < test) {
986166124Srafan			test = list[k];
987166124Srafan			kk = k;
988166124Srafan		    }
989166124Srafan		}
990166124Srafan		last = test;
991184989Srafan		assert(strlen(show) < (MAX_KP * 4));
992166124Srafan		switch (kk) {
993166124Srafan		case 0:
994166124Srafan		    strcat(show, " ka1");
995166124Srafan		    break;
996166124Srafan		case 1:
997166124Srafan		    strcat(show, " ka3");
998166124Srafan		    break;
999166124Srafan		case 2:
1000166124Srafan		    strcat(show, " kb2");
1001166124Srafan		    break;
1002166124Srafan		case 3:
1003166124Srafan		    strcat(show, " kc1");
1004166124Srafan		    break;
1005166124Srafan		case 4:
1006166124Srafan		    strcat(show, " kc3");
1007166124Srafan		    break;
1008166124Srafan		}
1009166124Srafan	    }
1010166124Srafan
1011166124Srafan	    _nc_warning("vt100 keypad order inconsistent: %s", show);
1012166124Srafan	}
1013166124Srafan
1014166124Srafan    } else if (VALID_STRING(key_a1) ||
1015166124Srafan	       VALID_STRING(key_a3) ||
1016166124Srafan	       VALID_STRING(key_b2) ||
1017166124Srafan	       VALID_STRING(key_c1) ||
1018166124Srafan	       VALID_STRING(key_c3)) {
1019166124Srafan	show[0] = '\0';
1020166124Srafan	if (keypad_index(key_a1) >= 0)
1021166124Srafan	    strcat(show, " ka1");
1022166124Srafan	if (keypad_index(key_a3) >= 0)
1023166124Srafan	    strcat(show, " ka3");
1024166124Srafan	if (keypad_index(key_b2) >= 0)
1025166124Srafan	    strcat(show, " kb2");
1026166124Srafan	if (keypad_index(key_c1) >= 0)
1027166124Srafan	    strcat(show, " kc1");
1028166124Srafan	if (keypad_index(key_c3) >= 0)
1029166124Srafan	    strcat(show, " kc3");
1030166124Srafan	if (*show != '\0')
1031166124Srafan	    _nc_warning("vt100 keypad map incomplete:%s", show);
1032166124Srafan    }
1033166124Srafan}
1034166124Srafan
1035166124Srafan/*
103666963Speter * Returns the expected number of parameters for the given capability.
103766963Speter */
103866963Speterstatic int
103976726Speterexpected_params(const char *name)
104066963Speter{
104166963Speter    /* *INDENT-OFF* */
104266963Speter    static const struct {
104366963Speter	const char *name;
104466963Speter	int count;
104566963Speter    } table[] = {
104676726Speter	{ "S0",			1 },	/* 'screen' extension */
104766963Speter	{ "birep",		2 },
104866963Speter	{ "chr",		1 },
104966963Speter	{ "colornm",		1 },
105066963Speter	{ "cpi",		1 },
105176726Speter	{ "csnm",		1 },
105266963Speter	{ "csr",		2 },
105366963Speter	{ "cub",		1 },
105466963Speter	{ "cud",		1 },
105566963Speter	{ "cuf",		1 },
105666963Speter	{ "cup",		2 },
105776726Speter	{ "cuu",		1 },
105866963Speter	{ "cvr",		1 },
105966963Speter	{ "cwin",		5 },
106066963Speter	{ "dch",		1 },
106176726Speter	{ "defc",		3 },
106266963Speter	{ "dial",		1 },
106366963Speter	{ "dispc",		1 },
106466963Speter	{ "dl",			1 },
106566963Speter	{ "ech",		1 },
106666963Speter	{ "getm",		1 },
106766963Speter	{ "hpa",		1 },
106866963Speter	{ "ich",		1 },
106966963Speter	{ "il",			1 },
107066963Speter	{ "indn",		1 },
107166963Speter	{ "initc",		4 },
107266963Speter	{ "initp",		7 },
107366963Speter	{ "lpi",		1 },
107466963Speter	{ "mc5p",		1 },
107566963Speter	{ "mrcup",		2 },
107666963Speter	{ "mvpa",		1 },
107766963Speter	{ "pfkey",		2 },
107866963Speter	{ "pfloc",		2 },
107966963Speter	{ "pfx",		2 },
108066963Speter	{ "pfxl",		3 },
108166963Speter	{ "pln",		2 },
108266963Speter	{ "qdial",		1 },
108376726Speter	{ "rcsd",		1 },
108466963Speter	{ "rep",		2 },
108566963Speter	{ "rin",		1 },
108666963Speter	{ "sclk",		3 },
108766963Speter	{ "scp",		1 },
108866963Speter	{ "scs",		1 },
108976726Speter	{ "scsd",		2 },
109066963Speter	{ "setab",		1 },
109166963Speter	{ "setaf",		1 },
109266963Speter	{ "setb",		1 },
109366963Speter	{ "setcolor",		1 },
109466963Speter	{ "setf",		1 },
109566963Speter	{ "sgr",		9 },
109666963Speter	{ "sgr1",		6 },
109766963Speter	{ "slength",		1 },
109866963Speter	{ "slines",		1 },
109998503Speter	{ "smgbp",		1 },	/* 2 if smgtp is not given */
110098503Speter	{ "smglp",		1 },
110166963Speter	{ "smglr",		2 },
110266963Speter	{ "smgrp",		1 },
110366963Speter	{ "smgtb",		2 },
110476726Speter	{ "smgtp",		1 },
110566963Speter	{ "tsl",		1 },
110666963Speter	{ "u6",			-1 },
110766963Speter	{ "vpa",		1 },
110866963Speter	{ "wind",		4 },
110966963Speter	{ "wingo",		1 },
111066963Speter    };
111166963Speter    /* *INDENT-ON* */
111266963Speter
111366963Speter    unsigned n;
111466963Speter    int result = 0;		/* function-keys, etc., use none */
111566963Speter
111666963Speter    for (n = 0; n < SIZEOF(table); n++) {
111766963Speter	if (!strcmp(name, table[n].name)) {
111866963Speter	    result = table[n].count;
111966963Speter	    break;
112066963Speter	}
112166963Speter    }
112266963Speter
112366963Speter    return result;
112466963Speter}
112566963Speter
112666963Speter/*
112766963Speter * Make a quick sanity check for the parameters which are used in the given
112866963Speter * strings.  If there are no "%p" tokens, then there should be no other "%"
112966963Speter * markers.
113066963Speter */
113166963Speterstatic void
1132166124Srafancheck_params(TERMTYPE *tp, const char *name, char *value)
113366963Speter{
113466963Speter    int expected = expected_params(name);
113566963Speter    int actual = 0;
113666963Speter    int n;
113766963Speter    bool params[10];
113866963Speter    char *s = value;
113966963Speter
1140166124Srafan#ifdef set_top_margin_parm
114198503Speter    if (!strcmp(name, "smgbp")
114298503Speter	&& set_top_margin_parm == 0)
114398503Speter	expected = 2;
1144166124Srafan#endif
114598503Speter
114666963Speter    for (n = 0; n < 10; n++)
114766963Speter	params[n] = FALSE;
114866963Speter
114966963Speter    while (*s != 0) {
115066963Speter	if (*s == '%') {
115166963Speter	    if (*++s == '\0') {
115266963Speter		_nc_warning("expected character after %% in %s", name);
115366963Speter		break;
115466963Speter	    } else if (*s == 'p') {
115566963Speter		if (*++s == '\0' || !isdigit((int) *s)) {
115666963Speter		    _nc_warning("expected digit after %%p in %s", name);
115766963Speter		    return;
115866963Speter		} else {
115966963Speter		    n = (*s - '0');
116066963Speter		    if (n > actual)
116166963Speter			actual = n;
116266963Speter		    params[n] = TRUE;
116366963Speter		}
116466963Speter	    }
116566963Speter	}
116666963Speter	s++;
116766963Speter    }
116866963Speter
116966963Speter    if (params[0]) {
117066963Speter	_nc_warning("%s refers to parameter 0 (%%p0), which is not allowed", name);
117166963Speter    }
117266963Speter    if (value == set_attributes || expected < 0) {
117366963Speter	;
117466963Speter    } else if (expected != actual) {
117566963Speter	_nc_warning("%s uses %d parameters, expected %d", name,
117666963Speter		    actual, expected);
117766963Speter	for (n = 1; n < actual; n++) {
117866963Speter	    if (!params[n])
117966963Speter		_nc_warning("%s omits parameter %d", name, n);
118066963Speter	}
118166963Speter    }
118266963Speter}
118366963Speter
118498503Speterstatic char *
118598503Speterskip_delay(char *s)
118698503Speter{
118798503Speter    while (*s == '/' || isdigit(UChar(*s)))
118898503Speter	++s;
118998503Speter    return s;
119098503Speter}
119198503Speter
119266963Speter/*
1193166124Srafan * Skip a delay altogether, e.g., when comparing a simple string to sgr,
1194166124Srafan * the latter may have a worst-case delay on the end.
1195166124Srafan */
1196166124Srafanstatic char *
1197166124Srafanignore_delays(char *s)
1198166124Srafan{
1199166124Srafan    int delaying = 0;
1200166124Srafan
1201166124Srafan    do {
1202166124Srafan	switch (*s) {
1203166124Srafan	case '$':
1204166124Srafan	    if (delaying == 0)
1205166124Srafan		delaying = 1;
1206166124Srafan	    break;
1207166124Srafan	case '<':
1208166124Srafan	    if (delaying == 1)
1209166124Srafan		delaying = 2;
1210166124Srafan	    break;
1211166124Srafan	case '\0':
1212166124Srafan	    delaying = 0;
1213166124Srafan	    break;
1214166124Srafan	default:
1215166124Srafan	    if (delaying) {
1216166124Srafan		s = skip_delay(s);
1217166124Srafan		if (*s == '>')
1218166124Srafan		    ++s;
1219166124Srafan		delaying = 0;
1220166124Srafan	    }
1221166124Srafan	    break;
1222166124Srafan	}
1223166124Srafan	if (delaying)
1224166124Srafan	    ++s;
1225166124Srafan    } while (delaying);
1226166124Srafan    return s;
1227166124Srafan}
1228166124Srafan
1229166124Srafan/*
123062449Speter * An sgr string may contain several settings other than the one we're
123162449Speter * interested in, essentially sgr0 + rmacs + whatever.  As long as the
123262449Speter * "whatever" is contained in the sgr string, that is close enough for our
123362449Speter * sanity check.
123462449Speter */
123562449Speterstatic bool
123698503Spetersimilar_sgr(int num, char *a, char *b)
123762449Speter{
123898503Speter    static const char *names[] =
123998503Speter    {
124098503Speter	"none"
124198503Speter	,"standout"
124298503Speter	,"underline"
124398503Speter	,"reverse"
124498503Speter	,"blink"
124598503Speter	,"dim"
124698503Speter	,"bold"
124798503Speter	,"invis"
124898503Speter	,"protect"
124998503Speter	,"altcharset"
125098503Speter    };
125198503Speter    char *base_a = a;
125298503Speter    char *base_b = b;
125398503Speter    int delaying = 0;
125498503Speter
125562449Speter    while (*b != 0) {
125662449Speter	while (*a != *b) {
125776726Speter	    if (*a == 0) {
125876726Speter		if (b[0] == '$'
125976726Speter		    && b[1] == '<') {
126076726Speter		    _nc_warning("Did not find delay %s", _nc_visbuf(b));
126176726Speter		} else {
126298503Speter		    _nc_warning("checking sgr(%s) %s\n\tcompare to %s\n\tunmatched %s",
126398503Speter				names[num], _nc_visbuf2(1, base_a),
126498503Speter				_nc_visbuf2(2, base_b),
126598503Speter				_nc_visbuf2(3, b));
126676726Speter		}
126762449Speter		return FALSE;
126898503Speter	    } else if (delaying) {
126998503Speter		a = skip_delay(a);
127098503Speter		b = skip_delay(b);
127198503Speter	    } else {
127298503Speter		a++;
127376726Speter	    }
127462449Speter	}
127598503Speter	switch (*a) {
127698503Speter	case '$':
127798503Speter	    if (delaying == 0)
127898503Speter		delaying = 1;
127998503Speter	    break;
128098503Speter	case '<':
128198503Speter	    if (delaying == 1)
128298503Speter		delaying = 2;
128398503Speter	    break;
128498503Speter	default:
128598503Speter	    delaying = 0;
128698503Speter	    break;
128798503Speter	}
128862449Speter	a++;
128962449Speter	b++;
129062449Speter    }
1291166124Srafan    /* ignore delays on the end of the string */
1292166124Srafan    a = ignore_delays(a);
1293166124Srafan    return ((num != 0) || (*a == 0));
129462449Speter}
129562449Speter
1296166124Srafanstatic char *
1297166124Srafancheck_sgr(TERMTYPE *tp, char *zero, int num, char *cap, const char *name)
129862449Speter{
1299166124Srafan    char *test;
1300166124Srafan
1301166124Srafan    _nc_tparm_err = 0;
1302166124Srafan    test = TPARM_9(set_attributes,
1303166124Srafan		   num == 1,
1304166124Srafan		   num == 2,
1305166124Srafan		   num == 3,
1306166124Srafan		   num == 4,
1307166124Srafan		   num == 5,
1308166124Srafan		   num == 6,
1309166124Srafan		   num == 7,
1310166124Srafan		   num == 8,
1311166124Srafan		   num == 9);
131262449Speter    if (test != 0) {
131362449Speter	if (PRESENT(cap)) {
131498503Speter	    if (!similar_sgr(num, test, cap)) {
131598503Speter		_nc_warning("%s differs from sgr(%d)\n\t%s=%s\n\tsgr(%d)=%s",
131698503Speter			    name, num,
131798503Speter			    name, _nc_visbuf2(1, cap),
131898503Speter			    num, _nc_visbuf2(2, test));
131962449Speter	    }
1320166124Srafan	} else if (_nc_capcmp(test, zero)) {
132162449Speter	    _nc_warning("sgr(%d) present, but not %s", num, name);
132262449Speter	}
132362449Speter    } else if (PRESENT(cap)) {
132462449Speter	_nc_warning("sgr(%d) missing, but %s present", num, name);
132562449Speter    }
1326166124Srafan    if (_nc_tparm_err)
1327166124Srafan	_nc_warning("stack error in sgr(%d) string", num);
1328166124Srafan    return test;
132962449Speter}
133062449Speter
133162449Speter#define CHECK_SGR(num,name) check_sgr(tp, zero, num, name, #name)
133262449Speter
1333166124Srafan#ifdef TRACE
1334166124Srafan/*
1335166124Srafan * If tic is compiled with TRACE, we'll be able to see the output from the
1336166124Srafan * DEBUG() macro.  But since it doesn't use traceon(), it always goes to
1337166124Srafan * the standard error.  Use this function to make it simpler to follow the
1338166124Srafan * resulting debug traces.
1339166124Srafan */
1340166124Srafanstatic void
1341166124Srafanshow_where(unsigned level)
1342166124Srafan{
1343166124Srafan    if (_nc_tracing >= DEBUG_LEVEL(level)) {
1344166124Srafan	char my_name[256];
1345166124Srafan	_nc_get_type(my_name);
1346166124Srafan	fprintf(stderr, "\"%s\", line %d, '%s' ",
1347166124Srafan		_nc_get_source(),
1348166124Srafan		_nc_curr_line, my_name);
1349166124Srafan    }
1350166124Srafan}
1351166124Srafan
1352166124Srafan#else
1353166124Srafan#define show_where(level)	/* nothing */
1354166124Srafan#endif
1355166124Srafan
135650276Speter/* other sanity-checks (things that we don't want in the normal
135750276Speter * logic that reads a terminfo entry)
135850276Speter */
135962449Speterstatic void
1360166124Srafancheck_termtype(TERMTYPE *tp, bool literal)
136150276Speter{
136262449Speter    bool conflict = FALSE;
136362449Speter    unsigned j, k;
136462449Speter    char fkeys[STRCOUNT];
136550276Speter
136662449Speter    /*
136762449Speter     * A terminal entry may contain more than one keycode assigned to
136862449Speter     * a given string (e.g., KEY_END and KEY_LL).  But curses will only
136962449Speter     * return one (the last one assigned).
137062449Speter     */
1371166124Srafan    if (!(_nc_syntax == SYN_TERMCAP && capdump)) {
1372166124Srafan	memset(fkeys, 0, sizeof(fkeys));
1373166124Srafan	for (j = 0; _nc_tinfo_fkeys[j].code; j++) {
1374166124Srafan	    char *a = tp->Strings[_nc_tinfo_fkeys[j].offset];
1375166124Srafan	    bool first = TRUE;
1376166124Srafan	    if (!VALID_STRING(a))
137750276Speter		continue;
1378166124Srafan	    for (k = j + 1; _nc_tinfo_fkeys[k].code; k++) {
1379166124Srafan		char *b = tp->Strings[_nc_tinfo_fkeys[k].offset];
1380166124Srafan		if (!VALID_STRING(b)
1381166124Srafan		    || fkeys[k])
1382166124Srafan		    continue;
1383166124Srafan		if (!_nc_capcmp(a, b)) {
1384166124Srafan		    fkeys[j] = 1;
1385166124Srafan		    fkeys[k] = 1;
1386166124Srafan		    if (first) {
1387166124Srafan			if (!conflict) {
1388166124Srafan			    _nc_warning("Conflicting key definitions (using the last)");
1389166124Srafan			    conflict = TRUE;
1390166124Srafan			}
1391166124Srafan			fprintf(stderr, "... %s is the same as %s",
1392166124Srafan				keyname((int) _nc_tinfo_fkeys[j].code),
1393166124Srafan				keyname((int) _nc_tinfo_fkeys[k].code));
1394166124Srafan			first = FALSE;
1395166124Srafan		    } else {
1396166124Srafan			fprintf(stderr, ", %s",
1397166124Srafan				keyname((int) _nc_tinfo_fkeys[k].code));
139850276Speter		    }
139950276Speter		}
140050276Speter	    }
1401166124Srafan	    if (!first)
1402166124Srafan		fprintf(stderr, "\n");
140350276Speter	}
140462449Speter    }
140550276Speter
140666963Speter    for (j = 0; j < NUM_STRINGS(tp); j++) {
140766963Speter	char *a = tp->Strings[j];
140866963Speter	if (VALID_STRING(a))
140966963Speter	    check_params(tp, ExtStrname(tp, j, strnames), a);
141066963Speter    }
141166963Speter
1412166124Srafan    check_acs(tp);
1413166124Srafan    check_colors(tp);
1414166124Srafan    check_keypad(tp);
141550276Speter
141662449Speter    /*
141762449Speter     * These may be mismatched because the terminal description relies on
141862449Speter     * restoring the cursor visibility by resetting it.
141962449Speter     */
142062449Speter    ANDMISSING(cursor_invisible, cursor_normal);
142162449Speter    ANDMISSING(cursor_visible, cursor_normal);
142250276Speter
142362449Speter    if (PRESENT(cursor_visible) && PRESENT(cursor_normal)
1424166124Srafan	&& !_nc_capcmp(cursor_visible, cursor_normal))
142562449Speter	_nc_warning("cursor_visible is same as cursor_normal");
142650276Speter
142762449Speter    /*
142862449Speter     * From XSI & O'Reilly, we gather that sc/rc are required if csr is
142962449Speter     * given, because the cursor position after the scrolling operation is
143062449Speter     * performed is undefined.
143162449Speter     */
143262449Speter    ANDMISSING(change_scroll_region, save_cursor);
143362449Speter    ANDMISSING(change_scroll_region, restore_cursor);
143450276Speter
143562449Speter    if (PRESENT(set_attributes)) {
1436166124Srafan	char *zero = 0;
143762449Speter
1438166124Srafan	_nc_tparm_err = 0;
1439166124Srafan	if (PRESENT(exit_attribute_mode)) {
1440166124Srafan	    zero = strdup(CHECK_SGR(0, exit_attribute_mode));
1441166124Srafan	} else {
1442166124Srafan	    zero = strdup(TPARM_9(set_attributes, 0, 0, 0, 0, 0, 0, 0, 0, 0));
1443166124Srafan	}
1444166124Srafan	if (_nc_tparm_err)
1445166124Srafan	    _nc_warning("stack error in sgr(0) string");
1446166124Srafan
1447166124Srafan	if (zero != 0) {
1448166124Srafan	    CHECK_SGR(1, enter_standout_mode);
1449166124Srafan	    CHECK_SGR(2, enter_underline_mode);
1450166124Srafan	    CHECK_SGR(3, enter_reverse_mode);
1451166124Srafan	    CHECK_SGR(4, enter_blink_mode);
1452166124Srafan	    CHECK_SGR(5, enter_dim_mode);
1453166124Srafan	    CHECK_SGR(6, enter_bold_mode);
1454166124Srafan	    CHECK_SGR(7, enter_secure_mode);
1455166124Srafan	    CHECK_SGR(8, enter_protected_mode);
1456166124Srafan	    CHECK_SGR(9, enter_alt_charset_mode);
1457166124Srafan	    free(zero);
1458166124Srafan	} else {
1459166124Srafan	    _nc_warning("sgr(0) did not return a value");
1460166124Srafan	}
1461166124Srafan    } else if (PRESENT(exit_attribute_mode) &&
1462166124Srafan	       set_attributes != CANCELLED_STRING) {
1463166124Srafan	if (_nc_syntax == SYN_TERMINFO)
1464166124Srafan	    _nc_warning("missing sgr string");
146562449Speter    }
146662449Speter
1467166124Srafan    if (PRESENT(exit_attribute_mode)) {
1468166124Srafan	char *check_sgr0 = _nc_trim_sgr0(tp);
1469166124Srafan
1470166124Srafan	if (check_sgr0 == 0 || *check_sgr0 == '\0') {
1471166124Srafan	    _nc_warning("trimmed sgr0 is empty");
1472166124Srafan	} else {
1473166124Srafan	    show_where(2);
1474166124Srafan	    if (check_sgr0 != exit_attribute_mode) {
1475166124Srafan		DEBUG(2,
1476166124Srafan		      ("will trim sgr0\n\toriginal sgr0=%s\n\ttrimmed  sgr0=%s",
1477166124Srafan		       _nc_visbuf2(1, exit_attribute_mode),
1478166124Srafan		       _nc_visbuf2(2, check_sgr0)));
1479166124Srafan		free(check_sgr0);
1480166124Srafan	    } else {
1481166124Srafan		DEBUG(2,
1482166124Srafan		      ("will not trim sgr0\n\toriginal sgr0=%s",
1483166124Srafan		       _nc_visbuf(exit_attribute_mode)));
1484166124Srafan	    }
1485166124Srafan	}
1486166124Srafan    }
1487166124Srafan#ifdef TRACE
1488166124Srafan    show_where(2);
1489166124Srafan    if (!auto_right_margin) {
1490166124Srafan	DEBUG(2,
1491166124Srafan	      ("can write to lower-right directly"));
1492166124Srafan    } else if (PRESENT(enter_am_mode) && PRESENT(exit_am_mode)) {
1493166124Srafan	DEBUG(2,
1494166124Srafan	      ("can write to lower-right by suppressing automargin"));
1495166124Srafan    } else if ((PRESENT(enter_insert_mode) && PRESENT(exit_insert_mode))
1496166124Srafan	       || PRESENT(insert_character) || PRESENT(parm_ich)) {
1497166124Srafan	DEBUG(2,
1498166124Srafan	      ("can write to lower-right by using inserts"));
1499166124Srafan    } else {
1500166124Srafan	DEBUG(2,
1501166124Srafan	      ("cannot write to lower-right"));
1502166124Srafan    }
1503166124Srafan#endif
1504166124Srafan
150562449Speter    /*
150662449Speter     * Some standard applications (e.g., vi) and some non-curses
1507166124Srafan     * applications (e.g., jove) get confused if we have both ich1 and
150862449Speter     * smir/rmir.  Let's be nice and warn about that, too, even though
150962449Speter     * ncurses handles it.
151062449Speter     */
151162449Speter    if ((PRESENT(enter_insert_mode) || PRESENT(exit_insert_mode))
1512166124Srafan	&& PRESENT(parm_ich)) {
1513166124Srafan	_nc_warning("non-curses applications may be confused by ich1 with smir/rmir");
151462449Speter    }
151562449Speter
151662449Speter    /*
151762449Speter     * Finally, do the non-verbose checks
151862449Speter     */
151962449Speter    if (save_check_termtype != 0)
1520166124Srafan	save_check_termtype(tp, literal);
152150276Speter}
1522