#include <Path.h>
#include <new>
#include <Directory.h>
#include <Entry.h>
#include <StorageDefs.h>
#include <String.h>
#include <syscalls.h>
#include "storage_support.h"
using namespace std;
BPath::BPath()
:
fName(NULL),
fCStatus(B_NO_INIT)
{
}
BPath::BPath(const BPath& path)
:
fName(NULL),
fCStatus(B_NO_INIT)
{
*this = path;
}
BPath::BPath(const entry_ref* ref)
:
fName(NULL),
fCStatus(B_NO_INIT)
{
SetTo(ref);
}
BPath::BPath(const BEntry* entry)
:
fName(NULL),
fCStatus(B_NO_INIT)
{
SetTo(entry);
}
BPath::BPath(const char* dir, const char* leaf, bool normalize)
:
fName(NULL),
fCStatus(B_NO_INIT)
{
SetTo(dir, leaf, normalize);
}
BPath::BPath(const BDirectory* dir, const char* leaf, bool normalize)
:
fName(NULL),
fCStatus(B_NO_INIT)
{
SetTo(dir, leaf, normalize);
}
BPath::~BPath()
{
Unset();
}
status_t
BPath::InitCheck() const
{
return fCStatus;
}
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);
return fCStatus;
}
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;
}
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) {
normalize |= !BPrivate::Storage::is_absolute_path(path);
uint32 pathLen = strlen(path);
if (pathLen >= sizeof(newPath))
error = B_NAME_TOO_LONG;
if (error == B_OK)
strcpy(newPath, path);
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);
}
}
if (error == B_OK && !normalize)
normalize = _MustNormalize(newPath, &error);
if (error == B_OK) {
if (normalize) {
BEntry entry;
error = entry.SetTo(newPath, false);
if (error == B_OK)
return SetTo(&entry);
} else
error = _SetPath(newPath);
}
}
if (error != B_OK)
Unset();
fCStatus = error;
return error;
}
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);
BEntry entry;
if (error == B_OK)
error = dir->GetEntry(&entry);
BPath dirPath;
if (error == B_OK)
error = dirPath.SetTo(&entry);
if (error == B_OK)
error = SetTo(dirPath.Path(), path, normalize);
if (error != B_OK)
Unset();
fCStatus = error;
return error;
}
void
BPath::Unset()
{
_SetPath(NULL);
fCStatus = B_NO_INIT;
}
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;
}
const char*
BPath::Path() const
{
return fName;
}
const char*
BPath::Leaf() const
{
if (InitCheck() != B_OK)
return NULL;
const char* result = fName + strlen(fName);
while (*result != '/' && result > fName)
result--;
result++;
return result;
}
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) {
return B_ENTRY_NOT_FOUND;
}
char parentPath[B_PATH_NAME_LENGTH];
length--;
while (fName[length] != '/' && length > 0)
length--;
if (length == 0) {
length++;
}
memcpy(parentPath, fName, length);
parentPath[length] = '\0';
return path->SetTo(parentPath);
}
bool
BPath::IsAbsolute() const
{
if (InitCheck() != B_OK)
return false;
return fName[0] == '/';
}
bool
BPath::operator==(const BPath& item) const
{
return *this == item.Path();
}
bool
BPath::operator==(const char* path) const
{
return (InitCheck() != B_OK && path == NULL)
|| (fName != NULL && path != NULL && strcmp(fName, path) == 0);
}
bool
BPath::operator!=(const BPath& item) const
{
return !(*this == item);
}
bool
BPath::operator!=(const char* path) const
{
return !(*this == path);
}
BPath&
BPath::operator=(const BPath& item)
{
if (this != &item)
*this = item.Path();
return *this;
}
BPath&
BPath::operator=(const char* path)
{
if (path == NULL)
Unset();
else
SetTo(path);
return *this;
}
struct flattened_entry_ref {
dev_t device;
ino_t directory;
char name[1];
};
static const size_t flattened_entry_ref_size
= sizeof(dev_t) + sizeof(ino_t);
bool
BPath::IsFixedSize() const
{
return false;
}
type_code
BPath::TypeCode() const
{
return B_REF_TYPE;
}
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;
}
status_t
BPath::Flatten(void* buffer, ssize_t size) const
{
if (buffer == NULL)
return B_BAD_VALUE;
ssize_t flattenedSize = FlattenedSize();
if (flattenedSize < 0)
return flattenedSize;
if (size < flattenedSize)
return B_BAD_VALUE;
BEntry entry;
entry_ref ref;
if (Path() != NULL) {
status_t status = entry.SetTo(Path());
if (status == B_OK)
status = entry.GetRef(&ref);
if (status != B_OK)
return status;
}
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;
}
bool
BPath::AllowsTypeCode(type_code code) const
{
return code == B_REF_TYPE;
}
status_t
BPath::Unflatten(type_code code, const void* buffer, ssize_t size)
{
Unset();
status_t error = B_OK;
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) {
} else {
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() {}
status_t
BPath::_SetPath(const char* path)
{
status_t error = B_OK;
const char* oldPath = fName;
if (path) {
fName = new(nothrow) char[strlen(path) + 1];
if (fName)
strcpy(fName, path);
else
error = B_NO_MEMORY;
} else
fName = NULL;
delete[] oldPath;
return error;
}
bool
BPath::_MustNormalize(const char* path, status_t* _error)
{
if (path == NULL || path[0] == 0) {
if (_error != NULL)
*_error = B_BAD_VALUE;
return false;
}
int len = strlen(path);
;
if (path[0] != '/')
return true;
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 (state == OneDot || state == TwoDots)
return true;
return false;
}
}