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