2 * Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT License.
6 #include "UnzipEngine.h"
13 #include <Directory.h>
20 #include "CommandPipe.h"
21 #include "SemaphoreLocker.h"
22 #include "ProgressReporter.h"
28 UnzipEngine::UnzipEngine(ProgressReporter
* reporter
,
29 sem_id cancelSemaphore
)
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()
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");
70 ret
= commandPipe
.AddArg("-l");
72 ret
= commandPipe
.AddArg(fPackage
.String());
76 // Launch the unzip thread and start reading the stdout and stderr output.
77 FILE* stdOutAndErrPipe
= NULL
;
78 thread_id unzipThread
= commandPipe
.PipeInto(&stdOutAndErrPipe
);
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
,
94 UnzipEngine::UnzipPackage()
96 if (fItemsToUncompress
== 0)
99 BPrivate::BCommandPipe commandPipe
;
100 status_t ret
= commandPipe
.AddArg("unzip");
102 ret
= commandPipe
.AddArg("-o");
104 ret
= commandPipe
.AddArg(fPackage
.String());
106 ret
= commandPipe
.AddArg("-d");
108 ret
= commandPipe
.AddArg(fDestinationFolder
.String());
110 fprintf(stderr
, "Faild to construct argument list for unzip "
111 "process: %s\n", strerror(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
);
119 return (status_t
)unzipThread
;
121 ret
= commandPipe
.ReadLines(stdOutAndErrPipe
, this);
123 fprintf(stderr
, "Piping the unzip process failed: %s\n",
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();
134 fprintf(stderr
, "Failed to construct path to "
135 ".OptionalPackageDescription: %s\n", strerror(ret
));
139 BEntry
descriptionEntry(descriptionPath
.Path());
140 if (!descriptionEntry
.Exists())
143 BFile
descriptionFile(&descriptionEntry
, B_READ_ONLY
);
144 ret
= descriptionFile
.InitCheck();
146 fprintf(stderr
, "Failed to construct file to "
147 ".OptionalPackageDescription: %s\n", strerror(ret
));
151 BPath
aboutSystemPath(fDestinationFolder
.String(),
152 "system/apps/AboutSystem");
153 ret
= aboutSystemPath
.InitCheck();
155 fprintf(stderr
, "Failed to construct path to AboutSystem: %s\n",
160 BNode
aboutSystemNode(aboutSystemPath
.Path());
161 ret
= aboutSystemNode
.InitCheck();
163 fprintf(stderr
, "Failed to construct node to AboutSystem: %s\n",
168 const char* kCopyrightsAttrName
= "COPYRIGHTS";
170 BString copyrightAttr
;
171 ret
= aboutSystemNode
.ReadAttrString(kCopyrightsAttrName
, ©rightAttr
);
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
));
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';
184 ssize_t read
= descriptionFile
.Read(buffer
, bufferSize
);
186 int32 length
= copyrightAttr
.Length();
187 if (read
< (ssize_t
)bufferSize
)
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 "
202 if (copyrightAttr
[copyrightAttr
.Length() - 1] != '\n')
203 copyrightAttr
<< '\n\n';
205 copyrightAttr
<< '\n';
207 ret
= aboutSystemNode
.WriteAttrString(kCopyrightsAttrName
, ©rightAttr
);
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
));
214 // Don't leave the .OptionalPackageDescription behind.
215 descriptionFile
.Unset();
216 descriptionEntry
.Remove();
226 UnzipEngine::IsCanceled()
228 if (fCancelSemaphore
< 0)
231 SemaphoreLocker
locker(fCancelSemaphore
);
232 return !locker
.IsLocked();
237 UnzipEngine::ReadLine(const BString
& line
)
239 if (fRetrievingListing
)
240 return _ReadLineListing(line
);
242 return _ReadLineExtract(line
);
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] == ' ')
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('/');
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) {
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());
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
);
291 // printf("listing not understood: %s", string);
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
++;
312 int pos
= line
.FindLast(" -> ");
314 line
.CopyInto(itemPath
, 13, pos
- 13);
316 line
.CopyInto(itemPath
, 13, line
.CountChars() - 14);
319 pos
= itemPath
.FindLast('/');
320 BString itemName
= itemPath
.String() + pos
+ 1;
321 itemPath
.Truncate(pos
);
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());
334 // printf("ignored: '%s'\n", line.String());
342 UnzipEngine::_UpdateProgress(const char* item
, const char* targetFolder
)
344 if (fProgressReporter
== NULL
)
348 if (fLastItemsUncompressed
< fItemsUncompressed
) {
349 items
= fItemsUncompressed
- fLastItemsUncompressed
;
350 fLastItemsUncompressed
= fItemsUncompressed
;
354 if (fLastBytesUncompressed
< fBytesUncompressed
) {
355 bytes
= fBytesUncompressed
- fLastBytesUncompressed
;
356 fLastBytesUncompressed
= fBytesUncompressed
;
359 fProgressReporter
->ItemsWritten(items
, bytes
, item
, targetFolder
);