mirror of
https://github.com/OpenTTD/OpenTTD
synced 2026-01-29 15:14:33 +01:00
Codechange: Replace all FILE * with FileHandle RAII class. (#12718)
This removes the need to manually ensure all files are closed.
This commit is contained in:
112
src/fileio.cpp
112
src/fileio.cpp
@@ -120,11 +120,8 @@ static void FillValidSearchPaths(bool only_local_path)
|
||||
*/
|
||||
bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
|
||||
{
|
||||
FILE *f = FioFOpenFile(filename, "rb", subdir);
|
||||
if (f == nullptr) return false;
|
||||
|
||||
FioFCloseFile(f);
|
||||
return true;
|
||||
auto f = FioFOpenFile(filename, "rb", subdir);
|
||||
return f.has_value();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,14 +135,6 @@ bool FileExists(const std::string &filename)
|
||||
return std::filesystem::exists(OTTD2FS(filename), ec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a file in a safe way.
|
||||
*/
|
||||
void FioFCloseFile(FILE *f)
|
||||
{
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a path to the filename in one of the search directories.
|
||||
* @param subdir Subdirectory to try.
|
||||
@@ -191,7 +180,7 @@ std::string FioFindDirectory(Subdirectory subdir)
|
||||
return _personal_dir;
|
||||
}
|
||||
|
||||
static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
|
||||
static std::optional<FileHandle> FioFOpenFileSp(const std::string &filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
/* fopen is implemented as a define with ellipses for
|
||||
@@ -201,7 +190,6 @@ static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searc
|
||||
wchar_t Lmode[5];
|
||||
MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, static_cast<int>(std::size(Lmode)));
|
||||
#endif
|
||||
FILE *f = nullptr;
|
||||
std::string buf;
|
||||
|
||||
if (subdir == NO_DIRECTORY) {
|
||||
@@ -210,17 +198,17 @@ static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searc
|
||||
buf = _searchpaths[sp] + _subdirs[subdir] + filename;
|
||||
}
|
||||
|
||||
f = fopen(buf.c_str(), mode);
|
||||
auto f = FileHandle::Open(buf, mode);
|
||||
#if !defined(_WIN32)
|
||||
if (f == nullptr && strtolower(buf, subdir == NO_DIRECTORY ? 0 : _searchpaths[sp].size() - 1) ) {
|
||||
f = fopen(buf.c_str(), mode);
|
||||
if (!f.has_value() && strtolower(buf, subdir == NO_DIRECTORY ? 0 : _searchpaths[sp].size() - 1) ) {
|
||||
f = FileHandle::Open(buf, mode);
|
||||
}
|
||||
#endif
|
||||
if (f != nullptr && filesize != nullptr) {
|
||||
if (f.has_value() && filesize != nullptr) {
|
||||
/* Find the size of the file */
|
||||
fseek(f, 0, SEEK_END);
|
||||
*filesize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fseek(*f, 0, SEEK_END);
|
||||
*filesize = ftell(*f);
|
||||
fseek(*f, 0, SEEK_SET);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
@@ -232,14 +220,13 @@ static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searc
|
||||
* @return File handle of the opened file, or \c nullptr if the file is not available.
|
||||
* @note The file is read from within the tar file, and may not return \c EOF after reading the whole file.
|
||||
*/
|
||||
FILE *FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
|
||||
static std::optional<FileHandle> FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
|
||||
{
|
||||
FILE *f = fopen(entry.tar_filename.c_str(), "rb");
|
||||
if (f == nullptr) return f;
|
||||
auto f = FileHandle::Open(entry.tar_filename, "rb");
|
||||
if (!f.has_value()) return std::nullopt;
|
||||
|
||||
if (fseek(f, entry.position, SEEK_SET) < 0) {
|
||||
fclose(f);
|
||||
return nullptr;
|
||||
if (fseek(*f, entry.position, SEEK_SET) < 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (filesize != nullptr) *filesize = entry.size;
|
||||
@@ -252,19 +239,18 @@ FILE *FioFOpenFileTar(const TarFileListEntry &entry, size_t *filesize)
|
||||
* @param subdir Subdirectory to open.
|
||||
* @return File handle of the opened file, or \c nullptr if the file is not available.
|
||||
*/
|
||||
FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
|
||||
std::optional<FileHandle> FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
|
||||
{
|
||||
FILE *f = nullptr;
|
||||
|
||||
std::optional<FileHandle> f = std::nullopt;
|
||||
assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
|
||||
|
||||
for (Searchpath sp : _valid_searchpaths) {
|
||||
f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
|
||||
if (f != nullptr || subdir == NO_DIRECTORY) break;
|
||||
if (f.has_value() || subdir == NO_DIRECTORY) break;
|
||||
}
|
||||
|
||||
/* We can only use .tar in case of data-dir, and read-mode */
|
||||
if (f == nullptr && mode[0] == 'r' && subdir != NO_DIRECTORY) {
|
||||
if (!f.has_value() && mode[0] == 'r' && subdir != NO_DIRECTORY) {
|
||||
/* Filenames in tars are always forced to be lowercase */
|
||||
std::string resolved_name = filename;
|
||||
strtolower(resolved_name);
|
||||
@@ -275,7 +261,7 @@ FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory s
|
||||
std::string token;
|
||||
while (std::getline(ss, token, PATHSEPCHAR)) {
|
||||
if (token == "..") {
|
||||
if (tokens.size() < 2) return nullptr;
|
||||
if (tokens.size() < 2) return std::nullopt;
|
||||
tokens.pop_back();
|
||||
} else if (token == ".") {
|
||||
/* Do nothing. "." means current folder, but you can create tar files with "." in the path.
|
||||
@@ -303,11 +289,11 @@ FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory s
|
||||
|
||||
/* Sometimes a full path is given. To support
|
||||
* the 'subdirectory' must be 'removed'. */
|
||||
if (f == nullptr && subdir != NO_DIRECTORY) {
|
||||
if (!f.has_value() && subdir != NO_DIRECTORY) {
|
||||
switch (subdir) {
|
||||
case BASESET_DIR:
|
||||
f = FioFOpenFile(filename, mode, OLD_GM_DIR, filesize);
|
||||
if (f != nullptr) break;
|
||||
if (f.has_value()) break;
|
||||
[[fallthrough]];
|
||||
case NEWGRF_DIR:
|
||||
f = FioFOpenFile(filename, mode, OLD_DATA_DIR, filesize);
|
||||
@@ -480,12 +466,13 @@ bool TarScanner::AddFile(const std::string &filename, size_t, [[maybe_unused]] c
|
||||
TarList::iterator it = _tar_list[this->subdir].find(filename);
|
||||
if (it != _tar_list[this->subdir].end()) return false;
|
||||
|
||||
FILE *f = fopen(filename.c_str(), "rb");
|
||||
auto of = FileHandle::Open(filename, "rb");
|
||||
/* Although the file has been found there can be
|
||||
* a number of reasons we cannot open the file.
|
||||
* Most common case is when we simply have not
|
||||
* been given read access. */
|
||||
if (f == nullptr) return false;
|
||||
if (!of.has_value()) return false;
|
||||
auto &f = *of;
|
||||
|
||||
_tar_list[this->subdir][filename] = std::string{};
|
||||
|
||||
@@ -510,7 +497,6 @@ bool TarScanner::AddFile(const std::string &filename, size_t, [[maybe_unused]] c
|
||||
if (memcmp(&th, &empty[0], 512) == 0) continue;
|
||||
|
||||
Debug(misc, 0, "The file '{}' isn't a valid tar-file", filename);
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -584,14 +570,12 @@ bool TarScanner::AddFile(const std::string &filename, size_t, [[maybe_unused]] c
|
||||
skip = Align(skip, 512);
|
||||
if (fseek(f, skip, SEEK_CUR) < 0) {
|
||||
Debug(misc, 0, "The file '{}' can't be read as a valid tar-file", filename);
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
pos += skip;
|
||||
}
|
||||
|
||||
Debug(misc, 4, "Found tar '{}' with {} new files", filename, num);
|
||||
fclose(f);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -638,15 +622,15 @@ bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
|
||||
|
||||
/* First open the file in the .tar. */
|
||||
size_t to_copy = 0;
|
||||
std::unique_ptr<FILE, FileDeleter> in(FioFOpenFileTar(it2.second, &to_copy));
|
||||
if (!in) {
|
||||
auto in = FioFOpenFileTar(it2.second, &to_copy);
|
||||
if (!in.has_value()) {
|
||||
Debug(misc, 6, "Extracting {} failed; could not open {}", filename, tar_filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Now open the 'output' file. */
|
||||
std::unique_ptr<FILE, FileDeleter> out(fopen(filename.c_str(), "wb"));
|
||||
if (!out) {
|
||||
auto out = FileHandle::Open(filename, "wb");
|
||||
if (!out.has_value()) {
|
||||
Debug(misc, 6, "Extracting {} failed; could not open {}", filename, filename);
|
||||
return false;
|
||||
}
|
||||
@@ -655,8 +639,8 @@ bool ExtractTar(const std::string &tar_filename, Subdirectory subdir)
|
||||
char buffer[4096];
|
||||
size_t read;
|
||||
for (; to_copy != 0; to_copy -= read) {
|
||||
read = fread(buffer, 1, std::min(to_copy, lengthof(buffer)), in.get());
|
||||
if (read <= 0 || fwrite(buffer, 1, read, out.get()) != read) break;
|
||||
read = fread(buffer, 1, std::min(to_copy, lengthof(buffer)), *in);
|
||||
if (read <= 0 || fwrite(buffer, 1, read, *out) != read) break;
|
||||
}
|
||||
|
||||
if (to_copy != 0) {
|
||||
@@ -1040,20 +1024,18 @@ void SanitizeFilename(std::string &filename)
|
||||
*/
|
||||
std::unique_ptr<char[]> ReadFileToMem(const std::string &filename, size_t &lenp, size_t maxsize)
|
||||
{
|
||||
FILE *in = fopen(filename.c_str(), "rb");
|
||||
if (in == nullptr) return nullptr;
|
||||
auto in = FileHandle::Open(filename, "rb");
|
||||
if (!in.has_value()) return nullptr;
|
||||
|
||||
FileCloser fc(in);
|
||||
|
||||
fseek(in, 0, SEEK_END);
|
||||
size_t len = ftell(in);
|
||||
fseek(in, 0, SEEK_SET);
|
||||
fseek(*in, 0, SEEK_END);
|
||||
size_t len = ftell(*in);
|
||||
fseek(*in, 0, SEEK_SET);
|
||||
if (len > maxsize) return nullptr;
|
||||
|
||||
std::unique_ptr<char[]> mem = std::make_unique<char[]>(len + 1);
|
||||
|
||||
mem.get()[len] = 0;
|
||||
if (fread(mem.get(), len, 1, in) != 1) return nullptr;
|
||||
if (fread(mem.get(), len, 1, *in) != 1) return nullptr;
|
||||
|
||||
lenp = len;
|
||||
return mem;
|
||||
@@ -1177,3 +1159,23 @@ uint FileScanner::Scan(const std::string_view extension, const std::string &dire
|
||||
AppendPathSeparator(path);
|
||||
return ScanPath(this, extension, OTTD2FS(path), path.size(), recursive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an RAII file handle if possible.
|
||||
* The canonical RAII-way is for FileHandle to open the file and throw an exception on failure, but we don't want that.
|
||||
* @param filename UTF-8 encoded filename to open.
|
||||
* @param mode Mode to open file.
|
||||
* @return FileHandle, or std::nullopt on failure.
|
||||
*/
|
||||
std::optional<FileHandle> FileHandle::Open(const std::string &filename, const std::string &mode)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
/* Windows also requires mode to be wchar_t. */
|
||||
auto f = _wfopen(OTTD2FS(filename).c_str(), OTTD2FS(mode).c_str());
|
||||
#else
|
||||
auto f = fopen(filename.c_str(), mode.c_str());
|
||||
#endif /* _WIN32 */
|
||||
|
||||
if (f == nullptr) return std::nullopt;
|
||||
return FileHandle(f);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user