repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / kits / storage / storage_support.cpp
blob8b6618a9e5a326b7262f1bb58a5415bab2df04fa
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 */
10 #include <new>
11 #include <ctype.h>
12 #include <string.h>
14 #include <StorageDefs.h>
15 #include <SupportDefs.h>
17 #include <syscalls.h>
19 #include "storage_support.h"
21 using std::nothrow;
23 namespace BPrivate {
24 namespace Storage {
26 /*! \param path the path
27 \return \c true, if \a path is not \c NULL and absolute, \c false otherwise
29 bool
30 is_absolute_path(const char *path)
32 return (path && path[0] == '/');
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.
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.
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.
54 status_t
55 parse_path(const char *fullPath, int &dirEnd, int &leafStart, int &leafEnd)
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;
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;
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;
91 // parse_path
92 /*! \brief Parses the supplied path and returns the leaf name part of the path
93 and its directory path part.
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.
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.
110 status_t
111 parse_path(const char *fullPath, char *dirPath, char *leaf)
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;
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;
130 // internal_parse_path
131 static
132 void
133 internal_parse_path(const char *fullPath, int &leafStart, int &leafEnd,
134 int &pathEnd)
136 if (fullPath == NULL)
137 return;
139 enum PathParserState { PPS_START, PPS_LEAF } state = PPS_START;
141 int len = strlen(fullPath);
143 leafStart = -1;
144 leafEnd = -1;
145 pathEnd = -2;
147 bool loop = true;
148 for (int pos = len-1; ; pos--) {
149 if (pos < 0)
150 break;
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;
160 break;
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;
169 break;
172 if (!loop)
173 break;
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
185 status_t
186 split_path(const char *fullPath, char *&path, char *&leaf)
188 return split_path(fullPath, &path, &leaf);
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
199 status_t
200 split_path(const char *fullPath, char **path, char **leaf)
202 if (path)
203 *path = NULL;
204 if (leaf)
205 *leaf = NULL;
207 if (fullPath == NULL)
208 return B_BAD_VALUE;
210 int leafStart, leafEnd, pathEnd, len;
211 internal_parse_path(fullPath, leafStart, leafEnd, pathEnd);
213 try {
214 // Tidy up/handle special cases
215 if (leafEnd == -1) {
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;
225 if (leaf) {
226 *leaf = new char[2];
227 (*leaf)[0] = '.';
228 (*leaf)[1] = 0;
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;
237 if (leaf) {
238 *leaf = new char[2];
239 (*leaf)[0] = '.';
240 (*leaf)[1] = 0;
242 return B_OK;
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;
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;
269 if (leaf) {
270 len = leafEnd - leafStart + 1;
271 *leaf = new char[len+1];
272 memcpy(*leaf, fullPath + leafStart, len);
273 (*leaf)[len] = 0;
275 } catch (std::bad_alloc exception) {
276 if (path)
277 delete[] *path;
278 if (leaf)
279 delete[] *leaf;
280 return B_NO_MEMORY;
282 return B_OK;
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
295 status_t
296 parse_first_path_component(const char *path, int32& length,
297 int32& nextComponent)
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;
315 return error;
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
329 status_t
330 parse_first_path_component(const char *path, char *&component,
331 int32& nextComponent)
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;
343 return error;
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.
356 status_t
357 check_entry_name(const char *entry)
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;
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;
370 return error;
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.
383 status_t
384 check_path_name(const char *path)
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;
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;
404 std::string
405 to_lower(const char *str)
407 std::string result;
408 to_lower(str, result);
409 return result;
412 void
413 to_lower(const char *str, std::string &result)
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)";
423 void
424 to_lower(const char *str, char *result)
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;
434 void
435 to_lower(char *str)
437 to_lower(str, str);
440 void escape_path(const char *str, char *result)
442 if (str && result) {
443 int32 len = strlen(str);
445 for (int32 i = 0; i < len; i++) {
446 char ch = str[i];
447 char escapeChar = 0;
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;
465 if (escapeChar) {
466 *(result++) = '\\';
467 *(result++) = escapeChar;
468 } else {
469 *(result++) = ch;
473 *result = 0;
477 void escape_path(char *str)
479 if (str) {
480 char *copy = new(nothrow) char[strlen(str)+1];
481 if (copy) {
482 strcpy(copy, str);
483 escape_path(copy, str);
485 delete [] copy;
489 // device_is_root_device
490 bool
491 device_is_root_device(dev_t device)
493 return device == 1;
496 // Close
497 void
498 FDCloser::Close()
500 if (fFD >= 0)
501 _kern_close(fFD);
502 fFD = -1;
505 }; // namespace Storage
506 }; // namespace BPrivate