fix regression: position reporting about PGN errors

Sun, 19 Jul 2026 19:17:04 +0200

author
Mike Becker <universe@uap-core.de>
date
Sun, 19 Jul 2026 19:17:04 +0200
changeset 150
7d57a252948e
parent 149
ea2448961441
child 151
f1b6a0388d85

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);

mercurial