xref: /haiku/src/system/kernel/fs/KPath.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
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 status_t
206 KPath::Append(const char *component, bool isComponent)
207 {
208 	// check initialization and parameter
209 	if (!fBuffer)
210 		return B_NO_INIT;
211 	if (!component)
212 		return B_BAD_VALUE;
213 	if (fPathLength == 0)
214 		return SetPath(component);
215 
216 	// get component length
217 	size_t componentLength = strlen(component);
218 	if (componentLength < 1)
219 		return 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 	size_t resultPathLength = fPathLength + componentLength + (insertSlash ? 1 : 0);
226 	if (resultPathLength >= fBufferSize)
227 		return B_BUFFER_OVERFLOW;
228 
229 	// compose the result path
230 	if (insertSlash)
231 		fBuffer[fPathLength++] = '/';
232 	memcpy(fBuffer + fPathLength, component, componentLength + 1);
233 	fPathLength = resultPathLength;
234 	return B_OK;
235 }
236 
237 
238 KPath&
239 KPath::operator=(const KPath& other)
240 {
241 	SetTo(other.fBuffer, other.fBufferSize);
242 	return *this;
243 }
244 
245 
246 KPath&
247 KPath::operator=(const char* path)
248 {
249 	SetTo(path);
250 	return *this;
251 }
252 
253 
254 bool
255 KPath::operator==(const KPath& other) const
256 {
257 	if (!fBuffer)
258 		return !other.fBuffer;
259 
260 	return (other.fBuffer
261 		&& fPathLength == other.fPathLength
262 		&& strcmp(fBuffer, other.fBuffer) == 0);
263 }
264 
265 
266 bool
267 KPath::operator==(const char* path) const
268 {
269 	if (!fBuffer)
270 		return (!path);
271 
272 	return path && !strcmp(fBuffer, path);
273 }
274 
275 
276 bool
277 KPath::operator!=(const KPath& other) const
278 {
279 	return !(*this == other);
280 }
281 
282 
283 bool
284 KPath::operator!=(const char* path) const
285 {
286 	return !(*this == path);
287 }
288 
289 
290 void
291 KPath::_ChopTrailingSlashes()
292 {
293 	if (fBuffer) {
294 		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
295 			fBuffer[--fPathLength] = '\0';
296 	}
297 }
298 
299