use std::fmt;
use std::path::Path;
use std::fs::File;
use std::io;
use std::io::{Read,Seek,SeekFrom,BufReader};
use std::error;
use std::slice;
pub const MAX_TOC: u64 = 64 * 1024 * 1024;
pub const MAX_ATTRIBUTES: u64 = 1 * 1204 * 1024;
#[derive(Debug, Clone)]
#[repr(C)]
pub struct PackageHeader {
pub magic: u32,
pub header_size: u16,
pub version: u16,
pub total_size: u64,
pub minor_version: u16,
pub heap_compression: u16,
pub heap_chunk_size: u32,
pub heap_size_compressed: u64,
pub heap_size_uncompressed: u64,
pub attributes_length: u32,
pub attributes_strings_length: u32,
pub attributes_strings_count: u32,
pub reserved1: u32,
pub toc_length: u64,
pub toc_strings_length: u64,
pub toc_strings_count: u64,
}
#[derive(Debug, Clone)]
pub struct PackageFileSection {
pub uncompressed_length: u32,
pub data: u8, pub offset: u64,
pub current_offset: u64,
pub strings_length: u64,
pub strings_count: u64,
pub strings: u8, pub name: String,
}
#[derive(Debug, Clone)]
pub struct Package {
pub header: Option<PackageHeader>,
pub name: Option<String>,
pub summary: Option<String>,
pub description: Option<String>,
pub vendor: Option<String>,
pub packager: Option<String>,
pub basepackage: Option<i32>,
pub checksum: Option<String>,
pub installpath: Option<String>,
pub filename: Option<String>,
pub flags: u32,
pub architecture: Option<String>,
}
fn read_struct<T, R: Read>(mut read: R) -> io::Result<T> {
let num_bytes = ::std::mem::size_of::<T>();
unsafe {
let mut s = ::std::mem::zeroed();
let buffer = slice::from_raw_parts_mut(&mut s as *mut T as *mut u8, num_bytes);
match read.read_exact(buffer) {
Ok(()) => Ok(s),
Err(e) => {
Err(e)
}
}
}
}
fn parse_header<P: AsRef<Path>>(hpkg_file: P)
-> Result<PackageHeader, Box<dyn error::Error>> {
let mut f = File::open(hpkg_file.as_ref())?;
f.seek(SeekFrom::Start(0))?;
let reader = BufReader::new(f);
let mut header = read_struct::<PackageHeader, _>(reader)?;
let magic_bytes = header.magic.to_ne_bytes();
if magic_bytes != [b'h', b'p', b'k', b'g'] {
return Err(From::from(format!("Unknown magic: {:?}", magic_bytes)));
}
header.header_size = u16::from_be(header.header_size);
header.version = u16::from_be(header.version);
header.total_size = u64::from_be(header.total_size);
header.minor_version = u16::from_be(header.minor_version);
header.heap_compression = u16::from_be(header.heap_compression);
header.heap_chunk_size = u32::from_be(header.heap_chunk_size);
header.heap_size_compressed = u64::from_be(header.heap_size_compressed);
header.heap_size_uncompressed = u64::from_be(header.heap_size_uncompressed);
header.attributes_length = u32::from_be(header.attributes_length);
header.attributes_strings_length = u32::from_be(header.attributes_strings_length);
header.attributes_strings_count = u32::from_be(header.attributes_strings_count);
header.reserved1 = u32::from_be(header.reserved1);
header.toc_length = u64::from_be(header.toc_length);
header.toc_strings_length = u64::from_be(header.toc_strings_length);
header.toc_strings_count = u64::from_be(header.toc_strings_count);
if header.version != 2 {
return Err(From::from(format!("Unknown repo version: {}", header.version)));
}
if header.minor_version != 0 {
return Err(From::from(format!("Unknown repo minor version: {}", header.version)));
}
Ok(header)
}
impl fmt::Display for Package {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "package. Name {:?}, Vendor {:?}, Summary {:?}, Arch {:?}",
self.name, self.vendor, self.summary, self.architecture)
}
}
impl Package {
pub fn new() -> Package {
Package {
header: None,
name: None,
summary: None,
description: None,
vendor: None,
packager: None,
basepackage: None,
checksum: None,
installpath: None,
filename: None,
flags: 0,
architecture: None,
}
}
pub fn load<P: AsRef<Path>>(hpkg_file: P)
-> Result<Package, Box<dyn error::Error>> {
let mut f = File::open(hpkg_file.as_ref())?;
f.seek(SeekFrom::Start(0))?;
let mut hpkg = Package::new();
hpkg.header = Some(self::parse_header(hpkg_file)?);
return Ok(hpkg);
}
}
#[test]
fn test_new_package() {
let _package = Package::new();
}
#[test]
fn test_load_valid_package() {
let hpkg = match Package::load("sample/ctags_source-5.8-5-source.hpkg") {
Ok(o) => o,
Err(e) => {
println!("ERROR: {}", e);
assert!(false);
return;
},
};
assert!(hpkg.header.is_some());
}
#[test]
fn test_load_invalid_package() {
assert!(Package::load("sample/source-5.8-5-source.hpkg").is_err());
}
#[test]
fn test_total_size() {
let metadata = match std::fs::metadata("sample/ctags_source-5.8-5-source.hpkg") {
Ok(o) => o,
Err(e) => {
println!("ERROR: {}", e);
assert!(false);
return;
},
};
let hpkg = match Package::load("sample/ctags_source-5.8-5-source.hpkg") {
Ok(o) => o,
Err(e) => {
println!("ERROR: {}", e);
assert!(false);
return;
},
};
let header = match hpkg.header {
Some(o) => o,
None => {
println!("ERROR: Invalid Header!");
assert!(false);
return;
},
};
assert_eq!(metadata.len(), header.total_size);
}
#[test]
fn test_dump_package_info() {
let hpkg = match Package::load("sample/ctags_source-5.8-5-source.hpkg") {
Ok(o) => o,
Err(e) => {
println!("ERROR: {}", e);
assert!(false);
return;
},
};
println!("{:?}", hpkg);
}