_nc_set_writedir(char *dir) { const char *destination; char actual[PATH_MAX]; if (dir == 0 #ifndef USE_ROOT_ENVIRON && use_terminfo_vars() #endif ) dir = getenv("TERMINFO"); if (dir != 0) (void) _nc_tic_dir(dir); destination = _nc_tic_dir(0); if (make_db_root(destination) < 0) { char *home = _nc_home_terminfo(); if (home != 0) { destination = home; if (make_db_root(destination) < 0) _nc_err_abort("%s: permission denied (errno %d)", destination, errno); } } /* * Note: because of this code, this logic should be exercised * *once only* per run. */ #if USE_HASHED_DB make_db_path(actual, destination, sizeof(actual)); #else if (chdir(_nc_tic_dir(destination)) < 0 || getcwd(actual, sizeof(actual)) == 0) _nc_err_abort("%s: not a directory", destination); #endif _nc_keep_tic_dir(strdup(actual)); }
void _nc_set_writedir(char *dir) /* set the write directory for compiled entries */ { const char *destination; if (dir != 0) (void) _nc_tic_dir(dir); else if (getenv("TERMINFO") != NULL) (void) _nc_tic_dir(getenv("TERMINFO")); destination = _nc_tic_dir(0); if (make_directory(destination) < 0) { char *home; /* ncurses extension...fall back on user's private directory */ if ((home = getenv("HOME")) != (char *)NULL && strlen(home) + sizeof(PRIVATE_INFO) <= PATH_MAX) { char *temp = malloc(sizeof(PRIVATE_INFO) + strlen(home)); if (temp == NULL) _nc_err_abort("Out of memory"); (void) sprintf(temp, PRIVATE_INFO, home); destination = temp; if (make_directory(destination) < 0) _nc_err_abort("%s: permission denied (errno %d)", destination, errno); } } /* * Note: because of this code, this logic should be exercised * *once only* per run. */ if (chdir(_nc_tic_dir(destination)) < 0) _nc_err_abort("%s: not a directory", destination); }
_nc_tic_dir(const char *path) { if (!KeepTicDirectory) { if (path != 0) { TicDirectory = path; HaveTicDirectory = TRUE; } else if (!HaveTicDirectory && use_terminfo_vars()) { char *envp; if ((envp = getenv("TERMINFO")) != 0) return _nc_tic_dir(envp); } } return TicDirectory; }
_nc_tic_dir(const char *path) { T(("_nc_tic_dir %s", NonNull(path))); if (!KeepTicDirectory) { if (path != 0) { TicDirectory = path; HaveTicDirectory = TRUE; } else if (HaveTicDirectory == 0) { if (use_terminfo_vars()) { char *envp; if ((envp = getenv("TERMINFO")) != 0) return _nc_tic_dir(envp); } } } return TicDirectory ? TicDirectory : TERMINFO; }
/* * Check for access rights to destination directories * Create any directories which don't exist. * * Note: there's no reason to return the result of make_db_root(), since * this function is called only in instances where that has to succeed. */ static void check_writeable(int code) { static const char dirnames[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; static bool verified[sizeof(dirnames)]; char dir[sizeof(LEAF_FMT)]; char *s = 0; if (code == 0 || (s = strchr(dirnames, code)) == 0) _nc_err_abort("Illegal terminfo subdirectory \"" LEAF_FMT "\"", code); if (verified[s - dirnames]) return; snprintf(dir, sizeof(dir), LEAF_FMT, code); if (make_db_root(dir) < 0) { _nc_err_abort("%s/%s: permission denied", _nc_tic_dir(0), dir); } verified[s - dirnames] = TRUE; }
static int make_db_path(char *dst, const char *src, unsigned limit) { int rc = -1; const char *top = _nc_tic_dir(0); if (src == top || _nc_is_abs_path(src)) { if (strlen(src) + 1 <= limit) { (void) strcpy(dst, src); rc = 0; } } else { if (strlen(top) + strlen(src) + 2 <= limit) { (void) sprintf(dst, "%s/%s", top, src); rc = 0; } } #if USE_HASHED_DB if (rc == 0) { if (_nc_is_dir_path(dst)) { rc = -1; } else { static const char suffix[] = DBM_SUFFIX; unsigned have = strlen(dst); unsigned need = strlen(suffix); if (have > need && strcmp(dst + have - need, suffix)) { if (have + need <= limit) strcat(dst, suffix); else rc = -1; } } } #endif return rc; }
_nc_write_entry(TERMTYPE *const tp) { #if USE_HASHED_DB char buffer[MAX_ENTRY_SIZE + 1]; unsigned limit = sizeof(buffer); unsigned offset = 0; #else /* !USE_HASHED_DB */ struct stat statbuf; char filename[PATH_MAX]; char linkname[PATH_MAX]; #if USE_SYMLINKS char symlinkname[PATH_MAX]; #if !HAVE_LINK #undef HAVE_LINK #define HAVE_LINK 1 #endif #endif /* USE_SYMLINKS */ static int call_count; static time_t start_time; /* time at start of writes */ #endif /* USE_HASHED_DB */ char name_list[MAX_TERMINFO_LENGTH]; char *first_name, *other_names; char *ptr; assert(strlen(tp->term_names) != 0); assert(strlen(tp->term_names) < sizeof(name_list)); (void) strlcpy(name_list, tp->term_names, sizeof(name_list)); DEBUG(7, ("Name list = '%s'", name_list)); first_name = name_list; ptr = &name_list[strlen(name_list) - 1]; other_names = ptr + 1; while (ptr > name_list && *ptr != '|') ptr--; if (ptr != name_list) { *ptr = '\0'; for (ptr = name_list; *ptr != '\0' && *ptr != '|'; ptr++) continue; if (*ptr == '\0') other_names = ptr; else { *ptr = '\0'; other_names = ptr + 1; } } DEBUG(7, ("First name = '%s'", first_name)); DEBUG(7, ("Other names = '%s'", other_names)); _nc_set_type(first_name); #if USE_HASHED_DB if (write_object(tp, buffer + 1, &offset, limit - 1) != ERR) { DB *capdb = _nc_db_open(_nc_tic_dir(0), TRUE); DBT key, data; if (capdb != 0) { buffer[0] = 0; memset(&key, 0, sizeof(key)); key.data = tp->term_names; key.size = strlen(tp->term_names); memset(&data, 0, sizeof(data)); data.data = buffer; data.size = offset + 1; _nc_db_put(capdb, &key, &data); buffer[0] = 2; key.data = name_list; key.size = strlen(name_list); strlcpy(buffer + 1, tp->term_names, sizeof(buffer) - 1); data.size = strlen(tp->term_names) + 1; _nc_db_put(capdb, &key, &data); while (*other_names != '\0') { ptr = other_names++; while (*other_names != '|' && *other_names != '\0') other_names++; if (*other_names != '\0') *(other_names++) = '\0'; key.data = ptr; key.size = strlen(ptr); _nc_db_put(capdb, &key, &data); } _nc_db_close(capdb); } } #else /* !USE_HASHED_DB */ if (call_count++ == 0) { start_time = 0; } if (strlen(first_name) >= sizeof(filename) - 3) _nc_warning("terminal name too long."); snprintf(filename, sizeof(filename), LEAF_FMT "/%s", first_name[0], first_name); /* * Has this primary name been written since the first call to * write_entry()? If so, the newer write will step on the older, * so warn the user. */ if (start_time > 0 && stat(filename, &statbuf) >= 0 && statbuf.st_mtime >= start_time) { _nc_warning("name multiply defined."); } check_writeable(first_name[0]); write_file(filename, tp); if (start_time == 0) { if (stat(filename, &statbuf) < 0 || (start_time = statbuf.st_mtime) == 0) { _nc_syserr_abort("error obtaining time from %s/%s", _nc_tic_dir(0), filename); } } while (*other_names != '\0') { ptr = other_names++; assert(ptr < buffer + sizeof(buffer) - 1); while (*other_names != '|' && *other_names != '\0') other_names++; if (*other_names != '\0') *(other_names++) = '\0'; if (strlen(ptr) > sizeof(linkname) - 3) { _nc_warning("terminal alias %s too long.", ptr); continue; } if (strchr(ptr, '/') != 0) { _nc_warning("cannot link alias %s.", ptr); continue; } check_writeable(ptr[0]); snprintf(linkname, sizeof(linkname), LEAF_FMT "/%s", ptr[0], ptr); if (strcmp(filename, linkname) == 0) { _nc_warning("self-synonym ignored"); } else if (stat(linkname, &statbuf) >= 0 && statbuf.st_mtime < start_time) { _nc_warning("alias %s multiply defined.", ptr); } else if (_nc_access(linkname, W_OK) == 0) #if HAVE_LINK { int code; #if USE_SYMLINKS strlcpy(symlinkname, "../", sizeof(symlinkname)); strlcat(symlinkname, filename, sizeof(symlinkname)); #endif /* USE_SYMLINKS */ #if HAVE_REMOVE code = remove(linkname); #else code = unlink(linkname); #endif if (code != 0 && errno == ENOENT) code = 0; #if USE_SYMLINKS if (symlink(symlinkname, linkname) < 0) #else if (link(filename, linkname) < 0) #endif /* USE_SYMLINKS */ { /* * If there wasn't anything there, and we cannot * link to the target because it is the same as the * target, then the source must be on a filesystem * that uses caseless filenames, such as Win32, etc. */ if (code == 0 && errno == EEXIST) _nc_warning("can't link %s to %s", filename, linkname); else if (code == 0 && (errno == EPERM || errno == ENOENT)) write_file(linkname, tp); else { #if MIXEDCASE_FILENAMES _nc_syserr_abort("can't link %s to %s", filename, linkname); #else _nc_warning("can't link %s to %s (errno=%d)", filename, linkname, errno); #endif } } else { DEBUG(1, ("Linked %s", linkname)); } } #else /* just make copies */ write_file(linkname, tp); #endif /* HAVE_LINK */ } #endif /* USE_HASHED_DB */ }
_nc_keep_tic_dir(const char *path) { _nc_tic_dir(path); KeepTicDirectory = TRUE; }
void _nc_write_entry(TERMTYPE * const tp) { struct stat statbuf; char name_list[MAX_TERMINFO_LENGTH]; char *first_name, *other_names; char *ptr; char filename[PATH_MAX]; char linkname[PATH_MAX]; #if USE_SYMLINKS char symlinkname[PATH_MAX]; #if !HAVE_LINK #undef HAVE_LINK #define HAVE_LINK 1 #endif #endif /* USE_SYMLINKS */ static int call_count; static time_t start_time; /* time at start of writes */ if (call_count++ == 0) { start_time = 0; } (void) strcpy(name_list, tp->term_names); DEBUG(7, ("Name list = '%s'", name_list)); first_name = name_list; ptr = &name_list[strlen(name_list) - 1]; other_names = ptr + 1; while (ptr > name_list && *ptr != '|') ptr--; if (ptr != name_list) { *ptr = '\0'; for (ptr = name_list; *ptr != '\0' && *ptr != '|'; ptr++) continue; if (*ptr == '\0') other_names = ptr; else { *ptr = '\0'; other_names = ptr + 1; } } DEBUG(7, ("First name = '%s'", first_name)); DEBUG(7, ("Other names = '%s'", other_names)); _nc_set_type(first_name); if (strlen(first_name) > sizeof(filename) - 3) _nc_warning("terminal name too long."); sprintf(filename, "%c/%s", first_name[0], first_name); /* * Has this primary name been written since the first call to * write_entry()? If so, the newer write will step on the older, * so warn the user. */ if (start_time > 0 && stat(filename, &statbuf) >= 0 && statbuf.st_mtime >= start_time) { _nc_warning("name multiply defined."); } check_writeable(first_name[0]); write_file(filename, tp); if (start_time == 0) { if (stat(filename, &statbuf) < 0 || (start_time = statbuf.st_mtime) == 0) { _nc_syserr_abort("error obtaining time from %s/%s", _nc_tic_dir(0), filename); } } while (*other_names != '\0') { ptr = other_names++; while (*other_names != '|' && *other_names != '\0') other_names++; if (*other_names != '\0') *(other_names++) = '\0'; if (strlen(ptr) > sizeof(linkname) - 3) { _nc_warning("terminal alias %s too long.", ptr); continue; } if (strchr(ptr, '/') != 0) { _nc_warning("cannot link alias %s.", ptr); continue; } check_writeable(ptr[0]); sprintf(linkname, "%c/%s", ptr[0], ptr); if (strcmp(filename, linkname) == 0) { _nc_warning("self-synonym ignored"); } else if (stat(linkname, &statbuf) >= 0 && statbuf.st_mtime < start_time) { _nc_warning("alias %s multiply defined.", ptr); } else if (_nc_access(linkname, W_OK) == 0) #if HAVE_LINK { int code; #if USE_SYMLINKS strcpy(symlinkname, "../"); strncat(symlinkname, filename, sizeof(symlinkname) - 4); symlinkname[sizeof(symlinkname) - 1] = '\0'; #endif /* USE_SYMLINKS */ #if HAVE_REMOVE code = remove(linkname); #else code = unlink(linkname); #endif if (code != 0 && errno == ENOENT) code = 0; #if USE_SYMLINKS if (symlink(symlinkname, linkname) < 0) #else if (link(filename, linkname) < 0) #endif /* USE_SYMLINKS */ { /* * If there wasn't anything there, and we cannot * link to the target because it is the same as the * target, then the source must be on a filesystem * that uses caseless filenames, such as Win32, etc. */ if (code == 0 && errno == EEXIST) _nc_warning("can't link %s to %s", filename, linkname); else if (code == 0 && (errno == EPERM || errno == ENOENT)) write_file(linkname, tp); else { #if MIXEDCASE_FILENAMES _nc_syserr_abort("can't link %s to %s", filename, linkname); #else _nc_warning("can't link %s to %s (errno=%d)", filename, linkname, errno); #endif } } else { DEBUG(1, ("Linked %s", linkname)); } } #else /* just make copies */ write_file(linkname, tp); #endif /* HAVE_LINK */ } }
_nc_next_db(DBDIRS * state, int *offset) { const char *result; char *envp; while (*state < dbdLAST) { DBDIRS next = (DBDIRS) ((int) (*state) + 1); result = 0; switch (*state) { case dbdTIC: if (HaveTicDirectory) result = _nc_tic_dir(0); break; #if USE_DATABASE case dbdEnvOnce: if (use_terminfo_vars()) { if ((envp = getenv("TERMINFO")) != 0) result = _nc_tic_dir(envp); } break; case dbdHome: if (use_terminfo_vars()) { result = _nc_home_terminfo(); } break; case dbdEnvList: if (use_terminfo_vars()) { if ((result = NEXT_DBD(getenv("TERMINFO_DIRS"), offset)) != 0) next = *state; } break; case dbdCfgList: #ifdef TERMINFO_DIRS if ((result = NEXT_DBD(TERMINFO_DIRS, offset)) != 0) next = *state; #endif break; case dbdCfgOnce: #ifndef TERMINFO_DIRS result = TERMINFO; #endif break; #endif /* USE_DATABASE */ #if USE_TERMCAP case dbdEnvOnce2: if (use_terminfo_vars()) { if ((envp = getenv("TERMCAP")) != 0) result = _nc_tic_dir(envp); } break; case dbdEnvList2: if (use_terminfo_vars()) { if ((result = NEXT_DBD(getenv("TERMPATH"), offset)) != 0) next = *state; } break; case dbdCfgList2: if ((result = NEXT_DBD(TERMPATH, offset)) != 0) next = *state; break; #endif /* USE_TERMCAP */ case dbdLAST: break; } if (*state != next) { *state = next; *offset = 0; _nc_last_db(); } if (result != 0) { return result; } } return 0; }