X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=progs%2Ftic.c;h=4881d655322b7e4674111430e854a253b4ada21e;hp=be0a36356c2209694d82ce078f261b592e4092b0;hb=18b7b94579f108e649ece3fb12165833dbf5d95a;hpb=8e25fff6a5f576b6dc35eb02b9783fa58680d07b diff --git a/progs/tic.c b/progs/tic.c index be0a3635..4881d655 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.151 2011/07/30 21:49:32 tom Exp $") +MODULE_ID("$Id: tic.c,v 1.165 2012/03/24 22:07:10 tom Exp $") + +#define STDIN_NAME "" const char *_nc_progname = "tic"; @@ -69,6 +73,7 @@ static const char usage_string[] = "\ 1\ a\ C\ +D\ c\ f\ G\ @@ -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 *fp = fopen(filename, "r"); + 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; 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; @@ -489,6 +659,7 @@ main(int argc, char *argv[]) int sortmode = S_TERMINFO; /* sort_mode */ int width = 60; + int height = 65535; bool formatted = FALSE; /* reformat complex strings? */ bool literal = FALSE; /* suppress post-processing? */ int numbers = 0; /* format "%'char'" to/from "%{number}" */ @@ -524,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': @@ -534,22 +705,40 @@ main(int argc, char *argv[]) width = (width * 10) + (this_opt - '0'); break; default: - if (this_opt != '1') + switch (this_opt) { + case '0': + last_opt = this_opt; + width = 65535; + height = 1; + break; + case '1': + last_opt = this_opt; + width = 0; + break; + default: usage(); - last_opt = this_opt; - width = 0; + } } continue; } switch (this_opt) { case 'K': _nc_strict_bsd = 1; - /* FALLTHRU */ + /* the initial version of -K in 20110730 fell-thru here, but the + * same flag is useful when reading sources -TD + */ + break; case 'C': capdump = TRUE; 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; @@ -625,7 +814,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) { @@ -645,7 +834,8 @@ main(int argc, char *argv[]) */ if (namelst && (!infodump && !capdump)) { (void) fprintf(stderr, - "Sorry, -e can't be used without -I or -C\n"); + "%s: Sorry, -e can't be used without -I or -C\n", + _nc_progname); cleanup(namelst); ExitProgram(EXIT_FAILURE); } @@ -670,16 +860,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 { @@ -694,19 +884,24 @@ main(int argc, char *argv[]) } } - 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, debug_level, formatted); - else if (capdump) + sortmode, width, height, debug_level, formatted); + } else if (capdump) { dump_init(tversion, outform, - sortmode, width, debug_level, FALSE); + sortmode, width, height, debug_level, FALSE); + } /* parse entries out of the source file */ _nc_set_source(source_file); @@ -1156,19 +1351,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; } } @@ -1183,15 +1378,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); }