xref: /haiku/src/system/kernel/fs/KPath.cpp (revision b028e77473189065f2baefc6f5e10d451cf591e2)
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 <KPath.h>
9 #include <team.h>
10 #include <vfs.h>
11 
12 #include <stdlib.h>
13 #include <string.h>
14 
15 
16 // debugging
17 #define TRACE(x) ;
18 //#define TRACE(x) dprintf x
19 
20 
21 KPath::KPath(size_t bufferSize)
22 	:
23 	fBuffer(NULL),
24 	fBufferSize(0),
25 	fPathLength(0),
26 	fLocked(false)
27 {
28 	SetTo(NULL, bufferSize);
29 }
30 
31 
32 KPath::KPath(const char* path, bool normalize, 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 
43 KPath::KPath(const KPath& other)
44 	:
45 	fBuffer(NULL),
46 	fBufferSize(0),
47 	fPathLength(0),
48 	fLocked(false)
49 {
50 	*this = other;
51 }
52 
53 
54 KPath::~KPath()
55 {
56 	free(fBuffer);
57 }
58 
59 
60 status_t
61 KPath::SetTo(const char* path, bool normalize, size_t bufferSize)
62 {
63 	if (bufferSize == 0)
64 		bufferSize = 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 B_NO_MEMORY;
80 	if (fBuffer) {
81 		fBufferSize = bufferSize;
82 		fBuffer[0] = '\0';
83 	}
84 	return SetPath(path, normalize);
85 }
86 
87 
88 status_t
89 KPath::InitCheck() const
90 {
91 	return fBuffer ? B_OK : B_NO_MEMORY;
92 }
93 
94 
95 status_t
96 KPath::SetPath(const char *path, bool normalize)
97 {
98 	if (!fBuffer)
99 		return B_NO_INIT;
100 
101 	if (path) {
102 		if (normalize) {
103 			// normalize path
104 			status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
105 				team_get_kernel_team_id() == team_get_current_team_id());
106 			if (error != B_OK) {
107 				SetPath(NULL);
108 				return error;
109 			}
110 			fPathLength = strlen(fBuffer);
111 		} else {
112 			// don't normalize path
113 			size_t length = strlen(path);
114 			if (length >= fBufferSize)
115 				return B_BUFFER_OVERFLOW;
116 
117 			memcpy(fBuffer, path, length + 1);
118 			fPathLength = length;
119 			_ChopTrailingSlashes();
120 		}
121 	} else {
122 		fBuffer[0] = '\0';
123 		fPathLength = 0;
124 	}
125 	return B_OK;
126 }
127 
128 
129 const char*
130 KPath::Path() const
131 {
132 	return fBuffer;
133 }
134 
135 
136 char *
137 KPath::LockBuffer()
138 {
139 	if (!fBuffer || fLocked)
140 		return NULL;
141 
142 	fLocked = true;
143 	return fBuffer;
144 }
145 
146 
147 void
148 KPath::UnlockBuffer()
149 {
150 	if (!fLocked) {
151 		TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n"));
152 		return;
153 	}
154 	fLocked = false;
155 	fPathLength = 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 
165 const char *
166 KPath::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 i = fPathLength - 1; i >= 0; i--) {
175 			if (fBuffer[i] == '/')
176 				return fBuffer + i + 1;
177 		}
178 	}
179 	return fBuffer;
180 }
181 
182 
183 status_t
184 KPath::ReplaceLeaf(const char *newLeaf)
185 {
186 	const char *leaf = Leaf();
187 	if (!leaf)
188 		return B_NO_INIT;
189 
190 	int32 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 B_OK;
202 }
203 
204 
205 bool
206 KPath::RemoveLeaf()
207 {
208 	// get the leaf -- bail out, if not initialized or only the "/" is left
209 	const char *leaf = Leaf();
210 	if (!leaf || leaf == fBuffer)
211 		return false;
212 
213 	// chop off the leaf
214 	int32 leafIndex = leaf - fBuffer;
215 	fBuffer[leafIndex] = '\0';
216 	fPathLength = leafIndex;
217 	_ChopTrailingSlashes();
218 
219 	return true;
220 }
221 
222 
223 status_t
224 KPath::Append(const char *component, bool isComponent)
225 {
226 	// check initialization and parameter
227 	if (!fBuffer)
228 		return B_NO_INIT;
229 	if (!component)
230 		return B_BAD_VALUE;
231 	if (fPathLength == 0)
232 		return SetPath(component);
233 
234 	// get component length
235 	size_t componentLength = strlen(component);
236 	if (componentLength < 1)
237 		return B_OK;
238 
239 	// if our current path is empty, we just copy the supplied one
240 	// compute the result path len
241 	bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/'
242 		&& component[0] != '/';
243 	size_t resultPathLength = fPathLength + componentLength + (insertSlash ? 1 : 0);
244 	if (resultPathLength >= fBufferSize)
245 		return B_BUFFER_OVERFLOW;
246 
247 	// compose the result path
248 	if (insertSlash)
249 		fBuffer[fPathLength++] = '/';
250 	memcpy(fBuffer + fPathLength, component, componentLength + 1);
251 	fPathLength = resultPathLength;
252 	return B_OK;
253 }
254 
255 
256 KPath&
257 KPath::operator=(const KPath& other)
258 {
259 	SetTo(other.fBuffer, other.fBufferSize);
260 	return *this;
261 }
262 
263 
264 KPath&
265 KPath::operator=(const char* path)
266 {
267 	SetTo(path);
268 	return *this;
269 }
270 
271 
272 bool
273 KPath::operator==(const KPath& other) const
274 {
275 	if (!fBuffer)
276 		return !other.fBuffer;
277 
278 	return (other.fBuffer
279 		&& fPathLength == other.fPathLength
280 		&& strcmp(fBuffer, other.fBuffer) == 0);
281 }
282 
283 
284 bool
285 KPath::operator==(const char* path) const
286 {
287 	if (!fBuffer)
288 		return (!path);
289 
290 	return path && !strcmp(fBuffer, path);
291 }
292 
293 
294 bool
295 KPath::operator!=(const KPath& other) const
296 {
297 	return !(*this == other);
298 }
299 
300 
301 bool
302 KPath::operator!=(const char* path) const
303 {
304 	return !(*this == path);
305 }
306 
307 
308 void
309 KPath::_ChopTrailingSlashes()
310 {
311 	if (fBuffer) {
312 		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
313 			fBuffer[--fPathLength] = '\0';
314 	}
315 }
316 
317