/* * Copyright 2002-2012, Haiku Inc. * Distributed under the terms of the MIT License. * * Authors: * Tyler Dauwalder * Axel Dörfler, axeld@pinc-software.de * Ingo Weinhold, bonefish@users.sf.net */ #include #include #include #include #include #include #include #include "storage_support.h" using namespace std; // Creates an uninitialized BPath object. BPath::BPath() : fName(NULL), fCStatus(B_NO_INIT) { } // Creates a copy of the given BPath object. BPath::BPath(const BPath& path) : fName(NULL), fCStatus(B_NO_INIT) { *this = path; } // Creates a BPath object and initializes it to the filesystem entry // specified by the passed in entry_ref struct. BPath::BPath(const entry_ref* ref) : fName(NULL), fCStatus(B_NO_INIT) { SetTo(ref); } // Creates a BPath object and initializes it to the filesystem entry // specified by the passed in BEntry object. BPath::BPath(const BEntry* entry) : fName(NULL), fCStatus(B_NO_INIT) { SetTo(entry); } // Creates a BPath object and initializes it to the specified path or // path and filename combination. BPath::BPath(const char* dir, const char* leaf, bool normalize) : fName(NULL), fCStatus(B_NO_INIT) { SetTo(dir, leaf, normalize); } // Creates a BPath object and initializes it to the specified directory // and filename combination. BPath::BPath(const BDirectory* dir, const char* leaf, bool normalize) : fName(NULL), fCStatus(B_NO_INIT) { SetTo(dir, leaf, normalize); } // Destroys the BPath object and frees any of its associated resources. BPath::~BPath() { Unset(); } // Checks whether or not the object was properly initialized. status_t BPath::InitCheck() const { return fCStatus; } // Reinitializes the object to the filesystem entry specified by the // passed in entry_ref struct. status_t BPath::SetTo(const entry_ref* ref) { Unset(); if (!ref) return fCStatus = B_BAD_VALUE; char path[B_PATH_NAME_LENGTH]; fCStatus = _kern_entry_ref_to_path(ref->device, ref->directory, ref->name, path, sizeof(path)); if (fCStatus != B_OK) return fCStatus; fCStatus = _SetPath(path); // the path is already normalized return fCStatus; } // Reinitializes the object to the specified filesystem entry. status_t BPath::SetTo(const BEntry* entry) { Unset(); if (entry == NULL) return B_BAD_VALUE; entry_ref ref; fCStatus = entry->GetRef(&ref); if (fCStatus == B_OK) fCStatus = SetTo(&ref); return fCStatus; } // Reinitializes the object to the passed in path or path and // leaf combination. status_t BPath::SetTo(const char* path, const char* leaf, bool normalize) { status_t error = (path ? B_OK : B_BAD_VALUE); if (error == B_OK && leaf && BPrivate::Storage::is_absolute_path(leaf)) error = B_BAD_VALUE; char newPath[B_PATH_NAME_LENGTH]; if (error == B_OK) { // we always normalize relative paths normalize |= !BPrivate::Storage::is_absolute_path(path); // build a new path from path and leaf // copy path first uint32 pathLen = strlen(path); if (pathLen >= sizeof(newPath)) error = B_NAME_TOO_LONG; if (error == B_OK) strcpy(newPath, path); // append leaf, if supplied if (error == B_OK && leaf) { bool needsSeparator = (pathLen > 0 && path[pathLen - 1] != '/'); uint32 wholeLen = pathLen + (needsSeparator ? 1 : 0) + strlen(leaf); if (wholeLen >= sizeof(newPath)) error = B_NAME_TOO_LONG; if (error == B_OK) { if (needsSeparator) { newPath[pathLen] = '/'; pathLen++; } strcpy(newPath + pathLen, leaf); } } // check, if necessary to normalize if (error == B_OK && !normalize) normalize = normalize || _MustNormalize(newPath, &error); // normalize the path, if necessary, otherwise just set it if (error == B_OK) { if (normalize) { // create a BEntry and initialize us with this entry BEntry entry; error = entry.SetTo(newPath, false); if (error == B_OK) return SetTo(&entry); } else error = _SetPath(newPath); } } // cleanup, if something went wrong if (error != B_OK) Unset(); fCStatus = error; return error; } // Reinitializes the object to the passed in dir and relative path combination. status_t BPath::SetTo(const BDirectory* dir, const char* path, bool normalize) { status_t error = (dir && dir->InitCheck() == B_OK ? B_OK : B_BAD_VALUE); // get the path of the BDirectory BEntry entry; if (error == B_OK) error = dir->GetEntry(&entry); BPath dirPath; if (error == B_OK) error = dirPath.SetTo(&entry); // let the other version do the work if (error == B_OK) error = SetTo(dirPath.Path(), path, normalize); if (error != B_OK) Unset(); fCStatus = error; return error; } // Returns the object to an uninitialized state. void BPath::Unset() { _SetPath(NULL); fCStatus = B_NO_INIT; } // Appends the passed in relative path to the end of the current path. status_t BPath::Append(const char* path, bool normalize) { status_t error = (InitCheck() == B_OK ? B_OK : B_BAD_VALUE); if (error == B_OK) error = SetTo(Path(), path, normalize); if (error != B_OK) Unset(); fCStatus = error; return error; } // Gets the entire path of the object. const char* BPath::Path() const { return fName; } // Gets the leaf portion of the path. const char* BPath::Leaf() const { if (InitCheck() != B_OK) return NULL; const char* result = fName + strlen(fName); // There should be no need for the second condition, since we deal // with absolute paths only and those contain at least one '/'. // However, it doesn't harm. while (*result != '/' && result > fName) result--; result++; return result; } // Initializes path with the parent directory of the BPath object. status_t BPath::GetParent(BPath* path) const { if (path == NULL) return B_BAD_VALUE; status_t error = InitCheck(); if (error != B_OK) return error; int32 length = strlen(fName); if (length == 1) { // handle "/" (path is supposed to be absolute) return B_ENTRY_NOT_FOUND; } char parentPath[B_PATH_NAME_LENGTH]; length--; while (fName[length] != '/' && length > 0) length--; if (length == 0) { // parent dir is "/" length++; } memcpy(parentPath, fName, length); parentPath[length] = '\0'; return path->SetTo(parentPath); } // Gets whether or not the path is absolute or relative. bool BPath::IsAbsolute() const { if (InitCheck() != B_OK) return false; return fName[0] == '/'; } // Performs a simple (string-wise) comparison of paths for equality. bool BPath::operator==(const BPath& item) const { return *this == item.Path(); } // Performs a simple (string-wise) comparison of paths for equality. bool BPath::operator==(const char* path) const { return (InitCheck() != B_OK && path == NULL) || (fName != NULL && path != NULL && strcmp(fName, path) == 0); } // Performs a simple (string-wise) comparison of paths for inequality. bool BPath::operator!=(const BPath& item) const { return !(*this == item); } // Performs a simple (string-wise) comparison of paths for inequality. bool BPath::operator!=(const char* path) const { return !(*this == path); } // Initializes the object as a copy of item. BPath& BPath::operator=(const BPath& item) { if (this != &item) *this = item.Path(); return *this; } // Initializes the object with the passed in path. BPath& BPath::operator=(const char* path) { if (path == NULL) Unset(); else SetTo(path); return *this; } // #pragma mark - BFlattenable functionality // that's the layout of a flattened entry_ref struct flattened_entry_ref { dev_t device; ino_t directory; char name[1]; }; // base size of a flattened entry ref static const size_t flattened_entry_ref_size = sizeof(dev_t) + sizeof(ino_t); // Overrides BFlattenable::IsFixedSize() bool BPath::IsFixedSize() const { return false; } // Overrides BFlattenable::TypeCode() type_code BPath::TypeCode() const { return B_REF_TYPE; } // Gets the size of the flattened entry_ref struct that represents // the path in bytes. ssize_t BPath::FlattenedSize() const { ssize_t size = flattened_entry_ref_size; BEntry entry; entry_ref ref; if (InitCheck() == B_OK && entry.SetTo(Path()) == B_OK && entry.GetRef(&ref) == B_OK) { size += strlen(ref.name) + 1; } return size; } // Converts the path of the object to an entry_ref and writes it into buffer. status_t BPath::Flatten(void* buffer, ssize_t size) const { if (buffer == NULL) return B_BAD_VALUE; // ToDo: Re-implement for performance reasons: Don't call FlattenedSize(). ssize_t flattenedSize = FlattenedSize(); if (flattenedSize < 0) return flattenedSize; if (size < flattenedSize) return B_BAD_VALUE; status_t status = InitCheck(); if (status != B_OK) return status; // convert the path to an entry_ref BEntry entry; entry_ref ref; status = entry.SetTo(Path()); if (status == B_OK) status = entry.GetRef(&ref); if (status != B_OK) return status; // store the entry_ref in the buffer flattened_entry_ref& fref = *(flattened_entry_ref*)buffer; fref.device = ref.device; fref.directory = ref.directory; if (ref.name) strcpy(fref.name, ref.name); return B_OK; } // Checks if type code is equal to B_REF_TYPE. bool BPath::AllowsTypeCode(type_code code) const { return code == B_REF_TYPE; } // Initializes the object with the flattened entry_ref data from the passed // in buffer. status_t BPath::Unflatten(type_code code, const void* buffer, ssize_t size) { Unset(); status_t error = B_OK; // check params if (!(code == B_REF_TYPE && buffer != NULL && size >= (ssize_t)flattened_entry_ref_size)) { error = B_BAD_VALUE; } if (error == B_OK) { if (size == (ssize_t)flattened_entry_ref_size) { // already Unset(); } else { // reconstruct the entry_ref from the buffer const flattened_entry_ref& fref = *(const flattened_entry_ref*)buffer; BString name(fref.name, size - flattened_entry_ref_size); entry_ref ref(fref.device, fref.directory, name.String()); error = SetTo(&ref); } } if (error != B_OK) fCStatus = error; return error; } void BPath::_WarPath1() {} void BPath::_WarPath2() {} void BPath::_WarPath3() {} // Sets the supplied path. status_t BPath::_SetPath(const char* path) { status_t error = B_OK; const char* oldPath = fName; // set the new path if (path) { fName = new(nothrow) char[strlen(path) + 1]; if (fName) strcpy(fName, path); else error = B_NO_MEMORY; } else fName = NULL; // delete the old one delete[] oldPath; return error; } // Checks a path to see if normalization is required. bool BPath::_MustNormalize(const char* path, status_t* _error) { // Check for useless input if (path == NULL || path[0] == 0) { if (_error != NULL) *_error = B_BAD_VALUE; return false; } int len = strlen(path); /* Look for anything in the string that forces us to normalize: + No leading / + any occurence of /./ or /../ or //, or a trailing /. or /.. + a trailing / */; if (path[0] != '/') return true; // not "/*" else if (len == 1) return false; // "/" else if (len > 1 && path[len-1] == '/') return true; // "*/" else { enum ParseState { NoMatch, InitialSlash, OneDot, TwoDots } state = NoMatch; for (int i = 0; path[i] != 0; i++) { switch (state) { case NoMatch: if (path[i] == '/') state = InitialSlash; break; case InitialSlash: if (path[i] == '/') return true; // "*//*" if (path[i] == '.') state = OneDot; else state = NoMatch; break; case OneDot: if (path[i] == '/') return true; // "*/./*" if (path[i] == '.') state = TwoDots; else state = NoMatch; break; case TwoDots: if (path[i] == '/') return true; // "*/../*" state = NoMatch; break; } } // If we hit the end of the string while in either // of these two states, there was a trailing /. or /.. if (state == OneDot || state == TwoDots) return true; return false; } }