packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmArchiveWrite.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2010 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
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"
13
14 #include "cmSystemTools.h"
15 #include <cmsys/ios/iostream>
16 #include <cmsys/Directory.hxx>
17 #include <cm_libarchive.h>
18
19 //----------------------------------------------------------------------------
20 class cmArchiveWrite::Entry
21 {
22   struct archive_entry* Object;
23 public:
24   Entry(): Object(archive_entry_new()) {}
25   ~Entry() { archive_entry_free(this->Object); }
26   operator struct archive_entry*() { return this->Object; }
27 };
28
29 //----------------------------------------------------------------------------
30 struct cmArchiveWrite::Callback
31 {
32   // archive_write_callback
33   static __LA_SSIZE_T Write(struct archive*, void *cd,
34                             const void *b, size_t n)
35     {
36     cmArchiveWrite* self = static_cast<cmArchiveWrite*>(cd);
37     if(self->Stream.write(static_cast<const char*>(b),
38                           static_cast<cmsys_ios::streamsize>(n)))
39       {
40       return static_cast<__LA_SSIZE_T>(n);
41       }
42     else
43       {
44       return static_cast<__LA_SSIZE_T>(-1);
45       }
46     }
47 };
48
49 //----------------------------------------------------------------------------
50 cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c, Type t):
51   Stream(os),
52   Archive(archive_write_new()),
53   Disk(archive_read_disk_new()),
54   Verbose(false)
55 {
56   switch (c)
57     {
58     case CompressNone:
59       if(archive_write_set_compression_none(this->Archive) != ARCHIVE_OK)
60         {
61         this->Error = "archive_write_set_compression_none: ";
62         this->Error += archive_error_string(this->Archive);
63         return;
64         }
65       break;
66     case CompressCompress:
67       if(archive_write_set_compression_compress(this->Archive) != ARCHIVE_OK)
68         {
69         this->Error = "archive_write_set_compression_compress: ";
70         this->Error += archive_error_string(this->Archive);
71         return;
72         }
73       break;
74     case CompressGZip:
75       if(archive_write_set_compression_gzip(this->Archive) != ARCHIVE_OK)
76         {
77         this->Error = "archive_write_set_compression_gzip: ";
78         this->Error += archive_error_string(this->Archive);
79         return;
80         }
81       break;
82     case CompressBZip2:
83       if(archive_write_set_compression_bzip2(this->Archive) != ARCHIVE_OK)
84         {
85         this->Error = "archive_write_set_compression_bzip2: ";
86         this->Error += archive_error_string(this->Archive);
87         return;
88         }
89       break;
90     case CompressLZMA:
91       if(archive_write_set_compression_lzma(this->Archive) != ARCHIVE_OK)
92         {
93         this->Error = "archive_write_set_compression_lzma: ";
94         this->Error += archive_error_string(this->Archive);
95         return;
96         }
97       break;
98     case CompressXZ:
99       if(archive_write_set_compression_xz(this->Archive) != ARCHIVE_OK)
100         {
101         this->Error = "archive_write_set_compression_xz: ";
102         this->Error += archive_error_string(this->Archive);
103         return;
104         }
105       break;
106     };
107 #if !defined(_WIN32) || defined(__CYGWIN__)
108   if (archive_read_disk_set_standard_lookup(this->Disk) != ARCHIVE_OK)
109     {
110     this->Error = "archive_read_disk_set_standard_lookup: ";
111     this->Error += archive_error_string(this->Archive);
112     return;;
113     }
114 #endif
115   switch (t)
116     {
117     case TypeZIP:
118       if(archive_write_set_format_zip(this->Archive) != ARCHIVE_OK)
119         {
120         this->Error = "archive_write_set_format_zip: ";
121         this->Error += archive_error_string(this->Archive);
122         return;
123         }
124       break;
125     case TypeTAR:
126       if(archive_write_set_format_pax_restricted(this->Archive) != ARCHIVE_OK)
127         {
128         this->Error = "archive_write_set_format_pax_restricted: ";
129         this->Error += archive_error_string(this->Archive);
130         return;
131         }
132     break;
133     }
134
135   // do not pad the last block!!
136   if (archive_write_set_bytes_in_last_block(this->Archive, 1))
137     {
138     this->Error = "archive_write_set_bytes_in_last_block: ";
139     this->Error += archive_error_string(this->Archive);
140     return;
141     }
142
143   if(archive_write_open(
144        this->Archive, this, 0,
145        reinterpret_cast<archive_write_callback*>(&Callback::Write),
146        0) != ARCHIVE_OK)
147     {
148     this->Error = "archive_write_open: ";
149     this->Error += archive_error_string(this->Archive);
150     return;
151     }
152 }
153
154 //----------------------------------------------------------------------------
155 cmArchiveWrite::~cmArchiveWrite()
156 {
157   archive_read_finish(this->Disk);
158   archive_write_finish(this->Archive);
159 }
160
161 //----------------------------------------------------------------------------
162 bool cmArchiveWrite::Add(std::string path, size_t skip, const char* prefix)
163 {
164   if(this->Okay())
165     {
166     if(!path.empty() && path[path.size()-1] == '/')
167       {
168       path.erase(path.size()-1);
169       }
170     this->AddPath(path.c_str(), skip, prefix);
171     }
172   return this->Okay();
173 }
174
175 //----------------------------------------------------------------------------
176 bool cmArchiveWrite::AddPath(const char* path,
177                              size_t skip, const char* prefix)
178 {
179   if(!this->AddFile(path, skip, prefix))
180     {
181     return false;
182     }
183   if(!cmSystemTools::FileIsDirectory(path) ||
184     cmSystemTools::FileIsSymlink(path))
185     {
186     return true;
187     }
188   cmsys::Directory d;
189   if(d.Load(path))
190     {
191     std::string next = path;
192     next += "/";
193     std::string::size_type end = next.size();
194     unsigned long n = d.GetNumberOfFiles();
195     for(unsigned long i = 0; i < n; ++i)
196       {
197       const char* file = d.GetFile(i);
198       if(strcmp(file, ".") != 0 && strcmp(file, "..") != 0)
199         {
200         next.erase(end);
201         next += file;
202         if(!this->AddPath(next.c_str(), skip, prefix))
203           {
204           return false;
205           }
206         }
207       }
208     }
209   return true;
210 }
211
212 //----------------------------------------------------------------------------
213 bool cmArchiveWrite::AddFile(const char* file,
214                              size_t skip, const char* prefix)
215 {
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))
219     {
220     return true;
221     }
222   const char* out = file + skip;
223
224   // Meta-data.
225   std::string dest = prefix? prefix : "";
226   dest += out;
227   if(this->Verbose)
228     {
229     std::cout << dest << "\n";
230     }
231   Entry e;
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)
235     {
236     this->Error = "archive_read_disk_entry_from_file: ";
237     this->Error += archive_error_string(this->Disk);
238     return false;
239     }
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)
245     {
246     this->Error = "archive_write_header: ";
247     this->Error += archive_error_string(this->Archive);
248     return false;
249     }
250
251   // do not copy content of symlink
252   if (!archive_entry_symlink(e))
253     {
254     // Content.
255     if(size_t size = static_cast<size_t>(archive_entry_size(e)))
256       {
257       return this->AddData(file, size);
258       }
259     }
260   return true;
261 }
262
263 //----------------------------------------------------------------------------
264 bool cmArchiveWrite::AddData(const char* file, size_t size)
265 {
266   std::ifstream fin(file, std::ios::in | cmsys_ios_binary);
267   if(!fin)
268     {
269     this->Error = "Error opening \"";
270     this->Error += file;
271     this->Error += "\": ";
272     this->Error += cmSystemTools::GetLastSystemError();
273     return false;
274     }
275
276   char buffer[16384];
277   size_t nleft = size;
278   while(nleft > 0)
279     {
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)
288       {
289       break;
290       }
291     if(archive_write_data(this->Archive, buffer, nnext) != nnext_s)
292       {
293       this->Error = "archive_write_data: ";
294       this->Error += archive_error_string(this->Archive);
295       return false;
296       }
297     nleft -= nnext;
298     }
299   if(nleft > 0)
300     {
301     this->Error = "Error reading \"";
302     this->Error += file;
303     this->Error += "\": ";
304     this->Error += cmSystemTools::GetLastSystemError();
305     return false;
306     }
307   return true;
308 }