// \file filesystem.cpp // // Copyright (C) 2014 MicroNeil Research Corporation. // // This program is part of the MicroNeil Research Open Library Project. For // more information go to http://www.microneil.com/OpenLibrary/index.html // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version. // // This program 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 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., 59 Temple // Place, Suite 330, Boston, MA 02111-1307 USA //============================================================================== #ifdef _WIN32 #include #include #else #include #include #include #include #include #include #include #endif #include #include #include "filesystem.hpp" namespace CodeDweller { void FileOps::moveFile(std::string const &from, std::string const &to) { #ifdef _WIN32 if (MoveFileEx(from.c_str(), to.c_str(), MOVEFILE_REPLACE_EXISTING) == 0) { #else if (rename(from.c_str(), to.c_str()) != 0) { #endif throw std::runtime_error("Error moving file \"" + from + "\" to file \"" + to + "\": " + FileReference::getErrorText()); } } #ifdef _WIN32 char const FilePath::DirectorySeparator = '\\'; #else char const FilePath::DirectorySeparator = '/'; #endif bool FilePath::isAbsolute(std::string const &path) { #ifdef _WIN32 return !PathIsRelative(path.c_str()); #else if (path.empty()) { return false; } return ('/' == path[0]); #endif } std::string FilePath::join(std::initializer_list components) { std::string path; for (auto &component : components) { if (!path.empty() && path.back() != FilePath::DirectorySeparator) { path += FilePath::DirectorySeparator; if (isAbsolute(component)) { throw std::invalid_argument("Attempted to use absolute path \"" + component + "\" where a relative path " "is required."); } } path += component; } if (!path.empty() && path.back() == FilePath::DirectorySeparator) { path.pop_back(); } return path; } FileReference::FileReference(std::string fileName) : name(fileName), modTimestamp(0), size_bytes(0), fileExists(false), fileIsDirectory(false) { refresh(); } std::string FileReference::FileName() const { return name; } void FileReference::refresh() { reset(); // Load info. struct stat statBuffer; int status = stat(name.c_str(), &statBuffer); if (-1 == status) { // File no longer exists. if (ENOENT == errno) { return; } // Something went wrong. throw std::runtime_error("Error updating status of file \"" + name + "\": " + getErrorText()); } modTimestamp = statBuffer.st_mtime; size_bytes = statBuffer.st_size; fileExists = true; fileIsDirectory = S_ISDIR(statBuffer.st_mode); } void FileReference:: reset() { modTimestamp = 0; size_bytes = 0; fileExists = false; fileIsDirectory = false; path.clear(); } time_t FileReference::ModTimestamp() const { return modTimestamp; } size_t FileReference::Size() const { return size_bytes; } std::string FileReference::FullPath() { if (!path.empty()) { return path; } if (!fileExists) { return ""; } #ifdef _WIN32 // Get the size of the full path name. DWORD nTchars = GetFullPathName(name.c_str(), 0, NULL, NULL); if (0 == nTchars) { throw std::runtime_error("Error getting full path length for \"" + name + "\": " + getErrorText()); } size_t bufSize = nTchars * sizeof(TCHAR); TCHAR fullPath[bufSize]; nTchars = GetFullPathName(name.c_str(), bufSize, fullPath, NULL); if (0 == nTchars) { throw std::runtime_error("Error getting full path for \"" + name + "\": " + getErrorText()); } path.assign(fullPath); #else char *realPath = realpath(name.c_str(), NULL); if (NULL == realPath) { // Nothing to do if the file doesn't exist. if (ENOENT == errno) { reset(); return ""; } // Something went wrong. throw std::runtime_error("Error checking file \"" + name + "\": " + getErrorText()); } path.assign(realPath); free(realPath); #endif return path; } bool FileReference::exists() const { return fileExists; } bool FileReference::isDirectory() const { return fileIsDirectory; } std::string FileReference::getErrorText() { #ifdef _WIN32 LPVOID winMsgBuf; DWORD lastError = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *) &winMsgBuf, 0, NULL ); std::string errMsg((char *) winMsgBuf); LocalFree(winMsgBuf); return errMsg; #else return strerror(errno); #endif } DirectoryReference::DirectoryReference(std::string dirName, bool (*dirFilter)(std::string)) : name(dirName), filter(dirFilter) { refresh(); } void DirectoryReference::refresh() { // Clear any entries in this object. this->clear(); #ifdef _WIN32 HANDLE hDirList; WIN32_FIND_DATA dirListData; std::string searchString = FilePath::join({name, "*"}); hDirList = FindFirstFile(searchString.c_str(), &dirListData); if (INVALID_HANDLE_VALUE == hDirList) { throw std::runtime_error("Error getting file list for \"" + name + "\": " + FileReference::getErrorText()); } std::string tempName; while (INVALID_HANDLE_VALUE != hDirList) { tempName = FilePath::join({name, dirListData.cFileName}); if ( (0 == filter) || (*filter)(dirListData.cFileName)) { emplace_back(tempName); } if (!FindNextFile(hDirList, &dirListData)) { FindClose(hDirList); hDirList = INVALID_HANDLE_VALUE; } } #else // Get new list. struct dirent **entries; int nEntries = scandir(name.c_str(), &entries, 0, 0); if (nEntries < 0) { throw std::runtime_error("Error getting file list for \"" + name + "\": " + FileReference::getErrorText()); } // Create the FileReference objects. while (nEntries--) { std::string tempName; tempName = FilePath::join({name, entries[nEntries]->d_name}); if ( (0 == filter) || (*filter)(entries[nEntries]->d_name)) { emplace_back(tempName); } free(entries[nEntries]); } free(entries); #endif } }