Sleds/cppcore/component/slfile.cpp

587 lines
15 KiB
C++

// 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 <Windows.h>
#include <io.h>
#include <direct.h>
#else
#include <libgen.h>
#include <unistd.h>
#include <sys/time.h>
#include <dirent.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <fstream>
#include <algorithm>
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<unsigned int>(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<std::string> 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<EyeTime &>(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<EyeTime &>(lastModified).GetTime();
int32_t microSec = ((const_cast<EyeTime &>(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<File> &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;
}
}