1 /* 2 * Copyright 2013, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 */ 8 9 10 #include <MergedDirectory.h> 11 12 #include <new> 13 #include <set> 14 #include <string> 15 16 #include <Directory.h> 17 #include <Entry.h> 18 19 #include <AutoDeleter.h> 20 21 #include "storage_support.h" 22 23 24 struct BMergedDirectory::EntryNameSet : std::set<std::string> { 25 }; 26 27 28 BMergedDirectory::BMergedDirectory(BPolicy policy) 29 : 30 BEntryList(), 31 fDirectories(10, true), 32 fPolicy(policy), 33 fDirectoryIndex(0), 34 fVisitedEntries(NULL) 35 { 36 } 37 38 39 BMergedDirectory::~BMergedDirectory() 40 { 41 delete fVisitedEntries; 42 } 43 44 45 status_t 46 BMergedDirectory::Init() 47 { 48 delete fVisitedEntries; 49 fDirectories.MakeEmpty(true); 50 51 fVisitedEntries = new(std::nothrow) EntryNameSet; 52 return fVisitedEntries != NULL ? B_OK : B_NO_MEMORY; 53 } 54 55 56 BMergedDirectory::BPolicy 57 BMergedDirectory::Policy() const 58 { 59 return fPolicy; 60 } 61 62 63 void 64 BMergedDirectory::SetPolicy(BPolicy policy) 65 { 66 fPolicy = policy; 67 } 68 69 70 status_t 71 BMergedDirectory::AddDirectory(BDirectory* directory) 72 { 73 return fDirectories.AddItem(directory) ? B_OK : B_NO_MEMORY; 74 } 75 76 77 status_t 78 BMergedDirectory::AddDirectory(const char* path) 79 { 80 BDirectory* directory = new(std::nothrow) BDirectory(path); 81 if (directory == NULL) 82 return B_NO_MEMORY; 83 ObjectDeleter<BDirectory> directoryDeleter(directory); 84 85 if (directory->InitCheck() != B_OK) 86 return directory->InitCheck(); 87 88 status_t error = AddDirectory(directory); 89 if (error != B_OK) 90 return error; 91 directoryDeleter.Detach(); 92 93 return B_OK; 94 } 95 96 97 status_t 98 BMergedDirectory::GetNextEntry(BEntry* entry, bool traverse) 99 { 100 entry_ref ref; 101 status_t error = GetNextRef(&ref); 102 if (error != B_OK) 103 return error; 104 105 return entry->SetTo(&ref, traverse); 106 } 107 108 109 status_t 110 BMergedDirectory::GetNextRef(entry_ref* ref) 111 { 112 BPrivate::Storage::LongDirEntry dirEntry; 113 int32 result = GetNextDirents(&dirEntry, sizeof(dirEntry), 1); 114 if (result < 0) 115 return result; 116 if (result == 0) 117 return B_ENTRY_NOT_FOUND; 118 119 BEntry entry; 120 status_t error 121 = entry.SetTo(fDirectories.ItemAt(fDirectoryIndex), dirEntry.d_name); 122 if (error != B_OK) 123 return error; 124 125 return entry.GetRef(ref); 126 } 127 128 129 int32 130 BMergedDirectory::GetNextDirents(struct dirent* direntBuffer, size_t bufferSize, 131 int32 maxEntries) 132 { 133 if (maxEntries <= 0) 134 return B_BAD_VALUE; 135 136 while (fDirectoryIndex < fDirectories.CountItems()) { 137 int32 count = fDirectories.ItemAt(fDirectoryIndex)->GetNextDirents( 138 direntBuffer, bufferSize, 1); 139 if (count < 0) 140 return count; 141 if (count == 0) { 142 fDirectoryIndex++; 143 continue; 144 } 145 146 if (strcmp(direntBuffer->d_name, ".") == 0 147 || strcmp(direntBuffer->d_name, "..") == 0) { 148 continue; 149 } 150 151 switch (fPolicy) { 152 case B_ALLOW_DUPLICATES: 153 return count; 154 155 case B_ALWAYS_FIRST: 156 case B_COMPARE: 157 if (fVisitedEntries != NULL 158 && fVisitedEntries->find(direntBuffer->d_name) 159 != fVisitedEntries->end()) { 160 continue; 161 } 162 163 if (fVisitedEntries != NULL) { 164 try { 165 fVisitedEntries->insert(direntBuffer->d_name); 166 } catch (std::bad_alloc&) { 167 return B_NO_MEMORY; 168 } 169 } 170 171 if (fPolicy == B_ALWAYS_FIRST 172 || _IsBestEntry(direntBuffer->d_name)) { 173 return 1; 174 } 175 176 fVisitedEntries->erase(direntBuffer->d_name); 177 break; 178 } 179 } 180 181 return B_ENTRY_NOT_FOUND; 182 } 183 184 185 status_t 186 BMergedDirectory::Rewind() 187 { 188 for (int32 i = 0; BDirectory* directory = fDirectories.ItemAt(i); i++) 189 directory->Rewind(); 190 191 if (fVisitedEntries != NULL) 192 fVisitedEntries->clear(); 193 194 fDirectoryIndex = 0; 195 return B_OK; 196 } 197 198 199 int32 200 BMergedDirectory::CountEntries() 201 { 202 int32 count = 0; 203 char buffer[sizeof(dirent) + B_FILE_NAME_LENGTH]; 204 while (GetNextDirents((dirent*)&buffer, sizeof(buffer), 1) == 1) 205 count++; 206 return count; 207 } 208 209 210 bool 211 BMergedDirectory::ShallPreferFirstEntry(const entry_ref& entry1, int32 index1, 212 const entry_ref& entry2, int32 index2) 213 { 214 // That's basically B_ALWAYS_FIRST semantics. A derived class will implement 215 // the desired semantics. 216 return true; 217 } 218 219 220 bool 221 BMergedDirectory::_IsBestEntry(const char* name) 222 { 223 entry_ref bestEntry; 224 if (BEntry(fDirectories.ItemAt(fDirectoryIndex), name).GetRef(&bestEntry) 225 != B_OK) { 226 return false; 227 } 228 229 int32 directoryCount = fDirectories.CountItems(); 230 for (int32 i = fDirectoryIndex + 1; i < directoryCount; i++) { 231 BEntry entry(fDirectories.ItemAt(i), name); 232 entry_ref ref; 233 if (entry.Exists() && entry.GetRef(&ref) == B_OK 234 && !ShallPreferFirstEntry(bestEntry, fDirectoryIndex, ref, i)) { 235 return false; 236 } 237 } 238 239 return true; 240 } 241