summaryrefslogtreecommitdiff
path: root/src/utils/path.cpp
blob: 95db40d31310d0b52f78084ad34d52a6fe79125c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 *  The Mana Server
 *  Copyright (C) 2013  The Mana World Development Team
 *
 *  This file is part of The Mana Server.
 *
 *  The Mana Server is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  The Mana Server is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with The Mana Server.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "utils/path.h"
#include <vector>

namespace utils
{

    /**
     * Returns the filePath sub-part corresponding to the filename only.
     * @return splittedPath: the file path ending with '/' or '\'
     *                       and the file name alone.
     */
    splittedPath splitFileNameAndPath(const std::string &fullFilePath)
    {
        // We'll reversed-search for '/' or'\' and extract the substrings
        // corresponding to the filename and the path separately.
        size_t slashPos = fullFilePath.find_last_of("/\\");

        splittedPath splittedFilePath;
        // Note the last slash is kept in the path name.
        splittedFilePath.path = fullFilePath.substr(0, slashPos + 1);
        splittedFilePath.file = fullFilePath.substr(slashPos + 1);

        return splittedFilePath;
    }

    /**
     * Join two path elements into one.
     *
     * This function helps build relative paths.
     *
     * Examples:
     *
     *     /foo + bar = /foo/bar
     *     /foo/ + bar = /foo/bar
     *     /foo + /bar = /bar
     *
     * This will work for PhysFS paths. Windows style paths (prefixed with drive letters) won't work.
     *
     * @return Joined paths or path2 if path2 was an absolute path.
     */
    std::string joinPaths(const std::string &path1, const std::string &path2)
    {
        if (path2.empty())
            return path1;

        if (path1.empty())
            return path2;

        // check if path2 is an absolute path that cannot be joined
        if (path2[0] == '/' || path2[0] == '\\')
            return path2;

        char p1end = path1[path1.size()-1];
        if (p1end == '/' || p1end == '\\')
        {
            return path1 + path2;
        }
        else
        {
            return path1 + "/" + path2;
        }
    }

    /**
     * Removes relative elements from the path.
     */
    std::string cleanPath(const std::string &path)
    {
        size_t prev, cur;
        std::string part, result;
        std::vector<std::string> pathStack;

        prev = 0;
        while (true)
        {
            cur = path.find_first_of("/\\", prev);
            if (cur == std::string::npos)
            {
                // FIXME add everything from prev to the end
                pathStack.push_back(path.substr(prev));
                break;
            }

            part = path.substr(prev, cur - prev);
            if (part == "..")
            {
                // go back one level
                if (!pathStack.empty())
                {
                    pathStack.pop_back();
                }
            }
            else if (part == ".")
            {
                // do nothing
            }
            else if (part == "")
            {
                if (pathStack.empty() && cur == 0)
                {
                    // handle first empty match before the root slash
                    pathStack.push_back(std::string());
                }
                else
                {
                    // empty match in the middle of the path should be ignored
                }
            }
            else
            {
                // normal path element
                pathStack.push_back(part);
            }

            cur++;
            prev = cur;
        }

        // join the pathStack into a normal path
        unsigned int i = 0;
        for (i = 0; i < pathStack.size(); i++)
        {
            result += pathStack[i];
            if (i < pathStack.size() - 1) {
                result += "/";
            }
        }

        return result;
    }


}