xref: /haiku/src/kits/storage/storage_support.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
1 //----------------------------------------------------------------------
2 //  This software is part of the OpenBeOS distribution and is covered
3 //  by the OpenBeOS license.
4 //----------------------------------------------------------------------
5 /*!
6 	\file storage_support.cpp
7 	Implementations of miscellaneous internal Storage Kit support functions.
8 */
9 
10 #include <new>
11 #include <ctype.h>
12 #include <string.h>
13 
14 #include <StorageDefs.h>
15 #include <SupportDefs.h>
16 #include "storage_support.h"
17 
18 using namespace std;
19 
20 namespace BPrivate {
21 namespace Storage {
22 
23 /*!	\param path the path
24 	\return \c true, if \a path is not \c NULL and absolute, \c false otherwise
25 */
26 bool
27 is_absolute_path(const char *path)
28 {
29 	return (path && path[0] == '/');
30 }
31 
32 // parse_path
33 static
34 void
35 parse_path(const char *fullPath, int &leafStart, int &leafEnd, int &pathEnd)
36 {
37 	if (fullPath == NULL)
38 		return;
39 
40 	enum PathParserState { PPS_START, PPS_LEAF } state = PPS_START;
41 
42 	int len = strlen(fullPath);
43 
44 	leafStart = -1;
45 	leafEnd = -1;
46 	pathEnd = -2;
47 
48 	bool loop = true;
49 	for (int pos = len-1; ; pos--) {
50 		if (pos < 0)
51 			break;
52 
53 		switch (state) {
54 			case PPS_START:
55 				// Skip all trailing '/' chars, then move on to
56 				// reading the leaf name
57 				if (fullPath[pos] != '/') {
58 					leafEnd = pos;
59 					state = PPS_LEAF;
60 				}
61 				break;
62 
63 			case PPS_LEAF:
64 				// Read leaf name chars until we hit a '/' char
65 				if (fullPath[pos] == '/') {
66 					leafStart = pos+1;
67 					pathEnd = pos-1;
68 					loop = false;
69 				}
70 				break;
71 		}
72 
73 		if (!loop)
74 			break;
75 	}
76 }
77 
78 /*!	The caller is responsible for deleting the returned directory path name
79 	and the leaf name.
80 	\param fullPath the path name to be split
81 	\param path a variable the directory path name pointer shall
82 		   be written into, may be NULL
83 	\param leaf a variable the leaf name pointer shall be
84 		   written into, may be NULL
85 */
86 status_t
87 split_path(const char *fullPath, char *&path, char *&leaf)
88 {
89 	return split_path(fullPath, &path, &leaf);
90 }
91 
92 /*!	The caller is responsible for deleting the returned directory path name
93 	and the leaf name.
94 	\param fullPath the path name to be split
95 	\param path a pointer to a variable the directory path name pointer shall
96 		   be written into, may be NULL
97 	\param leaf a pointer to a variable the leaf name pointer shall be
98 		   written into, may be NULL
99 */
100 status_t
101 split_path(const char *fullPath, char **path, char **leaf)
102 {
103 	if (path)
104 		*path = NULL;
105 	if (leaf)
106 		*leaf = NULL;
107 
108 	if (fullPath == NULL)
109 		return B_BAD_VALUE;
110 
111 	int leafStart, leafEnd, pathEnd, len;
112 	parse_path(fullPath, leafStart, leafEnd, pathEnd);
113 
114 	try {
115 		// Tidy up/handle special cases
116 		if (leafEnd == -1) {
117 
118 			// Handle special cases
119 			if (fullPath[0] == '/') {
120 				// Handle "/"
121 				if (path) {
122 					*path = new char[2];
123 					(*path)[0] = '/';
124 					(*path)[1] = 0;
125 				}
126 				if (leaf) {
127 					*leaf = new char[2];
128 					(*leaf)[0] = '.';
129 					(*leaf)[1] = 0;
130 				}
131 				return B_OK;
132 			} else if (fullPath[0] == 0) {
133 				// Handle "", which we'll treat as "./"
134 				if (path) {
135 					*path = new char[1];
136 					(*path)[0] = 0;
137 				}
138 				if (leaf) {
139 					*leaf = new char[2];
140 					(*leaf)[0] = '.';
141 					(*leaf)[1] = 0;
142 				}
143 				return B_OK;
144 			}
145 
146 		} else if (leafStart == -1) {
147 			// fullPath is just an entry name, no parent directories specified
148 			leafStart = 0;
149 		} else if (pathEnd == -1) {
150 			// The path is '/' (since pathEnd would be -2 if we had
151 			// run out of characters before hitting a '/')
152 			pathEnd = 0;
153 		}
154 
155 		// Alloc new strings and copy the path and leaf over
156 		if (path) {
157 			if (pathEnd == -2) {
158 				// empty path
159 				*path = new char[2];
160 				(*path)[0] = '.';
161 				(*path)[1] = 0;
162 			} else {
163 				// non-empty path
164 				len = pathEnd + 1;
165 				*path = new char[len+1];
166 				memcpy(*path, fullPath, len);
167 				(*path)[len] = 0;
168 			}
169 		}
170 		if (leaf) {
171 			len = leafEnd - leafStart + 1;
172 			*leaf = new char[len+1];
173 			memcpy(*leaf, fullPath + leafStart, len);
174 			(*leaf)[len] = 0;
175 		}
176 	} catch (std::bad_alloc exception) {
177 		if (path)
178 			delete[] *path;
179 		if (leaf)
180 			delete[] *leaf;
181 		return B_NO_MEMORY;
182 	}
183 	return B_OK;
184 }
185 
186 /*! The length of the first component is returned as well as the index at
187 	which the next one starts. These values are only valid, if the function
188 	returns \c B_OK.
189 	\param path the path to be parsed
190 	\param length the variable the length of the first component is written
191 		   into
192 	\param nextComponent the variable the index of the next component is
193 		   written into. \c 0 is returned, if there is no next component.
194 	\return \c B_OK, if \a path is not \c NULL, \c B_BAD_VALUE otherwise
195 */
196 status_t
197 parse_first_path_component(const char *path, int32& length,
198 						   int32& nextComponent)
199 {
200 	status_t error = (path ? B_OK : B_BAD_VALUE);
201 	if (error == B_OK) {
202 		int32 i = 0;
203 		// find first '/' or end of name
204 		for (; path[i] != '/' && path[i] != '\0'; i++);
205 		// handle special case "/..." (absolute path)
206 		if (i == 0 && path[i] != '\0')
207 			i = 1;
208 		length = i;
209 		// find last '/' or end of name
210 		for (; path[i] == '/' && path[i] != '\0'; i++);
211 		if (path[i] == '\0')	// this covers "" as well
212 			nextComponent = 0;
213 		else
214 			nextComponent = i;
215 	}
216 	return error;
217 }
218 
219 /*! A string containing the first component is returned and the index, at
220 	which the next one starts. These values are only valid, if the function
221 	returns \c B_OK.
222 	\param path the path to be parsed
223 	\param component the variable the pointer to the newly allocated string
224 		   containing the first path component is written into. The caller
225 		   is responsible for delete[]'ing the string.
226 	\param nextComponent the variable the index of the next component is
227 		   written into. \c 0 is returned, if there is no next component.
228 	\return \c B_OK, if \a path is not \c NULL, \c B_BAD_VALUE otherwise
229 */
230 status_t
231 parse_first_path_component(const char *path, char *&component,
232 						   int32& nextComponent)
233 {
234 	int32 length;
235 	status_t error = parse_first_path_component(path, length, nextComponent);
236 	if (error == B_OK) {
237 		component = new(nothrow) char[length + 1];
238 		if (component) {
239 			strncpy(component, path, length);
240 			component[length] = '\0';
241 		} else
242 			error = B_NO_MEMORY;
243 	}
244 	return error;
245 }
246 
247 /*!	An entry name is considered valid, if its length doesn't exceed
248 	\c B_FILE_NAME_LENGTH (including the terminating null) and it doesn't
249 	contain any \c "/".
250 	\param entry the entry name
251 	\return
252 	- \c B_OK, if \a entry is valid,
253 	- \c B_BAD_VALUE, if \a entry is \c NULL or contains a "/",
254 	- \c B_NAME_TOO_LONG, if \a entry is too long
255 	\note \c "" is considered a valid entry name.
256 */
257 status_t
258 check_entry_name(const char *entry)
259 {
260 	status_t error = (entry ? B_OK : B_BAD_VALUE);
261 	if (error == B_OK) {
262 		if (strlen(entry) >= B_FILE_NAME_LENGTH)
263 			error = B_NAME_TOO_LONG;
264 	}
265 	if (error == B_OK) {
266 		for (int32 i = 0; error == B_OK && entry[i] != '\0'; i++) {
267 			if (entry[i] == '/')
268 				error = B_BAD_VALUE;
269 		}
270 	}
271 	return error;
272 }
273 
274 /*!	An path name is considered valid, if its length doesn't exceed
275 	\c B_PATH_NAME_LENGTH (including the terminating null) and each of
276 	its components is a valid entry name.
277 	\param entry the entry name
278 	\return
279 	- \c B_OK, if \a path is valid,
280 	- \c B_BAD_VALUE, if \a path is \c NULL,
281 	- \c B_NAME_TOO_LONG, if \a path, or any of its components is too long
282 	\note \c "" is considered a valid path name.
283 */
284 status_t
285 check_path_name(const char *path)
286 {
287 	status_t error = (path ? B_OK : B_BAD_VALUE);
288 	// check the path components
289 	const char *remainder = path;
290 	int32 length, nextComponent;
291 	do {
292 		error = parse_first_path_component(remainder, length, nextComponent);
293 		if (error == B_OK) {
294 			if (length >= B_FILE_NAME_LENGTH)
295 				error = B_NAME_TOO_LONG;
296 			remainder += nextComponent;
297 		}
298 	} while (error == B_OK && nextComponent != 0);
299 	// check the length of the path
300 	if (error == B_OK && strlen(path) >= B_PATH_NAME_LENGTH)
301 		error = B_NAME_TOO_LONG;
302 	return error;
303 }
304 
305 std::string
306 to_lower(const char *str)
307 {
308 	std::string result;
309 	to_lower(str, result);
310 	return result;
311 }
312 
313 void
314 to_lower(const char *str, std::string &result)
315 {
316 	if (str) {
317 		result = "";
318 		for (int i = 0; i < (int)strlen(str); i++)
319 			result += tolower(str[i]);
320 	} else
321 		result = "(null)";
322 }
323 
324 void
325 to_lower(const char *str, char *result)
326 {
327 	if (str && result) {
328 		int i;
329 		for (i = 0; i < (int)strlen(str); i++)
330 			result[i] = tolower(str[i]);
331 		result[i] = 0;
332 	}
333 }
334 
335 void
336 to_lower(char *str)
337 {
338 	to_lower(str, str);
339 }
340 
341 void escape_path(const char *str, char *result)
342 {
343 	if (str && result) {
344 		int32 len = strlen(str);
345 
346 		for (int32 i = 0; i < len; i++) {
347 			char ch = str[i];
348 			char escapeChar = 0;
349 
350 			switch (ch) {
351 				case ' ':
352 				case '\'':
353 				case '"':
354 				case '?':
355 				case '\\':
356 				case '(':
357 				case ')':
358 				case '[':
359 				case ']':
360 				case '*':
361 				case '^':
362 					escapeChar = ch;
363 					break;
364 			}
365 
366 			if (escapeChar) {
367 				*(result++) = '\\';
368 				*(result++) = escapeChar;
369 			} else {
370 				*(result++) = ch;
371 			}
372 		}
373 
374 		*result = 0;
375 	}
376 }
377 
378 void escape_path(char *str)
379 {
380 	if (str) {
381 		char *copy = new(nothrow) char[strlen(str)+1];
382 		if (copy) {
383 			strcpy(copy, str);
384 			escape_path(copy, str);
385 		}
386 		delete [] copy;
387 	}
388 }
389 
390 };	// namespace Storage
391 };	// namespace BPrivate
392 
393