1 /* 2 * Copyright 2004-2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2008-2017, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 /*! A simple class wrapping a path. Has a fixed-sized buffer. */ 9 10 11 #include <fs/KPath.h> 12 #include <Path.h> 13 14 #include <stdlib.h> 15 #include <string.h> 16 17 #include <team.h> 18 19 20 // debugging 21 #define TRACE(x) ; 22 //#define TRACE(x) dprintf x 23 24 25 KPath::KPath(size_t bufferSize) 26 : 27 fBuffer(NULL), 28 fBufferSize(0), 29 fPathLength(0), 30 fLocked(false), 31 fLazy(false), 32 fFailed(false), 33 fIsNull(false) 34 { 35 SetTo(NULL, DEFAULT, bufferSize); 36 } 37 38 39 KPath::KPath(const char* path, int32 flags, size_t bufferSize) 40 : 41 fBuffer(NULL), 42 fBufferSize(0), 43 fPathLength(0), 44 fLocked(false), 45 fLazy(false), 46 fFailed(false), 47 fIsNull(false) 48 { 49 SetTo(path, flags, bufferSize); 50 } 51 52 53 KPath::KPath(const KPath& other) 54 : 55 fBuffer(NULL), 56 fBufferSize(0), 57 fPathLength(0), 58 fLocked(false), 59 fLazy(false), 60 fFailed(false), 61 fIsNull(false) 62 { 63 *this = other; 64 } 65 66 67 KPath::~KPath() 68 { 69 free(fBuffer); 70 } 71 72 73 status_t 74 KPath::SetTo(const char* path, int32 flags, size_t bufferSize) 75 { 76 if (bufferSize == 0) 77 bufferSize = B_PATH_NAME_LENGTH; 78 79 // free the previous buffer, if the buffer size differs 80 if (fBuffer != NULL && fBufferSize != bufferSize) { 81 free(fBuffer); 82 fBuffer = NULL; 83 fBufferSize = 0; 84 } 85 86 fPathLength = 0; 87 fLocked = false; 88 fBufferSize = bufferSize; 89 fLazy = (flags & LAZY_ALLOC) != 0; 90 fIsNull = path == NULL; 91 92 if (path != NULL || !fLazy) { 93 status_t status = _AllocateBuffer(); 94 if (status != B_OK) 95 return status; 96 } 97 98 return SetPath(path, flags); 99 } 100 101 102 void 103 KPath::Adopt(KPath& other) 104 { 105 free(fBuffer); 106 107 fBuffer = other.fBuffer; 108 fBufferSize = other.fBufferSize; 109 fPathLength = other.fPathLength; 110 fLazy = other.fLazy; 111 fFailed = other.fFailed; 112 fIsNull = other.fIsNull; 113 114 other.fBuffer = NULL; 115 if (!other.fLazy) 116 other.fBufferSize = 0; 117 other.fPathLength = 0; 118 other.fFailed = false; 119 other.fIsNull = other.fLazy; 120 } 121 122 123 status_t 124 KPath::InitCheck() const 125 { 126 if (fBuffer != NULL || (fLazy && !fFailed && fBufferSize != 0)) 127 return B_OK; 128 129 return fFailed ? B_NO_MEMORY : B_NO_INIT; 130 } 131 132 133 /*! \brief Sets the buffer to \a path. 134 135 \param flags Understands the following two options: 136 - \c NORMALIZE 137 - \c TRAVERSE_LEAF_LINK 138 */ 139 status_t 140 KPath::SetPath(const char* path, int32 flags) 141 { 142 if (path == NULL && fLazy && fBuffer == NULL) { 143 fIsNull = true; 144 return B_OK; 145 } 146 147 if (fBuffer == NULL) { 148 if (fLazy) { 149 status_t status = _AllocateBuffer(); 150 if (status != B_OK) 151 return B_NO_MEMORY; 152 } else 153 return B_NO_INIT; 154 } 155 156 fIsNull = false; 157 158 if (path != NULL) { 159 if ((flags & NORMALIZE) != 0) { 160 // normalize path 161 status_t status = _Normalize(path, 162 (flags & TRAVERSE_LEAF_LINK) != 0); 163 if (status != B_OK) 164 return status; 165 } else { 166 // don't normalize path 167 size_t length = strlen(path); 168 if (length >= fBufferSize) 169 return B_BUFFER_OVERFLOW; 170 171 memcpy(fBuffer, path, length + 1); 172 fPathLength = length; 173 _ChopTrailingSlashes(); 174 } 175 } else { 176 fBuffer[0] = '\0'; 177 fPathLength = 0; 178 if (fLazy) 179 fIsNull = true; 180 } 181 return B_OK; 182 } 183 184 185 const char* 186 KPath::Path() const 187 { 188 return fIsNull ? NULL : fBuffer; 189 } 190 191 192 /*! \brief Locks the buffer for external changes. 193 194 \param force In lazy mode, this will allocate a buffer when set. 195 Otherwise, \c NULL will be returned if set to NULL. 196 */ 197 char* 198 KPath::LockBuffer(bool force) 199 { 200 if (fBuffer == NULL && fLazy) { 201 if (fIsNull && !force) 202 return NULL; 203 204 _AllocateBuffer(); 205 } 206 207 if (fBuffer == NULL || fLocked) 208 return NULL; 209 210 fLocked = true; 211 fIsNull = false; 212 213 return fBuffer; 214 } 215 216 217 void 218 KPath::UnlockBuffer() 219 { 220 if (!fLocked) { 221 TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n")); 222 return; 223 } 224 225 fLocked = false; 226 227 if (fBuffer == NULL) 228 return; 229 230 fPathLength = strnlen(fBuffer, fBufferSize); 231 if (fPathLength == fBufferSize) { 232 TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n")); 233 fPathLength--; 234 fBuffer[fPathLength] = '\0'; 235 } 236 _ChopTrailingSlashes(); 237 } 238 239 240 char* 241 KPath::DetachBuffer() 242 { 243 char* buffer = fBuffer; 244 245 if (fBuffer != NULL) { 246 fBuffer = NULL; 247 fPathLength = 0; 248 fLocked = false; 249 } 250 251 return buffer; 252 } 253 254 255 const char* 256 KPath::Leaf() const 257 { 258 if (fBuffer == NULL) 259 return NULL; 260 261 for (int32 i = fPathLength - 1; i >= 0; i--) { 262 if (fBuffer[i] == '/') 263 return fBuffer + i + 1; 264 } 265 266 return fBuffer; 267 } 268 269 270 status_t 271 KPath::ReplaceLeaf(const char* newLeaf) 272 { 273 const char* leaf = Leaf(); 274 if (leaf == NULL) 275 return B_NO_INIT; 276 277 int32 leafIndex = leaf - fBuffer; 278 // chop off the current leaf (don't replace "/", though) 279 if (leafIndex != 0 || fBuffer[leafIndex - 1]) { 280 fBuffer[leafIndex] = '\0'; 281 fPathLength = leafIndex; 282 _ChopTrailingSlashes(); 283 } 284 285 // if a leaf was given, append it 286 if (newLeaf != NULL) 287 return Append(newLeaf); 288 return B_OK; 289 } 290 291 292 bool 293 KPath::RemoveLeaf() 294 { 295 // get the leaf -- bail out, if not initialized or only the "/" is left 296 const char* leaf = Leaf(); 297 if (leaf == NULL || leaf == fBuffer || leaf[0] == '\0') 298 return false; 299 300 // chop off the leaf 301 int32 leafIndex = leaf - fBuffer; 302 fBuffer[leafIndex] = '\0'; 303 fPathLength = leafIndex; 304 _ChopTrailingSlashes(); 305 306 return true; 307 } 308 309 310 status_t 311 KPath::Append(const char* component, bool isComponent) 312 { 313 // check initialization and parameter 314 if (fBuffer == NULL) 315 return B_NO_INIT; 316 if (component == NULL) 317 return B_BAD_VALUE; 318 if (fPathLength == 0) 319 return SetPath(component); 320 321 // get component length 322 size_t componentLength = strlen(component); 323 if (componentLength < 1) 324 return B_OK; 325 326 // if our current path is empty, we just copy the supplied one 327 // compute the result path len 328 bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/' 329 && component[0] != '/'; 330 size_t resultPathLength = fPathLength + componentLength 331 + (insertSlash ? 1 : 0); 332 if (resultPathLength >= fBufferSize) 333 return B_BUFFER_OVERFLOW; 334 335 // compose the result path 336 if (insertSlash) 337 fBuffer[fPathLength++] = '/'; 338 memcpy(fBuffer + fPathLength, component, componentLength + 1); 339 fPathLength = resultPathLength; 340 return B_OK; 341 } 342 343 344 status_t 345 KPath::Normalize(bool traverseLeafLink) 346 { 347 if (fBuffer == NULL) 348 return B_NO_INIT; 349 if (fPathLength == 0) 350 return B_BAD_VALUE; 351 352 return _Normalize(fBuffer, traverseLeafLink); 353 } 354 355 356 KPath& 357 KPath::operator=(const KPath& other) 358 { 359 SetTo(other.fBuffer, fLazy ? KPath::LAZY_ALLOC : KPath::DEFAULT, 360 other.fBufferSize); 361 return *this; 362 } 363 364 365 KPath& 366 KPath::operator=(const char* path) 367 { 368 SetPath(path); 369 return *this; 370 } 371 372 373 bool 374 KPath::operator==(const KPath& other) const 375 { 376 if (fBuffer == NULL) 377 return !other.fBuffer; 378 379 return other.fBuffer != NULL 380 && fPathLength == other.fPathLength 381 && strcmp(fBuffer, other.fBuffer) == 0; 382 } 383 384 385 bool 386 KPath::operator==(const char* path) const 387 { 388 if (fBuffer == NULL) 389 return path == NULL; 390 391 return path != NULL && strcmp(fBuffer, path) == 0; 392 } 393 394 395 bool 396 KPath::operator!=(const KPath& other) const 397 { 398 return !(*this == other); 399 } 400 401 402 bool 403 KPath::operator!=(const char* path) const 404 { 405 return !(*this == path); 406 } 407 408 409 status_t 410 KPath::_AllocateBuffer() 411 { 412 if (fBuffer == NULL && fBufferSize != 0) 413 fBuffer = (char*)malloc(fBufferSize); 414 if (fBuffer == NULL) { 415 fFailed = true; 416 return B_NO_MEMORY; 417 } 418 419 fBuffer[0] = '\0'; 420 fFailed = false; 421 return B_OK; 422 } 423 424 425 status_t 426 KPath::_Normalize(const char* path, bool traverseLeafLink) 427 { 428 BPath normalizedPath; 429 status_t error = normalizedPath.SetTo(path, NULL, true); 430 if (error != B_OK) { 431 fBuffer[0] = '\0'; 432 fPathLength = 0; 433 return error; 434 } 435 436 strlcpy(fBuffer, normalizedPath.Path(), fBufferSize); 437 fPathLength = strlen(fBuffer); 438 return B_OK; 439 } 440 441 442 void 443 KPath::_ChopTrailingSlashes() 444 { 445 if (fBuffer != NULL) { 446 while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/') 447 fBuffer[--fPathLength] = '\0'; 448 } 449 } 450 451