X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=progs%2Ftic.c;h=594474de44cab20b570ddfacbea68f88ef3e8d7b;hp=cc73a4db220cca19d333182f84e3929b8b199787;hb=46722468f47c2b77b3987729b4bcf2321cccfd01;hpb=b1f61d9f3aa244512045a6b02e759825d7049d34 diff --git a/progs/tic.c b/progs/tic.c index cc73a4db..594474de 100644 --- a/progs/tic.c +++ b/progs/tic.c @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc. * + * Copyright (c) 1998-2001,2002 Free Software Foundation, Inc. * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the * @@ -29,6 +29,7 @@ /**************************************************************************** * Author: Zeyd M. Ben-Halim 1992,1995 * * and: Eric S. Raymond * + * and: Thomas E. Dickey 1996 on * ****************************************************************************/ /* @@ -38,11 +39,13 @@ */ #include +#include #include #include +#include -MODULE_ID("$Id: tic.c,v 1.69 2000/04/08 23:53:49 tom Exp $") +MODULE_ID("$Id: tic.c,v 1.102 2002/10/05 19:59:41 tom Exp $") const char *_nc_progname = "tic"; @@ -50,11 +53,12 @@ static FILE *log_fp; static FILE *tmp_fp; static bool showsummary = FALSE; static const char *to_remove; +static int tparm_errs; static void (*save_check_termtype) (TERMTYPE *); static void check_termtype(TERMTYPE * tt); -static const char usage_string[] = "[-h] [-v[n]] [-e names] [-CILNRTcfrswx1] source-file\n"; +static const char usage_string[] = "[-V] [-v[n]] [-e names] [-CILNRTcfrswx1] source-file\n"; static void cleanup(void) @@ -85,23 +89,27 @@ usage(void) { "Options:", " -1 format translation output one capability per line", - " -C translate entries to termcap source form", - " -I translate entries to terminfo source form", - " -L translate entries to full terminfo source form", - " -N disable smart defaults for source translation", - " -R restrict translation to given terminfo/termcap version", - " -T remove size-restrictions on compiled description", #if NCURSES_XNAMES " -a retain commented-out capabilities (sets -x also)", #endif + " -C translate entries to termcap source form", " -c check only, validate input without compiling or translating", + " -e translate/compile only entries named by comma-separated list", " -f format complex strings for readability", " -G format %{number} to %'char'", " -g format %'char' to %{number}", - " -e translate/compile only entries named by comma-separated list", + " -I translate entries to terminfo source form", + " -L translate entries to full terminfo source form", + " -N disable smart defaults for source translation", " -o set output directory for compiled entry writes", + " -R restrict translation to given terminfo/termcap version", " -r force resolution of all use entries in source translation", " -s print summary statistics", + " -T remove size-restrictions on compiled description", +#if NCURSES_XNAMES + " -t suppress commented-out capabilities", +#endif + " -V print version", " -v[n] set verbosity level", " -w[n] set format width for translation output", #if NCURSES_XNAMES @@ -114,7 +122,7 @@ usage(void) size_t j; fprintf(stderr, "Usage: %s %s\n", _nc_progname, usage_string); - for (j = 0; j < sizeof(tbl) / sizeof(tbl[0]); j++) { + for (j = 0; j < SIZEOF(tbl); j++) { fputs(tbl[j], stderr); putc('\n', stderr); } @@ -148,7 +156,7 @@ write_it(ENTRY * ep) if (ch == '\\') { *d++ = *t++; } else if ((ch == '%') - && (*t == L_BRACE)) { + && (*t == L_BRACE)) { char *v = 0; long value = strtol(t + 1, &v, 0); if (v != 0 @@ -179,7 +187,7 @@ static bool immedhook(ENTRY * ep GCC_UNUSED) /* write out entries with no use capabilities immediately to save storage */ { -#ifndef HAVE_BIG_CORE +#if !HAVE_BIG_CORE /* * This is strictly a core-economy kluge. The really clean way to handle * compilation is to slurp the whole file into core and then do all the @@ -289,18 +297,36 @@ put_translate(int c) static char * stripped(char *src) { - while (isspace(*src)) + while (isspace(UChar(*src))) src++; if (*src != '\0') { char *dst = strcpy(malloc(strlen(src) + 1), src); size_t len = strlen(dst); - while (--len != 0 && isspace(dst[len])) + while (--len != 0 && isspace(UChar(dst[len]))) dst[len] = '\0'; return dst; } return 0; } +static FILE * +open_input(const char *filename) +{ + FILE *fp = fopen(filename, "r"); + struct stat sb; + + if (fp == 0) { + fprintf(stderr, "%s: Can't open %s\n", _nc_progname, filename); + exit(EXIT_FAILURE); + } + if (fstat(fileno(fp), &sb) < 0 + || (sb.st_mode & S_IFMT) != S_IFREG) { + fprintf(stderr, "%s: %s is not a file\n", _nc_progname, filename); + exit(EXIT_FAILURE); + } + return fp; +} + /* Parse the "-e" option-value into a list of names */ static const char ** make_namelist(char *src) @@ -314,9 +340,7 @@ make_namelist(char *src) if (src == 0) { /* EMPTY */ ; } else if (strchr(src, '/') != 0) { /* a filename */ - FILE *fp = fopen(src, "r"); - if (fp == 0) - failed(src); + FILE *fp = open_input(src); for (pass = 1; pass <= 2; pass++) { nn = 0; @@ -422,19 +446,17 @@ main(int argc, char *argv[]) const char **namelst = 0; char *outdir = (char *) NULL; bool check_only = FALSE; + bool suppress_untranslatable = FALSE; log_fp = stderr; - if ((_nc_progname = strrchr(argv[0], '/')) == NULL) - _nc_progname = argv[0]; - else - _nc_progname++; + _nc_progname = _nc_rootname(argv[0]); - if ((infodump = (strcmp(_nc_progname, "captoinfo") == 0)) != FALSE) { + if ((infodump = (strcmp(_nc_progname, PROG_CAPTOINFO) == 0)) != FALSE) { outform = F_TERMINFO; sortmode = S_TERMINFO; } - if ((capdump = (strcmp(_nc_progname, "infotocap") == 0)) != FALSE) { + if ((capdump = (strcmp(_nc_progname, PROG_INFOTOCAP) == 0)) != FALSE) { outform = F_TERMCAP; sortmode = S_TERMCAP; } @@ -448,7 +470,7 @@ main(int argc, char *argv[]) * be optional. */ while ((this_opt = getopt(argc, argv, - "0123456789CILNR:TVace:fGgo:rsvwx")) != EOF) { + "0123456789CILNR:TVace:fGgo:rstvwx")) != EOF) { if (isdigit(this_opt)) { switch (last_opt) { case 'v': @@ -491,7 +513,7 @@ main(int argc, char *argv[]) limited = FALSE; break; case 'V': - puts(NCURSES_VERSION); + puts(curses_version()); return EXIT_SUCCESS; case 'c': check_only = TRUE; @@ -524,6 +546,10 @@ main(int argc, char *argv[]) width = 0; break; #if NCURSES_XNAMES + case 't': + _nc_disable_period = FALSE; + suppress_untranslatable = TRUE; + break; case 'a': _nc_disable_period = TRUE; /* FALLTHRU */ @@ -544,7 +570,7 @@ main(int argc, char *argv[]) save_check_termtype = _nc_check_termtype; _nc_check_termtype = check_termtype; } -#ifndef HAVE_BIG_CORE +#if !HAVE_BIG_CORE /* * Aaargh! immedhook seriously hoses us! * @@ -557,7 +583,7 @@ main(int argc, char *argv[]) */ if (namelst && (!infodump && !capdump)) { (void) fprintf(stderr, - "Sorry, -e can't be used without -I or -C\n"); + "Sorry, -e can't be used without -I or -C\n"); cleanup(); return EXIT_FAILURE; } @@ -567,10 +593,10 @@ main(int argc, char *argv[]) source_file = argv[optind++]; if (optind < argc) { fprintf(stderr, - "%s: Too many file names. Usage:\n\t%s %s", - _nc_progname, - _nc_progname, - usage_string); + "%s: Too many file names. Usage:\n\t%s %s", + _nc_progname, + _nc_progname, + usage_string); return EXIT_FAILURE; } } else { @@ -582,11 +608,13 @@ main(int argc, char *argv[]) if (access(termcap, F_OK) == 0) { /* file exists */ source_file = termcap; - } else if ((tmp_fp = open_tempfile(my_tmpname)) != 0) { + } else if ((tmp_fp = open_tempfile(strcpy(my_tmpname, + "/tmp/XXXXXX"))) + != 0) { source_file = my_tmpname; fprintf(tmp_fp, "%s\n", termcap); fclose(tmp_fp); - tmp_fp = fopen(source_file, "r"); + tmp_fp = open_input(source_file); to_remove = source_file; } else { failed("tmpnam"); @@ -595,41 +623,38 @@ main(int argc, char *argv[]) } else { /* tic */ fprintf(stderr, - "%s: File name needed. Usage:\n\t%s %s", - _nc_progname, - _nc_progname, - usage_string); + "%s: File name needed. Usage:\n\t%s %s", + _nc_progname, + _nc_progname, + usage_string); cleanup(); return EXIT_FAILURE; } } - if (tmp_fp == 0 - && (tmp_fp = fopen(source_file, "r")) == 0) { - fprintf(stderr, "%s: Can't open %s\n", _nc_progname, source_file); - return EXIT_FAILURE; - } + if (tmp_fp == 0) + tmp_fp = open_input(source_file); if (infodump) dump_init(tversion, - smart_defaults - ? outform - : F_LITERAL, - sortmode, width, debug_level, formatted); + smart_defaults + ? outform + : F_LITERAL, + sortmode, width, debug_level, formatted); else if (capdump) dump_init(tversion, - outform, - sortmode, width, debug_level, FALSE); + outform, + sortmode, width, debug_level, FALSE); /* parse entries out of the source file */ _nc_set_source(source_file); -#ifndef HAVE_BIG_CORE +#if !HAVE_BIG_CORE if (!(check_only || infodump || capdump)) _nc_set_writedir(outdir); #endif /* HAVE_BIG_CORE */ _nc_read_entry_source(tmp_fp, (char *) NULL, - !smart_defaults, FALSE, - (check_only || infodump || capdump) ? NULLHOOK : immedhook); + !smart_defaults, FALSE, + (check_only || infodump || capdump) ? NULLHOOK : immedhook); /* do use resolution */ if (check_only || (!infodump && !capdump) || forceresolve) { @@ -643,13 +668,13 @@ main(int argc, char *argv[]) if (check_only && (capdump || infodump)) { for_entry_list(qp) { if (matches(namelst, qp->tterm.term_names)) { - int len = fmt_entry(&qp->tterm, NULL, TRUE, infodump, numbers); + int len = fmt_entry(&qp->tterm, NULL, FALSE, TRUE, infodump, numbers); if (len > (infodump ? MAX_TERMINFO_LENGTH : MAX_TERMCAP_LENGTH)) (void) fprintf(stderr, - "warning: resolved %s entry is %d bytes long\n", - _nc_first_name(qp->tterm.term_names), - len); + "warning: resolved %s entry is %d bytes long\n", + _nc_first_name(qp->tterm.term_names), + len); } } } @@ -682,7 +707,8 @@ main(int argc, char *argv[]) put_translate(fgetc(tmp_fp)); } - len = dump_entry(&qp->tterm, limited, numbers, NULL); + len = dump_entry(&qp->tterm, suppress_untranslatable, + limited, 0, numbers, NULL); for (j = 0; j < qp->nuses; j++) len += dump_uses(qp->uses[j].name, !capdump); (void) putchar('\n'); @@ -690,7 +716,7 @@ main(int argc, char *argv[]) printf("# length=%d\n", len); } } - if (!namelst) { + if (!namelst && _nc_tail) { int c, oldc = '\0'; bool in_comment = FALSE; bool trailing_comment = FALSE; @@ -722,8 +748,8 @@ main(int argc, char *argv[]) int total = _nc_tic_written(); if (total != 0) fprintf(log_fp, "%d entries written to %s\n", - total, - _nc_tic_dir((char *) 0)); + total, + _nc_tic_dir((char *) 0)); else fprintf(log_fp, "No entries written\n"); } @@ -742,6 +768,163 @@ TERMINAL *cur_term; /* tweak to avoid linking lib_cur_term.c */ #undef CUR #define CUR tp-> +/* + * Returns the expected number of parameters for the given capability. + */ +static int +expected_params(const char *name) +{ + /* *INDENT-OFF* */ + static const struct { + const char *name; + int count; + } table[] = { + { "S0", 1 }, /* 'screen' extension */ + { "birep", 2 }, + { "chr", 1 }, + { "colornm", 1 }, + { "cpi", 1 }, + { "csnm", 1 }, + { "csr", 2 }, + { "cub", 1 }, + { "cud", 1 }, + { "cuf", 1 }, + { "cup", 2 }, + { "cuu", 1 }, + { "cvr", 1 }, + { "cwin", 5 }, + { "dch", 1 }, + { "defc", 3 }, + { "dial", 1 }, + { "dispc", 1 }, + { "dl", 1 }, + { "ech", 1 }, + { "getm", 1 }, + { "hpa", 1 }, + { "ich", 1 }, + { "il", 1 }, + { "indn", 1 }, + { "initc", 4 }, + { "initp", 7 }, + { "lpi", 1 }, + { "mc5p", 1 }, + { "mrcup", 2 }, + { "mvpa", 1 }, + { "pfkey", 2 }, + { "pfloc", 2 }, + { "pfx", 2 }, + { "pfxl", 3 }, + { "pln", 2 }, + { "qdial", 1 }, + { "rcsd", 1 }, + { "rep", 2 }, + { "rin", 1 }, + { "sclk", 3 }, + { "scp", 1 }, + { "scs", 1 }, + { "scsd", 2 }, + { "setab", 1 }, + { "setaf", 1 }, + { "setb", 1 }, + { "setcolor", 1 }, + { "setf", 1 }, + { "sgr", 9 }, + { "sgr1", 6 }, + { "slength", 1 }, + { "slines", 1 }, + { "smgbp", 1 }, /* 2 if smgtp is not given */ + { "smglp", 1 }, + { "smglr", 2 }, + { "smgrp", 1 }, + { "smgtb", 2 }, + { "smgtp", 1 }, + { "tsl", 1 }, + { "u6", -1 }, + { "vpa", 1 }, + { "wind", 4 }, + { "wingo", 1 }, + }; + /* *INDENT-ON* */ + + unsigned n; + int result = 0; /* function-keys, etc., use none */ + + for (n = 0; n < SIZEOF(table); n++) { + if (!strcmp(name, table[n].name)) { + result = table[n].count; + break; + } + } + + return result; +} + +/* + * Make a quick sanity check for the parameters which are used in the given + * strings. If there are no "%p" tokens, then there should be no other "%" + * markers. + */ +static void +check_params(TERMTYPE * tp, const char *name, char *value) +{ + int expected = expected_params(name); + int actual = 0; + int n; + bool params[10]; + char *s = value; + +#ifdef set_top_margin_parm + if (!strcmp(name, "smgbp") + && set_top_margin_parm == 0) + expected = 2; +#endif + + for (n = 0; n < 10; n++) + params[n] = FALSE; + + while (*s != 0) { + if (*s == '%') { + if (*++s == '\0') { + _nc_warning("expected character after %% in %s", name); + break; + } else if (*s == 'p') { + if (*++s == '\0' || !isdigit((int) *s)) { + _nc_warning("expected digit after %%p in %s", name); + return; + } else { + n = (*s - '0'); + if (n > actual) + actual = n; + params[n] = TRUE; + } + } + } + s++; + } + + if (params[0]) { + _nc_warning("%s refers to parameter 0 (%%p0), which is not allowed", name); + } + if (value == set_attributes || expected < 0) { + ; + } else if (expected != actual) { + _nc_warning("%s uses %d parameters, expected %d", name, + actual, expected); + for (n = 1; n < actual; n++) { + if (!params[n]) + _nc_warning("%s omits parameter %d", name, n); + } + } +} + +static char * +skip_delay(char *s) +{ + while (*s == '/' || isdigit(UChar(*s))) + ++s; + return s; +} + /* * An sgr string may contain several settings other than the one we're * interested in, essentially sgr0 + rmacs + whatever. As long as the @@ -749,13 +932,57 @@ TERMINAL *cur_term; /* tweak to avoid linking lib_cur_term.c */ * sanity check. */ static bool -similar_sgr(char *a, char *b) +similar_sgr(int num, char *a, char *b) { + static const char *names[] = + { + "none" + ,"standout" + ,"underline" + ,"reverse" + ,"blink" + ,"dim" + ,"bold" + ,"invis" + ,"protect" + ,"altcharset" + }; + char *base_a = a; + char *base_b = b; + int delaying = 0; + while (*b != 0) { while (*a != *b) { - if (*a == 0) + if (*a == 0) { + if (b[0] == '$' + && b[1] == '<') { + _nc_warning("Did not find delay %s", _nc_visbuf(b)); + } else { + _nc_warning("checking sgr(%s) %s\n\tcompare to %s\n\tunmatched %s", + names[num], _nc_visbuf2(1, base_a), + _nc_visbuf2(2, base_b), + _nc_visbuf2(3, b)); + } return FALSE; - a++; + } else if (delaying) { + a = skip_delay(a); + b = skip_delay(b); + } else { + a++; + } + } + switch (*a) { + case '$': + if (delaying == 0) + delaying = 1; + break; + case '<': + if (delaying == 1) + delaying = 2; + break; + default: + delaying = 0; + break; } a++; b++; @@ -767,20 +994,23 @@ static void check_sgr(TERMTYPE * tp, char *zero, int num, char *cap, const char *name) { char *test = tparm(set_attributes, - num == 1, - num == 2, - num == 3, - num == 4, - num == 5, - num == 6, - num == 7, - num == 8, - num == 9); + num == 1, + num == 2, + num == 3, + num == 4, + num == 5, + num == 6, + num == 7, + num == 8, + num == 9); + tparm_errs += _nc_tparm_err; if (test != 0) { if (PRESENT(cap)) { - if (!similar_sgr(test, cap)) { - _nc_warning("%s differs from sgr(%d): %s", name, num, - _nc_visbuf(test)); + if (!similar_sgr(num, test, cap)) { + _nc_warning("%s differs from sgr(%d)\n\t%s=%s\n\tsgr(%d)=%s", + name, num, + name, _nc_visbuf2(1, cap), + num, _nc_visbuf2(2, test)); } } else if (strcmp(test, zero)) { _nc_warning("sgr(%d) present, but not %s", num, name); @@ -827,12 +1057,12 @@ check_termtype(TERMTYPE * tp) conflict = TRUE; } fprintf(stderr, "... %s is the same as %s", - keyname(_nc_tinfo_fkeys[j].code), - keyname(_nc_tinfo_fkeys[k].code)); + keyname(_nc_tinfo_fkeys[j].code), + keyname(_nc_tinfo_fkeys[k].code)); first = FALSE; } else { fprintf(stderr, ", %s", - keyname(_nc_tinfo_fkeys[k].code)); + keyname(_nc_tinfo_fkeys[k].code)); } } } @@ -840,13 +1070,20 @@ check_termtype(TERMTYPE * tp) fprintf(stderr, "\n"); } + for (j = 0; j < NUM_STRINGS(tp); j++) { + char *a = tp->Strings[j]; + if (VALID_STRING(a)) + check_params(tp, ExtStrname(tp, j, strnames), a); + } + /* * Quick check for color. We could also check if the ANSI versus * non-ANSI strings are misused. */ if ((max_colors > 0) != (max_pairs > 0) - || (max_colors > max_pairs)) - _nc_warning("inconsistent values for max_colors and max_pairs"); + || ((max_colors > max_pairs) && (initialize_pair == 0))) + _nc_warning("inconsistent values for max_colors (%d) and max_pairs (%d)", + max_colors, max_pairs); PAIRED(set_foreground, set_background); PAIRED(set_a_foreground, set_a_background); @@ -870,6 +1107,7 @@ check_termtype(TERMTYPE * tp) ANDMISSING(change_scroll_region, save_cursor); ANDMISSING(change_scroll_region, restore_cursor); + tparm_errs = 0; if (PRESENT(set_attributes)) { char *zero = tparm(set_attributes, 0, 0, 0, 0, 0, 0, 0, 0, 0); @@ -884,17 +1122,19 @@ check_termtype(TERMTYPE * tp) CHECK_SGR(8, enter_protected_mode); CHECK_SGR(9, enter_alt_charset_mode); free(zero); + if (tparm_errs) + _nc_warning("stack error in sgr string"); } /* * Some standard applications (e.g., vi) and some non-curses - * applications (e.g., jove) get confused if we have both ich/ich1 and + * applications (e.g., jove) get confused if we have both ich1 and * smir/rmir. Let's be nice and warn about that, too, even though * ncurses handles it. */ if ((PRESENT(enter_insert_mode) || PRESENT(exit_insert_mode)) - && (PRESENT(insert_character) || PRESENT(parm_ich))) { - _nc_warning("non-curses applications may be confused by ich/ich1 with smir/rmir"); + && PRESENT(parm_ich)) { + _nc_warning("non-curses applications may be confused by ich1 with smir/rmir"); } /*