use std::{
env, fs,
path::{self, Path},
process,
};
use sit::Fork;
use walkdir::{DirEntry, WalkDir};
fn main() {
pretty_env_logger::init();
log::debug!("Startup");
let Some(path) = env::args().nth(1) else {
eprintln!("Please provide a path to a StuffIt archive as the first argument");
process::exit(-1);
};
match fs::metadata(&path) {
Ok(meta) if meta.is_dir() => WalkDir::new(path)
.into_iter()
.filter_entry(|e| !is_hidden(e))
.filter_map(|e| e.ok())
.for_each(|f| match extract_file(f.path()) {
Ok(_) => {}
Err(e) => {
eprintln!(
"Could not fully extract file at {}: {}",
f.path().display(),
e
);
}
}),
Ok(meta) if meta.is_file() => match extract_file(&path) {
Ok(_) => {}
Err(e) => {
eprintln!("Could not extract file at {path}");
eprintln!("{e:?}");
process::exit(-3);
}
},
Ok(_) => {
eprintln!("Path is neither file nor directory, skipping analysis…");
}
Err(e) => {
eprintln!("Could not determine if path is a file or directory!");
eprintln!("{e:?}");
process::exit(-2);
}
}
log::debug!("Shutdown");
}
fn extract_file(path: impl AsRef<Path>) -> Result<(), sit::Error> {
log::info!("Extracting {}", path.as_ref().display());
let mut archive = match sit::Archive::open_path(path.as_ref()) {
Ok(i) => i,
Err(e) => {
eprintln!("Could not open archive at {}", path.as_ref().display());
eprintln!("{e:?}");
return Err(e);
}
};
let mut path = path::PathBuf::new();
for entry in archive.iter() {
match entry {
sit::Entry::File(file) => {
let cleaner_name = file.name().replace("/", ":");
if file.has(sit::Fork::Resource) {
path.push(format!("{cleaner_name}.rsrc"));
let mut reader = archive.open_fork(&file, Fork::Resource)?;
let mut writer = fs::File::create_new(&path)?;
std::io::copy(&mut reader, &mut writer)?;
path.pop();
}
if file.has(sit::Fork::Data) {
path.push(cleaner_name);
let mut reader = archive.open_fork(&file, Fork::Data)?;
let mut writer = fs::File::create_new(&path)?;
std::io::copy(&mut reader, &mut writer)?;
path.pop();
}
}
sit::Entry::Directory(directory) => {
path.push(directory.name().replace("/", ":"));
fs::create_dir(&path)?;
}
sit::Entry::DirectoryEnd(_) => {
path.pop();
}
}
}
Ok(())
}
fn is_hidden(entry: &DirEntry) -> bool {
entry
.file_name()
.to_str()
.map(|s| s.starts_with(""))
.unwrap_or(false)
}