use core::iter::Iterator;
use core::str;
#[allow(non_camel_case_types)]
pub enum FileType {
NORMAL,
HLINK,
SYMLINK,
CHAR_DEV,
BLOCK_DEV,
DIR,
FIFO,
CONT,
EXTHDR_META,
EXTHDR_METANXT,
RESERVED(u8),
}
#[derive(Clone)]
pub struct UstarArchiveIter<'a> {
pub archive: &'a [u8],
pub iter_curr: usize,
}
pub fn iter(archive: &[u8]) -> UstarArchiveIter<'_> {
UstarArchiveIter { archive, iter_curr: 0 }
}
impl<'a> Iterator for UstarArchiveIter<'a> {
type Item = UstarFile<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.iter_curr >= self.archive.len() {
return None;
}
let hdr = FileHdr::from_slice(&self.archive[self.iter_curr..]);
let hdr = hdr?;
if !hdr.is_ustar() {
return None;
}
let file_sz = hdr.size() as usize;
let file_start = self.iter_curr + 512;
let ret = Some(UstarFile {
hdr: hdr.clone(),
file: &self.archive[file_start..(file_start + file_sz)],
});
self.iter_curr += (((file_sz + 511) / 512) + 1) * 512;
return ret;
}
}
impl FileType {
pub fn from_u8(flag: u8) -> Self {
match flag as char {
'0' | '\0' => Self::NORMAL,
'1' => Self::HLINK,
'2' => Self::SYMLINK,
'3' => Self::CHAR_DEV,
'5' => Self::DIR,
'6' => Self::FIFO,
'7' => Self::CONT,
'g' => Self::EXTHDR_META,
'x' => Self::EXTHDR_METANXT,
_ => Self::RESERVED(flag),
}
}
}
#[derive(Clone)]
pub struct UstarFile<'a> {
pub hdr: FileHdr<'a>,
pub file: &'a [u8],
}
#[derive(Clone)]
pub struct FileHdr<'a>(&'a [u8]);
impl<'a> FileHdr<'a> {
pub fn name(&self) -> &str {
let file_name = to_cstr(&self.0[0..100]);
str::from_utf8(file_name.unwrap())
.unwrap()
.trim_matches(char::from(0))
}
pub fn owner(&self) -> &str {
let user_name = to_cstr(&self.0[265..265 + 32]);
str::from_utf8(user_name.unwrap()).unwrap()
}
pub fn owner_group(&self) -> &str {
let user_name = to_cstr(&self.0[297..297 + 32]);
str::from_utf8(user_name.unwrap()).unwrap()
}
pub fn size(&self) -> u32 {
let sz = &self.0[124..124 + 11];
oct2bin(sz)
}
pub fn is_ustar(&self) -> bool {
if let Ok(magic) = str::from_utf8(&self.0[257..(257 + 5)]) {
return magic == "ustar";
}
return false;
}
pub fn from_slice(ustar_slice: &'a [u8]) -> Option<Self> {
if ustar_slice.len() < 512 {
return None;
}
Some(Self(&ustar_slice[0..512]))
}
}
fn oct2bin(s: &[u8]) -> u32 {
let mut n: u32 = 0;
for u in s {
n *= 8;
let d = *u - b'0';
n += d as u32;
}
n
}
fn to_cstr(s: &[u8]) -> Option<&[u8]> {
let mut end = 0;
for c in s {
if *c == 0 {
if end == 0 {
return None;
} else {
break;
}
}
end += 1;
}
return Some(&s[0..=end]);
}