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