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 13 #include <stdlib.h> 14 #include <string.h> 15 16 #include <team.h> 17 #include <vfs.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 { 32 SetTo(NULL, KPath::DEFAULT, bufferSize); 33 } 34 35 36 KPath::KPath(const char* path, int32 flags, size_t bufferSize) 37 : 38 fBuffer(NULL), 39 fBufferSize(0), 40 fPathLength(0), 41 fLocked(false) 42 { 43 SetTo(path, flags, bufferSize); 44 } 45 46 47 KPath::KPath(const KPath& other) 48 : 49 fBuffer(NULL), 50 fBufferSize(0), 51 fPathLength(0), 52 fLocked(false) 53 { 54 *this = other; 55 } 56 57 58 KPath::~KPath() 59 { 60 free(fBuffer); 61 } 62 63 64 status_t 65 KPath::SetTo(const char* path, int32 flags, size_t bufferSize) 66 { 67 if (bufferSize == 0) 68 bufferSize = B_PATH_NAME_LENGTH; 69 70 // free the previous buffer, if the buffer size differs 71 if (fBuffer != NULL && fBufferSize != bufferSize) { 72 free(fBuffer); 73 fBuffer = NULL; 74 fBufferSize = 0; 75 } 76 fPathLength = 0; 77 fLocked = false; 78 79 // allocate buffer 80 if (fBuffer == NULL) 81 fBuffer = (char*)malloc(bufferSize); 82 if (fBuffer == NULL) 83 return B_NO_MEMORY; 84 85 fBufferSize = bufferSize; 86 fBuffer[0] = '\0'; 87 88 return SetPath(path, flags); 89 } 90 91 92 void 93 KPath::Adopt(KPath& other) 94 { 95 free(fBuffer); 96 97 fBuffer = other.fBuffer; 98 fBufferSize = other.fBufferSize; 99 fPathLength = other.fPathLength; 100 101 other.fBuffer = NULL; 102 other.fBufferSize = 0; 103 other.fPathLength = 0; 104 } 105 106 107 status_t 108 KPath::InitCheck() const 109 { 110 return fBuffer != NULL ? B_OK : B_NO_MEMORY; 111 } 112 113 114 status_t 115 KPath::SetPath(const char* path, int32 flags) 116 { 117 if (fBuffer == NULL) 118 return B_NO_INIT; 119 120 if (path != NULL) { 121 if ((flags & NORMALIZE) != 0) { 122 // normalize path 123 status_t error = vfs_normalize_path(path, fBuffer, fBufferSize, 124 (flags & TRAVERSE_LEAF_LINK) != 0, 125 team_get_kernel_team_id() == team_get_current_team_id()); 126 if (error != B_OK) { 127 SetPath(NULL); 128 return error; 129 } 130 fPathLength = strlen(fBuffer); 131 } else { 132 // don't normalize path 133 size_t length = strlen(path); 134 if (length >= fBufferSize) 135 return B_BUFFER_OVERFLOW; 136 137 memcpy(fBuffer, path, length + 1); 138 fPathLength = length; 139 _ChopTrailingSlashes(); 140 } 141 } else { 142 fBuffer[0] = '\0'; 143 fPathLength = 0; 144 } 145 return B_OK; 146 } 147 148 149 const char* 150 KPath::Path() const 151 { 152 return fBuffer; 153 } 154 155 156 char* 157 KPath::LockBuffer() 158 { 159 if (fBuffer == NULL || fLocked) 160 return NULL; 161 162 fLocked = true; 163 return fBuffer; 164 } 165 166 167 void 168 KPath::UnlockBuffer() 169 { 170 if (!fLocked) { 171 TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n")); 172 return; 173 } 174 fLocked = false; 175 fPathLength = strnlen(fBuffer, fBufferSize); 176 if (fPathLength == fBufferSize) { 177 TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n")); 178 fPathLength--; 179 fBuffer[fPathLength] = '\0'; 180 } 181 _ChopTrailingSlashes(); 182 } 183 184 185 char* 186 KPath::DetachBuffer() 187 { 188 char* buffer = fBuffer; 189 190 if (fBuffer != NULL) { 191 fBuffer = NULL; 192 fBufferSize = 0; 193 fPathLength = 0; 194 fLocked = false; 195 } 196 197 return buffer; 198 } 199 200 201 const char* 202 KPath::Leaf() const 203 { 204 if (fBuffer == NULL) 205 return NULL; 206 207 for (int32 i = fPathLength - 1; i >= 0; i--) { 208 if (fBuffer[i] == '/') 209 return fBuffer + i + 1; 210 } 211 212 return fBuffer; 213 } 214 215 216 status_t 217 KPath::ReplaceLeaf(const char* newLeaf) 218 { 219 const char* leaf = Leaf(); 220 if (leaf == NULL) 221 return B_NO_INIT; 222 223 int32 leafIndex = leaf - fBuffer; 224 // chop off the current leaf (don't replace "/", though) 225 if (leafIndex != 0 || fBuffer[leafIndex - 1]) { 226 fBuffer[leafIndex] = '\0'; 227 fPathLength = leafIndex; 228 _ChopTrailingSlashes(); 229 } 230 231 // if a leaf was given, append it 232 if (newLeaf != NULL) 233 return Append(newLeaf); 234 return B_OK; 235 } 236 237 238 bool 239 KPath::RemoveLeaf() 240 { 241 // get the leaf -- bail out, if not initialized or only the "/" is left 242 const char* leaf = Leaf(); 243 if (leaf == NULL || leaf == fBuffer || leaf[0] == '\0') 244 return false; 245 246 // chop off the leaf 247 int32 leafIndex = leaf - fBuffer; 248 fBuffer[leafIndex] = '\0'; 249 fPathLength = leafIndex; 250 _ChopTrailingSlashes(); 251 252 return true; 253 } 254 255 256 status_t 257 KPath::Append(const char* component, bool isComponent) 258 { 259 // check initialization and parameter 260 if (fBuffer == NULL) 261 return B_NO_INIT; 262 if (component == NULL) 263 return B_BAD_VALUE; 264 if (fPathLength == 0) 265 return SetPath(component); 266 267 // get component length 268 size_t componentLength = strlen(component); 269 if (componentLength < 1) 270 return B_OK; 271 272 // if our current path is empty, we just copy the supplied one 273 // compute the result path len 274 bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/' 275 && component[0] != '/'; 276 size_t resultPathLength = fPathLength + componentLength 277 + (insertSlash ? 1 : 0); 278 if (resultPathLength >= fBufferSize) 279 return B_BUFFER_OVERFLOW; 280 281 // compose the result path 282 if (insertSlash) 283 fBuffer[fPathLength++] = '/'; 284 memcpy(fBuffer + fPathLength, component, componentLength + 1); 285 fPathLength = resultPathLength; 286 return B_OK; 287 } 288 289 290 status_t 291 KPath::Normalize(bool traverseLeafLink) 292 { 293 if (fBuffer == NULL) 294 return B_NO_INIT; 295 if (fPathLength == 0) 296 return B_BAD_VALUE; 297 298 status_t error = vfs_normalize_path(fBuffer, fBuffer, fBufferSize, 299 traverseLeafLink, 300 team_get_kernel_team_id() == team_get_current_team_id()); 301 if (error != B_OK) { 302 // vfs_normalize_path() might have screwed up the previous path -- unset 303 // it completely to avoid weird problems. 304 fBuffer[0] = '\0'; 305 fPathLength = 0; 306 return error; 307 } 308 309 fPathLength = strlen(fBuffer); 310 return B_OK; 311 } 312 313 314 KPath& 315 KPath::operator=(const KPath& other) 316 { 317 SetTo(other.fBuffer, false, other.fBufferSize); 318 return *this; 319 } 320 321 322 KPath& 323 KPath::operator=(const char* path) 324 { 325 SetPath(path); 326 return *this; 327 } 328 329 330 bool 331 KPath::operator==(const KPath& other) const 332 { 333 if (fBuffer == NULL) 334 return !other.fBuffer; 335 336 return other.fBuffer 337 && fPathLength == other.fPathLength 338 && strcmp(fBuffer, other.fBuffer) == 0; 339 } 340 341 342 bool 343 KPath::operator==(const char* path) const 344 { 345 if (fBuffer == NULL) 346 return (!path); 347 348 return path && strcmp(fBuffer, path) == 0; 349 } 350 351 352 bool 353 KPath::operator!=(const KPath& other) const 354 { 355 return !(*this == other); 356 } 357 358 359 bool 360 KPath::operator!=(const char* path) const 361 { 362 return !(*this == path); 363 } 364 365 366 void 367 KPath::_ChopTrailingSlashes() 368 { 369 if (fBuffer != NULL) { 370 while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/') 371 fBuffer[--fPathLength] = '\0'; 372 } 373 } 374 375