xref: /haiku/src/tools/fs_shell/KPath.cpp (revision a38a92c955f46ea3dbd3929ad1398c2b418e80cb)
1*a38a92c9SIngo Weinhold /*
2*a38a92c9SIngo Weinhold  * Copyright 2004-2007, Ingo Weinhold, bonefish@users.sf.net.
3*a38a92c9SIngo Weinhold  * Distributed under the terms of the MIT License.
4*a38a92c9SIngo Weinhold  */
5*a38a92c9SIngo Weinhold 
6*a38a92c9SIngo Weinhold /** A simple class wrapping a path. Has a fixed-sized buffer. */
7*a38a92c9SIngo Weinhold 
8*a38a92c9SIngo Weinhold #include "KPath.h"
9*a38a92c9SIngo Weinhold 
10*a38a92c9SIngo Weinhold #include <stdlib.h>
11*a38a92c9SIngo Weinhold 
12*a38a92c9SIngo Weinhold #include "fssh_string.h"
13*a38a92c9SIngo Weinhold #include "vfs.h"
14*a38a92c9SIngo Weinhold 
15*a38a92c9SIngo Weinhold 
16*a38a92c9SIngo Weinhold // debugging
17*a38a92c9SIngo Weinhold #define TRACE(x) ;
18*a38a92c9SIngo Weinhold //#define TRACE(x) dprintf x
19*a38a92c9SIngo Weinhold 
20*a38a92c9SIngo Weinhold 
KPath(fssh_size_t bufferSize)21*a38a92c9SIngo Weinhold KPath::KPath(fssh_size_t bufferSize)
22*a38a92c9SIngo Weinhold 	:
23*a38a92c9SIngo Weinhold 	fBuffer(NULL),
24*a38a92c9SIngo Weinhold 	fBufferSize(0),
25*a38a92c9SIngo Weinhold 	fPathLength(0),
26*a38a92c9SIngo Weinhold 	fLocked(false)
27*a38a92c9SIngo Weinhold {
28*a38a92c9SIngo Weinhold 	SetTo(NULL, bufferSize);
29*a38a92c9SIngo Weinhold }
30*a38a92c9SIngo Weinhold 
31*a38a92c9SIngo Weinhold 
KPath(const char * path,bool normalize,fssh_size_t bufferSize)32*a38a92c9SIngo Weinhold KPath::KPath(const char* path, bool normalize, fssh_size_t bufferSize)
33*a38a92c9SIngo Weinhold 	:
34*a38a92c9SIngo Weinhold 	fBuffer(NULL),
35*a38a92c9SIngo Weinhold 	fBufferSize(0),
36*a38a92c9SIngo Weinhold 	fPathLength(0),
37*a38a92c9SIngo Weinhold 	fLocked(false)
38*a38a92c9SIngo Weinhold {
39*a38a92c9SIngo Weinhold 	SetTo(path, normalize, bufferSize);
40*a38a92c9SIngo Weinhold }
41*a38a92c9SIngo Weinhold 
42*a38a92c9SIngo Weinhold 
KPath(const KPath & other)43*a38a92c9SIngo Weinhold KPath::KPath(const KPath& other)
44*a38a92c9SIngo Weinhold 	:
45*a38a92c9SIngo Weinhold 	fBuffer(NULL),
46*a38a92c9SIngo Weinhold 	fBufferSize(0),
47*a38a92c9SIngo Weinhold 	fPathLength(0),
48*a38a92c9SIngo Weinhold 	fLocked(false)
49*a38a92c9SIngo Weinhold {
50*a38a92c9SIngo Weinhold 	*this = other;
51*a38a92c9SIngo Weinhold }
52*a38a92c9SIngo Weinhold 
53*a38a92c9SIngo Weinhold 
~KPath()54*a38a92c9SIngo Weinhold KPath::~KPath()
55*a38a92c9SIngo Weinhold {
56*a38a92c9SIngo Weinhold 	free(fBuffer);
57*a38a92c9SIngo Weinhold }
58*a38a92c9SIngo Weinhold 
59*a38a92c9SIngo Weinhold 
60*a38a92c9SIngo Weinhold fssh_status_t
SetTo(const char * path,bool normalize,fssh_size_t bufferSize)61*a38a92c9SIngo Weinhold KPath::SetTo(const char* path, bool normalize, fssh_size_t bufferSize)
62*a38a92c9SIngo Weinhold {
63*a38a92c9SIngo Weinhold 	if (bufferSize == 0)
64*a38a92c9SIngo Weinhold 		bufferSize = FSSH_B_PATH_NAME_LENGTH;
65*a38a92c9SIngo Weinhold 
66*a38a92c9SIngo Weinhold 	// free the previous buffer, if the buffer size differs
67*a38a92c9SIngo Weinhold 	if (fBuffer && fBufferSize != bufferSize) {
68*a38a92c9SIngo Weinhold 		free(fBuffer);
69*a38a92c9SIngo Weinhold 		fBuffer = NULL;
70*a38a92c9SIngo Weinhold 		fBufferSize = 0;
71*a38a92c9SIngo Weinhold 	}
72*a38a92c9SIngo Weinhold 	fPathLength = 0;
73*a38a92c9SIngo Weinhold 	fLocked = false;
74*a38a92c9SIngo Weinhold 
75*a38a92c9SIngo Weinhold 	// allocate buffer
76*a38a92c9SIngo Weinhold 	if (!fBuffer)
77*a38a92c9SIngo Weinhold 		fBuffer = (char*)malloc(bufferSize);
78*a38a92c9SIngo Weinhold 	if (!fBuffer)
79*a38a92c9SIngo Weinhold 		return FSSH_B_NO_MEMORY;
80*a38a92c9SIngo Weinhold 	if (fBuffer) {
81*a38a92c9SIngo Weinhold 		fBufferSize = bufferSize;
82*a38a92c9SIngo Weinhold 		fBuffer[0] = '\0';
83*a38a92c9SIngo Weinhold 	}
84*a38a92c9SIngo Weinhold 	return SetPath(path, normalize);
85*a38a92c9SIngo Weinhold }
86*a38a92c9SIngo Weinhold 
87*a38a92c9SIngo Weinhold 
88*a38a92c9SIngo Weinhold fssh_status_t
InitCheck() const89*a38a92c9SIngo Weinhold KPath::InitCheck() const
90*a38a92c9SIngo Weinhold {
91*a38a92c9SIngo Weinhold 	return fBuffer ? FSSH_B_OK : FSSH_B_NO_MEMORY;
92*a38a92c9SIngo Weinhold }
93*a38a92c9SIngo Weinhold 
94*a38a92c9SIngo Weinhold 
95*a38a92c9SIngo Weinhold fssh_status_t
SetPath(const char * path,bool normalize)96*a38a92c9SIngo Weinhold KPath::SetPath(const char *path, bool normalize)
97*a38a92c9SIngo Weinhold {
98*a38a92c9SIngo Weinhold 	if (!fBuffer)
99*a38a92c9SIngo Weinhold 		return FSSH_B_NO_INIT;
100*a38a92c9SIngo Weinhold 
101*a38a92c9SIngo Weinhold 	if (path) {
102*a38a92c9SIngo Weinhold 		if (normalize) {
103*a38a92c9SIngo Weinhold 			// normalize path
104*a38a92c9SIngo Weinhold 			fssh_status_t error = vfs_normalize_path(path, fBuffer, fBufferSize,
105*a38a92c9SIngo Weinhold 				true);
106*a38a92c9SIngo Weinhold 			if (error != FSSH_B_OK) {
107*a38a92c9SIngo Weinhold 				SetPath(NULL);
108*a38a92c9SIngo Weinhold 				return error;
109*a38a92c9SIngo Weinhold 			}
110*a38a92c9SIngo Weinhold 			fPathLength = fssh_strlen(fBuffer);
111*a38a92c9SIngo Weinhold 		} else {
112*a38a92c9SIngo Weinhold 			// don't normalize path
113*a38a92c9SIngo Weinhold 			fssh_size_t length = fssh_strlen(path);
114*a38a92c9SIngo Weinhold 			if (length >= fBufferSize)
115*a38a92c9SIngo Weinhold 				return FSSH_B_BUFFER_OVERFLOW;
116*a38a92c9SIngo Weinhold 
117*a38a92c9SIngo Weinhold 			fssh_memcpy(fBuffer, path, length + 1);
118*a38a92c9SIngo Weinhold 			fPathLength = length;
119*a38a92c9SIngo Weinhold 			_ChopTrailingSlashes();
120*a38a92c9SIngo Weinhold 		}
121*a38a92c9SIngo Weinhold 	} else {
122*a38a92c9SIngo Weinhold 		fBuffer[0] = '\0';
123*a38a92c9SIngo Weinhold 		fPathLength = 0;
124*a38a92c9SIngo Weinhold 	}
125*a38a92c9SIngo Weinhold 	return FSSH_B_OK;
126*a38a92c9SIngo Weinhold }
127*a38a92c9SIngo Weinhold 
128*a38a92c9SIngo Weinhold 
129*a38a92c9SIngo Weinhold const char*
Path() const130*a38a92c9SIngo Weinhold KPath::Path() const
131*a38a92c9SIngo Weinhold {
132*a38a92c9SIngo Weinhold 	return fBuffer;
133*a38a92c9SIngo Weinhold }
134*a38a92c9SIngo Weinhold 
135*a38a92c9SIngo Weinhold 
136*a38a92c9SIngo Weinhold char *
LockBuffer()137*a38a92c9SIngo Weinhold KPath::LockBuffer()
138*a38a92c9SIngo Weinhold {
139*a38a92c9SIngo Weinhold 	if (!fBuffer || fLocked)
140*a38a92c9SIngo Weinhold 		return NULL;
141*a38a92c9SIngo Weinhold 
142*a38a92c9SIngo Weinhold 	fLocked = true;
143*a38a92c9SIngo Weinhold 	return fBuffer;
144*a38a92c9SIngo Weinhold }
145*a38a92c9SIngo Weinhold 
146*a38a92c9SIngo Weinhold 
147*a38a92c9SIngo Weinhold void
UnlockBuffer()148*a38a92c9SIngo Weinhold KPath::UnlockBuffer()
149*a38a92c9SIngo Weinhold {
150*a38a92c9SIngo Weinhold 	if (!fLocked) {
151*a38a92c9SIngo Weinhold 		TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n"));
152*a38a92c9SIngo Weinhold 		return;
153*a38a92c9SIngo Weinhold 	}
154*a38a92c9SIngo Weinhold 	fLocked = false;
155*a38a92c9SIngo Weinhold 	fPathLength = fssh_strnlen(fBuffer, fBufferSize);
156*a38a92c9SIngo Weinhold 	if (fPathLength == fBufferSize) {
157*a38a92c9SIngo Weinhold 		TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n"));
158*a38a92c9SIngo Weinhold 		fPathLength--;
159*a38a92c9SIngo Weinhold 		fBuffer[fPathLength] = '\0';
160*a38a92c9SIngo Weinhold 	}
161*a38a92c9SIngo Weinhold 	_ChopTrailingSlashes();
162*a38a92c9SIngo Weinhold }
163*a38a92c9SIngo Weinhold 
164*a38a92c9SIngo Weinhold 
165*a38a92c9SIngo Weinhold const char *
Leaf() const166*a38a92c9SIngo Weinhold KPath::Leaf() const
167*a38a92c9SIngo Weinhold {
168*a38a92c9SIngo Weinhold 	if (!fBuffer)
169*a38a92c9SIngo Weinhold 		return NULL;
170*a38a92c9SIngo Weinhold 
171*a38a92c9SIngo Weinhold 	// only "/" has trailing slashes -- then we have to return the complete
172*a38a92c9SIngo Weinhold 	// buffer, as we have to do in case there are no slashes at all
173*a38a92c9SIngo Weinhold 	if (fPathLength != 1 || fBuffer[0] != '/') {
174*a38a92c9SIngo Weinhold 		for (int32_t i = fPathLength - 1; i >= 0; i--) {
175*a38a92c9SIngo Weinhold 			if (fBuffer[i] == '/')
176*a38a92c9SIngo Weinhold 				return fBuffer + i + 1;
177*a38a92c9SIngo Weinhold 		}
178*a38a92c9SIngo Weinhold 	}
179*a38a92c9SIngo Weinhold 	return fBuffer;
180*a38a92c9SIngo Weinhold }
181*a38a92c9SIngo Weinhold 
182*a38a92c9SIngo Weinhold 
183*a38a92c9SIngo Weinhold fssh_status_t
ReplaceLeaf(const char * newLeaf)184*a38a92c9SIngo Weinhold KPath::ReplaceLeaf(const char *newLeaf)
185*a38a92c9SIngo Weinhold {
186*a38a92c9SIngo Weinhold 	const char *leaf = Leaf();
187*a38a92c9SIngo Weinhold 	if (!leaf)
188*a38a92c9SIngo Weinhold 		return FSSH_B_NO_INIT;
189*a38a92c9SIngo Weinhold 
190*a38a92c9SIngo Weinhold 	int32_t leafIndex = leaf - fBuffer;
191*a38a92c9SIngo Weinhold 	// chop off the current leaf (don't replace "/", though)
192*a38a92c9SIngo Weinhold 	if (leafIndex != 0 || fBuffer[leafIndex - 1]) {
193*a38a92c9SIngo Weinhold 		fBuffer[leafIndex] = '\0';
194*a38a92c9SIngo Weinhold 		fPathLength = leafIndex;
195*a38a92c9SIngo Weinhold 		_ChopTrailingSlashes();
196*a38a92c9SIngo Weinhold 	}
197*a38a92c9SIngo Weinhold 
198*a38a92c9SIngo Weinhold 	// if a leaf was given, append it
199*a38a92c9SIngo Weinhold 	if (newLeaf)
200*a38a92c9SIngo Weinhold 		return Append(newLeaf);
201*a38a92c9SIngo Weinhold 	return FSSH_B_OK;
202*a38a92c9SIngo Weinhold }
203*a38a92c9SIngo Weinhold 
204*a38a92c9SIngo Weinhold 
205*a38a92c9SIngo Weinhold fssh_status_t
Append(const char * component,bool isComponent)206*a38a92c9SIngo Weinhold KPath::Append(const char *component, bool isComponent)
207*a38a92c9SIngo Weinhold {
208*a38a92c9SIngo Weinhold 	// check initialization and parameter
209*a38a92c9SIngo Weinhold 	if (!fBuffer)
210*a38a92c9SIngo Weinhold 		return FSSH_B_NO_INIT;
211*a38a92c9SIngo Weinhold 	if (!component)
212*a38a92c9SIngo Weinhold 		return FSSH_B_BAD_VALUE;
213*a38a92c9SIngo Weinhold 	if (fPathLength == 0)
214*a38a92c9SIngo Weinhold 		return SetPath(component);
215*a38a92c9SIngo Weinhold 
216*a38a92c9SIngo Weinhold 	// get component length
217*a38a92c9SIngo Weinhold 	fssh_size_t componentLength = fssh_strlen(component);
218*a38a92c9SIngo Weinhold 	if (componentLength < 1)
219*a38a92c9SIngo Weinhold 		return FSSH_B_OK;
220*a38a92c9SIngo Weinhold 
221*a38a92c9SIngo Weinhold 	// if our current path is empty, we just copy the supplied one
222*a38a92c9SIngo Weinhold 	// compute the result path len
223*a38a92c9SIngo Weinhold 	bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/'
224*a38a92c9SIngo Weinhold 		&& component[0] != '/';
225*a38a92c9SIngo Weinhold 	fssh_size_t resultPathLength = fPathLength + componentLength + (insertSlash ? 1 : 0);
226*a38a92c9SIngo Weinhold 	if (resultPathLength >= fBufferSize)
227*a38a92c9SIngo Weinhold 		return FSSH_B_BUFFER_OVERFLOW;
228*a38a92c9SIngo Weinhold 
229*a38a92c9SIngo Weinhold 	// compose the result path
230*a38a92c9SIngo Weinhold 	if (insertSlash)
231*a38a92c9SIngo Weinhold 		fBuffer[fPathLength++] = '/';
232*a38a92c9SIngo Weinhold 	fssh_memcpy(fBuffer + fPathLength, component, componentLength + 1);
233*a38a92c9SIngo Weinhold 	fPathLength = resultPathLength;
234*a38a92c9SIngo Weinhold 	return FSSH_B_OK;
235*a38a92c9SIngo Weinhold }
236*a38a92c9SIngo Weinhold 
237*a38a92c9SIngo Weinhold 
238*a38a92c9SIngo Weinhold KPath&
operator =(const KPath & other)239*a38a92c9SIngo Weinhold KPath::operator=(const KPath& other)
240*a38a92c9SIngo Weinhold {
241*a38a92c9SIngo Weinhold 	SetTo(other.fBuffer, other.fBufferSize);
242*a38a92c9SIngo Weinhold 	return *this;
243*a38a92c9SIngo Weinhold }
244*a38a92c9SIngo Weinhold 
245*a38a92c9SIngo Weinhold 
246*a38a92c9SIngo Weinhold KPath&
operator =(const char * path)247*a38a92c9SIngo Weinhold KPath::operator=(const char* path)
248*a38a92c9SIngo Weinhold {
249*a38a92c9SIngo Weinhold 	SetTo(path);
250*a38a92c9SIngo Weinhold 	return *this;
251*a38a92c9SIngo Weinhold }
252*a38a92c9SIngo Weinhold 
253*a38a92c9SIngo Weinhold 
254*a38a92c9SIngo Weinhold bool
operator ==(const KPath & other) const255*a38a92c9SIngo Weinhold KPath::operator==(const KPath& other) const
256*a38a92c9SIngo Weinhold {
257*a38a92c9SIngo Weinhold 	if (!fBuffer)
258*a38a92c9SIngo Weinhold 		return !other.fBuffer;
259*a38a92c9SIngo Weinhold 
260*a38a92c9SIngo Weinhold 	return (other.fBuffer
261*a38a92c9SIngo Weinhold 		&& fPathLength == other.fPathLength
262*a38a92c9SIngo Weinhold 		&& fssh_strcmp(fBuffer, other.fBuffer) == 0);
263*a38a92c9SIngo Weinhold }
264*a38a92c9SIngo Weinhold 
265*a38a92c9SIngo Weinhold 
266*a38a92c9SIngo Weinhold bool
operator ==(const char * path) const267*a38a92c9SIngo Weinhold KPath::operator==(const char* path) const
268*a38a92c9SIngo Weinhold {
269*a38a92c9SIngo Weinhold 	if (!fBuffer)
270*a38a92c9SIngo Weinhold 		return (!path);
271*a38a92c9SIngo Weinhold 
272*a38a92c9SIngo Weinhold 	return path && !fssh_strcmp(fBuffer, path);
273*a38a92c9SIngo Weinhold }
274*a38a92c9SIngo Weinhold 
275*a38a92c9SIngo Weinhold 
276*a38a92c9SIngo Weinhold bool
operator !=(const KPath & other) const277*a38a92c9SIngo Weinhold KPath::operator!=(const KPath& other) const
278*a38a92c9SIngo Weinhold {
279*a38a92c9SIngo Weinhold 	return !(*this == other);
280*a38a92c9SIngo Weinhold }
281*a38a92c9SIngo Weinhold 
282*a38a92c9SIngo Weinhold 
283*a38a92c9SIngo Weinhold bool
operator !=(const char * path) const284*a38a92c9SIngo Weinhold KPath::operator!=(const char* path) const
285*a38a92c9SIngo Weinhold {
286*a38a92c9SIngo Weinhold 	return !(*this == path);
287*a38a92c9SIngo Weinhold }
288*a38a92c9SIngo Weinhold 
289*a38a92c9SIngo Weinhold 
290*a38a92c9SIngo Weinhold void
_ChopTrailingSlashes()291*a38a92c9SIngo Weinhold KPath::_ChopTrailingSlashes()
292*a38a92c9SIngo Weinhold {
293*a38a92c9SIngo Weinhold 	if (fBuffer) {
294*a38a92c9SIngo Weinhold 		while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/')
295*a38a92c9SIngo Weinhold 			fBuffer[--fPathLength] = '\0';
296*a38a92c9SIngo Weinhold 	}
297*a38a92c9SIngo Weinhold }
298*a38a92c9SIngo Weinhold 
299