Tue, 25 Aug 2026 18:35:18 +0200
increase type safety + add backwards compatibility including C99
| Makefile | file | annotate | diff | comparison | revisions | |
| configure | file | annotate | diff | comparison | revisions | |
| make/cc.mk | file | annotate | diff | comparison | revisions | |
| make/project.xml | file | annotate | diff | comparison | revisions | |
| src/chess/king.c | file | annotate | diff | comparison | revisions | |
| src/chess/pawn.c | file | annotate | diff | comparison | revisions | |
| src/chess/pgn.c | file | annotate | diff | comparison | revisions | |
| src/chess/rules.c | file | annotate | diff | comparison | revisions | |
| src/chess/rules.h | file | annotate | diff | comparison | revisions | |
| src/main.c | file | annotate | diff | comparison | revisions | |
| test/Makefile | file | annotate | diff | comparison | revisions | |
| test/test-datatypes.c | file | annotate | diff | comparison | revisions | |
| test/test-real-pgn.c | file | annotate | diff | comparison | revisions |
--- a/Makefile Mon Aug 24 16:13:51 2026 +0200 +++ b/Makefile Tue Aug 25 18:35:18 2026 +0200 @@ -28,7 +28,7 @@ include config.mk -all: app tests +all: app app: $(BUILDDIR) FORCE cd src && $(MAKE) @@ -36,13 +36,13 @@ tests: $(BUILDDIR) $(BUILDDIR)/libchess$(LIB_EXT) FORCE cd test && $(MAKE) -check: all FORCE +check: tests FORCE $(BUILDDIR)/run-tests $(BUILDDIR): $(MKDIR) $(MKDIRFLAGS) $(BUILDDIR) -$(BUILDDIR)/libchess$(LIB_EXT): +$(BUILDDIR)/libchess$(LIB_EXT): FORCE cd src/chess && $(MAKE) clean:
--- a/configure Mon Aug 24 16:13:51 2026 +0200 +++ b/configure Tue Aug 25 18:35:18 2026 +0200 @@ -101,6 +101,7 @@ Optional Features: --enable-asan address sanitizer + --enable-strict-errors treat compiler warnings as errors __EOF__ abort_configure @@ -176,6 +177,8 @@ "--release") BUILD_TYPE="release" ;; "--enable-asan") FEATURE_ASAN=on ;; "--disable-asan") unset FEATURE_ASAN ;; + "--enable-strict-errors") FEATURE_STRICT_ERRORS=on ;; + "--disable-strict-errors") unset FEATURE_STRICT_ERRORS ;; "-"*) echo "unknown option: $ARG"; abort_configure ;; esac done @@ -447,6 +450,19 @@ else : fi +if [ -n "$FEATURE_STRICT_ERRORS" ]; then + if [ -n "$DISABLE_FEATURE_STRICT_ERRORS" ]; then + unset FEATURE_STRICT_ERRORS + fi +fi +if [ -n "$FEATURE_STRICT_ERRORS" ]; then + : + cat >> "$TEMP_DIR/make.mk" << __EOF__ +CFLAGS+=-Werror +__EOF__ +else + : +fi if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then @@ -496,6 +512,12 @@ else echo 'off' fi +printf ' %-16s' 'strict-errors:' +if [ -n "$FEATURE_STRICT_ERRORS" ]; then + echo 'on' +else + echo 'off' +fi echo # generate the config.mk file
--- a/make/cc.mk Mon Aug 24 16:13:51 2026 +0200 +++ b/make/cc.mk Tue Aug 25 18:35:18 2026 +0200 @@ -3,6 +3,6 @@ # CFLAGS = -DEBUG_CFLAGS = -g -Wall -Werror -Wno-pointer-sign +DEBUG_CFLAGS = -g -Wall -Wextra -Wenum-conversion RELEASE_CFLAGS = -O3 -DNDEBUG LDFLAGS =
--- a/make/project.xml Mon Aug 24 16:13:51 2026 +0200 +++ b/make/project.xml Tue Aug 25 18:35:18 2026 +0200 @@ -27,5 +27,9 @@ <desc>address sanitizer</desc> <dependencies>asan</dependencies> </feature> + <feature name="strict-errors"> + <desc>treat compiler warnings as errors</desc> + <make>CFLAGS+=-Werror</make> + </feature> </target> </project>
--- a/src/chess/king.c Mon Aug 24 16:13:51 2026 +0200 +++ b/src/chess/king.c Tue Aug 25 18:35:18 2026 +0200 @@ -66,7 +66,7 @@ bool king_isblocked(const GameState *gamestate, const Move *move) { - uint8_t opponent_color = opponent_color(piece_color(move->piece)); + uint8_t op_color = opponent_color(piece_color(move->piece)); /* being in check does not "block" the king, so don't test it here */ bool blocked = false; @@ -82,7 +82,7 @@ incheck = is_check_position(gamestate); } blocked |= incheck || gamestate->board[move->torow][midfile] || - is_covered(gamestate, move->torow, midfile, opponent_color); + is_covered(gamestate, move->torow, midfile, op_color); } return blocked;
--- a/src/chess/pawn.c Mon Aug 24 16:13:51 2026 +0200 +++ b/src/chess/pawn.c Tue Aug 25 18:35:18 2026 +0200 @@ -80,6 +80,7 @@ size_t pawn_moves(const GameState *gamestate, Color c, Row r, File f, Move *moves) { + (void) gamestate; /* no (theoretical) move depends on the game state */ size_t count = 0; int rowdelta = c == WHITE ? 1 : -1; int promotionrow = c == WHITE ? 7 : 0;
--- a/src/chess/pgn.c Mon Aug 24 16:13:51 2026 +0200 +++ b/src/chess/pgn.c Tue Aug 25 18:35:18 2026 +0200 @@ -231,7 +231,7 @@ int read_pgn(FILE *stream, GameState *gamestate) { fseek(stream, 0, SEEK_END); - long int size = ftell(stream); + size_t size = ftell(stream); fseek(stream, 0, SEEK_SET); char *data = malloc(size + 1); memset(data, 0, size + 1);
--- a/src/chess/rules.c Mon Aug 24 16:13:51 2026 +0200 +++ b/src/chess/rules.c Tue Aug 25 18:35:18 2026 +0200 @@ -1158,6 +1158,11 @@ move->check = move->checkmate = false; switch (determine_check_or_checkmate(gamestate, move)) { case 2: move->checkmate = true; +#if __STDC_VERSION__ > 202310L + [[fallthrough]]; +#elif __GNUC__ + __attribute__((fallthrough)); +#endif case 1: move->check = true; } }
--- a/src/chess/rules.h Mon Aug 24 16:13:51 2026 +0200 +++ b/src/chess/rules.h Tue Aug 25 18:35:18 2026 +0200 @@ -49,11 +49,27 @@ #define INVALID_CHECKMATE 11 #define RULES_VIOLATED 32 +#if __STDC_VERSION__ < 202310L +/* since #warning is also a C23 feature, the only hope is this: */ +#pragma GCC warning "Type safety for enums is only available since C23" +#define enum_uint8_t(name) enum e##name +#define typedef_enum_uint8_t(name) typedef uint8_t name +#else +#define enum_uint8_t(name) enum e##name : uint8_t +#define typedef_enum_uint8_t(name) typedef enum e##name name +#endif + #define ENPASSANT_THREAT 0x40u -#define WHITE 0x10u -#define BLACK 0x20u -#define opponent_color(color) ((color)==WHITE?BLACK:WHITE) +enum_uint8_t(Color) { + WHITE = 0x10u, + BLACK = 0x20u, +}; +typedef_enum_uint8_t(Color); + +static inline Color opponent_color(Color color) { + return color == WHITE ? BLACK : WHITE; +} #define PIECE_MASK 0x0Fu #define COLOR_MASK 0x30u @@ -65,25 +81,36 @@ #define QUEEN 0x05u #define KING 0x06u -#define WPAWN (WHITE|PAWN) -#define WROOK (WHITE|ROOK) -#define WKNIGHT (WHITE|KNIGHT) -#define WBISHOP (WHITE|BISHOP) -#define WQUEEN (WHITE|QUEEN) -#define WKING (WHITE|KING) -#define BPAWN (BLACK|PAWN) -#define BROOK (BLACK|ROOK) -#define BKNIGHT (BLACK|KNIGHT) -#define BBISHOP (BLACK|BISHOP) -#define BQUEEN (BLACK|QUEEN) -#define BKING (BLACK|KING) +enum_uint8_t(Piece) { + WPAWN = WHITE|PAWN, + WROOK = WHITE|ROOK, + WKNIGHT = WHITE|KNIGHT, + WBISHOP = WHITE|BISHOP, + WQUEEN = WHITE|QUEEN, + WKING = WHITE|KING, + BPAWN = BLACK|PAWN, + BROOK = BLACK|ROOK, + BKNIGHT = BLACK|KNIGHT, + BBISHOP = BLACK|BISHOP, + BQUEEN = BLACK|QUEEN, + BKING = BLACK|KING, +}; +typedef_enum_uint8_t(Piece); + +#define POS_UNSPECIFIED 255u +enum_uint8_t(Row) { + RANK_1 = 0, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, + RANK_UNSPECIFIED = POS_UNSPECIFIED +}; +typedef_enum_uint8_t(Row); + +enum_uint8_t(File) { + FILE_A = 0, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, + FILE_UNSPECIFIED = POS_UNSPECIFIED +}; +typedef_enum_uint8_t(File); typedef uint8_t Board[8][8]; -typedef uint8_t BoardIndex; -typedef BoardIndex Row; -typedef BoardIndex File; -typedef uint8_t Piece; -typedef uint8_t Color; struct movetimeval { uint64_t sec; @@ -154,31 +181,26 @@ bool review; } GameState; -#define piece_type(piece) ((piece)&PIECE_MASK) -#define piece_color(piece) ((piece)&COLOR_MASK) -#define mkpiece(type,color) ((type)|(color)) +#define piece_type(piece) ((uint8_t)(piece)&PIECE_MASK) +#define piece_color(piece) ((uint8_t)(piece)&COLOR_MASK) +#define mkpiece(type,color) (Piece)((type)|(color)) -#define POS_UNSPECIFIED UINT8_MAX #define mdst(m) (m)->torow, (m)->tofile #define msrc(m) (m)->fromrow, (m)->fromfile /** Checks if the index is specified and valid. */ -#define isidx(idx) ((BoardIndex)(idx) < 8) +static inline bool isidx(uint8_t idx) {return idx < 8;} /** Checks if the index is unspecified or valid. */ -#define isidxr(idx) ((idx) == POS_UNSPECIFIED || (BoardIndex)(idx) < 8) - -#define isfile(file) (file >= 'a' && file <= 'h') -#define isrow(row) (row >= '1' && row <= '8') +static inline bool isidxr(uint8_t idx) {return idx==POS_UNSPECIFIED || idx<8;} -#define rowidx(row) (row-'1') -#define fileidx(file) (file-'a') +static inline bool isfile(char file) {return file >= 'a' && file <= 'h';} +static inline bool isrow(char row) {return row >= '1' && row <= '8';} -#define rowchr(row) (row+'1') -#define filechr(file) (file+'a') +static inline Row rowidx(char row) {return row-'1';} +static inline File fileidx(char file) {return file-'a';} -/* secure versions - use, if index is not checked with isidx() */ -#define fileidx_s(c) (isfile(c)?fileidx(c):POS_UNSPECIFIED) -#define rowidx_s(c) (isrow(c)?rowidx(c):POS_UNSPECIFIED) +static inline char rowchr(Row row) {return (char)row+'1';} +static inline char filechr(File file) {return (char)file+'a';} static inline void enpassant_threat_add(GameState *gamestate,
--- a/src/main.c Mon Aug 24 16:13:51 2026 +0200 +++ b/src/main.c Tue Aug 25 18:35:18 2026 +0200 @@ -1038,6 +1038,7 @@ static int server_fd = -1; static void interrupt_listen(int sig) { + (void)sig; /* unused */ if (server_fd > -1) { // this interrupts close(server_fd);
--- a/test/Makefile Mon Aug 24 16:13:51 2026 +0200 +++ b/test/Makefile Tue Aug 25 18:35:18 2026 +0200 @@ -29,8 +29,9 @@ include ../config.mk SRC = run-tests.c \ - test-rules-helper.c \ - test-real-pgn.c + test-datatypes.c \ + test-rules-helper.c \ + test-real-pgn.c OBJ = $(SRC:%.c=$(BUILDDIR)/%.o) @@ -47,45 +48,12 @@ @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< +$(BUILDDIR)/test-datatypes.o: test-datatypes.c ../src/chess/rules.h + @echo "Compiling $<" + $(CC) -o $@ $(CFLAGS) -c $< + $(BUILDDIR)/test-real-pgn.o: test-real-pgn.c ucxtest.h ../src/chess/pgn.h \ - ../src/chess/rules.h data/game001.pgn data/game002.pgn data/game003.pgn \ - data/game004.pgn data/game005.pgn data/game006.pgn data/game007.pgn \ - data/game008.pgn data/game009.pgn data/game010.pgn data/game011.pgn \ - data/game012.pgn data/game013.pgn data/game014.pgn data/game015.pgn \ - data/game016.pgn data/game017.pgn data/game018.pgn data/game019.pgn \ - data/game020.pgn data/game021.pgn data/game022.pgn data/game023.pgn \ - data/game024.pgn data/game025.pgn data/game026.pgn data/game027.pgn \ - data/game028.pgn data/game029.pgn data/game030.pgn data/game031.pgn \ - data/game032.pgn data/game033.pgn data/game034.pgn data/game035.pgn \ - data/game036.pgn data/game037.pgn data/game038.pgn data/game039.pgn \ - data/game040.pgn data/game041.pgn data/game042.pgn data/game043.pgn \ - data/game044.pgn data/game045.pgn data/game046.pgn data/game047.pgn \ - data/game048.pgn data/game049.pgn data/game050.pgn data/game051.pgn \ - data/game052.pgn data/game053.pgn data/game054.pgn data/game055.pgn \ - data/game056.pgn data/game057.pgn data/game058.pgn data/game059.pgn \ - data/game060.pgn data/game061.pgn data/game062.pgn data/game063.pgn \ - data/game064.pgn data/game065.pgn data/game066.pgn data/game067.pgn \ - data/game068.pgn data/game069.pgn data/game070.pgn data/game071.pgn \ - data/game072.pgn data/game073.pgn data/game074.pgn data/game075.pgn \ - data/game076.pgn data/game077.pgn data/game078.pgn data/game079.pgn \ - data/game080.pgn data/game081.pgn data/game082.pgn data/game083.pgn \ - data/game084.pgn data/game085.pgn data/game086.pgn data/game087.pgn \ - data/game088.pgn data/game089.pgn data/game090.pgn data/game091.pgn \ - data/game092.pgn data/game093.pgn data/game094.pgn data/game095.pgn \ - data/game096.pgn data/game097.pgn data/game098.pgn data/game099.pgn \ - data/game100.pgn data/game101.pgn data/game102.pgn data/game103.pgn \ - data/game104.pgn data/game105.pgn data/game106.pgn data/game107.pgn \ - data/game108.pgn data/game109.pgn data/game110.pgn data/game111.pgn \ - data/game112.pgn data/game113.pgn data/game114.pgn data/game115.pgn \ - data/game116.pgn data/game117.pgn data/game118.pgn data/game119.pgn \ - data/game120.pgn data/game121.pgn data/game122.pgn data/game123.pgn \ - data/game124.pgn data/game125.pgn data/game126.pgn data/game127.pgn \ - data/game128.pgn data/game129.pgn data/game130.pgn data/game131.pgn \ - data/game132.pgn data/game133.pgn data/game134.pgn data/game135.pgn \ - data/game136.pgn data/game137.pgn data/game138.pgn data/game139.pgn \ - data/game140.pgn data/game141.pgn data/game142.pgn data/game143.pgn \ - data/game144.pgn data/game145.pgn data/game146.pgn data/game147.pgn \ - data/game148.pgn data/game149.pgn data/game150.pgn + ../src/chess/rules.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $<
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test-datatypes.c Tue Aug 25 18:35:18 2026 +0200 @@ -0,0 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2026 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "chess/rules.h" + +#if __STDC_VERSION__ < 201112L +#pragma GCC warning "Need at least C11 to compile test-datatypes.c" +#else +#if __STDC_VERSION__ < 202310L +#include <assert.h> +#endif + +static_assert(sizeof(Color) == 1); +static_assert(sizeof(Row) == 1); +static_assert(sizeof(File) == 1); +static_assert(sizeof(Piece) == 1); + +static_assert(sizeof(Board) == 64); + +#endif /* C11 */
--- a/test/test-real-pgn.c Mon Aug 24 16:13:51 2026 +0200 +++ b/test/test-real-pgn.c Tue Aug 25 18:35:18 2026 +0200 @@ -31,8 +31,13 @@ #include "chess/pgn.h" #if __STDC_VERSION__ < 202310L -#error Need C23 compiler to compile this test. -#endif +/* since #warning is also a C23 feature, the only hope is this: */ +#pragma GCC warning "Need at least a C23 compiler to compile test-real-pgn.c" +CxTestSuite* test_real_pgn_suite(void) { + CxTestSuite* suite = cx_test_suite_new("Skip testing real PGN Files"); + return suite; +} +#else const char pgn_data[150][1500]; @@ -858,3 +863,5 @@ , '\0' }, }; + +#endif /* C23 compiler */