use std::ffi::OsString;
use super::flavour;
use super::messages::{self as m, printf};
use super::unix::charset;
use super::{MSG_STDERR, PK_COOL, PK_OK, PK_PARAM, Uz};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct Parsed {
pub archive: OsString,
pub include: Vec<OsString>,
pub exclude: Vec<OsString>,
pub exdir: Option<OsString>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[allow(dead_code)]
pub(crate) struct Options {
pub exdir: Option<Vec<u8>>,
pub pwdarg: Option<Vec<u8>>,
pub zipinfo_mode: bool,
pub aflag: i32,
pub b_flag: bool,
pub cflag: bool,
pub c_flag: bool,
pub d_flag: i32,
pub fflag: bool,
pub acorn_nfs_ext: bool,
pub hflag: bool,
pub jflag: bool,
pub k_flag: bool,
pub lflag: i32,
pub l_flag: i32,
pub overwrite_none: bool,
pub overwrite_all: i32,
pub qflag: i32,
pub tflag: bool,
pub t_flag: bool,
pub uflag: bool,
pub u_flag: i32,
pub vflag: i32,
pub v_flag: bool,
pub w_flag: bool,
pub x_flag: i32,
pub zflag: i32,
pub ddotflag: i32,
pub cflxflag: i32,
pub m_flag: bool,
pub iso_cp: Vec<u8>,
pub oem_cp: Vec<u8>,
}
impl Options {
pub(crate) fn new() -> Options {
Options {
lflag: -1,
..Options::default()
}
}
}
const LFLAG: i32 = 3;
const WSIZE: usize = stuffr::infozip::WSIZE;
fn is_space(b: u8) -> bool {
matches!(b, b' ' | b'\t' | b'\n' | 0x0b | 0x0c | b'\r')
}
pub(crate) fn os(b: &[u8]) -> OsString {
#[cfg(unix)]
{
use std::os::unix::ffi::OsStringExt;
OsString::from_vec(b.to_vec())
}
#[cfg(not(unix))]
{
OsString::from(String::from_utf8_lossy(b).into_owned())
}
}
pub(crate) fn bytes(a: &std::ffi::OsStr) -> Vec<u8> {
a.as_encoded_bytes().to_vec()
}
fn getenv(uz: &Uz, name: &str) -> Option<Vec<u8>> {
(uz.env)(name).map(|v| bytes(&v))
}
pub(crate) fn envargs(uz: &Uz, argv: Vec<Vec<u8>>, envstr: &str, envstr2: &str) -> Vec<Vec<u8>> {
let skip_blanks = |v: Vec<u8>| -> Vec<u8> {
let start = v.iter().position(|&b| !is_space(b)).unwrap_or(v.len());
v[start..].to_vec()
};
let mut envptr = getenv(uz, envstr).map(skip_blanks);
if envptr.as_ref().is_none_or(|v| v.is_empty()) {
envptr = getenv(uz, envstr2).map(skip_blanks);
}
let buf = match envptr {
Some(v) if !v.is_empty() => v,
_ => return argv,
};
let at = |i: usize| buf.get(i).copied().unwrap_or(0);
let mut out: Vec<Vec<u8>> = Vec::with_capacity(argv.len() + 4);
let mut args = argv.into_iter();
out.extend(args.next());
let mut p = 0usize;
loop {
let mut ch;
if at(p) == b'"' {
p += 1;
let start = p;
ch = at(p);
while ch != 0 && ch != b'"' {
if ch == b'\\' && at(p + 1) != 0 {
p += 1; }
p += 1;
ch = at(p);
}
let mut arg = buf[start..p.min(buf.len())].to_vec();
if ch != 0 {
p += 1; }
let mut from = 0;
while let Some(j) = arg[from..].iter().position(|&b| b == b'\\') {
let j = from + j;
arg.remove(j);
from = if j < arg.len() { j + 1 } else { j };
if from >= arg.len() {
break;
}
}
out.push(arg);
} else {
let start = p;
ch = at(p);
while ch != 0 && !is_space(ch) {
p += 1;
ch = at(p);
}
out.push(buf[start..p].to_vec());
if ch != 0 {
p += 1;
}
}
ch = at(p);
while ch != 0 && is_space(ch) {
p += 1;
ch = at(p);
}
if ch == 0 {
break;
}
}
out.extend(args);
out
}
fn c_argv(args: &[OsString]) -> Vec<Vec<u8>> {
std::iter::once(Vec::new())
.chain(args.iter().map(|a| bytes(a)))
.collect()
}
pub(crate) fn parse_unzip(uz: &mut Uz, args: &[OsString]) -> Result<Parsed, i32> {
parse(uz, args, false)
}
pub(crate) fn parse_zipinfo(uz: &mut Uz, args: &[OsString]) -> Result<Parsed, i32> {
parse(uz, args, true)
}
fn parse(uz: &mut Uz, args: &[OsString], zipinfo: bool) -> Result<Parsed, i32> {
uz.noargs = args.is_empty(); uz.o.zipinfo_mode = zipinfo;
let argv = if zipinfo {
envargs(uz, c_argv(args), "ZIPINFO", "ZIPINFOOPT")
} else {
envargs(uz, c_argv(args), "UNZIP", "UNZIPOPT")
};
for (i, a) in argv.iter().enumerate().skip(1) {
if a.len() > (WSIZE >> 2) - 160 {
let n = i.to_string();
uz.info_fmt(0x401, m::CMD_LINE_PARAM_TOO_LONG, &[n.as_bytes()]);
return Err(PK_PARAM);
}
}
let at = if zipinfo {
zi_opts(uz, &argv)?
} else {
uz_opts(uz, &argv)?
};
operands(uz, &argv[at..])
}
fn operands(uz: &mut Uz, argv: &[Vec<u8>]) -> Result<Parsed, i32> {
let archive = os(&argv[0]);
let mut v: Vec<Option<&[u8]>> = argv[1..].iter().map(|a| Some(&a[..])).collect();
let argc = v.len();
let mut filespecs = argc;
let mut xfilespecs = 0usize;
let mut pfnames: Option<usize> = None;
let mut pxnames: Option<usize> = None;
let mut exdir = uz.o.exdir.clone();
if argc > 0 {
let mut in_files = false;
let mut in_xfiles = false;
uz.process_all_files = false;
pfnames = Some(0);
let mut pp = 0usize;
while let Some(Some(cur)) = v.get(pp).copied() {
if exdir.is_none() && cur.starts_with(b"-d") {
let firstarg = pp == 0;
let mut dir = cur[2..].to_vec();
if in_files {
v[pp] = None; filespecs = pp - pfnames.unwrap_or(0);
in_files = false;
} else if in_xfiles {
v[pp] = None; xfilespecs = pp - pxnames.unwrap_or(0);
}
if dir.is_empty() {
pp += 1;
match v.get(pp).copied().flatten() {
Some(next) => dir = next.to_vec(),
None => {
uz.info(0x401, m::MUST_GIVE_EXDIR.as_bytes());
return Err(PK_PARAM);
}
}
}
exdir = Some(dir);
if firstarg {
if v.get(pp + 1).copied().flatten().is_some() {
pfnames = Some(pp + 1);
filespecs = argc - (pp + 1); } else {
uz.process_all_files = true;
pfnames = None;
filespecs = 0;
break;
}
}
} else if !in_xfiles {
if cur == b"-x" {
in_xfiles = true;
if pfnames == Some(pp) {
pfnames = None; filespecs = 0;
} else if in_files {
v[pp] = None; filespecs = pp - pfnames.unwrap_or(0); in_files = false;
}
pxnames = Some(pp + 1); xfilespecs = argc - (pp + 1); } else {
in_files = true;
}
}
pp += 1;
}
} else {
uz.process_all_files = true; }
uz.o.exdir = exdir;
if uz.o.exdir.is_some() && !uz.extract_flag {
uz.info(0x401, m::NOT_EXTRACTING.as_bytes());
}
if flavour::unicode_support(uz.flavour) && uz.o.u_flag == 1 {
uz.unicode_escape_all = true;
}
let list = |start: Option<usize>, n: usize| -> Vec<OsString> {
start.map_or_else(Vec::new, |s| {
v[s..(s + n).min(v.len())]
.iter()
.map_while(|a| a.map(os))
.collect()
})
};
Ok(Parsed {
archive,
include: list(pfnames, filespecs),
exclude: list(pxnames, xfilespecs),
exdir: uz.o.exdir.as_deref().map(os),
})
}
fn uz_opts(uz: &mut Uz, argv: &[Vec<u8>]) -> Result<usize, i32> {
let f = uz.flavour;
let mut error = false;
let mut negative: i32 = 0;
let mut showhelp = 0;
let mut argc = argv.len() as i64;
let mut p = 0usize;
loop {
p += 1;
argc -= 1;
if !(argc > 0 && argv.get(p).is_some_and(|a| a.first() == Some(&b'-'))) {
break;
}
let arg = &argv[p];
let mut s = 1usize;
while s < arg.len() {
let c = arg[s];
s += 1;
match c {
b'-' => negative += 1,
b'a' => {
if negative != 0 {
uz.o.aflag = (uz.o.aflag - negative).max(0);
negative = 0;
} else {
uz.o.aflag += 1;
}
}
b'b' => {
if negative != 0 {
negative = 0; } else {
uz.o.aflag = 0;
}
}
b'B' => {
if negative != 0 {
uz.o.b_flag = false;
negative = 0;
} else {
uz.o.b_flag = true;
}
}
b'c' => {
if negative != 0 {
uz.o.cflag = false;
negative = 0;
} else {
uz.o.cflag = true;
}
}
b'C' => {
if negative != 0 {
uz.o.c_flag = false;
negative = 0;
} else {
uz.o.c_flag = true;
}
}
b'd' => {
if negative != 0 {
uz.info(0x401, m::MUST_GIVE_EXDIR.as_bytes());
return Err(PK_PARAM); }
if uz.o.exdir.is_some() {
uz.info(0x401, m::ONLY_ONE_EXDIR.as_bytes());
return Err(PK_PARAM); }
let mut exdir = arg[s..].to_vec();
if exdir.is_empty() {
if argc > 1 {
argc -= 1;
p += 1;
exdir = argv[p].clone();
if exdir.first() == Some(&b'-') {
uz.info(0x401, m::MUST_GIVE_EXDIR.as_bytes());
return Err(PK_PARAM);
}
} else {
uz.info(0x401, m::MUST_GIVE_EXDIR.as_bytes());
return Err(PK_PARAM);
}
}
uz.o.exdir = Some(exdir);
s = arg.len();
}
b'D' => {
if negative != 0 {
uz.o.d_flag = (uz.o.d_flag - negative).max(0);
negative = 0;
} else {
uz.o.d_flag += 1;
}
}
b'e' => {} b'f' => {
if negative != 0 {
uz.o.fflag = false;
uz.o.uflag = false;
negative = 0;
} else {
uz.o.fflag = true;
uz.o.uflag = true;
}
}
b'F' if flavour::acorn_ftype_nfs(f) => {
if negative != 0 {
uz.o.acorn_nfs_ext = false;
negative = 0;
} else {
uz.o.acorn_nfs_ext = true;
}
}
b'h' => {
if showhelp == 0 {
showhelp = if arg.get(s) == Some(&b'h') { 2 } else { 1 };
}
}
b'I' | b'O' if flavour::iconv_charsets(f) => {
let rest = &arg[s..];
charset_option(uz, c == b'O', negative, rest, argv, &mut p, &mut argc)?;
s = arg.len();
}
b'j' => {
if negative != 0 {
uz.o.jflag = false;
negative = 0;
} else {
uz.o.jflag = true;
}
}
b'K' => {
if negative != 0 {
uz.o.k_flag = false;
negative = 0;
} else {
uz.o.k_flag = true;
}
}
b'l' => {
if negative != 0 {
uz.o.vflag = (uz.o.vflag - negative).max(0);
negative = 0;
} else {
uz.o.vflag += 1;
}
}
b'L' => {
if negative != 0 {
uz.o.l_flag = (uz.o.l_flag - negative).max(0);
negative = 0;
} else {
uz.o.l_flag += 1;
}
}
b'M' => {
if negative != 0 {
uz.o.m_flag = false;
negative = 0;
} else {
uz.o.m_flag = true;
}
}
b'n' => {
if negative != 0 {
uz.o.overwrite_none = false;
negative = 0;
} else {
uz.o.overwrite_none = true;
}
}
b'o' => {
if negative != 0 {
uz.o.overwrite_all = (uz.o.overwrite_all - negative).max(0);
negative = 0;
} else {
uz.o.overwrite_all += 1;
}
}
b'p' => {
if negative != 0 {
uz.o.cflag = false;
uz.o.qflag = (uz.o.qflag - 999).max(0);
negative = 0;
} else {
uz.o.cflag = true;
uz.o.qflag += 999;
}
}
b'P' => {
if negative != 0 {
uz.info(0x401, m::MUST_GIVE_PASSWD.as_bytes());
return Err(PK_PARAM); }
if uz.o.pwdarg.is_none() {
let mut pwd = arg[s..].to_vec();
if pwd.is_empty() {
if argc > 1 {
argc -= 1;
p += 1;
pwd = argv[p].clone();
if pwd.first() == Some(&b'-') {
uz.info(0x401, m::MUST_GIVE_PASSWD.as_bytes());
return Err(PK_PARAM);
}
} else {
uz.info(0x401, m::MUST_GIVE_PASSWD.as_bytes());
return Err(PK_PARAM);
}
}
uz.o.pwdarg = Some(pwd);
s = arg.len();
}
}
b'q' => {
if negative != 0 {
uz.o.qflag = (uz.o.qflag - negative).max(0);
negative = 0;
} else {
uz.o.qflag += 1;
}
}
b't' => {
if negative != 0 {
uz.o.tflag = false;
negative = 0;
} else {
uz.o.tflag = true;
}
}
b'T' => {
if negative != 0 {
uz.o.t_flag = false;
negative = 0;
} else {
uz.o.t_flag = true;
}
}
b'u' => {
if negative != 0 {
uz.o.uflag = false;
negative = 0;
} else {
uz.o.uflag = true;
}
}
b'U' => {
if flavour::unicode_support(f) {
if negative != 0 {
uz.o.u_flag = (uz.o.u_flag - negative).max(0);
negative = 0;
} else {
uz.o.u_flag += 1;
}
} else if negative != 0 {
uz.o.l_flag = 1;
negative = 0;
} else {
uz.o.l_flag = 0;
}
}
b'v' => {
if negative != 0 {
uz.o.vflag = (uz.o.vflag - negative).max(0);
negative = 0;
} else if uz.o.vflag != 0 {
uz.o.vflag += 1;
} else {
uz.o.vflag = 2;
}
}
b'V' => {
if negative != 0 {
uz.o.v_flag = false;
negative = 0;
} else {
uz.o.v_flag = true;
}
}
b'W' if flavour::wild_stop_at_dir(f) => {
if negative != 0 {
uz.o.w_flag = false;
negative = 0;
} else {
uz.o.w_flag = true;
}
}
b'x' => {} b'X' => {
if negative != 0 {
uz.o.x_flag = (uz.o.x_flag - negative).max(0);
negative = 0;
} else {
uz.o.x_flag += 1;
}
}
b'z' => {
if negative != 0 {
uz.o.zflag = (uz.o.zflag - negative).max(0);
negative = 0;
} else {
uz.o.zflag += 1;
}
}
b'Z' => {
uz.info(0x401, m::Z_FIRST.as_bytes());
error = true;
}
b':' => {
if negative != 0 {
uz.o.ddotflag = (uz.o.ddotflag - negative).max(0);
negative = 0;
} else {
uz.o.ddotflag += 1;
}
}
b'^' => {
if negative != 0 {
uz.o.cflxflag = (uz.o.cflxflag - negative).max(0);
negative = 0;
} else {
uz.o.cflxflag += 1;
}
}
_ => error = true,
}
}
}
if showhelp > 0 {
if showhelp == 2 {
help_extended(uz);
return Err(PK_OK);
}
return Err(usage(uz, false));
}
let o = &uz.o;
if (o.cflag && (o.tflag || o.uflag)) || (o.tflag && o.uflag) || (o.fflag && o.overwrite_none) {
uz.info(0x401, m::INVALID_OPTIONS.as_bytes());
error = true;
}
if uz.o.aflag > 2 {
uz.o.aflag = 2;
}
if uz.o.overwrite_all != 0 && uz.o.overwrite_none {
uz.info(0x401, m::IGNORE_O_OPTION.as_bytes());
uz.o.overwrite_all = 0;
}
if uz.o.m_flag && !uz.host.stdout_is_tty() {
uz.o.m_flag = false;
}
let no_zipfile = argc == 0;
argc -= 1;
if no_zipfile || error {
if uz.o.vflag >= 2 && argc == -1 {
show_version_info(uz);
return Err(PK_OK);
}
if !uz.noargs && !error {
error = true; }
return Err(usage(uz, error));
}
let o = &uz.o;
uz.extract_flag = !(o.cflag || o.tflag || o.vflag != 0 || o.zflag != 0 || o.t_flag);
Ok(p)
}
fn zi_opts(uz: &mut Uz, argv: &[Vec<u8>]) -> Result<usize, i32> {
let f = uz.flavour;
let mut error = false;
let mut negative: i32 = 0;
let (mut hflag_slmv, mut hflag_2) = (true, false);
let (mut tflag_slm, mut tflag_2v) = (true, false);
let (mut explicit_h, mut explicit_t) = (false, false);
uz.extract_flag = false; let mut argc = argv.len() as i64;
let mut p = 0usize;
loop {
argc -= 1;
if argc <= 0 {
break;
}
p += 1;
if argv[p].first() != Some(&b'-') {
break;
}
let arg = &argv[p];
let mut s = 1usize;
while s < arg.len() {
let c = arg[s];
s += 1;
match c {
b'-' => negative += 1,
b'1' => {
if negative != 0 {
uz.o.lflag = -2;
negative = 0;
} else {
uz.o.lflag = 1;
}
}
b'2' => {
if negative != 0 {
uz.o.lflag = -2;
negative = 0;
} else {
uz.o.lflag = 2;
}
}
b'C' => {
if negative != 0 {
uz.o.c_flag = false;
negative = 0;
} else {
uz.o.c_flag = true;
}
}
b'h' => {
if negative != 0 {
hflag_2 = false;
hflag_slmv = false;
negative = 0;
} else {
hflag_2 = true;
hflag_slmv = true;
explicit_h = true;
if uz.o.lflag == -1 {
uz.o.lflag = 0;
}
}
}
b'l' => {
if negative != 0 {
uz.o.lflag = -2;
negative = 0;
} else {
uz.o.lflag = 5;
}
}
b'm' => {
if negative != 0 {
uz.o.lflag = -2;
negative = 0;
} else {
uz.o.lflag = 4;
}
}
b'M' => {
if negative != 0 {
uz.o.m_flag = false;
negative = 0;
} else {
uz.o.m_flag = true;
}
}
b's' => {
if negative != 0 {
uz.o.lflag = -2;
negative = 0;
} else {
uz.o.lflag = 3;
}
}
b't' => {
if negative != 0 {
tflag_2v = false;
tflag_slm = false;
negative = 0;
} else {
tflag_2v = true;
tflag_slm = true;
explicit_t = true;
if uz.o.lflag == -1 {
uz.o.lflag = 0;
}
}
}
b'I' | b'O' if flavour::iconv_charsets(f) => {
let rest = &arg[s..];
charset_option(uz, c == b'O', negative, rest, argv, &mut p, &mut argc)?;
s = arg.len();
}
b'T' => {
if negative != 0 {
uz.o.t_flag = false;
negative = 0;
} else {
uz.o.t_flag = true;
}
}
b'U' if flavour::unicode_support(f) => {
if negative != 0 {
uz.o.u_flag = (uz.o.u_flag - negative).max(0);
negative = 0;
} else {
uz.o.u_flag += 1;
}
}
b'v' => {
if negative != 0 {
uz.o.lflag = -2;
negative = 0;
} else {
uz.o.lflag = 10;
}
}
b'W' if flavour::wild_stop_at_dir(f) => {
if negative != 0 {
uz.o.w_flag = false;
negative = 0;
} else {
uz.o.w_flag = true;
}
}
b'z' => {
if negative != 0 {
uz.o.zflag = 0;
negative = 0;
} else {
uz.o.zflag = 1;
}
}
b'Z' => {} _ => error = true,
}
}
}
let no_zipfile = argc == 0;
argc -= 1;
if no_zipfile || error {
return Err(usage(uz, error));
}
if uz.o.m_flag && !uz.host.stdout_is_tty() {
uz.o.m_flag = false;
}
if uz.o.lflag < 0 || (argc > 0 && uz.o.lflag == 0) {
uz.o.lflag = LFLAG;
}
match uz.o.lflag {
0 | 2 => {
uz.o.hflag = hflag_2;
uz.o.tflag = tflag_2v;
}
1 => {
uz.o.hflag = false;
uz.o.tflag = false;
uz.o.zflag = 0;
}
3..=5 => {
uz.o.hflag = if argc > 0 && !explicit_h {
false
} else {
hflag_slmv
};
uz.o.tflag = if argc > 0 && !explicit_t {
false
} else {
tflag_slm
};
}
10 => {
uz.o.hflag = hflag_slmv;
uz.o.tflag = tflag_2v;
}
_ => {}
}
Ok(p)
}
pub(crate) fn usage(uz: &mut Uz, error: bool) -> i32 {
let flag = if error { MSG_STDERR } else { 0 };
let f = uz.flavour;
let version = env!("CARGO_PKG_VERSION").as_bytes();
if uz.o.zipinfo_mode {
uz.info_fmt(flag, flavour::zipinfo_banner(f), &[version]);
uz.info(flag, m::ZIPINFO_USAGE_LINE1.as_bytes());
uz.info(flag, m::ZIPINFO_USAGE_LINE2.as_bytes());
let mut line3 = m::ZIPINFO_USAGE_LINE3.to_owned();
if flavour::iconv_charsets(f) {
line3.push_str(m::ZIPINFO_USAGE_ICONV);
}
uz.info(flag, line3.as_bytes());
} else {
let mut line1 = Vec::new();
printf(&mut line1, flavour::unzip_banner(f), &[version]);
line1.extend_from_slice(flavour::unzip_banner_rest(f).as_bytes());
uz.info(flag, &line1);
uz.info(flag, m::UNZIP_USAGE_LINE2.as_bytes());
uz.info(flag, m::UNZIP_USAGE_LINE3.as_bytes());
let mut line4 = m::UNZIP_USAGE_LINE4_HEAD.to_owned();
if flavour::unicode_support(f) {
line4.push_str(m::UNZIP_USAGE_LINE4_UNICODE);
}
line4.push_str(m::UNZIP_USAGE_LINE4_TAIL);
if flavour::iconv_charsets(f) {
line4.push_str(m::UNZIP_USAGE_LINE4_ICONV);
}
uz.info(flag, line4.as_bytes());
uz.info(flag, m::UNZIP_USAGE_LINE5.as_bytes());
}
if error { PK_PARAM } else { PK_COOL }
}
fn help_extended(uz: &mut Uz) {
let iconv = flavour::iconv_charsets(uz.flavour);
for line in m::HELP_EXTENDED {
let mut l = line.as_bytes().to_vec();
l.push(b'\n');
uz.info(0, &l);
if iconv && let Some((_, more)) = m::HELP_EXTENDED_ICONV.iter().find(|(a, _)| a == line) {
for extra in *more {
let mut l = extra.as_bytes().to_vec();
l.push(b'\n');
uz.info(0, &l);
}
}
}
}
fn charset_option(
uz: &mut Uz,
oem: bool,
negative: i32,
rest: &[u8],
argv: &[Vec<u8>],
p: &mut usize,
argc: &mut i64,
) -> Result<(), i32> {
if negative != 0 {
uz.info(0x401, m::ENCODINGS_NEGATED.as_bytes());
return Err(PK_PARAM);
}
let name = if !rest.is_empty() {
if rest[0] == b'-' {
uz.info(0x401, m::ENCODING_SHOULD_FOLLOW_I.as_bytes());
return Err(PK_PARAM);
}
rest.to_vec()
} else {
*p += 1;
*argc -= 1;
if !(*argc > 0 && argv.get(*p).is_some_and(|a| a.first() != Some(&b'-'))) {
let msg = if oem {
m::ENCODING_SHOULD_FOLLOW_O
} else {
m::ENCODING_SHOULD_FOLLOW_I
};
uz.info(0x401, msg.as_bytes());
return Err(PK_PARAM);
}
argv[*p].clone()
};
let cp = charset::cp_name(&name);
if oem {
uz.o.oem_cp = cp;
} else {
uz.o.iso_cp = cp;
}
Ok(())
}
fn show_version_info(uz: &mut Uz) {
let f = uz.flavour;
if uz.o.qflag > 3 {
uz.info(0, b"600\n");
return;
}
let mut line1 = Vec::new();
printf(
&mut line1,
flavour::unzip_banner(f),
&[env!("CARGO_PKG_VERSION").as_bytes()],
);
line1.extend_from_slice(flavour::unzip_banner_rest(f).as_bytes());
uz.info(0, &line1);
uz.info(0, m::UNZIP_USAGE_LINE2V.as_bytes());
version(uz);
uz.info(0, m::COMPILE_OPTIONS.as_bytes());
let mut opts: Vec<String> = Vec::new();
if flavour::acorn_ftype_nfs(f) {
opts.push(m::OPT_ACORN_FTYPE_NFS.into());
}
opts.extend(
[
m::OPT_COPYRIGHT_CLEAN,
m::OPT_SET_DIR_ATTRIB,
m::OPT_SYMLINKS,
m::OPT_TIMESTAMP,
m::OPT_UNIXBACKUP,
m::OPT_USE_EF_UT_TIME,
m::OPT_USE_UNSHRINK,
m::OPT_USE_DEFLATE64,
]
.map(String::from),
);
if flavour::unicode_support(f) {
let coding = if uz.native_is_utf8 {
m::SYS_CH_UTF8
} else {
m::SYS_CH_OTHER
};
let mut line = Vec::new();
printf(&mut line, m::OPT_USE_UNICODE, &[coding.as_bytes()]);
opts.push(String::from_utf8_lossy(&line).into_owned());
}
opts.extend([m::OPT_USE_LFS, m::OPT_USE_ZIP64].map(String::from));
if flavour::use_bzip2(f) {
opts.push(m::OPT_USE_BZIP2.into());
}
opts.push(m::OPT_VMS_TEXT_CONV.into());
if flavour::wild_stop_at_dir(f) {
opts.push(m::OPT_WILD_STOP_AT_DIR.into());
}
for o in &opts {
uz.info_fmt(0, m::COMPILE_OPT_FORMAT, &[o.as_bytes()]);
}
uz.info(0, m::DECRYPTION.as_bytes());
uz.info(0, m::ENV_OPTIONS.as_bytes());
for name in ["UNZIP", "UNZIPOPT", "ZIPINFO", "ZIPINFOOPT"] {
let value = getenv(uz, name).filter(|v| !v.is_empty());
let mut line = format!("{name:>16}: ").into_bytes();
match value {
Some(v) => line.extend_from_slice(&v[..v.len().min(1024)]),
None => line.extend_from_slice(m::NONE.as_bytes()),
}
line.push(b'\n');
uz.info(0, &line);
}
}
fn version(uz: &mut Uz) {
uz.info(0, flavour::compiled_with(uz.flavour).as_bytes());
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct FunzipArgs {
pub password: Option<Vec<u8>>,
pub input: Option<OsString>,
}
pub(crate) fn parse_funzip(args: &[OsString]) -> FunzipArgs {
let mut rest = args.iter();
let mut out = FunzipArgs::default();
let mut next = rest.next();
if let Some(a) = next {
let b = bytes(a);
if b.first() == Some(&b'-') {
out.password = Some(b[1..].to_vec());
next = rest.next();
}
}
out.input = next.cloned();
out
}
#[cfg(test)]
mod tests;