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