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