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