#include <simgear_config.h>
#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/strutils.hxx>
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
+#include <fstream>
#ifdef _WIN32
# include <direct.h>
#include <boost/algorithm/string/case_conv.hpp>
using std::string;
+using simgear::strutils::starts_with;
/**
* define directory path separators
#endif
-// If Unix, replace all ":" with "/". In windoze, allow the
-// second character to be a ":" for things like c:\foo\bar
-
+// For windows, replace "\" by "/".
void
SGPath::fix()
{
- for ( string::size_type i = 0; i < path.size(); ++i ) {
-#if defined( WIN32 )
- // for windoze, don't replace the ":" for the second character
- if ( i == 1 ) {
- continue;
- }
-#endif
- if ( path[i] == sgDirPathSepBad ) {
- path[i] = sgDirPathSep;
+ string::size_type sz = path.size();
+ for ( string::size_type i = 0; i < sz; ++i ) {
+ if ( path[i] == sgDirPathSepBad ) {
+ path[i] = sgDirPathSep;
+ }
}
+ // drop trailing "/"
+ while ((sz>1)&&(path[sz-1]==sgDirPathSep))
+ {
+ path.resize(--sz);
}
}
_modTime(p._modTime)
{
}
-
+
SGPath& SGPath::operator=(const SGPath& p)
{
path = p.path;
// append another piece to the existing path
void SGPath::append( const string& p ) {
- if ( path.size() == 0 ) {
- path = p;
+ if ( path.empty() ) {
+ path = p;
} else {
if ( p[0] != sgDirPathSep ) {
path += sgDirPathSep;
}
- path += p;
+ path += p;
}
fix();
_cached = false;
}
+//------------------------------------------------------------------------------
+SGPath SGPath::operator/( const std::string& p ) const
+{
+ SGPath ret = *this;
+ ret.append(p);
+ return ret;
+}
+
//add a new path component to the existing path string
void SGPath::add( const string& p ) {
append( sgSearchPathSep+p );
// concatenate a string to the end of the path without inserting a
// path separator
void SGPath::concat( const string& p ) {
- if ( path.size() == 0 ) {
- path = p;
+ if ( path.empty() ) {
+ path = p;
} else {
- path += p;
+ path += p;
}
fix();
_cached = false;
// Get the file part of the path (everything after the last path sep)
-string SGPath::file() const {
- int index = path.rfind(sgDirPathSep);
- if (index >= 0) {
- return path.substr(index + 1);
+string SGPath::file() const
+{
+ string::size_type index = path.rfind(sgDirPathSep);
+ if (index != string::npos) {
+ return path.substr(index + 1);
} else {
- return "";
+ return path;
}
}
string SGPath::dir() const {
int index = path.rfind(sgDirPathSep);
if (index >= 0) {
- return path.substr(0, index);
+ return path.substr(0, index);
} else {
- return "";
+ return "";
}
}
// get the base part of the path (everything but the extension.)
-string SGPath::base() const {
- unsigned int index = path.rfind(sgDirPathSep);
- if (index == string::npos) {
- index = 0; // no separator in the name
- } else {
- ++index; // skip past the separator
- }
-
-// find the first dot after the final separator
- unsigned int firstDot = path.find(".", index);
- if (firstDot == string::npos) {
- return path; // no extension, return whole path
+string SGPath::base() const
+{
+ string::size_type index = path.rfind(".");
+ string::size_type lastSep = path.rfind(sgDirPathSep);
+
+// tolerate dots inside directory names
+ if ((lastSep != string::npos) && (index < lastSep)) {
+ return path;
}
- return path.substr(0, firstDot);
+ if (index != string::npos) {
+ return path.substr(0, index);
+ } else {
+ return path;
+ }
}
string SGPath::file_base() const
{
- unsigned int index = path.rfind(sgDirPathSep);
+ string::size_type index = path.rfind(sgDirPathSep);
if (index == string::npos) {
index = 0; // no separator in the name
} else {
++index; // skip past the separator
}
- unsigned int firstDot = path.find(".", index);
+ string::size_type firstDot = path.find(".", index);
if (firstDot == string::npos) {
return path.substr(index); // no extensions
}
string SGPath::complete_lower_extension() const
{
- unsigned int index = path.rfind(sgDirPathSep);
+ string::size_type index = path.rfind(sgDirPathSep);
if (index == string::npos) {
index = 0; // no separator in the name
} else {
++index; // skip past the separator
}
- int firstDot = path.find(".", index);
- if ((firstDot >= 0) && (path.find(sgDirPathSep, firstDot) == string::npos)) {
+ string::size_type firstDot = path.find(".", index);
+ if ((firstDot != string::npos) && (path.find(sgDirPathSep, firstDot) == string::npos)) {
return boost::to_lower_copy(path.substr(firstDot + 1));
} else {
return "";
string_list sgPathBranchSplit( const string &dirpath ) {
string_list path_elements;
string element, path = dirpath;
- while ( path.size() ) {
+ while ( ! path.empty() ) {
size_t p = path.find( sgDirPathSep );
if ( p != string::npos ) {
element = path.substr( 0, p );
element = path;
path = "";
}
- if ( element.size() )
+ if ( ! element.empty() )
path_elements.push_back( element );
}
return path_elements;
bool SGPath::isNull() const
{
- return path.empty() || (path == "");
+ return path.empty();
}
std::string SGPath::str_native() const
return (path != other.path);
}
+bool SGPath::rename(const SGPath& newName)
+{
+ if (::rename(c_str(), newName.c_str()) != 0) {
+ SG_LOG(SG_IO, SG_WARN, "renamed failed: from " << str() << " to " << newName.str()
+ << " reason: " << strerror(errno));
+ return false;
+ }
+
+ path = newName.path;
+ _cached = false;
+ return true;
+}
+
+//------------------------------------------------------------------------------
+SGPath SGPath::fromEnv(const char* name, const SGPath& def)
+{
+ const char* val = getenv(name);
+ if( val && val[0] )
+ return SGPath(val);
+ return def;
+}
+
+#ifdef _WIN32
+//------------------------------------------------------------------------------
+SGPath SGPath::home()
+{
+ // TODO
+ return SGPath();
+}
+#else
+//------------------------------------------------------------------------------
+SGPath SGPath::home()
+{
+ return fromEnv("HOME");
+}
+#endif
+
+#ifdef _WIN32
+
+#include <ShlObj.h> // for CSIDL
+
+//------------------------------------------------------------------------------
+SGPath SGPath::desktop()
+{
+ typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPSTR, int, BOOL);
+ static GetSpecialFolderPath SHGetSpecialFolderPath = NULL;
+
+ // lazy open+resolve of shell32
+ if (!SHGetSpecialFolderPath) {
+ HINSTANCE shellDll = ::LoadLibrary("shell32");
+ SHGetSpecialFolderPath = (GetSpecialFolderPath) GetProcAddress(shellDll, "SHGetSpecialFolderPathA");
+ }
+
+ if (!SHGetSpecialFolderPath){
+ return SGPath();
+ }
+
+ char path[MAX_PATH];
+ if (SHGetSpecialFolderPath(0, path, CSIDL_DESKTOPDIRECTORY, false)) {
+ return SGPath(path);
+ }
+
+ SG_LOG(SG_GENERAL, SG_ALERT, "SGPath::desktop() failed, bad" );
+ return SGPath();
+}
+#elif __APPLE__
+#include <CoreServices/CoreServices.h>
+
+//------------------------------------------------------------------------------
+SGPath SGPath::desktop()
+{
+ FSRef ref;
+ OSErr err = FSFindFolder(kUserDomain, kDesktopFolderType, false, &ref);
+ if (err) {
+ return SGPath();
+ }
+
+ unsigned char path[1024];
+ if (FSRefMakePath(&ref, path, 1024) != noErr) {
+ return SGPath();
+ }
+
+ return SGPath((const char*) path);
+}
+#else
+//------------------------------------------------------------------------------
+SGPath SGPath::desktop()
+{
+ // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+
+ // $XDG_CONFIG_HOME defines the base directory relative to which user specific
+ // configuration files should be stored. If $XDG_CONFIG_HOME is either not set
+ // or empty, a default equal to $HOME/.config should be used.
+ const SGPath user_dirs = fromEnv("XDG_CONFIG_HOME", home() / ".config")
+ / "user-dirs.dirs";
+
+ // Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
+ // homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an absolute
+ // path. No other format is supported.
+ const std::string DESKTOP = "XDG_DESKTOP_DIR=\"";
+
+ std::ifstream user_dirs_file( user_dirs.c_str() );
+ std::string line;
+ while( std::getline(user_dirs_file, line).good() )
+ {
+ if( !starts_with(line, DESKTOP) || *line.rbegin() != '"' )
+ continue;
+
+ // Extract dir from XDG_DESKTOP_DIR="<dir>"
+ line = line.substr(DESKTOP.length(), line.length() - DESKTOP.length() - 1 );
+
+ const std::string HOME = "$HOME";
+ if( starts_with(line, HOME) )
+ return home() / simgear::strutils::unescape(line.substr(HOME.length()));
+
+ return SGPath(line);
+ }
+
+ return home() / "Desktop";
+}
+#endif
+
+std::string SGPath::realpath() const
+{
+#if (defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED <= 1050)
+ // Workaround for Mac OS 10.5. Somehow fgfs crashes on Mac at ::realpath.
+ // This means relative paths cannot be used on Mac OS <= 10.5
+ return path;
+#else
+ #if defined(_MSC_VER) /*for MS compilers */ || defined(_WIN32) /*needed for non MS windows compilers like MingW*/
+ // with absPath NULL, will allocate, and ignore length
+ char *buf = _fullpath( NULL, path.c_str(), _MAX_PATH );
+ #else
+ // POSIX
+ char* buf = ::realpath(path.c_str(), NULL);
+ #endif
+ if (!buf)
+ {
+ SG_LOG(SG_IO, SG_ALERT, "ERROR: The path '" << path << "' does not exist in the file system.");
+ return path;
+ }
+ std::string p(buf);
+ free(buf);
+ return p;
+#endif
+}