@@ -101,6 +101,17 @@ pub struct stat {
101101 pub st_ctime : libc:: time_t ,
102102}
103103
104+ /// Path and uncompressed size for a single archive entry.
105+ ///
106+ /// `size` comes from the archive header and may be `0` for formats that do
107+ /// not record it there (some raw compressed streams, ZIP entries using a
108+ /// data descriptor). Tar and standard ZIP populate it reliably.
109+ #[ derive( Clone , Debug ) ]
110+ pub struct ArchiveEntryInfo {
111+ pub path : String ,
112+ pub size : u64 ,
113+ }
114+
104115/// Determine the ownership behavior when unpacking the archive.
105116#[ derive( Clone , Copy , Debug ) ]
106117pub enum Ownership {
@@ -146,6 +157,61 @@ pub(crate) fn decode_utf8(bytes: &[u8]) -> Result<String> {
146157/// # }
147158/// ```
148159pub fn list_archive_files_with_encoding < R > ( source : R , decode : DecodeCallback ) -> Result < Vec < String > >
160+ where
161+ R : Read + Seek ,
162+ {
163+ Ok ( list_archive_entries_with_encoding ( source, decode) ?
164+ . into_iter ( )
165+ . map ( |e| e. path )
166+ . collect ( ) )
167+ }
168+
169+ /// Get all files in a archive using `source` as a reader.
170+ /// # Example
171+ ///
172+ /// ```no_run
173+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
174+ /// use compress_tools::*;
175+ /// use std::fs::File;
176+ ///
177+ /// let mut source = File::open("tree.tar")?;
178+ ///
179+ /// let file_list = list_archive_files(&mut source)?;
180+ /// # Ok(())
181+ /// # }
182+ /// ```
183+ pub fn list_archive_files < R > ( source : R ) -> Result < Vec < String > >
184+ where
185+ R : Read + Seek ,
186+ {
187+ list_archive_files_with_encoding ( source, decode_utf8)
188+ }
189+
190+ /// Get entry metadata (path and uncompressed size) for every entry in an
191+ /// archive without extracting their contents.
192+ ///
193+ /// See [`ArchiveEntryInfo`] for caveats on `size` reporting across formats.
194+ ///
195+ /// # Example
196+ ///
197+ /// ```no_run
198+ /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
199+ /// use compress_tools::*;
200+ /// use std::fs::File;
201+ ///
202+ /// let mut source = File::open("tree.tar")?;
203+ /// let decode_utf8 = |bytes: &[u8]| Ok(std::str::from_utf8(bytes)?.to_owned());
204+ ///
205+ /// for entry in list_archive_entries_with_encoding(&mut source, decode_utf8)? {
206+ /// println!("{}: {} bytes", entry.path, entry.size);
207+ /// }
208+ /// # Ok(())
209+ /// # }
210+ /// ```
211+ pub fn list_archive_entries_with_encoding < R > (
212+ source : R ,
213+ decode : DecodeCallback ,
214+ ) -> Result < Vec < ArchiveEntryInfo > >
149215where
150216 R : Read + Seek ,
151217{
@@ -154,23 +220,28 @@ where
154220 Ownership :: Ignore ,
155221 source,
156222 |archive_reader, _, mut entry| unsafe {
157- let mut file_list = Vec :: new ( ) ;
223+ let mut entries = Vec :: new ( ) ;
158224 loop {
159225 match ffi:: archive_read_next_header ( archive_reader, & mut entry) {
160- ffi:: ARCHIVE_EOF => return Ok ( file_list ) ,
226+ ffi:: ARCHIVE_EOF => return Ok ( entries ) ,
161227 value => archive_result ( value, archive_reader) ?,
162228 }
163229
164230 let _utf8_guard = ffi:: WindowsUTF8LocaleGuard :: new ( ) ;
165231 let cstr = libarchive_entry_pathname ( entry) ?;
166- let file_name = decode ( cstr. to_bytes ( ) ) ?;
167- file_list. push ( file_name) ;
232+ let path = decode ( cstr. to_bytes ( ) ) ?;
233+ let size = libarchive_entry_size ( entry) ;
234+ entries. push ( ArchiveEntryInfo { path, size } ) ;
168235 }
169236 } ,
170237 )
171238}
172239
173- /// Get all files in a archive using `source` as a reader.
240+ /// Get entry metadata (path and uncompressed size) for every entry in an
241+ /// archive without extracting their contents.
242+ ///
243+ /// See [`ArchiveEntryInfo`] for caveats on `size` reporting across formats.
244+ ///
174245/// # Example
175246///
176247/// ```no_run
@@ -180,15 +251,17 @@ where
180251///
181252/// let mut source = File::open("tree.tar")?;
182253///
183- /// let file_list = list_archive_files(&mut source)?;
254+ /// for entry in list_archive_entries(&mut source)? {
255+ /// println!("{}: {} bytes", entry.path, entry.size);
256+ /// }
184257/// # Ok(())
185258/// # }
186259/// ```
187- pub fn list_archive_files < R > ( source : R ) -> Result < Vec < String > >
260+ pub fn list_archive_entries < R > ( source : R ) -> Result < Vec < ArchiveEntryInfo > >
188261where
189262 R : Read + Seek ,
190263{
191- list_archive_files_with_encoding ( source, decode_utf8)
264+ list_archive_entries_with_encoding ( source, decode_utf8)
192265}
193266
194267/// Uncompress a file using the `source` need as reader and the `target` as a
@@ -621,6 +694,13 @@ fn libarchive_copy_data(
621694 }
622695}
623696
697+ fn libarchive_entry_size ( entry : * mut ffi:: archive_entry ) -> u64 {
698+ // `st_size` is `i32` on Windows (see the `stat` struct above) and `i64`
699+ // on Unix. Widen through `i64` to keep the cast platform-agnostic.
700+ let size = unsafe { ( * ffi:: archive_entry_stat ( entry) ) . st_size } as i64 ;
701+ size. max ( 0 ) as u64
702+ }
703+
624704fn libarchive_entry_pathname < ' a > ( entry : * mut ffi:: archive_entry ) -> Result < & ' a CStr > {
625705 let pathname = unsafe { ffi:: archive_entry_pathname ( entry) } ;
626706 if pathname. is_null ( ) {
0 commit comments