* Move Volume::Exception to top level and into own files.
* Add utility class FSUtils, move Volume::RelativePath there and add a
bunch of FS utility functionality.
* Add FSTransaction, a helper class to record FS operations and revert
them.
* When activating a package we now extract the writable
files/directories it declares. The handling is not quite complete:
- We don't handle merges yet. I.e. the user will have to do that
manually for now.
- We don't propagate issues/infos regarding the writable files (e.g.
that a manual intervention is required) to the user yet.
230 lines
4.8 KiB
C++
230 lines
4.8 KiB
C++
/*
|
|
* Copyright 2013, Ingo Weinhold, [email protected].
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
#ifndef FS_UTILS_H
|
|
#define FS_UTILS_H
|
|
|
|
|
|
#include <new>
|
|
|
|
#include <String.h>
|
|
|
|
#include <EntryOperationEngineBase.h>
|
|
|
|
|
|
class BDirectory;
|
|
class BFile;
|
|
class BPositionIO;
|
|
class BSymLink;
|
|
|
|
|
|
class FSUtils {
|
|
public:
|
|
typedef ::BPrivate::BEntryOperationEngineBase::Entry Entry;
|
|
|
|
public:
|
|
struct RelativePath {
|
|
RelativePath(const char* component1 = NULL,
|
|
const char* component2 = NULL, const char* component3 = NULL)
|
|
:
|
|
fComponentCount(kMaxComponentCount)
|
|
{
|
|
fComponents[0] = component1;
|
|
fComponents[1] = component2;
|
|
fComponents[2] = component3;
|
|
|
|
for (size_t i = 0; i < kMaxComponentCount; i++) {
|
|
if (fComponents[i] == NULL) {
|
|
fComponentCount = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IsEmpty() const
|
|
{
|
|
return fComponentCount == 0;
|
|
}
|
|
|
|
RelativePath HeadPath(size_t componentsToDropCount = 1)
|
|
{
|
|
RelativePath result;
|
|
if (componentsToDropCount < fComponentCount) {
|
|
result.fComponentCount
|
|
= fComponentCount - componentsToDropCount;
|
|
for (size_t i = 0; i < result.fComponentCount; i++)
|
|
result.fComponents[i] = fComponents[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
const char* LastComponent() const
|
|
{
|
|
return fComponentCount > 0
|
|
? fComponents[fComponentCount - 1] : NULL;
|
|
}
|
|
|
|
BString ToString() const
|
|
{
|
|
if (fComponentCount == 0)
|
|
return BString();
|
|
|
|
size_t length = fComponentCount - 1;
|
|
for (size_t i = 0; i < fComponentCount; i++)
|
|
length += strlen(fComponents[i]);
|
|
|
|
BString result;
|
|
char* buffer = result.LockBuffer(length + 1);
|
|
if (buffer == NULL)
|
|
return BString();
|
|
|
|
for (size_t i = 0; i < fComponentCount; i++) {
|
|
if (i > 0) {
|
|
*buffer = '/';
|
|
buffer++;
|
|
}
|
|
strcpy(buffer, fComponents[i]);
|
|
buffer += strlen(buffer);
|
|
}
|
|
|
|
return result.UnlockBuffer();
|
|
}
|
|
|
|
private:
|
|
static const size_t kMaxComponentCount = 3;
|
|
|
|
const char* fComponents[kMaxComponentCount];
|
|
size_t fComponentCount;
|
|
};
|
|
|
|
// throwing std::bad_alloc()
|
|
class Path {
|
|
public:
|
|
Path(const char* path)
|
|
:
|
|
fPath(path)
|
|
{
|
|
if (fPath.IsEmpty()) {
|
|
if (path[0] != '\0')
|
|
throw std::bad_alloc();
|
|
} else {
|
|
// remove duplicate '/'s
|
|
char* buffer = fPath.LockBuffer(fPath.Length());
|
|
int32 k = 0;
|
|
for (int32 i = 0; buffer[i] != '\0'; i++) {
|
|
if (buffer[i] == '/' && k > 0 && buffer[k - 1] == '/')
|
|
continue;
|
|
buffer[k++] = buffer[i];
|
|
}
|
|
|
|
// remove trailing '/'
|
|
if (k > 1 && buffer[k - 1] == '/')
|
|
k--;
|
|
|
|
fPath.LockBuffer(k);
|
|
}
|
|
}
|
|
|
|
Path& AppendComponent(const char* component)
|
|
{
|
|
if (fPath.IsEmpty()) {
|
|
fPath = component;
|
|
if (fPath.IsEmpty() && component[0] != '\0')
|
|
throw std::bad_alloc();
|
|
} else {
|
|
int32 length = fPath.Length();
|
|
if (fPath[length - 1] != '/') {
|
|
fPath += '/';
|
|
if (++length != fPath.Length())
|
|
throw std::bad_alloc();
|
|
}
|
|
|
|
fPath += component;
|
|
if (fPath.Length() <= length)
|
|
throw std::bad_alloc();
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
Path& RemoveLastComponent()
|
|
{
|
|
int32 index = fPath.FindLast('/');
|
|
if (index < 0 || (index == 0 && fPath.Length() == 1))
|
|
fPath.Truncate(0);
|
|
else if (index == 0)
|
|
fPath.Truncate(1);
|
|
else
|
|
fPath.Truncate(index - 1);
|
|
return *this;
|
|
}
|
|
|
|
const char* Leaf() const
|
|
{
|
|
int32 index = fPath.FindLast('/');
|
|
if (index < 0 || (index == 0 && fPath.Length() == 1))
|
|
return fPath.String();
|
|
return fPath.String() + index + 1;
|
|
}
|
|
|
|
const BString ToString() const
|
|
{
|
|
return fPath;
|
|
}
|
|
|
|
const char* ToCString() const
|
|
{
|
|
return fPath.String();
|
|
}
|
|
|
|
operator const BString&() const
|
|
{
|
|
return fPath;
|
|
}
|
|
|
|
operator const char*() const
|
|
{
|
|
return fPath;
|
|
}
|
|
|
|
private:
|
|
BString fPath;
|
|
};
|
|
|
|
public:
|
|
static BString ShellEscapeString(const BString& string);
|
|
// throw std::bad_alloc
|
|
|
|
static status_t OpenSubDirectory(BDirectory& baseDirectory,
|
|
const RelativePath& path, bool create,
|
|
BDirectory& _directory);
|
|
|
|
static status_t CompareFileContent(const Entry& entry1,
|
|
const Entry& entry2, bool& _equal);
|
|
static status_t CompareFileContent(BPositionIO& content1,
|
|
BPositionIO& content2, bool& _equal);
|
|
|
|
static status_t CompareSymLinks(const Entry& entry1,
|
|
const Entry& entry2, bool& _equal);
|
|
static status_t CompareSymLinks(BSymLink& symLink1,
|
|
BSymLink& symLink2, bool& _equal);
|
|
|
|
static status_t ExtractPackageContent(const Entry& packageEntry,
|
|
const char* contentPath,
|
|
const Entry& targetDirectoryEntry);
|
|
static status_t ExtractPackageContent(const char* packagePath,
|
|
const char* contentPath,
|
|
const char* targetDirectoryPath);
|
|
|
|
private:
|
|
static status_t _OpenFile(const Entry& entry, BFile& file);
|
|
static status_t _OpenSymLink(const Entry& entry,
|
|
BSymLink& symLink);
|
|
};
|
|
|
|
|
|
|
|
#endif // FS_UTILS_H
|