Description
Chess games viewer from PGN files on terminal. It includes a basic PGN files parser using regex and a, even basic, check todeterminate the correctness of the moves performed.
Chess games viewer from PGN files on terminal. It includes a basic PGN files parser using regex and a, even basic, check todeterminate the correctness of the moves performed.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <regex.h> #include <sys/types.h> #include <unistd.h> #define W_FLAG 0 /* White Player Flag */ #define B_FLAG 1 /* Black Player Flag */ /* A piece contains info on player and type: * b8 b7b6b5b4b3b2b1 - b8 - Player Info, b7-1 Piece Name */ #define P_FLAG(p) ((int) ((p) & 128) >> 7) #define P_TYPE(p) ((char) ((p) & 127)) #define P_SETS(type,flag) ((flag) ? 128 : 0) | ((type) & 127) /* Coordinate on column and row is put in an unsigned char. * b8b7b6b5 b4b3b2b1 - b8-5 = col, b4-1 = row */ #define P_COLROW(col,row) ((((col) << 4) & 0xf0) | ((row) & 0x0f)) #define P_COL(col) (((col) >> 4) & 0x0f) #define P_ROW(row) ((row) & 0x0f) #define R_ATOI(a) ('8' - (a)) #define R_ITOA(a) (R_ATOI(a)) #define C_ATOI(a) ((a) - 'a') #define C_ITOA(a) ((a) + 'a') #define P_EXIST(p) (P_TYPE(p) == 'R' || P_TYPE(p) == 'N' || \ P_TYPE(p) == 'B' || P_TYPE(p) == 'p' || \ P_TYPE(p) == 'K' || P_TYPE(p) == 'Q') #define REGEX_INFO "^[ ]*\\[([^ ]+) ?[\"'](.+)[\"']\\]" #define REGEX_MOVE "([0-9]+)\\.[ ]*([a-h1-8RNBKQpx:+#]+|[O-]+)[ ]*([a-h1-8RNBKQpx:+#]+|[O-]+)" #define REGEX_MOVE_DETAILS "([RNBKQp])?([a-h])?(x)?([a-h])?([1-8])?(=[RNBKQ])?(\\+{1,2}|#)?" typedef unsigned char Piece; typedef Piece * Row; typedef Row * Chess; struct _global { unsigned color : 3; unsigned chess_print : 3; unsigned pause : 2; } global_opt = { 0, 1, 0 }; typedef struct { unsigned wb_flag : 1; unsigned piece : 7; unsigned start_col : 4; unsigned x : 1; unsigned end_col : 3; unsigned end_row : 3; unsigned flags : 2; unsigned promotion : 7; } Move; typedef struct _allow_move { unsigned char coord; struct _allow_move *next; } AllowMoves; /* Initialize a new 8x8 array: * White Pieces in row 6,7 - Black Pieces in row 0,1 */ Chess chess_new (void) { const char base[8] = "RNBQKBNR"; Chess init; char j; init = (Chess) malloc (8 * sizeof (Row)); for ( j = 0; j < 8; init[j] = (Row) calloc (8, sizeof (Piece)), j++ ); for ( j = 0; j < 8; j++ ) { init[0][j] = P_SETS (base[j], B_FLAG); init[1][j] = P_SETS ( 'p', B_FLAG); init[7][j] = P_SETS (base[j], W_FLAG); init[6][j] = P_SETS ( 'p', W_FLAG); } return init; } /* Free the array created by chess_new () */ void chess_free (Chess to_free) { char j; for ( j = 0; j < 8; free (to_free[j]), j++ ); free (to_free); } /* Prints the chess in the current state. Identify White pieces with + * and Black pieces with -. Writes nothing if NO_PRINT is defined */ void chess_print (Chess chess) { char y, x, j; if ( !global_opt.chess_print ) return; if ( global_opt.color ) { /* FG_BG | W: White, B: Black, G: Green, R: Red */ #define B_W(s) ("\e[30;47m" s) #define W_B(s) ("\e[37;0m" s) #define W_R(s) ("\e[37;41m" s) #define B_G(s) ("\e[30;42m" s) for ( y = 0; y < 8; y++ ) { printf (W_B("+------+------+------+------+------+------+------+------+\n")); for ( x = 0; x < 8; x++ ) printf (W_B("|%s"), ((x ^ y) & 1) ? W_B("######") : B_W(" ")); printf (W_B("|\n")); for ( x = 0; x < 8; x++ ) { if ( P_EXIST(chess[y][x]) ) printf (W_B("|%s %s%c%c %s"), ((x ^ y) & 1) ? W_B("#") : B_W(" "), (P_FLAG (chess[y][x]) ? W_R("") : B_G("")), P_TYPE (chess[y][x]), P_FLAG (chess[y][x]) ? '-' : '+', ((x ^ y) & 1) ? W_B("#") : B_W(" ")); else printf (W_B("|%s"), ((x ^ y) & 1) ? W_B ("# #") : B_W(" ")); } printf (W_B ("| %d\n"), 8 - y); for ( x = 0; x < 8; x++ ) printf (W_B("|%s"), ((x ^ y) & 1) ? W_B("######") : B_W(" ")); printf (W_B("|\n")); } printf (W_B("+------+------+------+------+------+------+------+------+\n")); printf ("\e[0m"); } else { for ( y = 0; y < 8; y++ ) { printf ("+------+------+------+------+------+------+------+------+\n"); for ( x = 0; x < 8; x++ ) printf ("|%s", ((x ^ y) & 1) ? "######" : " "); printf ("|\n"); for ( x = 0; x < 8; x++ ) { if ( P_EXIST(chess[y][x]) ) printf ("|%c %c%c %c", ((x ^ y) & 1) ? '#' : ' ', P_TYPE (chess[y][x]), P_FLAG (chess[y][x]) ? '-' : '+', ((x ^ y) & 1) ? '#' : ' '); else printf ("|%s", ((x ^ y) & 1) ? "# #" : " "); } printf ("| %d\n", 8 - y); for ( x = 0; x < 8; x++ ) printf ("|%s", ((x ^ y) & 1) ? "######" : " "); printf ("|\n"); } printf ("+------+------+------+------+------+------+------+------+\n"); } for ( x = 0; x < 8; x++ ) printf (" %c ", 'A' + x); printf ("\n\n"); if ( global_opt.pause ) { printf ("Press ENTER to continue.. "); getchar (); } } /* Parse a PGN file to get the info '[name "value"]' */ void PGN_print_info (char *pgn_data, int data_len, int *last_match) { char *info; int off_start, off_end, rc; regex_t regex; regmatch_t match[4]; if ( rc = regcomp (®ex, REGEX_INFO, REG_EXTENDED) ) { info = (char *) malloc (128 * sizeof (char)); regerror(rc, ®ex, info, 128); fprintf(stderr, "(%d) regcomp() failed with '%s'\n", __LINE__, info); exit(EXIT_FAILURE); } /* Cuts the PGN file in lines, for each line apply a regex to find an * info. If name and value are not empty, it print them. */ for ( off_start = 0; off_start < data_len; off_start = off_end) { if ( pgn_data[off_start] == '\n' || !off_start ) { for ( off_end = off_start + 1; off_end < data_len; off_end++ ) { if ( pgn_data[off_end] == '\n' ) break; } if ( (off_end - off_start - 1) < 2 ) continue; info = strndup (pgn_data + (off_start + 1), off_end - off_start); if ( regexec(®ex, info, 3, match, 0) ) continue; free (info); info = strndup (pgn_data + (off_start + 1 + match[1].rm_so), match[1].rm_eo - match[1].rm_so); printf ("%s: ", info); free (info); info = strndup (pgn_data + (off_start + 1 + match[2].rm_so), match[2].rm_eo - match[2].rm_so); printf ("%s\n", info); free (info); *last_match = off_start + match[0].rm_eo; } else off_end = off_start + 1; } regfree (®ex); } /* Every move is splitted in groups: * 1: Name - Optional if pawn * 2: Start Col | End Col if 3 doesn't exists - Only if 3 setted, Optional * 3: x - Optional * 4: End Col if 3 exists - Required * 5: End Row - Required * 6: Promotion - Optional * 7: Flags ( +, ++, # ) - Optional * * Return a Move structure. */ Move move_parse (char *move, int flag) { Move move_details = { 0, 0, 0, 0, 0, 0, 0, 0 }; int rc; char *string; regex_t regex; regmatch_t match[9]; if ( rc = regcomp (®ex, REGEX_MOVE_DETAILS, REG_EXTENDED) ) { string = (char *) malloc (128 * sizeof (char)); regerror(rc, ®ex, string, 128); fprintf(stderr, "(%d) regcomp() failed with '%s'\n", __LINE__, string); exit(EXIT_FAILURE); } if ( regexec (®ex, move, 8, match, 0) ) { fprintf (stderr, "Move: %s - String Malformed\n", move); regfree (®ex); return move_details; } move_details.wb_flag = flag & 1; if ( match[1].rm_so == -1 ) move_details.piece = P_TYPE ('p' - 'A'); else move_details.piece = P_TYPE (move[match[1].rm_so] - 'A'); if ( (P_TYPE (move_details.piece) + 'A') == 'p' && match[6].rm_so != -1 ) move_details.promotion = P_TYPE (move[match[6].rm_so + 1] - 'A'); move_details.flags = 0; if ( match[7].rm_so != -1 ) { if ( (match[7].rm_eo - match[7].rm_so) < 2 ) { switch (move[match[7].rm_so]) { case '+': move_details.flags = 1; break; case '#': move_details.flags = 3; break; } } else move_details.flags = 2; } if ( match[3].rm_so == -1 ) { move_details.x = 0; if ( match[4].rm_so == -1 ) move_details.end_col = C_ATOI (move[match[2].rm_so]); else { move_details.start_col = move[match[2].rm_so] - ('a' - 1); move_details.end_col = C_ATOI (move[match[4].rm_so]); } } else { move_details.x = 1; if ( match[2].rm_so != -1 ) move_details.start_col = move[match[2].rm_so] - ('a' - 1); else move_details.start_col = 0; move_details.end_col = C_ATOI (move[match[4].rm_so]); } move_details.end_row = R_ATOI (move[match[5].rm_so]); regfree (®ex); return move_details; } /* Add a coordinate in AllowMoves list */ AllowMoves *allow_moves_add (AllowMoves *list, unsigned char col, unsigned char row) { AllowMoves *p; p = (AllowMoves *) malloc (sizeof (AllowMoves)); p->coord = P_COLROW (col, row); p->next = list; return p; } /* Check if coord passed exist in AllowMoves list */ int allow_moves_check (AllowMoves *list, unsigned char coord) { AllowMoves *p; for ( p = list; p; p = p->next ) if ( p->coord == coord ) return 1; return 0; } /* Free AllowMoves list */ void allow_moves_free (AllowMoves *list) { AllowMoves *p; if ( !list ) return; do { p = list->next; free (list); list = p; } while ( list ); } /* Check if the move passed is valid. Return 1 if Valid, 0 if not. */ int check_move (Chess chess, int flag, char piece, char s_col, char s_row, char e_col, char e_row) { int j; unsigned char coord_start, coord_end; AllowMoves *allows = NULL; coord_start = P_COLROW (C_ATOI (s_col), R_ATOI (s_row)); coord_end = P_COLROW (C_ATOI (e_col), R_ATOI (e_row)); if ( coord_start == coord_end ) return 0; #define POS(row,r_add,col,c_add) (chess[R_ATOI (row) + (r_add)][C_ATOI (col) + (c_add)]) #define MOVE_ADD(m_list,row,r_add,col,c_add,extra) \ if ( !P_EXIST (POS ((row), (r_add), (col), (c_add))) ) { \ m_list = allow_moves_add (m_list, \ C_ATOI (col) + (c_add), \ R_ATOI (row) + (r_add)); \ } \ else if ( P_FLAG (POS ((row), (r_add), (col), (c_add))) != flag) { \ m_list = allow_moves_add (m_list, \ C_ATOI (col) + (c_add), \ R_ATOI (row) + (r_add)); \ extra; \ } switch (piece) { case 'p': if ( (flag && s_row > '1') || (!flag && s_row < '8') ) { /* Move Forward */ if ( !P_EXIST (POS (s_row, flag ? 1 : -1, s_col, 0)) ) { allows = allow_moves_add (allows, C_ATOI (s_col), R_ATOI (s_row) + (flag ? 1 : -1)); if ( (flag && s_row == '7') || (!flag && s_row == '2') ) { if ( !P_EXIST (POS (s_row, flag ? 2 : -2, s_col, 0)) ) allows = allow_moves_add (allows, C_ATOI (s_col), R_ATOI (s_row) + (flag ? 2 : -2)); } } /* Eat */ if ( s_col > 'a' ) if ( P_EXIST (POS (s_row, flag ? 1 : -1, s_col, -1)) && P_FLAG (POS (s_row, flag ? 1 : -1, s_col, -1)) != flag ) allows = allow_moves_add (allows, C_ATOI (s_col) - 1, R_ATOI (s_row) + (flag ? 1 : -1)); if ( s_col < 'h' ) if ( P_EXIST (POS (s_row, flag ? 1 : -1, s_col, 1)) && P_FLAG (POS (s_row, flag ? 1 : -1, s_col, 1)) != flag ) allows = allow_moves_add (allows, C_ATOI (s_col) + 1, R_ATOI (s_row) + (flag ? 1 : -1)); } break; case 'R': /* right, left, bottom, top */ for ( j = C_ATOI (s_col); j < 8; j++ ) { MOVE_ADD (allows, s_row, 0, C_ITOA (j), 0, break); } for ( j = C_ATOI (s_col); j > -1; j-- ) { MOVE_ADD (allows, s_row, 0, C_ITOA (j), 0, break); } for ( j = R_ATOI (s_row); j < 8; j++ ) { MOVE_ADD (allows, R_ITOA (j), 0, s_col, 0, break); } for ( j = R_ATOI (s_row); j > -1; j-- ) { MOVE_ADD (allows, R_ITOA (j), 0, s_col, 0, break); } break; case 'N': /* 5 3 * 7 _|x|_|x|_ * x|_|_|_|x 1 * _|_|x|_|_ * 8 x|_|_|_|x 2 * |x| |x| * 6 4 */ if ( s_col < 'g' && s_row < '8' ) { MOVE_ADD (allows, s_row, -1, s_col, +2, 0); } if ( s_col < 'g' && s_row > '1' ) { MOVE_ADD (allows, s_row, +1, s_col, +2, 0); } if ( s_col < 'h' && s_row < '7' ) { MOVE_ADD (allows, s_row, -2, s_col, +1, 0); } if ( s_col < 'h' && s_row > '2' ) { MOVE_ADD (allows, s_row, +2, s_col, +1, 0); } if ( s_col > 'a' && s_row < '7' ) { MOVE_ADD (allows, s_row, -2, s_col, -1, 0); } if ( s_col > 'a' && s_row > '2' ) { MOVE_ADD (allows, s_row, +2, s_col, -1, 0); } if ( s_col > 'b' && s_row < '8' ) { MOVE_ADD (allows, s_row, -1, s_col, -2, 0); } if ( s_col > 'b' && s_row > '1' ) { MOVE_ADD (allows, s_row, +1, s_col, -2, 0); } break; case 'B': /* bottom-right, bottom-left, top-right, top-left */ for ( j = 0; (R_ATOI (s_row) + j) < 8 && (C_ATOI (s_col) + j) < 8; j++ ) { MOVE_ADD (allows, s_row, j, s_col, j, break); } for ( j = 0; (R_ATOI (s_row) + j) < 8 && (C_ATOI (s_col) - j) > -1; j++ ) { MOVE_ADD (allows, s_row, j, s_col, j * -1, break); } for ( j = 0; (R_ATOI (s_row) - j) > -1 && (C_ATOI (s_col) + j) < 8; j++ ) { MOVE_ADD (allows, s_row, j * -1, s_col, j, break); } for ( j = 0; (R_ATOI (s_row) - j) > -1 && (C_ATOI (s_col) - j) > -1; j++ ) { MOVE_ADD (allows, s_row, j * -1, s_col, j * -1, break); } break; case 'Q': /* right, left, bottom, top */ for ( j = C_ATOI (s_col); j < 8; j++ ) { MOVE_ADD (allows, s_row, 0, C_ITOA (j), 0, break); } for ( j = C_ATOI (s_col); j > -1; j-- ) { MOVE_ADD (allows, s_row, 0, C_ITOA (j), 0, break); } for ( j = R_ATOI (s_row); j < 8; j++ ) { MOVE_ADD (allows, R_ITOA (j), 0, s_col, 0, break); } for ( j = R_ATOI (s_row); j > -1; j-- ) { MOVE_ADD (allows, R_ITOA (j), 0, s_col, 0, break); } /* bottom-right, bottom-left, top-right, top-left */ for ( j = 0; (R_ATOI (s_row) + j) < 8 && (C_ATOI (s_col) + j) < 8; j++ ) { MOVE_ADD (allows, s_row, j, s_col, j, break); } for ( j = 0; (R_ATOI (s_row) + j) < 8 && (C_ATOI (s_col) - j) > -1; j++ ) { MOVE_ADD (allows, s_row, j, s_col, j * -1, break); } for ( j = 0; (R_ATOI (s_row) - j) > -1 && (C_ATOI (s_col) + j) < 8; j++ ) { MOVE_ADD (allows, s_row, j * -1, s_col, j, break); } for ( j = 0; (R_ATOI (s_row) - j) > -1 && (C_ATOI (s_col) - j) > -1; j++ ) { MOVE_ADD (allows, s_row, j * -1, s_col, j * -1, break); } break; case 'K': /* right, left, top, bottom */ if ( s_col < 'h' ) MOVE_ADD (allows, s_row, +0, s_col, +1, 0); if ( s_col > 'a' ) MOVE_ADD (allows, s_row, +0, s_col, -1, 0); if ( s_row < '8' ) MOVE_ADD (allows, s_row, -1, s_col, +0, 0); if ( s_row > '1' ) MOVE_ADD (allows, s_row, +1, s_col, +0, 0); /* top-right, bottom-right, top-left, bottom-left */ if ( s_col < 'h' && s_row < '8' ) MOVE_ADD (allows, s_row, -1, s_col, +1, 0); if ( s_col < 'h' && s_row > '1' ) MOVE_ADD (allows, s_row, +1, s_col, +1, 0); if ( s_col > 'a' && s_row < '8' ) MOVE_ADD (allows, s_row, -1, s_col, -1, 0); if ( s_col > 'a' && s_row > '1' ) MOVE_ADD (allows, s_row, +1, s_col, -1, 0); break; } if ( allow_moves_check (allows, coord_end) ) { allow_moves_free (allows); return 1; } allow_moves_free (allows); return 0; } /* Get current coordinate of a piece. If col or row are not setted ( * equal to 0 ) it search the piece passed in the whole col or row. * col and e_col can be: 'a', 'b', ..., 'h' * row and e_row can be: '1', '2', ..., '8' * * If check_flag is 1 call check_move function () to check if the * move is valid. * * Return 1 if Valid, 0 if not. */ int get_coord (Chess chess, int flag, char piece, char col, char row, unsigned char *result, int check_flag, char e_col, char e_row) { char f_col, f_row; char sc = 0, ec = 7, sr = 0, er = 7; if ( row ) { sr = R_ATOI (row); er = sr; } if ( col ) { sc = C_ATOI (col); ec = sc; } for ( f_row = sr; f_row <= er; f_row++ ) { for ( f_col = sc; f_col <= ec; f_col++ ) { if ( P_TYPE (chess[f_row][f_col]) == piece && P_FLAG (chess[f_row][f_col]) == flag ) { *result = P_COLROW (f_col, f_row); if ( check_flag ) { if ( check_move (chess, flag, piece, C_ITOA (f_col), R_ITOA (f_row), e_col, e_row) ) return 1; else continue; } else return 1; } } } return 0; } /* Do the move passed for the flag ( White or Black ) player. */ void chess_move (Chess *chess, char *move, int flag) { Move move_details; unsigned char p_col; unsigned char K_coord, T_coord, coord; if ( !strcmp (move, "O-O") ) { if ( !get_coord (*chess, flag, 'K', 'e', flag ? '8' : '1', &K_coord, 0, 0, 0) ) { fprintf (stderr, "Invalid Move: '%s' - %s\n", move, flag ? "Black" : "White"); exit (1); } if ( !get_coord (*chess, flag, 'R', 'h', flag ? '8' : '1', &T_coord, 0, 0, 0) ) { fprintf (stderr, "Invalid Move: '%s' - %s\n", move, flag ? "Black" : "White"); exit (1); } if ( (flag && (P_EXIST ((*chess)[0][5]) || P_EXIST ((*chess)[0][6]))) || (!flag && (P_EXIST ((*chess)[7][5]) || P_EXIST ((*chess)[7][6]))) ) { fprintf (stderr, "Invalid Move: '%s' - %s\n", move, flag ? "Black" : "White"); exit (1); } (*chess)[P_ROW (K_coord)][P_COL (K_coord)] = 0; (*chess)[flag ? 0 : 7][6] = P_SETS ('K', flag); (*chess)[P_ROW (T_coord)][P_COL (T_coord)] = 0; (*chess)[flag ? 0 : 7][5] = P_SETS ('R', flag); return; } if ( !strcmp (move, "O-O-O") ) { if ( !get_coord (*chess, flag, 'K', 'e', flag ? '8' : '1', &K_coord, 0, 0, 0) ) { fprintf (stderr, "Invalid Move: '%s' - %s\n", move, flag ? "Black" : "White"); exit (1); } if ( !get_coord (*chess, flag, 'R', 'a', flag ? '8' : '1', &T_coord, 0, 0, 0) ) { fprintf (stderr, "Invalid Move: '%s' - %s\n", move, flag ? "Black" : "White"); exit (1); } if ( (flag && (P_EXIST ((*chess)[0][1]) || P_EXIST ((*chess)[0][2]) || P_EXIST ((*chess)[0][3]))) || (!flag && (P_EXIST ((*chess)[7][1]) || P_EXIST ((*chess)[7][2]) || P_EXIST ((*chess)[7][3]))) ) { fprintf (stderr, "Invalid Move: '%s' - %s\n", move, flag ? "Black" : "White"); exit (1); } (*chess)[P_ROW (K_coord)][P_COL (K_coord)] = 0; (*chess)[flag ? 0 : 7][2] = P_SETS ('K', flag); (*chess)[P_ROW (T_coord)][P_COL (T_coord)] = 0; (*chess)[flag ? 0 : 7][3] = P_SETS ('R', flag); return; } move_details = move_parse (move, flag); if ( move_details.start_col ) p_col = move_details.start_col + ('a' - 1); else p_col = 0; if ( !get_coord (*chess, move_details.wb_flag, move_details.piece + 'A', p_col, 0, &coord, 1, C_ITOA (move_details.end_col), R_ITOA (move_details.end_row)) ) { fprintf (stderr, "Invalid Move: '%s' - %s\n", move, flag ? "Black" : "White"); exit (1); } (*chess)[P_ROW (coord)][P_COL (coord)] = 0; if ( move_details.promotion ) (*chess)[move_details.end_row][move_details.end_col] = P_SETS (move_details.promotion + 'A', move_details.wb_flag); else (*chess)[move_details.end_row][move_details.end_col] = P_SETS (move_details.piece + 'A', move_details.wb_flag); } /* Search the moves in the PGN file passed and print the chess status * after every move for each player */ void PGN_print_moves (char *pgn_data, int data_len) { Chess chess; char *string; int rc, n; regex_t regex; regmatch_t match[5]; chess = chess_new (); printf ("\n0) Start\n\n"), chess_print (chess); if ( rc = regcomp (®ex, REGEX_MOVE, REG_EXTENDED | REG_NEWLINE) ) { string = (char *) malloc (128 * sizeof (char)); regerror(rc, ®ex, string, 128); fprintf (stderr, "(%d) regcomp() failed with '%s'\n", __LINE__, string); exit(EXIT_FAILURE); } for ( n = 0; n < data_len; n += match[0].rm_eo ) { if ( regexec(®ex, pgn_data + n, 4, match, 0) ) break; if ( (match[2].rm_eo - match[2].rm_so) < 2 || (match[3].rm_eo - match[3].rm_so) < 2 ) continue; string = strndup (pgn_data + (n + match[1].rm_so), match[1].rm_eo - match[1].rm_so); printf ("------------------------ %6s ------------------------\n", string); free (string); string = strndup (pgn_data + (n + match[2].rm_so), match[2].rm_eo - match[2].rm_so); printf ("White[+]: %s\n", string); chess_move (&chess, string, W_FLAG); chess_print (chess); free (string); if ( match[3].rm_so != -1 ) { string = strndup (pgn_data + (n + match[3].rm_so), match[3].rm_eo - match[3].rm_so); printf ("Black[-]: %s\n", string); chess_move (&chess, string, B_FLAG); free (string); chess_print (chess); } else { chess_print (chess); break; } } regfree (®ex); chess_free (chess); } int main (int argc, char *argv[]) { FILE *input; char *data; int data_size, last_match; int opt; if ( argc < 2 ) { fprintf (stderr, "Usage: %s [Options] <PGN file>\n" " Options:\n" " -c Print with shell color" " -i Print only info" " -p Pause every move", *argv); return 1; } while ( (opt = getopt (argc, argv, "cip")) != -1 ) { switch (opt) { case 'c': global_opt.color = 1; break; case 'i': global_opt.chess_print = 0; break; case 'p': global_opt.pause = 1; break; } } if ( !(input = fopen (argv[argc-1], "rb")) ) { fprintf (stderr, "Cannot open '%s'\n", argv[argc-1]); return 1; } fseek (input, 0, SEEK_END); data_size = ftell (input); rewind (input); data = (char *) malloc (data_size * sizeof (char)); fread (data, sizeof (char), data_size, input); fclose (input); printf ("Legend: - Black, + White\n\n"); PGN_print_info (data, data_size, &last_match); PGN_print_moves (data + last_match, data_size); free (data); }