1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
use std::fs::{self, File};
use std::io;
use std::path::PathBuf;
use std::time::UNIX_EPOCH;
use clap::{Parser, Subcommand};
use time::{OffsetDateTime, PrimitiveDateTime};
use cab::{Cabinet, CabinetBuilder, CompressionType, FileEntry, FolderEntry};
#[derive(Parser, Debug)]
#[command(author, about, version)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand, Debug)]
enum Command {
/// Concatenates and prints streams
Cat { path: PathBuf, files: Vec<String> },
/// Creates a new cabinet
Create {
/// Sets compression type
#[clap(short, long, default_value_t = String::from("mszip"))]
compress: String,
/// Sets output path
#[clap(short, long)]
output: Option<PathBuf>,
files: Vec<String>,
},
/// Lists files in the cabinet
Ls {
/// Lists in long format
#[clap(short, long)]
long: bool,
path: PathBuf,
},
}
fn main() {
let cli = Cli::parse();
match cli.command {
Command::Cat { path, files } => {
let mut cabinet = Cabinet::new(File::open(path).unwrap()).unwrap();
for filename in files {
let mut file_reader = cabinet.read_file(&filename).unwrap();
io::copy(&mut file_reader, &mut io::stdout()).unwrap();
}
}
Command::Create { compress, output, files } => {
let compress = match compress.as_str() {
"none" => CompressionType::None,
"mszip" => CompressionType::MsZip,
_ => panic!("Invalid compression type: {}", compress),
};
let output = output.unwrap_or_else(|| {
let mut path = PathBuf::from("out.cab");
let mut index: i32 = 0;
while path.exists() {
index += 1;
path = PathBuf::from(format!("out{}.cab", index));
}
path
});
let mut builder = CabinetBuilder::new();
let mut file_index: usize = 0;
while file_index < files.len() {
let folder = builder.add_folder(compress);
let mut folder_size: u64 = 0;
while file_index < files.len() && folder_size < 0x8000 {
let filename = files[file_index].as_str();
let metadata = fs::metadata(filename).unwrap();
folder_size += metadata.len();
let file = folder.add_file(filename);
if let Ok(time) = metadata.modified() {
if let Ok(dur) = time.duration_since(UNIX_EPOCH) {
let dt = OffsetDateTime::from_unix_timestamp(
dur.as_secs() as i64,
)
.unwrap();
file.set_datetime(PrimitiveDateTime::new(
dt.date(),
dt.time(),
));
}
}
file_index += 1;
}
}
let file = File::create(&output).unwrap();
let mut cabinet = builder.build(file).unwrap();
while let Some(mut writer) = cabinet.next_file().unwrap() {
let mut file = File::open(writer.file_name()).unwrap();
io::copy(&mut file, &mut writer).unwrap();
}
cabinet.finish().unwrap();
}
Command::Ls { path, long } => {
let cabinet = Cabinet::new(File::open(path).unwrap()).unwrap();
for (index, folder) in cabinet.folder_entries().enumerate() {
for file in folder.file_entries() {
list_file(index, &folder, file, long);
}
}
}
}
}
fn list_file(
folder_index: usize,
folder: &FolderEntry,
file: &FileEntry,
long: bool,
) {
if !long {
println!("{}", file.name());
return;
}
let ctype = match folder.compression_type() {
CompressionType::None => "None".to_string(),
CompressionType::MsZip => "MsZip".to_string(),
CompressionType::Quantum(v, m) => format!("Q{}/{}", v, m),
CompressionType::Lzx(w) => format!("Lzx{:?}", w),
};
let file_size = if file.uncompressed_size() >= 100_000_000 {
format!("{} MB", file.uncompressed_size() / (1 << 20))
} else if file.uncompressed_size() >= 1_000_000 {
format!("{} kB", file.uncompressed_size() / (1 << 10))
} else {
format!("{} B ", file.uncompressed_size())
};
println!(
"{}{}{}{}{}{} {:>2} {:<5} {:>10} {} {}",
if file.is_read_only() { 'R' } else { '-' },
if file.is_hidden() { 'H' } else { '-' },
if file.is_system() { 'S' } else { '-' },
if file.is_archive() { 'A' } else { '-' },
if file.is_exec() { 'E' } else { '-' },
if file.is_name_utf() { 'U' } else { '-' },
folder_index,
ctype,
file_size,
file.datetime()
.map(|dt| dt.to_string())
.unwrap_or("invalid datetime".to_string()),
file.name()
);
}
|