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