From e9fa1e4e946918e7dd3eb3a2c296adcaf0c4a107 Mon Sep 17 00:00:00 2001 From: Rangi Date: Mon, 13 Jul 2026 13:02:46 -0400 Subject: [PATCH] Avoid closing stdin/stdout/stderr standard streams --- include/asm/main.hpp | 3 ++- include/util.hpp | 5 +++++ src/asm/actions.cpp | 3 ++- src/asm/lexer.cpp | 6 +----- src/asm/output.cpp | 5 +++-- src/asm/section.cpp | 5 +++-- src/fix/fix.cpp | 9 ++++----- src/fix/main.cpp | 2 +- src/link/object.cpp | 3 ++- src/link/output.cpp | 8 ++++---- src/util.cpp | 15 +++++++++++++++ 11 files changed, 42 insertions(+), 22 deletions(-) diff --git a/include/asm/main.hpp b/include/asm/main.hpp index c9b3c3ae..d6d863b6 100644 --- a/include/asm/main.hpp +++ b/include/asm/main.hpp @@ -9,6 +9,7 @@ #include #include "helpers.hpp" // assume +#include "util.hpp" // xfclose enum MissingInclude { INC_ERROR, // A missing included file is an error that halts assembly @@ -32,7 +33,7 @@ struct Options { ~Options() { if (dependFile) { - fclose(dependFile); + xfclose(dependFile); } } diff --git a/include/util.hpp b/include/util.hpp index cf3459e8..61232bd6 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,10 @@ enum NumberBase { BASE_16 = 16, }; +// File-closing functions that will not close standard streams +int xfclose(FILE *file); +int xclose(int fd); + // Locale-independent character class functions bool isNewline(int c); bool isBlankSpace(int c); diff --git a/src/asm/actions.cpp b/src/asm/actions.cpp index 9fc2d68a..a3e22308 100644 --- a/src/asm/actions.cpp +++ b/src/asm/actions.cpp @@ -18,6 +18,7 @@ #include "extern/utf8decoder.hpp" #include "helpers.hpp" #include "linkdefs.hpp" +#include "util.hpp" // xfclose #include "asm/charmap.hpp" #include "asm/format.hpp" @@ -147,7 +148,7 @@ std::optional act_ReadFile(std::string const &name, uint32_t maxLen } return ""; } - Defer closeFile{[&] { fclose(file); }}; + Defer closeFile{[&] { xfclose(file); }}; size_t readSize = maxLen; if (fseek(file, 0, SEEK_END) == 0) { diff --git a/src/asm/lexer.cpp b/src/asm/lexer.cpp index a7c64375..43a5b2d7 100644 --- a/src/asm/lexer.cpp +++ b/src/asm/lexer.cpp @@ -405,11 +405,7 @@ void LexerState::setFileAsNextState(std::string const &filePath, bool updateStat if (fd >= 0) { // If the file is stdin, or if measuring its size failed, read it in pieces - Defer closeFile{[&] { - if (fd != STDIN_FILENO) { - close(fd); - } - }}; + Defer closeFile{[&] { xclose(fd); }}; // Reasonably large buffer size for `read` performance char buf[8192]; diff --git a/src/asm/output.cpp b/src/asm/output.cpp index e3aa3c16..5d017888 100644 --- a/src/asm/output.cpp +++ b/src/asm/output.cpp @@ -17,6 +17,7 @@ #include "helpers.hpp" // assume, Defer #include "linkdefs.hpp" #include "platform.hpp" +#include "util.hpp" // xfclose #include "asm/charmap.hpp" #include "asm/fstack.hpp" @@ -210,7 +211,7 @@ void out_WriteObject() { fatal("Failed to open object file \"%s\": %s", objectFileName, strerror(errno)); // LCOV_EXCL_STOP } - Defer closeFile{[&] { fclose(file); }}; + Defer closeFile{[&] { xfclose(file); }}; // Also write symbols that weren't written above sym_ForEach(out_RegisterSymbol); @@ -389,7 +390,7 @@ void out_WriteState(std::string name, std::vector const &features) fatal("Failed to open state file \"%s\": %s", name.c_str(), strerror(errno)); // LCOV_EXCL_STOP } - Defer closeFile{[&] { fclose(file); }}; + Defer closeFile{[&] { xfclose(file); }}; static char const *dumpHeadings[NB_STATE_FEATURES] = { "Numeric constants", diff --git a/src/asm/section.cpp b/src/asm/section.cpp index 90b86c48..63d38f22 100644 --- a/src/asm/section.cpp +++ b/src/asm/section.cpp @@ -21,6 +21,7 @@ #include "helpers.hpp" #include "itertools.hpp" // InsertionOrderedMap #include "linkdefs.hpp" +#include "util.hpp" // xfclose #include "asm/fstack.hpp" #include "asm/lexer.hpp" @@ -974,7 +975,7 @@ bool sect_BinaryFile(std::string const &name, uint32_t startPos) { if (!file) { return fstk_FileError(name, "`INCBIN`"); } - Defer closeFile{[&] { fclose(file); }}; + Defer closeFile{[&] { xfclose(file); }}; if (fseek(file, 0, SEEK_END) == 0) { if (unsigned long fsize = ftell(file); @@ -1035,7 +1036,7 @@ bool sect_BinaryFileSlice(std::string const &name, uint32_t startPos, uint32_t l if (!file) { return fstk_FileError(name, "`INCBIN`"); } - Defer closeFile{[&] { fclose(file); }}; + Defer closeFile{[&] { xfclose(file); }}; if (fseek(file, 0, SEEK_END) == 0) { if (unsigned long fsize = ftell(file); diff --git a/src/fix/fix.cpp b/src/fix/fix.cpp index 3c49b667..47b461a0 100644 --- a/src/fix/fix.cpp +++ b/src/fix/fix.cpp @@ -15,6 +15,7 @@ #include "diagnostics.hpp" #include "helpers.hpp" #include "platform.hpp" +#include "util.hpp" // xclose, xfclose #include "fix/main.hpp" #include "fix/mbc.hpp" @@ -463,7 +464,6 @@ bool fix_ProcessFile(char const *name, char const *outputName) { } int output = -1; - bool openedOutput = false; if (outputName) { if (!strcmp(outputName, "-")) { output = STDOUT_FILENO; @@ -476,12 +476,11 @@ bool fix_ProcessFile(char const *name, char const *outputName) { return true; // LCOV_EXCL_STOP } - openedOutput = true; } } Defer closeOutput{[&] { - if (openedOutput) { - close(output); + if (outputName) { + xclose(output); } }}; @@ -497,7 +496,7 @@ bool fix_ProcessFile(char const *name, char const *outputName) { // operations may fail, all of which we handle. error("Failed to open \"%s\" for reading+writing: %s", name, strerror(errno)); } else { - Defer closeInput{[&] { close(input); }}; + Defer closeInput{[&] { xclose(input); }}; struct stat stat; if (fstat(input, &stat) == -1) { error("Failed to stat \"%s\": %s", name, strerror(errno)); // LCOV_EXCL_LINE diff --git a/src/fix/main.cpp b/src/fix/main.cpp index 3fbf1cf6..8e2a224b 100644 --- a/src/fix/main.cpp +++ b/src/fix/main.cpp @@ -308,7 +308,7 @@ static void initLogo() { fatal("Failed to open \"%s\" for reading: %s", logoFilename, strerror(errno)); // LCOV_EXCL_STOP } - Defer closeLogo{[&] { fclose(logoFile); }}; + Defer closeLogo{[&] { xfclose(logoFile); }}; uint8_t logoBpp[sizeof(options.logo)]; if (size_t nbRead = fread(logoBpp, 1, sizeof(logoBpp), logoFile); diff --git a/src/link/object.cpp b/src/link/object.cpp index 433b1a1a..a8c60bb9 100644 --- a/src/link/object.cpp +++ b/src/link/object.cpp @@ -19,6 +19,7 @@ #include "helpers.hpp" #include "linkdefs.hpp" #include "platform.hpp" +#include "util.hpp" // xfclose #include "verbosity.hpp" #include "version.hpp" @@ -446,7 +447,7 @@ void obj_ReadFile(std::string const &filePath, size_t fileID) { if (!file) { fatal("Failed to open file \"%s\": %s", fileName, strerror(errno)); } - Defer closeFile{[&] { fclose(file); }}; + Defer closeFile{[&] { xfclose(file); }}; // First, check if the object is a RGBDS object, a SDCC one, or neither. // A single `ungetc` is guaranteed to work. diff --git a/src/link/output.cpp b/src/link/output.cpp index fbb36bb2..2135c63f 100644 --- a/src/link/output.cpp +++ b/src/link/output.cpp @@ -218,7 +218,7 @@ static void writeROM() { } Defer closeOutputFile{[&] { if (outputFile) { - fclose(outputFile); + xfclose(outputFile); } }}; @@ -237,7 +237,7 @@ static void writeROM() { } Defer closeOverlayFile{[&] { if (overlayFile) { - fclose(overlayFile); + xfclose(overlayFile); } }}; @@ -556,7 +556,7 @@ static void writeSym() { if (!symFile) { fatal("Failed to open sym file \"%s\": %s", symFileName, strerror(errno)); } - Defer closeSymFile{[&] { fclose(symFile); }}; + Defer closeSymFile{[&] { xfclose(symFile); }}; fputs("; File generated by rgblink\n", symFile); @@ -607,7 +607,7 @@ static void writeMap() { if (!mapFile) { fatal("Failed to open map file \"%s\": %s", mapFileName, strerror(errno)); } - Defer closeMapFile{[&] { fclose(mapFile); }}; + Defer closeMapFile{[&] { xfclose(mapFile); }}; writeMapSummary(); diff --git a/src/util.cpp b/src/util.cpp index e44804f1..0b97cc18 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -9,6 +9,21 @@ #include // strspn #include "helpers.hpp" // assume +#include "platform.hpp" + +int xfclose(FILE *file) { + if (file == stdin || file == stdout || file == stderr) { + return 0; + } + return fclose(file); +} + +int xclose(int fd) { + if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) { + return 0; + } + return close(fd); +} bool isNewline(int c) { return c == '\r' || c == '\n';