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