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 21 KPath::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 32 KPath::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 43 KPath::KPath(const KPath& other) 44 : 45 fBuffer(NULL), 46 fBufferSize(0), 47 fPathLength(0), 48 fLocked(false) 49 { 50 *this = other; 51 } 52 53 54 KPath::~KPath() 55 { 56 free(fBuffer); 57 } 58 59 60 fssh_status_t 61 KPath::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 88 fssh_status_t 89 KPath::InitCheck() const 90 { 91 return fBuffer ? FSSH_B_OK : FSSH_B_NO_MEMORY; 92 } 93 94 95 fssh_status_t 96 KPath::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 129 const char* 130 KPath::Path() const 131 { 132 return fBuffer; 133 } 134 135 136 char * 137 KPath::LockBuffer() 138 { 139 if (!fBuffer || fLocked) 140 return NULL; 141 142 fLocked = true; 143 return fBuffer; 144 } 145 146 147 void 148 KPath::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 165 const char * 166 KPath::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 183 fssh_status_t 184 KPath::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 205 fssh_status_t 206 KPath::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 238 KPath& 239 KPath::operator=(const KPath& other) 240 { 241 SetTo(other.fBuffer, other.fBufferSize); 242 return *this; 243 } 244 245 246 KPath& 247 KPath::operator=(const char* path) 248 { 249 SetTo(path); 250 return *this; 251 } 252 253 254 bool 255 KPath::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 266 bool 267 KPath::operator==(const char* path) const 268 { 269 if (!fBuffer) 270 return (!path); 271 272 return path && !fssh_strcmp(fBuffer, path); 273 } 274 275 276 bool 277 KPath::operator!=(const KPath& other) const 278 { 279 return !(*this == other); 280 } 281 282 283 bool 284 KPath::operator!=(const char* path) const 285 { 286 return !(*this == path); 287 } 288 289 290 void 291 KPath::_ChopTrailingSlashes() 292 { 293 if (fBuffer) { 294 while (fPathLength > 1 && fBuffer[fPathLength - 1] == '/') 295 fBuffer[--fPathLength] = '\0'; 296 } 297 } 298 299