bat/src/output.rs

120 lines
3.6 KiB
Rust
Raw Normal View History

use std::env;
use std::ffi::OsString;
2018-05-21 14:59:42 +02:00
use std::io::{self, Write};
use std::path::PathBuf;
2018-05-21 14:59:42 +02:00
use std::process::{Child, Command, Stdio};
2018-10-17 20:10:29 +02:00
use shell_words;
2018-08-22 22:29:12 +02:00
use app::PagingMode;
use errors::*;
2018-05-21 14:59:42 +02:00
pub enum OutputType {
Pager(Child),
Stdout(io::Stdout),
}
impl OutputType {
2018-10-21 12:52:29 +02:00
pub fn from_mode(mode: PagingMode, pager: Option<&str>) -> Result<Self> {
2018-05-21 14:59:42 +02:00
use self::PagingMode::*;
2018-10-17 20:21:58 +02:00
Ok(match mode {
2018-10-21 12:52:29 +02:00
Always => OutputType::try_pager(false, pager)?,
QuitIfOneScreen => OutputType::try_pager(true, pager)?,
2018-05-21 14:59:42 +02:00
_ => OutputType::stdout(),
2018-10-17 20:21:58 +02:00
})
2018-05-21 14:59:42 +02:00
}
/// Try to launch the pager. Fall back to stdout in case of errors.
2018-10-21 12:52:29 +02:00
fn try_pager(quit_if_one_screen: bool, pager_from_config: Option<&str>) -> Result<Self> {
let pager_from_env =
env::var("BAT_PAGER").or_else(|_| env::var("PAGER").map(add_default_flags_to_less));
2018-11-05 21:41:51 +01:00
let pager = pager_from_config
2018-10-21 13:15:17 +02:00
.map(|p| p.to_string())
2018-10-21 12:52:29 +02:00
.or(pager_from_env.ok())
.unwrap_or(String::from("less"));
2018-10-17 20:21:58 +02:00
let pagerflags = shell_words::split(&pager)
.chain_err(|| "Could not parse (BAT_)PAGER environment variable.")?;
match pagerflags.split_first() {
Some((pager_name, mut args)) => {
let mut pager_path = PathBuf::from(pager_name);
if pager_path.file_stem() == Some(&OsString::from("bat")) {
pager_path = PathBuf::from("less");
args = &[];
}
2018-11-05 21:41:51 +01:00
let is_less = pager_path.file_stem() == Some(&OsString::from("less"));
let mut process = if is_less {
let mut p = Command::new(&pager_path);
2018-10-17 21:12:23 +02:00
if args.is_empty() {
p.args(vec!["--RAW-CONTROL-CHARS", "--no-init"]);
if quit_if_one_screen {
p.arg("--quit-if-one-screen");
}
}
p.env("LESSCHARSET", "UTF-8");
p
} else {
Command::new(&pager_path)
};
Ok(process
.args(args)
.stdin(Stdio::piped())
.spawn()
.map(OutputType::Pager)
.unwrap_or_else(|_| OutputType::stdout()))
}
None => Ok(OutputType::stdout()),
}
2018-05-21 14:59:42 +02:00
}
fn stdout() -> Self {
OutputType::Stdout(io::stdout())
}
pub fn handle(&mut self) -> Result<&mut Write> {
Ok(match *self {
OutputType::Pager(ref mut command) => command
.stdin
.as_mut()
.chain_err(|| "Could not open stdin for pager")?,
OutputType::Stdout(ref mut handle) => handle,
})
}
}
// Since someone could set PAGER to less without arg -R that bat needs,
// ignore flags for less and change flags to -FRX.
// If a user wants an env variable with flags unchanged, he/she should use
// BAT_PAGER.
fn add_default_flags_to_less(pager: String) -> String {
if let Some(pager_name) = shell_words::split(&pager)
.ok()
.as_ref()
.and_then(|flags| flags.first())
.and_then(|pager_path| {
let path_buf = PathBuf::from(pager_path);
path_buf.file_stem().map(|os_str| os_str.to_os_string())
})
{
if pager_name == OsString::from("less") {
return "less -FRX".to_string();
}
}
pager.to_string()
}
2018-05-21 14:59:42 +02:00
impl Drop for OutputType {
fn drop(&mut self) {
if let OutputType::Pager(ref mut command) = *self {
let _ = command.wait();
}
}
}