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