use std::io::{Read, Seek, SeekFrom};
use super::messages as m;
use super::unix::charset;
use super::{PK_COOL, PK_EOF, PK_ERR, PK_OK, PK_WARN, Uz, flavour};
use crate::compat::host::{Host, InFile};
pub(crate) const INBUFSIZ: usize = 8192;
pub(crate) const OUTBUFSIZ: usize = stuffr::infozip::WSIZE;
pub(crate) const WSIZE: usize = stuffr::infozip::WSIZE;
const CR: u8 = 13;
const LF: u8 = 10;
pub(crate) const FS_FAT_: u8 = 0;
pub(crate) const FS_HPFS_: u8 = 6;
pub(crate) const FS_NTFS_: u8 = 11;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum DoString {
Skip,
Display,
Displ8,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct PInfo {
pub hostver: u8,
pub hostnum: u8,
pub lcflag: bool,
pub vollabel: bool,
pub has_ux_att: bool,
pub gpf_is_utf8: bool,
}
pub(crate) struct ZipIn {
pub(crate) fd: Option<InFile>,
pub(crate) inbuf: Box<[u8]>,
pub(crate) incnt: i64,
pub(crate) inptr: usize,
pub(crate) cur_zipfile_bufstart: i64,
pub(crate) extra_bytes: i64,
pub(crate) ziplen: i64,
pub(crate) inptr_leftover: usize,
pub(crate) incnt_leftover: i64,
}
impl Default for ZipIn {
fn default() -> ZipIn {
ZipIn {
fd: None,
inbuf: Box::default(),
incnt: 0,
inptr: 0,
cur_zipfile_bufstart: 0,
extra_bytes: 0,
ziplen: 0,
inptr_leftover: 0,
incnt_leftover: 0,
}
}
}
impl ZipIn {
pub(crate) fn new() -> ZipIn {
ZipIn {
fd: None,
inbuf: vec![0u8; INBUFSIZ + 4].into_boxed_slice(),
incnt: 0,
inptr: 0,
cur_zipfile_bufstart: 0,
extra_bytes: 0,
ziplen: 0,
inptr_leftover: 0,
incnt_leftover: 0,
}
}
pub(crate) fn read_at(&mut self, at: usize, n: usize) -> i64 {
let Some(fd) = self.fd.as_mut() else {
return -1;
};
loop {
match fd.read(&mut self.inbuf[at..at + n]) {
Ok(k) => return k as i64,
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(_) => return -1,
}
}
}
pub(crate) fn read_into(&mut self, buf: &mut [u8]) -> i64 {
let Some(fd) = self.fd.as_mut() else {
return -1;
};
loop {
match fd.read(buf) {
Ok(k) => return k as i64,
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(_) => return -1,
}
}
}
pub(crate) fn lseek(&mut self, off: i64) -> i64 {
let Some(fd) = self.fd.as_mut() else {
return -1;
};
if off < 0 {
return -1;
}
fd.seek(SeekFrom::Start(off as u64))
.map_or(-1, |p| p as i64)
}
}
pub(crate) fn makeword(b: &[u8]) -> u16 {
u16::from(b[0]) | (u16::from(b[1]) << 8)
}
pub(crate) fn makelong(b: &[u8]) -> u32 {
u32::from_le_bytes([b[0], b[1], b[2], b[3]])
}
pub(crate) fn makeint64(f: flavour::Flavour, b: &[u8]) -> u64 {
let mut v = (u64::from(b[7]) << 56)
.wrapping_add(u64::from(b[6]) << 48)
.wrapping_add(u64::from(b[4]) << 32)
.wrapping_add(u64::from(makelong(b)));
if flavour::makeint64_reads_byte_5(f) {
v = v.wrapping_add(u64::from(b[5]) << 40);
}
v
}
pub(crate) fn readbuf(uz: &mut Uz, buf: &mut [u8]) -> usize {
let n = buf.len();
let mut size = n;
let mut at = 0usize;
while size > 0 {
if uz.zin.incnt <= 0 {
uz.zin.incnt = uz.zin.read_at(0, INBUFSIZ);
if uz.zin.incnt == 0 {
return n - size;
} else if uz.zin.incnt < 0 {
uz.info(0x401, m::READ_ERROR.as_bytes());
return 0; }
uz.zin.cur_zipfile_bufstart = uz.zin.cur_zipfile_bufstart.wrapping_add(INBUFSIZ as i64);
uz.zin.inptr = 0;
}
let count = size.min(uz.zin.incnt as usize);
let from = uz.zin.inptr;
for (k, b) in buf[at..at + count].iter_mut().enumerate() {
*b = uz.zin.inbuf.get(from + k).copied().unwrap_or(0);
}
at += count;
uz.zin.inptr += count;
uz.zin.incnt -= count as i64;
size -= count;
}
n
}
pub(crate) fn seek_zipf(uz: &mut Uz, abs_offset: i64) -> i32 {
let request = abs_offset.wrapping_add(uz.zin.extra_bytes);
let inbuf_offset = request.wrapping_rem(INBUFSIZ as i64);
let bufstart = request.wrapping_sub(inbuf_offset);
if request < 0 {
let msg = {
let mut b = Vec::new();
m::printf(
&mut b,
m::SEEK_MSG,
&[&uz.zipfn.clone(), m::REPORT_MSG.as_bytes()],
);
b
};
uz.info(1, &msg);
return super::PK_BADERR;
} else if bufstart != uz.zin.cur_zipfile_bufstart {
uz.zin.cur_zipfile_bufstart = uz.zin.lseek(bufstart);
uz.zin.incnt = uz.zin.read_at(0, INBUFSIZ);
if uz.zin.incnt <= 0 {
return PK_EOF;
}
uz.zin.incnt -= inbuf_offset;
uz.zin.inptr = inbuf_offset as usize;
} else {
uz.zin.incnt += uz.zin.inptr as i64 - inbuf_offset;
uz.zin.inptr = inbuf_offset as usize;
}
PK_OK
}
fn here_plus(uz: &Uz, length: usize) -> i64 {
uz.zin
.cur_zipfile_bufstart
.wrapping_sub(uz.zin.extra_bytes)
.wrapping_add(uz.zin.inptr as i64)
.wrapping_add(length as i64)
}
pub(crate) fn ext_ascii_to_native(uz: &Uz, s: &mut [u8], p: &PInfo, islochdr: bool) {
let ubuntu = flavour::iconv_charsets(uz.flavour);
let oem = (p.hostnum == FS_FAT_
&& !((islochdr || p.has_ux_att)
&& (p.hostver == 25 || p.hostver == 26 || p.hostver == 40)))
|| p.hostnum == FS_HPFS_
|| (p.hostnum == FS_NTFS_ && (ubuntu || p.hostver == 50));
if ubuntu {
let from = if oem { &uz.o.oem_cp } else { &uz.o.iso_cp };
let buflen = flavour::filnamsiz(uz.flavour);
if let Some(out) = charset::charset_to_intern(&uz.host, s, from, buflen) {
let n = s.len().min(buflen);
s[..n].fill(0);
let k = out.len().min(n);
s[..k].copy_from_slice(&out[..k]);
}
return;
}
if oem {
for b in s.iter_mut() {
if *b == 0 {
break;
}
if *b & 0x80 != 0 {
*b = m::OEM2ISO_850[usize::from(*b & 0x7f)];
}
}
}
}
pub(crate) fn strlower(s: &mut [u8]) {
for b in s.iter_mut() {
if *b == 0 {
break;
}
b.make_ascii_lowercase();
}
}
pub(crate) fn c_str(buf: &[u8]) -> &[u8] {
match buf.iter().position(|&b| b == 0) {
Some(n) => &buf[..n],
None => buf,
}
}
pub(crate) fn do_string(
uz: &mut Uz,
length: usize,
option: DoString,
pinfo: Option<&PInfo>,
) -> i32 {
if length == 0 {
return PK_COOL;
}
match option {
DoString::Display | DoString::Displ8 => {
let mut comment_bytes_left = length;
let mut block_len = OUTBUFSIZ; let mut outbuf = vec![0u8; OUTBUFSIZ.min(length)];
while comment_bytes_left > 0 && block_len > 0 {
let want = OUTBUFSIZ.min(comment_bytes_left);
block_len = readbuf(uz, &mut outbuf[..want]);
if block_len == 0 {
return PK_EOF;
}
comment_bytes_left -= block_len;
let mut text: Vec<u8> = c_str(&outbuf[..block_len])
.iter()
.copied()
.filter(|&b| b != CR)
.collect();
if option == DoString::Displ8
&& let Some(p) = pinfo
{
let mut buf = text.clone();
buf.resize((text.len() + 1).max(flavour::filnamsiz(uz.flavour)), 0);
ext_ascii_to_native(uz, &mut buf, p, false);
text = c_str(&buf).to_vec();
}
let mut q = 0usize;
let mut i = 0usize;
while i < text.len() {
let mut pause = false;
let c = text[i];
if c == 0x1b {
uz.slide[q] = b'^';
uz.slide[q + 1] = b'[';
q += 2;
} else if c == 0x13 {
pause = true;
let at = |k: usize| text.get(k).copied().unwrap_or(0);
if at(i + 1) == LF {
i += 1;
uz.slide[q] = text[i];
q += 1;
} else if at(i + 1) == CR && at(i + 2) == LF {
i += 1;
uz.slide[q] = text[i];
i += 1;
uz.slide[q + 1] = text[i];
q += 2;
}
} else {
uz.slide[q] = c;
q += 1;
}
if q > WSIZE - 3 || pause {
let out = uz.slide[..q].to_vec();
uz.info(0, &out);
q = 0;
if pause && uz.extract_flag {
more_pause(uz, m::QUIT_PROMPT.as_bytes(), 0);
if uz.abort.is_some() {
return PK_COOL;
}
}
}
i += 1;
}
let out = uz.slide[..q].to_vec();
uz.info(0, &out);
}
uz.info(super::MSG_TNEWLN, b"");
PK_COOL
}
DoString::Skip => {
let to = here_plus(uz, length);
seek_zipf(uz, to);
PK_COOL
}
}
}
pub(crate) fn read_filename(uz: &mut Uz, length: usize, pinfo: &PInfo, islochdr: bool) -> i32 {
if length == 0 {
return PK_COOL;
}
if flavour::unicode_support(uz.flavour) {
return read_filename_full(uz, length, pinfo, islochdr);
}
let filnamsiz = uz.filename.len();
let mut error = PK_OK;
let mut length = length;
let block_len = if length >= filnamsiz {
uz.info(0x401, m::FILENAME_TOO_LONG_TRUNC.as_bytes());
error = PK_WARN;
let excess = length - (filnamsiz - 1);
length = filnamsiz - 1;
excess
} else {
0 };
let mut name = std::mem::take(&mut uz.filename);
let got = readbuf(uz, &mut name[..length]);
if got == 0 {
uz.filename = name;
return PK_EOF;
}
name[length] = 0;
native_name(uz, &mut name, length, pinfo, islochdr);
uz.filename = name;
if block_len == 0 {
return error;
}
let name = uz.filename.clone();
let shown = fnfilter1(uz, c_str(&name));
let mut msg = Vec::new();
m::printf(&mut msg, m::TRUNCATED_NAME, &[&shown]);
uz.info(0x401, &msg);
let to = here_plus(uz, block_len);
seek_zipf(uz, to);
error
}
fn native_name(uz: &Uz, name: &mut [u8], length: usize, pinfo: &PInfo, islochdr: bool) {
ext_ascii_to_native(uz, name, pinfo, islochdr);
if pinfo.lcflag {
strlower(name);
}
if pinfo.vollabel && length > 8 && name[8] == b'.' {
let mut i = 8;
while name[i] != 0 {
name[i] = name[i + 1];
i += 1;
}
}
}
fn read_filename_full(uz: &mut Uz, length: usize, pinfo: &PInfo, islochdr: bool) -> i32 {
let filnamsiz = uz.filename.len();
let mut error = PK_OK;
let mut full = uz.filename_full.take().unwrap_or_default();
if full.len() <= length {
full = vec![0u8; filnamsiz.max(length + 1)];
}
let got = readbuf(uz, &mut full[..length]);
if got == 0 {
uz.filename_full = Some(full);
return PK_EOF;
}
full[length] = 0;
let mut length = length;
if length >= filnamsiz {
uz.info(0x401, m::FILENAME_TOO_LONG_TRUNC.as_bytes());
error = PK_WARN;
length = filnamsiz - 1;
}
let mut name = std::mem::take(&mut uz.filename);
strncpy(&mut name, &full, length);
name[length] = 0; uz.filename_full = Some(full);
native_name(uz, &mut name, length, pinfo, islochdr);
uz.filename = name;
error
}
fn strncpy(dst: &mut [u8], src: &[u8], n: usize) {
let s = c_str(src);
let k = s.len().min(n);
dst[..k].copy_from_slice(&s[..k]);
dst[k..n].fill(0);
}
pub(crate) fn unicode_name(uz: &mut Uz, ef: &[u8], pinfo: &PInfo) -> i32 {
use super::process::unicode::{self, UniPath};
if uz.o.u_flag >= 2 {
return PK_COOL;
}
let full = uz.filename_full.clone();
let unipath: Option<Vec<u8>> = if pinfo.gpf_is_utf8 {
full.as_deref().map(|f| c_str(f).to_vec())
} else {
match unicode::get_unicode_data(uz, ef, full.as_deref()).1 {
UniPath::None => None,
UniPath::Standard => full.as_deref().map(|f| c_str(f).to_vec()),
UniPath::Name(n) => Some(n),
}
};
let Some(unipath) = unipath else {
return PK_COOL;
};
let filnamsiz = uz.filename.len();
let mut error = PK_COOL;
if uz.native_is_utf8 && !uz.unicode_escape_all {
let mut name = std::mem::take(&mut uz.filename);
strncpy(&mut name, &unipath, filnamsiz - 1);
uz.filename = name;
if unipath.len() >= filnamsiz {
uz.filename[filnamsiz - 1] = 0;
uz.info(0x401, m::UFILENAME_TOO_LONG_TRUNC.as_bytes());
error = PK_WARN;
}
} else {
let escape_all = uz.unicode_escape_all;
match unicode::utf8_to_local_string(uz, &unipath, escape_all) {
None => {
uz.info(0x401, m::UFILENAME_CORRUPT.as_bytes());
error = PK_ERR;
}
Some(mut fname) => {
if fname.len() >= filnamsiz {
fname.truncate(filnamsiz - 1);
uz.info(0x401, m::UFILENAME_TOO_LONG_TRUNC.as_bytes());
error = PK_WARN;
}
uz.filename[..fname.len()].copy_from_slice(&fname);
uz.filename[fname.len()] = 0;
}
}
}
error
}
pub(crate) fn init_locale(host: &Host) -> bool {
if !matches!(host, Host::Real) {
return false;
}
real_locale_is_utf8()
}
#[cfg(unix)]
fn real_locale_is_utf8() -> bool {
unsafe {
libc::setlocale(libc::LC_CTYPE, c"".as_ptr());
let codeset = libc::nl_langinfo(libc::CODESET);
!codeset.is_null() && std::ffi::CStr::from_ptr(codeset).to_bytes() == b"UTF-8"
}
}
#[cfg(not(unix))]
fn real_locale_is_utf8() -> bool {
false
}
pub(crate) fn is_print(uz: &Uz, c: u8) -> bool {
if matches!(uz.host, Host::Real) {
return libc_isprint(c);
}
(0x20..0x7f).contains(&c)
}
#[cfg(unix)]
fn libc_isprint(c: u8) -> bool {
unsafe { libc::isprint(libc::c_int::from(c)) != 0 }
}
#[cfg(not(unix))]
fn libc_isprint(c: u8) -> bool {
(0x20..0x7f).contains(&c)
}
pub(crate) fn fnfilter_at(uz: &mut Uz, raw: &[u8], at: usize, size: usize) -> Vec<u8> {
let raw = c_str(raw);
let working_isprint = !flavour::no_working_isprint(uz.flavour);
let mut out: Vec<u8> = Vec::with_capacity(raw.len().min(size));
let slim = size.saturating_sub(4);
let mut se: Option<usize> = None;
let mut have_overflow = false;
let mut r = 0usize;
while r < raw.len() {
let s = out.len();
if size > 0 && s >= slim && se.is_none() {
se = Some(s);
}
let c = raw[r];
let printable = !working_isprint || is_print(uz, c);
if c < 32 {
if se.is_some() && s + 4 > size {
have_overflow = true;
break;
}
out.push(b'^');
out.push(64 + c);
r += 1;
} else if !printable {
if se.is_some() && s + 3 > size {
have_overflow = true;
break;
}
out.push(b'?');
r += 1;
} else {
if se.is_some() && s + 3 > size {
have_overflow = true;
break;
}
out.push(c);
r += 1;
}
}
if have_overflow {
let se = se.unwrap_or(out.len());
out.truncate(se);
out.extend_from_slice(b"...");
}
let n = out.len().min(size);
uz.put_slide_at(at, &out[..n], true);
out
}
pub(crate) fn fnfilter1(uz: &mut Uz, raw: &[u8]) -> Vec<u8> {
fnfilter_at(uz, raw, WSIZE >> 1, WSIZE >> 2)
}
pub(crate) fn fnfilter2(uz: &mut Uz, raw: &[u8]) -> Vec<u8> {
fnfilter_at(uz, raw, (WSIZE >> 1) + (WSIZE >> 2), WSIZE >> 2)
}
pub(crate) fn fnfilterp(uz: &mut Uz, pfx: Option<&[u8]>, raw: &[u8]) -> Vec<u8> {
let (mut at, mut size) = (WSIZE >> 1, WSIZE >> 2);
let mut out = Vec::new();
if let Some(pfx) = pfx.filter(|p| !p.is_empty()) {
let p = fnfilter_at(uz, pfx, at, size);
let n = c_str(&p).len().min(size);
at += n;
size -= n;
out.extend_from_slice(&p[..n]);
uz.put_slide_at(at, b"/", false);
out.push(b'/');
at += 1;
size = size.saturating_sub(1);
}
out.extend_from_slice(&fnfilter_at(uz, raw, at, size));
out
}
pub(crate) fn fnprint(uz: &mut Uz) {
let name = uz.filename.clone();
let shown = fnfilter_at(uz, &name, 0, WSIZE >> 1);
uz.info(0, &shown);
uz.info(0, b"\n");
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct Tm {
pub year: i64,
pub mon: u32,
pub mday: u32,
pub hour: u32,
pub min: u32,
pub sec: u32,
pub gmtoff: i64,
pub isdst: bool,
}
pub(crate) fn localtime(uz: &Uz, t: i64) -> Option<Tm> {
if matches!(uz.host, crate::compat::host::Host::Real) {
return localtime_real(t);
}
Some(utc(t))
}
#[cfg(unix)]
unsafe extern "C" {
fn tzset();
}
#[cfg(unix)]
fn localtime_real(t: i64) -> Option<Tm> {
let tt = t as libc::time_t;
let mut tm: libc::tm = unsafe { std::mem::zeroed() };
let ok = unsafe {
tzset();
!libc::localtime_r(&tt, &mut tm).is_null()
};
ok.then(|| Tm {
year: i64::from(tm.tm_year) + 1900,
mon: (tm.tm_mon + 1) as u32,
mday: tm.tm_mday as u32,
hour: tm.tm_hour as u32,
min: tm.tm_min as u32,
sec: tm.tm_sec as u32,
gmtoff: tm.tm_gmtoff as i64,
isdst: tm.tm_isdst > 0,
})
}
#[cfg(not(unix))]
fn localtime_real(t: i64) -> Option<Tm> {
Some(utc(t))
}
pub(crate) fn gmtime(t: i64) -> Tm {
utc(t)
}
fn utc(t: i64) -> Tm {
let days = t.div_euclid(86_400);
let secs = t.rem_euclid(86_400);
let z = days + 719_468;
let era = z.div_euclid(146_097);
let doe = z - era * 146_097;
let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
let y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let mo = if mp < 10 { mp + 3 } else { mp - 9 };
Tm {
year: if mo <= 2 { y + 1 } else { y },
mon: mo as u32,
mday: d as u32,
hour: (secs / 3600) as u32,
min: (secs / 60 % 60) as u32,
sec: (secs % 60) as u32,
gmtoff: 0,
isdst: false,
}
}
pub(crate) const DOSTIME_2038_01_18: u32 = 0x7432_0000;
const U_TIME_T_MAX: i64 = 0xffff_ffff;
const S_TIME_T_MAX: i64 = 0x7fff_ffff;
const YDAYS: [u32; 13] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
pub(crate) fn dos_to_unix_time(uz: &Uz, dosdatetime: u32) -> i64 {
const YRBASE: i64 = 1970;
let yr = i64::from((dosdatetime >> 25) & 0x7f) + (1980 - YRBASE);
let mo = i64::from((dosdatetime >> 21) & 0x0f) - 1;
let dy = i64::from((dosdatetime >> 16) & 0x1f) - 1;
let hh = i64::from((dosdatetime >> 11) & 0x1f);
let mm = i64::from((dosdatetime >> 5) & 0x3f);
let ss = i64::from(dosdatetime & 0x1f) * 2;
let leap = (yr + YRBASE - 1) / 4; let ydays = if mo < 0 {
0
} else {
i64::from(YDAYS[(mo as usize).min(12)])
};
let mut days = (yr * 365) + (leap - 492) + ydays;
if mo > 1 && (yr + YRBASE) % 4 == 0 && (yr + YRBASE) != 2100 {
days += 1; }
let mut m_time = (days + dy) * 86_400 + hh * 3600 + (mm * 60 + ss);
let saturate = |t: i64| -> i64 {
let mut t = t;
if dosdatetime >= DOSTIME_2038_01_18 && t < 0x7000_0000 {
t = U_TIME_T_MAX; }
if t < 0 {
t = S_TIME_T_MAX; }
t
};
if flavour::dos_time_bsd4_4(uz.flavour) {
m_time = saturate(m_time);
if let Some(tm) = localtime(uz, m_time) {
m_time -= tm.gmtoff; }
} else {
m_time += timezone(uz); m_time = saturate(m_time);
if localtime(uz, m_time).is_some_and(|tm| tm.isdst) {
m_time -= 60 * 60; }
}
saturate(m_time)
}
fn timezone(uz: &Uz) -> i64 {
if matches!(uz.host, crate::compat::host::Host::Real) {
return timezone_real();
}
0
}
#[cfg(target_os = "linux")]
#[allow(clippy::useless_conversion)] fn timezone_real() -> i64 {
unsafe extern "C" {
#[link_name = "timezone"]
static c_timezone: libc::c_long;
}
unsafe {
tzset();
i64::from(c_timezone)
}
}
#[cfg(not(target_os = "linux"))]
fn timezone_real() -> i64 {
[1_577_836_800i64, 1_593_561_600]
.iter()
.filter_map(|&t| localtime_real(t))
.find(|tm| !tm.isdst)
.map_or(0, |tm| -tm.gmtoff)
}
pub(crate) fn more_pause(uz: &mut Uz, prompt: &[u8], flag: u32) {
if !uz.sol {
let _ = uz.err.write_all(b"\n");
}
let _ = uz.err.write_all(prompt);
let _ = uz.err.flush();
let c = if flag & 1 != 0 {
loop {
match super::ttyio::zgetch(uz) {
Some(c @ (b'\r' | b'\n' | b' ' | b'q' | b'Q')) => break Some(c),
Some(_) => {}
None => break None,
}
}
} else {
super::ttyio::zgetch(uz)
};
let _ = uz.err.write_all(m::HIDE_PROMPT.as_bytes());
let _ = uz.err.flush();
if c.is_some_and(|c| c.eq_ignore_ascii_case(&b'q')) {
uz.exit_run(PK_COOL);
return;
}
if c.is_none() && flag & 1 != 0 {
uz.o.m_flag = false;
}
uz.sol = true;
if flag & 1 != 0 && c == Some(b' ') {
uz.lines = 0;
}
}
pub(crate) enum Pw {
Entered(super::ttyio::Password),
CancelAll,
Error,
}
pub(crate) fn passw_prompt(uz: &mut Uz, zfn: &[u8], efn: &[u8]) -> Vec<u8> {
let zfnf = fnfilter1(uz, zfn);
let efnf = fnfilter2(uz, efn);
let room = 2 * flavour::filnamsiz(uz.flavour);
if zfnf.len() + efnf.len() <= room {
let mut p = Vec::new();
m::printf(&mut p, m::PASSW_PROMPT, &[&zfnf, &efnf]);
p
} else {
m::PASSW_PROMPT2.as_bytes().to_vec()
}
}
pub(crate) fn uzp_password(uz: &mut Uz, rcnt: &mut i32) -> Pw {
let prompt: Vec<u8> = if *rcnt == 0 {
*rcnt = 2;
let zfn = uz.zipfn.clone();
let efn = c_str(&uz.filename).to_vec();
passw_prompt(uz, &zfn, &efn)
} else {
*rcnt -= 1;
m::PASSW_RETRY.as_bytes().to_vec()
};
match super::ttyio::getp(uz, &prompt) {
Err(_) => Pw::Error,
Ok(p) if p.is_empty_answer() => Pw::CancelAll,
Ok(p) => Pw::Entered(p),
}
}
#[cfg(test)]
mod tests;