coreutils_rs/stdbuf/
core.rs1use std::io;
8use std::process;
9
10#[derive(Clone, Debug)]
12pub enum BufferMode {
13 Line,
15 Unbuffered,
17 Size(usize),
19}
20
21impl BufferMode {
22 pub fn to_env_value(&self) -> String {
24 match self {
25 BufferMode::Line => "L".to_string(),
26 BufferMode::Unbuffered => "0".to_string(),
27 BufferMode::Size(n) => n.to_string(),
28 }
29 }
30}
31
32#[derive(Clone, Debug)]
34pub struct StdbufConfig {
35 pub input: Option<BufferMode>,
36 pub output: Option<BufferMode>,
37 pub error: Option<BufferMode>,
38 pub command: String,
39 pub args: Vec<String>,
40}
41
42pub fn parse_buffer_mode(s: &str) -> Result<BufferMode, String> {
49 if s.eq_ignore_ascii_case("L") {
50 return Ok(BufferMode::Line);
51 }
52 if s == "0" {
53 return Ok(BufferMode::Unbuffered);
54 }
55
56 let (num_str, multiplier) =
58 if let Some(prefix) = s.strip_suffix('K').or_else(|| s.strip_suffix('k')) {
59 (prefix, 1024_usize)
60 } else if let Some(prefix) = s.strip_suffix('M').or_else(|| s.strip_suffix('m')) {
61 (prefix, 1024 * 1024)
62 } else if let Some(prefix) = s.strip_suffix('G').or_else(|| s.strip_suffix('g')) {
63 (prefix, 1024 * 1024 * 1024)
64 } else if let Some(prefix) = s.strip_suffix('T').or_else(|| s.strip_suffix('t')) {
65 (prefix, 1024_usize.wrapping_mul(1024 * 1024 * 1024))
66 } else if let Some(prefix) = s.strip_suffix("KB").or_else(|| s.strip_suffix("kB")) {
67 (prefix, 1000)
68 } else if let Some(prefix) = s.strip_suffix("MB") {
69 (prefix, 1_000_000)
70 } else if let Some(prefix) = s.strip_suffix("GB") {
71 (prefix, 1_000_000_000)
72 } else {
73 (s, 1)
74 };
75
76 let n: usize = num_str
77 .parse()
78 .map_err(|_| format!("invalid mode '{}'", s))?;
79
80 if n == 0 && multiplier == 1 {
81 return Ok(BufferMode::Unbuffered);
82 }
83
84 let size = n
85 .checked_mul(multiplier)
86 .ok_or_else(|| format!("mode size too large: '{}'", s))?;
87
88 if size == 0 {
89 Ok(BufferMode::Unbuffered)
90 } else {
91 Ok(BufferMode::Size(size))
92 }
93}
94
95pub fn run_stdbuf(config: &StdbufConfig) -> io::Result<()> {
97 let mut cmd = process::Command::new(&config.command);
98 cmd.args(&config.args);
99
100 if let Some(ref mode) = config.input {
101 cmd.env("_STDBUF_I", mode.to_env_value());
102 }
103 if let Some(ref mode) = config.output {
104 cmd.env("_STDBUF_O", mode.to_env_value());
105 }
106 if let Some(ref mode) = config.error {
107 cmd.env("_STDBUF_E", mode.to_env_value());
108 }
109
110 let status = cmd.status()?;
111 process::exit(status.code().unwrap_or(125));
112}