2018-05-21 14:59:42 +02:00
|
|
|
use std::io::{self, Write};
|
2020-03-30 19:36:26 +02:00
|
|
|
#[cfg(feature = "paging")]
|
|
|
|
use std::process::Child;
|
2018-05-21 14:59:42 +02:00
|
|
|
|
2020-04-22 21:45:47 +02:00
|
|
|
use crate::error::*;
|
2020-03-30 19:36:26 +02:00
|
|
|
#[cfg(feature = "paging")]
|
2019-12-23 09:54:18 +01:00
|
|
|
use crate::less::retrieve_less_version;
|
2020-04-22 22:54:33 +02:00
|
|
|
#[cfg(feature = "paging")]
|
|
|
|
use crate::paging::PagingMode;
|
2018-08-22 22:29:12 +02:00
|
|
|
|
2019-10-15 03:25:53 +02:00
|
|
|
#[derive(Debug)]
|
2018-05-21 14:59:42 +02:00
|
|
|
pub enum OutputType {
|
2020-03-30 19:36:26 +02:00
|
|
|
#[cfg(feature = "paging")]
|
2018-05-21 14:59:42 +02:00
|
|
|
Pager(Child),
|
|
|
|
Stdout(io::Stdout),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl OutputType {
|
2020-03-30 22:18:41 +02:00
|
|
|
#[cfg(feature = "paging")]
|
2018-10-21 12:52:29 +02:00
|
|
|
pub fn from_mode(mode: PagingMode, pager: Option<&str>) -> Result<Self> {
|
2020-03-30 22:18:41 +02:00
|
|
|
use self::PagingMode::*;
|
2018-10-17 20:21:58 +02:00
|
|
|
Ok(match mode {
|
2020-03-30 22:18:41 +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.
|
2020-03-30 19:36:26 +02:00
|
|
|
#[cfg(feature = "paging")]
|
2018-10-21 12:52:29 +02:00
|
|
|
fn try_pager(quit_if_one_screen: bool, pager_from_config: Option<&str>) -> Result<Self> {
|
2020-03-30 19:36:26 +02:00
|
|
|
use std::env;
|
|
|
|
use std::ffi::OsString;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::process::{Command, Stdio};
|
|
|
|
|
2019-02-07 22:12:41 +01:00
|
|
|
let mut replace_arguments_to_less = false;
|
|
|
|
|
|
|
|
let pager_from_env = match (env::var("BAT_PAGER"), env::var("PAGER")) {
|
|
|
|
(Ok(bat_pager), _) => Some(bat_pager),
|
|
|
|
(_, Ok(pager)) => {
|
|
|
|
// less needs to be called with the '-R' option in order to properly interpret the
|
|
|
|
// ANSI color sequences printed by bat. If someone has set PAGER="less -F", we
|
|
|
|
// therefore need to overwrite the arguments and add '-R'.
|
|
|
|
//
|
|
|
|
// We only do this for PAGER (as it is not specific to 'bat'), not for BAT_PAGER
|
|
|
|
// or bats '--pager' command line option.
|
|
|
|
replace_arguments_to_less = true;
|
|
|
|
Some(pager)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
};
|
2018-11-01 00:22:56 +01:00
|
|
|
|
2019-02-18 19:53:14 +01:00
|
|
|
let pager_from_config = pager_from_config.map(|p| p.to_string());
|
|
|
|
|
|
|
|
if pager_from_config.is_some() {
|
|
|
|
replace_arguments_to_less = false;
|
|
|
|
}
|
|
|
|
|
2018-11-05 21:41:51 +01:00
|
|
|
let pager = pager_from_config
|
2019-02-07 22:12:41 +01:00
|
|
|
.or(pager_from_env)
|
2019-03-08 11:46:49 +01:00
|
|
|
.unwrap_or_else(|| String::from("less"));
|
2018-07-23 22:49:25 +02:00
|
|
|
|
2019-02-07 22:12:41 +01:00
|
|
|
let pagerflags =
|
|
|
|
shell_words::split(&pager).chain_err(|| "Could not parse pager command.")?;
|
2018-10-12 17:37:57 +02:00
|
|
|
|
2018-10-17 20:31:22 +02:00
|
|
|
match pagerflags.split_first() {
|
2019-02-07 22:12:41 +01:00
|
|
|
Some((pager_name, args)) => {
|
2018-11-06 19:53:32 +01:00
|
|
|
let mut pager_path = PathBuf::from(pager_name);
|
|
|
|
|
|
|
|
if pager_path.file_stem() == Some(&OsString::from("bat")) {
|
|
|
|
pager_path = PathBuf::from("less");
|
2018-11-05 21:54:49 +01:00
|
|
|
}
|
2018-11-05 21:41:51 +01:00
|
|
|
|
2018-10-17 20:31:22 +02:00
|
|
|
let is_less = pager_path.file_stem() == Some(&OsString::from("less"));
|
2018-09-06 15:41:38 +02:00
|
|
|
|
Fix pager process execution under 'windows'
## [why]
For 'windows' platforms, directly spawning a process (eg, called PATHNAME) bypasses the
usual windows shell machinery for determining which process to execute. Specifically,
the extensions in PATHEXT will not be used to determine the final executable. So,
`PATHNAME.bat`, `PATHNAME.cmd`, ... will *not* be executed even if on they exist on the
PATH; and this is counter to the usual expectation of a Windows user. Additionally,
built-in commands, such as `echo` and `dir`, will never be accessible as they do not
have a PATH to execute and, so, will never be found.
To use the usual machinery, giving access to PATHNAME.bat and `echo`, execute the PATHNAME
using the windows shell, eg `cmd /d/c PATHNAME`. Note this may expose the constructed
command line to the windows shell quoting vagaries (sadly, that may be part of the price).
Following Windows standards, the ComSpec environment variable is used to determine which
shell to use, with a fallback to the "modern", built-in `cmd` shell.
2020-05-28 04:58:28 +02:00
|
|
|
#[cfg(windows)]
|
|
|
|
let (pager_path, args) = {
|
|
|
|
let p = std::env::var("ComSpec").unwrap_or_else(|_| "cmd".to_string());
|
|
|
|
let mut a = args.to_vec();
|
|
|
|
a.insert(0, pager_path.to_str().unwrap().to_string());
|
|
|
|
a.insert(0, "/d/c".to_string());
|
|
|
|
(p, a)
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut p = Command::new(&pager_path);
|
|
|
|
|
2018-10-17 20:31:22 +02:00
|
|
|
let mut process = if is_less {
|
2019-02-07 22:12:41 +01:00
|
|
|
if args.is_empty() || replace_arguments_to_less {
|
2019-12-23 09:54:18 +01:00
|
|
|
p.arg("--RAW-CONTROL-CHARS");
|
2018-10-17 20:31:22 +02:00
|
|
|
if quit_if_one_screen {
|
|
|
|
p.arg("--quit-if-one-screen");
|
|
|
|
}
|
2019-12-23 09:54:18 +01:00
|
|
|
|
|
|
|
// Passing '--no-init' fixes a bug with '--quit-if-one-screen' in older
|
|
|
|
// versions of 'less'. Unfortunately, it also breaks mouse-wheel support.
|
|
|
|
//
|
|
|
|
// See: http://www.greenwoodsoftware.com/less/news.530.html
|
2020-04-21 18:01:18 +02:00
|
|
|
//
|
|
|
|
// For newer versions (530 or 558 on Windows), we omit '--no-init' as it
|
|
|
|
// is not needed anymore.
|
2020-04-21 17:29:01 +02:00
|
|
|
match retrieve_less_version() {
|
2019-12-23 12:08:18 +01:00
|
|
|
None => {
|
|
|
|
p.arg("--no-init");
|
|
|
|
}
|
2020-04-21 18:01:18 +02:00
|
|
|
Some(version)
|
|
|
|
if (version < 530 || (cfg!(windows) && version < 558)) =>
|
|
|
|
{
|
2019-12-23 09:54:18 +01:00
|
|
|
p.arg("--no-init");
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
2019-02-07 22:12:41 +01:00
|
|
|
} else {
|
|
|
|
p.args(args);
|
2018-10-17 20:31:22 +02:00
|
|
|
}
|
|
|
|
p.env("LESSCHARSET", "UTF-8");
|
|
|
|
p
|
|
|
|
} else {
|
2019-02-07 22:12:41 +01:00
|
|
|
p.args(args);
|
|
|
|
p
|
2018-10-17 20:31:22 +02:00
|
|
|
};
|
2018-07-23 22:49:25 +02:00
|
|
|
|
2018-10-17 20:31:22 +02:00
|
|
|
Ok(process
|
|
|
|
.stdin(Stdio::piped())
|
|
|
|
.spawn()
|
|
|
|
.map(OutputType::Pager)
|
|
|
|
.unwrap_or_else(|_| OutputType::stdout()))
|
|
|
|
}
|
|
|
|
None => Ok(OutputType::stdout()),
|
|
|
|
}
|
2018-05-21 14:59:42 +02:00
|
|
|
}
|
|
|
|
|
2020-03-30 22:18:41 +02:00
|
|
|
pub(crate) fn stdout() -> Self {
|
2018-05-21 14:59:42 +02:00
|
|
|
OutputType::Stdout(io::stdout())
|
|
|
|
}
|
|
|
|
|
2020-04-25 12:25:25 +02:00
|
|
|
#[cfg(feature = "paging")]
|
|
|
|
pub(crate) fn is_pager(&self) -> bool {
|
|
|
|
if let OutputType::Pager(_) = self {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "paging"))]
|
|
|
|
pub(crate) fn is_pager(&self) -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2019-08-02 09:14:57 +02:00
|
|
|
pub fn handle(&mut self) -> Result<&mut dyn Write> {
|
2018-05-21 14:59:42 +02:00
|
|
|
Ok(match *self {
|
2020-03-30 19:36:26 +02:00
|
|
|
#[cfg(feature = "paging")]
|
2018-05-21 14:59:42 +02:00
|
|
|
OutputType::Pager(ref mut command) => command
|
|
|
|
.stdin
|
|
|
|
.as_mut()
|
|
|
|
.chain_err(|| "Could not open stdin for pager")?,
|
|
|
|
OutputType::Stdout(ref mut handle) => handle,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-30 19:36:26 +02:00
|
|
|
#[cfg(feature = "paging")]
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|