File: path.h

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (262 lines) | stat: -rw-r--r-- 7,659 bytes parent folder | download | duplicates (3)
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.

This file is part of GtkRadiant.

GtkRadiant 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
(at your option) any later version.

GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
#pragma once

/// \file
/// \brief OS file-system path comparison, decomposition and manipulation.
///
/// - Paths are c-style null-terminated-character-arrays.
/// - Path separators must be forward slashes (unix style).
/// - Directory paths must end in a separator.
/// - Paths must not contain the ascii characters \\ : * ? " < > or |.
/// - Paths may be encoded in UTF-8 or any extended-ascii character set.

#include "string/string.h"

#include "string/predicate.h"
#include "string/replace.h"

#if defined(WIN32)
#define OS_CASE_INSENSITIVE
#endif

/// \brief Returns true if the first \p n bytes of \p path and \p other form paths that refer to the same file or directory.
/// If the paths are UTF-8 encoded, [\p path, \p path + \p n) must be a complete path.
/// O(n)
inline bool path_equal_n(const char* path, const char* other, std::size_t n)
{
#if defined(OS_CASE_INSENSITIVE)
  return string_equal_nocase_n(path, other, n);
#else
  return string_equal_n(path, other, n);
#endif
}


/// \brief Returns true if \p path is a fully qualified file-system path.
/// O(1)
inline bool path_is_absolute(const char* path)
{
#if defined(WIN32)
  return path[0] == '/'
    || (path[0] != '\0' && path[1] == ':'); // local drive
#elif defined(POSIX)
  return path[0] == '/';
#endif
}

/// \brief Returns a pointer to the first character of the component of \p path following the first directory component.
/// O(n)
inline const char* path_remove_directory(const char* path)
{
  const char* first_separator = strchr(path, '/');
  if(first_separator != 0)
  {
    return ++first_separator;
  }
  return "";
}

/** General utility functions for OS-related tasks
 */
namespace os
{
    /** Convert the slashes in a Doom 3 path to forward-slashes. Doom 3 accepts either
     * forward or backslashes in its definitions
     */

    inline std::string standardPath(const std::string& inPath)
	{
        return string::replace_all_copy(inPath, "\\", "/");
    }

    /** greebo: OS Folder names have forward slashes and a trailing slash
     * at the end by convention. Empty strings are returned unchanged.
     */
    inline std::string standardPathWithSlash(const std::string& input) 
	{
		std::string output = standardPath(input);

		// Append a slash at the end, if there isn't already one
		if (!output.empty() && !string::ends_with(output, "/"))
		{
			output += "/";
		}
		return output;
	}

    /**
     * Return the path of fullPath relative to basePath, as long as fullPath
     * is contained within basePath. If not, fullPath is returned unchanged.
     */
    inline std::string getRelativePath(const std::string& fullPath,
                                       const std::string& basePath)
    {
#ifdef OS_CASE_INSENSITIVE
		if (string::istarts_with(fullPath, basePath))
#else
		if (string::starts_with(fullPath, basePath))
#endif
		{
			return fullPath.substr(basePath.length());
        }
        else {
            return fullPath;
        }
    }

	/**
	 * stifu: Does the same as getRelativePath, but also strips the filename.
	 */
	inline std::string getRelativePathMinusFilename(const std::string& fullPath,
												 const std::string& basePath)
	{
#ifdef OS_CASE_INSENSITIVE
		if (string::istarts_with(fullPath, basePath))
#else
		if (string::starts_with(fullPath, basePath))
#endif
		{
			return fullPath.substr(basePath.length(), fullPath.rfind('/') - basePath.length());
        }
        else {
            return fullPath;
        }
	}


    /**
     * greebo: Get the filename contained in the given path (the part after the last slash).
     * If there is no filename, an empty string is returned.
	 * If there's no slash, the input string is returned as is.
     *
     * Note: The input string is expected to be standardised (forward slashes).
     */
    inline std::string getFilename(const std::string& path) 
	{
        std::size_t slashPos = path.rfind('/');

		return slashPos == std::string::npos ? path : path.substr(slashPos + 1);
    }

    /**
     * Get the extension of the given filename. If there is no extension, an
     * empty string is returned.
     */
    inline std::string getExtension(const std::string& path) {
        std::size_t dotPos = path.rfind('.');
        if (dotPos == std::string::npos) {
            return "";
        }
        else {
            return path.substr(dotPos + 1);
        }
    }

    /**
     * Strip off the extension of the given path and return the new string. 
     * If there is no extension, the original string is returned.
     */
    inline std::string removeExtension(const std::string& path)
    {
        std::size_t dotPos = path.rfind('.');

        if (dotPos == std::string::npos)
        {
            return path;
        }
        
        return path.substr(0, dotPos);
    }

    /**
     * Get the containing folder of the specified object. This is calculated
     * as the directory before the rightmost slash (which will be the object
     * itself, if the pathname ends in a slash).
     *
     * If the path does not contain a slash, the empty string will be returned.
     *
     * E.g.
     * blah/bleh/file   -> "bleh"
     * blah/bloog/      -> "bloog"
     */
    inline std::string getContainingDir(const std::string& path) {
        std::size_t lastSlash = path.rfind('/');
        if (lastSlash == std::string::npos) {
            return "";
        }
        std::string trimmed = path.substr(0, lastSlash);
        lastSlash = trimmed.rfind('/');
        return trimmed.substr(lastSlash + 1);
    }

	/**
	 * Returns the directory part of the given path, cutting off the filename
	 * right after the last slash. Use standard paths (forward slashes) only!
	 *
	 * E.g.
	 * blah/bleh/file.ext   -> "blah/bleh/"
	 * blah/bloog			-> "blah/"
	 */
	inline std::string getDirectory(const std::string& path)
	{
		std::size_t lastSlash = path.rfind('/');

		if (lastSlash == std::string::npos)
		{
			return path;
		}

		return path.substr(0, lastSlash + 1);
	}

    /**
     * Returns the outermost directory name of the given path, which must be
     * provided in standardised form (forward slashes only).
     * 
     * E.g. 
     * blah/bleh/file.ext   -> "blah/"
	 * blah/bloog           -> "blah/"
     * blah                 -> ""
     * test.mtr             -> ""
     * ""                   -> ""
     */
    inline std::string getToplevelDirectory(const std::string& path)
    {
        std::size_t firstSlash = path.find('/');

        if (firstSlash == std::string::npos)
        {
            return std::string();
        }

        return path.substr(0, firstSlash + 1); // include the slash
    }

	/**
	 * Returns true if the given string qualifies as path to a directory,
	 * which is equal to the path string ending with the slash '/' character.
	 */
	inline bool isDirectory(const std::string& path)
	{
		return !path.empty() && path.back() == '/';
	}
}