Ensure CRLF line endings are preserved when necessary

Some test cases need CRLF line endings checked out even on Unix.
Also some source files had inadvertently contained CR bytes.
This commit is contained in:
Rangi
2026-05-25 21:06:42 -04:00
parent 55db252a8f
commit 996469ee28
10 changed files with 328 additions and 313 deletions
+21 -21
View File
@@ -1,21 +1,21 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
#ifndef RGBDS_CLI_HPP #ifndef RGBDS_CLI_HPP
#define RGBDS_CLI_HPP #define RGBDS_CLI_HPP
#include <stdarg.h> #include <stdarg.h>
#include <string> #include <string>
#include "extern/getopt.hpp" // option #include "extern/getopt.hpp" // option
#include "usage.hpp" #include "usage.hpp"
void cli_ParseArgs( void cli_ParseArgs(
int argc, int argc,
char *argv[], char *argv[],
char const *shortOpts, char const *shortOpts,
option const *longOpts, option const *longOpts,
void (*parseArg)(int, char *), void (*parseArg)(int, char *),
Usage usage Usage usage
); );
#endif // RGBDS_CLI_HPP #endif // RGBDS_CLI_HPP
+23 -23
View File
@@ -1,23 +1,23 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_FLIP_HPP #ifndef RGBDS_GFX_FLIP_HPP
#define RGBDS_GFX_FLIP_HPP #define RGBDS_GFX_FLIP_HPP
#include <array> #include <array>
#include <stdint.h> #include <stdint.h>
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up // Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
static std::array<uint16_t, 256> flipTable = ([]() constexpr { static std::array<uint16_t, 256> flipTable = ([]() constexpr {
std::array<uint16_t, 256> table{}; std::array<uint16_t, 256> table{};
for (uint16_t i = 0; i < table.size(); ++i) { for (uint16_t i = 0; i < table.size(); ++i) {
// To flip all the bits, we'll flip both nibbles, then each nibble half, etc. // To flip all the bits, we'll flip both nibbles, then each nibble half, etc.
uint16_t byte = i; uint16_t byte = i;
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4; byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2; byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2;
byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1; byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1;
table[i] = byte; table[i] = byte;
} }
return table; return table;
})(); })();
#endif // RGBDS_GFX_FLIP_HPP #endif // RGBDS_GFX_FLIP_HPP
+27 -27
View File
@@ -1,27 +1,27 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_PALETTE_HPP #ifndef RGBDS_GFX_PALETTE_HPP
#define RGBDS_GFX_PALETTE_HPP #define RGBDS_GFX_PALETTE_HPP
#include <array> #include <array>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
struct Palette { struct Palette {
// An array of 4 GBC-native (RGB555) colors // An array of 4 GBC-native (RGB555) colors
std::array<uint16_t, 4> colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; std::array<uint16_t, 4> colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
void addColor(uint16_t color); void addColor(uint16_t color);
uint8_t indexOf(uint16_t color) const; uint8_t indexOf(uint16_t color) const;
uint16_t &operator[](size_t index) { return colors[index]; } uint16_t &operator[](size_t index) { return colors[index]; }
uint16_t const &operator[](size_t index) const { return colors[index]; } uint16_t const &operator[](size_t index) const { return colors[index]; }
decltype(colors)::iterator begin(); decltype(colors)::iterator begin();
decltype(colors)::iterator end(); decltype(colors)::iterator end();
decltype(colors)::const_iterator begin() const; decltype(colors)::const_iterator begin() const;
decltype(colors)::const_iterator end() const; decltype(colors)::const_iterator end() const;
uint8_t size() const; uint8_t size() const;
}; };
#endif // RGBDS_GFX_PALETTE_HPP #endif // RGBDS_GFX_PALETTE_HPP
+159 -159
View File
@@ -1,159 +1,159 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
#include "cli.hpp" #include "cli.hpp"
#include <errno.h> #include <errno.h>
#include <fstream> #include <fstream>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include "extern/getopt.hpp" #include "extern/getopt.hpp"
#include "style.hpp" #include "style.hpp"
#include "usage.hpp" #include "usage.hpp"
#include "util.hpp" // isBlankSpace #include "util.hpp" // isBlankSpace
using namespace std::literals; using namespace std::literals;
// Turn an at-file's contents into an argv that `getopt` can handle, appending them to `argPool`. // Turn an at-file's contents into an argv that `getopt` can handle, appending them to `argPool`.
static std::vector<size_t> static std::vector<size_t>
readAtFile(std::string const &path, std::vector<char> &argPool, Usage usage) { readAtFile(std::string const &path, std::vector<char> &argPool, Usage usage) {
std::vector<size_t> argvOfs; std::vector<size_t> argvOfs;
std::filebuf file; std::filebuf file;
if (!file.open(path, std::ios_base::in)) { if (!file.open(path, std::ios_base::in)) {
int errnum = errno; int errnum = errno;
style_Set(stderr, STYLE_RED, true); style_Set(stderr, STYLE_RED, true);
fputs("FATAL: ", stderr); fputs("FATAL: ", stderr);
style_Reset(stderr); style_Reset(stderr);
fprintf(stderr, "Failed to open at-file \"%s\": %s\n", path.c_str(), strerror(errnum)); fprintf(stderr, "Failed to open at-file \"%s\": %s\n", path.c_str(), strerror(errnum));
usage.printAndExit(1); usage.printAndExit(1);
} }
for (;;) { for (;;) {
int c = file.sbumpc(); int c = file.sbumpc();
// First, discard any leading blank space // First, discard any leading blank space
while (isBlankSpace(c)) { while (isBlankSpace(c)) {
c = file.sbumpc(); c = file.sbumpc();
} }
// If it's a comment, discard everything until EOL // If it's a comment, discard everything until EOL
if (c == '#') { if (c == '#') {
c = file.sbumpc(); c = file.sbumpc();
while (c != EOF && !isNewline(c)) { while (c != EOF && !isNewline(c)) {
c = file.sbumpc(); c = file.sbumpc();
} }
} }
if (c == EOF) { if (c == EOF) {
return argvOfs; return argvOfs;
} else if (isNewline(c)) { } else if (isNewline(c)) {
continue; // Start processing the next line continue; // Start processing the next line
} }
// Alright, now we can parse the line // Alright, now we can parse the line
do { do {
argvOfs.push_back(argPool.size()); argvOfs.push_back(argPool.size());
// Read one argument (until the next whitespace char). // Read one argument (until the next whitespace char).
// We know there is one because we already have its first character in `c`. // We know there is one because we already have its first character in `c`.
for (; c != EOF && !isWhitespace(c); c = file.sbumpc()) { for (; c != EOF && !isWhitespace(c); c = file.sbumpc()) {
argPool.push_back(c); argPool.push_back(c);
} }
argPool.push_back('\0'); argPool.push_back('\0');
// Discard blank space until the next argument (candidate) // Discard blank space until the next argument (candidate)
while (isBlankSpace(c)) { while (isBlankSpace(c)) {
c = file.sbumpc(); c = file.sbumpc();
} }
} while (c != EOF && !isNewline(c)); // End if we reached EOL } while (c != EOF && !isNewline(c)); // End if we reached EOL
} }
} }
void cli_ParseArgs( void cli_ParseArgs(
int argc, int argc,
char *argv[], char *argv[],
char const *shortOpts, char const *shortOpts,
option const *longOpts, option const *longOpts,
void (*parseArg)(int, char *), void (*parseArg)(int, char *),
Usage usage Usage usage
) { ) {
struct AtFileStackEntry { struct AtFileStackEntry {
int parentInd; // Saved offset into parent argv int parentInd; // Saved offset into parent argv
std::vector<char *> argv; // This context's arg pointer vec std::vector<char *> argv; // This context's arg pointer vec
AtFileStackEntry(int parentInd_, std::vector<char *> argv_) AtFileStackEntry(int parentInd_, std::vector<char *> argv_)
: parentInd(parentInd_), argv(argv_) {} : parentInd(parentInd_), argv(argv_) {}
}; };
std::vector<AtFileStackEntry> atFileStack; std::vector<AtFileStackEntry> atFileStack;
int curArgc = argc; int curArgc = argc;
char **curArgv = argv; char **curArgv = argv;
std::string optString = "-"s + shortOpts; // Request position arguments with a leading '-' std::string optString = "-"s + shortOpts; // Request position arguments with a leading '-'
std::vector<std::vector<char>> argPools; std::vector<std::vector<char>> argPools;
for (;;) { for (;;) {
char *atFileName = nullptr; char *atFileName = nullptr;
for (int ch; for (int ch;
(ch = musl_getopt_long_only(curArgc, curArgv, optString.c_str(), longOpts)) != -1;) { (ch = musl_getopt_long_only(curArgc, curArgv, optString.c_str(), longOpts)) != -1;) {
if (ch == 1 && musl_optarg[0] == '@') { if (ch == 1 && musl_optarg[0] == '@') {
atFileName = &musl_optarg[1]; atFileName = &musl_optarg[1];
break; break;
} else { } else {
parseArg(ch, musl_optarg); parseArg(ch, musl_optarg);
} }
} }
if (atFileName) { if (atFileName) {
// We need to allocate a new arg pool for each at-file, so as not to invalidate pointers // We need to allocate a new arg pool for each at-file, so as not to invalidate pointers
// previous at-files may have generated to their own arg pools. // previous at-files may have generated to their own arg pools.
// But for the same reason, the arg pool must also outlive the at-file's stack entry! // But for the same reason, the arg pool must also outlive the at-file's stack entry!
std::vector<char> &argPool = argPools.emplace_back(); std::vector<char> &argPool = argPools.emplace_back();
// Copy `argv[0]` for error reporting, and because option parsing skips it // Copy `argv[0]` for error reporting, and because option parsing skips it
AtFileStackEntry &stackEntry = AtFileStackEntry &stackEntry =
atFileStack.emplace_back(musl_optind, std::vector{atFileName}); atFileStack.emplace_back(musl_optind, std::vector{atFileName});
// It would be nice to compute the char pointers on the fly, but reallocs don't allow // It would be nice to compute the char pointers on the fly, but reallocs don't allow
// that; so we must compute the offsets after the pool is fixed // that; so we must compute the offsets after the pool is fixed
std::vector<size_t> offsets = readAtFile(&musl_optarg[1], argPool, usage); std::vector<size_t> offsets = readAtFile(&musl_optarg[1], argPool, usage);
stackEntry.argv.reserve(offsets.size() + 2); // Avoid a bunch of reallocs stackEntry.argv.reserve(offsets.size() + 2); // Avoid a bunch of reallocs
for (size_t ofs : offsets) { for (size_t ofs : offsets) {
stackEntry.argv.push_back(&argPool.data()[ofs]); stackEntry.argv.push_back(&argPool.data()[ofs]);
} }
stackEntry.argv.push_back(nullptr); // Don't forget the arg vector terminator! stackEntry.argv.push_back(nullptr); // Don't forget the arg vector terminator!
curArgc = stackEntry.argv.size() - 1; curArgc = stackEntry.argv.size() - 1;
curArgv = stackEntry.argv.data(); curArgv = stackEntry.argv.data();
musl_optind = 1; // Don't use 0 because we're not scanning a different argv per se musl_optind = 1; // Don't use 0 because we're not scanning a different argv per se
} else { } else {
if (musl_optind != curArgc) { if (musl_optind != curArgc) {
// This happens if `--` is passed, process the remaining arg(s) as positional // This happens if `--` is passed, process the remaining arg(s) as positional
assume(musl_optind < curArgc); assume(musl_optind < curArgc);
for (int i = musl_optind; i < curArgc; ++i) { for (int i = musl_optind; i < curArgc; ++i) {
parseArg(1, argv[i]); // Positional argument parseArg(1, argv[i]); // Positional argument
} }
} }
// Pop off the top stack entry, or end parsing if none // Pop off the top stack entry, or end parsing if none
if (atFileStack.empty()) { if (atFileStack.empty()) {
break; break;
} }
// OK to restore `optind` directly, because `optpos` must be 0 right now. // OK to restore `optind` directly, because `optpos` must be 0 right now.
// (Providing 0 would be a "proper" reset, but we want to resume parsing) // (Providing 0 would be a "proper" reset, but we want to resume parsing)
musl_optind = atFileStack.back().parentInd; musl_optind = atFileStack.back().parentInd;
atFileStack.pop_back(); atFileStack.pop_back();
if (atFileStack.empty()) { if (atFileStack.empty()) {
curArgc = argc; curArgc = argc;
curArgv = argv; curArgv = argv;
} else { } else {
std::vector<char *> &vec = atFileStack.back().argv; std::vector<char *> &vec = atFileStack.back().argv;
curArgc = vec.size(); curArgc = vec.size();
curArgv = vec.data(); curArgv = vec.data();
} }
} }
} }
} }
+55 -55
View File
@@ -1,55 +1,55 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
#include "gfx/palette.hpp" #include "gfx/palette.hpp"
#include <algorithm> #include <algorithm>
#include <stdint.h> #include <stdint.h>
#include "helpers.hpp" #include "helpers.hpp"
#include "gfx/main.hpp" #include "gfx/main.hpp"
#include "gfx/rgba.hpp" #include "gfx/rgba.hpp"
void Palette::addColor(uint16_t color) { void Palette::addColor(uint16_t color) {
for (size_t i = 0; true; ++i) { for (size_t i = 0; true; ++i) {
assume(i < colors.size()); // The packing should guarantee this assume(i < colors.size()); // The packing should guarantee this
if (colors[i] == color) { // The color is already present if (colors[i] == color) { // The color is already present
break; break;
} else if (colors[i] == UINT16_MAX) { // Empty slot } else if (colors[i] == UINT16_MAX) { // Empty slot
colors[i] = color; colors[i] = color;
break; break;
} }
} }
} }
// Returns the ID of the color in the palette, or `size()` if the color is not in // Returns the ID of the color in the palette, or `size()` if the color is not in
uint8_t Palette::indexOf(uint16_t color) const { uint8_t Palette::indexOf(uint16_t color) const {
return color == Rgba::transparent return color == Rgba::transparent
? 0 ? 0
: std::find(begin(), colors.end(), color) - begin() + options.hasTransparentPixels; : std::find(begin(), colors.end(), color) - begin() + options.hasTransparentPixels;
} }
auto Palette::begin() -> decltype(colors)::iterator { auto Palette::begin() -> decltype(colors)::iterator {
// Skip the first slot if reserved for transparency // Skip the first slot if reserved for transparency
return colors.begin() + options.hasTransparentPixels; return colors.begin() + options.hasTransparentPixels;
} }
auto Palette::end() -> decltype(colors)::iterator { auto Palette::end() -> decltype(colors)::iterator {
// Return an iterator pointing past the last non-empty element. // Return an iterator pointing past the last non-empty element.
// Since the palette may contain gaps, we must scan from the end. // Since the palette may contain gaps, we must scan from the end.
return std::find_if(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base(); return std::find_if(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base();
} }
auto Palette::begin() const -> decltype(colors)::const_iterator { auto Palette::begin() const -> decltype(colors)::const_iterator {
// Same as the non-const begin(). // Same as the non-const begin().
return colors.begin() + options.hasTransparentPixels; return colors.begin() + options.hasTransparentPixels;
} }
auto Palette::end() const -> decltype(colors)::const_iterator { auto Palette::end() const -> decltype(colors)::const_iterator {
// Same as the non-const end(). // Same as the non-const end().
return std::find_if(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base(); return std::find_if(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base();
} }
uint8_t Palette::size() const { uint8_t Palette::size() const {
return end() - colors.begin(); return end() - colors.begin();
} }
+4
View File
@@ -0,0 +1,4 @@
# Some files test CRLF line endings
character-escapes.out -text diff
crlf.asm text eol=crlf
string-literal-macro-arg.out -text diff
+11 -2
View File
@@ -8,6 +8,15 @@ assert !strcmp("{s}", "Hello, world!")
* block comment * block comment
*/ */
DEF t EQUS """Hello, REPT 2
REDEF t EQUS """Hello,
world!""" world!"""
assert !strcmp("{t}", "Hello,\nworld!") assert !strcmp("{t}", "Hello,\nworld!")
ENDR
MACRO m
assert "\1" === "Hello, world!"
ENDM
m Hello\, \
world!
+24 -24
View File
@@ -1,24 +1,24 @@
MACRO compare MACRO compare
print "\3: " print "\3: "
if _NARG == 4 if _NARG == 4
def v1 = \3(\4q\1, \1) def v1 = \3(\4q\1, \1)
def v2 = \3(\4q\2, \2) def v2 = \3(\4q\2, \2)
elif _NARG == 5 elif _NARG == 5
def v1 = \3(\4q\1, \5q\1, \1) def v1 = \3(\4q\1, \5q\1, \1)
def v2 = \3(\4q\2, \5q\2, \2) def v2 = \3(\4q\2, \5q\2, \2)
endc endc
println "{.4q\1f:v1} == {.4q\2f:v2}" println "{.4q\1f:v1} == {.4q\2f:v2}"
ENDM ENDM
compare 8, 16, mul, 6.0, 7.0 compare 8, 16, mul, 6.0, 7.0
compare 12, 24, div, 115.625, 9.25 compare 12, 24, div, 115.625, 9.25
compare 7, 14, pow, 3.5, 5.5 compare 7, 14, pow, 3.5, 5.5
compare 4, 8, sin, 0.25 compare 4, 8, sin, 0.25
compare 5, 9, cos, 0.75 compare 5, 9, cos, 0.75
compare 6, 10, asin, 1.0 compare 6, 10, asin, 1.0
compare 7, 11, acos, 0.0 compare 7, 11, acos, 0.0
compare 3, 6, round, 1.75 compare 3, 6, round, 1.75
compare 10, 20, ceil, 123.4 compare 10, 20, ceil, 123.4
compare 13, 17, floor, 567.8 compare 13, 17, floor, 567.8
+2 -2
View File
@@ -1,2 +1,2 @@
SECTION UNION "wat", ROM0 SECTION UNION "wat", ROM0
db 42 db 42
+2
View File
@@ -0,0 +1,2 @@
# Some files test CRLF line endings
linkerscript-escapes-test.link -text diff