Maintainence to support all compilers.
[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 void DisplayAnalysis P((void));
188
189 void ParseGameHistory P((char *game));
190 void ParseBoard12 P((char *string));
191 void StartClocks P((void));
192 void SwitchClocks P((void));
193 void StopClocks P((void));
194 void ResetClocks P((void));
195 char *PGNDate P((void));
196 void SetGameInfo P((void));
197 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
198 int RegisterMove P((void));
199 void MakeRegisteredMove P((void));
200 void TruncateGame P((void));
201 int looking_at P((char *, int *, char *));
202 void CopyPlayerNameIntoFileName P((char **, char *));
203 char *SavePart P((char *));
204 int SaveGameOldStyle P((FILE *));
205 int SaveGamePGN P((FILE *));
206 void GetTimeMark P((TimeMark *));
207 long SubtractTimeMarks P((TimeMark *, TimeMark *));
208 int CheckFlags P((void));
209 long NextTickLength P((long));
210 void CheckTimeControl P((void));
211 void show_bytes P((FILE *, char *, int));
212 int string_to_rating P((char *str));
213 void ParseFeatures P((char* args, ChessProgramState *cps));
214 void InitBackEnd3 P((void));
215 void FeatureDone P((ChessProgramState* cps, int val));
216 void InitChessProgram P((ChessProgramState *cps, int setup));
217 void OutputKibitz(int window, char *text);
218 int PerpetualChase(int first, int last);
219 int EngineOutputIsUp();
220 void InitDrawingSizes(int x, int y);
221
222 #ifdef WIN32
223        extern void ConsoleCreate();
224 #endif
225
226 ChessProgramState *WhitePlayer();
227 void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c
228 int VerifyDisplayMode P(());
229
230 char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment
231 void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c
232 char *ProbeBook P((int moveNr, char *book)); // [HGM] book: returns a book move
233 char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book
234 void ics_update_width P((int new_width));
235 extern char installDir[MSG_SIZ];
236
237 extern int tinyLayout, smallLayout;
238 ChessProgramStats programStats;
239 static int exiting = 0; /* [HGM] moved to top */
240 static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;
241 int startedFromPositionFile = FALSE; Board filePosition;       /* [HGM] loadPos */
242 char endingGame = 0;    /* [HGM] crash: flag to prevent recursion of GameEnds() */
243 int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS     */
244 VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */
245 int lastIndex = 0;      /* [HGM] autoinc: last game/position used in match mode */
246 int opponentKibitzes;
247 int lastSavedGame; /* [HGM] save: ID of game */
248 char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */
249 extern int chatCount;
250 int chattingPartner;
251
252 /* States for ics_getting_history */
253 #define H_FALSE 0
254 #define H_REQUESTED 1
255 #define H_GOT_REQ_HEADER 2
256 #define H_GOT_UNREQ_HEADER 3
257 #define H_GETTING_MOVES 4
258 #define H_GOT_UNWANTED_HEADER 5
259
260 /* whosays values for GameEnds */
261 #define GE_ICS 0
262 #define GE_ENGINE 1
263 #define GE_PLAYER 2
264 #define GE_FILE 3
265 #define GE_XBOARD 4
266 #define GE_ENGINE1 5
267 #define GE_ENGINE2 6
268
269 /* Maximum number of games in a cmail message */
270 #define CMAIL_MAX_GAMES 20
271
272 /* Different types of move when calling RegisterMove */
273 #define CMAIL_MOVE   0
274 #define CMAIL_RESIGN 1
275 #define CMAIL_DRAW   2
276 #define CMAIL_ACCEPT 3
277
278 /* Different types of result to remember for each game */
279 #define CMAIL_NOT_RESULT 0
280 #define CMAIL_OLD_RESULT 1
281 #define CMAIL_NEW_RESULT 2
282
283 /* Telnet protocol constants */
284 #define TN_WILL 0373
285 #define TN_WONT 0374
286 #define TN_DO   0375
287 #define TN_DONT 0376
288 #define TN_IAC  0377
289 #define TN_ECHO 0001
290 #define TN_SGA  0003
291 #define TN_PORT 23
292
293 /* [AS] */
294 static char * safeStrCpy( char * dst, const char * src, size_t count )
295 {
296     assert( dst != NULL );
297     assert( src != NULL );
298     assert( count > 0 );
299
300     strncpy( dst, src, count );
301     dst[ count-1 ] = '\0';
302     return dst;
303 }
304
305 /* Some compiler can't cast u64 to double
306  * This function do the job for us:
307
308  * We use the highest bit for cast, this only
309  * works if the highest bit is not
310  * in use (This should not happen)
311  *
312  * We used this for all compiler
313  */
314 double
315 u64ToDouble(u64 value)
316 {
317   double r;
318   u64 tmp = value & u64Const(0x7fffffffffffffff);
319   r = (double)(s64)tmp;
320   if (value & u64Const(0x8000000000000000))
321        r +=  9.2233720368547758080e18; /* 2^63 */
322  return r;
323 }
324
325 /* Fake up flags for now, as we aren't keeping track of castling
326    availability yet. [HGM] Change of logic: the flag now only
327    indicates the type of castlings allowed by the rule of the game.
328    The actual rights themselves are maintained in the array
329    castlingRights, as part of the game history, and are not probed
330    by this function.
331  */
332 int
333 PosFlags(index)
334 {
335   int flags = F_ALL_CASTLE_OK;
336   if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
337   switch (gameInfo.variant) {
338   case VariantSuicide:
339     flags &= ~F_ALL_CASTLE_OK;
340   case VariantGiveaway:         // [HGM] moved this case label one down: seems Giveaway does have castling on ICC!
341     flags |= F_IGNORE_CHECK;
342   case VariantLosers:
343     flags |= F_MANDATORY_CAPTURE; //[HGM] losers: sets flag so TestLegality rejects non-capts if capts exist
344     break;
345   case VariantAtomic:
346     flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
347     break;
348   case VariantKriegspiel:
349     flags |= F_KRIEGSPIEL_CAPTURE;
350     break;
351   case VariantCapaRandom: 
352   case VariantFischeRandom:
353     flags |= F_FRC_TYPE_CASTLING; /* [HGM] enable this through flag */
354   case VariantNoCastle:
355   case VariantShatranj:
356   case VariantCourier:
357     flags &= ~F_ALL_CASTLE_OK;
358     break;
359   default:
360     break;
361   }
362   return flags;
363 }
364
365 FILE *gameFileFP, *debugFP;
366
367 /* 
368     [AS] Note: sometimes, the sscanf() function is used to parse the input
369     into a fixed-size buffer. Because of this, we must be prepared to
370     receive strings as long as the size of the input buffer, which is currently
371     set to 4K for Windows and 8K for the rest.
372     So, we must either allocate sufficiently large buffers here, or
373     reduce the size of the input buffer in the input reading part.
374 */
375
376 char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
377 char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
378 char thinkOutput1[MSG_SIZ*10];
379
380 ChessProgramState first, second;
381
382 /* premove variables */
383 int premoveToX = 0;
384 int premoveToY = 0;
385 int premoveFromX = 0;
386 int premoveFromY = 0;
387 int premovePromoChar = 0;
388 int gotPremove = 0;
389 Boolean alarmSounded;
390 /* end premove variables */
391
392 char *ics_prefix = "$";
393 int ics_type = ICS_GENERIC;
394
395 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
396 int pauseExamForwardMostMove = 0;
397 int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
398 int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
399 int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
400 int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
401 int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
402 int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
403 int whiteFlag = FALSE, blackFlag = FALSE;
404 int userOfferedDraw = FALSE;
405 int ics_user_moved = 0, ics_gamenum = -1, ics_getting_history = H_FALSE;
406 int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
407 int cmailMoveType[CMAIL_MAX_GAMES];
408 long ics_clock_paused = 0;
409 ProcRef icsPR = NoProc, cmailPR = NoProc;
410 InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
411 GameMode gameMode = BeginningOfGame;
412 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
413 char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
414 ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */
415 int hiddenThinkOutputState = 0; /* [AS] */
416 int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */
417 int adjudicateLossPlies = 6;
418 char white_holding[64], black_holding[64];
419 TimeMark lastNodeCountTime;
420 long lastNodeCount=0;
421 int have_sent_ICS_logon = 0;
422 int movesPerSession;
423 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
424 long timeControl_2; /* [AS] Allow separate time controls */
425 char *fullTimeControlString = NULL; /* [HGM] secondary TC: merge of MPS, TC and inc */
426 long timeRemaining[2][MAX_MOVES];
427 int matchGame = 0;
428 TimeMark programStartTime;
429 char ics_handle[MSG_SIZ];
430 int have_set_title = 0;
431
432 /* animateTraining preserves the state of appData.animate
433  * when Training mode is activated. This allows the
434  * response to be animated when appData.animate == TRUE and
435  * appData.animateDragging == TRUE.
436  */
437 Boolean animateTraining;
438
439 GameInfo gameInfo;
440
441 AppData appData;
442
443 Board boards[MAX_MOVES];
444 /* [HGM] Following 7 needed for accurate legality tests: */
445 signed char  epStatus[MAX_MOVES];
446 signed char  castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1
447 signed char  castlingRank[BOARD_SIZE]; // and corresponding ranks
448 signed char  initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE], fileRights[BOARD_SIZE];
449 int   nrCastlingRights; // For TwoKings, or to implement castling-unknown status
450 int   initialRulePlies, FENrulePlies;
451 char  FENepStatus;
452 FILE  *serverMoves = NULL; // next two for broadcasting (/serverMoves option)
453 int loadFlag = 0; 
454 int shuffleOpenings;
455 int mute; // mute all sounds
456
457 ChessSquare  FIDEArray[2][BOARD_SIZE] = {
458     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
459         WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
460     { BlackRook, BlackKnight, BlackBishop, BlackQueen,
461         BlackKing, BlackBishop, BlackKnight, BlackRook }
462 };
463
464 ChessSquare twoKingsArray[2][BOARD_SIZE] = {
465     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
466         WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
467     { BlackRook, BlackKnight, BlackBishop, BlackQueen,
468         BlackKing, BlackKing, BlackKnight, BlackRook }
469 };
470
471 ChessSquare  KnightmateArray[2][BOARD_SIZE] = {
472     { WhiteRook, WhiteMan, WhiteBishop, WhiteQueen,
473         WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook },
474     { BlackRook, BlackMan, BlackBishop, BlackQueen,
475         BlackUnicorn, BlackBishop, BlackMan, BlackRook }
476 };
477
478 ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */
479     { WhiteCannon, WhiteNightrider, WhiteAlfil, WhiteQueen,
480         WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
481     { BlackCannon, BlackNightrider, BlackAlfil, BlackQueen,
482         BlackKing, BlackBishop, BlackKnight, BlackRook }
483 };
484
485 ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */
486     { WhiteRook, WhiteKnight, WhiteAlfil, WhiteKing,
487         WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },
488     { BlackRook, BlackKnight, BlackAlfil, BlackKing,
489         BlackFerz, BlackAlfil, BlackKnight, BlackRook }
490 };
491
492
493 #if (BOARD_SIZE>=10)
494 ChessSquare ShogiArray[2][BOARD_SIZE] = {
495     { WhiteQueen, WhiteKnight, WhiteFerz, WhiteWazir,
496         WhiteKing, WhiteWazir, WhiteFerz, WhiteKnight, WhiteQueen },
497     { BlackQueen, BlackKnight, BlackFerz, BlackWazir,
498         BlackKing, BlackWazir, BlackFerz, BlackKnight, BlackQueen }
499 };
500
501 ChessSquare XiangqiArray[2][BOARD_SIZE] = {
502     { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz,
503         WhiteWazir, WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },
504     { BlackRook, BlackKnight, BlackAlfil, BlackFerz,
505         BlackWazir, BlackFerz, BlackAlfil, BlackKnight, BlackRook }
506 };
507
508 ChessSquare CapablancaArray[2][BOARD_SIZE] = {
509     { WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen, 
510         WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },
511     { BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen, 
512         BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }
513 };
514
515 ChessSquare GreatArray[2][BOARD_SIZE] = {
516     { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing, 
517         WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },
518     { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing, 
519         BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },
520 };
521
522 ChessSquare JanusArray[2][BOARD_SIZE] = {
523     { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing, 
524         WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },
525     { BlackRook, BlackAngel, BlackKnight, BlackBishop, BlackKing, 
526         BlackQueen, BlackBishop, BlackKnight, BlackAngel, BlackRook }
527 };
528
529 #ifdef GOTHIC
530 ChessSquare GothicArray[2][BOARD_SIZE] = {
531     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall, 
532         WhiteKing, WhiteAngel, WhiteBishop, WhiteKnight, WhiteRook },
533     { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall, 
534         BlackKing, BlackAngel, BlackBishop, BlackKnight, BlackRook }
535 };
536 #else // !GOTHIC
537 #define GothicArray CapablancaArray
538 #endif // !GOTHIC
539
540 #ifdef FALCON
541 ChessSquare FalconArray[2][BOARD_SIZE] = {
542     { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen, 
543         WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },
544     { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen, 
545         BlackKing, BlackLance, BlackBishop, BlackKnight, BlackRook }
546 };
547 #else // !FALCON
548 #define FalconArray CapablancaArray
549 #endif // !FALCON
550
551 #else // !(BOARD_SIZE>=10)
552 #define XiangqiPosition FIDEArray
553 #define CapablancaArray FIDEArray
554 #define GothicArray FIDEArray
555 #define GreatArray FIDEArray
556 #endif // !(BOARD_SIZE>=10)
557
558 #if (BOARD_SIZE>=12)
559 ChessSquare CourierArray[2][BOARD_SIZE] = {
560     { WhiteRook, WhiteKnight, WhiteAlfil, WhiteBishop, WhiteMan, WhiteKing,
561         WhiteFerz, WhiteWazir, WhiteBishop, WhiteAlfil, WhiteKnight, WhiteRook },
562     { BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing,
563         BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook }
564 };
565 #else // !(BOARD_SIZE>=12)
566 #define CourierArray CapablancaArray
567 #endif // !(BOARD_SIZE>=12)
568
569
570 Board initialPosition;
571
572
573 /* Convert str to a rating. Checks for special cases of "----",
574
575    "++++", etc. Also strips ()'s */
576 int
577 string_to_rating(str)
578   char *str;
579 {
580   while(*str && !isdigit(*str)) ++str;
581   if (!*str)
582     return 0;   /* One of the special "no rating" cases */
583   else
584     return atoi(str);
585 }
586
587 void
588 ClearProgramStats()
589 {
590     /* Init programStats */
591     programStats.movelist[0] = 0;
592     programStats.depth = 0;
593     programStats.nr_moves = 0;
594     programStats.moves_left = 0;
595     programStats.nodes = 0;
596     programStats.time = -1;        // [HGM] PGNtime: make invalid to recognize engine output
597     programStats.score = 0;
598     programStats.got_only_move = 0;
599     programStats.got_fail = 0;
600     programStats.line_is_book = 0;
601 }
602
603 void
604 InitBackEnd1()
605 {
606     int matched, min, sec;
607
608     ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options
609
610     GetTimeMark(&programStartTime);
611     srand(programStartTime.ms); // [HGM] book: makes sure random is unpredictabe to msec level
612
613     ClearProgramStats();
614     programStats.ok_to_send = 1;
615     programStats.seen_stat = 0;
616
617     /*
618      * Initialize game list
619      */
620     ListNew(&gameList);
621
622
623     /*
624      * Internet chess server status
625      */
626     if (appData.icsActive) {
627         appData.matchMode = FALSE;
628         appData.matchGames = 0;
629 #if ZIPPY       
630         appData.noChessProgram = !appData.zippyPlay;
631 #else
632         appData.zippyPlay = FALSE;
633         appData.zippyTalk = FALSE;
634         appData.noChessProgram = TRUE;
635 #endif
636         if (*appData.icsHelper != NULLCHAR) {
637             appData.useTelnet = TRUE;
638             appData.telnetProgram = appData.icsHelper;
639         }
640     } else {
641         appData.zippyTalk = appData.zippyPlay = FALSE;
642     }
643
644     /* [AS] Initialize pv info list [HGM] and game state */
645     {
646         int i, j;
647
648         for( i=0; i<MAX_MOVES; i++ ) {
649             pvInfoList[i].depth = -1;
650             epStatus[i]=EP_NONE;
651             for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;
652         }
653     }
654
655     /*
656      * Parse timeControl resource
657      */
658     if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
659                           appData.movesPerSession)) {
660         char buf[MSG_SIZ];
661         snprintf(buf, sizeof(buf), _("bad timeControl option %s"), appData.timeControl);
662         DisplayFatalError(buf, 0, 2);
663     }
664
665     /*
666      * Parse searchTime resource
667      */
668     if (*appData.searchTime != NULLCHAR) {
669         matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
670         if (matched == 1) {
671             searchTime = min * 60;
672         } else if (matched == 2) {
673             searchTime = min * 60 + sec;
674         } else {
675             char buf[MSG_SIZ];
676             snprintf(buf, sizeof(buf), _("bad searchTime option %s"), appData.searchTime);
677             DisplayFatalError(buf, 0, 2);
678         }
679     }
680
681     /* [AS] Adjudication threshold */
682     adjudicateLossThreshold = appData.adjudicateLossThreshold;
683     
684     first.which = "first";
685     second.which = "second";
686     first.maybeThinking = second.maybeThinking = FALSE;
687     first.pr = second.pr = NoProc;
688     first.isr = second.isr = NULL;
689     first.sendTime = second.sendTime = 2;
690     first.sendDrawOffers = 1;
691     if (appData.firstPlaysBlack) {
692         first.twoMachinesColor = "black\n";
693         second.twoMachinesColor = "white\n";
694     } else {
695         first.twoMachinesColor = "white\n";
696         second.twoMachinesColor = "black\n";
697     }
698     first.program = appData.firstChessProgram;
699     second.program = appData.secondChessProgram;
700     first.host = appData.firstHost;
701     second.host = appData.secondHost;
702     first.dir = appData.firstDirectory;
703     second.dir = appData.secondDirectory;
704     first.other = &second;
705     second.other = &first;
706     first.initString = appData.initString;
707     second.initString = appData.secondInitString;
708     first.computerString = appData.firstComputerString;
709     second.computerString = appData.secondComputerString;
710     first.useSigint = second.useSigint = TRUE;
711     first.useSigterm = second.useSigterm = TRUE;
712     first.reuse = appData.reuseFirst;
713     second.reuse = appData.reuseSecond;
714     first.nps = appData.firstNPS;   // [HGM] nps: copy nodes per second
715     second.nps = appData.secondNPS;
716     first.useSetboard = second.useSetboard = FALSE;
717     first.useSAN = second.useSAN = FALSE;
718     first.usePing = second.usePing = FALSE;
719     first.lastPing = second.lastPing = 0;
720     first.lastPong = second.lastPong = 0;
721     first.usePlayother = second.usePlayother = FALSE;
722     first.useColors = second.useColors = TRUE;
723     first.useUsermove = second.useUsermove = FALSE;
724     first.sendICS = second.sendICS = FALSE;
725     first.sendName = second.sendName = appData.icsActive;
726     first.sdKludge = second.sdKludge = FALSE;
727     first.stKludge = second.stKludge = FALSE;
728     TidyProgramName(first.program, first.host, first.tidy);
729     TidyProgramName(second.program, second.host, second.tidy);
730     first.matchWins = second.matchWins = 0;
731     strcpy(first.variants, appData.variant);
732     strcpy(second.variants, appData.variant);
733     first.analysisSupport = second.analysisSupport = 2; /* detect */
734     first.analyzing = second.analyzing = FALSE;
735     first.initDone = second.initDone = FALSE;
736
737     /* New features added by Tord: */
738     first.useFEN960 = FALSE; second.useFEN960 = FALSE;
739     first.useOOCastle = TRUE; second.useOOCastle = TRUE;
740     /* End of new features added by Tord. */
741     first.fenOverride  = appData.fenOverride1;
742     second.fenOverride = appData.fenOverride2;
743
744     /* [HGM] time odds: set factor for each machine */
745     first.timeOdds  = appData.firstTimeOdds;
746     second.timeOdds = appData.secondTimeOdds;
747     { int norm = 1;
748         if(appData.timeOddsMode) {
749             norm = first.timeOdds;
750             if(norm > second.timeOdds) norm = second.timeOdds;
751         }
752         first.timeOdds /= norm;
753         second.timeOdds /= norm;
754     }
755
756     /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/
757     first.accumulateTC = appData.firstAccumulateTC;
758     second.accumulateTC = appData.secondAccumulateTC;
759     first.maxNrOfSessions = second.maxNrOfSessions = 1;
760
761     /* [HGM] debug */
762     first.debug = second.debug = FALSE;
763     first.supportsNPS = second.supportsNPS = UNKNOWN;
764
765     /* [HGM] options */
766     first.optionSettings  = appData.firstOptions;
767     second.optionSettings = appData.secondOptions;
768
769     first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */
770     second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */
771     first.isUCI = appData.firstIsUCI; /* [AS] */
772     second.isUCI = appData.secondIsUCI; /* [AS] */
773     first.hasOwnBookUCI = appData.firstHasOwnBookUCI; /* [AS] */
774     second.hasOwnBookUCI = appData.secondHasOwnBookUCI; /* [AS] */
775
776     if (appData.firstProtocolVersion > PROTOVER ||
777         appData.firstProtocolVersion < 1) {
778       char buf[MSG_SIZ];
779       sprintf(buf, _("protocol version %d not supported"),
780               appData.firstProtocolVersion);
781       DisplayFatalError(buf, 0, 2);
782     } else {
783       first.protocolVersion = appData.firstProtocolVersion;
784     }
785
786     if (appData.secondProtocolVersion > PROTOVER ||
787         appData.secondProtocolVersion < 1) {
788       char buf[MSG_SIZ];
789       sprintf(buf, _("protocol version %d not supported"),
790               appData.secondProtocolVersion);
791       DisplayFatalError(buf, 0, 2);
792     } else {
793       second.protocolVersion = appData.secondProtocolVersion;
794     }
795
796     if (appData.icsActive) {
797         appData.clockMode = TRUE;  /* changes dynamically in ICS mode */
798     } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
799         appData.clockMode = FALSE;
800         first.sendTime = second.sendTime = 0;
801     }
802     
803 #if ZIPPY
804     /* Override some settings from environment variables, for backward
805        compatibility.  Unfortunately it's not feasible to have the env
806        vars just set defaults, at least in xboard.  Ugh.
807     */
808     if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
809       ZippyInit();
810     }
811 #endif
812     
813     if (appData.noChessProgram) {
814         programVersion = (char*) malloc(5 + strlen(PACKAGE_STRING));
815         sprintf(programVersion, "%s", PACKAGE_STRING);
816     } else {
817       /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */
818       programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
819       sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
820     }
821
822     if (!appData.icsActive) {
823       char buf[MSG_SIZ];
824       /* Check for variants that are supported only in ICS mode,
825          or not at all.  Some that are accepted here nevertheless
826          have bugs; see comments below.
827       */
828       VariantClass variant = StringToVariant(appData.variant);
829       switch (variant) {
830       case VariantBughouse:     /* need four players and two boards */
831       case VariantKriegspiel:   /* need to hide pieces and move details */
832       /* case VariantFischeRandom: (Fabien: moved below) */
833         sprintf(buf, _("Variant %s supported only in ICS mode"), appData.variant);
834         DisplayFatalError(buf, 0, 2);
835         return;
836
837       case VariantUnknown:
838       case VariantLoadable:
839       case Variant29:
840       case Variant30:
841       case Variant31:
842       case Variant32:
843       case Variant33:
844       case Variant34:
845       case Variant35:
846       case Variant36:
847       default:
848         sprintf(buf, _("Unknown variant name %s"), appData.variant);
849         DisplayFatalError(buf, 0, 2);
850         return;
851
852       case VariantXiangqi:    /* [HGM] repetition rules not implemented */
853       case VariantFairy:      /* [HGM] TestLegality definitely off! */
854       case VariantGothic:     /* [HGM] should work */
855       case VariantCapablanca: /* [HGM] should work */
856       case VariantCourier:    /* [HGM] initial forced moves not implemented */
857       case VariantShogi:      /* [HGM] drops not tested for legality */
858       case VariantKnightmate: /* [HGM] should work */
859       case VariantCylinder:   /* [HGM] untested */
860       case VariantFalcon:     /* [HGM] untested */
861       case VariantCrazyhouse: /* holdings not shown, ([HGM] fixed that!)
862                                  offboard interposition not understood */
863       case VariantNormal:     /* definitely works! */
864       case VariantWildCastle: /* pieces not automatically shuffled */
865       case VariantNoCastle:   /* pieces not automatically shuffled */
866       case VariantFischeRandom: /* [HGM] works and shuffles pieces */
867       case VariantLosers:     /* should work except for win condition,
868                                  and doesn't know captures are mandatory */
869       case VariantSuicide:    /* should work except for win condition,
870                                  and doesn't know captures are mandatory */
871       case VariantGiveaway:   /* should work except for win condition,
872                                  and doesn't know captures are mandatory */
873       case VariantTwoKings:   /* should work */
874       case VariantAtomic:     /* should work except for win condition */
875       case Variant3Check:     /* should work except for win condition */
876       case VariantShatranj:   /* should work except for all win conditions */
877       case VariantBerolina:   /* might work if TestLegality is off */
878       case VariantCapaRandom: /* should work */
879       case VariantJanus:      /* should work */
880       case VariantSuper:      /* experimental */
881       case VariantGreat:      /* experimental, requires legality testing to be off */
882         break;
883       }
884     }
885
886     InitEngineUCI( installDir, &first );  // [HGM] moved here from winboard.c, to make available in xboard
887     InitEngineUCI( installDir, &second );
888 }
889
890 int NextIntegerFromString( char ** str, long * value )
891 {
892     int result = -1;
893     char * s = *str;
894
895     while( *s == ' ' || *s == '\t' ) {
896         s++;
897     }
898
899     *value = 0;
900
901     if( *s >= '0' && *s <= '9' ) {
902         while( *s >= '0' && *s <= '9' ) {
903             *value = *value * 10 + (*s - '0');
904             s++;
905         }
906
907         result = 0;
908     }
909
910     *str = s;
911
912     return result;
913 }
914
915 int NextTimeControlFromString( char ** str, long * value )
916 {
917     long temp;
918     int result = NextIntegerFromString( str, &temp );
919
920     if( result == 0 ) {
921         *value = temp * 60; /* Minutes */
922         if( **str == ':' ) {
923             (*str)++;
924             result = NextIntegerFromString( str, &temp );
925             *value += temp; /* Seconds */
926         }
927     }
928
929     return result;
930 }
931
932 int NextSessionFromString( char ** str, int *moves, long * tc, long *inc)
933 {   /* [HGM] routine added to read '+moves/time' for secondary time control */
934     int result = -1; long temp, temp2;
935
936     if(**str != '+') return -1; // old params remain in force!
937     (*str)++;
938     if( NextTimeControlFromString( str, &temp ) ) return -1;
939
940     if(**str != '/') {
941         /* time only: incremental or sudden-death time control */
942         if(**str == '+') { /* increment follows; read it */
943             (*str)++;
944             if(result = NextIntegerFromString( str, &temp2)) return -1;
945             *inc = temp2 * 1000;
946         } else *inc = 0;
947         *moves = 0; *tc = temp * 1000; 
948         return 0;
949     } else if(temp % 60 != 0) return -1;     /* moves was given as min:sec */
950
951     (*str)++; /* classical time control */
952     result = NextTimeControlFromString( str, &temp2);
953     if(result == 0) {
954         *moves = temp/60;
955         *tc    = temp2 * 1000;
956         *inc   = 0;
957     }
958     return result;
959 }
960
961 int GetTimeQuota(int movenr)
962 {   /* [HGM] get time to add from the multi-session time-control string */
963     int moves=1; /* kludge to force reading of first session */
964     long time, increment;
965     char *s = fullTimeControlString;
966
967     if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", fullTimeControlString);
968     do {
969         if(moves) NextSessionFromString(&s, &moves, &time, &increment);
970         if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment);
971         if(movenr == -1) return time;    /* last move before new session     */
972         if(!moves) return increment;     /* current session is incremental   */
973         if(movenr >= 0) movenr -= moves; /* we already finished this session */
974     } while(movenr >= -1);               /* try again for next session       */
975
976     return 0; // no new time quota on this move
977 }
978
979 int
980 ParseTimeControl(tc, ti, mps)
981      char *tc;
982      int ti;
983      int mps;
984 {
985   long tc1;
986   long tc2;
987   char buf[MSG_SIZ];
988   
989   if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;
990   if(ti > 0) {
991     if(mps)
992       sprintf(buf, "+%d/%s+%d", mps, tc, ti);
993     else sprintf(buf, "+%s+%d", tc, ti);
994   } else {
995     if(mps)
996              sprintf(buf, "+%d/%s", mps, tc);
997     else sprintf(buf, "+%s", tc);
998   }
999   fullTimeControlString = StrSave(buf);
1000   
1001   if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
1002     return FALSE;
1003   }
1004   
1005   if( *tc == '/' ) {
1006     /* Parse second time control */
1007     tc++;
1008     
1009     if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {
1010       return FALSE;
1011     }
1012     
1013     if( tc2 == 0 ) {
1014       return FALSE;
1015     }
1016     
1017     timeControl_2 = tc2 * 1000;
1018   }
1019   else {
1020     timeControl_2 = 0;
1021   }
1022   
1023   if( tc1 == 0 ) {
1024     return FALSE;
1025   }
1026   
1027   timeControl = tc1 * 1000;
1028   
1029   if (ti >= 0) {
1030     timeIncrement = ti * 1000;  /* convert to ms */
1031     movesPerSession = 0;
1032   } else {
1033     timeIncrement = 0;
1034     movesPerSession = mps;
1035   }
1036   return TRUE;
1037 }
1038
1039 void
1040 InitBackEnd2()
1041 {
1042     if (appData.debugMode) {
1043         fprintf(debugFP, "%s\n", programVersion);
1044     }
1045
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    int oldCurrentMove = currentMove, oldForwardMostMove = forwardMostMove, oldBackwardMostMove = backwardMostMove;
1944 //   Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;
1945
1946    startedFromPositionFile = FALSE;
1947    if(gameInfo.variant == newVariant) return;
1948
1949    /* [HGM] This routine is called each time an assignment is made to
1950     * gameInfo.variant during a game, to make sure the board sizes
1951     * are set to match the new variant. If that means adding or deleting
1952     * holdings, we shift the playing board accordingly
1953     * This kludge is needed because in ICS observe mode, we get boards
1954     * of an ongoing game without knowing the variant, and learn about the
1955     * latter only later. This can be because of the move list we requested,
1956     * in which case the game history is refilled from the beginning anyway,
1957     * but also when receiving holdings of a crazyhouse game. In the latter
1958     * case we want to add those holdings to the already received position.
1959     */
1960
1961
1962   if (appData.debugMode) {
1963     fprintf(debugFP, "Switch board from %s to %s\n",
1964                VariantName(gameInfo.variant), VariantName(newVariant));
1965     setbuf(debugFP, NULL);
1966   }
1967     shuffleOpenings = 0;       /* [HGM] shuffle */
1968     gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */
1969     switch(newVariant) {
1970             case VariantShogi:
1971               newWidth = 9;  newHeight = 9;
1972               gameInfo.holdingsSize = 7;
1973             case VariantBughouse:
1974             case VariantCrazyhouse:
1975               newHoldingsWidth = 2; break;
1976             default:
1977               newHoldingsWidth = gameInfo.holdingsSize = 0;
1978     }
1979
1980     if(newWidth  != gameInfo.boardWidth  ||
1981        newHeight != gameInfo.boardHeight ||
1982        newHoldingsWidth != gameInfo.holdingsWidth ) {
1983
1984         /* shift position to new playing area, if needed */
1985         if(newHoldingsWidth > gameInfo.holdingsWidth) {
1986            for(i=0; i<BOARD_HEIGHT; i++) 
1987                for(j=BOARD_RGHT-1; j>=BOARD_LEFT; j--)
1988                    board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
1989                                                      board[i][j];
1990            for(i=0; i<newHeight; i++) {
1991                board[i][0] = board[i][newWidth+2*newHoldingsWidth-1] = EmptySquare;
1992                board[i][1] = board[i][newWidth+2*newHoldingsWidth-2] = (ChessSquare) 0;
1993            }
1994         } else if(newHoldingsWidth < gameInfo.holdingsWidth) {
1995            for(i=0; i<BOARD_HEIGHT; i++)
1996                for(j=BOARD_LEFT; j<BOARD_RGHT; j++)
1997                    board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =
1998                                                  board[i][j];
1999         }
2000
2001         gameInfo.boardWidth  = newWidth;
2002         gameInfo.boardHeight = newHeight;
2003         gameInfo.holdingsWidth = newHoldingsWidth;
2004         gameInfo.variant = newVariant;
2005         InitDrawingSizes(-2, 0);
2006
2007         /* [HGM] The following should definitely be solved in a better way */
2008         InitPosition(FALSE);          /* this sets up board[0], but also other stuff        */
2009     } else { gameInfo.variant = newVariant; InitPosition(FALSE); }
2010
2011     forwardMostMove = oldForwardMostMove;
2012     backwardMostMove = oldBackwardMostMove;
2013     currentMove = oldCurrentMove; /* InitPos reset these, but we need still to redraw the position */
2014 }
2015
2016 static int loggedOn = FALSE;
2017
2018 /*-- Game start info cache: --*/
2019 int gs_gamenum;
2020 char gs_kind[MSG_SIZ];
2021 static char player1Name[128] = "";
2022 static char player2Name[128] = "";
2023 static int player1Rating = -1;
2024 static int player2Rating = -1;
2025 /*----------------------------*/
2026
2027 ColorClass curColor = ColorNormal;
2028 int suppressKibitz = 0;
2029
2030 void
2031 read_from_ics(isr, closure, data, count, error)
2032      InputSourceRef isr;
2033      VOIDSTAR closure;
2034      char *data;
2035      int count;
2036      int error;
2037 {
2038 #define BUF_SIZE 8192
2039 #define STARTED_NONE 0
2040 #define STARTED_MOVES 1
2041 #define STARTED_BOARD 2
2042 #define STARTED_OBSERVE 3
2043 #define STARTED_HOLDINGS 4
2044 #define STARTED_CHATTER 5
2045 #define STARTED_COMMENT 6
2046 #define STARTED_MOVES_NOHIDE 7
2047     
2048     static int started = STARTED_NONE;
2049     static char parse[20000];
2050     static int parse_pos = 0;
2051     static char buf[BUF_SIZE + 1];
2052     static int firstTime = TRUE, intfSet = FALSE;
2053     static ColorClass prevColor = ColorNormal;
2054     static int savingComment = FALSE;
2055     char str[500];
2056     int i, oldi;
2057     int buf_len;
2058     int next_out;
2059     int tkind;
2060     int backup;    /* [DM] For zippy color lines */
2061     char *p;
2062     char talker[MSG_SIZ]; // [HGM] chat
2063     int channel;
2064
2065     if (appData.debugMode) {
2066       if (!error) {
2067         fprintf(debugFP, "<ICS: ");
2068         show_bytes(debugFP, data, count);
2069         fprintf(debugFP, "\n");
2070       }
2071     }
2072
2073     if (appData.debugMode) { int f = forwardMostMove;
2074         fprintf(debugFP, "ics input %d, castling = %d %d %d %d %d %d\n", f,
2075                 castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);
2076     }
2077     if (count > 0) {
2078         /* If last read ended with a partial line that we couldn't parse,
2079            prepend it to the new read and try again. */
2080         if (leftover_len > 0) {
2081             for (i=0; i<leftover_len; i++)
2082               buf[i] = buf[leftover_start + i];
2083         }
2084
2085         /* Copy in new characters, removing nulls and \r's */
2086         buf_len = leftover_len;
2087         for (i = 0; i < count; i++) {
2088             if (data[i] != NULLCHAR && data[i] != '\r')
2089               buf[buf_len++] = data[i];
2090             if(!appData.noJoin && buf_len >= 5 && buf[buf_len-5]=='\n' && buf[buf_len-4]=='\\' && 
2091                                buf[buf_len-3]==' '  && buf[buf_len-2]==' '  && buf[buf_len-1]==' ') {
2092                 buf_len -= 5; // [HGM] ICS: join continuation line of Lasker 2.2.3 server with previous
2093                 if(buf_len == 0 || buf[buf_len-1] != ' ')
2094                    buf[buf_len++] = ' '; // add space (assumes ICS does not break lines within word)
2095             }
2096         }
2097
2098         buf[buf_len] = NULLCHAR;
2099         next_out = leftover_len;
2100         leftover_start = 0;
2101         
2102         i = 0;
2103         while (i < buf_len) {
2104             /* Deal with part of the TELNET option negotiation
2105                protocol.  We refuse to do anything beyond the
2106                defaults, except that we allow the WILL ECHO option,
2107                which ICS uses to turn off password echoing when we are
2108                directly connected to it.  We reject this option
2109                if localLineEditing mode is on (always on in xboard)
2110                and we are talking to port 23, which might be a real
2111                telnet server that will try to keep WILL ECHO on permanently.
2112              */
2113             if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
2114                 static int remoteEchoOption = FALSE; /* telnet ECHO option */
2115                 unsigned char option;
2116                 oldi = i;
2117                 switch ((unsigned char) buf[++i]) {
2118                   case TN_WILL:
2119                     if (appData.debugMode)
2120                       fprintf(debugFP, "\n<WILL ");
2121                     switch (option = (unsigned char) buf[++i]) {
2122                       case TN_ECHO:
2123                         if (appData.debugMode)
2124                           fprintf(debugFP, "ECHO ");
2125                         /* Reply only if this is a change, according
2126                            to the protocol rules. */
2127                         if (remoteEchoOption) break;
2128                         if (appData.localLineEditing &&
2129                             atoi(appData.icsPort) == TN_PORT) {
2130                             TelnetRequest(TN_DONT, TN_ECHO);
2131                         } else {
2132                             EchoOff();
2133                             TelnetRequest(TN_DO, TN_ECHO);
2134                             remoteEchoOption = TRUE;
2135                         }
2136                         break;
2137                       default:
2138                         if (appData.debugMode)
2139                           fprintf(debugFP, "%d ", option);
2140                         /* Whatever this is, we don't want it. */
2141                         TelnetRequest(TN_DONT, option);
2142                         break;
2143                     }
2144                     break;
2145                   case TN_WONT:
2146                     if (appData.debugMode)
2147                       fprintf(debugFP, "\n<WONT ");
2148                     switch (option = (unsigned char) buf[++i]) {
2149                       case TN_ECHO:
2150                         if (appData.debugMode)
2151                           fprintf(debugFP, "ECHO ");
2152                         /* Reply only if this is a change, according
2153                            to the protocol rules. */
2154                         if (!remoteEchoOption) break;
2155                         EchoOn();
2156                         TelnetRequest(TN_DONT, TN_ECHO);
2157                         remoteEchoOption = FALSE;
2158                         break;
2159                       default:
2160                         if (appData.debugMode)
2161                           fprintf(debugFP, "%d ", (unsigned char) option);
2162                         /* Whatever this is, it must already be turned
2163                            off, because we never agree to turn on
2164                            anything non-default, so according to the
2165                            protocol rules, we don't reply. */
2166                         break;
2167                     }
2168                     break;
2169                   case TN_DO:
2170                     if (appData.debugMode)
2171                       fprintf(debugFP, "\n<DO ");
2172                     switch (option = (unsigned char) buf[++i]) {
2173                       default:
2174                         /* Whatever this is, we refuse to do it. */
2175                         if (appData.debugMode)
2176                           fprintf(debugFP, "%d ", option);
2177                         TelnetRequest(TN_WONT, option);
2178                         break;
2179                     }
2180                     break;
2181                   case TN_DONT:
2182                     if (appData.debugMode)
2183                       fprintf(debugFP, "\n<DONT ");
2184                     switch (option = (unsigned char) buf[++i]) {
2185                       default:
2186                         if (appData.debugMode)
2187                           fprintf(debugFP, "%d ", option);
2188                         /* Whatever this is, we are already not doing
2189                            it, because we never agree to do anything
2190                            non-default, so according to the protocol
2191                            rules, we don't reply. */
2192                         break;
2193                     }
2194                     break;
2195                   case TN_IAC:
2196                     if (appData.debugMode)
2197                       fprintf(debugFP, "\n<IAC ");
2198                     /* Doubled IAC; pass it through */
2199                     i--;
2200                     break;
2201                   default:
2202                     if (appData.debugMode)
2203                       fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
2204                     /* Drop all other telnet commands on the floor */
2205                     break;
2206                 }
2207                 if (oldi > next_out)
2208                   SendToPlayer(&buf[next_out], oldi - next_out);
2209                 if (++i > next_out)
2210                   next_out = i;
2211                 continue;
2212             }
2213                 
2214             /* OK, this at least will *usually* work */
2215             if (!loggedOn && looking_at(buf, &i, "ics%")) {
2216                 loggedOn = TRUE;
2217             }
2218             
2219             if (loggedOn && !intfSet) {
2220                 if (ics_type == ICS_ICC) {
2221                   sprintf(str,
2222                           "/set-quietly interface %s\n/set-quietly style 12\n",
2223                           programVersion);
2224           if (!appData.noJoin)
2225               strcat(str, "/set-quietly wrap 0\n");
2226                 } else if (ics_type == ICS_CHESSNET) {
2227                   sprintf(str, "/style 12\n");
2228                 } else {
2229                   strcpy(str, "alias $ @\n$set interface ");
2230                   strcat(str, programVersion);
2231                   strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
2232 #ifdef WIN32
2233                   strcat(str, "$iset nohighlight 1\n");
2234 #endif
2235           if (!appData.noJoin)
2236               strcat(str, "$iset nowrap 1\n");
2237                   strcat(str, "$iset lock 1\n$style 12\n");
2238                 }
2239                 SendToICS(str);
2240                 NotifyFrontendLogin();
2241                 intfSet = TRUE;
2242             }
2243
2244             if (started == STARTED_COMMENT) {
2245                 /* Accumulate characters in comment */
2246                 parse[parse_pos++] = buf[i];
2247                 if (buf[i] == '\n') {
2248                     parse[parse_pos] = NULLCHAR;
2249                     if(chattingPartner>=0) {
2250                         char mess[MSG_SIZ];
2251                         sprintf(mess, "%s%s", talker, parse);
2252                         OutputChatMessage(chattingPartner, mess);
2253                         chattingPartner = -1;
2254                     } else
2255                     if(!suppressKibitz) // [HGM] kibitz
2256                         AppendComment(forwardMostMove, StripHighlight(parse));
2257                     else { // [HGM kibitz: divert memorized engine kibitz to engine-output window
2258                         int nrDigit = 0, nrAlph = 0, i;
2259                         if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input
2260                         { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }
2261                         parse[parse_pos] = NULLCHAR;
2262                         // try to be smart: if it does not look like search info, it should go to
2263                         // ICS interaction window after all, not to engine-output window.
2264                         for(i=0; i<parse_pos; i++) { // count letters and digits
2265                             nrDigit += (parse[i] >= '0' && parse[i] <= '9');
2266                             nrAlph  += (parse[i] >= 'a' && parse[i] <= 'z');
2267                             nrAlph  += (parse[i] >= 'A' && parse[i] <= 'Z');
2268                         }
2269                         if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info
2270                             int depth=0; float score;
2271                             if(sscanf(parse, "!!! %f/%d", &score, &depth) == 2 && depth>0) {
2272                                 // [HGM] kibitz: save kibitzed opponent info for PGN and eval graph
2273                                 pvInfoList[forwardMostMove-1].depth = depth;
2274                                 pvInfoList[forwardMostMove-1].score = 100*score;
2275                             }
2276                             OutputKibitz(suppressKibitz, parse);
2277                         } else {
2278                             char tmp[MSG_SIZ];
2279                             sprintf(tmp, _("your opponent kibitzes: %s"), parse);
2280                             SendToPlayer(tmp, strlen(tmp));
2281                         }
2282                     }
2283                     started = STARTED_NONE;
2284                 } else {
2285                     /* Don't match patterns against characters in chatter */
2286                     i++;
2287                     continue;
2288                 }
2289             }
2290             if (started == STARTED_CHATTER) {
2291                 if (buf[i] != '\n') {
2292                     /* Don't match patterns against characters in chatter */
2293                     i++;
2294                     continue;
2295                 }
2296                 started = STARTED_NONE;
2297             }
2298
2299             /* Kludge to deal with rcmd protocol */
2300             if (firstTime && looking_at(buf, &i, "\001*")) {
2301                 DisplayFatalError(&buf[1], 0, 1);
2302                 continue;
2303             } else {
2304                 firstTime = FALSE;
2305             }
2306
2307             if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
2308                 ics_type = ICS_ICC;
2309                 ics_prefix = "/";
2310                 if (appData.debugMode)
2311                   fprintf(debugFP, "ics_type %d\n", ics_type);
2312                 continue;
2313             }
2314             if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
2315                 ics_type = ICS_FICS;
2316                 ics_prefix = "$";
2317                 if (appData.debugMode)
2318                   fprintf(debugFP, "ics_type %d\n", ics_type);
2319                 continue;
2320             }
2321             if (!loggedOn && looking_at(buf, &i, "chess.net")) {
2322                 ics_type = ICS_CHESSNET;
2323                 ics_prefix = "/";
2324                 if (appData.debugMode)
2325                   fprintf(debugFP, "ics_type %d\n", ics_type);
2326                 continue;
2327             }
2328
2329             if (!loggedOn &&
2330                 (looking_at(buf, &i, "\"*\" is *a registered name") ||
2331                  looking_at(buf, &i, "Logging you in as \"*\"") ||
2332                  looking_at(buf, &i, "will be \"*\""))) {
2333               strcpy(ics_handle, star_match[0]);
2334               continue;
2335             }
2336
2337             if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
2338               char buf[MSG_SIZ];
2339               snprintf(buf, sizeof(buf), "%s@%s", ics_handle, appData.icsHost);
2340               DisplayIcsInteractionTitle(buf);
2341               have_set_title = TRUE;
2342             }
2343
2344             /* skip finger notes */
2345             if (started == STARTED_NONE &&
2346                 ((buf[i] == ' ' && isdigit(buf[i+1])) ||
2347                  (buf[i] == '1' && buf[i+1] == '0')) &&
2348                 buf[i+2] == ':' && buf[i+3] == ' ') {
2349               started = STARTED_CHATTER;
2350               i += 3;
2351               continue;
2352             }
2353
2354             /* skip formula vars */
2355             if (started == STARTED_NONE &&
2356                 buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
2357               started = STARTED_CHATTER;
2358               i += 3;
2359               continue;
2360             }
2361
2362             oldi = i;
2363             // [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window
2364             if (appData.autoKibitz && started == STARTED_NONE && 
2365                 !appData.icsEngineAnalyze &&                     // [HGM] [DM] ICS analyze
2366                 (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {
2367                 if(looking_at(buf, &i, "* kibitzes: ") &&
2368                    (StrStr(star_match[0], gameInfo.white) == star_match[0] || 
2369                     StrStr(star_match[0], gameInfo.black) == star_match[0]   )) { // kibitz of self or opponent
2370                         suppressKibitz = TRUE;
2371                         if((StrStr(star_match[0], gameInfo.white) == star_match[0]
2372                                 && (gameMode == IcsPlayingWhite)) ||
2373                            (StrStr(star_match[0], gameInfo.black) == star_match[0]
2374                                 && (gameMode == IcsPlayingBlack))   ) // opponent kibitz
2375                             started = STARTED_CHATTER; // own kibitz we simply discard
2376                         else {
2377                             started = STARTED_COMMENT; // make sure it will be collected in parse[]
2378                             parse_pos = 0; parse[0] = NULLCHAR;
2379                             savingComment = TRUE;
2380                             suppressKibitz = gameMode != IcsObserving ? 2 :
2381                                 (StrStr(star_match[0], gameInfo.white) == NULL) + 1;
2382                         } 
2383                         continue;
2384                 } else
2385                 if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz
2386                     started = STARTED_CHATTER;
2387                     suppressKibitz = TRUE;
2388                 }
2389             } // [HGM] kibitz: end of patch
2390
2391 //if(appData.debugMode) fprintf(debugFP, "hunt for tell, buf = %s\n", buf+i);
2392
2393             // [HGM] chat: intercept tells by users for which we have an open chat window
2394             channel = -1;
2395             if(started == STARTED_NONE && (looking_at(buf, &i, "* tells you:") || looking_at(buf, &i, "* says:") || 
2396                                            looking_at(buf, &i, "* whispers:") ||
2397                                            looking_at(buf, &i, "*(*):") && (sscanf(star_match[1], "%d", &channel),1) ||
2398                                            looking_at(buf, &i, "*(*)(*):") && sscanf(star_match[2], "%d", &channel) == 1 )) {
2399                 int p;
2400                 sscanf(star_match[0], "%[^(]", talker+1); // strip (C) or (U) off ICS handle
2401                 chattingPartner = -1;
2402
2403                 if(channel >= 0) // channel broadcast; look if there is a chatbox for this channel
2404                 for(p=0; p<MAX_CHAT; p++) {
2405                     if(channel == atoi(chatPartner[p])) {
2406                     talker[0] = '['; strcat(talker, "]");
2407                     chattingPartner = p; break;
2408                     }
2409                 } else
2410                 if(buf[i-3] == 'r') // whisper; look if there is a WHISPER chatbox
2411                 for(p=0; p<MAX_CHAT; p++) {
2412                     if(!strcmp("WHISPER", chatPartner[p])) {
2413                         talker[0] = '['; strcat(talker, "]");
2414                         chattingPartner = p; break;
2415                     }
2416                 }
2417                 if(chattingPartner<0) // if not, look if there is a chatbox for this indivdual
2418                 for(p=0; p<MAX_CHAT; p++) if(!StrCaseCmp(talker+1, chatPartner[p])) {
2419                     talker[0] = 0;
2420                     chattingPartner = p; break;
2421                 }
2422                 if(chattingPartner<0) i = oldi; else {
2423                     started = STARTED_COMMENT;
2424                     parse_pos = 0; parse[0] = NULLCHAR;
2425                     savingComment = TRUE;
2426                     suppressKibitz = TRUE;
2427                 }
2428             } // [HGM] chat: end of patch
2429
2430             if (appData.zippyTalk || appData.zippyPlay) {
2431                 /* [DM] Backup address for color zippy lines */
2432                 backup = i;
2433 #if ZIPPY
2434        #ifdef WIN32
2435                if (loggedOn == TRUE)
2436                        if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||
2437                           (appData.zippyPlay && ZippyMatch(buf, &backup)));
2438        #else
2439                 if (ZippyControl(buf, &i) ||
2440                     ZippyConverse(buf, &i) ||
2441                     (appData.zippyPlay && ZippyMatch(buf, &i))) {
2442                       loggedOn = TRUE;
2443                       if (!appData.colorize) continue;
2444                 }
2445        #endif
2446 #endif
2447             } // [DM] 'else { ' deleted
2448                 if (
2449                     /* Regular tells and says */
2450                     (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
2451                     looking_at(buf, &i, "* (your partner) tells you: ") ||
2452                     looking_at(buf, &i, "* says: ") ||
2453                     /* Don't color "message" or "messages" output */
2454                     (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
2455                     looking_at(buf, &i, "*. * at *:*: ") ||
2456                     looking_at(buf, &i, "--* (*:*): ") ||
2457                     /* Message notifications (same color as tells) */
2458                     looking_at(buf, &i, "* has left a message ") ||
2459                     looking_at(buf, &i, "* just sent you a message:\n") ||
2460                     /* Whispers and kibitzes */
2461                     (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
2462                     looking_at(buf, &i, "* kibitzes: ") ||
2463                     /* Channel tells */
2464                     (tkind = 3, looking_at(buf, &i, "*(*: "))) {
2465
2466                   if (tkind == 1 && strchr(star_match[0], ':')) {
2467                       /* Avoid "tells you:" spoofs in channels */
2468                      tkind = 3;
2469                   }
2470                   if (star_match[0][0] == NULLCHAR ||
2471                       strchr(star_match[0], ' ') ||
2472                       (tkind == 3 && strchr(star_match[1], ' '))) {
2473                     /* Reject bogus matches */
2474                     i = oldi;
2475                   } else {
2476                     if (appData.colorize) {
2477                       if (oldi > next_out) {
2478                         SendToPlayer(&buf[next_out], oldi - next_out);
2479                         next_out = oldi;
2480                       }
2481                       switch (tkind) {
2482                       case 1:
2483                         Colorize(ColorTell, FALSE);
2484                         curColor = ColorTell;
2485                         break;
2486                       case 2:
2487                         Colorize(ColorKibitz, FALSE);
2488                         curColor = ColorKibitz;
2489                         break;
2490                       case 3:
2491                         p = strrchr(star_match[1], '(');
2492                         if (p == NULL) {
2493                           p = star_match[1];
2494                         } else {
2495                           p++;
2496                         }
2497                         if (atoi(p) == 1) {
2498                           Colorize(ColorChannel1, FALSE);
2499                           curColor = ColorChannel1;
2500                         } else {
2501                           Colorize(ColorChannel, FALSE);
2502                           curColor = ColorChannel;
2503                         }
2504                         break;
2505                       case 5:
2506                         curColor = ColorNormal;
2507                         break;
2508                       }
2509                     }
2510                     if (started == STARTED_NONE && appData.autoComment &&
2511                         (gameMode == IcsObserving ||
2512                          gameMode == IcsPlayingWhite ||
2513                          gameMode == IcsPlayingBlack)) {
2514                       parse_pos = i - oldi;
2515                       memcpy(parse, &buf[oldi], parse_pos);
2516                       parse[parse_pos] = NULLCHAR;
2517                       started = STARTED_COMMENT;
2518                       savingComment = TRUE;
2519                     } else {
2520                       started = STARTED_CHATTER;
2521                       savingComment = FALSE;
2522                     }
2523                     loggedOn = TRUE;
2524                     continue;
2525                   }
2526                 }
2527
2528                 if (looking_at(buf, &i, "* s-shouts: ") ||
2529                     looking_at(buf, &i, "* c-shouts: ")) {
2530                     if (appData.colorize) {
2531                         if (oldi > next_out) {
2532                             SendToPlayer(&buf[next_out], oldi - next_out);
2533                             next_out = oldi;
2534                         }
2535                         Colorize(ColorSShout, FALSE);
2536                         curColor = ColorSShout;
2537                     }
2538                     loggedOn = TRUE;
2539                     started = STARTED_CHATTER;
2540                     continue;
2541                 }
2542
2543                 if (looking_at(buf, &i, "--->")) {
2544                     loggedOn = TRUE;
2545                     continue;
2546                 }
2547
2548                 if (looking_at(buf, &i, "* shouts: ") ||
2549                     looking_at(buf, &i, "--> ")) {
2550                     if (appData.colorize) {
2551                         if (oldi > next_out) {
2552                             SendToPlayer(&buf[next_out], oldi - next_out);
2553                             next_out = oldi;
2554                         }
2555                         Colorize(ColorShout, FALSE);
2556                         curColor = ColorShout;
2557                     }
2558                     loggedOn = TRUE;
2559                     started = STARTED_CHATTER;
2560                     continue;
2561                 }
2562
2563                 if (looking_at( buf, &i, "Challenge:")) {
2564                     if (appData.colorize) {
2565                         if (oldi > next_out) {
2566                             SendToPlayer(&buf[next_out], oldi - next_out);
2567                             next_out = oldi;
2568                         }
2569                         Colorize(ColorChallenge, FALSE);
2570                         curColor = ColorChallenge;
2571                     }
2572                     loggedOn = TRUE;
2573                     continue;
2574                 }
2575
2576                 if (looking_at(buf, &i, "* offers you") ||
2577                     looking_at(buf, &i, "* offers to be") ||
2578                     looking_at(buf, &i, "* would like to") ||
2579                     looking_at(buf, &i, "* requests to") ||
2580                     looking_at(buf, &i, "Your opponent offers") ||
2581                     looking_at(buf, &i, "Your opponent requests")) {
2582
2583                     if (appData.colorize) {
2584                         if (oldi > next_out) {
2585                             SendToPlayer(&buf[next_out], oldi - next_out);
2586                             next_out = oldi;
2587                         }
2588                         Colorize(ColorRequest, FALSE);
2589                         curColor = ColorRequest;
2590                     }
2591                     continue;
2592                 }
2593
2594                 if (looking_at(buf, &i, "* (*) seeking")) {
2595                     if (appData.colorize) {
2596                         if (oldi > next_out) {
2597                             SendToPlayer(&buf[next_out], oldi - next_out);
2598                             next_out = oldi;
2599                         }
2600                         Colorize(ColorSeek, FALSE);
2601                         curColor = ColorSeek;
2602                     }
2603                     continue;
2604             }
2605
2606             if (looking_at(buf, &i, "\\   ")) {
2607                 if (prevColor != ColorNormal) {
2608                     if (oldi > next_out) {
2609                         SendToPlayer(&buf[next_out], oldi - next_out);
2610                         next_out = oldi;
2611                     }
2612                     Colorize(prevColor, TRUE);
2613                     curColor = prevColor;
2614                 }
2615                 if (savingComment) {
2616                     parse_pos = i - oldi;
2617                     memcpy(parse, &buf[oldi], parse_pos);
2618                     parse[parse_pos] = NULLCHAR;
2619                     started = STARTED_COMMENT;
2620                 } else {
2621                     started = STARTED_CHATTER;
2622                 }
2623                 continue;
2624             }
2625
2626             if (looking_at(buf, &i, "Black Strength :") ||
2627                 looking_at(buf, &i, "<<< style 10 board >>>") ||
2628                 looking_at(buf, &i, "<10>") ||
2629                 looking_at(buf, &i, "#@#")) {
2630                 /* Wrong board style */
2631                 loggedOn = TRUE;
2632                 SendToICS(ics_prefix);
2633                 SendToICS("set style 12\n");
2634                 SendToICS(ics_prefix);
2635                 SendToICS("refresh\n");
2636                 continue;
2637             }
2638             
2639             if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
2640                 ICSInitScript();
2641                 have_sent_ICS_logon = 1;
2642                 continue;
2643             }
2644               
2645             if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ && 
2646                 (looking_at(buf, &i, "\n<12> ") ||
2647                  looking_at(buf, &i, "<12> "))) {
2648                 loggedOn = TRUE;
2649                 if (oldi > next_out) {
2650                     SendToPlayer(&buf[next_out], oldi - next_out);
2651                 }
2652                 next_out = i;
2653                 started = STARTED_BOARD;
2654                 parse_pos = 0;
2655                 continue;
2656             }
2657
2658             if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
2659                 looking_at(buf, &i, "<b1> ")) {
2660                 if (oldi > next_out) {
2661                     SendToPlayer(&buf[next_out], oldi - next_out);
2662                 }
2663                 next_out = i;
2664                 started = STARTED_HOLDINGS;
2665                 parse_pos = 0;
2666                 continue;
2667             }
2668
2669             if (looking_at(buf, &i, "* *vs. * *--- *")) {
2670                 loggedOn = TRUE;
2671                 /* Header for a move list -- first line */
2672
2673                 switch (ics_getting_history) {
2674                   case H_FALSE:
2675                     switch (gameMode) {
2676                       case IcsIdle:
2677                       case BeginningOfGame:
2678                         /* User typed "moves" or "oldmoves" while we
2679                            were idle.  Pretend we asked for these
2680                            moves and soak them up so user can step
2681                            through them and/or save them.
2682                            */
2683                         Reset(FALSE, TRUE);
2684                         gameMode = IcsObserving;
2685                         ModeHighlight();
2686                         ics_gamenum = -1;
2687                         ics_getting_history = H_GOT_UNREQ_HEADER;
2688                         break;
2689                       case EditGame: /*?*/
2690                       case EditPosition: /*?*/
2691                         /* Should above feature work in these modes too? */
2692                         /* For now it doesn't */
2693                         ics_getting_history = H_GOT_UNWANTED_HEADER;
2694                         break;
2695                       default:
2696                         ics_getting_history = H_GOT_UNWANTED_HEADER;
2697                         break;
2698                     }
2699                     break;
2700                   case H_REQUESTED:
2701                     /* Is this the right one? */
2702                     if (gameInfo.white && gameInfo.black &&
2703                         strcmp(gameInfo.white, star_match[0]) == 0 &&
2704                         strcmp(gameInfo.black, star_match[2]) == 0) {
2705                         /* All is well */
2706                         ics_getting_history = H_GOT_REQ_HEADER;
2707                     }
2708                     break;
2709                   case H_GOT_REQ_HEADER:
2710                   case H_GOT_UNREQ_HEADER:
2711                   case H_GOT_UNWANTED_HEADER:
2712                   case H_GETTING_MOVES:
2713                     /* Should not happen */
2714                     DisplayError(_("Error gathering move list: two headers"), 0);
2715                     ics_getting_history = H_FALSE;
2716                     break;
2717                 }
2718
2719                 /* Save player ratings into gameInfo if needed */
2720                 if ((ics_getting_history == H_GOT_REQ_HEADER ||
2721                      ics_getting_history == H_GOT_UNREQ_HEADER) &&
2722                     (gameInfo.whiteRating == -1 ||
2723                      gameInfo.blackRating == -1)) {
2724
2725                     gameInfo.whiteRating = string_to_rating(star_match[1]);
2726                     gameInfo.blackRating = string_to_rating(star_match[3]);
2727                     if (appData.debugMode)
2728                       fprintf(debugFP, _("Ratings from header: W %d, B %d\n"), 
2729                               gameInfo.whiteRating, gameInfo.blackRating);
2730                 }
2731                 continue;
2732             }
2733
2734             if (looking_at(buf, &i,
2735               "* * match, initial time: * minute*, increment: * second")) {
2736                 /* Header for a move list -- second line */
2737                 /* Initial board will follow if this is a wild game */
2738                 if (gameInfo.event != NULL) free(gameInfo.event);
2739                 sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
2740                 gameInfo.event = StrSave(str);
2741                 /* [HGM] we switched variant. Translate boards if needed. */
2742                 VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event));
2743                 continue;
2744             }
2745
2746             if (looking_at(buf, &i, "Move  ")) {
2747                 /* Beginning of a move list */
2748                 switch (ics_getting_history) {
2749                   case H_FALSE:
2750                     /* Normally should not happen */
2751                     /* Maybe user hit reset while we were parsing */
2752                     break;
2753                   case H_REQUESTED:
2754                     /* Happens if we are ignoring a move list that is not
2755                      * the one we just requested.  Common if the user
2756                      * tries to observe two games without turning off
2757                      * getMoveList */
2758                     break;
2759                   case H_GETTING_MOVES:
2760                     /* Should not happen */
2761                     DisplayError(_("Error gathering move list: nested"), 0);
2762                     ics_getting_history = H_FALSE;
2763                     break;
2764                   case H_GOT_REQ_HEADER:
2765                     ics_getting_history = H_GETTING_MOVES;
2766                     started = STARTED_MOVES;
2767                     parse_pos = 0;
2768                     if (oldi > next_out) {
2769                         SendToPlayer(&buf[next_out], oldi - next_out);
2770                     }
2771                     break;
2772                   case H_GOT_UNREQ_HEADER:
2773                     ics_getting_history = H_GETTING_MOVES;
2774                     started = STARTED_MOVES_NOHIDE;
2775                     parse_pos = 0;
2776                     break;
2777                   case H_GOT_UNWANTED_HEADER:
2778                     ics_getting_history = H_FALSE;
2779                     break;
2780                 }
2781                 continue;
2782             }                           
2783             
2784             if (looking_at(buf, &i, "% ") ||
2785                 ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
2786                  && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book
2787                 savingComment = FALSE;
2788                 switch (started) {
2789                   case STARTED_MOVES:
2790                   case STARTED_MOVES_NOHIDE:
2791                     memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
2792                     parse[parse_pos + i - oldi] = NULLCHAR;
2793                     ParseGameHistory(parse);
2794 #if ZIPPY
2795                     if (appData.zippyPlay && first.initDone) {
2796                         FeedMovesToProgram(&first, forwardMostMove);
2797                         if (gameMode == IcsPlayingWhite) {
2798                             if (WhiteOnMove(forwardMostMove)) {
2799                                 if (first.sendTime) {
2800                                   if (first.useColors) {
2801                                     SendToProgram("black\n", &first); 
2802                                   }
2803                                   SendTimeRemaining(&first, TRUE);
2804                                 }
2805                                 if (first.useColors) {
2806                                   SendToProgram("white\n", &first); // [HGM] book: made sending of "go\n" book dependent
2807                                 }
2808                                 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: probe book for initial pos
2809                                 first.maybeThinking = TRUE;
2810                             } else {
2811                                 if (first.usePlayother) {
2812                                   if (first.sendTime) {
2813                                     SendTimeRemaining(&first, TRUE);
2814                                   }
2815                                   SendToProgram("playother\n", &first);
2816                                   firstMove = FALSE;
2817                                 } else {
2818                                   firstMove = TRUE;
2819                                 }
2820                             }
2821                         } else if (gameMode == IcsPlayingBlack) {
2822                             if (!WhiteOnMove(forwardMostMove)) {
2823                                 if (first.sendTime) {
2824                                   if (first.useColors) {
2825                                     SendToProgram("white\n", &first);
2826                                   }
2827                                   SendTimeRemaining(&first, FALSE);
2828                                 }
2829                                 if (first.useColors) {
2830                                   SendToProgram("black\n", &first);
2831                                 }
2832                                 bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE);
2833                                 first.maybeThinking = TRUE;
2834                             } else {
2835                                 if (first.usePlayother) {
2836                                   if (first.sendTime) {
2837                                     SendTimeRemaining(&first, FALSE);
2838                                   }
2839                                   SendToProgram("playother\n", &first);
2840                                   firstMove = FALSE;
2841                                 } else {
2842                                   firstMove = TRUE;
2843                                 }
2844                             }
2845                         }                       
2846                     }
2847 #endif
2848                     if (gameMode == IcsObserving && ics_gamenum == -1) {
2849                         /* Moves came from oldmoves or moves command
2850                            while we weren't doing anything else.
2851                            */
2852                         currentMove = forwardMostMove;
2853                         ClearHighlights();/*!!could figure this out*/
2854                         flipView = appData.flipView;
2855                         DrawPosition(FALSE, boards[currentMove]);
2856                         DisplayBothClocks();
2857                         sprintf(str, "%s vs. %s",
2858                                 gameInfo.white, gameInfo.black);
2859                         DisplayTitle(str);
2860                         gameMode = IcsIdle;
2861                     } else {
2862                         /* Moves were history of an active game */
2863                         if (gameInfo.resultDetails != NULL) {
2864                             free(gameInfo.resultDetails);
2865                             gameInfo.resultDetails = NULL;
2866                         }
2867                     }
2868                     HistorySet(parseList, backwardMostMove,
2869                                forwardMostMove, currentMove-1);
2870                     DisplayMove(currentMove - 1);
2871                     if (started == STARTED_MOVES) next_out = i;
2872                     started = STARTED_NONE;
2873                     ics_getting_history = H_FALSE;
2874                     break;
2875
2876                   case STARTED_OBSERVE:
2877                     started = STARTED_NONE;
2878                     SendToICS(ics_prefix);
2879                     SendToICS("refresh\n");
2880                     break;
2881
2882                   default:
2883                     break;
2884                 }
2885                 if(bookHit) { // [HGM] book: simulate book reply
2886                     static char bookMove[MSG_SIZ]; // a bit generous?
2887
2888                     programStats.nodes = programStats.depth = programStats.time = 
2889                     programStats.score = programStats.got_only_move = 0;
2890                     sprintf(programStats.movelist, "%s (xbook)", bookHit);
2891
2892                     strcpy(bookMove, "move ");
2893                     strcat(bookMove, bookHit);
2894                     HandleMachineMove(bookMove, &first);
2895                 }
2896                 continue;
2897             }
2898             
2899             if ((started == STARTED_MOVES || started == STARTED_BOARD ||
2900                  started == STARTED_HOLDINGS ||
2901                  started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
2902                 /* Accumulate characters in move list or board */
2903                 parse[parse_pos++] = buf[i];
2904             }
2905             
2906             /* Start of game messages.  Mostly we detect start of game
2907                when the first board image arrives.  On some versions
2908                of the ICS, though, we need to do a "refresh" after starting
2909                to observe in order to get the current board right away. */
2910             if (looking_at(buf, &i, "Adding game * to observation list")) {
2911                 started = STARTED_OBSERVE;
2912                 continue;
2913             }
2914
2915             /* Handle auto-observe */
2916             if (appData.autoObserve &&
2917                 (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
2918                 looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
2919                 char *player;
2920                 /* Choose the player that was highlighted, if any. */
2921                 if (star_match[0][0] == '\033' ||
2922                     star_match[1][0] != '\033') {
2923                     player = star_match[0];
2924                 } else {
2925                     player = star_match[2];
2926                 }
2927                 sprintf(str, "%sobserve %s\n",
2928                         ics_prefix, StripHighlightAndTitle(player));
2929                 SendToICS(str);
2930
2931                 /* Save ratings from notify string */
2932                 strcpy(player1Name, star_match[0]);
2933                 player1Rating = string_to_rating(star_match[1]);
2934                 strcpy(player2Name, star_match[2]);
2935                 player2Rating = string_to_rating(star_match[3]);
2936
2937                 if (appData.debugMode)
2938                   fprintf(debugFP, 
2939                           "Ratings from 'Game notification:' %s %d, %s %d\n",
2940                           player1Name, player1Rating,
2941                           player2Name, player2Rating);
2942
2943                 continue;
2944             }
2945
2946             /* Deal with automatic examine mode after a game,
2947                and with IcsObserving -> IcsExamining transition */
2948             if (looking_at(buf, &i, "Entering examine mode for game *") ||
2949                 looking_at(buf, &i, "has made you an examiner of game *")) {
2950
2951                 int gamenum = atoi(star_match[0]);
2952                 if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
2953                     gamenum == ics_gamenum) {
2954                     /* We were already playing or observing this game;
2955                        no need to refetch history */
2956                     gameMode = IcsExamining;
2957                     if (pausing) {
2958                         pauseExamForwardMostMove = forwardMostMove;
2959                     } else if (currentMove < forwardMostMove) {
2960                         ForwardInner(forwardMostMove);
2961                     }
2962                 } else {
2963                     /* I don't think this case really can happen */
2964                     SendToICS(ics_prefix);
2965                     SendToICS("refresh\n");
2966                 }
2967                 continue;
2968             }    
2969             
2970             /* Error messages */
2971 //          if (ics_user_moved) {
2972             if (1) { // [HGM] old way ignored error after move type in; ics_user_moved is not set then!
2973                 if (looking_at(buf, &i, "Illegal move") ||
2974                     looking_at(buf, &i, "Not a legal move") ||
2975                     looking_at(buf, &i, "Your king is in check") ||
2976                     looking_at(buf, &i, "It isn't your turn") ||
2977                     looking_at(buf, &i, "It is not your move")) {
2978                     /* Illegal move */
2979                     if (ics_user_moved && forwardMostMove > backwardMostMove) { // only backup if we already moved
2980                         currentMove = --forwardMostMove;
2981                         DisplayMove(currentMove - 1); /* before DMError */
2982                         DrawPosition(FALSE, boards[currentMove]);
2983                         SwitchClocks();
2984                         DisplayBothClocks();
2985                     }
2986                     DisplayMoveError(_("Illegal move (rejected by ICS)")); // [HGM] but always relay error msg
2987                     ics_user_moved = 0;
2988                     continue;
2989                 }
2990             }
2991
2992             if (looking_at(buf, &i, "still have time") ||
2993                 looking_at(buf, &i, "not out of time") ||
2994                 looking_at(buf, &i, "either player is out of time") ||
2995                 looking_at(buf, &i, "has timeseal; checking")) {
2996                 /* We must have called his flag a little too soon */
2997                 whiteFlag = blackFlag = FALSE;
2998                 continue;
2999             }
3000
3001             if (looking_at(buf, &i, "added * seconds to") ||
3002                 looking_at(buf, &i, "seconds were added to")) {
3003                 /* Update the clocks */
3004                 SendToICS(ics_prefix);
3005                 SendToICS("refresh\n");
3006                 continue;
3007             }
3008
3009             if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
3010                 ics_clock_paused = TRUE;
3011                 StopClocks();
3012                 continue;
3013             }
3014
3015             if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
3016                 ics_clock_paused = FALSE;
3017                 StartClocks();
3018                 continue;
3019             }
3020
3021             /* Grab player ratings from the Creating: message.
3022                Note we have to check for the special case when
3023                the ICS inserts things like [white] or [black]. */
3024             if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
3025                 looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
3026                 /* star_matches:
3027                    0    player 1 name (not necessarily white)
3028                    1    player 1 rating
3029                    2    empty, white, or black (IGNORED)
3030                    3    player 2 name (not necessarily black)
3031                    4    player 2 rating
3032                    
3033                    The names/ratings are sorted out when the game
3034                    actually starts (below).
3035                 */
3036                 strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
3037                 player1Rating = string_to_rating(star_match[1]);
3038                 strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
3039                 player2Rating = string_to_rating(star_match[4]);
3040
3041                 if (appData.debugMode)
3042                   fprintf(debugFP, 
3043                           "Ratings from 'Creating:' %s %d, %s %d\n",
3044                           player1Name, player1Rating,
3045                           player2Name, player2Rating);
3046
3047                 continue;
3048             }
3049             
3050             /* Improved generic start/end-of-game messages */
3051             if ((tkind=0, looking_at(buf, &i, "{Game * (* vs. *) *}*")) ||
3052                 (tkind=1, looking_at(buf, &i, "{Game * (*(*) vs. *(*)) *}*"))){
3053                 /* If tkind == 0: */
3054                 /* star_match[0] is the game number */
3055                 /*           [1] is the white player's name */
3056                 /*           [2] is the black player's name */
3057                 /* For end-of-game: */
3058                 /*           [3] is the reason for the game end */
3059                 /*           [4] is a PGN end game-token, preceded by " " */
3060                 /* For start-of-game: */
3061                 /*           [3] begins with "Creating" or "Continuing" */
3062                 /*           [4] is " *" or empty (don't care). */
3063                 int gamenum = atoi(star_match[0]);
3064                 char *whitename, *blackname, *why, *endtoken;
3065                 ChessMove endtype = (ChessMove) 0;
3066
3067                 if (tkind == 0) {
3068                   whitename = star_match[1];
3069                   blackname = star_match[2];
3070                   why = star_match[3];
3071                   endtoken = star_match[4];
3072                 } else {
3073                   whitename = star_match[1];
3074                   blackname = star_match[3];
3075                   why = star_match[5];
3076                   endtoken = star_match[6];
3077                 }
3078
3079                 /* Game start messages */
3080                 if (strncmp(why, "Creating ", 9) == 0 ||
3081                     strncmp(why, "Continuing ", 11) == 0) {
3082                     gs_gamenum = gamenum;
3083                     strcpy(gs_kind, strchr(why, ' ') + 1);
3084 #if ZIPPY
3085                     if (appData.zippyPlay) {
3086                         ZippyGameStart(whitename, blackname);
3087                     }
3088 #endif /*ZIPPY*/
3089                     continue;
3090                 }
3091
3092                 /* Game end messages */
3093                 if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
3094                     ics_gamenum != gamenum) {
3095                     continue;
3096                 }
3097                 while (endtoken[0] == ' ') endtoken++;
3098                 switch (endtoken[0]) {
3099                   case '*':
3100                   default:
3101                     endtype = GameUnfinished;
3102                     break;
3103                   case '0':
3104                     endtype = BlackWins;
3105                     break;
3106                   case '1':
3107                     if (endtoken[1] == '/')
3108                       endtype = GameIsDrawn;
3109                     else
3110                       endtype = WhiteWins;
3111                     break;
3112                 }
3113                 GameEnds(endtype, why, GE_ICS);
3114 #if ZIPPY
3115                 if (appData.zippyPlay && first.initDone) {
3116                     ZippyGameEnd(endtype, why);
3117                     if (first.pr == NULL) {
3118                       /* Start the next process early so that we'll
3119                          be ready for the next challenge */
3120                       StartChessProgram(&first);
3121                     }
3122                     /* Send "new" early, in case this command takes
3123                        a long time to finish, so that we'll be ready
3124                        for the next challenge. */
3125                     gameInfo.variant = VariantNormal; // [HGM] variantswitch: suppress sending of 'variant'
3126                     Reset(TRUE, TRUE);
3127                 }
3128 #endif /*ZIPPY*/
3129                 continue;
3130             }
3131
3132             if (looking_at(buf, &i, "Removing game * from observation") ||
3133                 looking_at(buf, &i, "no longer observing game *") ||
3134                 looking_at(buf, &i, "Game * (*) has no examiners")) {
3135                 if (gameMode == IcsObserving &&
3136                     atoi(star_match[0]) == ics_gamenum)
3137                   {
3138                       /* icsEngineAnalyze */
3139                       if (appData.icsEngineAnalyze) {
3140                             ExitAnalyzeMode();
3141                             ModeHighlight();
3142                       }
3143                       StopClocks();
3144                       gameMode = IcsIdle;
3145                       ics_gamenum = -1;
3146                       ics_user_moved = FALSE;
3147                   }
3148                 continue;
3149             }
3150
3151             if (looking_at(buf, &i, "no longer examining game *")) {
3152                 if (gameMode == IcsExamining &&
3153                     atoi(star_match[0]) == ics_gamenum)
3154                   {
3155                       gameMode = IcsIdle;
3156                       ics_gamenum = -1;
3157                       ics_user_moved = FALSE;
3158                   }
3159                 continue;
3160             }
3161
3162             /* Advance leftover_start past any newlines we find,
3163                so only partial lines can get reparsed */
3164             if (looking_at(buf, &i, "\n")) {
3165                 prevColor = curColor;
3166                 if (curColor != ColorNormal) {
3167                     if (oldi > next_out) {
3168                         SendToPlayer(&buf[next_out], oldi - next_out);
3169                         next_out = oldi;
3170                     }
3171                     Colorize(ColorNormal, FALSE);
3172                     curColor = ColorNormal;
3173                 }
3174                 if (started == STARTED_BOARD) {
3175                     started = STARTED_NONE;
3176                     parse[parse_pos] = NULLCHAR;
3177                     ParseBoard12(parse);
3178                     ics_user_moved = 0;
3179
3180                     /* Send premove here */
3181                     if (appData.premove) {
3182                       char str[MSG_SIZ];
3183                       if (currentMove == 0 &&
3184                           gameMode == IcsPlayingWhite &&
3185                           appData.premoveWhite) {
3186                         sprintf(str, "%s%s\n", ics_prefix,
3187                                 appData.premoveWhiteText);
3188                         if (appData.debugMode)
3189                           fprintf(debugFP, "Sending premove:\n");
3190                         SendToICS(str);
3191                       } else if (currentMove == 1 &&
3192                                  gameMode == IcsPlayingBlack &&
3193                                  appData.premoveBlack) {
3194                         sprintf(str, "%s%s\n", ics_prefix,
3195                                 appData.premoveBlackText);
3196                         if (appData.debugMode)
3197                           fprintf(debugFP, "Sending premove:\n");
3198                         SendToICS(str);
3199                       } else if (gotPremove) {
3200                         gotPremove = 0;
3201                         ClearPremoveHighlights();
3202                         if (appData.debugMode)
3203                           fprintf(debugFP, "Sending premove:\n");
3204                           UserMoveEvent(premoveFromX, premoveFromY, 
3205                                         premoveToX, premoveToY, 
3206                                         premovePromoChar);
3207                       }
3208                     }
3209
3210                     /* Usually suppress following prompt */
3211                     if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
3212                         if (looking_at(buf, &i, "*% ")) {
3213                             savingComment = FALSE;
3214                         }
3215                     }
3216                     next_out = i;
3217                 } else if (started == STARTED_HOLDINGS) {
3218                     int gamenum;
3219                     char new_piece[MSG_SIZ];
3220                     started = STARTED_NONE;
3221                     parse[parse_pos] = NULLCHAR;
3222                     if (appData.debugMode)
3223                       fprintf(debugFP, "Parsing holdings: %s, currentMove = %d\n",
3224                                                         parse, currentMove);
3225                     if (sscanf(parse, " game %d", &gamenum) == 1 &&
3226                         gamenum == ics_gamenum) {
3227                         if (gameInfo.variant == VariantNormal) {
3228                           /* [HGM] We seem to switch variant during a game!
3229                            * Presumably no holdings were displayed, so we have
3230                            * to move the position two files to the right to
3231                            * create room for them!
3232                            */
3233                           VariantSwitch(boards[currentMove], VariantCrazyhouse); /* temp guess */
3234                           /* Get a move list just to see the header, which
3235                              will tell us whether this is really bug or zh */
3236                           if (ics_getting_history == H_FALSE) {
3237                             ics_getting_history = H_REQUESTED;
3238                             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3239                             SendToICS(str);
3240                           }
3241                         }
3242                         new_piece[0] = NULLCHAR;
3243                         sscanf(parse, "game %d white [%s black [%s <- %s",
3244                                &gamenum, white_holding, black_holding,
3245                                new_piece);
3246                         white_holding[strlen(white_holding)-1] = NULLCHAR;
3247                         black_holding[strlen(black_holding)-1] = NULLCHAR;
3248                         /* [HGM] copy holdings to board holdings area */
3249                         CopyHoldings(boards[currentMove], white_holding, WhitePawn);
3250                         CopyHoldings(boards[currentMove], black_holding, BlackPawn);
3251 #if ZIPPY
3252                         if (appData.zippyPlay && first.initDone) {
3253                             ZippyHoldings(white_holding, black_holding,
3254                                           new_piece);
3255                         }
3256 #endif /*ZIPPY*/
3257                         if (tinyLayout || smallLayout) {
3258                             char wh[16], bh[16];
3259                             PackHolding(wh, white_holding);
3260                             PackHolding(bh, black_holding);
3261                             sprintf(str, "[%s-%s] %s-%s", wh, bh,
3262                                     gameInfo.white, gameInfo.black);
3263                         } else {
3264                             sprintf(str, "%s [%s] vs. %s [%s]",
3265                                     gameInfo.white, white_holding,
3266                                     gameInfo.black, black_holding);
3267                         }
3268
3269                         DrawPosition(FALSE, boards[currentMove]);
3270                         DisplayTitle(str);
3271                     }
3272                     /* Suppress following prompt */
3273                     if (looking_at(buf, &i, "*% ")) {
3274                         savingComment = FALSE;
3275                     }
3276                     next_out = i;
3277                 }
3278                 continue;
3279             }
3280
3281             i++;                /* skip unparsed character and loop back */
3282         }
3283         
3284         if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window
3285             started != STARTED_HOLDINGS && i > next_out) {
3286             SendToPlayer(&buf[next_out], i - next_out);
3287             next_out = i;
3288         }
3289         suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above
3290         
3291         leftover_len = buf_len - leftover_start;
3292         /* if buffer ends with something we couldn't parse,
3293            reparse it after appending the next read */
3294         
3295     } else if (count == 0) {
3296         RemoveInputSource(isr);
3297         DisplayFatalError(_("Connection closed by ICS"), 0, 0);
3298     } else {
3299         DisplayFatalError(_("Error reading from ICS"), error, 1);
3300     }
3301 }
3302
3303
3304 /* Board style 12 looks like this:
3305    
3306    <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
3307    
3308  * The "<12> " is stripped before it gets to this routine.  The two
3309  * trailing 0's (flip state and clock ticking) are later addition, and
3310  * some chess servers may not have them, or may have only the first.
3311  * Additional trailing fields may be added in the future.  
3312  */
3313
3314 #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"
3315
3316 #define RELATION_OBSERVING_PLAYED    0
3317 #define RELATION_OBSERVING_STATIC   -2   /* examined, oldmoves, or smoves */
3318 #define RELATION_PLAYING_MYMOVE      1
3319 #define RELATION_PLAYING_NOTMYMOVE  -1
3320 #define RELATION_EXAMINING           2
3321 #define RELATION_ISOLATED_BOARD     -3
3322 #define RELATION_STARTING_POSITION  -4   /* FICS only */
3323
3324 void
3325 ParseBoard12(string)
3326      char *string;
3327
3328     GameMode newGameMode;
3329     int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;
3330     int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;
3331     int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
3332     char to_play, board_chars[200];
3333     char move_str[500], str[500], elapsed_time[500];
3334     char black[32], white[32];
3335     Board board;
3336     int prevMove = currentMove;
3337     int ticking = 2;
3338     ChessMove moveType;
3339     int fromX, fromY, toX, toY;
3340     char promoChar;
3341     int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */
3342     char *bookHit = NULL; // [HGM] book
3343
3344     fromX = fromY = toX = toY = -1;
3345     
3346     newGame = FALSE;
3347
3348     if (appData.debugMode)
3349       fprintf(debugFP, _("Parsing board: %s\n"), string);
3350
3351     move_str[0] = NULLCHAR;
3352     elapsed_time[0] = NULLCHAR;
3353     {   /* [HGM] figure out how many ranks and files the board has, for ICS extension used by Capablanca server */
3354         int  i = 0, j;
3355         while(i < 199 && (string[i] != ' ' || string[i+2] != ' ')) {
3356             if(string[i] == ' ') { ranks++; files = 0; }
3357             else files++;
3358             i++;
3359         }
3360         for(j = 0; j <i; j++) board_chars[j] = string[j];
3361         board_chars[i] = '\0';
3362         string += i + 1;
3363     }
3364     n = sscanf(string, PATTERN, &to_play, &double_push,
3365                &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,
3366                &gamenum, white, black, &relation, &basetime, &increment,
3367                &white_stren, &black_stren, &white_time, &black_time,
3368                &moveNum, str, elapsed_time, move_str, &ics_flip,
3369                &ticking);
3370
3371     if (n < 21) {
3372         snprintf(str, sizeof(str), _("Failed to parse board string:\n\"%s\""), string);
3373         DisplayError(str, 0);
3374         return;
3375     }
3376
3377     /* Convert the move number to internal form */
3378     moveNum = (moveNum - 1) * 2;
3379     if (to_play == 'B') moveNum++;
3380     if (moveNum >= MAX_MOVES) {
3381       DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),
3382                         0, 1);
3383       return;
3384     }
3385     
3386     switch (relation) {
3387       case RELATION_OBSERVING_PLAYED:
3388       case RELATION_OBSERVING_STATIC:
3389         if (gamenum == -1) {
3390             /* Old ICC buglet */
3391             relation = RELATION_OBSERVING_STATIC;
3392         }
3393         newGameMode = IcsObserving;
3394         break;
3395       case RELATION_PLAYING_MYMOVE:
3396       case RELATION_PLAYING_NOTMYMOVE:
3397         newGameMode =
3398           ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
3399             IcsPlayingWhite : IcsPlayingBlack;
3400         break;
3401       case RELATION_EXAMINING:
3402         newGameMode = IcsExamining;
3403         break;
3404       case RELATION_ISOLATED_BOARD:
3405       default:
3406         /* Just display this board.  If user was doing something else,
3407            we will forget about it until the next board comes. */ 
3408         newGameMode = IcsIdle;
3409         break;
3410       case RELATION_STARTING_POSITION:
3411         newGameMode = gameMode;
3412         break;
3413     }
3414     
3415     /* Modify behavior for initial board display on move listing
3416        of wild games.
3417        */
3418     switch (ics_getting_history) {
3419       case H_FALSE:
3420       case H_REQUESTED:
3421         break;
3422       case H_GOT_REQ_HEADER:
3423       case H_GOT_UNREQ_HEADER:
3424         /* This is the initial position of the current game */
3425         gamenum = ics_gamenum;
3426         moveNum = 0;            /* old ICS bug workaround */
3427         if (to_play == 'B') {
3428           startedFromSetupPosition = TRUE;
3429           blackPlaysFirst = TRUE;
3430           moveNum = 1;
3431           if (forwardMostMove == 0) forwardMostMove = 1;
3432           if (backwardMostMove == 0) backwardMostMove = 1;
3433           if (currentMove == 0) currentMove = 1;
3434         }
3435         newGameMode = gameMode;
3436         relation = RELATION_STARTING_POSITION; /* ICC needs this */
3437         break;
3438       case H_GOT_UNWANTED_HEADER:
3439         /* This is an initial board that we don't want */
3440         return;
3441       case H_GETTING_MOVES:
3442         /* Should not happen */
3443         DisplayError(_("Error gathering move list: extra board"), 0);
3444         ics_getting_history = H_FALSE;
3445         return;
3446     }
3447     
3448     /* Take action if this is the first board of a new game, or of a
3449        different game than is currently being displayed.  */
3450     if (gamenum != ics_gamenum || newGameMode != gameMode ||
3451         relation == RELATION_ISOLATED_BOARD) {
3452         
3453         /* Forget the old game and get the history (if any) of the new one */
3454         if (gameMode != BeginningOfGame) {
3455           Reset(FALSE, TRUE);
3456         }
3457         newGame = TRUE;
3458         if (appData.autoRaiseBoard) BoardToTop();
3459         prevMove = -3;
3460         if (gamenum == -1) {
3461             newGameMode = IcsIdle;
3462         } else if (moveNum > 0 && newGameMode != IcsIdle &&
3463                    appData.getMoveList) {
3464             /* Need to get game history */
3465             ics_getting_history = H_REQUESTED;
3466             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3467             SendToICS(str);
3468         }
3469         
3470         /* Initially flip the board to have black on the bottom if playing
3471            black or if the ICS flip flag is set, but let the user change
3472            it with the Flip View button. */
3473         flipView = appData.autoFlipView ? 
3474           (newGameMode == IcsPlayingBlack) || ics_flip :
3475           appData.flipView;
3476         
3477         /* Done with values from previous mode; copy in new ones */
3478         gameMode = newGameMode;
3479         ModeHighlight();
3480         ics_gamenum = gamenum;
3481         if (gamenum == gs_gamenum) {
3482             int klen = strlen(gs_kind);
3483             if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
3484             sprintf(str, "ICS %s", gs_kind);
3485             gameInfo.event = StrSave(str);
3486         } else {
3487             gameInfo.event = StrSave("ICS game");
3488         }
3489         gameInfo.site = StrSave(appData.icsHost);
3490         gameInfo.date = PGNDate();
3491         gameInfo.round = StrSave("-");
3492         gameInfo.white = StrSave(white);
3493         gameInfo.black = StrSave(black);
3494         timeControl = basetime * 60 * 1000;
3495         timeControl_2 = 0;
3496         timeIncrement = increment * 1000;
3497         movesPerSession = 0;
3498         gameInfo.timeControl = TimeControlTagValue();
3499         VariantSwitch(board, StringToVariant(gameInfo.event) );
3500   if (appData.debugMode) {
3501     fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event);
3502     fprintf(debugFP, "recognized as %s\n", VariantName(gameInfo.variant));
3503     setbuf(debugFP, NULL);
3504   }
3505
3506         gameInfo.outOfBook = NULL;
3507         
3508         /* Do we have the ratings? */
3509         if (strcmp(player1Name, white) == 0 &&
3510             strcmp(player2Name, black) == 0) {
3511             if (appData.debugMode)
3512               fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
3513                       player1Rating, player2Rating);
3514             gameInfo.whiteRating = player1Rating;
3515             gameInfo.blackRating = player2Rating;
3516         } else if (strcmp(player2Name, white) == 0 &&
3517                    strcmp(player1Name, black) == 0) {
3518             if (appData.debugMode)
3519               fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
3520                       player2Rating, player1Rating);
3521             gameInfo.whiteRating = player2Rating;
3522             gameInfo.blackRating = player1Rating;
3523         }
3524         player1Name[0] = player2Name[0] = NULLCHAR;
3525
3526         /* Silence shouts if requested */
3527         if (appData.quietPlay &&
3528             (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)) {
3529             SendToICS(ics_prefix);
3530             SendToICS("set shout 0\n");
3531         }
3532     }
3533     
3534     /* Deal with midgame name changes */
3535     if (!newGame) {
3536         if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {
3537             if (gameInfo.white) free(gameInfo.white);
3538             gameInfo.white = StrSave(white);
3539         }
3540         if (!gameInfo.black || strcmp(gameInfo.black, black) != 0) {
3541             if (gameInfo.black) free(gameInfo.black);
3542             gameInfo.black = StrSave(black);
3543         }
3544     }
3545     
3546     /* Throw away game result if anything actually changes in examine mode */
3547     if (gameMode == IcsExamining && !newGame) {
3548         gameInfo.result = GameUnfinished;
3549         if (gameInfo.resultDetails != NULL) {
3550             free(gameInfo.resultDetails);
3551             gameInfo.resultDetails = NULL;
3552         }
3553     }
3554     
3555     /* In pausing && IcsExamining mode, we ignore boards coming
3556        in if they are in a different variation than we are. */
3557     if (pauseExamInvalid) return;
3558     if (pausing && gameMode == IcsExamining) {
3559         if (moveNum <= pauseExamForwardMostMove) {
3560             pauseExamInvalid = TRUE;
3561             forwardMostMove = pauseExamForwardMostMove;
3562             return;
3563         }
3564     }
3565     
3566   if (appData.debugMode) {
3567     fprintf(debugFP, "load %dx%d board\n", files, ranks);
3568   }
3569     /* Parse the board */
3570     for (k = 0; k < ranks; k++) {
3571       for (j = 0; j < files; j++)
3572         board[k][j+gameInfo.holdingsWidth] = CharToPiece(board_chars[(ranks-1-k)*(files+1) + j]);
3573       if(gameInfo.holdingsWidth > 1) {
3574            board[k][0] = board[k][BOARD_WIDTH-1] = EmptySquare;
3575            board[k][1] = board[k][BOARD_WIDTH-2] = (ChessSquare) 0;;
3576       }
3577     }
3578     CopyBoard(boards[moveNum], board);
3579     if (moveNum == 0) {
3580         startedFromSetupPosition =
3581           !CompareBoards(board, initialPosition);
3582         if(startedFromSetupPosition)
3583             initialRulePlies = irrev_count; /* [HGM] 50-move counter offset */
3584     }
3585
3586     /* [HGM] Set castling rights. Take the outermost Rooks,
3587        to make it also work for FRC opening positions. Note that board12
3588        is really defective for later FRC positions, as it has no way to
3589        indicate which Rook can castle if they are on the same side of King.
3590        For the initial position we grant rights to the outermost Rooks,
3591        and remember thos rights, and we then copy them on positions
3592        later in an FRC game. This means WB might not recognize castlings with
3593        Rooks that have moved back to their original position as illegal,
3594        but in ICS mode that is not its job anyway.
3595     */
3596     if(moveNum == 0 || gameInfo.variant != VariantFischeRandom)
3597     { int i, j; ChessSquare wKing = WhiteKing, bKing = BlackKing;
3598
3599         for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)
3600             if(board[0][i] == WhiteRook) j = i;
3601         initialRights[0] = castlingRights[moveNum][0] = (castle_ws == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);
3602         for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)
3603             if(board[0][i] == WhiteRook) j = i;
3604         initialRights[1] = castlingRights[moveNum][1] = (castle_wl == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);
3605         for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)
3606             if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;
3607         initialRights[3] = castlingRights[moveNum][3] = (castle_bs == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);
3608         for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)
3609             if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;
3610         initialRights[4] = castlingRights[moveNum][4] = (castle_bl == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);
3611
3612         if(gameInfo.variant == VariantKnightmate) { wKing = WhiteUnicorn; bKing = BlackUnicorn; }
3613         for(k=BOARD_LEFT; k<BOARD_RGHT; k++)
3614             if(board[0][k] == wKing) initialRights[2] = castlingRights[moveNum][2] = k;
3615         for(k=BOARD_LEFT; k<BOARD_RGHT; k++)
3616             if(board[BOARD_HEIGHT-1][k] == bKing)
3617                 initialRights[5] = castlingRights[moveNum][5] = k;
3618     } else { int r;
3619         r = castlingRights[moveNum][0] = initialRights[0];
3620         if(board[0][r] != WhiteRook) castlingRights[moveNum][0] = -1;
3621         r = castlingRights[moveNum][1] = initialRights[1];
3622         if(board[0][r] != WhiteRook) castlingRights[moveNum][1] = -1;
3623         r = castlingRights[moveNum][3] = initialRights[3];
3624         if(board[BOARD_HEIGHT-1][r] != BlackRook) castlingRights[moveNum][3] = -1;
3625         r = castlingRights[moveNum][4] = initialRights[4];
3626         if(board[BOARD_HEIGHT-1][r] != BlackRook) castlingRights[moveNum][4] = -1;
3627         /* wildcastle kludge: always assume King has rights */
3628         r = castlingRights[moveNum][2] = initialRights[2];
3629         r = castlingRights[moveNum][5] = initialRights[5];
3630     }
3631     /* [HGM] e.p. rights. Assume that ICS sends file number here? */
3632     epStatus[moveNum] = double_push == -1 ? EP_NONE : double_push + BOARD_LEFT;
3633
3634     
3635     if (ics_getting_history == H_GOT_REQ_HEADER ||
3636         ics_getting_history == H_GOT_UNREQ_HEADER) {
3637         /* This was an initial position from a move list, not
3638            the current position */
3639         return;
3640     }
3641     
3642     /* Update currentMove and known move number limits */
3643     newMove = newGame || moveNum > forwardMostMove;
3644
3645     /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */
3646     if (!newGame && appData.icsEngineAnalyze && moveNum < forwardMostMove) {
3647         takeback = forwardMostMove - moveNum;
3648         for (i = 0; i < takeback; i++) {
3649              if (appData.debugMode) fprintf(debugFP, "take back move\n");
3650              SendToProgram("undo\n", &first);
3651         }
3652     }
3653
3654     if (newGame) {
3655         forwardMostMove = backwardMostMove = currentMove = moveNum;
3656         if (gameMode == IcsExamining && moveNum == 0) {
3657           /* Workaround for ICS limitation: we are not told the wild
3658              type when starting to examine a game.  But if we ask for
3659              the move list, the move list header will tell us */
3660             ics_getting_history = H_REQUESTED;
3661             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3662             SendToICS(str);
3663         }
3664     } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
3665                || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {
3666         forwardMostMove = moveNum;
3667         if (!pausing || currentMove > forwardMostMove)
3668           currentMove = forwardMostMove;
3669     } else {
3670         /* New part of history that is not contiguous with old part */ 
3671         if (pausing && gameMode == IcsExamining) {
3672             pauseExamInvalid = TRUE;
3673             forwardMostMove = pauseExamForwardMostMove;
3674             return;
3675         }
3676         forwardMostMove = backwardMostMove = currentMove = moveNum;
3677         if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {
3678             ics_getting_history = H_REQUESTED;
3679             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3680             SendToICS(str);
3681         }
3682     }
3683     
3684     /* Update the clocks */
3685     if (strchr(elapsed_time, '.')) {
3686       /* Time is in ms */
3687       timeRemaining[0][moveNum] = whiteTimeRemaining = white_time;
3688       timeRemaining[1][moveNum] = blackTimeRemaining = black_time;
3689     } else {
3690       /* Time is in seconds */
3691       timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;
3692       timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;
3693     }
3694       
3695
3696 #if ZIPPY
3697     if (appData.zippyPlay && newGame &&
3698         gameMode != IcsObserving && gameMode != IcsIdle &&
3699         gameMode != IcsExamining)
3700       ZippyFirstBoard(moveNum, basetime, increment);
3701 #endif
3702     
3703     /* Put the move on the move list, first converting
3704        to canonical algebraic form. */
3705     if (moveNum > 0) {
3706   if (appData.debugMode) {
3707     if (appData.debugMode) { int f = forwardMostMove;
3708         fprintf(debugFP, "parseboard %d, castling = %d %d %d %d %d %d\n", f,
3709                 castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);
3710     }
3711     fprintf(debugFP, "accepted move %s from ICS, parse it.\n", move_str);
3712     fprintf(debugFP, "moveNum = %d\n", moveNum);
3713     fprintf(debugFP, "board = %d-%d x %d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT);
3714     setbuf(debugFP, NULL);
3715   }
3716         if (moveNum <= backwardMostMove) {
3717             /* We don't know what the board looked like before
3718                this move.  Punt. */
3719             strcpy(parseList[moveNum - 1], move_str);
3720             strcat(parseList[moveNum - 1], " ");
3721             strcat(parseList[moveNum - 1], elapsed_time);
3722             moveList[moveNum - 1][0] = NULLCHAR;
3723         } else if (strcmp(move_str, "none") == 0) {
3724             // [HGM] long SAN: swapped order; test for 'none' before parsing move
3725             /* Again, we don't know what the board looked like;
3726                this is really the start of the game. */
3727             parseList[moveNum - 1][0] = NULLCHAR;
3728             moveList[moveNum - 1][0] = NULLCHAR;
3729             backwardMostMove = moveNum;
3730             startedFromSetupPosition = TRUE;
3731             fromX = fromY = toX = toY = -1;
3732         } else {
3733           // [HGM] long SAN: if legality-testing is off, disambiguation might not work or give wrong move. 
3734           //                 So we parse the long-algebraic move string in stead of the SAN move
3735           int valid; char buf[MSG_SIZ], *prom;
3736
3737           // str looks something like "Q/a1-a2"; kill the slash
3738           if(str[1] == '/') 
3739                 sprintf(buf, "%c%s", str[0], str+2);
3740           else  strcpy(buf, str); // might be castling
3741           if((prom = strstr(move_str, "=")) && !strstr(buf, "=")) 
3742                 strcat(buf, prom); // long move lacks promo specification!
3743           if(!appData.testLegality && move_str[1] != '@') { // drops never ambiguous (parser chokes on long form!)
3744                 if(appData.debugMode) 
3745                         fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf);
3746                 strcpy(move_str, buf);
3747           }
3748           valid = ParseOneMove(move_str, moveNum - 1, &moveType,
3749                                 &fromX, &fromY, &toX, &toY, &promoChar)
3750                || ParseOneMove(buf, moveNum - 1, &moveType,
3751                                 &fromX, &fromY, &toX, &toY, &promoChar);
3752           // end of long SAN patch
3753           if (valid) {
3754             (void) CoordsToAlgebraic(boards[moveNum - 1],
3755                                      PosFlags(moveNum - 1), EP_UNKNOWN,
3756                                      fromY, fromX, toY, toX, promoChar,
3757                                      parseList[moveNum-1]);
3758             switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN,
3759                              castlingRights[moveNum]) ) {
3760               case MT_NONE:
3761               case MT_STALEMATE:
3762               default:
3763                 break;
3764               case MT_CHECK:
3765                 if(gameInfo.variant != VariantShogi)
3766                     strcat(parseList[moveNum - 1], "+");
3767                 break;
3768               case MT_CHECKMATE:
3769               case MT_STAINMATE: // [HGM] xq: for notation stalemate that wins counts as checkmate
3770                 strcat(parseList[moveNum - 1], "#");
3771                 break;
3772             }
3773             strcat(parseList[moveNum - 1], " ");
3774             strcat(parseList[moveNum - 1], elapsed_time);
3775             /* currentMoveString is set as a side-effect of ParseOneMove */
3776             strcpy(moveList[moveNum - 1], currentMoveString);
3777             strcat(moveList[moveNum - 1], "\n");
3778           } else {
3779             /* Move from ICS was illegal!?  Punt. */
3780   if (appData.debugMode) {
3781     fprintf(debugFP, "Illegal move from ICS '%s'\n", move_str);
3782     fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);
3783   }
3784             strcpy(parseList[moveNum - 1], move_str);
3785             strcat(parseList[moveNum - 1], " ");
3786             strcat(parseList[moveNum - 1], elapsed_time);
3787             moveList[moveNum - 1][0] = NULLCHAR;
3788             fromX = fromY = toX = toY = -1;
3789           }
3790         }
3791   if (appData.debugMode) {
3792     fprintf(debugFP, "Move parsed to '%s'\n", parseList[moveNum - 1]);
3793     setbuf(debugFP, NULL);
3794   }
3795
3796 #if ZIPPY
3797         /* Send move to chess program (BEFORE animating it). */
3798         if (appData.zippyPlay && !newGame && newMove && 
3799            (!appData.getMoveList || backwardMostMove == 0) && first.initDone) {
3800
3801             if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||
3802                 (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {
3803                 if (moveList[moveNum - 1][0] == NULLCHAR) {
3804                     sprintf(str, _("Couldn't parse move \"%s\" from ICS"),
3805                             move_str);
3806                     DisplayError(str, 0);
3807                 } else {
3808                     if (first.sendTime) {
3809                         SendTimeRemaining(&first, gameMode == IcsPlayingWhite);
3810                     }
3811                     bookHit = SendMoveToBookUser(moveNum - 1, &first, FALSE); // [HGM] book
3812                     if (firstMove && !bookHit) {
3813                         firstMove = FALSE;
3814                         if (first.useColors) {
3815                           SendToProgram(gameMode == IcsPlayingWhite ?
3816                                         "white\ngo\n" :
3817                                         "black\ngo\n", &first);
3818                         } else {
3819                           SendToProgram("go\n", &first);
3820                         }