Use a Defer struct to close files and restore lexer state with RAII (#1379)

This commit is contained in:
Sylvie
2024-03-27 10:42:53 -04:00
committed by GitHub
parent 32db0a0f18
commit a68bebf4a2
13 changed files with 131 additions and 172 deletions

View File

@@ -86,4 +86,12 @@ static inline int clz(unsigned int x) {
// For lack of <ranges>, this adds some more brevity // For lack of <ranges>, this adds some more brevity
#define RANGE(s) std::begin(s), std::end(s) #define RANGE(s) std::begin(s), std::end(s)
// For ad-hoc RAII in place of a `defer` statement or cross-platform `__attribute__((cleanup))`
template<typename T>
struct Defer {
T deferred;
Defer(T func) : deferred(func) {}
~Defer() { deferred(); }
};
#endif // HELPERS_H #endif // HELPERS_H

View File

@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
#include "asm/fstack.hpp" #include "asm/fstack.hpp"
#include <sys/stat.h> #include <sys/stat.h>
#include <assert.h> #include <assert.h>

View File

@@ -90,6 +90,8 @@ static void mapFile(void *&mappingAddr, int fd, std::string const &path, size_t
#endif // !( defined(_MSC_VER) || defined(__MINGW32__) ) #endif // !( defined(_MSC_VER) || defined(__MINGW32__) )
using namespace std::literals;
// Bison 3.6 changed token "types" to "kinds"; cast to int for simple compatibility // Bison 3.6 changed token "types" to "kinds"; cast to int for simple compatibility
#define T_(name) (int)yy::parser::token::name #define T_(name) (int)yy::parser::token::name
@@ -526,9 +528,12 @@ static bool continuesIdentifier(int c);
static uint32_t readBracketedMacroArgNum() { static uint32_t readBracketedMacroArgNum() {
bool disableMacroArgs = lexerState->disableMacroArgs; bool disableMacroArgs = lexerState->disableMacroArgs;
bool disableInterpolation = lexerState->disableInterpolation; bool disableInterpolation = lexerState->disableInterpolation;
lexerState->disableMacroArgs = false; lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false; lexerState->disableInterpolation = false;
Defer restoreExpansions{[&] {
lexerState->disableMacroArgs = disableMacroArgs;
lexerState->disableInterpolation = disableInterpolation;
}};
uint32_t num = 0; uint32_t num = 0;
int c = peek(); int c = peek();
@@ -573,12 +578,10 @@ static uint32_t readBracketedMacroArgNum() {
} else if (num == 0 && !symbolError) { } else if (num == 0 && !symbolError) {
error("Invalid bracketed macro argument '\\<0>'\n"); error("Invalid bracketed macro argument '\\<0>'\n");
return 0; return 0;
} } else {
lexerState->disableMacroArgs = disableMacroArgs;
lexerState->disableInterpolation = disableInterpolation;
return num; return num;
} }
}
static std::shared_ptr<std::string> readMacroArg(char name) { static std::shared_ptr<std::string> readMacroArg(char name) {
if (name == '@') { if (name == '@') {
@@ -817,6 +820,15 @@ static void handleCRLF(int c) {
shiftChar(); shiftChar();
} }
static auto scopedDisableExpansions() {
lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = true;
return Defer{[&] {
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
}};
}
// "Services" provided by the lexer to the rest of the program // "Services" provided by the lexer to the rest of the program
uint32_t lexer_GetLineNo() { uint32_t lexer_GetLineNo() {
@@ -841,15 +853,14 @@ void lexer_DumpStringExpansions() {
// Functions to discard non-tokenized characters // Functions to discard non-tokenized characters
static void discardBlockComment() { static void discardBlockComment() {
lexerState->disableMacroArgs = true; Defer reenableExpansions = scopedDisableExpansions();
lexerState->disableInterpolation = true;
for (;;) { for (;;) {
int c = nextChar(); int c = nextChar();
switch (c) { switch (c) {
case EOF: case EOF:
error("Unterminated block comment\n"); error("Unterminated block comment\n");
goto finish; return;
case '\r': case '\r':
// Handle CRLF before nextLine() since shiftChar updates colNo // Handle CRLF before nextLine() since shiftChar updates colNo
handleCRLF(c); handleCRLF(c);
@@ -866,29 +877,23 @@ static void discardBlockComment() {
case '*': case '*':
if (peek() == '/') { if (peek() == '/') {
shiftChar(); shiftChar();
goto finish; return;
} }
// fallthrough // fallthrough
default: default:
continue; continue;
} }
} }
finish:
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
} }
static void discardComment() { static void discardComment() {
lexerState->disableMacroArgs = true; Defer reenableExpansions = scopedDisableExpansions();
lexerState->disableInterpolation = true;
for (;; shiftChar()) { for (;; shiftChar()) {
int c = peek(); int c = peek();
if (c == EOF || c == '\r' || c == '\n') if (c == EOF || c == '\r' || c == '\n')
break; break;
} }
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
} }
static void discardLineContinuation() { static void discardLineContinuation() {
@@ -1229,13 +1234,10 @@ static void appendEscapedSubstring(std::string &yylval, std::string const &str)
} }
static std::string readString(bool raw) { static std::string readString(bool raw) {
lexerState->disableMacroArgs = true; Defer reenableExpansions = scopedDisableExpansions();
lexerState->disableInterpolation = true;
std::string yylval;
bool multiline = false;
// We reach this function after reading a single quote, but we also support triple quotes // We reach this function after reading a single quote, but we also support triple quotes
bool multiline = false;
if (peek() == '"') { if (peek() == '"') {
shiftChar(); shiftChar();
if (peek() == '"') { if (peek() == '"') {
@@ -1244,17 +1246,17 @@ static std::string readString(bool raw) {
multiline = true; multiline = true;
} else { } else {
// "" is an empty string, skip the loop // "" is an empty string, skip the loop
goto finish; return ""s;
} }
} }
for (;;) { for (std::string yylval = ""s;;) {
int c = peek(); int c = peek();
// '\r', '\n' or EOF ends a single-line string early // '\r', '\n' or EOF ends a single-line string early
if (c == EOF || (!multiline && (c == '\r' || c == '\n'))) { if (c == EOF || (!multiline && (c == '\r' || c == '\n'))) {
error("Unterminated string\n"); error("Unterminated string\n");
break; return yylval;
} }
// We'll be staying in the string, so we can safely consume the char // We'll be staying in the string, so we can safely consume the char
@@ -1281,7 +1283,7 @@ static std::string readString(bool raw) {
} }
shiftChar(); shiftChar();
} }
goto finish; return yylval;
case '\\': // Character escape or macro arg case '\\': // Character escape or macro arg
if (raw) if (raw)
@@ -1364,21 +1366,13 @@ static std::string readString(bool raw) {
yylval += c; yylval += c;
} }
finish:
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
return yylval;
} }
static void appendStringLiteral(std::string &yylval, bool raw) { static void appendStringLiteral(std::string &yylval, bool raw) {
lexerState->disableMacroArgs = true; Defer reenableExpansions = scopedDisableExpansions();
lexerState->disableInterpolation = true;
bool multiline = false;
// We reach this function after reading a single quote, but we also support triple quotes // We reach this function after reading a single quote, but we also support triple quotes
bool multiline = false;
yylval += '"'; yylval += '"';
if (peek() == '"') { if (peek() == '"') {
yylval += '"'; yylval += '"';
@@ -1390,7 +1384,7 @@ static void appendStringLiteral(std::string &yylval, bool raw) {
multiline = true; multiline = true;
} else { } else {
// "" is an empty string, skip the loop // "" is an empty string, skip the loop
goto finish; return;
} }
} }
@@ -1400,7 +1394,7 @@ static void appendStringLiteral(std::string &yylval, bool raw) {
// '\r', '\n' or EOF ends a single-line string early // '\r', '\n' or EOF ends a single-line string early
if (c == EOF || (!multiline && (c == '\r' || c == '\n'))) { if (c == EOF || (!multiline && (c == '\r' || c == '\n'))) {
error("Unterminated string\n"); error("Unterminated string\n");
break; return;
} }
// We'll be staying in the string, so we can safely consume the char // We'll be staying in the string, so we can safely consume the char
@@ -1428,7 +1422,7 @@ static void appendStringLiteral(std::string &yylval, bool raw) {
shiftChar(); shiftChar();
} }
yylval += '"'; yylval += '"';
goto finish; return;
case '\\': // Character escape or macro arg case '\\': // Character escape or macro arg
if (raw) if (raw)
@@ -1506,10 +1500,6 @@ static void appendStringLiteral(std::string &yylval, bool raw) {
yylval += c; yylval += c;
} }
finish:
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
} }
// Lexer core // Lexer core
@@ -1981,12 +1971,11 @@ finish:
static Token skipIfBlock(bool toEndc) { static Token skipIfBlock(bool toEndc) {
lexer_SetMode(LEXER_NORMAL); lexer_SetMode(LEXER_NORMAL);
uint32_t startingDepth = lexer_GetIFDepth(); uint32_t startingDepth = lexer_GetIFDepth();
Token token;
bool atLineStart = lexerState->atLineStart;
// Prevent expanding macro args and symbol interpolation in this state bool atLineStart = lexerState->atLineStart;
lexerState->disableMacroArgs = true; Defer notAtLineStart{[&] { lexerState->atLineStart = false; }};
lexerState->disableInterpolation = true;
Defer reenableExpansions = scopedDisableExpansions();
for (;;) { for (;;) {
if (atLineStart) { if (atLineStart) {
@@ -2000,8 +1989,7 @@ static Token skipIfBlock(bool toEndc) {
if (startsIdentifier(c)) { if (startsIdentifier(c)) {
shiftChar(); shiftChar();
token = readIdentifier(c); switch (Token token = readIdentifier(c); token.type) {
switch (token.type) {
case T_(POP_IF): case T_(POP_IF):
lexer_IncIFDepth(); lexer_IncIFDepth();
break; break;
@@ -2010,7 +1998,7 @@ static Token skipIfBlock(bool toEndc) {
if (lexer_ReachedELSEBlock()) if (lexer_ReachedELSEBlock())
fatalerror("Found ELIF after an ELSE block\n"); fatalerror("Found ELIF after an ELSE block\n");
if (!toEndc && lexer_GetIFDepth() == startingDepth) if (!toEndc && lexer_GetIFDepth() == startingDepth)
goto finish; return token;
break; break;
case T_(POP_ELSE): case T_(POP_ELSE):
@@ -2018,12 +2006,12 @@ static Token skipIfBlock(bool toEndc) {
fatalerror("Found ELSE after an ELSE block\n"); fatalerror("Found ELSE after an ELSE block\n");
lexer_ReachELSEBlock(); lexer_ReachELSEBlock();
if (!toEndc && lexer_GetIFDepth() == startingDepth) if (!toEndc && lexer_GetIFDepth() == startingDepth)
goto finish; return token;
break; break;
case T_(POP_ENDC): case T_(POP_ENDC):
if (lexer_GetIFDepth() == startingDepth) if (lexer_GetIFDepth() == startingDepth)
goto finish; return token;
lexer_DecIFDepth(); lexer_DecIFDepth();
break; break;
@@ -2039,8 +2027,7 @@ static Token skipIfBlock(bool toEndc) {
int c = nextChar(); int c = nextChar();
if (c == EOF) { if (c == EOF) {
token = Token(T_(YYEOF)); return Token(T_(YYEOF));
goto finish;
} else if (c == '\\') { } else if (c == '\\') {
// Unconditionally skip the next char, including line continuations // Unconditionally skip the next char, including line continuations
c = nextChar(); c = nextChar();
@@ -2056,13 +2043,6 @@ static Token skipIfBlock(bool toEndc) {
} }
} while (!atLineStart); } while (!atLineStart);
} }
finish:
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
lexerState->atLineStart = false;
return token;
} }
static Token yylex_SKIP_TO_ELIF() { static Token yylex_SKIP_TO_ELIF() {
@@ -2076,11 +2056,11 @@ static Token yylex_SKIP_TO_ENDC() {
static Token yylex_SKIP_TO_ENDR() { static Token yylex_SKIP_TO_ENDR() {
lexer_SetMode(LEXER_NORMAL); lexer_SetMode(LEXER_NORMAL);
int depth = 1; int depth = 1;
bool atLineStart = lexerState->atLineStart;
// Prevent expanding macro args and symbol interpolation in this state bool atLineStart = lexerState->atLineStart;
lexerState->disableMacroArgs = true; Defer notAtLineStart{[&] { lexerState->atLineStart = false; }};
lexerState->disableInterpolation = true;
Defer reenableExpansions = scopedDisableExpansions();
for (;;) { for (;;) {
if (atLineStart) { if (atLineStart) {
@@ -2104,7 +2084,7 @@ static Token yylex_SKIP_TO_ENDR() {
case T_(POP_ENDR): case T_(POP_ENDR):
depth--; depth--;
if (!depth) if (!depth)
goto finish; return Token(T_(YYEOF)); // yywrap() will finish the REPT/FOR loop
break; break;
case T_(POP_IF): case T_(POP_IF):
@@ -2127,7 +2107,7 @@ static Token yylex_SKIP_TO_ENDR() {
int c = nextChar(); int c = nextChar();
if (c == EOF) { if (c == EOF) {
goto finish; return Token(T_(YYEOF));
} else if (c == '\\') { } else if (c == '\\') {
// Unconditionally skip the next char, including line continuations // Unconditionally skip the next char, including line continuations
c = nextChar(); c = nextChar();
@@ -2143,14 +2123,6 @@ static Token yylex_SKIP_TO_ENDR() {
} }
} while (!atLineStart); } while (!atLineStart);
} }
finish:
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
lexerState->atLineStart = false;
// yywrap() will finish the REPT/FOR loop
return Token(T_(YYEOF));
} }
yy::parser::symbol_type yylex() { yy::parser::symbol_type yylex() {
@@ -2195,11 +2167,9 @@ static Capture startCapture() {
// The following assertion checks that. // The following assertion checks that.
assert(lexerState->atLineStart); assert(lexerState->atLineStart);
assert(!lexerState->capturing); assert(!lexerState->capturing && lexerState->captureBuf == nullptr);
lexerState->capturing = true; lexerState->capturing = true;
lexerState->captureSize = 0; lexerState->captureSize = 0;
lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = true;
Capture capture = {.lineNo = lexer_GetLineNo(), .body = nullptr, .size = 0}; Capture capture = {.lineNo = lexer_GetLineNo(), .body = nullptr, .size = 0};
if (auto *mmap = std::get_if<MmappedContent>(&lexerState->content); if (auto *mmap = std::get_if<MmappedContent>(&lexerState->content);
@@ -2231,13 +2201,13 @@ static void endCapture(Capture &capture) {
lexerState->capturing = false; lexerState->capturing = false;
lexerState->captureBuf = nullptr; lexerState->captureBuf = nullptr;
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
} }
Capture lexer_CaptureRept() { Capture lexer_CaptureRept() {
Capture capture = startCapture(); Capture capture = startCapture();
Defer reenableExpansions = scopedDisableExpansions();
size_t depth = 0; size_t depth = 0;
int c = EOF; int c = EOF;
@@ -2258,10 +2228,11 @@ Capture lexer_CaptureRept() {
case T_(POP_ENDR): case T_(POP_ENDR):
if (!depth) { if (!depth) {
endCapture(capture);
// The final ENDR has been captured, but we don't want it! // The final ENDR has been captured, but we don't want it!
// We know we have read exactly "ENDR", not e.g. an EQUS // We know we have read exactly "ENDR", not e.g. an EQUS
lexerState->captureSize -= strlen("ENDR"); capture.size -= strlen("ENDR");
goto finish; return capture;
} }
depth--; depth--;
break; break;
@@ -2275,26 +2246,22 @@ Capture lexer_CaptureRept() {
for (;; c = nextChar()) { for (;; c = nextChar()) {
if (c == EOF) { if (c == EOF) {
error("Unterminated REPT/FOR block\n"); error("Unterminated REPT/FOR block\n");
goto finish; endCapture(capture);
capture.body = nullptr; // Indicates that it reached EOF before an ENDR
return capture;
} else if (c == '\n' || c == '\r') { } else if (c == '\n' || c == '\r') {
handleCRLF(c); handleCRLF(c);
break; break;
} }
} }
} }
finish:
endCapture(capture);
if (c == EOF)
capture.body = nullptr; // Indicates that it reached EOF before an ENDR terminated it
return capture;
} }
Capture lexer_CaptureMacro() { Capture lexer_CaptureMacro() {
Capture capture = startCapture(); Capture capture = startCapture();
Defer reenableExpansions = scopedDisableExpansions();
// If the file is `mmap`ed, we need not to unmap it to keep access to the macro // If the file is `mmap`ed, we need not to unmap it to keep access to the macro
if (auto *mmap = std::get_if<MmappedContent>(&lexerState->content); mmap) if (auto *mmap = std::get_if<MmappedContent>(&lexerState->content); mmap)
mmap->isReferenced = true; mmap->isReferenced = true;
@@ -2311,10 +2278,11 @@ Capture lexer_CaptureMacro() {
if (startsIdentifier(c)) { if (startsIdentifier(c)) {
switch (readIdentifier(c).type) { switch (readIdentifier(c).type) {
case T_(POP_ENDM): case T_(POP_ENDM):
endCapture(capture);
// The ENDM has been captured, but we don't want it! // The ENDM has been captured, but we don't want it!
// We know we have read exactly "ENDM", not e.g. an EQUS // We know we have read exactly "ENDM", not e.g. an EQUS
lexerState->captureSize -= strlen("ENDM"); capture.size -= strlen("ENDM");
goto finish; return capture;
default: default:
break; break;
@@ -2325,19 +2293,13 @@ Capture lexer_CaptureMacro() {
for (;; c = nextChar()) { for (;; c = nextChar()) {
if (c == EOF) { if (c == EOF) {
error("Unterminated macro definition\n"); error("Unterminated macro definition\n");
goto finish; endCapture(capture);
capture.body = nullptr; // Indicates that it reached EOF before an ENDM
return capture;
} else if (c == '\n' || c == '\r') { } else if (c == '\n' || c == '\r') {
handleCRLF(c); handleCRLF(c);
break; break;
} }
} }
} }
finish:
endCapture(capture);
if (c == EOF)
capture.body = nullptr; // Indicates that it reached EOF before an ENDM terminated it
return capture;
} }

View File

@@ -12,6 +12,7 @@
#include <vector> #include <vector>
#include "error.hpp" #include "error.hpp"
#include "helpers.hpp" // Defer
#include "asm/fstack.hpp" #include "asm/fstack.hpp"
#include "asm/lexer.hpp" #include "asm/lexer.hpp"
@@ -304,7 +305,6 @@ static void writeFileStackNode(FileStackNode const &node, FILE *file) {
// Write an objectfile // Write an objectfile
void out_WriteObject() { void out_WriteObject() {
FILE *file; FILE *file;
if (objectName != "-") { if (objectName != "-") {
file = fopen(objectName.c_str(), "wb"); file = fopen(objectName.c_str(), "wb");
} else { } else {
@@ -313,6 +313,7 @@ void out_WriteObject() {
} }
if (!file) if (!file)
err("Failed to open object file '%s'", objectName.c_str()); err("Failed to open object file '%s'", objectName.c_str());
Defer closeFile{[&] { fclose(file); }};
// Also write symbols that weren't written above // Also write symbols that weren't written above
sym_ForEach(registerUnregisteredSymbol); sym_ForEach(registerUnregisteredSymbol);
@@ -349,8 +350,6 @@ void out_WriteObject() {
for (Assertion &assert : assertions) for (Assertion &assert : assertions)
writeassert(assert, file); writeassert(assert, file);
fclose(file);
} }
// Set the objectfilename // Set the objectfilename

View File

@@ -833,8 +833,9 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
if (!checkcodesection()) if (!checkcodesection())
return; return;
std::optional<std::string> fullPath = fstk_FindFile(name); FILE *file = nullptr;
FILE *file = fullPath ? fopen(fullPath->c_str(), "rb") : nullptr; if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath)
file = fopen(fullPath->c_str(), "rb");
if (!file) { if (!file) {
if (generatedMissingIncludes) { if (generatedMissingIncludes) {
if (verbose) if (verbose)
@@ -845,6 +846,7 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno)); error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
return; return;
} }
Defer closeFile{[&] { fclose(file); }};
int32_t fsize = -1; int32_t fsize = -1;
int byte; int byte;
@@ -854,12 +856,12 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
if (startPos > fsize) { if (startPos > fsize) {
error("Specified start position is greater than length of file\n"); error("Specified start position is greater than length of file\n");
goto cleanup; return;
} }
fseek(file, startPos, SEEK_SET); fseek(file, startPos, SEEK_SET);
if (!reserveSpace(fsize - startPos)) if (!reserveSpace(fsize - startPos))
goto cleanup; return;
} else { } else {
if (errno != ESPIPE) if (errno != ESPIPE)
error( error(
@@ -878,9 +880,6 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
if (ferror(file)) if (ferror(file))
error("Error reading INCBIN file '%s': %s\n", name.c_str(), strerror(errno)); error("Error reading INCBIN file '%s': %s\n", name.c_str(), strerror(errno));
cleanup:
fclose(file);
} }
void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t length) { void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t length) {
@@ -901,8 +900,9 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
if (!reserveSpace(length)) if (!reserveSpace(length))
return; return;
std::optional<std::string> fullPath = fstk_FindFile(name); FILE *file = nullptr;
FILE *file = fullPath ? fopen(fullPath->c_str(), "rb") : nullptr; if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath)
file = fopen(fullPath->c_str(), "rb");
if (!file) { if (!file) {
if (generatedMissingIncludes) { if (generatedMissingIncludes) {
if (verbose) if (verbose)
@@ -913,6 +913,7 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
} }
return; return;
} }
Defer closeFile{[&] { fclose(file); }};
int32_t fsize; int32_t fsize;
@@ -921,7 +922,7 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
if (startPos > fsize) { if (startPos > fsize) {
error("Specified start position is greater than length of file\n"); error("Specified start position is greater than length of file\n");
goto cleanup; return;
} }
if ((startPos + length) > fsize) { if ((startPos + length) > fsize) {
@@ -932,7 +933,7 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
length, length,
fsize fsize
); );
goto cleanup; return;
} }
fseek(file, startPos, SEEK_SET); fseek(file, startPos, SEEK_SET);
@@ -957,9 +958,6 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
error("Premature end of file (%" PRId32 " bytes left to read)\n", length + 1); error("Premature end of file (%" PRId32 " bytes left to read)\n", length + 1);
} }
} }
cleanup:
fclose(file);
} }
// Section stack routines // Section stack routines

View File

@@ -1166,31 +1166,29 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
static bool processFilename(char const *name) { static bool processFilename(char const *name) {
nbErrors = 0; nbErrors = 0;
if (!strcmp(name, "-")) { if (!strcmp(name, "-")) {
(void)setmode(STDIN_FILENO, O_BINARY); (void)setmode(STDIN_FILENO, O_BINARY);
(void)setmode(STDOUT_FILENO, O_BINARY); (void)setmode(STDOUT_FILENO, O_BINARY);
name = "<stdin>"; name = "<stdin>";
processFile(STDIN_FILENO, STDOUT_FILENO, name, 0); processFile(STDIN_FILENO, STDOUT_FILENO, name, 0);
} else { } else {
// POSIX specifies that the results of O_RDWR on a FIFO are undefined. // POSIX specifies that the results of O_RDWR on a FIFO are undefined.
// However, this is necessary to avoid a TOCTTOU, if the file was changed between // However, this is necessary to avoid a TOCTTOU, if the file was changed between
// `stat()` and `open(O_RDWR)`, which could trigger the UB anyway. // `stat()` and `open(O_RDWR)`, which could trigger the UB anyway.
// Thus, we're going to hope that either the `open` fails, or it succeeds but IO // Thus, we're going to hope that either the `open` fails, or it succeeds but IO
// operations may fail, all of which we handle. // operations may fail, all of which we handle.
int input = open(name, O_RDWR | O_BINARY); if (int input = open(name, O_RDWR | O_BINARY); input == -1) {
struct stat stat;
if (input == -1) {
report("FATAL: Failed to open \"%s\" for reading+writing: %s\n", name, strerror(errno)); report("FATAL: Failed to open \"%s\" for reading+writing: %s\n", name, strerror(errno));
goto finish; } else {
} Defer closeInput{[&] { close(input); }};
struct stat stat;
if (fstat(input, &stat) == -1) { if (fstat(input, &stat) == -1) {
report("FATAL: Failed to stat \"%s\": %s\n", name, strerror(errno)); report("FATAL: Failed to stat \"%s\": %s\n", name, strerror(errno));
} else if (!S_ISREG(stat.st_mode)) { // TODO: Do we want to support other types? } else if (!S_ISREG(stat.st_mode)) { // TODO: Do we want to support other types?
report( report(
"FATAL: \"%s\" is not a regular file, and thus cannot be modified in-place\n", name "FATAL: \"%s\" is not a regular file, and thus cannot be modified in-place\n",
name
); );
} else if (stat.st_size < 0x150) { } else if (stat.st_size < 0x150) {
// This check is in theory redundant with the one in `processFile`, but it // This check is in theory redundant with the one in `processFile`, but it
@@ -1203,10 +1201,9 @@ static bool processFilename(char const *name) {
} else { } else {
processFile(input, input, name, stat.st_size); processFile(input, input, name, stat.st_size);
} }
close(input);
} }
finish: }
if (nbErrors) if (nbErrors)
fprintf( fprintf(
stderr, stderr,

View File

@@ -1113,7 +1113,7 @@ void process() {
case ProtoPalette::THEY_BIGGER: case ProtoPalette::THEY_BIGGER:
// Do nothing, they already contain us // Do nothing, they already contain us
attrs.protoPaletteID = n; attrs.protoPaletteID = n;
goto contained; goto continue_visiting_tiles; // Can't `continue` from within a nested loop
case ProtoPalette::NEITHER: case ProtoPalette::NEITHER:
break; // Keep going break; // Keep going
@@ -1139,7 +1139,7 @@ void process() {
); );
} }
protoPalettes.push_back(tileColors); protoPalettes.push_back(tileColors);
contained:; continue_visiting_tiles:;
} }
options.verbosePrint( options.verbosePrint(

View File

@@ -393,7 +393,7 @@ void assign_AssignSections() {
fprintf(stderr, "%c \"%s\"", nbSections == 0 ? ';' : ',', section->name.c_str()); fprintf(stderr, "%c \"%s\"", nbSections == 0 ? ';' : ',', section->name.c_str());
nbSections++; nbSections++;
if (nbSections == 10) if (nbSections == 10)
goto max_out; goto max_out; // Can't `break` out of a nested loop
} }
} }

View File

@@ -320,7 +320,7 @@ static void parseScrambleSpec(char const *spec) {
argErr('S', "Cannot imply limit for region \"%.*s\"", regionNamePrintLen, regionName); argErr('S', "Cannot imply limit for region \"%.*s\"", regionNamePrintLen, regionName);
} }
next: next: // Can't `continue` a `for` loop with this nontrivial iteration logic
if (spec) { if (spec) {
assert(*spec == ',' || *spec == '\0'); assert(*spec == ',' || *spec == '\0');
if (*spec == ',') if (*spec == ',')

View File

@@ -473,7 +473,6 @@ static void readAssertion(
void obj_ReadFile(char const *fileName, unsigned int fileID) { void obj_ReadFile(char const *fileName, unsigned int fileID) {
FILE *file; FILE *file;
if (strcmp(fileName, "-")) { if (strcmp(fileName, "-")) {
file = fopen(fileName, "rb"); file = fopen(fileName, "rb");
} else { } else {
@@ -482,6 +481,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
} }
if (!file) if (!file)
err("Failed to open file \"%s\"", fileName); err("Failed to open file \"%s\"", fileName);
Defer closeFile{[&] { fclose(file); }};
// First, check if the object is a RGBDS object or a SDCC one. If the first byte is 'R', // First, check if the object is a RGBDS object or a SDCC one. If the first byte is 'R',
// we'll assume it's a RGBDS object file, and otherwise, that it's a SDCC object file. // we'll assume it's a RGBDS object file, and otherwise, that it's a SDCC object file.
@@ -630,8 +630,6 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
} }
} }
} }
fclose(file);
} }
void obj_CheckAssertions() { void obj_CheckAssertions() {

View File

@@ -199,6 +199,10 @@ static void writeROM() {
if (!outputFile) if (!outputFile)
err("Failed to open output file \"%s\"", outputFileName); err("Failed to open output file \"%s\"", outputFileName);
} }
Defer closeOutputFile{[&] {
if (outputFile)
fclose(outputFile);
}};
if (overlayFileName) { if (overlayFileName) {
if (strcmp(overlayFileName, "-")) { if (strcmp(overlayFileName, "-")) {
@@ -210,6 +214,10 @@ static void writeROM() {
if (!overlayFile) if (!overlayFile)
err("Failed to open overlay file \"%s\"", overlayFileName); err("Failed to open overlay file \"%s\"", overlayFileName);
} }
Defer closeOverlayFile{[&] {
if (overlayFile)
fclose(overlayFile);
}};
uint32_t nbOverlayBanks = checkOverlaySize(); uint32_t nbOverlayBanks = checkOverlaySize();
@@ -230,11 +238,6 @@ static void writeROM() {
sectionTypeInfo[SECTTYPE_ROMX].size sectionTypeInfo[SECTTYPE_ROMX].size
); );
} }
if (outputFile)
fclose(outputFile);
if (overlayFile)
fclose(overlayFile);
} }
// Checks whether this character is legal as the first character of a symbol's name in a sym file // Checks whether this character is legal as the first character of a symbol's name in a sym file
@@ -533,6 +536,7 @@ static void writeSym() {
} }
if (!symFile) if (!symFile)
err("Failed to open sym file \"%s\"", symFileName); err("Failed to open sym file \"%s\"", symFileName);
Defer closeSymFile{[&] { fclose(symFile); }};
fputs("; File generated by rgblink\n", symFile); fputs("; File generated by rgblink\n", symFile);
@@ -542,8 +546,6 @@ static void writeSym() {
for (uint32_t bank = 0; bank < sections[type].size(); bank++) for (uint32_t bank = 0; bank < sections[type].size(); bank++)
writeSymBank(sections[type][bank], type, bank); writeSymBank(sections[type][bank], type, bank);
} }
fclose(symFile);
} }
// Writes the map file, if applicable. // Writes the map file, if applicable.
@@ -559,6 +561,7 @@ static void writeMap() {
} }
if (!mapFile) if (!mapFile)
err("Failed to open map file \"%s\"", mapFileName); err("Failed to open map file \"%s\"", mapFileName);
Defer closeMapFile{[&] { fclose(mapFile); }};
writeMapSummary(); writeMapSummary();
@@ -568,8 +571,6 @@ static void writeMap() {
for (uint32_t bank = 0; bank < sections[type].size(); bank++) for (uint32_t bank = 0; bank < sections[type].size(); bank++)
writeMapBank(sections[type][bank], type, bank); writeMapBank(sections[type][bank], type, bank);
} }
fclose(mapFile);
} }
void out_WriteFiles() { void out_WriteFiles() {

View File

@@ -225,7 +225,6 @@ static uint8_t parseHexDigit(int c) {
} }
yy::parser::symbol_type yylex() { yy::parser::symbol_type yylex() {
try_again: // Can't use a `do {} while(0)` loop, otherwise compilers (wrongly) think it can end.
auto &context = lexerStack.back(); auto &context = lexerStack.back();
auto c = context.file.sbumpc(); auto c = context.file.sbumpc();
@@ -245,7 +244,7 @@ try_again: // Can't use a `do {} while(0)` loop, otherwise compilers (wrongly) t
// Basically yywrap(). // Basically yywrap().
if (lexerStack.size() != 1) { if (lexerStack.size() != 1) {
lexerStack.pop_back(); lexerStack.pop_back();
goto try_again; return yylex();
} else if (!atEof) { } else if (!atEof) {
// Inject a newline at EOF, to avoid errors for files that don't end with one. // Inject a newline at EOF, to avoid errors for files that don't end with one.
atEof = true; atEof = true;
@@ -353,7 +352,7 @@ try_again: // Can't use a `do {} while(0)` loop, otherwise compilers (wrongly) t
} }
scriptError(context, "Unknown keyword \"%s\"", ident.c_str()); scriptError(context, "Unknown keyword \"%s\"", ident.c_str());
goto try_again; // Try lexing another token. return yylex();
} else { } else {
scriptError(context, "Unexpected character '%s'", printChar(c)); scriptError(context, "Unexpected character '%s'", printChar(c));
// Keep reading characters until the EOL, to avoid reporting too many errors. // Keep reading characters until the EOL, to avoid reporting too many errors.
@@ -363,7 +362,7 @@ try_again: // Can't use a `do {} while(0)` loop, otherwise compilers (wrongly) t
} }
context.file.sbumpc(); context.file.sbumpc();
} }
goto try_again; return yylex();
} }
// Not marking as unreachable; this will generate a warning if any codepath forgets to return. // Not marking as unreachable; this will generate a warning if any codepath forgets to return.
} }

View File

@@ -797,6 +797,10 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
} }
} }
#undef expectEol
#undef expectToken
#undef getToken
if (!data.empty()) if (!data.empty())
warning(&where, lineNo, "Last 'T' line had no 'R' line (ignored)"); warning(&where, lineNo, "Last 'T' line had no 'R' line (ignored)");
if (fileSections.size() < expectedNbAreas) if (fileSections.size() < expectedNbAreas)
@@ -841,10 +845,4 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// Calling `sect_AddSection` invalidates the contents of `fileSections`! // Calling `sect_AddSection` invalidates the contents of `fileSections`!
sect_AddSection(std::move(section)); sect_AddSection(std::move(section));
} }
#undef expectEol
#undef expectToken
#undef getToken
fclose(file);
} }