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 25KPath::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 39KPath::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 53KPath::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 67KPath::~KPath() 68{ 69 free(fBuffer); 70} 71 72 73status_t 74KPath::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 102void 103KPath::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 123status_t 124KPath::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*/ 139status_t 140KPath::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 185const char* 186KPath::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*/ 197char* 198KPath::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 217void 218KPath::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 240char* 241KPath::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 255const char* 256KPath::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 270status_t 271KPath::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 292bool 293KPath::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 310status_t 311KPath::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 344status_t 345KPath::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 356KPath& 357KPath::operator=(const KPath& other) 358{ 359 SetTo(other.fBuffer, fLazy ? KPath::LAZY_ALLOC : KPath::DEFAULT, 360 other.fBufferSize); 361 return *this; 362} 363 364 365KPath& 366KPath::operator=(const char* path) 367{ 368 SetPath(path); 369 return *this; 370} 371 372 373bool 374KPath::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 385bool 386KPath::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 395bool 396KPath::operator!=(const KPath& other) const 397{ 398 return !(*this == other); 399} 400 401 402bool 403KPath::operator!=(const char* path) const 404{ 405 return !(*this == path); 406} 407 408 409status_t 410KPath::_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 425status_t 426KPath::_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 442void 443KPath::_ChopTrailingSlashes() 444{ 445 if (fBuffer != NULL) { 446 while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/') 447 fBuffer[--fPathLength] = '\0'; 448 } 449} 450 451