xref: /haiku/src/system/kernel/fs/KPath.cpp (revision 90ca02568835b140b0e59de496a7f1f1d3513f67)
1 /*
2  * Copyright 2004-2006, 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 <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 {
64 	if (bufferSize == 0)
65 		bufferSize = B_PATH_NAME_LENGTH;
66 
67 	// free the previous buffer, if the buffer size differs
68 	if (fBuffer && fBufferSize != bufferSize) {
69 		free(fBuffer);
70 		fBuffer = NULL;
71 		fBufferSize = 0;
72 	}
73 	fPathLength = 0;
74 	fLocked = false;
75 
76 	// allocate buffer
77 	if (!fBuffer)
78 		fBuffer = (char*)malloc(bufferSize);
79 	if (!fBuffer)
80 		return B_NO_MEMORY;
81 	if (fBuffer) {
82 		fBufferSize = bufferSize;
83 		fBuffer[0] = '\0';
84 	}
85 	return SetPath(path, normalize);
86 }
87 
88 
89 void
90 KPath::Adopt(KPath& other)
91 {
92 	free(fBuffer);
93 
94 	fBuffer = other.fBuffer;
95 	fBufferSize = other.fBufferSize;
96 
97 	other.fBuffer = NULL;
98 }
99 
100 
101 status_t
102 KPath::InitCheck() const
103 {
104 	return fBuffer ? B_OK : B_NO_MEMORY;
105 }
106 
107 
108 status_t
109 KPath::SetPath(const char *path, bool normalize)
110 {
111 	if (!fBuffer)
112 		return B_NO_INIT;
113 
114 	if (path) {
115 		if (normalize) {
116 			// normalize path
117 			status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
118 				team_get_kernel_team_id() == team_get_current_team_id());
119 			if (error != B_OK) {
120 				SetPath(NULL);
121 				return error;
122 			}
123 			fPathLength = strlen(fBuffer);
124 		} else {
125 			// don't normalize path
126 			size_t length = strlen(path);
127 			if (length >= fBufferSize)
128 				return B_BUFFER_OVERFLOW;
129 
130 			memcpy(fBuffer, path, length + 1);
131 			fPathLength = length;
132 			_ChopTrailingSlashes();
133 		}
134 	} else {
135 		fBuffer[0] = '\0';
136 		fPathLength = 0;
137 	}
138 	return B_OK;
139 }
140 
141 
142 const char*
143 KPath::Path() const
144 {
145 	return fBuffer;
146 }
147 
148 
149 char *
150 KPath::LockBuffer()
151 {
152 	if (!fBuffer || fLocked)
153 		return NULL;
154 
155 	fLocked = true;
156 	return fBuffer;
157 }
158 
159 
160 void
161 KPath::UnlockBuffer()
162 {
163 	if (!fLocked) {
164 		TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n"));
165 		return;
166 	}
167 	fLocked = false;
168 	fPathLength = strnlen(fBuffer, fBufferSize);
169 	if (fPathLength == fBufferSize) {
170 		TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n"));
171 		fPathLength--;
172 		fBuffer[fPathLength] = '\0';
173 	}
174 	_ChopTrailingSlashes();
175 }
176 
177 
178 const char *
179 KPath::Leaf() const
180 {
181 	if (!fBuffer)
182 		return NULL;
183 
184 	// only "/" has trailing slashes -- then we have to return the complete
185 	// buffer, as we have to do in case there are no slashes at all
186 	if (fPathLength != 1 || fBuffer[0] != '/') {
187 		for (int32 i = fPathLength - 1; i >= 0; i--) {
188 			if (fBuffer[i] == '/')
189 				return fBuffer + i + 1;
190 		}
191 	}
192 	return fBuffer;
193 }
194 
195 
196 status_t
197 KPath::ReplaceLeaf(const char *newLeaf)
198 {
199 	const char *leaf = Leaf();
200 	if (!leaf)
201 		return B_NO_INIT;
202 
203 	int32 leafIndex = leaf - fBuffer;
204 	// chop off the current leaf (don't replace "/", though)
205 	if (leafIndex != 0 || fBuffer[leafIndex - 1]) {
206 		fBuffer[leafIndex] = '\0';
207 		fPathLength = leafIndex;
208 		_ChopTrailingSlashes();
209 	}
210 
211 	// if a leaf was given, append it
212 	if (newLeaf)
213 		return Append(newLeaf);
214 	return B_OK;
215 }
216 
217 
218 bool
219 KPath::RemoveLeaf()
220 {
221 	// get the leaf -- bail out, if not initialized or only the "/" is left
222 	const char *leaf = Leaf();
223 	if (!leaf || leaf == fBuffer)
224 		return false;
225 
226 	// chop off the leaf
227 	int32 leafIndex = leaf - fBuffer;
228 	fBuffer[leafIndex] = '\0';
229 	fPathLength = leafIndex;
230 	_ChopTrailingSlashes();
231 
232 	return true;
233 }
234 
235 
236 status_t
237 KPath::Append(const char *component, bool isComponent)
238 {
239 	// check initialization and parameter
240 	if (!fBuffer)
241 		return B_NO_INIT;
242 	if (!component)
243 		return B_BAD_VALUE;
244 	if (fPathLength == 0)
245 		return SetPath(component);
246 
247 	// get component length
248 	size_t componentLength = strlen(component);
249 	if (componentLength < 1)
250 		return B_OK;
251 
252 	// if our current path is empty, we just copy the supplied one
253 	// compute the result path len
254 	bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/'
255 		&& component[0] != '/';
256 	size_t resultPathLength = fPathLength + componentLength
257 		+ (insertSlash ? 1 : 0);
258 	if (resultPathLength >= fBufferSize)
259 		return B_BUFFER_OVERFLOW;
260 
261 	// compose the result path
262 	if (insertSlash)
263 		fBuffer[fPathLength++] = '/';
264 	memcpy(fBuffer + fPathLength, component, componentLength + 1);
265 	fPathLength = resultPathLength;
266 	return B_OK;
267 }
268 
269 
270 KPath&
271 KPath::operator=(const KPath& other)
272 {
273 	SetTo(other.fBuffer, false, other.fBufferSize);
274 	return *this;
275 }
276 
277 
278 KPath&
279 KPath::operator=(const char* path)
280 {
281 	SetTo(path);
282 	return *this;
283 }
284 
285 
286 bool
287 KPath::operator==(const KPath& other) const
288 {
289 	if (!fBuffer)
290 		return !other.fBuffer;
291 
292 	return (other.fBuffer
293 		&& fPathLength == other.fPathLength
294 		&& strcmp(fBuffer, other.fBuffer) == 0);
295 }
296 
297 
298 bool
299 KPath::operator==(const char* path) const
300 {
301 	if (!fBuffer)
302 		return (!path);
303 
304 	return path && !strcmp(fBuffer, path);
305 }
306 
307 
308 bool
309 KPath::operator!=(const KPath& other) const
310 {
311 	return !(*this == other);
312 }
313 
314 
315 bool
316 KPath::operator!=(const char* path) const
317 {
318 	return !(*this == path);
319 }
320 
321 
322 void
323 KPath::_ChopTrailingSlashes()
324 {
325 	if (fBuffer) {
326 		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
327 			fBuffer[--fPathLength] = '\0';
328 	}
329 }
330 
331