tic.c revision 98503
150276Speter/**************************************************************************** 298503Speter * Copyright (c) 1998-2001,2002 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> * 3298503Speter * 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> 4550276Speter#include <term_entry.h> 4666963Speter#include <transform.h> 4750276Speter 4898503SpeterMODULE_ID("$Id: tic.c,v 1.96 2002/06/01 20:42:53 tom Exp $") 4950276Speter 5050276Speterconst char *_nc_progname = "tic"; 5150276Speter 5262449Speterstatic FILE *log_fp; 5362449Speterstatic FILE *tmp_fp; 5462449Speterstatic bool showsummary = FALSE; 5562449Speterstatic const char *to_remove; 5676726Speterstatic int tparm_errs; 5750276Speter 5862449Speterstatic void (*save_check_termtype) (TERMTYPE *); 5962449Speterstatic void check_termtype(TERMTYPE * tt); 6050276Speter 6166963Speterstatic const char usage_string[] = "[-V] [-v[n]] [-e names] [-CILNRTcfrswx1] source-file\n"; 6250276Speter 6362449Speterstatic void 6462449Spetercleanup(void) 6550276Speter{ 6662449Speter if (tmp_fp != 0) 6762449Speter fclose(tmp_fp); 6862449Speter if (to_remove != 0) { 6950276Speter#if HAVE_REMOVE 7062449Speter remove(to_remove); 7150276Speter#else 7262449Speter unlink(to_remove); 7350276Speter#endif 7462449Speter } 7550276Speter} 7650276Speter 7762449Speterstatic void 7862449Speterfailed(const char *msg) 7950276Speter{ 8062449Speter perror(msg); 8162449Speter cleanup(); 8262449Speter exit(EXIT_FAILURE); 8350276Speter} 8450276Speter 8562449Speterstatic void 8662449Speterusage(void) 8750276Speter{ 8862449Speter static const char *const tbl[] = 8962449Speter { 9050276Speter "Options:", 9150276Speter " -1 format translation output one capability per line", 9250276Speter " -C translate entries to termcap source form", 9350276Speter " -I translate entries to terminfo source form", 9450276Speter " -L translate entries to full terminfo source form", 9550276Speter " -N disable smart defaults for source translation", 9650276Speter " -R restrict translation to given terminfo/termcap version", 9750276Speter " -T remove size-restrictions on compiled description", 9866963Speter " -V print version", 9962449Speter#if NCURSES_XNAMES 10062449Speter " -a retain commented-out capabilities (sets -x also)", 10162449Speter#endif 10250276Speter " -c check only, validate input without compiling or translating", 10350276Speter " -f format complex strings for readability", 10450276Speter " -G format %{number} to %'char'", 10550276Speter " -g format %'char' to %{number}", 10650276Speter " -e<names> translate/compile only entries named by comma-separated list", 10750276Speter " -o<dir> set output directory for compiled entry writes", 10850276Speter " -r force resolution of all use entries in source translation", 10950276Speter " -s print summary statistics", 11050276Speter " -v[n] set verbosity level", 11150276Speter " -w[n] set format width for translation output", 11250276Speter#if NCURSES_XNAMES 11350276Speter " -x treat unknown capabilities as user-defined", 11450276Speter#endif 11550276Speter "", 11650276Speter "Parameters:", 11750276Speter " <file> file to translate or compile" 11862449Speter }; 11962449Speter size_t j; 12050276Speter 12162449Speter fprintf(stderr, "Usage: %s %s\n", _nc_progname, usage_string); 12266963Speter for (j = 0; j < SIZEOF(tbl); j++) { 12362449Speter fputs(tbl[j], stderr); 12462449Speter putc('\n', stderr); 12562449Speter } 12662449Speter exit(EXIT_FAILURE); 12750276Speter} 12850276Speter 12950276Speter#define L_BRACE '{' 13050276Speter#define R_BRACE '}' 13150276Speter#define S_QUOTE '\''; 13250276Speter 13362449Speterstatic void 13462449Speterwrite_it(ENTRY * ep) 13550276Speter{ 13662449Speter unsigned n; 13762449Speter int ch; 13862449Speter char *s, *d, *t; 13962449Speter char result[MAX_ENTRY_SIZE]; 14050276Speter 14162449Speter /* 14262449Speter * Look for strings that contain %{number}, convert them to %'char', 14362449Speter * which is shorter and runs a little faster. 14462449Speter */ 14562449Speter for (n = 0; n < STRCOUNT; n++) { 14662449Speter s = ep->tterm.Strings[n]; 14762449Speter if (VALID_STRING(s) 14862449Speter && strchr(s, L_BRACE) != 0) { 14962449Speter d = result; 15062449Speter t = s; 15162449Speter while ((ch = *t++) != 0) { 15262449Speter *d++ = ch; 15362449Speter if (ch == '\\') { 15462449Speter *d++ = *t++; 15562449Speter } else if ((ch == '%') 15666963Speter && (*t == L_BRACE)) { 15762449Speter char *v = 0; 15862449Speter long value = strtol(t + 1, &v, 0); 15962449Speter if (v != 0 16062449Speter && *v == R_BRACE 16162449Speter && value > 0 16262449Speter && value != '\\' /* FIXME */ 16362449Speter && value < 127 16462449Speter && isprint((int) value)) { 16562449Speter *d++ = S_QUOTE; 16662449Speter *d++ = (int) value; 16762449Speter *d++ = S_QUOTE; 16862449Speter t = (v + 1); 16962449Speter } 17050276Speter } 17162449Speter } 17262449Speter *d = 0; 17362449Speter if (strlen(result) < strlen(s)) 17462449Speter strcpy(s, result); 17550276Speter } 17662449Speter } 17750276Speter 17862449Speter _nc_set_type(_nc_first_name(ep->tterm.term_names)); 17962449Speter _nc_curr_line = ep->startline; 18062449Speter _nc_write_entry(&ep->tterm); 18150276Speter} 18250276Speter 18362449Speterstatic bool 18462449Speterimmedhook(ENTRY * ep GCC_UNUSED) 18550276Speter/* write out entries with no use capabilities immediately to save storage */ 18650276Speter{ 18766963Speter#if !HAVE_BIG_CORE 18850276Speter /* 18950276Speter * This is strictly a core-economy kluge. The really clean way to handle 19050276Speter * compilation is to slurp the whole file into core and then do all the 19150276Speter * name-collision checks and entry writes in one swell foop. But the 19250276Speter * terminfo master file is large enough that some core-poor systems swap 19350276Speter * like crazy when you compile it this way...there have been reports of 19450276Speter * this process taking *three hours*, rather than the twenty seconds or 19550276Speter * less typical on my development box. 19650276Speter * 19750276Speter * So. This hook *immediately* writes out the referenced entry if it 19850276Speter * has no use capabilities. The compiler main loop refrains from 19950276Speter * adding the entry to the in-core list when this hook fires. If some 20050276Speter * other entry later needs to reference an entry that got written 20150276Speter * immediately, that's OK; the resolution code will fetch it off disk 20250276Speter * when it can't find it in core. 20350276Speter * 20450276Speter * Name collisions will still be detected, just not as cleanly. The 20550276Speter * write_entry() code complains before overwriting an entry that 20650276Speter * postdates the time of tic's first call to write_entry(). Thus 20750276Speter * it will complain about overwriting entries newly made during the 20850276Speter * tic run, but not about overwriting ones that predate it. 20950276Speter * 21050276Speter * The reason this is a hook, and not in line with the rest of the 21150276Speter * compiler code, is that the support for termcap fallback cannot assume 21250276Speter * it has anywhere to spool out these entries! 21350276Speter * 21450276Speter * The _nc_set_type() call here requires a compensating one in 21550276Speter * _nc_parse_entry(). 21650276Speter * 21750276Speter * If you define HAVE_BIG_CORE, you'll disable this kluge. This will 21850276Speter * make tic a bit faster (because the resolution code won't have to do 21950276Speter * disk I/O nearly as often). 22050276Speter */ 22162449Speter if (ep->nuses == 0) { 22262449Speter int oldline = _nc_curr_line; 22350276Speter 22450276Speter write_it(ep); 22550276Speter _nc_curr_line = oldline; 22650276Speter free(ep->tterm.str_table); 22762449Speter return (TRUE); 22850276Speter } 22950276Speter#endif /* HAVE_BIG_CORE */ 23062449Speter return (FALSE); 23150276Speter} 23250276Speter 23362449Speterstatic void 23462449Speterput_translate(int c) 23550276Speter/* emit a comment char, translating terminfo names to termcap names */ 23650276Speter{ 23750276Speter static bool in_name = FALSE; 23862449Speter static size_t have, used; 23962449Speter static char *namebuf, *suffix; 24050276Speter 24162449Speter if (in_name) { 24262449Speter if (used + 1 >= have) { 24362449Speter have += 132; 24462449Speter namebuf = typeRealloc(char, have, namebuf); 24562449Speter suffix = typeRealloc(char, have, suffix); 24650276Speter } 24762449Speter if (c == '\n' || c == '@') { 24862449Speter namebuf[used++] = '\0'; 24962449Speter (void) putchar('<'); 25062449Speter (void) fputs(namebuf, stdout); 25150276Speter putchar(c); 25262449Speter in_name = FALSE; 25362449Speter } else if (c != '>') { 25462449Speter namebuf[used++] = c; 25562449Speter } else { /* ah! candidate name! */ 25662449Speter char *up; 25762449Speter NCURSES_CONST char *tp; 25850276Speter 25962449Speter namebuf[used++] = '\0'; 26062449Speter in_name = FALSE; 26150276Speter 26262449Speter suffix[0] = '\0'; 26362449Speter if ((up = strchr(namebuf, '#')) != 0 26462449Speter || (up = strchr(namebuf, '=')) != 0 26562449Speter || ((up = strchr(namebuf, '@')) != 0 && up[1] == '>')) { 26662449Speter (void) strcpy(suffix, up); 26762449Speter *up = '\0'; 26862449Speter } 26950276Speter 27062449Speter if ((tp = nametrans(namebuf)) != 0) { 27162449Speter (void) putchar(':'); 27262449Speter (void) fputs(tp, stdout); 27362449Speter (void) fputs(suffix, stdout); 27462449Speter (void) putchar(':'); 27562449Speter } else { 27662449Speter /* couldn't find a translation, just dump the name */ 27762449Speter (void) putchar('<'); 27862449Speter (void) fputs(namebuf, stdout); 27962449Speter (void) fputs(suffix, stdout); 28062449Speter (void) putchar('>'); 28162449Speter } 28250276Speter } 28362449Speter } else { 28462449Speter used = 0; 28562449Speter if (c == '<') { 28662449Speter in_name = TRUE; 28762449Speter } else { 28862449Speter putchar(c); 28950276Speter } 29050276Speter } 29150276Speter} 29250276Speter 29350276Speter/* Returns a string, stripped of leading/trailing whitespace */ 29462449Speterstatic char * 29562449Speterstripped(char *src) 29650276Speter{ 29797049Speter while (isspace(UChar(*src))) 29862449Speter src++; 29962449Speter if (*src != '\0') { 30062449Speter char *dst = strcpy(malloc(strlen(src) + 1), src); 30162449Speter size_t len = strlen(dst); 30297049Speter while (--len != 0 && isspace(UChar(dst[len]))) 30362449Speter dst[len] = '\0'; 30462449Speter return dst; 30562449Speter } 30662449Speter return 0; 30750276Speter} 30850276Speter 30966963Speterstatic FILE * 31066963Speteropen_input(const char *filename) 31166963Speter{ 31266963Speter FILE *fp = fopen(filename, "r"); 31366963Speter struct stat sb; 31466963Speter 31566963Speter if (fp == 0) { 31666963Speter fprintf(stderr, "%s: Can't open %s\n", _nc_progname, filename); 31766963Speter exit(EXIT_FAILURE); 31866963Speter } 31966963Speter if (fstat(fileno(fp), &sb) < 0 32066963Speter || (sb.st_mode & S_IFMT) != S_IFREG) { 32166963Speter fprintf(stderr, "%s: %s is not a file\n", _nc_progname, filename); 32266963Speter exit(EXIT_FAILURE); 32366963Speter } 32466963Speter return fp; 32566963Speter} 32666963Speter 32750276Speter/* Parse the "-e" option-value into a list of names */ 32862449Speterstatic const char ** 32962449Spetermake_namelist(char *src) 33050276Speter{ 33162449Speter const char **dst = 0; 33250276Speter 33362449Speter char *s, *base; 33462449Speter unsigned pass, n, nn; 33562449Speter char buffer[BUFSIZ]; 33650276Speter 33762449Speter if (src == 0) { 33862449Speter /* EMPTY */ ; 33962449Speter } else if (strchr(src, '/') != 0) { /* a filename */ 34066963Speter FILE *fp = open_input(src); 34150276Speter 34262449Speter for (pass = 1; pass <= 2; pass++) { 34362449Speter nn = 0; 34462449Speter while (fgets(buffer, sizeof(buffer), fp) != 0) { 34562449Speter if ((s = stripped(buffer)) != 0) { 34662449Speter if (dst != 0) 34762449Speter dst[nn] = s; 34862449Speter nn++; 34950276Speter } 35062449Speter } 35162449Speter if (pass == 1) { 35262449Speter dst = typeCalloc(const char *, nn + 1); 35362449Speter rewind(fp); 35462449Speter } 35562449Speter } 35662449Speter fclose(fp); 35762449Speter } else { /* literal list of names */ 35862449Speter for (pass = 1; pass <= 2; pass++) { 35962449Speter for (n = nn = 0, base = src;; n++) { 36062449Speter int mark = src[n]; 36162449Speter if (mark == ',' || mark == '\0') { 36262449Speter if (pass == 1) { 36362449Speter nn++; 36462449Speter } else { 36562449Speter src[n] = '\0'; 36662449Speter if ((s = stripped(base)) != 0) 36762449Speter dst[nn++] = s; 36862449Speter base = &src[n + 1]; 36962449Speter } 37050276Speter } 37162449Speter if (mark == '\0') 37262449Speter break; 37362449Speter } 37462449Speter if (pass == 1) 37562449Speter dst = typeCalloc(const char *, nn + 1); 37650276Speter } 37762449Speter } 37862449Speter if (showsummary) { 37962449Speter fprintf(log_fp, "Entries that will be compiled:\n"); 38062449Speter for (n = 0; dst[n] != 0; n++) 38162449Speter fprintf(log_fp, "%d:%s\n", n + 1, dst[n]); 38262449Speter } 38362449Speter return dst; 38450276Speter} 38550276Speter 38662449Speterstatic bool 38762449Spetermatches(const char **needle, const char *haystack) 38850276Speter/* does entry in needle list match |-separated field in haystack? */ 38950276Speter{ 39062449Speter bool code = FALSE; 39162449Speter size_t n; 39250276Speter 39362449Speter if (needle != 0) { 39462449Speter for (n = 0; needle[n] != 0; n++) { 39562449Speter if (_nc_name_match(haystack, needle[n], "|")) { 39662449Speter code = TRUE; 39762449Speter break; 39862449Speter } 39950276Speter } 40062449Speter } else 40162449Speter code = TRUE; 40262449Speter return (code); 40350276Speter} 40450276Speter 40562449Speterstatic FILE * 40662449Speteropen_tempfile(char *name) 40750276Speter{ 40862449Speter FILE *result = 0; 40962449Speter#if HAVE_MKSTEMP 41062449Speter int fd = mkstemp(name); 41162449Speter if (fd >= 0) 41262449Speter result = fdopen(fd, "w"); 41362449Speter#else 41462449Speter if (tmpnam(name) != 0) 41562449Speter result = fopen(name, "w"); 41662449Speter#endif 41762449Speter return result; 41862449Speter} 41950276Speter 42062449Speterint 42162449Spetermain(int argc, char *argv[]) 42262449Speter{ 42362449Speter char my_tmpname[PATH_MAX]; 42462449Speter int v_opt = -1, debug_level; 42562449Speter int smart_defaults = TRUE; 42662449Speter char *termcap; 42762449Speter ENTRY *qp; 42850276Speter 42962449Speter int this_opt, last_opt = '?'; 43050276Speter 43162449Speter int outform = F_TERMINFO; /* output format */ 43262449Speter int sortmode = S_TERMINFO; /* sort_mode */ 43350276Speter 43462449Speter int width = 60; 43562449Speter bool formatted = FALSE; /* reformat complex strings? */ 43662449Speter int numbers = 0; /* format "%'char'" to/from "%{number}" */ 43762449Speter bool infodump = FALSE; /* running as captoinfo? */ 43862449Speter bool capdump = FALSE; /* running as infotocap? */ 43962449Speter bool forceresolve = FALSE; /* force resolution */ 44062449Speter bool limited = TRUE; 44162449Speter char *tversion = (char *) NULL; 44262449Speter const char *source_file = "terminfo"; 44362449Speter const char **namelst = 0; 44462449Speter char *outdir = (char *) NULL; 44562449Speter bool check_only = FALSE; 44650276Speter 44762449Speter log_fp = stderr; 44850276Speter 44997049Speter _nc_progname = _nc_rootname(argv[0]); 45062449Speter 45166963Speter if ((infodump = (strcmp(_nc_progname, PROG_CAPTOINFO) == 0)) != FALSE) { 45262449Speter outform = F_TERMINFO; 45362449Speter sortmode = S_TERMINFO; 45462449Speter } 45566963Speter if ((capdump = (strcmp(_nc_progname, PROG_INFOTOCAP) == 0)) != FALSE) { 45662449Speter outform = F_TERMCAP; 45762449Speter sortmode = S_TERMCAP; 45862449Speter } 45950276Speter#if NCURSES_XNAMES 46062449Speter use_extended_names(FALSE); 46150276Speter#endif 46250276Speter 46362449Speter /* 46462449Speter * Processing arguments is a little complicated, since someone made a 46562449Speter * design decision to allow the numeric values for -w, -v options to 46662449Speter * be optional. 46762449Speter */ 46862449Speter while ((this_opt = getopt(argc, argv, 46966963Speter "0123456789CILNR:TVace:fGgo:rsvwx")) != EOF) { 47062449Speter if (isdigit(this_opt)) { 47162449Speter switch (last_opt) { 47262449Speter case 'v': 47362449Speter v_opt = (v_opt * 10) + (this_opt - '0'); 47462449Speter break; 47562449Speter case 'w': 47662449Speter width = (width * 10) + (this_opt - '0'); 47762449Speter break; 47862449Speter default: 47962449Speter if (this_opt != '1') 48062449Speter usage(); 48162449Speter last_opt = this_opt; 48262449Speter width = 0; 48362449Speter } 48462449Speter continue; 48562449Speter } 48662449Speter switch (this_opt) { 48762449Speter case 'C': 48862449Speter capdump = TRUE; 48962449Speter outform = F_TERMCAP; 49062449Speter sortmode = S_TERMCAP; 49162449Speter break; 49262449Speter case 'I': 49362449Speter infodump = TRUE; 49462449Speter outform = F_TERMINFO; 49562449Speter sortmode = S_TERMINFO; 49662449Speter break; 49762449Speter case 'L': 49862449Speter infodump = TRUE; 49962449Speter outform = F_VARIABLE; 50062449Speter sortmode = S_VARIABLE; 50162449Speter break; 50262449Speter case 'N': 50362449Speter smart_defaults = FALSE; 50462449Speter break; 50562449Speter case 'R': 50662449Speter tversion = optarg; 50762449Speter break; 50862449Speter case 'T': 50962449Speter limited = FALSE; 51062449Speter break; 51162449Speter case 'V': 51266963Speter puts(curses_version()); 51362449Speter return EXIT_SUCCESS; 51462449Speter case 'c': 51562449Speter check_only = TRUE; 51662449Speter break; 51762449Speter case 'e': 51862449Speter namelst = make_namelist(optarg); 51962449Speter break; 52062449Speter case 'f': 52162449Speter formatted = TRUE; 52262449Speter break; 52362449Speter case 'G': 52462449Speter numbers = 1; 52562449Speter break; 52662449Speter case 'g': 52762449Speter numbers = -1; 52862449Speter break; 52962449Speter case 'o': 53062449Speter outdir = optarg; 53162449Speter break; 53262449Speter case 'r': 53362449Speter forceresolve = TRUE; 53462449Speter break; 53562449Speter case 's': 53662449Speter showsummary = TRUE; 53762449Speter break; 53862449Speter case 'v': 53962449Speter v_opt = 0; 54062449Speter break; 54162449Speter case 'w': 54262449Speter width = 0; 54362449Speter break; 54450276Speter#if NCURSES_XNAMES 54562449Speter case 'a': 54662449Speter _nc_disable_period = TRUE; 54762449Speter /* FALLTHRU */ 54862449Speter case 'x': 54962449Speter use_extended_names(TRUE); 55062449Speter break; 55150276Speter#endif 55262449Speter default: 55362449Speter usage(); 55450276Speter } 55562449Speter last_opt = this_opt; 55662449Speter } 55750276Speter 55862449Speter debug_level = (v_opt > 0) ? v_opt : (v_opt == 0); 55962449Speter set_trace_level(debug_level); 56050276Speter 56162449Speter if (_nc_tracing) { 56262449Speter save_check_termtype = _nc_check_termtype; 56362449Speter _nc_check_termtype = check_termtype; 56462449Speter } 56566963Speter#if !HAVE_BIG_CORE 56662449Speter /* 56762449Speter * Aaargh! immedhook seriously hoses us! 56862449Speter * 56962449Speter * One problem with immedhook is it means we can't do -e. Problem 57062449Speter * is that we can't guarantee that for each terminal listed, all the 57162449Speter * terminals it depends on will have been kept in core for reference 57262449Speter * resolution -- in fact it's certain the primitive types at the end 57362449Speter * of reference chains *won't* be in core unless they were explicitly 57462449Speter * in the select list themselves. 57562449Speter */ 57662449Speter if (namelst && (!infodump && !capdump)) { 57762449Speter (void) fprintf(stderr, 57866963Speter "Sorry, -e can't be used without -I or -C\n"); 57962449Speter cleanup(); 58062449Speter return EXIT_FAILURE; 58162449Speter } 58250276Speter#endif /* HAVE_BIG_CORE */ 58350276Speter 58462449Speter if (optind < argc) { 58562449Speter source_file = argv[optind++]; 58650276Speter if (optind < argc) { 58762449Speter fprintf(stderr, 58866963Speter "%s: Too many file names. Usage:\n\t%s %s", 58966963Speter _nc_progname, 59066963Speter _nc_progname, 59166963Speter usage_string); 59262449Speter return EXIT_FAILURE; 59362449Speter } 59462449Speter } else { 59562449Speter if (infodump == TRUE) { 59662449Speter /* captoinfo's no-argument case */ 59762449Speter source_file = "/etc/termcap"; 59862449Speter if ((termcap = getenv("TERMCAP")) != 0 59962449Speter && (namelst = make_namelist(getenv("TERM"))) != 0) { 60062449Speter if (access(termcap, F_OK) == 0) { 60162449Speter /* file exists */ 60262449Speter source_file = termcap; 60366963Speter } else if ((tmp_fp = open_tempfile(strcpy(my_tmpname, 60466963Speter "/tmp/XXXXXX"))) 60566963Speter != 0) { 60662449Speter source_file = my_tmpname; 60762449Speter fprintf(tmp_fp, "%s\n", termcap); 60862449Speter fclose(tmp_fp); 60966963Speter tmp_fp = open_input(source_file); 61062449Speter to_remove = source_file; 61162449Speter } else { 61262449Speter failed("tmpnam"); 61350276Speter } 61462449Speter } 61550276Speter } else { 61662449Speter /* tic */ 61762449Speter fprintf(stderr, 61866963Speter "%s: File name needed. Usage:\n\t%s %s", 61966963Speter _nc_progname, 62066963Speter _nc_progname, 62166963Speter usage_string); 62262449Speter cleanup(); 62362449Speter return EXIT_FAILURE; 62450276Speter } 62562449Speter } 62650276Speter 62766963Speter if (tmp_fp == 0) 62866963Speter tmp_fp = open_input(source_file); 62950276Speter 63062449Speter if (infodump) 63162449Speter dump_init(tversion, 63266963Speter smart_defaults 63366963Speter ? outform 63466963Speter : F_LITERAL, 63566963Speter sortmode, width, debug_level, formatted); 63662449Speter else if (capdump) 63762449Speter dump_init(tversion, 63866963Speter outform, 63966963Speter sortmode, width, debug_level, FALSE); 64050276Speter 64162449Speter /* parse entries out of the source file */ 64262449Speter _nc_set_source(source_file); 64366963Speter#if !HAVE_BIG_CORE 64462449Speter if (!(check_only || infodump || capdump)) 64562449Speter _nc_set_writedir(outdir); 64650276Speter#endif /* HAVE_BIG_CORE */ 64762449Speter _nc_read_entry_source(tmp_fp, (char *) NULL, 64866963Speter !smart_defaults, FALSE, 64966963Speter (check_only || infodump || capdump) ? NULLHOOK : immedhook); 65050276Speter 65162449Speter /* do use resolution */ 65262449Speter if (check_only || (!infodump && !capdump) || forceresolve) { 65362449Speter if (!_nc_resolve_uses(TRUE) && !check_only) { 65462449Speter cleanup(); 65562449Speter return EXIT_FAILURE; 65650276Speter } 65762449Speter } 65850276Speter 65962449Speter /* length check */ 66062449Speter if (check_only && (capdump || infodump)) { 66162449Speter for_entry_list(qp) { 66262449Speter if (matches(namelst, qp->tterm.term_names)) { 66362449Speter int len = fmt_entry(&qp->tterm, NULL, TRUE, infodump, numbers); 66450276Speter 66562449Speter if (len > (infodump ? MAX_TERMINFO_LENGTH : MAX_TERMCAP_LENGTH)) 66662449Speter (void) fprintf(stderr, 66766963Speter "warning: resolved %s entry is %d bytes long\n", 66866963Speter _nc_first_name(qp->tterm.term_names), 66966963Speter len); 67050276Speter } 67150276Speter } 67262449Speter } 67350276Speter 67462449Speter /* write or dump all entries */ 67562449Speter if (!check_only) { 67662449Speter if (!infodump && !capdump) { 67762449Speter _nc_set_writedir(outdir); 67862449Speter for_entry_list(qp) { 67962449Speter if (matches(namelst, qp->tterm.term_names)) 68062449Speter write_it(qp); 68150276Speter } 68262449Speter } else { 68362449Speter /* this is in case infotocap() generates warnings */ 68462449Speter _nc_curr_col = _nc_curr_line = -1; 68550276Speter 68662449Speter for_entry_list(qp) { 68762449Speter if (matches(namelst, qp->tterm.term_names)) { 68862449Speter int j = qp->cend - qp->cstart; 68962449Speter int len = 0; 69050276Speter 69162449Speter /* this is in case infotocap() generates warnings */ 69262449Speter _nc_set_type(_nc_first_name(qp->tterm.term_names)); 69350276Speter 69462449Speter (void) fseek(tmp_fp, qp->cstart, SEEK_SET); 69562449Speter while (j--) { 69662449Speter if (infodump) 69762449Speter (void) putchar(fgetc(tmp_fp)); 69862449Speter else 69962449Speter put_translate(fgetc(tmp_fp)); 70050276Speter } 70150276Speter 70262449Speter len = dump_entry(&qp->tterm, limited, numbers, NULL); 70362449Speter for (j = 0; j < qp->nuses; j++) 70462449Speter len += dump_uses(qp->uses[j].name, !capdump); 70562449Speter (void) putchar('\n'); 70662449Speter if (debug_level != 0 && !limited) 70762449Speter printf("# length=%d\n", len); 70862449Speter } 70962449Speter } 71076726Speter if (!namelst && _nc_tail) { 71162449Speter int c, oldc = '\0'; 71262449Speter bool in_comment = FALSE; 71362449Speter bool trailing_comment = FALSE; 71462449Speter 71562449Speter (void) fseek(tmp_fp, _nc_tail->cend, SEEK_SET); 71662449Speter while ((c = fgetc(tmp_fp)) != EOF) { 71762449Speter if (oldc == '\n') { 71862449Speter if (c == '#') { 71962449Speter trailing_comment = TRUE; 72062449Speter in_comment = TRUE; 72162449Speter } else { 72262449Speter in_comment = FALSE; 72350276Speter } 72450276Speter } 72562449Speter if (trailing_comment 72662449Speter && (in_comment || (oldc == '\n' && c == '\n'))) 72762449Speter putchar(c); 72862449Speter oldc = c; 72950276Speter } 73050276Speter } 73150276Speter } 73262449Speter } 73350276Speter 73462449Speter /* Show the directory into which entries were written, and the total 73562449Speter * number of entries 73662449Speter */ 73762449Speter if (showsummary 73862449Speter && (!(check_only || infodump || capdump))) { 73962449Speter int total = _nc_tic_written(); 74062449Speter if (total != 0) 74162449Speter fprintf(log_fp, "%d entries written to %s\n", 74266963Speter total, 74366963Speter _nc_tic_dir((char *) 0)); 74462449Speter else 74562449Speter fprintf(log_fp, "No entries written\n"); 74662449Speter } 74762449Speter cleanup(); 74862449Speter return (EXIT_SUCCESS); 74950276Speter} 75050276Speter 75150276Speter/* 75250276Speter * This bit of legerdemain turns all the terminfo variable names into 75350276Speter * references to locations in the arrays Booleans, Numbers, and Strings --- 75450276Speter * precisely what's needed (see comp_parse.c). 75550276Speter */ 75650276Speter 75762449SpeterTERMINAL *cur_term; /* tweak to avoid linking lib_cur_term.c */ 75850276Speter 75950276Speter#undef CUR 76050276Speter#define CUR tp-> 76150276Speter 76262449Speter/* 76366963Speter * Returns the expected number of parameters for the given capability. 76466963Speter */ 76566963Speterstatic int 76676726Speterexpected_params(const char *name) 76766963Speter{ 76866963Speter /* *INDENT-OFF* */ 76966963Speter static const struct { 77066963Speter const char *name; 77166963Speter int count; 77266963Speter } table[] = { 77376726Speter { "S0", 1 }, /* 'screen' extension */ 77466963Speter { "birep", 2 }, 77566963Speter { "chr", 1 }, 77666963Speter { "colornm", 1 }, 77766963Speter { "cpi", 1 }, 77876726Speter { "csnm", 1 }, 77966963Speter { "csr", 2 }, 78066963Speter { "cub", 1 }, 78166963Speter { "cud", 1 }, 78266963Speter { "cuf", 1 }, 78366963Speter { "cup", 2 }, 78476726Speter { "cuu", 1 }, 78566963Speter { "cvr", 1 }, 78666963Speter { "cwin", 5 }, 78766963Speter { "dch", 1 }, 78876726Speter { "defc", 3 }, 78966963Speter { "dial", 1 }, 79066963Speter { "dispc", 1 }, 79166963Speter { "dl", 1 }, 79266963Speter { "ech", 1 }, 79366963Speter { "getm", 1 }, 79466963Speter { "hpa", 1 }, 79566963Speter { "ich", 1 }, 79666963Speter { "il", 1 }, 79766963Speter { "indn", 1 }, 79866963Speter { "initc", 4 }, 79966963Speter { "initp", 7 }, 80066963Speter { "lpi", 1 }, 80166963Speter { "mc5p", 1 }, 80266963Speter { "mrcup", 2 }, 80366963Speter { "mvpa", 1 }, 80466963Speter { "pfkey", 2 }, 80566963Speter { "pfloc", 2 }, 80666963Speter { "pfx", 2 }, 80766963Speter { "pfxl", 3 }, 80866963Speter { "pln", 2 }, 80966963Speter { "qdial", 1 }, 81076726Speter { "rcsd", 1 }, 81166963Speter { "rep", 2 }, 81266963Speter { "rin", 1 }, 81366963Speter { "sclk", 3 }, 81466963Speter { "scp", 1 }, 81566963Speter { "scs", 1 }, 81676726Speter { "scsd", 2 }, 81766963Speter { "setab", 1 }, 81866963Speter { "setaf", 1 }, 81966963Speter { "setb", 1 }, 82066963Speter { "setcolor", 1 }, 82166963Speter { "setf", 1 }, 82266963Speter { "sgr", 9 }, 82366963Speter { "sgr1", 6 }, 82466963Speter { "slength", 1 }, 82566963Speter { "slines", 1 }, 82698503Speter { "smgbp", 1 }, /* 2 if smgtp is not given */ 82798503Speter { "smglp", 1 }, 82866963Speter { "smglr", 2 }, 82966963Speter { "smgrp", 1 }, 83066963Speter { "smgtb", 2 }, 83176726Speter { "smgtp", 1 }, 83266963Speter { "tsl", 1 }, 83366963Speter { "u6", -1 }, 83466963Speter { "vpa", 1 }, 83566963Speter { "wind", 4 }, 83666963Speter { "wingo", 1 }, 83766963Speter }; 83866963Speter /* *INDENT-ON* */ 83966963Speter 84066963Speter unsigned n; 84166963Speter int result = 0; /* function-keys, etc., use none */ 84266963Speter 84366963Speter for (n = 0; n < SIZEOF(table); n++) { 84466963Speter if (!strcmp(name, table[n].name)) { 84566963Speter result = table[n].count; 84666963Speter break; 84766963Speter } 84866963Speter } 84966963Speter 85066963Speter return result; 85166963Speter} 85266963Speter 85366963Speter/* 85466963Speter * Make a quick sanity check for the parameters which are used in the given 85566963Speter * strings. If there are no "%p" tokens, then there should be no other "%" 85666963Speter * markers. 85766963Speter */ 85866963Speterstatic void 85976726Spetercheck_params(TERMTYPE * tp, const char *name, char *value) 86066963Speter{ 86166963Speter int expected = expected_params(name); 86266963Speter int actual = 0; 86366963Speter int n; 86466963Speter bool params[10]; 86566963Speter char *s = value; 86666963Speter 86798503Speter if (!strcmp(name, "smgbp") 86898503Speter && set_top_margin_parm == 0) 86998503Speter expected = 2; 87098503Speter 87166963Speter for (n = 0; n < 10; n++) 87266963Speter params[n] = FALSE; 87366963Speter 87466963Speter while (*s != 0) { 87566963Speter if (*s == '%') { 87666963Speter if (*++s == '\0') { 87766963Speter _nc_warning("expected character after %% in %s", name); 87866963Speter break; 87966963Speter } else if (*s == 'p') { 88066963Speter if (*++s == '\0' || !isdigit((int) *s)) { 88166963Speter _nc_warning("expected digit after %%p in %s", name); 88266963Speter return; 88366963Speter } else { 88466963Speter n = (*s - '0'); 88566963Speter if (n > actual) 88666963Speter actual = n; 88766963Speter params[n] = TRUE; 88866963Speter } 88966963Speter } 89066963Speter } 89166963Speter s++; 89266963Speter } 89366963Speter 89466963Speter if (params[0]) { 89566963Speter _nc_warning("%s refers to parameter 0 (%%p0), which is not allowed", name); 89666963Speter } 89766963Speter if (value == set_attributes || expected < 0) { 89866963Speter ; 89966963Speter } else if (expected != actual) { 90066963Speter _nc_warning("%s uses %d parameters, expected %d", name, 90166963Speter actual, expected); 90266963Speter for (n = 1; n < actual; n++) { 90366963Speter if (!params[n]) 90466963Speter _nc_warning("%s omits parameter %d", name, n); 90566963Speter } 90666963Speter } 90766963Speter} 90866963Speter 90998503Speterstatic char * 91098503Speterskip_delay(char *s) 91198503Speter{ 91298503Speter while (*s == '/' || isdigit(UChar(*s))) 91398503Speter ++s; 91498503Speter return s; 91598503Speter} 91698503Speter 91766963Speter/* 91862449Speter * An sgr string may contain several settings other than the one we're 91962449Speter * interested in, essentially sgr0 + rmacs + whatever. As long as the 92062449Speter * "whatever" is contained in the sgr string, that is close enough for our 92162449Speter * sanity check. 92262449Speter */ 92362449Speterstatic bool 92498503Spetersimilar_sgr(int num, char *a, char *b) 92562449Speter{ 92698503Speter static const char *names[] = 92798503Speter { 92898503Speter "none" 92998503Speter ,"standout" 93098503Speter ,"underline" 93198503Speter ,"reverse" 93298503Speter ,"blink" 93398503Speter ,"dim" 93498503Speter ,"bold" 93598503Speter ,"invis" 93698503Speter ,"protect" 93798503Speter ,"altcharset" 93898503Speter }; 93998503Speter char *base_a = a; 94098503Speter char *base_b = b; 94198503Speter int delaying = 0; 94298503Speter 94362449Speter while (*b != 0) { 94462449Speter while (*a != *b) { 94576726Speter if (*a == 0) { 94676726Speter if (b[0] == '$' 94776726Speter && b[1] == '<') { 94876726Speter _nc_warning("Did not find delay %s", _nc_visbuf(b)); 94976726Speter } else { 95098503Speter _nc_warning("checking sgr(%s) %s\n\tcompare to %s\n\tunmatched %s", 95198503Speter names[num], _nc_visbuf2(1, base_a), 95298503Speter _nc_visbuf2(2, base_b), 95398503Speter _nc_visbuf2(3, b)); 95476726Speter } 95562449Speter return FALSE; 95698503Speter } else if (delaying) { 95798503Speter a = skip_delay(a); 95898503Speter b = skip_delay(b); 95998503Speter } else { 96098503Speter a++; 96176726Speter } 96262449Speter } 96398503Speter switch (*a) { 96498503Speter case '$': 96598503Speter if (delaying == 0) 96698503Speter delaying = 1; 96798503Speter break; 96898503Speter case '<': 96998503Speter if (delaying == 1) 97098503Speter delaying = 2; 97198503Speter break; 97298503Speter default: 97398503Speter delaying = 0; 97498503Speter break; 97598503Speter } 97662449Speter a++; 97762449Speter b++; 97862449Speter } 97962449Speter return TRUE; 98062449Speter} 98162449Speter 98262449Speterstatic void 98362449Spetercheck_sgr(TERMTYPE * tp, char *zero, int num, char *cap, const char *name) 98462449Speter{ 98562449Speter char *test = tparm(set_attributes, 98666963Speter num == 1, 98766963Speter num == 2, 98866963Speter num == 3, 98966963Speter num == 4, 99066963Speter num == 5, 99166963Speter num == 6, 99266963Speter num == 7, 99366963Speter num == 8, 99466963Speter num == 9); 99576726Speter tparm_errs += _nc_tparm_err; 99662449Speter if (test != 0) { 99762449Speter if (PRESENT(cap)) { 99898503Speter if (!similar_sgr(num, test, cap)) { 99998503Speter _nc_warning("%s differs from sgr(%d)\n\t%s=%s\n\tsgr(%d)=%s", 100098503Speter name, num, 100198503Speter name, _nc_visbuf2(1, cap), 100298503Speter num, _nc_visbuf2(2, test)); 100362449Speter } 100462449Speter } else if (strcmp(test, zero)) { 100562449Speter _nc_warning("sgr(%d) present, but not %s", num, name); 100662449Speter } 100762449Speter } else if (PRESENT(cap)) { 100862449Speter _nc_warning("sgr(%d) missing, but %s present", num, name); 100962449Speter } 101062449Speter} 101162449Speter 101262449Speter#define CHECK_SGR(num,name) check_sgr(tp, zero, num, name, #name) 101362449Speter 101450276Speter/* other sanity-checks (things that we don't want in the normal 101550276Speter * logic that reads a terminfo entry) 101650276Speter */ 101762449Speterstatic void 101862449Spetercheck_termtype(TERMTYPE * tp) 101950276Speter{ 102062449Speter bool conflict = FALSE; 102162449Speter unsigned j, k; 102262449Speter char fkeys[STRCOUNT]; 102350276Speter 102462449Speter /* 102562449Speter * A terminal entry may contain more than one keycode assigned to 102662449Speter * a given string (e.g., KEY_END and KEY_LL). But curses will only 102762449Speter * return one (the last one assigned). 102862449Speter */ 102962449Speter memset(fkeys, 0, sizeof(fkeys)); 103062449Speter for (j = 0; _nc_tinfo_fkeys[j].code; j++) { 103162449Speter char *a = tp->Strings[_nc_tinfo_fkeys[j].offset]; 103262449Speter bool first = TRUE; 103362449Speter if (!VALID_STRING(a)) 103462449Speter continue; 103562449Speter for (k = j + 1; _nc_tinfo_fkeys[k].code; k++) { 103662449Speter char *b = tp->Strings[_nc_tinfo_fkeys[k].offset]; 103762449Speter if (!VALID_STRING(b) 103862449Speter || fkeys[k]) 103950276Speter continue; 104062449Speter if (!strcmp(a, b)) { 104162449Speter fkeys[j] = 1; 104262449Speter fkeys[k] = 1; 104362449Speter if (first) { 104462449Speter if (!conflict) { 104562449Speter _nc_warning("Conflicting key definitions (using the last)"); 104662449Speter conflict = TRUE; 104750276Speter } 104862449Speter fprintf(stderr, "... %s is the same as %s", 104966963Speter keyname(_nc_tinfo_fkeys[j].code), 105066963Speter keyname(_nc_tinfo_fkeys[k].code)); 105162449Speter first = FALSE; 105262449Speter } else { 105362449Speter fprintf(stderr, ", %s", 105466963Speter keyname(_nc_tinfo_fkeys[k].code)); 105550276Speter } 105650276Speter } 105750276Speter } 105862449Speter if (!first) 105962449Speter fprintf(stderr, "\n"); 106062449Speter } 106150276Speter 106266963Speter for (j = 0; j < NUM_STRINGS(tp); j++) { 106366963Speter char *a = tp->Strings[j]; 106466963Speter if (VALID_STRING(a)) 106566963Speter check_params(tp, ExtStrname(tp, j, strnames), a); 106666963Speter } 106766963Speter 106862449Speter /* 106962449Speter * Quick check for color. We could also check if the ANSI versus 107062449Speter * non-ANSI strings are misused. 107162449Speter */ 107262449Speter if ((max_colors > 0) != (max_pairs > 0) 107398503Speter || ((max_colors > max_pairs) && (initialize_pair == 0))) 107498503Speter _nc_warning("inconsistent values for max_colors (%d) and max_pairs (%d)", 107598503Speter max_colors, max_pairs); 107650276Speter 107762449Speter PAIRED(set_foreground, set_background); 107862449Speter PAIRED(set_a_foreground, set_a_background); 107950276Speter 108062449Speter /* 108162449Speter * These may be mismatched because the terminal description relies on 108262449Speter * restoring the cursor visibility by resetting it. 108362449Speter */ 108462449Speter ANDMISSING(cursor_invisible, cursor_normal); 108562449Speter ANDMISSING(cursor_visible, cursor_normal); 108650276Speter 108762449Speter if (PRESENT(cursor_visible) && PRESENT(cursor_normal) 108862449Speter && !strcmp(cursor_visible, cursor_normal)) 108962449Speter _nc_warning("cursor_visible is same as cursor_normal"); 109050276Speter 109162449Speter /* 109262449Speter * From XSI & O'Reilly, we gather that sc/rc are required if csr is 109362449Speter * given, because the cursor position after the scrolling operation is 109462449Speter * performed is undefined. 109562449Speter */ 109662449Speter ANDMISSING(change_scroll_region, save_cursor); 109762449Speter ANDMISSING(change_scroll_region, restore_cursor); 109850276Speter 109976726Speter tparm_errs = 0; 110062449Speter if (PRESENT(set_attributes)) { 110162449Speter char *zero = tparm(set_attributes, 0, 0, 0, 0, 0, 0, 0, 0, 0); 110262449Speter 110362449Speter zero = strdup(zero); 110462449Speter CHECK_SGR(1, enter_standout_mode); 110562449Speter CHECK_SGR(2, enter_underline_mode); 110662449Speter CHECK_SGR(3, enter_reverse_mode); 110762449Speter CHECK_SGR(4, enter_blink_mode); 110862449Speter CHECK_SGR(5, enter_dim_mode); 110962449Speter CHECK_SGR(6, enter_bold_mode); 111062449Speter CHECK_SGR(7, enter_secure_mode); 111162449Speter CHECK_SGR(8, enter_protected_mode); 111262449Speter CHECK_SGR(9, enter_alt_charset_mode); 111362449Speter free(zero); 111476726Speter if (tparm_errs) 111576726Speter _nc_warning("stack error in sgr string"); 111662449Speter } 111762449Speter 111862449Speter /* 111962449Speter * Some standard applications (e.g., vi) and some non-curses 112062449Speter * applications (e.g., jove) get confused if we have both ich/ich1 and 112162449Speter * smir/rmir. Let's be nice and warn about that, too, even though 112262449Speter * ncurses handles it. 112362449Speter */ 112462449Speter if ((PRESENT(enter_insert_mode) || PRESENT(exit_insert_mode)) 112562449Speter && (PRESENT(insert_character) || PRESENT(parm_ich))) { 112662449Speter _nc_warning("non-curses applications may be confused by ich/ich1 with smir/rmir"); 112762449Speter } 112862449Speter 112962449Speter /* 113062449Speter * Finally, do the non-verbose checks 113162449Speter */ 113262449Speter if (save_check_termtype != 0) 113362449Speter save_check_termtype(tp); 113450276Speter} 1135