How to Parse and Edit PGN Tags
This guide shows how to parse a PGN string into a game handle, inspect existing header tags, modify or remove tags, and export the updated game back to PGN using the libscid C ABI.
1. Overview of Symbols
scid_position_create_from_fen: Initialises the starting board position against which moves are validated.scid_game_create: Parses a PGN text buffer and constructs a game handle.scid_game_tag_count_get: Retrieves the number of header tags present on the game.scid_game_tag_at_get: Reads the key name and string value of a tag at a specified 0-based index using caller-allocated buffers.scid_game_tag_set: Sets or overwrites a tag value.scid_game_tag_remove: Deletes a tag from the game header.scid_game_to_pgn: Serialises the game tree and headers back to a PGN-formatted string.
2. Complete Recipe
#include <scid/scid.h>
#include <stdio.h>
#include <string.h>
static int
check(
scid_error error,
const char* call)
{
if (error == SCID_OK)
{
return 1;
}
fprintf(stderr, "%s failed with scid_error %hu\n", call, error);
return 0;
}
static int
contains(
const char* text,
const char* needle)
{
return strstr(text, needle) != NULL;
}
int
main(void)
{
const char* start_fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
const char* pgn = "[Event \"St Petersburg final\"]\n"
"[Site \"St Petersburg\"]\n"
"[Date \"1914.05.18\"]\n"
"[Round \"7\"]\n"
"[White \"Lasker, Emanuel\"]\n"
"[Black \"Capablanca, Jose Raul\"]\n"
"[Result \"1-0\"]\n"
"[ECO \"C68\"]\n"
"[EventDate \"1914.04.21\"]\n"
"[Annotator \"Example\"]\n"
"\n"
"1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 1-0\n";
scid_game* game = NULL;
scid_position* position = NULL;
char diagnostic[1024];
char name[64];
char value[256];
char encoded[4096];
size_t diagnostic_size = 0;
size_t name_size = 0;
size_t value_size = 0;
size_t encoded_size = 0;
size_t tag_count = 0;
int removed = 0;
if (!check(
scid_position_create_from_fen(start_fen, &position), "scid_position_create_from_fen") ||
!check(
scid_game_create(
position, pgn, strlen(pgn), &game, diagnostic, sizeof(diagnostic),
&diagnostic_size),
"scid_game_create"))
{
fprintf(stderr, "%.*s\n", (int)diagnostic_size, diagnostic);
scid_position_free(position);
return 1;
}
if (!check(scid_game_tag_count_get(game, &tag_count), "scid_game_tag_count_get"))
{
scid_position_free(position);
scid_game_free(game);
return 1;
}
for (size_t i = 0; i < tag_count; ++i)
{
if (!check(
scid_game_tag_at_get(
game, i, name, sizeof(name), &name_size, value, sizeof(value), &value_size),
"scid_game_tag_at_get"))
{
scid_position_free(position);
scid_game_free(game);
return 1;
}
printf("%.*s: %.*s\n", (int)name_size, name, (int)value_size, value);
}
if (!check(scid_game_tag_set(game, "Annotator", "C ABI example"), "scid_game_tag_set") ||
!check(scid_game_tag_remove(game, "EventDate", &removed), "scid_game_tag_remove") ||
!check(
scid_game_to_pgn(game, NULL, encoded, sizeof(encoded), &encoded_size),
"scid_game_to_pgn"))
{
scid_position_free(position);
scid_game_free(game);
return 1;
}
printf("\n%.*s", (int)encoded_size, encoded);
if (!removed || !contains(encoded, "[Annotator \"C ABI example\"]") ||
contains(encoded, "[EventDate "))
{
scid_position_free(position);
scid_game_free(game);
return 1;
}
scid_position_free(position);
scid_game_free(game);
return 0;
}
3. Step-by-Step Breakdown
Parsing the Game
Initialise a starting position from standard FEN, then pass the PGN text buffer to scid_game_create. If syntax errors occur during parsing, diagnostic text is written into the caller's diagnostic buffer:
scid_position* position = NULL;
scid_position_create_from_fen(start_fen, &position);
scid_game* game = NULL;
char diagnostic[1024];
size_t diagnostic_size = 0;
scid_error err = scid_game_create(
position, pgn, strlen(pgn), &game, diagnostic, sizeof(diagnostic), &diagnostic_size);
Inspecting Tags
Tags can be enumerated using 0-based indices. Call scid_game_tag_count_get followed by scid_game_tag_at_get:
size_t tag_count = 0;
scid_game_tag_count_get(game, &tag_count);
for (size_t i = 0; i < tag_count; ++i)
{
char name[64];
char value[256];
size_t name_size = 0;
size_t value_size = 0;
scid_game_tag_at_get(
game, i, name, sizeof(name), &name_size, value, sizeof(value), &value_size);
printf("%.*s: %.*s\n", (int)name_size, name, (int)value_size, value);
}
Modifying and Exporting
Use scid_game_tag_set to add or update tags, scid_game_tag_remove to delete tags, and scid_game_to_pgn to export the modified game:
scid_game_tag_set(game, "Annotator", "C ABI example");
int removed = 0;
scid_game_tag_remove(game, "EventDate", &removed);
char encoded[4096];
size_t encoded_size = 0;
scid_game_to_pgn(game, NULL, encoded, sizeof(encoded), &encoded_size);
Remember to release handles with scid_position_free(position) and scid_game_free(game).