1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
//! ref: composer/src/Composer/Package/Archiver/ZipArchiver.php
use crate::package::archiver::ArchivableFilesFinder;
use crate::package::archiver::ArchiverInterface;
use crate::util::Filesystem;
use crate::util::Platform;
use indexmap::IndexMap;
use shirabe_php_shim::{
PhpMixed, RuntimeException, ZipArchive, class_exists, fileperms, method_exists, pack, realpath,
};
#[derive(Debug)]
pub struct ZipArchiver;
impl ZipArchiver {
pub fn new() -> Self {
Self
}
fn formats() -> IndexMap<String, bool> {
let mut map = IndexMap::new();
map.insert("zip".to_string(), true);
map
}
fn compression_available(&self) -> bool {
class_exists("ZipArchive")
}
}
impl ArchiverInterface for ZipArchiver {
fn archive(
&self,
sources: String,
target: String,
format: String,
excludes: Vec<String>,
ignore_filters: bool,
) -> anyhow::Result<String> {
let fs = Filesystem::new(None);
let sources_realpath = realpath(&sources);
let sources = if let Some(p) = sources_realpath {
p
} else {
sources
};
let sources = fs.normalize_path(&sources);
let mut zip = ZipArchive::new();
if zip.open(&target, ZipArchive::CREATE).is_ok() {
let files = ArchivableFilesFinder::new(&sources, excludes, ignore_filters)?;
for file in files {
let filepath = file.get_pathname();
let mut relative_path = file.get_relative_path_name();
if Platform::is_windows() {
relative_path = shirabe_php_shim::strtr(&relative_path, "\\", "/");
}
if file.is_dir() {
zip.add_empty_dir(&relative_path);
} else {
zip.add_file(&filepath, &relative_path);
}
// setExternalAttributesName() is only available with libzip 0.11.2 or above
if method_exists(&PhpMixed::Null, "setExternalAttributesName") {
let perms = fileperms(&filepath);
zip.set_external_attributes_name(
&relative_path,
ZipArchive::OPSYS_UNIX,
perms << 16,
);
}
}
if zip.close() {
if !std::path::Path::new(&target).exists() {
// create minimal valid ZIP file (Empty Central Directory + End of Central Directory record)
let eocd = pack(
"VvvvvVVv",
&[
PhpMixed::Int(0x06054b50), // End of central directory signature
PhpMixed::Int(0), // Number of this disk
PhpMixed::Int(0), // Disk where central directory starts
PhpMixed::Int(0), // Number of central directory records on this disk
PhpMixed::Int(0), // Total number of central directory records
PhpMixed::Int(0), // Size of central directory (bytes)
PhpMixed::Int(0), // Offset of start of central directory
PhpMixed::Int(0), // Comment length
],
);
std::fs::write(&target, &eocd)?;
}
return Ok(target);
}
}
let message = format!(
"Could not create archive '{}' from '{}': {}",
target,
sources,
zip.get_status_string()
);
Err(RuntimeException { message, code: 0 }.into())
}
fn supports(&self, format: String, _source_type: Option<String>) -> bool {
Self::formats().contains_key(&format) && self.compression_available()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
|