1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2010 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
8 This software is distributed WITHOUT ANY WARRANTY; without even the
9 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License for more information.
11 ============================================================================*/
12 #include "cmArchiveWrite.h"
14 #include "cmSystemTools.h"
15 #include <cmsys/ios/iostream>
16 #include <cmsys/Directory.hxx>
17 #include <cm_libarchive.h>
19 //----------------------------------------------------------------------------
20 class cmArchiveWrite::Entry
22 struct archive_entry* Object;
24 Entry(): Object(archive_entry_new()) {}
25 ~Entry() { archive_entry_free(this->Object); }
26 operator struct archive_entry*() { return this->Object; }
29 //----------------------------------------------------------------------------
30 struct cmArchiveWrite::Callback
32 // archive_write_callback
33 static __LA_SSIZE_T Write(struct archive*, void *cd,
34 const void *b, size_t n)
36 cmArchiveWrite* self = static_cast<cmArchiveWrite*>(cd);
37 if(self->Stream.write(static_cast<const char*>(b),
38 static_cast<cmsys_ios::streamsize>(n)))
40 return static_cast<__LA_SSIZE_T>(n);
44 return static_cast<__LA_SSIZE_T>(-1);
49 //----------------------------------------------------------------------------
50 cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c, Type t):
52 Archive(archive_write_new()),
53 Disk(archive_read_disk_new()),
59 if(archive_write_set_compression_none(this->Archive) != ARCHIVE_OK)
61 this->Error = "archive_write_set_compression_none: ";
62 this->Error += archive_error_string(this->Archive);
66 case CompressCompress:
67 if(archive_write_set_compression_compress(this->Archive) != ARCHIVE_OK)
69 this->Error = "archive_write_set_compression_compress: ";
70 this->Error += archive_error_string(this->Archive);
75 if(archive_write_set_compression_gzip(this->Archive) != ARCHIVE_OK)
77 this->Error = "archive_write_set_compression_gzip: ";
78 this->Error += archive_error_string(this->Archive);
83 if(archive_write_set_compression_bzip2(this->Archive) != ARCHIVE_OK)
85 this->Error = "archive_write_set_compression_bzip2: ";
86 this->Error += archive_error_string(this->Archive);
91 if(archive_write_set_compression_lzma(this->Archive) != ARCHIVE_OK)
93 this->Error = "archive_write_set_compression_lzma: ";
94 this->Error += archive_error_string(this->Archive);
99 if(archive_write_set_compression_xz(this->Archive) != ARCHIVE_OK)
101 this->Error = "archive_write_set_compression_xz: ";
102 this->Error += archive_error_string(this->Archive);
107 #if !defined(_WIN32) || defined(__CYGWIN__)
108 if (archive_read_disk_set_standard_lookup(this->Disk) != ARCHIVE_OK)
110 this->Error = "archive_read_disk_set_standard_lookup: ";
111 this->Error += archive_error_string(this->Archive);
118 if(archive_write_set_format_zip(this->Archive) != ARCHIVE_OK)
120 this->Error = "archive_write_set_format_zip: ";
121 this->Error += archive_error_string(this->Archive);
126 if(archive_write_set_format_pax_restricted(this->Archive) != ARCHIVE_OK)
128 this->Error = "archive_write_set_format_pax_restricted: ";
129 this->Error += archive_error_string(this->Archive);
135 // do not pad the last block!!
136 if (archive_write_set_bytes_in_last_block(this->Archive, 1))
138 this->Error = "archive_write_set_bytes_in_last_block: ";
139 this->Error += archive_error_string(this->Archive);
143 if(archive_write_open(
144 this->Archive, this, 0,
145 reinterpret_cast<archive_write_callback*>(&Callback::Write),
148 this->Error = "archive_write_open: ";
149 this->Error += archive_error_string(this->Archive);
154 //----------------------------------------------------------------------------
155 cmArchiveWrite::~cmArchiveWrite()
157 archive_read_finish(this->Disk);
158 archive_write_finish(this->Archive);
161 //----------------------------------------------------------------------------
162 bool cmArchiveWrite::Add(std::string path, size_t skip, const char* prefix)
166 if(!path.empty() && path[path.size()-1] == '/')
168 path.erase(path.size()-1);
170 this->AddPath(path.c_str(), skip, prefix);
175 //----------------------------------------------------------------------------
176 bool cmArchiveWrite::AddPath(const char* path,
177 size_t skip, const char* prefix)
179 if(!this->AddFile(path, skip, prefix))
183 if(!cmSystemTools::FileIsDirectory(path) ||
184 cmSystemTools::FileIsSymlink(path))
191 std::string next = path;
193 std::string::size_type end = next.size();
194 unsigned long n = d.GetNumberOfFiles();
195 for(unsigned long i = 0; i < n; ++i)
197 const char* file = d.GetFile(i);
198 if(strcmp(file, ".") != 0 && strcmp(file, "..") != 0)
202 if(!this->AddPath(next.c_str(), skip, prefix))
212 //----------------------------------------------------------------------------
213 bool cmArchiveWrite::AddFile(const char* file,
214 size_t skip, const char* prefix)
216 // Skip the file if we have no name for it. This may happen on a
217 // top-level directory, which does not need to be included anyway.
218 if(skip >= strlen(file))
222 const char* out = file + skip;
225 std::string dest = prefix? prefix : "";
229 std::cout << dest << "\n";
232 archive_entry_copy_sourcepath(e, file);
233 archive_entry_set_pathname(e, dest.c_str());
234 if(archive_read_disk_entry_from_file(this->Disk, e, -1, 0) != ARCHIVE_OK)
236 this->Error = "archive_read_disk_entry_from_file: ";
237 this->Error += archive_error_string(this->Disk);
240 // Clear acl and xattr fields not useful for distribution.
241 archive_entry_acl_clear(e);
242 archive_entry_xattr_clear(e);
243 archive_entry_set_fflags(e, 0, 0);
244 if(archive_write_header(this->Archive, e) != ARCHIVE_OK)
246 this->Error = "archive_write_header: ";
247 this->Error += archive_error_string(this->Archive);
251 // do not copy content of symlink
252 if (!archive_entry_symlink(e))
255 if(size_t size = static_cast<size_t>(archive_entry_size(e)))
257 return this->AddData(file, size);
263 //----------------------------------------------------------------------------
264 bool cmArchiveWrite::AddData(const char* file, size_t size)
266 std::ifstream fin(file, std::ios::in | cmsys_ios_binary);
269 this->Error = "Error opening \"";
271 this->Error += "\": ";
272 this->Error += cmSystemTools::GetLastSystemError();
280 typedef cmsys_ios::streamsize ssize_type;
281 size_t const nnext = nleft > sizeof(buffer)? sizeof(buffer) : nleft;
282 ssize_type const nnext_s = static_cast<ssize_type>(nnext);
283 fin.read(buffer, nnext_s);
284 // Some stream libraries (older HPUX) return failure at end of
285 // file on the last read even if some data were read. Check
286 // gcount instead of trusting the stream error status.
287 if(static_cast<size_t>(fin.gcount()) != nnext)
291 if(archive_write_data(this->Archive, buffer, nnext) != nnext_s)
293 this->Error = "archive_write_data: ";
294 this->Error += archive_error_string(this->Archive);
301 this->Error = "Error reading \"";
303 this->Error += "\": ";
304 this->Error += cmSystemTools::GetLastSystemError();