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