Refactor warnings and errors (#1728)

* Remove `err` and `warn`, keep `errx` and `warnx`, using them in RGBGFX too

* Separate RGBGFX and RGBLINK warnings/errors from main options

* Separate `report` function into `error` and `fatal` messages

* Implicit newlines for most RGBASM errors
This commit is contained in:
Rangi
2025-07-08 12:58:23 -04:00
committed by GitHub
parent 991b74dd0d
commit 35962dedc4
39 changed files with 753 additions and 757 deletions

View File

@@ -84,6 +84,7 @@ rgblink_obj := \
src/link/sdas_obj.o \ src/link/sdas_obj.o \
src/link/section.o \ src/link/section.o \
src/link/symbol.o \ src/link/symbol.o \
src/link/warning.o \
src/extern/getopt.o \ src/extern/getopt.o \
src/extern/utf8decoder.o \ src/extern/utf8decoder.o \
src/error.o \ src/error.o \
@@ -107,6 +108,7 @@ rgbgfx_obj := \
src/gfx/proto_palette.o \ src/gfx/proto_palette.o \
src/gfx/reverse.o \ src/gfx/reverse.o \
src/gfx/rgba.o \ src/gfx/rgba.o \
src/gfx/warning.o \
src/extern/getopt.o \ src/extern/getopt.o \
src/error.o src/error.o

View File

@@ -74,4 +74,11 @@ void fatalerror(char const *fmt, ...);
[[gnu::format(printf, 1, 2)]] [[gnu::format(printf, 1, 2)]]
void error(char const *fmt, ...); void error(char const *fmt, ...);
// Used for errors that make it impossible to assemble correctly, but don't
// affect the following code. The code will fail to assemble but the user will
// get a list of all errors at the end, making it easier to fix all of them at
// once.
[[gnu::format(printf, 1, 2)]]
void errorNoNewline(char const *fmt, ...);
#endif // RGBDS_ASM_WARNING_HPP #endif // RGBDS_ASM_WARNING_HPP

View File

@@ -3,16 +3,10 @@
#ifndef RGBDS_ERROR_HPP #ifndef RGBDS_ERROR_HPP
#define RGBDS_ERROR_HPP #define RGBDS_ERROR_HPP
extern "C" { [[gnu::format(printf, 1, 2)]]
[[gnu::format(printf, 1, 2)]] void warnx(char const *fmt, ...);
void warn(char const *fmt...);
[[gnu::format(printf, 1, 2)]]
void warnx(char const *fmt, ...);
[[gnu::format(printf, 1, 2), noreturn]] [[gnu::format(printf, 1, 2), noreturn]]
void err(char const *fmt, ...); void errx(char const *fmt, ...);
[[gnu::format(printf, 1, 2), noreturn]]
void errx(char const *fmt, ...);
}
#endif // RGBDS_ERROR_HPP #endif // RGBDS_ERROR_HPP

View File

@@ -85,16 +85,9 @@ extern Options options;
void giveUp(); void giveUp();
// If any error has been emitted thus far, calls `giveUp()`. // If any error has been emitted thus far, calls `giveUp()`.
void requireZeroErrors(); void requireZeroErrors();
// Prints a warning, and does not change the error count
[[gnu::format(printf, 1, 2)]]
void warning(char const *fmt, ...);
// Prints an error, and increments the error count // Prints an error, and increments the error count
[[gnu::format(printf, 1, 2)]] [[gnu::format(printf, 1, 2)]]
void error(char const *fmt, ...); void error(char const *fmt, ...);
// Prints an error, and increments the error count
// Does not take format arguments so `format_` and `-Wformat-security` won't complain about
// calling `errorMessage(msg)`.
void errorMessage(char const *msg);
// Prints a fatal error, increments the error count, and gives up // Prints a fatal error, increments the error count, and gives up
[[gnu::format(printf, 1, 2), noreturn]] [[gnu::format(printf, 1, 2), noreturn]]
void fatal(char const *fmt, ...); void fatal(char const *fmt, ...);

21
include/gfx/warning.hpp Normal file
View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_WARNING_HPP
#define RGBDS_GFX_WARNING_HPP
// Prints the error count, and exits with failure
[[noreturn]]
void giveUp();
// If any error has been emitted thus far, calls `giveUp()`
void requireZeroErrors();
// Prints an error, and increments the error count
[[gnu::format(printf, 1, 2)]]
void error(char const *fmt, ...);
// Prints a fatal error, increments the error count, and gives up
[[gnu::format(printf, 1, 2), noreturn]]
void fatal(char const *fmt, ...);
#endif // RGBDS_GFX_WARNING_HPP

View File

@@ -59,11 +59,4 @@ struct FileStackNode {
std::string const &dump(uint32_t curLineNo) const; std::string const &dump(uint32_t curLineNo) const;
}; };
[[gnu::format(printf, 3, 4)]]
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 3, 4)]]
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 3, 4), noreturn]]
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
#endif // RGBDS_LINK_MAIN_HPP #endif // RGBDS_LINK_MAIN_HPP

23
include/link/warning.hpp Normal file
View File

@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_WARNING_HPP
#define RGBDS_LINK_WARNING_HPP
#include <stdint.h>
struct FileStackNode;
[[gnu::format(printf, 3, 4)]]
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 3, 4)]]
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 1, 2)]]
void errorNoDump(char const *fmt, ...);
[[gnu::format(printf, 2, 3)]]
void argErr(char flag, char const *fmt, ...);
[[gnu::format(printf, 3, 4), noreturn]]
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
void requireZeroErrors();
#endif // RGBDS_LINK_WARNING_HPP

View File

@@ -47,30 +47,15 @@ set(rgbasm_src
"asm/section.cpp" "asm/section.cpp"
"asm/symbol.cpp" "asm/symbol.cpp"
"asm/warning.cpp" "asm/warning.cpp"
"extern/getopt.cpp"
"extern/utf8decoder.cpp" "extern/utf8decoder.cpp"
"diagnostics.cpp" "diagnostics.cpp"
"error.cpp"
"linkdefs.cpp" "linkdefs.cpp"
"opmath.cpp" "opmath.cpp"
"util.cpp" "util.cpp"
) )
set(rgbfix_src
"fix/main.cpp"
)
set(rgbgfx_src
"gfx/main.cpp"
"gfx/pal_packing.cpp"
"gfx/pal_sorting.cpp"
"gfx/pal_spec.cpp"
"gfx/process.cpp"
"gfx/proto_palette.cpp"
"gfx/reverse.cpp"
"gfx/rgba.cpp"
"extern/getopt.cpp"
"error.cpp"
)
set(rgblink_src set(rgblink_src
"${BISON_LINKER_SCRIPT_PARSER_OUTPUT_SOURCE}" "${BISON_LINKER_SCRIPT_PARSER_OUTPUT_SOURCE}"
"link/assign.cpp" "link/assign.cpp"
@@ -81,12 +66,35 @@ set(rgblink_src
"link/sdas_obj.cpp" "link/sdas_obj.cpp"
"link/section.cpp" "link/section.cpp"
"link/symbol.cpp" "link/symbol.cpp"
"link/warning.cpp"
"extern/getopt.cpp"
"extern/utf8decoder.cpp" "extern/utf8decoder.cpp"
"error.cpp"
"linkdefs.cpp" "linkdefs.cpp"
"opmath.cpp" "opmath.cpp"
"util.cpp" "util.cpp"
) )
set(rgbfix_src
"fix/main.cpp"
"extern/getopt.cpp"
"error.cpp"
)
set(rgbgfx_src
"gfx/main.cpp"
"gfx/pal_packing.cpp"
"gfx/pal_sorting.cpp"
"gfx/pal_spec.cpp"
"gfx/process.cpp"
"gfx/proto_palette.cpp"
"gfx/reverse.cpp"
"gfx/rgba.cpp"
"gfx/warning.cpp"
"extern/getopt.cpp"
"error.cpp"
)
foreach(PROG "asm" "fix" "gfx" "link") foreach(PROG "asm" "fix" "gfx" "link")
add_executable(rgb${PROG} add_executable(rgb${PROG}
${rgb${PROG}_src} ${rgb${PROG}_src}

View File

@@ -86,14 +86,14 @@ void charmap_New(std::string const &name, std::string const *baseName) {
if (baseName != nullptr) { if (baseName != nullptr) {
if (auto search = charmapMap.find(*baseName); search == charmapMap.end()) { if (auto search = charmapMap.find(*baseName); search == charmapMap.end()) {
error("Base charmap '%s' doesn't exist\n", baseName->c_str()); error("Base charmap '%s' doesn't exist", baseName->c_str());
} else { } else {
baseIdx = search->second; baseIdx = search->second;
} }
} }
if (charmapMap.find(name) != charmapMap.end()) { if (charmapMap.find(name) != charmapMap.end()) {
error("Charmap '%s' already exists\n", name.c_str()); error("Charmap '%s' already exists", name.c_str());
return; return;
} }
@@ -114,7 +114,7 @@ void charmap_New(std::string const &name, std::string const *baseName) {
void charmap_Set(std::string const &name) { void charmap_Set(std::string const &name) {
if (auto search = charmapMap.find(name); search == charmapMap.end()) { if (auto search = charmapMap.find(name); search == charmapMap.end()) {
error("Charmap '%s' doesn't exist\n", name.c_str()); error("Charmap '%s' doesn't exist", name.c_str());
} else { } else {
currentCharmap = &charmapList[search->second]; currentCharmap = &charmapList[search->second];
} }
@@ -126,7 +126,7 @@ void charmap_Push() {
void charmap_Pop() { void charmap_Pop() {
if (charmapStack.empty()) { if (charmapStack.empty()) {
error("No entries in the charmap stack\n"); error("No entries in the charmap stack");
return; return;
} }
@@ -136,13 +136,13 @@ void charmap_Pop() {
void charmap_CheckStack() { void charmap_CheckStack() {
if (!charmapStack.empty()) { if (!charmapStack.empty()) {
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHC` without corresponding `POPC`\n"); warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHC` without corresponding `POPC`");
} }
} }
void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) { void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
if (mapping.empty()) { if (mapping.empty()) {
error("Cannot map an empty string\n"); error("Cannot map an empty string");
return; return;
} }
@@ -168,7 +168,7 @@ void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
CharmapNode &node = charmap.nodes[nodeIdx]; CharmapNode &node = charmap.nodes[nodeIdx];
if (node.isTerminal()) { if (node.isTerminal()) {
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping\n"); warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping");
} }
std::swap(node.value, value); std::swap(node.value, value);
@@ -268,7 +268,7 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
// This will write the codepoint's value to `output`, little-endian // This will write the codepoint's value to `output`, little-endian
for (uint32_t state = 0, codepoint = 0; inputIdx + codepointLen < input.length();) { for (uint32_t state = 0, codepoint = 0; inputIdx + codepointLen < input.length();) {
if (decode(&state, &codepoint, input[inputIdx + codepointLen]) == 1) { if (decode(&state, &codepoint, input[inputIdx + codepointLen]) == 1) {
error("Input string is not valid UTF-8\n"); error("Input string is not valid UTF-8");
codepointLen = 1; codepointLen = 1;
break; break;
} }
@@ -286,11 +286,11 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
// Warn if this character is not mapped but any others are // Warn if this character is not mapped but any others are
if (int firstChar = input[inputIdx]; charmap.nodes.size() > 1) { if (int firstChar = input[inputIdx]; charmap.nodes.size() > 1) {
warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s\n", printChar(firstChar)); warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s", printChar(firstChar));
} else if (charmap.name != DEFAULT_CHARMAP_NAME) { } else if (charmap.name != DEFAULT_CHARMAP_NAME) {
warning( warning(
WARNING_UNMAPPED_CHAR_2, WARNING_UNMAPPED_CHAR_2,
"Unmapped character %s not in " DEFAULT_CHARMAP_NAME " charmap\n", "Unmapped character %s not in " DEFAULT_CHARMAP_NAME " charmap",
printChar(firstChar) printChar(firstChar)
); );
} }

View File

@@ -162,19 +162,19 @@ void FormatSpec::appendString(std::string &str, std::string const &value) const
} }
if (sign) { if (sign) {
error("Formatting string with sign flag '%c'\n", sign); error("Formatting string with sign flag '%c'", sign);
} }
if (padZero) { if (padZero) {
error("Formatting string with padding flag '0'\n"); error("Formatting string with padding flag '0'");
} }
if (hasFrac) { if (hasFrac) {
error("Formatting string with fractional width\n"); error("Formatting string with fractional width");
} }
if (hasPrec) { if (hasPrec) {
error("Formatting string with fractional precision\n"); error("Formatting string with fractional precision");
} }
if (useType != 's') { if (useType != 's') {
error("Formatting string as type '%c'\n", useType); error("Formatting string as type '%c'", useType);
} }
std::string useValue = exact ? escapeString(value) : value; std::string useValue = exact ? escapeString(value) : value;
@@ -203,16 +203,16 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
if (useType != 'X' && useType != 'x' && useType != 'b' && useType != 'o' && useType != 'f' if (useType != 'X' && useType != 'x' && useType != 'b' && useType != 'o' && useType != 'f'
&& useExact) { && useExact) {
error("Formatting type '%c' with exact flag '#'\n", useType); error("Formatting type '%c' with exact flag '#'", useType);
} }
if (useType != 'f' && hasFrac) { if (useType != 'f' && hasFrac) {
error("Formatting type '%c' with fractional width\n", useType); error("Formatting type '%c' with fractional width", useType);
} }
if (useType != 'f' && hasPrec) { if (useType != 'f' && hasPrec) {
error("Formatting type '%c' with fractional precision\n", useType); error("Formatting type '%c' with fractional precision", useType);
} }
if (useType == 's') { if (useType == 's') {
error("Formatting number as type 's'\n"); error("Formatting number as type 's'");
} }
char signChar = sign; // 0 or ' ' or '+' char signChar = sign; // 0 or ' ' or '+'
@@ -254,7 +254,7 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
// Default fractional width (C++'s is 6 for "%f"; here 5 is enough for Q16.16) // Default fractional width (C++'s is 6 for "%f"; here 5 is enough for Q16.16)
size_t useFracWidth = hasFrac ? fracWidth : 5; size_t useFracWidth = hasFrac ? fracWidth : 5;
if (useFracWidth > 255) { if (useFracWidth > 255) {
error("Fractional width %zu too long, limiting to 255\n", useFracWidth); error("Fractional width %zu too long, limiting to 255", useFracWidth);
useFracWidth = 255; useFracWidth = 255;
} }
@@ -262,7 +262,7 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
size_t usePrec = hasPrec ? precision : defaultPrec; size_t usePrec = hasPrec ? precision : defaultPrec;
if (usePrec < 1 || usePrec > 31) { if (usePrec < 1 || usePrec > 31) {
error( error(
"Fixed-point constant precision %zu invalid, defaulting to %zu\n", "Fixed-point constant precision %zu invalid, defaulting to %zu",
usePrec, usePrec,
defaultPrec defaultPrec
); );

View File

@@ -160,7 +160,7 @@ bool yywrap() {
if (ifDepth != 0) { if (ifDepth != 0) {
fatalerror( fatalerror(
"Ended block with %" PRIu32 " unterminated IF construct%s\n", "Ended block with %" PRIu32 " unterminated IF construct%s",
ifDepth, ifDepth,
ifDepth == 1 ? "" : "s" ifDepth == 1 ? "" : "s"
); );
@@ -188,7 +188,7 @@ bool yywrap() {
// This error message will refer to the current iteration // This error message will refer to the current iteration
if (sym->type != SYM_VAR) { if (sym->type != SYM_VAR) {
fatalerror("Failed to update FOR symbol value\n"); fatalerror("Failed to update FOR symbol value");
} }
} }
// Advance to the next iteration // Advance to the next iteration
@@ -211,7 +211,7 @@ bool yywrap() {
static void checkRecursionDepth() { static void checkRecursionDepth() {
if (contextStack.size() > maxRecursionDepth) { if (contextStack.size() > maxRecursionDepth) {
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth); fatalerror("Recursion limit (%zu) exceeded", maxRecursionDepth);
} }
} }
@@ -317,13 +317,13 @@ void fstk_RunInclude(std::string const &path, bool preInclude) {
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
failedOnMissingInclude = true; failedOnMissingInclude = true;
} else { } else {
error("Unable to open included file '%s': %s\n", path.c_str(), strerror(errno)); error("Unable to open included file '%s': %s", path.c_str(), strerror(errno));
} }
return; return;
} }
if (!newFileContext(*fullPath, false)) { if (!newFileContext(*fullPath, false)) {
fatalerror("Failed to set up lexer for file include\n"); // LCOV_EXCL_LINE fatalerror("Failed to set up lexer for file include"); // LCOV_EXCL_LINE
} }
} }
@@ -332,14 +332,14 @@ void fstk_RunMacro(std::string const &macroName, std::shared_ptr<MacroArgs> macr
if (!macro) { if (!macro) {
if (sym_IsPurgedExact(macroName)) { if (sym_IsPurgedExact(macroName)) {
error("Macro \"%s\" not defined; it was purged\n", macroName.c_str()); error("Macro \"%s\" not defined; it was purged", macroName.c_str());
} else { } else {
error("Macro \"%s\" not defined\n", macroName.c_str()); error("Macro \"%s\" not defined", macroName.c_str());
} }
return; return;
} }
if (macro->type != SYM_MACRO) { if (macro->type != SYM_MACRO) {
error("\"%s\" is not a macro\n", macroName.c_str()); error("\"%s\" is not a macro", macroName.c_str());
return; return;
} }
@@ -372,13 +372,11 @@ void fstk_RunFor(
} else if (step < 0 && stop < start) { } else if (step < 0 && stop < start) {
count = (static_cast<int64_t>(start) - stop - 1) / -static_cast<int64_t>(step) + 1; count = (static_cast<int64_t>(start) - stop - 1) / -static_cast<int64_t>(step) + 1;
} else if (step == 0) { } else if (step == 0) {
error("FOR cannot have a step value of 0\n"); error("FOR cannot have a step value of 0");
} }
if ((step > 0 && start > stop) || (step < 0 && start < stop)) { if ((step > 0 && start > stop) || (step < 0 && start < stop)) {
warning( warning(WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d", start, stop, step);
WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d\n", start, stop, step
);
} }
if (count == 0) { if (count == 0) {
@@ -394,7 +392,7 @@ void fstk_RunFor(
bool fstk_Break() { bool fstk_Break() {
if (contextStack.top().fileInfo->type != NODE_REPT) { if (contextStack.top().fileInfo->type != NODE_REPT) {
error("BREAK can only be used inside a REPT/FOR block\n"); error("BREAK can only be used inside a REPT/FOR block");
return false; return false;
} }
@@ -404,14 +402,14 @@ bool fstk_Break() {
void fstk_NewRecursionDepth(size_t newDepth) { void fstk_NewRecursionDepth(size_t newDepth) {
if (contextStack.size() > newDepth + 1) { if (contextStack.size() > newDepth + 1) {
fatalerror("Recursion limit (%zu) exceeded\n", newDepth); fatalerror("Recursion limit (%zu) exceeded", newDepth);
} }
maxRecursionDepth = newDepth; maxRecursionDepth = newDepth;
} }
void fstk_Init(std::string const &mainPath, size_t maxDepth) { void fstk_Init(std::string const &mainPath, size_t maxDepth) {
if (!newFileContext(mainPath, true)) { if (!newFileContext(mainPath, true)) {
fatalerror("Failed to open main file\n"); fatalerror("Failed to open main file");
} }
maxRecursionDepth = maxDepth; maxRecursionDepth = maxDepth;

View File

@@ -384,7 +384,7 @@ void lexer_IncIFDepth() {
void lexer_DecIFDepth() { void lexer_DecIFDepth() {
if (lexerState->ifStack.empty()) { if (lexerState->ifStack.empty()) {
fatalerror("Found ENDC outside of an IF construct\n"); fatalerror("Found ENDC outside of an IF construct");
} }
lexerState->ifStack.pop_front(); lexerState->ifStack.pop_front();
@@ -423,7 +423,7 @@ bool LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
struct stat statBuf; struct stat statBuf;
if (stat(filePath.c_str(), &statBuf) != 0) { if (stat(filePath.c_str(), &statBuf) != 0) {
// LCOV_EXCL_START // LCOV_EXCL_START
error("Failed to stat file \"%s\": %s\n", filePath.c_str(), strerror(errno)); error("Failed to stat file \"%s\": %s", filePath.c_str(), strerror(errno));
return false; return false;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
@@ -432,7 +432,7 @@ bool LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
int fd = open(path.c_str(), O_RDONLY); int fd = open(path.c_str(), O_RDONLY);
if (fd < 0) { if (fd < 0) {
// LCOV_EXCL_START // LCOV_EXCL_START
error("Failed to open file \"%s\": %s\n", path.c_str(), strerror(errno)); error("Failed to open file \"%s\": %s", path.c_str(), strerror(errno));
return false; return false;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
@@ -565,7 +565,7 @@ size_t BufferedContent::readMore(size_t startIndex, size_t nbChars) {
if (nbReadChars == -1) { if (nbReadChars == -1) {
// LCOV_EXCL_START // LCOV_EXCL_START
fatalerror("Error while reading \"%s\": %s\n", lexerState->path.c_str(), strerror(errno)); fatalerror("Error while reading \"%s\": %s", lexerState->path.c_str(), strerror(errno));
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
@@ -600,7 +600,7 @@ static void beginExpansion(std::shared_ptr<std::string> str, std::optional<std::
void lexer_CheckRecursionDepth() { void lexer_CheckRecursionDepth() {
if (lexerState->expansions.size() > maxRecursionDepth + 1) { if (lexerState->expansions.size() > maxRecursionDepth + 1) {
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth); fatalerror("Recursion limit (%zu) exceeded", maxRecursionDepth);
} }
} }
@@ -637,7 +637,7 @@ static uint32_t readBracketedMacroArgNum() {
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
uint32_t n = readDecimalNumber(0); uint32_t n = readDecimalNumber(0);
if (n > INT32_MAX) { if (n > INT32_MAX) {
error("Number in bracketed macro argument is too large\n"); error("Number in bracketed macro argument is too large");
return 0; return 0;
} }
num = negative ? -n : static_cast<int32_t>(n); num = negative ? -n : static_cast<int32_t>(n);
@@ -646,7 +646,7 @@ static uint32_t readBracketedMacroArgNum() {
shiftChar(); shiftChar();
c = peek(); c = peek();
if (!startsIdentifier(c)) { if (!startsIdentifier(c)) {
error("Empty raw symbol in bracketed macro argument\n"); error("Empty raw symbol in bracketed macro argument");
return 0; return 0;
} }
} }
@@ -662,14 +662,14 @@ static uint32_t readBracketedMacroArgNum() {
if (!sym) { if (!sym) {
if (sym_IsPurgedScoped(symName)) { if (sym_IsPurgedScoped(symName)) {
error("Bracketed symbol \"%s\" does not exist; it was purged\n", symName.c_str()); error("Bracketed symbol \"%s\" does not exist; it was purged", symName.c_str());
} else { } else {
error("Bracketed symbol \"%s\" does not exist\n", symName.c_str()); error("Bracketed symbol \"%s\" does not exist", symName.c_str());
} }
num = 0; num = 0;
symbolError = true; symbolError = true;
} else if (!sym->isNumeric()) { } else if (!sym->isNumeric()) {
error("Bracketed symbol \"%s\" is not numeric\n", symName.c_str()); error("Bracketed symbol \"%s\" is not numeric", symName.c_str());
num = 0; num = 0;
symbolError = true; symbolError = true;
} else { } else {
@@ -682,13 +682,13 @@ static uint32_t readBracketedMacroArgNum() {
c = peek(); c = peek();
shiftChar(); shiftChar();
if (c != '>') { if (c != '>') {
error("Invalid character in bracketed macro argument %s\n", printChar(c)); error("Invalid character in bracketed macro argument %s", printChar(c));
return 0; return 0;
} else if (empty) { } else if (empty) {
error("Empty bracketed macro argument\n"); error("Empty bracketed macro argument");
return 0; return 0;
} else if (num == 0 && !symbolError) { } else if (num == 0 && !symbolError) {
error("Invalid bracketed macro argument '\\<0>'\n"); error("Invalid bracketed macro argument '\\<0>'");
return 0; return 0;
} else { } else {
return num; return num;
@@ -699,13 +699,13 @@ static std::shared_ptr<std::string> readMacroArg(char name) {
if (name == '@') { if (name == '@') {
auto str = fstk_GetUniqueIDStr(); auto str = fstk_GetUniqueIDStr();
if (!str) { if (!str) {
error("'\\@' cannot be used outside of a macro or REPT/FOR block\n"); error("'\\@' cannot be used outside of a macro or REPT/FOR block");
} }
return str; return str;
} else if (name == '#') { } else if (name == '#') {
MacroArgs *macroArgs = fstk_GetCurrentMacroArgs(); MacroArgs *macroArgs = fstk_GetCurrentMacroArgs();
if (!macroArgs) { if (!macroArgs) {
error("'\\#' cannot be used outside of a macro\n"); error("'\\#' cannot be used outside of a macro");
return nullptr; return nullptr;
} }
@@ -721,13 +721,13 @@ static std::shared_ptr<std::string> readMacroArg(char name) {
MacroArgs *macroArgs = fstk_GetCurrentMacroArgs(); MacroArgs *macroArgs = fstk_GetCurrentMacroArgs();
if (!macroArgs) { if (!macroArgs) {
error("'\\<%" PRIu32 ">' cannot be used outside of a macro\n", num); error("'\\<%" PRIu32 ">' cannot be used outside of a macro", num);
return nullptr; return nullptr;
} }
auto str = macroArgs->getArg(num); auto str = macroArgs->getArg(num);
if (!str) { if (!str) {
error("Macro argument '\\<%" PRId32 ">' not defined\n", num); error("Macro argument '\\<%" PRId32 ">' not defined", num);
} }
return str; return str;
} else { } else {
@@ -735,13 +735,13 @@ static std::shared_ptr<std::string> readMacroArg(char name) {
MacroArgs *macroArgs = fstk_GetCurrentMacroArgs(); MacroArgs *macroArgs = fstk_GetCurrentMacroArgs();
if (!macroArgs) { if (!macroArgs) {
error("'\\%c' cannot be used outside of a macro\n", name); error("'\\%c' cannot be used outside of a macro", name);
return nullptr; return nullptr;
} }
auto str = macroArgs->getArg(name - '0'); auto str = macroArgs->getArg(name - '0');
if (!str) { if (!str) {
error("Macro argument '\\%c' not defined\n", name); error("Macro argument '\\%c' not defined", name);
} }
return str; return str;
} }
@@ -945,7 +945,7 @@ static void discardBlockComment() {
switch (c) { switch (c) {
case EOF: case EOF:
error("Unterminated block comment\n"); error("Unterminated block comment");
return; return;
case '\r': case '\r':
handleCRLF(c); handleCRLF(c);
@@ -957,7 +957,7 @@ static void discardBlockComment() {
continue; continue;
case '/': case '/':
if (peek() == '*') { if (peek() == '*') {
warning(WARNING_NESTED_COMMENT, "/* in block comment\n"); warning(WARNING_NESTED_COMMENT, "/* in block comment");
} }
continue; continue;
case '*': case '*':
@@ -999,7 +999,7 @@ static void discardLineContinuation() {
} else if (c == ';') { } else if (c == ';') {
discardComment(); discardComment();
} else { } else {
error("Begun line continuation, but encountered character %s\n", printChar(c)); error("Begun line continuation, but encountered character %s", printChar(c));
break; break;
} }
} }
@@ -1041,7 +1041,7 @@ static uint32_t readFractionalPart(uint32_t integer) {
break; break;
} }
if (divisor > (UINT32_MAX - (c - '0')) / 10) { if (divisor > (UINT32_MAX - (c - '0')) / 10) {
warning(WARNING_LARGE_CONSTANT, "Precision of fixed-point constant is too large\n"); warning(WARNING_LARGE_CONSTANT, "Precision of fixed-point constant is too large");
// Discard any additional digits // Discard any additional digits
shiftChar(); shiftChar();
while (c = peek(), (c >= '0' && c <= '9') || c == '_') { while (c = peek(), (c >= '0' && c <= '9') || c == '_') {
@@ -1064,16 +1064,16 @@ static uint32_t readFractionalPart(uint32_t integer) {
if (precision == 0) { if (precision == 0) {
if (state >= READFRACTIONALPART_PRECISION) { if (state >= READFRACTIONALPART_PRECISION) {
error("Invalid fixed-point constant, no significant digits after 'q'\n"); error("Invalid fixed-point constant, no significant digits after 'q'");
} }
precision = fixPrecision; precision = fixPrecision;
} else if (precision > 31) { } else if (precision > 31) {
error("Fixed-point constant precision must be between 1 and 31\n"); error("Fixed-point constant precision must be between 1 and 31");
precision = fixPrecision; precision = fixPrecision;
} }
if (integer >= (1ULL << (32 - precision))) { if (integer >= (1ULL << (32 - precision))) {
warning(WARNING_LARGE_CONSTANT, "Magnitude of fixed-point constant is too large\n"); warning(WARNING_LARGE_CONSTANT, "Magnitude of fixed-point constant is too large");
} }
// Cast to unsigned avoids undefined overflow behavior // Cast to unsigned avoids undefined overflow behavior
@@ -1096,18 +1096,18 @@ static bool checkDigitErrors(char const *digits, size_t n, char const *type) {
char c = digits[i]; char c = digits[i];
if (!isValidDigit(c)) { if (!isValidDigit(c)) {
error("Invalid digit for %s constant %s\n", type, printChar(c)); error("Invalid digit for %s constant %s", type, printChar(c));
return false; return false;
} }
if (c >= '0' && c < static_cast<char>(n + '0') && c != static_cast<char>(i + '0')) { if (c >= '0' && c < static_cast<char>(n + '0') && c != static_cast<char>(i + '0')) {
error("Changed digit for %s constant %s\n", type, printChar(c)); error("Changed digit for %s constant %s", type, printChar(c));
return false; return false;
} }
for (size_t j = i + 1; j < n; j++) { for (size_t j = i + 1; j < n; j++) {
if (c == digits[j]) { if (c == digits[j]) {
error("Repeated digit for %s constant %s\n", type, printChar(c)); error("Repeated digit for %s constant %s", type, printChar(c));
return false; return false;
} }
} }
@@ -1146,7 +1146,7 @@ static uint32_t readBinaryNumber() {
break; break;
} }
if (value > (UINT32_MAX - bit) / 2) { if (value > (UINT32_MAX - bit) / 2) {
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n"); warning(WARNING_LARGE_CONSTANT, "Integer constant is too large");
} }
value = value * 2 + bit; value = value * 2 + bit;
@@ -1154,7 +1154,7 @@ static uint32_t readBinaryNumber() {
} }
if (empty) { if (empty) {
error("Invalid integer constant, no digits after '%%'\n"); error("Invalid integer constant, no digits after '%%'");
} }
return value; return value;
@@ -1176,7 +1176,7 @@ static uint32_t readOctalNumber() {
} }
if (value > (UINT32_MAX - c) / 8) { if (value > (UINT32_MAX - c) / 8) {
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n"); warning(WARNING_LARGE_CONSTANT, "Integer constant is too large");
} }
value = value * 8 + c; value = value * 8 + c;
@@ -1184,7 +1184,7 @@ static uint32_t readOctalNumber() {
} }
if (empty) { if (empty) {
error("Invalid integer constant, no digits after '&'\n"); error("Invalid integer constant, no digits after '&'");
} }
return value; return value;
@@ -1206,7 +1206,7 @@ static uint32_t readDecimalNumber(int initial) {
} }
if (value > (UINT32_MAX - c) / 10) { if (value > (UINT32_MAX - c) / 10) {
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n"); warning(WARNING_LARGE_CONSTANT, "Integer constant is too large");
} }
value = value * 10 + c; value = value * 10 + c;
@@ -1214,7 +1214,7 @@ static uint32_t readDecimalNumber(int initial) {
} }
if (empty) { if (empty) {
error("Invalid integer constant, no digits\n"); error("Invalid integer constant, no digits");
} }
return value; return value;
@@ -1240,7 +1240,7 @@ static uint32_t readHexNumber() {
} }
if (value > (UINT32_MAX - c) / 16) { if (value > (UINT32_MAX - c) / 16) {
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n"); warning(WARNING_LARGE_CONSTANT, "Integer constant is too large");
} }
value = value * 16 + c; value = value * 16 + c;
@@ -1248,7 +1248,7 @@ static uint32_t readHexNumber() {
} }
if (empty) { if (empty) {
error("Invalid integer constant, no digits after '$'\n"); error("Invalid integer constant, no digits after '$'");
} }
return value; return value;
@@ -1286,11 +1286,10 @@ static uint32_t readGfxConstant() {
} }
if (width == 0) { if (width == 0) {
error("Invalid graphics constant, no digits after '`'\n"); error("Invalid graphics constant, no digits after '`'");
} else if (width == 9) { } else if (width == 9) {
warning( warning(
WARNING_LARGE_CONSTANT, WARNING_LARGE_CONSTANT, "Graphics constant is too long, only first 8 pixels considered"
"Graphics constant is too long, only first 8 pixels considered\n"
); );
} }
@@ -1320,7 +1319,7 @@ static Token readIdentifier(char firstChar, bool raw) {
if (!raw) { if (!raw) {
if (auto search = keywordDict.find(identifier); search != keywordDict.end()) { if (auto search = keywordDict.find(identifier); search != keywordDict.end()) {
if (search == ldio) { if (search == ldio) {
warning(WARNING_OBSOLETE, "LDIO is deprecated; use LDH\n"); warning(WARNING_OBSOLETE, "LDIO is deprecated; use LDH");
} }
return Token(search->second); return Token(search->second);
} }
@@ -1338,7 +1337,7 @@ static Token readIdentifier(char firstChar, bool raw) {
static std::shared_ptr<std::string> readInterpolation(size_t depth) { static std::shared_ptr<std::string> readInterpolation(size_t depth) {
if (depth > maxRecursionDepth) { if (depth > maxRecursionDepth) {
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth); fatalerror("Recursion limit (%zu) exceeded", maxRecursionDepth);
} }
std::string fmtBuf; std::string fmtBuf;
@@ -1360,7 +1359,7 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth) {
} }
continue; // Restart, reading from the new buffer continue; // Restart, reading from the new buffer
} else if (c == EOF || c == '\r' || c == '\n' || c == '"') { } else if (c == EOF || c == '\r' || c == '\n' || c == '"') {
error("Missing }\n"); error("Missing }");
break; break;
} else if (c == '}') { } else if (c == '}') {
shiftChar(); shiftChar();
@@ -1372,7 +1371,7 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth) {
} }
fmt.finishCharacters(); fmt.finishCharacters();
if (!fmt.isValid()) { if (!fmt.isValid()) {
error("Invalid format spec '%s'\n", fmtBuf.c_str()); error("Invalid format spec '%s'", fmtBuf.c_str());
} }
fmtBuf.clear(); // Now that format has been set, restart at beginning of string fmtBuf.clear(); // Now that format has been set, restart at beginning of string
} else { } else {
@@ -1391,7 +1390,7 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth) {
// Don't allow symbols that alias keywords without a '#' prefix. // Don't allow symbols that alias keywords without a '#' prefix.
error( error(
"Interpolated symbol \"%s\" is a reserved keyword; add a '#' prefix to use it as a raw " "Interpolated symbol \"%s\" is a reserved keyword; add a '#' prefix to use it as a raw "
"symbol\n", "symbol",
fmtBuf.c_str() fmtBuf.c_str()
); );
return nullptr; return nullptr;
@@ -1401,9 +1400,9 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth) {
if (!sym || !sym->isDefined()) { if (!sym || !sym->isDefined()) {
if (sym_IsPurgedScoped(fmtBuf)) { if (sym_IsPurgedScoped(fmtBuf)) {
error("Interpolated symbol \"%s\" does not exist; it was purged\n", fmtBuf.c_str()); error("Interpolated symbol \"%s\" does not exist; it was purged", fmtBuf.c_str());
} else { } else {
error("Interpolated symbol \"%s\" does not exist\n", fmtBuf.c_str()); error("Interpolated symbol \"%s\" does not exist", fmtBuf.c_str());
} }
} else if (sym->type == SYM_EQUS) { } else if (sym->type == SYM_EQUS) {
auto buf = std::make_shared<std::string>(); auto buf = std::make_shared<std::string>();
@@ -1414,7 +1413,7 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth) {
fmt.appendNumber(*buf, sym->getConstantValue()); fmt.appendNumber(*buf, sym->getConstantValue());
return buf; return buf;
} else { } else {
error("Interpolated symbol \"%s\" is not a numeric or string symbol\n", fmtBuf.c_str()); error("Interpolated symbol \"%s\" is not a numeric or string symbol", fmtBuf.c_str());
} }
return nullptr; return nullptr;
} }
@@ -1472,7 +1471,7 @@ static std::string readString(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");
return str; return str;
} }
@@ -1560,12 +1559,12 @@ static std::string readString(bool raw) {
continue; // Do not copy an additional character continue; // Do not copy an additional character
case EOF: // Can't really print that one case EOF: // Can't really print that one
error("Illegal character escape at end of input\n"); error("Illegal character escape at end of input");
c = '\\'; c = '\\';
break; break;
default: default:
error("Illegal character escape %s\n", printChar(c)); error("Illegal character escape %s", printChar(c));
shiftChar(); shiftChar();
break; break;
} }
@@ -1616,7 +1615,7 @@ static void appendStringLiteral(std::string &str, 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");
return; return;
} }
@@ -1697,12 +1696,12 @@ static void appendStringLiteral(std::string &str, bool raw) {
} }
case EOF: // Can't really print that one case EOF: // Can't really print that one
error("Illegal character escape at end of input\n"); error("Illegal character escape at end of input");
c = '\\'; c = '\\';
break; break;
default: default:
error("Illegal character escape %s\n", printChar(c)); error("Illegal character escape %s", printChar(c));
shiftChar(); shiftChar();
break; break;
} }
@@ -2077,9 +2076,9 @@ static Token yylex_NORMAL() {
garbage += ", "; garbage += ", ";
garbage += printChar(c); garbage += printChar(c);
} }
error("Unknown characters %s\n", garbage.c_str()); error("Unknown characters %s", garbage.c_str());
} else { } else {
error("Unknown character %s\n", printChar(c)); error("Unknown character %s", printChar(c));
} }
} }
} }
@@ -2203,7 +2202,7 @@ backslash:
continue; continue;
case EOF: // Can't really print that one case EOF: // Can't really print that one
error("Illegal character escape at end of input\n"); error("Illegal character escape at end of input");
c = '\\'; c = '\\';
break; break;
@@ -2211,7 +2210,7 @@ backslash:
// '\#', and '\0'-'\9' should not occur here. // '\#', and '\0'-'\9' should not occur here.
default: default:
error("Illegal character escape %s\n", printChar(c)); error("Illegal character escape %s", printChar(c));
break; break;
} }
[[fallthrough]]; [[fallthrough]];
@@ -2293,7 +2292,7 @@ static Token skipIfBlock(bool toEndc) {
case T_(POP_ELIF): case T_(POP_ELIF):
if (lexer_ReachedELSEBlock()) { if (lexer_ReachedELSEBlock()) {
// This should be redundant, as the parser handles this error first. // This should be redundant, as the parser handles this error first.
fatalerror("Found ELIF after an ELSE block\n"); // LCOV_EXCL_LINE fatalerror("Found ELIF after an ELSE block"); // LCOV_EXCL_LINE
} }
if (!toEndc && lexer_GetIFDepth() == startingDepth) { if (!toEndc && lexer_GetIFDepth() == startingDepth) {
return token; return token;
@@ -2302,7 +2301,7 @@ static Token skipIfBlock(bool toEndc) {
case T_(POP_ELSE): case T_(POP_ELSE):
if (lexer_ReachedELSEBlock()) { if (lexer_ReachedELSEBlock()) {
fatalerror("Found ELSE after an ELSE block\n"); fatalerror("Found ELSE after an ELSE block");
} }
lexer_ReachELSEBlock(); lexer_ReachELSEBlock();
if (!toEndc && lexer_GetIFDepth() == startingDepth) { if (!toEndc && lexer_GetIFDepth() == startingDepth) {
@@ -2552,7 +2551,7 @@ Capture lexer_CaptureRept() {
// Just consume characters until EOL or EOF // Just consume characters until EOL or EOF
for (;; c = nextChar()) { for (;; c = nextChar()) {
if (c == EOF) { if (c == EOF) {
error("Unterminated REPT/FOR block\n"); error("Unterminated REPT/FOR block");
endCapture(capture); endCapture(capture);
capture.span.ptr = nullptr; // Indicates that it reached EOF before an ENDR capture.span.ptr = nullptr; // Indicates that it reached EOF before an ENDR
return capture; return capture;
@@ -2595,7 +2594,7 @@ Capture lexer_CaptureMacro() {
// Just consume characters until EOL or EOF // Just consume characters until EOL or EOF
for (;; c = nextChar()) { for (;; c = nextChar()) {
if (c == EOF) { if (c == EOF) {
error("Unterminated macro definition\n"); error("Unterminated macro definition");
endCapture(capture); endCapture(capture);
capture.span.ptr = nullptr; // Indicates that it reached EOF before an ENDM capture.span.ptr = nullptr; // Indicates that it reached EOF before an ENDM
return capture; return capture;

View File

@@ -52,7 +52,7 @@ std::shared_ptr<std::string> MacroArgs::getAllArgs() const {
void MacroArgs::appendArg(std::shared_ptr<std::string> arg) { void MacroArgs::appendArg(std::shared_ptr<std::string> arg) {
if (arg->empty()) { if (arg->empty()) {
warning(WARNING_EMPTY_MACRO_ARG, "Empty macro argument\n"); warning(WARNING_EMPTY_MACRO_ARG, "Empty macro argument");
} }
args.push_back(arg); args.push_back(arg);
} }
@@ -60,10 +60,10 @@ void MacroArgs::appendArg(std::shared_ptr<std::string> arg) {
void MacroArgs::shiftArgs(int32_t count) { void MacroArgs::shiftArgs(int32_t count) {
if (size_t nbArgs = args.size(); if (size_t nbArgs = args.size();
count > 0 && (static_cast<uint32_t>(count) > nbArgs || shift > nbArgs - count)) { count > 0 && (static_cast<uint32_t>(count) > nbArgs || shift > nbArgs - count)) {
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their end\n"); warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their end");
shift = nbArgs; shift = nbArgs;
} else if (count < 0 && shift < static_cast<uint32_t>(-count)) { } else if (count < 0 && shift < static_cast<uint32_t>(-count)) {
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their beginning\n"); warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their beginning");
shift = 0; shift = 0;
} else { } else {
shift += count; shift += count;

View File

@@ -3,8 +3,10 @@
#include "asm/main.hpp" #include "asm/main.hpp"
#include <algorithm> #include <algorithm>
#include <errno.h>
#include <limits.h> #include <limits.h>
#include <memory> #include <memory>
#include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
@@ -109,6 +111,19 @@ static void printUsage() {
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
[[gnu::format(printf, 1, 2), noreturn]]
static void fatalWithUsage(char const *fmt, ...) {
va_list ap;
fputs("FATAL: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
printUsage();
exit(1);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
time_t now = time(nullptr); time_t now = time(nullptr);
// Support SOURCE_DATE_EPOCH for reproducible builds // Support SOURCE_DATE_EPOCH for reproducible builds
@@ -198,7 +213,9 @@ int main(int argc, char *argv[]) {
dependFileName = "<stdout>"; dependFileName = "<stdout>";
} }
if (dependFile == nullptr) { if (dependFile == nullptr) {
err("Failed to open dependfile \"%s\"", dependFileName); // LCOV_EXCL_LINE // LCOV_EXCL_START
errx("Failed to open dependfile \"%s\": %s", dependFileName, strerror(errno));
// LCOV_EXCL_STOP
} }
break; break;
@@ -386,15 +403,9 @@ int main(int argc, char *argv[]) {
} }
if (argc == musl_optind) { if (argc == musl_optind) {
fputs( fatalWithUsage("Please specify an input file (pass `-` to read from standard input)");
"FATAL: Please specify an input file (pass `-` to read from standard input)\n", stderr
);
printUsage();
exit(1);
} else if (argc != musl_optind + 1) { } else if (argc != musl_optind + 1) {
fputs("FATAL: More than one input file specified\n", stderr); fatalWithUsage("More than one input file specified");
printUsage();
exit(1);
} }
std::string mainFileName = argv[musl_optind]; std::string mainFileName = argv[musl_optind];

View File

@@ -47,7 +47,7 @@ void opt_R(size_t newDepth) {
void opt_W(char const *flag) { void opt_W(char const *flag) {
if (warnings.processWarningFlag(flag) == "numeric-string") { if (warnings.processWarningFlag(flag) == "numeric-string") {
warning(WARNING_OBSOLETE, "Warning flag \"numeric-string\" is deprecated\n"); warning(WARNING_OBSOLETE, "Warning flag \"numeric-string\" is deprecated");
} }
} }
@@ -57,7 +57,7 @@ void opt_Parse(char const *s) {
if (strlen(&s[1]) == 2) { if (strlen(&s[1]) == 2) {
opt_B(&s[1]); opt_B(&s[1]);
} else { } else {
error("Must specify exactly 2 characters for option 'b'\n"); error("Must specify exactly 2 characters for option 'b'");
} }
break; break;
@@ -65,7 +65,7 @@ void opt_Parse(char const *s) {
if (strlen(&s[1]) == 4) { if (strlen(&s[1]) == 4) {
opt_G(&s[1]); opt_G(&s[1]);
} else { } else {
error("Must specify exactly 4 characters for option 'g'\n"); error("Must specify exactly 4 characters for option 'g'");
} }
break; break;
@@ -76,14 +76,14 @@ void opt_Parse(char const *s) {
result = sscanf(&s[1], "%x", &padByte); result = sscanf(&s[1], "%x", &padByte);
if (result != 1) { if (result != 1) {
error("Invalid argument for option 'p'\n"); error("Invalid argument for option 'p'");
} else if (padByte > 0xFF) { } else if (padByte > 0xFF) {
error("Argument for option 'p' must be between 0 and 0xFF\n"); error("Argument for option 'p' must be between 0 and 0xFF");
} else { } else {
opt_P(padByte); opt_P(padByte);
} }
} else { } else {
error("Invalid argument for option 'p'\n"); error("Invalid argument for option 'p'");
} }
break; break;
@@ -99,14 +99,14 @@ void opt_Parse(char const *s) {
result = sscanf(precisionArg, "%u", &precision); result = sscanf(precisionArg, "%u", &precision);
if (result != 1) { if (result != 1) {
error("Invalid argument for option 'Q'\n"); error("Invalid argument for option 'Q'");
} else if (precision < 1 || precision > 31) { } else if (precision < 1 || precision > 31) {
error("Argument for option 'Q' must be between 1 and 31\n"); error("Argument for option 'Q' must be between 1 and 31");
} else { } else {
opt_Q(precision); opt_Q(precision);
} }
} else { } else {
error("Invalid argument for option 'Q'\n"); error("Invalid argument for option 'Q'");
} }
break; break;
@@ -117,7 +117,7 @@ void opt_Parse(char const *s) {
} }
if (s[0] == '\0') { if (s[0] == '\0') {
error("Missing argument to option 'r'\n"); error("Missing argument to option 'r'");
break; break;
} }
@@ -125,9 +125,9 @@ void opt_Parse(char const *s) {
unsigned long newDepth = strtoul(s, &endptr, 10); unsigned long newDepth = strtoul(s, &endptr, 10);
if (*endptr != '\0') { if (*endptr != '\0') {
error("Invalid argument to option 'r' (\"%s\")\n", s); error("Invalid argument to option 'r' (\"%s\")", s);
} else if (errno == ERANGE) { } else if (errno == ERANGE) {
error("Argument to 'r' is out of range (\"%s\")\n", s); error("Argument to 'r' is out of range (\"%s\")", s);
} else { } else {
opt_R(newDepth); opt_R(newDepth);
} }
@@ -138,12 +138,12 @@ void opt_Parse(char const *s) {
if (strlen(&s[1]) > 0) { if (strlen(&s[1]) > 0) {
opt_W(&s[1]); opt_W(&s[1]);
} else { } else {
error("Must specify an argument for option 'W'\n"); error("Must specify an argument for option 'W'");
} }
break; break;
default: default:
error("Unknown option '%c'\n", s[0]); error("Unknown option '%c'", s[0]);
break; break;
} }
} }
@@ -168,7 +168,7 @@ void opt_Push() {
void opt_Pop() { void opt_Pop() {
if (stack.empty()) { if (stack.empty()) {
error("No entries in the option stack\n"); error("No entries in the option stack");
return; return;
} }
@@ -187,6 +187,6 @@ void opt_Pop() {
void opt_CheckStack() { void opt_CheckStack() {
if (!stack.empty()) { if (!stack.empty()) {
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHO` without corresponding `POPO`\n"); warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHO` without corresponding `POPO`");
} }
} }

View File

@@ -4,6 +4,7 @@
#include <algorithm> #include <algorithm>
#include <deque> #include <deque>
#include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -72,7 +73,7 @@ static uint32_t getSectIDIfAny(Section *sect) {
} }
// Every section that exists should be in `sectionMap` // Every section that exists should be in `sectionMap`
fatalerror("Unknown section '%s'\n", sect->name.c_str()); // LCOV_EXCL_LINE fatalerror("Unknown section '%s'", sect->name.c_str()); // LCOV_EXCL_LINE
} }
static void writePatch(Patch const &patch, FILE *file) { static void writePatch(Patch const &patch, FILE *file) {
@@ -324,7 +325,9 @@ void out_WriteObject() {
file = stdout; file = stdout;
} }
if (!file) { if (!file) {
err("Failed to open object file '%s'", objectFileName.c_str()); // LCOV_EXCL_LINE // LCOV_EXCL_START
errx("Failed to open object file '%s': %s", objectFileName.c_str(), strerror(errno));
// LCOV_EXCL_STOP
} }
Defer closeFile{[&] { fclose(file); }}; Defer closeFile{[&] { fclose(file); }};
@@ -524,7 +527,9 @@ void out_WriteState(std::string name, std::vector<StateFeature> const &features)
file = stdout; file = stdout;
} }
if (!file) { if (!file) {
err("Failed to open state file '%s'", name.c_str()); // LCOV_EXCL_LINE // LCOV_EXCL_START
errx("Failed to open state file '%s': %s", name.c_str(), strerror(errno));
// LCOV_EXCL_STOP
} }
Defer closeFile{[&] { fclose(file); }}; Defer closeFile{[&] { fclose(file); }};

View File

@@ -438,12 +438,12 @@ diff_mark:
%empty // OK %empty // OK
| OP_ADD { | OP_ADD {
::error( ::error(
"syntax error, unexpected + at the beginning of the line (is it a leftover diff mark?)\n" "syntax error, unexpected + at the beginning of the line (is it a leftover diff mark?)"
); );
} }
| OP_SUB { | OP_SUB {
::error( ::error(
"syntax error, unexpected - at the beginning of the line (is it a leftover diff mark?)\n" "syntax error, unexpected - at the beginning of the line (is it a leftover diff mark?)"
); );
} }
; ;
@@ -488,11 +488,11 @@ if:
elif: elif:
POP_ELIF iconst NEWLINE { POP_ELIF iconst NEWLINE {
if (lexer_GetIFDepth() == 0) { if (lexer_GetIFDepth() == 0) {
fatalerror("Found ELIF outside of an IF construct\n"); fatalerror("Found ELIF outside of an IF construct");
} }
if (lexer_RanIFBlock()) { if (lexer_RanIFBlock()) {
if (lexer_ReachedELSEBlock()) { if (lexer_ReachedELSEBlock()) {
fatalerror("Found ELIF after an ELSE block\n"); fatalerror("Found ELIF after an ELSE block");
} }
lexer_SetMode(LEXER_SKIP_TO_ENDC); lexer_SetMode(LEXER_SKIP_TO_ENDC);
} else if ($2) { } else if ($2) {
@@ -506,11 +506,11 @@ elif:
else: else:
POP_ELSE NEWLINE { POP_ELSE NEWLINE {
if (lexer_GetIFDepth() == 0) { if (lexer_GetIFDepth() == 0) {
fatalerror("Found ELSE outside of an IF construct\n"); fatalerror("Found ELSE outside of an IF construct");
} }
if (lexer_RanIFBlock()) { if (lexer_RanIFBlock()) {
if (lexer_ReachedELSEBlock()) { if (lexer_ReachedELSEBlock()) {
fatalerror("Found ELSE after an ELSE block\n"); fatalerror("Found ELSE after an ELSE block");
} }
lexer_SetMode(LEXER_SKIP_TO_ENDC); lexer_SetMode(LEXER_SKIP_TO_ENDC);
} else { } else {
@@ -695,7 +695,7 @@ align:
align_spec: align_spec:
uconst { uconst {
if ($1 > 16) { if ($1 > 16) {
::error("Alignment must be between 0 and 16, not %u\n", $1); ::error("Alignment must be between 0 and 16, not %u", $1);
$$.alignment = $$.alignOfs = 0; $$.alignment = $$.alignOfs = 0;
} else { } else {
$$.alignment = $1; $$.alignment = $1;
@@ -704,11 +704,11 @@ align_spec:
} }
| uconst COMMA iconst { | uconst COMMA iconst {
if ($1 > 16) { if ($1 > 16) {
::error("Alignment must be between 0 and 16, not %u\n", $1); ::error("Alignment must be between 0 and 16, not %u", $1);
$$.alignment = $$.alignOfs = 0; $$.alignment = $$.alignOfs = 0;
} else if ($3 <= -(1 << $1) || $3 >= 1 << $1) { } else if ($3 <= -(1 << $1) || $3 >= 1 << $1) {
::error( ::error(
"The absolute alignment offset (%" PRIu32 ") must be less than alignment size (%d)\n", "The absolute alignment offset (%" PRIu32 ") must be less than alignment size (%d)",
static_cast<uint32_t>($3 < 0 ? -$3 : $3), static_cast<uint32_t>($3 < 0 ? -$3 : $3),
1 << $1 1 << $1
); );
@@ -779,13 +779,13 @@ endsection:
fail: fail:
POP_FAIL string { POP_FAIL string {
fatalerror("%s\n", $2.c_str()); fatalerror("%s", $2.c_str());
} }
; ;
warn: warn:
POP_WARN string { POP_WARN string {
warning(WARNING_USER, "%s\n", $2.c_str()); warning(WARNING_USER, "%s", $2.c_str());
} }
; ;
@@ -836,7 +836,7 @@ shift:
if (MacroArgs *macroArgs = fstk_GetCurrentMacroArgs(); macroArgs) { if (MacroArgs *macroArgs = fstk_GetCurrentMacroArgs(); macroArgs) {
macroArgs->shiftArgs($2); macroArgs->shiftArgs($2);
} else { } else {
::error("Cannot shift macro arguments outside of a macro\n"); ::error("Cannot shift macro arguments outside of a macro");
} }
} }
; ;
@@ -1577,7 +1577,7 @@ relocexpr_no_str:
| OP_CHARSIZE LPAREN string RPAREN { | OP_CHARSIZE LPAREN string RPAREN {
size_t charSize = charmap_CharSize($3); size_t charSize = charmap_CharSize($3);
if (charSize == 0) { if (charSize == 0) {
::error("CHARSIZE: No character mapping for \"%s\"\n", $3.c_str()); ::error("CHARSIZE: No character mapping for \"%s\"", $3.c_str());
} }
$$.makeNumber(charSize); $$.makeNumber(charSize);
} }
@@ -1589,13 +1589,13 @@ relocexpr_no_str:
} else { } else {
warning( warning(
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
"CHARVAL: Index %" PRIu32 " is past the end of the character mapping\n", "CHARVAL: Index %" PRIu32 " is past the end of the character mapping",
idx idx
); );
$$.makeNumber(0); $$.makeNumber(0);
} }
} else { } else {
::error("CHARVAL: No character mapping for \"%s\"\n", $3.c_str()); ::error("CHARVAL: No character mapping for \"%s\"", $3.c_str());
$$.makeNumber(0); $$.makeNumber(0);
} }
} }
@@ -1608,7 +1608,7 @@ uconst:
iconst { iconst {
$$ = $1; $$ = $1;
if ($$ < 0) { if ($$ < 0) {
fatalerror("Constant must not be negative: %d\n", $$); fatalerror("Constant must not be negative: %d", $$);
} }
} }
; ;
@@ -1626,7 +1626,7 @@ precision_arg:
| COMMA iconst { | COMMA iconst {
$$ = $2; $$ = $2;
if ($$ < 1 || $$ > 31) { if ($$ < 1 || $$ > 31) {
::error("Fixed-point precision must be between 1 and 31, not %" PRId32 "\n", $$); ::error("Fixed-point precision must be between 1 and 31, not %" PRId32, $$);
$$ = fix_Precision(); $$ = fix_Precision();
} }
} }
@@ -1675,9 +1675,9 @@ string_literal:
bool unique; bool unique;
$$ = charmap_Reverse($3, unique); $$ = charmap_Reverse($3, unique);
if (!unique) { if (!unique) {
::error("REVCHAR: Multiple character mappings to values\n"); ::error("REVCHAR: Multiple character mappings to values");
} else if ($$.empty()) { } else if ($$.empty()) {
::error("REVCHAR: No character mapping to values\n"); ::error("REVCHAR: No character mapping to values");
} }
} }
| OP_STRCAT LPAREN RPAREN { | OP_STRCAT LPAREN RPAREN {
@@ -1705,15 +1705,15 @@ string_literal:
if (!sym) { if (!sym) {
if (sym_IsPurgedScoped($3)) { if (sym_IsPurgedScoped($3)) {
fatalerror("Unknown symbol \"%s\"; it was purged\n", $3.c_str()); fatalerror("Unknown symbol \"%s\"; it was purged", $3.c_str());
} else { } else {
fatalerror("Unknown symbol \"%s\"\n", $3.c_str()); fatalerror("Unknown symbol \"%s\"", $3.c_str());
} }
} }
Section const *section = sym->getSection(); Section const *section = sym->getSection();
if (!section) { if (!section) {
fatalerror("\"%s\" does not belong to any section\n", sym->name.c_str()); fatalerror("\"%s\" does not belong to any section", sym->name.c_str());
} }
// Section names are capped by rgbasm's maximum string length, // Section names are capped by rgbasm's maximum string length,
// so this currently can't overflow. // so this currently can't overflow.
@@ -1729,7 +1729,7 @@ string:
if (Symbol *sym = sym_FindScopedSymbol($1); sym && sym->type == SYM_EQUS) { if (Symbol *sym = sym_FindScopedSymbol($1); sym && sym->type == SYM_EQUS) {
$$ = *sym->getEqus(); $$ = *sym->getEqus();
} else { } else {
::error("'%s' is not a string symbol\n", $1.c_str()); ::error("'%s' is not a string symbol", $1.c_str());
} }
} }
; ;
@@ -1833,7 +1833,7 @@ sect_org:
| LBRACK uconst RBRACK { | LBRACK uconst RBRACK {
$$ = $2; $$ = $2;
if ($$ < 0 || $$ > 0xFFFF) { if ($$ < 0 || $$ > 0xFFFF) {
::error("Address $%x is not 16-bit\n", $$); ::error("Address $%x is not 16-bit", $$);
$$ = -1; $$ = -1;
} }
} }
@@ -2088,7 +2088,7 @@ sm83_ldh:
if ($4.makeCheckHRAM()) { if ($4.makeCheckHRAM()) {
warning( warning(
WARNING_OBSOLETE, WARNING_OBSOLETE,
"LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF\n" "LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF"
); );
} }
@@ -2099,7 +2099,7 @@ sm83_ldh:
if ($2.makeCheckHRAM()) { if ($2.makeCheckHRAM()) {
warning( warning(
WARNING_OBSOLETE, WARNING_OBSOLETE,
"LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF\n" "LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF"
); );
} }
@@ -2126,7 +2126,7 @@ ff00_c_ind:
LBRACK relocexpr OP_ADD MODE_C RBRACK { LBRACK relocexpr OP_ADD MODE_C RBRACK {
// This has to use `relocexpr`, not `iconst`, to avoid a shift/reduce conflict // This has to use `relocexpr`, not `iconst`, to avoid a shift/reduce conflict
if ($2.getConstVal() != 0xFF00) { if ($2.getConstVal() != 0xFF00) {
::error("Base value must be equal to $FF00 for $FF00+C\n"); ::error("Base value must be equal to $FF00 for $FF00+C");
} }
} }
; ;
@@ -2148,7 +2148,7 @@ sm83_ld_hl:
sect_RelByte($5, 1); sect_RelByte($5, 1);
} }
| SM83_LD MODE_HL COMMA MODE_SP { | SM83_LD MODE_HL COMMA MODE_SP {
::error("LD HL, SP is not a valid instruction; use LD HL, SP + 0\n"); ::error("LD HL, SP is not a valid instruction; use LD HL, SP + 0");
} }
| SM83_LD MODE_HL COMMA reloc_16bit { | SM83_LD MODE_HL COMMA reloc_16bit {
sect_ConstByte(0x01 | (REG_HL << 4)); sect_ConstByte(0x01 | (REG_HL << 4));
@@ -2156,7 +2156,7 @@ sm83_ld_hl:
} }
| SM83_LD MODE_HL COMMA reg_tt_no_af { | SM83_LD MODE_HL COMMA reg_tt_no_af {
::error( ::error(
"LD HL, %s is not a valid instruction; use LD H, %s and LD L, %s\n", "LD HL, %s is not a valid instruction; use LD H, %s and LD L, %s",
reg_tt_names[$4], reg_tt_names[$4],
reg_tt_high_names[$4], reg_tt_high_names[$4],
reg_tt_low_names[$4] reg_tt_low_names[$4]
@@ -2169,7 +2169,7 @@ sm83_ld_sp:
sect_ConstByte(0xF9); sect_ConstByte(0xF9);
} }
| SM83_LD MODE_SP COMMA reg_bc_or_de { | SM83_LD MODE_SP COMMA reg_bc_or_de {
::error("LD SP, %s is not a valid instruction\n", reg_tt_names[$4]); ::error("LD SP, %s is not a valid instruction", reg_tt_names[$4]);
} }
| SM83_LD MODE_SP COMMA reloc_16bit { | SM83_LD MODE_SP COMMA reloc_16bit {
sect_ConstByte(0x01 | (REG_SP << 4)); sect_ConstByte(0x01 | (REG_SP << 4));
@@ -2193,7 +2193,7 @@ sm83_ld_c_ind:
sect_ConstByte(0xE2); sect_ConstByte(0xE2);
} }
| SM83_LD c_ind COMMA MODE_A { | SM83_LD c_ind COMMA MODE_A {
warning(WARNING_OBSOLETE, "LD [C], A is deprecated; use LDH [C], A\n"); warning(WARNING_OBSOLETE, "LD [C], A is deprecated; use LDH [C], A");
sect_ConstByte(0xE2); sect_ConstByte(0xE2);
} }
; ;
@@ -2211,7 +2211,7 @@ sm83_ld_r_no_a:
} }
| SM83_LD reg_r_no_a COMMA reg_r { | SM83_LD reg_r_no_a COMMA reg_r {
if ($2 == REG_HL_IND && $4 == REG_HL_IND) { if ($2 == REG_HL_IND && $4 == REG_HL_IND) {
::error("LD [HL], [HL] is not a valid instruction\n"); ::error("LD [HL], [HL] is not a valid instruction");
} else { } else {
sect_ConstByte(0x40 | ($2 << 3) | $4); sect_ConstByte(0x40 | ($2 << 3) | $4);
} }
@@ -2230,7 +2230,7 @@ sm83_ld_a:
sect_ConstByte(0xF2); sect_ConstByte(0xF2);
} }
| SM83_LD reg_a COMMA c_ind { | SM83_LD reg_a COMMA c_ind {
warning(WARNING_OBSOLETE, "LD A, [C] is deprecated; use LDH A, [C]\n"); warning(WARNING_OBSOLETE, "LD A, [C] is deprecated; use LDH A, [C]");
sect_ConstByte(0xF2); sect_ConstByte(0xF2);
} }
| SM83_LD reg_a COMMA reg_rr { | SM83_LD reg_a COMMA reg_rr {
@@ -2249,7 +2249,7 @@ sm83_ld_ss:
} }
| SM83_LD reg_bc_or_de COMMA reg_tt_no_af { | SM83_LD reg_bc_or_de COMMA reg_tt_no_af {
::error( ::error(
"LD %s, %s is not a valid instruction; use LD %s, %s and LD %s, %s\n", "LD %s, %s is not a valid instruction; use LD %s, %s and LD %s, %s",
reg_tt_names[$2], reg_tt_names[$2],
reg_tt_names[$4], reg_tt_names[$4],
reg_tt_high_names[$2], reg_tt_high_names[$2],
@@ -2644,7 +2644,7 @@ hl_ind_dec:
/******************** Semantic actions ********************/ /******************** Semantic actions ********************/
void yy::parser::error(std::string const &str) { void yy::parser::error(std::string const &str) {
::error("%s\n", str.c_str()); ::error("%s", str.c_str());
} }
static uint32_t strToNum(std::vector<int32_t> const &s) { static uint32_t strToNum(std::vector<int32_t> const &s) {
@@ -2656,7 +2656,7 @@ static uint32_t strToNum(std::vector<int32_t> const &s) {
return static_cast<uint32_t>(s[0]); return static_cast<uint32_t>(s[0]);
} }
warning(WARNING_OBSOLETE, "Treating multi-unit strings as numbers is deprecated\n"); warning(WARNING_OBSOLETE, "Treating multi-unit strings as numbers is deprecated");
for (int32_t v : s) { for (int32_t v : s) {
if (!checkNBit(v, 8, "All character units")) { if (!checkNBit(v, 8, "All character units")) {
@@ -2675,7 +2675,7 @@ static uint32_t strToNum(std::vector<int32_t> const &s) {
} }
static void errorInvalidUTF8Byte(uint8_t byte, char const *functionName) { static void errorInvalidUTF8Byte(uint8_t byte, char const *functionName) {
error("%s: Invalid UTF-8 byte 0x%02hhX\n", functionName, byte); error("%s: Invalid UTF-8 byte 0x%02hhX", functionName, byte);
} }
static size_t strlenUTF8(std::string const &str, bool printErrors) { static size_t strlenUTF8(std::string const &str, bool printErrors) {
@@ -2702,7 +2702,7 @@ static size_t strlenUTF8(std::string const &str, bool printErrors) {
// Check for partial code point. // Check for partial code point.
if (state != 0) { if (state != 0) {
if (printErrors) { if (printErrors) {
error("STRLEN: Incomplete UTF-8 character\n"); error("STRLEN: Incomplete UTF-8 character");
} }
len++; len++;
} }
@@ -2736,7 +2736,7 @@ static std::string strsliceUTF8(std::string const &str, uint32_t start, uint32_t
if (!ptr[index] && start > curIdx) { if (!ptr[index] && start > curIdx) {
warning( warning(
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
"STRSLICE: Start index %" PRIu32 " is past the end of the string\n", "STRSLICE: Start index %" PRIu32 " is past the end of the string",
start start
); );
} }
@@ -2759,14 +2759,14 @@ static std::string strsliceUTF8(std::string const &str, uint32_t start, uint32_t
// Check for partial code point. // Check for partial code point.
if (state != 0) { if (state != 0) {
error("STRSLICE: Incomplete UTF-8 character\n"); error("STRSLICE: Incomplete UTF-8 character");
curIdx++; curIdx++;
} }
if (curIdx < stop) { if (curIdx < stop) {
warning( warning(
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
"STRSLICE: Stop index %" PRIu32 " is past the end of the string\n", "STRSLICE: Stop index %" PRIu32 " is past the end of the string",
stop stop
); );
} }
@@ -2799,7 +2799,7 @@ static std::string strsubUTF8(std::string const &str, uint32_t pos, uint32_t len
// "Length too big" warning below if the length is nonzero. // "Length too big" warning below if the length is nonzero.
if (!ptr[index] && pos > curPos) { if (!ptr[index] && pos > curPos) {
warning( warning(
WARNING_BUILTIN_ARG, "STRSUB: Position %" PRIu32 " is past the end of the string\n", pos WARNING_BUILTIN_ARG, "STRSUB: Position %" PRIu32 " is past the end of the string", pos
); );
} }
@@ -2822,12 +2822,12 @@ static std::string strsubUTF8(std::string const &str, uint32_t pos, uint32_t len
// Check for partial code point. // Check for partial code point.
if (state != 0) { if (state != 0) {
error("STRSUB: Incomplete UTF-8 character\n"); error("STRSUB: Incomplete UTF-8 character");
curLen++; curLen++;
} }
if (curLen < len) { if (curLen < len) {
warning(WARNING_BUILTIN_ARG, "STRSUB: Length too big: %" PRIu32 "\n", len); warning(WARNING_BUILTIN_ARG, "STRSUB: Length too big: %" PRIu32, len);
} }
return std::string(ptr + startIndex, ptr + index); return std::string(ptr + startIndex, ptr + index);
@@ -2856,7 +2856,7 @@ static std::string strcharUTF8(std::string const &str, uint32_t idx) {
if (!charmap_ConvertNext(view, nullptr)) { if (!charmap_ConvertNext(view, nullptr)) {
warning( warning(
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
"STRCHAR: Index %" PRIu32 " is past the end of the string\n", "STRCHAR: Index %" PRIu32 " is past the end of the string",
idx idx
); );
} }
@@ -2879,7 +2879,7 @@ static std::string charsubUTF8(std::string const &str, uint32_t pos) {
if (!charmap_ConvertNext(view, nullptr)) { if (!charmap_ConvertNext(view, nullptr)) {
warning( warning(
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
"CHARSUB: Position %" PRIu32 " is past the end of the string\n", "CHARSUB: Position %" PRIu32 " is past the end of the string",
pos pos
); );
} }
@@ -2922,7 +2922,7 @@ static uint32_t adjustNegativeIndex(int32_t idx, size_t len, char const *functio
idx += len; idx += len;
} }
if (idx < 0) { if (idx < 0) {
warning(WARNING_BUILTIN_ARG, "%s: Index starts at 0\n", functionName); warning(WARNING_BUILTIN_ARG, "%s: Index starts at 0", functionName);
idx = 0; idx = 0;
} }
return static_cast<uint32_t>(idx); return static_cast<uint32_t>(idx);
@@ -2935,7 +2935,7 @@ static uint32_t adjustNegativePos(int32_t pos, size_t len, char const *functionN
pos += len + 1; pos += len + 1;
} }
if (pos < 1) { if (pos < 1) {
warning(WARNING_BUILTIN_ARG, "%s: Position starts at 1\n", functionName); warning(WARNING_BUILTIN_ARG, "%s: Position starts at 1", functionName);
pos = 1; pos = 1;
} }
return static_cast<uint32_t>(pos); return static_cast<uint32_t>(pos);
@@ -2943,7 +2943,7 @@ static uint32_t adjustNegativePos(int32_t pos, size_t len, char const *functionN
static std::string strrpl(std::string_view str, std::string const &old, std::string const &rep) { static std::string strrpl(std::string_view str, std::string const &old, std::string const &rep) {
if (old.empty()) { if (old.empty()) {
warning(WARNING_EMPTY_STRRPL, "STRRPL: Cannot replace an empty string\n"); warning(WARNING_EMPTY_STRRPL, "STRRPL: Cannot replace an empty string");
return std::string(str); return std::string(str);
} }
@@ -2994,13 +2994,13 @@ static std::string
} }
if (fmt.isEmpty()) { if (fmt.isEmpty()) {
error("STRFMT: Illegal '%%' at end of format string\n"); error("STRFMT: Illegal '%%' at end of format string");
str += '%'; str += '%';
break; break;
} }
if (!fmt.isValid()) { if (!fmt.isValid()) {
error("STRFMT: Invalid format spec for argument %zu\n", argIndex + 1); error("STRFMT: Invalid format spec for argument %zu", argIndex + 1);
str += '%'; str += '%';
} else if (argIndex >= args.size()) { } else if (argIndex >= args.size()) {
// Will warn after formatting is done. // Will warn after formatting is done.
@@ -3015,10 +3015,10 @@ static std::string
} }
if (argIndex < args.size()) { if (argIndex < args.size()) {
error("STRFMT: %zu unformatted argument(s)\n", args.size() - argIndex); error("STRFMT: %zu unformatted argument(s)", args.size() - argIndex);
} else if (argIndex > args.size()) { } else if (argIndex > args.size()) {
error( error(
"STRFMT: Not enough arguments for format spec, got: %zu, need: %zu\n", "STRFMT: Not enough arguments for format spec, got: %zu, need: %zu",
args.size(), args.size(),
argIndex argIndex
); );
@@ -3041,12 +3041,12 @@ static void compoundAssignment(std::string const &symName, RPNCommand op, int32_
static void failAssert(AssertionType type) { static void failAssert(AssertionType type) {
switch (type) { switch (type) {
case ASSERT_FATAL: case ASSERT_FATAL:
fatalerror("Assertion failed\n"); fatalerror("Assertion failed");
case ASSERT_ERROR: case ASSERT_ERROR:
error("Assertion failed\n"); error("Assertion failed");
break; break;
case ASSERT_WARN: case ASSERT_WARN:
warning(WARNING_ASSERT, "Assertion failed\n"); warning(WARNING_ASSERT, "Assertion failed");
break; break;
} }
} }
@@ -3054,12 +3054,12 @@ static void failAssert(AssertionType type) {
static void failAssertMsg(AssertionType type, std::string const &message) { static void failAssertMsg(AssertionType type, std::string const &message) {
switch (type) { switch (type) {
case ASSERT_FATAL: case ASSERT_FATAL:
fatalerror("Assertion failed: %s\n", message.c_str()); fatalerror("Assertion failed: %s", message.c_str());
case ASSERT_ERROR: case ASSERT_ERROR:
error("Assertion failed: %s\n", message.c_str()); error("Assertion failed: %s", message.c_str());
break; break;
case ASSERT_WARN: case ASSERT_WARN:
warning(WARNING_ASSERT, "Assertion failed: %s\n", message.c_str()); warning(WARNING_ASSERT, "Assertion failed: %s", message.c_str());
break; break;
} }
} }

View File

@@ -39,7 +39,7 @@ uint8_t *Expression::reserveSpace(uint32_t size, uint32_t patchSize) {
int32_t Expression::getConstVal() const { int32_t Expression::getConstVal() const {
if (!isKnown()) { if (!isKnown()) {
error("Expected constant expression: %s\n", data.get<std::string>().c_str()); error("Expected constant expression: %s", data.get<std::string>().c_str());
return 0; return 0;
} }
return value(); return value();
@@ -73,10 +73,10 @@ void Expression::makeNumber(uint32_t value) {
void Expression::makeSymbol(std::string const &symName) { void Expression::makeSymbol(std::string const &symName) {
clear(); clear();
if (Symbol *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym) && !sect_GetSymbolSection()) { if (Symbol *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym) && !sect_GetSymbolSection()) {
error("PC has no value outside of a section\n"); error("PC has no value outside of a section");
data = 0; data = 0;
} else if (sym && !sym->isNumeric() && !sym->isLabel()) { } else if (sym && !sym->isNumeric() && !sym->isLabel()) {
error("'%s' is not a numeric symbol\n", symName.c_str()); error("'%s' is not a numeric symbol", symName.c_str());
data = 0; data = 0;
} else if (!sym || !sym->isConstant()) { } else if (!sym || !sym->isConstant()) {
isSymbol = true; isSymbol = true;
@@ -103,7 +103,7 @@ void Expression::makeBankSymbol(std::string const &symName) {
if (Symbol const *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym)) { if (Symbol const *sym = sym_FindScopedSymbol(symName); sym_IsPC(sym)) {
// The @ symbol is treated differently. // The @ symbol is treated differently.
if (!currentSection) { if (!currentSection) {
error("PC has no bank outside of a section\n"); error("PC has no bank outside of a section");
data = 1; data = 1;
} else if (currentSection->bank == UINT32_MAX) { } else if (currentSection->bank == UINT32_MAX) {
data = "Current section's bank is not known"; data = "Current section's bank is not known";
@@ -114,7 +114,7 @@ void Expression::makeBankSymbol(std::string const &symName) {
} }
return; return;
} else if (sym && !sym->isLabel()) { } else if (sym && !sym->isLabel()) {
error("BANK argument must be a label\n"); error("BANK argument must be a label");
data = 1; data = 1;
} else { } else {
sym = sym_Ref(symName); sym = sym_Ref(symName);
@@ -394,43 +394,37 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
break; break;
case RPN_SHL: case RPN_SHL:
if (rval < 0) { if (rval < 0) {
warning( warning(WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32, rval);
WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32 "\n", rval
);
} }
if (rval >= 32) { if (rval >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n", rval); warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32, rval);
} }
data = op_shift_left(lval, rval); data = op_shift_left(lval, rval);
break; break;
case RPN_SHR: case RPN_SHR:
if (lval < 0) { if (lval < 0) {
warning(WARNING_SHIFT, "Shifting right negative value %" PRId32 "\n", lval); warning(WARNING_SHIFT, "Shifting right negative value %" PRId32, lval);
} }
if (rval < 0) { if (rval < 0) {
warning( warning(WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, rval);
WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
);
} }
if (rval >= 32) { if (rval >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval); warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, rval);
} }
data = op_shift_right(lval, rval); data = op_shift_right(lval, rval);
break; break;
case RPN_USHR: case RPN_USHR:
if (rval < 0) { if (rval < 0) {
warning( warning(WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, rval);
WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32 "\n", rval
);
} }
if (rval >= 32) { if (rval >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32 "\n", rval); warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, rval);
} }
data = op_shift_right_unsigned(lval, rval); data = op_shift_right_unsigned(lval, rval);
@@ -440,13 +434,13 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
break; break;
case RPN_DIV: case RPN_DIV:
if (rval == 0) { if (rval == 0) {
fatalerror("Division by zero\n"); fatalerror("Division by zero");
} }
if (lval == INT32_MIN && rval == -1) { if (lval == INT32_MIN && rval == -1) {
warning( warning(
WARNING_DIV, WARNING_DIV,
"Division of %" PRId32 " by -1 yields %" PRId32 "\n", "Division of %" PRId32 " by -1 yields %" PRId32,
INT32_MIN, INT32_MIN,
INT32_MIN INT32_MIN
); );
@@ -457,7 +451,7 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
break; break;
case RPN_MOD: case RPN_MOD:
if (rval == 0) { if (rval == 0) {
fatalerror("Modulo by zero\n"); fatalerror("Modulo by zero");
} }
if (lval == INT32_MIN && rval == -1) { if (lval == INT32_MIN && rval == -1) {
@@ -468,7 +462,7 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
break; break;
case RPN_EXP: case RPN_EXP:
if (rval < 0) { if (rval < 0) {
fatalerror("Exponentiation by negative power\n"); fatalerror("Exponentiation by negative power");
} }
data = op_exponent(lval, rval); data = op_exponent(lval, rval);
@@ -551,7 +545,7 @@ bool Expression::makeCheckHRAM() {
// That range is valid, but deprecated // That range is valid, but deprecated
return true; return true;
} else { } else {
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", val); error("Source address $%" PRIx32 " not between $FF00 to $FFFF", val);
} }
return false; return false;
} }
@@ -561,7 +555,7 @@ void Expression::makeCheckRST() {
*reserveSpace(1) = RPN_RST; *reserveSpace(1) = RPN_RST;
} else if (int32_t val = value(); val & ~0x38) { } else if (int32_t val = value(); val & ~0x38) {
// A valid RST address must be masked with 0x38 // A valid RST address must be masked with 0x38
error("Invalid address $%" PRIx32 " for RST\n", val); error("Invalid address $%" PRIx32 " for RST", val);
} }
} }
@@ -575,7 +569,7 @@ void Expression::makeCheckBitIndex(uint8_t mask) {
} else if (int32_t val = value(); val & ~0x07) { } else if (int32_t val = value(); val & ~0x07) {
// A valid bit index must be masked with 0x07 // A valid bit index must be masked with 0x07
static char const *instructions[4] = {"instruction", "BIT", "RES", "SET"}; static char const *instructions[4] = {"instruction", "BIT", "RES", "SET"};
error("Invalid bit index %" PRId32 " for %s\n", val, instructions[mask >> 6]); error("Invalid bit index %" PRId32 " for %s", val, instructions[mask >> 6]);
} }
} }
@@ -593,20 +587,20 @@ bool checkNBit(int32_t v, uint8_t n, char const *name) {
if (v < -(1 << n) || v >= 1 << n) { if (v < -(1 << n) || v >= 1 << n) {
warning( warning(
WARNING_TRUNCATION_1, WARNING_TRUNCATION_1,
n == 8 && !name ? "%s must be %u-bit; use LOW() to force 8-bit\n" "%s must be %u-bit%s",
: "%s must be %u-bit\n",
name ? name : "Expression", name ? name : "Expression",
n n,
n == 8 && !name ? "; use LOW() to force 8-bit" : ""
); );
return false; return false;
} }
if (v < -(1 << (n - 1))) { if (v < -(1 << (n - 1))) {
warning( warning(
WARNING_TRUNCATION_2, WARNING_TRUNCATION_2,
n == 8 && !name ? "%s must be %u-bit; use LOW() to force 8-bit\n" "%s must be %u-bit%s",
: "%s must be %u-bit\n",
name ? name : "Expression", name ? name : "Expression",
n n,
n == 8 && !name ? "; use LOW() to force 8-bit" : ""
); );
return false; return false;
} }

View File

@@ -55,7 +55,7 @@ static bool requireSection() {
return true; return true;
} }
error("Cannot output data outside of a SECTION\n"); error("Cannot output data outside of a SECTION");
return false; return false;
} }
@@ -72,8 +72,7 @@ static bool requireCodeSection() {
} }
error( error(
"Section '%s' cannot contain code or data (not ROM0 or ROMX)\n", "Section '%s' cannot contain code or data (not ROM0 or ROMX)", currentSection->name.c_str()
currentSection->name.c_str()
); );
return false; return false;
} }
@@ -82,8 +81,7 @@ void sect_CheckSizes() {
for (Section const &sect : sectionList) { for (Section const &sect : sectionList) {
if (uint32_t maxSize = sectionTypeInfo[sect.type].size; sect.size > maxSize) { if (uint32_t maxSize = sectionTypeInfo[sect.type].size; sect.size > maxSize) {
error( error(
"Section '%s' grew too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32 "Section '%s' grew too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32 ")",
")\n",
sect.name.c_str(), sect.name.c_str(),
maxSize, maxSize,
sect.size sect.size
@@ -113,18 +111,18 @@ static unsigned int mergeSectUnion(
// Unionized sections only need "compatible" constraints, and they end up with the strictest // Unionized sections only need "compatible" constraints, and they end up with the strictest
// combination of both. // combination of both.
if (sect_HasData(type)) { if (sect_HasData(type)) {
sectError("Cannot declare ROM sections as UNION\n"); sectError("Cannot declare ROM sections as UNION");
} }
if (org != UINT32_MAX) { if (org != UINT32_MAX) {
// If both are fixed, they must be the same // If both are fixed, they must be the same
if (sect.org != UINT32_MAX && sect.org != org) { if (sect.org != UINT32_MAX && sect.org != org) {
sectError( sectError(
"Section already declared as fixed at different address $%04" PRIx32 "\n", sect.org "Section already declared as fixed at different address $%04" PRIx32, sect.org
); );
} else if (sect.align != 0 && (mask(sect.align) & (org - sect.alignOfs))) { } else if (sect.align != 0 && (mask(sect.align) & (org - sect.alignOfs))) {
sectError( sectError(
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n", "Section already declared as aligned to %u bytes (offset %" PRIu16 ")",
1U << sect.align, 1U << sect.align,
sect.alignOfs sect.alignOfs
); );
@@ -138,15 +136,14 @@ static unsigned int mergeSectUnion(
if (sect.org != UINT32_MAX) { if (sect.org != UINT32_MAX) {
if ((sect.org - alignOffset) & mask(alignment)) { if ((sect.org - alignOffset) & mask(alignment)) {
sectError( sectError(
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n", "Section already declared as fixed at incompatible address $%04" PRIx32,
sect.org sect.org
); );
} }
// Check if alignment offsets are compatible // Check if alignment offsets are compatible
} else if ((alignOffset & mask(sect.align)) != (sect.alignOfs & mask(alignment))) { } else if ((alignOffset & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
sectError( sectError(
"Section already declared with incompatible %u" "Section already declared with incompatible %u-byte alignment (offset %" PRIu16 ")",
"-byte alignment (offset %" PRIu16 ")\n",
1U << sect.align, 1U << sect.align,
sect.alignOfs sect.alignOfs
); );
@@ -174,12 +171,11 @@ static unsigned int
// If both are fixed, they must be the same // If both are fixed, they must be the same
if (sect.org != UINT32_MAX && sect.org != curOrg) { if (sect.org != UINT32_MAX && sect.org != curOrg) {
sectError( sectError(
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n", "Section already declared as fixed at incompatible address $%04" PRIx32, sect.org
sect.org
); );
} else if (sect.align != 0 && (mask(sect.align) & (curOrg - sect.alignOfs))) { } else if (sect.align != 0 && (mask(sect.align) & (curOrg - sect.alignOfs))) {
sectError( sectError(
"Section already declared as aligned to %u bytes (offset %" PRIu16 ")\n", "Section already declared as aligned to %u bytes (offset %" PRIu16 ")",
1U << sect.align, 1U << sect.align,
sect.alignOfs sect.alignOfs
); );
@@ -199,15 +195,14 @@ static unsigned int
if (sect.org != UINT32_MAX) { if (sect.org != UINT32_MAX) {
if ((sect.org - curOfs) & mask(alignment)) { if ((sect.org - curOfs) & mask(alignment)) {
sectError( sectError(
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n", "Section already declared as fixed at incompatible address $%04" PRIx32,
sect.org sect.org
); );
} }
// Check if alignment offsets are compatible // Check if alignment offsets are compatible
} else if ((curOfs & mask(sect.align)) != (sect.alignOfs & mask(alignment))) { } else if ((curOfs & mask(sect.align)) != (sect.alignOfs & mask(alignment))) {
sectError( sectError(
"Section already declared with incompatible %u" "Section already declared with incompatible %u-byte alignment (offset %" PRIu16 ")",
"-byte alignment (offset %" PRIu16 ")\n",
1U << sect.align, 1U << sect.align,
sect.alignOfs sect.alignOfs
); );
@@ -234,12 +229,12 @@ static void mergeSections(
if (type != sect.type) { if (type != sect.type) {
sectError( sectError(
"Section already exists but with type %s\n", sectionTypeInfo[sect.type].name.c_str() "Section already exists but with type %s", sectionTypeInfo[sect.type].name.c_str()
); );
} }
if (sect.modifier != mod) { if (sect.modifier != mod) {
sectError("Section already declared as SECTION %s\n", sectionModNames[sect.modifier]); sectError("Section already declared as SECTION %s", sectionModNames[sect.modifier]);
} else { } else {
switch (mod) { switch (mod) {
case SECTION_UNION: case SECTION_UNION:
@@ -256,21 +251,22 @@ static void mergeSections(
} }
// If both specify a bank, it must be the same one // If both specify a bank, it must be the same one
else if (bank != UINT32_MAX && sect.bank != bank) { else if (bank != UINT32_MAX && sect.bank != bank) {
sectError("Section already declared with different bank %" PRIu32 "\n", sect.bank); sectError("Section already declared with different bank %" PRIu32, sect.bank);
} }
break; break;
case SECTION_NORMAL: case SECTION_NORMAL:
sectError("Section already defined previously at "); errorNoNewline("Section already defined previously at ");
sect.src->dump(sect.fileLine); sect.src->dump(sect.fileLine);
putc('\n', stderr); putc('\n', stderr);
nbSectErrors++;
break; break;
} }
} }
if (nbSectErrors) { if (nbSectErrors) {
fatalerror( fatalerror(
"Cannot create section \"%s\" (%u error%s)\n", "Cannot create section \"%s\" (%u error%s)",
sect.name.c_str(), sect.name.c_str(),
nbSectErrors, nbSectErrors,
nbSectErrors == 1 ? "" : "s" nbSectErrors == 1 ? "" : "s"
@@ -332,11 +328,11 @@ static Section *getSection(
if (bank != UINT32_MAX) { if (bank != UINT32_MAX) {
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM && type != SECTTYPE_SRAM if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM && type != SECTTYPE_SRAM
&& type != SECTTYPE_WRAMX) { && type != SECTTYPE_WRAMX) {
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n"); error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections");
} else if (bank < sectionTypeInfo[type].firstBank } else if (bank < sectionTypeInfo[type].firstBank
|| bank > sectionTypeInfo[type].lastBank) { || bank > sectionTypeInfo[type].lastBank) {
error( error(
"%s bank value $%04" PRIx32 " out of range ($%04" PRIx32 " to $%04" PRIx32 ")\n", "%s bank value $%04" PRIx32 " out of range ($%04" PRIx32 " to $%04" PRIx32 ")",
sectionTypeInfo[type].name.c_str(), sectionTypeInfo[type].name.c_str(),
bank, bank,
sectionTypeInfo[type].firstBank, sectionTypeInfo[type].firstBank,
@@ -350,7 +346,7 @@ static Section *getSection(
if (alignOffset >= 1 << alignment) { if (alignOffset >= 1 << alignment) {
error( error(
"Alignment offset (%" PRIu16 ") must be smaller than alignment size (%u)\n", "Alignment offset (%" PRIu16 ") must be smaller than alignment size (%u)",
alignOffset, alignOffset,
1U << alignment 1U << alignment
); );
@@ -361,7 +357,7 @@ static Section *getSection(
if (org < sectionTypeInfo[type].startAddr || org > endaddr(type)) { if (org < sectionTypeInfo[type].startAddr || org > endaddr(type)) {
error( error(
"Section \"%s\"'s fixed address $%04" PRIx32 " is outside of range [$%04" PRIx16 "Section \"%s\"'s fixed address $%04" PRIx32 " is outside of range [$%04" PRIx16
"; $%04" PRIx16 "]\n", "; $%04" PRIx16 "]",
name.c_str(), name.c_str(),
org, org,
sectionTypeInfo[type].startAddr, sectionTypeInfo[type].startAddr,
@@ -372,7 +368,7 @@ static Section *getSection(
if (alignment != 0) { if (alignment != 0) {
if (alignment > 16) { if (alignment > 16) {
error("Alignment must be between 0 and 16, not %u\n", alignment); error("Alignment must be between 0 and 16, not %u", alignment);
alignment = 16; alignment = 16;
} }
// It doesn't make sense to have both alignment and org set // It doesn't make sense to have both alignment and org set
@@ -380,12 +376,12 @@ static Section *getSection(
if (org != UINT32_MAX) { if (org != UINT32_MAX) {
if ((org - alignOffset) & mask) { if ((org - alignOffset) & mask) {
error("Section \"%s\"'s fixed address doesn't match its alignment\n", name.c_str()); error("Section \"%s\"'s fixed address doesn't match its alignment", name.c_str());
} }
alignment = 0; // Ignore it if it's satisfied alignment = 0; // Ignore it if it's satisfied
} else if (sectionTypeInfo[type].startAddr & mask) { } else if (sectionTypeInfo[type].startAddr & mask) {
error( error(
"Section \"%s\"'s alignment cannot be attained in %s\n", "Section \"%s\"'s alignment cannot be attained in %s",
name.c_str(), name.c_str(),
sectionTypeInfo[type].name.c_str() sectionTypeInfo[type].name.c_str()
); );
@@ -415,7 +411,7 @@ static Section *getSection(
// Set the current section // Set the current section
static void changeSection() { static void changeSection() {
if (!currentUnionStack.empty()) { if (!currentUnionStack.empty()) {
fatalerror("Cannot change the section within a UNION\n"); fatalerror("Cannot change the section within a UNION");
} }
sym_ResetCurrentLabelScopes(); sym_ResetCurrentLabelScopes();
@@ -452,7 +448,7 @@ void sect_NewSection(
) { ) {
for (SectionStackEntry &entry : sectionStack) { for (SectionStackEntry &entry : sectionStack) {
if (entry.section && entry.section->name == name) { if (entry.section && entry.section->name == name) {
fatalerror("Section '%s' is already on the stack\n", name.c_str()); fatalerror("Section '%s' is already on the stack", name.c_str());
} }
} }
@@ -486,7 +482,7 @@ void sect_SetLoadSection(
} }
if (sect_HasData(type)) { if (sect_HasData(type)) {
error("`LOAD` blocks cannot create a ROM section\n"); error("`LOAD` blocks cannot create a ROM section");
return; return;
} }
@@ -505,13 +501,11 @@ void sect_SetLoadSection(
void sect_EndLoadSection(char const *cause) { void sect_EndLoadSection(char const *cause) {
if (cause) { if (cause) {
warning( warning(WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by `%s`", cause);
WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by `%s`\n", cause
);
} }
if (!currentLoadSection) { if (!currentLoadSection) {
error("Found `ENDL` outside of a `LOAD` block\n"); error("Found `ENDL` outside of a `LOAD` block");
return; return;
} }
@@ -524,7 +518,7 @@ void sect_EndLoadSection(char const *cause) {
void sect_CheckLoadClosed() { void sect_CheckLoadClosed() {
if (currentLoadSection) { if (currentLoadSection) {
warning(WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by EOF\n"); warning(WARNING_UNTERMINATED_LOAD, "`LOAD` block without `ENDL` terminated by EOF");
} }
} }
@@ -575,7 +569,7 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset) {
if (uint32_t actualOffset = (sect->org + curOffset) % alignSize; actualOffset != offset) { if (uint32_t actualOffset = (sect->org + curOffset) % alignSize; actualOffset != offset) {
error( error(
"Section is misaligned (at PC = $%04" PRIx32 ", expected ALIGN[%" PRIu32 "Section is misaligned (at PC = $%04" PRIx32 ", expected ALIGN[%" PRIu32
", %" PRIu32 "], got ALIGN[%" PRIu32 ", %" PRIu32 "])\n", ", %" PRIu32 "], got ALIGN[%" PRIu32 ", %" PRIu32 "])",
sect->org + curOffset, sect->org + curOffset,
alignment, alignment,
offset, offset,
@@ -590,7 +584,7 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset) {
error( error(
"Section is misaligned ($%04" PRIx32 "Section is misaligned ($%04" PRIx32
" bytes into the section, expected ALIGN[%" PRIu32 ", %" PRIu32 " bytes into the section, expected ALIGN[%" PRIu32 ", %" PRIu32
"], got ALIGN[%" PRIu32 ", %" PRIu32 "])\n", "], got ALIGN[%" PRIu32 ", %" PRIu32 "])",
curOffset, curOffset,
alignment, alignment,
offset, offset,
@@ -601,7 +595,7 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset) {
// Treat an alignment large enough as fixing the address. // Treat an alignment large enough as fixing the address.
// Note that this also ensures that a section's alignment never becomes 16 or greater. // Note that this also ensures that a section's alignment never becomes 16 or greater.
if (alignment > 16) { if (alignment > 16) {
error("Alignment must be between 0 and 16, not %u\n", alignment); error("Alignment must be between 0 and 16, not %u", alignment);
} }
sect->align = 0; // Reset the alignment, since we're fixing the address. sect->align = 0; // Reset the alignment, since we're fixing the address.
sect->org = offset - curOffset; sect->org = offset - curOffset;
@@ -615,7 +609,7 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset) {
static void growSection(uint32_t growth) { static void growSection(uint32_t growth) {
if (growth > 0 && curOffset > UINT32_MAX - growth) { if (growth > 0 && curOffset > UINT32_MAX - growth) {
fatalerror("Section size would overflow internal counter\n"); fatalerror("Section size would overflow internal counter");
} }
curOffset += growth; curOffset += growth;
if (uint32_t outOffset = sect_GetOutputOffset(); outOffset > currentSection->size) { if (uint32_t outOffset = sect_GetOutputOffset(); outOffset > currentSection->size) {
@@ -656,11 +650,11 @@ void sect_StartUnion() {
// your own peril! ^^ // your own peril! ^^
if (!currentSection) { if (!currentSection) {
error("UNIONs must be inside a SECTION\n"); error("UNIONs must be inside a SECTION");
return; return;
} }
if (sect_HasData(currentSection->type)) { if (sect_HasData(currentSection->type)) {
error("Cannot use UNION inside of ROM0 or ROMX sections\n"); error("Cannot use UNION inside of ROM0 or ROMX sections");
return; return;
} }
@@ -679,7 +673,7 @@ static void endUnionMember() {
void sect_NextUnionMember() { void sect_NextUnionMember() {
if (currentUnionStack.empty()) { if (currentUnionStack.empty()) {
error("Found NEXTU outside of a UNION construct\n"); error("Found NEXTU outside of a UNION construct");
return; return;
} }
endUnionMember(); endUnionMember();
@@ -687,7 +681,7 @@ void sect_NextUnionMember() {
void sect_EndUnion() { void sect_EndUnion() {
if (currentUnionStack.empty()) { if (currentUnionStack.empty()) {
error("Found ENDU outside of a UNION construct\n"); error("Found ENDU outside of a UNION construct");
return; return;
} }
endUnionMember(); endUnionMember();
@@ -697,7 +691,7 @@ void sect_EndUnion() {
void sect_CheckUnionClosed() { void sect_CheckUnionClosed() {
if (!currentUnionStack.empty()) { if (!currentUnionStack.empty()) {
error("Unterminated UNION construct\n"); error("Unterminated UNION construct");
} }
} }
@@ -767,7 +761,7 @@ void sect_Skip(uint32_t skip, bool ds) {
if (!ds) { if (!ds) {
warning( warning(
WARNING_EMPTY_DATA_DIRECTIVE, WARNING_EMPTY_DATA_DIRECTIVE,
"%s directive without data in ROM\n", "%s directive without data in ROM",
(skip == 4) ? "DL" (skip == 4) ? "DL"
: (skip == 2) ? "DW" : (skip == 2) ? "DW"
: "DB" : "DB"
@@ -862,7 +856,7 @@ void sect_PCRelByte(Expression const &expr, uint32_t pcShift) {
if (offset < -128 || offset > 127) { if (offset < -128 || offset > 127) {
error( error(
"JR target must be between -128 and 127 bytes away, not %" PRId16 "JR target must be between -128 and 127 bytes away, not %" PRId16
"; use JP instead\n", "; use JP instead",
offset offset
); );
writeByte(0); writeByte(0);
@@ -875,7 +869,7 @@ void sect_PCRelByte(Expression const &expr, uint32_t pcShift) {
// Output a binary file // Output a binary file
void sect_BinaryFile(std::string const &name, int32_t startPos) { void sect_BinaryFile(std::string const &name, int32_t startPos) {
if (startPos < 0) { if (startPos < 0) {
error("Start position cannot be negative (%" PRId32 ")\n", startPos); error("Start position cannot be negative (%" PRId32 ")", startPos);
startPos = 0; startPos = 0;
} }
if (!requireCodeSection()) { if (!requireCodeSection()) {
@@ -895,7 +889,7 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
failedOnMissingInclude = true; failedOnMissingInclude = true;
} else { } else {
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno)); error("Error opening INCBIN file '%s': %s", name.c_str(), strerror(errno));
} }
return; return;
} }
@@ -903,23 +897,19 @@ void sect_BinaryFile(std::string const &name, int32_t startPos) {
if (fseek(file, 0, SEEK_END) != -1) { if (fseek(file, 0, SEEK_END) != -1) {
if (startPos > ftell(file)) { if (startPos > ftell(file)) {
error("Specified start position is greater than length of file '%s'\n", name.c_str()); error("Specified start position is greater than length of file '%s'", name.c_str());
return; return;
} }
// The file is seekable; skip to the specified start position // The file is seekable; skip to the specified start position
fseek(file, startPos, SEEK_SET); fseek(file, startPos, SEEK_SET);
} else { } else {
if (errno != ESPIPE) { if (errno != ESPIPE) {
error( error("Error determining size of INCBIN file '%s': %s", name.c_str(), strerror(errno));
"Error determining size of INCBIN file '%s': %s\n", name.c_str(), strerror(errno)
);
} }
// The file isn't seekable, so we'll just skip bytes one at a time // The file isn't seekable, so we'll just skip bytes one at a time
while (startPos--) { while (startPos--) {
if (fgetc(file) == EOF) { if (fgetc(file) == EOF) {
error( error("Specified start position is greater than length of file '%s'", name.c_str());
"Specified start position is greater than length of file '%s'\n", name.c_str()
);
return; return;
} }
} }
@@ -930,18 +920,18 @@ 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", name.c_str(), strerror(errno));
} }
} }
// Output a slice of a binary file // Output a slice of a binary 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) {
if (startPos < 0) { if (startPos < 0) {
error("Start position cannot be negative (%" PRId32 ")\n", startPos); error("Start position cannot be negative (%" PRId32 ")", startPos);
startPos = 0; startPos = 0;
} }
if (length < 0) { if (length < 0) {
error("Number of bytes to read cannot be negative (%" PRId32 ")\n", length); error("Number of bytes to read cannot be negative (%" PRId32 ")", length);
length = 0; length = 0;
} }
if (!requireCodeSection()) { if (!requireCodeSection()) {
@@ -964,7 +954,7 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
failedOnMissingInclude = true; failedOnMissingInclude = true;
} else { } else {
error("Error opening INCBIN file '%s': %s\n", name.c_str(), strerror(errno)); error("Error opening INCBIN file '%s': %s", name.c_str(), strerror(errno));
} }
return; return;
} }
@@ -972,12 +962,12 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
if (fseek(file, 0, SEEK_END) != -1) { if (fseek(file, 0, SEEK_END) != -1) {
if (int32_t fsize = ftell(file); startPos > fsize) { if (int32_t fsize = ftell(file); startPos > fsize) {
error("Specified start position is greater than length of file '%s'\n", name.c_str()); error("Specified start position is greater than length of file '%s'", name.c_str());
return; return;
} else if (startPos + length > fsize) { } else if (startPos + length > fsize) {
error( error(
"Specified range in INCBIN file '%s' is out of bounds (%" PRIu32 " + %" PRIu32 "Specified range in INCBIN file '%s' is out of bounds (%" PRIu32 " + %" PRIu32
" > %" PRIu32 ")\n", " > %" PRIu32 ")",
name.c_str(), name.c_str(),
startPos, startPos,
length, length,
@@ -989,16 +979,12 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
fseek(file, startPos, SEEK_SET); fseek(file, startPos, SEEK_SET);
} else { } else {
if (errno != ESPIPE) { if (errno != ESPIPE) {
error( error("Error determining size of INCBIN file '%s': %s", name.c_str(), strerror(errno));
"Error determining size of INCBIN file '%s': %s\n", name.c_str(), strerror(errno)
);
} }
// The file isn't seekable, so we'll just skip bytes one at a time // The file isn't seekable, so we'll just skip bytes one at a time
while (startPos--) { while (startPos--) {
if (fgetc(file) == EOF) { if (fgetc(file) == EOF) {
error( error("Specified start position is greater than length of file '%s'", name.c_str());
"Specified start position is greater than length of file '%s'\n", name.c_str()
);
return; return;
} }
} }
@@ -1008,10 +994,10 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
if (int byte = fgetc(file); byte != EOF) { if (int byte = fgetc(file); byte != EOF) {
writeByte(byte); writeByte(byte);
} else if (ferror(file)) { } else if (ferror(file)) {
error("Error reading INCBIN file '%s': %s\n", name.c_str(), strerror(errno)); error("Error reading INCBIN file '%s': %s", name.c_str(), strerror(errno));
} else { } else {
error( error(
"Premature end of INCBIN file '%s' (%" PRId32 " bytes left to read)\n", "Premature end of INCBIN file '%s' (%" PRId32 " bytes left to read)",
name.c_str(), name.c_str(),
length + 1 length + 1
); );
@@ -1039,7 +1025,7 @@ void sect_PushSection() {
void sect_PopSection() { void sect_PopSection() {
if (sectionStack.empty()) { if (sectionStack.empty()) {
fatalerror("No entries in the section stack\n"); fatalerror("No entries in the section stack");
} }
if (currentLoadSection) { if (currentLoadSection) {
@@ -1060,17 +1046,17 @@ void sect_PopSection() {
void sect_CheckStack() { void sect_CheckStack() {
if (!sectionStack.empty()) { if (!sectionStack.empty()) {
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHS` without corresponding `POPS`\n"); warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHS` without corresponding `POPS`");
} }
} }
void sect_EndSection() { void sect_EndSection() {
if (!currentSection) { if (!currentSection) {
fatalerror("Cannot end the section outside of a SECTION\n"); fatalerror("Cannot end the section outside of a SECTION");
} }
if (!currentUnionStack.empty()) { if (!currentUnionStack.empty()) {
fatalerror("Cannot end the section within a UNION\n"); fatalerror("Cannot end the section within a UNION");
} }
if (currentLoadSection) { if (currentLoadSection) {

View File

@@ -3,6 +3,7 @@
#include "asm/symbol.hpp" #include "asm/symbol.hpp"
#include <algorithm> #include <algorithm>
#include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <unordered_map> #include <unordered_map>
@@ -51,14 +52,14 @@ static int32_t NARGCallback() {
if (MacroArgs const *macroArgs = fstk_GetCurrentMacroArgs(); macroArgs) { if (MacroArgs const *macroArgs = fstk_GetCurrentMacroArgs(); macroArgs) {
return macroArgs->nbArgs(); return macroArgs->nbArgs();
} else { } else {
error("_NARG has no value outside of a macro\n"); error("_NARG has no value outside of a macro");
return 0; return 0;
} }
} }
static std::shared_ptr<std::string> globalScopeCallback() { static std::shared_ptr<std::string> globalScopeCallback() {
if (!globalScope) { if (!globalScope) {
error("\".\" has no value outside of a label scope\n"); error("\".\" has no value outside of a label scope");
return std::make_shared<std::string>(""); return std::make_shared<std::string>("");
} }
return std::make_shared<std::string>(globalScope->name); return std::make_shared<std::string>(globalScope->name);
@@ -66,7 +67,7 @@ static std::shared_ptr<std::string> globalScopeCallback() {
static std::shared_ptr<std::string> localScopeCallback() { static std::shared_ptr<std::string> localScopeCallback() {
if (!localScope) { if (!localScope) {
error("\"..\" has no value outside of a local label scope\n"); error("\"..\" has no value outside of a local label scope");
return std::make_shared<std::string>(""); return std::make_shared<std::string>("");
} }
return std::make_shared<std::string>(localScope->name); return std::make_shared<std::string>(localScope->name);
@@ -111,6 +112,7 @@ std::shared_ptr<std::string> Symbol::getEqus() const {
} }
static void dumpFilename(Symbol const &sym) { static void dumpFilename(Symbol const &sym) {
fputs(" at ", stderr);
if (sym.src) { if (sym.src) {
sym.src->dump(sym.fileLine); sym.src->dump(sym.fileLine);
putc('\n', stderr); putc('\n', stderr);
@@ -140,13 +142,12 @@ static bool isValidIdentifier(std::string const &s) {
static void alreadyDefinedError(Symbol const &sym, char const *asType) { static void alreadyDefinedError(Symbol const &sym, char const *asType) {
if (sym.isBuiltin && !sym_FindScopedValidSymbol(sym.name)) { if (sym.isBuiltin && !sym_FindScopedValidSymbol(sym.name)) {
// `DEF()` would return false, so we should not claim the symbol is already defined // `DEF()` would return false, so we should not claim the symbol is already defined
error("'%s' is reserved for a built-in symbol\n", sym.name.c_str()); error("'%s' is reserved for a built-in symbol", sym.name.c_str());
} else { } else {
error("'%s' already defined", sym.name.c_str()); errorNoNewline("'%s' already defined", sym.name.c_str());
if (asType) { if (asType) {
fprintf(stderr, " as %s", asType); fprintf(stderr, " as %s", asType);
} }
fputs(" at ", stderr);
dumpFilename(sym); dumpFilename(sym);
if (sym.type == SYM_EQUS) { if (sym.type == SYM_EQUS) {
if (std::string const &contents = *sym.getEqus(); isValidIdentifier(contents)) { if (std::string const &contents = *sym.getEqus(); isValidIdentifier(contents)) {
@@ -164,9 +165,9 @@ static void redefinedError(Symbol const &sym) {
assume(sym.isBuiltin); assume(sym.isBuiltin);
if (!sym_FindScopedValidSymbol(sym.name)) { if (!sym_FindScopedValidSymbol(sym.name)) {
// `DEF()` would return false, so we should not imply the symbol is already defined // `DEF()` would return false, so we should not imply the symbol is already defined
error("'%s' is reserved for a built-in symbol\n", sym.name.c_str()); error("'%s' is reserved for a built-in symbol", sym.name.c_str());
} else { } else {
error("Built-in symbol '%s' cannot be redefined\n", sym.name.c_str()); error("Built-in symbol '%s' cannot be redefined", sym.name.c_str());
} }
} }
@@ -215,12 +216,12 @@ static bool isAutoScoped(std::string const &symName) {
// Check for nothing after the dot // Check for nothing after the dot
if (dotPos == symName.length() - 1) { if (dotPos == symName.length() - 1) {
fatalerror("'%s' is a nonsensical reference to an empty local label\n", symName.c_str()); fatalerror("'%s' is a nonsensical reference to an empty local label", symName.c_str());
} }
// Check for more than one dot // Check for more than one dot
if (symName.find('.', dotPos + 1) != std::string::npos) { if (symName.find('.', dotPos + 1) != std::string::npos) {
fatalerror("'%s' is a nonsensical reference to a nested local label\n", symName.c_str()); fatalerror("'%s' is a nonsensical reference to a nested local label", symName.c_str());
} }
// Check for already-qualified local label // Check for already-qualified local label
@@ -230,7 +231,7 @@ static bool isAutoScoped(std::string const &symName) {
// Check for unqualifiable local label // Check for unqualifiable local label
if (!globalScope) { if (!globalScope) {
fatalerror("Unqualified local label '%s' in main scope\n", symName.c_str()); fatalerror("Unqualified local label '%s' in main scope", symName.c_str());
} }
return true; return true;
@@ -279,19 +280,19 @@ void sym_Purge(std::string const &symName) {
if (!sym) { if (!sym) {
if (sym_IsPurgedScoped(symName)) { if (sym_IsPurgedScoped(symName)) {
error("'%s' was already purged\n", symName.c_str()); error("'%s' was already purged", symName.c_str());
} else { } else {
error("'%s' not defined\n", symName.c_str()); error("'%s' not defined", symName.c_str());
} }
} else if (sym->isBuiltin) { } else if (sym->isBuiltin) {
error("Built-in symbol '%s' cannot be purged\n", symName.c_str()); error("Built-in symbol '%s' cannot be purged", symName.c_str());
} else if (sym->ID != UINT32_MAX) { } else if (sym->ID != UINT32_MAX) {
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName.c_str()); error("Symbol \"%s\" is referenced and thus cannot be purged", symName.c_str());
} else { } else {
if (sym->isExported) { if (sym->isExported) {
warning(WARNING_PURGE_1, "Purging an exported symbol \"%s\"\n", symName.c_str()); warning(WARNING_PURGE_1, "Purging an exported symbol \"%s\"", symName.c_str());
} else if (sym->isLabel()) { } else if (sym->isLabel()) {
warning(WARNING_PURGE_2, "Purging a label \"%s\"\n", symName.c_str()); warning(WARNING_PURGE_2, "Purging a label \"%s\"", symName.c_str());
} }
// Do not keep a reference to the label after purging it // Do not keep a reference to the label after purging it
if (sym == globalScope) { if (sym == globalScope) {
@@ -331,12 +332,12 @@ uint32_t Symbol::getConstantValue() const {
if (sym_IsPC(this)) { if (sym_IsPC(this)) {
if (!getSection()) { if (!getSection()) {
error("PC has no value outside of a section\n"); error("PC has no value outside of a section");
} else { } else {
error("PC does not have a constant value; the current section is not fixed\n"); error("PC does not have a constant value; the current section is not fixed");
} }
} else { } else {
error("\"%s\" does not have a constant value\n", name.c_str()); error("\"%s\" does not have a constant value", name.c_str());
} }
return 0; return 0;
} }
@@ -371,7 +372,7 @@ static Symbol *createNonrelocSymbol(std::string const &symName, bool numeric) {
return nullptr; // Don't allow overriding the symbol, that'd be bad! return nullptr; // Don't allow overriding the symbol, that'd be bad!
} else if (!numeric) { } else if (!numeric) {
// The symbol has already been referenced, but it's not allowed // The symbol has already been referenced, but it's not allowed
error("'%s' already referenced at ", symName.c_str()); errorNoNewline("'%s' already referenced", symName.c_str());
dumpFilename(*sym); dumpFilename(*sym);
return nullptr; // Don't allow overriding the symbol, that'd be bad! return nullptr; // Don't allow overriding the symbol, that'd be bad!
} }
@@ -437,7 +438,7 @@ Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string>
if (sym->isDefined()) { if (sym->isDefined()) {
alreadyDefinedError(*sym, "non-EQUS"); alreadyDefinedError(*sym, "non-EQUS");
} else { } else {
error("'%s' already referenced at ", symName.c_str()); errorNoNewline("'%s' already referenced", symName.c_str());
dumpFilename(*sym); dumpFilename(*sym);
} }
return nullptr; return nullptr;
@@ -493,7 +494,7 @@ static Symbol *addLabel(std::string const &symName) {
sym->section = sect_GetSymbolSection(); sym->section = sect_GetSymbolSection();
if (sym && !sym->section) { if (sym && !sym->section) {
error("Label \"%s\" created outside of a SECTION\n", symName.c_str()); error("Label \"%s\" created outside of a SECTION", symName.c_str());
} }
return sym; return sym;
@@ -549,7 +550,7 @@ std::string sym_MakeAnonLabelName(uint32_t ofs, bool neg) {
if (ofs > anonLabelID) { if (ofs > anonLabelID) {
error( error(
"Reference to anonymous label %" PRIu32 " before, when only %" PRIu32 "Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
" ha%s been created so far\n", " ha%s been created so far",
ofs, ofs,
anonLabelID, anonLabelID,
anonLabelID == 1 ? "s" : "ve" anonLabelID == 1 ? "s" : "ve"
@@ -563,7 +564,7 @@ std::string sym_MakeAnonLabelName(uint32_t ofs, bool neg) {
// LCOV_EXCL_START // LCOV_EXCL_START
error( error(
"Reference to anonymous label %" PRIu32 " after, when only %" PRIu32 "Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
" may still be created\n", " may still be created",
ofs + 1, ofs + 1,
UINT32_MAX - anonLabelID UINT32_MAX - anonLabelID
); );
@@ -580,7 +581,7 @@ void sym_Export(std::string const &symName) {
if (symName.starts_with('!')) { if (symName.starts_with('!')) {
// LCOV_EXCL_START // LCOV_EXCL_START
// The parser does not accept anonymous labels for an `EXPORT` directive // The parser does not accept anonymous labels for an `EXPORT` directive
error("Anonymous labels cannot be exported\n"); error("Anonymous labels cannot be exported");
return; return;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
@@ -666,7 +667,7 @@ void sym_Init(time_t now) {
// LCOV_EXCL_START // LCOV_EXCL_START
if (now == static_cast<time_t>(-1)) { if (now == static_cast<time_t>(-1)) {
warn("Failed to determine current time"); warnx("Failed to determine current time: %s", strerror(errno));
// Fall back by pretending we are at the Epoch // Fall back by pretending we are at the Epoch
now = 0; now = 0;
} }

View File

@@ -65,7 +65,7 @@ Diagnostics<WarningLevel, WarningID> warnings = {
}; };
// clang-format on // clang-format on
void printDiag( static void printDiag(
char const *fmt, va_list args, char const *type, char const *flagfmt, char const *flag char const *fmt, va_list args, char const *type, char const *flagfmt, char const *flag
) { ) {
fputs(type, stderr); fputs(type, stderr);
@@ -74,6 +74,7 @@ void printDiag(
fprintf(stderr, flagfmt, flag); fprintf(stderr, flagfmt, flag);
fputs("\n ", stderr); fputs("\n ", stderr);
vfprintf(stderr, fmt, args); vfprintf(stderr, fmt, args);
putc('\n', stderr);
lexer_DumpStringExpansions(); lexer_DumpStringExpansions();
} }
@@ -96,6 +97,28 @@ void error(char const *fmt, ...) {
} }
} }
void errorNoNewline(char const *fmt, ...) {
va_list args;
fputs("error: ", stderr);
fstk_DumpCurrent();
fputs(":\n ", stderr);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
// This intentionally makes 0 act as "unlimited" (or at least "limited to sizeof(unsigned)")
nbErrors++;
if (nbErrors == maxErrors) {
errx(
"The maximum of %u error%s was reached (configure with \"-X/--max-errors\"); assembly "
"aborted!",
maxErrors,
maxErrors == 1 ? "" : "s"
);
}
}
[[noreturn]] [[noreturn]]
void fatalerror(char const *fmt, ...) { void fatalerror(char const *fmt, ...) {
va_list args; va_list args;

View File

@@ -8,68 +8,22 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
static void vwarn(char const *fmt, va_list ap) {
char const *error = strerror(errno);
fprintf(stderr, "warning: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, ": %s\n", error);
}
static void vwarnx(char const *fmt, va_list ap) {
fprintf(stderr, "warning: ");
vfprintf(stderr, fmt, ap);
putc('\n', stderr);
}
[[noreturn]]
static void verr(char const *fmt, va_list ap) {
char const *error = strerror(errno);
fprintf(stderr, "error: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, ": %s\n", error);
va_end(ap);
exit(1);
}
[[noreturn]]
static void verrx(char const *fmt, va_list ap) {
fprintf(stderr, "error: ");
vfprintf(stderr, fmt, ap);
putc('\n', stderr);
va_end(ap);
exit(1);
}
void warn(char const *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vwarn(fmt, ap);
va_end(ap);
}
void warnx(char const *fmt, ...) { void warnx(char const *fmt, ...) {
va_list ap; va_list ap;
fputs("warning: ", stderr);
va_start(ap, fmt); va_start(ap, fmt);
vwarnx(fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap); va_end(ap);
} putc('\n', stderr);
[[noreturn]]
void err(char const *fmt, ...) {
va_list ap;
va_start(ap, fmt);
verr(fmt, ap);
} }
[[noreturn]] [[noreturn]]
void errx(char const *fmt, ...) { void errx(char const *fmt, ...) {
va_list ap; va_list ap;
fputs("error: ", stderr);
va_start(ap, fmt); va_start(ap, fmt);
verrx(fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
exit(1);
} }

View File

@@ -12,6 +12,7 @@
#include <string.h> #include <string.h>
#include <vector> #include <vector>
#include "error.hpp"
#include "extern/getopt.hpp" #include "extern/getopt.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "platform.hpp" #include "platform.hpp"
@@ -77,17 +78,32 @@ static void printUsage() {
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
static uint8_t nbErrors; static uint32_t nbErrors;
[[gnu::format(printf, 1, 2)]] [[gnu::format(printf, 1, 2)]]
static void report(char const *fmt, ...) { static void error(char const *fmt, ...) {
va_list ap; va_list ap;
fputs("error: ", stderr);
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap); va_end(ap);
putc('\n', stderr);
if (nbErrors != UINT8_MAX) { if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
[[gnu::format(printf, 1, 2)]]
static void fatal(char const *fmt, ...) {
va_list ap;
fputs("FATAL: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
if (nbErrors != UINT32_MAX) {
nbErrors++; nbErrors++;
} }
} }
@@ -164,6 +180,7 @@ enum MbcType {
}; };
static void printAcceptedMBCNames() { static void printAcceptedMBCNames() {
fputs("Accepted MBC names:\n", stderr);
fputs("\tROM ($00) [aka ROM_ONLY]\n", stderr); fputs("\tROM ($00) [aka ROM_ONLY]\n", stderr);
fputs("\tMBC1 ($01), MBC1+RAM ($02), MBC1+RAM+BATTERY ($03)\n", stderr); fputs("\tMBC1 ($01), MBC1+RAM ($02), MBC1+RAM+BATTERY ($03)\n", stderr);
fputs("\tMBC2 ($05), MBC2+BATTERY ($06)\n", stderr); fputs("\tMBC2 ($05), MBC2+BATTERY ($06)\n", stderr);
@@ -212,7 +229,6 @@ static bool readMBCSlice(char const *&name, char const *expected) {
static MbcType parseMBC(char const *name) { static MbcType parseMBC(char const *name) {
if (!strcasecmp(name, "help")) { if (!strcasecmp(name, "help")) {
fputs("Accepted MBC names:\n", stderr);
printAcceptedMBCNames(); printAcceptedMBCNames();
exit(0); exit(0);
} }
@@ -343,12 +359,12 @@ static MbcType parseMBC(char const *name) {
unsigned long val = strtoul(ptr, &endptr, 10); unsigned long val = strtoul(ptr, &endptr, 10);
if (endptr == ptr) { if (endptr == ptr) {
report("error: Failed to parse TPP1 major revision number\n"); error("Failed to parse TPP1 major revision number");
return MBC_BAD_TPP1; return MBC_BAD_TPP1;
} }
ptr = endptr; ptr = endptr;
if (val != 1) { if (val != 1) {
report("error: RGBFIX only supports TPP1 version 1.0\n"); error("RGBFIX only supports TPP1 version 1.0");
return MBC_BAD_TPP1; return MBC_BAD_TPP1;
} }
tpp1Rev[0] = val; tpp1Rev[0] = val;
@@ -356,12 +372,12 @@ static MbcType parseMBC(char const *name) {
// Minor // Minor
val = strtoul(ptr, &endptr, 10); val = strtoul(ptr, &endptr, 10);
if (endptr == ptr) { if (endptr == ptr) {
report("error: Failed to parse TPP1 minor revision number\n"); error("Failed to parse TPP1 minor revision number");
return MBC_BAD_TPP1; return MBC_BAD_TPP1;
} }
ptr = endptr; ptr = endptr;
if (val > 0xFF) { if (val > 0xFF) {
report("error: TPP1 minor revision number must be 8-bit\n"); error("TPP1 minor revision number must be 8-bit");
return MBC_BAD_TPP1; return MBC_BAD_TPP1;
} }
tpp1Rev[1] = val; tpp1Rev[1] = val;
@@ -509,7 +525,7 @@ static MbcType parseMBC(char const *name) {
// Handle timer, which also requires battery // Handle timer, which also requires battery
if (features & TIMER) { if (features & TIMER) {
if (!(features & BATTERY)) { if (!(features & BATTERY)) {
fprintf(stderr, "warning: MBC3+TIMER implies BATTERY\n"); warnx("MBC3+TIMER implies BATTERY");
} }
features &= ~(TIMER | BATTERY); // Reset those bits features &= ~(TIMER | BATTERY); // Reset those bits
mbc = MBC3_TIMER_BATTERY; mbc = MBC3_TIMER_BATTERY;
@@ -571,9 +587,7 @@ static MbcType parseMBC(char const *name) {
case TPP1: case TPP1:
if (features & RAM) { if (features & RAM) {
fprintf( warnx("TPP1 requests RAM implicitly if given a non-zero RAM size");
stderr, "warning: TPP1 requests RAM implicitly if given a non-zero RAM size"
);
} }
if (features & BATTERY) { if (features & BATTERY) {
mbc |= 0x08; mbc |= 0x08;
@@ -864,7 +878,7 @@ static void overwriteByte(uint8_t *rom0, uint16_t addr, uint8_t fixedByte, char
uint8_t origByte = rom0[addr]; uint8_t origByte = rom0[addr];
if (!overwriteRom && origByte != 0 && origByte != fixedByte) { if (!overwriteRom && origByte != 0 && origByte != fixedByte) {
fprintf(stderr, "warning: Overwrote a non-zero byte in the %s\n", areaName); warnx("Overwrote a non-zero byte in the %s", areaName);
} }
rom0[addr] = fixedByte; rom0[addr] = fixedByte;
@@ -878,7 +892,7 @@ static void overwriteBytes(
uint8_t origByte = rom0[i + startAddr]; uint8_t origByte = rom0[i + startAddr];
if (origByte != 0 && origByte != fixed[i]) { if (origByte != 0 && origByte != fixed[i]) {
fprintf(stderr, "warning: Overwrote a non-zero byte in the %s\n", areaName); warnx("Overwrote a non-zero byte in the %s", areaName);
break; break;
} }
} }
@@ -902,12 +916,12 @@ static void
if (rom0Len == -1) { if (rom0Len == -1) {
// LCOV_EXCL_START // LCOV_EXCL_START
report("FATAL: Failed to read \"%s\"'s header: %s\n", name, strerror(errno)); fatal("Failed to read \"%s\"'s header: %s", name, strerror(errno));
return; return;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} else if (rom0Len < headerSize) { } else if (rom0Len < headerSize) {
report( fatal(
"FATAL: \"%s\" too short, expected at least %jd ($%jx) bytes, got only %jd\n", "\"%s\" too short, expected at least %jd ($%jx) bytes, got only %jd",
name, name,
static_cast<intmax_t>(headerSize), static_cast<intmax_t>(headerSize),
static_cast<intmax_t>(headerSize), static_cast<intmax_t>(headerSize),
@@ -990,11 +1004,7 @@ static void
if (oldLicensee != UNSPECIFIED) { if (oldLicensee != UNSPECIFIED) {
overwriteByte(rom0, 0x14B, oldLicensee, "old licensee code"); overwriteByte(rom0, 0x14B, oldLicensee, "old licensee code");
} else if (sgb && rom0[0x14B] != 0x33) { } else if (sgb && rom0[0x14B] != 0x33) {
fprintf( warnx("SGB compatibility enabled, but old licensee was 0x%02x, not 0x33", rom0[0x14B]);
stderr,
"warning: SGB compatibility enabled, but old licensee was 0x%02x, not 0x33\n",
rom0[0x14B]
);
} }
if (romVersion != UNSPECIFIED) { if (romVersion != UNSPECIFIED) {
@@ -1019,7 +1029,7 @@ static void
// Handle ROMX // Handle ROMX
if (input == output) { if (input == output) {
if (fileSize >= 0x10000 * BANK_SIZE) { if (fileSize >= 0x10000 * BANK_SIZE) {
report("FATAL: \"%s\" has more than 65536 banks\n", name); fatal("\"%s\" has more than 65536 banks", name);
return; return;
} }
// This should be guaranteed from the size cap... // This should be guaranteed from the size cap...
@@ -1041,7 +1051,7 @@ static void
0x10000 * BANK_SIZE <= SSIZE_MAX, "Max input file size too large for OS" 0x10000 * BANK_SIZE <= SSIZE_MAX, "Max input file size too large for OS"
); );
if (nbBanks == 0x10000) { if (nbBanks == 0x10000) {
report("FATAL: \"%s\" has more than 65536 banks\n", name); fatal("\"%s\" has more than 65536 banks", name);
return; return;
} }
nbBanks++; nbBanks++;
@@ -1143,7 +1153,7 @@ static void
if (input == output) { if (input == output) {
if (lseek(output, 0, SEEK_SET) == static_cast<off_t>(-1)) { if (lseek(output, 0, SEEK_SET) == static_cast<off_t>(-1)) {
// LCOV_EXCL_START // LCOV_EXCL_START
report("FATAL: Failed to rewind \"%s\": %s\n", name, strerror(errno)); fatal("Failed to rewind \"%s\": %s", name, strerror(errno));
return; return;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
@@ -1157,13 +1167,13 @@ static void
if (writeLen == -1) { if (writeLen == -1) {
// LCOV_EXCL_START // LCOV_EXCL_START
report("FATAL: Failed to write \"%s\"'s ROM0: %s\n", name, strerror(errno)); fatal("Failed to write \"%s\"'s ROM0: %s", name, strerror(errno));
return; return;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} else if (writeLen < rom0Len) { } else if (writeLen < rom0Len) {
// LCOV_EXCL_START // LCOV_EXCL_START
report( fatal(
"FATAL: Could only write %jd of \"%s\"'s %jd ROM0 bytes\n", "Could only write %jd of \"%s\"'s %jd ROM0 bytes",
static_cast<intmax_t>(writeLen), static_cast<intmax_t>(writeLen),
name, name,
static_cast<intmax_t>(rom0Len) static_cast<intmax_t>(rom0Len)
@@ -1179,13 +1189,13 @@ static void
writeLen = writeBytes(output, romx.data(), totalRomxLen); writeLen = writeBytes(output, romx.data(), totalRomxLen);
if (writeLen == -1) { if (writeLen == -1) {
// LCOV_EXCL_START // LCOV_EXCL_START
report("FATAL: Failed to write \"%s\"'s ROMX: %s\n", name, strerror(errno)); fatal("Failed to write \"%s\"'s ROMX: %s", name, strerror(errno));
return; return;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} else if (static_cast<size_t>(writeLen) < totalRomxLen) { } else if (static_cast<size_t>(writeLen) < totalRomxLen) {
// LCOV_EXCL_START // LCOV_EXCL_START
report( fatal(
"FATAL: Could only write %jd of \"%s\"'s %zu ROMX bytes\n", "Could only write %jd of \"%s\"'s %zu ROMX bytes",
static_cast<intmax_t>(writeLen), static_cast<intmax_t>(writeLen),
name, name,
totalRomxLen totalRomxLen
@@ -1200,7 +1210,7 @@ static void
if (input == output) { if (input == output) {
if (lseek(output, 0, SEEK_END) == static_cast<off_t>(-1)) { if (lseek(output, 0, SEEK_END) == static_cast<off_t>(-1)) {
// LCOV_EXCL_START // LCOV_EXCL_START
report("FATAL: Failed to seek to end of \"%s\": %s\n", name, strerror(errno)); fatal("Failed to seek to end of \"%s\": %s", name, strerror(errno));
return; return;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
@@ -1217,7 +1227,7 @@ static void
// so it's fine to cast to `size_t` // so it's fine to cast to `size_t`
if (static_cast<size_t>(ret) != thisLen) { if (static_cast<size_t>(ret) != thisLen) {
// LCOV_EXCL_START // LCOV_EXCL_START
report("FATAL: Failed to write \"%s\"'s padding: %s\n", name, strerror(errno)); fatal("Failed to write \"%s\"'s padding: %s", name, strerror(errno));
break; break;
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} }
@@ -1243,9 +1253,7 @@ static bool processFilename(char const *name, char const *outputName) {
} else { } else {
output = open(outputName, O_WRONLY | O_BINARY | O_CREAT, 0600); output = open(outputName, O_WRONLY | O_BINARY | O_CREAT, 0600);
if (output == -1) { if (output == -1) {
report( fatal("Failed to open \"%s\" for writing: %s", outputName, strerror(errno));
"FATAL: Failed to open \"%s\" for writing: %s\n", outputName, strerror(errno)
);
return true; return true;
} }
openedOutput = true; openedOutput = true;
@@ -1268,26 +1276,23 @@ static bool processFilename(char const *name, char const *outputName) {
// 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.
if (int input = open(name, (outputName ? O_RDONLY : O_RDWR) | O_BINARY); input == -1) { if (int input = open(name, (outputName ? O_RDONLY : O_RDWR) | O_BINARY); input == -1) {
report("FATAL: Failed to open \"%s\" for reading+writing: %s\n", name, strerror(errno)); fatal("Failed to open \"%s\" for reading+writing: %s", name, strerror(errno));
} else { } else {
Defer closeInput{[&] { close(input); }}; Defer closeInput{[&] { close(input); }};
struct stat stat; struct stat stat;
if (fstat(input, &stat) == -1) { if (fstat(input, &stat) == -1) {
// LCOV_EXCL_START // LCOV_EXCL_START
report("FATAL: Failed to stat \"%s\": %s\n", name, strerror(errno)); fatal("Failed to stat \"%s\": %s", name, strerror(errno));
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} else if (!S_ISREG(stat.st_mode)) { // We do not support FIFOs or symlinks } else if (!S_ISREG(stat.st_mode)) { // We do not support FIFOs or symlinks
// LCOV_EXCL_START // LCOV_EXCL_START
report( fatal("\"%s\" is not a regular file, and thus cannot be modified in-place", name);
"FATAL: \"%s\" is not a regular file, and thus cannot be modified in-place\n",
name
);
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
} 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
// prevents passing a file size of 0, which usually indicates pipes // prevents passing a file size of 0, which usually indicates pipes
report( fatal(
"FATAL: \"%s\" too short, expected at least 336 ($150) bytes, got only %jd\n", "\"%s\" too short, expected at least 336 ($150) bytes, got only %jd",
name, name,
static_cast<intmax_t>(stat.st_size) static_cast<intmax_t>(stat.st_size)
); );
@@ -1314,7 +1319,7 @@ static bool processFilename(char const *name, char const *outputName) {
static void parseByte(uint16_t &output, char name) { static void parseByte(uint16_t &output, char name) {
if (musl_optarg[0] == 0) { if (musl_optarg[0] == 0) {
report("error: Argument to option '%c' may not be empty\n", name); error("Argument to option '%c' may not be empty", name);
} else { } else {
char *endptr; char *endptr;
unsigned long value; unsigned long value;
@@ -1325,11 +1330,9 @@ static void parseByte(uint16_t &output, char name) {
value = strtoul(musl_optarg, &endptr, 0); value = strtoul(musl_optarg, &endptr, 0);
} }
if (*endptr) { if (*endptr) {
report( error("Expected number as argument to option '%c', got %s", name, musl_optarg);
"error: Expected number as argument to option '%c', got %s\n", name, musl_optarg
);
} else if (value > 0xFF) { } else if (value > 0xFF) {
report("error: Argument to option '%c' is larger than 255: %lu\n", name, value); error("Argument to option '%c' is larger than 255: %lu", name, value);
} else { } else {
output = value; output = value;
} }
@@ -1349,7 +1352,7 @@ int main(int argc, char *argv[]) {
model = ch == 'c' ? BOTH : CGB; model = ch == 'c' ? BOTH : CGB;
if (titleLen > 15) { if (titleLen > 15) {
titleLen = 15; titleLen = 15;
fprintf(stderr, "warning: Truncating title \"%s\" to 15 chars\n", title); warnx("Truncating title \"%s\" to 15 chars", title);
} }
break; break;
@@ -1360,7 +1363,7 @@ int main(int argc, char *argv[]) {
#define OVERRIDE_SPEC(cur, bad, curFlag, badFlag) \ #define OVERRIDE_SPEC(cur, bad, curFlag, badFlag) \
case STR(cur)[0]: \ case STR(cur)[0]: \
if (fixSpec & badFlag) { \ if (fixSpec & badFlag) { \
fprintf(stderr, "warning: '" STR(cur) "' overriding '" STR(bad) "' in fix spec\n"); \ warnx("'" STR(cur) "' overriding '" STR(bad) "' in fix spec"); \
} \ } \
fixSpec = (fixSpec & ~badFlag) | curFlag; \ fixSpec = (fixSpec & ~badFlag) | curFlag; \
break break
@@ -1374,7 +1377,7 @@ int main(int argc, char *argv[]) {
#undef overrideSpecs #undef overrideSpecs
default: default:
fprintf(stderr, "warning: Ignoring '%c' in fix spec\n", *musl_optarg); warnx("Ignoring '%c' in fix spec", *musl_optarg);
} }
musl_optarg++; musl_optarg++;
} }
@@ -1391,12 +1394,12 @@ int main(int argc, char *argv[]) {
len = strlen(gameID); len = strlen(gameID);
if (len > 4) { if (len > 4) {
len = 4; len = 4;
fprintf(stderr, "warning: Truncating game ID \"%s\" to 4 chars\n", gameID); warnx("Truncating game ID \"%s\" to 4 chars", gameID);
} }
gameIDLen = len; gameIDLen = len;
if (titleLen > 11) { if (titleLen > 11) {
titleLen = 11; titleLen = 11;
fprintf(stderr, "warning: Truncating title \"%s\" to 11 chars\n", title); warnx("Truncating title \"%s\" to 11 chars", title);
} }
break; break;
@@ -1409,9 +1412,7 @@ int main(int argc, char *argv[]) {
len = strlen(newLicensee); len = strlen(newLicensee);
if (len > 2) { if (len > 2) {
len = 2; len = 2;
fprintf( warnx("Truncating new licensee \"%s\" to 2 chars", newLicensee);
stderr, "warning: Truncating new licensee \"%s\" to 2 chars\n", newLicensee
);
} }
newLicenseeLen = len; newLicenseeLen = len;
break; break;
@@ -1427,22 +1428,15 @@ int main(int argc, char *argv[]) {
case 'm': case 'm':
cartridgeType = parseMBC(musl_optarg); cartridgeType = parseMBC(musl_optarg);
if (cartridgeType == MBC_BAD) { if (cartridgeType == MBC_BAD) {
report("error: Unknown MBC \"%s\"\nAccepted MBC names:\n", musl_optarg); error("Unknown MBC \"%s\"", musl_optarg);
printAcceptedMBCNames(); printAcceptedMBCNames();
} else if (cartridgeType == MBC_WRONG_FEATURES) { } else if (cartridgeType == MBC_WRONG_FEATURES) {
report( error("Features incompatible with MBC (\"%s\")", musl_optarg);
"error: Features incompatible with MBC (\"%s\")\nAccepted combinations:\n",
musl_optarg
);
printAcceptedMBCNames(); printAcceptedMBCNames();
} else if (cartridgeType == MBC_BAD_RANGE) { } else if (cartridgeType == MBC_BAD_RANGE) {
report("error: Specified MBC ID out of range 0-255: %s\n", musl_optarg); error("Specified MBC ID out of range 0-255: %s", musl_optarg);
} else if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) { } else if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
fprintf( warnx("MBC \"%s\" is under-specified and poorly supported", musl_optarg);
stderr,
"warning: MBC \"%s\" is under-specified and poorly supported\n",
musl_optarg
);
} }
break; break;
@@ -1477,7 +1471,7 @@ int main(int argc, char *argv[]) {
if (len > maxLen) { if (len > maxLen) {
len = maxLen; len = maxLen;
fprintf(stderr, "warning: Truncating title \"%s\" to %u chars\n", title, maxLen); warnx("Truncating title \"%s\" to %u chars", title, maxLen);
} }
titleLen = len; titleLen = len;
break; break;
@@ -1502,52 +1496,30 @@ int main(int argc, char *argv[]) {
} }
if ((cartridgeType & 0xFF00) == TPP1 && !japanese) { if ((cartridgeType & 0xFF00) == TPP1 && !japanese) {
fprintf( warnx("TPP1 overwrites region flag for its identification code, ignoring `-j`");
stderr,
"warning: TPP1 overwrites region flag for its identification code, ignoring `-j`\n"
);
} }
// Check that RAM size is correct for "standard" mappers // Check that RAM size is correct for "standard" mappers
if (ramSize != UNSPECIFIED && (cartridgeType & 0xFF00) == 0) { if (ramSize != UNSPECIFIED && (cartridgeType & 0xFF00) == 0) {
if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) { if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
if (ramSize != 1) { if (ramSize != 1) {
fprintf( warnx("MBC \"%s\" should have 2 KiB of RAM (-r 1)", mbcName(cartridgeType));
stderr,
"warning: MBC \"%s\" should have 2 KiB of RAM (-r 1)\n",
mbcName(cartridgeType)
);
} }
} else if (hasRAM(cartridgeType)) { } else if (hasRAM(cartridgeType)) {
if (!ramSize) { if (!ramSize) {
fprintf( warnx("MBC \"%s\" has RAM, but RAM size was set to 0", mbcName(cartridgeType));
stderr,
"warning: MBC \"%s\" has RAM, but RAM size was set to 0\n",
mbcName(cartridgeType)
);
} else if (ramSize == 1) { } else if (ramSize == 1) {
fprintf( warnx("RAM size 1 (2 KiB) was specified for MBC \"%s\"", mbcName(cartridgeType));
stderr,
"warning: RAM size 1 (2 KiB) was specified for MBC \"%s\"\n",
mbcName(cartridgeType)
);
} }
} else if (ramSize) { } else if (ramSize) {
fprintf( warnx(
stderr, "MBC \"%s\" has no RAM, but RAM size was set to %u", mbcName(cartridgeType), ramSize
"warning: MBC \"%s\" has no RAM, but RAM size was set to %u\n",
mbcName(cartridgeType),
ramSize
); );
} }
} }
if (sgb && oldLicensee != UNSPECIFIED && oldLicensee != 0x33) { if (sgb && oldLicensee != UNSPECIFIED && oldLicensee != 0x33) {
fprintf( warnx("SGB compatibility enabled, but old licensee is 0x%02x, not 0x33", oldLicensee);
stderr,
"warning: SGB compatibility enabled, but old licensee is 0x%02x, not 0x33\n",
oldLicensee
);
} }
argv += musl_optind; argv += musl_optind;
@@ -1563,19 +1535,14 @@ int main(int argc, char *argv[]) {
logoFile = stdin; logoFile = stdin;
} }
if (!logoFile) { if (!logoFile) {
fprintf( fatal("Failed to open \"%s\" for reading: %s", logoFilename, strerror(errno));
stderr,
"FATAL: Failed to open \"%s\" for reading: %s\n",
logoFilename,
strerror(errno)
);
exit(1); exit(1);
} }
Defer closeLogo{[&] { fclose(logoFile); }}; Defer closeLogo{[&] { fclose(logoFile); }};
uint8_t logoBpp[sizeof(logo)]; uint8_t logoBpp[sizeof(logo)];
if (size_t nbRead = fread(logoBpp, 1, sizeof(logoBpp), logoFile); if (size_t nbRead = fread(logoBpp, 1, sizeof(logoBpp), logoFile);
nbRead != sizeof(logo) || fgetc(logoFile) != EOF || ferror(logoFile)) { nbRead != sizeof(logo) || fgetc(logoFile) != EOF || ferror(logoFile)) {
fprintf(stderr, "FATAL: \"%s\" is not %zu bytes\n", logoFilename, sizeof(logo)); fatal("\"%s\" is not %zu bytes", logoFilename, sizeof(logo));
exit(1); exit(1);
} }
auto highs = [&logoBpp](size_t i) { auto highs = [&logoBpp](size_t i) {
@@ -1605,15 +1572,13 @@ int main(int argc, char *argv[]) {
} }
if (!*argv) { if (!*argv) {
fputs( fatal("Please specify an input file (pass `-` to read from standard input)");
"FATAL: Please specify an input file (pass `-` to read from standard input)\n", stderr
);
printUsage(); printUsage();
exit(1); exit(1);
} }
if (outputFilename && argc != musl_optind + 1) { if (outputFilename && argc != musl_optind + 1) {
fputs("FATAL: If `-o` is set then only a single input file may be specified\n", stderr); fatal("If `-o` is set then only a single input file may be specified");
printUsage(); printUsage();
exit(1); exit(1);
} }

View File

@@ -14,6 +14,7 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#include "error.hpp"
#include "extern/getopt.hpp" #include "extern/getopt.hpp"
#include "file.hpp" #include "file.hpp"
#include "platform.hpp" #include "platform.hpp"
@@ -22,6 +23,7 @@
#include "gfx/pal_spec.hpp" #include "gfx/pal_spec.hpp"
#include "gfx/process.hpp" #include "gfx/process.hpp"
#include "gfx/reverse.hpp" #include "gfx/reverse.hpp"
#include "gfx/warning.hpp"
using namespace std::literals::string_view_literals; using namespace std::literals::string_view_literals;
@@ -37,69 +39,6 @@ static struct LocalOptions {
bool reverse; bool reverse;
} localOptions; } localOptions;
static uintmax_t nbErrors;
[[noreturn]]
void giveUp() {
fprintf(stderr, "Conversion aborted after %ju error%s\n", nbErrors, nbErrors == 1 ? "" : "s");
exit(1);
}
void requireZeroErrors() {
if (nbErrors != 0) {
giveUp();
}
}
void warning(char const *fmt, ...) {
va_list ap;
fputs("warning: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
}
void error(char const *fmt, ...) {
va_list ap;
fputs("error: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max()) {
nbErrors++;
}
}
void errorMessage(char const *msg) {
fprintf(stderr, "error: %s\n", msg);
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max()) {
nbErrors++;
}
}
[[noreturn]]
void fatal(char const *fmt, ...) {
va_list ap;
fputs("FATAL: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max()) {
nbErrors++;
}
giveUp();
}
void Options::verbosePrint(uint8_t level, char const *fmt, ...) const { void Options::verbosePrint(uint8_t level, char const *fmt, ...) const {
// LCOV_EXCL_START // LCOV_EXCL_START
if (verbosity >= level) { if (verbosity >= level) {
@@ -179,6 +118,19 @@ static void printUsage() {
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
[[gnu::format(printf, 1, 2), noreturn]]
static void fatalWithUsage(char const *fmt, ...) {
va_list ap;
fputs("FATAL: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
printUsage();
exit(1);
}
// Parses a number at the beginning of a string, moving the pointer to skip the parsed characters. // Parses a number at the beginning of a string, moving the pointer to skip the parsed characters.
// Returns the provided errVal on error. // Returns the provided errVal on error.
static uint16_t parseNumber(char *&string, char const *errPrefix, uint16_t errVal = UINT16_MAX) { static uint16_t parseNumber(char *&string, char const *errPrefix, uint16_t errVal = UINT16_MAX) {
@@ -256,19 +208,13 @@ static void skipWhitespace(char *&arg) {
static void registerInput(char const *arg) { static void registerInput(char const *arg) {
if (!options.input.empty()) { if (!options.input.empty()) {
fprintf( fatalWithUsage(
stderr, "Input image specified more than once! (first \"%s\", then \"%s\")",
"FATAL: input image specified more than once! (first \"%s\", then "
"\"%s\")\n",
options.input.c_str(), options.input.c_str(),
arg arg
); );
printUsage();
exit(1);
} else if (arg[0] == '\0') { // Empty input path } else if (arg[0] == '\0') { // Empty input path
fprintf(stderr, "FATAL: input image path cannot be empty\n"); fatalWithUsage("Input image path cannot be empty");
printUsage();
exit(1);
} else { } else {
options.input = arg; options.input = arg;
} }
@@ -361,7 +307,7 @@ static char *parseArgv(int argc, char *argv[]) {
case 'a': case 'a':
localOptions.autoAttrmap = false; localOptions.autoAttrmap = false;
if (!options.attrmap.empty()) { if (!options.attrmap.empty()) {
warning("Overriding attrmap file %s", options.attrmap.c_str()); warnx("Overriding attrmap file %s", options.attrmap.c_str());
} }
options.attrmap = musl_optarg; options.attrmap = musl_optarg;
break; break;
@@ -438,7 +384,7 @@ static char *parseArgv(int argc, char *argv[]) {
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
case 'i': case 'i':
if (!options.inputTileset.empty()) { if (!options.inputTileset.empty()) {
warning("Overriding input tileset file %s", options.inputTileset.c_str()); warnx("Overriding input tileset file %s", options.inputTileset.c_str());
} }
options.inputTileset = musl_optarg; options.inputTileset = musl_optarg;
break; break;
@@ -548,7 +494,7 @@ static char *parseArgv(int argc, char *argv[]) {
break; break;
case 'o': case 'o':
if (!options.output.empty()) { if (!options.output.empty()) {
warning("Overriding tile data file %s", options.output.c_str()); warnx("Overriding tile data file %s", options.output.c_str());
} }
options.output = musl_optarg; options.output = musl_optarg;
break; break;
@@ -558,7 +504,7 @@ static char *parseArgv(int argc, char *argv[]) {
case 'p': case 'p':
localOptions.autoPalettes = false; localOptions.autoPalettes = false;
if (!options.palettes.empty()) { if (!options.palettes.empty()) {
warning("Overriding palettes file %s", options.palettes.c_str()); warnx("Overriding palettes file %s", options.palettes.c_str());
} }
options.palettes = musl_optarg; options.palettes = musl_optarg;
break; break;
@@ -568,7 +514,7 @@ static char *parseArgv(int argc, char *argv[]) {
case 'q': case 'q':
localOptions.autoPalmap = false; localOptions.autoPalmap = false;
if (!options.palmap.empty()) { if (!options.palmap.empty()) {
warning("Overriding palette map file %s", options.palmap.c_str()); warnx("Overriding palette map file %s", options.palmap.c_str());
} }
options.palmap = musl_optarg; options.palmap = musl_optarg;
break; break;
@@ -596,7 +542,7 @@ static char *parseArgv(int argc, char *argv[]) {
case 't': case 't':
localOptions.autoTilemap = false; localOptions.autoTilemap = false;
if (!options.tilemap.empty()) { if (!options.tilemap.empty()) {
warning("Overriding tilemap file %s", options.tilemap.c_str()); warnx("Overriding tilemap file %s", options.tilemap.c_str());
} }
options.tilemap = musl_optarg; options.tilemap = musl_optarg;
break; break;
@@ -728,13 +674,10 @@ int main(int argc, char *argv[]) {
if (autoOptEnabled) { if (autoOptEnabled) {
auto &image = localOptions.groupOutputs ? options.output : options.input; auto &image = localOptions.groupOutputs ? options.output : options.input;
if (image.empty()) { if (image.empty()) {
fprintf( fatalWithUsage(
stderr, "No %s specified",
"FATAL: No %s specified\n",
localOptions.groupOutputs ? "output tile data file" : "input image" localOptions.groupOutputs ? "output tile data file" : "input image"
); );
printUsage();
exit(1);
} }
// Manual implementation of std::filesystem::path.replace_extension(). // Manual implementation of std::filesystem::path.replace_extension().
@@ -921,9 +864,7 @@ int main(int argc, char *argv[]) {
&& !localOptions.reverse) { && !localOptions.reverse) {
processPalettes(); processPalettes();
} else { } else {
fputs("FATAL: No input image specified\n", stderr); fatalWithUsage("No input image specified");
printUsage();
exit(1);
} }
requireZeroErrors(); requireZeroErrors();

View File

@@ -16,10 +16,12 @@
#include <string_view> #include <string_view>
#include <tuple> #include <tuple>
#include "error.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "platform.hpp" #include "platform.hpp"
#include "gfx/main.hpp" #include "gfx/main.hpp"
#include "gfx/warning.hpp"
using namespace std::string_view_literals; using namespace std::string_view_literals;
@@ -60,7 +62,7 @@ void parseInlinePalSpec(char const * const rawArg) {
assume(ofs <= arg.length()); assume(ofs <= arg.length());
assume(len <= arg.length()); assume(len <= arg.length());
errorMessage(msg); error("%s", msg); // `format_` and `-Wformat-security` would complain about `error(msg);`
fprintf( fprintf(
stderr, stderr,
"In inline palette spec: %s\n" "In inline palette spec: %s\n"
@@ -286,7 +288,7 @@ static void parsePSPFile(std::filebuf &file) {
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes; if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes;
*nbColors > nbPalColors) { *nbColors > nbPalColors) {
warning( warnx(
"PSP file contains %" PRIu16 " colors, but there can only be %" PRIu16 "PSP file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra", "; ignoring extra",
*nbColors, *nbColors,
@@ -368,7 +370,7 @@ static void parseGPLFile(std::filebuf &file) {
} }
if (nbColors > maxNbColors) { if (nbColors > maxNbColors) {
warning( warnx(
"GPL file contains %" PRIu16 " colors, but there can only be %" PRIu16 "GPL file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra", "; ignoring extra",
nbColors, nbColors,
@@ -416,7 +418,7 @@ static void parseHEXFile(std::filebuf &file) {
} }
if (nbColors > maxNbColors) { if (nbColors > maxNbColors) {
warning( warnx(
"HEX file contains %" PRIu16 " colors, but there can only be %" PRIu16 "HEX file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra", "; ignoring extra",
nbColors, nbColors,
@@ -445,7 +447,7 @@ static void parseACTFile(std::filebuf &file) {
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes; if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes;
nbColors > nbPalColors) { nbColors > nbPalColors) {
warning( warnx(
"ACT file contains %" PRIu16 " colors, but there can only be %" PRIu16 "ACT file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra", "; ignoring extra",
nbColors, nbColors,
@@ -499,7 +501,7 @@ static void parseACOFile(std::filebuf &file) {
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes; if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes;
nbColors > nbPalColors) { nbColors > nbPalColors) {
warning( warnx(
"ACO file contains %" PRIu16 " colors, but there can only be %" PRIu16 "ACO file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra", "; ignoring extra",
nbColors, nbColors,

View File

@@ -16,6 +16,7 @@
#include <vector> #include <vector>
#include "defaultinitvec.hpp" #include "defaultinitvec.hpp"
#include "error.hpp"
#include "file.hpp" #include "file.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "itertools.hpp" #include "itertools.hpp"
@@ -24,6 +25,7 @@
#include "gfx/pal_packing.hpp" #include "gfx/pal_packing.hpp"
#include "gfx/pal_sorting.hpp" #include "gfx/pal_sorting.hpp"
#include "gfx/proto_palette.hpp" #include "gfx/proto_palette.hpp"
#include "gfx/warning.hpp"
static bool isBgColorTransparent() { static bool isBgColorTransparent() {
return options.bgColor.has_value() && options.bgColor->isTransparent(); return options.bgColor.has_value() && options.bgColor->isTransparent();
@@ -92,7 +94,7 @@ class Png {
static void handleWarning(png_structp png, char const *msg) { static void handleWarning(png_structp png, char const *msg) {
Png *self = reinterpret_cast<Png *>(png_get_error_ptr(png)); Png *self = reinterpret_cast<Png *>(png_get_error_ptr(png));
warning("In input image (\"%s\"): %s", self->c_str(), msg); warnx("In input image (\"%s\"): %s", self->c_str(), msg);
} }
static void readData(png_structp png, png_bytep data, size_t length) { static void readData(png_structp png, png_bytep data, size_t length) {
@@ -385,7 +387,7 @@ public:
std::tuple conflicting{color.toCSS(), other->toCSS()}; std::tuple conflicting{color.toCSS(), other->toCSS()};
// Do not report combinations twice // Do not report combinations twice
if (std::find(RANGE(conflicts), conflicting) == conflicts.end()) { if (std::find(RANGE(conflicts), conflicting) == conflicts.end()) {
warning( warnx(
"Fusing colors #%08x and #%08x into Game Boy color $%04x [first seen " "Fusing colors #%08x and #%08x into Game Boy color $%04x [first seen "
"at x: %" PRIu32 ", y: %" PRIu32 "]", "at x: %" PRIu32 ", y: %" PRIu32 "]",
std::get<0>(conflicting), std::get<0>(conflicting),

View File

@@ -13,10 +13,12 @@
#include <vector> #include <vector>
#include "defaultinitvec.hpp" #include "defaultinitvec.hpp"
#include "error.hpp"
#include "file.hpp" #include "file.hpp"
#include "helpers.hpp" // assume #include "helpers.hpp" // assume
#include "gfx/main.hpp" #include "gfx/main.hpp"
#include "gfx/warning.hpp"
static DefaultInitVec<uint8_t> readInto(std::string const &path) { static DefaultInitVec<uint8_t> readInto(std::string const &path) {
File file; File file;
@@ -59,7 +61,7 @@ static void pngError(png_structp png, char const *msg) {
} }
static void pngWarning(png_structp png, char const *msg) { static void pngWarning(png_structp png, char const *msg) {
warning( warnx(
"While writing reversed image (\"%s\"): %s", "While writing reversed image (\"%s\"): %s",
static_cast<char const *>(png_get_error_ptr(png)), static_cast<char const *>(png_get_error_ptr(png)),
msg msg
@@ -106,19 +108,19 @@ void reverse() {
} }
if (options.allowDedup && options.tilemap.empty()) { if (options.allowDedup && options.tilemap.empty()) {
warning("Tile deduplication is enabled, but no tilemap is provided?"); warnx("Tile deduplication is enabled, but no tilemap is provided?");
} }
if (options.useColorCurve) { if (options.useColorCurve) {
warning("The color curve is not yet supported in reverse mode..."); warnx("The color curve is not yet supported in reverse mode...");
} }
if (options.inputSlice.left != 0 || options.inputSlice.top != 0 if (options.inputSlice.left != 0 || options.inputSlice.top != 0
|| options.inputSlice.height != 0) { || options.inputSlice.height != 0) {
warning("\"Sliced-off\" pixels are ignored in reverse mode"); warnx("\"Sliced-off\" pixels are ignored in reverse mode");
} }
if (options.inputSlice.width != 0 && options.inputSlice.width != options.reversedWidth * 8) { if (options.inputSlice.width != 0 && options.inputSlice.width != options.reversedWidth * 8) {
warning( warnx(
"Specified input slice width (%" PRIu16 "Specified input slice width (%" PRIu16
") doesn't match provided reversing width (%" PRIu16 " * 8)", ") doesn't match provided reversing width (%" PRIu16 " * 8)",
options.inputSlice.width, options.inputSlice.width,
@@ -152,7 +154,7 @@ void reverse() {
fatal("Cannot generate empty image"); fatal("Cannot generate empty image");
} }
if (mapSize > options.maxNbTiles[0] + options.maxNbTiles[1]) { if (mapSize > options.maxNbTiles[0] + options.maxNbTiles[1]) {
warning( warnx(
"Total number of tiles (%zu) is more than the limit of %" PRIu16 " + %" PRIu16, "Total number of tiles (%zu) is more than the limit of %" PRIu16 " + %" PRIu16,
mapSize, mapSize,
options.maxNbTiles[0], options.maxNbTiles[0],
@@ -234,7 +236,7 @@ void reverse() {
} while (nbRead != 0); } while (nbRead != 0);
if (palettes.size() > options.nbPalettes) { if (palettes.size() > options.nbPalettes) {
warning( warnx(
"Read %zu palettes, more than the specified limit of %" PRIu16, "Read %zu palettes, more than the specified limit of %" PRIu16,
palettes.size(), palettes.size(),
options.nbPalettes options.nbPalettes
@@ -242,7 +244,7 @@ void reverse() {
} }
if (options.palSpecType == Options::EXPLICIT && palettes != options.palSpec) { if (options.palSpecType == Options::EXPLICIT && palettes != options.palSpec) {
warning("Colors in the palette file do not match those specified with `-c`!"); warnx("Colors in the palette file do not match those specified with `-c`!");
// This spacing aligns "...versus with `-c`" above the column of `-c` palettes // This spacing aligns "...versus with `-c`" above the column of `-c` palettes
fputs("Colors specified in the palette file: ...versus with `-c`:\n", stderr); fputs("Colors specified in the palette file: ...versus with `-c`:\n", stderr);
for (size_t i = 0; i < palettes.size() && i < options.palSpec.size(); ++i) { for (size_t i = 0; i < palettes.size() && i < options.palSpec.size(); ++i) {
@@ -263,7 +265,7 @@ void reverse() {
palettes[0][i] = grayColors[options.dmgValue(i)]; palettes[0][i] = grayColors[options.dmgValue(i)];
} }
} else if (options.palSpecType == Options::EMBEDDED) { } else if (options.palSpecType == Options::EMBEDDED) {
warning("An embedded palette was requested, but no palette file was specified; ignoring " warnx("An embedded palette was requested, but no palette file was specified; ignoring "
"request."); "request.");
} else if (options.palSpecType == Options::EXPLICIT) { } else if (options.palSpecType == Options::EXPLICIT) {
palettes = std::move(options.palSpec); // We won't be using it again. palettes = std::move(options.palSpec); // We won't be using it again.
@@ -303,7 +305,7 @@ void reverse() {
if (!tilemap) { if (!tilemap) {
if (bank) { if (bank) {
warning( warnx(
"Attribute map assigns tile at (%zu, %zu) to bank 1, but no tilemap " "Attribute map assigns tile at (%zu, %zu) to bank 1, but no tilemap "
"specified; " "specified; "
"ignoring the bank bit", "ignoring the bank bit",

52
src/gfx/warning.cpp Normal file
View File

@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
#include "gfx/warning.hpp"
#include <limits>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
static uintmax_t nbErrors;
[[noreturn]]
void giveUp() {
fprintf(stderr, "Conversion aborted after %ju error%s\n", nbErrors, nbErrors == 1 ? "" : "s");
exit(1);
}
void requireZeroErrors() {
if (nbErrors != 0) {
giveUp();
}
}
void error(char const *fmt, ...) {
va_list ap;
fputs("error: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max()) {
nbErrors++;
}
}
[[noreturn]]
void fatal(char const *fmt, ...) {
va_list ap;
fputs("FATAL: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
if (nbErrors != std::numeric_limits<decltype(nbErrors)>::max()) {
nbErrors++;
}
giveUp();
}

View File

@@ -19,6 +19,7 @@
#include "link/output.hpp" #include "link/output.hpp"
#include "link/section.hpp" #include "link/section.hpp"
#include "link/symbol.hpp" #include "link/symbol.hpp"
#include "link/warning.hpp"
struct MemoryLocation { struct MemoryLocation {
uint16_t address; uint16_t address;

View File

@@ -23,6 +23,7 @@
#include "link/patch.hpp" #include "link/patch.hpp"
#include "link/section.hpp" #include "link/section.hpp"
#include "link/symbol.hpp" #include "link/symbol.hpp"
#include "link/warning.hpp"
bool isDmgMode; // -d bool isDmgMode; // -d
char const *linkerScriptName; // -l char const *linkerScriptName; // -l
@@ -44,8 +45,6 @@ bool disablePadding; // -x
FILE *linkerScript; FILE *linkerScript;
static uint32_t nbErrors = 0;
std::string const &FileStackNode::dump(uint32_t curLineNo) const { std::string const &FileStackNode::dump(uint32_t curLineNo) const {
if (data.holds<std::vector<uint32_t>>()) { if (data.holds<std::vector<uint32_t>>()) {
assume(parent); // REPT nodes use their parent's name assume(parent); // REPT nodes use their parent's name
@@ -69,72 +68,6 @@ std::string const &FileStackNode::dump(uint32_t curLineNo) const {
} }
} }
void printDiag(
char const *fmt, va_list args, char const *type, FileStackNode const *where, uint32_t lineNo
) {
fputs(type, stderr);
fputs(": ", stderr);
if (where) {
where->dump(lineNo);
fputs(": ", stderr);
}
vfprintf(stderr, fmt, args);
putc('\n', stderr);
}
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "warning", where, lineNo);
va_end(args);
}
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "error", where, lineNo);
va_end(args);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
[[gnu::format(printf, 2, 3)]]
void argErr(char flag, char const *fmt, ...) {
va_list args;
fprintf(stderr, "error: Invalid argument for option '%c': ", flag);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
putc('\n', stderr);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
[[noreturn]]
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "FATAL", where, lineNo);
va_end(args);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
fprintf(
stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
// Short options // Short options
static char const *optstring = "dhl:m:Mn:O:o:p:S:tVvwx"; static char const *optstring = "dhl:m:Mn:O:o:p:S:tVvwx";
@@ -185,6 +118,19 @@ static void printUsage() {
} }
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
[[gnu::format(printf, 1, 2), noreturn]]
static void fatalWithUsage(char const *fmt, ...) {
va_list ap;
fputs("FATAL: ", stderr);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
printUsage();
exit(1);
}
enum ScrambledRegion { enum ScrambledRegion {
SCRAMBLE_ROMX, SCRAMBLE_ROMX,
SCRAMBLE_SRAM, SCRAMBLE_SRAM,
@@ -327,14 +273,6 @@ next: // Can't `continue` a `for` loop with this nontrivial iteration logic
} }
} }
[[noreturn]]
void reportErrors() {
fprintf(
stderr, "Linking failed with %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
// Parse options // Parse options
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) { for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
@@ -429,11 +367,7 @@ int main(int argc, char *argv[]) {
// If no input files were specified, the user must have screwed up // If no input files were specified, the user must have screwed up
if (curArgIndex == argc) { if (curArgIndex == argc) {
fputs( fatalWithUsage("Please specify an input file (pass `-` to read from standard input)");
"FATAL: Please specify an input file (pass `-` to read from standard input)\n", stderr
);
printUsage();
exit(1);
} }
// Patch the size array depending on command-line options // Patch the size array depending on command-line options
@@ -461,23 +395,17 @@ int main(int argc, char *argv[]) {
script_ProcessScript(linkerScriptName); script_ProcessScript(linkerScriptName);
// If the linker script produced any errors, some sections may be in an invalid state // If the linker script produced any errors, some sections may be in an invalid state
if (nbErrors != 0) { requireZeroErrors();
reportErrors();
}
} }
// then process them, // then process them,
sect_DoSanityChecks(); sect_DoSanityChecks();
if (nbErrors != 0) { requireZeroErrors();
reportErrors();
}
assign_AssignSections(); assign_AssignSections();
patch_CheckAssertions(); patch_CheckAssertions();
// and finally output the result. // and finally output the result.
patch_ApplyPatches(); patch_ApplyPatches();
if (nbErrors != 0) { requireZeroErrors();
reportErrors();
}
out_WriteFiles(); out_WriteFiles();
} }

View File

@@ -24,6 +24,7 @@
#include "link/sdas_obj.hpp" #include "link/sdas_obj.hpp"
#include "link/section.hpp" #include "link/section.hpp"
#include "link/symbol.hpp" #include "link/symbol.hpp"
#include "link/warning.hpp"
static std::deque<std::vector<Symbol>> symbolLists; static std::deque<std::vector<Symbol>> symbolLists;
static std::vector<std::vector<FileStackNode>> nodes; static std::vector<std::vector<FileStackNode>> nodes;
@@ -445,7 +446,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
file = stdin; file = stdin;
} }
if (!file) { if (!file) {
err("Failed to open file \"%s\"", fileName); errx("Failed to open file \"%s\": %s", fileName, strerror(errno));
} }
Defer closeFile{[&] { fclose(file); }}; Defer closeFile{[&] { fclose(file); }};

View File

@@ -4,6 +4,7 @@
#include <algorithm> #include <algorithm>
#include <deque> #include <deque>
#include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -20,6 +21,7 @@
#include "link/main.hpp" #include "link/main.hpp"
#include "link/section.hpp" #include "link/section.hpp"
#include "link/symbol.hpp" #include "link/symbol.hpp"
#include "link/warning.hpp"
static constexpr size_t BANK_SIZE = 0x4000; static constexpr size_t BANK_SIZE = 0x4000;
@@ -211,7 +213,7 @@ static void writeROM() {
outputFile = stdout; outputFile = stdout;
} }
if (!outputFile) { if (!outputFile) {
err("Failed to open output file \"%s\"", outputFileName); errx("Failed to open output file \"%s\": %s", outputFileName, strerror(errno));
} }
} }
Defer closeOutputFile{[&] { Defer closeOutputFile{[&] {
@@ -229,7 +231,7 @@ static void writeROM() {
overlayFile = stdin; overlayFile = stdin;
} }
if (!overlayFile) { if (!overlayFile) {
err("Failed to open overlay file \"%s\"", overlayFileName); errx("Failed to open overlay file \"%s\": %s", overlayFileName, strerror(errno));
} }
} }
Defer closeOverlayFile{[&] { Defer closeOverlayFile{[&] {
@@ -572,7 +574,7 @@ static void writeSym() {
symFile = stdout; symFile = stdout;
} }
if (!symFile) { if (!symFile) {
err("Failed to open sym file \"%s\"", symFileName); errx("Failed to open sym file \"%s\": %s", symFileName, strerror(errno));
} }
Defer closeSymFile{[&] { fclose(symFile); }}; Defer closeSymFile{[&] { fclose(symFile); }};
@@ -623,7 +625,7 @@ static void writeMap() {
mapFile = stdout; mapFile = stdout;
} }
if (!mapFile) { if (!mapFile) {
err("Failed to open map file \"%s\"", mapFileName); errx("Failed to open map file \"%s\": %s", mapFileName, strerror(errno));
} }
Defer closeMapFile{[&] { fclose(mapFile); }}; Defer closeMapFile{[&] { fclose(mapFile); }};

View File

@@ -14,6 +14,7 @@
#include "link/main.hpp" #include "link/main.hpp"
#include "link/section.hpp" #include "link/section.hpp"
#include "link/symbol.hpp" #include "link/symbol.hpp"
#include "link/warning.hpp"
std::deque<Assertion> assertions; std::deque<Assertion> assertions;

View File

@@ -30,6 +30,7 @@
#include "link/main.hpp" #include "link/main.hpp"
#include "link/section.hpp" #include "link/section.hpp"
#include "link/warning.hpp"
using namespace std::literals; using namespace std::literals;

View File

@@ -17,6 +17,7 @@
#include "link/main.hpp" #include "link/main.hpp"
#include "link/section.hpp" #include "link/section.hpp"
#include "link/symbol.hpp" #include "link/symbol.hpp"
#include "link/warning.hpp"
enum NumberType { enum NumberType {
HEX = 16, // X HEX = 16, // X
@@ -307,7 +308,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
getToken(nullptr, "'A' line is too short"); getToken(nullptr, "'A' line is too short");
tmp = parseNumber(where, lineNo, token, numberType); tmp = parseNumber(where, lineNo, token, numberType);
if (tmp & (1 << AREA_PAGING)) { if (tmp & (1 << AREA_PAGING)) {
fatal(&where, lineNo, "Internal error: paging is not supported"); fatal(&where, lineNo, "Paging is not supported");
} }
curSection->isAddressFixed = tmp & (1 << AREA_ISABS); curSection->isAddressFixed = tmp & (1 << AREA_ISABS);
curSection->isBankFixed = curSection->isAddressFixed; curSection->isBankFixed = curSection->isAddressFixed;
@@ -430,11 +431,8 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|| (symbolSection && !symbolSection->isAddressFixed)) { || (symbolSection && !symbolSection->isAddressFixed)) {
sym_AddSymbol(symbol); // This will error out sym_AddSymbol(symbol); // This will error out
} else if (otherValue != symbolValue) { } else if (otherValue != symbolValue) {
fprintf( errorNoDump(
stderr, "\"%s\" is defined as %" PRId32 " at ", symbol.name.c_str(), symbolValue
"error: \"%s\" is defined as %" PRId32 " at ",
symbol.name.c_str(),
symbolValue
); );
symbol.src->dump(symbol.lineNo); symbol.src->dump(symbol.lineNo);
fprintf(stderr, ", but as %" PRId32 " at ", otherValue); fprintf(stderr, ", but as %" PRId32 " at ", otherValue);

View File

@@ -10,6 +10,8 @@
#include "error.hpp" #include "error.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "link/warning.hpp"
std::vector<std::unique_ptr<Section>> sectionList; std::vector<std::unique_ptr<Section>> sectionList;
std::unordered_map<std::string, size_t> sectionMap; // Indexes into `sectionList` std::unordered_map<std::string, size_t> sectionMap; // Indexes into `sectionList`
@@ -22,9 +24,8 @@ void sect_ForEach(void (*callback)(Section &)) {
static void checkAgainstFixedAddress(Section const &target, Section const &other, uint16_t org) { static void checkAgainstFixedAddress(Section const &target, Section const &other, uint16_t org) {
if (target.isAddressFixed) { if (target.isAddressFixed) {
if (target.org != org) { if (target.org != org) {
fprintf( errorNoDump(
stderr, "Section \"%s\" is defined with address $%04" PRIx16 " at ",
"error: Section \"%s\" is defined with address $%04" PRIx16 " at ",
target.name.c_str(), target.name.c_str(),
target.org target.org
); );
@@ -36,9 +37,8 @@ static void checkAgainstFixedAddress(Section const &target, Section const &other
} }
} else if (target.isAlignFixed) { } else if (target.isAlignFixed) {
if ((org - target.alignOfs) & target.alignMask) { if ((org - target.alignOfs) & target.alignMask) {
fprintf( errorNoDump(
stderr, "Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
"error: Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
target.name.c_str(), target.name.c_str(),
target.alignMask + 1, target.alignMask + 1,
target.alignOfs target.alignOfs
@@ -55,9 +55,8 @@ static void checkAgainstFixedAddress(Section const &target, Section const &other
static bool checkAgainstFixedAlign(Section const &target, Section const &other, int32_t ofs) { static bool checkAgainstFixedAlign(Section const &target, Section const &other, int32_t ofs) {
if (target.isAddressFixed) { if (target.isAddressFixed) {
if ((target.org - ofs) & other.alignMask) { if ((target.org - ofs) & other.alignMask) {
fprintf( errorNoDump(
stderr, "Section \"%s\" is defined with address $%04" PRIx16 " at ",
"error: Section \"%s\" is defined with address $%04" PRIx16 " at ",
target.name.c_str(), target.name.c_str(),
target.org target.org
); );
@@ -75,9 +74,8 @@ static bool checkAgainstFixedAlign(Section const &target, Section const &other,
return false; return false;
} else if (target.isAlignFixed } else if (target.isAlignFixed
&& (other.alignMask & target.alignOfs) != (target.alignMask & ofs)) { && (other.alignMask & target.alignOfs) != (target.alignMask & ofs)) {
fprintf( errorNoDump(
stderr, "Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
"error: Section \"%s\" is defined with %d-byte alignment (offset %" PRIu16 ") at ",
target.name.c_str(), target.name.c_str(),
target.alignMask + 1, target.alignMask + 1,
target.alignOfs target.alignOfs
@@ -131,9 +129,8 @@ static void checkFragmentCompat(Section &target, Section &other) {
static void mergeSections(Section &target, std::unique_ptr<Section> &&other) { static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
if (target.modifier != other->modifier) { if (target.modifier != other->modifier) {
fprintf( errorNoDump(
stderr, "Section \"%s\" is defined as SECTION %s at ",
"error: Section \"%s\" is defined as SECTION %s at ",
target.name.c_str(), target.name.c_str(),
sectionModNames[target.modifier] sectionModNames[target.modifier]
); );
@@ -143,16 +140,15 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
putc('\n', stderr); putc('\n', stderr);
exit(1); exit(1);
} else if (other->modifier == SECTION_NORMAL) { } else if (other->modifier == SECTION_NORMAL) {
fprintf(stderr, "error: Section \"%s\" is defined at ", target.name.c_str()); errorNoDump("Section \"%s\" is defined at ", target.name.c_str());
target.src->dump(target.lineNo); target.src->dump(target.lineNo);
fputs(", but also at ", stderr); fputs(", but also at ", stderr);
other->src->dump(other->lineNo); other->src->dump(other->lineNo);
putc('\n', stderr); putc('\n', stderr);
exit(1); exit(1);
} else if (target.type != other->type) { } else if (target.type != other->type) {
fprintf( errorNoDump(
stderr, "Section \"%s\" is defined with type %s at ",
"error: Section \"%s\" is defined with type %s at ",
target.name.c_str(), target.name.c_str(),
sectionTypeInfo[target.type].name.c_str() sectionTypeInfo[target.type].name.c_str()
); );
@@ -168,9 +164,8 @@ static void mergeSections(Section &target, std::unique_ptr<Section> &&other) {
target.isBankFixed = true; target.isBankFixed = true;
target.bank = other->bank; target.bank = other->bank;
} else if (target.bank != other->bank) { } else if (target.bank != other->bank) {
fprintf( errorNoDump(
stderr, "Section \"%s\" is defined with bank %" PRIu32 " at ",
"error: Section \"%s\" is defined with bank %" PRIu32 " at ",
target.name.c_str(), target.name.c_str(),
target.bank target.bank
); );

View File

@@ -11,6 +11,7 @@
#include "link/main.hpp" #include "link/main.hpp"
#include "link/section.hpp" #include "link/section.hpp"
#include "link/warning.hpp"
std::unordered_map<std::string, Symbol *> symbols; std::unordered_map<std::string, Symbol *> symbols;
std::unordered_map<std::string, std::vector<Symbol *>> localSymbols; std::unordered_map<std::string, std::vector<Symbol *>> localSymbols;
@@ -36,7 +37,7 @@ void sym_AddSymbol(Symbol &symbol) {
// Check if the symbol already exists with a different value // Check if the symbol already exists with a different value
if (other && !(symValue && otherValue && *symValue == *otherValue)) { if (other && !(symValue && otherValue && *symValue == *otherValue)) {
fprintf(stderr, "error: \"%s\" is defined as ", symbol.name.c_str()); errorNoDump("\"%s\" is defined as ", symbol.name.c_str());
if (symValue) { if (symValue) {
fprintf(stderr, "%" PRId32, *symValue); fprintf(stderr, "%" PRId32, *symValue);
} else { } else {

92
src/link/warning.cpp Normal file
View File

@@ -0,0 +1,92 @@
// SPDX-License-Identifier: MIT
#include "link/warning.hpp"
#include <inttypes.h>
#include <stdarg.h>
#include "link/main.hpp"
static uint32_t nbErrors = 0;
static void printDiag(
char const *fmt, va_list args, char const *type, FileStackNode const *where, uint32_t lineNo
) {
fputs(type, stderr);
fputs(": ", stderr);
if (where) {
where->dump(lineNo);
fputs(": ", stderr);
}
vfprintf(stderr, fmt, args);
putc('\n', stderr);
}
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "warning", where, lineNo);
va_end(args);
}
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "error", where, lineNo);
va_end(args);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
void errorNoDump(char const *fmt, ...) {
va_list args;
fputs("error: ", stderr);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
void argErr(char flag, char const *fmt, ...) {
va_list args;
fprintf(stderr, "error: Invalid argument for option '%c': ", flag);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
putc('\n', stderr);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
}
[[noreturn]]
void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "FATAL", where, lineNo);
va_end(args);
if (nbErrors != UINT32_MAX) {
nbErrors++;
}
fprintf(
stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
void requireZeroErrors() {
if (nbErrors != 0) {
fprintf(
stderr, "Linking failed with %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
}

View File

@@ -1,5 +1,5 @@
error: Features incompatible with MBC ("mbc1+multirumble") error: Features incompatible with MBC ("mbc1+multirumble")
Accepted combinations: Accepted MBC names:
ROM ($00) [aka ROM_ONLY] ROM ($00) [aka ROM_ONLY]
MBC1 ($01), MBC1+RAM ($02), MBC1+RAM+BATTERY ($03) MBC1 ($01), MBC1+RAM ($02), MBC1+RAM+BATTERY ($03)
MBC2 ($05), MBC2+BATTERY ($06) MBC2 ($05), MBC2+BATTERY ($06)