changes from H.G. Muller; version 4.3.13
[xboard.git] / pgntags.c
1 /*\r
2  * pgntags.c -- Functions to manage PGN tags\r
3  * XBoard $Id: pgntags.c,v 2.1 2003/10/27 19:21:00 mann Exp $\r
4  *\r
5  * Copyright 1995 Free Software Foundation, Inc.\r
6  *\r
7  * ------------------------------------------------------------------------\r
8  * This program is free software; you can redistribute it and/or modify\r
9  * it under the terms of the GNU General Public License as published by\r
10  * the Free Software Foundation; either version 2 of the License, or\r
11  * (at your option) any later version.\r
12  *\r
13  * This program is distributed in the hope that it will be useful,\r
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
16  * GNU General Public License for more details.\r
17  *\r
18  * You should have received a copy of the GNU General Public License\r
19  * along with this program; if not, write to the Free Software\r
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.\r
21  * ------------------------------------------------------------------------\r
22  *\r
23  * This file could well be a part of backend.c, but I prefer it this\r
24  * way.\r
25  */\r
26 \r
27 #include "config.h"\r
28 \r
29 #include <stdio.h>\r
30 #include <errno.h>\r
31 #include <ctype.h>\r
32 #if STDC_HEADERS\r
33 # include <stdlib.h>\r
34 # include <string.h>\r
35 #else /* not STDC_HEADERS */\r
36 # if HAVE_STRING_H\r
37 #  include <string.h>\r
38 # else /* not HAVE_STRING_H */\r
39 #  include <strings.h>\r
40 # endif /* not HAVE_STRING_H */\r
41 #endif /* not STDC_HEADERS */\r
42 \r
43 #include "common.h"\r
44 #include "frontend.h"\r
45 #include "backend.h"\r
46 #include "parser.h"\r
47 \r
48 static char *PGNTagsStatic P((GameInfo *));\r
49 \r
50 \r
51 \r
52 /* Parse PGN tags; returns 0 for success or error number\r
53  */\r
54 int ParsePGNTag(tag, gameInfo)\r
55     char *tag;\r
56     GameInfo *gameInfo;\r
57 {\r
58     char *name, *value, *p, *oldTags;\r
59     int len;\r
60     int success;\r
61 \r
62     name = tag;\r
63     while (!isalpha(*name) && !isdigit(*name)) {\r
64         name++;\r
65     }\r
66     p = name;\r
67     while (*p != ' ' && *p != '\t' && *p != '\n') {\r
68         p++;\r
69     }\r
70     *p = NULLCHAR;\r
71     value = strchr(p + 1, '"') + 1;\r
72     p = strrchr(value, '"');\r
73     *p = NULLCHAR;\r
74 \r
75     if (StrCaseCmp(name, "Event") == 0) {\r
76         success = StrSavePtr(value, &gameInfo->event) != NULL;\r
77     } else if (StrCaseCmp(name, "Site") == 0) {\r
78         success = StrSavePtr(value, &gameInfo->site) != NULL;\r
79     } else if (StrCaseCmp(name, "Date") == 0) {\r
80         success = StrSavePtr(value, &gameInfo->date) != NULL;\r
81     } else if (StrCaseCmp(name, "Round") == 0) {\r
82         success = StrSavePtr(value, &gameInfo->round) != NULL;\r
83     } else if (StrCaseCmp(name, "White") == 0) {\r
84         success = StrSavePtr(value, &gameInfo->white) != NULL;\r
85     } else if (StrCaseCmp(name, "Black") == 0) {\r
86         success = StrSavePtr(value, &gameInfo->black) != NULL;\r
87     }\r
88     /* Fold together the various ways of denoting White/Black rating */\r
89     else if ((StrCaseCmp(name, "WhiteElo")==0) ||\r
90              (StrCaseCmp(name, "WhiteUSCF")==0) ) {\r
91       success = TRUE;\r
92       gameInfo->whiteRating = atoi( value );\r
93     } else if ((StrCaseCmp(name, "BlackElo")==0) ||\r
94                (StrCaseCmp(name, "BlackUSCF")==0)) {\r
95       success = TRUE;\r
96       gameInfo->blackRating = atoi( value );\r
97     }\r
98     else if (StrCaseCmp(name, "Result") == 0) {\r
99         if (strcmp(value, "1-0") == 0)\r
100             gameInfo->result = WhiteWins;\r
101         else if (strcmp(value, "0-1") == 0)\r
102             gameInfo->result = BlackWins;\r
103         else if (strcmp(value, "1/2-1/2") == 0)\r
104             gameInfo->result = GameIsDrawn;\r
105         else\r
106             gameInfo->result = GameUnfinished;\r
107         success = TRUE;\r
108     } else if (StrCaseCmp(name, "FEN") == 0) {\r
109         success = StrSavePtr(value, &gameInfo->fen) != NULL;\r
110     } else if (StrCaseCmp(name, "SetUp") == 0) {\r
111         /* ignore on input; presence of FEN governs */\r
112         success = TRUE;\r
113     } else if (StrCaseCmp(name, "Variant") == 0) {\r
114         /* xboard-defined extension */\r
115         gameInfo->variant = StringToVariant(value);\r
116         success = TRUE;\r
117     } else if (StrCaseCmp(name, PGN_OUT_OF_BOOK) == 0) {\r
118         /* [AS] Out of book annotation */\r
119         success = StrSavePtr(value, &gameInfo->outOfBook) != NULL;\r
120     } else {\r
121         if (gameInfo->extraTags == NULL) {\r
122             oldTags = "";\r
123         } else {\r
124             oldTags = gameInfo->extraTags;\r
125         }\r
126         /* Buffer size includes 7 bytes of space for [ ""]\n\0 */\r
127         len = strlen(oldTags) + strlen(value) + strlen(name) + 7;\r
128         if ((p = (char *) malloc(len))  !=  NULL) {\r
129             sprintf(p, "%s[%s \"%s\"]\n", oldTags, name, value);\r
130             if (gameInfo->extraTags != NULL) free(gameInfo->extraTags);\r
131             gameInfo->extraTags = p;\r
132             success = TRUE;\r
133         } else {\r
134             success = FALSE;\r
135         }\r
136     }\r
137     return(success ? 0 : ENOMEM);\r
138 }\r
139 \r
140 \r
141 \r
142 /* Return a static buffer with a game's data.\r
143  */\r
144 static char *PGNTagsStatic(gameInfo)\r
145     GameInfo *gameInfo;\r
146 {\r
147     static char buf[8192];\r
148     char buf1[MSG_SIZ];\r
149 \r
150     buf[0] = NULLCHAR;\r
151 \r
152     sprintf(buf1, "[Event \"%s\"]\n",\r
153             gameInfo->event ? gameInfo->event : "?");\r
154     strcat(buf, buf1);\r
155     sprintf(buf1, "[Site \"%s\"]\n",\r
156             gameInfo->site ? gameInfo->site : "?");\r
157     strcat(buf, buf1);\r
158     sprintf(buf1, "[Date \"%s\"]\n",\r
159             gameInfo->date ? gameInfo->date : "?");\r
160     strcat(buf, buf1);\r
161     sprintf(buf1, "[Round \"%s\"]\n",\r
162             gameInfo->round ? gameInfo->round : "-");\r
163     strcat(buf, buf1);\r
164     sprintf(buf1, "[White \"%s\"]\n",\r
165             gameInfo->white ? gameInfo->white : "?");\r
166     strcat(buf, buf1);\r
167     sprintf(buf1, "[Black \"%s\"]\n",\r
168             gameInfo->black ? gameInfo->black : "?");\r
169     strcat(buf, buf1);\r
170     sprintf(buf1, "[Result \"%s\"]\n", PGNResult(gameInfo->result));\r
171     strcat(buf, buf1);\r
172  \r
173     if (gameInfo->whiteRating >= 0 ) {\r
174         sprintf(buf1, "[WhiteElo \"%d\"]\n", gameInfo->whiteRating );\r
175         strcat(buf, buf1);\r
176     }\r
177     if ( gameInfo->blackRating >= 0 ) {\r
178         sprintf(buf1, "[BlackElo \"%d\"]\n", gameInfo->blackRating );\r
179         strcat(buf, buf1);\r
180     }    \r
181     if (gameInfo->timeControl != NULL) {\r
182         sprintf(buf1, "[TimeControl \"%s\"]\n", gameInfo->timeControl);\r
183         strcat(buf, buf1);\r
184     }\r
185     if (gameInfo->variant != VariantNormal) {\r
186         sprintf(buf1, "[Variant \"%s\"]\n", VariantName(gameInfo->variant));\r
187         strcat(buf, buf1);\r
188     }\r
189     if (gameInfo->extraTags != NULL) {\r
190         strcat(buf, gameInfo->extraTags);\r
191     }\r
192     return buf;\r
193 }\r
194 \r
195 \r
196  \r
197 /* Print game info\r
198  */\r
199 void PrintPGNTags(fp, gameInfo)\r
200      FILE *fp;\r
201      GameInfo *gameInfo;\r
202 {\r
203     fprintf(fp, "%s", PGNTagsStatic(gameInfo));\r
204 }\r
205 \r
206 \r
207 /* Return a non-static buffer with a games info.\r
208  */\r
209 char *PGNTags(gameInfo)\r
210     GameInfo *gameInfo;\r
211 {\r
212     return StrSave(PGNTagsStatic(gameInfo));\r
213 }\r
214 \r
215 \r
216 /* Returns pointer to a static string with a result.\r
217  */\r
218 char *PGNResult(result)\r
219      ChessMove result;\r
220 {\r
221     switch (result) {\r
222       case GameUnfinished:\r
223       default:\r
224         return "*";\r
225       case WhiteWins:\r
226         return "1-0";\r
227       case BlackWins:\r
228         return "0-1";\r
229       case GameIsDrawn:\r
230         return "1/2-1/2";\r
231     }\r
232 }  \r
233 \r
234 /* Returns 0 for success, nonzero for error */\r
235 int\r
236 ReplaceTags(tags, gameInfo)\r
237      char *tags;\r
238      GameInfo *gameInfo;\r
239 {\r
240     ChessMove moveType;\r
241     int err;\r
242 \r
243     ClearGameInfo(gameInfo);\r
244     yynewstr(tags);\r
245     for (;;) {\r
246         yyboardindex = 0;\r
247         moveType = (ChessMove) yylex();\r
248         if (moveType == (ChessMove) 0) {\r
249             break;\r
250         } else if (moveType == PGNTag) {\r
251             err = ParsePGNTag(yy_text, gameInfo);\r
252             if (err != 0) return err;\r
253         } \r
254     }\r
255     /* just one problem...if there is a result in the new tags,\r
256      * DisplayMove() won't ever show it because ClearGameInfo() set\r
257      * gameInfo->resultDetails to NULL. So we must plug something in if there\r
258      * is a result.\r
259      */\r
260     if (gameInfo->result != GameUnfinished) {\r
261       if (gameInfo->resultDetails) free(gameInfo->resultDetails);\r
262       gameInfo->resultDetails = strdup("");\r
263     }\r
264     return 0;\r
265 }\r