Sun, 19 Jul 2026 19:17:04 +0200
fix regression: position reporting about PGN errors
| src/chess/pgn.c | file | annotate | diff | comparison | revisions | |
| src/chess/pgn.h | file | annotate | diff | comparison | revisions | |
| src/main.c | file | annotate | diff | comparison | revisions | |
| test/test-real-pgn.c | file | annotate | diff | comparison | revisions |
--- a/src/chess/pgn.c Sun Jul 19 17:13:16 2026 +0200 +++ b/src/chess/pgn.c Sun Jul 19 19:17:04 2026 +0200 @@ -52,6 +52,12 @@ return pgn_error_strings[code]; } +size_t pgn_error_pos; + +size_t pgn_error_position(void) { + return pgn_error_pos; +} + static const char *pgn_result(GameState *gamestate) { if (gamestate->stalemate || gamestate->remis) { return "1/2-1/2"; @@ -70,7 +76,10 @@ } } +#define return(code) pgn_error_pos = pgndata - pgndata0; return code + int parse_pgn(const char *pgndata, GameState *gamestate) { + const char * const pgndata0 = pgndata; int i; char c; @@ -86,7 +95,7 @@ break; } if (c != '[') { - return pgn_error_missing_bracket; + return(pgn_error_missing_bracket); } while (isspace(c = *(pgndata++))); i = 0; @@ -96,18 +105,18 @@ tagkey[i] = '\0'; while (isspace(c = *(pgndata++))); if (c != '"') { - return pgn_error_missing_quote; + return(pgn_error_missing_quote); } i = 0; while ((c = *(pgndata++)) != '"') { if (c == '\n' || c == 0) { - return pgn_error_missing_quote; + return(pgn_error_missing_quote); } tagvalue[i++] = c; } tagvalue[i] = '\0'; if (*(pgndata++) != ']') { - return pgn_error_missing_bracket; + return(pgn_error_missing_bracket); } // TODO: read clock info @@ -119,7 +128,7 @@ /* read moves */ if (*(pgndata++) != '.') { - return pgn_error_missing_dot; + return(pgn_error_missing_dot); } char movestr[10]; @@ -134,16 +143,16 @@ do { movestr[i++] = c; if (i >= 10) { - return pgn_error_move_syntax; + return(pgn_error_move_syntax); } } while (!isspace(c = *(pgndata++))); movestr[i] = '\0'; if (eval_move(gamestate, movestr, &move, curcol) != VALID_MOVE_SYNTAX) { - return pgn_error_move_syntax; + return(pgn_error_move_syntax); } if (validate_move(gamestate, &move) != VALID_MOVE_SEMANTICS) { - return pgn_error_move_semantics; + return(pgn_error_move_semantics); } apply_move(gamestate, &move); @@ -157,7 +166,7 @@ c = *(pgndata++); } while (c != '}' && c != 0); if (c == 0) { - return pgn_error_missing_brace; + return(pgn_error_missing_brace); } /* skip spaces */ while (isspace(c = *(pgndata++))); @@ -180,7 +189,7 @@ } else if (c == '0') { gamestate->bresign = true; } else { - return pgn_error_result_syntax; + return(pgn_error_result_syntax); } } movetext_ends_with_result = true; @@ -202,7 +211,7 @@ if (curcol == BLACK) { while (isdigit(c = *(pgndata++))); if (c != '.') { - return pgn_error_missing_dot; + return(pgn_error_missing_dot); } } curcol = opponent_color(curcol); @@ -211,14 +220,15 @@ /* sanity check result - if it was specified */ if (movetext_ends_with_result && result[0]) { if (strncmp(result, pgn_result(gamestate), 8) != 0) { - fprintf(stderr, "Fuck it: %s\n", result); - return pgn_error_result_mismatch; + return(pgn_error_result_mismatch); } } - return pgn_no_error; + return(pgn_no_error); } +#undef return + int read_pgn(FILE *stream, GameState *gamestate) { fseek(stream, 0, SEEK_END); long int size = ftell(stream); @@ -228,7 +238,10 @@ size_t read = 0; do { size_t r = fread(data + read, 1, size - read, stream); - if (r == 0) return pgn_error_unexpected_eof; + if (r == 0) { + pgn_error_pos = read; + return pgn_error_unexpected_eof; + } read += r; } while (read < size); int result = parse_pgn(data, gamestate);
--- a/src/chess/pgn.h Sun Jul 19 17:13:16 2026 +0200 +++ b/src/chess/pgn.h Sun Jul 19 19:17:04 2026 +0200 @@ -59,6 +59,7 @@ const char *pgn_player_name(GameState *gamestate, uint8_t color); const char* pgn_error_str(int code); +size_t pgn_error_position(void); #ifdef __cplusplus }
--- a/src/main.c Sun Jul 19 17:13:16 2026 +0200 +++ b/src/main.c Sun Jul 19 19:17:04 2026 +0200 @@ -895,11 +895,10 @@ FILE *pgnfile = fopen(settings.continuepgn, "r"); if (pgnfile) { int result = read_pgn(pgnfile, &gamestate); - long position = ftell(pgnfile); fclose(pgnfile); if (result) { - printw("Invalid PGN file content at position %ld:\n%s\n", - position, pgn_error_str(result)); + printw("Invalid PGN file content at position %zu:\n%s\n", + pgn_error_position(), pgn_error_str(result)); gamestate_cleanup(&gamestate); return; } @@ -1051,11 +1050,10 @@ FILE *pgnfile = fopen(settings.continuepgn, "r"); if (pgnfile) { int result = read_pgn(pgnfile, &gamestate); - long position = ftell(pgnfile); fclose(pgnfile); if (result) { - printw("Invalid PGN file content at position %ld:\n%s\n", - position, pgn_error_str(result)); + printw("Invalid PGN file content at position %zu:\n%s\n", + pgn_error_position(), pgn_error_str(result)); cancel = true; } else if (!is_game_running(&gamestate)) { addstr("Game has ended. Use -s to analyze it locally.\n");
--- a/test/test-real-pgn.c Sun Jul 19 17:13:16 2026 +0200 +++ b/test/test-real-pgn.c Sun Jul 19 19:17:04 2026 +0200 @@ -43,8 +43,9 @@ gamestate_init(&gamestate); CX_TEST_DO { int result = parse_pgn(pgn_data[index-1], &gamestate); - char msg[32]; - sprintf(msg, "failed to parse game%03u.pgn", index); + char msg[64]; + sprintf(msg, "failed to parse game%03u.pgn: code %d, pos: %zu", + index, result, pgn_error_position()); CX_TEST_ASSERTM(result == pgn_no_error, msg); } gamestate_cleanup(&gamestate);