xref: /haiku/src/build/libbe/storage/MergedDirectory.cpp (revision 204dee708a999d5a71d0cb9497650ee7cef85d0a)
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