root/src/servers/package/FSUtils.h
/*
 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
 * 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);
                        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(
                                                                        const 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