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