user selectable font in game list
[xboard.git] / gamelist.c
1 /*
2  * gamelist.c -- Functions to manage a gamelist
3  * XBoard $Id: gamelist.c,v 2.1 2003/10/27 19:21:00 mann Exp $
4  *
5  * Copyright 1995,2009 Free Software Foundation, Inc.
6  *
7  * ------------------------------------------------------------------------
8  *
9  * GNU XBoard is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or (at
12  * your option) any later version.
13  *
14  * GNU XBoard is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see http://www.gnu.org/licenses/.  
21  *
22  *------------------------------------------------------------------------
23  ** See the file ChangeLog for a revision history.  */
24
25 #include "config.h"
26
27 #include <stdio.h>
28 #include <errno.h>
29 #if STDC_HEADERS
30 # include <stdlib.h>
31 # include <string.h>
32 #else /* not STDC_HEADERS */
33 # if HAVE_STRING_H
34 #  include <string.h>
35 # else /* not HAVE_STRING_H */
36 #  include <strings.h>
37 # endif /* not HAVE_STRING_H */
38 #endif /* not STDC_HEADERS */
39
40 #include "common.h"
41 #include "frontend.h"
42 #include "backend.h"
43 #include "parser.h"
44
45
46 /* Variables
47  */
48 List gameList;
49
50
51 /* Local function prototypes
52  */
53 static void GameListDeleteGame P((ListGame *));
54 static ListGame *GameListCreate P((void));
55 static void GameListFree P((List *));
56 static int GameListNewGame P((ListGame **));
57
58 /* Delete a ListGame; implies removint it from a list.
59  */
60 static void GameListDeleteGame(listGame)
61     ListGame *listGame;
62 {
63     if (listGame) {
64         if (listGame->gameInfo.event) free(listGame->gameInfo.event);
65         if (listGame->gameInfo.site) free(listGame->gameInfo.site);
66         if (listGame->gameInfo.date) free(listGame->gameInfo.date);
67         if (listGame->gameInfo.round) free(listGame->gameInfo.round);
68         if (listGame->gameInfo.white) free(listGame->gameInfo.white);
69         if (listGame->gameInfo.black) free(listGame->gameInfo.black);
70         if (listGame->gameInfo.fen) free(listGame->gameInfo.fen);
71         if (listGame->gameInfo.resultDetails) free(listGame->gameInfo.resultDetails);
72         if (listGame->gameInfo.timeControl) free(listGame->gameInfo.timeControl);
73         if (listGame->gameInfo.extraTags) free(listGame->gameInfo.extraTags);
74         if (listGame->gameInfo.outOfBook) free(listGame->gameInfo.outOfBook);
75         ListNodeFree((ListNode *) listGame);
76     }
77 }
78
79
80 /* Free the previous list of games.
81  */
82 static void GameListFree(gameList)
83     List *gameList;
84 {
85     while (!ListEmpty(gameList))
86     {
87         GameListDeleteGame((ListGame *) gameList->head);
88     }
89 }
90
91
92
93 /* Initialize a new GameInfo structure.
94  */
95 void GameListInitGameInfo(gameInfo)
96     GameInfo *gameInfo;
97 {
98     gameInfo->event = NULL;
99     gameInfo->site = NULL;
100     gameInfo->date = NULL;
101     gameInfo->round = NULL;
102     gameInfo->white = NULL;
103     gameInfo->black = NULL;
104     gameInfo->result = GameUnfinished;
105     gameInfo->fen = NULL;
106     gameInfo->resultDetails = NULL;
107     gameInfo->timeControl = NULL;
108     gameInfo->extraTags = NULL;
109     gameInfo->whiteRating = -1; /* unknown */
110     gameInfo->blackRating = -1; /* unknown */
111     gameInfo->variant = VariantNormal;
112     gameInfo->outOfBook = NULL;
113 }
114
115
116 /* Create empty ListGame; returns ListGame or NULL, if out of memory.
117  *
118  * Note, that the ListGame is *not* added to any list
119  */
120 static ListGame *GameListCreate()
121
122 {
123     ListGame *listGame;
124
125     if ((listGame = (ListGame *) ListNodeCreate(sizeof(*listGame)))) {
126         GameListInitGameInfo(&listGame->gameInfo);
127     }
128     return(listGame);
129 }
130
131
132 /* Creates a new game for the gamelist.
133  */
134 static int GameListNewGame(listGamePtr)
135      ListGame **listGamePtr;
136 {
137     if (!(*listGamePtr = (ListGame *) GameListCreate())) {
138         GameListFree(&gameList);
139         return(ENOMEM);
140     }
141     ListAddTail(&gameList, (ListNode *) *listGamePtr);
142     return(0);
143 }
144
145
146 /* Build the list of games in the open file f.
147  * Returns 0 for success or error number.
148  */
149 int GameListBuild(f)
150     FILE *f;
151 {
152     ChessMove cm, lastStart;
153     int gameNumber;
154     ListGame *currentListGame = NULL;
155     int error;
156     int offset;
157
158     GameListFree(&gameList);
159     yynewfile(f);
160     gameNumber = 0;
161
162     lastStart = (ChessMove) 0;
163     yyskipmoves = FALSE;
164     do {
165         yyboardindex = 0;
166         offset = yyoffset();
167         cm = (ChessMove) yylex();
168         switch (cm) {
169           case GNUChessGame:
170             if ((error = GameListNewGame(&currentListGame))) {
171                 rewind(f);
172                 yyskipmoves = FALSE;
173                 return(error);
174             }
175             currentListGame->number = ++gameNumber;
176             currentListGame->offset = offset;
177             if (currentListGame->gameInfo.event != NULL) {
178                 free(currentListGame->gameInfo.event);
179             }
180             currentListGame->gameInfo.event = StrSave(yy_text);
181             lastStart = cm;
182             break;
183           case XBoardGame:
184             lastStart = cm;
185             break;
186           case MoveNumberOne:
187             switch (lastStart) {
188               case GNUChessGame:
189                 break;          /*  ignore  */
190               case PGNTag:
191                 lastStart = cm;
192                 break;          /*  Already started */
193               case (ChessMove) 0:
194               case MoveNumberOne:
195               case XBoardGame:
196                 if ((error = GameListNewGame(&currentListGame))) {
197                     rewind(f);
198                     yyskipmoves = FALSE;
199                     return(error);
200                 }
201                 currentListGame->number = ++gameNumber;
202                 currentListGame->offset = offset;
203                 lastStart = cm;
204                 break;
205               default:
206                 break;          /*  impossible  */
207             }
208             break;
209           case PGNTag:
210             lastStart = cm;
211             if ((error = GameListNewGame(&currentListGame))) {
212                 rewind(f);
213                 yyskipmoves = FALSE;
214                 return(error);
215             }
216             currentListGame->number = ++gameNumber;
217             currentListGame->offset = offset;
218             ParsePGNTag(yy_text, &currentListGame->gameInfo);
219             do {
220                 yyboardindex = 1;
221                 offset = yyoffset();
222                 cm = (ChessMove) yylex();
223                 if (cm == PGNTag) {
224                     ParsePGNTag(yy_text, &currentListGame->gameInfo);
225                 }
226             } while (cm == PGNTag || cm == Comment);
227             break;
228           case NormalMove:
229             /* Allow the first game to start with an unnumbered move */
230             yyskipmoves = TRUE;
231             if (lastStart == (ChessMove) 0) {
232               if ((error = GameListNewGame(&currentListGame))) {
233                 rewind(f);
234                 yyskipmoves = FALSE;
235                 return(error);
236               }
237               currentListGame->number = ++gameNumber;
238               currentListGame->offset = offset;
239               lastStart = MoveNumberOne;
240             }
241             break;
242           default:
243             break;
244         }
245     }
246     while (cm != (ChessMove) 0);
247
248
249     if (appData.debugMode) {
250         for (currentListGame = (ListGame *) gameList.head;
251              currentListGame->node.succ;
252              currentListGame = (ListGame *) currentListGame->node.succ) {
253
254             fprintf(debugFP, "Parsed game number %d, offset %ld:\n",
255                     currentListGame->number, currentListGame->offset);
256             PrintPGNTags(debugFP, &currentListGame->gameInfo);
257         }
258     }
259
260     rewind(f);
261     yyskipmoves = FALSE;
262     return 0;
263 }
264
265
266 /* Clear an existing GameInfo structure.
267  */
268 void ClearGameInfo(gameInfo)
269     GameInfo *gameInfo;
270 {
271     if (gameInfo->event != NULL) {
272         free(gameInfo->event);
273     }
274     if (gameInfo->site != NULL) {
275         free(gameInfo->site);
276     }
277     if (gameInfo->date != NULL) {
278         free(gameInfo->date);
279     }
280     if (gameInfo->round != NULL) {
281         free(gameInfo->round);
282     }
283     if (gameInfo->white != NULL) {
284         free(gameInfo->white);
285     }
286     if (gameInfo->black != NULL) {
287         free(gameInfo->black);
288     }
289     if (gameInfo->resultDetails != NULL) {
290         free(gameInfo->resultDetails);
291     }
292     if (gameInfo->fen != NULL) {
293         free(gameInfo->fen);
294     }
295     if (gameInfo->timeControl != NULL) {
296         free(gameInfo->timeControl);
297     }
298     if (gameInfo->extraTags != NULL) {
299         free(gameInfo->extraTags);
300     }
301     if (gameInfo->outOfBook != NULL) {
302         free(gameInfo->outOfBook);
303     }
304
305     GameListInitGameInfo(gameInfo);
306 }
307
308 /* [AS] Replaced by "dynamic" tag selection below */
309 char *
310 GameListLineOld(number, gameInfo)
311      int number;
312      GameInfo *gameInfo;
313 {
314     char *event = (gameInfo->event && strcmp(gameInfo->event, "?") != 0) ?
315                      gameInfo->event : gameInfo->site ? gameInfo->site : "?";
316     char *white = gameInfo->white ? gameInfo->white : "?";
317     char *black = gameInfo->black ? gameInfo->black : "?";
318     char *date = gameInfo->date ? gameInfo->date : "?";
319     int len = 10 + strlen(event) + 2 + strlen(white) + 1 + 
320       strlen(black) + 11 + strlen(date) + 1;
321     char *ret = (char *) malloc(len);
322     sprintf(ret, "%d. %s, %s-%s, %s, %s",
323             number, event, white, black, PGNResult(gameInfo->result), date);
324     return ret;
325 }
326
327 #define MAX_FIELD_LEN   64  /* To avoid overflowing the buffer */
328
329 char * GameListLine( int number, GameInfo * gameInfo )
330 {
331     char buffer[1024];
332     char * buf = buffer;
333     char * glt = appData.gameListTags;
334     
335     buf += sprintf( buffer, "%d.", number );
336
337     while( *glt != '\0' ) {
338         *buf++ = ' ';
339
340         switch( *glt ) {
341         case GLT_EVENT:
342             strncpy( buf, gameInfo->event ? gameInfo->event : "?", MAX_FIELD_LEN );
343             break;
344         case GLT_SITE:
345             strncpy( buf, gameInfo->site ? gameInfo->site : "?", MAX_FIELD_LEN );
346             break;
347         case GLT_DATE:
348             strncpy( buf, gameInfo->date ? gameInfo->date : "?", MAX_FIELD_LEN );
349             break;
350         case GLT_ROUND:
351             strncpy( buf, gameInfo->round ? gameInfo->round : "?", MAX_FIELD_LEN );
352             break;
353         case GLT_PLAYERS:
354             strncpy( buf, gameInfo->white ? gameInfo->white : "?", MAX_FIELD_LEN );
355             buf[ MAX_FIELD_LEN-1 ] = '\0';
356             buf += strlen( buf );
357             *buf++ = '-';
358             strncpy( buf, gameInfo->black ? gameInfo->black : "?", MAX_FIELD_LEN );
359             break;
360         case GLT_RESULT:
361             strcpy( buf, PGNResult(gameInfo->result) );
362             break;
363         case GLT_WHITE_ELO:
364             if( gameInfo->whiteRating > 0 )
365                 sprintf( buf, "%d", gameInfo->whiteRating );
366             else
367                 strcpy( buf, "?" );
368             break;
369         case GLT_BLACK_ELO:
370             if( gameInfo->blackRating > 0 )
371                 sprintf( buf, "%d", gameInfo->blackRating );
372             else
373                 strcpy( buf, "?" );
374             break;
375         case GLT_TIME_CONTROL:
376             strncpy( buf, gameInfo->timeControl ? gameInfo->timeControl : "?", MAX_FIELD_LEN );
377             break;
378         case GLT_VARIANT:
379             break;
380         case GLT_OUT_OF_BOOK:
381             strncpy( buf, gameInfo->outOfBook ? gameInfo->outOfBook : "?", MAX_FIELD_LEN );
382             break;
383         default:
384             break;
385         }
386
387         buf[MAX_FIELD_LEN-1] = '\0';
388
389         buf += strlen( buf );
390
391         glt++;
392
393         if( *glt != '\0' ) {
394             *buf++ = ',';
395         }
396     }
397
398     *buf = '\0';
399
400     return strdup( buffer );
401 }
402
403 char * GameListLineFull( int number, GameInfo * gameInfo )
404 {
405     char * event = gameInfo->event ? gameInfo->event : "?";
406     char * site = gameInfo->site ? gameInfo->site : "?";
407     char * white = gameInfo->white ? gameInfo->white : "?";
408     char * black = gameInfo->black ? gameInfo->black : "?";
409     char * round = gameInfo->round ? gameInfo->round : "?";
410     char * date = gameInfo->date ? gameInfo->date : "?";
411     char * oob = gameInfo->outOfBook ? gameInfo->outOfBook : "";
412     
413     int len = 64 + strlen(event) + strlen(site) + strlen(white) + strlen(black) + strlen(date) + strlen(oob);
414
415     char *ret = (char *) malloc(len);
416
417     sprintf(ret, "%d, \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"", number, event, site, round, white, black, PGNResult(gameInfo->result), date, oob );
418
419     return ret;
420 }