9c49e05068782aac0e3d9b58390148ebea5b2d2d
[xboard.git] / backend.c
1 /*\r
2  * backend.c -- Common back end for X and Windows NT versions of\r
3  * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * The following terms apply to Digital Equipment Corporation's copyright\r
11  * interest in XBoard:\r
12  * ------------------------------------------------------------------------\r
13  * All Rights Reserved\r
14  *\r
15  * Permission to use, copy, modify, and distribute this software and its\r
16  * documentation for any purpose and without fee is hereby granted,\r
17  * provided that the above copyright notice appear in all copies and that\r
18  * both that copyright notice and this permission notice appear in\r
19  * supporting documentation, and that the name of Digital not be\r
20  * used in advertising or publicity pertaining to distribution of the\r
21  * software without specific, written prior permission.\r
22  *\r
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
29  * SOFTWARE.\r
30  * ------------------------------------------------------------------------\r
31  *\r
32  * The following terms apply to the enhanced version of XBoard\r
33  * distributed by the Free Software Foundation:\r
34  * ------------------------------------------------------------------------\r
35  *\r
36  * GNU XBoard is free software: you can redistribute it and/or modify\r
37  * it under the terms of the GNU General Public License as published by\r
38  * the Free Software Foundation, either version 3 of the License, or (at\r
39  * your option) any later version.\r
40  *\r
41  * GNU XBoard is distributed in the hope that it will be useful, but\r
42  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
44  * General Public License for more details.\r
45  *\r
46  * You should have received a copy of the GNU General Public License\r
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
48  *\r
49  *------------------------------------------------------------------------\r
50  ** See the file ChangeLog for a revision history.  */\r
51 \r
52 /* [AS] Also useful here for debugging */\r
53 #ifdef WIN32\r
54 #include <windows.h>\r
55 \r
56 #define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );\r
57 \r
58 #else\r
59 \r
60 #define DoSleep( n ) if( (n) >= 0) sleep(n)\r
61 \r
62 #endif\r
63 \r
64 #include "config.h"\r
65 \r
66 #include <assert.h>\r
67 #include <stdio.h>\r
68 #include <ctype.h>\r
69 #include <errno.h>\r
70 #include <sys/types.h>\r
71 #include <sys/stat.h>\r
72 #include <math.h>\r
73 \r
74 #if STDC_HEADERS\r
75 # include <stdlib.h>\r
76 # include <string.h>\r
77 #else /* not STDC_HEADERS */\r
78 # if HAVE_STRING_H\r
79 #  include <string.h>\r
80 # else /* not HAVE_STRING_H */\r
81 #  include <strings.h>\r
82 # endif /* not HAVE_STRING_H */\r
83 #endif /* not STDC_HEADERS */\r
84 \r
85 #if HAVE_SYS_FCNTL_H\r
86 # include <sys/fcntl.h>\r
87 #else /* not HAVE_SYS_FCNTL_H */\r
88 # if HAVE_FCNTL_H\r
89 #  include <fcntl.h>\r
90 # endif /* HAVE_FCNTL_H */\r
91 #endif /* not HAVE_SYS_FCNTL_H */\r
92 \r
93 #if TIME_WITH_SYS_TIME\r
94 # include <sys/time.h>\r
95 # include <time.h>\r
96 #else\r
97 # if HAVE_SYS_TIME_H\r
98 #  include <sys/time.h>\r
99 # else\r
100 #  include <time.h>\r
101 # endif\r
102 #endif\r
103 \r
104 #if defined(_amigados) && !defined(__GNUC__)\r
105 struct timezone {\r
106     int tz_minuteswest;\r
107     int tz_dsttime;\r
108 };\r
109 extern int gettimeofday(struct timeval *, struct timezone *);\r
110 #endif\r
111 \r
112 #if HAVE_UNISTD_H\r
113 # include <unistd.h>\r
114 #endif\r
115 \r
116 #include "common.h"\r
117 #include "frontend.h"\r
118 #include "backend.h"\r
119 #include "parser.h"\r
120 #include "moves.h"\r
121 #if ZIPPY\r
122 # include "zippy.h"\r
123 #endif\r
124 #include "backendz.h"\r
125 #include "gettext.h" \r
126  \r
127 #ifdef ENABLE_NLS \r
128 # define _(s) gettext (s) \r
129 # define N_(s) gettext_noop (s) \r
130 #else \r
131 # define _(s) (s) \r
132 # define N_(s) s \r
133 #endif \r
134 \r
135 \r
136 /* A point in time */\r
137 typedef struct {\r
138     long sec;  /* Assuming this is >= 32 bits */\r
139     int ms;    /* Assuming this is >= 16 bits */\r
140 } TimeMark;\r
141 \r
142 int establish P((void));\r
143 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,\r
144                          char *buf, int count, int error));\r
145 void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,\r
146                       char *buf, int count, int error));\r
147 void SendToICS P((char *s));\r
148 void SendToICSDelayed P((char *s, long msdelay));\r
149 void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,\r
150                       int toX, int toY));\r
151 void InitPosition P((int redraw));\r
152 void HandleMachineMove P((char *message, ChessProgramState *cps));\r
153 int AutoPlayOneMove P((void));\r
154 int LoadGameOneMove P((ChessMove readAhead));\r
155 int LoadGameFromFile P((char *filename, int n, char *title, int useList));\r
156 int LoadPositionFromFile P((char *filename, int n, char *title));\r
157 int SavePositionToFile P((char *filename));\r
158 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,\r
159                   Board board));\r
160 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));\r
161 void ShowMove P((int fromX, int fromY, int toX, int toY));\r
162 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
163                    /*char*/int promoChar));\r
164 void BackwardInner P((int target));\r
165 void ForwardInner P((int target));\r
166 void GameEnds P((ChessMove result, char *resultDetails, int whosays));\r
167 void EditPositionDone P((void));\r
168 void PrintOpponents P((FILE *fp));\r
169 void PrintPosition P((FILE *fp, int move));\r
170 void StartChessProgram P((ChessProgramState *cps));\r
171 void SendToProgram P((char *message, ChessProgramState *cps));\r
172 void SendMoveToProgram P((int moveNum, ChessProgramState *cps));\r
173 void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,\r
174                            char *buf, int count, int error));\r
175 void SendTimeControl P((ChessProgramState *cps,\r
176                         int mps, long tc, int inc, int sd, int st));\r
177 char *TimeControlTagValue P((void));\r
178 void Attention P((ChessProgramState *cps));\r
179 void FeedMovesToProgram P((ChessProgramState *cps, int upto));\r
180 void ResurrectChessProgram P((void));\r
181 void DisplayComment P((int moveNumber, char *text));\r
182 void DisplayMove P((int moveNumber));\r
183 void DisplayAnalysis P((void));\r
184 \r
185 void ParseGameHistory P((char *game));\r
186 void ParseBoard12 P((char *string));\r
187 void StartClocks P((void));\r
188 void SwitchClocks P((void));\r
189 void StopClocks P((void));\r
190 void ResetClocks P((void));\r
191 char *PGNDate P((void));\r
192 void SetGameInfo P((void));\r
193 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
194 int RegisterMove P((void));\r
195 void MakeRegisteredMove P((void));\r
196 void TruncateGame P((void));\r
197 int looking_at P((char *, int *, char *));\r
198 void CopyPlayerNameIntoFileName P((char **, char *));\r
199 char *SavePart P((char *));\r
200 int SaveGameOldStyle P((FILE *));\r
201 int SaveGamePGN P((FILE *));\r
202 void GetTimeMark P((TimeMark *));\r
203 long SubtractTimeMarks P((TimeMark *, TimeMark *));\r
204 int CheckFlags P((void));\r
205 long NextTickLength P((long));\r
206 void CheckTimeControl P((void));\r
207 void show_bytes P((FILE *, char *, int));\r
208 int string_to_rating P((char *str));\r
209 void ParseFeatures P((char* args, ChessProgramState *cps));\r
210 void InitBackEnd3 P((void));\r
211 void FeatureDone P((ChessProgramState* cps, int val));\r
212 void InitChessProgram P((ChessProgramState *cps, int setup));\r
213 \r
214 #ifdef WIN32\r
215        extern void ConsoleCreate();\r
216 #endif\r
217 \r
218 ChessProgramState *WhitePlayer();\r
219 void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c\r
220 int VerifyDisplayMode P(());\r
221 \r
222 char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment\r
223 void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c\r
224 char *ProbeBook P((int moveNr, char *book)); // [HGM] book: returns a book move\r
225 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book\r
226 extern char installDir[MSG_SIZ];\r
227 \r
228 extern int tinyLayout, smallLayout;\r
229 ChessProgramStats programStats;\r
230 static int exiting = 0; /* [HGM] moved to top */\r
231 static int setboardSpoiledMachineBlack = 0, errorExitFlag = 0;\r
232 extern int startedFromPositionFile;\r
233 int startedFromPositionFile = FALSE; Board filePosition;       /* [HGM] loadPos */\r
234 char endingGame = 0;    /* [HGM] crash: flag to prevent recursion of GameEnds() */\r
235 int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS     */\r
236 VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */\r
237 int lastIndex = 0;      /* [HGM] autoinc: last game/position used in match mode */\r
238 int opponentKibitzes;\r
239 \r
240 /* States for ics_getting_history */\r
241 #define H_FALSE 0\r
242 #define H_REQUESTED 1\r
243 #define H_GOT_REQ_HEADER 2\r
244 #define H_GOT_UNREQ_HEADER 3\r
245 #define H_GETTING_MOVES 4\r
246 #define H_GOT_UNWANTED_HEADER 5\r
247 \r
248 /* whosays values for GameEnds */\r
249 #define GE_ICS 0\r
250 #define GE_ENGINE 1\r
251 #define GE_PLAYER 2\r
252 #define GE_FILE 3\r
253 #define GE_XBOARD 4\r
254 #define GE_ENGINE1 5\r
255 #define GE_ENGINE2 6\r
256 \r
257 /* Maximum number of games in a cmail message */\r
258 #define CMAIL_MAX_GAMES 20\r
259 \r
260 /* Different types of move when calling RegisterMove */\r
261 #define CMAIL_MOVE   0\r
262 #define CMAIL_RESIGN 1\r
263 #define CMAIL_DRAW   2\r
264 #define CMAIL_ACCEPT 3\r
265 \r
266 /* Different types of result to remember for each game */\r
267 #define CMAIL_NOT_RESULT 0\r
268 #define CMAIL_OLD_RESULT 1\r
269 #define CMAIL_NEW_RESULT 2\r
270 \r
271 /* Telnet protocol constants */\r
272 #define TN_WILL 0373\r
273 #define TN_WONT 0374\r
274 #define TN_DO   0375\r
275 #define TN_DONT 0376\r
276 #define TN_IAC  0377\r
277 #define TN_ECHO 0001\r
278 #define TN_SGA  0003\r
279 #define TN_PORT 23\r
280 \r
281 /* [AS] */\r
282 static char * safeStrCpy( char * dst, const char * src, size_t count )\r
283 {\r
284     assert( dst != NULL );\r
285     assert( src != NULL );\r
286     assert( count > 0 );\r
287 \r
288     strncpy( dst, src, count );\r
289     dst[ count-1 ] = '\0';\r
290     return dst;\r
291 }\r
292 \r
293 static char * safeStrCat( char * dst, const char * src, size_t count )\r
294 {\r
295     size_t  dst_len;\r
296 \r
297     assert( dst != NULL );\r
298     assert( src != NULL );\r
299     assert( count > 0 );\r
300 \r
301     dst_len = strlen(dst);\r
302 \r
303     assert( count > dst_len ); /* Buffer size must be greater than current length */\r
304 \r
305     safeStrCpy( dst + dst_len, src, count - dst_len );\r
306 \r
307     return dst;\r
308 }\r
309 \r
310 /* Some compiler can't cast u64 to double\r
311  * This function do the job for us:\r
312 \r
313  * We use the highest bit for cast, this only\r
314  * works if the highest bit is not\r
315  * in use (This should not happen)\r
316  *\r
317  * We used this for all compiler\r
318  */\r
319 double\r
320 u64ToDouble(u64 value)\r
321 {\r
322   double r;\r
323   u64 tmp = value & u64Const(0x7fffffffffffffff);\r
324   r = (double)(s64)tmp;\r
325   if (value & u64Const(0x8000000000000000))\r
326        r +=  9.2233720368547758080e18; /* 2^63 */\r
327  return r;\r
328 }\r
329 \r
330 /* Fake up flags for now, as we aren't keeping track of castling\r
331    availability yet. [HGM] Change of logic: the flag now only\r
332    indicates the type of castlings allowed by the rule of the game.\r
333    The actual rights themselves are maintained in the array\r
334    castlingRights, as part of the game history, and are not probed\r
335    by this function.\r
336  */\r
337 int\r
338 PosFlags(index)\r
339 {\r
340   int flags = F_ALL_CASTLE_OK;\r
341   if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;\r
342   switch (gameInfo.variant) {\r
343   case VariantSuicide:\r
344     flags &= ~F_ALL_CASTLE_OK;\r
345   case VariantGiveaway:         // [HGM] moved this case label one down: seems Giveaway does have castling on ICC!\r
346     flags |= F_IGNORE_CHECK;\r
347     break;\r
348   case VariantAtomic:\r
349     flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;\r
350     break;\r
351   case VariantKriegspiel:\r
352     flags |= F_KRIEGSPIEL_CAPTURE;\r
353     break;\r
354   case VariantCapaRandom: \r
355   case VariantFischeRandom:\r
356     flags |= F_FRC_TYPE_CASTLING; /* [HGM] enable this through flag */\r
357   case VariantNoCastle:\r
358   case VariantShatranj:\r
359   case VariantCourier:\r
360     flags &= ~F_ALL_CASTLE_OK;\r
361     break;\r
362   default:\r
363     break;\r
364   }\r
365   return flags;\r
366 }\r
367 \r
368 FILE *gameFileFP, *debugFP;\r
369 \r
370 /* \r
371     [AS] Note: sometimes, the sscanf() function is used to parse the input\r
372     into a fixed-size buffer. Because of this, we must be prepared to\r
373     receive strings as long as the size of the input buffer, which is currently\r
374     set to 4K for Windows and 8K for the rest.\r
375     So, we must either allocate sufficiently large buffers here, or\r
376     reduce the size of the input buffer in the input reading part.\r
377 */\r
378 \r
379 char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];\r
380 char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];\r
381 char thinkOutput1[MSG_SIZ*10];\r
382 \r
383 ChessProgramState first, second;\r
384 \r
385 /* premove variables */\r
386 int premoveToX = 0;\r
387 int premoveToY = 0;\r
388 int premoveFromX = 0;\r
389 int premoveFromY = 0;\r
390 int premovePromoChar = 0;\r
391 int gotPremove = 0;\r
392 Boolean alarmSounded;\r
393 /* end premove variables */\r
394 \r
395 char *ics_prefix = "$";\r
396 int ics_type = ICS_GENERIC;\r
397 \r
398 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;\r
399 int pauseExamForwardMostMove = 0;\r
400 int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;\r
401 int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];\r
402 int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;\r
403 int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;\r
404 int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;\r
405 int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;\r
406 int whiteFlag = FALSE, blackFlag = FALSE;\r
407 int userOfferedDraw = FALSE;\r
408 int ics_user_moved = 0, ics_gamenum = -1, ics_getting_history = H_FALSE;\r
409 int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;\r
410 int cmailMoveType[CMAIL_MAX_GAMES];\r
411 long ics_clock_paused = 0;\r
412 ProcRef icsPR = NoProc, cmailPR = NoProc;\r
413 InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;\r
414 GameMode gameMode = BeginningOfGame;\r
415 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];\r
416 char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];\r
417 ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */\r
418 int hiddenThinkOutputState = 0; /* [AS] */\r
419 int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */\r
420 int adjudicateLossPlies = 6;\r
421 char white_holding[64], black_holding[64];\r
422 TimeMark lastNodeCountTime;\r
423 long lastNodeCount=0;\r
424 int have_sent_ICS_logon = 0;\r
425 int movesPerSession;\r
426 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;\r
427 long timeControl_2; /* [AS] Allow separate time controls */\r
428 char *fullTimeControlString = NULL; /* [HGM] secondary TC: merge of MPS, TC and inc */\r
429 long timeRemaining[2][MAX_MOVES];\r
430 int matchGame = 0;\r
431 TimeMark programStartTime;\r
432 char ics_handle[MSG_SIZ];\r
433 int have_set_title = 0;\r
434 \r
435 /* animateTraining preserves the state of appData.animate\r
436  * when Training mode is activated. This allows the\r
437  * response to be animated when appData.animate == TRUE and\r
438  * appData.animateDragging == TRUE.\r
439  */\r
440 Boolean animateTraining;\r
441 \r
442 GameInfo gameInfo;\r
443 \r
444 AppData appData;\r
445 \r
446 Board boards[MAX_MOVES];\r
447 /* [HGM] Following 7 needed for accurate legality tests: */\r
448 char  epStatus[MAX_MOVES];\r
449 char  castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1\r
450 char  castlingRank[BOARD_SIZE]; // and corresponding ranks\r
451 char  initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE], fileRights[BOARD_SIZE];\r
452 int   nrCastlingRights; // For TwoKings, or to implement castling-unknown status\r
453 int   initialRulePlies, FENrulePlies;\r
454 char  FENepStatus;\r
455 FILE  *serverMoves = NULL; // next two for broadcasting (/serverMoves option)\r
456 int loadFlag = 0; \r
457 int shuffleOpenings;\r
458 \r
459 ChessSquare  FIDEArray[2][BOARD_SIZE] = {\r
460     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,\r
461         WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },\r
462     { BlackRook, BlackKnight, BlackBishop, BlackQueen,\r
463         BlackKing, BlackBishop, BlackKnight, BlackRook }\r
464 };\r
465 \r
466 ChessSquare twoKingsArray[2][BOARD_SIZE] = {\r
467     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,\r
468         WhiteKing, WhiteKing, WhiteKnight, WhiteRook },\r
469     { BlackRook, BlackKnight, BlackBishop, BlackQueen,\r
470         BlackKing, BlackKing, BlackKnight, BlackRook }\r
471 };\r
472 \r
473 ChessSquare  KnightmateArray[2][BOARD_SIZE] = {\r
474     { WhiteRook, WhiteMan, WhiteBishop, WhiteQueen,\r
475         WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook },\r
476     { BlackRook, BlackMan, BlackBishop, BlackQueen,\r
477         BlackUnicorn, BlackBishop, BlackMan, BlackRook }\r
478 };\r
479 \r
480 ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */\r
481     { WhiteCannon, WhiteNightrider, WhiteAlfil, WhiteQueen,\r
482         WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },\r
483     { BlackCannon, BlackNightrider, BlackAlfil, BlackQueen,\r
484         BlackKing, BlackBishop, BlackKnight, BlackRook }\r
485 };\r
486 \r
487 ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */\r
488     { WhiteRook, WhiteKnight, WhiteAlfil, WhiteKing,\r
489         WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },\r
490     { BlackRook, BlackKnight, BlackAlfil, BlackKing,\r
491         BlackFerz, BlackAlfil, BlackKnight, BlackRook }\r
492 };\r
493 \r
494 \r
495 #if (BOARD_SIZE>=10)\r
496 ChessSquare ShogiArray[2][BOARD_SIZE] = {\r
497     { WhiteQueen, WhiteKnight, WhiteFerz, WhiteWazir,\r
498         WhiteKing, WhiteWazir, WhiteFerz, WhiteKnight, WhiteQueen },\r
499     { BlackQueen, BlackKnight, BlackFerz, BlackWazir,\r
500         BlackKing, BlackWazir, BlackFerz, BlackKnight, BlackQueen }\r
501 };\r
502 \r
503 ChessSquare XiangqiArray[2][BOARD_SIZE] = {\r
504     { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz,\r
505         WhiteWazir, WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },\r
506     { BlackRook, BlackKnight, BlackAlfil, BlackFerz,\r
507         BlackWazir, BlackFerz, BlackAlfil, BlackKnight, BlackRook }\r
508 };\r
509 \r
510 ChessSquare CapablancaArray[2][BOARD_SIZE] = {\r
511     { WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen, \r
512         WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },\r
513     { BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen, \r
514         BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }\r
515 };\r
516 \r
517 ChessSquare GreatArray[2][BOARD_SIZE] = {\r
518     { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing, \r
519         WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },\r
520     { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing, \r
521         BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },\r
522 };\r
523 \r
524 ChessSquare JanusArray[2][BOARD_SIZE] = {\r
525     { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing, \r
526         WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },\r
527     { BlackRook, BlackAngel, BlackKnight, BlackBishop, BlackKing, \r
528         BlackQueen, BlackBishop, BlackKnight, BlackAngel, BlackRook }\r
529 };\r
530 \r
531 #ifdef GOTHIC\r
532 ChessSquare GothicArray[2][BOARD_SIZE] = {\r
533     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall, \r
534         WhiteKing, WhiteAngel, WhiteBishop, WhiteKnight, WhiteRook },\r
535     { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall, \r
536         BlackKing, BlackAngel, BlackBishop, BlackKnight, BlackRook }\r
537 };\r
538 #else // !GOTHIC\r
539 #define GothicArray CapablancaArray\r
540 #endif // !GOTHIC\r
541 \r
542 #ifdef FALCON\r
543 ChessSquare FalconArray[2][BOARD_SIZE] = {\r
544     { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen, \r
545         WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },\r
546     { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen, \r
547         BlackKing, BlackLance, BlackBishop, BlackKnight, BlackRook }\r
548 };\r
549 #else // !FALCON\r
550 #define FalconArray CapablancaArray\r
551 #endif // !FALCON\r
552 \r
553 #else // !(BOARD_SIZE>=10)\r
554 #define XiangqiPosition FIDEArray\r
555 #define CapablancaArray FIDEArray\r
556 #define GothicArray FIDEArray\r
557 #define GreatArray FIDEArray\r
558 #endif // !(BOARD_SIZE>=10)\r
559 \r
560 #if (BOARD_SIZE>=12)\r
561 ChessSquare CourierArray[2][BOARD_SIZE] = {\r
562     { WhiteRook, WhiteKnight, WhiteAlfil, WhiteBishop, WhiteMan, WhiteKing,\r
563         WhiteFerz, WhiteWazir, WhiteBishop, WhiteAlfil, WhiteKnight, WhiteRook },\r
564     { BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing,\r
565         BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook }\r
566 };\r
567 #else // !(BOARD_SIZE>=12)\r
568 #define CourierArray CapablancaArray\r
569 #endif // !(BOARD_SIZE>=12)\r
570 \r
571 \r
572 Board initialPosition;\r
573 \r
574 \r
575 /* Convert str to a rating. Checks for special cases of "----",\r
576 \r
577    "++++", etc. Also strips ()'s */\r
578 int\r
579 string_to_rating(str)\r
580   char *str;\r
581 {\r
582   while(*str && !isdigit(*str)) ++str;\r
583   if (!*str)\r
584     return 0;   /* One of the special "no rating" cases */\r
585   else\r
586     return atoi(str);\r
587 }\r
588 \r
589 void\r
590 ClearProgramStats()\r
591 {\r
592     /* Init programStats */\r
593     programStats.movelist[0] = 0;\r
594     programStats.depth = 0;\r
595     programStats.nr_moves = 0;\r
596     programStats.moves_left = 0;\r
597     programStats.nodes = 0;\r
598     programStats.time = -1;        // [HGM] PGNtime: make invalid to recognize engine output\r
599     programStats.score = 0;\r
600     programStats.got_only_move = 0;\r
601     programStats.got_fail = 0;\r
602     programStats.line_is_book = 0;\r
603 }\r
604 \r
605 void\r
606 InitBackEnd1()\r
607 {\r
608     int matched, min, sec;\r
609 \r
610     ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options\r
611 \r
612     GetTimeMark(&programStartTime);\r
613 \r
614     ClearProgramStats();\r
615     programStats.ok_to_send = 1;\r
616     programStats.seen_stat = 0;\r
617 \r
618     /*\r
619      * Initialize game list\r
620      */\r
621     ListNew(&gameList);\r
622 \r
623 \r
624     /*\r
625      * Internet chess server status\r
626      */\r
627     if (appData.icsActive) {\r
628         appData.matchMode = FALSE;\r
629         appData.matchGames = 0;\r
630 #if ZIPPY       \r
631         appData.noChessProgram = !appData.zippyPlay;\r
632 #else\r
633         appData.zippyPlay = FALSE;\r
634         appData.zippyTalk = FALSE;\r
635         appData.noChessProgram = TRUE;\r
636 #endif\r
637         if (*appData.icsHelper != NULLCHAR) {\r
638             appData.useTelnet = TRUE;\r
639             appData.telnetProgram = appData.icsHelper;\r
640         }\r
641     } else {\r
642         appData.zippyTalk = appData.zippyPlay = FALSE;\r
643     }\r
644 \r
645     /* [AS] Initialize pv info list [HGM] and game state */\r
646     {\r
647         int i, j;\r
648 \r
649         for( i=0; i<MAX_MOVES; i++ ) {\r
650             pvInfoList[i].depth = -1;\r
651             epStatus[i]=EP_NONE;\r
652             for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;\r
653         }\r
654     }\r
655 \r
656     /*\r
657      * Parse timeControl resource\r
658      */\r
659     if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,\r
660                           appData.movesPerSession)) {\r
661         char buf[MSG_SIZ];\r
662         sprintf(buf, _("bad timeControl option %s"), appData.timeControl);\r
663         DisplayFatalError(buf, 0, 2);\r
664     }\r
665 \r
666     /*\r
667      * Parse searchTime resource\r
668      */\r
669     if (*appData.searchTime != NULLCHAR) {\r
670         matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);\r
671         if (matched == 1) {\r
672             searchTime = min * 60;\r
673         } else if (matched == 2) {\r
674             searchTime = min * 60 + sec;\r
675         } else {\r
676             char buf[MSG_SIZ];\r
677             sprintf(buf, _("bad searchTime option %s"), appData.searchTime);\r
678             DisplayFatalError(buf, 0, 2);\r
679         }\r
680     }\r
681 \r
682     /* [AS] Adjudication threshold */\r
683     adjudicateLossThreshold = appData.adjudicateLossThreshold;\r
684     \r
685     first.which = "first";\r
686     second.which = "second";\r
687     first.maybeThinking = second.maybeThinking = FALSE;\r
688     first.pr = second.pr = NoProc;\r
689     first.isr = second.isr = NULL;\r
690     first.sendTime = second.sendTime = 2;\r
691     first.sendDrawOffers = 1;\r
692     if (appData.firstPlaysBlack) {\r
693         first.twoMachinesColor = "black\n";\r
694         second.twoMachinesColor = "white\n";\r
695     } else {\r
696         first.twoMachinesColor = "white\n";\r
697         second.twoMachinesColor = "black\n";\r
698     }\r
699     first.program = appData.firstChessProgram;\r
700     second.program = appData.secondChessProgram;\r
701     first.host = appData.firstHost;\r
702     second.host = appData.secondHost;\r
703     first.dir = appData.firstDirectory;\r
704     second.dir = appData.secondDirectory;\r
705     first.other = &second;\r
706     second.other = &first;\r
707     first.initString = appData.initString;\r
708     second.initString = appData.secondInitString;\r
709     first.computerString = appData.firstComputerString;\r
710     second.computerString = appData.secondComputerString;\r
711     first.useSigint = second.useSigint = TRUE;\r
712     first.useSigterm = second.useSigterm = TRUE;\r
713     first.reuse = appData.reuseFirst;\r
714     second.reuse = appData.reuseSecond;\r
715     first.nps = appData.firstNPS;   // [HGM] nps: copy nodes per second\r
716     second.nps = appData.secondNPS;\r
717     first.useSetboard = second.useSetboard = FALSE;\r
718     first.useSAN = second.useSAN = FALSE;\r
719     first.usePing = second.usePing = FALSE;\r
720     first.lastPing = second.lastPing = 0;\r
721     first.lastPong = second.lastPong = 0;\r
722     first.usePlayother = second.usePlayother = FALSE;\r
723     first.useColors = second.useColors = TRUE;\r
724     first.useUsermove = second.useUsermove = FALSE;\r
725     first.sendICS = second.sendICS = FALSE;\r
726     first.sendName = second.sendName = appData.icsActive;\r
727     first.sdKludge = second.sdKludge = FALSE;\r
728     first.stKludge = second.stKludge = FALSE;\r
729     TidyProgramName(first.program, first.host, first.tidy);\r
730     TidyProgramName(second.program, second.host, second.tidy);\r
731     first.matchWins = second.matchWins = 0;\r
732     strcpy(first.variants, appData.variant);\r
733     strcpy(second.variants, appData.variant);\r
734     first.analysisSupport = second.analysisSupport = 2; /* detect */\r
735     first.analyzing = second.analyzing = FALSE;\r
736     first.initDone = second.initDone = FALSE;\r
737 \r
738     /* New features added by Tord: */\r
739     first.useFEN960 = FALSE; second.useFEN960 = FALSE;\r
740     first.useOOCastle = TRUE; second.useOOCastle = TRUE;\r
741     /* End of new features added by Tord. */\r
742 \r
743     /* [HGM] time odds: set factor for each machine */\r
744     first.timeOdds  = appData.firstTimeOdds;\r
745     second.timeOdds = appData.secondTimeOdds;\r
746     { int norm = 1;\r
747         if(appData.timeOddsMode) {\r
748             norm = first.timeOdds;\r
749             if(norm > second.timeOdds) norm = second.timeOdds;\r
750         }\r
751         first.timeOdds /= norm;\r
752         second.timeOdds /= norm;\r
753     }\r
754 \r
755     /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/\r
756     first.accumulateTC = appData.firstAccumulateTC;\r
757     second.accumulateTC = appData.secondAccumulateTC;\r
758     first.maxNrOfSessions = second.maxNrOfSessions = 1;\r
759 \r
760     /* [HGM] debug */\r
761     first.debug = second.debug = FALSE;\r
762     first.supportsNPS = second.supportsNPS = UNKNOWN;\r
763 \r
764     /* [HGM] options */\r
765     first.optionSettings  = appData.firstOptions;\r
766     second.optionSettings = appData.secondOptions;\r
767 \r
768     first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */\r
769     second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */\r
770     first.isUCI = appData.firstIsUCI; /* [AS] */\r
771     second.isUCI = appData.secondIsUCI; /* [AS] */\r
772     first.hasOwnBookUCI = appData.firstHasOwnBookUCI; /* [AS] */\r
773     second.hasOwnBookUCI = appData.secondHasOwnBookUCI; /* [AS] */\r
774 \r
775     if (appData.firstProtocolVersion > PROTOVER ||\r
776         appData.firstProtocolVersion < 1) {\r
777       char buf[MSG_SIZ];\r
778       sprintf(buf, _("protocol version %d not supported"),\r
779               appData.firstProtocolVersion);\r
780       DisplayFatalError(buf, 0, 2);\r
781     } else {\r
782       first.protocolVersion = appData.firstProtocolVersion;\r
783     }\r
784 \r
785     if (appData.secondProtocolVersion > PROTOVER ||\r
786         appData.secondProtocolVersion < 1) {\r
787       char buf[MSG_SIZ];\r
788       sprintf(buf, _("protocol version %d not supported"),\r
789               appData.secondProtocolVersion);\r
790       DisplayFatalError(buf, 0, 2);\r
791     } else {\r
792       second.protocolVersion = appData.secondProtocolVersion;\r
793     }\r
794 \r
795     if (appData.icsActive) {\r
796         appData.clockMode = TRUE;  /* changes dynamically in ICS mode */\r
797     } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {\r
798         appData.clockMode = FALSE;\r
799         first.sendTime = second.sendTime = 0;\r
800     }\r
801     \r
802 #if ZIPPY\r
803     /* Override some settings from environment variables, for backward\r
804        compatibility.  Unfortunately it's not feasible to have the env\r
805        vars just set defaults, at least in xboard.  Ugh.\r
806     */\r
807     if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {\r
808       ZippyInit();\r
809     }\r
810 #endif\r
811     \r
812     if (appData.noChessProgram) {\r
813         programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)\r
814                                         + strlen(PATCHLEVEL));\r
815         sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);\r
816     } else {\r
817 #if 0\r
818         char *p, *q;\r
819         q = first.program;\r
820         while (*q != ' ' && *q != NULLCHAR) q++;\r
821         p = q;\r
822         while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] bckslash added */\r
823         programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)\r
824                                         + strlen(PATCHLEVEL) + (q - p));\r
825         sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);\r
826         strncat(programVersion, p, q - p);\r
827 #else\r
828         /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */\r
829         programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)\r
830                                         + strlen(PATCHLEVEL) + strlen(first.tidy));\r
831         sprintf(programVersion, "%s %s.%s + %s", PRODUCT, VERSION, PATCHLEVEL, first.tidy);\r
832 #endif\r
833     }\r
834 \r
835     if (!appData.icsActive) {\r
836       char buf[MSG_SIZ];\r
837       /* Check for variants that are supported only in ICS mode,\r
838          or not at all.  Some that are accepted here nevertheless\r
839          have bugs; see comments below.\r
840       */\r
841       VariantClass variant = StringToVariant(appData.variant);\r
842       switch (variant) {\r
843       case VariantBughouse:     /* need four players and two boards */\r
844       case VariantKriegspiel:   /* need to hide pieces and move details */\r
845       /* case VariantFischeRandom: (Fabien: moved below) */\r
846         sprintf(buf, _("Variant %s supported only in ICS mode"), appData.variant);\r
847         DisplayFatalError(buf, 0, 2);\r
848         return;\r
849 \r
850       case VariantUnknown:\r
851       case VariantLoadable:\r
852       case Variant29:\r
853       case Variant30:\r
854       case Variant31:\r
855       case Variant32:\r
856       case Variant33:\r
857       case Variant34:\r
858       case Variant35:\r
859       case Variant36:\r
860       default:\r
861         sprintf(buf, _("Unknown variant name %s"), appData.variant);\r
862         DisplayFatalError(buf, 0, 2);\r
863         return;\r
864 \r
865       case VariantXiangqi:    /* [HGM] repetition rules not implemented */\r
866       case VariantFairy:      /* [HGM] TestLegality definitely off! */\r
867       case VariantGothic:     /* [HGM] should work */\r
868       case VariantCapablanca: /* [HGM] should work */\r
869       case VariantCourier:    /* [HGM] initial forced moves not implemented */\r
870       case VariantShogi:      /* [HGM] drops not tested for legality */\r
871       case VariantKnightmate: /* [HGM] should work */\r
872       case VariantCylinder:   /* [HGM] untested */\r
873       case VariantFalcon:     /* [HGM] untested */\r
874       case VariantCrazyhouse: /* holdings not shown, ([HGM] fixed that!)\r
875                                  offboard interposition not understood */\r
876       case VariantNormal:     /* definitely works! */\r
877       case VariantWildCastle: /* pieces not automatically shuffled */\r
878       case VariantNoCastle:   /* pieces not automatically shuffled */\r
879       case VariantFischeRandom: /* [HGM] works and shuffles pieces */\r
880       case VariantLosers:     /* should work except for win condition,\r
881                                  and doesn't know captures are mandatory */\r
882       case VariantSuicide:    /* should work except for win condition,\r
883                                  and doesn't know captures are mandatory */\r
884       case VariantGiveaway:   /* should work except for win condition,\r
885                                  and doesn't know captures are mandatory */\r
886       case VariantTwoKings:   /* should work */\r
887       case VariantAtomic:     /* should work except for win condition */\r
888       case Variant3Check:     /* should work except for win condition */\r
889       case VariantShatranj:   /* should work except for all win conditions */\r
890       case VariantBerolina:   /* might work if TestLegality is off */\r
891       case VariantCapaRandom: /* should work */\r
892       case VariantJanus:      /* should work */\r
893       case VariantSuper:      /* experimental */\r
894       case VariantGreat:      /* experimental, requires legality testing to be off */\r
895         break;\r
896       }\r
897     }\r
898 \r
899     InitEngineUCI( installDir, &first );  // [HGM] moved here from winboard.c, to make available in xboard\r
900     InitEngineUCI( installDir, &second );\r
901 }\r
902 \r
903 int NextIntegerFromString( char ** str, long * value )\r
904 {\r
905     int result = -1;\r
906     char * s = *str;\r
907 \r
908     while( *s == ' ' || *s == '\t' ) {\r
909         s++;\r
910     }\r
911 \r
912     *value = 0;\r
913 \r
914     if( *s >= '0' && *s <= '9' ) {\r
915         while( *s >= '0' && *s <= '9' ) {\r
916             *value = *value * 10 + (*s - '0');\r
917             s++;\r
918         }\r
919 \r
920         result = 0;\r
921     }\r
922 \r
923     *str = s;\r
924 \r
925     return result;\r
926 }\r
927 \r
928 int NextTimeControlFromString( char ** str, long * value )\r
929 {\r
930     long temp;\r
931     int result = NextIntegerFromString( str, &temp );\r
932 \r
933     if( result == 0 ) {\r
934         *value = temp * 60; /* Minutes */\r
935         if( **str == ':' ) {\r
936             (*str)++;\r
937             result = NextIntegerFromString( str, &temp );\r
938             *value += temp; /* Seconds */\r
939         }\r
940     }\r
941 \r
942     return result;\r
943 }\r
944 \r
945 int NextSessionFromString( char ** str, int *moves, long * tc, long *inc)\r
946 {   /* [HGM] routine added to read '+moves/time' for secondary time control */\r
947     int result = -1; long temp, temp2;\r
948 \r
949     if(**str != '+') return -1; // old params remain in force!\r
950     (*str)++;\r
951     if( NextTimeControlFromString( str, &temp ) ) return -1;\r
952 \r
953     if(**str != '/') {\r
954         /* time only: incremental or sudden-death time control */\r
955         if(**str == '+') { /* increment follows; read it */\r
956             (*str)++;\r
957             if(result = NextIntegerFromString( str, &temp2)) return -1;\r
958             *inc = temp2 * 1000;\r
959         } else *inc = 0;\r
960         *moves = 0; *tc = temp * 1000; \r
961         return 0;\r
962     } else if(temp % 60 != 0) return -1;     /* moves was given as min:sec */\r
963 \r
964     (*str)++; /* classical time control */\r
965     result = NextTimeControlFromString( str, &temp2);\r
966     if(result == 0) {\r
967         *moves = temp/60;\r
968         *tc    = temp2 * 1000;\r
969         *inc   = 0;\r
970     }\r
971     return result;\r
972 }\r
973 \r
974 int GetTimeQuota(int movenr)\r
975 {   /* [HGM] get time to add from the multi-session time-control string */\r
976     int moves=1; /* kludge to force reading of first session */\r
977     long time, increment;\r
978     char *s = fullTimeControlString;\r
979 \r
980     if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", fullTimeControlString);\r
981     do {\r
982         if(moves) NextSessionFromString(&s, &moves, &time, &increment);\r
983         if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment);\r
984         if(movenr == -1) return time;    /* last move before new session     */\r
985         if(!moves) return increment;     /* current session is incremental   */\r
986         if(movenr >= 0) movenr -= moves; /* we already finished this session */\r
987     } while(movenr >= -1);               /* try again for next session       */\r
988 \r
989     return 0; // no new time quota on this move\r
990 }\r
991 \r
992 int\r
993 ParseTimeControl(tc, ti, mps)\r
994      char *tc;\r
995      int ti;\r
996      int mps;\r
997 {\r
998 #if 0\r
999     int matched, min, sec;\r
1000 \r
1001     matched = sscanf(tc, "%d:%d", &min, &sec);\r
1002     if (matched == 1) {\r
1003         timeControl = min * 60 * 1000;\r
1004     } else if (matched == 2) {\r
1005         timeControl = (min * 60 + sec) * 1000;\r
1006     } else {\r
1007         return FALSE;\r
1008     }\r
1009 #else\r
1010     long tc1;\r
1011     long tc2;\r
1012     char buf[MSG_SIZ];\r
1013 \r
1014     if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;\r
1015     if(ti > 0) {\r
1016         if(mps)\r
1017              sprintf(buf, "+%d/%s+%d", mps, tc, ti);\r
1018         else sprintf(buf, "+%s+%d", tc, ti);\r
1019     } else {\r
1020         if(mps)\r
1021              sprintf(buf, "+%d/%s", mps, tc);\r
1022         else sprintf(buf, "+%s", tc);\r
1023     }\r
1024     fullTimeControlString = StrSave(buf);\r
1025 \r
1026     if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {\r
1027         return FALSE;\r
1028     }\r
1029 \r
1030     if( *tc == '/' ) {\r
1031         /* Parse second time control */\r
1032         tc++;\r
1033 \r
1034         if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {\r
1035             return FALSE;\r
1036         }\r
1037 \r
1038         if( tc2 == 0 ) {\r
1039             return FALSE;\r
1040         }\r
1041 \r
1042         timeControl_2 = tc2 * 1000;\r
1043     }\r
1044     else {\r
1045         timeControl_2 = 0;\r
1046     }\r
1047 \r
1048     if( tc1 == 0 ) {\r
1049         return FALSE;\r
1050     }\r
1051 \r
1052     timeControl = tc1 * 1000;\r
1053 #endif\r
1054 \r
1055     if (ti >= 0) {\r
1056         timeIncrement = ti * 1000;  /* convert to ms */\r
1057         movesPerSession = 0;\r
1058     } else {\r
1059         timeIncrement = 0;\r
1060         movesPerSession = mps;\r
1061     }\r
1062     return TRUE;\r
1063 }\r
1064 \r
1065 void\r
1066 InitBackEnd2()\r
1067 {\r
1068     if (appData.debugMode) {\r
1069         fprintf(debugFP, "%s\n", programVersion);\r
1070     }\r
1071 \r
1072     if (appData.matchGames > 0) {\r
1073         appData.matchMode = TRUE;\r
1074     } else if (appData.matchMode) {\r
1075         appData.matchGames = 1;\r
1076     }\r
1077     if(appData.matchMode && appData.sameColorGames > 0) /* [HGM] alternate: overrule matchGames */\r
1078         appData.matchGames = appData.sameColorGames;\r
1079     if(appData.rewindIndex > 1) { /* [HGM] autoinc: rewind implies auto-increment and overrules given index */\r
1080         if(appData.loadPositionIndex >= 0) appData.loadPositionIndex = -1;\r
1081         if(appData.loadGameIndex >= 0) appData.loadGameIndex = -1;\r
1082     }\r
1083     Reset(TRUE, FALSE);\r
1084     if (appData.noChessProgram || first.protocolVersion == 1) {\r
1085       InitBackEnd3();\r
1086     } else {\r
1087       /* kludge: allow timeout for initial "feature" commands */\r
1088       FreezeUI();\r
1089       DisplayMessage("", _("Starting chess program"));\r
1090       ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);\r
1091     }\r
1092 }\r
1093 \r
1094 void\r
1095 InitBackEnd3 P((void))\r
1096 {\r
1097     GameMode initialMode;\r
1098     char buf[MSG_SIZ];\r
1099     int err;\r
1100 \r
1101     InitChessProgram(&first, startedFromSetupPosition);\r
1102 \r
1103 \r
1104     if (appData.icsActive) {\r
1105 #ifdef WIN32\r
1106         /* [DM] Make a console window if needed [HGM] merged ifs */\r
1107         ConsoleCreate(); \r
1108 #endif\r
1109         err = establish();\r
1110         if (err != 0) {\r
1111             if (*appData.icsCommPort != NULLCHAR) {\r
1112                 sprintf(buf, _("Could not open comm port %s"),  \r
1113                         appData.icsCommPort);\r
1114             } else {\r
1115                 sprintf(buf, _("Could not connect to host %s, port %s"),  \r
1116                         appData.icsHost, appData.icsPort);\r
1117             }\r
1118             DisplayFatalError(buf, err, 1);\r
1119             return;\r
1120         }\r
1121         SetICSMode();\r
1122         telnetISR =\r
1123           AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);\r
1124         fromUserISR =\r
1125           AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);\r
1126     } else if (appData.noChessProgram) {\r
1127         SetNCPMode();\r
1128     } else {\r
1129         SetGNUMode();\r
1130     }\r
1131 \r
1132     if (*appData.cmailGameName != NULLCHAR) {\r
1133         SetCmailMode();\r
1134         OpenLoopback(&cmailPR);\r
1135         cmailISR =\r
1136           AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);\r
1137     }\r
1138     \r
1139     ThawUI();\r
1140     DisplayMessage("", "");\r
1141     if (StrCaseCmp(appData.initialMode, "") == 0) {\r
1142       initialMode = BeginningOfGame;\r
1143     } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {\r
1144       initialMode = TwoMachinesPlay;\r
1145     } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {\r
1146       initialMode = AnalyzeFile; \r
1147     } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {\r
1148       initialMode = AnalyzeMode;\r
1149     } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {\r
1150       initialMode = MachinePlaysWhite;\r
1151     } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {\r
1152       initialMode = MachinePlaysBlack;\r
1153     } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {\r
1154       initialMode = EditGame;\r
1155     } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {\r
1156       initialMode = EditPosition;\r
1157     } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {\r
1158       initialMode = Training;\r
1159     } else {\r
1160       sprintf(buf, _("Unknown initialMode %s"), appData.initialMode);\r
1161       DisplayFatalError(buf, 0, 2);\r
1162       return;\r
1163     }\r
1164 \r
1165     if (appData.matchMode) {\r
1166         /* Set up machine vs. machine match */\r
1167         if (appData.noChessProgram) {\r
1168             DisplayFatalError(_("Can't have a match with no chess programs"),\r
1169                               0, 2);\r
1170             return;\r
1171         }\r
1172         matchMode = TRUE;\r
1173         matchGame = 1;\r
1174         if (*appData.loadGameFile != NULLCHAR) {\r
1175             int index = appData.loadGameIndex; // [HGM] autoinc\r
1176             if(index<0) lastIndex = index = 1;\r
1177             if (!LoadGameFromFile(appData.loadGameFile,\r
1178                                   index,\r
1179                                   appData.loadGameFile, FALSE)) {\r
1180                 DisplayFatalError(_("Bad game file"), 0, 1);\r
1181                 return;\r
1182             }\r
1183         } else if (*appData.loadPositionFile != NULLCHAR) {\r
1184             int index = appData.loadPositionIndex; // [HGM] autoinc\r
1185             if(index<0) lastIndex = index = 1;\r
1186             if (!LoadPositionFromFile(appData.loadPositionFile,\r
1187                                       index,\r
1188                                       appData.loadPositionFile)) {\r
1189                 DisplayFatalError(_("Bad position file"), 0, 1);\r
1190                 return;\r
1191             }\r
1192         }\r
1193         TwoMachinesEvent();\r
1194     } else if (*appData.cmailGameName != NULLCHAR) {\r
1195         /* Set up cmail mode */\r
1196         ReloadCmailMsgEvent(TRUE);\r
1197     } else {\r
1198         /* Set up other modes */\r
1199         if (initialMode == AnalyzeFile) {\r
1200           if (*appData.loadGameFile == NULLCHAR) {\r
1201             DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1);\r
1202             return;\r
1203           }\r
1204         }\r
1205         if (*appData.loadGameFile != NULLCHAR) {\r
1206             (void) LoadGameFromFile(appData.loadGameFile,\r
1207                                     appData.loadGameIndex,\r
1208                                     appData.loadGameFile, TRUE);\r
1209         } else if (*appData.loadPositionFile != NULLCHAR) {\r
1210             (void) LoadPositionFromFile(appData.loadPositionFile,\r
1211                                         appData.loadPositionIndex,\r
1212                                         appData.loadPositionFile);\r
1213             /* [HGM] try to make self-starting even after FEN load */\r
1214             /* to allow automatic setup of fairy variants with wtm */\r
1215             if(initialMode == BeginningOfGame && !blackPlaysFirst) {\r
1216                 gameMode = BeginningOfGame;\r
1217                 setboardSpoiledMachineBlack = 1;\r
1218             }\r
1219             /* [HGM] loadPos: make that every new game uses the setup */\r
1220             /* from file as long as we do not switch variant          */\r
1221             if(!blackPlaysFirst) { int i;\r
1222                 startedFromPositionFile = TRUE;\r
1223                 CopyBoard(filePosition, boards[0]);\r
1224                 for(i=0; i<BOARD_SIZE; i++) fileRights[i] = castlingRights[0][i];\r
1225             }\r
1226         }\r
1227         if (initialMode == AnalyzeMode) {\r
1228           if (appData.noChessProgram) {\r
1229             DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2);\r
1230             return;\r
1231           }\r
1232           if (appData.icsActive) {\r
1233             DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2);\r
1234             return;\r
1235           }\r
1236           AnalyzeModeEvent();\r
1237         } else if (initialMode == AnalyzeFile) {\r
1238           appData.showThinking = TRUE; // [HGM] thinking: moved out of ShowThinkingEvent\r
1239           ShowThinkingEvent();\r
1240           AnalyzeFileEvent();\r
1241           AnalysisPeriodicEvent(1);\r
1242         } else if (initialMode == MachinePlaysWhite) {\r
1243           if (appData.noChessProgram) {\r
1244             DisplayFatalError(_("MachineWhite mode requires a chess engine"),\r
1245                               0, 2);\r
1246             return;\r
1247           }\r
1248           if (appData.icsActive) {\r
1249             DisplayFatalError(_("MachineWhite mode does not work with ICS mode"),\r
1250                               0, 2);\r
1251             return;\r
1252           }\r
1253           MachineWhiteEvent();\r
1254         } else if (initialMode == MachinePlaysBlack) {\r
1255           if (appData.noChessProgram) {\r
1256             DisplayFatalError(_("MachineBlack mode requires a chess engine"),\r
1257                               0, 2);\r
1258             return;\r
1259           }\r
1260           if (appData.icsActive) {\r
1261             DisplayFatalError(_("MachineBlack mode does not work with ICS mode"),\r
1262                               0, 2);\r
1263             return;\r
1264           }\r
1265           MachineBlackEvent();\r
1266         } else if (initialMode == TwoMachinesPlay) {\r
1267           if (appData.noChessProgram) {\r
1268             DisplayFatalError(_("TwoMachines mode requires a chess engine"),\r
1269                               0, 2);\r
1270             return;\r
1271           }\r
1272           if (appData.icsActive) {\r
1273             DisplayFatalError(_("TwoMachines mode does not work with ICS mode"),\r
1274                               0, 2);\r
1275             return;\r
1276           }\r
1277           TwoMachinesEvent();\r
1278         } else if (initialMode == EditGame) {\r
1279           EditGameEvent();\r
1280         } else if (initialMode == EditPosition) {\r
1281           EditPositionEvent();\r
1282         } else if (initialMode == Training) {\r
1283           if (*appData.loadGameFile == NULLCHAR) {\r
1284             DisplayFatalError(_("Training mode requires a game file"), 0, 2);\r
1285             return;\r
1286           }\r
1287           TrainingEvent();\r
1288         }\r
1289     }\r
1290 }\r
1291 \r
1292 /*\r
1293  * Establish will establish a contact to a remote host.port.\r
1294  * Sets icsPR to a ProcRef for a process (or pseudo-process)\r
1295  *  used to talk to the host.\r
1296  * Returns 0 if okay, error code if not.\r
1297  */\r
1298 int\r
1299 establish()\r
1300 {\r
1301     char buf[MSG_SIZ];\r
1302 \r
1303     if (*appData.icsCommPort != NULLCHAR) {\r
1304         /* Talk to the host through a serial comm port */\r
1305         return OpenCommPort(appData.icsCommPort, &icsPR);\r
1306 \r
1307     } else if (*appData.gateway != NULLCHAR) {\r
1308         if (*appData.remoteShell == NULLCHAR) {\r
1309             /* Use the rcmd protocol to run telnet program on a gateway host */\r
1310             sprintf(buf, "%s %s %s",\r
1311                     appData.telnetProgram, appData.icsHost, appData.icsPort);\r
1312             return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);\r
1313 \r
1314         } else {\r
1315             /* Use the rsh program to run telnet program on a gateway host */\r
1316             if (*appData.remoteUser == NULLCHAR) {\r
1317                 sprintf(buf, "%s %s %s %s %s", appData.remoteShell,\r
1318                         appData.gateway, appData.telnetProgram,\r
1319                         appData.icsHost, appData.icsPort);\r
1320             } else {\r
1321                 sprintf(buf, "%s %s -l %s %s %s %s",\r
1322                         appData.remoteShell, appData.gateway, \r
1323                         appData.remoteUser, appData.telnetProgram,\r
1324                         appData.icsHost, appData.icsPort);\r
1325             }\r
1326             return StartChildProcess(buf, "", &icsPR);\r
1327 \r
1328         }\r
1329     } else if (appData.useTelnet) {\r
1330         return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);\r
1331 \r
1332     } else {\r
1333         /* TCP socket interface differs somewhat between\r
1334            Unix and NT; handle details in the front end.\r
1335            */\r
1336         return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);\r
1337     }\r
1338 }\r
1339 \r
1340 void\r
1341 show_bytes(fp, buf, count)\r
1342      FILE *fp;\r
1343      char *buf;\r
1344      int count;\r
1345 {\r
1346     while (count--) {\r
1347         if (*buf < 040 || *(unsigned char *) buf > 0177) {\r
1348             fprintf(fp, "\\%03o", *buf & 0xff);\r
1349         } else {\r
1350             putc(*buf, fp);\r
1351         }\r
1352         buf++;\r
1353     }\r
1354     fflush(fp);\r
1355 }\r
1356 \r
1357 /* Returns an errno value */\r
1358 int\r
1359 OutputMaybeTelnet(pr, message, count, outError)\r
1360      ProcRef pr;\r
1361      char *message;\r
1362      int count;\r
1363      int *outError;\r
1364 {\r
1365     char buf[8192], *p, *q, *buflim;\r
1366     int left, newcount, outcount;\r
1367 \r
1368     if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||\r
1369         *appData.gateway != NULLCHAR) {\r
1370         if (appData.debugMode) {\r
1371             fprintf(debugFP, ">ICS: ");\r
1372             show_bytes(debugFP, message, count);\r
1373             fprintf(debugFP, "\n");\r
1374         }\r
1375         return OutputToProcess(pr, message, count, outError);\r
1376     }\r
1377 \r
1378     buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */\r
1379     p = message;\r
1380     q = buf;\r
1381     left = count;\r
1382     newcount = 0;\r
1383     while (left) {\r
1384         if (q >= buflim) {\r
1385             if (appData.debugMode) {\r
1386                 fprintf(debugFP, ">ICS: ");\r
1387                 show_bytes(debugFP, buf, newcount);\r
1388                 fprintf(debugFP, "\n");\r
1389             }\r
1390             outcount = OutputToProcess(pr, buf, newcount, outError);\r
1391             if (outcount < newcount) return -1; /* to be sure */\r
1392             q = buf;\r
1393             newcount = 0;\r
1394         }\r
1395         if (*p == '\n') {\r
1396             *q++ = '\r';\r
1397             newcount++;\r
1398         } else if (((unsigned char) *p) == TN_IAC) {\r
1399             *q++ = (char) TN_IAC;\r
1400             newcount ++;\r
1401         }\r
1402         *q++ = *p++;\r
1403         newcount++;\r
1404         left--;\r
1405     }\r
1406     if (appData.debugMode) {\r
1407         fprintf(debugFP, ">ICS: ");\r
1408         show_bytes(debugFP, buf, newcount);\r
1409         fprintf(debugFP, "\n");\r
1410     }\r
1411     outcount = OutputToProcess(pr, buf, newcount, outError);\r
1412     if (outcount < newcount) return -1; /* to be sure */\r
1413     return count;\r
1414 }\r
1415 \r
1416 void\r
1417 read_from_player(isr, closure, message, count, error)\r
1418      InputSourceRef isr;\r
1419      VOIDSTAR closure;\r
1420      char *message;\r
1421      int count;\r
1422      int error;\r
1423 {\r
1424     int outError, outCount;\r
1425     static int gotEof = 0;\r
1426 \r
1427     /* Pass data read from player on to ICS */\r
1428     if (count > 0) {\r
1429         gotEof = 0;\r
1430         outCount = OutputMaybeTelnet(icsPR, message, count, &outError);\r
1431         if (outCount < count) {\r
1432             DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
1433         }\r
1434     } else if (count < 0) {\r
1435         RemoveInputSource(isr);\r
1436         DisplayFatalError(_("Error reading from keyboard"), error, 1);\r
1437     } else if (gotEof++ > 0) {\r
1438         RemoveInputSource(isr);\r
1439         DisplayFatalError(_("Got end of file from keyboard"), 0, 0);\r
1440     }\r
1441 }\r
1442 \r
1443 void\r
1444 SendToICS(s)\r
1445      char *s;\r
1446 {\r
1447     int count, outCount, outError;\r
1448 \r
1449     if (icsPR == NULL) return;\r
1450 \r
1451     count = strlen(s);\r
1452     outCount = OutputMaybeTelnet(icsPR, s, count, &outError);\r
1453     if (outCount < count) {\r
1454         DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
1455     }\r
1456 }\r
1457 \r
1458 /* This is used for sending logon scripts to the ICS. Sending\r
1459    without a delay causes problems when using timestamp on ICC\r
1460    (at least on my machine). */\r
1461 void\r
1462 SendToICSDelayed(s,msdelay)\r
1463      char *s;\r
1464      long msdelay;\r
1465 {\r
1466     int count, outCount, outError;\r
1467 \r
1468     if (icsPR == NULL) return;\r
1469 \r
1470     count = strlen(s);\r
1471     if (appData.debugMode) {\r
1472         fprintf(debugFP, ">ICS: ");\r
1473         show_bytes(debugFP, s, count);\r
1474         fprintf(debugFP, "\n");\r
1475     }\r
1476     outCount = OutputToProcessDelayed(icsPR, s, count, &outError,\r
1477                                       msdelay);\r
1478     if (outCount < count) {\r
1479         DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
1480     }\r
1481 }\r
1482 \r
1483 \r
1484 /* Remove all highlighting escape sequences in s\r
1485    Also deletes any suffix starting with '(' \r
1486    */\r
1487 char *\r
1488 StripHighlightAndTitle(s)\r
1489      char *s;\r
1490 {\r
1491     static char retbuf[MSG_SIZ];\r
1492     char *p = retbuf;\r
1493 \r
1494     while (*s != NULLCHAR) {\r
1495         while (*s == '\033') {\r
1496             while (*s != NULLCHAR && !isalpha(*s)) s++;\r
1497             if (*s != NULLCHAR) s++;\r
1498         }\r
1499         while (*s != NULLCHAR && *s != '\033') {\r
1500             if (*s == '(' || *s == '[') {\r
1501                 *p = NULLCHAR;\r
1502                 return retbuf;\r
1503             }\r
1504             *p++ = *s++;\r
1505         }\r
1506     }\r
1507     *p = NULLCHAR;\r
1508     return retbuf;\r
1509 }\r
1510 \r
1511 /* Remove all highlighting escape sequences in s */\r
1512 char *\r
1513 StripHighlight(s)\r
1514      char *s;\r
1515 {\r
1516     static char retbuf[MSG_SIZ];\r
1517     char *p = retbuf;\r
1518 \r
1519     while (*s != NULLCHAR) {\r
1520         while (*s == '\033') {\r
1521             while (*s != NULLCHAR && !isalpha(*s)) s++;\r
1522             if (*s != NULLCHAR) s++;\r
1523         }\r
1524         while (*s != NULLCHAR && *s != '\033') {\r
1525             *p++ = *s++;\r
1526         }\r
1527     }\r
1528     *p = NULLCHAR;\r
1529     return retbuf;\r
1530 }\r
1531 \r
1532 char *variantNames[] = VARIANT_NAMES;\r
1533 char *\r
1534 VariantName(v)\r
1535      VariantClass v;\r
1536 {\r
1537     return variantNames[v];\r
1538 }\r
1539 \r
1540 \r
1541 /* Identify a variant from the strings the chess servers use or the\r
1542    PGN Variant tag names we use. */\r
1543 VariantClass\r
1544 StringToVariant(e)\r
1545      char *e;\r
1546 {\r
1547     char *p;\r
1548     int wnum = -1;\r
1549     VariantClass v = VariantNormal;\r
1550     int i, found = FALSE;\r
1551     char buf[MSG_SIZ];\r
1552 \r
1553     if (!e) return v;\r
1554 \r
1555     /* [HGM] skip over optional board-size prefixes */\r
1556     if( sscanf(e, "%dx%d_", &i, &i) == 2 ||\r
1557         sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) {\r
1558         while( *e++ != '_');\r
1559     }\r
1560 \r
1561     for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {\r
1562       if (StrCaseStr(e, variantNames[i])) {\r
1563         v = (VariantClass) i;\r
1564         found = TRUE;\r
1565         break;\r
1566       }\r
1567     }\r
1568 \r
1569     if (!found) {\r
1570       if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))\r
1571           || StrCaseStr(e, "wild/fr") \r
1572           || StrCaseStr(e, "frc") || StrCaseStr(e, "960")) {\r
1573         v = VariantFischeRandom;\r
1574       } else if ((i = 4, p = StrCaseStr(e, "wild")) ||\r
1575                  (i = 1, p = StrCaseStr(e, "w"))) {\r
1576         p += i;\r
1577         while (*p && (isspace(*p) || *p == '(' || *p == '/')) p++;\r
1578         if (isdigit(*p)) {\r
1579           wnum = atoi(p);\r
1580         } else {\r
1581           wnum = -1;\r
1582         }\r
1583         switch (wnum) {\r
1584         case 0: /* FICS only, actually */\r
1585         case 1:\r
1586           /* Castling legal even if K starts on d-file */\r
1587           v = VariantWildCastle;\r
1588           break;\r
1589         case 2:\r
1590         case 3:\r
1591         case 4:\r
1592           /* Castling illegal even if K & R happen to start in\r
1593              normal positions. */\r
1594           v = VariantNoCastle;\r
1595           break;\r
1596         case 5:\r
1597         case 7:\r
1598         case 8:\r
1599         case 10:\r
1600         case 11:\r
1601         case 12:\r
1602         case 13:\r
1603         case 14:\r
1604         case 15:\r
1605         case 18:\r
1606         case 19:\r
1607           /* Castling legal iff K & R start in normal positions */\r
1608           v = VariantNormal;\r
1609           break;\r
1610         case 6:\r
1611         case 20:\r
1612         case 21:\r
1613           /* Special wilds for position setup; unclear what to do here */\r
1614           v = VariantLoadable;\r
1615           break;\r
1616         case 9:\r
1617           /* Bizarre ICC game */\r
1618           v = VariantTwoKings;\r
1619           break;\r
1620         case 16:\r
1621           v = VariantKriegspiel;\r
1622           break;\r
1623         case 17:\r
1624           v = VariantLosers;\r
1625           break;\r
1626         case 22:\r
1627           v = VariantFischeRandom;\r
1628           break;\r
1629         case 23:\r
1630           v = VariantCrazyhouse;\r
1631           break;\r
1632         case 24:\r
1633           v = VariantBughouse;\r
1634           break;\r
1635         case 25:\r
1636           v = Variant3Check;\r
1637           break;\r
1638         case 26:\r
1639           /* Not quite the same as FICS suicide! */\r
1640           v = VariantGiveaway;\r
1641           break;\r
1642         case 27:\r
1643           v = VariantAtomic;\r
1644           break;\r
1645         case 28:\r
1646           v = VariantShatranj;\r
1647           break;\r
1648 \r
1649         /* Temporary names for future ICC types.  The name *will* change in \r
1650            the next xboard/WinBoard release after ICC defines it. */\r
1651         case 29:\r
1652           v = Variant29;\r
1653           break;\r
1654         case 30:\r
1655           v = Variant30;\r
1656           break;\r
1657         case 31:\r
1658           v = Variant31;\r
1659           break;\r
1660         case 32:\r
1661           v = Variant32;\r
1662           break;\r
1663         case 33:\r
1664           v = Variant33;\r
1665           break;\r
1666         case 34:\r
1667           v = Variant34;\r
1668           break;\r
1669         case 35:\r
1670           v = Variant35;\r
1671           break;\r
1672         case 36:\r
1673           v = Variant36;\r
1674           break;\r
1675         case 37:\r
1676           v = VariantShogi;\r
1677           break;\r
1678         case 38:\r
1679           v = VariantXiangqi;\r
1680           break;\r
1681         case 39:\r
1682           v = VariantCourier;\r
1683           break;\r
1684         case 40:\r
1685           v = VariantGothic;\r
1686           break;\r
1687         case 41:\r
1688           v = VariantCapablanca;\r
1689           break;\r
1690         case 42:\r
1691           v = VariantKnightmate;\r
1692           break;\r
1693         case 43:\r
1694           v = VariantFairy;\r
1695           break;\r
1696         case 44:\r
1697           v = VariantCylinder;\r
1698           break;\r
1699         case 45:\r
1700           v = VariantFalcon;\r
1701           break;\r
1702         case 46:\r
1703           v = VariantCapaRandom;\r
1704           break;\r
1705         case 47:\r
1706           v = VariantBerolina;\r
1707           break;\r
1708         case 48:\r
1709           v = VariantJanus;\r
1710           break;\r
1711         case 49:\r
1712           v = VariantSuper;\r
1713           break;\r
1714         case 50:\r
1715           v = VariantGreat;\r
1716           break;\r
1717         case -1:\r
1718           /* Found "wild" or "w" in the string but no number;\r
1719              must assume it's normal chess. */\r
1720           v = VariantNormal;\r
1721           break;\r
1722         default:\r
1723           sprintf(buf, _("Unknown wild type %d"), wnum);\r
1724           DisplayError(buf, 0);\r
1725           v = VariantUnknown;\r
1726           break;\r
1727         }\r
1728       }\r
1729     }\r
1730     if (appData.debugMode) {\r
1731       fprintf(debugFP, _("recognized '%s' (%d) as variant %s\n"),\r
1732               e, wnum, VariantName(v));\r
1733     }\r
1734     return v;\r
1735 }\r
1736 \r
1737 static int leftover_start = 0, leftover_len = 0;\r
1738 char star_match[STAR_MATCH_N][MSG_SIZ];\r
1739 \r
1740 /* Test whether pattern is present at &buf[*index]; if so, return TRUE,\r
1741    advance *index beyond it, and set leftover_start to the new value of\r
1742    *index; else return FALSE.  If pattern contains the character '*', it\r
1743    matches any sequence of characters not containing '\r', '\n', or the\r
1744    character following the '*' (if any), and the matched sequence(s) are\r
1745    copied into star_match.\r
1746    */\r
1747 int\r
1748 looking_at(buf, index, pattern)\r
1749      char *buf;\r
1750      int *index;\r
1751      char *pattern;\r
1752 {\r
1753     char *bufp = &buf[*index], *patternp = pattern;\r
1754     int star_count = 0;\r
1755     char *matchp = star_match[0];\r
1756     \r
1757     for (;;) {\r
1758         if (*patternp == NULLCHAR) {\r
1759             *index = leftover_start = bufp - buf;\r
1760             *matchp = NULLCHAR;\r
1761             return TRUE;\r
1762         }\r
1763         if (*bufp == NULLCHAR) return FALSE;\r
1764         if (*patternp == '*') {\r
1765             if (*bufp == *(patternp + 1)) {\r
1766                 *matchp = NULLCHAR;\r
1767                 matchp = star_match[++star_count];\r
1768                 patternp += 2;\r
1769                 bufp++;\r
1770                 continue;\r
1771             } else if (*bufp == '\n' || *bufp == '\r') {\r
1772                 patternp++;\r
1773                 if (*patternp == NULLCHAR)\r
1774                   continue;\r
1775                 else\r
1776                   return FALSE;\r
1777             } else {\r
1778                 *matchp++ = *bufp++;\r
1779                 continue;\r
1780             }\r
1781         }\r
1782         if (*patternp != *bufp) return FALSE;\r
1783         patternp++;\r
1784         bufp++;\r
1785     }\r
1786 }\r
1787 \r
1788 void\r
1789 SendToPlayer(data, length)\r
1790      char *data;\r
1791      int length;\r
1792 {\r
1793     int error, outCount;\r
1794     outCount = OutputToProcess(NoProc, data, length, &error);\r
1795     if (outCount < length) {\r
1796         DisplayFatalError(_("Error writing to display"), error, 1);\r
1797     }\r
1798 }\r
1799 \r
1800 void\r
1801 PackHolding(packed, holding)\r
1802      char packed[];\r
1803      char *holding;\r
1804 {\r
1805     char *p = holding;\r
1806     char *q = packed;\r
1807     int runlength = 0;\r
1808     int curr = 9999;\r
1809     do {\r
1810         if (*p == curr) {\r
1811             runlength++;\r
1812         } else {\r
1813             switch (runlength) {\r
1814               case 0:\r
1815                 break;\r
1816               case 1:\r
1817                 *q++ = curr;\r
1818                 break;\r
1819               case 2:\r
1820                 *q++ = curr;\r
1821                 *q++ = curr;\r
1822                 break;\r
1823               default:\r
1824                 sprintf(q, "%d", runlength);\r
1825                 while (*q) q++;\r
1826                 *q++ = curr;\r
1827                 break;\r
1828             }\r
1829             runlength = 1;\r
1830             curr = *p;\r
1831         }\r
1832     } while (*p++);\r
1833     *q = NULLCHAR;\r
1834 }\r
1835 \r
1836 /* Telnet protocol requests from the front end */\r
1837 void\r
1838 TelnetRequest(ddww, option)\r
1839      unsigned char ddww, option;\r
1840 {\r
1841     unsigned char msg[3];\r
1842     int outCount, outError;\r
1843 \r
1844     if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;\r
1845 \r
1846     if (appData.debugMode) {\r
1847         char buf1[8], buf2[8], *ddwwStr, *optionStr;\r
1848         switch (ddww) {\r
1849           case TN_DO:\r
1850             ddwwStr = "DO";\r
1851             break;\r
1852           case TN_DONT:\r
1853             ddwwStr = "DONT";\r
1854             break;\r
1855           case TN_WILL:\r
1856             ddwwStr = "WILL";\r
1857             break;\r
1858           case TN_WONT:\r
1859             ddwwStr = "WONT";\r
1860             break;\r
1861           default:\r
1862             ddwwStr = buf1;\r
1863             sprintf(buf1, "%d", ddww);\r
1864             break;\r
1865         }\r
1866         switch (option) {\r
1867           case TN_ECHO:\r
1868             optionStr = "ECHO";\r
1869             break;\r
1870           default:\r
1871             optionStr = buf2;\r
1872             sprintf(buf2, "%d", option);\r
1873             break;\r
1874         }\r
1875         fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);\r
1876     }\r
1877     msg[0] = TN_IAC;\r
1878     msg[1] = ddww;\r
1879     msg[2] = option;\r
1880     outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);\r
1881     if (outCount < 3) {\r
1882         DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
1883     }\r
1884 }\r
1885 \r
1886 void\r
1887 DoEcho()\r
1888 {\r
1889     if (!appData.icsActive) return;\r
1890     TelnetRequest(TN_DO, TN_ECHO);\r
1891 }\r
1892 \r
1893 void\r
1894 DontEcho()\r
1895 {\r
1896     if (!appData.icsActive) return;\r
1897     TelnetRequest(TN_DONT, TN_ECHO);\r
1898 }\r
1899 \r
1900 void\r
1901 CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)\r
1902 {\r
1903     /* put the holdings sent to us by the server on the board holdings area */\r
1904     int i, j, holdingsColumn, holdingsStartRow, direction, countsColumn;\r
1905     char p;\r
1906     ChessSquare piece;\r
1907 \r
1908     if(gameInfo.holdingsWidth < 2)  return;\r
1909 \r
1910     if( (int)lowestPiece >= BlackPawn ) {\r
1911         holdingsColumn = 0;\r
1912         countsColumn = 1;\r
1913         holdingsStartRow = BOARD_HEIGHT-1;\r
1914         direction = -1;\r
1915     } else {\r
1916         holdingsColumn = BOARD_WIDTH-1;\r
1917         countsColumn = BOARD_WIDTH-2;\r
1918         holdingsStartRow = 0;\r
1919         direction = 1;\r
1920     }\r
1921 \r
1922     for(i=0; i<BOARD_HEIGHT; i++) { /* clear holdings */\r
1923         board[i][holdingsColumn] = EmptySquare;\r
1924         board[i][countsColumn]   = (ChessSquare) 0;\r
1925     }\r
1926     while( (p=*holdings++) != NULLCHAR ) {\r
1927         piece = CharToPiece( ToUpper(p) );\r
1928         if(piece == EmptySquare) continue;\r
1929         /*j = (int) piece - (int) WhitePawn;*/\r
1930         j = PieceToNumber(piece);\r
1931         if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */\r
1932         if(j < 0) continue;               /* should not happen */\r
1933         piece = (ChessSquare) ( (int)piece + (int)lowestPiece );\r
1934         board[holdingsStartRow+j*direction][holdingsColumn] = piece;\r
1935         board[holdingsStartRow+j*direction][countsColumn]++;\r
1936     }\r
1937 \r
1938 }\r
1939 \r
1940 \r
1941 void\r
1942 VariantSwitch(Board board, VariantClass newVariant)\r
1943 {\r
1944    int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;\r
1945    int oldCurrentMove = currentMove, oldForwardMostMove = forwardMostMove, oldBackwardMostMove = backwardMostMove;\r
1946    Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;\r
1947 \r
1948    startedFromPositionFile = FALSE;\r
1949    if(gameInfo.variant == newVariant) return;\r
1950 \r
1951    /* [HGM] This routine is called each time an assignment is made to\r
1952     * gameInfo.variant during a game, to make sure the board sizes\r
1953     * are set to match the new variant. If that means adding or deleting\r
1954     * holdings, we shift the playing board accordingly\r
1955     * This kludge is needed because in ICS observe mode, we get boards\r
1956     * of an ongoing game without knowing the variant, and learn about the\r
1957     * latter only later. This can be because of the move list we requested,\r
1958     * in which case the game history is refilled from the beginning anyway,\r
1959     * but also when receiving holdings of a crazyhouse game. In the latter\r
1960     * case we want to add those holdings to the already received position.\r
1961     */\r
1962 \r
1963 \r
1964   if (appData.debugMode) {\r
1965     fprintf(debugFP, "Switch board from %s to %s\n",\r
1966                VariantName(gameInfo.variant), VariantName(newVariant));\r
1967     setbuf(debugFP, NULL);\r
1968   }\r
1969     shuffleOpenings = 0;       /* [HGM] shuffle */\r
1970     gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */\r
1971     switch(newVariant) {\r
1972             case VariantShogi:\r
1973               newWidth = 9;  newHeight = 9;\r
1974               gameInfo.holdingsSize = 7;\r
1975             case VariantBughouse:\r
1976             case VariantCrazyhouse:\r
1977               newHoldingsWidth = 2; break;\r
1978             default:\r
1979               newHoldingsWidth = gameInfo.holdingsSize = 0;\r
1980     }\r
1981 \r
1982     if(newWidth  != gameInfo.boardWidth  ||\r
1983        newHeight != gameInfo.boardHeight ||\r
1984        newHoldingsWidth != gameInfo.holdingsWidth ) {\r
1985 \r
1986         /* shift position to new playing area, if needed */\r
1987         if(newHoldingsWidth > gameInfo.holdingsWidth) {\r
1988            for(i=0; i<BOARD_HEIGHT; i++) \r
1989                for(j=BOARD_RGHT-1; j>=BOARD_LEFT; j--)\r
1990                    board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =\r
1991                                                      board[i][j];\r
1992            for(i=0; i<newHeight; i++) {\r
1993                board[i][0] = board[i][newWidth+2*newHoldingsWidth-1] = EmptySquare;\r
1994                board[i][1] = board[i][newWidth+2*newHoldingsWidth-2] = (ChessSquare) 0;\r
1995            }\r
1996         } else if(newHoldingsWidth < gameInfo.holdingsWidth) {\r
1997            for(i=0; i<BOARD_HEIGHT; i++)\r
1998                for(j=BOARD_LEFT; j<BOARD_RGHT; j++)\r
1999                    board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =\r
2000                                                  board[i][j];\r
2001         }\r
2002 \r
2003         gameInfo.boardWidth  = newWidth;\r
2004         gameInfo.boardHeight = newHeight;\r
2005         gameInfo.holdingsWidth = newHoldingsWidth;\r
2006         gameInfo.variant = newVariant;\r
2007         InitDrawingSizes(-2, 0);\r
2008 \r
2009         /* [HGM] The following should definitely be solved in a better way */\r
2010 #if 0\r
2011         CopyBoard(board, tempBoard); /* save position in case it is board[0] */\r
2012         for(i=0; i<BOARD_SIZE; i++) saveCastling[i] = castlingRights[0][i];\r
2013         saveEP = epStatus[0];\r
2014 #endif\r
2015         InitPosition(FALSE);          /* this sets up board[0], but also other stuff        */\r
2016 #if 0\r
2017         epStatus[0] = saveEP;\r
2018         for(i=0; i<BOARD_SIZE; i++) castlingRights[0][i] = saveCastling[i];\r
2019         CopyBoard(tempBoard, board); /* restore position received from ICS   */\r
2020 #endif\r
2021     } else { gameInfo.variant = newVariant; InitPosition(FALSE); }\r
2022 \r
2023     forwardMostMove = oldForwardMostMove;\r
2024     backwardMostMove = oldBackwardMostMove;\r
2025     currentMove = oldCurrentMove; /* InitPos reset these, but we need still to redraw the position */\r
2026 }\r
2027 \r
2028 static int loggedOn = FALSE;\r
2029 \r
2030 /*-- Game start info cache: --*/\r
2031 int gs_gamenum;\r
2032 char gs_kind[MSG_SIZ];\r
2033 static char player1Name[128] = "";\r
2034 static char player2Name[128] = "";\r
2035 static int player1Rating = -1;\r
2036 static int player2Rating = -1;\r
2037 /*----------------------------*/\r
2038 \r
2039 ColorClass curColor = ColorNormal;\r
2040 int suppressKibitz = 0;\r
2041 \r
2042 void\r
2043 read_from_ics(isr, closure, data, count, error)\r
2044      InputSourceRef isr;\r
2045      VOIDSTAR closure;\r
2046      char *data;\r
2047      int count;\r
2048      int error;\r
2049 {\r
2050 #define BUF_SIZE 8192\r
2051 #define STARTED_NONE 0\r
2052 #define STARTED_MOVES 1\r
2053 #define STARTED_BOARD 2\r
2054 #define STARTED_OBSERVE 3\r
2055 #define STARTED_HOLDINGS 4\r
2056 #define STARTED_CHATTER 5\r
2057 #define STARTED_COMMENT 6\r
2058 #define STARTED_MOVES_NOHIDE 7\r
2059     \r
2060     static int started = STARTED_NONE;\r
2061     static char parse[20000];\r
2062     static int parse_pos = 0;\r
2063     static char buf[BUF_SIZE + 1];\r
2064     static int firstTime = TRUE, intfSet = FALSE;\r
2065     static ColorClass prevColor = ColorNormal;\r
2066     static int savingComment = FALSE;\r
2067     char str[500];\r
2068     int i, oldi;\r
2069     int buf_len;\r
2070     int next_out;\r
2071     int tkind;\r
2072     int backup;    /* [DM] For zippy color lines */\r
2073     char *p;\r
2074 \r
2075     if (appData.debugMode) {\r
2076       if (!error) {\r
2077         fprintf(debugFP, "<ICS: ");\r
2078         show_bytes(debugFP, data, count);\r
2079         fprintf(debugFP, "\n");\r
2080       }\r
2081     }\r
2082 \r
2083     if (appData.debugMode) { int f = forwardMostMove;\r
2084         fprintf(debugFP, "ics input %d, castling = %d %d %d %d %d %d\n", f,\r
2085                 castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);\r
2086     }\r
2087     if (count > 0) {\r
2088         /* If last read ended with a partial line that we couldn't parse,\r
2089            prepend it to the new read and try again. */\r
2090         if (leftover_len > 0) {\r
2091             for (i=0; i<leftover_len; i++)\r
2092               buf[i] = buf[leftover_start + i];\r
2093         }\r
2094 \r
2095         /* Copy in new characters, removing nulls and \r's */\r
2096         buf_len = leftover_len;\r
2097         for (i = 0; i < count; i++) {\r
2098             if (data[i] != NULLCHAR && data[i] != '\r')\r
2099               buf[buf_len++] = data[i];\r
2100             if(buf_len >= 5 && buf[buf_len-5]=='\n' && buf[buf_len-4]=='\\' && \r
2101                                buf[buf_len-3]==' '  && buf[buf_len-2]==' '  && buf[buf_len-1]==' ') \r
2102                 buf_len -= 5; // [HGM] ICS: join continuation line of Lasker 2.2.3 server with previous\r
2103         }\r
2104 \r
2105         buf[buf_len] = NULLCHAR;\r
2106         next_out = leftover_len;\r
2107         leftover_start = 0;\r
2108         \r
2109         i = 0;\r
2110         while (i < buf_len) {\r
2111             /* Deal with part of the TELNET option negotiation\r
2112                protocol.  We refuse to do anything beyond the\r
2113                defaults, except that we allow the WILL ECHO option,\r
2114                which ICS uses to turn off password echoing when we are\r
2115                directly connected to it.  We reject this option\r
2116                if localLineEditing mode is on (always on in xboard)\r
2117                and we are talking to port 23, which might be a real\r
2118                telnet server that will try to keep WILL ECHO on permanently.\r
2119              */\r
2120             if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {\r
2121                 static int remoteEchoOption = FALSE; /* telnet ECHO option */\r
2122                 unsigned char option;\r
2123                 oldi = i;\r
2124                 switch ((unsigned char) buf[++i]) {\r
2125                   case TN_WILL:\r
2126                     if (appData.debugMode)\r
2127                       fprintf(debugFP, "\n<WILL ");\r
2128                     switch (option = (unsigned char) buf[++i]) {\r
2129                       case TN_ECHO:\r
2130                         if (appData.debugMode)\r
2131                           fprintf(debugFP, "ECHO ");\r
2132                         /* Reply only if this is a change, according\r
2133                            to the protocol rules. */\r
2134                         if (remoteEchoOption) break;\r
2135                         if (appData.localLineEditing &&\r
2136                             atoi(appData.icsPort) == TN_PORT) {\r
2137                             TelnetRequest(TN_DONT, TN_ECHO);\r
2138                         } else {\r
2139                             EchoOff();\r
2140                             TelnetRequest(TN_DO, TN_ECHO);\r
2141                             remoteEchoOption = TRUE;\r
2142                         }\r
2143                         break;\r
2144                       default:\r
2145                         if (appData.debugMode)\r
2146                           fprintf(debugFP, "%d ", option);\r
2147                         /* Whatever this is, we don't want it. */\r
2148                         TelnetRequest(TN_DONT, option);\r
2149                         break;\r
2150                     }\r
2151                     break;\r
2152                   case TN_WONT:\r
2153                     if (appData.debugMode)\r
2154                       fprintf(debugFP, "\n<WONT ");\r
2155                     switch (option = (unsigned char) buf[++i]) {\r
2156                       case TN_ECHO:\r
2157                         if (appData.debugMode)\r
2158                           fprintf(debugFP, "ECHO ");\r
2159                         /* Reply only if this is a change, according\r
2160                            to the protocol rules. */\r
2161                         if (!remoteEchoOption) break;\r
2162                         EchoOn();\r
2163                         TelnetRequest(TN_DONT, TN_ECHO);\r
2164                         remoteEchoOption = FALSE;\r
2165                         break;\r
2166                       default:\r
2167                         if (appData.debugMode)\r
2168                           fprintf(debugFP, "%d ", (unsigned char) option);\r
2169                         /* Whatever this is, it must already be turned\r
2170                            off, because we never agree to turn on\r
2171                            anything non-default, so according to the\r
2172                            protocol rules, we don't reply. */\r
2173                         break;\r
2174                     }\r
2175                     break;\r
2176                   case TN_DO:\r
2177                     if (appData.debugMode)\r
2178                       fprintf(debugFP, "\n<DO ");\r
2179                     switch (option = (unsigned char) buf[++i]) {\r
2180                       default:\r
2181                         /* Whatever this is, we refuse to do it. */\r
2182                         if (appData.debugMode)\r
2183                           fprintf(debugFP, "%d ", option);\r
2184                         TelnetRequest(TN_WONT, option);\r
2185                         break;\r
2186                     }\r
2187                     break;\r
2188                   case TN_DONT:\r
2189                     if (appData.debugMode)\r
2190                       fprintf(debugFP, "\n<DONT ");\r
2191                     switch (option = (unsigned char) buf[++i]) {\r
2192                       default:\r
2193                         if (appData.debugMode)\r
2194                           fprintf(debugFP, "%d ", option);\r
2195                         /* Whatever this is, we are already not doing\r
2196                            it, because we never agree to do anything\r
2197                            non-default, so according to the protocol\r
2198                            rules, we don't reply. */\r
2199                         break;\r
2200                     }\r
2201                     break;\r
2202                   case TN_IAC:\r
2203                     if (appData.debugMode)\r
2204                       fprintf(debugFP, "\n<IAC ");\r
2205                     /* Doubled IAC; pass it through */\r
2206                     i--;\r
2207                     break;\r
2208                   default:\r
2209                     if (appData.debugMode)\r
2210                       fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);\r
2211                     /* Drop all other telnet commands on the floor */\r
2212                     break;\r
2213                 }\r
2214                 if (oldi > next_out)\r
2215                   SendToPlayer(&buf[next_out], oldi - next_out);\r
2216                 if (++i > next_out)\r
2217                   next_out = i;\r
2218                 continue;\r
2219             }\r
2220                 \r
2221             /* OK, this at least will *usually* work */\r
2222             if (!loggedOn && looking_at(buf, &i, "ics%")) {\r
2223                 loggedOn = TRUE;\r
2224             }\r
2225             \r
2226             if (loggedOn && !intfSet) {\r
2227                 if (ics_type == ICS_ICC) {\r
2228                   sprintf(str,\r
2229                           "/set-quietly interface %s\n/set-quietly style 12\n",\r
2230                           programVersion);\r
2231 \r
2232                 } else if (ics_type == ICS_CHESSNET) {\r
2233                   sprintf(str, "/style 12\n");\r
2234                 } else {\r
2235                   strcpy(str, "alias $ @\n$set interface ");\r
2236                   strcat(str, programVersion);\r
2237                   strcat(str, "\n$iset startpos 1\n$iset ms 1\n");\r
2238 #ifdef WIN32\r
2239                   strcat(str, "$iset nohighlight 1\n");\r
2240 #endif\r
2241                   strcat(str, "$iset lock 1\n$style 12\n");\r
2242                 }\r
2243                 SendToICS(str);\r
2244                 intfSet = TRUE;\r
2245             }\r
2246 \r
2247             if (started == STARTED_COMMENT) {\r
2248                 /* Accumulate characters in comment */\r
2249                 parse[parse_pos++] = buf[i];\r
2250                 if (buf[i] == '\n') {\r
2251                     parse[parse_pos] = NULLCHAR;\r
2252                     if(!suppressKibitz) // [HGM] kibitz\r
2253                         AppendComment(forwardMostMove, StripHighlight(parse));\r
2254                     else { // [HGM kibitz: divert memorized engine kibitz to engine-output window\r
2255                         int nrDigit = 0, nrAlph = 0, i;\r
2256                         if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input\r
2257                         { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }\r
2258                         parse[parse_pos] = NULLCHAR;\r
2259                         // try to be smart: if it does not look like search info, it should go to\r
2260                         // ICS interaction window after all, not to engine-output window.\r
2261                         for(i=0; i<parse_pos; i++) { // count letters and digits\r
2262                             nrDigit += (parse[i] >= '0' && parse[i] <= '9');\r
2263                             nrAlph  += (parse[i] >= 'a' && parse[i] <= 'z');\r
2264                             nrAlph  += (parse[i] >= 'A' && parse[i] <= 'Z');\r
2265                         }\r
2266                         if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info\r
2267                             OutputKibitz(suppressKibitz, parse);\r
2268                         } else {\r
2269                             char tmp[MSG_SIZ];\r
2270                             sprintf(tmp, _("your opponent kibitzes: %s"), parse);\r
2271                             SendToPlayer(tmp, strlen(tmp));\r
2272                         }\r
2273                     }\r
2274                     started = STARTED_NONE;\r
2275                 } else {\r
2276                     /* Don't match patterns against characters in chatter */\r
2277                     i++;\r
2278                     continue;\r
2279                 }\r
2280             }\r
2281             if (started == STARTED_CHATTER) {\r
2282                 if (buf[i] != '\n') {\r
2283                     /* Don't match patterns against characters in chatter */\r
2284                     i++;\r
2285                     continue;\r
2286                 }\r
2287                 started = STARTED_NONE;\r
2288             }\r
2289 \r
2290             /* Kludge to deal with rcmd protocol */\r
2291             if (firstTime && looking_at(buf, &i, "\001*")) {\r
2292                 DisplayFatalError(&buf[1], 0, 1);\r
2293                 continue;\r
2294             } else {\r
2295                 firstTime = FALSE;\r
2296             }\r
2297 \r
2298             if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {\r
2299                 ics_type = ICS_ICC;\r
2300                 ics_prefix = "/";\r
2301                 if (appData.debugMode)\r
2302                   fprintf(debugFP, "ics_type %d\n", ics_type);\r
2303                 continue;\r
2304             }\r
2305             if (!loggedOn && looking_at(buf, &i, "freechess.org")) {\r
2306                 ics_type = ICS_FICS;\r
2307                 ics_prefix = "$";\r
2308                 if (appData.debugMode)\r
2309                   fprintf(debugFP, "ics_type %d\n", ics_type);\r
2310                 continue;\r
2311             }\r
2312             if (!loggedOn && looking_at(buf, &i, "chess.net")) {\r
2313                 ics_type = ICS_CHESSNET;\r
2314                 ics_prefix = "/";\r
2315                 if (appData.debugMode)\r
2316                   fprintf(debugFP, "ics_type %d\n", ics_type);\r
2317                 continue;\r
2318             }\r
2319 \r
2320             if (!loggedOn &&\r
2321                 (looking_at(buf, &i, "\"*\" is *a registered name") ||\r
2322                  looking_at(buf, &i, "Logging you in as \"*\"") ||\r
2323                  looking_at(buf, &i, "will be \"*\""))) {\r
2324               strcpy(ics_handle, star_match[0]);\r
2325               continue;\r
2326             }\r
2327 \r
2328             if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {\r
2329               char buf[MSG_SIZ];\r
2330               sprintf(buf, "%s@%s", ics_handle, appData.icsHost);\r
2331               DisplayIcsInteractionTitle(buf);\r
2332               have_set_title = TRUE;\r
2333             }\r
2334 \r
2335             /* skip finger notes */\r
2336             if (started == STARTED_NONE &&\r
2337                 ((buf[i] == ' ' && isdigit(buf[i+1])) ||\r
2338                  (buf[i] == '1' && buf[i+1] == '0')) &&\r
2339                 buf[i+2] == ':' && buf[i+3] == ' ') {\r
2340               started = STARTED_CHATTER;\r
2341               i += 3;\r
2342               continue;\r
2343             }\r
2344 \r
2345             /* skip formula vars */\r
2346             if (started == STARTED_NONE &&\r
2347                 buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {\r
2348               started = STARTED_CHATTER;\r
2349               i += 3;\r
2350               continue;\r
2351             }\r
2352 \r
2353             oldi = i;\r
2354             // [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window\r
2355             if (appData.autoKibitz && started == STARTED_NONE && \r
2356                 !appData.icsEngineAnalyze &&                     // [HGM] [DM] ICS analyze\r
2357                 (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {\r
2358                 if(looking_at(buf, &i, "* kibitzes: ") &&\r
2359                    (StrStr(star_match[0], gameInfo.white) == star_match[0] || \r
2360                     StrStr(star_match[0], gameInfo.black) == star_match[0]   )) { // kibitz of self or opponent\r
2361                         suppressKibitz = TRUE;\r
2362                         if((StrStr(star_match[0], gameInfo.white) == star_match[0])\r
2363                                 && (gameMode == IcsPlayingWhite) ||\r
2364                            (StrStr(star_match[0], gameInfo.black) == star_match[0])\r
2365                                 && (gameMode == IcsPlayingBlack)   ) // opponent kibitz\r
2366                             started = STARTED_CHATTER; // own kibitz we simply discard\r
2367                         else {\r
2368                             started = STARTED_COMMENT; // make sure it will be collected in parse[]\r
2369                             parse_pos = 0; parse[0] = NULLCHAR;\r
2370                             savingComment = TRUE;\r
2371                             suppressKibitz = gameMode != IcsObserving ? 2 :\r
2372                                 (StrStr(star_match[0], gameInfo.white) == NULL) + 1;\r
2373                         } \r
2374                         continue;\r
2375                 } else\r
2376                 if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz\r
2377                     started = STARTED_CHATTER;\r
2378                     suppressKibitz = TRUE;\r
2379                 }\r
2380             } // [HGM] kibitz: end of patch\r
2381 \r
2382             if (appData.zippyTalk || appData.zippyPlay) {\r
2383                 /* [DM] Backup address for color zippy lines */\r
2384                 backup = i;\r
2385 #if ZIPPY\r
2386        #ifdef WIN32\r
2387                if (loggedOn == TRUE)\r
2388                        if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||\r
2389                           (appData.zippyPlay && ZippyMatch(buf, &backup)));\r
2390        #else\r
2391                 if (ZippyControl(buf, &i) ||\r
2392                     ZippyConverse(buf, &i) ||\r
2393                     (appData.zippyPlay && ZippyMatch(buf, &i))) {\r
2394                       loggedOn = TRUE;\r
2395                       if (!appData.colorize) continue;\r
2396                 }\r
2397        #endif\r
2398 #endif\r
2399             } // [DM] 'else { ' deleted\r
2400                 if (/* Don't color "message" or "messages" output */\r
2401                     (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||\r
2402                     looking_at(buf, &i, "*. * at *:*: ") ||\r
2403                     looking_at(buf, &i, "--* (*:*): ") ||\r
2404                     /* Regular tells and says */\r
2405                     (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||\r
2406                     looking_at(buf, &i, "* (your partner) tells you: ") ||\r
2407                     looking_at(buf, &i, "* says: ") ||\r
2408                     /* Message notifications (same color as tells) */\r
2409                     looking_at(buf, &i, "* has left a message ") ||\r
2410                     looking_at(buf, &i, "* just sent you a message:\n") ||\r
2411                     /* Whispers and kibitzes */\r
2412                     (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||\r
2413                     looking_at(buf, &i, "* kibitzes: ") ||\r
2414                     /* Channel tells */\r
2415                     (tkind = 3, looking_at(buf, &i, "*(*: "))) {\r
2416 \r
2417                   if (tkind == 1 && strchr(star_match[0], ':')) {\r
2418                       /* Avoid "tells you:" spoofs in channels */\r
2419                      tkind = 3;\r
2420                   }\r
2421                   if (star_match[0][0] == NULLCHAR ||\r
2422                       strchr(star_match[0], ' ') ||\r
2423                       (tkind == 3 && strchr(star_match[1], ' '))) {\r
2424                     /* Reject bogus matches */\r
2425                     i = oldi;\r
2426                   } else {\r
2427                     if (appData.colorize) {\r
2428                       if (oldi > next_out) {\r
2429                         SendToPlayer(&buf[next_out], oldi - next_out);\r
2430                         next_out = oldi;\r
2431                       }\r
2432                       switch (tkind) {\r
2433                       case 1:\r
2434                         Colorize(ColorTell, FALSE);\r
2435                         curColor = ColorTell;\r
2436                         break;\r
2437                       case 2:\r
2438                         Colorize(ColorKibitz, FALSE);\r
2439                         curColor = ColorKibitz;\r
2440                         break;\r
2441                       case 3:\r
2442                         p = strrchr(star_match[1], '(');\r
2443                         if (p == NULL) {\r
2444                           p = star_match[1];\r
2445                         } else {\r
2446                           p++;\r
2447                         }\r
2448                         if (atoi(p) == 1) {\r
2449                           Colorize(ColorChannel1, FALSE);\r
2450                           curColor = ColorChannel1;\r
2451                         } else {\r
2452                           Colorize(ColorChannel, FALSE);\r
2453                           curColor = ColorChannel;\r
2454                         }\r
2455                         break;\r
2456                       case 5:\r
2457                         curColor = ColorNormal;\r
2458                         break;\r
2459                       }\r
2460                     }\r
2461                     if (started == STARTED_NONE && appData.autoComment &&\r
2462                         (gameMode == IcsObserving ||\r
2463                          gameMode == IcsPlayingWhite ||\r
2464                          gameMode == IcsPlayingBlack)) {\r
2465                       parse_pos = i - oldi;\r
2466                       memcpy(parse, &buf[oldi], parse_pos);\r
2467                       parse[parse_pos] = NULLCHAR;\r
2468                       started = STARTED_COMMENT;\r
2469                       savingComment = TRUE;\r
2470                     } else {\r
2471                       started = STARTED_CHATTER;\r
2472                       savingComment = FALSE;\r
2473                     }\r
2474                     loggedOn = TRUE;\r
2475                     continue;\r
2476                   }\r
2477                 }\r
2478 \r
2479                 if (looking_at(buf, &i, "* s-shouts: ") ||\r
2480                     looking_at(buf, &i, "* c-shouts: ")) {\r
2481                     if (appData.colorize) {\r
2482                         if (oldi > next_out) {\r
2483                             SendToPlayer(&buf[next_out], oldi - next_out);\r
2484                             next_out = oldi;\r
2485                         }\r
2486                         Colorize(ColorSShout, FALSE);\r
2487                         curColor = ColorSShout;\r
2488                     }\r
2489                     loggedOn = TRUE;\r
2490                     started = STARTED_CHATTER;\r
2491                     continue;\r
2492                 }\r
2493 \r
2494                 if (looking_at(buf, &i, "--->")) {\r
2495                     loggedOn = TRUE;\r
2496                     continue;\r
2497                 }\r
2498 \r
2499                 if (looking_at(buf, &i, "* shouts: ") ||\r
2500                     looking_at(buf, &i, "--> ")) {\r
2501                     if (appData.colorize) {\r
2502                         if (oldi > next_out) {\r
2503                             SendToPlayer(&buf[next_out], oldi - next_out);\r
2504                             next_out = oldi;\r
2505                         }\r
2506                         Colorize(ColorShout, FALSE);\r
2507                         curColor = ColorShout;\r
2508                     }\r
2509                     loggedOn = TRUE;\r
2510                     started = STARTED_CHATTER;\r
2511                     continue;\r
2512                 }\r
2513 \r
2514                 if (looking_at( buf, &i, "Challenge:")) {\r
2515                     if (appData.colorize) {\r
2516                         if (oldi > next_out) {\r
2517                             SendToPlayer(&buf[next_out], oldi - next_out);\r
2518                             next_out = oldi;\r
2519                         }\r
2520                         Colorize(ColorChallenge, FALSE);\r
2521                         curColor = ColorChallenge;\r
2522                     }\r
2523                     loggedOn = TRUE;\r
2524                     continue;\r
2525                 }\r
2526 \r
2527                 if (looking_at(buf, &i, "* offers you") ||\r
2528                     looking_at(buf, &i, "* offers to be") ||\r
2529                     looking_at(buf, &i, "* would like to") ||\r
2530                     looking_at(buf, &i, "* requests to") ||\r
2531                     looking_at(buf, &i, "Your opponent offers") ||\r
2532                     looking_at(buf, &i, "Your opponent requests")) {\r
2533 \r
2534                     if (appData.colorize) {\r
2535                         if (oldi > next_out) {\r
2536                             SendToPlayer(&buf[next_out], oldi - next_out);\r
2537                             next_out = oldi;\r
2538                         }\r
2539                         Colorize(ColorRequest, FALSE);\r
2540                         curColor = ColorRequest;\r
2541                     }\r
2542                     continue;\r
2543                 }\r
2544 \r
2545                 if (looking_at(buf, &i, "* (*) seeking")) {\r
2546                     if (appData.colorize) {\r
2547                         if (oldi > next_out) {\r
2548                             SendToPlayer(&buf[next_out], oldi - next_out);\r
2549                             next_out = oldi;\r
2550                         }\r
2551                         Colorize(ColorSeek, FALSE);\r
2552                         curColor = ColorSeek;\r
2553                     }\r
2554                     continue;\r
2555             }\r
2556 \r
2557             if (looking_at(buf, &i, "\\   ")) {\r
2558                 if (prevColor != ColorNormal) {\r
2559                     if (oldi > next_out) {\r
2560                         SendToPlayer(&buf[next_out], oldi - next_out);\r
2561                         next_out = oldi;\r
2562                     }\r
2563                     Colorize(prevColor, TRUE);\r
2564                     curColor = prevColor;\r
2565                 }\r
2566                 if (savingComment) {\r
2567                     parse_pos = i - oldi;\r
2568                     memcpy(parse, &buf[oldi], parse_pos);\r
2569                     parse[parse_pos] = NULLCHAR;\r
2570                     started = STARTED_COMMENT;\r
2571                 } else {\r
2572                     started = STARTED_CHATTER;\r
2573                 }\r
2574                 continue;\r
2575             }\r
2576 \r
2577             if (looking_at(buf, &i, "Black Strength :") ||\r
2578                 looking_at(buf, &i, "<<< style 10 board >>>") ||\r
2579                 looking_at(buf, &i, "<10>") ||\r
2580                 looking_at(buf, &i, "#@#")) {\r
2581                 /* Wrong board style */\r
2582                 loggedOn = TRUE;\r
2583                 SendToICS(ics_prefix);\r
2584                 SendToICS("set style 12\n");\r
2585                 SendToICS(ics_prefix);\r
2586                 SendToICS("refresh\n");\r
2587                 continue;\r
2588             }\r
2589             \r
2590             if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {\r
2591                 ICSInitScript();\r
2592                 have_sent_ICS_logon = 1;\r
2593                 continue;\r
2594             }\r
2595               \r
2596             if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ && \r
2597                 (looking_at(buf, &i, "\n<12> ") ||\r
2598                  looking_at(buf, &i, "<12> "))) {\r
2599                 loggedOn = TRUE;\r
2600                 if (oldi > next_out) {\r
2601                     SendToPlayer(&buf[next_out], oldi - next_out);\r
2602                 }\r
2603                 next_out = i;\r
2604                 started = STARTED_BOARD;\r
2605                 parse_pos = 0;\r
2606                 continue;\r
2607             }\r
2608 \r
2609             if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||\r
2610                 looking_at(buf, &i, "<b1> ")) {\r
2611                 if (oldi > next_out) {\r
2612                     SendToPlayer(&buf[next_out], oldi - next_out);\r
2613                 }\r
2614                 next_out = i;\r
2615                 started = STARTED_HOLDINGS;\r
2616                 parse_pos = 0;\r
2617                 continue;\r
2618             }\r
2619 \r
2620             if (looking_at(buf, &i, "* *vs. * *--- *")) {\r
2621                 loggedOn = TRUE;\r
2622                 /* Header for a move list -- first line */\r
2623 \r
2624                 switch (ics_getting_history) {\r
2625                   case H_FALSE:\r
2626                     switch (gameMode) {\r
2627                       case IcsIdle:\r
2628                       case BeginningOfGame:\r
2629                         /* User typed "moves" or "oldmoves" while we\r
2630                            were idle.  Pretend we asked for these\r
2631                            moves and soak them up so user can step\r
2632                            through them and/or save them.\r
2633                            */\r
2634                         Reset(FALSE, TRUE);\r
2635                         gameMode = IcsObserving;\r
2636                         ModeHighlight();\r
2637                         ics_gamenum = -1;\r
2638                         ics_getting_history = H_GOT_UNREQ_HEADER;\r
2639                         break;\r
2640                       case EditGame: /*?*/\r
2641                       case EditPosition: /*?*/\r
2642                         /* Should above feature work in these modes too? */\r
2643                         /* For now it doesn't */\r
2644                         ics_getting_history = H_GOT_UNWANTED_HEADER;\r
2645                         break;\r
2646                       default:\r
2647                         ics_getting_history = H_GOT_UNWANTED_HEADER;\r
2648                         break;\r
2649                     }\r
2650                     break;\r
2651                   case H_REQUESTED:\r
2652                     /* Is this the right one? */\r
2653                     if (gameInfo.white && gameInfo.black &&\r
2654                         strcmp(gameInfo.white, star_match[0]) == 0 &&\r
2655                         strcmp(gameInfo.black, star_match[2]) == 0) {\r
2656                         /* All is well */\r
2657                         ics_getting_history = H_GOT_REQ_HEADER;\r
2658                     }\r
2659                     break;\r
2660                   case H_GOT_REQ_HEADER:\r
2661                   case H_GOT_UNREQ_HEADER:\r
2662                   case H_GOT_UNWANTED_HEADER:\r
2663                   case H_GETTING_MOVES:\r
2664                     /* Should not happen */\r
2665                     DisplayError(_("Error gathering move list: two headers"), 0);\r
2666                     ics_getting_history = H_FALSE;\r
2667                     break;\r
2668                 }\r
2669 \r
2670                 /* Save player ratings into gameInfo if needed */\r
2671                 if ((ics_getting_history == H_GOT_REQ_HEADER ||\r
2672                      ics_getting_history == H_GOT_UNREQ_HEADER) &&\r
2673                     (gameInfo.whiteRating == -1 ||\r
2674                      gameInfo.blackRating == -1)) {\r
2675 \r
2676                     gameInfo.whiteRating = string_to_rating(star_match[1]);\r
2677                     gameInfo.blackRating = string_to_rating(star_match[3]);\r
2678                     if (appData.debugMode)\r
2679                       fprintf(debugFP, _("Ratings from header: W %d, B %d\n"), \r
2680                               gameInfo.whiteRating, gameInfo.blackRating);\r
2681                 }\r
2682                 continue;\r
2683             }\r
2684 \r
2685             if (looking_at(buf, &i,\r
2686               "* * match, initial time: * minute*, increment: * second")) {\r
2687                 /* Header for a move list -- second line */\r
2688                 /* Initial board will follow if this is a wild game */\r
2689                 if (gameInfo.event != NULL) free(gameInfo.event);\r
2690                 sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);\r
2691                 gameInfo.event = StrSave(str);\r
2692                 /* [HGM] we switched variant. Translate boards if needed. */\r
2693                 VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event));\r
2694                 continue;\r
2695             }\r
2696 \r
2697             if (looking_at(buf, &i, "Move  ")) {\r
2698                 /* Beginning of a move list */\r
2699                 switch (ics_getting_history) {\r
2700                   case H_FALSE:\r
2701                     /* Normally should not happen */\r
2702                     /* Maybe user hit reset while we were parsing */\r
2703                     break;\r
2704                   case H_REQUESTED:\r
2705                     /* Happens if we are ignoring a move list that is not\r
2706                      * the one we just requested.  Common if the user\r
2707                      * tries to observe two games without turning off\r
2708                      * getMoveList */\r
2709                     break;\r
2710                   case H_GETTING_MOVES:\r
2711                     /* Should not happen */\r
2712                     DisplayError(_("Error gathering move list: nested"), 0);\r
2713                     ics_getting_history = H_FALSE;\r
2714                     break;\r
2715                   case H_GOT_REQ_HEADER:\r
2716                     ics_getting_history = H_GETTING_MOVES;\r
2717                     started = STARTED_MOVES;\r
2718                     parse_pos = 0;\r
2719                     if (oldi > next_out) {\r
2720                         SendToPlayer(&buf[next_out], oldi - next_out);\r
2721                     }\r
2722                     break;\r
2723                   case H_GOT_UNREQ_HEADER:\r
2724                     ics_getting_history = H_GETTING_MOVES;\r
2725                     started = STARTED_MOVES_NOHIDE;\r
2726                     parse_pos = 0;\r
2727                     break;\r
2728                   case H_GOT_UNWANTED_HEADER:\r
2729                     ics_getting_history = H_FALSE;\r
2730                     break;\r
2731                 }\r
2732                 continue;\r
2733             }                           \r
2734             \r
2735             if (looking_at(buf, &i, "% ") ||\r
2736                 ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)\r
2737                  && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book\r
2738                 savingComment = FALSE;\r
2739                 switch (started) {\r
2740                   case STARTED_MOVES:\r
2741                   case STARTED_MOVES_NOHIDE:\r
2742                     memcpy(&parse[parse_pos], &buf[oldi], i - oldi);\r
2743                     parse[parse_pos + i - oldi] = NULLCHAR;\r
2744                     ParseGameHistory(parse);\r
2745 #if ZIPPY\r
2746                     if (appData.zippyPlay && first.initDone) {\r
2747                         FeedMovesToProgram(&first, forwardMostMove);\r
2748                         if (gameMode == IcsPlayingWhite) {\r
2749                             if (WhiteOnMove(forwardMostMove)) {\r
2750                                 if (first.sendTime) {\r
2751                                   if (first.useColors) {\r
2752                                     SendToProgram("black\n", &first); \r
2753                                   }\r
2754                                   SendTimeRemaining(&first, TRUE);\r
2755                                 }\r
2756 #if 0\r
2757                                 if (first.useColors) {\r
2758                                   SendToProgram("white\ngo\n", &first);\r
2759                                 } else {\r
2760                                   SendToProgram("go\n", &first);\r
2761                                 }\r
2762 #else\r
2763                                 if (first.useColors) {\r
2764                                   SendToProgram("white\n", &first); // [HGM] book: made sending of "go\n" book dependent\r
2765                                 }\r
2766                                 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: probe book for initial pos\r
2767 #endif\r
2768                                 first.maybeThinking = TRUE;\r
2769                             } else {\r
2770                                 if (first.usePlayother) {\r
2771                                   if (first.sendTime) {\r
2772                                     SendTimeRemaining(&first, TRUE);\r
2773                                   }\r
2774                                   SendToProgram("playother\n", &first);\r
2775                                   firstMove = FALSE;\r
2776                                 } else {\r
2777                                   firstMove = TRUE;\r
2778                                 }\r
2779                             }\r
2780                         } else if (gameMode == IcsPlayingBlack) {\r
2781                             if (!WhiteOnMove(forwardMostMove)) {\r
2782                                 if (first.sendTime) {\r
2783                                   if (first.useColors) {\r
2784                                     SendToProgram("white\n", &first);\r
2785                                   }\r
2786                                   SendTimeRemaining(&first, FALSE);\r
2787                                 }\r
2788 #if 0\r
2789                                 if (first.useColors) {\r
2790                                   SendToProgram("black\ngo\n", &first);\r
2791                                 } else {\r
2792                                   SendToProgram("go\n", &first);\r
2793                                 }\r
2794 #else\r
2795                                 if (first.useColors) {\r
2796                                   SendToProgram("black\n", &first);\r
2797                                 }\r
2798                                 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE);\r
2799 #endif\r
2800                                 first.maybeThinking = TRUE;\r
2801                             } else {\r
2802                                 if (first.usePlayother) {\r
2803                                   if (first.sendTime) {\r
2804                                     SendTimeRemaining(&first, FALSE);\r
2805                                   }\r
2806                                   SendToProgram("playother\n", &first);\r
2807                                   firstMove = FALSE;\r
2808                                 } else {\r
2809                                   firstMove = TRUE;\r
2810                                 }\r
2811                             }\r
2812                         }                       \r
2813                     }\r
2814 #endif\r
2815                     if (gameMode == IcsObserving && ics_gamenum == -1) {\r
2816                         /* Moves came from oldmoves or moves command\r
2817                            while we weren't doing anything else.\r
2818                            */\r
2819                         currentMove = forwardMostMove;\r
2820                         ClearHighlights();/*!!could figure this out*/\r
2821                         flipView = appData.flipView;\r
2822                         DrawPosition(FALSE, boards[currentMove]);\r
2823                         DisplayBothClocks();\r
2824                         sprintf(str, "%s vs. %s",\r
2825                                 gameInfo.white, gameInfo.black);\r
2826                         DisplayTitle(str);\r
2827                         gameMode = IcsIdle;\r
2828                     } else {\r
2829                         /* Moves were history of an active game */\r
2830                         if (gameInfo.resultDetails != NULL) {\r
2831                             free(gameInfo.resultDetails);\r
2832                             gameInfo.resultDetails = NULL;\r
2833                         }\r
2834                     }\r
2835                     HistorySet(parseList, backwardMostMove,\r
2836                                forwardMostMove, currentMove-1);\r
2837                     DisplayMove(currentMove - 1);\r
2838                     if (started == STARTED_MOVES) next_out = i;\r
2839                     started = STARTED_NONE;\r
2840                     ics_getting_history = H_FALSE;\r
2841                     break;\r
2842 \r
2843                   case STARTED_OBSERVE:\r
2844                     started = STARTED_NONE;\r
2845                     SendToICS(ics_prefix);\r
2846                     SendToICS("refresh\n");\r
2847                     break;\r
2848 \r
2849                   default:\r
2850                     break;\r
2851                 }\r
2852                 if(bookHit) { // [HGM] book: simulate book reply\r
2853                     static char bookMove[MSG_SIZ]; // a bit generous?\r
2854 \r
2855                     programStats.depth = programStats.nodes = programStats.time = \r
2856                     programStats.score = programStats.got_only_move = 0;\r
2857                     sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
2858 \r
2859                     strcpy(bookMove, "move ");\r
2860                     strcat(bookMove, bookHit);\r
2861                     HandleMachineMove(bookMove, &first);\r
2862                 }\r
2863                 continue;\r
2864             }\r
2865             \r
2866             if ((started == STARTED_MOVES || started == STARTED_BOARD ||\r
2867                  started == STARTED_HOLDINGS ||\r
2868                  started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {\r
2869                 /* Accumulate characters in move list or board */\r
2870                 parse[parse_pos++] = buf[i];\r
2871             }\r
2872             \r
2873             /* Start of game messages.  Mostly we detect start of game\r
2874                when the first board image arrives.  On some versions\r
2875                of the ICS, though, we need to do a "refresh" after starting\r
2876                to observe in order to get the current board right away. */\r
2877             if (looking_at(buf, &i, "Adding game * to observation list")) {\r
2878                 started = STARTED_OBSERVE;\r
2879                 continue;\r
2880             }\r
2881 \r
2882             /* Handle auto-observe */\r
2883             if (appData.autoObserve &&\r
2884                 (gameMode == IcsIdle || gameMode == BeginningOfGame) &&\r
2885                 looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {\r
2886                 char *player;\r
2887                 /* Choose the player that was highlighted, if any. */\r
2888                 if (star_match[0][0] == '\033' ||\r
2889                     star_match[1][0] != '\033') {\r
2890                     player = star_match[0];\r
2891                 } else {\r
2892                     player = star_match[2];\r
2893                 }\r
2894                 sprintf(str, "%sobserve %s\n",\r
2895                         ics_prefix, StripHighlightAndTitle(player));\r
2896                 SendToICS(str);\r
2897 \r
2898                 /* Save ratings from notify string */\r
2899                 strcpy(player1Name, star_match[0]);\r
2900                 player1Rating = string_to_rating(star_match[1]);\r
2901                 strcpy(player2Name, star_match[2]);\r
2902                 player2Rating = string_to_rating(star_match[3]);\r
2903 \r
2904                 if (appData.debugMode)\r
2905                   fprintf(debugFP, \r
2906                           "Ratings from 'Game notification:' %s %d, %s %d\n",\r
2907                           player1Name, player1Rating,\r
2908                           player2Name, player2Rating);\r
2909 \r
2910                 continue;\r
2911             }\r
2912 \r
2913             /* Deal with automatic examine mode after a game,\r
2914                and with IcsObserving -> IcsExamining transition */\r
2915             if (looking_at(buf, &i, "Entering examine mode for game *") ||\r
2916                 looking_at(buf, &i, "has made you an examiner of game *")) {\r
2917 \r
2918                 int gamenum = atoi(star_match[0]);\r
2919                 if ((gameMode == IcsIdle || gameMode == IcsObserving) &&\r
2920                     gamenum == ics_gamenum) {\r
2921                     /* We were already playing or observing this game;\r
2922                        no need to refetch history */\r
2923                     gameMode = IcsExamining;\r
2924                     if (pausing) {\r
2925                         pauseExamForwardMostMove = forwardMostMove;\r
2926                     } else if (currentMove < forwardMostMove) {\r
2927                         ForwardInner(forwardMostMove);\r
2928                     }\r
2929                 } else {\r
2930                     /* I don't think this case really can happen */\r
2931                     SendToICS(ics_prefix);\r
2932                     SendToICS("refresh\n");\r
2933                 }\r
2934                 continue;\r
2935             }    \r
2936             \r
2937             /* Error messages */\r
2938             if (ics_user_moved) {\r
2939                 if (looking_at(buf, &i, "Illegal move") ||\r
2940                     looking_at(buf, &i, "Not a legal move") ||\r
2941                     looking_at(buf, &i, "Your king is in check") ||\r
2942                     looking_at(buf, &i, "It isn't your turn") ||\r
2943                     looking_at(buf, &i, "It is not your move")) {\r
2944                     /* Illegal move */\r
2945                     ics_user_moved = 0;\r
2946                     if (forwardMostMove > backwardMostMove) {\r
2947                         currentMove = --forwardMostMove;\r
2948                         DisplayMove(currentMove - 1); /* before DMError */\r
2949                         DisplayMoveError(_("Illegal move (rejected by ICS)"));\r
2950                         DrawPosition(FALSE, boards[currentMove]);\r
2951                         SwitchClocks();\r
2952                         DisplayBothClocks();\r
2953                     }\r
2954                     continue;\r
2955                 }\r
2956             }\r
2957 \r
2958             if (looking_at(buf, &i, "still have time") ||\r
2959                 looking_at(buf, &i, "not out of time") ||\r
2960                 looking_at(buf, &i, "either player is out of time") ||\r
2961                 looking_at(buf, &i, "has timeseal; checking")) {\r
2962                 /* We must have called his flag a little too soon */\r
2963                 whiteFlag = blackFlag = FALSE;\r
2964                 continue;\r
2965             }\r
2966 \r
2967             if (looking_at(buf, &i, "added * seconds to") ||\r
2968                 looking_at(buf, &i, "seconds were added to")) {\r
2969                 /* Update the clocks */\r
2970                 SendToICS(ics_prefix);\r
2971                 SendToICS("refresh\n");\r
2972                 continue;\r
2973             }\r
2974 \r
2975             if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {\r
2976                 ics_clock_paused = TRUE;\r
2977                 StopClocks();\r
2978                 continue;\r
2979             }\r
2980 \r
2981             if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {\r
2982                 ics_clock_paused = FALSE;\r
2983                 StartClocks();\r
2984                 continue;\r
2985             }\r
2986 \r
2987             /* Grab player ratings from the Creating: message.\r
2988                Note we have to check for the special case when\r
2989                the ICS inserts things like [white] or [black]. */\r
2990             if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||\r
2991                 looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {\r
2992                 /* star_matches:\r
2993                    0    player 1 name (not necessarily white)\r
2994                    1    player 1 rating\r
2995                    2    empty, white, or black (IGNORED)\r
2996                    3    player 2 name (not necessarily black)\r
2997                    4    player 2 rating\r
2998                    \r
2999                    The names/ratings are sorted out when the game\r
3000                    actually starts (below).\r
3001                 */\r
3002                 strcpy(player1Name, StripHighlightAndTitle(star_match[0]));\r
3003                 player1Rating = string_to_rating(star_match[1]);\r
3004                 strcpy(player2Name, StripHighlightAndTitle(star_match[3]));\r
3005                 player2Rating = string_to_rating(star_match[4]);\r
3006 \r
3007                 if (appData.debugMode)\r
3008                   fprintf(debugFP, \r
3009                           "Ratings from 'Creating:' %s %d, %s %d\n",\r
3010                           player1Name, player1Rating,\r
3011                           player2Name, player2Rating);\r
3012 \r
3013                 continue;\r
3014             }\r
3015             \r
3016             /* Improved generic start/end-of-game messages */\r
3017             if ((tkind=0, looking_at(buf, &i, "{Game * (* vs. *) *}*")) ||\r
3018                 (tkind=1, looking_at(buf, &i, "{Game * (*(*) vs. *(*)) *}*"))){\r
3019                 /* If tkind == 0: */\r
3020                 /* star_match[0] is the game number */\r
3021                 /*           [1] is the white player's name */\r
3022                 /*           [2] is the black player's name */\r
3023                 /* For end-of-game: */\r
3024                 /*           [3] is the reason for the game end */\r
3025                 /*           [4] is a PGN end game-token, preceded by " " */\r
3026                 /* For start-of-game: */\r
3027                 /*           [3] begins with "Creating" or "Continuing" */\r
3028                 /*           [4] is " *" or empty (don't care). */\r
3029                 int gamenum = atoi(star_match[0]);\r
3030                 char *whitename, *blackname, *why, *endtoken;\r
3031                 ChessMove endtype = (ChessMove) 0;\r
3032 \r
3033                 if (tkind == 0) {\r
3034                   whitename = star_match[1];\r
3035                   blackname = star_match[2];\r
3036                   why = star_match[3];\r
3037                   endtoken = star_match[4];\r
3038                 } else {\r
3039                   whitename = star_match[1];\r
3040                   blackname = star_match[3];\r
3041                   why = star_match[5];\r
3042                   endtoken = star_match[6];\r
3043                 }\r
3044 \r
3045                 /* Game start messages */\r
3046                 if (strncmp(why, "Creating ", 9) == 0 ||\r
3047                     strncmp(why, "Continuing ", 11) == 0) {\r
3048                     gs_gamenum = gamenum;\r
3049                     strcpy(gs_kind, strchr(why, ' ') + 1);\r
3050 #if ZIPPY\r
3051                     if (appData.zippyPlay) {\r
3052                         ZippyGameStart(whitename, blackname);\r
3053                     }\r
3054 #endif /*ZIPPY*/\r
3055                     continue;\r
3056                 }\r
3057 \r
3058                 /* Game end messages */\r
3059                 if (gameMode == IcsIdle || gameMode == BeginningOfGame ||\r
3060                     ics_gamenum != gamenum) {\r
3061                     continue;\r
3062                 }\r
3063                 while (endtoken[0] == ' ') endtoken++;\r
3064                 switch (endtoken[0]) {\r
3065                   case '*':\r
3066                   default:\r
3067                     endtype = GameUnfinished;\r
3068                     break;\r
3069                   case '0':\r
3070                     endtype = BlackWins;\r
3071                     break;\r
3072                   case '1':\r
3073                     if (endtoken[1] == '/')\r
3074                       endtype = GameIsDrawn;\r
3075                     else\r
3076                       endtype = WhiteWins;\r
3077                     break;\r
3078                 }\r
3079                 GameEnds(endtype, why, GE_ICS);\r
3080 #if ZIPPY\r
3081                 if (appData.zippyPlay && first.initDone) {\r
3082                     ZippyGameEnd(endtype, why);\r
3083                     if (first.pr == NULL) {\r
3084                       /* Start the next process early so that we'll\r
3085                          be ready for the next challenge */\r
3086                       StartChessProgram(&first);\r
3087                     }\r
3088                     /* Send "new" early, in case this command takes\r
3089                        a long time to finish, so that we'll be ready\r
3090                        for the next challenge. */\r
3091                     gameInfo.variant = VariantNormal; // [HGM] variantswitch: suppress sending of 'variant'\r
3092                     Reset(TRUE, TRUE);\r
3093                 }\r
3094 #endif /*ZIPPY*/\r
3095                 continue;\r
3096             }\r
3097 \r
3098             if (looking_at(buf, &i, "Removing game * from observation") ||\r
3099                 looking_at(buf, &i, "no longer observing game *") ||\r
3100                 looking_at(buf, &i, "Game * (*) has no examiners")) {\r
3101                 if (gameMode == IcsObserving &&\r
3102                     atoi(star_match[0]) == ics_gamenum)\r
3103                   {\r
3104                       /* icsEngineAnalyze */\r
3105                       if (appData.icsEngineAnalyze) {\r
3106                             ExitAnalyzeMode();\r
3107                             ModeHighlight();\r
3108                       }\r
3109                       StopClocks();\r
3110                       gameMode = IcsIdle;\r
3111                       ics_gamenum = -1;\r
3112                       ics_user_moved = FALSE;\r
3113                   }\r
3114                 continue;\r
3115             }\r
3116 \r
3117             if (looking_at(buf, &i, "no longer examining game *")) {\r
3118                 if (gameMode == IcsExamining &&\r
3119                     atoi(star_match[0]) == ics_gamenum)\r
3120                   {\r
3121                       gameMode = IcsIdle;\r
3122                       ics_gamenum = -1;\r
3123                       ics_user_moved = FALSE;\r
3124                   }\r
3125                 continue;\r
3126             }\r
3127 \r
3128             /* Advance leftover_start past any newlines we find,\r
3129                so only partial lines can get reparsed */\r
3130             if (looking_at(buf, &i, "\n")) {\r
3131                 prevColor = curColor;\r
3132                 if (curColor != ColorNormal) {\r
3133                     if (oldi > next_out) {\r
3134                         SendToPlayer(&buf[next_out], oldi - next_out);\r
3135                         next_out = oldi;\r
3136                     }\r
3137                     Colorize(ColorNormal, FALSE);\r
3138                     curColor = ColorNormal;\r
3139                 }\r
3140                 if (started == STARTED_BOARD) {\r
3141                     started = STARTED_NONE;\r
3142                     parse[parse_pos] = NULLCHAR;\r
3143                     ParseBoard12(parse);\r
3144                     ics_user_moved = 0;\r
3145 \r
3146                     /* Send premove here */\r
3147                     if (appData.premove) {\r
3148                       char str[MSG_SIZ];\r
3149                       if (currentMove == 0 &&\r
3150                           gameMode == IcsPlayingWhite &&\r
3151                           appData.premoveWhite) {\r
3152                         sprintf(str, "%s%s\n", ics_prefix,\r
3153                                 appData.premoveWhiteText);\r
3154                         if (appData.debugMode)\r
3155                           fprintf(debugFP, "Sending premove:\n");\r
3156                         SendToICS(str);\r
3157                       } else if (currentMove == 1 &&\r
3158                                  gameMode == IcsPlayingBlack &&\r
3159                                  appData.premoveBlack) {\r
3160                         sprintf(str, "%s%s\n", ics_prefix,\r
3161                                 appData.premoveBlackText);\r
3162                         if (appData.debugMode)\r
3163                           fprintf(debugFP, "Sending premove:\n");\r
3164                         SendToICS(str);\r
3165                       } else if (gotPremove) {\r
3166                         gotPremove = 0;\r
3167                         ClearPremoveHighlights();\r
3168                         if (appData.debugMode)\r
3169                           fprintf(debugFP, "Sending premove:\n");\r
3170                           UserMoveEvent(premoveFromX, premoveFromY, \r
3171                                         premoveToX, premoveToY, \r
3172                                         premovePromoChar);\r
3173                       }\r
3174                     }\r
3175 \r
3176                     /* Usually suppress following prompt */\r
3177                     if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {\r
3178                         if (looking_at(buf, &i, "*% ")) {\r
3179                             savingComment = FALSE;\r
3180                         }\r
3181                     }\r
3182                     next_out = i;\r
3183                 } else if (started == STARTED_HOLDINGS) {\r
3184                     int gamenum;\r
3185                     char new_piece[MSG_SIZ];\r
3186                     started = STARTED_NONE;\r
3187                     parse[parse_pos] = NULLCHAR;\r
3188                     if (appData.debugMode)\r
3189                       fprintf(debugFP, "Parsing holdings: %s, currentMove = %d\n",\r
3190                                                         parse, currentMove);\r
3191                     if (sscanf(parse, " game %d", &gamenum) == 1 &&\r
3192                         gamenum == ics_gamenum) {\r
3193                         if (gameInfo.variant == VariantNormal) {\r
3194                           /* [HGM] We seem to switch variant during a game!\r
3195                            * Presumably no holdings were displayed, so we have\r
3196                            * to move the position two files to the right to\r
3197                            * create room for them!\r
3198                            */\r
3199                           VariantSwitch(boards[currentMove], VariantCrazyhouse); /* temp guess */\r
3200                           /* Get a move list just to see the header, which\r
3201                              will tell us whether this is really bug or zh */\r
3202                           if (ics_getting_history == H_FALSE) {\r
3203                             ics_getting_history = H_REQUESTED;\r
3204                             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);\r
3205                             SendToICS(str);\r
3206                           }\r
3207                         }\r
3208                         new_piece[0] = NULLCHAR;\r
3209                         sscanf(parse, "game %d white [%s black [%s <- %s",\r
3210                                &gamenum, white_holding, black_holding,\r
3211                                new_piece);\r
3212                         white_holding[strlen(white_holding)-1] = NULLCHAR;\r
3213                         black_holding[strlen(black_holding)-1] = NULLCHAR;\r
3214                         /* [HGM] copy holdings to board holdings area */\r
3215                         CopyHoldings(boards[currentMove], white_holding, WhitePawn);\r
3216                         CopyHoldings(boards[currentMove], black_holding, BlackPawn);\r
3217 #if ZIPPY\r
3218                         if (appData.zippyPlay && first.initDone) {\r
3219                             ZippyHoldings(white_holding, black_holding,\r
3220                                           new_piece);\r
3221                         }\r
3222 #endif /*ZIPPY*/\r
3223                         if (tinyLayout || smallLayout) {\r
3224                             char wh[16], bh[16];\r
3225                             PackHolding(wh, white_holding);\r
3226                             PackHolding(bh, black_holding);\r
3227                             sprintf(str, "[%s-%s] %s-%s", wh, bh,\r
3228                                     gameInfo.white, gameInfo.black);\r
3229                         } else {\r
3230                             sprintf(str, "%s [%s] vs. %s [%s]",\r
3231                                     gameInfo.white, white_holding,\r
3232                                     gameInfo.black, black_holding);\r
3233                         }\r
3234 \r
3235                         DrawPosition(FALSE, boards[currentMove]);\r
3236                         DisplayTitle(str);\r
3237                     }\r
3238                     /* Suppress following prompt */\r
3239                     if (looking_at(buf, &i, "*% ")) {\r
3240                         savingComment = FALSE;\r
3241                     }\r
3242                     next_out = i;\r
3243                 }\r
3244                 continue;\r
3245             }\r
3246 \r
3247             i++;                /* skip unparsed character and loop back */\r
3248         }\r
3249         \r
3250         if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window\r
3251             started != STARTED_HOLDINGS && i > next_out) {\r
3252             SendToPlayer(&buf[next_out], i - next_out);\r
3253             next_out = i;\r
3254         }\r
3255         suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above\r
3256         \r
3257         leftover_len = buf_len - leftover_start;\r
3258         /* if buffer ends with something we couldn't parse,\r
3259            reparse it after appending the next read */\r
3260         \r
3261     } else if (count == 0) {\r
3262         RemoveInputSource(isr);\r
3263         DisplayFatalError(_("Connection closed by ICS"), 0, 0);\r
3264     } else {\r
3265         DisplayFatalError(_("Error reading from ICS"), error, 1);\r
3266     }\r
3267 }\r
3268 \r
3269 \r
3270 /* Board style 12 looks like this:\r
3271    \r
3272    <12> r-b---k- pp----pp ---bP--- ---p---- q------- ------P- P--Q--BP -----R-K W -1 0 0 0 0 0 0 paf MaxII 0 2 12 21 25 234 174 24 Q/d7-a4 (0:06) Qxa4 0 0\r
3273    \r
3274  * The "<12> " is stripped before it gets to this routine.  The two\r
3275  * trailing 0's (flip state and clock ticking) are later addition, and\r
3276  * some chess servers may not have them, or may have only the first.\r
3277  * Additional trailing fields may be added in the future.  \r
3278  */\r
3279 \r
3280 #define PATTERN "%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"\r
3281 \r
3282 #define RELATION_OBSERVING_PLAYED    0\r
3283 #define RELATION_OBSERVING_STATIC   -2   /* examined, oldmoves, or smoves */\r
3284 #define RELATION_PLAYING_MYMOVE      1\r
3285 #define RELATION_PLAYING_NOTMYMOVE  -1\r
3286 #define RELATION_EXAMINING           2\r
3287 #define RELATION_ISOLATED_BOARD     -3\r
3288 #define RELATION_STARTING_POSITION  -4   /* FICS only */\r
3289 \r
3290 void\r
3291 ParseBoard12(string)\r
3292      char *string;\r
3293\r
3294     GameMode newGameMode;\r
3295     int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;\r
3296     int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;\r
3297     int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;\r
3298     char to_play, board_chars[200];\r
3299     char move_str[500], str[500], elapsed_time[500];\r
3300     char black[32], white[32];\r
3301     Board board;\r
3302     int prevMove = currentMove;\r
3303     int ticking = 2;\r
3304     ChessMove moveType;\r
3305     int fromX, fromY, toX, toY;\r
3306     char promoChar;\r
3307     int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */\r
3308     char *bookHit = NULL; // [HGM] book\r
3309 \r
3310     fromX = fromY = toX = toY = -1;\r
3311     \r
3312     newGame = FALSE;\r
3313 \r
3314     if (appData.debugMode)\r
3315       fprintf(debugFP, _("Parsing board: %s\n"), string);\r
3316 \r
3317     move_str[0] = NULLCHAR;\r
3318     elapsed_time[0] = NULLCHAR;\r
3319     {   /* [HGM] figure out how many ranks and files the board has, for ICS extension used by Capablanca server */\r
3320         int  i = 0, j;\r
3321         while(i < 199 && (string[i] != ' ' || string[i+2] != ' ')) {\r
3322             if(string[i] == ' ') { ranks++; files = 0; }\r
3323             else files++;\r
3324             i++;\r
3325         }\r
3326         for(j = 0; j <i; j++) board_chars[j] = string[j];\r
3327         board_chars[i] = '\0';\r
3328         string += i + 1;\r
3329     }\r
3330     n = sscanf(string, PATTERN, &to_play, &double_push,\r
3331                &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,\r
3332                &gamenum, white, black, &relation, &basetime, &increment,\r
3333                &white_stren, &black_stren, &white_time, &black_time,\r
3334                &moveNum, str, elapsed_time, move_str, &ics_flip,\r
3335                &ticking);\r
3336 \r
3337     if (n < 21) {\r
3338         sprintf(str, _("Failed to parse board string:\n\"%s\""), string);\r
3339         DisplayError(str, 0);\r
3340         return;\r
3341     }\r
3342 \r
3343     /* Convert the move number to internal form */\r
3344     moveNum = (moveNum - 1) * 2;\r
3345     if (to_play == 'B') moveNum++;\r
3346     if (moveNum >= MAX_MOVES) {\r
3347       DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),\r
3348                         0, 1);\r
3349       return;\r
3350     }\r
3351     \r
3352     switch (relation) {\r
3353       case RELATION_OBSERVING_PLAYED:\r
3354       case RELATION_OBSERVING_STATIC:\r
3355         if (gamenum == -1) {\r
3356             /* Old ICC buglet */\r
3357             relation = RELATION_OBSERVING_STATIC;\r
3358         }\r
3359         newGameMode = IcsObserving;\r
3360         break;\r
3361       case RELATION_PLAYING_MYMOVE:\r
3362       case RELATION_PLAYING_NOTMYMOVE:\r
3363         newGameMode =\r
3364           ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?\r
3365             IcsPlayingWhite : IcsPlayingBlack;\r
3366         break;\r
3367       case RELATION_EXAMINING:\r
3368         newGameMode = IcsExamining;\r
3369         break;\r
3370       case RELATION_ISOLATED_BOARD:\r
3371       default:\r
3372         /* Just display this board.  If user was doing something else,\r
3373            we will forget about it until the next board comes. */ \r
3374         newGameMode = IcsIdle;\r
3375         break;\r
3376       case RELATION_STARTING_POSITION:\r
3377         newGameMode = gameMode;\r
3378         break;\r
3379     }\r
3380     \r
3381     /* Modify behavior for initial board display on move listing\r
3382        of wild games.\r
3383        */\r
3384     switch (ics_getting_history) {\r
3385       case H_FALSE:\r
3386       case H_REQUESTED:\r
3387         break;\r
3388       case H_GOT_REQ_HEADER:\r
3389       case H_GOT_UNREQ_HEADER:\r
3390         /* This is the initial position of the current game */\r
3391         gamenum = ics_gamenum;\r
3392         moveNum = 0;            /* old ICS bug workaround */\r
3393         if (to_play == 'B') {\r
3394           startedFromSetupPosition = TRUE;\r
3395           blackPlaysFirst = TRUE;\r
3396           moveNum = 1;\r
3397           if (forwardMostMove == 0) forwardMostMove = 1;\r
3398           if (backwardMostMove == 0) backwardMostMove = 1;\r
3399           if (currentMove == 0) currentMove = 1;\r
3400         }\r
3401         newGameMode = gameMode;\r
3402         relation = RELATION_STARTING_POSITION; /* ICC needs this */\r
3403         break;\r
3404       case H_GOT_UNWANTED_HEADER:\r
3405         /* This is an initial board that we don't want */\r
3406         return;\r
3407       case H_GETTING_MOVES:\r
3408         /* Should not happen */\r
3409         DisplayError(_("Error gathering move list: extra board"), 0);\r
3410         ics_getting_history = H_FALSE;\r
3411         return;\r
3412     }\r
3413     \r
3414     /* Take action if this is the first board of a new game, or of a\r
3415        different game than is currently being displayed.  */\r
3416     if (gamenum != ics_gamenum || newGameMode != gameMode ||\r
3417         relation == RELATION_ISOLATED_BOARD) {\r
3418         \r
3419         /* Forget the old game and get the history (if any) of the new one */\r
3420         if (gameMode != BeginningOfGame) {\r
3421           Reset(FALSE, TRUE);\r
3422         }\r
3423         newGame = TRUE;\r
3424         if (appData.autoRaiseBoard) BoardToTop();\r
3425         prevMove = -3;\r
3426         if (gamenum == -1) {\r
3427             newGameMode = IcsIdle;\r
3428         } else if (moveNum > 0 && newGameMode != IcsIdle &&\r
3429                    appData.getMoveList) {\r
3430             /* Need to get game history */\r
3431             ics_getting_history = H_REQUESTED;\r
3432             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);\r
3433             SendToICS(str);\r
3434         }\r
3435         \r
3436         /* Initially flip the board to have black on the bottom if playing\r
3437            black or if the ICS flip flag is set, but let the user change\r
3438            it with the Flip View button. */\r
3439         flipView = appData.autoFlipView ? \r
3440           (newGameMode == IcsPlayingBlack) || ics_flip :\r
3441           appData.flipView;\r
3442         \r
3443         /* Done with values from previous mode; copy in new ones */\r
3444         gameMode = newGameMode;\r
3445         ModeHighlight();\r
3446         ics_gamenum = gamenum;\r
3447         if (gamenum == gs_gamenum) {\r
3448             int klen = strlen(gs_kind);\r
3449             if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;\r
3450             sprintf(str, "ICS %s", gs_kind);\r
3451             gameInfo.event = StrSave(str);\r
3452         } else {\r
3453             gameInfo.event = StrSave("ICS game");\r
3454         }\r
3455         gameInfo.site = StrSave(appData.icsHost);\r
3456         gameInfo.date = PGNDate();\r
3457         gameInfo.round = StrSave("-");\r
3458         gameInfo.white = StrSave(white);\r
3459         gameInfo.black = StrSave(black);\r
3460         timeControl = basetime * 60 * 1000;\r
3461         timeControl_2 = 0;\r
3462         timeIncrement = increment * 1000;\r
3463         movesPerSession = 0;\r
3464         gameInfo.timeControl = TimeControlTagValue();\r
3465         VariantSwitch(board, StringToVariant(gameInfo.event) );\r
3466   if (appData.debugMode) {\r
3467     fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event);\r
3468     fprintf(debugFP, "recognized as %s\n", VariantName(gameInfo.variant));\r
3469     setbuf(debugFP, NULL);\r
3470   }\r
3471 \r
3472         gameInfo.outOfBook = NULL;\r
3473         \r
3474         /* Do we have the ratings? */\r
3475         if (strcmp(player1Name, white) == 0 &&\r
3476             strcmp(player2Name, black) == 0) {\r
3477             if (appData.debugMode)\r
3478               fprintf(debugFP, "Remembered ratings: W %d, B %d\n",\r
3479                       player1Rating, player2Rating);\r
3480             gameInfo.whiteRating = player1Rating;\r
3481             gameInfo.blackRating = player2Rating;\r
3482         } else if (strcmp(player2Name, white) == 0 &&\r
3483                    strcmp(player1Name, black) == 0) {\r
3484             if (appData.debugMode)\r
3485               fprintf(debugFP, "Remembered ratings: W %d, B %d\n",\r
3486                       player2Rating, player1Rating);\r
3487             gameInfo.whiteRating = player2Rating;\r
3488             gameInfo.blackRating = player1Rating;\r
3489         }\r
3490         player1Name[0] = player2Name[0] = NULLCHAR;\r
3491 \r
3492