X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=progs%2Ftic.c;h=53fa7225a65285cc6979c3037b08499fa4daccd1;hp=23282e08d3c49e471331054cc56c0a797c6a6142;hb=ba39fbc2e0cee4681395df4079d9e61c27262132;hpb=40cf934fff2d2790c060619e3a29bd54c20994b0 diff --git a/progs/tic.c b/progs/tic.c index 23282e08..53fa7225 100644 --- a/progs/tic.c +++ b/progs/tic.c @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright (c) 1998-2009,2010 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,10 @@ #include #include +#include #include -MODULE_ID("$Id: tic.c,v 1.140 2010/01/02 22:54:01 tom Exp $") +MODULE_ID("$Id: tic.c,v 1.162 2012/02/22 23:59:45 tom Exp $") const char *_nc_progname = "tic"; @@ -69,11 +71,13 @@ static const char usage_string[] = "\ 1\ a\ C\ +D\ c\ f\ G\ g\ I\ +K\ L\ N\ r\ @@ -133,7 +137,9 @@ usage(void) #if NCURSES_XNAMES " -a retain commented-out capabilities (sets -x also)", #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", @@ -173,7 +179,7 @@ usage(void) #define L_BRACE '{' #define R_BRACE '}' -#define S_QUOTE '\''; +#define S_QUOTE '\'' static void write_it(ENTRY * ep) @@ -216,12 +222,12 @@ write_it(ENTRY * ep) } *d = 0; if (strlen(result) < strlen(s)) - strcpy(s, result); + _nc_STRCPY(s, result, strlen(s) + 1); } } _nc_set_type(_nc_first_name(ep->tterm.term_names)); - _nc_curr_line = ep->startline; + _nc_curr_line = (int) ep->startline; _nc_write_entry(&ep->tterm); } @@ -308,7 +314,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'; } @@ -339,20 +345,23 @@ put_translate(int c) static char * stripped(char *src) { + char *dst = 0; + while (isspace(UChar(*src))) src++; + if (*src != '\0') { - char *dst; size_t len; - if ((dst = strdup(src)) == NULL) + if ((dst = strdup(src)) == NULL) { failed("strdup"); - len = strlen(dst); - while (--len != 0 && isspace(UChar(dst[len]))) - dst[len] = '\0'; - return dst; + } else { + len = strlen(dst); + while (--len != 0 && isspace(UChar(dst[len]))) + dst[len] = '\0'; + } } - return 0; + return dst; } static FILE * @@ -468,11 +477,113 @@ open_tempfile(char *name) return result; } +static const char * +valid_db_path(const char *nominal) +{ + 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 + 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]; - int v_opt = -1, debug_level; + int v_opt = -1; + unsigned debug_level; int smart_defaults = TRUE; char *termcap; ENTRY *qp; @@ -483,6 +594,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}" */ @@ -510,6 +622,7 @@ main(int argc, char *argv[]) #if NCURSES_XNAMES use_extended_names(FALSE); #endif + _nc_strict_bsd = 0; /* * Processing arguments is a little complicated, since someone made a @@ -517,7 +630,7 @@ main(int argc, char *argv[]) * be optional. */ while ((this_opt = getopt(argc, argv, - "0123456789CILNR:TUVace:fGgo:rstvwx")) != -1) { + "0123456789CDIKLNR:TUVace:fGgo:rstvwx")) != -1) { if (isdigit(this_opt)) { switch (last_opt) { case 'v': @@ -527,19 +640,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; + /* 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; @@ -615,7 +749,7 @@ main(int argc, char *argv[]) last_opt = this_opt; } - debug_level = (v_opt > 0) ? v_opt : (v_opt == 0); + debug_level = VtoTrace(v_opt); set_trace_level(debug_level); if (_nc_tracing) { @@ -635,7 +769,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); } @@ -660,16 +795,19 @@ 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"); + _nc_STRCPY(my_tmpname, + "/tmp/XXXXXX", + sizeof(my_tmpname)); + 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); + to_remove = source_file; + } else { + failed("tmpnam"); + } } } } else { @@ -692,11 +830,11 @@ main(int argc, char *argv[]) smart_defaults ? outform : F_LITERAL, - sortmode, width, debug_level, formatted); + 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); @@ -747,7 +885,7 @@ main(int argc, char *argv[]) for_entry_list(qp) { if (matches(namelst, qp->tterm.term_names)) { - int j = qp->cend - qp->cstart; + long j = qp->cend - qp->cstart; int len = 0; /* this is in case infotocap() generates warnings */ @@ -761,9 +899,10 @@ main(int argc, char *argv[]) put_translate(fgetc(tmp_fp)); } + repair_acsc(&qp->tterm); dump_entry(&qp->tterm, suppress_untranslatable, limited, numbers, NULL); - for (j = 0; j < (int) qp->nuses; j++) + for (j = 0; j < (long) qp->nuses; j++) dump_uses(qp->uses[j].name, !capdump); len = show_entry(); if (debug_level != 0 && !limited) @@ -911,22 +1050,172 @@ keypad_final(const char *string) return result; } -static int +static long keypad_index(const char *string) { char *test; const char *list = "PQRSwxymtuvlqrsPpn"; /* app-keypad except "Enter" */ int ch; - int result = -1; + long result = -1; if ((ch = keypad_final(string)) != '\0') { test = strchr(list, ch); if (test != 0) - result = (test - list); + result = (long) (test - list); } return result; } +/* + * list[] is down, up, left, right + * "left" may be ^H rather than \E[D + * "down" may be ^J rather than \E[B + * But up/right are generally consistently escape sequences for ANSI terminals. + */ +static void +check_ansi_cursor(char *list[4]) +{ + int j, k; + int want; + size_t prefix = 0; + size_t suffix; + bool skip[4]; + bool repeated = FALSE; + + for (j = 0; j < 4; ++j) { + skip[j] = FALSE; + for (k = 0; k < j; ++k) { + if (j != k + && !strcmp(list[j], list[k])) { + char *value = _nc_tic_expand(list[k], TRUE, 0); + _nc_warning("repeated cursor control %s\n", value); + repeated = TRUE; + } + } + } + if (!repeated) { + char *up = list[1]; + + if (UChar(up[0]) == '\033') { + if (up[1] == '[') { + prefix = 2; + } else { + prefix = 1; + } + } else if (UChar(up[0]) == UChar('\233')) { + prefix = 1; + } + if (prefix) { + suffix = prefix; + while (up[suffix] && isdigit(UChar(up[suffix]))) + ++suffix; + } + if (prefix && up[suffix] == 'A') { + skip[1] = TRUE; + if (!strcmp(list[0], "\n")) + skip[0] = TRUE; + if (!strcmp(list[2], "\b")) + skip[2] = TRUE; + + for (j = 0; j < 4; ++j) { + if (skip[j] || strlen(list[j]) == 1) + continue; + if (memcmp(list[j], up, prefix)) { + char *value = _nc_tic_expand(list[j], TRUE, 0); + _nc_warning("inconsistent prefix for %s\n", value); + continue; + } + if (strlen(list[j]) < suffix) { + char *value = _nc_tic_expand(list[j], TRUE, 0); + _nc_warning("inconsistent length for %s, expected %d\n", + value, (int) suffix + 1); + continue; + } + want = "BADC"[j]; + if (list[j][suffix] != want) { + char *value = _nc_tic_expand(list[j], TRUE, 0); + _nc_warning("inconsistent suffix for %s, expected %c, have %c\n", + value, want, list[j][suffix]); + } + } + } + } +} + +#define EXPECTED(name) if (!PRESENT(name)) _nc_warning("expected " #name) + +static void +check_cursor(TERMTYPE *tp) +{ + int count; + char *list[4]; + + /* if we have a parameterized form, then the non-parameterized is easy */ + ANDMISSING(parm_down_cursor, cursor_down); + ANDMISSING(parm_up_cursor, cursor_up); + 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. + * Technically this is not true (we could use cursor_address to fill in + * unsupported controls), but it is likely. + */ + count = 0; + if (PRESENT(parm_down_cursor)) { + list[count++] = parm_down_cursor; + } + if (PRESENT(parm_up_cursor)) { + list[count++] = parm_up_cursor; + } + if (PRESENT(parm_left_cursor)) { + list[count++] = parm_left_cursor; + } + if (PRESENT(parm_right_cursor)) { + list[count++] = parm_right_cursor; + } + if (count == 4) { + check_ansi_cursor(list); + } else if (count != 0) { + EXPECTED(parm_down_cursor); + EXPECTED(parm_up_cursor); + EXPECTED(parm_left_cursor); + EXPECTED(parm_right_cursor); + } + + count = 0; + if (PRESENT(cursor_down)) { + list[count++] = cursor_down; + } + if (PRESENT(cursor_up)) { + list[count++] = cursor_up; + } + if (PRESENT(cursor_left)) { + list[count++] = cursor_left; + } + if (PRESENT(cursor_right)) { + list[count++] = cursor_right; + } + if (count == 4) { + check_ansi_cursor(list); + } else if (count != 0) { + count = 0; + if (PRESENT(cursor_down) && strcmp(cursor_down, "\n")) + ++count; + if (PRESENT(cursor_left) && strcmp(cursor_left, "\b")) + ++count; + if (PRESENT(cursor_up) && strlen(cursor_up) > 1) + ++count; + if (PRESENT(cursor_right) && strlen(cursor_right) > 1) + ++count; + if (count) { + EXPECTED(cursor_down); + EXPECTED(cursor_up); + EXPECTED(cursor_left); + EXPECTED(cursor_right); + } + } +} + #define MAX_KP 5 /* * Do a quick sanity-check for vt100-style keypads to see if the 5-key keypad @@ -943,11 +1232,11 @@ check_keypad(TERMTYPE *tp) VALID_STRING(key_c1) && VALID_STRING(key_c3)) { char final[MAX_KP + 1]; - int list[MAX_KP]; + long list[MAX_KP]; int increase = 0; int j, k, kk; - int last; - int test; + long last; + long test; final[0] = keypad_final(key_a1); final[1] = keypad_final(key_a3); @@ -995,19 +1284,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; } } @@ -1022,20 +1311,46 @@ 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); } } +static void +check_printer(TERMTYPE *tp) +{ + PAIRED(enter_doublewide_mode, exit_doublewide_mode); + PAIRED(enter_italics_mode, exit_italics_mode); + PAIRED(enter_leftward_mode, exit_leftward_mode); + PAIRED(enter_micro_mode, exit_micro_mode); + PAIRED(enter_shadow_mode, exit_shadow_mode); + PAIRED(enter_subscript_mode, exit_subscript_mode); + PAIRED(enter_superscript_mode, exit_superscript_mode); + PAIRED(enter_upward_mode, exit_upward_mode); + + ANDMISSING(start_char_set_def, stop_char_set_def); + + /* if we have a parameterized form, then the non-parameterized is easy */ + ANDMISSING(set_bottom_margin_parm, set_bottom_margin); + ANDMISSING(set_left_margin_parm, set_left_margin); + ANDMISSING(set_right_margin_parm, set_right_margin); + ANDMISSING(set_top_margin_parm, set_top_margin); + + ANDMISSING(parm_down_micro, micro_down); + ANDMISSING(parm_left_micro, micro_left); + ANDMISSING(parm_right_micro, micro_right); + ANDMISSING(parm_up_micro, micro_up); +} + /* * Returns the expected number of parameters for the given capability. */ @@ -1349,7 +1664,7 @@ show_where(unsigned level) if (_nc_tracing >= DEBUG_LEVEL(level)) { char my_name[256]; _nc_get_type(my_name); - fprintf(stderr, "\"%s\", line %d, '%s' ", + _tracef("\"%s\", line %d, '%s'", _nc_get_source(), _nc_curr_line, my_name); } @@ -1412,12 +1727,14 @@ check_termtype(TERMTYPE *tp, bool literal) 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); + check_params(tp, ExtStrname(tp, (int) j, strnames), a); } check_acs(tp); check_colors(tp); + check_cursor(tp); check_keypad(tp); + check_printer(tp); /* * These may be mismatched because the terminal description relies on @@ -1438,6 +1755,11 @@ check_termtype(TERMTYPE *tp, bool literal) ANDMISSING(change_scroll_region, save_cursor); ANDMISSING(change_scroll_region, restore_cursor); + /* + * If we can clear tabs, we should be able to initialize them. + */ + ANDMISSING(clear_all_tabs, set_tab); + if (PRESENT(set_attributes)) { char *zero = 0;