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