-// doDelete - Implement the delete operation. This function deletes zero or more
-// members from the archive. Note that if the count is specified, there should
-// be no more than one path in the Paths list or else this algorithm breaks.
-// That check is enforced in parseCommandLine (above).
-bool
-doDelete(std::string* ErrMsg) {
- if (buildPaths(false, ErrMsg))
- return true;
- if (Paths.empty())
- return false;
- unsigned countDown = Count;
- for (Archive::iterator I = TheArchive->begin(), E = TheArchive->end();
- I != E; ) {
- if (std::find(Paths.begin(), Paths.end(), I->getPath()) != Paths.end()) {
- if (countDown == 1) {
- Archive::iterator J = I;
- ++I;
- TheArchive->erase(J);
- } else
- countDown--;
- } else {
- ++I;
+namespace {
+class NewArchiveIterator {
+ bool IsNewMember;
+ StringRef Name;
+
+ object::Archive::child_iterator OldI;
+
+ std::string NewFilename;
+ mutable int NewFD;
+ mutable sys::fs::file_status NewStatus;
+
+public:
+ NewArchiveIterator(object::Archive::child_iterator I, StringRef Name);
+ NewArchiveIterator(std::string *I, StringRef Name);
+ NewArchiveIterator();
+ bool isNewMember() const;
+ StringRef getName() const;
+
+ object::Archive::child_iterator getOld() const;
+
+ const char *getNew() const;
+ int getFD() const;
+ const sys::fs::file_status &getStatus() const;
+};
+}
+
+NewArchiveIterator::NewArchiveIterator() {}
+
+NewArchiveIterator::NewArchiveIterator(object::Archive::child_iterator I,
+ StringRef Name)
+ : IsNewMember(false), Name(Name), OldI(I) {}
+
+NewArchiveIterator::NewArchiveIterator(std::string *NewFilename, StringRef Name)
+ : IsNewMember(true), Name(Name), NewFilename(*NewFilename), NewFD(-1) {}
+
+StringRef NewArchiveIterator::getName() const { return Name; }
+
+bool NewArchiveIterator::isNewMember() const { return IsNewMember; }
+
+object::Archive::child_iterator NewArchiveIterator::getOld() const {
+ assert(!IsNewMember);
+ return OldI;
+}
+
+const char *NewArchiveIterator::getNew() const {
+ assert(IsNewMember);
+ return NewFilename.c_str();
+}
+
+int NewArchiveIterator::getFD() const {
+ assert(IsNewMember);
+ if (NewFD != -1)
+ return NewFD;
+ failIfError(sys::fs::openFileForRead(NewFilename, NewFD), NewFilename);
+ assert(NewFD != -1);
+
+ failIfError(sys::fs::status(NewFD, NewStatus), NewFilename);
+
+ // Opening a directory doesn't make sense. Let it fail.
+ // Linux cannot open directories with open(2), although
+ // cygwin and *bsd can.
+ if (NewStatus.type() == sys::fs::file_type::directory_file)
+ failIfError(make_error_code(std::errc::is_a_directory), NewFilename);
+
+ return NewFD;
+}
+
+const sys::fs::file_status &NewArchiveIterator::getStatus() const {
+ assert(IsNewMember);
+ assert(NewFD != -1 && "Must call getFD first");
+ return NewStatus;
+}
+
+template <typename T>
+void addMember(std::vector<NewArchiveIterator> &Members, T I, StringRef Name,
+ int Pos = -1) {
+ NewArchiveIterator NI(I, Name);
+ if (Pos == -1)
+ Members.push_back(NI);
+ else
+ Members[Pos] = NI;
+}
+
+enum InsertAction {
+ IA_AddOldMember,
+ IA_AddNewMeber,
+ IA_Delete,
+ IA_MoveOldMember,
+ IA_MoveNewMember
+};
+
+static InsertAction
+computeInsertAction(ArchiveOperation Operation,
+ object::Archive::child_iterator I, StringRef Name,
+ std::vector<std::string>::iterator &Pos) {
+ if (Operation == QuickAppend || Members.empty())
+ return IA_AddOldMember;
+
+ std::vector<std::string>::iterator MI = std::find_if(
+ Members.begin(), Members.end(),
+ [Name](StringRef Path) { return Name == sys::path::filename(Path); });
+
+ if (MI == Members.end())
+ return IA_AddOldMember;
+
+ Pos = MI;
+
+ if (Operation == Delete)
+ return IA_Delete;
+
+ if (Operation == Move)
+ return IA_MoveOldMember;
+
+ if (Operation == ReplaceOrInsert) {
+ StringRef PosName = sys::path::filename(RelPos);
+ if (!OnlyUpdate) {
+ if (PosName.empty())
+ return IA_AddNewMeber;
+ return IA_MoveNewMember;
+ }
+
+ // We could try to optimize this to a fstat, but it is not a common
+ // operation.
+ sys::fs::file_status Status;
+ failIfError(sys::fs::status(*MI, Status), *MI);
+ if (Status.getLastModificationTime() < I->getLastModified()) {
+ if (PosName.empty())
+ return IA_AddOldMember;
+ return IA_MoveOldMember;