1/*
2 * Copyright 2004-2007, Ingo Weinhold, bonefish@users.sf.net.
3 * Distributed under the terms of the MIT License.
4 */
5
6/** A simple class wrapping a path. Has a fixed-sized buffer. */
7
8#include "KPath.h"
9
10#include <stdlib.h>
11
12#include "fssh_string.h"
13#include "vfs.h"
14
15
16// debugging
17#define TRACE(x) ;
18//#define TRACE(x) dprintf x
19
20
21KPath::KPath(fssh_size_t bufferSize)
22	:
23	fBuffer(NULL),
24	fBufferSize(0),
25	fPathLength(0),
26	fLocked(false)
27{
28	SetTo(NULL, bufferSize);
29}
30
31
32KPath::KPath(const char* path, bool normalize, fssh_size_t bufferSize)
33	:
34	fBuffer(NULL),
35	fBufferSize(0),
36	fPathLength(0),
37	fLocked(false)
38{
39	SetTo(path, normalize, bufferSize);
40}
41
42
43KPath::KPath(const KPath& other)
44	:
45	fBuffer(NULL),
46	fBufferSize(0),
47	fPathLength(0),
48	fLocked(false)
49{
50	*this = other;
51}
52
53
54KPath::~KPath()
55{
56	free(fBuffer);
57}
58
59
60fssh_status_t
61KPath::SetTo(const char* path, bool normalize, fssh_size_t bufferSize)
62{
63	if (bufferSize == 0)
64		bufferSize = FSSH_B_PATH_NAME_LENGTH;
65
66	// free the previous buffer, if the buffer size differs
67	if (fBuffer && fBufferSize != bufferSize) {
68		free(fBuffer);
69		fBuffer = NULL;
70		fBufferSize = 0;
71	}
72	fPathLength = 0;
73	fLocked = false;
74
75	// allocate buffer
76	if (!fBuffer)
77		fBuffer = (char*)malloc(bufferSize);
78	if (!fBuffer)
79		return FSSH_B_NO_MEMORY;
80	if (fBuffer) {
81		fBufferSize = bufferSize;
82		fBuffer[0] = '\0';
83	}
84	return SetPath(path, normalize);
85}
86
87
88fssh_status_t
89KPath::InitCheck() const
90{
91	return fBuffer ? FSSH_B_OK : FSSH_B_NO_MEMORY;
92}
93
94
95fssh_status_t
96KPath::SetPath(const char *path, bool normalize)
97{
98	if (!fBuffer)
99		return FSSH_B_NO_INIT;
100
101	if (path) {
102		if (normalize) {
103			// normalize path
104			fssh_status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
105				true);
106			if (error != FSSH_B_OK) {
107				SetPath(NULL);
108				return error;
109			}
110			fPathLength = fssh_strlen(fBuffer);
111		} else {
112			// don't normalize path
113			fssh_size_t length = fssh_strlen(path);
114			if (length >= fBufferSize)
115				return FSSH_B_BUFFER_OVERFLOW;
116
117			fssh_memcpy(fBuffer, path, length + 1);
118			fPathLength = length;
119			_ChopTrailingSlashes();
120		}
121	} else {
122		fBuffer[0] = '\0';
123		fPathLength = 0;
124	}
125	return FSSH_B_OK;
126}
127
128
129const char*
130KPath::Path() const
131{
132	return fBuffer;
133}
134
135
136char *
137KPath::LockBuffer()
138{
139	if (!fBuffer || fLocked)
140		return NULL;
141
142	fLocked = true;
143	return fBuffer;
144}
145
146
147void
148KPath::UnlockBuffer()
149{
150	if (!fLocked) {
151		TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n"));
152		return;
153	}
154	fLocked = false;
155	fPathLength = fssh_strnlen(fBuffer, fBufferSize);
156	if (fPathLength == fBufferSize) {
157		TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n"));
158		fPathLength--;
159		fBuffer[fPathLength] = '\0';
160	}
161	_ChopTrailingSlashes();
162}
163
164
165const char *
166KPath::Leaf() const
167{
168	if (!fBuffer)
169		return NULL;
170
171	// only "/" has trailing slashes -- then we have to return the complete
172	// buffer, as we have to do in case there are no slashes at all
173	if (fPathLength != 1 || fBuffer[0] != '/') {
174		for (int32_t i = fPathLength - 1; i >= 0; i--) {
175			if (fBuffer[i] == '/')
176				return fBuffer + i + 1;
177		}
178	}
179	return fBuffer;
180}
181
182
183fssh_status_t
184KPath::ReplaceLeaf(const char *newLeaf)
185{
186	const char *leaf = Leaf();
187	if (!leaf)
188		return FSSH_B_NO_INIT;
189
190	int32_t leafIndex = leaf - fBuffer;
191	// chop off the current leaf (don't replace "/", though)
192	if (leafIndex != 0 || fBuffer[leafIndex - 1]) {
193		fBuffer[leafIndex] = '\0';
194		fPathLength = leafIndex;
195		_ChopTrailingSlashes();
196	}
197
198	// if a leaf was given, append it
199	if (newLeaf)
200		return Append(newLeaf);
201	return FSSH_B_OK;
202}
203
204
205fssh_status_t
206KPath::Append(const char *component, bool isComponent)
207{
208	// check initialization and parameter
209	if (!fBuffer)
210		return FSSH_B_NO_INIT;
211	if (!component)
212		return FSSH_B_BAD_VALUE;
213	if (fPathLength == 0)
214		return SetPath(component);
215
216	// get component length
217	fssh_size_t componentLength = fssh_strlen(component);
218	if (componentLength < 1)
219		return FSSH_B_OK;
220
221	// if our current path is empty, we just copy the supplied one
222	// compute the result path len
223	bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/'
224		&& component[0] != '/';
225	fssh_size_t resultPathLength = fPathLength + componentLength + (insertSlash ? 1 : 0);
226	if (resultPathLength >= fBufferSize)
227		return FSSH_B_BUFFER_OVERFLOW;
228
229	// compose the result path
230	if (insertSlash)
231		fBuffer[fPathLength++] = '/';
232	fssh_memcpy(fBuffer + fPathLength, component, componentLength + 1);
233	fPathLength = resultPathLength;
234	return FSSH_B_OK;
235}
236
237
238KPath&
239KPath::operator=(const KPath& other)
240{
241	SetTo(other.fBuffer, other.fBufferSize);
242	return *this;
243}
244
245
246KPath&
247KPath::operator=(const char* path)
248{
249	SetTo(path);
250	return *this;
251}
252
253
254bool
255KPath::operator==(const KPath& other) const
256{
257	if (!fBuffer)
258		return !other.fBuffer;
259
260	return (other.fBuffer
261		&& fPathLength == other.fPathLength
262		&& fssh_strcmp(fBuffer, other.fBuffer) == 0);
263}
264
265
266bool
267KPath::operator==(const char* path) const
268{
269	if (!fBuffer)
270		return (!path);
271
272	return path && !fssh_strcmp(fBuffer, path);
273}
274
275
276bool
277KPath::operator!=(const KPath& other) const
278{
279	return !(*this == other);
280}
281
282
283bool
284KPath::operator!=(const char* path) const
285{
286	return !(*this == path);
287}
288
289
290void
291KPath::_ChopTrailingSlashes()
292{
293	if (fBuffer) {
294		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
295			fBuffer[--fPathLength] = '\0';
296	}
297}
298
299