// Copyright (c) 2013, IOnU Security, Inc. // Copyright (c) 2016, Sequence Logic, Inc. All rights reserved. #include "slfile.h" #include "eyetime.h" #include "../internal.h" #ifdef WIN32 #include #include #include #else #include #include #include #include #endif #include #include #include #include #include #include using namespace sequencelogic; const char File::DIR_SEP = '/'; File& File::operator=(const File &oldFile) { if (&oldFile != this) { _mount = oldFile._mount; _path = oldFile._path; _fileName = oldFile._fileName; _ext = oldFile._ext; } return *this; } File::File(const File &parent, const std::string &child) { std::string tmpName = parent._mount + parent._path; if ((tmpName != "") && (tmpName[tmpName.size()-1] != DIR_SEP)) tmpName += DIR_SEP; if ((child != "") && (tmpName != "") && (tmpName[tmpName.size()-1] == DIR_SEP) && (child[0] == DIR_SEP)) tmpName.erase(tmpName.size()-1); tmpName += child; setFilename(tmpName); } File::File(const File &parent, const char *pChild) { std::string tmpName = parent._mount + parent._path; if ((tmpName != "") && (tmpName[tmpName.size()-1] != DIR_SEP)) tmpName += DIR_SEP; if ((pChild != NULL) && (pChild[0] != '\0')) tmpName += pChild; setFilename(tmpName); } File::File(const std::string &mount, const std::string &path, const char *pFileName) : _mount(mount), _path(path) { // Parse the filename... if ((pFileName != NULL) && (pFileName[0] != '\0')) { _ext = ""; _fileName = pFileName; if (_fileName.rfind('.') != std::string::npos) { _ext = _fileName; _ext.erase(0, _fileName.rfind('.')); _fileName.erase(_fileName.rfind('.')); } } } /** * Split this string into it's various "filename" parts, and set the member * variables accordingly. */ void File::setFilename(const char *pFilename) { std::string tmpFilename = pFilename; std::replace_if(tmpFilename.begin(), tmpFilename.end(), [] (std::string::value_type nChar) { return (nChar == '\\'); }, File::DIR_SEP); #ifdef WIN32 _mount.resize(_MAX_DRIVE); _path.resize(_MAX_DIR); _fileName.resize(_MAX_FNAME); _ext.resize(_MAX_EXT); if (_splitpath_s(tmpFilename.c_str(), &_mount[0], _MAX_DRIVE, &_path[0], _MAX_DIR, &_fileName[0], _MAX_FNAME, &_ext[0], _MAX_EXT) != 0) { _mount = ""; _path = ""; _fileName = ""; _ext = ""; } else { _mount.resize(::strlen(_mount.c_str())); _path.resize(::strlen(_path.c_str())); _fileName.resize(::strlen(_fileName.c_str())); _ext.resize(::strlen(_ext.c_str())); } #else // Good ol' *NIX. No parser for filenames... // Hmmm, 'dirname' and 'pathname' appear to modify the buffer passed in... _mount = ""; _ext = ""; if (tmpFilename[tmpFilename.length()-1] == DIR_SEP) _path = tmpFilename; else { _path = dirname(&tmpFilename[0]); tmpFilename = pFilename; _fileName = basename(&tmpFilename[0]); if (_fileName.rfind('.') != std::string::npos) { _ext = _fileName; _ext.erase(0, _fileName.rfind('.')); _fileName.erase(_fileName.rfind('.')); } } #endif } /** * Get the parent directory. */ File File::getParentFile() const { // The "parent file" is the full path, without the file.. std::string tmpName = _mount; tmpName += _path; if ((tmpName.length() > 0) && (tmpName[tmpName.length()-1] != DIR_SEP)) tmpName += DIR_SEP; return File(tmpName); } /** * Does this file exist? */ bool File::exists() const { #ifdef WIN32 return (_access_s(getAbsolutePath().c_str(), 00) == 0); #else return (access(getAbsolutePath().c_str(), F_OK) == 0); #endif } /** * Can I read this file? */ bool File::canRead() const { #ifdef WIN32 return (_access_s(getAbsolutePath().c_str(), 04) == 0); #else return (access(getAbsolutePath().c_str(), R_OK) == 0); #endif } /** * Can I write this file? */ bool File::canWrite() const { #ifdef WIN32 return (_access_s(getAbsolutePath().c_str(), 02) == 0); #else return (access(getAbsolutePath().c_str(), W_OK) == 0); #endif } /** * Is this a file? */ bool File::isFile() const { bool bRetVal = false; if (_fileName != "" || _ext != "") { #ifdef WIN32 struct _stat fStat = {0}; if (_stat(getAbsolutePath().c_str(), &fStat) == 0) bRetVal = (fStat.st_mode & _S_IFREG) == _S_IFREG; #else struct stat fStat; if(stat(getAbsolutePath().c_str(), &fStat) == 0) bRetVal = S_ISREG(fStat.st_mode); #endif } return bRetVal; } /** * Is this a directory? */ bool File::isDirectory() const { bool bRetVal = false; if (_fileName == "") { std::string dirName = getAbsolutePath(); if (*dirName.rbegin() == DIR_SEP) { // Workaround for Android's lack of pop_back //dirName.pop_back(); std::string::reverse_iterator rIter = dirName.rbegin(); ++rIter; dirName.erase(rIter.base()); } #ifdef WIN32 struct _stat fStat = {0}; if (_stat(dirName.c_str(), &fStat) == 0) bRetVal = (fStat.st_mode & _S_IFDIR) == _S_IFDIR; #else struct stat fStat; if(stat(getAbsolutePath().c_str(), &fStat) == 0) bRetVal = S_ISDIR(fStat.st_mode); #endif } return bRetVal; } /** * Delete the file. */ bool File::deleteFile() const { bool bRetVal = false; if (isFile()) bRetVal = (remove(getAbsolutePath().c_str()) == 0); #ifdef _DEBUG if (!bRetVal) { bool bReadOnly = (errno == EACCES); bool bFileNotFound (errno == ENOENT); } #endif return bRetVal; } /** * Get the size of this file... */ unsigned int File::length() const { unsigned int nRetVal = 0; if (isFile()) { std::ifstream fileStr; fileStr.open(getAbsolutePath(), std::ios_base::binary | std::ios_base::in); if (fileStr.good() && !fileStr.eof() && fileStr.is_open()) { fileStr.seekg(0, std::ios_base::beg); std::ifstream::pos_type startPos = fileStr.tellg(); fileStr.seekg(0, std::ios_base::end); nRetVal = static_cast(fileStr.tellg() - startPos); fileStr.close(); } } return nRetVal; } /** * Make all the directories in the path. */ bool File::mkdirs() const { bool bRetVal = true; if (!exists()) { // Directory doesn't exist... Make it... std::vector dirs; SplitString(_path, DIR_SEP, dirs); std::string tmpPath = _mount; for (unsigned int i = 0; i < dirs.size(); ++i) { if ((dirs[i] == ".") || (dirs[i] == "")) continue; if ((tmpPath != "") && (*tmpPath.rbegin() != DIR_SEP)) tmpPath += DIR_SEP; tmpPath += dirs[i]; #ifdef WIN32 bRetVal &= ((_mkdir(tmpPath.c_str()) == 0) || (errno == EEXIST)); #else bRetVal &= ((mkdir(tmpPath.c_str(), 0777) == 0) || (errno == EEXIST)); #endif } } else if (isDirectory()) bRetVal = true; return bRetVal; } /** * When was this file last modified? */ sequencelogic::EyeTime File::lastModified() const { EyeTime retVal; #ifdef WIN32 HANDLE hFile = ::CreateFile(getAbsolutePath().c_str(), GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile != INVALID_HANDLE_VALUE) { FILETIME lastMod; if (::GetFileTime(hFile, NULL, NULL, &lastMod)) { SYSTEMTIME sysTime; ::FileTimeToSystemTime(&lastMod, &sysTime); retVal.SetTime(sysTime); } ::CloseHandle(hFile); } #else struct stat fStat; if(stat(getAbsolutePath().c_str(), &fStat) == 0) { #if defined(ANDROID) long long timeMS = fStat.st_mtime * 1000; timeMS += (fStat.st_mtime_nsec / 1000000); #elif !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) long long timeMS = fStat.st_mtimespec.tv_sec * 1000; timeMS += (fStat.st_mtimespec.tv_nsec / 1000000); #else long long timeMS = fStat.st_mtim.tv_sec * 1000; timeMS += (fStat.st_mtim.tv_nsec / 1000000); #endif retVal.SetTimeMs(timeMS); } #endif return retVal; } /** * Set a file's last modified time. */ void File::setLastModified (const EyeTime &lastModified) { #ifdef WIN32 HANDLE hFile = ::CreateFile(getAbsolutePath().c_str(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { FILETIME lastWriteTime; SYSTEMTIME tmpSysTime = const_cast(lastModified).GetSysTime(); if (!::SystemTimeToFileTime(&tmpSysTime, &lastWriteTime)) { #ifdef DEBUG std::stringstream msg; msg << "File::setLastModified (" << __LINE__ << "): ERROR: Cannot convert SYSTEMTIME to FILETIME:\n"; msg << "File::setLastModified (" << __LINE__ << "): " << getLastError() << "\n"; ::OutputDebugString(msg.str().c_str(); #endif } if (!::SetFileTime(hFile, NULL, NULL, &lastWriteTime)) { #ifdef DEBUG std::stringstream msg; msg << "File::setLastModified (" << __LINE__ << "): ERROR: Cannot SetFileTime:\n"; msg << "File::setLastModified (" << __LINE__ << "): " << getLastError() << "\n"; ::OutputDebugString(msg.str().c_str(); #endif } ::CloseHandle(hFile); } #else struct stat fStat; if(stat(getAbsolutePath().c_str(), &fStat) == 0) { // Get the last access time. 'utime' set's the access/modified time to the current // time if we don't specify a time. timeval uTimeBuf[2]; #if defined(ANDROID) uTimeBuf[0].tv_sec = fStat.st_atime; uTimeBuf[0].tv_usec = (int32_t)(fStat.st_atime_nsec) / 1000; #elif !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) uTimeBuf[0].tv_sec = fStat.st_atimespec.tv_sec; uTimeBuf[0].tv_usec = (int32_t)(fStat.st_atimespec.tv_nsec) / 1000; #else uTimeBuf[0].tv_sec = fStat.st_atim.tv_sec; uTimeBuf[0].tv_usec = (int32_t)(fStat.st_atim.tv_nsec) / 1000; #endif uTimeBuf[1].tv_sec = const_cast(lastModified).GetTime(); int32_t microSec = ((const_cast(lastModified).GetTimeMs()) % 1000) * 1000; uTimeBuf[1].tv_usec = microSec; utimes(getAbsolutePath().c_str(), uTimeBuf); } #endif } std::string File::getAbsolutePath() const { std::string retVal = _mount; retVal += _path; if ((retVal != "") && (*retVal.rbegin() != DIR_SEP)) retVal += DIR_SEP; retVal += _fileName + _ext; return retVal; } /** * Copy this file to the new filename. */ bool File::copyFile(const File &destFile)const { bool bRetVal = false; std::ifstream inFile (getAbsolutePath().c_str(), std::ios::binary); std::ofstream outFile (destFile.getAbsolutePath(), std::ios::binary); if (!inFile.bad() && !outFile.bad()) { outFile << inFile.rdbuf(); bRetVal = !outFile.bad(); } outFile.close(); inFile.close(); return bRetVal; } /** * If this is a directory, get a list of all the files in the directory. The list does *not* * include the '.' or '..' names. * * @param fileList The list of files in the directory * @return The number of files found */ unsigned int File::listFiles(std::vector &fileList, const std::string &fileFilter /*= ""*/) const { fileList.clear(); if (isDirectory()) { #ifdef WIN32 ::WIN32_FIND_DATA fileData = {0}; std::string filesToFind = getDirectory(); if (fileFilter != "") { if (*filesToFind.rbegin() != DIR_SEP) filesToFind += DIR_SEP; filesToFind += "*"; filesToFind += fileFilter; } else if (*(filesToFind.rbegin()) == DIR_SEP) filesToFind += "*"; else { filesToFind += DIR_SEP; filesToFind += "*"; } HANDLE hFile = INVALID_HANDLE_VALUE; if ((hFile = ::FindFirstFile(filesToFind.c_str(), &fileData)) != INVALID_HANDLE_VALUE) { do { if ((::strcmp(fileData.cFileName, ".") == 0) || (::strcmp(fileData.cFileName, "..") == 0)) continue; if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) fileList.push_back(File(_mount, _path + fileData.cFileName + DIR_SEP, "")); else fileList.push_back(File(_mount, _path, fileData.cFileName)); } while (::FindNextFile(hFile, &fileData)); ::FindClose(hFile); } #else std::string dirName = getDirectory(); dirent *pDirEntry; struct stat fStat; DIR *pDir = opendir(dirName.c_str()); if (pDir != NULL) { if (dirName[dirName.length()-1] != DIR_SEP) dirName += DIR_SEP; while ((pDirEntry = readdir(pDir)) != NULL) { std::string fileName = pDirEntry->d_name; if ((fileName == ".") || (fileName == "..")) continue; if ((fileFilter != "") && (fileName.find(fileFilter) == std::string::npos)) continue; if (stat(std::string(dirName + fileName).c_str(), &fStat) == 0) { if (S_ISDIR(fStat.st_mode)) fileList.push_back(File(_mount, _path + fileName + DIR_SEP, "")); else fileList.push_back(File(_mount, _path, fileName.c_str())); } } closedir(pDir); } #endif } return (unsigned int)fileList.size(); } /** * Get the name. This is just the filename (no extension), or the last directory name (if there is no filename set). */ std::string File::getName() const { if (_fileName != "") return _fileName; else { std::string retVal = _path; if (retVal != "") { if (*retVal.rbegin() == DIR_SEP) retVal.pop_back(); retVal.erase(0, retVal.rfind(DIR_SEP)); if (*(retVal.begin()) == DIR_SEP) retVal.erase(0, 1); } return retVal; } } /** * Get the absolute filename, in a URI format: * 'file://{absolute path name' */ std::string File::toURI() const { std::string retVal = "file://"; if (isDirectory()) retVal += (getDirectory() + "/"); else retVal += getAbsolutePath(); return retVal; } namespace sequencelogic { std::ostream& operator<<(std::ostream &out, const File &node) { out << node.getAbsolutePath(); return out; } }