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> {
50 if s == "L" {
52 return Ok(BufferMode::Line);
53 }
54 if s == "0" {
55 return Ok(BufferMode::Unbuffered);
56 }
57
58 let (num_str, multiplier) =
61 if let Some(prefix) = s.strip_suffix("KB").or_else(|| s.strip_suffix("kB")) {
62 (prefix, 1000_usize)
63 } else if let Some(prefix) = s.strip_suffix("MB") {
64 (prefix, 1_000_000)
65 } else if let Some(prefix) = s.strip_suffix("GB") {
66 (prefix, 1_000_000_000)
67 } else if let Some(prefix) = s.strip_suffix('K').or_else(|| s.strip_suffix('k')) {
68 (prefix, 1024_usize)
69 } else if let Some(prefix) = s.strip_suffix('M').or_else(|| s.strip_suffix('m')) {
70 (prefix, 1024 * 1024)
71 } else if let Some(prefix) = s.strip_suffix('G').or_else(|| s.strip_suffix('g')) {
72 (prefix, 1024 * 1024 * 1024)
73 } else if let Some(prefix) = s.strip_suffix('T').or_else(|| s.strip_suffix('t')) {
74 (prefix, 1024_usize.wrapping_mul(1024 * 1024 * 1024))
75 } else {
76 (s, 1)
77 };
78
79 let n: usize = if num_str.is_empty() {
81 1
82 } else {
83 num_str
84 .parse()
85 .map_err(|_| format!("invalid mode '{}'", s))?
86 };
87
88 if n == 0 && multiplier == 1 {
89 return Ok(BufferMode::Unbuffered);
90 }
91
92 let size = n
93 .checked_mul(multiplier)
94 .ok_or_else(|| format!("mode size too large: '{}'", s))?;
95
96 if size == 0 {
97 Ok(BufferMode::Unbuffered)
98 } else {
99 Ok(BufferMode::Size(size))
100 }
101}
102
103pub fn run_stdbuf(config: &StdbufConfig) -> io::Result<()> {
105 let mut cmd = process::Command::new(&config.command);
106 cmd.args(&config.args);
107
108 if let Some(ref mode) = config.input {
109 cmd.env("_STDBUF_I", mode.to_env_value());
110 }
111 if let Some(ref mode) = config.output {
112 cmd.env("_STDBUF_O", mode.to_env_value());
113 }
114 if let Some(ref mode) = config.error {
115 cmd.env("_STDBUF_E", mode.to_env_value());
116 }
117
118 let status = cmd.status()?;
119 process::exit(status.code().unwrap_or(125));
120}