Files
create/src/Base/FileInfo.cpp
Markus Reitböck 5a423dab39 Base: use CMake to generate precompiled headers on all platforms
"Professional CMake" book suggest the following:

"Targets should build successfully with or without compiler support for precompiled headers. It
should be considered an optimization, not a requirement. In particular, do not explicitly include a
precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically
generated precompile header on the compiler command line instead. This is more portable across
the major compilers and is likely to be easier to maintain. It will also avoid warnings being
generated from certain code checking tools like iwyu (include what you use)."

Therefore, removed the "#include <PreCompiled.h>" from sources, also
there is no need for the "#ifdef _PreComp_" anymore
2025-09-14 09:47:01 +02:00

506 lines
13 KiB
C++

/***************************************************************************
* Copyright (c) 2005 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License (LGPL) *
* as published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* for detail see the LICENCE text file. *
* *
* FreeCAD 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with FreeCAD; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
* *
***************************************************************************/
#include <FCConfig.h>
#include <algorithm>
#include <codecvt>
#include <cstring>
#include <iostream>
#include <system_error>
#ifdef FC_OS_WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
#include "FileInfo.h"
#include "Exception.h"
#include "TimeInfo.h"
using namespace Base;
namespace fs = std::filesystem;
//**********************************************************************************
// helper
#ifdef FC_OS_WIN32
std::string ConvertFromWideString(const std::wstring& string)
{
int neededSize = WideCharToMultiByte(CP_UTF8, 0, string.c_str(), -1, 0, 0, 0, 0);
char* CharString = new char[static_cast<size_t>(neededSize)];
WideCharToMultiByte(CP_UTF8, 0, string.c_str(), -1, CharString, neededSize, 0, 0);
std::string String(CharString);
delete[] CharString;
CharString = NULL;
return String;
}
std::wstring ConvertToWideString(const std::string& string)
{
int neededSize = MultiByteToWideChar(CP_UTF8, 0, string.c_str(), -1, 0, 0);
wchar_t* wideCharString = new wchar_t[static_cast<size_t>(neededSize)];
MultiByteToWideChar(CP_UTF8, 0, string.c_str(), -1, wideCharString, neededSize);
std::wstring wideString(wideCharString);
delete[] wideCharString;
wideCharString = NULL;
return wideString;
}
#endif
//**********************************************************************************
// FileInfo
FileInfo::FileInfo(const char* fileName)
{
setFile(fileName);
}
FileInfo::FileInfo(const std::string& fileName)
{
setFile(fileName.c_str());
}
const std::string& FileInfo::getTempPath()
{
static std::string tempPath;
if (tempPath.empty()) {
fs::path tmp = fs::temp_directory_path();
tmp += fs::path::preferred_separator;
tempPath = pathToString(tmp);
}
return tempPath;
}
std::string FileInfo::getTempFileName(const char* FileName, const char* Path)
{
// FIXME: To avoid race conditions we should rather return a file pointer
// than a file name.
#ifdef FC_OS_WIN32
wchar_t buf[MAX_PATH + 2];
// Path where the file is located
std::wstring path;
if (Path) {
path = ConvertToWideString(std::string(Path));
}
else {
path = ConvertToWideString(getTempPath());
}
// File name in the path
std::wstring file;
if (FileName) {
file = ConvertToWideString(std::string(FileName));
}
else {
file = L"FCTempFile";
}
// this already creates the file
GetTempFileNameW(path.c_str(), file.c_str(), 0, buf);
DeleteFileW(buf);
return std::string(ConvertFromWideString(std::wstring(buf)));
#else
std::string buf;
// Path where the file is located
if (Path) {
buf = Path;
}
else {
buf = getTempPath();
}
// File name in the path
if (FileName) {
buf += "/";
buf += FileName;
buf += "XXXXXX";
}
else {
buf += "/fileXXXXXX";
}
std::vector<char> vec;
std::copy(buf.begin(), buf.end(), std::back_inserter(vec));
vec.push_back('\0');
/* coverity[secure_temp] mkstemp uses 0600 as the mode and is safe */
int id = mkstemp(vec.data());
if (id > -1) {
FILE* file = fdopen(id, "w");
fclose(file);
vec.pop_back(); // remove '\0'
std::string str(vec.begin(), vec.end());
buf.swap(str);
unlink(buf.c_str());
}
return buf;
#endif
}
fs::path FileInfo::stringToPath(const std::string& str)
{
#ifdef FC_OS_WIN32
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
fs::path path(converter.from_bytes(str));
#else
fs::path path(str);
#endif
return path;
}
std::string FileInfo::pathToString(const fs::path& path)
{
#ifdef FC_OS_WIN32
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.to_bytes(path.wstring());
#else
return path.string();
#endif
}
void FileInfo::setFile(const char* name)
{
if (!name) {
FileName.clear();
return;
}
FileName = name;
// keep the UNC paths intact
if (FileName.substr(0, 2) == std::string("\\\\")) {
std::replace(FileName.begin() + 2, FileName.end(), '\\', '/');
}
else {
std::replace(FileName.begin(), FileName.end(), '\\', '/');
}
}
std::string FileInfo::filePath() const
{
return FileName;
}
std::string FileInfo::fileName() const
{
return FileName.substr(FileName.find_last_of('/') + 1);
}
std::string FileInfo::dirPath() const
{
std::size_t last_pos {};
std::string retval;
last_pos = FileName.find_last_of('/');
if (last_pos != std::string::npos) {
retval = FileName.substr(0, last_pos);
}
else {
retval = pathToString(fs::current_path());
}
return retval;
}
std::string FileInfo::fileNamePure() const
{
std::string temp = fileName();
std::string::size_type pos = temp.find_last_of('.');
if (pos != std::string::npos) {
return temp.substr(0, pos);
}
return temp;
}
std::wstring FileInfo::toStdWString() const
{
// As FileName is UTF-8 is encoded we have to convert it
// for Windows because the path names are UTF-16 encoded.
#ifdef FC_OS_WIN32
return ConvertToWideString(FileName);
#else
// On other platforms it's discouraged to use wchar_t for file names
throw Base::FileException("Cannot use FileInfo::toStdWString() on this platform");
#endif
}
std::string FileInfo::extension() const
{
std::string::size_type pos = FileName.find_last_of('.');
if (pos == std::string::npos) {
return {};
}
return FileName.substr(pos + 1);
}
std::string FileInfo::completeExtension() const
{
std::string::size_type pos = FileName.find_first_of('.');
if (pos == std::string::npos) {
return {};
}
return FileName.substr(pos + 1);
}
bool FileInfo::hasExtension(const char* Ext) const
{
#ifdef FC_OS_WIN32
return _stricmp(Ext, extension().c_str()) == 0;
#else
return strcasecmp(Ext, extension().c_str()) == 0;
#endif
}
bool FileInfo::hasExtension(std::initializer_list<const char*> Exts) const
{
return std::ranges::any_of(Exts, [this](const char* ext) {
return hasExtension(ext);
});
}
bool FileInfo::exists() const
{
fs::path path(stringToPath(FileName));
return fs::exists(path);
}
bool FileInfo::isReadable() const
{
fs::path path = stringToPath(FileName);
if (!fs::exists(path)) {
return false;
}
fs::file_status stat = fs::status(path);
fs::perms perms = stat.permissions();
return (perms & fs::perms::owner_read) == fs::perms::owner_read;
}
bool FileInfo::isWritable() const
{
fs::path path = stringToPath(FileName);
if (!fs::exists(path)) {
return false;
}
fs::file_status stat = fs::status(path);
fs::perms perms = stat.permissions();
return (perms & fs::perms::owner_write) == fs::perms::owner_write;
}
bool FileInfo::setPermissions(Permissions perms)
{
fs::perms mode = fs::perms::none;
if (perms & FileInfo::ReadOnly) {
mode |= fs::perms::owner_read;
}
if (perms & FileInfo::WriteOnly) {
mode |= fs::perms::owner_write;
}
if (mode == fs::perms::none) { // bad argument
return false;
}
fs::path file_path = stringToPath(FileName);
if (!fs::exists(file_path)) {
return false;
}
fs::permissions(file_path, mode);
fs::file_status stat = fs::status(file_path);
return stat.permissions() == mode;
}
bool FileInfo::isFile() const
{
fs::path path = stringToPath(FileName);
if (fs::exists(path)) {
return fs::is_regular_file(path);
}
// TODO: Check for valid file name
return true;
}
bool FileInfo::isDir() const
{
fs::path path = stringToPath(FileName);
if (fs::exists(path)) {
return fs::is_directory(path);
}
return false;
}
unsigned int FileInfo::size() const
{
unsigned int bytes {};
fs::path path = stringToPath(FileName);
if (fs::exists(path)) {
bytes = fs::file_size(path);
}
return bytes;
}
template<typename TP>
std::time_t to_time_t(TP tp)
{
using namespace std::chrono;
auto sctp =
time_point_cast<system_clock::duration>(tp - TP::clock::now() + system_clock::now());
return system_clock::to_time_t(sctp);
}
TimeInfo FileInfo::lastModified() const
{
TimeInfo ti = TimeInfo::null();
if (exists()) {
fs::path path = stringToPath(FileName);
ti.setTime_t(to_time_t(fs::last_write_time(path)));
}
return ti;
}
bool FileInfo::deleteFile() const
{
try {
fs::path path = stringToPath(FileName);
return fs::remove(path);
}
catch (const fs::filesystem_error& e) {
std::clog << e.what() << '\n';
return false;
}
}
bool FileInfo::renameFile(const char* NewName)
{
try {
fs::path old_path = stringToPath(FileName);
fs::path new_path = stringToPath(NewName);
fs::rename(old_path, new_path);
FileName = NewName;
return true;
}
catch (const fs::filesystem_error& e) {
std::clog << "Error in renameFile: " << e.what() << '\n';
return false;
}
}
bool FileInfo::copyTo(const char* NewName) const
{
try {
fs::path old_path = stringToPath(FileName);
fs::path new_path = stringToPath(NewName);
fs::copy(old_path, new_path);
return true;
}
catch (const fs::filesystem_error&) {
return false;
}
}
bool FileInfo::createDirectory() const
{
try {
fs::path path(stringToPath(FileName));
return fs::create_directory(path);
}
catch (const fs::filesystem_error&) {
return false;
}
}
bool FileInfo::createDirectories() const
{
try {
fs::path path(stringToPath(FileName));
if (fs::exists(path)) {
return true;
}
return fs::create_directories(path);
}
catch (const fs::filesystem_error&) {
return false;
}
}
bool FileInfo::deleteDirectory() const
{
if (!isDir()) {
return false;
}
try {
fs::path path = stringToPath(FileName);
return fs::remove(path);
}
catch (const fs::filesystem_error& e) {
std::clog << e.what() << '\n';
return false;
}
}
bool FileInfo::deleteDirectoryRecursive() const
{
if (!isDir()) {
return false;
}
try {
fs::path path = stringToPath(FileName);
return fs::remove_all(path) > 0;
}
catch (const fs::filesystem_error& e) {
std::clog << e.what() << '\n';
return false;
}
}
std::vector<Base::FileInfo> FileInfo::getDirectoryContent() const
{
std::error_code ec;
std::vector<Base::FileInfo> list;
fs::path path = stringToPath(FileName);
for (const fs::directory_entry& f : fs::directory_iterator {path, ec}) {
list.emplace_back(pathToString(f.path()));
}
return list;
}