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