1/* 2 * Copyright 2004-2007, 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 "KPath.h" 9 10#include <stdlib.h> 11 12#include "fssh_string.h" 13#include "vfs.h" 14 15 16// debugging 17#define TRACE(x) ; 18//#define TRACE(x) dprintf x 19 20 21KPath::KPath(fssh_size_t bufferSize) 22 : 23 fBuffer(NULL), 24 fBufferSize(0), 25 fPathLength(0), 26 fLocked(false) 27{ 28 SetTo(NULL, bufferSize); 29} 30 31 32KPath::KPath(const char* path, bool normalize, fssh_size_t bufferSize) 33 : 34 fBuffer(NULL), 35 fBufferSize(0), 36 fPathLength(0), 37 fLocked(false) 38{ 39 SetTo(path, normalize, bufferSize); 40} 41 42 43KPath::KPath(const KPath& other) 44 : 45 fBuffer(NULL), 46 fBufferSize(0), 47 fPathLength(0), 48 fLocked(false) 49{ 50 *this = other; 51} 52 53 54KPath::~KPath() 55{ 56 free(fBuffer); 57} 58 59 60fssh_status_t 61KPath::SetTo(const char* path, bool normalize, fssh_size_t bufferSize) 62{ 63 if (bufferSize == 0) 64 bufferSize = FSSH_B_PATH_NAME_LENGTH; 65 66 // free the previous buffer, if the buffer size differs 67 if (fBuffer && fBufferSize != bufferSize) { 68 free(fBuffer); 69 fBuffer = NULL; 70 fBufferSize = 0; 71 } 72 fPathLength = 0; 73 fLocked = false; 74 75 // allocate buffer 76 if (!fBuffer) 77 fBuffer = (char*)malloc(bufferSize); 78 if (!fBuffer) 79 return FSSH_B_NO_MEMORY; 80 if (fBuffer) { 81 fBufferSize = bufferSize; 82 fBuffer[0] = '\0'; 83 } 84 return SetPath(path, normalize); 85} 86 87 88fssh_status_t 89KPath::InitCheck() const 90{ 91 return fBuffer ? FSSH_B_OK : FSSH_B_NO_MEMORY; 92} 93 94 95fssh_status_t 96KPath::SetPath(const char *path, bool normalize) 97{ 98 if (!fBuffer) 99 return FSSH_B_NO_INIT; 100 101 if (path) { 102 if (normalize) { 103 // normalize path 104 fssh_status_t error = vfs_normalize_path(path, fBuffer, fBufferSize, 105 true); 106 if (error != FSSH_B_OK) { 107 SetPath(NULL); 108 return error; 109 } 110 fPathLength = fssh_strlen(fBuffer); 111 } else { 112 // don't normalize path 113 fssh_size_t length = fssh_strlen(path); 114 if (length >= fBufferSize) 115 return FSSH_B_BUFFER_OVERFLOW; 116 117 fssh_memcpy(fBuffer, path, length + 1); 118 fPathLength = length; 119 _ChopTrailingSlashes(); 120 } 121 } else { 122 fBuffer[0] = '\0'; 123 fPathLength = 0; 124 } 125 return FSSH_B_OK; 126} 127 128 129const char* 130KPath::Path() const 131{ 132 return fBuffer; 133} 134 135 136char * 137KPath::LockBuffer() 138{ 139 if (!fBuffer || fLocked) 140 return NULL; 141 142 fLocked = true; 143 return fBuffer; 144} 145 146 147void 148KPath::UnlockBuffer() 149{ 150 if (!fLocked) { 151 TRACE(("KPath::UnlockBuffer(): ERROR: Buffer not locked!\n")); 152 return; 153 } 154 fLocked = false; 155 fPathLength = fssh_strnlen(fBuffer, fBufferSize); 156 if (fPathLength == fBufferSize) { 157 TRACE(("KPath::UnlockBuffer(): WARNING: Unterminated buffer!\n")); 158 fPathLength--; 159 fBuffer[fPathLength] = '\0'; 160 } 161 _ChopTrailingSlashes(); 162} 163 164 165const char * 166KPath::Leaf() const 167{ 168 if (!fBuffer) 169 return NULL; 170 171 // only "/" has trailing slashes -- then we have to return the complete 172 // buffer, as we have to do in case there are no slashes at all 173 if (fPathLength != 1 || fBuffer[0] != '/') { 174 for (int32_t i = fPathLength - 1; i >= 0; i--) { 175 if (fBuffer[i] == '/') 176 return fBuffer + i + 1; 177 } 178 } 179 return fBuffer; 180} 181 182 183fssh_status_t 184KPath::ReplaceLeaf(const char *newLeaf) 185{ 186 const char *leaf = Leaf(); 187 if (!leaf) 188 return FSSH_B_NO_INIT; 189 190 int32_t leafIndex = leaf - fBuffer; 191 // chop off the current leaf (don't replace "/", though) 192 if (leafIndex != 0 || fBuffer[leafIndex - 1]) { 193 fBuffer[leafIndex] = '\0'; 194 fPathLength = leafIndex; 195 _ChopTrailingSlashes(); 196 } 197 198 // if a leaf was given, append it 199 if (newLeaf) 200 return Append(newLeaf); 201 return FSSH_B_OK; 202} 203 204 205fssh_status_t 206KPath::Append(const char *component, bool isComponent) 207{ 208 // check initialization and parameter 209 if (!fBuffer) 210 return FSSH_B_NO_INIT; 211 if (!component) 212 return FSSH_B_BAD_VALUE; 213 if (fPathLength == 0) 214 return SetPath(component); 215 216 // get component length 217 fssh_size_t componentLength = fssh_strlen(component); 218 if (componentLength < 1) 219 return FSSH_B_OK; 220 221 // if our current path is empty, we just copy the supplied one 222 // compute the result path len 223 bool insertSlash = isComponent && fBuffer[fPathLength - 1] != '/' 224 && component[0] != '/'; 225 fssh_size_t resultPathLength = fPathLength + componentLength + (insertSlash ? 1 : 0); 226 if (resultPathLength >= fBufferSize) 227 return FSSH_B_BUFFER_OVERFLOW; 228 229 // compose the result path 230 if (insertSlash) 231 fBuffer[fPathLength++] = '/'; 232 fssh_memcpy(fBuffer + fPathLength, component, componentLength + 1); 233 fPathLength = resultPathLength; 234 return FSSH_B_OK; 235} 236 237 238KPath& 239KPath::operator=(const KPath& other) 240{ 241 SetTo(other.fBuffer, other.fBufferSize); 242 return *this; 243} 244 245 246KPath& 247KPath::operator=(const char* path) 248{ 249 SetTo(path); 250 return *this; 251} 252 253 254bool 255KPath::operator==(const KPath& other) const 256{ 257 if (!fBuffer) 258 return !other.fBuffer; 259 260 return (other.fBuffer 261 && fPathLength == other.fPathLength 262 && fssh_strcmp(fBuffer, other.fBuffer) == 0); 263} 264 265 266bool 267KPath::operator==(const char* path) const 268{ 269 if (!fBuffer) 270 return (!path); 271 272 return path && !fssh_strcmp(fBuffer, path); 273} 274 275 276bool 277KPath::operator!=(const KPath& other) const 278{ 279 return !(*this == other); 280} 281 282 283bool 284KPath::operator!=(const char* path) const 285{ 286 return !(*this == path); 287} 288 289 290void 291KPath::_ChopTrailingSlashes() 292{ 293 if (fBuffer) { 294 while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/') 295 fBuffer[--fPathLength] = '\0'; 296 } 297} 298 299