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