1use std::{
17 io,
18 process::{Command, Stdio},
19 string::FromUtf8Error,
20};
21
22pub fn bash_command(command: &str) -> io::Result<Command> {
27 let mut command = make_script_command(command, Some("bash"))?;
28 command.stdout(Stdio::piped()).stderr(Stdio::piped());
29
30 Ok(command)
31}
32
33pub fn command(command: &str, shell: Option<&str>) -> io::Result<Command> {
38 let mut command = make_script_command(command, shell)?;
39 command.stdout(Stdio::piped()).stderr(Stdio::piped());
40
41 Ok(command)
42}
43
44pub fn make_script_command(c: &str, shell: Option<&str>) -> io::Result<Command> {
65 let shell = which_shell(shell.unwrap_or("bash"))?;
66
67 #[cfg(any(target_os = "linux", target_os = "android"))]
68 {
69 let mut command = Command::new("script");
70 command.args(&["-qec", c, "/dev/null"]);
71 command.env("SHELL", shell.trim());
72
73 Ok(command)
74 }
75
76 #[cfg(any(target_os = "macos", target_os = "freebsd"))]
77 {
78 let mut command = Command::new("script");
79 command.args(&["-q", "/dev/null", shell.trim(), "-c", c]);
80 Ok(command)
81 }
82
83 #[cfg(not(any(
84 target_os = "android",
85 target_os = "linux",
86 target_os = "macos",
87 target_os = "freebsd"
88 )))]
89 compile_error!("This platform is not supported. See https://github.com/Aloso/to-html/issues/3")
90}
91
92pub fn get_stdout(stdout: Vec<u8>) -> Result<String, FromUtf8Error> {
94 let out = String::from_utf8(stdout)?;
95
96 #[cfg(any(target_os = "linux", target_os = "android"))]
97 {
98 Ok(out.replace("\r\n", "\n"))
99 }
100
101 #[cfg(any(target_os = "macos", target_os = "freebsd"))]
102 {
103 let mut out = out.replace("\r\n", "\n");
104 if out.starts_with("^D\u{8}\u{8}") {
105 out = out["^D\u{8}\u{8}".len()..].to_string()
106 }
107 Ok(out)
108 }
109}
110
111fn which_shell(shell: &str) -> io::Result<String> {
112 let which = Command::new("which")
113 .arg(shell)
114 .stdout(Stdio::piped())
115 .output()?;
116
117 if which.status.success() {
118 Ok(String::from_utf8(which.stdout).unwrap())
119 } else {
120 Err(io::Error::new(
121 io::ErrorKind::Other,
122 String::from_utf8(which.stderr).unwrap(),
123 ))
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 fn run(s: &str) -> String {
130 let output = crate::bash_command(s).unwrap().output().unwrap();
131 let s1 = crate::get_stdout(output.stdout).unwrap();
132
133 if crate::which_shell("zsh").is_ok() {
134 let output = crate::command(s, Some("zsh")).unwrap().output().unwrap();
135 let s2 = crate::get_stdout(output.stdout).unwrap();
136
137 assert_eq!(s1, s2);
138 }
139
140 s1
141 }
142
143 #[test]
144 fn echo() {
145 assert_eq!(run("echo hello world"), "hello world\n");
146 }
147
148 #[test]
149 fn seq() {
150 assert_eq!(run("seq 3"), "1\n2\n3\n");
151 }
152
153 #[test]
154 fn echo_quotes() {
155 assert_eq!(run(r#"echo "Hello \$\`' world!""#), "Hello $`' world!\n");
156 }
157
158 #[test]
159 fn echo_and_cat() {
160 assert_eq!(
161 run("echo 'look, bash support!' | cat"),
162 "look, bash support!\n"
163 );
164 }
165}