xref: /haiku/src/kits/storage/storage_support.cpp (revision 5e7964b0a929555415798dea3373db9ac4611caa)
1 //----------------------------------------------------------------------
2 //  This software is part of the OpenBeOS distribution and is covered
3 //  by the MIT 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 
17 #include <syscalls.h>
18 
19 #include "storage_support.h"
20 
21 using std::nothrow;
22 
23 namespace BPrivate {
24 namespace Storage {
25 
26 /*!	\param path the path
27 	\return \c true, if \a path is not \c NULL and absolute, \c false otherwise
28 */
29 bool
30 is_absolute_path(const char *path)
31 {
32 	return (path && path[0] == '/');
33 }
34 
35 // parse_path
36 /*!	\brief Parses the supplied path and returns the position of the leaf name
37 		   part of the path and the length of its directory path part.
38 
39 	The value returned in \a fullPath is guaranteed to be > 0, i.e. the
40 	function always returns a non-empty directory path part. The leaf name
41 	part may be empty though (i.e. \code leafStart == leafEnd \endcode), which
42 	will happen, if the supplied path consists only of one component.
43 
44 	\param fullPath The path to be parsed.
45 	\param dirEnd Reference to a variable into which the end index of the
46 		   directory part shall be written. The index is exclusive.
47 	\param leafStart Reference to a variable into which the start index of
48 		   the leaf name part shall be written. The index is inclusive.
49 	\param leafEnd Reference to a variable into which the end index of
50 		   the leaf name part shall be written. The index is exclusive.
51 	\return \c B_OK, if everything went fine, B_BAD_VALUE, if the supplied
52 		   path is invalid.
53 */
54 status_t
55 parse_path(const char *fullPath, int &dirEnd, int &leafStart, int &leafEnd)
56 {
57 	// check path and get length
58 	if (!fullPath)
59 		return B_BAD_VALUE;
60 	int pathLen = strlen(fullPath);
61 	if (pathLen == 0)
62 		return B_BAD_VALUE;
63 	// find then end of the leaf name (skip trailing '/')
64 	int i = pathLen - 1;
65 	while (i >= 0 && fullPath[i] == '/')
66 		i--;
67 	leafEnd = i + 1;
68 	if (leafEnd == 0) {
69 		// fullPath consists of slashes only
70 		dirEnd = leafStart = leafEnd = 1;
71 		return B_OK;
72 	}
73 	// find the start of the leaf name
74 	while (i >= 0 && fullPath[i] != '/')
75 		i--;
76 	leafStart = i + 1;
77 	if (leafStart == 0) {
78 		// fullPath contains only one component
79 		dirEnd = leafStart = leafEnd;
80 		return B_OK;
81 	}
82 	// find the end of the dir path
83 	while (i >= 0 && fullPath[i] == '/')
84 		i--;
85 	dirEnd = i + 1;
86 	if (dirEnd == 0)	// => fullPath[0] == '/' (an absolute path)
87 		dirEnd = 1;
88 	return B_OK;
89 }
90 
91 // parse_path
92 /*!	\brief Parses the supplied path and returns the leaf name part of the path
93 		   and its directory path part.
94 
95 	The value returned in \a fullPath is guaranteed to be > 0, i.e. the
96 	function always returns a non-empty directory path part. The leaf name
97 	part may be empty though (i.e. \code leafStart == leafEnd \endcode), which
98 	will happen, if the supplied path consists only of one component.
99 
100 	\param fullPath The path to be parsed.
101 	\param dirPath Pointer to a character array of size \c B_PATH_NAME_LENGTH
102 		   or greater, into which the directory part shall be written.
103 		   May be \c NULL.
104 	\param leaf Pointer to a character array of size \c B_FILE_NAME_LENGTH
105 		   or greater, into which the leaf name part shall be written.
106 		   May be \c NULL.
107 	\return \c B_OK, if everything went fine, B_BAD_VALUE, if the supplied
108 		   path is invalid.
109 */
110 status_t
111 parse_path(const char *fullPath, char *dirPath, char *leaf)
112 {
113 	// parse the path and check the lengths
114 	int leafStart, leafEnd, dirEnd;
115 	status_t error = parse_path(fullPath, dirEnd, leafStart, leafEnd);
116 	if (error != B_OK)
117 		return error;
118 	if (dirEnd >= B_PATH_NAME_LENGTH
119 		|| leafEnd - leafStart >= B_FILE_NAME_LENGTH) {
120 		return B_NAME_TOO_LONG;
121 	}
122 	// copy the result strings
123 	if (dirPath)
124 		strlcpy(dirPath, fullPath, dirEnd + 1);
125 	if (leaf)
126 		strlcpy(leaf, fullPath + leafStart, leafEnd - leafStart + 1);
127 	return B_OK;
128 }
129 
130 // internal_parse_path
131 static
132 void
133 internal_parse_path(const char *fullPath, int &leafStart, int &leafEnd,
134 	int &pathEnd)
135 {
136 	if (fullPath == NULL)
137 		return;
138 
139 	enum PathParserState { PPS_START, PPS_LEAF } state = PPS_START;
140 
141 	int len = strlen(fullPath);
142 
143 	leafStart = -1;
144 	leafEnd = -1;
145 	pathEnd = -2;
146 
147 	bool loop = true;
148 	for (int pos = len-1; ; pos--) {
149 		if (pos < 0)
150 			break;
151 
152 		switch (state) {
153 			case PPS_START:
154 				// Skip all trailing '/' chars, then move on to
155 				// reading the leaf name
156 				if (fullPath[pos] != '/') {
157 					leafEnd = pos;
158 					state = PPS_LEAF;
159 				}
160 				break;
161 
162 			case PPS_LEAF:
163 				// Read leaf name chars until we hit a '/' char
164 				if (fullPath[pos] == '/') {
165 					leafStart = pos+1;
166 					pathEnd = pos-1;
167 					loop = false;
168 				}
169 				break;
170 		}
171 
172 		if (!loop)
173 			break;
174 	}
175 }
176 
177 /*!	The caller is responsible for deleting the returned directory path name
178 	and the leaf name.
179 	\param fullPath the path name to be split
180 	\param path a variable the directory path name pointer shall
181 		   be written into, may be NULL
182 	\param leaf a variable the leaf name pointer shall be
183 		   written into, may be NULL
184 */
185 status_t
186 split_path(const char *fullPath, char *&path, char *&leaf)
187 {
188 	return split_path(fullPath, &path, &leaf);
189 }
190 
191 /*!	The caller is responsible for deleting the returned directory path name
192 	and the leaf name.
193 	\param fullPath the path name to be split
194 	\param path a pointer to a variable the directory path name pointer shall
195 		   be written into, may be NULL
196 	\param leaf a pointer to a variable the leaf name pointer shall be
197 		   written into, may be NULL
198 */
199 status_t
200 split_path(const char *fullPath, char **path, char **leaf)
201 {
202 	if (path)
203 		*path = NULL;
204 	if (leaf)
205 		*leaf = NULL;
206 
207 	if (fullPath == NULL)
208 		return B_BAD_VALUE;
209 
210 	int leafStart, leafEnd, pathEnd, len;
211 	internal_parse_path(fullPath, leafStart, leafEnd, pathEnd);
212 
213 	try {
214 		// Tidy up/handle special cases
215 		if (leafEnd == -1) {
216 
217 			// Handle special cases
218 			if (fullPath[0] == '/') {
219 				// Handle "/"
220 				if (path) {
221 					*path = new char[2];
222 					(*path)[0] = '/';
223 					(*path)[1] = 0;
224 				}
225 				if (leaf) {
226 					*leaf = new char[2];
227 					(*leaf)[0] = '.';
228 					(*leaf)[1] = 0;
229 				}
230 				return B_OK;
231 			} else if (fullPath[0] == 0) {
232 				// Handle "", which we'll treat as "./"
233 				if (path) {
234 					*path = new char[1];
235 					(*path)[0] = 0;
236 				}
237 				if (leaf) {
238 					*leaf = new char[2];
239 					(*leaf)[0] = '.';
240 					(*leaf)[1] = 0;
241 				}
242 				return B_OK;
243 			}
244 
245 		} else if (leafStart == -1) {
246 			// fullPath is just an entry name, no parent directories specified
247 			leafStart = 0;
248 		} else if (pathEnd == -1) {
249 			// The path is '/' (since pathEnd would be -2 if we had
250 			// run out of characters before hitting a '/')
251 			pathEnd = 0;
252 		}
253 
254 		// Alloc new strings and copy the path and leaf over
255 		if (path) {
256 			if (pathEnd == -2) {
257 				// empty path
258 				*path = new char[2];
259 				(*path)[0] = '.';
260 				(*path)[1] = 0;
261 			} else {
262 				// non-empty path
263 				len = pathEnd + 1;
264 				*path = new char[len+1];
265 				memcpy(*path, fullPath, len);
266 				(*path)[len] = 0;
267 			}
268 		}
269 		if (leaf) {
270 			len = leafEnd - leafStart + 1;
271 			*leaf = new char[len+1];
272 			memcpy(*leaf, fullPath + leafStart, len);
273 			(*leaf)[len] = 0;
274 		}
275 	} catch (std::bad_alloc exception) {
276 		if (path)
277 			delete[] *path;
278 		if (leaf)
279 			delete[] *leaf;
280 		return B_NO_MEMORY;
281 	}
282 	return B_OK;
283 }
284 
285 /*! The length of the first component is returned as well as the index at
286 	which the next one starts. These values are only valid, if the function
287 	returns \c B_OK.
288 	\param path the path to be parsed
289 	\param length the variable the length of the first component is written
290 		   into
291 	\param nextComponent the variable the index of the next component is
292 		   written into. \c 0 is returned, if there is no next component.
293 	\return \c B_OK, if \a path is not \c NULL, \c B_BAD_VALUE otherwise
294 */
295 status_t
296 parse_first_path_component(const char *path, int32& length,
297 						   int32& nextComponent)
298 {
299 	status_t error = (path ? B_OK : B_BAD_VALUE);
300 	if (error == B_OK) {
301 		int32 i = 0;
302 		// find first '/' or end of name
303 		for (; path[i] != '/' && path[i] != '\0'; i++);
304 		// handle special case "/..." (absolute path)
305 		if (i == 0 && path[i] != '\0')
306 			i = 1;
307 		length = i;
308 		// find last '/' or end of name
309 		for (; path[i] == '/' && path[i] != '\0'; i++);
310 		if (path[i] == '\0')	// this covers "" as well
311 			nextComponent = 0;
312 		else
313 			nextComponent = i;
314 	}
315 	return error;
316 }
317 
318 /*! A string containing the first component is returned and the index, at
319 	which the next one starts. These values are only valid, if the function
320 	returns \c B_OK.
321 	\param path the path to be parsed
322 	\param component the variable the pointer to the newly allocated string
323 		   containing the first path component is written into. The caller
324 		   is responsible for delete[]'ing the string.
325 	\param nextComponent the variable the index of the next component is
326 		   written into. \c 0 is returned, if there is no next component.
327 	\return \c B_OK, if \a path is not \c NULL, \c B_BAD_VALUE otherwise
328 */
329 status_t
330 parse_first_path_component(const char *path, char *&component,
331 						   int32& nextComponent)
332 {
333 	int32 length;
334 	status_t error = parse_first_path_component(path, length, nextComponent);
335 	if (error == B_OK) {
336 		component = new(nothrow) char[length + 1];
337 		if (component) {
338 			strncpy(component, path, length);
339 			component[length] = '\0';
340 		} else
341 			error = B_NO_MEMORY;
342 	}
343 	return error;
344 }
345 
346 /*!	An entry name is considered valid, if its length doesn't exceed
347 	\c B_FILE_NAME_LENGTH (including the terminating null) and it doesn't
348 	contain any \c "/".
349 	\param entry the entry name
350 	\return
351 	- \c B_OK, if \a entry is valid,
352 	- \c B_BAD_VALUE, if \a entry is \c NULL or contains a "/",
353 	- \c B_NAME_TOO_LONG, if \a entry is too long
354 	\note \c "" is considered a valid entry name.
355 */
356 status_t
357 check_entry_name(const char *entry)
358 {
359 	status_t error = (entry ? B_OK : B_BAD_VALUE);
360 	if (error == B_OK) {
361 		if (strlen(entry) >= B_FILE_NAME_LENGTH)
362 			error = B_NAME_TOO_LONG;
363 	}
364 	if (error == B_OK) {
365 		for (int32 i = 0; error == B_OK && entry[i] != '\0'; i++) {
366 			if (entry[i] == '/')
367 				error = B_BAD_VALUE;
368 		}
369 	}
370 	return error;
371 }
372 
373 /*!	An path name is considered valid, if its length doesn't exceed
374 	\c B_PATH_NAME_LENGTH (including the terminating null) and each of
375 	its components is a valid entry name.
376 	\param entry the entry name
377 	\return
378 	- \c B_OK, if \a path is valid,
379 	- \c B_BAD_VALUE, if \a path is \c NULL,
380 	- \c B_NAME_TOO_LONG, if \a path, or any of its components is too long
381 	\note \c "" is considered a valid path name.
382 */
383 status_t
384 check_path_name(const char *path)
385 {
386 	status_t error = (path ? B_OK : B_BAD_VALUE);
387 	// check the path components
388 	const char *remainder = path;
389 	int32 length, nextComponent;
390 	do {
391 		error = parse_first_path_component(remainder, length, nextComponent);
392 		if (error == B_OK) {
393 			if (length >= B_FILE_NAME_LENGTH)
394 				error = B_NAME_TOO_LONG;
395 			remainder += nextComponent;
396 		}
397 	} while (error == B_OK && nextComponent != 0);
398 	// check the length of the path
399 	if (error == B_OK && strlen(path) >= B_PATH_NAME_LENGTH)
400 		error = B_NAME_TOO_LONG;
401 	return error;
402 }
403 
404 std::string
405 to_lower(const char *str)
406 {
407 	std::string result;
408 	to_lower(str, result);
409 	return result;
410 }
411 
412 void
413 to_lower(const char *str, std::string &result)
414 {
415 	if (str) {
416 		result = "";
417 		for (int i = 0; i < (int)strlen(str); i++)
418 			result += tolower(str[i]);
419 	} else
420 		result = "(null)";
421 }
422 
423 void
424 to_lower(const char *str, char *result)
425 {
426 	if (str && result) {
427 		int i;
428 		for (i = 0; i < (int)strlen(str); i++)
429 			result[i] = tolower(str[i]);
430 		result[i] = 0;
431 	}
432 }
433 
434 void
435 to_lower(char *str)
436 {
437 	to_lower(str, str);
438 }
439 
440 void escape_path(const char *str, char *result)
441 {
442 	if (str && result) {
443 		int32 len = strlen(str);
444 
445 		for (int32 i = 0; i < len; i++) {
446 			char ch = str[i];
447 			char escapeChar = 0;
448 
449 			switch (ch) {
450 				case ' ':
451 				case '\'':
452 				case '"':
453 				case '?':
454 				case '\\':
455 				case '(':
456 				case ')':
457 				case '[':
458 				case ']':
459 				case '*':
460 				case '^':
461 					escapeChar = ch;
462 					break;
463 			}
464 
465 			if (escapeChar) {
466 				*(result++) = '\\';
467 				*(result++) = escapeChar;
468 			} else {
469 				*(result++) = ch;
470 			}
471 		}
472 
473 		*result = 0;
474 	}
475 }
476 
477 void escape_path(char *str)
478 {
479 	if (str) {
480 		char *copy = new(nothrow) char[strlen(str)+1];
481 		if (copy) {
482 			strcpy(copy, str);
483 			escape_path(copy, str);
484 		}
485 		delete [] copy;
486 	}
487 }
488 
489 // device_is_root_device
490 bool
491 device_is_root_device(dev_t device)
492 {
493 	return device == 1;
494 }
495 
496 // Close
497 void
498 FDCloser::Close()
499 {
500 	if (fFD >= 0)
501 		_kern_close(fFD);
502 	fFD = -1;
503 }
504 
505 };	// namespace Storage
506 };	// namespace BPrivate
507 
508