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