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