use shell_words::ParseError; use std::env; /// If we use a pager, this enum tells us from where we were told to use it. #[derive(Debug, PartialEq)] pub(crate) enum PagerSource { /// From --config Config, /// From the env var BAT_PAGER EnvVarBatPager, /// From the env var PAGER EnvVarPager, /// No pager was specified, default is used Default, } /// We know about some pagers, for example 'less'. This is a list of all pagers we know about #[derive(Debug, PartialEq)] pub(crate) enum PagerKind { /// bat Bat, /// less Less, /// more More, /// most Most, /// A pager we don't know about Unknown, } impl PagerKind { fn from_bin(bin: &str) -> PagerKind { use std::path::Path; // Set to `less` by default on most Linux distros. let pager_bin = Path::new(bin).file_stem(); // The name of the current running binary. Normally `bat` but sometimes // `batcat` for compatibility reasons. let current_bin = env::args_os().next(); // Check if the current running binary is set to be our pager. let is_current_bin_pager = current_bin .map(|s| Path::new(&s).file_stem() == pager_bin) .unwrap_or(false); match pager_bin.map(|s| s.to_string_lossy()).as_deref() { Some("less") => PagerKind::Less, Some("more") => PagerKind::More, Some("most") => PagerKind::Most, _ if is_current_bin_pager => PagerKind::Bat, _ => PagerKind::Unknown, } } } /// A pager such as 'less', and from where we got it. #[derive(Debug)] pub(crate) struct Pager { /// The pager binary pub bin: String, /// The pager binary arguments (that we might tweak) pub args: Vec, /// What pager this is pub kind: PagerKind, /// From where this pager comes pub source: PagerSource, } impl Pager { fn new(bin: &str, args: &[String], kind: PagerKind, source: PagerSource) -> Pager { Pager { bin: String::from(bin), args: args.to_vec(), kind, source, } } } /// Returns what pager to use, after looking at both config and environment variables. pub(crate) fn get_pager(config_pager: Option<&str>) -> Result, ParseError> { let bat_pager = env::var("BAT_PAGER"); let pager = env::var("PAGER"); let (cmd, source) = match (config_pager, &bat_pager, &pager) { (Some(config_pager), _, _) => (config_pager, PagerSource::Config), (_, Ok(bat_pager), _) => (bat_pager.as_str(), PagerSource::EnvVarBatPager), (_, _, Ok(pager)) => (pager.as_str(), PagerSource::EnvVarPager), _ => ("less", PagerSource::Default), }; let parts = shell_words::split(cmd)?; match parts.split_first() { Some((bin, args)) => { let kind = PagerKind::from_bin(bin); let use_less_instead = if source == PagerSource::EnvVarPager { // 'more' and 'most' do not supports colors; automatically use // 'less' instead if the problematic pager came from the // generic PAGER env var. // If PAGER=bat, silently use 'less' instead to prevent // recursion. // Never silently use 'less' if BAT_PAGER or --pager has been // specified. matches!(kind, PagerKind::More | PagerKind::Most | PagerKind::Bat) } else { false }; Ok(Some(if use_less_instead { let no_args = vec![]; Pager::new("less", &no_args, PagerKind::Less, PagerSource::EnvVarPager) } else { Pager::new(bin, args, kind, source) })) } None => Ok(None), } }