xref: /haiku/src/tests/add-ons/kernel/kernelland_emu/KPath.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
1 /*
2  * Copyright 2004-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2008-2017, Axel Dörfler, axeld@pinc-software.de.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 /*! A simple class wrapping a path. Has a fixed-sized buffer. */
9 
10 
11 #include <fs/KPath.h>
12 #include <Path.h>
13 
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include <team.h>
18 
19 
20 // debugging
21 #define TRACE(x) ;
22 //#define TRACE(x) dprintf x
23 
24 
25 KPath::KPath(size_t bufferSize)
26 	:
27 	fBuffer(NULL),
28 	fBufferSize(0),
29 	fPathLength(0),
30 	fLocked(false),
31 	fLazy(false),
32 	fFailed(false),
33 	fIsNull(false)
34 {
35 	SetTo(NULL, DEFAULT, bufferSize);
36 }
37 
38 
39 KPath::KPath(const char* path, int32 flags, size_t bufferSize)
40 	:
41 	fBuffer(NULL),
42 	fBufferSize(0),
43 	fPathLength(0),
44 	fLocked(false),
45 	fLazy(false),
46 	fFailed(false),
47 	fIsNull(false)
48 {
49 	SetTo(path, flags, bufferSize);
50 }
51 
52 
53 KPath::KPath(const KPath& other)
54 	:
55 	fBuffer(NULL),
56 	fBufferSize(0),
57 	fPathLength(0),
58 	fLocked(false),
59 	fLazy(false),
60 	fFailed(false),
61 	fIsNull(false)
62 {
63 	*this = other;
64 }
65 
66 
67 KPath::~KPath()
68 {
69 	free(fBuffer);
70 }
71 
72 
73 status_t
74 KPath::SetTo(const char* path, int32 flags, size_t bufferSize)
75 {
76 	if (bufferSize == 0)
77 		bufferSize = B_PATH_NAME_LENGTH;
78 
79 	// free the previous buffer, if the buffer size differs
80 	if (fBuffer != NULL && fBufferSize != bufferSize) {
81 		free(fBuffer);
82 		fBuffer = NULL;
83 		fBufferSize = 0;
84 	}
85 
86 	fPathLength = 0;
87 	fLocked = false;
88 	fBufferSize = bufferSize;
89 	fLazy = (flags & LAZY_ALLOC) != 0;
90 	fIsNull = path == NULL;
91 
92 	if (path != NULL || !fLazy) {
93 		status_t status = _AllocateBuffer();
94 		if (status != B_OK)
95 			return status;
96 	}
97 
98 	return SetPath(path, flags);
99 }
100 
101 
102 void
103 KPath::Adopt(KPath& other)
104 {
105 	free(fBuffer);
106 
107 	fBuffer = other.fBuffer;
108 	fBufferSize = other.fBufferSize;
109 	fPathLength = other.fPathLength;
110 	fLazy = other.fLazy;
111 	fFailed = other.fFailed;
112 	fIsNull = other.fIsNull;
113 
114 	other.fBuffer = NULL;
115 	if (!other.fLazy)
116 		other.fBufferSize = 0;
117 	other.fPathLength = 0;
118 	other.fFailed = false;
119 	other.fIsNull = other.fLazy;
120 }
121 
122 
123 status_t
124 KPath::InitCheck() const
125 {
126 	if (fBuffer != NULL || (fLazy && !fFailed && fBufferSize != 0))
127 		return B_OK;
128 
129 	return fFailed ? B_NO_MEMORY : B_NO_INIT;
130 }
131 
132 
133 /*!	\brief Sets the buffer to \a path.
134 
135 	\param flags Understands the following two options:
136 		- \c NORMALIZE
137 		- \c TRAVERSE_LEAF_LINK
138 */
139 status_t
140 KPath::SetPath(const char* path, int32 flags)
141 {
142 	if (path == NULL && fLazy && fBuffer == NULL) {
143 		fIsNull = true;
144 		return B_OK;
145 	}
146 
147 	if (fBuffer == NULL) {
148 		if (fLazy) {
149 			status_t status = _AllocateBuffer();
150 			if (status != B_OK)
151 				return B_NO_MEMORY;
152 		} else
153 			return B_NO_INIT;
154 	}
155 
156 	fIsNull = false;
157 
158 	if (path != NULL) {
159 		if ((flags & NORMALIZE) != 0) {
160 			// normalize path
161 			status_t status = _Normalize(path,
162 				(flags & TRAVERSE_LEAF_LINK) != 0);
163 			if (status != B_OK)
164 				return status;
165 		} else {
166 			// don't normalize path
167 			size_t length = strlen(path);
168 			if (length >= fBufferSize)
169 				return B_BUFFER_OVERFLOW;
170 
171 			memcpy(fBuffer, path, length + 1);
172 			fPathLength = length;
173 			_ChopTrailingSlashes();
174 		}
175 	} else {
176 		fBuffer[0] = '\0';
177 		fPathLength = 0;
178 		if (fLazy)
179 			fIsNull = true;
180 	}
181 	return B_OK;
182 }
183 
184 
185 const char*
186 KPath::Path() const
187 {
188 	return fIsNull ? NULL : fBuffer;
189 }
190 
191 
192 /*!	\brief Locks the buffer for external changes.
193 
194 	\param force In lazy mode, this will allocate a buffer when set.
195 		Otherwise, \c NULL will be returned if set to NULL.
196 */
197 char*
198 KPath::LockBuffer(bool force)
199 {
200 	if (fBuffer == NULL && fLazy) {
201 		if (fIsNull && !force)
202 			return NULL;
203 
204 		_AllocateBuffer();
205 	}
206 
207 	if (fBuffer == NULL || fLocked)
208 		return NULL;
209 
210 	fLocked = true;
211 	fIsNull = false;
212 
213 	return fBuffer;
214 }
215 
216 
217 void
218 KPath::UnlockBuffer()
219 {
220 	if (!fLocked) {
221 		TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n"));
222 		return;
223 	}
224 
225 	fLocked = false;
226 
227 	if (fBuffer == NULL)
228 		return;
229 
230 	fPathLength = strnlen(fBuffer, fBufferSize);
231 	if (fPathLength == fBufferSize) {
232 		TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n"));
233 		fPathLength--;
234 		fBuffer[fPathLength] = '\0';
235 	}
236 	_ChopTrailingSlashes();
237 }
238 
239 
240 char*
241 KPath::DetachBuffer()
242 {
243 	char* buffer = fBuffer;
244 
245 	if (fBuffer != NULL) {
246 		fBuffer = NULL;
247 		fPathLength = 0;
248 		fLocked = false;
249 	}
250 
251 	return buffer;
252 }
253 
254 
255 const char*
256 KPath::Leaf() const
257 {
258 	if (fBuffer == NULL)
259 		return NULL;
260 
261 	for (int32 i = fPathLength - 1; i >= 0; i--) {
262 		if (fBuffer[i] == '/')
263 			return fBuffer + i + 1;
264 	}
265 
266 	return fBuffer;
267 }
268 
269 
270 status_t
271 KPath::ReplaceLeaf(const char* newLeaf)
272 {
273 	const char* leaf = Leaf();
274 	if (leaf == NULL)
275 		return B_NO_INIT;
276 
277 	int32 leafIndex = leaf - fBuffer;
278 	// chop off the current leaf (don't replace "/", though)
279 	if (leafIndex != 0 || fBuffer[leafIndex - 1]) {
280 		fBuffer[leafIndex] = '\0';
281 		fPathLength = leafIndex;
282 		_ChopTrailingSlashes();
283 	}
284 
285 	// if a leaf was given, append it
286 	if (newLeaf != NULL)
287 		return Append(newLeaf);
288 	return B_OK;
289 }
290 
291 
292 bool
293 KPath::RemoveLeaf()
294 {
295 	// get the leaf -- bail out, if not initialized or only the "/" is left
296 	const char* leaf = Leaf();
297 	if (leaf == NULL || leaf == fBuffer || leaf[0] == '\0')
298 		return false;
299 
300 	// chop off the leaf
301 	int32 leafIndex = leaf - fBuffer;
302 	fBuffer[leafIndex] = '\0';
303 	fPathLength = leafIndex;
304 	_ChopTrailingSlashes();
305 
306 	return true;
307 }
308 
309 
310 status_t
311 KPath::Append(const char* component, bool isComponent)
312 {
313 	// check initialization and parameter
314 	if (fBuffer == NULL)
315 		return B_NO_INIT;
316 	if (component == NULL)
317 		return B_BAD_VALUE;
318 	if (fPathLength == 0)
319 		return SetPath(component);
320 
321 	// get component length
322 	size_t componentLength = strlen(component);
323 	if (componentLength < 1)
324 		return B_OK;
325 
326 	// if our current path is empty, we just copy the supplied one
327 	// compute the result path len
328 	bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/'
329 		&& component[0] != '/';
330 	size_t resultPathLength = fPathLength + componentLength
331 		+ (insertSlash ? 1 : 0);
332 	if (resultPathLength >= fBufferSize)
333 		return B_BUFFER_OVERFLOW;
334 
335 	// compose the result path
336 	if (insertSlash)
337 		fBuffer[fPathLength++] = '/';
338 	memcpy(fBuffer + fPathLength, component, componentLength + 1);
339 	fPathLength = resultPathLength;
340 	return B_OK;
341 }
342 
343 
344 status_t
345 KPath::Normalize(bool traverseLeafLink)
346 {
347 	if (fBuffer == NULL)
348 		return B_NO_INIT;
349 	if (fPathLength == 0)
350 		return B_BAD_VALUE;
351 
352 	return _Normalize(fBuffer, traverseLeafLink);
353 }
354 
355 
356 KPath&
357 KPath::operator=(const KPath& other)
358 {
359 	SetTo(other.fBuffer, fLazy ? KPath::LAZY_ALLOC : KPath::DEFAULT,
360 		other.fBufferSize);
361 	return *this;
362 }
363 
364 
365 KPath&
366 KPath::operator=(const char* path)
367 {
368 	SetPath(path);
369 	return *this;
370 }
371 
372 
373 bool
374 KPath::operator==(const KPath& other) const
375 {
376 	if (fBuffer == NULL)
377 		return !other.fBuffer;
378 
379 	return other.fBuffer != NULL
380 		&& fPathLength == other.fPathLength
381 		&& strcmp(fBuffer, other.fBuffer) == 0;
382 }
383 
384 
385 bool
386 KPath::operator==(const char* path) const
387 {
388 	if (fBuffer == NULL)
389 		return path == NULL;
390 
391 	return path != NULL && strcmp(fBuffer, path) == 0;
392 }
393 
394 
395 bool
396 KPath::operator!=(const KPath& other) const
397 {
398 	return !(*this == other);
399 }
400 
401 
402 bool
403 KPath::operator!=(const char* path) const
404 {
405 	return !(*this == path);
406 }
407 
408 
409 status_t
410 KPath::_AllocateBuffer()
411 {
412 	if (fBuffer == NULL && fBufferSize != 0)
413 		fBuffer = (char*)malloc(fBufferSize);
414 	if (fBuffer == NULL) {
415 		fFailed = true;
416 		return B_NO_MEMORY;
417 	}
418 
419 	fBuffer[0] = '\0';
420 	fFailed = false;
421 	return B_OK;
422 }
423 
424 
425 status_t
426 KPath::_Normalize(const char* path, bool traverseLeafLink)
427 {
428 	BPath normalizedPath;
429 	status_t error = normalizedPath.SetTo(path, NULL, true);
430 	if (error != B_OK) {
431 		fBuffer[0] = '\0';
432 		fPathLength = 0;
433 		return error;
434 	}
435 
436 	strlcpy(fBuffer, normalizedPath.Path(), fBufferSize);
437 	fPathLength = strlen(fBuffer);
438 	return B_OK;
439 }
440 
441 
442 void
443 KPath::_ChopTrailingSlashes()
444 {
445 	if (fBuffer != NULL) {
446 		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
447 			fBuffer[--fPathLength] = '\0';
448 	}
449 }
450 
451