use std::collections::BTreeMap;
use std::ffi::OsString;
use std::io::{self, Write};
use std::path::Path;
use std::process::ExitCode;
use std::sync::atomic::AtomicU64;
use std::sync::{Arc, Mutex};
use super::CountedRun;
use super::host::{Host, SANDBOX_OUTPUT_CAP, Sandbox, reset_sigpipe};
mod apple;
mod flavour;
mod ncompress;
#[cfg(test)]
mod test_scratch;
pub(crate) use flavour::flavour_from;
pub use flavour::{Flavour, default_flavour};
const FLAVOR_VAR: &str = "STUFFR_COMPRESS_FLAVOR";
pub fn run(name: &'static str, args: Vec<OsString>) -> ExitCode {
reset_sigpipe();
let mut err = io::stderr();
let flavour = match flavour_from(std::env::var_os(FLAVOR_VAR)) {
Ok(f) => f,
Err(v) => {
let _ = writeln!(
err,
"{name}: {FLAVOR_VAR} must be apple or ncompress, not '{}'",
v.to_string_lossy()
);
return ExitCode::from(1);
}
};
let argv0 = std::env::args_os()
.next()
.map(|a| a.as_encoded_bytes().to_vec())
.unwrap_or_else(|| name.as_bytes().to_vec());
let code = main(flavour, name, &argv0, &args, &mut Host::Real, &mut err);
ExitCode::from(code)
}
fn main(
flavour: Flavour,
name: &'static str,
argv0: &[u8],
args: &[OsString],
host: &mut Host,
err: &mut dyn Write,
) -> u8 {
match flavour {
Flavour::Apple => apple::main(name, argv0, args, host, err),
Flavour::Ncompress => ncompress::main(name, args, host, err),
}
}
#[doc(hidden)]
pub fn run_in_counted(
flavour: Flavour,
dir: &Path,
name: &'static str,
args: &[OsString],
stdin: &[u8],
env: &dyn Fn(&str) -> Option<OsString>,
) -> CountedRun {
let _ = env;
let stdout = Arc::new(Mutex::new(Vec::new()));
let reads = Arc::new(Mutex::new(BTreeMap::new()));
let mut host = Host::Sandbox(Sandbox {
dir: dir.to_path_buf(),
stdin: Some(stdin.to_vec()),
stdout: stdout.clone(),
budget: Arc::new(AtomicU64::new(SANDBOX_OUTPUT_CAP)),
reads: reads.clone(),
});
let mut err = Vec::new();
let code = main(flavour, name, name.as_bytes(), args, &mut host, &mut err);
drop(host);
let out = std::mem::take(&mut *stdout.lock().unwrap_or_else(|p| p.into_inner()));
let reads = std::mem::take(&mut *reads.lock().unwrap_or_else(|p| p.into_inner()));
(i32::from(code), out, err, reads)
}