# HG changeset patch # User Mike Becker # Date 1789051713 -7200 # Node ID 13b934b7d03ede25b630483ec129dcffdaf9195a # Parent 447d1e1928074f77ea02e6b34e36b3b994c42f5d add possibility to specify the width for a PGN move block + function to generate only the move block somehow also relates to #924 and issue #970 diff -r 447d1e192807 -r 13b934b7d03e src/chess/pgn.c --- a/src/chess/pgn.c Thu Sep 10 09:35:41 2026 +0200 +++ b/src/chess/pgn.c Thu Sep 10 16:48:33 2026 +0200 @@ -275,7 +275,7 @@ return result; } -static void pgn_insert_newlines(char *block) { +static void pgn_insert_newlines(char *block, unsigned max_width) { size_t pos = 0; size_t last_space_pos = 0; size_t line_len = 0; @@ -288,7 +288,7 @@ last_space_pos = pos; } line_len++; - if (line_len > 80) { + if (line_len > max_width) { block[last_space_pos] = '\n'; line_len = pos - last_space_pos; } @@ -341,43 +341,12 @@ return 0; } -char *create_pgn(const GameState *gamestate, bool export_comments) { - size_t resultcap = 4096; - char *result = malloc(resultcap); - if (result == NULL) return NULL; - char *resp = result; - - /* STR tag pairs */ - resp += sprintf(resp, "[Event \"%s\"]\n", "terminal-chess game"); - resp += sprintf(resp, "[Site \"%s\"]\n", "Somewhere on Earth"); - resp += sprintf(resp, "[Date \"%s\"]\n", pgn_date(gamestate)); - resp += sprintf(resp, "[Round \"%s\"]\n", "-"); - resp += sprintf(resp, "[White \"%s\"]\n", pgn_player_name(gamestate, WHITE)); - resp += sprintf(resp, "[Black \"%s\"]\n", pgn_player_name(gamestate, BLACK)); - const char *gameresult = pgn_game_result(gamestate); - resp += sprintf(resp, "[Result \"%s\"]\n\n", gameresult); - - // TODO: add optional clock info - - /* moves */ - char *moveblk = resp; /* remember where the move block starts */ - +static size_t pgn_move_text_add( + char *buffer, + const GameState *gamestate, + unsigned max_width, bool export_comments) { + char *resp = buffer; for (unsigned i = 0 ; i < gamestate->movecount ; i++) { - /* reallocate buffer if needed */ - { - size_t rsize = resp - result; - if (rsize + 128 > resultcap) { - resultcap *= 2; - char *newresult = realloc(result, resultcap); - if (newresult == NULL) { - free(result); - return NULL; - } - result = newresult; - resp = result + rsize; - } - } - if (i % 2 == 0) { // TODO: add move_start offset resp += snprintf(resp, 16, "%d.\x1f%s", @@ -401,7 +370,7 @@ resp += print_clk(clk, resp, true); *(resp++) = ']'; *(resp++) = '}'; - + /* elapsed move time */ memcpy(resp, " {[%emt ", 8); resp += 8; @@ -410,20 +379,63 @@ *(resp++) = ']'; *(resp++) = '}'; } - + *(resp++) = ' '; } - + + const char *gameresult = pgn_game_result(gamestate); if (gameresult[0] != '*') { size_t rlen = strlen(gameresult); memcpy(resp, gameresult, rlen); resp += rlen; } *(resp++) = '\n'; - *resp = 0; + *(resp++) = '\0'; /* post-process move block and format it nicely */ - pgn_insert_newlines(moveblk); + pgn_insert_newlines(buffer, max_width); + + return resp-buffer; +} + +char *pgn_move_text(const GameState *gamestate, + unsigned max_width, bool export_comments) { + size_t bufcap = 16; + bufcap += gamestate->movecount * 16; + if (export_comments) { + bufcap += gamestate->movecount * 40; + } + char *result = malloc(bufcap); + if (result == NULL) return NULL; + size_t n = pgn_move_text_add(result, gamestate, max_width, export_comments); + return realloc(result, n); +} - return result; +char *create_pgn(const GameState *gamestate, bool export_comments) { + /* calculate generous upper bound for memory usage */ + size_t bufcap = 256; + bufcap += gamestate->movecount * 16; + if (export_comments) { + bufcap += gamestate->movecount * 40; + } + char *result = malloc(bufcap); + if (result == NULL) return NULL; + char *resp = result; + + /* STR tag pairs */ + resp += sprintf(resp, "[Event \"%s\"]\n", "terminal-chess game"); + resp += sprintf(resp, "[Site \"%s\"]\n", "Somewhere on Earth"); + resp += sprintf(resp, "[Date \"%s\"]\n", pgn_date(gamestate)); + resp += sprintf(resp, "[Round \"%s\"]\n", "-"); + resp += sprintf(resp, "[White \"%s\"]\n", pgn_player_name(gamestate, WHITE)); + resp += sprintf(resp, "[Black \"%s\"]\n", pgn_player_name(gamestate, BLACK)); + resp += sprintf(resp, "[Result \"%s\"]\n", pgn_game_result(gamestate)); + + // TODO: add optional clock info + + *(resp++) = '\n'; + resp += pgn_move_text_add(resp, gamestate, 80, export_comments); + + /* shrink memory to fit */ + return realloc(result, resp-result); } diff -r 447d1e192807 -r 13b934b7d03e src/chess/pgn.h --- a/src/chess/pgn.h Thu Sep 10 09:35:41 2026 +0200 +++ b/src/chess/pgn.h Thu Sep 10 16:48:33 2026 +0200 @@ -64,6 +64,9 @@ pgn_result parse_pgn(const char *data, GameState *gamestate); char *create_pgn(const GameState *gamestate, bool export_comments); +char *pgn_move_text(const GameState *gamestate, + unsigned max_width, bool export_comments); + // TODO: this function looks a bit misplaced const char *pgn_player_name(const GameState *gamestate, Color color);