ar/
lib.rs

1//! A library for encoding/decoding Unix archive files.
2//!
3//! This library provides utilities necessary to manage [Unix archive
4//! files](https://en.wikipedia.org/wiki/Ar_(Unix)) (as generated by the
5//! standard `ar` command line utility) abstracted over a reader or writer.
6//! This library provides a streaming interface that avoids having to ever load
7//! a full archive entry into memory.
8//!
9//! The API of this crate is meant to be similar to that of the
10//! [`tar`](https://crates.io/crates/tar) crate.
11//!
12//! # Format variants
13//!
14//! Unix archive files come in several variants, of which three are the most
15//! common:
16//!
17//! * The *common variant*, used for Debian package (`.deb`) files among other
18//!   things, which only supports filenames up to 16 characters.
19//! * The *BSD variant*, used by the `ar` utility on BSD systems (including Mac
20//!   OS X), which is backwards-compatible with the common variant, but extends
21//!   it to support longer filenames and filenames containing spaces.
22//! * The *GNU variant*, used by the `ar` utility on GNU and many other systems
23//!   (including Windows), which is similar to the common format, but which
24//!   stores filenames in a slightly different, incompatible way, and has its
25//!   own strategy for supporting long filenames.
26//!
27//! This crate supports reading and writing all three of these variants.
28//!
29//! # Example usage
30//!
31//! Writing an archive:
32//!
33//! ```no_run
34//! use ar::Builder;
35//! use std::fs::File;
36//! // Create a new archive that will be written to foo.a:
37//! let mut builder = Builder::new(File::create("foo.a").unwrap());
38//! // Add foo/bar.txt to the archive, under the name "bar.txt":
39//! builder.append_path("foo/bar.txt").unwrap();
40//! // Add foo/baz.txt to the archive, under the name "hello.txt":
41//! let mut file = File::open("foo/baz.txt").unwrap();
42//! builder.append_file(b"hello.txt", &mut file).unwrap();
43//! ```
44//!
45//! Reading an archive:
46//!
47//! ```no_run
48//! use ar::Archive;
49//! use std::fs::File;
50//! use std::io;
51//! use std::str;
52//! // Read an archive from the file foo.a:
53//! let mut archive = Archive::new(File::open("foo.a").unwrap());
54//! // Iterate over all entries in the archive:
55//! while let Some(entry_result) = archive.next_entry() {
56//!     let mut entry = entry_result.unwrap();
57//!     // Create a new file with the same name as the archive entry:
58//!     let mut file = File::create(
59//!         str::from_utf8(entry.header().identifier()).unwrap(),
60//!     ).unwrap();
61//!     // The Entry object also acts as an io::Read, so we can easily copy the
62//!     // contents of the archive entry into the file:
63//!     io::copy(&mut entry, &mut file).unwrap();
64//! }
65//! ```
66
67#![warn(missing_docs)]
68
69use std::cmp;
70use std::collections::{HashMap, HashSet};
71use std::ffi::OsStr;
72use std::fs::{File, Metadata};
73use std::io::{
74    self, BufRead, BufReader, Error, ErrorKind, Read, Result, Seek, SeekFrom,
75    Write,
76};
77use std::path::Path;
78use std::str;
79
80#[cfg(unix)]
81use std::os::unix::fs::MetadataExt;
82
83#[cfg(unix)]
84use std::os::unix::ffi::OsStrExt;
85
86// ========================================================================= //
87
88fn read_le_u32(r: &mut impl io::Read) -> io::Result<u32> {
89    let mut buf = [0; 4];
90    r.read_exact(&mut buf).map(|()| u32::from_le_bytes(buf))
91}
92
93fn read_be_u32(r: &mut impl io::Read) -> io::Result<u32> {
94    let mut buf = [0; 4];
95    r.read_exact(&mut buf).map(|()| u32::from_be_bytes(buf))
96}
97
98// ========================================================================= //
99
100const GLOBAL_HEADER_LEN: usize = 8;
101const GLOBAL_HEADER: &'static [u8; GLOBAL_HEADER_LEN] = b"!<arch>\n";
102
103const ENTRY_HEADER_LEN: usize = 60;
104
105const BSD_SYMBOL_LOOKUP_TABLE_ID: &[u8] = b"__.SYMDEF";
106const BSD_SORTED_SYMBOL_LOOKUP_TABLE_ID: &[u8] = b"__.SYMDEF SORTED";
107
108const GNU_NAME_TABLE_ID: &str = "//";
109const GNU_SYMBOL_LOOKUP_TABLE_ID: &[u8] = b"/";
110
111// ========================================================================= //
112
113/// Variants of the Unix archive format.
114#[derive(Clone, Copy, Debug, Eq, PartialEq)]
115pub enum Variant {
116    /// Used by Debian package files; allows only short filenames.
117    Common,
118    /// Used by BSD `ar` (and OS X); backwards-compatible with common variant.
119    BSD,
120    /// Used by GNU `ar` (and Windows); incompatible with common variant.
121    GNU,
122}
123
124// ========================================================================= //
125
126/// Representation of an archive entry header.
127#[derive(Clone, Debug, Eq, PartialEq)]
128pub struct Header {
129    identifier: Vec<u8>,
130    mtime: u64,
131    uid: u32,
132    gid: u32,
133    mode: u32,
134    size: u64,
135}
136
137impl Header {
138    /// Creates a header with the given file identifier and size, and all
139    /// other fields set to zero.
140    pub fn new(identifier: Vec<u8>, size: u64) -> Header {
141        Header { identifier, mtime: 0, uid: 0, gid: 0, mode: 0, size }
142    }
143
144    /// Creates a header with the given file identifier and all other fields
145    /// set from the given filesystem metadata.
146    #[cfg(unix)]
147    pub fn from_metadata(identifier: Vec<u8>, meta: &Metadata) -> Header {
148        Header {
149            identifier,
150            mtime: meta.mtime() as u64,
151            uid: meta.uid(),
152            gid: meta.gid(),
153            mode: meta.mode(),
154            size: meta.len(),
155        }
156    }
157
158    #[cfg(not(unix))]
159    pub fn from_metadata(identifier: Vec<u8>, meta: &Metadata) -> Header {
160        Header::new(identifier, meta.len())
161    }
162
163    /// Returns the file identifier.
164    pub fn identifier(&self) -> &[u8] {
165        &self.identifier
166    }
167
168    /// Sets the file identifier.
169    pub fn set_identifier(&mut self, identifier: Vec<u8>) {
170        self.identifier = identifier;
171    }
172
173    /// Returns the last modification time in Unix time format.
174    pub fn mtime(&self) -> u64 {
175        self.mtime
176    }
177
178    /// Sets the last modification time in Unix time format.
179    pub fn set_mtime(&mut self, mtime: u64) {
180        self.mtime = mtime;
181    }
182
183    /// Returns the value of the owner's user ID field.
184    pub fn uid(&self) -> u32 {
185        self.uid
186    }
187
188    /// Sets the value of the owner's user ID field.
189    pub fn set_uid(&mut self, uid: u32) {
190        self.uid = uid;
191    }
192
193    /// Returns the value of the group's user ID field.
194    pub fn gid(&self) -> u32 {
195        self.gid
196    }
197
198    /// Returns the value of the group's user ID field.
199    pub fn set_gid(&mut self, gid: u32) {
200        self.gid = gid;
201    }
202
203    /// Returns the mode bits for this file.
204    pub fn mode(&self) -> u32 {
205        self.mode
206    }
207
208    /// Sets the mode bits for this file.
209    pub fn set_mode(&mut self, mode: u32) {
210        self.mode = mode;
211    }
212
213    /// Returns the length of the file, in bytes.
214    pub fn size(&self) -> u64 {
215        self.size
216    }
217
218    /// Sets the length of the file, in bytes.
219    pub fn set_size(&mut self, size: u64) {
220        self.size = size;
221    }
222
223    /// Parses and returns the next header and its length.  Returns `Ok(None)`
224    /// if we are at EOF.
225    fn read<R>(
226        reader: &mut R,
227        variant: &mut Variant,
228        name_table: &mut Vec<u8>,
229    ) -> Result<Option<(Header, u64)>>
230    where
231        R: Read,
232    {
233        let mut buffer = [0; 60];
234        let bytes_read = reader.read(&mut buffer)?;
235        if bytes_read == 0 {
236            return Ok(None);
237        } else if bytes_read < buffer.len() {
238            if let Err(error) = reader.read_exact(&mut buffer[bytes_read..]) {
239                if error.kind() == ErrorKind::UnexpectedEof {
240                    let msg = "unexpected EOF in the middle of archive entry \
241                               header";
242                    return Err(Error::new(ErrorKind::UnexpectedEof, msg));
243                } else {
244                    let msg = "failed to read archive entry header";
245                    return Err(annotate(error, msg));
246                }
247            }
248        }
249        let mut identifier = buffer[0..16].to_vec();
250        while identifier.last() == Some(&b' ') {
251            identifier.pop();
252        }
253        let mut size = parse_number("file size", &buffer[48..58], 10)?;
254        let mut header_len = ENTRY_HEADER_LEN as u64;
255        if *variant != Variant::BSD && identifier.starts_with(b"/") {
256            *variant = Variant::GNU;
257            if identifier == GNU_SYMBOL_LOOKUP_TABLE_ID {
258                io::copy(&mut reader.by_ref().take(size), &mut io::sink())?;
259                return Ok(Some((Header::new(identifier, size), header_len)));
260            } else if identifier == GNU_NAME_TABLE_ID.as_bytes() {
261                *name_table = vec![0; size as usize];
262                reader.read_exact(name_table as &mut [u8]).map_err(|err| {
263                    annotate(err, "failed to read name table")
264                })?;
265                return Ok(Some((Header::new(identifier, size), header_len)));
266            }
267            let start = parse_number("GNU filename index", &buffer[1..16], 10)?
268                as usize;
269            let end = match name_table[start..]
270                .iter()
271                .position(|&ch| ch == b'/' || ch == b'\x00')
272            {
273                Some(len) => start + len,
274                None => name_table.len(),
275            };
276            identifier = name_table[start..end].to_vec();
277        } else if *variant != Variant::BSD && identifier.ends_with(b"/") {
278            *variant = Variant::GNU;
279            identifier.pop();
280        }
281        let mtime = parse_number("timestamp", &buffer[16..28], 10)?;
282        let uid = if *variant == Variant::GNU {
283            parse_number_permitting_empty("owner ID", &buffer[28..34], 10)?
284        } else {
285            parse_number("owner ID", &buffer[28..34], 10)?
286        } as u32;
287        let gid = if *variant == Variant::GNU {
288            parse_number_permitting_empty("group ID", &buffer[34..40], 10)?
289        } else {
290            parse_number("group ID", &buffer[34..40], 10)?
291        } as u32;
292        let mode = parse_number("file mode", &buffer[40..48], 8)? as u32;
293        if *variant != Variant::GNU && identifier.starts_with(b"#1/") {
294            *variant = Variant::BSD;
295            let padded_length =
296                parse_number("BSD filename length", &buffer[3..16], 10)?;
297            if size < padded_length {
298                let msg = format!(
299                    "Entry size ({}) smaller than extended \
300                                   entry identifier length ({})",
301                    size, padded_length
302                );
303                return Err(Error::new(ErrorKind::InvalidData, msg));
304            }
305            size -= padded_length;
306            header_len += padded_length;
307            let mut id_buffer = vec![0; padded_length as usize];
308            let bytes_read = reader.read(&mut id_buffer)?;
309            if bytes_read < id_buffer.len() {
310                if let Err(error) =
311                    reader.read_exact(&mut id_buffer[bytes_read..])
312                {
313                    if error.kind() == ErrorKind::UnexpectedEof {
314                        let msg = "unexpected EOF in the middle of extended \
315                                   entry identifier";
316                        return Err(Error::new(ErrorKind::UnexpectedEof, msg));
317                    } else {
318                        let msg = "failed to read extended entry identifier";
319                        return Err(annotate(error, msg));
320                    }
321                }
322            }
323            while id_buffer.last() == Some(&0) {
324                id_buffer.pop();
325            }
326            identifier = id_buffer;
327            if identifier == BSD_SYMBOL_LOOKUP_TABLE_ID
328                || identifier == BSD_SORTED_SYMBOL_LOOKUP_TABLE_ID
329            {
330                io::copy(&mut reader.by_ref().take(size), &mut io::sink())?;
331                return Ok(Some((Header::new(identifier, size), header_len)));
332            }
333        }
334        Ok(Some((
335            Header { identifier, mtime, uid, gid, mode, size },
336            header_len,
337        )))
338    }
339
340    fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
341        if self.identifier.len() > 16 || self.identifier.contains(&b' ') {
342            let padding_length = (4 - self.identifier.len() % 4) % 4;
343            let padded_length = self.identifier.len() + padding_length;
344            write!(
345                writer,
346                "#1/{:<13}{:<12}{:<6}{:<6}{:<8o}{:<10}`\n",
347                padded_length,
348                self.mtime,
349                self.uid,
350                self.gid,
351                self.mode,
352                self.size + padded_length as u64
353            )?;
354            writer.write_all(&self.identifier)?;
355            writer.write_all(&vec![0; padding_length])?;
356        } else {
357            writer.write_all(&self.identifier)?;
358            writer.write_all(&vec![b' '; 16 - self.identifier.len()])?;
359            write!(
360                writer,
361                "{:<12}{:<6}{:<6}{:<8o}{:<10}`\n",
362                self.mtime, self.uid, self.gid, self.mode, self.size
363            )?;
364        }
365        Ok(())
366    }
367
368    fn write_gnu<W>(
369        &self,
370        writer: &mut W,
371        names: &HashMap<Vec<u8>, usize>,
372    ) -> Result<()>
373    where
374        W: Write,
375    {
376        if self.identifier.len() > 15 {
377            let offset = names[&self.identifier];
378            write!(writer, "/{:<15}", offset)?;
379        } else {
380            writer.write_all(&self.identifier)?;
381            writer.write_all(b"/")?;
382            writer.write_all(&vec![b' '; 15 - self.identifier.len()])?;
383        }
384        write!(
385            writer,
386            "{:<12}{:<6}{:<6}{:<8o}{:<10}`\n",
387            self.mtime, self.uid, self.gid, self.mode, self.size
388        )?;
389        Ok(())
390    }
391}
392
393fn parse_number(field_name: &str, bytes: &[u8], radix: u32) -> Result<u64> {
394    if let Ok(string) = str::from_utf8(bytes) {
395        if let Ok(value) = u64::from_str_radix(string.trim_end(), radix) {
396            return Ok(value);
397        }
398    }
399    let msg = format!(
400        "Invalid {} field in entry header ({:?})",
401        field_name,
402        String::from_utf8_lossy(bytes)
403    );
404    Err(Error::new(ErrorKind::InvalidData, msg))
405}
406
407/*
408 * Equivalent to parse_number() except for the case of bytes being
409 * all spaces (eg all 0x20) as MS tools emit for UID/GID
410 */
411fn parse_number_permitting_empty(
412    field_name: &str,
413    bytes: &[u8],
414    radix: u32,
415) -> Result<u64> {
416    if let Ok(string) = str::from_utf8(bytes) {
417        let trimmed = string.trim_end();
418        if trimmed.len() == 0 {
419            return Ok(0);
420        } else if let Ok(value) = u64::from_str_radix(trimmed, radix) {
421            return Ok(value);
422        }
423    }
424    let msg = format!(
425        "Invalid {} field in entry header ({:?})",
426        field_name,
427        String::from_utf8_lossy(bytes)
428    );
429    Err(Error::new(ErrorKind::InvalidData, msg))
430}
431
432// ========================================================================= //
433
434struct HeaderAndLocation {
435    header: Header,
436    header_start: u64,
437    data_start: u64,
438}
439
440// ========================================================================= //
441
442/// A structure for reading archives.
443pub struct Archive<R: Read> {
444    reader: R,
445    variant: Variant,
446    name_table: Vec<u8>,
447    entry_headers: Vec<HeaderAndLocation>,
448    new_entry_start: u64,
449    next_entry_index: usize,
450    symbol_table_header: Option<HeaderAndLocation>,
451    symbol_table: Option<Vec<(Vec<u8>, u64)>>,
452    started: bool, // True if we've read past the global header.
453    padding: bool, // True if there's a padding byte before the next entry.
454    scanned: bool, // True if entry_headers is complete.
455    error: bool,   // True if we have encountered an error.
456}
457
458impl<R: Read> Archive<R> {
459    /// Create a new archive reader with the underlying reader object as the
460    /// source of all data read.
461    pub fn new(reader: R) -> Archive<R> {
462        Archive {
463            reader,
464            variant: Variant::Common,
465            name_table: Vec::new(),
466            entry_headers: Vec::new(),
467            new_entry_start: GLOBAL_HEADER_LEN as u64,
468            next_entry_index: 0,
469            symbol_table_header: None,
470            symbol_table: None,
471            started: false,
472            padding: false,
473            scanned: false,
474            error: false,
475        }
476    }
477
478    /// Returns which format variant this archive appears to be so far.
479    ///
480    /// Note that this may not be accurate before the archive has been fully
481    /// read (i.e. before the `next_entry()` method returns `None`).  In
482    /// particular, a new `Archive` object that hasn't yet read any data at all
483    /// will always return `Variant::Common`.
484    pub fn variant(&self) -> Variant {
485        self.variant
486    }
487
488    /// Unwrap this archive reader, returning the underlying reader object.
489    pub fn into_inner(self) -> Result<R> {
490        Ok(self.reader)
491    }
492
493    fn is_name_table_id(&self, identifier: &[u8]) -> bool {
494        self.variant == Variant::GNU
495            && identifier == GNU_NAME_TABLE_ID.as_bytes()
496    }
497
498    fn is_symbol_lookup_table_id(&self, identifier: &[u8]) -> bool {
499        match self.variant {
500            Variant::Common => false,
501            Variant::BSD => {
502                identifier == BSD_SYMBOL_LOOKUP_TABLE_ID
503                    || identifier == BSD_SORTED_SYMBOL_LOOKUP_TABLE_ID
504            }
505            Variant::GNU => identifier == GNU_SYMBOL_LOOKUP_TABLE_ID,
506        }
507    }
508
509    fn read_global_header_if_necessary(&mut self) -> Result<()> {
510        if self.started {
511            return Ok(());
512        }
513        let mut buffer = [0; GLOBAL_HEADER_LEN];
514        match self.reader.read_exact(&mut buffer) {
515            Ok(()) => {}
516            Err(error) => {
517                self.error = true;
518                return Err(annotate(error, "failed to read global header"));
519            }
520        }
521        if &buffer != GLOBAL_HEADER {
522            self.error = true;
523            let msg = "Not an archive file (invalid global header)";
524            return Err(Error::new(ErrorKind::InvalidData, msg));
525        }
526        self.started = true;
527        Ok(())
528    }
529
530    /// Reads the next entry from the archive, or returns None if there are no
531    /// more.
532    pub fn next_entry(&mut self) -> Option<Result<Entry<R>>> {
533        loop {
534            if self.error {
535                return None;
536            }
537            if self.scanned
538                && self.next_entry_index == self.entry_headers.len()
539            {
540                return None;
541            }
542            match self.read_global_header_if_necessary() {
543                Ok(()) => {}
544                Err(error) => return Some(Err(error)),
545            }
546            if self.padding {
547                let mut buffer = [0u8; 1];
548                match self.reader.read_exact(&mut buffer) {
549                    Ok(()) => {
550                        if buffer[0] != b'\n' {
551                            self.error = true;
552                            let msg = format!(
553                                "invalid padding byte ({})",
554                                buffer[0]
555                            );
556                            let error =
557                                Error::new(ErrorKind::InvalidData, msg);
558                            return Some(Err(error));
559                        }
560                    }
561                    Err(error) => {
562                        if error.kind() != ErrorKind::UnexpectedEof {
563                            self.error = true;
564                            let msg = "failed to read padding byte";
565                            return Some(Err(annotate(error, msg)));
566                        }
567                    }
568                }
569                self.padding = false;
570            }
571            let header_start = self.new_entry_start;
572            match Header::read(
573                &mut self.reader,
574                &mut self.variant,
575                &mut self.name_table,
576            ) {
577                Ok(Some((header, header_len))) => {
578                    let size = header.size();
579                    if size % 2 != 0 {
580                        self.padding = true;
581                    }
582                    if self.next_entry_index == self.entry_headers.len() {
583                        self.new_entry_start += header_len + size + (size % 2);
584                    }
585                    if self.is_name_table_id(header.identifier()) {
586                        continue;
587                    }
588                    if self.is_symbol_lookup_table_id(header.identifier()) {
589                        self.symbol_table_header = Some(HeaderAndLocation {
590                            header,
591                            header_start,
592                            data_start: header_start + header_len,
593                        });
594                        continue;
595                    }
596                    if self.next_entry_index == self.entry_headers.len() {
597                        self.entry_headers.push(HeaderAndLocation {
598                            header,
599                            header_start,
600                            data_start: header_start + header_len,
601                        });
602                    }
603                    let header =
604                        &self.entry_headers[self.next_entry_index].header;
605                    self.next_entry_index += 1;
606                    return Some(Ok(Entry {
607                        header,
608                        reader: self.reader.by_ref(),
609                        length: size,
610                        position: 0,
611                    }));
612                }
613                Ok(None) => {
614                    self.scanned = true;
615                    return None;
616                }
617                Err(error) => {
618                    self.error = true;
619                    return Some(Err(error));
620                }
621            }
622        }
623    }
624}
625
626impl<R: Read + Seek> Archive<R> {
627    fn scan_if_necessary(&mut self) -> io::Result<()> {
628        if self.scanned {
629            return Ok(());
630        }
631        self.read_global_header_if_necessary()?;
632        loop {
633            let header_start = self.new_entry_start;
634            self.reader.seek(SeekFrom::Start(header_start))?;
635            if let Some((header, header_len)) = Header::read(
636                &mut self.reader,
637                &mut self.variant,
638                &mut self.name_table,
639            )? {
640                let size = header.size();
641                self.new_entry_start += header_len + size + (size % 2);
642                if self.is_name_table_id(header.identifier()) {
643                    continue;
644                }
645                if self.is_symbol_lookup_table_id(header.identifier()) {
646                    self.symbol_table_header = Some(HeaderAndLocation {
647                        header,
648                        header_start,
649                        data_start: header_start + header_len,
650                    });
651                    continue;
652                }
653                self.entry_headers.push(HeaderAndLocation {
654                    header,
655                    header_start,
656                    data_start: header_start + header_len,
657                });
658            } else {
659                break;
660            }
661        }
662        // Resume our previous position in the file.
663        if self.next_entry_index < self.entry_headers.len() {
664            let offset =
665                self.entry_headers[self.next_entry_index].header_start;
666            self.reader.seek(SeekFrom::Start(offset))?;
667        }
668        self.scanned = true;
669        Ok(())
670    }
671
672    /// Scans the archive and returns the total number of entries in the
673    /// archive (not counting special entries, such as the GNU archive name
674    /// table or symbol table, that are not returned by `next_entry()`).
675    pub fn count_entries(&mut self) -> io::Result<usize> {
676        self.scan_if_necessary()?;
677        Ok(self.entry_headers.len())
678    }
679
680    /// Scans the archive and jumps to the entry at the given index.  Returns
681    /// an error if the index is not less than the result of `count_entries()`.
682    pub fn jump_to_entry(&mut self, index: usize) -> io::Result<Entry<R>> {
683        self.scan_if_necessary()?;
684        if index >= self.entry_headers.len() {
685            let msg = "Entry index out of bounds";
686            return Err(Error::new(ErrorKind::InvalidInput, msg));
687        }
688        let offset = self.entry_headers[index].data_start;
689        self.reader.seek(SeekFrom::Start(offset))?;
690        let header = &self.entry_headers[index].header;
691        let size = header.size();
692        if size % 2 != 0 {
693            self.padding = true;
694        } else {
695            self.padding = false;
696        }
697        self.next_entry_index = index + 1;
698        Ok(Entry {
699            header,
700            reader: self.reader.by_ref(),
701            length: size,
702            position: 0,
703        })
704    }
705
706    fn parse_symbol_table_if_necessary(&mut self) -> io::Result<()> {
707        self.scan_if_necessary()?;
708        if self.symbol_table.is_some() {
709            return Ok(());
710        }
711        if let Some(ref header_and_loc) = self.symbol_table_header {
712            let offset = header_and_loc.data_start;
713            self.reader.seek(SeekFrom::Start(offset))?;
714            let mut reader = BufReader::new(
715                self.reader.by_ref().take(header_and_loc.header.size()),
716            );
717            if self.variant == Variant::GNU {
718                let num_symbols = read_be_u32(&mut reader)? as usize;
719                let mut symbol_offsets =
720                    Vec::<u32>::with_capacity(num_symbols);
721                for _ in 0..num_symbols {
722                    let offset = read_be_u32(&mut reader)?;
723                    symbol_offsets.push(offset);
724                }
725                let mut symbol_table = Vec::with_capacity(num_symbols);
726                for offset in symbol_offsets.into_iter() {
727                    let mut buffer = Vec::<u8>::new();
728                    reader.read_until(0, &mut buffer)?;
729                    if buffer.last() == Some(&0) {
730                        buffer.pop();
731                    }
732                    buffer.shrink_to_fit();
733                    symbol_table.push((buffer, offset as u64));
734                }
735                self.symbol_table = Some(symbol_table);
736            } else {
737                let num_symbols = (read_le_u32(&mut reader)? / 8) as usize;
738                let mut symbol_offsets =
739                    Vec::<(u32, u32)>::with_capacity(num_symbols);
740                for _ in 0..num_symbols {
741                    let str_offset = read_le_u32(&mut reader)?;
742                    let file_offset = read_le_u32(&mut reader)?;
743                    symbol_offsets.push((str_offset, file_offset));
744                }
745                let str_table_len = read_le_u32(&mut reader)?;
746                let mut str_table_data = vec![0u8; str_table_len as usize];
747                reader.read_exact(&mut str_table_data).map_err(|err| {
748                    annotate(err, "failed to read string table")
749                })?;
750                let mut symbol_table = Vec::with_capacity(num_symbols);
751                for (str_start, file_offset) in symbol_offsets.into_iter() {
752                    let str_start = str_start as usize;
753                    let mut str_end = str_start;
754                    while str_end < str_table_data.len()
755                        && str_table_data[str_end] != 0u8
756                    {
757                        str_end += 1;
758                    }
759                    let string = &str_table_data[str_start..str_end];
760                    symbol_table.push((string.to_vec(), file_offset as u64));
761                }
762                self.symbol_table = Some(symbol_table);
763            }
764        }
765        // Resume our previous position in the file.
766        if self.entry_headers.len() > 0 {
767            let offset =
768                self.entry_headers[self.next_entry_index].header_start;
769            self.reader.seek(SeekFrom::Start(offset))?;
770        }
771        Ok(())
772    }
773
774    /// Scans the archive and returns an iterator over the symbols in the
775    /// archive's symbol table.  If the archive doesn't have a symbol table,
776    /// this method will still succeed, but the iterator won't produce any
777    /// values.
778    pub fn symbols(&mut self) -> io::Result<Symbols<R>> {
779        self.parse_symbol_table_if_necessary()?;
780        Ok(Symbols { archive: self, index: 0 })
781    }
782}
783
784// ========================================================================= //
785
786/// Representation of an archive entry.
787///
788/// `Entry` objects implement the `Read` trait, and can be used to extract the
789/// data from this archive entry.  If the underlying reader supports the `Seek`
790/// trait, then the `Entry` object supports `Seek` as well.
791pub struct Entry<'a, R: 'a + Read> {
792    header: &'a Header,
793    reader: &'a mut R,
794    length: u64,
795    position: u64,
796}
797
798impl<'a, R: 'a + Read> Entry<'a, R> {
799    /// Returns the header for this archive entry.
800    pub fn header(&self) -> &Header {
801        self.header
802    }
803}
804
805impl<'a, R: 'a + Read> Read for Entry<'a, R> {
806    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
807        debug_assert!(self.position <= self.length);
808        if self.position == self.length {
809            return Ok(0);
810        }
811        let max_len =
812            cmp::min(self.length - self.position, buf.len() as u64) as usize;
813        let bytes_read = self.reader.read(&mut buf[0..max_len])?;
814        self.position += bytes_read as u64;
815        debug_assert!(self.position <= self.length);
816        Ok(bytes_read)
817    }
818}
819
820impl<'a, R: 'a + Read + Seek> Seek for Entry<'a, R> {
821    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
822        let delta = match pos {
823            SeekFrom::Start(offset) => offset as i64 - self.position as i64,
824            SeekFrom::End(offset) => {
825                self.length as i64 + offset - self.position as i64
826            }
827            SeekFrom::Current(delta) => delta,
828        };
829        let new_position = self.position as i64 + delta;
830        if new_position < 0 {
831            let msg = format!(
832                "Invalid seek to negative position ({})",
833                new_position
834            );
835            return Err(Error::new(ErrorKind::InvalidInput, msg));
836        }
837        let new_position = new_position as u64;
838        if new_position > self.length {
839            let msg = format!(
840                "Invalid seek to position past end of entry ({} vs. {})",
841                new_position, self.length
842            );
843            return Err(Error::new(ErrorKind::InvalidInput, msg));
844        }
845        self.reader.seek(SeekFrom::Current(delta))?;
846        self.position = new_position;
847        Ok(self.position)
848    }
849}
850
851impl<'a, R: 'a + Read> Drop for Entry<'a, R> {
852    fn drop(&mut self) {
853        if self.position < self.length {
854            // Consume the rest of the data in this entry.
855            let mut remaining = self.reader.take(self.length - self.position);
856            let _ = io::copy(&mut remaining, &mut io::sink());
857        }
858    }
859}
860
861// ========================================================================= //
862
863/// An iterator over the symbols in the symbol table of an archive.
864pub struct Symbols<'a, R: 'a + Read> {
865    archive: &'a Archive<R>,
866    index: usize,
867}
868
869impl<'a, R: Read> Iterator for Symbols<'a, R> {
870    type Item = &'a [u8];
871
872    fn next(&mut self) -> Option<&'a [u8]> {
873        if let Some(ref table) = self.archive.symbol_table {
874            if self.index < table.len() {
875                let next = table[self.index].0.as_slice();
876                self.index += 1;
877                return Some(next);
878            }
879        }
880        None
881    }
882
883    fn size_hint(&self) -> (usize, Option<usize>) {
884        let remaining = if let Some(ref table) = self.archive.symbol_table {
885            table.len() - self.index
886        } else {
887            0
888        };
889        (remaining, Some(remaining))
890    }
891}
892
893impl<'a, R: Read> ExactSizeIterator for Symbols<'a, R> {}
894
895// ========================================================================= //
896
897/// A structure for building Common or BSD-variant archives (the archive format
898/// typically used on e.g. BSD and Mac OS X systems).
899///
900/// This structure has methods for building up an archive from scratch into any
901/// arbitrary writer.
902pub struct Builder<W: Write> {
903    writer: W,
904    started: bool,
905}
906
907impl<W: Write> Builder<W> {
908    /// Create a new archive builder with the underlying writer object as the
909    /// destination of all data written.
910    pub fn new(writer: W) -> Builder<W> {
911        Builder { writer, started: false }
912    }
913
914    /// Unwrap this archive builder, returning the underlying writer object.
915    pub fn into_inner(self) -> Result<W> {
916        Ok(self.writer)
917    }
918
919    /// Adds a new entry to this archive.
920    pub fn append<R: Read>(
921        &mut self,
922        header: &Header,
923        mut data: R,
924    ) -> Result<()> {
925        if !self.started {
926            self.writer.write_all(GLOBAL_HEADER)?;
927            self.started = true;
928        }
929        header.write(&mut self.writer)?;
930        let actual_size = io::copy(&mut data, &mut self.writer)?;
931        if actual_size != header.size() {
932            let msg = format!(
933                "Wrong file size (header.size() = {}, actual \
934                               size was {})",
935                header.size(),
936                actual_size
937            );
938            return Err(Error::new(ErrorKind::InvalidData, msg));
939        }
940        if actual_size % 2 != 0 {
941            self.writer.write_all(&['\n' as u8])?;
942        }
943        Ok(())
944    }
945
946    /// Adds a file on the local filesystem to this archive, using the file
947    /// name as its identifier.
948    pub fn append_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
949        let name: &OsStr = path.as_ref().file_name().ok_or_else(|| {
950            let msg = "Given path doesn't have a file name";
951            Error::new(ErrorKind::InvalidInput, msg)
952        })?;
953        let identifier = osstr_to_bytes(name)?;
954        let mut file = File::open(&path)?;
955        self.append_file_id(identifier, &mut file)
956    }
957
958    /// Adds a file to this archive, with the given name as its identifier.
959    pub fn append_file(&mut self, name: &[u8], file: &mut File) -> Result<()> {
960        self.append_file_id(name.to_vec(), file)
961    }
962
963    fn append_file_id(&mut self, id: Vec<u8>, file: &mut File) -> Result<()> {
964        let metadata = file.metadata()?;
965        let header = Header::from_metadata(id, &metadata);
966        self.append(&header, file)
967    }
968}
969
970// ========================================================================= //
971
972/// A structure for building GNU-variant archives (the archive format typically
973/// used on e.g. GNU/Linux and Windows systems).
974///
975/// This structure has methods for building up an archive from scratch into any
976/// arbitrary writer.
977pub struct GnuBuilder<W: Write> {
978    writer: W,
979    short_names: HashSet<Vec<u8>>,
980    long_names: HashMap<Vec<u8>, usize>,
981    name_table_size: usize,
982    name_table_needs_padding: bool,
983    started: bool,
984}
985
986impl<W: Write> GnuBuilder<W> {
987    /// Create a new archive builder with the underlying writer object as the
988    /// destination of all data written.  The `identifiers` parameter must give
989    /// the complete list of entry identifiers that will be included in this
990    /// archive.
991    pub fn new(writer: W, identifiers: Vec<Vec<u8>>) -> GnuBuilder<W> {
992        let mut short_names = HashSet::<Vec<u8>>::new();
993        let mut long_names = HashMap::<Vec<u8>, usize>::new();
994        let mut name_table_size: usize = 0;
995        for identifier in identifiers.into_iter() {
996            let length = identifier.len();
997            if length > 15 {
998                long_names.insert(identifier, name_table_size);
999                name_table_size += length + 2;
1000            } else {
1001                short_names.insert(identifier);
1002            }
1003        }
1004        let name_table_needs_padding = name_table_size % 2 != 0;
1005        if name_table_needs_padding {
1006            name_table_size += 3; // ` /\n`
1007        }
1008
1009        GnuBuilder {
1010            writer,
1011            short_names,
1012            long_names,
1013            name_table_size,
1014            name_table_needs_padding,
1015            started: false,
1016        }
1017    }
1018
1019    /// Unwrap this archive builder, returning the underlying writer object.
1020    pub fn into_inner(self) -> Result<W> {
1021        Ok(self.writer)
1022    }
1023
1024    /// Adds a new entry to this archive.
1025    pub fn append<R: Read>(
1026        &mut self,
1027        header: &Header,
1028        mut data: R,
1029    ) -> Result<()> {
1030        let is_long_name = header.identifier().len() > 15;
1031        let has_name = if is_long_name {
1032            self.long_names.contains_key(header.identifier())
1033        } else {
1034            self.short_names.contains(header.identifier())
1035        };
1036        if !has_name {
1037            let msg = format!(
1038                "Identifier {:?} was not in the list of \
1039                 identifiers passed to GnuBuilder::new()",
1040                String::from_utf8_lossy(header.identifier())
1041            );
1042            return Err(Error::new(ErrorKind::InvalidInput, msg));
1043        }
1044
1045        if !self.started {
1046            self.writer.write_all(GLOBAL_HEADER)?;
1047            if !self.long_names.is_empty() {
1048                write!(
1049                    self.writer,
1050                    "{:<48}{:<10}`\n",
1051                    GNU_NAME_TABLE_ID, self.name_table_size
1052                )?;
1053                let mut entries: Vec<(usize, &[u8])> = self
1054                    .long_names
1055                    .iter()
1056                    .map(|(id, &start)| (start, id.as_slice()))
1057                    .collect();
1058                entries.sort();
1059                for (_, id) in entries {
1060                    self.writer.write_all(id)?;
1061                    self.writer.write_all(b"/\n")?;
1062                }
1063                if self.name_table_needs_padding {
1064                    self.writer.write_all(b" /\n")?;
1065                }
1066            }
1067            self.started = true;
1068        }
1069
1070        header.write_gnu(&mut self.writer, &self.long_names)?;
1071        let actual_size = io::copy(&mut data, &mut self.writer)?;
1072        if actual_size != header.size() {
1073            let msg = format!(
1074                "Wrong file size (header.size() = {}, actual \
1075                               size was {})",
1076                header.size(),
1077                actual_size
1078            );
1079            return Err(Error::new(ErrorKind::InvalidData, msg));
1080        }
1081        if actual_size % 2 != 0 {
1082            self.writer.write_all(&['\n' as u8])?;
1083        }
1084
1085        Ok(())
1086    }
1087
1088    /// Adds a file on the local filesystem to this archive, using the file
1089    /// name as its identifier.
1090    pub fn append_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1091        let name: &OsStr = path.as_ref().file_name().ok_or_else(|| {
1092            let msg = "Given path doesn't have a file name";
1093            Error::new(ErrorKind::InvalidInput, msg)
1094        })?;
1095        let identifier = osstr_to_bytes(name)?;
1096        let mut file = File::open(&path)?;
1097        self.append_file_id(identifier, &mut file)
1098    }
1099
1100    /// Adds a file to this archive, with the given name as its identifier.
1101    pub fn append_file(&mut self, name: &[u8], file: &mut File) -> Result<()> {
1102        self.append_file_id(name.to_vec(), file)
1103    }
1104
1105    fn append_file_id(&mut self, id: Vec<u8>, file: &mut File) -> Result<()> {
1106        let metadata = file.metadata()?;
1107        let header = Header::from_metadata(id, &metadata);
1108        self.append(&header, file)
1109    }
1110}
1111
1112// ========================================================================= //
1113
1114#[cfg(unix)]
1115fn osstr_to_bytes(string: &OsStr) -> Result<Vec<u8>> {
1116    Ok(string.as_bytes().to_vec())
1117}
1118
1119#[cfg(not(unix))]
1120fn osstr_to_bytes(string: &OsStr) -> Result<Vec<u8>> {
1121    let utf8: &str = string.to_str().ok_or_else(|| {
1122        Error::new(ErrorKind::InvalidData, "Non-UTF8 file name")
1123    })?;
1124    Ok(utf8.as_bytes().to_vec())
1125}
1126
1127// ========================================================================= //
1128
1129fn annotate(error: io::Error, msg: &str) -> io::Error {
1130    let kind = error.kind();
1131    if let Some(inner) = error.into_inner() {
1132        io::Error::new(kind, format!("{}: {}", msg, inner))
1133    } else {
1134        io::Error::new(kind, msg)
1135    }
1136}
1137
1138// ========================================================================= //
1139
1140#[cfg(test)]
1141mod tests {
1142    use super::{Archive, Builder, GnuBuilder, Header, Variant};
1143    use std::io::{Cursor, Read, Result, Seek, SeekFrom};
1144    use std::str;
1145
1146    struct SlowReader<'a> {
1147        current_position: usize,
1148        buffer: &'a [u8],
1149    }
1150
1151    impl<'a> Read for SlowReader<'a> {
1152        fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
1153            if self.current_position >= self.buffer.len() {
1154                return Ok(0);
1155            }
1156            buf[0] = self.buffer[self.current_position];
1157            self.current_position += 1;
1158            return Ok(1);
1159        }
1160    }
1161
1162    #[test]
1163    fn build_common_archive() {
1164        let mut builder = Builder::new(Vec::new());
1165        let mut header1 = Header::new(b"foo.txt".to_vec(), 7);
1166        header1.set_mtime(1487552916);
1167        header1.set_uid(501);
1168        header1.set_gid(20);
1169        header1.set_mode(0o100644);
1170        builder.append(&header1, "foobar\n".as_bytes()).unwrap();
1171        let header2 = Header::new(b"baz.txt".to_vec(), 4);
1172        builder.append(&header2, "baz\n".as_bytes()).unwrap();
1173        let actual = builder.into_inner().unwrap();
1174        let expected = "\
1175        !<arch>\n\
1176        foo.txt         1487552916  501   20    100644  7         `\n\
1177        foobar\n\n\
1178        baz.txt         0           0     0     0       4         `\n\
1179        baz\n";
1180        assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1181    }
1182
1183    #[test]
1184    fn build_bsd_archive_with_long_filenames() {
1185        let mut builder = Builder::new(Vec::new());
1186        let mut header1 = Header::new(b"short".to_vec(), 1);
1187        header1.set_identifier(b"this_is_a_very_long_filename.txt".to_vec());
1188        header1.set_mtime(1487552916);
1189        header1.set_uid(501);
1190        header1.set_gid(20);
1191        header1.set_mode(0o100644);
1192        header1.set_size(7);
1193        builder.append(&header1, "foobar\n".as_bytes()).unwrap();
1194        let header2 = Header::new(
1195            b"and_this_is_another_very_long_filename.txt".to_vec(),
1196            4,
1197        );
1198        builder.append(&header2, "baz\n".as_bytes()).unwrap();
1199        let actual = builder.into_inner().unwrap();
1200        let expected = "\
1201        !<arch>\n\
1202        #1/32           1487552916  501   20    100644  39        `\n\
1203        this_is_a_very_long_filename.txtfoobar\n\n\
1204        #1/44           0           0     0     0       48        `\n\
1205        and_this_is_another_very_long_filename.txt\x00\x00baz\n";
1206        assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1207    }
1208
1209    #[test]
1210    fn build_bsd_archive_with_space_in_filename() {
1211        let mut builder = Builder::new(Vec::new());
1212        let header = Header::new(b"foo bar".to_vec(), 4);
1213        builder.append(&header, "baz\n".as_bytes()).unwrap();
1214        let actual = builder.into_inner().unwrap();
1215        let expected = "\
1216        !<arch>\n\
1217        #1/8            0           0     0     0       12        `\n\
1218        foo bar\x00baz\n";
1219        assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1220    }
1221
1222    #[test]
1223    fn build_gnu_archive() {
1224        let names = vec![b"baz.txt".to_vec(), b"foo.txt".to_vec()];
1225        let mut builder = GnuBuilder::new(Vec::new(), names);
1226        let mut header1 = Header::new(b"foo.txt".to_vec(), 7);
1227        header1.set_mtime(1487552916);
1228        header1.set_uid(501);
1229        header1.set_gid(20);
1230        header1.set_mode(0o100644);
1231        builder.append(&header1, "foobar\n".as_bytes()).unwrap();
1232        let header2 = Header::new(b"baz.txt".to_vec(), 4);
1233        builder.append(&header2, "baz\n".as_bytes()).unwrap();
1234        let actual = builder.into_inner().unwrap();
1235        let expected = "\
1236        !<arch>\n\
1237        foo.txt/        1487552916  501   20    100644  7         `\n\
1238        foobar\n\n\
1239        baz.txt/        0           0     0     0       4         `\n\
1240        baz\n";
1241        assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1242    }
1243
1244    #[test]
1245    fn build_gnu_archive_with_long_filenames() {
1246        let names = vec![
1247            b"this_is_a_very_long_filename.txt".to_vec(),
1248            b"and_this_is_another_very_long_filename.txt".to_vec(),
1249        ];
1250        let mut builder = GnuBuilder::new(Vec::new(), names);
1251        let mut header1 = Header::new(b"short".to_vec(), 1);
1252        header1.set_identifier(b"this_is_a_very_long_filename.txt".to_vec());
1253        header1.set_mtime(1487552916);
1254        header1.set_uid(501);
1255        header1.set_gid(20);
1256        header1.set_mode(0o100644);
1257        header1.set_size(7);
1258        builder.append(&header1, "foobar\n".as_bytes()).unwrap();
1259        let header2 = Header::new(
1260            b"and_this_is_another_very_long_filename.txt".to_vec(),
1261            4,
1262        );
1263        builder.append(&header2, "baz\n".as_bytes()).unwrap();
1264        let actual = builder.into_inner().unwrap();
1265        let expected = "\
1266        !<arch>\n\
1267        //                                              78        `\n\
1268        this_is_a_very_long_filename.txt/\n\
1269        and_this_is_another_very_long_filename.txt/\n\
1270        /0              1487552916  501   20    100644  7         `\n\
1271        foobar\n\n\
1272        /34             0           0     0     0       4         `\n\
1273        baz\n";
1274        assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1275    }
1276
1277    #[test]
1278    fn build_gnu_archive_with_space_in_filename() {
1279        let names = vec![b"foo bar".to_vec()];
1280        let mut builder = GnuBuilder::new(Vec::new(), names);
1281        let header = Header::new(b"foo bar".to_vec(), 4);
1282        builder.append(&header, "baz\n".as_bytes()).unwrap();
1283        let actual = builder.into_inner().unwrap();
1284        let expected = "\
1285        !<arch>\n\
1286        foo bar/        0           0     0     0       4         `\n\
1287        baz\n";
1288        assert_eq!(str::from_utf8(&actual).unwrap(), expected);
1289    }
1290
1291    #[test]
1292    #[should_panic(
1293        expected = "Identifier \\\"bar\\\" was not in the list of \
1294                               identifiers passed to GnuBuilder::new()"
1295    )]
1296    fn build_gnu_archive_with_unexpected_identifier() {
1297        let names = vec![b"foo".to_vec()];
1298        let mut builder = GnuBuilder::new(Vec::new(), names);
1299        let header = Header::new(b"bar".to_vec(), 4);
1300        builder.append(&header, "baz\n".as_bytes()).unwrap();
1301    }
1302
1303    #[test]
1304    fn read_common_archive() {
1305        let input = "\
1306        !<arch>\n\
1307        foo.txt         1487552916  501   20    100644  7         `\n\
1308        foobar\n\n\
1309        bar.awesome.txt 1487552919  501   20    100644  22        `\n\
1310        This file is awesome!\n\
1311        baz.txt         1487552349  42    12345 100664  4         `\n\
1312        baz\n";
1313        let reader =
1314            SlowReader { current_position: 0, buffer: input.as_bytes() };
1315        let mut archive = Archive::new(reader);
1316        {
1317            // Parse the first entry and check the header values.
1318            let mut entry = archive.next_entry().unwrap().unwrap();
1319            assert_eq!(entry.header().identifier(), b"foo.txt");
1320            assert_eq!(entry.header().mtime(), 1487552916);
1321            assert_eq!(entry.header().uid(), 501);
1322            assert_eq!(entry.header().gid(), 20);
1323            assert_eq!(entry.header().mode(), 0o100644);
1324            assert_eq!(entry.header().size(), 7);
1325            // Read the first few bytes of the entry data and make sure they're
1326            // correct.
1327            let mut buffer = [0; 4];
1328            entry.read_exact(&mut buffer).unwrap();
1329            assert_eq!(&buffer, "foob".as_bytes());
1330            // Dropping the Entry object should automatically consume the rest
1331            // of the entry data so that the archive reader is ready to parse
1332            // the next entry.
1333        }
1334        {
1335            // Parse the second entry and check a couple header values.
1336            let mut entry = archive.next_entry().unwrap().unwrap();
1337            assert_eq!(entry.header().identifier(), b"bar.awesome.txt");
1338            assert_eq!(entry.header().size(), 22);
1339            // Read in all the entry data.
1340            let mut buffer = Vec::new();
1341            entry.read_to_end(&mut buffer).unwrap();
1342            assert_eq!(&buffer as &[u8], "This file is awesome!\n".as_bytes());
1343        }
1344        {
1345            // Parse the third entry and check a couple header values.
1346            let entry = archive.next_entry().unwrap().unwrap();
1347            assert_eq!(entry.header().identifier(), b"baz.txt");
1348            assert_eq!(entry.header().size(), 4);
1349        }
1350        assert!(archive.next_entry().is_none());
1351        assert_eq!(archive.variant(), Variant::Common);
1352    }
1353
1354    #[test]
1355    fn read_bsd_archive_with_long_filenames() {
1356        let input = "\
1357        !<arch>\n\
1358        #1/32           1487552916  501   20    100644  39        `\n\
1359        this_is_a_very_long_filename.txtfoobar\n\n\
1360        #1/44           0           0     0     0       48        `\n\
1361        and_this_is_another_very_long_filename.txt\x00\x00baz\n";
1362        let mut archive = Archive::new(input.as_bytes());
1363        {
1364            // Parse the first entry and check the header values.
1365            let mut entry = archive.next_entry().unwrap().unwrap();
1366            assert_eq!(
1367                entry.header().identifier(),
1368                "this_is_a_very_long_filename.txt".as_bytes()
1369            );
1370            assert_eq!(entry.header().mtime(), 1487552916);
1371            assert_eq!(entry.header().uid(), 501);
1372            assert_eq!(entry.header().gid(), 20);
1373            assert_eq!(entry.header().mode(), 0o100644);
1374            // We should get the size of the actual file, not including the
1375            // filename, even though this is not the value that's in the size
1376            // field in the input.
1377            assert_eq!(entry.header().size(), 7);
1378            // Read in the entry data; we should get only the payload and not
1379            // the filename.
1380            let mut buffer = Vec::new();
1381            entry.read_to_end(&mut buffer).unwrap();
1382            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1383        }
1384        {
1385            // Parse the second entry and check a couple header values.
1386            let mut entry = archive.next_entry().unwrap().unwrap();
1387            assert_eq!(
1388                entry.header().identifier(),
1389                "and_this_is_another_very_long_filename.txt".as_bytes()
1390            );
1391            assert_eq!(entry.header().size(), 4);
1392            // Read in the entry data; we should get only the payload and not
1393            // the filename or the padding bytes.
1394            let mut buffer = Vec::new();
1395            entry.read_to_end(&mut buffer).unwrap();
1396            assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1397        }
1398        assert!(archive.next_entry().is_none());
1399        assert_eq!(archive.variant(), Variant::BSD);
1400    }
1401
1402    #[test]
1403    fn read_bsd_archive_with_space_in_filename() {
1404        let input = "\
1405        !<arch>\n\
1406        #1/8            0           0     0     0       12        `\n\
1407        foo bar\x00baz\n";
1408        let mut archive = Archive::new(input.as_bytes());
1409        {
1410            let mut entry = archive.next_entry().unwrap().unwrap();
1411            assert_eq!(entry.header().identifier(), "foo bar".as_bytes());
1412            assert_eq!(entry.header().size(), 4);
1413            let mut buffer = Vec::new();
1414            entry.read_to_end(&mut buffer).unwrap();
1415            assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1416        }
1417        assert!(archive.next_entry().is_none());
1418        assert_eq!(archive.variant(), Variant::BSD);
1419    }
1420
1421    #[test]
1422    fn read_gnu_archive() {
1423        let input = "\
1424        !<arch>\n\
1425        foo.txt/        1487552916  501   20    100644  7         `\n\
1426        foobar\n\n\
1427        bar.awesome.txt/1487552919  501   20    100644  22        `\n\
1428        This file is awesome!\n\
1429        baz.txt/        1487552349  42    12345 100664  4         `\n\
1430        baz\n";
1431        let mut archive = Archive::new(input.as_bytes());
1432        {
1433            let entry = archive.next_entry().unwrap().unwrap();
1434            assert_eq!(entry.header().identifier(), "foo.txt".as_bytes());
1435            assert_eq!(entry.header().size(), 7);
1436        }
1437        {
1438            let entry = archive.next_entry().unwrap().unwrap();
1439            assert_eq!(
1440                entry.header().identifier(),
1441                "bar.awesome.txt".as_bytes()
1442            );
1443            assert_eq!(entry.header().size(), 22);
1444        }
1445        {
1446            let entry = archive.next_entry().unwrap().unwrap();
1447            assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1448            assert_eq!(entry.header().size(), 4);
1449        }
1450        assert!(archive.next_entry().is_none());
1451        assert_eq!(archive.variant(), Variant::GNU);
1452    }
1453
1454    #[test]
1455    fn read_gnu_archive_with_long_filenames() {
1456        let input = "\
1457        !<arch>\n\
1458        //                                              78        `\n\
1459        this_is_a_very_long_filename.txt/\n\
1460        and_this_is_another_very_long_filename.txt/\n\
1461        /0              1487552916  501   20    100644  7         `\n\
1462        foobar\n\n\
1463        /34             0           0     0     0       4         `\n\
1464        baz\n";
1465        let mut archive = Archive::new(input.as_bytes());
1466        {
1467            let mut entry = archive.next_entry().unwrap().unwrap();
1468            assert_eq!(
1469                entry.header().identifier(),
1470                "this_is_a_very_long_filename.txt".as_bytes()
1471            );
1472            assert_eq!(entry.header().mtime(), 1487552916);
1473            assert_eq!(entry.header().uid(), 501);
1474            assert_eq!(entry.header().gid(), 20);
1475            assert_eq!(entry.header().mode(), 0o100644);
1476            assert_eq!(entry.header().size(), 7);
1477            let mut buffer = Vec::new();
1478            entry.read_to_end(&mut buffer).unwrap();
1479            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1480        }
1481        {
1482            let mut entry = archive.next_entry().unwrap().unwrap();
1483            assert_eq!(
1484                entry.header().identifier(),
1485                "and_this_is_another_very_long_filename.txt".as_bytes()
1486            );
1487            assert_eq!(entry.header().size(), 4);
1488            let mut buffer = Vec::new();
1489            entry.read_to_end(&mut buffer).unwrap();
1490            assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1491        }
1492        assert!(archive.next_entry().is_none());
1493        assert_eq!(archive.variant(), Variant::GNU);
1494    }
1495
1496    // MS `.lib` files are very similar to GNU `ar` archives, but with a few
1497    // tweaks:
1498    // * File names in the name table are terminated by null, rather than /\n
1499    // * Numeric entries may be all empty string, interpreted as 0, possibly?
1500    #[test]
1501    fn read_ms_archive_with_long_filenames() {
1502        let input = "\
1503        !<arch>\n\
1504        //                                              76        `\n\
1505        this_is_a_very_long_filename.txt\x00\
1506        and_this_is_another_very_long_filename.txt\x00\
1507        /0              1487552916              100644  7         `\n\
1508        foobar\n\n\
1509        /33             1446790218              100666  4         `\n\
1510        baz\n";
1511        let mut archive = Archive::new(input.as_bytes());
1512        {
1513            let mut entry = archive.next_entry().unwrap().unwrap();
1514            assert_eq!(
1515                entry.header().identifier(),
1516                "this_is_a_very_long_filename.txt".as_bytes()
1517            );
1518            assert_eq!(entry.header().mtime(), 1487552916);
1519            assert_eq!(entry.header().uid(), 0);
1520            assert_eq!(entry.header().gid(), 0);
1521            assert_eq!(entry.header().mode(), 0o100644);
1522            assert_eq!(entry.header().size(), 7);
1523            let mut buffer = Vec::new();
1524            entry.read_to_end(&mut buffer).unwrap();
1525            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1526        }
1527        {
1528            let mut entry = archive.next_entry().unwrap().unwrap();
1529            assert_eq!(
1530                entry.header().identifier(),
1531                "and_this_is_another_very_long_filename.txt".as_bytes()
1532            );
1533            assert_eq!(entry.header().size(), 4);
1534            let mut buffer = Vec::new();
1535            entry.read_to_end(&mut buffer).unwrap();
1536            assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1537        }
1538        assert!(archive.next_entry().is_none());
1539        assert_eq!(archive.variant(), Variant::GNU);
1540    }
1541
1542    #[test]
1543    fn read_gnu_archive_with_space_in_filename() {
1544        let input = "\
1545        !<arch>\n\
1546        foo bar/        0           0     0     0       4         `\n\
1547        baz\n";
1548        let mut archive = Archive::new(input.as_bytes());
1549        {
1550            let mut entry = archive.next_entry().unwrap().unwrap();
1551            assert_eq!(entry.header().identifier(), "foo bar".as_bytes());
1552            assert_eq!(entry.header().size(), 4);
1553            let mut buffer = Vec::new();
1554            entry.read_to_end(&mut buffer).unwrap();
1555            assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1556        }
1557        assert!(archive.next_entry().is_none());
1558        assert_eq!(archive.variant(), Variant::GNU);
1559    }
1560
1561    #[test]
1562    fn read_gnu_archive_with_symbol_lookup_table() {
1563        let input = b"\
1564        !<arch>\n\
1565        /               0           0     0     0       15        `\n\
1566        \x00\x00\x00\x01\x00\x00\x00\xb2foobar\x00\n\
1567        //                                              34        `\n\
1568        this_is_a_very_long_filename.txt/\n\
1569        /0              1487552916  501   20    100644  7         `\n\
1570        foobar\n";
1571        let mut archive = Archive::new(input as &[u8]);
1572        {
1573            let mut entry = archive.next_entry().unwrap().unwrap();
1574            assert_eq!(
1575                entry.header().identifier(),
1576                "this_is_a_very_long_filename.txt".as_bytes()
1577            );
1578            let mut buffer = Vec::new();
1579            entry.read_to_end(&mut buffer).unwrap();
1580            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1581        }
1582        assert!(archive.next_entry().is_none());
1583    }
1584
1585    #[test]
1586    fn read_archive_with_no_padding_byte_in_final_entry() {
1587        let input = "\
1588        !<arch>\n\
1589        foo.txt         1487552916  501   20    100644  7         `\n\
1590        foobar\n\n\
1591        bar.txt         1487552919  501   20    100644  3         `\n\
1592        foo";
1593        let mut archive = Archive::new(input.as_bytes());
1594        {
1595            let entry = archive.next_entry().unwrap().unwrap();
1596            assert_eq!(entry.header().identifier(), "foo.txt".as_bytes());
1597            assert_eq!(entry.header().size(), 7);
1598        }
1599        {
1600            let entry = archive.next_entry().unwrap().unwrap();
1601            assert_eq!(entry.header().identifier(), "bar.txt".as_bytes());
1602            assert_eq!(entry.header().size(), 3);
1603        }
1604        assert!(archive.next_entry().is_none());
1605    }
1606
1607    #[test]
1608    #[should_panic(expected = "Invalid timestamp field in entry header \
1609                               (\\\"helloworld  \\\")")]
1610    fn read_archive_with_invalid_mtime() {
1611        let input = "\
1612        !<arch>\n\
1613        foo.txt         helloworld  501   20    100644  7         `\n\
1614        foobar\n\n";
1615        let mut archive = Archive::new(input.as_bytes());
1616        archive.next_entry().unwrap().unwrap();
1617    }
1618
1619    #[test]
1620    #[should_panic(expected = "Invalid owner ID field in entry header \
1621                               (\\\"foo   \\\")")]
1622    fn read_archive_with_invalid_uid() {
1623        let input = "\
1624        !<arch>\n\
1625        foo.txt         1487552916  foo   20    100644  7         `\n\
1626        foobar\n\n";
1627        let mut archive = Archive::new(input.as_bytes());
1628        archive.next_entry().unwrap().unwrap();
1629    }
1630
1631    #[test]
1632    #[should_panic(expected = "Invalid group ID field in entry header \
1633                               (\\\"bar   \\\")")]
1634    fn read_archive_with_invalid_gid() {
1635        let input = "\
1636        !<arch>\n\
1637        foo.txt         1487552916  501   bar   100644  7         `\n\
1638        foobar\n\n";
1639        let mut archive = Archive::new(input.as_bytes());
1640        archive.next_entry().unwrap().unwrap();
1641    }
1642
1643    #[test]
1644    #[should_panic(expected = "Invalid file mode field in entry header \
1645                               (\\\"foobar  \\\")")]
1646    fn read_archive_with_invalid_mode() {
1647        let input = "\
1648        !<arch>\n\
1649        foo.txt         1487552916  501   20    foobar  7         `\n\
1650        foobar\n\n";
1651        let mut archive = Archive::new(input.as_bytes());
1652        archive.next_entry().unwrap().unwrap();
1653    }
1654
1655    #[test]
1656    #[should_panic(expected = "Invalid file size field in entry header \
1657                               (\\\"whatever  \\\")")]
1658    fn read_archive_with_invalid_size() {
1659        let input = "\
1660        !<arch>\n\
1661        foo.txt         1487552916  501   20    100644  whatever  `\n\
1662        foobar\n\n";
1663        let mut archive = Archive::new(input.as_bytes());
1664        archive.next_entry().unwrap().unwrap();
1665    }
1666
1667    #[test]
1668    #[should_panic(expected = "Invalid BSD filename length field in entry \
1669                               header (\\\"foobar       \\\")")]
1670    fn read_bsd_archive_with_invalid_filename_length() {
1671        let input = "\
1672        !<arch>\n\
1673        #1/foobar       1487552916  501   20    100644  39        `\n\
1674        this_is_a_very_long_filename.txtfoobar\n\n";
1675        let mut archive = Archive::new(input.as_bytes());
1676        archive.next_entry().unwrap().unwrap();
1677    }
1678
1679    #[test]
1680    #[should_panic(expected = "Invalid GNU filename index field in entry \
1681                               header (\\\"foobar         \\\")")]
1682    fn read_gnu_archive_with_invalid_filename_index() {
1683        let input = "\
1684        !<arch>\n\
1685        //                                              34        `\n\
1686        this_is_a_very_long_filename.txt/\n\
1687        /foobar         1487552916  501   20    100644  7         `\n\
1688        foobar\n\n";
1689        let mut archive = Archive::new(input.as_bytes());
1690        archive.next_entry().unwrap().unwrap();
1691    }
1692
1693    #[test]
1694    fn seek_within_entry() {
1695        let input = "\
1696        !<arch>\n\
1697        foo.txt         1487552916  501   20    100644  31        `\n\
1698        abcdefghij0123456789ABCDEFGHIJ\n\n\
1699        bar.awesome.txt 1487552919  501   20    100644  22        `\n\
1700        This file is awesome!\n";
1701        let mut archive = Archive::new(Cursor::new(input.as_bytes()));
1702        {
1703            // Parse the first entry, then seek around the entry, performing
1704            // different reads.
1705            let mut entry = archive.next_entry().unwrap().unwrap();
1706            let mut buffer = [0; 5];
1707            entry.seek(SeekFrom::Start(10)).unwrap();
1708            entry.read_exact(&mut buffer).unwrap();
1709            assert_eq!(&buffer, "01234".as_bytes());
1710            entry.seek(SeekFrom::Start(5)).unwrap();
1711            entry.read_exact(&mut buffer).unwrap();
1712            assert_eq!(&buffer, "fghij".as_bytes());
1713            entry.seek(SeekFrom::End(-10)).unwrap();
1714            entry.read_exact(&mut buffer).unwrap();
1715            assert_eq!(&buffer, "BCDEF".as_bytes());
1716            entry.seek(SeekFrom::End(-30)).unwrap();
1717            entry.read_exact(&mut buffer).unwrap();
1718            assert_eq!(&buffer, "bcdef".as_bytes());
1719            entry.seek(SeekFrom::Current(10)).unwrap();
1720            entry.read_exact(&mut buffer).unwrap();
1721            assert_eq!(&buffer, "6789A".as_bytes());
1722            entry.seek(SeekFrom::Current(-8)).unwrap();
1723            entry.read_exact(&mut buffer).unwrap();
1724            assert_eq!(&buffer, "34567".as_bytes());
1725            // Dropping the Entry object should automatically consume the rest
1726            // of the entry data so that the archive reader is ready to parse
1727            // the next entry.
1728        }
1729        {
1730            // Parse the second entry and read in all the entry data.
1731            let mut entry = archive.next_entry().unwrap().unwrap();
1732            let mut buffer = Vec::new();
1733            entry.read_to_end(&mut buffer).unwrap();
1734            assert_eq!(&buffer as &[u8], "This file is awesome!\n".as_bytes());
1735        }
1736    }
1737
1738    #[test]
1739    #[should_panic(expected = "Invalid seek to negative position (-17)")]
1740    fn seek_entry_to_negative_position() {
1741        let input = "\
1742        !<arch>\n\
1743        foo.txt         1487552916  501   20    100644  30        `\n\
1744        abcdefghij0123456789ABCDEFGHIJ";
1745        let mut archive = Archive::new(Cursor::new(input.as_bytes()));
1746        let mut entry = archive.next_entry().unwrap().unwrap();
1747        entry.seek(SeekFrom::End(-47)).unwrap();
1748    }
1749
1750    #[test]
1751    #[should_panic(expected = "Invalid seek to position past end of entry \
1752                               (47 vs. 30)")]
1753    fn seek_entry_beyond_end() {
1754        let input = "\
1755        !<arch>\n\
1756        foo.txt         1487552916  501   20    100644  30        `\n\
1757        abcdefghij0123456789ABCDEFGHIJ";
1758        let mut archive = Archive::new(Cursor::new(input.as_bytes()));
1759        let mut entry = archive.next_entry().unwrap().unwrap();
1760        entry.seek(SeekFrom::Start(47)).unwrap();
1761    }
1762
1763    #[test]
1764    fn count_entries_in_bsd_archive() {
1765        let input = b"\
1766        !<arch>\n\
1767        #1/32           1487552916  501   20    100644  39        `\n\
1768        this_is_a_very_long_filename.txtfoobar\n\n\
1769        baz.txt         0           0     0     0       4         `\n\
1770        baz\n";
1771        let mut archive = Archive::new(Cursor::new(input as &[u8]));
1772        assert_eq!(archive.count_entries().unwrap(), 2);
1773        {
1774            let mut entry = archive.next_entry().unwrap().unwrap();
1775            assert_eq!(
1776                entry.header().identifier(),
1777                "this_is_a_very_long_filename.txt".as_bytes()
1778            );
1779            let mut buffer = Vec::new();
1780            entry.read_to_end(&mut buffer).unwrap();
1781            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1782        }
1783        assert_eq!(archive.count_entries().unwrap(), 2);
1784        {
1785            let mut entry = archive.next_entry().unwrap().unwrap();
1786            assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1787            let mut buffer = Vec::new();
1788            entry.read_to_end(&mut buffer).unwrap();
1789            assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1790        }
1791        assert_eq!(archive.count_entries().unwrap(), 2);
1792    }
1793
1794    #[test]
1795    fn count_entries_in_gnu_archive() {
1796        let input = b"\
1797        !<arch>\n\
1798        /               0           0     0     0       15        `\n\
1799        \x00\x00\x00\x01\x00\x00\x00\xb2foobar\x00\n\
1800        //                                              34        `\n\
1801        this_is_a_very_long_filename.txt/\n\
1802        /0              1487552916  501   20    100644  7         `\n\
1803        foobar\n\n\
1804        baz.txt/        1487552349  42    12345 100664  4         `\n\
1805        baz\n";
1806        let mut archive = Archive::new(Cursor::new(input as &[u8]));
1807        assert_eq!(archive.count_entries().unwrap(), 2);
1808        {
1809            let mut entry = archive.next_entry().unwrap().unwrap();
1810            assert_eq!(
1811                entry.header().identifier(),
1812                "this_is_a_very_long_filename.txt".as_bytes()
1813            );
1814            let mut buffer = Vec::new();
1815            entry.read_to_end(&mut buffer).unwrap();
1816            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1817        }
1818        assert_eq!(archive.count_entries().unwrap(), 2);
1819        {
1820            let mut entry = archive.next_entry().unwrap().unwrap();
1821            assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1822            let mut buffer = Vec::new();
1823            entry.read_to_end(&mut buffer).unwrap();
1824            assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1825        }
1826        assert_eq!(archive.count_entries().unwrap(), 2);
1827    }
1828
1829    #[test]
1830    fn jump_to_entry_in_bsd_archive() {
1831        let input = b"\
1832        !<arch>\n\
1833        hello.txt       1487552316  42    12345 100644  14        `\n\
1834        Hello, world!\n\
1835        #1/32           1487552916  501   20    100644  39        `\n\
1836        this_is_a_very_long_filename.txtfoobar\n\n\
1837        baz.txt         1487552349  42    12345 100664  4         `\n\
1838        baz\n";
1839        let mut archive = Archive::new(Cursor::new(input as &[u8]));
1840        {
1841            // Jump to the second entry and check its contents.
1842            let mut entry = archive.jump_to_entry(1).unwrap();
1843            assert_eq!(
1844                entry.header().identifier(),
1845                "this_is_a_very_long_filename.txt".as_bytes()
1846            );
1847            let mut buffer = Vec::new();
1848            entry.read_to_end(&mut buffer).unwrap();
1849            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1850        }
1851        {
1852            // Read the next entry, which should be the third one now.
1853            let mut entry = archive.next_entry().unwrap().unwrap();
1854            assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1855            let mut buffer = Vec::new();
1856            entry.read_to_end(&mut buffer).unwrap();
1857            assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1858        }
1859        // We should be at the end of the archive now.
1860        assert!(archive.next_entry().is_none());
1861        {
1862            // Jump back to the first entry and check its contents.
1863            let mut entry = archive.jump_to_entry(0).unwrap();
1864            assert_eq!(entry.header().identifier(), "hello.txt".as_bytes());
1865            let mut buffer = Vec::new();
1866            entry.read_to_end(&mut buffer).unwrap();
1867            assert_eq!(&buffer as &[u8], "Hello, world!\n".as_bytes());
1868        }
1869        {
1870            // Read the next entry, which should be the second one again.
1871            let mut entry = archive.jump_to_entry(1).unwrap();
1872            assert_eq!(
1873                entry.header().identifier(),
1874                "this_is_a_very_long_filename.txt".as_bytes()
1875            );
1876            let mut buffer = Vec::new();
1877            entry.read_to_end(&mut buffer).unwrap();
1878            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1879        }
1880        {
1881            // Jump back to the first entry and check its contents.
1882            let mut entry = archive.jump_to_entry(0).unwrap();
1883            assert_eq!(entry.header().identifier(), "hello.txt".as_bytes());
1884            let mut buffer = Vec::new();
1885            entry.read_to_end(&mut buffer).unwrap();
1886            assert_eq!(&buffer as &[u8], "Hello, world!\n".as_bytes());
1887        }
1888        {
1889            // Read the next entry, which should be the second one again.
1890            let mut entry = archive.next_entry().unwrap().unwrap();
1891            assert_eq!(
1892                entry.header().identifier(),
1893                "this_is_a_very_long_filename.txt".as_bytes()
1894            );
1895            let mut buffer = Vec::new();
1896            entry.read_to_end(&mut buffer).unwrap();
1897            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1898        }
1899    }
1900
1901    #[test]
1902    fn jump_to_entry_in_gnu_archive() {
1903        let input = b"\
1904        !<arch>\n\
1905        //                                              34        `\n\
1906        this_is_a_very_long_filename.txt/\n\
1907        hello.txt/      1487552316  42    12345 100644  14        `\n\
1908        Hello, world!\n\
1909        /0              1487552916  501   20    100644  7         `\n\
1910        foobar\n\n\
1911        baz.txt/        1487552349  42    12345 100664  4         `\n\
1912        baz\n";
1913        let mut archive = Archive::new(Cursor::new(input as &[u8]));
1914        {
1915            // Jump to the second entry and check its contents.
1916            let mut entry = archive.jump_to_entry(1).unwrap();
1917            assert_eq!(
1918                entry.header().identifier(),
1919                "this_is_a_very_long_filename.txt".as_bytes()
1920            );
1921            let mut buffer = Vec::new();
1922            entry.read_to_end(&mut buffer).unwrap();
1923            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1924        }
1925        {
1926            // Read the next entry, which should be the third one now.
1927            let mut entry = archive.next_entry().unwrap().unwrap();
1928            assert_eq!(entry.header().identifier(), "baz.txt".as_bytes());
1929            let mut buffer = Vec::new();
1930            entry.read_to_end(&mut buffer).unwrap();
1931            assert_eq!(&buffer as &[u8], "baz\n".as_bytes());
1932        }
1933        // We should be at the end of the archive now.
1934        assert!(archive.next_entry().is_none());
1935        {
1936            // Jump back to the first entry and check its contents.
1937            let mut entry = archive.jump_to_entry(0).unwrap();
1938            assert_eq!(entry.header().identifier(), "hello.txt".as_bytes());
1939            let mut buffer = Vec::new();
1940            entry.read_to_end(&mut buffer).unwrap();
1941            assert_eq!(&buffer as &[u8], "Hello, world!\n".as_bytes());
1942        }
1943        {
1944            // Read the next entry, which should be the second one again.
1945            let mut entry = archive.next_entry().unwrap().unwrap();
1946            assert_eq!(
1947                entry.header().identifier(),
1948                "this_is_a_very_long_filename.txt".as_bytes()
1949            );
1950            let mut buffer = Vec::new();
1951            entry.read_to_end(&mut buffer).unwrap();
1952            assert_eq!(&buffer as &[u8], "foobar\n".as_bytes());
1953        }
1954    }
1955
1956    #[test]
1957    fn list_symbols_in_bsd_archive() {
1958        let input = b"\
1959        !<arch>\n\
1960        #1/12           0           0     0     0       60        `\n\
1961        __.SYMDEF\x00\x00\x00\x18\x00\x00\x00\
1962        \x00\x00\x00\x00\x80\x00\x00\x00\
1963        \x07\x00\x00\x00\x80\x00\x00\x00\
1964        \x0b\x00\x00\x00\x80\x00\x00\x00\
1965        \x10\x00\x00\x00foobar\x00baz\x00quux\x00\
1966        foo.o/          1487552916  501   20    100644  16        `\n\
1967        foobar,baz,quux\n";
1968        let mut archive = Archive::new(Cursor::new(input as &[u8]));
1969        assert_eq!(archive.symbols().unwrap().len(), 3);
1970        assert_eq!(archive.variant(), Variant::BSD);
1971        let symbols = archive.symbols().unwrap().collect::<Vec<&[u8]>>();
1972        let expected: Vec<&[u8]> = vec![b"foobar", b"baz", b"quux"];
1973        assert_eq!(symbols, expected);
1974    }
1975
1976    #[test]
1977    fn list_sorted_symbols_in_bsd_archive() {
1978        let input = b"\
1979        !<arch>\n\
1980        #1/16           0           0     0     0       64        `\n\
1981        __.SYMDEF SORTED\x18\x00\x00\x00\
1982        \x00\x00\x00\x00\x80\x00\x00\x00\
1983        \x04\x00\x00\x00\x80\x00\x00\x00\
1984        \x0b\x00\x00\x00\x80\x00\x00\x00\
1985        \x10\x00\x00\x00baz\x00foobar\x00quux\x00\
1986        foo.o/          1487552916  501   20    100644  16        `\n\
1987        foobar,baz,quux\n";
1988        let mut archive = Archive::new(Cursor::new(input as &[u8]));
1989        assert_eq!(archive.symbols().unwrap().len(), 3);
1990        assert_eq!(archive.variant(), Variant::BSD);
1991        let symbols = archive.symbols().unwrap().collect::<Vec<&[u8]>>();
1992        let expected: Vec<&[u8]> = vec![b"baz", b"foobar", b"quux"];
1993        assert_eq!(symbols, expected);
1994    }
1995
1996    #[test]
1997    fn list_symbols_in_gnu_archive() {
1998        let input = b"\
1999        !<arch>\n\
2000        /               0           0     0     0       32        `\n\
2001        \x00\x00\x00\x03\x00\x00\x00\x5c\x00\x00\x00\x5c\x00\x00\x00\x5c\
2002        foobar\x00baz\x00quux\x00\
2003        foo.o/          1487552916  501   20    100644  16        `\n\
2004        foobar,baz,quux\n";
2005        let mut archive = Archive::new(Cursor::new(input as &[u8]));
2006        assert_eq!(archive.symbols().unwrap().len(), 3);
2007        assert_eq!(archive.variant(), Variant::GNU);
2008        let symbols = archive.symbols().unwrap().collect::<Vec<&[u8]>>();
2009        let expected: Vec<&[u8]> = vec![b"foobar", b"baz", b"quux"];
2010        assert_eq!(symbols, expected);
2011    }
2012
2013    #[test]
2014    fn non_multiple_of_two_long_ident_in_gnu_archive() {
2015        let mut buffer = std::io::Cursor::new(Vec::new());
2016
2017        {
2018            let filenames = vec![
2019                b"rust.metadata.bin".to_vec(),
2020                b"compiler_builtins-78891cf83a7d3547.dummy_name.rcgu.o"
2021                    .to_vec(),
2022            ];
2023            let mut builder = GnuBuilder::new(&mut buffer, filenames.clone());
2024
2025            for filename in filenames {
2026                builder
2027                    .append(&Header::new(filename, 1), &mut (&[b'?'] as &[u8]))
2028                    .expect("add file");
2029            }
2030        }
2031
2032        buffer.set_position(0);
2033
2034        let mut archive = Archive::new(buffer);
2035        while let Some(entry) = archive.next_entry() {
2036            entry.unwrap();
2037        }
2038    }
2039}
2040
2041// ========================================================================= //