X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=progs%2Ftic.c;h=d96555e9bee6a11dc34018b438d2ced1245e210a;hp=d40dde021bb0ed59e0953ae394999a7459f279c4;hb=beb0f0c6911096ee19815bdf2601c4317d80341f;hpb=af4c589f0c605e1a1dd3825678a0b1a97df02d37 diff --git a/progs/tic.c b/progs/tic.c index d40dde02..d96555e9 100644 --- a/progs/tic.c +++ b/progs/tic.c @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (c) 1998-2010,2011 Free Software Foundation, Inc. * + * Copyright (c) 1998-2011,2012 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 * @@ -35,6 +35,7 @@ /* * tic.c --- Main program for terminfo compiler * by Eric S. Raymond + * and Thomas E Dickey * */ @@ -42,9 +43,12 @@ #include #include +#include #include -MODULE_ID("$Id: tic.c,v 1.153 2011/09/26 23:50:46 tom Exp $") +MODULE_ID("$Id: tic.c,v 1.176 2012/04/29 00:23:38 tom Exp $") + +#define STDIN_NAME "" const char *_nc_progname = "tic"; @@ -53,6 +57,7 @@ static FILE *tmp_fp; static bool capdump = FALSE; /* running as infotocap? */ static bool infodump = FALSE; /* running as captoinfo? */ static bool showsummary = FALSE; +static char **namelst = 0; static const char *to_remove; static void (*save_check_termtype) (TERMTYPE *, bool); @@ -69,6 +74,7 @@ static const char usage_string[] = "\ 1\ a\ C\ +D\ c\ f\ G\ @@ -100,7 +106,7 @@ free_namelist(char **src) #endif static void -cleanup(char **namelst GCC_UNUSED) +cleanup(void) { #if NO_LEAKS free_namelist(namelst); @@ -120,7 +126,6 @@ static void failed(const char *msg) { perror(msg); - cleanup((char **) 0); ExitProgram(EXIT_FAILURE); } @@ -136,6 +141,7 @@ usage(void) #endif " -K translate entries to termcap source form with BSD syntax", " -C translate entries to termcap source form", + " -D print list of tic's database locations (first must be writable)", " -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", @@ -175,7 +181,7 @@ usage(void) #define L_BRACE '{' #define R_BRACE '}' -#define S_QUOTE '\''; +#define S_QUOTE '\'' static void write_it(ENTRY * ep) @@ -218,7 +224,7 @@ write_it(ENTRY * ep) } *d = 0; if (strlen(result) < strlen(s)) - strcpy(s, result); + _nc_STRCPY(s, result, strlen(s) + 1); } } @@ -310,7 +316,7 @@ put_translate(int c) if ((up = strchr(namebuf, '#')) != 0 || (up = strchr(namebuf, '=')) != 0 || ((up = strchr(namebuf, '@')) != 0 && up[1] == '>')) { - (void) strcpy(suffix, up); + _nc_STRCPY(suffix, up, have); *up = '\0'; } @@ -361,19 +367,96 @@ stripped(char *src) } static FILE * -open_input(const char *filename) +open_tempfile(char *filename) +{ + FILE *result = 0; + + _nc_STRCPY(filename, "/tmp/XXXXXX", PATH_MAX); +#if HAVE_MKSTEMP + { + int fd = mkstemp(filename); + if (fd >= 0) + result = fdopen(fd, "w"); + } +#else + if (tmpnam(filename) != 0) + result = fopen(filename, "w"); +#endif + return result; +} + +static FILE * +copy_input(FILE *source, const char *filename, char *alt_file) +{ + FILE *result = 0; + FILE *target = open_tempfile(alt_file); + int ch; + + if (source == 0) { + failed("copy_input (source)"); + } else if (target == 0) { + failed("copy_input (target)"); + } else { + clearerr(source); + for (;;) { + ch = fgetc(source); + if (feof(source)) { + break; + } else if (ferror(source)) { + failed(filename); + } else if (ch == 0) { + /* don't loop in case someone wants to convert /dev/zero */ + fprintf(stderr, "%s: %s is not a text-file\n", _nc_progname, filename); + ExitProgram(EXIT_FAILURE); + } + fputc(ch, target); + } + fclose(source); + /* + * rewind() does not force the target file's data to disk (not does + * fflush()...). So open a second stream on the data and then close + * the one that we were writing on before starting to read from the + * second stream. + */ + result = fopen(alt_file, "r+"); + fclose(target); + to_remove = alt_file; + } + return result; +} + +static FILE * +open_input(const char *filename, char *alt_file) { - FILE *fp = fopen(filename, "r"); + FILE *fp; struct stat sb; + int mode; - if (fp == 0) { - fprintf(stderr, "%s: Can't open %s\n", _nc_progname, filename); + if (!strcmp(filename, "-")) { + fp = copy_input(stdin, STDIN_NAME, alt_file); + } else if (stat(filename, &sb) < 0) { + fprintf(stderr, "%s: %s %s\n", _nc_progname, filename, strerror(errno)); ExitProgram(EXIT_FAILURE); - } - if (fstat(fileno(fp), &sb) < 0 - || (sb.st_mode & S_IFMT) != S_IFREG) { + } else if ((mode = (sb.st_mode & S_IFMT)) == S_IFDIR + || (mode != S_IFREG && mode != S_IFCHR)) { fprintf(stderr, "%s: %s is not a file\n", _nc_progname, filename); ExitProgram(EXIT_FAILURE); + } else { + fp = fopen(filename, "r"); + + if (fp == 0) { + fprintf(stderr, "%s: Can't open %s\n", _nc_progname, filename); + ExitProgram(EXIT_FAILURE); + } + if (mode != S_IFREG) { + if (alt_file != 0) { + FILE *fp2 = copy_input(fp, filename, alt_file); + fp = fp2; + } else { + fprintf(stderr, "%s: %s is not a file\n", _nc_progname, filename); + ExitProgram(EXIT_FAILURE); + } + } } return fp; } @@ -391,7 +474,7 @@ make_namelist(char *src) if (src == 0) { /* EMPTY */ ; } else if (strchr(src, '/') != 0) { /* a filename */ - FILE *fp = open_input(src); + FILE *fp = open_input(src, (char *) 0); for (pass = 1; pass <= 2; pass++) { nn = 0; @@ -458,25 +541,112 @@ matches(char **needle, const char *haystack) return (code); } -static FILE * -open_tempfile(char *name) +static const char * +valid_db_path(const char *nominal) { - FILE *result = 0; -#if HAVE_MKSTEMP - int fd = mkstemp(name); - if (fd >= 0) - result = fdopen(fd, "w"); + struct stat sb; +#if USE_HASHED_DB + char suffix[] = DBM_SUFFIX; + size_t need = strlen(nominal) + sizeof(suffix); + char *result = malloc(need); + + _nc_STRCPY(result, nominal, need); + if (strcmp(result + need - sizeof(suffix), suffix)) { + _nc_STRCAT(result, suffix, need); + } #else - if (tmpnam(name) != 0) - result = fopen(name, "w"); + char *result = strdup(nominal); #endif + + DEBUG(1, ("** stat(%s)", result)); + if (stat(result, &sb) >= 0) { +#if USE_HASHED_DB + if (!S_ISREG(sb.st_mode) + || access(result, R_OK | W_OK) != 0) { + DEBUG(1, ("...not a writable file")); + free(result); + result = 0; + } +#else + if (!S_ISDIR(sb.st_mode) + || access(result, R_OK | W_OK | X_OK) != 0) { + DEBUG(1, ("...not a writable directory")); + free(result); + result = 0; + } +#endif + } else { + /* check if parent is directory and is writable */ + unsigned leaf = _nc_pathlast(result); + + DEBUG(1, ("...not found")); + if (leaf) { + char save = result[leaf]; + result[leaf] = 0; + if (stat(result, &sb) >= 0 + && S_ISDIR(sb.st_mode) + && access(result, R_OK | W_OK | X_OK) == 0) { + result[leaf] = save; + } else { + DEBUG(1, ("...parent directory %s is not writable", result)); + free(result); + result = 0; + } + } else { + DEBUG(1, ("... no parent directory")); + free(result); + result = 0; + } + } return result; } +/* + * Show the databases to which tic could write. The location to which it + * writes is always the first one. If none are writable, print an error + * message. + */ +static void +show_databases(const char *outdir) +{ + bool specific = (outdir != 0) || getenv("TERMINFO") != 0; + const char *result; + const char *tried = 0; + + if (outdir == 0) { + outdir = _nc_tic_dir(0); + } + if ((result = valid_db_path(outdir)) != 0) { + printf("%s\n", result); + } else { + tried = outdir; + } + + if ((outdir = _nc_home_terminfo())) { + if ((result = valid_db_path(outdir)) != 0) { + printf("%s\n", result); + } else if (!specific) { + tried = outdir; + } + } + + /* + * If we can write in neither location, give an error message. + */ + if (tried) { + fflush(stdout); + fprintf(stderr, "%s: %s (no permission)\n", _nc_progname, tried); + ExitProgram(EXIT_FAILURE); + } +} + +#define VtoTrace(opt) (unsigned) ((opt > 0) ? opt : (opt == 0)) + int main(int argc, char *argv[]) { char my_tmpname[PATH_MAX]; + char my_altfile[PATH_MAX]; int v_opt = -1; unsigned debug_level; int smart_defaults = TRUE; @@ -497,7 +667,6 @@ main(int argc, char *argv[]) bool limited = TRUE; char *tversion = (char *) NULL; const char *source_file = "terminfo"; - char **namelst = 0; char *outdir = (char *) NULL; bool check_only = FALSE; bool suppress_untranslatable = FALSE; @@ -505,6 +674,7 @@ main(int argc, char *argv[]) log_fp = stderr; _nc_progname = _nc_rootname(argv[0]); + atexit(cleanup); if ((infodump = same_program(_nc_progname, PROG_CAPTOINFO)) != FALSE) { outform = F_TERMINFO; @@ -525,7 +695,7 @@ main(int argc, char *argv[]) * be optional. */ while ((this_opt = getopt(argc, argv, - "0123456789CIKLNR:TUVace:fGgo:rstvwx")) != -1) { + "0123456789CDIKLNR:TUVace:fGgo:rstvwx")) != -1) { if (isdigit(this_opt)) { switch (last_opt) { case 'v': @@ -563,6 +733,12 @@ main(int argc, char *argv[]) outform = F_TERMCAP; sortmode = S_TERMCAP; break; + case 'D': + debug_level = VtoTrace(v_opt); + set_trace_level(debug_level); + show_databases(outdir); + ExitProgram(EXIT_SUCCESS); + break; case 'I': infodump = TRUE; outform = F_TERMINFO; @@ -588,7 +764,6 @@ main(int argc, char *argv[]) break; case 'V': puts(curses_version()); - cleanup(namelst); ExitProgram(EXIT_SUCCESS); case 'c': check_only = TRUE; @@ -638,7 +813,7 @@ main(int argc, char *argv[]) last_opt = this_opt; } - debug_level = (unsigned) ((v_opt > 0) ? v_opt : (v_opt == 0)); + debug_level = VtoTrace(v_opt); set_trace_level(debug_level); if (_nc_tracing) { @@ -658,8 +833,8 @@ main(int argc, char *argv[]) */ if (namelst && (!infodump && !capdump)) { (void) fprintf(stderr, - "Sorry, -e can't be used without -I or -C\n"); - cleanup(namelst); + "%s: Sorry, -e can't be used without -I or -C\n", + _nc_progname); ExitProgram(EXIT_FAILURE); } #endif /* HAVE_BIG_CORE */ @@ -683,16 +858,16 @@ main(int argc, char *argv[]) if (access(termcap, F_OK) == 0) { /* file exists */ source_file = termcap; - } else if ((tmp_fp = open_tempfile(strcpy(my_tmpname, - "/tmp/XXXXXX"))) - != 0) { - source_file = my_tmpname; - fprintf(tmp_fp, "%s\n", termcap); - fclose(tmp_fp); - tmp_fp = open_input(source_file); - to_remove = source_file; } else { - failed("tmpnam"); + if ((tmp_fp = open_tempfile(my_tmpname)) != 0) { + source_file = my_tmpname; + fprintf(tmp_fp, "%s\n", termcap); + fclose(tmp_fp); + tmp_fp = open_input(source_file, (char *) 0); + to_remove = source_file; + } else { + failed("tmpnam"); + } } } } else { @@ -702,24 +877,28 @@ main(int argc, char *argv[]) _nc_progname, _nc_progname, usage_string); - cleanup(namelst); ExitProgram(EXIT_FAILURE); } } - if (tmp_fp == 0) - tmp_fp = open_input(source_file); + if (tmp_fp == 0) { + tmp_fp = open_input(source_file, my_altfile); + if (!strcmp(source_file, "-")) { + source_file = STDIN_NAME; + } + } - if (infodump) + if (infodump) { dump_init(tversion, smart_defaults ? outform : F_LITERAL, sortmode, width, height, debug_level, formatted); - else if (capdump) + } else if (capdump) { dump_init(tversion, outform, sortmode, width, height, debug_level, FALSE); + } /* parse entries out of the source file */ _nc_set_source(source_file); @@ -736,7 +915,6 @@ main(int argc, char *argv[]) /* do use resolution */ if (check_only || (!infodump && !capdump) || forceresolve) { if (!_nc_resolve_uses2(TRUE, literal) && !check_only) { - cleanup(namelst); ExitProgram(EXIT_FAILURE); } } @@ -831,7 +1009,6 @@ main(int argc, char *argv[]) else fprintf(log_fp, "No entries written\n"); } - cleanup(namelst); ExitProgram(EXIT_SUCCESS); } @@ -1041,7 +1218,7 @@ check_cursor(TERMTYPE *tp) ANDMISSING(parm_left_cursor, cursor_left); ANDMISSING(parm_right_cursor, cursor_right); - /* Given any of a set of cursor movement, the whole set should be present. + /* Given any of a set of cursor movement, the whole set should be present. * Technically this is not true (we could use cursor_address to fill in * unsupported controls), but it is likely. */ @@ -1169,19 +1346,19 @@ check_keypad(TERMTYPE *tp) assert(strlen(show) < (MAX_KP * 4)); switch (kk) { case 0: - strcat(show, " ka1"); + _nc_STRCAT(show, " ka1", sizeof(show)); break; case 1: - strcat(show, " ka3"); + _nc_STRCAT(show, " ka3", sizeof(show)); break; case 2: - strcat(show, " kb2"); + _nc_STRCAT(show, " kb2", sizeof(show)); break; case 3: - strcat(show, " kc1"); + _nc_STRCAT(show, " kc1", sizeof(show)); break; case 4: - strcat(show, " kc3"); + _nc_STRCAT(show, " kc3", sizeof(show)); break; } } @@ -1196,15 +1373,15 @@ check_keypad(TERMTYPE *tp) VALID_STRING(key_c3)) { show[0] = '\0'; if (keypad_index(key_a1) >= 0) - strcat(show, " ka1"); + _nc_STRCAT(show, " ka1", sizeof(show)); if (keypad_index(key_a3) >= 0) - strcat(show, " ka3"); + _nc_STRCAT(show, " ka3", sizeof(show)); if (keypad_index(key_b2) >= 0) - strcat(show, " kb2"); + _nc_STRCAT(show, " kb2", sizeof(show)); if (keypad_index(key_c1) >= 0) - strcat(show, " kc1"); + _nc_STRCAT(show, " kc1", sizeof(show)); if (keypad_index(key_c3) >= 0) - strcat(show, " kc3"); + _nc_STRCAT(show, " kc3", sizeof(show)); if (*show != '\0') _nc_warning("vt100 keypad map incomplete:%s", show); } @@ -1236,6 +1413,72 @@ check_printer(TERMTYPE *tp) ANDMISSING(parm_up_micro, micro_up); } +static bool +uses_SGR_39_49(const char *value) +{ + return (strstr(value, "39;49") != 0 + || strstr(value, "49;39") != 0); +} + +/* + * Check consistency of termcap extensions related to "screen". + */ +static void +check_screen(TERMTYPE *tp) +{ + if (_nc_user_definable) { + int have_XT = tigetflag("XT"); + int have_XM = tigetflag("XM"); + int have_bce = back_color_erase; + bool have_kmouse = FALSE; + bool use_sgr_39_49 = FALSE; + char *name = _nc_first_name(tp->term_names); + + if (!VALID_BOOLEAN(have_bce)) { + have_bce = FALSE; + } + if (!VALID_BOOLEAN(have_XM)) { + have_XM = FALSE; + } + if (!VALID_BOOLEAN(have_XT)) { + have_XT = FALSE; + } + if (VALID_STRING(key_mouse)) { + have_kmouse = !strcmp("\033[M", key_mouse); + } + if (VALID_STRING(orig_colors)) { + use_sgr_39_49 = uses_SGR_39_49(orig_colors); + } else if (VALID_STRING(orig_pair)) { + use_sgr_39_49 = uses_SGR_39_49(orig_pair); + } + + if (have_XM && have_XT) { + _nc_warning("Screen's XT capability conflicts with XM"); + } else if (have_XT + && strstr(name, "screen") != 0 + && strchr(name, '.') != 0) { + _nc_warning("Screen's \"screen\" entries should not have XT set"); + } else if (have_XT) { + if (!have_kmouse && have_bce) { + if (VALID_STRING(key_mouse)) { + _nc_warning("Value of kmous inconsistent with screen's usage"); + } else { + _nc_warning("Expected kmous capability with XT"); + } + } + if (!have_bce && max_colors > 0) + _nc_warning("Expected bce capability with XT"); + if (!use_sgr_39_49 && have_bce && max_colors > 0) + _nc_warning("Expected orig_colors capability with XT to have 39/49 parameters"); + if (VALID_STRING(to_status_line)) + _nc_warning("\"tsl\" capability is redundant, given XT"); + } else { + if (have_kmouse && !have_XM) + _nc_warning("Expected XT to be set, given kmous"); + } + } +} + /* * Returns the expected number of parameters for the given capability. */ @@ -1547,7 +1790,7 @@ static void show_where(unsigned level) { if (_nc_tracing >= DEBUG_LEVEL(level)) { - char my_name[256]; + char my_name[MAX_NAME_SIZE]; _nc_get_type(my_name); _tracef("\"%s\", line %d, '%s'", _nc_get_source(), @@ -1559,6 +1802,55 @@ show_where(unsigned level) #define show_where(level) /* nothing */ #endif +typedef struct { + int keycode; + const char *name; + const char *value; +} NAME_VALUE; + +static NAME_VALUE * +get_fkey_list(TERMTYPE *tp) +{ + NAME_VALUE *result = typeMalloc(NAME_VALUE, NUM_STRINGS(tp) + 1); + const struct tinfo_fkeys *all_fkeys = _nc_tinfo_fkeys; + int used = 0; + int j; + + for (j = 0; all_fkeys[j].code; j++) { + char *a = tp->Strings[all_fkeys[j].offset]; + if (VALID_STRING(a)) { + result[used].keycode = (int) all_fkeys[j].code; + result[used].name = strnames[all_fkeys[j].offset]; + result[used].value = a; + ++used; + } + } +#if NCURSES_XNAMES + for (j = STRCOUNT; j < NUM_STRINGS(tp); ++j) { + const char *name = ExtStrname(tp, j, strnames); + if (*name == 'k') { + result[used].keycode = -1; + result[used].name = name; + result[used].value = tp->Strings[j]; + ++used; + } + } +#endif + result[used].keycode = 0; + return result; +} + +static void +show_fkey_name(NAME_VALUE * data) +{ + if (data->keycode > 0) { + fprintf(stderr, " %s", keyname(data->keycode)); + fprintf(stderr, " (capability \"%s\")", data->name); + } else { + fprintf(stderr, " capability \"%s\"", data->name); + } +} + /* other sanity-checks (things that we don't want in the normal * logic that reads a terminfo entry) */ @@ -1567,7 +1859,6 @@ check_termtype(TERMTYPE *tp, bool literal) { bool conflict = FALSE; unsigned j, k; - char fkeys[STRCOUNT]; /* * A terminal entry may contain more than one keycode assigned to @@ -1575,41 +1866,44 @@ check_termtype(TERMTYPE *tp, bool literal) * return one (the last one assigned). */ if (!(_nc_syntax == SYN_TERMCAP && capdump)) { - memset(fkeys, 0, sizeof(fkeys)); - for (j = 0; _nc_tinfo_fkeys[j].code; j++) { - char *a = tp->Strings[_nc_tinfo_fkeys[j].offset]; + char *check = calloc((size_t) (NUM_STRINGS(tp) + 1), sizeof(char)); + NAME_VALUE *given = get_fkey_list(tp); + + for (j = 0; given[j].keycode; ++j) { + const char *a = given[j].value; bool first = TRUE; - if (!VALID_STRING(a)) - continue; - for (k = j + 1; _nc_tinfo_fkeys[k].code; k++) { - char *b = tp->Strings[_nc_tinfo_fkeys[k].offset]; - if (!VALID_STRING(b) - || fkeys[k]) + + for (k = j + 1; given[k].keycode; k++) { + const char *b = given[k].value; + if (check[k]) continue; if (!_nc_capcmp(a, b)) { - fkeys[j] = 1; - fkeys[k] = 1; + check[j] = 1; + check[k] = 1; if (first) { if (!conflict) { _nc_warning("Conflicting key definitions (using the last)"); conflict = TRUE; } - fprintf(stderr, "... %s is the same as %s", - keyname((int) _nc_tinfo_fkeys[j].code), - keyname((int) _nc_tinfo_fkeys[k].code)); + fprintf(stderr, "..."); + show_fkey_name(given + j); + fprintf(stderr, " is the same as"); + show_fkey_name(given + k); first = FALSE; } else { - fprintf(stderr, ", %s", - keyname((int) _nc_tinfo_fkeys[k].code)); + fprintf(stderr, ", "); + show_fkey_name(given + k); } } } if (!first) fprintf(stderr, "\n"); } + free(given); + free(check); } - for (j = 0; j < NUM_STRINGS(tp); j++) { + for_each_string(j, tp) { char *a = tp->Strings[j]; if (VALID_STRING(a)) check_params(tp, ExtStrname(tp, (int) j, strnames), a); @@ -1620,6 +1914,7 @@ check_termtype(TERMTYPE *tp, bool literal) check_cursor(tp); check_keypad(tp); check_printer(tp); + check_screen(tp); /* * These may be mismatched because the terminal description relies on