Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: always search ~/.terminfo for terminfo files #122

Merged
merged 1 commit into from
Dec 14, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 37 additions & 31 deletions src/terminfo/searcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,61 +16,67 @@ use std::env;
use std::fs;
use std::path::PathBuf;

// The default terminfo location should be /usr/lib/terminfo but that's not guaranteed, so we check
// a few more locations. See https://tldp.org/HOWTO/Text-Terminal-HOWTO-16.html#ss16.2
const DEFAULT_LOCATIONS: &[&str] = &[
"/etc/terminfo",
"/usr/share/terminfo",
"/usr/lib/terminfo",
"/lib/terminfo",
#[cfg(target_os = "haiku")]
"/boot/system/data/terminfo",
];

/// Return path to database entry for `term`
pub fn get_dbpath_for_term(term: &str) -> Option<PathBuf> {
let mut dirs_to_search = Vec::new();
let mut default_locations = DEFAULT_LOCATIONS.iter().map(PathBuf::from);
let first_char = match term.chars().next() {
Some(c) => c,
None => return None,
};

// Find search directory
// The terminfo manual says:
//
// > If the environment variable TERMINFO is set, it is interpreted
// > as the pathname of a directory containing the compiled description
// > you are working on. Only that directory is searched.
//
// However, the ncurses manual says:
// From the manual.
//
// > If the environment variable TERMINFO is defined, any program using
// > curses checks for a local terminal definition before checking in
// > the standard place.
//
// Given that ncurses is the defacto standard, we follow the ncurses manual.
// > The environment variable TERMINFO is checked first, for a terminal
// > database containing the terminal description.
if let Some(dir) = env::var_os("TERMINFO") {
dirs_to_search.push(PathBuf::from(dir));
}

// > Next, ncurses looks in $HOME/.terminfo for a compiled description.
if let Some(mut homedir) = home::home_dir() {
homedir.push(".terminfo");
dirs_to_search.push(homedir)
}

// > Next, if the environment variable TERMINFO_DIRS is set, ncurses interprets
// > the contents of that variable as a list of colon-separated pathnames of
// > terminal databases to be searched.
// >
// > An empty pathname (i.e., if the variable begins or ends with a
// > colon, or contains adjacent colons) is interpreted as the system location
// > /usr/share/terminfo.
if let Ok(dirs) = env::var("TERMINFO_DIRS") {
for i in dirs.split(':') {
if i.is_empty() {
dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
dirs_to_search.extend(&mut default_locations);
} else {
dirs_to_search.push(PathBuf::from(i));
}
}
} else {
// Found nothing in TERMINFO_DIRS, use the default paths:
// According to /etc/terminfo/README, after looking at
// ~/.terminfo, ncurses will search /etc/terminfo, then
// /lib/terminfo, and eventually /usr/share/terminfo.
// On Haiku the database can be found at /boot/system/data/terminfo
if let Some(mut homedir) = home::home_dir() {
homedir.push(".terminfo");
dirs_to_search.push(homedir)
}

dirs_to_search.push(PathBuf::from("/etc/terminfo"));
dirs_to_search.push(PathBuf::from("/lib/terminfo"));
dirs_to_search.push(PathBuf::from("/usr/share/terminfo"));
dirs_to_search.push(PathBuf::from("/boot/system/data/terminfo"));
}

// > Finally, ncurses searches these compiled-in locations...
//
// NOTE: We only append these to `dirs_to_search` once. If we've already added these
// directories as specified in `TERMINFO_DIRS`, this operation will be a no-op.
dirs_to_search.extend(&mut default_locations);

// Look for the terminal in all of the search directories
for mut p in dirs_to_search {
if fs::metadata(&p).is_ok() {
p.push(&first_char.to_string());
p.push(first_char.to_string());
p.push(term);
if fs::metadata(&p).is_ok() {
return Some(p);
Expand All @@ -80,7 +86,7 @@ pub fn get_dbpath_for_term(term: &str) -> Option<PathBuf> {

// on some installations the dir is named after the hex of the char
// (e.g. OS X)
p.push(&format!("{:x}", first_char as usize));
p.push(format!("{:x}", first_char as usize));
p.push(term);
if fs::metadata(&p).is_ok() {
return Some(p);
Expand Down
Loading