btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / installer / UnzipEngine.cpp
blob7bf767c9bca50fd052e2065c22f8bfbb52d93c91
1 /*
2 * Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
6 #include "UnzipEngine.h"
8 #include <new>
10 #include <stdio.h>
11 #include <string.h>
13 #include <Directory.h>
14 #include <Entry.h>
15 #include <File.h>
16 #include <Node.h>
17 #include <Path.h>
18 #include <String.h>
20 #include "CommandPipe.h"
21 #include "SemaphoreLocker.h"
22 #include "ProgressReporter.h"
25 using std::nothrow;
28 UnzipEngine::UnzipEngine(ProgressReporter* reporter,
29 sem_id cancelSemaphore)
31 fPackage(""),
32 fRetrievingListing(false),
34 fBytesToUncompress(0),
35 fBytesUncompressed(0),
36 fLastBytesUncompressed(0),
37 fItemsToUncompress(0),
38 fItemsUncompressed(0),
39 fLastItemsUncompressed(0),
41 fProgressReporter(reporter),
42 fCancelSemaphore(cancelSemaphore)
47 UnzipEngine::~UnzipEngine()
52 status_t
53 UnzipEngine::SetTo(const char* pathToPackage, const char* destinationFolder)
55 fPackage = pathToPackage;
56 fDestinationFolder = destinationFolder;
58 fEntrySizeMap.Clear();
60 fBytesToUncompress = 0;
61 fBytesUncompressed = 0;
62 fLastBytesUncompressed = 0;
63 fItemsToUncompress = 0;
64 fItemsUncompressed = 0;
65 fLastItemsUncompressed = 0;
67 BPrivate::BCommandPipe commandPipe;
68 status_t ret = commandPipe.AddArg("unzip");
69 if (ret == B_OK)
70 ret = commandPipe.AddArg("-l");
71 if (ret == B_OK)
72 ret = commandPipe.AddArg(fPackage.String());
73 if (ret != B_OK)
74 return ret;
76 // Launch the unzip thread and start reading the stdout and stderr output.
77 FILE* stdOutAndErrPipe = NULL;
78 thread_id unzipThread = commandPipe.PipeInto(&stdOutAndErrPipe);
79 if (unzipThread < 0)
80 return (status_t)unzipThread;
82 fRetrievingListing = true;
83 ret = commandPipe.ReadLines(stdOutAndErrPipe, this);
84 fRetrievingListing = false;
86 printf("%" B_PRIu64 " items in %" B_PRIdOFF " bytes\n", fItemsToUncompress,
87 fBytesToUncompress);
89 return ret;
93 status_t
94 UnzipEngine::UnzipPackage()
96 if (fItemsToUncompress == 0)
97 return B_NO_INIT;
99 BPrivate::BCommandPipe commandPipe;
100 status_t ret = commandPipe.AddArg("unzip");
101 if (ret == B_OK)
102 ret = commandPipe.AddArg("-o");
103 if (ret == B_OK)
104 ret = commandPipe.AddArg(fPackage.String());
105 if (ret == B_OK)
106 ret = commandPipe.AddArg("-d");
107 if (ret == B_OK)
108 ret = commandPipe.AddArg(fDestinationFolder.String());
109 if (ret != B_OK) {
110 fprintf(stderr, "Faild to construct argument list for unzip "
111 "process: %s\n", strerror(ret));
112 return ret;
115 // Launch the unzip thread and start reading the stdout and stderr output.
116 FILE* stdOutAndErrPipe = NULL;
117 thread_id unzipThread = commandPipe.PipeInto(&stdOutAndErrPipe);
118 if (unzipThread < 0)
119 return (status_t)unzipThread;
121 ret = commandPipe.ReadLines(stdOutAndErrPipe, this);
122 if (ret != B_OK) {
123 fprintf(stderr, "Piping the unzip process failed: %s\n",
124 strerror(ret));
125 return ret;
128 // Add the contents of a potentially existing .OptionalPackageDescription
129 // to the COPYRIGHTS attribute of AboutSystem.
130 BPath descriptionPath(fDestinationFolder.String(),
131 ".OptionalPackageDescription");
132 ret = descriptionPath.InitCheck();
133 if (ret != B_OK) {
134 fprintf(stderr, "Failed to construct path to "
135 ".OptionalPackageDescription: %s\n", strerror(ret));
136 return ret;
139 BEntry descriptionEntry(descriptionPath.Path());
140 if (!descriptionEntry.Exists())
141 return B_OK;
143 BFile descriptionFile(&descriptionEntry, B_READ_ONLY);
144 ret = descriptionFile.InitCheck();
145 if (ret != B_OK) {
146 fprintf(stderr, "Failed to construct file to "
147 ".OptionalPackageDescription: %s\n", strerror(ret));
148 return ret;
151 BPath aboutSystemPath(fDestinationFolder.String(),
152 "system/apps/AboutSystem");
153 ret = aboutSystemPath.InitCheck();
154 if (ret != B_OK) {
155 fprintf(stderr, "Failed to construct path to AboutSystem: %s\n",
156 strerror(ret));
157 return ret;
160 BNode aboutSystemNode(aboutSystemPath.Path());
161 ret = aboutSystemNode.InitCheck();
162 if (ret != B_OK) {
163 fprintf(stderr, "Failed to construct node to AboutSystem: %s\n",
164 strerror(ret));
165 return ret;
168 const char* kCopyrightsAttrName = "COPYRIGHTS";
170 BString copyrightAttr;
171 ret = aboutSystemNode.ReadAttrString(kCopyrightsAttrName, &copyrightAttr);
172 if (ret != B_OK && ret != B_ENTRY_NOT_FOUND) {
173 fprintf(stderr, "Failed to read current COPYRIGHTS attribute from "
174 "AboutSystem: %s\n", strerror(ret));
175 return ret;
178 // Append the contents of the current optional package description to
179 // the existing COPYRIGHTS attribute from AboutSystem
180 size_t bufferSize = 2048;
181 char buffer[bufferSize + 1];
182 buffer[bufferSize] = '\0';
183 while (true) {
184 ssize_t read = descriptionFile.Read(buffer, bufferSize);
185 if (read > 0) {
186 int32 length = copyrightAttr.Length();
187 if (read < (ssize_t)bufferSize)
188 buffer[read] = '\0';
189 int32 bufferLength = strlen(buffer);
190 // Should be "read", but maybe we have a zero in the
191 // buffer in which case the next check would be fooled.
192 copyrightAttr << buffer;
193 if (copyrightAttr.Length() != length + bufferLength) {
194 fprintf(stderr, "Failed to append buffer to COPYRIGHTS "
195 "attribute.\n");
196 return B_NO_MEMORY;
198 } else
199 break;
202 if (copyrightAttr[copyrightAttr.Length() - 1] != '\n')
203 copyrightAttr << '\n\n';
204 else
205 copyrightAttr << '\n';
207 ret = aboutSystemNode.WriteAttrString(kCopyrightsAttrName, &copyrightAttr);
208 if (ret != B_OK && ret != B_ENTRY_NOT_FOUND) {
209 fprintf(stderr, "Failed to read current COPYRIGHTS attribute from "
210 "AboutSystem: %s\n", strerror(ret));
211 return ret;
214 // Don't leave the .OptionalPackageDescription behind.
215 descriptionFile.Unset();
216 descriptionEntry.Remove();
218 return B_OK;
222 // #pragma mark -
225 bool
226 UnzipEngine::IsCanceled()
228 if (fCancelSemaphore < 0)
229 return false;
231 SemaphoreLocker locker(fCancelSemaphore);
232 return !locker.IsLocked();
236 status_t
237 UnzipEngine::ReadLine(const BString& line)
239 if (fRetrievingListing)
240 return _ReadLineListing(line);
241 else
242 return _ReadLineExtract(line);
246 status_t
247 UnzipEngine::_ReadLineListing(const BString& line)
249 static const char* kListingFormat = "%llu %s %s %s\n";
251 const char* string = line.String();
252 while (string[0] == ' ')
253 string++;
255 uint64 bytes;
256 char date[16];
257 char time[16];
258 char path[1024];
259 if (sscanf(string, kListingFormat, &bytes, &date, &time, &path) == 4) {
260 fBytesToUncompress += bytes;
262 BString itemPath(path);
263 BString itemName(path);
264 int leafPos = itemPath.FindLast('/');
265 if (leafPos >= 0)
266 itemName = itemPath.String() + leafPos + 1;
268 // We check if the target folder exists and don't increment
269 // the item count in that case. Unzip won't report on folders that did
270 // not need to be created. This may mess up our current item count.
271 uint32 itemCount = 1;
272 if (bytes == 0 && itemName.Length() == 0) {
273 // a folder?
274 BPath destination(fDestinationFolder.String());
275 if (destination.Append(itemPath.String()) == B_OK) {
276 BEntry test(destination.Path());
277 if (test.Exists() && test.IsDirectory()) {
278 // printf("ignoring %s\n", itemPath.String());
279 itemCount = 0;
284 fItemsToUncompress += itemCount;
286 // printf("item %s with %llu bytes to %s\n", itemName.String(),
287 // bytes, itemPath.String());
289 fEntrySizeMap.Put(itemName.String(), bytes);
290 } else {
291 // printf("listing not understood: %s", string);
294 return B_OK;
298 status_t
299 UnzipEngine::_ReadLineExtract(const BString& line)
301 const char* kCreatingFormat = " creating:";
302 const char* kInflatingFormat = " inflating:";
303 const char* kLinkingFormat = " linking:";
304 if (line.FindFirst(kCreatingFormat) == 0
305 || line.FindFirst(kInflatingFormat) == 0
306 || line.FindFirst(kLinkingFormat) == 0) {
308 fItemsUncompressed++;
310 BString itemPath;
312 int pos = line.FindLast(" -> ");
313 if (pos > 0)
314 line.CopyInto(itemPath, 13, pos - 13);
315 else
316 line.CopyInto(itemPath, 13, line.CountChars() - 14);
318 itemPath.Trim();
319 pos = itemPath.FindLast('/');
320 BString itemName = itemPath.String() + pos + 1;
321 itemPath.Truncate(pos);
323 off_t bytes = 0;
324 if (fEntrySizeMap.ContainsKey(itemName.String())) {
325 bytes = fEntrySizeMap.Get(itemName.String());
326 fBytesUncompressed += bytes;
329 // printf("%llu extracted %s to %s (%llu)\n", fItemsUncompressed,
330 // itemName.String(), itemPath.String(), bytes);
332 _UpdateProgress(itemName.String(), itemPath.String());
333 } else {
334 // printf("ignored: '%s'\n", line.String());
337 return B_OK;
341 void
342 UnzipEngine::_UpdateProgress(const char* item, const char* targetFolder)
344 if (fProgressReporter == NULL)
345 return;
347 uint64 items = 0;
348 if (fLastItemsUncompressed < fItemsUncompressed) {
349 items = fItemsUncompressed - fLastItemsUncompressed;
350 fLastItemsUncompressed = fItemsUncompressed;
353 off_t bytes = 0;
354 if (fLastBytesUncompressed < fBytesUncompressed) {
355 bytes = fBytesUncompressed - fLastBytesUncompressed;
356 fLastBytesUncompressed = fBytesUncompressed;
359 fProgressReporter->ItemsWritten(items, bytes, item, targetFolder);