use super::super::fileio::{self, DoString};
use super::super::list::find_compr_idx;
use super::super::messages as m;
use super::super::process::CentralEntry;
use super::super::{PK_COOL, PK_ERR, PK_WARN, Uz};
use super::short::{
Attribs, THS_IFDIR, THS_IFIND, THS_IFKEY, THS_IFLIB, THS_IFMT, THS_IFP16, THS_IFP32, THS_IFR16,
THS_IFREG, THS_IFREL, amiga_letters, theos_letters, unix_letters, vms_workspace,
};
use super::{
ACORN_, AMIGA_, Clock, DEFLATED, ENHDEFLATED, FS_FAT_, FS_HPFS_, FS_NTFS_, FS_VFAT_, IMPLODED,
MVS_, NUM_HOSTS, NUM_METHODS, THEOS_, UNIX_, VM_CMS_, VMS_, extra, next_endprev, ut_mtime,
zi_time, zu, zx,
};
const EF_IZUNIX: u16 = 0x5855;
fn unknown(n: i64) -> Vec<u8> {
let mut b = Vec::new();
m::printf(&mut b, m::UNKNOWN_NO, &[n.to_string().as_bytes()]);
b
}
pub(super) fn zi_long(
uz: &mut Uz,
e: &CentralEntry,
p_endprev: &mut u64,
error_in_archive: i32,
) -> i32 {
let mut error_in_archive = error_in_archive;
if e.relative_offset_local_header != *p_endprev && *p_endprev > 0 {
let gap = e.relative_offset_local_header.wrapping_sub(*p_endprev) as i64;
uz.info_fmt(0, m::EXTRA_BYTES_PRECEDING, &[gap.to_string().as_bytes()]);
}
*p_endprev = next_endprev(e);
let hostnum = e.pinfo.hostnum;
let hostver = e.pinfo.hostver;
let extnum = e.version_needed_to_extract[1].min(NUM_HOSTS);
let extver = e.version_needed_to_extract[0];
let methid = e.compression_method;
let methnum = find_compr_idx(e.compression_method);
uz.info(0, b" ");
fileio::fnprint(uz);
let off = e.relative_offset_local_header;
uz.info_fmt(0, m::LOCAL_HEADER_OFFSET, &[&zu(off), &zx(off)]);
let varmsg = if hostnum >= NUM_HOSTS {
unknown(i64::from(e.version_made_by[1]))
} else {
m::OS_NAMES[usize::from(hostnum)]
.unwrap_or(m::PRINTF_NULL)
.as_bytes()
.to_vec()
};
uz.info_fmt(0, m::HOST_OS, &[&varmsg]);
let (a, b) = ((hostver / 10).to_string(), (hostver % 10).to_string());
uz.info_fmt(0, m::ENCODE_SW_VER, &[a.as_bytes(), b.as_bytes()]);
let varmsg = match m::OS_NAMES.get(usize::from(extnum)).copied().flatten() {
Some(name) => name.as_bytes().to_vec(),
None => unknown(i64::from(e.version_needed_to_extract[1])),
};
uz.info_fmt(0, m::MIN_OS_COMP_REQ, &[&varmsg]);
let (a, b) = ((extver / 10).to_string(), (extver % 10).to_string());
uz.info_fmt(0, m::MIN_SW_VER_REQ, &[a.as_bytes(), b.as_bytes()]);
let varmsg = if methnum >= NUM_METHODS {
unknown(i64::from(e.compression_method))
} else {
m::ZI_LONG_METHODS[methnum].as_bytes().to_vec()
};
uz.info_fmt(0, m::COMPRESS_METHOD, &[&varmsg]);
let flag = e.general_purpose_bit_flag;
if methid == IMPLODED {
let w: &[u8] = if flag & 2 != 0 { b"8" } else { b"4" };
uz.info_fmt(0, m::SLIDE_WINDOW_SIZE_IMPLODE, &[w]);
let t: &[u8] = if flag & 4 != 0 { b"3" } else { b"2" };
uz.info_fmt(0, m::SHANNON_FANO_TREES, &[t]);
} else if methid == DEFLATED || methid == ENHDEFLATED {
let dnum = usize::from((flag >> 1) & 3);
uz.info_fmt(0, m::COMPRESS_SUBTYPE, &[m::DTYPELNG[dnum].as_bytes()]);
}
let not: &[u8] = if flag & 1 != 0 {
b""
} else {
m::NOT_.as_bytes()
};
uz.info_fmt(0, m::FILE_SECURITY, &[not]);
let yes = if flag & 8 != 0 { m::YES } else { m::NO };
uz.info_fmt(0, m::EXTENDED_LOCAL_HDR, &[yes.as_bytes()]);
let dos = e.last_mod_dos_datetime;
let t = zi_time(uz, dos, None, Clock::Local);
uz.info_fmt(0, m::FILE_MOD_DATE, &[&t]);
if let Some(mtime) = ut_mtime(uz.flavour, e) {
let t = zi_time(uz, dos, Some(mtime), Clock::Local);
uz.info_fmt(0, m::UT_FILE_MOD_DATE, &[&t, m::LOCAL_TIME.as_bytes()]);
let t = zi_time(uz, dos, Some(mtime), Clock::Utc);
uz.info_fmt(0, m::UT_FILE_MOD_DATE, &[&t, m::GM_TIME.as_bytes()]);
}
let crc = format!("{:08x}", e.crc32).into_bytes();
uz.info_fmt(0, m::CRC32_VALUE, &[&crc]);
uz.info_fmt(0, m::COMPRESSED_FILE_SIZE, &[&zu(e.csize)]);
uz.info_fmt(0, m::UNCOMPRESSED_FILE_SIZE, &[&zu(e.ucsize)]);
uz.info_fmt(
0,
m::FILENAME_LENGTH,
&[e.filename_length.to_string().as_bytes()],
);
uz.info_fmt(
0,
m::EXTRA_FIELD_LENGTH,
&[e.extra_field_length.to_string().as_bytes()],
);
uz.info_fmt(
0,
m::FILE_COMMENT_LENGTH,
&[e.file_comment_length.to_string().as_bytes()],
);
uz.info_fmt(
0,
m::FILE_DISK_NUM,
&[&zu(e.disk_number_start.wrapping_add(1))],
);
let kind = if e.internal_file_attributes & 1 != 0 {
m::TEXT
} else if e.internal_file_attributes & 2 != 0 {
m::EBCDIC
} else {
m::BINARY
}; uz.info_fmt(0, m::APPARENT_FILE_TYPE, &[kind.as_bytes()]);
let ext = e.external_file_attributes;
let xattr = ((ext >> 16) & 0xFFFF) as u32;
let octal = format!("{xattr:06o}").into_bytes();
if hostnum == VMS_ {
let workspace = vms_workspace(xattr);
let groups: Vec<Vec<u8>> = workspace
.chunks(4)
.map(|g| g.iter().copied().filter(|&c| c != 0).collect())
.collect();
let mut attribs = vec![b'('];
for (j, g) in groups.iter().enumerate() {
attribs.extend_from_slice(g);
attribs.push(b','); if j == 0 {
attribs.extend_from_slice(g);
attribs.push(b',');
}
}
attribs.pop();
attribs.push(b')'); uz.info_fmt(0, m::VMS_FILE_ATTRIBUTES, &[&octal, &attribs]);
} else if hostnum == AMIGA_ {
let mut attribs = Attribs::new(22, 0);
amiga_letters(&mut attribs, xattr);
attribs.set(9, 0); uz.info_fmt(0, m::AMIGA_FILE_ATTRIBUTES, &[&octal, attribs.text()]);
} else if hostnum == THEOS_ {
let ftyp = match xattr & THS_IFMT {
THS_IFLIB => m::THEOS_FTYP_LIB,
THS_IFDIR => m::THEOS_FTYP_DIR,
THS_IFREG => m::THEOS_FTYP_REG,
THS_IFREL => m::THEOS_FTYP_REL,
THS_IFKEY => m::THEOS_FTYP_KEY,
THS_IFIND => m::THEOS_FTYP_IND,
THS_IFR16 => m::THEOS_FTYP_R16,
THS_IFP16 => m::THEOS_FTYP_P16,
THS_IFP32 => m::THEOS_FTYP_P32,
_ => m::THEOS_FTYP_UKN,
};
let mut attribs = Attribs::new(0, 0);
attribs.sprintf_at(0, ftyp.as_bytes());
theos_letters(&mut attribs, xattr, 12);
attribs.set(20, 0);
let hex = format!("{xattr:04X}").into_bytes();
uz.info_fmt(0, m::THEOS_FILE_ATTRIBUTES, &[&hex, attribs.text()]);
} else if !matches!(
hostnum,
FS_FAT_ | FS_HPFS_ | FS_NTFS_ | FS_VFAT_ | ACORN_ | VM_CMS_ | MVS_
) {
let mut attribs = Attribs::new(22, 0);
unix_letters(&mut attribs, xattr, b'l');
attribs.set(10, 0);
uz.info_fmt(0, m::UNIX_FILE_ATTRIBUTES, &[&octal, attribs.text()]);
} else {
let hex = format!("{:06X}", ext >> 8).into_bytes();
uz.info_fmt(0, m::NON_MSDOS_FILE_ATTRIBUTES, &[&hex]);
}
let xattr = (ext & 0xFF) as u32;
let hex = format!("{xattr:02X}").into_bytes();
if xattr == 0 {
uz.info_fmt(0, m::MSDOS_FILE_ATTRIBUTES, &[&hex]);
} else if xattr == 1 {
uz.info_fmt(0, m::MSDOS_FILE_ATTRIBUTES_RO, &[&hex]);
} else {
let mut args: Vec<&[u8]> = vec![&hex];
for (bit, word) in m::MSDOS_ATTR_WORDS.iter().enumerate() {
args.push(if xattr & (1 << bit) != 0 {
word.as_bytes()
} else {
b""
});
}
uz.info_fmt(0, m::MSDOS_FILE_ATTRIBUTES_ALPHA, &args);
}
if e.extra_field_length > 0 {
if error_in_archive > PK_WARN {
return error_in_archive;
}
let Some(ef) = e.extra.as_deref() else {
return PK_ERR; };
extra::dump(uz, ef, hostnum, off, p_endprev);
}
let xattr = ((ext & 0xC000) >> 12) as u32;
if xattr & 8 != 0 {
if matches!(hostnum, UNIX_ | FS_HPFS_ | FS_NTFS_) {
let what = if xattr & 4 != 0 {
m::EF_IZ_UID
} else {
m::EF_IZ_NOUID
};
uz.info_fmt(
0,
m::L_EXTRA_FIELD_TYPE,
&[
m::IS.as_bytes(),
format!("{EF_IZUNIX:04x}").as_bytes(),
m::EF_IZUNIX_NAME.as_bytes(),
(xattr & 12).to_string().as_bytes(),
what.as_bytes(),
],
);
if *p_endprev > 0 {
*p_endprev = p_endprev.wrapping_add(u64::from(xattr & 12));
}
} else if hostnum == FS_FAT_ && xattr & 4 == 0 {
uz.info_fmt(
0,
m::L_EXTRA_FIELD_TYPE,
&[
m::MAY_BE.as_bytes(),
format!("{EF_IZUNIX:04x}").as_bytes(),
m::EF_IZUNIX_NAME.as_bytes(),
b"8",
m::EF_IZ_NOUID.as_bytes(),
],
);
}
}
if e.file_comment_length == 0 {
uz.info(0, m::NO_FILE_COMMENT.as_bytes());
} else {
uz.info(0, m::FILE_COMM_BEGIN.as_bytes());
let pinfo = e.pinfo;
let error = fileio::do_string(
uz,
usize::from(e.file_comment_length),
DoString::Displ8,
Some(&pinfo),
);
if error != PK_COOL {
error_in_archive = error; if error > PK_WARN {
return error;
}
}
uz.info(0, m::FILE_COMM_END.as_bytes());
}
error_in_archive
}