Merge pull request #217 from Crypto-Spartan/zip-mem-warnings
[ouch.git] / src / archive / tar.rs
blob6b7cd0c3237beb094b1c1f07ae81671f93342d58
1 //! Contains Tar-specific building and unpacking functions
3 use std::{
4     env,
5     io::prelude::*,
6     path::{Path, PathBuf},
7 };
9 use fs_err as fs;
10 use tar;
11 use walkdir::WalkDir;
13 use crate::{
14     error::FinalError,
15     info,
16     list::FileInArchive,
17     utils::{self, Bytes},
20 /// Unpacks the archive given by `archive` into the folder given by `into`.
21 /// Assumes that output_folder is empty
22 pub fn unpack_archive(
23     reader: Box<dyn Read>,
24     output_folder: &Path,
25     mut display_handle: impl Write,
26 ) -> crate::Result<Vec<PathBuf>> {
27     assert!(output_folder.read_dir().expect("dir exists").count() == 0);
28     let mut archive = tar::Archive::new(reader);
30     let mut files_unpacked = vec![];
31     for file in archive.entries()? {
32         let mut file = file?;
34         let file_path = output_folder.join(file.path()?);
35         file.unpack_in(output_folder)?;
37         // This is printed for every file in the archive and has little
38         // importance for most users, but would generate lots of
39         // spoken text for users using screen readers, braille displays
40         // and so on
41         info!(@display_handle, inaccessible, "{:?} extracted. ({})", output_folder.join(file.path()?), Bytes::new(file.size()));
43         files_unpacked.push(file_path);
44     }
46     Ok(files_unpacked)
49 /// List contents of `archive`, returning a vector of archive entries
50 pub fn list_archive(reader: Box<dyn Read>) -> crate::Result<Vec<FileInArchive>> {
51     let mut archive = tar::Archive::new(reader);
53     let mut files = vec![];
54     for file in archive.entries()? {
55         let file = file?;
57         let path = file.path()?.into_owned();
58         let is_dir = file.header().entry_type().is_dir();
60         files.push(FileInArchive { path, is_dir });
61     }
63     Ok(files)
66 /// Compresses the archives given by `input_filenames` into the file given previously to `writer`.
67 pub fn build_archive_from_paths<W, D>(input_filenames: &[PathBuf], writer: W, mut display_handle: D) -> crate::Result<W>
68 where
69     W: Write,
70     D: Write,
72     let mut builder = tar::Builder::new(writer);
74     for filename in input_filenames {
75         let previous_location = utils::cd_into_same_dir_as(filename)?;
77         // Safe unwrap, input shall be treated before
78         let filename = filename.file_name().unwrap();
80         for entry in WalkDir::new(&filename) {
81             let entry = entry?;
82             let path = entry.path();
84             // This is printed for every file in `input_filenames` and has
85             // little importance for most users, but would generate lots of
86             // spoken text for users using screen readers, braille displays
87             // and so on
88             info!(@display_handle, inaccessible, "Compressing '{}'.", utils::to_utf(path));
90             if path.is_dir() {
91                 builder.append_dir(path, path)?;
92             } else {
93                 let mut file = fs::File::open(path)?;
94                 builder.append_file(path, file.file_mut()).map_err(|err| {
95                     FinalError::with_title("Could not create archive")
96                         .detail("Unexpected error while trying to read file")
97                         .detail(format!("Error: {}.", err))
98                 })?;
99             }
100         }
101         env::set_current_dir(previous_location)?;
102     }
104     Ok(builder.into_inner()?)