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