2015-12-08 02:42:08 +00:00
|
|
|
#![crate_name = "uu_stdbuf"]
|
2014-12-09 11:02:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This file is part of the uutils coreutils package.
|
|
|
|
*
|
|
|
|
* (c) Dorota Kapturkiewicz <dokaptur@gmail.com>
|
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern crate getopts;
|
2017-12-30 06:17:54 +00:00
|
|
|
extern crate tempdir;
|
2015-05-18 03:27:57 +00:00
|
|
|
|
2015-11-24 01:00:51 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate uucore;
|
|
|
|
|
2015-05-22 01:31:21 +00:00
|
|
|
use getopts::{Matches, Options};
|
2017-12-30 06:17:54 +00:00
|
|
|
use tempdir::TempDir;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{self, Write};
|
2015-05-18 03:27:57 +00:00
|
|
|
use std::os::unix::process::ExitStatusExt;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::process::Command;
|
2015-07-31 17:59:05 +00:00
|
|
|
|
2014-12-09 11:02:22 +00:00
|
|
|
static NAME: &'static str = "stdbuf";
|
2015-11-25 09:52:10 +00:00
|
|
|
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
2017-12-30 06:17:54 +00:00
|
|
|
|
|
|
|
const STDBUF_INJECT: &'static [u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));
|
2014-12-09 11:02:22 +00:00
|
|
|
|
|
|
|
enum BufferType {
|
|
|
|
Default,
|
|
|
|
Line,
|
2018-03-12 08:20:58 +00:00
|
|
|
Size(u64),
|
2014-12-09 11:02:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct ProgramOptions {
|
|
|
|
stdin: BufferType,
|
|
|
|
stdout: BufferType,
|
|
|
|
stderr: BufferType,
|
|
|
|
}
|
|
|
|
|
|
|
|
enum ErrMsg {
|
|
|
|
Retry,
|
2018-03-12 08:20:58 +00:00
|
|
|
Fatal,
|
2014-12-09 11:02:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
enum OkMsg {
|
|
|
|
Buffering,
|
|
|
|
Help,
|
2018-03-12 08:20:58 +00:00
|
|
|
Version,
|
2014-12-09 11:02:22 +00:00
|
|
|
}
|
|
|
|
|
2014-12-14 00:20:49 +00:00
|
|
|
#[cfg(target_os = "linux")]
|
2017-12-11 04:57:39 +00:00
|
|
|
fn preload_strings() -> (&'static str, &'static str) {
|
2017-12-30 06:17:54 +00:00
|
|
|
("LD_PRELOAD", "so")
|
2014-12-14 00:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_os = "macos")]
|
2017-12-11 04:57:39 +00:00
|
|
|
fn preload_strings() -> (&'static str, &'static str) {
|
2017-12-30 06:17:54 +00:00
|
|
|
("DYLD_LIBRARY_PATH", "dylib")
|
2014-12-14 00:20:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
2017-12-11 04:57:39 +00:00
|
|
|
fn preload_strings() -> (&'static str, &'static str) {
|
2014-12-14 00:20:49 +00:00
|
|
|
crash!(1, "Command not supported for this operating system!")
|
|
|
|
}
|
|
|
|
|
2014-12-09 11:02:22 +00:00
|
|
|
fn print_version() {
|
2015-05-25 18:50:15 +00:00
|
|
|
println!("{} {}", NAME, VERSION);
|
2014-12-09 11:02:22 +00:00
|
|
|
}
|
|
|
|
|
2015-05-22 01:31:21 +00:00
|
|
|
fn print_usage(opts: &Options) {
|
2018-03-12 08:20:58 +00:00
|
|
|
let brief = "Run COMMAND, with modified buffering operations for its standard streams\n \
|
|
|
|
Mandatory arguments to long options are mandatory for short options too.";
|
2017-12-11 04:57:39 +00:00
|
|
|
let explanation =
|
2014-12-09 11:02:22 +00:00
|
|
|
"If MODE is 'L' the corresponding stream will be line buffered.\n \
|
2018-03-12 08:20:58 +00:00
|
|
|
This option is invalid with standard input.\n\n \
|
|
|
|
If MODE is '0' the corresponding stream will be unbuffered.\n\n \
|
|
|
|
Otherwise MODE is a number which may be followed by one of the following:\n\n \
|
|
|
|
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n \
|
|
|
|
In this case the corresponding stream will be fully buffered with the buffer size set to \
|
|
|
|
MODE bytes.\n\n \
|
|
|
|
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then \
|
|
|
|
that will override corresponding settings changed by 'stdbuf'.\n \
|
|
|
|
Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
|
|
|
|
and are thus unaffected by 'stdbuf' settings.\n";
|
2015-05-22 01:31:21 +00:00
|
|
|
println!("{} {}", NAME, VERSION);
|
|
|
|
println!("");
|
|
|
|
println!("Usage: stdbuf OPTION... COMMAND");
|
|
|
|
println!("");
|
|
|
|
println!("{}\n{}", opts.usage(brief), explanation);
|
2014-12-09 11:02:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_size(size: &str) -> Option<u64> {
|
2015-03-08 18:06:04 +00:00
|
|
|
let ext = size.trim_left_matches(|c: char| c.is_digit(10));
|
|
|
|
let num = size.trim_right_matches(|c: char| c.is_alphabetic());
|
2016-01-05 19:42:52 +00:00
|
|
|
let mut recovered = num.to_owned();
|
2014-12-09 11:02:22 +00:00
|
|
|
recovered.push_str(ext);
|
2015-05-18 03:27:57 +00:00
|
|
|
if recovered != size {
|
2014-12-09 11:02:22 +00:00
|
|
|
return None;
|
|
|
|
}
|
2015-02-03 21:19:13 +00:00
|
|
|
let buf_size: u64 = match num.parse().ok() {
|
2014-12-09 11:02:22 +00:00
|
|
|
Some(m) => m,
|
|
|
|
None => return None,
|
|
|
|
};
|
2015-03-08 18:06:30 +00:00
|
|
|
let (power, base): (u32, u64) = match ext {
|
2014-12-09 11:02:22 +00:00
|
|
|
"" => (0, 0),
|
|
|
|
"KB" => (1, 1024),
|
|
|
|
"K" => (1, 1000),
|
|
|
|
"MB" => (2, 1024),
|
|
|
|
"M" => (2, 1000),
|
|
|
|
"GB" => (3, 1024),
|
|
|
|
"G" => (3, 1000),
|
|
|
|
"TB" => (4, 1024),
|
|
|
|
"T" => (4, 1000),
|
|
|
|
"PB" => (5, 1024),
|
|
|
|
"P" => (5, 1000),
|
|
|
|
"EB" => (6, 1024),
|
|
|
|
"E" => (6, 1000),
|
|
|
|
"ZB" => (7, 1024),
|
|
|
|
"Z" => (7, 1000),
|
|
|
|
"YB" => (8, 1024),
|
|
|
|
"Y" => (8, 1000),
|
|
|
|
_ => return None,
|
|
|
|
};
|
|
|
|
Some(buf_size * base.pow(power))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_option(matches: &Matches, name: &str, modified: &mut bool) -> Option<BufferType> {
|
|
|
|
match matches.opt_str(name) {
|
|
|
|
Some(value) => {
|
|
|
|
*modified = true;
|
2015-05-18 03:27:57 +00:00
|
|
|
match &value[..] {
|
2014-12-09 11:02:22 +00:00
|
|
|
"L" => {
|
|
|
|
if name == "input" {
|
2015-01-25 14:06:41 +00:00
|
|
|
show_info!("line buffering stdin is meaningless");
|
2014-12-09 11:02:22 +00:00
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(BufferType::Line)
|
|
|
|
}
|
2018-03-12 08:20:58 +00:00
|
|
|
}
|
2014-12-09 11:02:22 +00:00
|
|
|
x => {
|
|
|
|
let size = match parse_size(x) {
|
|
|
|
Some(m) => m,
|
2018-03-12 08:20:58 +00:00
|
|
|
None => {
|
|
|
|
show_error!("Invalid mode {}", x);
|
|
|
|
return None;
|
|
|
|
}
|
2014-12-09 11:02:22 +00:00
|
|
|
};
|
|
|
|
Some(BufferType::Size(size))
|
2018-03-12 08:20:58 +00:00
|
|
|
}
|
2014-12-09 11:02:22 +00:00
|
|
|
}
|
2018-03-12 08:20:58 +00:00
|
|
|
}
|
2014-12-09 11:02:22 +00:00
|
|
|
None => Some(BufferType::Default),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-12 08:20:58 +00:00
|
|
|
fn parse_options(
|
|
|
|
args: &[String],
|
|
|
|
options: &mut ProgramOptions,
|
|
|
|
optgrps: &Options,
|
|
|
|
) -> Result<OkMsg, ErrMsg> {
|
2015-05-22 01:31:21 +00:00
|
|
|
let matches = match optgrps.parse(args) {
|
2014-12-09 11:02:22 +00:00
|
|
|
Ok(m) => m,
|
2018-03-12 08:20:58 +00:00
|
|
|
Err(_) => return Err(ErrMsg::Retry),
|
2014-12-09 11:02:22 +00:00
|
|
|
};
|
|
|
|
if matches.opt_present("help") {
|
|
|
|
return Ok(OkMsg::Help);
|
|
|
|
}
|
|
|
|
if matches.opt_present("version") {
|
|
|
|
return Ok(OkMsg::Version);
|
|
|
|
}
|
|
|
|
let mut modified = false;
|
|
|
|
options.stdin = try!(check_option(&matches, "input", &mut modified).ok_or(ErrMsg::Fatal));
|
|
|
|
options.stdout = try!(check_option(&matches, "output", &mut modified).ok_or(ErrMsg::Fatal));
|
|
|
|
options.stderr = try!(check_option(&matches, "error", &mut modified).ok_or(ErrMsg::Fatal));
|
|
|
|
|
|
|
|
if matches.free.len() != 1 {
|
|
|
|
return Err(ErrMsg::Retry);
|
|
|
|
}
|
|
|
|
if !modified {
|
2015-01-25 14:06:41 +00:00
|
|
|
show_error!("you must specify a buffering mode option");
|
2014-12-09 11:02:22 +00:00
|
|
|
return Err(ErrMsg::Fatal);
|
|
|
|
}
|
|
|
|
Ok(OkMsg::Buffering)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_command_env(command: &mut Command, buffer_name: &str, buffer_type: BufferType) {
|
|
|
|
match buffer_type {
|
2018-03-12 08:20:58 +00:00
|
|
|
BufferType::Size(m) => {
|
|
|
|
command.env(buffer_name, m.to_string());
|
|
|
|
}
|
|
|
|
BufferType::Line => {
|
|
|
|
command.env(buffer_name, "L");
|
|
|
|
}
|
|
|
|
BufferType::Default => {}
|
2014-12-09 11:02:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-30 06:17:54 +00:00
|
|
|
fn get_preload_env(tmp_dir: &mut TempDir) -> io::Result<(String, PathBuf)> {
|
2014-12-30 19:11:36 +00:00
|
|
|
let (preload, extension) = preload_strings();
|
2017-12-30 06:17:54 +00:00
|
|
|
let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);
|
|
|
|
|
|
|
|
let mut file = File::create(&inject_path)?;
|
|
|
|
file.write_all(STDBUF_INJECT)?;
|
2018-03-12 08:20:58 +00:00
|
|
|
|
2017-12-30 06:17:54 +00:00
|
|
|
Ok((preload.to_owned(), inject_path))
|
2014-12-30 19:11:36 +00:00
|
|
|
}
|
2014-12-09 11:02:22 +00:00
|
|
|
|
2015-02-06 13:48:07 +00:00
|
|
|
pub fn uumain(args: Vec<String>) -> i32 {
|
2015-05-22 01:31:21 +00:00
|
|
|
let mut opts = Options::new();
|
|
|
|
|
2018-03-12 08:20:58 +00:00
|
|
|
opts.optopt(
|
|
|
|
"i",
|
|
|
|
"input",
|
|
|
|
"adjust standard input stream buffering",
|
|
|
|
"MODE",
|
|
|
|
);
|
|
|
|
opts.optopt(
|
|
|
|
"o",
|
|
|
|
"output",
|
|
|
|
"adjust standard output stream buffering",
|
|
|
|
"MODE",
|
|
|
|
);
|
|
|
|
opts.optopt(
|
|
|
|
"e",
|
|
|
|
"error",
|
|
|
|
"adjust standard error stream buffering",
|
|
|
|
"MODE",
|
|
|
|
);
|
2015-05-22 01:31:21 +00:00
|
|
|
opts.optflag("", "help", "display this help and exit");
|
|
|
|
opts.optflag("", "version", "output version information and exit");
|
|
|
|
|
2018-03-12 08:20:58 +00:00
|
|
|
let mut options = ProgramOptions {
|
|
|
|
stdin: BufferType::Default,
|
|
|
|
stdout: BufferType::Default,
|
|
|
|
stderr: BufferType::Default,
|
|
|
|
};
|
2015-09-28 03:29:07 +00:00
|
|
|
let mut command_idx: i32 = -1;
|
2018-03-12 08:20:58 +00:00
|
|
|
for i in 1..args.len() + 1 {
|
|
|
|
match parse_options(&args[1..i], &mut options, &opts) {
|
2014-12-09 11:02:22 +00:00
|
|
|
Ok(OkMsg::Buffering) => {
|
2015-09-28 03:34:23 +00:00
|
|
|
command_idx = (i as i32) - 1;
|
2014-12-09 11:02:22 +00:00
|
|
|
break;
|
2018-03-12 08:20:58 +00:00
|
|
|
}
|
2014-12-09 11:02:22 +00:00
|
|
|
Ok(OkMsg::Help) => {
|
2015-05-22 01:31:21 +00:00
|
|
|
print_usage(&opts);
|
2014-12-09 11:02:22 +00:00
|
|
|
return 0;
|
2018-03-12 08:20:58 +00:00
|
|
|
}
|
2014-12-09 11:02:22 +00:00
|
|
|
Ok(OkMsg::Version) => {
|
|
|
|
print_version();
|
|
|
|
return 0;
|
2018-03-12 08:20:58 +00:00
|
|
|
}
|
2014-12-09 11:02:22 +00:00
|
|
|
Err(ErrMsg::Fatal) => break,
|
|
|
|
Err(ErrMsg::Retry) => continue,
|
|
|
|
}
|
2018-03-12 08:20:58 +00:00
|
|
|
}
|
2014-12-09 11:02:22 +00:00
|
|
|
if command_idx == -1 {
|
2018-03-12 08:20:58 +00:00
|
|
|
crash!(
|
|
|
|
125,
|
|
|
|
"Invalid options\nTry 'stdbuf --help' for more information."
|
|
|
|
);
|
2014-12-09 11:02:22 +00:00
|
|
|
}
|
2016-01-05 19:42:52 +00:00
|
|
|
let command_name = &args[command_idx as usize];
|
2014-12-09 11:02:22 +00:00
|
|
|
let mut command = Command::new(command_name);
|
2018-03-12 08:20:58 +00:00
|
|
|
|
2017-12-30 06:17:54 +00:00
|
|
|
let mut tmp_dir = return_if_err!(1, TempDir::new("stdbuf"));
|
|
|
|
let (preload_env, libstdbuf) = return_if_err!(1, get_preload_env(&mut tmp_dir));
|
2018-03-12 08:20:58 +00:00
|
|
|
command
|
|
|
|
.args(&args[(command_idx as usize) + 1..])
|
|
|
|
.env(preload_env, libstdbuf);
|
2014-12-09 11:02:22 +00:00
|
|
|
set_command_env(&mut command, "_STDBUF_I", options.stdin);
|
|
|
|
set_command_env(&mut command, "_STDBUF_O", options.stdout);
|
|
|
|
set_command_env(&mut command, "_STDBUF_E", options.stderr);
|
2015-01-25 14:06:41 +00:00
|
|
|
let mut process = match command.spawn() {
|
|
|
|
Ok(p) => p,
|
2018-03-12 08:20:58 +00:00
|
|
|
Err(e) => crash!(1, "failed to execute process: {}", e),
|
2015-01-25 14:06:41 +00:00
|
|
|
};
|
|
|
|
match process.wait() {
|
2018-03-12 08:20:58 +00:00
|
|
|
Ok(status) => match status.code() {
|
|
|
|
Some(i) => return i,
|
|
|
|
None => crash!(1, "process killed by signal {}", status.signal().unwrap()),
|
2015-01-25 14:06:41 +00:00
|
|
|
},
|
2018-03-12 08:20:58 +00:00
|
|
|
Err(e) => crash!(1, "{}", e),
|
2015-01-25 14:06:41 +00:00
|
|
|
};
|
2014-12-09 11:02:22 +00:00
|
|
|
}
|