492 lines
11 KiB
C++
492 lines
11 KiB
C++
/** @file
|
|
* @author Edouard DUPIN
|
|
* @copyright 2018, Edouard DUPIN, all right reserved
|
|
* @license MPL v2.0 (see license file)
|
|
*/
|
|
#include <etk/path/Path.hpp>
|
|
#include <etk/debug.hpp>
|
|
#include <etk/Exception.hpp>
|
|
#include <etk/path/fileSystem.hpp>
|
|
|
|
#include <etk/typeInfo.hpp>
|
|
ETK_DECLARE_TYPE(etk::Path);
|
|
|
|
#define TK_DBG_MODE TK_VERBOSE
|
|
//#define TK_DBG_MODE TK_WARNING
|
|
|
|
|
|
static etk::String simplifyPath(etk::String _input) {
|
|
// step 1 : for windows change \ in /:
|
|
TK_DEBUG("Simplify(1) : '" << _input << "'");
|
|
size_t currentPos = 0;
|
|
if (_input.size() == 0) {
|
|
return _input;
|
|
}
|
|
if (_input[0] == '~') {
|
|
_input = etk::path::getHomePath().getString() + "/" + _input.extract(1);
|
|
}
|
|
// step 2 : remove all '//'
|
|
TK_DBG_MODE("Simplify(2) : '" << _input << "'");
|
|
currentPos = 0;
|
|
if (_input.size() <= 1) {
|
|
return _input;
|
|
}
|
|
while(currentPos < _input.size()-1) {
|
|
if ( _input[currentPos] != '/'
|
|
|| _input[currentPos+1] != '/') {
|
|
currentPos++;
|
|
continue;
|
|
}
|
|
_input.erase(currentPos, 1);
|
|
}
|
|
// step 3 : remove all '/./'
|
|
TK_DBG_MODE("Simplify(3) : '" << _input << "'");
|
|
currentPos = 0;
|
|
if (_input.size() <= 1) {
|
|
return _input;
|
|
}
|
|
while( currentPos < _input.size()-2
|
|
&& _input.size() > 2) {
|
|
if ( _input[currentPos] != '/'
|
|
|| _input[currentPos+1] != '.'
|
|
|| _input[currentPos+2] != '/') {
|
|
currentPos++;
|
|
continue;
|
|
}
|
|
_input.erase(currentPos, 2);
|
|
}
|
|
if (end_with(_input, "/.") == true) {
|
|
_input.erase(_input.size()-1, 1);
|
|
}
|
|
// step 4 remove xxx/..
|
|
TK_DBG_MODE("Simplify(4) : '" << _input << "'");
|
|
size_t lastSlashPos = etk::String::npos;
|
|
currentPos = 0;
|
|
while( currentPos < _input.size()-2
|
|
&& _input.size() > 2) {
|
|
if ( _input[currentPos] != '/'
|
|
|| _input[currentPos+1] != '.'
|
|
|| _input[currentPos+2] != '.') {
|
|
if (_input[currentPos] == '/') {
|
|
lastSlashPos = currentPos;
|
|
}
|
|
currentPos++;
|
|
continue;
|
|
}
|
|
if (lastSlashPos == etk::String::npos) {
|
|
currentPos++;
|
|
continue;
|
|
}
|
|
_input.erase(lastSlashPos, currentPos+2-lastSlashPos+1);
|
|
TK_DEBUG("update : '" << _input << "'");
|
|
lastSlashPos = etk::String::npos;
|
|
currentPos = 0;
|
|
}
|
|
TK_DBG_MODE("Simplify(5) : '" << _input << "'");
|
|
if (_input.size() == 0) {
|
|
_input = "/";
|
|
}
|
|
#ifdef __TARGET_OS__Windows
|
|
etk::String base;
|
|
base += _input[0];
|
|
base += ":";
|
|
if ( _input == base + "/../"
|
|
|| _input == base + "/.."
|
|
|| _input == base + "/./"
|
|
|| _input == base + "/."
|
|
|| _input == "/") {
|
|
_input = base + "/";
|
|
}
|
|
#else
|
|
if ( _input == "/../"
|
|
|| _input == "/.."
|
|
|| _input == "/./"
|
|
|| _input == "/.") {
|
|
_input = "/";
|
|
}
|
|
#endif
|
|
if ( _input.size() >= 1
|
|
&& _input[_input.size()-1] == '/') {
|
|
_input.erase(_input.size()-1, 1);
|
|
}
|
|
TK_DEBUG("Simplify(end) : '" << _input << "'");
|
|
return _input;
|
|
}
|
|
|
|
static etk::String convertToWindows(etk::String _path) {
|
|
if ( _path.size() >= 3
|
|
&& _path[0] == '/'
|
|
&& _path[2] == '/') {
|
|
_path[0] = _path[1];
|
|
_path[1] = ':';
|
|
} else if ( _path.size() == 2
|
|
&& _path[0] == '/') {
|
|
_path[0] = _path[1];
|
|
_path[1] = ':';
|
|
_path += '\\';
|
|
}
|
|
_path.replace("/", "\\");
|
|
return _path;
|
|
}
|
|
|
|
static etk::String convertToUnix(etk::String _path) {
|
|
_path.replace("\\", "/");
|
|
if ( _path.size() >= 3
|
|
&& _path[1] == ':'
|
|
&& _path[2] == '/') {
|
|
#ifndef __TARGET_OS__Windows
|
|
TK_DEBUG("Path name have a windows form: '" << _path << "' c:/ but not a windwos platform");
|
|
#endif
|
|
if ( _path[0] >= 'A'
|
|
&& _path[0] <= 'Z') {
|
|
_path[1] = _path[0] + 'a' - 'A';
|
|
} else {
|
|
_path[1] = _path[0];
|
|
}
|
|
_path[0] = '/';
|
|
}
|
|
return _path;
|
|
}
|
|
static etk::String parsePath(etk::String _path) {
|
|
etk::String out = _path;
|
|
TK_DBG_MODE("1 : Set Name : '" << out << "'");
|
|
// Replace all time to prevent Windows user error when port on Unix
|
|
out = convertToUnix(out);
|
|
out = simplifyPath(out);
|
|
TK_DBG_MODE("3 : parse done : '" << _path << "' ==>\"" << out << "\"");
|
|
return out;
|
|
}
|
|
|
|
|
|
etk::Path::Path() {
|
|
// nothing to do
|
|
}
|
|
|
|
etk::Path::Path(const etk::String& _value) {
|
|
m_data = parsePath(_value);
|
|
}
|
|
|
|
etk::Path::Path(const char * _value) {
|
|
m_data = parsePath(_value);
|
|
}
|
|
|
|
etk::String etk::Path::getString() const {
|
|
return m_data;
|
|
}
|
|
|
|
etk::String etk::Path::getStringWindows() const {
|
|
etk::String out = getString();
|
|
out = convertToWindows(out);
|
|
return out;
|
|
}
|
|
|
|
etk::String etk::Path::getNative() const {
|
|
#ifdef __TARGET_OS__Windows
|
|
return getStringWindows();
|
|
#else
|
|
return getString();
|
|
#endif
|
|
}
|
|
|
|
etk::String etk::Path::getRelative() const {
|
|
if (isRelative() == true) {
|
|
return m_data;
|
|
}
|
|
etk::String execPath = etk::path::getExecutionPath().getString();
|
|
etk::String currentPath = m_data;
|
|
etk::Vector<etk::String> execPathSplit = execPath.split('/');
|
|
etk::Vector<etk::String> currentPathSplit = currentPath.split('/');
|
|
for (size_t iii=0; iii<execPathSplit.size() && iii<currentPathSplit.size(); ++iii) {
|
|
if (execPathSplit[0] == currentPathSplit[0]) {
|
|
execPathSplit.popFront();
|
|
currentPathSplit.popFront();
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
etk::String out = "";
|
|
for (size_t iii=0; iii<execPathSplit.size(); ++iii) {
|
|
out += "../";
|
|
}
|
|
for (size_t iii=0; iii<currentPathSplit.size(); ++iii) {
|
|
out += currentPathSplit[iii];
|
|
if (currentPathSplit.size()-1 != iii) {
|
|
out += "/";
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
etk::String etk::Path::getRelativeWindows() const {
|
|
etk::String out = getRelative();
|
|
out = convertToWindows(out);
|
|
return out;
|
|
}
|
|
|
|
etk::String etk::Path::getRelativeNative() const {
|
|
#ifdef __TARGET_OS__Windows
|
|
return getRelativeWindows();
|
|
#else
|
|
return getRelative();
|
|
#endif
|
|
}
|
|
|
|
etk::String etk::Path::getAbsolute() const {
|
|
if (isAbsolute() == true) {
|
|
return m_data;
|
|
}
|
|
return (etk::path::getExecutionPath() / m_data).getString();
|
|
}
|
|
|
|
etk::String etk::Path::getAbsoluteWindows() const {
|
|
etk::String out = getAbsolute();
|
|
out = convertToWindows(out);
|
|
return out;
|
|
}
|
|
|
|
etk::String etk::Path::getAbsoluteNative() const {
|
|
#ifdef __TARGET_OS__Windows
|
|
return getAbsoluteWindows();
|
|
#else
|
|
return getAbsolute();
|
|
#endif
|
|
}
|
|
|
|
bool etk::Path::isRelative() const {
|
|
if ( m_data.size() >= 1
|
|
&& m_data[0] != '/') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool etk::Path::isAbsolute() const {
|
|
if ( m_data.size() >= 1
|
|
&& m_data[0] == '/') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
etk::String etk::Path::getFileName() const {
|
|
size_t pos = m_data.rfind('/');
|
|
if (pos == etk::String::npos) {
|
|
return m_data;
|
|
}
|
|
return m_data.extract(pos+1);
|
|
}
|
|
|
|
etk::String etk::Path::getExtention() const {
|
|
size_t pos = m_data.rfind('.');
|
|
size_t posSlash = m_data.rfind('/');
|
|
if (pos == etk::String::npos) {
|
|
return "";
|
|
}
|
|
if ( posSlash != etk::String::npos
|
|
&& posSlash > pos) {
|
|
return "";
|
|
}
|
|
if ( pos == 0
|
|
|| ( posSlash != etk::String::npos
|
|
&& posSlash == pos-1
|
|
)
|
|
) {
|
|
// a simple name started with a .
|
|
return "";
|
|
}
|
|
return m_data.extract(pos+1);
|
|
}
|
|
|
|
void etk::Path::removeExtention() {
|
|
size_t pos = m_data.rfind('.');
|
|
size_t posSlash = m_data.rfind('/');
|
|
if (pos == etk::String::npos) {
|
|
return;
|
|
}
|
|
if ( posSlash != etk::String::npos
|
|
&& posSlash > pos) {
|
|
return;
|
|
}
|
|
if ( pos == 0
|
|
|| ( posSlash != etk::String::npos
|
|
&& posSlash == pos-1
|
|
)
|
|
) {
|
|
// a simple name started with a .
|
|
return;
|
|
}
|
|
m_data = m_data.extract(0, pos);
|
|
}
|
|
|
|
etk::Path etk::Path::getExtentionRemoved() const {
|
|
etk::Path tmp(*this);
|
|
tmp.removeExtention();
|
|
return tmp;
|
|
}
|
|
|
|
void etk::Path::parent() {
|
|
size_t pos = m_data.rfind('/');
|
|
if (pos == etk::String::npos) {
|
|
m_data = "";
|
|
return;
|
|
}
|
|
if (pos == 0) {
|
|
m_data = "/";
|
|
return;
|
|
}
|
|
m_data = m_data.extract(0, pos);
|
|
}
|
|
|
|
etk::Path etk::Path::getParent() const {
|
|
etk::Path out = *this;
|
|
out.parent();
|
|
return out;
|
|
}
|
|
|
|
void etk::Path::clear() {
|
|
m_data.clear();
|
|
}
|
|
|
|
bool etk::Path::operator== (const etk::Path& _obj) const {
|
|
return m_data == _obj.m_data;
|
|
}
|
|
|
|
bool etk::Path::operator!= (const etk::Path& _obj) const {
|
|
return m_data != _obj.m_data;
|
|
}
|
|
|
|
etk::Path etk::Path::operator/ (const etk::String& _element) const {
|
|
etk::Path tmp = *this;
|
|
tmp /= etk::Path(_element);
|
|
return tmp;
|
|
}
|
|
|
|
etk::Path etk::Path::operator/ (const char* _element) const {
|
|
etk::Path tmp = *this;
|
|
tmp /= etk::Path(_element);
|
|
return tmp;
|
|
}
|
|
|
|
etk::Path etk::Path::operator/ (const etk::Path& _path) const {
|
|
etk::Path tmp = *this;
|
|
tmp /= _path;
|
|
return tmp;
|
|
}
|
|
|
|
etk::Path& etk::Path::operator/= (const etk::String& _element) {
|
|
*this /= etk::Path(_element);
|
|
return *this;
|
|
}
|
|
|
|
etk::Path& etk::Path::operator/= (const char* _element) {
|
|
*this /= etk::Path(_element);
|
|
return *this;
|
|
}
|
|
|
|
etk::Path& etk::Path::operator/= (const etk::Path& _path) {
|
|
if (_path.m_data.size() == 0) {
|
|
return *this;
|
|
}
|
|
if (_path.m_data[0] == '/') {
|
|
ETK_THROW_EXCEPTION(etk::exception::InvalidArgument("add path that is absolute"));
|
|
}
|
|
if (m_data.isEmpty() == true){
|
|
m_data = _path.m_data;
|
|
return *this;
|
|
}
|
|
m_data += '/' + _path.m_data;
|
|
m_data = simplifyPath(m_data);
|
|
return *this;
|
|
}
|
|
|
|
etk::Path etk::Path::operator+ (const char* _element) const {
|
|
etk::Path tmp = *this;
|
|
tmp += etk::Path(_element);
|
|
return tmp;
|
|
}
|
|
|
|
etk::Path etk::Path::operator+ (const etk::String& _element) const {
|
|
etk::Path tmp = *this;
|
|
tmp += etk::Path(_element);
|
|
return tmp;
|
|
}
|
|
|
|
etk::Path etk::Path::operator+ (const etk::Path& _element) const {
|
|
etk::Path tmp = *this;
|
|
tmp += _element;
|
|
return tmp;
|
|
}
|
|
|
|
etk::Path& etk::Path::operator+= (const char* _element) {
|
|
*this += etk::Path(_element);
|
|
return *this;
|
|
}
|
|
|
|
etk::Path& etk::Path::operator+= (const etk::String& _element) {
|
|
*this += etk::Path(_element);
|
|
return *this;
|
|
}
|
|
|
|
etk::Path& etk::Path::operator+= (const etk::Path& _element) {
|
|
if (_element.m_data.size() == 0) {
|
|
return *this;
|
|
}
|
|
m_data += _element.m_data;
|
|
m_data = simplifyPath(m_data);
|
|
return *this;
|
|
}
|
|
|
|
etk::Path& etk::Path::operator= (const etk::String& _element) {
|
|
m_data = parsePath(_element);
|
|
return *this;
|
|
}
|
|
|
|
etk::Path& etk::Path::operator= (const char* _element) {
|
|
m_data = parsePath(_element);
|
|
return *this;
|
|
}
|
|
|
|
|
|
etk::Stream& etk::operator <<(etk::Stream& _os, const etk::Path& _obj) {
|
|
_os << _obj.getString();
|
|
return _os;
|
|
}
|
|
|
|
bool etk::operator> (const Path& _left, const Path& _right) {
|
|
return _left.getString() > _right.getString();
|
|
}
|
|
|
|
bool etk::operator>= (const Path& _left, const Path& _right) {
|
|
return _left.getString() >= _right.getString();
|
|
}
|
|
|
|
bool etk::operator< (const Path& _left, const Path& _right) {
|
|
return _left.getString() < _right.getString();
|
|
}
|
|
|
|
bool etk::operator<= (const Path& _left, const Path& _right) {
|
|
return _left.getString() <= _right.getString();
|
|
}
|
|
|
|
#include <etk/UString.hpp>
|
|
namespace etk {
|
|
template<> etk::String toString<etk::Path>(const etk::Path& _val) {
|
|
return _val.getString();
|
|
}
|
|
#if __cplusplus >= 201103L
|
|
template<> etk::UString toUString<etk::Path>(const etk::Path& _val) {
|
|
return toUString(_val.getString());
|
|
}
|
|
|
|
template<> bool from_string<etk::Path>(etk::Path& _variableRet, const etk::UString& _value) {
|
|
_variableRet = etk::Path(toString(_value));
|
|
return true;
|
|
}
|
|
#endif
|
|
template<> bool from_string<etk::Path >(etk::Path& _variableRet, const etk::String& _value) {
|
|
_variableRet = etk::Path(_value);
|
|
return true;
|
|
}
|
|
}
|
|
|