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