2021-02-14 13:58:46 +00:00
|
|
|
use std::io::{self, Write};
|
|
|
|
#[cfg(unix)]
|
|
|
|
use std::os::unix::ffi::OsStrExt;
|
2021-02-14 14:25:32 +00:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
use snailquote::escape;
|
|
|
|
|
|
|
|
use crate::scanerror::ScanError;
|
2021-02-18 09:50:22 +00:00
|
|
|
use crate::Findings;
|
2021-02-14 14:25:32 +00:00
|
|
|
|
|
|
|
const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
|
2021-02-14 13:58:46 +00:00
|
|
|
|
2021-02-10 09:20:22 +00:00
|
|
|
type Entries = [Result<Findings, (ScanError, PathBuf)>];
|
|
|
|
|
2021-02-14 13:58:46 +00:00
|
|
|
fn write_pathbuf<W: Write>(f: &mut W, path: &PathBuf) -> io::Result<()> {
|
|
|
|
match path.to_str() {
|
2021-02-18 09:48:38 +00:00
|
|
|
Some(string) => {
|
|
|
|
write!(f, "{}", escape(string))
|
|
|
|
}
|
2021-02-14 13:58:46 +00:00
|
|
|
None => {
|
|
|
|
write!(f, "'")?;
|
|
|
|
#[cfg(unix)]
|
2021-02-18 09:48:38 +00:00
|
|
|
f.write_all(&*path.as_os_str().as_bytes())?;
|
2021-02-14 13:58:46 +00:00
|
|
|
#[cfg(windows)]
|
2021-02-19 17:57:19 +00:00
|
|
|
write!(f, "{}", path.as_os_str().to_string_lossy())?; // TODO: implement bonked strings for windows
|
|
|
|
// f.write_all(&*path.as_os_str().encode_wide().collect::<Vec<u16>>())?;
|
2021-02-14 13:58:46 +00:00
|
|
|
write!(f, "'")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-10 09:20:22 +00:00
|
|
|
pub trait Format {
|
|
|
|
fn new() -> Self;
|
2021-02-14 13:58:46 +00:00
|
|
|
fn rename<W: Write>(&self, f: &mut W, from: &PathBuf, to: &PathBuf) -> io::Result<()>;
|
|
|
|
fn no_known_extension<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()>;
|
|
|
|
fn unreadable<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()>;
|
|
|
|
fn unknown_type<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()>;
|
2021-02-14 14:25:32 +00:00
|
|
|
fn header<W: Write>(&self, entries: &Entries, f: &mut W) -> io::Result<()>;
|
|
|
|
fn footer<W: Write>(&self, entries: &Entries, f: &mut W) -> io::Result<()>;
|
2021-02-10 09:20:22 +00:00
|
|
|
|
2021-02-14 13:58:46 +00:00
|
|
|
fn write_all<W: Write>(&self, entries: &Entries, f: &mut W) -> io::Result<()> {
|
2021-02-10 09:20:22 +00:00
|
|
|
// TODO: clean this up - it's horrifying
|
2021-02-14 14:25:32 +00:00
|
|
|
self.header(entries, f)?;
|
|
|
|
|
2021-02-10 09:20:22 +00:00
|
|
|
for entry in entries {
|
|
|
|
match entry {
|
|
|
|
Ok(finding) => {
|
|
|
|
// the file was successfully scanned, and a mimetype was detected
|
|
|
|
if !finding.valid {
|
2021-02-14 13:58:46 +00:00
|
|
|
// the file's extension is wrong - check for known extension
|
|
|
|
if let Some(ext) = finding.recommended_extension() {
|
2021-02-18 09:48:38 +00:00
|
|
|
self.rename(f, &finding.file, &finding.file.with_extension(ext.as_str()))?
|
2021-02-14 13:58:46 +00:00
|
|
|
} else {
|
|
|
|
self.no_known_extension(f, &finding.file)?
|
|
|
|
}
|
2021-02-10 09:20:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(error) => {
|
|
|
|
// something went wrong 0uo
|
|
|
|
match error.0 {
|
|
|
|
// failed to read the file
|
2021-02-14 13:58:46 +00:00
|
|
|
ScanError::File => self.unreadable(f, &error.1)?,
|
2021-02-10 09:20:22 +00:00
|
|
|
// file was read successfully, but we couldn't determine a mimetype
|
2021-02-18 09:48:38 +00:00
|
|
|
ScanError::Mime => self.unknown_type(f, &error.1)?,
|
2021-02-10 09:20:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-14 14:25:32 +00:00
|
|
|
|
|
|
|
self.footer(entries, f)
|
2021-02-10 09:20:22 +00:00
|
|
|
}
|
2021-02-06 11:51:20 +00:00
|
|
|
}
|
|
|
|
|
2021-02-18 11:43:24 +00:00
|
|
|
// TODO: maybe make a batch script version for windows
|
2021-02-10 09:20:22 +00:00
|
|
|
pub struct Script {}
|
2021-02-14 17:12:27 +00:00
|
|
|
|
2021-02-06 11:51:20 +00:00
|
|
|
impl Format for Script {
|
2021-02-18 09:48:38 +00:00
|
|
|
fn new() -> Self {
|
|
|
|
Self {}
|
|
|
|
}
|
2021-02-10 09:20:22 +00:00
|
|
|
|
2021-02-14 13:58:46 +00:00
|
|
|
fn rename<W: Write>(&self, f: &mut W, from: &PathBuf, to: &PathBuf) -> io::Result<()> {
|
|
|
|
// TODO: surely there's a better way...
|
|
|
|
write!(f, "mv -v -i -- ")?;
|
|
|
|
write_pathbuf(f, from)?;
|
|
|
|
write!(f, " ")?;
|
|
|
|
write_pathbuf(f, to)?;
|
2021-02-18 09:48:38 +00:00
|
|
|
writeln!(f,)
|
2021-02-10 09:20:22 +00:00
|
|
|
}
|
|
|
|
|
2021-02-14 13:58:46 +00:00
|
|
|
fn no_known_extension<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()> {
|
2021-02-18 11:43:24 +00:00
|
|
|
write!(f, "printf No known extension for ")?;
|
2021-02-14 13:58:46 +00:00
|
|
|
write_pathbuf(f, path)?;
|
2021-02-18 11:43:24 +00:00
|
|
|
writeln!(f,"\nprintf '\n'")
|
2021-02-06 11:51:20 +00:00
|
|
|
}
|
|
|
|
|
2021-02-14 13:58:46 +00:00
|
|
|
fn unreadable<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()> {
|
|
|
|
write!(f, "# Failed to read ")?;
|
|
|
|
write_pathbuf(f, path)?;
|
2021-02-18 09:48:38 +00:00
|
|
|
writeln!(f,)
|
2021-02-06 11:51:20 +00:00
|
|
|
}
|
|
|
|
|
2021-02-14 13:58:46 +00:00
|
|
|
fn unknown_type<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()> {
|
|
|
|
write!(f, "# Failed to detect mime type for ")?;
|
|
|
|
write_pathbuf(f, path)?;
|
2021-02-18 09:48:38 +00:00
|
|
|
writeln!(f,)
|
2021-02-06 11:51:20 +00:00
|
|
|
}
|
2021-02-14 14:25:32 +00:00
|
|
|
|
|
|
|
fn header<W: Write>(&self, _: &Entries, f: &mut W) -> io::Result<()> {
|
2021-02-18 09:48:38 +00:00
|
|
|
write!(
|
|
|
|
f,
|
2021-02-18 11:43:24 +00:00
|
|
|
"#!/usr/bin/env sh\n# Generated by fif {}.\n\nset -e\n\n",
|
2021-02-18 09:48:38 +00:00
|
|
|
VERSION.unwrap_or("???")
|
|
|
|
)
|
2021-02-14 14:25:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn footer<W: Write>(&self, _: &Entries, f: &mut W) -> io::Result<()> {
|
2021-02-18 11:43:24 +00:00
|
|
|
writeln!(f, "\nprintf 'Done.\\n'")
|
2021-02-14 14:25:32 +00:00
|
|
|
}
|
2021-02-18 09:48:38 +00:00
|
|
|
}
|