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
25KPath::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
39KPath::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
53KPath::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
67KPath::~KPath()
68{
69	free(fBuffer);
70}
71
72
73status_t
74KPath::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
102void
103KPath::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
123status_t
124KPath::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*/
139status_t
140KPath::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
185const char*
186KPath::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*/
197char*
198KPath::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
217void
218KPath::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
240char*
241KPath::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
255const char*
256KPath::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
270status_t
271KPath::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
292bool
293KPath::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
310status_t
311KPath::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
344status_t
345KPath::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
356KPath&
357KPath::operator=(const KPath& other)
358{
359	SetTo(other.fBuffer, fLazy ? KPath::LAZY_ALLOC : KPath::DEFAULT,
360		other.fBufferSize);
361	return *this;
362}
363
364
365KPath&
366KPath::operator=(const char* path)
367{
368	SetPath(path);
369	return *this;
370}
371
372
373bool
374KPath::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
385bool
386KPath::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
395bool
396KPath::operator!=(const KPath& other) const
397{
398	return !(*this == other);
399}
400
401
402bool
403KPath::operator!=(const char* path) const
404{
405	return !(*this == path);
406}
407
408
409status_t
410KPath::_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
425status_t
426KPath::_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
442void
443KPath::_ChopTrailingSlashes()
444{
445	if (fBuffer != NULL) {
446		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
447			fBuffer[--fPathLength] = '\0';
448	}
449}
450
451