From: Bertrand <bco...@us...> - 2017-02-25 14:23:23
|
Update of /cvsroot/jsbsim/JSBSim/src/simgear/misc In directory sfp-cvs-1.v30.ch3.sourceforge.com:/tmp/cvs-serv24758/src/simgear/misc Modified Files: CMakeLists.txt Added Files: sg_path.cxx sg_path.hxx strutils.cxx strutils.hxx Log Message: JSBSim now uses SGPath to manage file names. It allows to use absolute paths, to use Unicode file names and to benefit from SimGear maintenance for the corresponding code. --- NEW FILE --- // sg_path.cxx -- routines to abstract out path separator differences // between MacOS and the rest of the world // // Written by Curtis L. Olson, started April 1999. // // Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id: sg_path.cxx,v 1.1 2017/02/25 14:23:20 bcoconni Exp $ #include <simgear/compiler.h> #include <simgear/misc/strutils.hxx> #include <stdio.h> #include <sys/stat.h> #include <errno.h> #include <fstream> #include <cstdlib> #include <iostream> #ifdef _WIN32 # include <direct.h> #endif #include "sg_path.hxx" using std::string; /** * define directory path separators */ static const char sgDirPathSep = '/'; static const char sgDirPathSepBad = '\\'; #ifdef _WIN32 const char SGPath::pathListSep = ';'; #else const char SGPath::pathListSep = ':'; #endif // For windows, replace "\" by "/". void SGPath::fix() { 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); } } // default constructor SGPath::SGPath(PermissionChecker validator) : path(""), _permission_checker(validator), _cached(false), _rwCached(false), _cacheEnabled(true) { } // create a path based on "path" SGPath::SGPath( const std::string& p, PermissionChecker validator ) : path(p), _permission_checker(validator), _cached(false), _rwCached(false), _cacheEnabled(true) { fix(); } // create a path based on "path" SGPath::SGPath(const std::wstring& p, PermissionChecker validator) : _permission_checker(validator), _cached(false), _rwCached(false), _cacheEnabled(true) { path = simgear::strutils::convertWStringToUtf8(p); fix(); } // create a path based on "path" and a "subpath" SGPath::SGPath( const SGPath& p, const std::string& r, PermissionChecker validator ) : path(p.path), _permission_checker(validator), _cached(false), _rwCached(false), _cacheEnabled(p._cacheEnabled) { append(r); fix(); } SGPath SGPath::fromLocal8Bit(const char *name) { return SGPath(simgear::strutils::convertWindowsLocal8BitToUtf8(name)); } SGPath SGPath::fromUtf8(const std::string& bytes, PermissionChecker p) { return SGPath(bytes, p); } SGPath::SGPath(const SGPath& p) : path(p.path), _permission_checker(p._permission_checker), _cached(p._cached), _rwCached(p._rwCached), _cacheEnabled(p._cacheEnabled), _canRead(p._canRead), _canWrite(p._canWrite), _exists(p._exists), _isDir(p._isDir), _isFile(p._isFile), _modTime(p._modTime), _size(p._size) { } SGPath& SGPath::operator=(const SGPath& p) { path = p.path; _permission_checker = p._permission_checker, _cached = p._cached; _rwCached = p._rwCached; _cacheEnabled = p._cacheEnabled; _canRead = p._canRead; _canWrite = p._canWrite; _exists = p._exists; _isDir = p._isDir; _isFile = p._isFile; _modTime = p._modTime; _size = p._size; return *this; } // destructor SGPath::~SGPath() { } // set path void SGPath::set( const string& p ) { path = p; fix(); _cached = false; _rwCached = false; } //------------------------------------------------------------------------------ void SGPath::setPermissionChecker(PermissionChecker validator) { _permission_checker = validator; _rwCached = false; } //------------------------------------------------------------------------------ SGPath::PermissionChecker SGPath::getPermissionChecker() const { return _permission_checker; } //------------------------------------------------------------------------------ void SGPath::set_cached(bool cached) { _cacheEnabled = cached; _cached = false; } // append another piece to the existing path void SGPath::append( const string& p ) { if ( path.empty() ) { path = p; } else { if ( p[0] != sgDirPathSep ) { path += sgDirPathSep; } path += p; } fix(); _cached = false; _rwCached = false; } //------------------------------------------------------------------------------ SGPath SGPath::operator/( const std::string& p ) const { SGPath ret = *this; ret.append(p); return ret; } #if defined(ENABLE_OLD_PATH_API) //add a new path component to the existing path string void SGPath::add( const string& p ) { append( SGPath::pathListSep+p ); } #endif // concatenate a string to the end of the path without inserting a // path separator void SGPath::concat( const string& p ) { if ( path.empty() ) { path = p; } else { path += p; } fix(); _cached = false; _rwCached = false; } std::string SGPath::local8BitStr() const { return simgear::strutils::convertUtf8ToWindowsLocal8Bit(path); } // Get the file part of the path (everything after the last path sep) string SGPath::file() const { string::size_type index = path.rfind(sgDirPathSep); if (index != string::npos) { return path.substr(index + 1); } else { return path; } } // get the directory part of the path. string SGPath::dir() const { int index = path.rfind(sgDirPathSep); if (index >= 0) { return path.substr(0, index); } else { return ""; } } SGPath SGPath::dirPath() const { return SGPath::fromUtf8(dir()); } // get the base part of the path (everything but the extension.) 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; } if (index != string::npos) { return path.substr(0, index); } else { return path; } } string SGPath::file_base() const { string::size_type index = path.rfind(sgDirPathSep); if (index == string::npos) { index = 0; // no separator in the name } else { ++index; // skip past the separator } string::size_type firstDot = path.find(".", index); if (firstDot == string::npos) { return path.substr(index); // no extensions } return path.substr(index, firstDot - index); } // get the extension (everything after the final ".") // but make sure no "/" follows the "." character (otherwise it // is has to be a directory name containing a "."). string SGPath::extension() const { int index = path.rfind("."); if ((index >= 0) && (path.find("/", index) == string::npos)) { return path.substr(index + 1); } else { return ""; } } //------------------------------------------------------------------------------ void SGPath::validate() const { if (_cached && _cacheEnabled) { return; } if (path.empty()) { _exists = false; _canWrite = _canRead = false; return; } #if defined(SG_WINDOWS) struct _stat buf ; bool remove_trailing = false; std::wstring statPath(wstr()); if ((path.length() > 1) && (path.back() == '/')) { statPath.pop_back(); } if (_wstat(statPath.c_str(), &buf ) < 0) { _exists = false; _canRead = false; // check parent directory for write-ability std::wstring parentPath = simgear::strutils::convertUtf8ToWString(dir()); struct _stat parentBuf; if (_wstat(parentPath.c_str(), &parentBuf) >= 0) { _canWrite = parentBuf.st_mode & _S_IWRITE; } else { _canWrite = false; } } else { _exists = true; _isFile = ((S_IFREG & buf.st_mode ) !=0); _isDir = ((S_IFDIR & buf.st_mode ) !=0); _modTime = buf.st_mtime; _size = buf.st_size; _canRead = _S_IREAD & buf.st_mode; _canWrite = _S_IWRITE & buf.st_mode; } #else struct stat buf ; if (stat(path.c_str(), &buf ) < 0) { _exists = false; _canRead = false; // check parent directory for write-ability std::string parentPath = dir(); struct stat parentBuf; if (stat(parentPath.c_str(), &parentBuf) >= 0) { _canWrite = parentBuf.st_mode & S_IWUSR; } else { _canWrite = false; } } else { _exists = true; _isFile = ((S_ISREG(buf.st_mode )) != 0); _isDir = ((S_ISDIR(buf.st_mode )) != 0); _modTime = buf.st_mtime; _size = buf.st_size; _canRead = S_IRUSR & buf.st_mode; _canWrite = S_IWUSR & buf.st_mode; } #endif // ensure permissions are no less restrictive than what the // permissions checker offers if ( _permission_checker ) { Permissions p = _permission_checker(*this); _canRead &= p.read; _canWrite &= p.write; } _cached = true; } //------------------------------------------------------------------------------ void SGPath::checkAccess() const { if( _rwCached && _cacheEnabled ) return; validate(); _rwCached = true; } bool SGPath::exists() const { validate(); return _exists; } //------------------------------------------------------------------------------ bool SGPath::canRead() const { checkAccess(); return _canRead; } //------------------------------------------------------------------------------ bool SGPath::canWrite() const { checkAccess(); return _canWrite; } bool SGPath::isDir() const { validate(); return _exists && _isDir; } bool SGPath::isFile() const { validate(); return _exists && _isFile; } string_list sgPathBranchSplit( const string &dirpath ) { string_list path_elements; string element, path = dirpath; while ( ! path.empty() ) { size_t p = path.find( sgDirPathSep ); if ( p != string::npos ) { element = path.substr( 0, p ); path.erase( 0, p + 1 ); } else { element = path; path = ""; } if ( ! element.empty() ) path_elements.push_back( element ); } return path_elements; } string_list sgPathSplit( const string &search_path ) { string tmp = search_path; string_list result; result.clear(); bool done = false; while ( !done ) { int index = tmp.find(SGPath::pathListSep); if (index >= 0) { result.push_back( tmp.substr(0, index) ); tmp = tmp.substr( index + 1 ); } else { if ( !tmp.empty() ) result.push_back( tmp ); done = true; } } return result; } bool SGPath::isAbsolute() const { if (path.empty()) { return false; } #ifdef _WIN32 // detect '[A-Za-z]:/' if (path.size() > 2) { if (isalpha(path[0]) && (path[1] == ':') && (path[2] == sgDirPathSep)) { return true; } } #endif return (path[0] == sgDirPathSep); } bool SGPath::isNull() const { return path.empty(); } #if defined(ENABLE_OLD_PATH_API) std::string SGPath::str_native() const { #ifdef _WIN32 std::string s = local8BitStr(); std::string::size_type pos; std::string nativeSeparator; nativeSeparator = sgDirPathSepBad; while( (pos=s.find( sgDirPathSep )) != std::string::npos ) { s.replace( pos, 1, nativeSeparator ); } return s; #else return utf8Str(); #endif } #endif time_t SGPath::modTime() const { validate(); return _modTime; } size_t SGPath::sizeInBytes() const { validate(); return _size; } bool SGPath::operator==(const SGPath& other) const { return (path == other.path); } bool SGPath::operator!=(const SGPath& other) const { return (path != other.path); } //------------------------------------------------------------------------------ SGPath SGPath::fromEnv(const char* name, const SGPath& def) { #if defined(SG_WINDOWS) std::wstring wname = simgear::strutils::convertUtf8ToWString(name); const wchar_t* val = _wgetenv(wname.c_str()); if (val && val[0]) return SGPath(val, def._permission_checker); #else const char* val = getenv(name); if( val && val[0] ) return SGPath(val, def._permission_checker); #endif return def; } //------------------------------------------------------------------------------ std::vector<SGPath> SGPath::pathsFromEnv(const char *name) { std::vector<SGPath> r; #if defined(SG_WINDOWS) std::wstring wname = simgear::strutils::convertUtf8ToWString(name); const wchar_t* val = _wgetenv(wname.c_str()); #else const char* val = getenv(name); #endif if (!val) { return r; } #if defined(SG_WINDOWS) return pathsFromUtf8(simgear::strutils::convertWStringToUtf8(val)); #else return pathsFromUtf8(val); #endif } //------------------------------------------------------------------------------ std::vector<SGPath> SGPath::pathsFromUtf8(const std::string& paths) { std::vector<SGPath> r; string_list items = sgPathSplit(paths); string_list_iterator it; for (it = items.begin(); it != items.end(); ++it) { r.push_back(SGPath::fromUtf8(it->c_str())); } return r; } //------------------------------------------------------------------------------ std::vector<SGPath> SGPath::pathsFromLocal8Bit(const std::string& paths) { std::vector<SGPath> r; string_list items = sgPathSplit(paths); string_list_iterator it; for (it = items.begin(); it != items.end(); ++it) { r.push_back(SGPath::fromLocal8Bit(it->c_str())); } return r; } //------------------------------------------------------------------------------ SGPath SGPath::realpath() const { #if defined(SG_WINDOWS) // with absPath NULL, will allocate, and ignore length std::wstring ws = wstr(); wchar_t *buf = _wfullpath( NULL, ws.c_str(), _MAX_PATH ); #else // POSIX char* buf = ::realpath(path.c_str(), NULL); #endif if (!buf) // File does not exist: return the realpath it would have if created now // (needed for fgValidatePath security) { if (path.empty()) { return SGPath(".").realpath(); // current directory } std::string this_dir = dir(); if (isAbsolute() && this_dir.empty()) { // top level this_dir = "/"; } if (file() == "..") { this_dir = SGPath(this_dir).realpath().dir(); if (this_dir.empty()) { // invalid path: .. above root return SGPath(); } return SGPath(this_dir).realpath(); // use native path separator, // and handle 'existing/nonexisting/../symlink' paths } return SGPath(this_dir).realpath() / file(); } #if defined(SG_WINDOWS) SGPath p = SGPath(std::wstring(buf), NULL); #else SGPath p(SGPath::fromLocal8Bit(buf)); #endif free(buf); return p; } //------------------------------------------------------------------------------ std::string SGPath::join(const std::vector<SGPath>& paths, const std::string& joinWith) { std::string r; if (paths.empty()) { return r; } r = paths[0].utf8Str(); for (size_t i=1; i<paths.size(); ++i) { r += joinWith + paths[i].utf8Str(); } return r; } //------------------------------------------------------------------------------ std::wstring SGPath::wstr() const { return simgear::strutils::convertUtf8ToWString(path); } //------------------------------------------------------------------------------ bool SGPath::permissionsAllowsWrite() const { return _permission_checker ? _permission_checker(*this).write : true; } --- NEW FILE --- /** * \file sg_path.hxx * Routines to abstract out path separator differences between MacOS * and the rest of the world. */ // Written by Curtis L. Olson, started April 1999. // // Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id: sg_path.hxx,v 1.1 2017/02/25 14:23:20 bcoconni Exp $ #ifndef _SG_PATH_HXX #define _SG_PATH_HXX #include <sys/types.h> #include <simgear/compiler.h> #include <string> #include <ctime> #include <vector> #ifdef _MSC_VER typedef int mode_t; #endif typedef std::vector<std::string> string_list; typedef string_list::iterator string_list_iterator; /** * A class to hide path separator difference across platforms and assist * in managing file system path names. * * Paths can be input in any platform format and will be converted * automatically to the proper format. */ class SGPath { public: // OS-dependent separator used in paths lists static const char pathListSep; struct Permissions { bool read : 1; bool write : 1; }; typedef Permissions (*PermissionChecker)(const SGPath&); /** Default constructor */ explicit SGPath(PermissionChecker validator = NULL); /** Copy contructor */ SGPath(const SGPath& p); SGPath& operator=(const SGPath& p); /** * Construct a path based on the starting path provided. * @param p initial path */ explicit SGPath( const std::string& p, PermissionChecker validator = NULL ); explicit SGPath(const std::wstring& p, PermissionChecker validator = NULL); /** * Construct a path based on the starting path provided and a relative subpath * @param p initial path * @param r relative subpath */ SGPath( const SGPath& p, const std::string& r, PermissionChecker validator = NULL ); /** Destructor */ ~SGPath(); /** * Set path to a new value * @param p new path */ void set( const std::string& p ); SGPath& operator= ( const char* p ) { this->set(p); return *this; } bool operator==(const SGPath& other) const; bool operator!=(const SGPath& other) const; void setPermissionChecker(PermissionChecker validator); PermissionChecker getPermissionChecker() const; /** * Set if file information (exists, type, mod-time) is cached or * retrieved each time it is queried. Caching is enabled by default */ void set_cached(bool cached); /** * Append another piece to the existing path. Inserts a path * separator between the existing component and the new component. * @param p additional path component */ void append( const std::string& p ); /** * Get a copy of this path with another piece appended. * * @param p additional path component */ SGPath operator/( const std::string& p ) const; /** * Append a new piece to the existing path. Inserts a search path * separator to the existing path and the new patch component. * @param p additional path component */ void add( const std::string& p ); /** * Concatenate a string to the end of the path without inserting a * path separator. * @param p additional path suffix */ void concat( const std::string& p ); /** * Returns a path with the absolute pathname that names the same file, whose * resolution does not involve '.', '..', or symbolic links. */ SGPath realpath() const; /** * Get the file part of the path (everything after the last path sep) * @return file string */ std::string file() const; /** * Get the directory part of the path. * @return directory string */ std::string dir() const; /** * Get the base part of the path (everything but the final extension.) * @return the base string */ std::string base() const; /** * Get the base part of the filename (everything before the first '.') * @return the base filename */ std::string file_base() const; /** * Get the extension part of the path (everything after the final ".") * @return the extension string */ std::string extension() const; /** * Get the path string * @return path string */ std::string str() const { return path; } std::string utf8Str() const { return path; } std::string local8BitStr() const; std::wstring wstr() const; /** * Get the path string * @return path in "C" string (ptr to char array) form. */ const char* c_str() const { return path.c_str(); } /** * Get the path string in OS native form */ std::string str_native() const; /** * Determine if file exists by attempting to fopen it. * @return true if file exists, otherwise returns false. */ bool exists() const; /** * Check if reading file is allowed. Readabilty does not imply the existance * of the file. * * @note By default all files will be marked as readable. No check is made * if the operating system allows the given file to be read. Derived * classes may actually implement custom read/write rights. */ bool canRead() const; bool canWrite() const; bool isFile() const; bool isDir() const; /** * Opposite sense to isAbsolute */ bool isRelative() const { return !isAbsolute(); } /** * Is this an absolute path? * I.e starts with a directory seperator, or a single character + colon */ bool isAbsolute() const; /** * check for default constructed path */ bool isNull() const; /** * modification time of the file */ time_t modTime() const; /** * */ size_t sizeInBytes() const; /** * return the path of the parent directory of this path. */ SGPath dirPath() const; /** * Get a path stored in the environment variable with the given \a name. * * @param name Name of the environment variable * @param def Default path to return if the environment variable does not * exist or is empty. */ static SGPath fromEnv(const char* name, const SGPath& def = SGPath()); static SGPath fromUtf8(const std::string& bytes, PermissionChecker p = NULL); static SGPath fromLocal8Bit(const char* name); static std::vector<SGPath> pathsFromEnv(const char* name); static std::vector<SGPath> pathsFromUtf8(const std::string& paths); static std::vector<SGPath> pathsFromLocal8Bit(const std::string& paths); static std::string join(const std::vector<SGPath>& paths, const std::string& joinWith); private: void fix(); void validate() const; void checkAccess() const; bool permissionsAllowsWrite() const; std::string path; PermissionChecker _permission_checker; mutable bool _cached : 1; mutable bool _rwCached : 1; bool _cacheEnabled : 1; ///< cacheing can be disbled if required mutable bool _canRead : 1; mutable bool _canWrite : 1; mutable bool _exists : 1; mutable bool _isDir : 1; mutable bool _isFile : 1; mutable time_t _modTime; mutable size_t _size; }; /// Output to an ostream template<typename char_type, typename traits_type> inline std::basic_ostream<char_type, traits_type>& operator<<(std::basic_ostream<char_type, traits_type>& s, const SGPath& p) { return s << "Path \"" << p.utf8Str() << "\""; } /** * Split a directory string into a list of it's parent directories. */ string_list sgPathBranchSplit( const std::string &path ); /** * Split a directory search path into a vector of individual paths */ string_list sgPathSplit( const std::string &search_path ); #endif // _SG_PATH_HXX --- NEW FILE --- // String utilities. // // Written by Bernie Bright, started 1998 // // Copyright (C) 1998 Bernie Bright - bb...@bi... // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id: strutils.cxx,v 1.1 2017/02/25 14:23:20 bcoconni Exp $ #include <ctype.h> #include <cstring> #include <sstream> #include <algorithm> #include <iostream> #include <string.h> // strerror_r() and strerror_s() #include <errno.h> #if defined(HAVE_CPP11_CODECVT) #include <codecvt> // new in C++11 #endif #include "strutils.hxx" #include <simgear/compiler.h> // SG_WINDOWS #if defined(SG_WINDOWS) #include <windows.h> #endif using std::string; using std::vector; using std::stringstream; using std::cout; using std::endl; namespace simgear { namespace strutils { /* * utf8ToLatin1() convert utf8 to latin, useful for accent character (i.e éâà îè...) */ template <typename Iterator> size_t get_length (Iterator p) { unsigned char c = static_cast<unsigned char> (*p); if (c < 0x80) return 1; else if (!(c & 0x20)) return 2; else if (!(c & 0x10)) return 3; else if (!(c & 0x08)) return 4; else if (!(c & 0x04)) return 5; else return 6; } typedef unsigned int value_type; template <typename Iterator> value_type get_value (Iterator p) { size_t len = get_length (p); if (len == 1) return *p; value_type res = static_cast<unsigned char> ( *p & (0xff >> (len + 1))) << ((len - 1) * 6 ); for (--len; len; --len) { value_type next_byte = static_cast<unsigned char> (*(++p)) - 0x80; if (next_byte & 0xC0) return 0x00ffffff; // invalid UTF-8 res |= next_byte << ((len - 1) * 6); } return res; } string utf8ToLatin1( string& s_utf8 ) { string s_latin1; for (string::iterator p = s_utf8.begin(); p != s_utf8.end(); ++p) { value_type value = get_value<string::iterator&>(p); if (value > 0x10ffff) return s_utf8; // invalid UTF-8: guess that the input was already Latin-1 if (value > 0xff) SG_LOG(SG_IO, SG_WARN, "utf8ToLatin1: wrong char value: " << value); s_latin1 += static_cast<char>(value); } return s_latin1; } /** * */ static vector<string> split_whitespace( const string& str, int maxsplit ) { vector<string> result; string::size_type len = str.length(); string::size_type i = 0; string::size_type j; int countsplit = 0; while (i < len) { while (i < len && isspace((unsigned char)str[i])) { ++i; } j = i; while (i < len && !isspace((unsigned char)str[i])) { ++i; } if (j < i) { result.push_back( str.substr(j, i-j) ); ++countsplit; while (i < len && isspace((unsigned char)str[i])) { ++i; } if (maxsplit && (countsplit >= maxsplit) && i < len) { result.push_back( str.substr( i, len-i ) ); i = len; } } } return result; } /** * */ vector<string> split( const string& str, const char* sep, int maxsplit ) { if (sep == 0) return split_whitespace( str, maxsplit ); vector<string> result; int n = std::strlen( sep ); if (n == 0) { // Error: empty separator string return result; } const char* s = str.c_str(); string::size_type len = str.length(); string::size_type i = 0; string::size_type j = 0; int splitcount = 0; while (i+n <= len) { if (s[i] == sep[0] && (n == 1 || std::memcmp(s+i, sep, n) == 0)) { result.push_back( str.substr(j,i-j) ); i = j = i + n; ++splitcount; if (maxsplit && (splitcount >= maxsplit)) break; } else { ++i; } } result.push_back( str.substr(j,len-j) ); return result; } string_list split_on_any_of(const std::string& str, const char* seperators) { if (seperators == nullptr || (strlen(seperators) == 0)) { throw "illegal/missing seperator string"; } string_list result; size_t pos = 0; size_t startPos = str.find_first_not_of(seperators, 0); for(;;) { pos = str.find_first_of(seperators, startPos); if (pos == string::npos) { result.push_back(str.substr(startPos)); break; } result.push_back(str.substr(startPos, pos - startPos)); startPos = str.find_first_not_of(seperators, pos); if (startPos == string::npos) { break; } } return result; } /** * The lstrip(), rstrip() and strip() functions are implemented * in do_strip() which uses an additional parameter to indicate what * type of strip should occur. */ const int LEFTSTRIP = 0; const int RIGHTSTRIP = 1; const int BOTHSTRIP = 2; static string do_strip( const string& s, int striptype ) { string::size_type len = s.length(); if( len == 0 ) // empty string is trivial return s; string::size_type i = 0; if (striptype != RIGHTSTRIP) { while (i < len && isspace(s[i])) { ++i; } } string::size_type j = len; if (striptype != LEFTSTRIP) { do { --j; } while (j >= 1 && isspace(s[j])); ++j; } if (i == 0 && j == len) { return s; } else { return s.substr( i, j - i ); } } string lstrip( const string& s ) { return do_strip( s, LEFTSTRIP ); } string rstrip( const string& s ) { return do_strip( s, RIGHTSTRIP ); } string strip( const string& s ) { return do_strip( s, BOTHSTRIP ); } void stripTrailingNewlines_inplace(string& s) { // The following (harder to read) implementation is much slower on // my system (g++ 6.2.1 on Debian): 11.4 vs. 3.9 seconds on // 50,000,000 iterations performed on a short CRLF-terminated // string---and it is even a bit slower (12.9 seconds) with // std::next(it) instead of (it+1). // // for (string::reverse_iterator it = s.rbegin(); // it != s.rend() && (*it == '\r' || *it == '\n'); /* empty */) { // it = string::reverse_iterator(s.erase( (it+1).base() )); // } // Simple and fast while (!s.empty() && (s.back() == '\r' || s.back() == '\n')) { s.pop_back(); } } string stripTrailingNewlines(const string& s) { string res = s; stripTrailingNewlines_inplace(res); return res; } string rpad( const string & s, string::size_type length, char c ) { string::size_type l = s.length(); if( l >= length ) return s; string reply = s; return reply.append( length-l, c ); } string lpad( const string & s, size_t length, char c ) { string::size_type l = s.length(); if( l >= length ) return s; string reply = s; return reply.insert( 0, length-l, c ); } bool starts_with( const string & s, const string & substr ) { return s.compare(0, substr.length(), substr) == 0; } bool ends_with( const string & s, const string & substr ) { if( substr.length() > s.length() ) return false; return s.compare( s.length() - substr.length(), substr.length(), substr ) == 0; } string simplify(const string& s) { string result; // reserve size of 's'? string::const_iterator it = s.begin(), end = s.end(); // advance to first non-space char - simplifes logic in main loop, // since we can always prepend a single space when we see a // space -> non-space transition for (; (it != end) && isspace(*it); ++it) { /* nothing */ } bool lastWasSpace = false; for (; it != end; ++it) { char c = *it; if (isspace(c)) { lastWasSpace = true; continue; } if (lastWasSpace) { result.push_back(' '); } lastWasSpace = false; result.push_back(c); } return result; } int to_int(const std::string& s, int base) { stringstream ss(s); switch (base) { case 8: ss >> std::oct; break; case 16: ss >> std::hex; break; default: break; } int result; ss >> result; return result; } int compare_versions(const string& v1, const string& v2, int maxComponents) { vector<string> v1parts(split(v1, ".")); vector<string> v2parts(split(v2, ".")); int lastPart = std::min(v1parts.size(), v2parts.size()); if (maxComponents > 0) { lastPart = std::min(lastPart, maxComponents); } for (int part=0; part < lastPart; ++part) { int part1 = to_int(v1parts[part]); int part2 = to_int(v2parts[part]); if (part1 != part2) { return part1 - part2; } } // of parts iteration // reached end - longer wins return v1parts.size() - v2parts.size(); } string join(const string_list& l, const string& joinWith) { string result; unsigned int count = l.size(); for (unsigned int i=0; i < count; ++i) { result += l[i]; if (i < (count - 1)) { result += joinWith; } } return result; } string uppercase(const string &s) { string rslt(s); for(string::iterator p = rslt.begin(); p != rslt.end(); p++){ *p = toupper(*p); } return rslt; } string lowercase(const string &s) { string rslt(s); for(string::iterator p = rslt.begin(); p != rslt.end(); p++){ *p = tolower(*p); } return rslt; } void lowercase(string &s) { for(string::iterator p = s.begin(); p != s.end(); p++){ *p = tolower(*p); } } #if defined(SG_WINDOWS) static std::wstring convertMultiByteToWString(DWORD encoding, const std::string& a) { std::vector<wchar_t> result; DWORD flags = 0; int requiredWideChars = MultiByteToWideChar(encoding, flags, a.c_str(), a.size(), NULL, 0); result.resize(requiredWideChars); MultiByteToWideChar(encoding, flags, a.c_str(), a.size(), result.data(), result.size()); return std::wstring(result.data(), result.size()); } static std::string convertWStringToMultiByte(DWORD encoding, const std::wstring& w) { std::vector<char> result; DWORD flags = 0; int requiredMBChars = WideCharToMultiByte(encoding, flags, w.data(), w.size(), NULL, 0, NULL, NULL); result.resize(requiredMBChars); WideCharToMultiByte(encoding, flags, w.data(), w.size(), result.data(), result.size(), NULL, NULL); return std::string(result.data(), result.size()); } #endif std::wstring convertUtf8ToWString(const std::string& a) { #ifdef SG_WINDOWS return convertMultiByteToWString(CP_UTF8, a); #elif defined(HAVE_CPP11_CODECVT) std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv; return ucs2conv.from_bytes(a); #else return std::wstring(); #endif } std::string convertWStringToUtf8(const std::wstring& w) { #ifdef SG_WINDOWS return convertWStringToMultiByte(CP_UTF8, w); #elif defined(HAVE_CPP11_CODECVT) std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> ucs2conv; return ucs2conv.to_bytes(w); #else return std::string(); #endif } std::string convertWindowsLocal8BitToUtf8(const std::string& a) { #ifdef SG_WINDOWS return convertWStringToMultiByte(CP_UTF8, convertMultiByteToWString(CP_ACP, a)); #else return a; #endif } std::string convertUtf8ToWindowsLocal8Bit(const std::string& a) { #ifdef SG_WINDOWS return convertWStringToMultiByte(CP_ACP, convertMultiByteToWString(CP_UTF8, a)); #else return a; #endif } //------------------------------------------------------------------------------ static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; static const unsigned char base64_decode_map[128] = { 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 62, 127, 127, 127, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, 127, 64, 127, 127, 127, 0, 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, 127, 127, 127, 127, 127, 127, 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, 127, 127, 127, 127, 127 }; static inline bool is_base64(unsigned char c) { return (isalnum(c) || (c == '+') || (c == '/')); } static bool is_whitespace(unsigned char c) { return ((c == ' ') || (c == '\r') || (c == '\n')); } void decodeBase64(const std::string& encoded_string, std::vector<unsigned char>& ret) { int in_len = encoded_string.size(); int i = 0; int j = 0; int in_ = 0; unsigned char char_array_4[4], char_array_3[3]; while (in_len-- && ( encoded_string[in_] != '=')) { if (is_whitespace( encoded_string[in_])) { in_++; continue; } if (!is_base64(encoded_string[in_])) { break; } char_array_4[i++] = encoded_string[in_]; in_++; if (i ==4) { for (i = 0; i <4; i++) char_array_4[i] = base64_decode_map[char_array_4[i]]; char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (i = 0; (i < 3); i++) ret.push_back(char_array_3[i]); i = 0; } } if (i) { for (j = i; j <4; j++) char_array_4[j] = 0; for (j = 0; j <4; j++) char_array_4[j] = base64_decode_map[char_array_4[j]]; char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]); } } //------------------------------------------------------------------------------ const char hexChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; std::string encodeHex(const std::string& bytes) { return encodeHex( reinterpret_cast<const unsigned char*>(bytes.c_str()), bytes.size() ); } std::string encodeHex(const unsigned char* rawBytes, unsigned int length) { std::string hex(length * 2, '\0'); for (unsigned int i=0; i<length;++i) { unsigned char c = *rawBytes++; hex[i * 2] = hexChar[c >> 4]; hex[i * 2 + 1] = hexChar[c & 0x0f]; } return hex; } //------------------------------------------------------------------------------ std::string unescape(const char* s) { std::string r; while( *s ) { if( *s != '\\' ) { r += *s++; continue; } if( !*++s ) break; if (*s == '\\') { r += '\\'; } else if (*s == 'n') { r += '\n'; } else if (*s == 'r') { r += '\r'; } else if (*s == 't') { r += '\t'; } else if (*s == 'v') { r += '\v'; } else if (*s == 'f') { r += '\f'; } else if (*s == 'a') { r += '\a'; } else if (*s == 'b') { r += '\b'; } else if (*s == 'x') { if (!*++s) break; int v = 0; for (int i = 0; i < 2 && isxdigit(*s); i++, s++) v = v * 16 + (isdigit(*s) ? *s - '0' : 10 + tolower(*s) - 'a'); r += v; continue; } else if (*s >= '0' && *s <= '7') { int v = *s++ - '0'; for (int i = 0; i < 3 && *s >= '0' && *s <= '7'; i++, s++) v = v * 8 + *s - '0'; r += v; continue; } else { r += *s; } s++; } return r; } string sanitizePrintfFormat(const string& input) { string::size_type i = input.find("%n"); if (i != string::npos) { SG_LOG(SG_IO, SG_WARN, "sanitizePrintfFormat: bad format string:" << input); return string(); } return input; } std::string error_string(int errnum) { char buf[512]; // somewhat arbitrary... // This could be simplified with C11 (annex K, optional...), which offers: // // errno_t strerror_s( char *buf, rsize_t bufsz, errno_t errnum ); // size_t strerrorlen_s( errno_t errnum ); #if defined(_WIN32) errno_t retcode; // Always makes the string in 'buf' null-terminated retcode = strerror_s(buf, sizeof(buf), errnum); #elif defined(_GNU_SOURCE) return std::string(strerror_r(errnum, buf, sizeof(buf))); #elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC) || defined(__FreeBSD__) int retcode; // POSIX.1-2001 and POSIX.1-2008 retcode = strerror_r(errnum, buf, sizeof(buf)); #else #error "Could not find a thread-safe alternative to strerror()." #endif #if !defined(_GNU_SOURCE) if (retcode) { std::string msg = "unable to get error message for a given error number"; // C++11 would make this shorter with std::to_string() std::ostringstream ostr; ostr << errnum; #if !defined(_WIN32) if (retcode == ERANGE) { // more specific error message in this case msg = std::string("buffer too small to hold the error message for " "the specified error number"); } #endif throw msg + ostr.str(); } return std::string(buf); #endif // !defined(_GNU_SOURCE) } } // end namespace strutils } // end namespace simgear --- NEW FILE --- /** * \file strutils.hxx * String utilities. */ // Written by Bernie Bright, started 1998 // // Copyright (C) 1998 Bernie Bright - bb...@bi... // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library 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 // Library General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id: strutils.hxx,v 1.1 2017/02/25 14:23:20 bcoconni Exp $ #ifndef STRUTILS_H #define STRUTILS_H #include <simgear/compiler.h> #include <string> #include <vector> #include <cstdlib> typedef std::vector < std::string > string_list; namespace simgear { namespace strutils { /** * utf8ToLatin1() convert utf8 to latin, useful for accent character (i.e éâà îè...) */ std::string utf8ToLatin1( std::string & s_utf8 ); // /** // * atof() wrapper for "string" type // */ // inline double // atof( const string& str ) // { // return ::atof( str.c_str() ); // } // /** // * atoi() wrapper for "string" type // */ // inline int // atoi( const string& str ) // { // return ::atoi( str.c_str() ); // } /** * Strip leading and/or trailing whitespace from s. * @param s String to strip. * @return The stripped string. */ std::string lstrip( const std::string& s ); std::string rstrip( const std::string& s ); std::string strip( const std::string& s ); /** * Return a new string with any trailing \r and \n characters removed. * Typically useful to clean a CR-terminated line obtained from * std::getline() which, upon reading CRLF (\r\n), discards the Line * Feed character (\n) but leaves the Carriage Return (\r) in the * string. * @param s Input string * @return The cleaned string */ std::string stripTrailingNewlines(const std::string& s); /** * Strip any trailing \r and \n characters from a string. * Should have slightly less overhead than stripTrailingNewlines(). * @param s Input string (modified in-place) */ void stripTrailingNewlines_inplace(std::string& s); /** * Right-padding of a string to a given length * @param s String to pad * @param length The total length of the resulting string * @param c The character to pad with * @return The padded string */ std::string rpad( const std::string & s, size_t length, char c ); /** * Left-padding of a string to a given length * @param s String to pad * @param length The total length of the resulting string * @param c The character to pad with * @return The padded string */ std::string lpad( const std::string & s, size_t length, char c ); /** * Split a string into a words using 'sep' as the delimiter string. * Produces a result similar to the perl and python functions of the * same name. * * @param s The string to split into words, * @param sep Word delimiters. If not specified then any whitespace is a separator, * @param maxsplit If given, splits at no more than maxsplit places, * resulting in at most maxsplit+1 words. * @return Array of words. */ string_list split( const std::string& s, const char* sep = 0, int maxsplit = 0 ); /** * split a string on any of several characters. Commonly used to deal * with strings containing whitespace, newlines. To parse CSS style * string, use with '\n\t ,' as the seperator list. * * Note consecutive seperators will not produce empty entries in the * the result, i.e splitting 'a,b,,c,d' with a ',' will produce a result * with four entries, not five. */ string_list split_on_any_of(const std::string&, const char* seperators); /** * create a single string by joining the elements of a list with * another string. */ std::string join(const string_list& l, const std::string& joinWith = ""); /** * Test if a string starts with a string * * @param s The string to be tested * @param substr The string to test * @return True, if s starts with substr, False otherwise */ bool starts_with( const std::string & s, const std::string & substr ); /** * Test if a string ends with a string * * @param s The string to be tested * @param substr The string to test * @return True, if s ends with substr, False otherwise */ bool ends_with( const std::string & s, const std::string & substr ); /** * Strip all leading/trailing whitespace, and transform all interal * whitespace into a single ' ' character - i.e newlines/carriage returns/ * tabs/multiple spaces will be condensed. */ std::string simplify(const std::string& s); /** * convert a string representing a decimal number, to an int */ int to_int(const std::string& s, int base = 10); /** * Like strcmp(), but for dotted versions strings NN.NN.NN * any number of terms are supported. * @return 0 if versions match, -ve number if v1 is lower, +ve if v1 * is greater * @param maxComponents is the maximum number of components to look at. * This can be used to ignore (say) the patch level by setting it to 2 */ int compare_versions(const std::string& v1, const std::string& v2, int maxComponents = 0); /** * Convert a string to upper case. * @return upper case string */ std::string uppercase(const std::string &s); /** * Convert a string to lower case. * @return lower case string */ std::string lowercase(const std::string &s); /** * Convert a string to lower case in place */ void lowercase(std::string &s); /** * convert a string in the local Windows 8-bit encoding to UTF-8 * (no-op on other platforms) */ std::string convertWindowsLocal8BitToUtf8(const std::string& a); /** * */ std::string convertUtf8ToWindowsLocal8Bit(const std::string& a); std::wstring convertUtf8ToWString(const std::string& a); std::string convertWStringToUtf8(const std::wstring& w); /** * convert base-64 encoded data to raw bytes (possibly with embedded * NULs). Throws an exception if input data is not base64, or is * malformed */ void decodeBase64(const std::string& a, std::vector<unsigned char>& output); /** * convert bytes to hexadecimal equivalent */ std::string encodeHex(const std::string& bytes); std::string encodeHex(const unsigned char* rawBytes, unsigned int length); /** * Unescape string. * * @param str String possibly containing escaped characters. * @return string with escaped characters replaced by single character * values. */ std::string unescape(const char* str); inline std::string unescape(const std::string& str) { return unescape(str.c_str()); } /** * Check a printf-style format string for dangerous (buffer-overflowing, * memory re-writing) format tokens. If a problematic token is * found, logs an error (SG_WARN) and returns an empty format string. */ std::string sanitizePrintfFormat(const std::string& input); /** * Get the message corresponding to a given value of errno. * * Similar to strerror(), except it should be thread-safe and returns an * std::string. */ std::string error_string(int errnum); } // end namespace strutils } // end namespace simgear #endif // STRUTILS_H Index: CMakeLists.txt =================================================================== RCS file: /cvsroot/jsbsim/JSBSim/src/simgear/misc/CMakeLists.txt,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -r1.2 -r1.3 *** CMakeLists.txt 22 Jan 2014 11:54:52 -0000 1.2 --- CMakeLists.txt 25 Feb 2017 14:23:19 -0000 1.3 *************** *** 1,4 **** ! set(JSBSIM_SIMGEAR_MISC_HDR stdint.hxx) ! install(FILES ${JSBSIM_SIMGEAR_MISC_HDR} DESTINATION include/JSBSim/simgear/misc) --- 1,8 ---- ! set(SOURCES sg_path.cxx strutils.cxx) ! set(HEADERS stdint.hxx sg_path.hxx strutils.hxx) + add_full_path_name(MISC_SRC "${SOURCES}") + add_full_path_name(MISC_HDR "${HEADERS}") + + install(FILES ${HEADERS} DESTINATION include/JSBSim/simgear/misc) |