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