diff -r bdc9528d3e2b -r 36dd94278142 src/chess/pgn.c --- a/src/chess/pgn.c Fri Apr 17 13:17:34 2026 +0200 +++ b/src/chess/pgn.c Fri Apr 17 14:08:10 2026 +0200 @@ -28,6 +28,8 @@ */ #include "pgn.h" + +#include #include #include @@ -177,6 +179,23 @@ return 0; } +static void pgn_insert_newlines(char *block) { + size_t pos = 0; + size_t last_space_pos = 0; + size_t line_len = 0; + while (block[pos] != '\0') { + if (block[pos] == ' ') { + last_space_pos = pos; + } + line_len++; + if (line_len > 80) { + block[last_space_pos] = '\n'; + line_len = pos - last_space_pos; + } + pos++; + } +} + void write_pgn(FILE* stream, GameState *gamestate, GameInfo *gameinfo) { // TODO: tag pairs @@ -197,40 +216,76 @@ fprintf(stream, "[Result \"%s\"]\n\n", result); /* moves */ + size_t moveblkcap = 4096; + char *moveblk = malloc(moveblkcap); + char *moveblkptr = moveblk; + if (moveblk == NULL) { + // TODO: error handling (for the entire function actually) + abort(); + } for (unsigned i = 0 ; i < gamestate->movecount ; i++) { - + /* reallocate move block buffer if needed */ + { + size_t moveblksize = moveblkptr - moveblk; + if (moveblksize + 128 < moveblkcap) { + moveblkcap *= 2; + char *newmoveblk = realloc(moveblk, moveblkcap); + if (newmoveblk == NULL) { + free(moveblk); + abort(); + } + moveblk = newmoveblk; + moveblkptr = moveblk + moveblksize; + } + } + + int snpr; /* return value of printf calls */ + if (i % 2 == 0) { - fprintf(stream, "%d. %s", 1+i/2, gamestate->moves[i].string); + snpr = snprintf(moveblkptr, 16, "%d. %s", + 1+i/2, gamestate->moves[i].string); } else { - fprintf(stream, "%s", gamestate->moves[i].string); + snpr = snprintf(moveblkptr, 16, "%s", + gamestate->moves[i].string); } + moveblkptr += snpr; /* add clock times when the game was under time control */ if (gameinfo->timecontrol) { - char clkstr[16]; + memcpy(moveblkptr, " {[%clk ", 8); + moveblkptr += 8; unsigned clkmv = i + 2; /* time for the next move! */ uint16_t clk = remaining_movetime2(gameinfo, gamestate, clkmv); - print_clk(clk, clkstr, true); - fprintf(stream, " {[%%clk %s]}", clkstr); + snpr = print_clk(clk, moveblkptr, true); + moveblkptr += snpr; + *(moveblkptr++) = ']'; + *(moveblkptr++) = '}'; /* elapsed move time */ - print_clk(gamestate->moves[i].movetime.tv_sec, clkstr, true); - fprintf(stream, " {[%%emt %s]}", clkstr); + memcpy(moveblkptr, " {[%emt ", 8); + moveblkptr += 8; + uint16_t emt = gamestate->moves[i].movetime.tv_sec; + snpr = print_clk(emt, moveblkptr, true); + moveblkptr += snpr; + *(moveblkptr++) = ']'; + *(moveblkptr++) = '}'; } - /* line break every 10 half-moves */ - if ((i+1) % 10) { - fputc(' ', stream); - } else { - fputc('\n', stream); - } + *(moveblkptr++) = ' '; } - if (result[0] == '*') { - fputc('\n', stream); - } else { - fprintf(stream, "%s\n", result); + if (result[0] != '*') { + size_t rlen = strlen(result); + memcpy(moveblkptr, result, rlen); + moveblkptr += rlen; } + *(moveblkptr++) = '\n'; + *moveblkptr = 0; + + pgn_insert_newlines(moveblk); + fputs(moveblk, stream); + + free(moveblk); } static size_t fen_pieces(char *str, GameState *gamestate) {