1 /*
2  * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3  *
4  * This file is part of Arx Libertatis.
5  *
6  * Arx Libertatis is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Arx Libertatis is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Arx Libertatis.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef ARX_IO_RESOURCE_RESOURCEPATH_H
21 #define ARX_IO_RESOURCE_RESOURCEPATH_H
22 
23 #include <string>
24 #include <ostream>
25 
26 namespace res {
27 
28 class path {
29 
30 private:
31 
32 	std::string pathstr;
33 
34 #ifdef ARX_DEBUG
35 	void check() const;
36 #else
check()37 	inline void check() const { }
38 #endif
39 
40 	static path resolve(const path & base, const path & branch);
41 
42 public:
43 
44 	static const char dir_or_ext_sep[];
45 	static const char dir_sep = '/';
46 	static const char ext_sep = '.';
47 
path()48 	path() { }
path(const path & other)49 	path(const path & other) : pathstr(other.pathstr) { }
path(const std::string & str)50 	/* implicit */ path(const std::string & str) : pathstr(str) { check(); }
path(const char * str)51 	/* implicit */ path(const char * str) : pathstr(str) { check(); }
52 
53 	inline path & operator=(const path & other) {
54 		return (pathstr = other.pathstr, *this);
55 	}
56 
57 	inline path & operator=(const std::string & str) {
58 		return (pathstr = str, check(), *this);
59 	}
60 
61 	inline path & operator=(const char * str) {
62 		return (pathstr = str, check(), *this);
63 	}
64 
65 	path operator/(const path & other) const;
66 
67 	path & operator/=(const path & other);
68 
string()69 	inline const std::string & string() const {
70 		return pathstr;
71 	}
72 
73 	/*!
74 	 * If pathstr contains a slash, return everything preceding it.
75 	 * Otherwise, return path().
76 	 */
parent()77 	inline path parent() const {
78 		if(has_info()) {
79 			size_t dirpos = pathstr.find_last_of(dir_sep);
80 			return (dirpos == std::string::npos) ? path() : path(pathstr.substr(0, dirpos));
81 		} else {
82 			return empty() ? path("..") : path(pathstr + dir_sep + "..");
83 		}
84 	}
85 
86 	/*!
87 	 * return *this = parent()
88 	 */
up()89 	path & up() {
90 		if(has_info()) {
91 			size_t dirpos = pathstr.find_last_of(dir_sep);
92 			return (((dirpos != std::string::npos) ? pathstr.resize(dirpos) : pathstr.clear()), *this);
93 		} else {
94 			return ((empty() ? pathstr = ".." : (pathstr += dir_sep).append("..")), *this);
95 		}
96 	}
97 
98 	/*!
99 	 * If pathstr contains a slash, return everything following it.
100 	 * Otherwise, return pathstr.
101 	 */
filename()102 	inline std::string filename() const {
103 		size_t dirpos = pathstr.find_last_of(dir_sep);
104 		return (dirpos == std::string::npos) ? pathstr : pathstr.substr(dirpos + 1);
105 	}
106 
107 	/*!
108 	 * If filename() constains a dot, return everything in filename() preceeding the dot.
109 	 * Otherwise, return filename().
110 	 */
111 	std::string basename() const;
112 
113 	/*!
114 	 * If filename() constains a dot, return dot and everything folowing it.
115 	 * Otherwise, return std::string().
116 	 */
117 	std::string ext() const;
118 
empty()119 	inline bool empty() const {
120 		return pathstr.empty();
121 	}
122 
123 	//! @return pathstr == other.pathstr
124 	inline bool operator==(const path & other) const {
125 		return (pathstr == other.pathstr);
126 	}
127 
128 	//! @return pathstr == str
129 	inline bool operator==(const std::string & str) const {
130 		return (pathstr == str);
131 	}
132 
133 	/*!
134 	 * @return pathstr == str
135 	 * This overload is neccessary so comparing with string constants isn't ambigous
136 	 */
137 	inline bool operator==(const char * str) const {
138 		return !pathstr.compare(0, pathstr.length(), str);
139 	}
140 
141 	//! @return pathstr != other.pathstr
142 	inline bool operator!=(const path & other) const {
143 		return (pathstr != other.pathstr);
144 	}
145 
146 	//! @return pathstr != str
147 	inline bool operator!=(const std::string & str) const {
148 		return (pathstr != str);
149 	}
150 
151 	/*!
152 	 * @return pathstr != str
153 	 * This overload is neccessary so comparing with string constants isn't ambigous
154 	 */
155 	inline bool operator!=(const char * str) const {
156 		return pathstr.compare(0, pathstr.length(), str) != 0;
157 	}
158 
159 	/*!
160 	 * To allow path being used in std::map, etc
161 	 * @return pathstr < other.pathstr
162 	 */
163 	inline bool operator<(const path & other) const {
164 		return (pathstr < other.pathstr);
165 	}
166 
167 	/*!
168 	 * If ext starts with a dot, return *this = remove_ext().append(ext).
169 	 * Otherwise, return *this = remove_ext().append('.').append(ext).
170 	 */
171 	path & set_ext(const std::string & ext);
172 
173 	/*!
174 	 * If pathstr constains a dot after the last slash, return everything preceeding the last dot
175 	 * @return *this
176 	 */
177 	path & remove_ext();
178 
179 	//! *this = parent() / filename;
180 	path & set_filename(const std::string & filename);
181 
182 	path & set_basename(const std::string & basename);
183 
184 	//! @return set_basename(get_basename() + basename_part)
185 	path & append_basename(const std::string & basename_part);
186 
swap(path & other)187 	inline void swap(path & other) {
188 		pathstr.swap(other.pathstr);
189 	}
190 
191 	//! return str.empty() ? !ext().empty() : ext() == str || ext.substr(1) == str();
192 	bool has_ext(const std::string & str = std::string()) const;
193 
is_up()194 	inline bool is_up() const {
195 		return (pathstr.length() == 2 && pathstr[0] == '.' && pathstr[1] == '.')
196 		       || (pathstr.length() >= 3 && pathstr[0] == '.' && pathstr[1] == '.' && pathstr[2] == dir_sep);
197 	}
198 
has_info()199 	inline bool has_info() const {
200 		return !pathstr.empty() && !(pathstr.length() == 2 && pathstr[0] == '.' && pathstr[1] == '.')
201 		       && !(pathstr.length() >= 3 && pathstr[pathstr.length() - 1] == '.'
202 		           && pathstr[pathstr.length() - 2] == '.' && pathstr[pathstr.length() - 3] == dir_sep);
203 	}
204 
205 	static path load(const std::string & str);
206 
207 	/*!
208 	 * Append a string to the paths filename component
209 	 *
210 	 * The appended string must not contain a path seperator or be "." or "..".
211 	 */
212 	path & append(const std::string & str);
213 
214 	//! @return append(str)
215 	path & operator+=(const std::string & str) { append(str); return *this; }
216 
217 	//! @return path(*this).append(str)
218 	path operator+(const std::string & str) const {
219 		return path(*this) += str;
220 	}
221 
clear()222 	inline void clear() { pathstr.clear(); }
223 
224 };
225 
226 inline path operator/(const char * a, const path & b) {
227 	return path(a) / b;
228 }
229 
230 inline path operator/(const std::string & a, const path & b) {
231 	return path(a) / b;
232 }
233 
234 inline bool operator==(const std::string & a, const path & b) {
235 	return (b == a);
236 }
237 
238 inline bool operator==(const char * a, const path & b) {
239 	return (b == a);
240 }
241 
242 inline bool operator!=(const std::string & a, const path & b) {
243 	return (b != a);
244 }
245 
246 inline bool operator!=(const char * a, const path & b) {
247 	return (b != a);
248 }
249 
250 inline std::ostream & operator<<(std::ostream & strm, const path & path) {
251 	return strm << '"' << path.string() << '"';
252 }
253 
254 } // namespace fs
255 
256 #endif // ARX_IO_RESOURCE_RESOURCEPATH_H
257