xref: /haiku/src/system/kernel/fs/KPath.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
1 /*
2  * Copyright 2004-2008, Ingo Weinhold, ingo_weinhold@gmx.de.
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 <fs/KPath.h>
9 
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include <team.h>
14 #include <vfs.h>
15 
16 
17 // debugging
18 #define TRACE(x) ;
19 //#define TRACE(x) dprintf x
20 
21 
22 KPath::KPath(size_t bufferSize)
23 	:
24 	fBuffer(NULL),
25 	fBufferSize(0),
26 	fPathLength(0),
27 	fLocked(false)
28 {
29 	SetTo(NULL, false, bufferSize);
30 }
31 
32 
33 KPath::KPath(const char* path, bool normalize, size_t bufferSize)
34 	:
35 	fBuffer(NULL),
36 	fBufferSize(0),
37 	fPathLength(0),
38 	fLocked(false)
39 {
40 	SetTo(path, normalize, bufferSize);
41 }
42 
43 
44 KPath::KPath(const KPath& other)
45 	:
46 	fBuffer(NULL),
47 	fBufferSize(0),
48 	fPathLength(0),
49 	fLocked(false)
50 {
51 	*this = other;
52 }
53 
54 
55 KPath::~KPath()
56 {
57 	free(fBuffer);
58 }
59 
60 
61 status_t
62 KPath::SetTo(const char* path, bool normalize, size_t bufferSize,
63 	bool traverseLeafLink)
64 {
65 	if (bufferSize == 0)
66 		bufferSize = B_PATH_NAME_LENGTH;
67 
68 	// free the previous buffer, if the buffer size differs
69 	if (fBuffer && fBufferSize != bufferSize) {
70 		free(fBuffer);
71 		fBuffer = NULL;
72 		fBufferSize = 0;
73 	}
74 	fPathLength = 0;
75 	fLocked = false;
76 
77 	// allocate buffer
78 	if (!fBuffer)
79 		fBuffer = (char*)malloc(bufferSize);
80 	if (!fBuffer)
81 		return B_NO_MEMORY;
82 	if (fBuffer) {
83 		fBufferSize = bufferSize;
84 		fBuffer[0] = '\0';
85 	}
86 	return SetPath(path, normalize, traverseLeafLink);
87 }
88 
89 
90 void
91 KPath::Adopt(KPath& other)
92 {
93 	free(fBuffer);
94 
95 	fBuffer = other.fBuffer;
96 	fBufferSize = other.fBufferSize;
97 
98 	other.fBuffer = NULL;
99 }
100 
101 
102 status_t
103 KPath::InitCheck() const
104 {
105 	return fBuffer ? B_OK : B_NO_MEMORY;
106 }
107 
108 
109 status_t
110 KPath::SetPath(const char* path, bool normalize, bool traverseLeafLink)
111 {
112 	if (fBuffer == NULL)
113 		return B_NO_INIT;
114 
115 	if (path != NULL) {
116 		if (normalize) {
117 			// normalize path
118 			status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
119 				traverseLeafLink,
120 				team_get_kernel_team_id() == team_get_current_team_id());
121 			if (error != B_OK) {
122 				SetPath(NULL);
123 				return error;
124 			}
125 			fPathLength = strlen(fBuffer);
126 		} else {
127 			// don't normalize path
128 			size_t length = strlen(path);
129 			if (length >= fBufferSize)
130 				return B_BUFFER_OVERFLOW;
131 
132 			memcpy(fBuffer, path, length + 1);
133 			fPathLength = length;
134 			_ChopTrailingSlashes();
135 		}
136 	} else {
137 		fBuffer[0] = '\0';
138 		fPathLength = 0;
139 	}
140 	return B_OK;
141 }
142 
143 
144 const char*
145 KPath::Path() const
146 {
147 	return fBuffer;
148 }
149 
150 
151 char*
152 KPath::LockBuffer()
153 {
154 	if (fBuffer == NULL || fLocked)
155 		return NULL;
156 
157 	fLocked = true;
158 	return fBuffer;
159 }
160 
161 
162 void
163 KPath::UnlockBuffer()
164 {
165 	if (!fLocked) {
166 		TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n"));
167 		return;
168 	}
169 	fLocked = false;
170 	fPathLength = strnlen(fBuffer, fBufferSize);
171 	if (fPathLength == fBufferSize) {
172 		TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n"));
173 		fPathLength--;
174 		fBuffer[fPathLength] = '\0';
175 	}
176 	_ChopTrailingSlashes();
177 }
178 
179 
180 char*
181 KPath::DetachBuffer()
182 {
183 	char* buffer = fBuffer;
184 
185 	if (fBuffer != NULL) {
186 		fBuffer = NULL;
187 		fBufferSize = 0;
188 		fPathLength = 0;
189 		fLocked = false;
190 	}
191 
192 	return buffer;
193 }
194 
195 
196 const char*
197 KPath::Leaf() const
198 {
199 	if (!fBuffer)
200 		return NULL;
201 
202 	// only "/" has trailing slashes -- then we have to return the complete
203 	// buffer, as we have to do in case there are no slashes at all
204 	if (fPathLength != 1 || fBuffer[0] != '/') {
205 		for (int32 i = fPathLength - 1; i >= 0; i--) {
206 			if (fBuffer[i] == '/')
207 				return fBuffer + i + 1;
208 		}
209 	}
210 	return fBuffer;
211 }
212 
213 
214 status_t
215 KPath::ReplaceLeaf(const char* newLeaf)
216 {
217 	const char* leaf = Leaf();
218 	if (!leaf)
219 		return B_NO_INIT;
220 
221 	int32 leafIndex = leaf - fBuffer;
222 	// chop off the current leaf (don't replace "/", though)
223 	if (leafIndex != 0 || fBuffer[leafIndex - 1]) {
224 		fBuffer[leafIndex] = '\0';
225 		fPathLength = leafIndex;
226 		_ChopTrailingSlashes();
227 	}
228 
229 	// if a leaf was given, append it
230 	if (newLeaf)
231 		return Append(newLeaf);
232 	return B_OK;
233 }
234 
235 
236 bool
237 KPath::RemoveLeaf()
238 {
239 	// get the leaf -- bail out, if not initialized or only the "/" is left
240 	const char* leaf = Leaf();
241 	if (!leaf || leaf == fBuffer)
242 		return false;
243 
244 	// chop off the leaf
245 	int32 leafIndex = leaf - fBuffer;
246 	fBuffer[leafIndex] = '\0';
247 	fPathLength = leafIndex;
248 	_ChopTrailingSlashes();
249 
250 	return true;
251 }
252 
253 
254 status_t
255 KPath::Append(const char* component, bool isComponent)
256 {
257 	// check initialization and parameter
258 	if (fBuffer == NULL)
259 		return B_NO_INIT;
260 	if (component == NULL)
261 		return B_BAD_VALUE;
262 	if (fPathLength == 0)
263 		return SetPath(component);
264 
265 	// get component length
266 	size_t componentLength = strlen(component);
267 	if (componentLength < 1)
268 		return B_OK;
269 
270 	// if our current path is empty, we just copy the supplied one
271 	// compute the result path len
272 	bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/'
273 		&& component[0] != '/';
274 	size_t resultPathLength = fPathLength + componentLength
275 		+ (insertSlash ? 1 : 0);
276 	if (resultPathLength >= fBufferSize)
277 		return B_BUFFER_OVERFLOW;
278 
279 	// compose the result path
280 	if (insertSlash)
281 		fBuffer[fPathLength++] = '/';
282 	memcpy(fBuffer + fPathLength, component, componentLength + 1);
283 	fPathLength = resultPathLength;
284 	return B_OK;
285 }
286 
287 
288 status_t
289 KPath::Normalize(bool traverseLeafLink)
290 {
291 	if (fBuffer == NULL)
292 		return B_NO_INIT;
293 	if (fPathLength == 0)
294 		return B_BAD_VALUE;
295 
296 	status_t error = vfs_normalize_path(fBuffer, fBuffer, fBufferSize,
297 		traverseLeafLink,
298 		team_get_kernel_team_id() == team_get_current_team_id());
299 	if (error != B_OK) {
300 		// vfs_normalize_path() might have screwed up the previous path -- unset
301 		// it completely to avoid weird problems.
302 		fBuffer[0] = '\0';
303 		fPathLength = 0;
304 	}
305 
306 	fPathLength = strlen(fBuffer);
307 	return B_OK;
308 }
309 
310 
311 KPath&
312 KPath::operator=(const KPath& other)
313 {
314 	SetTo(other.fBuffer, false, other.fBufferSize);
315 	return *this;
316 }
317 
318 
319 KPath&
320 KPath::operator=(const char* path)
321 {
322 	SetTo(path);
323 	return *this;
324 }
325 
326 
327 bool
328 KPath::operator==(const KPath& other) const
329 {
330 	if (!fBuffer)
331 		return !other.fBuffer;
332 
333 	return (other.fBuffer
334 		&& fPathLength == other.fPathLength
335 		&& strcmp(fBuffer, other.fBuffer) == 0);
336 }
337 
338 
339 bool
340 KPath::operator==(const char* path) const
341 {
342 	if (!fBuffer)
343 		return (!path);
344 
345 	return path && !strcmp(fBuffer, path);
346 }
347 
348 
349 bool
350 KPath::operator!=(const KPath& other) const
351 {
352 	return !(*this == other);
353 }
354 
355 
356 bool
357 KPath::operator!=(const char* path) const
358 {
359 	return !(*this == path);
360 }
361 
362 
363 void
364 KPath::_ChopTrailingSlashes()
365 {
366 	if (fBuffer) {
367 		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
368 			fBuffer[--fPathLength] = '\0';
369 	}
370 }
371 
372