1
0
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:
Peter Nelson
2024-09-16 08:45:26 +01:00
committed by GitHub
parent 3784a3d3d6
commit 908ee7292b
40 changed files with 368 additions and 442 deletions

View File

@@ -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);
}