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