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