1 //---------------------------------------------------------------------- 2 // This software is part of the Haiku 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 // check the path is not NULL 387 status_t error = (path ? B_OK : B_BAD_VALUE); 388 if (error == B_BAD_VALUE) 389 return error; 390 // check the path components 391 const char *remainder = path; 392 int32 length, nextComponent; 393 do { 394 error = parse_first_path_component(remainder, length, nextComponent); 395 if (error == B_OK) { 396 if (length >= B_FILE_NAME_LENGTH) 397 error = B_NAME_TOO_LONG; 398 remainder += nextComponent; 399 } 400 } while (error == B_OK && nextComponent != 0); 401 // check the length of the path 402 if (error == B_OK && strlen(path) >= B_PATH_NAME_LENGTH) 403 error = B_NAME_TOO_LONG; 404 return error; 405 } 406 407 std::string 408 to_lower(const char *str) 409 { 410 std::string result; 411 to_lower(str, result); 412 return result; 413 } 414 415 void 416 to_lower(const char *str, std::string &result) 417 { 418 if (str) { 419 result = ""; 420 for (int i = 0; i < (int)strlen(str); i++) 421 result += tolower(str[i]); 422 } else 423 result = "(null)"; 424 } 425 426 void 427 to_lower(const char *str, char *result) 428 { 429 if (str && result) { 430 int i; 431 for (i = 0; i < (int)strlen(str); i++) 432 result[i] = tolower(str[i]); 433 result[i] = 0; 434 } 435 } 436 437 void 438 to_lower(char *str) 439 { 440 to_lower(str, str); 441 } 442 443 void escape_path(const char *str, char *result) 444 { 445 if (str && result) { 446 int32 len = strlen(str); 447 448 for (int32 i = 0; i < len; i++) { 449 char ch = str[i]; 450 char escapeChar = 0; 451 452 switch (ch) { 453 case ' ': 454 case '\'': 455 case '"': 456 case '?': 457 case '\\': 458 case '(': 459 case ')': 460 case '[': 461 case ']': 462 case '*': 463 case '^': 464 escapeChar = ch; 465 break; 466 } 467 468 if (escapeChar) { 469 *(result++) = '\\'; 470 *(result++) = escapeChar; 471 } else { 472 *(result++) = ch; 473 } 474 } 475 476 *result = 0; 477 } 478 } 479 480 void escape_path(char *str) 481 { 482 if (str) { 483 char *copy = new(nothrow) char[strlen(str)+1]; 484 if (copy) { 485 strcpy(copy, str); 486 escape_path(copy, str); 487 } 488 delete [] copy; 489 } 490 } 491 492 // device_is_root_device 493 bool 494 device_is_root_device(dev_t device) 495 { 496 return device == 1; 497 } 498 499 // Close 500 void 501 FDCloser::Close() 502 { 503 if (fFD >= 0) 504 _kern_close(fFD); 505 fFD = -1; 506 } 507 508 }; // namespace Storage 509 }; // namespace BPrivate 510 511