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