1 /* 2 * Copyright 2004-2006, Ingo Weinhold, bonefish@users.sf.net. 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 { 64 if (bufferSize == 0) 65 bufferSize = B_PATH_NAME_LENGTH; 66 67 // free the previous buffer, if the buffer size differs 68 if (fBuffer && fBufferSize != bufferSize) { 69 free(fBuffer); 70 fBuffer = NULL; 71 fBufferSize = 0; 72 } 73 fPathLength = 0; 74 fLocked = false; 75 76 // allocate buffer 77 if (!fBuffer) 78 fBuffer = (char*)malloc(bufferSize); 79 if (!fBuffer) 80 return B_NO_MEMORY; 81 if (fBuffer) { 82 fBufferSize = bufferSize; 83 fBuffer[0] = '\0'; 84 } 85 return SetPath(path, normalize); 86 } 87 88 89 void 90 KPath::Adopt(KPath& other) 91 { 92 free(fBuffer); 93 94 fBuffer = other.fBuffer; 95 fBufferSize = other.fBufferSize; 96 97 other.fBuffer = NULL; 98 } 99 100 101 status_t 102 KPath::InitCheck() const 103 { 104 return fBuffer ? B_OK : B_NO_MEMORY; 105 } 106 107 108 status_t 109 KPath::SetPath(const char *path, bool normalize) 110 { 111 if (!fBuffer) 112 return B_NO_INIT; 113 114 if (path) { 115 if (normalize) { 116 // normalize path 117 status_t error = vfs_normalize_path(path, fBuffer, fBufferSize, 118 team_get_kernel_team_id() == team_get_current_team_id()); 119 if (error != B_OK) { 120 SetPath(NULL); 121 return error; 122 } 123 fPathLength = strlen(fBuffer); 124 } else { 125 // don't normalize path 126 size_t length = strlen(path); 127 if (length >= fBufferSize) 128 return B_BUFFER_OVERFLOW; 129 130 memcpy(fBuffer, path, length + 1); 131 fPathLength = length; 132 _ChopTrailingSlashes(); 133 } 134 } else { 135 fBuffer[0] = '\0'; 136 fPathLength = 0; 137 } 138 return B_OK; 139 } 140 141 142 const char* 143 KPath::Path() const 144 { 145 return fBuffer; 146 } 147 148 149 char * 150 KPath::LockBuffer() 151 { 152 if (!fBuffer || fLocked) 153 return NULL; 154 155 fLocked = true; 156 return fBuffer; 157 } 158 159 160 void 161 KPath::UnlockBuffer() 162 { 163 if (!fLocked) { 164 TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n")); 165 return; 166 } 167 fLocked = false; 168 fPathLength = strnlen(fBuffer, fBufferSize); 169 if (fPathLength == fBufferSize) { 170 TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n")); 171 fPathLength--; 172 fBuffer[fPathLength] = '\0'; 173 } 174 _ChopTrailingSlashes(); 175 } 176 177 178 const char * 179 KPath::Leaf() const 180 { 181 if (!fBuffer) 182 return NULL; 183 184 // only "/" has trailing slashes -- then we have to return the complete 185 // buffer, as we have to do in case there are no slashes at all 186 if (fPathLength != 1 || fBuffer[0] != '/') { 187 for (int32 i = fPathLength - 1; i >= 0; i--) { 188 if (fBuffer[i] == '/') 189 return fBuffer + i + 1; 190 } 191 } 192 return fBuffer; 193 } 194 195 196 status_t 197 KPath::ReplaceLeaf(const char *newLeaf) 198 { 199 const char *leaf = Leaf(); 200 if (!leaf) 201 return B_NO_INIT; 202 203 int32 leafIndex = leaf - fBuffer; 204 // chop off the current leaf (don't replace "/", though) 205 if (leafIndex != 0 || fBuffer[leafIndex - 1]) { 206 fBuffer[leafIndex] = '\0'; 207 fPathLength = leafIndex; 208 _ChopTrailingSlashes(); 209 } 210 211 // if a leaf was given, append it 212 if (newLeaf) 213 return Append(newLeaf); 214 return B_OK; 215 } 216 217 218 bool 219 KPath::RemoveLeaf() 220 { 221 // get the leaf -- bail out, if not initialized or only the "/" is left 222 const char *leaf = Leaf(); 223 if (!leaf || leaf == fBuffer) 224 return false; 225 226 // chop off the leaf 227 int32 leafIndex = leaf - fBuffer; 228 fBuffer[leafIndex] = '\0'; 229 fPathLength = leafIndex; 230 _ChopTrailingSlashes(); 231 232 return true; 233 } 234 235 236 status_t 237 KPath::Append(const char *component, bool isComponent) 238 { 239 // check initialization and parameter 240 if (!fBuffer) 241 return B_NO_INIT; 242 if (!component) 243 return B_BAD_VALUE; 244 if (fPathLength == 0) 245 return SetPath(component); 246 247 // get component length 248 size_t componentLength = strlen(component); 249 if (componentLength < 1) 250 return B_OK; 251 252 // if our current path is empty, we just copy the supplied one 253 // compute the result path len 254 bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/' 255 && component[0] != '/'; 256 size_t resultPathLength = fPathLength + componentLength 257 + (insertSlash ? 1 : 0); 258 if (resultPathLength >= fBufferSize) 259 return B_BUFFER_OVERFLOW; 260 261 // compose the result path 262 if (insertSlash) 263 fBuffer[fPathLength++] = '/'; 264 memcpy(fBuffer + fPathLength, component, componentLength + 1); 265 fPathLength = resultPathLength; 266 return B_OK; 267 } 268 269 270 KPath& 271 KPath::operator=(const KPath& other) 272 { 273 SetTo(other.fBuffer, false, other.fBufferSize); 274 return *this; 275 } 276 277 278 KPath& 279 KPath::operator=(const char* path) 280 { 281 SetTo(path); 282 return *this; 283 } 284 285 286 bool 287 KPath::operator==(const KPath& other) const 288 { 289 if (!fBuffer) 290 return !other.fBuffer; 291 292 return (other.fBuffer 293 && fPathLength == other.fPathLength 294 && strcmp(fBuffer, other.fBuffer) == 0); 295 } 296 297 298 bool 299 KPath::operator==(const char* path) const 300 { 301 if (!fBuffer) 302 return (!path); 303 304 return path && !strcmp(fBuffer, path); 305 } 306 307 308 bool 309 KPath::operator!=(const KPath& other) const 310 { 311 return !(*this == other); 312 } 313 314 315 bool 316 KPath::operator!=(const char* path) const 317 { 318 return !(*this == path); 319 } 320 321 322 void 323 KPath::_ChopTrailingSlashes() 324 { 325 if (fBuffer) { 326 while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/') 327 fBuffer[--fPathLength] = '\0'; 328 } 329 } 330 331