cpp-utilities/io/path.cpp

214 lines
7.2 KiB
C++
Raw Normal View History

#include "./path.h"
#include <string>
#include <sstream>
#include <fstream>
#include <cstdlib>
2017-01-14 00:33:02 +01:00
#if defined(PLATFORM_UNIX)
# include <unistd.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <pwd.h>
# include <dirent.h>
2017-01-14 00:33:02 +01:00
#elif defined(PLATFORM_WINDOWS)
# ifdef UNICODE
# undef UNICODE
# endif
2017-01-14 00:33:02 +01:00
# ifdef _UNICODE
# undef _UNICODE
# endif
# include <windows.h>
#else
# error Platform not supported.
#endif
using namespace std;
namespace IoUtilities {
/*!
* \brief Returns the file name and extension of the specified \a path string.
*/
string fileName(const string &path)
{
size_t lastSlash = path.rfind('/');
size_t lastBackSlash = path.rfind('\\');
size_t lastSeparator;
if(lastSlash == string::npos && lastBackSlash == string::npos) {
return path;
} else if(lastSlash == string::npos) {
lastSeparator = lastBackSlash;
} else if(lastBackSlash == string::npos) {
lastSeparator = lastSlash;
} else {
lastSeparator = lastSlash > lastBackSlash ? lastSlash : lastBackSlash;
}
return path.substr(lastSeparator + 1);
}
/*!
* \brief Returns the directory of the specified \a path string (including trailing slash).
*/
string directory(const string &path)
{
size_t lastSlash = path.rfind('/');
size_t lastBackSlash = path.rfind('\\');
size_t lastSeparator;
if(lastSlash == string::npos && lastBackSlash == string::npos) {
return string();
} else if(lastSlash == string::npos) {
lastSeparator = lastBackSlash;
} else if(lastBackSlash == string::npos) {
lastSeparator = lastSlash;
} else {
lastSeparator = lastSlash > lastBackSlash ? lastSlash : lastBackSlash;
}
return path.substr(0, lastSeparator + 1);
}
/*!
* \brief Removes invalid characters from the specified \a fileName.
*
* The characters <, >, ?, !, *, |, /, :, \ and new lines are considered as invalid.
*/
void removeInvalidChars(std::string &fileName)
{
size_t startPos = 0;
static const char invalidPathChars[] = {'\"', '<', '>', '?', '!', '*', '|', '/', ':', '\\', '\n'};
for(const char *i = invalidPathChars, *end = invalidPathChars + sizeof(invalidPathChars); i != end; ++i) {
startPos = fileName.find(*i);
while(startPos != string::npos) {
fileName.replace(startPos, 1, string());
startPos = fileName.find(*i, startPos);
}
}
}
/*!
* \brief Locates a directory meant to store application settings.
* \param result Specifies a string to store the path in.
* \param applicationDirectoryName Specifies the name for the application subdirectory.
* \param createApplicationDirectory Indicates wheter the application subdirectory should be created if not present.
2017-01-14 00:33:02 +01:00
* \returns Returns whether a settings directory could be located.
* \deprecated This function has FIXMEs. Since it is not used actually also a good candidate for being removed.
*/
bool settingsDirectory(std::string &result, std::string applicationDirectoryName, bool createApplicationDirectory)
{
result.clear();
2017-01-14 00:33:02 +01:00
// FIXME: this kind of configuration is not actually used so get rid of it, maybe just read env variable instead
fstream pathConfigFile("path.config", ios_base::in);
if(pathConfigFile.good()) {
for(string line; getline(pathConfigFile, line); ) {
string::size_type p = line.find('=');
if((p != string::npos) && (p + 1 < line.length())) {
string fieldName = line.substr(0, p);
if(fieldName == "settings") {
result.assign(line.substr(p + 1));
}
}
}
}
if(!result.empty()) {
2017-01-14 00:33:02 +01:00
#if defined(PLATFORM_UNIX)
struct stat sb;
return (stat(result.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode));
2017-01-14 00:33:02 +01:00
#else // PLATFORM_WINDOWS
// FIXME: use UTF-16 API to support unicode, or rewrite using fs abstraction lib
DWORD ftyp = GetFileAttributesA(result.c_str());
return (ftyp != INVALID_FILE_ATTRIBUTES) && (ftyp & FILE_ATTRIBUTE_DIRECTORY);
#endif
} else {
if(!applicationDirectoryName.empty()) {
removeInvalidChars(applicationDirectoryName);
}
2017-01-14 00:33:02 +01:00
#if defined(PLATFORM_UNIX) || defined(PLATFORM_MAC)
if(char *homeDir = getenv("HOME")) {
result = homeDir;
} else {
struct passwd *pw = getpwuid(getuid());
result = pw->pw_dir;
}
struct stat sb;
result += "/.config";
if(createApplicationDirectory && !(stat(result.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode))) {
if(mkdir(result.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
return false;
}
}
if(!applicationDirectoryName.empty()) {
result += '/';
result += applicationDirectoryName;
if(createApplicationDirectory && !(stat(result.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode))) {
if(mkdir(result.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
return false;
}
}
}
2017-01-14 00:33:02 +01:00
#else // PLATFORM_WINDOWS
if(char *appData = getenv("appdata")) {
result = appData;
if(!applicationDirectoryName.empty()) {
result += '\\';
result += applicationDirectoryName;
if(createApplicationDirectory) {
2017-01-14 00:33:02 +01:00
// FIXME: use UTF-16 API to support unicode, or rewrite using fs abstraction lib
DWORD ftyp = GetFileAttributesA(result.c_str());
if(ftyp == INVALID_FILE_ATTRIBUTES) {
return false;
} else if(ftyp & FILE_ATTRIBUTE_DIRECTORY) {
return true;
} else {
if(CreateDirectory(result.c_str(), NULL) == 0) {
return false;
} else {
return true;
}
}
}
}
} else {
return false;
}
#endif
}
return true;
}
/*!
* \brief Returns the names of the directory entries in the specified \a path with the specified \a types.
2017-01-14 00:33:02 +01:00
* \deprecated This function has FIXMEs. Since it can be replaced by using fs abstraction lib it is a good candidate for being replaced.
*/
std::list<std::string> directoryEntries(const char *path, DirectoryEntryType types)
{
#ifdef PLATFORM_UNIX
list<string> entries;
if(auto dir = opendir(path)) {
while(auto dirEntry = readdir(dir)) {
bool filter = false;
switch(dirEntry->d_type) {
case DT_REG:
filter = (types & DirectoryEntryType::File) != DirectoryEntryType::None;
break;
case DT_DIR:
filter = (types & DirectoryEntryType::Directory) != DirectoryEntryType::None;
break;
case DT_LNK:
filter = (types & DirectoryEntryType::Symlink) != DirectoryEntryType::None;
break;
default:
filter = (types & DirectoryEntryType::All) != DirectoryEntryType::None;
}
if(filter) {
entries.emplace_back(dirEntry->d_name);
}
}
closedir(dir);
}
return entries;
#else
return list<string>(); // TODO
#endif
}
}