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