Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / bin / package / command_extract.cpp
bloba786c07b8452d5423c16011eb5366b10d7badca2
1 /*
2 * Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <ctype.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <getopt.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
17 #include <algorithm>
18 #include <new>
20 #include <fs_attr.h>
21 #include <String.h>
23 #include <AutoDeleter.h>
24 #include <HashString.h>
26 #include <util/OpenHashTable.h>
28 #include <package/hpkg/BlockBufferPoolNoLock.h>
29 #include <package/hpkg/PackageContentHandler.h>
30 #include <package/hpkg/PackageDataReader.h>
31 #include <package/hpkg/PackageEntry.h>
32 #include <package/hpkg/PackageEntryAttribute.h>
33 #include <package/hpkg/PackageReader.h>
34 #include <package/hpkg/StandardErrorOutput.h>
35 #include <package/hpkg/v1/PackageContentHandler.h>
36 #include <package/hpkg/v1/PackageDataReader.h>
37 #include <package/hpkg/v1/PackageEntry.h>
38 #include <package/hpkg/v1/PackageEntryAttribute.h>
39 #include <package/hpkg/v1//PackageReader.h>
41 #include "package.h"
44 using BPackageKit::BHPKG::BAbstractBufferedDataReader;
45 using BPackageKit::BHPKG::BBlockBufferPoolNoLock;
46 using BPackageKit::BHPKG::BBufferDataReader;
47 using BPackageKit::BHPKG::BBufferPool;
48 using BPackageKit::BHPKG::BDataReader;
49 using BPackageKit::BHPKG::BErrorOutput;
50 using BPackageKit::BHPKG::BFDDataReader;
51 using BPackageKit::BHPKG::BPackageInfoAttributeValue;
52 using BPackageKit::BHPKG::BStandardErrorOutput;
55 struct VersionPolicyV1 {
56 typedef BPackageKit::BHPKG::V1::BPackageContentHandler
57 PackageContentHandler;
58 typedef BPackageKit::BHPKG::V1::BPackageData PackageData;
59 typedef BPackageKit::BHPKG::V1::BPackageEntry PackageEntry;
60 typedef BPackageKit::BHPKG::V1::BPackageEntryAttribute
61 PackageEntryAttribute;
62 typedef BPackageKit::BHPKG::V1::BPackageReader PackageReader;
63 typedef BDataReader HeapReaderBase;
65 static inline size_t BufferSize()
67 return BPackageKit::BHPKG::V1::B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB;
70 static inline const char* PackageInfoFileName()
72 return BPackageKit::BHPKG::V1::B_HPKG_PACKAGE_INFO_FILE_NAME;
75 static inline uint64 PackageDataCompressedSize(const PackageData& data)
77 return data.CompressedSize();
80 static inline uint64 PackageDataUncompressedSize(const PackageData& data)
82 return data.UncompressedSize();
85 static inline status_t InitReader(PackageReader& packageReader,
86 const char* fileName)
88 return packageReader.Init(fileName);
91 static status_t GetHeapReader(PackageReader& packageReader,
92 HeapReaderBase*& _heapReader, bool& _mustDelete)
94 _heapReader = new(std::nothrow) BFDDataReader(
95 packageReader.PackageFileFD());
96 _mustDelete = false;
97 return _heapReader != NULL ? B_OK : B_NO_MEMORY;
100 static status_t CreatePackageDataReader(BBufferPool* bufferPool,
101 HeapReaderBase* heapReader, const PackageData& data,
102 BAbstractBufferedDataReader*& _reader)
104 return BPackageKit::BHPKG::V1::BPackageDataReaderFactory(bufferPool)
105 .CreatePackageDataReader(heapReader, data, _reader);
109 struct VersionPolicyV2 {
110 typedef BPackageKit::BHPKG::BPackageContentHandler PackageContentHandler;
111 typedef BPackageKit::BHPKG::BPackageData PackageData;
112 typedef BPackageKit::BHPKG::BPackageEntry PackageEntry;
113 typedef BPackageKit::BHPKG::BPackageEntryAttribute PackageEntryAttribute;
114 typedef BPackageKit::BHPKG::BPackageReader PackageReader;
115 typedef BAbstractBufferedDataReader HeapReaderBase;
117 static inline size_t BufferSize()
119 return 64 * 1024;
122 static inline const char* PackageInfoFileName()
124 return BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME;
127 static inline uint64 PackageDataCompressedSize(const PackageData& data)
129 return data.Size();
132 static inline uint64 PackageDataUncompressedSize(const PackageData& data)
134 return data.Size();
137 static inline status_t InitReader(PackageReader& packageReader,
138 const char* fileName)
140 return packageReader.Init(fileName,
141 BPackageKit::BHPKG
142 ::B_HPKG_READER_DONT_PRINT_VERSION_MISMATCH_MESSAGE);
145 static status_t GetHeapReader(PackageReader& packageReader,
146 HeapReaderBase*& _heapReader, bool& _mustDelete)
148 _heapReader = packageReader.HeapReader();
149 _mustDelete = false;
150 return B_OK;
153 static status_t CreatePackageDataReader(BBufferPool* bufferPool,
154 HeapReaderBase* heapReader, const PackageData& data,
155 BAbstractBufferedDataReader*& _reader)
157 return BPackageKit::BHPKG::BPackageDataReaderFactory()
158 .CreatePackageDataReader(heapReader, data, _reader);
163 struct Entry {
164 Entry(Entry* parent, char* name, bool implicit)
166 fParent(parent),
167 fName(name),
168 fImplicit(implicit),
169 fSeen(false)
173 ~Entry()
175 _DeleteChildren();
177 free(fName);
180 status_t Init()
182 return fChildren.Init();
185 static status_t Create(Entry* parent, const char* name, bool implicit,
186 Entry*& _entry)
188 if (parent != NULL) {
189 Entry* entryInParent = parent->FindChild(name);
190 if (entryInParent != NULL) {
191 _entry = entryInParent;
192 return B_OK;
196 char* clonedName = strdup(name);
197 if (clonedName == NULL)
198 return B_NO_MEMORY;
200 Entry* entry = new(std::nothrow) Entry(parent, clonedName, implicit);
201 if (entry == NULL) {
202 free(clonedName);
203 return B_NO_MEMORY;
206 status_t error = entry->Init();
207 if (error != B_OK) {
208 delete entry;
209 return error;
212 if (parent != NULL)
213 parent->fChildren.Insert(entry);
215 _entry = entry;
216 return B_OK;
219 Entry* Parent() const
221 return fParent;
224 const char* Name() const
226 return fName;
229 bool IsImplicit() const
231 return fImplicit;
234 void SetExplicit()
236 // remove all children and set this entry non-implicit
237 _DeleteChildren();
238 fImplicit = false;
241 void SetSeen()
243 fSeen = true;
246 bool Seen() const
248 return fSeen;
251 Entry* FindChild(const char* name) const
253 return fChildren.Lookup(name);
256 private:
257 struct ChildHashDefinition {
258 typedef const char* KeyType;
259 typedef Entry ValueType;
261 size_t HashKey(const char* key) const
263 return string_hash(key);
266 size_t Hash(const Entry* value) const
268 return HashKey(value->Name());
271 bool Compare(const char* key, const Entry* value) const
273 return strcmp(value->Name(), key) == 0;
276 Entry*& GetLink(Entry* value) const
278 return value->fHashTableNext;
282 typedef BOpenHashTable<ChildHashDefinition> ChildTable;
284 private:
285 void _DeleteChildren()
287 Entry* child = fChildren.Clear(true);
288 while (child != NULL) {
289 Entry* next = child->fHashTableNext;
290 delete child;
291 child = next;
295 public:
296 Entry* fHashTableNext;
298 private:
299 Entry* fParent;
300 char* fName;
301 bool fImplicit;
302 bool fSeen;
303 ChildTable fChildren;
307 template<typename VersionPolicy>
308 struct PackageContentExtractHandler : VersionPolicy::PackageContentHandler {
309 PackageContentExtractHandler(BBufferPool* bufferPool,
310 typename VersionPolicy::HeapReaderBase* heapReader)
312 fBufferPool(bufferPool),
313 fPackageFileReader(heapReader),
314 fDataBuffer(NULL),
315 fDataBufferSize(0),
316 fRootFilterEntry(NULL, NULL, true),
317 fBaseDirectory(AT_FDCWD),
318 fInfoFileName(NULL),
319 fErrorOccurred(false)
323 ~PackageContentExtractHandler()
325 free(fDataBuffer);
328 status_t Init()
330 status_t error = fRootFilterEntry.Init();
331 if (error != B_OK)
332 return error;
334 fDataBufferSize = 64 * 1024;
335 fDataBuffer = malloc(fDataBufferSize);
336 if (fDataBuffer == NULL)
337 return B_NO_MEMORY;
339 return B_OK;
342 void SetBaseDirectory(int fd)
344 fBaseDirectory = fd;
347 void SetPackageInfoFile(const char* infoFileName)
349 fInfoFileName = infoFileName;
352 void SetExtractAll()
354 fRootFilterEntry.SetExplicit();
357 status_t AddFilterEntry(const char* fileName)
359 // add all components of the path
360 Entry* entry = &fRootFilterEntry;
361 while (*fileName != 0) {
362 const char* nextSlash = strchr(fileName, '/');
363 // no slash, just add the file name
364 if (nextSlash == NULL) {
365 return _AddFilterEntry(entry, fileName, strlen(fileName),
366 false, entry);
369 // find the start of the next component, skipping slashes
370 const char* nextComponent = nextSlash + 1;
371 while (*nextComponent == '/')
372 nextComponent++;
374 status_t error = _AddFilterEntry(entry, fileName,
375 nextSlash - fileName, *nextComponent != '\0', entry);
376 if (error != B_OK)
377 return error;
379 fileName = nextComponent;
382 return B_OK;
385 Entry* FindFilterEntry(const char* fileName)
387 // add all components of the path
388 Entry* entry = &fRootFilterEntry;
389 while (entry != NULL && *fileName != 0) {
390 const char* nextSlash = strchr(fileName, '/');
391 // no slash, just add the file name
392 if (nextSlash == NULL)
393 return entry->FindChild(fileName);
395 // find the start of the next component, skipping slashes
396 const char* nextComponent = nextSlash + 1;
397 while (*nextComponent == '/')
398 nextComponent++;
400 BString componentName(fileName, nextSlash - fileName);
401 entry = entry->FindChild(componentName);
403 fileName = nextComponent;
406 return entry;
409 virtual status_t HandleEntry(typename VersionPolicy::PackageEntry* entry)
411 // create a token
412 Token* token = new(std::nothrow) Token;
413 if (token == NULL)
414 return B_NO_MEMORY;
415 ObjectDeleter<Token> tokenDeleter(token);
417 // check whether this entry shall be ignored or is implicit
418 Entry* parentFilterEntry;
419 bool implicit;
420 if (entry->Parent() != NULL) {
421 Token* parentToken = (Token*)entry->Parent()->UserToken();
422 if (parentToken == NULL) {
423 // parent is ignored, so ignore this entry, too
424 return B_OK;
427 parentFilterEntry = parentToken->filterEntry;
428 implicit = parentToken->implicit;
429 } else {
430 parentFilterEntry = &fRootFilterEntry;
431 implicit = fRootFilterEntry.IsImplicit();
434 Entry* filterEntry = parentFilterEntry != NULL
435 ? parentFilterEntry->FindChild(entry->Name()) : NULL;
437 if (implicit && filterEntry == NULL) {
438 // parent is implicit and the filter doesn't include this entry
439 // -- ignore it
440 return B_OK;
443 // If the entry is in the filter, get its implicit flag.
444 if (filterEntry != NULL) {
445 implicit = filterEntry->IsImplicit();
446 filterEntry->SetSeen();
449 token->filterEntry = filterEntry;
450 token->implicit = implicit;
452 // get parent FD and the entry name
453 int parentFD;
454 const char* entryName;
455 _GetParentFDAndEntryName(entry, parentFD, entryName);
457 // check whether something is in the way
458 struct stat st;
459 bool entryExists = fstatat(parentFD, entryName, &st,
460 AT_SYMLINK_NOFOLLOW) == 0;
461 if (entryExists) {
462 if (S_ISREG(entry->Mode()) || S_ISLNK(entry->Mode())) {
463 // If the entry in the way is a regular file or a symlink,
464 // remove it, otherwise fail.
465 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
466 fprintf(stderr, "Error: Can't create entry \"%s\", since "
467 "something is in the way\n",
468 _EntryPath(entry).String());
469 return B_FILE_EXISTS;
472 if (unlinkat(parentFD, entryName, 0) != 0) {
473 fprintf(stderr, "Error: Failed to unlink entry \"%s\": %s\n",
474 _EntryPath(entry).String(), strerror(errno));
475 return errno;
478 entryExists = false;
479 } else if (S_ISDIR(entry->Mode())) {
480 // If the entry in the way is a directory, merge, otherwise
481 // fail.
482 if (!S_ISDIR(st.st_mode)) {
483 fprintf(stderr, "Error: Can't create directory \"%s\", "
484 "since something is in the way\n",
485 _EntryPath(entry).String());
486 return B_FILE_EXISTS;
491 // create the entry
492 int fd = -1;
493 if (S_ISREG(entry->Mode())) {
494 if (implicit) {
495 fprintf(stderr, "Error: File \"%s\" was specified as a "
496 "path directory component.\n", _EntryPath(entry).String());
497 return B_BAD_VALUE;
500 // create the file
501 fd = openat(parentFD, entryName, O_RDWR | O_CREAT | O_EXCL,
502 S_IRUSR | S_IWUSR);
503 // Note: We use read+write user permissions now -- so write
504 // operations (e.g. attributes) won't fail, but set them to the
505 // desired ones in HandleEntryDone().
506 if (fd < 0) {
507 fprintf(stderr, "Error: Failed to create file \"%s\": %s\n",
508 _EntryPath(entry).String(), strerror(errno));
509 return errno;
512 // write data
513 status_t error = _ExtractFileData(fPackageFileReader, entry->Data(),
514 fd);
515 if (error != B_OK)
516 return error;
517 } else if (S_ISLNK(entry->Mode())) {
518 if (implicit) {
519 fprintf(stderr, "Error: Symlink \"%s\" was specified as a "
520 "path directory component.\n", _EntryPath(entry).String());
521 return B_BAD_VALUE;
524 // create the symlink
525 const char* symlinkPath = entry->SymlinkPath();
526 if (symlinkat(symlinkPath != NULL ? symlinkPath : "", parentFD,
527 entryName) != 0) {
528 fprintf(stderr, "Error: Failed to create symlink \"%s\": %s\n",
529 _EntryPath(entry).String(), strerror(errno));
530 return errno;
532 // TODO: Set symlink permissions?
533 } else if (S_ISDIR(entry->Mode())) {
534 // create the directory, if necessary
535 if (!entryExists
536 && mkdirat(parentFD, entryName, S_IRWXU) != 0) {
537 // Note: We use read+write+exec user permissions now -- so write
538 // operations (e.g. attributes) won't fail, but set them to the
539 // desired ones in HandleEntryDone().
540 fprintf(stderr, "Error: Failed to create directory \"%s\": "
541 "%s\n", _EntryPath(entry).String(), strerror(errno));
542 return errno;
544 } else {
545 fprintf(stderr, "Error: Invalid file type for entry \"%s\"\n",
546 _EntryPath(entry).String());
547 return B_BAD_DATA;
550 // If not done yet (symlink, dir), open the node -- we need the FD.
551 if (fd < 0 && (!implicit || S_ISDIR(entry->Mode()))) {
552 fd = openat(parentFD, entryName, O_RDONLY | O_NOTRAVERSE);
553 if (fd < 0) {
554 fprintf(stderr, "Error: Failed to open entry \"%s\": %s\n",
555 _EntryPath(entry).String(), strerror(errno));
556 return errno;
559 token->fd = fd;
561 // set the file times
562 if (!entryExists && !implicit) {
563 timespec times[2] = {entry->AccessTime(), entry->ModifiedTime()};
564 futimens(fd, times);
566 // set user/group
567 // TODO:...
570 entry->SetUserToken(tokenDeleter.Detach());
571 return B_OK;
574 virtual status_t HandleEntryAttribute(
575 typename VersionPolicy::PackageEntry* entry,
576 typename VersionPolicy::PackageEntryAttribute* attribute)
578 // don't write attributes of ignored or implicit entries
579 Token* token = (Token*)entry->UserToken();
580 if (token == NULL || token->implicit)
581 return B_OK;
583 int entryFD = token->fd;
585 // create the attribute
586 int fd = fs_fopen_attr(entryFD, attribute->Name(), attribute->Type(),
587 O_WRONLY | O_CREAT | O_TRUNC);
588 if (fd < 0) {
589 int parentFD;
590 const char* entryName;
591 _GetParentFDAndEntryName(entry, parentFD, entryName);
593 fprintf(stderr, "Error: Failed to create attribute \"%s\" of "
594 "file \"%s\": %s\n", attribute->Name(),
595 _EntryPath(entry).String(), strerror(errno));
596 return errno;
599 // write data
600 status_t error = _ExtractFileData(fPackageFileReader, attribute->Data(),
601 fd);
603 fs_close_attr(fd);
605 return error;
608 virtual status_t HandleEntryDone(
609 typename VersionPolicy::PackageEntry* entry)
611 Token* token = (Token*)entry->UserToken();
613 // set the node permissions for non-symlinks
614 if (token != NULL && !S_ISLNK(entry->Mode())) {
615 // get parent FD and entry name
616 int parentFD;
617 const char* entryName;
618 _GetParentFDAndEntryName(entry, parentFD, entryName);
620 if (fchmodat(parentFD, entryName, entry->Mode() & ALLPERMS,
621 /*AT_SYMLINK_NOFOLLOW*/0) != 0) {
622 fprintf(stderr, "Warning: Failed to set permissions of file "
623 "\"%s\": %s\n", _EntryPath(entry).String(),
624 strerror(errno));
628 if (token != NULL) {
629 delete token;
630 entry->SetUserToken(NULL);
633 return B_OK;
636 virtual status_t HandlePackageAttribute(
637 const BPackageInfoAttributeValue& value)
639 return B_OK;
642 virtual void HandleErrorOccurred()
644 fErrorOccurred = true;
647 private:
648 struct Token {
649 Entry* filterEntry;
650 int fd;
651 bool implicit;
653 Token()
655 filterEntry(NULL),
656 fd(-1),
657 implicit(true)
661 ~Token()
663 if (fd >= 0)
664 close(fd);
668 private:
669 status_t _AddFilterEntry(Entry* parentEntry, const char* _name,
670 size_t nameLength, bool implicit, Entry*& _entry)
672 BString name(_name, nameLength);
673 if (name.IsEmpty())
674 return B_NO_MEMORY;
676 return Entry::Create(parentEntry, name.String(), implicit, _entry);
679 void _GetParentFDAndEntryName(typename VersionPolicy::PackageEntry* entry,
680 int& _parentFD, const char*& _entryName)
682 _entryName = entry->Name();
684 if (fInfoFileName != NULL
685 && strcmp(_entryName, VersionPolicy::PackageInfoFileName()) == 0) {
686 _parentFD = AT_FDCWD;
687 _entryName = fInfoFileName;
688 } else {
689 _parentFD = entry->Parent() != NULL
690 ? ((Token*)entry->Parent()->UserToken())->fd : fBaseDirectory;
694 BString _EntryPath(const typename VersionPolicy::PackageEntry* entry)
696 BString path;
698 if (const typename VersionPolicy::PackageEntry* parent
699 = entry->Parent()) {
700 path = _EntryPath(parent);
701 path << '/';
704 path << entry->Name();
705 return path;
708 status_t _ExtractFileData(
709 typename VersionPolicy::HeapReaderBase* dataReader,
710 const typename VersionPolicy::PackageData& data, int fd)
712 // create a PackageDataReader
713 BAbstractBufferedDataReader* reader;
714 status_t error = VersionPolicy::CreatePackageDataReader(fBufferPool,
715 dataReader, data, reader);
716 if (error != B_OK)
717 return error;
718 ObjectDeleter<BAbstractBufferedDataReader> readerDeleter(reader);
720 // write the data
721 off_t bytesRemaining = VersionPolicy::PackageDataUncompressedSize(data);
722 off_t offset = 0;
723 while (bytesRemaining > 0) {
724 // read
725 size_t toCopy = std::min((off_t)fDataBufferSize, bytesRemaining);
726 error = reader->ReadData(offset, fDataBuffer, toCopy);
727 if (error != B_OK) {
728 fprintf(stderr, "Error: Failed to read data: %s\n",
729 strerror(error));
730 return error;
733 // write
734 ssize_t bytesWritten = write_pos(fd, offset, fDataBuffer, toCopy);
735 if (bytesWritten < 0) {
736 fprintf(stderr, "Error: Failed to write data: %s\n",
737 strerror(errno));
738 return errno;
740 if ((size_t)bytesWritten != toCopy) {
741 fprintf(stderr, "Error: Failed to write all data (%zd of "
742 "%zu)\n", bytesWritten, toCopy);
743 return B_ERROR;
746 offset += toCopy;
747 bytesRemaining -= toCopy;
750 return B_OK;
753 private:
754 BBufferPool* fBufferPool;
755 typename VersionPolicy::HeapReaderBase* fPackageFileReader;
756 void* fDataBuffer;
757 size_t fDataBufferSize;
758 Entry fRootFilterEntry;
759 int fBaseDirectory;
760 const char* fInfoFileName;
761 bool fErrorOccurred;
765 template<typename VersionPolicy>
766 static void
767 do_extract(const char* packageFileName, const char* changeToDirectory,
768 const char* packageInfoFileName, const char* const* explicitEntries,
769 int explicitEntryCount, bool ignoreVersionError)
771 // open package
772 BStandardErrorOutput errorOutput;
773 BBlockBufferPoolNoLock bufferPool(VersionPolicy::BufferSize(), 2);
774 if (bufferPool.Init() != B_OK) {
775 errorOutput.PrintError("Error: Out of memory!\n");
776 exit(1);
779 typename VersionPolicy::PackageReader packageReader(&errorOutput);
780 status_t error = VersionPolicy::InitReader(packageReader, packageFileName);
781 if (error != B_OK) {
782 if (ignoreVersionError && error == B_MISMATCHED_VALUES)
783 return;
784 exit(1);
787 typename VersionPolicy::HeapReaderBase* heapReader;
788 bool mustDeleteHeapReader;
789 error = VersionPolicy::GetHeapReader(packageReader, heapReader,
790 mustDeleteHeapReader);
791 if (error != B_OK) {
792 fprintf(stderr, "Error: Failed to create heap reader: \"%s\"\n",
793 strerror(error));
794 exit(1);
796 ObjectDeleter<BDataReader> heapReaderDeleter(
797 mustDeleteHeapReader ? heapReader : NULL);
799 PackageContentExtractHandler<VersionPolicy> handler(&bufferPool,
800 heapReader);
801 error = handler.Init();
802 if (error != B_OK)
803 exit(1);
805 // If entries to extract have been specified explicitly, add those to the
806 // filtered ones.
807 if (explicitEntryCount > 0) {
808 for (int i = 0; i < explicitEntryCount; i++) {
809 const char* entryName = explicitEntries[i];
810 if (entryName[0] == '\0' || entryName[0] == '/') {
811 fprintf(stderr, "Error: Invalid entry name: \"%s\"\n",
812 entryName);
813 exit(1);
815 if (handler.AddFilterEntry(entryName) != B_OK)
816 exit(1);
818 } else
819 handler.SetExtractAll();
821 // get the target directory, if requested
822 if (changeToDirectory != NULL) {
823 int currentDirFD = open(changeToDirectory, O_RDONLY);
824 if (currentDirFD < 0) {
825 fprintf(stderr, "Error: Failed to change the current working "
826 "directory to \"%s\": %s\n", changeToDirectory,
827 strerror(errno));
828 exit(1);
831 handler.SetBaseDirectory(currentDirFD);
834 // If a package info file name is given, set it.
835 if (packageInfoFileName != NULL)
836 handler.SetPackageInfoFile(packageInfoFileName);
838 // extract
839 error = packageReader.ParseContent(&handler);
840 if (error != B_OK)
841 exit(1);
843 // check whether all explicitly specified entries have been extracted
844 if (explicitEntryCount > 0) {
845 for (int i = 0; i < explicitEntryCount; i++) {
846 if (Entry* entry = handler.FindFilterEntry(explicitEntries[i])) {
847 if (!entry->Seen()) {
848 fprintf(stderr, "Warning: Entry \"%s\" not found.\n",
849 explicitEntries[i]);
855 exit(0);
860 command_extract(int argc, const char* const* argv)
862 const char* changeToDirectory = NULL;
863 const char* packageInfoFileName = NULL;
865 while (true) {
866 static struct option sLongOptions[] = {
867 { "help", no_argument, 0, 'h' },
868 { 0, 0, 0, 0 }
871 opterr = 0; // don't print errors
872 int c = getopt_long(argc, (char**)argv, "+C:hi:", sLongOptions, NULL);
873 if (c == -1)
874 break;
876 switch (c) {
877 case 'C':
878 changeToDirectory = optarg;
879 break;
881 case 'h':
882 print_usage_and_exit(false);
883 break;
885 case 'i':
886 packageInfoFileName = optarg;
887 break;
889 default:
890 print_usage_and_exit(true);
891 break;
895 // At least one argument should remain -- the package file name. Any further
896 // arguments are the names of the entries to extract.
897 if (optind + 1 > argc)
898 print_usage_and_exit(true);
900 const char* packageFileName = argv[optind++];
901 const char* const* explicitEntries = argv + optind;
902 int explicitEntryCount = argc - optind;
903 do_extract<VersionPolicyV2>(packageFileName, changeToDirectory,
904 packageInfoFileName, explicitEntries, explicitEntryCount, true);
905 do_extract<VersionPolicyV1>(packageFileName, changeToDirectory,
906 packageInfoFileName, explicitEntries, explicitEntryCount, false);
908 return 0;