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