/* * path.cpp * * Created on: Jul 25, 2012 * Author: gregor */ #ifdef WIN32 #include #include #else #include #include #include #include #include #endif #include "GLTB/exception.h" #include "GLTB/path.h" #include "GLTB/stringconvert.h" namespace gltb { Path::Path() { path="."; } Path::Path(std::string path) : path(path) { } Path Path::getCanonicalPath() { #ifdef WIN32 /* * On windows, only convert to an absolute path until I find how to resolve * NTFS links and junktion points to their targets on Win32. * * This may not actually be sufficient, because the relative path is not shortend * by . and .. elements that might be pressent. */ return makeAbsolute(); #else // use realpath on absolute version of path here to resolve all symlinks in path Path absolutePath=makeAbsolute(); char *realPath=realpath(absolutePath.path.c_str(),NULL); Path canonicalPath(realPath); free(realPath); return canonicalPath; #endif } Path Path::getDirectoryPath() { if(isFile()) { // remove file name part of path std::string dirPath=path; if(dirPath[dirPath.length()-1]==pathSeparator) { dirPath=dirPath.substr(0,dirPath.length()-1); } auto pos=dirPath.rfind(pathSeparator); if(pos==std::string::npos) { // path must point to file in current directory, so return "." return Path("."); } dirPath=dirPath.substr(0,pos); return Path(dirPath); } else { // path is already a directory path return Path(path); } } Path Path::getParentPath() { // check if path is a root path and return self if this is the case auto rootPaths = getRootPaths(); for(size_t i = 0; i < rootPaths.size(); i++) { if(rootPaths[i].getPath() == path) { return *this; } } // remove last part of path if it exists std::string dirPath = path; if(dirPath[dirPath.length() - 1] == pathSeparator) { dirPath = dirPath.substr(0, dirPath.length() - 1); } auto pos = dirPath.rfind(pathSeparator); if(pos == std::string::npos) { // path must point to file in current directory, so return "." return Path("."); } dirPath = dirPath.substr(0,pos); return Path(dirPath); } Path Path::makeRelativeTo(Path startingPath) { #ifdef WIN32 if (isRelative()) { throw Exception("Cannot make a relative path relative to another path; make path absolute first", "gltb::Path::maeRelativeTo()"); } // NOTE: this does not work for paths longer than 260 characters - needs improvement char result[MAX_PATH]; BOOL returnValue = PathRelativePathToA(result, startingPath.path.c_str(), FILE_ATTRIBUTE_DIRECTORY, path.c_str(), FILE_ATTRIBUTE_NORMAL); if (returnValue == FALSE) { throw Exception("PathRelativePathToA failed", "gltb::Path::makeRelativeTo()"); } return Path(result); #else throw Error("unimplemented function","gltb::Path::makeRelativeTo()"); // TODO Algorithm: // Cut off common substring between both paths until / before first difference // For any extra directory the starting path contains, prepend "../" to this path #endif } Path Path::makeAbsolute() { return makeAbsolute(getCurrentDirectory()); } Path Path::makeAbsolute(Path startingPath) { if(isRelative()) { // it is sufficient to properly concatenate paths here Path startingDirPath=startingPath.getDirectoryPath(); std::string resultPath=startingPath.path; if(resultPath[resultPath.length()-1]!=pathSeparator) { resultPath+=pathSeparator; } resultPath+=path; return Path(resultPath); } else { // already an absolute path, nothing to do return Path(path); } } bool Path::isRelative() { return !isAbsolute(); } bool Path::isAbsolute() { #ifdef WIN32 /* FIXME Win32 allows relative path names on other drives than the current one, * for example C:somefile.txt or F:..\otherfile.txt. Should they be handled as * relative or absolute paths? */ // path is a full UNC path if(path.length()>2 && path[0]==Path::pathSeparator && path[1]==Path::pathSeparator) { return true; } // path is a full path with a drive letter if(path.length()>2 && path[1]==Path::driveSeparator) { return true; } #else // paths starting with / are always absolute if(path.length()>1 && path[0]==Path::pathSeparator) { return true; } #endif return false; } std::string Path::getFilename() { std::string filename; auto lastSeparatorPos = path.rfind(Path::pathSeparator); if(lastSeparatorPos != std::string::npos) { filename = path.substr(lastSeparatorPos + 1, path.length() - lastSeparatorPos - 1); } else { filename = path; } #ifdef WIN32 if(lastSeparatorPos == std::string::npos) { // strip drive letter if present lastSeparatorPos = path.rfind(Path::driveSeparator); if(lastSeparatorPos != std::string::npos) { filename = path.substr(lastSeparatorPos + 1, path.length() - lastSeparatorPos - 1); } } else { filename = path; } #endif return filename; } bool Path::exists() { return statPath(); } bool Path::isFile() { if(!statPath()) { return false; } #ifdef WIN32 return (statResult.st_mode&_S_IFREG); #else return S_ISREG(statResult.st_mode); #endif } bool Path::isDirectory() { if(!statPath()) { return false; } #ifdef WIN32 return (statResult.st_mode&_S_IFDIR); #else return S_ISDIR(statResult.st_mode); #endif } bool Path::isLink() { #ifdef WIN32 throw Error("unimplemented function","gltb::Path::isLink()"); #else if(!statPath()) { return false; } return S_ISLNK(statResult.st_mode); #endif } std::vector Path::listContents() { if(!isDirectory()) { throw Exception("listing contents is only possible on directories","gltb::Path::listContents()"); } std::vector result; #ifdef WIN32 WIN32_FIND_DATA findData; // append search pattern, or else only the directory itself will be found std::string searchPath=path; if(searchPath[searchPath.length()-1]!=pathSeparator) { searchPath+=pathSeparator; } searchPath+='*'; HANDLE findHandle=FindFirstFile(utf8ToUtf16WString(searchPath).c_str(),&findData); if(findHandle==INVALID_HANDLE_VALUE) { // if error is FILE_NOT_FOUND, there simply are no matches int lastError=GetLastError(); if(lastError!=ERROR_FILE_NOT_FOUND) { throw Exception("unable to list directory contents","gltb::Path::listContents()"); } else { return result; } } result.push_back(utf16ToUtf8String(findData.cFileName)); int returnValue; do { returnValue=FindNextFile(findHandle,&findData); if(returnValue==0) { int lastError=GetLastError(); if(lastError!=ERROR_NO_MORE_FILES) { FindClose(findHandle); throw Exception("unable to list directory contents","gltb::Path::listContents()"); } } else { result.push_back(utf16ToUtf8String(findData.cFileName)); } } while(returnValue!=0); FindClose(findHandle); #else DIR *directory=opendir(path.c_str()); if(directory==NULL) { throw Exception("unable to list directory contents","gltb::Path::listContents()"); } dirent *dirEntry; do { errno=0; dirEntry=readdir(directory); if(dirEntry!=0) { result.push_back(Path(dirEntry->d_name)); } else if(errno!=0) { throw Exception("error while listing directory contents","gltb::Path::listContents()"); } } while(dirEntry!=NULL); closedir(directory); #endif return result; } bool Path::statPath() { #ifdef WIN32 if(_stat(path.c_str(),&statResult)==-1) { if(errno==ENOENT) { // no file or directory with that name return false; } // some other, more serious error happend throw Exception("unable to stat() path " + path,"gltb::Path::statPath()"); } return true; #else if(stat(path.c_str(),&statResult)==-1) { if(errno==ENOENT) { // no file or directory with that name return false; } // some other, more serious error happend throw Exception("unable to stat() path " + path,"gltb::Path::statPath()"); } return true; #endif } Path Path::getCurrentDirectory() { #ifdef WIN32 int pathLength=GetCurrentDirectory(0,NULL); wchar_t *pathBuffer=new wchar_t[pathLength]; int result=GetCurrentDirectory(pathLength,pathBuffer); if(result==0) { throw Exception("unable to determine current directory","gltb::Path::getCurrentDirectory()"); } std::string pathStr=utf16ToUtf8Char(pathBuffer); return Path(pathStr); #else // FIXME glibc specific: getcwd allocates the string buffer itself char *currentDir=getcwd(NULL,0); Path currentDirPath(currentDir); free(currentDir); return currentDirPath; #endif } std::vector Path::getRootPaths() { std::vector result; #ifdef WIN32 DWORD driveMap=GetLogicalDrives(); char driveLetter='A'; for(int i=0;i<26;i++) { if(driveMap&1) { result.push_back(Path(driveLetter+std::string(":\\"))); } driveMap>>=1; driveLetter++; } #else result.push_back(Path("/")); #endif return result; } }