translated a handfull of strings and set the codeset to UTF-8 for the translations.
[xboard.git] / winboard-dm-beta4 / backend.c
1 /*
2  * backend.c -- Common back end for X and Windows NT versions of
3  * XBoard $Id$
4  *
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
7  *
8  * The following terms apply to Digital Equipment Corporation's copyright
9  * interest in XBoard:
10  * ------------------------------------------------------------------------
11  * All Rights Reserved
12  *
13  * Permission to use, copy, modify, and distribute this software and its
14  * documentation for any purpose and without fee is hereby granted,
15  * provided that the above copyright notice appear in all copies and that
16  * both that copyright notice and this permission notice appear in
17  * supporting documentation, and that the name of Digital not be
18  * used in advertising or publicity pertaining to distribution of the
19  * software without specific, written prior permission.
20  *
21  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
22  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
23  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
24  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
25  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
26  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
27  * SOFTWARE.
28  * ------------------------------------------------------------------------
29  *
30  * The following terms apply to the enhanced version of XBoard distributed
31  * by the Free Software Foundation:
32  * ------------------------------------------------------------------------
33  * This program is free software; you can redistribute it and/or modify
34  * it under the terms of the GNU General Public License as published by
35  * the Free Software Foundation; either version 2 of the License, or
36  * (at your option) any later version.
37  *
38  * This program is distributed in the hope that it will be useful,
39  * but WITHOUT ANY WARRANTY; without even the implied warranty of
40  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
41  * GNU General Public License for more details.
42  *
43  * You should have received a copy of the GNU General Public License
44  * along with this program; if not, write to the Free Software
45  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
46  * ------------------------------------------------------------------------
47  *
48  * See the file ChangeLog for a revision history.  */
49
50 #include "config.h"
51
52 #include <stdio.h>
53 #include <ctype.h>
54 #include <errno.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <math.h>
58 /* daniel */
59 #include <windows.h> 
60
61 #if STDC_HEADERS
62 # include <stdlib.h>
63 # include <string.h>
64 #else /* not STDC_HEADERS */
65 # if HAVE_STRING_H
66 #  include <string.h>
67 # else /* not HAVE_STRING_H */
68 #  include <strings.h>
69 # endif /* not HAVE_STRING_H */
70 #endif /* not STDC_HEADERS */
71
72 #if HAVE_SYS_FCNTL_H
73 # include <sys/fcntl.h>
74 #else /* not HAVE_SYS_FCNTL_H */
75 # if HAVE_FCNTL_H
76 #  include <fcntl.h>
77 # endif /* HAVE_FCNTL_H */
78 #endif /* not HAVE_SYS_FCNTL_H */
79
80 #if TIME_WITH_SYS_TIME
81 # include <sys/time.h>
82 # include <time.h>
83 #else
84 # if HAVE_SYS_TIME_H
85 #  include <sys/time.h>
86 # else
87 #  include <time.h>
88 # endif
89 #endif
90
91 #if defined(_amigados) && !defined(__GNUC__)
92 struct timezone {
93     int tz_minuteswest;
94     int tz_dsttime;
95 };
96 extern int gettimeofday(struct timeval *, struct timezone *);
97 #endif
98
99 #if HAVE_UNISTD_H
100 # include <unistd.h>
101 #endif
102
103 #include "common.h"
104 #include "frontend.h"
105 #include "backend.h"
106 #include "parser.h"
107 #include "moves.h"
108 #if ZIPPY
109 # include "zippy.h"
110 #endif
111 #include "backendz.h"
112 #include "winboard.h"
113
114 /* A point in time */
115 typedef struct {
116     long sec;  /* Assuming this is >= 32 bits */
117     int ms;    /* Assuming this is >= 16 bits */
118 } TimeMark;
119
120 int establish P((void));
121 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
122                          char *buf, int count, int error));
123 void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
124                       char *buf, int count, int error));
125 void SendToICS P((char *s));
126 void SendToICSDelayed P((char *s, long msdelay));
127 void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
128                       int toX, int toY));
129 void InitPosition P((int redraw));
130 void HandleMachineMove P((char *message, ChessProgramState *cps));
131 int AutoPlayOneMove P((void));
132 int LoadGameOneMove P((ChessMove readAhead));
133 int LoadGameFromFile P((char *filename, int n, char *title, int useList));
134 int LoadPositionFromFile P((char *filename, int n, char *title));
135 int SavePositionToFile P((char *filename));
136 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
137                   Board board));
138 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
139 void ShowMove P((int fromX, int fromY, int toX, int toY));
140 void FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
141                    /*char*/int promoChar));
142 void BackwardInner P((int target));
143 void ForwardInner P((int target));
144 void GameEnds P((ChessMove result, char *resultDetails, int whosays));
145 void EditPositionDone P((void));
146 void PrintOpponents P((FILE *fp));
147 void PrintPosition P((FILE *fp, int move));
148 void StartChessProgram P((ChessProgramState *cps));
149 void GuiCommand P((int command, int param));
150 void IcsAnalyzeOutPut P((ChessProgramState *cps, int endThink)); 
151 int  SwitchGames P((void));
152 void SendToProgram P((char *message, ChessProgramState *cps));
153 void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
154 void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
155                            char *buf, int count, int error));
156 void SendTimeControl P((ChessProgramState *cps,
157                         int mps, long tc, int inc, int sd, int st));
158 char *TimeControlTagValue P((void));
159 void Attention P((ChessProgramState *cps));
160 void FeedMovesToProgram P((ChessProgramState *cps, int upto));
161 void ResurrectChessProgram P((void));
162 void DisplayComment P((int moveNumber, char *text));
163 void DisplayMove P((int moveNumber));
164 void GetTimeMark P((TimeMark *));
165 void ParseGameHistory P((char *game));
166 void ParseBoard12 P((char *string));
167 void StartClocks P((void));
168 void SwitchClocks P((void));
169 void StopClocks P((void));
170 void ResetClocks P((void));
171 char *PGNDate P((void));
172 void SetGameInfo P((void));
173 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
174 int RegisterMove P((void));
175 void MakeRegisteredMove P((void));
176 void TruncateGame P((void));
177 int looking_at P((char *, int *, char *));
178 void CopyPlayerNameIntoFileName P((char **, char *));
179 char *SavePart P((char *));
180 int SaveGameOldStyle P((FILE *));
181 int SaveGamePGN P((FILE *));
182
183 long SubtractTimeMarks P((TimeMark *, TimeMark *));
184 int CheckFlags P((void));
185 long NextTickLength P((long));
186 void CheckTimeControl P((void));
187 void show_bytes P((FILE *, char *, int));
188 int string_to_rating P((char *str));
189 void ParseFeatures P((char* args, ChessProgramState *cps));
190 void InitBackEnd3 P((void));
191 void FeatureDone P((ChessProgramState* cps, int val));
192 void InitChessProgram P((ChessProgramState *cps));
193 extern int tinyLayout, smallLayout;
194
195 void ParseZippyP3 P((char* data, char* player));
196
197 /* States for ics_getting_history */
198 #define H_FALSE 0
199 #define H_REQUESTED 1
200 #define H_GOT_REQ_HEADER 2
201 #define H_GOT_UNREQ_HEADER 3
202 #define H_GETTING_MOVES 4
203 #define H_GOT_UNWANTED_HEADER 5
204
205 /* whosays values for GameEnds */
206 #define GE_ICS 0
207 #define GE_ENGINE 1
208 #define GE_PLAYER 2
209 #define GE_FILE 3
210 #define GE_XBOARD 4
211
212 /* Maximum number of games in a cmail message */
213 #define CMAIL_MAX_GAMES 20
214
215 /* Different types of move when calling RegisterMove */
216 #define CMAIL_MOVE   0
217 #define CMAIL_RESIGN 1
218 #define CMAIL_DRAW   2
219 #define CMAIL_ACCEPT 3
220
221 /* Different types of result to remember for each game */
222 #define CMAIL_NOT_RESULT 0
223 #define CMAIL_OLD_RESULT 1
224 #define CMAIL_NEW_RESULT 2
225
226 /* Telnet protocol constants */
227 #define TN_WILL 0373
228 #define TN_WONT 0374
229 #define TN_DO   0375
230 #define TN_DONT 0376
231 #define TN_IAC  0377
232 #define TN_ECHO 0001
233 #define TN_SGA  0003
234 #define TN_PORT 23
235
236 /* Fake up flags for now, as we aren't keeping track of castling
237    availability yet */
238 int
239 PosFlags(index)
240 {
241   int flags = F_ALL_CASTLE_OK;
242   if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
243   switch (gameInfo.variant) {
244   case VariantSuicide:
245   case VariantGiveaway:
246     flags |= F_IGNORE_CHECK;
247     flags &= ~F_ALL_CASTLE_OK;
248     break;
249   case VariantAtomic:
250     flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
251     break;
252   case VariantKriegspiel:
253     flags |= F_KRIEGSPIEL_CAPTURE;
254     break;
255   case VariantNoCastle:
256     flags &= ~F_ALL_CASTLE_OK;
257     break;
258   default:
259     break;
260   }
261   return flags;
262 }
263
264 FILE *gameFileFP, *debugFP;
265
266 char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
267 char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
268 char thinkOutput1[MSG_SIZ*10];
269
270 /*ChessProgramState first, second; */
271
272 /* premove variables */
273 int premoveToX = 0;
274 int premoveToY = 0;
275 int premoveFromX = 0;
276 int premoveFromY = 0;
277 int premovePromoChar = 0;
278 int gotPremove = 0;
279 Boolean alarmSounded;
280 /* end premove variables */
281
282 #define ICS_GENERIC 0
283 #define ICS_ICC 1
284 #define ICS_FICS 2
285 #define ICS_CHESSNET 3 /* not really supported */
286 int ics_type = ICS_GENERIC;
287 char *ics_prefix = "$";
288
289 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
290 int pauseExamForwardMostMove = 0;
291 int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
292 int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
293 int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
294 int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
295 int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
296 int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
297 int whiteFlag = FALSE, blackFlag = FALSE;
298 int userOfferedDraw = FALSE;
299 int ics_user_moved = 0, ics_getting_history = H_FALSE, ics_gamenum = -1;
300 int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
301 int cmailMoveType[CMAIL_MAX_GAMES];
302 prefixHint = 0; /* PonderMove true/false */
303 long ics_clock_paused = 0;
304 ProcRef icsPR = NoProc, cmailPR = NoProc;
305 InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
306 gameMode = BeginningOfGame;
307 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
308 char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
309 char white_holding[64], black_holding[64];
310 TimeMark lastNodeCountTime;
311 long lastNodeCount = 0;
312 int have_sent_ICS_logon = 0;
313 int movesPerSession;
314 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
315 long timeRemaining[2][MAX_MOVES];
316 int matchGame = 0;
317 TimeMark programStartTime;
318 char ics_handle[MSG_SIZ];
319 int have_set_title = 0;
320 smartQueue icsQueue[max_gamenum];
321
322 /* zippypassword3 */
323 typedef struct {
324         char string[512];
325 } value[6];
326 value command;
327
328 /* Search stats from chessprogram */
329 typedef struct {
330   char movelist[MSG_SIZ]; /* Last PV we were sent */
331   char ponderMove[12];    /* Current ponder move */
332   int depth;              /* Current search depth */
333   int nr_moves;           /* Total nr of root moves */
334   int moves_left;         /* Moves remaining to be searched */
335   char move_name[MOVE_LEN];  /* Current move being searched, if provided */
336   unsigned long nodes;    /* # of nodes searched */
337   int time;               /* Search time (centiseconds) */
338   int score;              /* Score (centipawns) */
339   int got_only_move;      /* If last msg was "(only move)" */
340   int got_fail;           /* 0 - nothing, 1 - got "--", 2 - got "++" */
341   int ok_to_send;         /* handshaking between send & recv */
342   int line_is_book;       /* 1 if movelist is book moves */
343   int seen_stat;          /* 1 if we've seen the stat01: line */
344   int GUI_time;                   /* Time from EngineRoom */
345 } ChessProgramStats;
346
347 static ChessProgramStats programStats;
348  
349 /* animateTraining preserves the state of appData.animate
350  * when Training mode is activated. This allows the
351  * response to be animated when appData.animate == TRUE and
352  * appData.animateDragging == TRUE.
353  */
354 Boolean animateTraining;
355
356 GameInfo gameInfo;
357
358 AppData appData;
359
360 Board boards[MAX_MOVES];
361 Board initialPosition = {
362     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
363         WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
364     { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
365         WhitePawn, WhitePawn, WhitePawn, WhitePawn },
366     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
367         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
368     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
369         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
370     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
371         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
372     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
373         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
374     { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
375         BlackPawn, BlackPawn, BlackPawn, BlackPawn },
376     { BlackRook, BlackKnight, BlackBishop, BlackQueen,
377         BlackKing, BlackBishop, BlackKnight, BlackRook }
378 };
379 Board twoKingsPosition = {
380     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
381         WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
382     { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
383         WhitePawn, WhitePawn, WhitePawn, WhitePawn },
384     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
385         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
386     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
387         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
388     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
389         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
390     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
391         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
392     { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
393         BlackPawn, BlackPawn, BlackPawn, BlackPawn },
394     { BlackRook, BlackKnight, BlackBishop, BlackQueen,
395         BlackKing, BlackKing, BlackKnight, BlackRook }
396 };
397
398 /* Convert str to a rating. Checks for special cases of "----",
399    "++++", etc. Also strips ()'s */
400 int
401 string_to_rating(str)
402   char *str;
403 {
404   while(*str && !isdigit(*str)) ++str;
405   if (!*str)
406     return 0;   /* One of the special "no rating" cases */
407   else
408     return atoi(str);
409 }
410
411 void
412 ClearProgramStats()
413 {
414     /* Init programStats */
415     programStats.movelist[0] = NULLCHAR;
416         programStats.ponderMove[0] = 0;
417     programStats.depth = 0;
418     programStats.nr_moves = 0;
419     programStats.moves_left = 0;
420     programStats.nodes = 0;
421     programStats.time = 100;
422     programStats.score = 0;
423     programStats.got_only_move = 0;
424     programStats.got_fail = 0;
425     programStats.line_is_book = 0;
426 }
427
428 void
429 InitBackEnd1()
430 {
431     int matched, min, sec;
432
433     GetTimeMark(&programStartTime);
434
435     ClearProgramStats();
436     programStats.ok_to_send = 1;
437     programStats.seen_stat = 0;
438         supportStat = 0;
439
440     /*
441      * Initialize game list
442      */
443     ListNew(&gameList);
444
445
446     /*
447      * Internet chess server status
448      */
449     if (appData.icsActive) {
450         appData.matchMode = FALSE;
451         appData.matchGames = 0;
452 #if ZIPPY       
453         appData.noChessProgram = !appData.zippyPlay;
454 #else
455         appData.zippyPlay = FALSE;
456         appData.zippyTalk = FALSE;
457         appData.noChessProgram = TRUE;
458 #endif
459         if (*appData.icsHelper != NULLCHAR) {
460             appData.useTelnet = TRUE;
461             appData.telnetProgram = appData.icsHelper;
462         }
463     } else {
464         appData.zippyTalk = appData.zippyPlay = FALSE;
465     }
466
467     /*
468      * Parse timeControl resource
469      */
470     if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
471                           appData.movesPerSession)) {
472         char buf[MSG_SIZ];
473         sprintf(buf, "bad timeControl option %s", appData.timeControl);
474         DisplayFatalError(buf, 0, 2);
475     }
476
477     /*
478      * Parse searchTime resource
479      */
480     if (*appData.searchTime != NULLCHAR) {
481         matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
482         if (matched == 1) {
483             searchTime = min * 60;
484         } else if (matched == 2) {
485             searchTime = min * 60 + sec;
486         } else {
487             char buf[MSG_SIZ];
488             sprintf(buf, "bad searchTime option %s", appData.searchTime);
489             DisplayFatalError(buf, 0, 2);
490         }
491     }
492     
493     first.which = "first";
494     second.which = "second";
495     first.maybeThinking = second.maybeThinking = FALSE;
496     first.pr = second.pr = NoProc;
497     first.isr = second.isr = NULL;
498     first.sendTime = second.sendTime = 2;
499     first.sendDrawOffers = 1;
500     if (appData.firstPlaysBlack) {
501         first.twoMachinesColor = "black\n";
502         second.twoMachinesColor = "white\n";
503     } else {
504         first.twoMachinesColor = "white\n";
505         second.twoMachinesColor = "black\n";
506     }
507     first.program = appData.firstChessProgram;
508     second.program = appData.secondChessProgram;
509     first.host = appData.firstHost;
510     second.host = appData.secondHost;
511     first.dir = appData.firstDirectory;
512     second.dir = appData.secondDirectory;
513     first.other = &second;
514     second.other = &first;
515     first.initString = appData.initString;
516     second.initString = appData.secondInitString;
517     first.computerString = appData.firstComputerString;
518     second.computerString = appData.secondComputerString;
519     first.useSigint = second.useSigint = TRUE;
520     first.useSigterm = second.useSigterm = TRUE;
521     first.reuse = appData.reuseFirst;
522     second.reuse = appData.reuseSecond;
523     first.useSetboard = second.useSetboard = FALSE;
524     first.useSAN = second.useSAN = FALSE;
525     first.usePing = second.usePing = FALSE;
526     first.lastPing = second.lastPing = 0;
527     first.lastPong = second.lastPong = 0;
528     first.usePlayother = second.usePlayother = FALSE;
529     first.useColors = second.useColors = TRUE;
530     first.useUsermove = second.useUsermove = FALSE;
531     first.sendICS = second.sendICS = FALSE;
532     first.sendName = second.sendName = appData.icsActive;
533     first.sdKludge = second.sdKludge = FALSE;
534     first.stKludge = second.stKludge = FALSE;
535     TidyProgramName(first.program, first.host, first.tidy);
536     TidyProgramName(second.program, second.host, second.tidy);
537     first.matchWins = second.matchWins = 0;
538     strcpy(first.variants, appData.variant);
539     strcpy(second.variants, appData.variant);
540     first.analysisSupport = second.analysisSupport = 2; /* detect */
541     first.analyzing = second.analyzing = FALSE;
542     first.initDone = second.initDone = FALSE;
543
544     if (appData.firstProtocolVersion > PROTOVER ||
545         appData.firstProtocolVersion < 1) {
546       char buf[MSG_SIZ];
547       sprintf(buf, "protocol version %d not supported",
548               appData.firstProtocolVersion);
549       DisplayFatalError(buf, 0, 2);
550     } else {
551       first.protocolVersion = appData.firstProtocolVersion;
552     }
553
554     if (appData.secondProtocolVersion > PROTOVER ||
555         appData.secondProtocolVersion < 1) {
556       char buf[MSG_SIZ];
557       sprintf(buf, "protocol version %d not supported",
558               appData.secondProtocolVersion);
559       DisplayFatalError(buf, 0, 2);
560     } else {
561       second.protocolVersion = appData.secondProtocolVersion;
562     }
563
564     if (appData.icsActive) {
565         appData.clockMode = TRUE;  /* changes dynamically in ICS mode */
566     } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
567         appData.clockMode = FALSE;
568         first.sendTime = second.sendTime = 0;
569     }
570     
571 #if ZIPPY
572     /* Override some settings from environment variables, for backward
573        compatibility.  Unfortunately it's not feasible to have the env
574        vars just set defaults, at least in xboard.  Ugh.
575     */
576     if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
577       ZippyInit();
578     }
579 #endif
580     
581     if (appData.noChessProgram) {
582         programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)
583                                         + strlen(PATCHLEVEL));
584         sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);
585     } else {
586         char *p, *q;
587         q = first.program;
588         while (*q != ' ' && *q != NULLCHAR) q++;
589         p = q;
590         while (p > first.program && *(p-1) != '/') p--;
591         programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
592                                         + strlen(PATCHLEVEL) + (q - p));
593         sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);
594         strncat(programVersion, p, q - p);
595     }
596
597     if (!appData.icsActive) {
598       char buf[MSG_SIZ];
599       /* Check for variants that are supported only in ICS mode,
600          or not at all.  Some that are accepted here nevertheless
601          have bugs; see comments below.
602       */
603       VariantClass variant = StringToVariant(appData.variant);
604       switch (variant) {
605       case VariantBughouse:     /* need four players and two boards */
606       case VariantKriegspiel:   /* need to hide pieces and move details */
607       case VariantFischeRandom: /* castling doesn't work, shuffle not done */
608         sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);
609         DisplayFatalError(buf, 0, 2);
610         return;
611
612       case VariantUnknown:
613       case VariantLoadable:
614       case Variant29:
615       case Variant30:
616       case Variant31:
617       case Variant32:
618       case Variant33:
619       case Variant34:
620       case Variant35:
621       case Variant36:
622       default:
623         sprintf(buf, "Unknown variant name %s", appData.variant);
624         DisplayFatalError(buf, 0, 2);
625         return;
626
627       case VariantNormal:     /* definitely works! */
628       case VariantWildCastle: /* pieces not automatically shuffled */
629       case VariantNoCastle:   /* pieces not automatically shuffled */
630       case VariantCrazyhouse: /* holdings not shown,
631                                  offboard interposition not understood */
632       case VariantLosers:     /* should work except for win condition,
633                                  and doesn't know captures are mandatory */
634       case VariantSuicide:    /* should work except for win condition,
635                                  and doesn't know captures are mandatory */
636       case VariantGiveaway:   /* should work except for win condition,
637                                  and doesn't know captures are mandatory */
638       case VariantTwoKings:   /* should work */
639       case VariantAtomic:     /* should work except for win condition */
640       case Variant3Check:     /* should work except for win condition */
641       case VariantShatranj:   /* might work if TestLegality is off */
642         break;
643       }
644     }
645 }
646
647 int
648 ParseTimeControl(tc, ti, mps)
649      char *tc;
650      int ti;
651      int mps;
652 {
653     int matched, min, sec;
654
655     matched = sscanf(tc, "%d:%d", &min, &sec);
656     if (matched == 1) {
657         timeControl = min * 60 * 1000;
658     } else if (matched == 2) {
659         timeControl = (min * 60 + sec) * 1000;
660     } else {
661         return FALSE;
662     }
663
664     if (ti >= 0) {
665         timeIncrement = ti * 1000;  /* convert to ms */
666         movesPerSession = 0;
667     } else {
668         timeIncrement = 0;
669         movesPerSession = mps;
670     }
671     return TRUE;
672 }
673
674 void
675 InitBackEnd2()
676 {
677     if (appData.debugMode) {
678         fprintf(debugFP, "%s\n", programVersion);
679     }
680
681     if (appData.matchGames > 0) {
682         appData.matchMode = TRUE;
683     } else if (appData.matchMode) {
684         appData.matchGames = 1;
685     }
686     Reset(TRUE, FALSE);
687     if (appData.noChessProgram || first.protocolVersion == 1) {
688       InitBackEnd3();
689     } else {
690       /* kludge: allow timeout for initial "feature" commands */
691       FreezeUI();
692           if (appData.icsActive) {
693                         DisplayMessage("", "ICS-Mode: Waiting for chessprogram...");
694         } else {
695                         DisplayMessage("", "Starting chessprogram");
696         }
697       ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
698     }
699 }
700
701 void
702 InitBackEnd3 P((void))
703 {
704     GameMode initialMode;
705     char buf[MSG_SIZ];
706     int err;
707
708     InitChessProgram(&first);
709
710         /* Make a console window if needed */
711         if (appData.icsActive) {
712                 ConsoleCreate();
713         }
714         /* engine Room */
715         supportStat = 0;
716
717     if (appData.icsActive) {
718         err = establish();
719         if (err != 0) {
720             if (*appData.icsCommPort != NULLCHAR) {
721                 sprintf(buf, "Could not open comm port %s",  
722                         appData.icsCommPort);
723             } else {
724                 sprintf(buf, "Could not connect to host %s, port %s",  
725                         appData.icsHost, appData.icsPort);
726             }
727             DisplayFatalError(buf, err, 1);
728             return;
729         }
730         SetICSMode();
731         telnetISR =
732           AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
733         fromUserISR =
734           AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
735     } else if (appData.noChessProgram) {
736         SetNCPMode();
737     } else {
738         SetGNUMode();
739     }
740
741     if (*appData.cmailGameName != NULLCHAR) {
742         SetCmailMode();
743         OpenLoopback(&cmailPR);
744         cmailISR =
745           AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
746     }
747     
748     ThawUI();
749     DisplayMessage("", "");
750     if (StrCaseCmp(appData.initialMode, "") == 0) {
751       initialMode = BeginningOfGame; 
752     } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
753       initialMode = TwoMachinesPlay;
754     } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
755       initialMode = AnalyzeFile; 
756     } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
757       initialMode = AnalyzeMode;
758     } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
759       initialMode = MachinePlaysWhite;
760     } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
761       initialMode = MachinePlaysBlack;
762     } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
763       initialMode = EditGame;
764     } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
765       initialMode = EditPosition;
766     } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
767       initialMode = Training;
768     } else {
769       sprintf(buf, "Unknown initialMode %s", appData.initialMode);
770       DisplayFatalError(buf, 0, 2);
771       return;
772     }
773
774     if (appData.matchMode) {
775         /* Set up machine vs. machine match */
776         if (appData.noChessProgram) {
777             DisplayFatalError("Can't have a match with no chess programs",
778                               0, 2);
779             return;
780         }
781         matchMode = TRUE;
782         matchGame = 1;
783         if (*appData.loadGameFile != NULLCHAR) {
784             if (!LoadGameFromFile(appData.loadGameFile,
785                                   appData.loadGameIndex,
786                                   appData.loadGameFile, FALSE)) {
787                 DisplayFatalError("Bad game file", 0, 1);
788                 return;
789             }
790         } else if (*appData.loadPositionFile != NULLCHAR) {
791             if (!LoadPositionFromFile(appData.loadPositionFile,
792                                       appData.loadPositionIndex,
793                                       appData.loadPositionFile)) {
794                 DisplayFatalError("Bad position file", 0, 1);
795                 return;
796             }
797         }
798         TwoMachinesEvent();
799     } else if (*appData.cmailGameName != NULLCHAR) {
800         /* Set up cmail mode */
801         ReloadCmailMsgEvent(TRUE);
802     } else {
803         /* Set up other modes */
804         if (initialMode == AnalyzeFile) {
805           if (*appData.loadGameFile == NULLCHAR) {
806             DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1);
807             return;
808           }
809         }
810         if (*appData.loadGameFile != NULLCHAR) {
811             (void) LoadGameFromFile(appData.loadGameFile,
812                                     appData.loadGameIndex,
813                                     appData.loadGameFile, TRUE);
814         } else if (*appData.loadPositionFile != NULLCHAR) {
815             (void) LoadPositionFromFile(appData.loadPositionFile,
816                                         appData.loadPositionIndex,
817                                         appData.loadPositionFile);
818         }
819         if (initialMode == AnalyzeMode) {
820           if (appData.noChessProgram) {
821             DisplayFatalError("Analysis mode requires a chess engine", 0, 2);
822             return;
823           }
824           if (appData.icsActive) {
825             DisplayFatalError("Analysis mode does not work with ICS mode",0,2);
826             return;
827           }
828           AnalyzeModeEvent();
829         } else if (initialMode == AnalyzeFile) {
830           ShowThinkingEvent(TRUE);
831           AnalyzeFileEvent();
832           AnalysisPeriodicEvent(1);
833         } else if (initialMode == MachinePlaysWhite) {
834           if (appData.noChessProgram) {
835             DisplayFatalError("MachineWhite mode requires a chess engine",
836                               0, 2);
837             return;
838           }
839           if (appData.icsActive) {
840             DisplayFatalError("MachineWhite mode does not work with ICS mode",
841                               0, 2);
842             return;
843           }
844           MachineWhiteEvent();
845         } else if (initialMode == MachinePlaysBlack) {
846           if (appData.noChessProgram) {
847             DisplayFatalError("MachineBlack mode requires a chess engine",
848                               0, 2);
849             return;
850           }
851           if (appData.icsActive) {
852             DisplayFatalError("MachineBlack mode does not work with ICS mode",
853                               0, 2);
854             return;
855           }
856           MachineBlackEvent();
857         } else if (initialMode == TwoMachinesPlay) {
858           if (appData.noChessProgram) {
859             DisplayFatalError("TwoMachines mode requires a chess engine",
860                               0, 2);
861             return;
862           }
863           if (appData.icsActive) {
864             DisplayFatalError("TwoMachines mode does not work with ICS mode",
865                               0, 2);
866             return;
867           }
868           TwoMachinesEvent();
869         } else if (initialMode == EditGame) {
870           EditGameEvent();
871         } else if (initialMode == EditPosition) {
872           EditPositionEvent();
873         } else if (initialMode == Training) {
874           if (*appData.loadGameFile == NULLCHAR) {
875             DisplayFatalError("Training mode requires a game file", 0, 2);
876             return;
877           }
878           TrainingEvent();
879         }
880     }
881         /* If EngineRoom TRUE start */
882         if (appData.AnalysisWindow && appData.showThinking &&
883                 !appData.noChessProgram) {
884                 if (appData.icsActive && appData.zippyPlay) {
885                         AnalysisPopUp(0,0,0,0,0,0,0,5);
886                 }
887                 if (!appData.icsActive) AnalysisPopUp(0,0,0,0,0,0,0,5);
888                 StartAnalysisClock();
889         }
890 }
891
892 /*
893  * Establish will establish a contact to a remote host.port.
894  * Sets icsPR to a ProcRef for a process (or pseudo-process)
895  *  used to talk to the host.
896  * Returns 0 if okay, error code if not.
897  */
898 int
899 establish()
900 {
901     char buf[MSG_SIZ];
902
903     if (*appData.icsCommPort != NULLCHAR) {
904         /* Talk to the host through a serial comm port */
905         return OpenCommPort(appData.icsCommPort, &icsPR);
906
907     } else if (*appData.gateway != NULLCHAR) {
908         if (*appData.remoteShell == NULLCHAR) {
909             /* Use the rcmd protocol to run telnet program on a gateway host */
910             sprintf(buf, "%s %s %s",
911                     appData.telnetProgram, appData.icsHost, appData.icsPort);
912             return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);
913
914         } else {
915             /* Use the rsh program to run telnet program on a gateway host */
916             if (*appData.remoteUser == NULLCHAR) {
917                 sprintf(buf, "%s %s %s %s %s", appData.remoteShell,
918                         appData.gateway, appData.telnetProgram,
919                         appData.icsHost, appData.icsPort);
920             } else {
921                 sprintf(buf, "%s %s -l %s %s %s %s",
922                         appData.remoteShell, appData.gateway, 
923                         appData.remoteUser, appData.telnetProgram,
924                         appData.icsHost, appData.icsPort);
925             }
926             return StartChildProcess(buf, "", &icsPR);
927
928         }
929     } else if (appData.useTelnet) {
930         return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
931
932     } else {
933         /* TCP socket interface differs somewhat between
934            Unix and NT; handle details in the front end.
935            */
936         return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
937     }
938 }
939
940 void
941 show_bytes(fp, buf, count)
942      FILE *fp;
943      char *buf;
944      int count;
945 {
946     while (count--) {
947         if (*buf < 040 || *(unsigned char *) buf > 0177) {
948             fprintf(fp, "\\%03o", *buf & 0xff);
949         } else {
950             putc(*buf, fp);
951         }
952         buf++;
953     }
954     fflush(fp);
955 }
956
957 /* Returns an errno value */
958 int
959 OutputMaybeTelnet(pr, message, count, outError)
960      ProcRef pr;
961      char *message;
962      int count;
963      int *outError;
964 {
965     char buf[8192], *p, *q, *buflim;
966     int left, newcount, outcount;
967
968     if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
969         *appData.gateway != NULLCHAR) {
970         if (appData.debugMode) {
971             fprintf(debugFP, ">ICS: ");
972             show_bytes(debugFP, message, count);
973             fprintf(debugFP, "\n");
974         }
975         return OutputToProcess(pr, message, count, outError);
976     }
977
978     buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
979     p = message;
980     q = buf;
981     left = count;
982     newcount = 0;
983     while (left) {
984         if (q >= buflim) {
985             if (appData.debugMode) {
986                 fprintf(debugFP, ">ICS: ");
987                 show_bytes(debugFP, buf, newcount);
988                 fprintf(debugFP, "\n");
989             }
990             outcount = OutputToProcess(pr, buf, newcount, outError);
991             if (outcount < newcount) return -1; /* to be sure */
992             q = buf;
993             newcount = 0;
994         }
995         if (*p == '\n') {
996             *q++ = '\r';
997             newcount++;
998         } else if (((unsigned char) *p) == TN_IAC) {
999             *q++ = (char) TN_IAC;
1000             newcount ++;
1001         }
1002         *q++ = *p++;
1003         newcount++;
1004         left--;
1005     }
1006     if (appData.debugMode) {
1007         fprintf(debugFP, ">ICS: ");
1008         show_bytes(debugFP, buf, newcount);
1009         fprintf(debugFP, "\n");
1010     }
1011     outcount = OutputToProcess(pr, buf, newcount, outError);
1012     if (outcount < newcount) return -1; /* to be sure */
1013     return count;
1014 }
1015
1016 void
1017 read_from_player(isr, closure, message, count, error)
1018      InputSourceRef isr;
1019      VOIDSTAR closure;
1020      char *message;
1021      int count;
1022      int error;
1023 {
1024     int outError, outCount;
1025     static int gotEof = 0;
1026
1027     /* Pass data read from player on to ICS */
1028     if (count > 0) {
1029         gotEof = 0;
1030         outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
1031         if (outCount < count) {
1032             DisplayFatalError("Error writing to ICS", outError, 1);
1033         }
1034     } else if (count < 0) {
1035         RemoveInputSource(isr);
1036         DisplayFatalError("Error reading from keyboard", error, 1);
1037     } else if (gotEof++ > 0) {
1038         RemoveInputSource(isr);
1039         DisplayFatalError("Got end of file from keyboard", 0, 0);
1040     }
1041 }
1042
1043 void
1044 SendToICS(s)
1045      char *s;
1046 {
1047     int count, outCount, outError;
1048
1049     if (icsPR == NULL) return;
1050
1051     count = strlen(s);
1052     outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
1053     if (outCount < count) {
1054         DisplayFatalError("Error writing to ICS", outError, 1);
1055     }
1056 }
1057
1058 /* This is used for sending logon scripts to the ICS. Sending
1059    without a delay causes problems when using timestamp on ICC
1060    (at least on my machine). */
1061 void
1062 SendToICSDelayed(s,msdelay)
1063      char *s;
1064      long msdelay;
1065 {
1066     int count, outCount, outError;
1067
1068     if (icsPR == NULL) return;
1069
1070     count = strlen(s);
1071     if (appData.debugMode) {
1072         fprintf(debugFP, ">ICS: ");
1073         show_bytes(debugFP, s, count);
1074         fprintf(debugFP, "\n");
1075     }
1076     outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
1077                                       msdelay);
1078     if (outCount < count) {
1079         DisplayFatalError("Error writing to ICS", outError, 1);
1080     }
1081 }
1082
1083
1084 /* Remove all highlighting escape sequences in s
1085    Also deletes any suffix starting with '(' 
1086    */
1087 char *
1088 StripHighlightAndTitle(s)
1089      char *s;
1090 {
1091     static char retbuf[MSG_SIZ];
1092     char *p = retbuf;
1093
1094     while (*s != NULLCHAR) {
1095         while (*s == '\033') {
1096             while (*s != NULLCHAR && !isalpha(*s)) s++;
1097             if (*s != NULLCHAR) s++;
1098         }
1099         while (*s != NULLCHAR && *s != '\033') {
1100             if (*s == '(' || *s == '[') {
1101                 *p = NULLCHAR;
1102                 return retbuf;
1103             }
1104             *p++ = *s++;
1105         }
1106     }
1107     *p = NULLCHAR;
1108     return retbuf;
1109 }
1110
1111 /* Remove all highlighting escape sequences in s */
1112 char *
1113 StripHighlight(s)
1114      char *s;
1115 {
1116     static char retbuf[MSG_SIZ];
1117     char *p = retbuf;
1118
1119     while (*s != NULLCHAR) {
1120         while (*s == '\033') {
1121             while (*s != NULLCHAR && !isalpha(*s)) s++;
1122             if (*s != NULLCHAR) s++;
1123         }
1124         while (*s != NULLCHAR && *s != '\033') {
1125             *p++ = *s++;
1126         }
1127     }
1128     *p = NULLCHAR;
1129     return retbuf;
1130 }
1131
1132 char *variantNames[] = VARIANT_NAMES;
1133 char *
1134 VariantName(v)
1135      VariantClass v;
1136 {
1137     return variantNames[v];
1138 }
1139
1140
1141 /* Identify a variant from the strings the chess servers use or the
1142    PGN Variant tag names we use. */
1143 VariantClass
1144 StringToVariant(e)
1145      char *e;
1146 {
1147     char *p;
1148     int wnum = -1;
1149     VariantClass v = VariantNormal;
1150     int i, found = FALSE;
1151     char buf[MSG_SIZ];
1152
1153     if (!e) return v;
1154     
1155     for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
1156       if (StrCaseStr(e, variantNames[i])) {
1157         v = (VariantClass) i;
1158         found = TRUE;
1159         break;
1160       }
1161     }
1162
1163     if (!found) {
1164       if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
1165           || StrCaseStr(e, "wild/fr")) {
1166         v = VariantFischeRandom;
1167       } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
1168                  (i = 1, p = StrCaseStr(e, "w"))) {
1169         p += i;
1170         while (*p && (isspace(*p) || *p == '(')) p++;
1171         if (isdigit(*p)) {
1172           wnum = atoi(p);
1173         } else {
1174           wnum = -1;
1175         }
1176         switch (wnum) {
1177         case 0: /* FICS only, actually */
1178         case 1:
1179           /* Castling legal even if K starts on d-file */
1180           v = VariantWildCastle;
1181           break;
1182         case 2:
1183         case 3:
1184         case 4:
1185           /* Castling illegal even if K & R happen to start in
1186              normal positions. */
1187           v = VariantNoCastle;
1188           break;
1189         case 5:
1190         case 7:
1191         case 8:
1192         case 10:
1193         case 11:
1194         case 12:
1195         case 13:
1196         case 14:
1197         case 15:
1198         case 18:
1199         case 19:
1200           /* Castling legal iff K & R start in normal positions */
1201           v = VariantNormal;
1202           break;
1203         case 6:
1204         case 20:
1205         case 21:
1206           /* Special wilds for position setup; unclear what to do here */
1207           v = VariantLoadable;
1208           break;
1209         case 9:
1210           /* Bizarre ICC game */
1211           v = VariantTwoKings;
1212           break;
1213         case 16:
1214           v = VariantKriegspiel;
1215           break;
1216         case 17:
1217           v = VariantLosers;
1218           break;
1219         case 22:
1220           v = VariantFischeRandom;
1221           break;
1222         case 23:
1223           v = VariantCrazyhouse;
1224           break;
1225         case 24:
1226           v = VariantBughouse;
1227           break;
1228         case 25:
1229           v = Variant3Check;
1230           break;
1231         case 26:
1232           /* Not quite the same as FICS suicide! */
1233           v = VariantGiveaway;
1234           break;
1235         case 27:
1236           v = VariantAtomic;
1237           break;
1238         case 28:
1239           v = VariantShatranj;
1240           break;
1241
1242         /* Temporary names for future ICC types.  The name *will* change in 
1243            the next xboard/WinBoard release after ICC defines it. */
1244         case 29:
1245           v = Variant29;
1246           break;
1247         case 30:
1248           v = Variant30;
1249           break;
1250         case 31:
1251           v = Variant31;
1252           break;
1253         case 32:
1254           v = Variant32;
1255           break;
1256         case 33:
1257           v = Variant33;
1258           break;
1259         case 34:
1260           v = Variant34;
1261           break;
1262         case 35:
1263           v = Variant35;
1264           break;
1265         case 36:
1266           v = Variant36;
1267           break;
1268
1269         case -1:
1270           /* Found "wild" or "w" in the string but no number;
1271              must assume it's normal chess. */
1272           v = VariantNormal;
1273           break;
1274         default:
1275           sprintf(buf, "Unknown wild type %d", wnum);
1276           DisplayError(buf, 0);
1277           v = VariantUnknown;
1278           break;
1279         }
1280       }
1281     }
1282     if (appData.debugMode) {
1283       fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",
1284               e, wnum, VariantName(v));
1285     }
1286     return v;
1287 }
1288
1289 static int leftover_start = 0, leftover_len = 0;
1290 char star_match[STAR_MATCH_N][MSG_SIZ];
1291
1292 /* Test whether pattern is present at &buf[*index]; if so, return TRUE,
1293    advance *index beyond it, and set leftover_start to the new value of
1294    *index; else return FALSE.  If pattern contains the character '*', it
1295    matches any sequence of characters not containing '\r', '\n', or the
1296    character following the '*' (if any), and the matched sequence(s) are
1297    copied into star_match.
1298    */
1299 int
1300 looking_at(buf, index, pattern)
1301      char *buf;
1302      int *index;
1303      char *pattern;
1304 {
1305     char *bufp = &buf[*index], *patternp = pattern;
1306     int star_count = 0;
1307     char *matchp = star_match[0];
1308     
1309     for (;;) {
1310         if (*patternp == NULLCHAR) {
1311             *index = leftover_start = bufp - buf;
1312             *matchp = NULLCHAR;
1313             return TRUE;
1314         }
1315         if (*bufp == NULLCHAR) return FALSE;
1316         if (*patternp == '*') {
1317             if (*bufp == *(patternp + 1)) {
1318                 *matchp = NULLCHAR;
1319                 matchp = star_match[++star_count];
1320                 patternp += 2;
1321                 bufp++;
1322                 continue;
1323             } else if (*bufp == '\n' || *bufp == '\r') {
1324                 patternp++;
1325                 if (*patternp == NULLCHAR)
1326                   continue;
1327                 else
1328                   return FALSE;
1329             } else {
1330                 *matchp++ = *bufp++;
1331                 continue;
1332             }
1333         }
1334         if (*patternp != *bufp) return FALSE;
1335         patternp++;
1336         bufp++;
1337     }
1338 }
1339
1340 void
1341 SendToPlayer(data, length)
1342      char *data;
1343      int length;
1344 {
1345     int error, outCount;
1346     outCount = OutputToProcess(NoProc, data, length, &error);
1347     if (outCount < length) {
1348         DisplayFatalError("Error writing to display", error, 1);
1349     }
1350 }
1351
1352 void
1353 PackHolding(packed, holding)
1354      char packed[];
1355      char *holding;
1356 {
1357     char *p = holding;
1358     char *q = packed;
1359     int runlength = 0;
1360     int curr = 9999;
1361     do {
1362         if (*p == curr) {
1363             runlength++;
1364         } else {
1365             switch (runlength) {
1366               case 0:
1367                 break;
1368               case 1:
1369                 *q++ = curr;
1370                 break;
1371               case 2:
1372                 *q++ = curr;
1373                 *q++ = curr;
1374                 break;
1375               default:
1376                 sprintf(q, "%d", runlength);
1377                 while (*q) q++;
1378                 *q++ = curr;
1379                 break;
1380             }
1381             runlength = 1;
1382             curr = *p;
1383         }
1384     } while (*p++);
1385     *q = NULLCHAR;
1386 }
1387
1388 /* Telnet protocol requests from the front end */
1389 void
1390 TelnetRequest(ddww, option)
1391      unsigned char ddww, option;
1392 {
1393     unsigned char msg[3];
1394     int outCount, outError;
1395
1396     if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
1397
1398     if (appData.debugMode) {
1399         char buf1[8], buf2[8], *ddwwStr, *optionStr;
1400         switch (ddww) {
1401           case TN_DO:
1402             ddwwStr = "DO";
1403             break;
1404           case TN_DONT:
1405             ddwwStr = "DONT";
1406             break;
1407           case TN_WILL:
1408             ddwwStr = "WILL";
1409             break;
1410           case TN_WONT:
1411             ddwwStr = "WONT";
1412             break;
1413           default:
1414             ddwwStr = buf1;
1415             sprintf(buf1, "%d", ddww);
1416             break;
1417         }
1418         switch (option) {
1419           case TN_ECHO:
1420             optionStr = "ECHO";
1421             break;
1422           default:
1423             optionStr = buf2;
1424             sprintf(buf2, "%d", option);
1425             break;
1426         }
1427         fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
1428     }
1429     msg[0] = TN_IAC;
1430     msg[1] = ddww;
1431     msg[2] = option;
1432     outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
1433     if (outCount < 3) {
1434         DisplayFatalError("Error writing to ICS", outError, 1);
1435     }
1436 }
1437
1438 void
1439 DoEcho()
1440 {
1441     if (!appData.icsActive) return;
1442     TelnetRequest(TN_DO, TN_ECHO);
1443 }
1444
1445 void
1446 DontEcho()
1447 {
1448     if (!appData.icsActive) return;
1449     TelnetRequest(TN_DONT, TN_ECHO);
1450 }
1451
1452 static int loggedOn = FALSE;
1453
1454 /*-- Game start info cache: --*/
1455 int gs_gamenum;
1456 char gs_kind[MSG_SIZ];
1457 static char player1Name[128] = "";
1458 static char player2Name[128] = "";
1459 static int player1Rating = -1;
1460 static int player2Rating = -1;
1461 /*----------------------------*/
1462
1463
1464 /* ICC user send showinfo <game number> und we send latest pv */
1465 /* If username beginn with guest* send advertisement ;-) */
1466 void
1467 showInfo(data, player)
1468 char* data;
1469 char* player;
1470 {       
1471         char *ptr = data;
1472         char temp[MSG_SIZ];
1473         char buf[MSG_SIZ];
1474         int i;
1475         
1476         /* this feature is only for ICC admins or chessprogramer */
1477         /* You must set commandline /icc to use this */
1478         if (appData.userVersion == TRUE) return;
1479         if (appData.ICC_feature == FALSE || ics_type != ICS_ICC) return;
1480         while (*ptr == ' ') ptr++;
1481         if (*ptr == 0 || (sizeof(ptr) > MSG_SIZ)) return;
1482         sscanf(ptr, "%s", buf);
1483         
1484         /* showgames command */
1485         if (strncmp(buf, "showgames", 9) == 0) {
1486                 {
1487                 int counter;
1488                 for (counter = 1; counter <= max_gamenum; counter++) {
1489                         if (icsQueue[counter].killPv > 0) {
1490                                 sprintf(temp, "tell %s I analyze game %d (%s/%s)\n", player, counter, 
1491                                                 icsQueue[counter].white, icsQueue[counter].black);
1492                                 SendToICS(temp);
1493                         }
1494                 }
1495                 return;
1496                 }
1497         }
1498         i = atoi(buf);  /* security first - user could try to buffer overflow data ! */
1499         if (i > max_gamenum || icsQueue[i].killPv == 0) {
1500                 sprintf(temp, "tell %s I don't analysis this game\n", player);
1501                 SendToICS(temp);
1502                 sprintf(temp, "tell %s \"tell %s showgames\" to see which games are currently analyzed.\n", player, ics_handle);
1503                 SendToICS(temp);
1504         } else if (icsQueue[i].killPv > 0) {
1505                 sprintf(temp, "tell %s Hello %s from Winboard-DM\n", player, player);
1506                 SendToICS(temp);
1507                 /* pos after "Depth " */
1508                 if (icsQueue[i].lastpv[0]) {
1509                         sprintf(temp, "tell %s Last analyze of game %d (%s/%s): (%s) Depth=%s\n", 
1510                                 player, i, icsQueue[i].white, icsQueue[i].black, first.tidy, icsQueue[i].lastpv);
1511                 } else {
1512                         sprintf(temp, "tell %s Sorry, no analysis avaible from game %d at the moment. Please try it later again.\n", 
1513                                 player, i);
1514                 }
1515                 SendToICS(temp);
1516                         /* for ICC guest send member and register info */
1517                 if (strncmp(player, "guest", 5) == 0) {
1518                         sprintf(temp, "tell %s Be member of ICC. Get a free 7-days trial membership: \"http://www.chessclub.com/register/english.html\" For more information type: \"help reg\"\n", player);
1519                         SendToICS(temp);
1520                 }
1521         }
1522 }
1523
1524 /* ICS-Analyze: 
1525    Call this to see if we have a game where we not send a message 
1526    for the current move
1527  */
1528
1529 int
1530 SwitchGames()
1531 {
1532         int counter;
1533         char buf[32];
1534         
1535         for (counter = 1; counter <= max_gamenum; counter++) {
1536                 if (icsQueue[counter].killPv > 0 && icsQueue[counter].CurrentMove >=
1537                         icsQueue[counter].MessageMove && icsQueue[counter].flag == 0) {
1538                                 if (appData.debugMode) fprintf(debugFP, "ICS-Analyze: We switch to game %d \n", counter);
1539                                 sprintf(buf, "refresh %d \n", counter);
1540                                 SendToICS(buf);
1541                                 return 0;
1542                                 break;
1543                 }
1544         }
1545         return 1;
1546 }
1547
1548 /* parse and action from zippy password3 */
1549 void 
1550 ParseZippyP3(data, player)
1551 char* data;
1552 char* player;
1553 {
1554         int counter = 0;
1555         int forward = 0;
1556         int i, j;
1557         char *ptr = data;
1558         char temp[MSG_SIZ];
1559
1560         if (appData.userVersion == TRUE) return;
1561
1562         for (;;) {
1563                 if (counter > 6) {
1564                         sprintf(temp, "tell %s Sorry, to many values. Max 6 values each line. Try again.\n", player);
1565                         SendToICS(temp);
1566                         sprintf(temp, "tell %s Or try send me \"help\"\n", player);
1567                         SendToICS(temp);
1568                         return;
1569                 }
1570                 while (*ptr == ' ') ptr++;
1571                 if (*ptr == 0) break;
1572                 sscanf(ptr, "%s", command[counter].string);
1573                 if (appData.debugMode) fprintf(debugFP, "zp3: %s\n", command[counter].string);
1574                 ptr = ptr + strlen(command[counter].string);
1575                 counter++;
1576         }
1577         if(strncmp(command[forward].string, "help", 4) == 0) {
1578                 /* icc don't accept text after new line :((( */
1579                 /* So, we must write every line */
1580                 sprintf(temp, "tell %s Hello %s from %s %s%s\n", player, player, PRODUCT, VERSION, PATCHLEVEL);
1581                 SendToICS(temp);
1582                 sprintf(temp, "tell %s analyze <observe|follow|unobserve|unfollow> <Playername|Gamenumber>\n", player);
1583                 SendToICS(temp);
1584                 sprintf(temp, "tell %s analyze <start|stop>\n", player);
1585                 SendToICS(temp);
1586                 sprintf(temp, "tell %s analyze output <whisper|kibitz|tell|none> if tell <value>\n", player);
1587                 SendToICS(temp);
1588                 sprintf(temp, "tell %s analyze killpv <value> <Gamenumber>\n", player);
1589                 SendToICS(temp);
1590                 sprintf(temp, "tell %s if you are setup killpv you enable it automaticly\n", player);
1591                 SendToICS(temp);
1592                 sprintf(temp, "tell %s analyze smartqueue <1|0>\n", player);
1593                 SendToICS(temp);
1594                 sprintf(temp, "tell %s analyze show <Gamenumber>\n", player);
1595                 SendToICS(temp);
1596                 sprintf(temp, "tell %s engine reset\n", player);
1597                 SendToICS(temp);
1598                 sprintf(temp, "tell %s <whisper|kibitz>\n", player);
1599                 SendToICS(temp);
1600                 return;
1601         }
1602
1603         if (strncmp(command[forward].string, "engine", 6) == 0) {
1604                 if (strncmp(command[forward+1].string, "reset", 5) == 0) {
1605                         sprintf(temp, "tell %s reset engine...\n", player);
1606                         SendToICS(temp);
1607                         ResurrectChessProgram();
1608                 }
1609                 return;
1610         }
1611         /*
1612         if (strncmp(command[forward].string, "whisper", 7) == 0) {
1613                         sprintf(temp, "tell %s GUI = whisper\n", player);
1614                         SendToICS(temp);
1615                         appData.SendOutPutToICS = 1;
1616                         return;
1617         } else if (trncmp(command[forward].string, "kibitz", 6) == 0) {
1618                         sprintf(temp, "tell %s GUI = kibitz\n", player);
1619                         SendToICS(temp);
1620                         appData.SendOutPutToICS = 2;
1621                         return;
1622         }
1623         */
1624         if(gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) {
1625                 sprintf(temp, "tell %s Sorry, i'm playing a game!\n", player);
1626                 SendToICS(temp);
1627                 return;
1628         }
1629         if (strncmp(command[forward].string, "analyze", 10) == 0) {
1630                 if (strncmp(command[forward+1].string, "observe", 7) == 0 ||
1631                         strncmp(command[forward+1].string, "follow", 6) == 0 ||
1632                         strncmp(command[forward+1].string, "unobserve", 9) == 0 ||
1633                         strncmp(command[forward+1].string, "unfollow", 8) == 0) {
1634
1635                         /* reset Queue if leave */
1636                         if (strncmp(command[forward+1].string, "unobserve", 9) == 0 ||
1637                                 strncmp(command[forward+1].string, "unfollow", 8) == 0) ResetIcsQueue(ics_gamenum);
1638                         
1639                         /* secure */
1640                         if(gameMode != IcsPlayingWhite || gameMode != IcsPlayingBlack) {
1641                                         sprintf(temp, "%s %s\n", command[forward+1].string,
1642                                         command[forward+2].string);
1643                                         SendToICS(temp);
1644                                         sprintf(temp, "tell %s action: %s %s\n", player, command[forward+1].string,
1645                                         command[forward+2].string);
1646                                 if (strncmp(command[forward+1].string, "observe", 7) == 0 ||
1647                                     strncmp(command[forward+1].string, "follow", 6) == 0) {
1648                 
1649                                         if (ics_gamenum > max_gamenum) {
1650                                                 sprintf(temp, "tell %s gamenumber to high\n", player);
1651                                                 SendToICS(temp);
1652                                                 return;
1653                                         }
1654                                 }
1655                         } else {
1656                                 sprintf(temp, "tell %s Sorry, i'm playing\n", player);
1657                         }
1658                 SendToICS(temp);
1659                 return;
1660                 }
1661
1662                 if (strncmp(command[forward+1].string, "start", 5) == 0) {
1663                         if (gameMode != IcsObserving) {
1664                                 sprintf(temp, "tell %s I must observe a game\n", player);
1665                         } else if (gameMode == IcsObserving) {
1666                                 sprintf(temp, "tell %s Starting ICS analyze...\n", player);
1667                                 appData.icsAnalyze = TRUE;
1668                                 IcsAnalyze(TRUE);
1669                         }
1670                         SendToICS(temp);
1671                         return;
1672                 }
1673                 if (strncmp(command[forward+1].string, "stop", 4) == 0) {
1674                         if (gameMode != IcsObserving) {
1675                                 sprintf(temp, "tell %s I must observe a game\n", player);
1676                         } else if (gameMode == IcsObserving) {
1677                                 sprintf(temp, "tell %s Stopping ICS analyze...\n", player);
1678                                 IcsAnalyze(FALSE);
1679                         }
1680                         SendToICS(temp);
1681                         return;
1682                 }
1683                 if (strncmp(command[forward+1].string, "output", 6) == 0) {
1684                         if(strncmp(command[forward+2].string, "whisper", 7) == 0) {
1685                                         appData.icsAnalyzeOutPut = 1;
1686                                         sprintf(temp, "tell %s analyze output is whisper\n", player);
1687                         } else if (strncmp(command[forward+2].string, "kibitz", 6) == 0) {
1688                                         appData.icsAnalyzeOutPut = 2;
1689                                         sprintf(temp, "tell %s analyze output is kibitz\n", player);
1690                         } else if (strncmp(command[forward+2].string, "tell", 4) == 0) {
1691                                         appData.icsAnalyzeOutPut = 3;
1692                                         if (command[forward+3].string[0] != NULLCHAR) {
1693                                                 if (strncmp(command[forward+3].string, "none", 4) == 0) {
1694                                                         appData.icsAnalyzeOutPut = 4;
1695                                                         sprintf(temp, "tell %s analyze output is: none\n", player);
1696                                                         SendToICS(temp);
1697                                                         return;
1698                                                 }
1699                                                 strcpy(appData.icsTells, command[forward+3].string);
1700                                                 sprintf(temp, "tell %s analyze output is tell: %s\n", player,
1701                                                         appData.icsTells);
1702                                         } else {
1703                                                 sprintf(temp, "tell %s Error: I don't know where i should send my analysis\n", player);
1704                                                 appData.icsAnalyzeOutPut = 4;
1705                                         }
1706                         }       
1707                         SendToICS(temp);
1708                 }
1709                 if (strncmp(command[forward+1].string, "killpv", 6) == 0) {
1710                         i = atoi(command[forward+3].string);
1711                         j = atoi(command[forward+2].string);
1712                         if (i <= max_gamenum && i != 0) {
1713                                 if (icsQueue[i].killPv == 0) {
1714                                         sprintf(temp, "tell %s Error: Mh, killpv is zero. Wrong gamenumber?\n", player);
1715                                         SendToICS(temp);
1716                                 } else {
1717                                         if ( j > 20 || j < 1) {
1718                                                 sprintf(temp, "tell %s Error: killpv must be 1-20\n", player);
1719                                         } else { 
1720                                                 sprintf(temp, "tell %s killpv for game %d is now %d\n", player, i, j);
1721                                                 icsQueue[i].killPv = j;
1722                                                 appData.icsEngineKillPV = TRUE;
1723                                         }
1724                                         SendToICS(temp);
1725                                 }
1726                         } else {
1727                                 sprintf(temp, "tell %s Error: not possible gamenumber\n", player);
1728                                 SendToICS(temp);
1729                         }
1730                         return;
1731                 }
1732                 if (strncmp(command[forward+1].string, "smartqueue", 10) == 0) {
1733                         j = atoi(command[forward+2].string);
1734                         if (j == 0 || j == 1) {
1735                                 appData.smartQueue = j;
1736                                 sprintf(temp, "tell %s smartqueue is now: %d", player, appData.smartQueue);
1737                         } else {
1738                                 sprintf(temp, "tell %s Error: not possible smartquere value\n", player);
1739                         }
1740                         SendToICS(temp);
1741                         return;
1742                 }
1743                 if (strncmp(command[forward+1].string, "show", 4) == 0) {
1744                         i = atoi(command[forward+2].string);
1745                         if (i <=  max_gamenum && i != 0) {
1746                                 if (icsQueue[i].killPv == 0) {
1747                                         sprintf(temp, "tell %s Error: emtpy game slot. Wrong gamenumber?\n", player);
1748                                         SendToICS(temp);
1749                                 } else {
1750                                         sprintf(temp, "tell %s summary information about game: %d\n", player, i);
1751                                         SendToICS(temp);
1752                                         sprintf(temp, "tell %s enable killpv: %d, killpv value = %d\n", player,
1753                                                 appData.icsEngineKillPV, icsQueue[i].killPv);
1754                                         SendToICS(temp);
1755                                         sprintf(temp, "tell %s enable smartqueue (all games): %d\n", player, appData.smartQueue);
1756                                         SendToICS(temp);
1757                                         sprintf(temp, "tell %s analyze output (all games) is %d\n", player,
1758                                                         appData.icsAnalyzeOutPut);
1759                                         SendToICS(temp);
1760                                 }
1761                         } else {
1762                                 sprintf(temp, "tell %s Error: not possible gamenumber\n", player);
1763                                 SendToICS(temp);
1764                         }
1765                 return;
1766                 }
1767         }       
1768 }
1769
1770 void
1771 read_from_ics(isr, closure, data, count, error)
1772      InputSourceRef isr;
1773      VOIDSTAR closure;
1774      char *data;
1775      int count;
1776      int error;
1777 {
1778 #define BUF_SIZE 8192
1779 #define STARTED_NONE 0
1780 #define STARTED_MOVES 1
1781 #define STARTED_BOARD 2
1782 #define STARTED_OBSERVE 3
1783 #define STARTED_HOLDINGS 4
1784 #define STARTED_CHATTER 5
1785 #define STARTED_COMMENT 6
1786 #define STARTED_MOVES_NOHIDE 7
1787     
1788     static int started = STARTED_NONE;
1789     static char parse[20000];
1790     static int parse_pos = 0;
1791     static char buf[BUF_SIZE + 1];
1792     static int firstTime = TRUE, intfSet = FALSE;
1793     static ColorClass curColor = ColorNormal;
1794     static ColorClass prevColor = ColorNormal;
1795     static int savingComment = FALSE;
1796     char str[512];
1797     int i, oldi;
1798     int buf_len;
1799     int next_out;
1800     int tkind;
1801         /* extra zippy vars */
1802         int save;
1803         char *q;
1804         char *p = 0;
1805         char *player;
1806         char reply[MSG_SIZ];
1807
1808     if (count > 0) {
1809         /* If last read ended with a partial line that we couldn't parse,
1810            prepend it to the new read and try again. */
1811         if (leftover_len > 0) {
1812             for (i=0; i<leftover_len; i++)
1813               buf[i] = buf[leftover_start + i];
1814         }
1815
1816         /* Copy in new characters, removing nulls and \r's */
1817         buf_len = leftover_len;
1818         for (i = 0; i < count; i++) {
1819             if (data[i] != NULLCHAR && data[i] != '\r')
1820               buf[buf_len++] = data[i];
1821         }
1822
1823         buf[buf_len] = NULLCHAR;
1824         next_out = leftover_len;
1825         leftover_start = 0;
1826         
1827         i = 0;
1828         while (i < buf_len) {
1829             /* Deal with part of the TELNET option negotiation
1830                protocol.  We refuse to do anything beyond the
1831                defaults, except that we allow the WILL ECHO option,
1832                which ICS uses to turn off password echoing when we are
1833                directly connected to it.  We reject this option
1834                if localLineEditing mode is on (always on in xboard)
1835                and we are talking to port 23, which might be a real
1836                telnet server that will try to keep WILL ECHO on permanently.
1837              */
1838             if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
1839                 static int remoteEchoOption = FALSE; /* telnet ECHO option */
1840                 unsigned char option;
1841                 oldi = i;
1842                 switch ((unsigned char) buf[++i]) {
1843                   case TN_WILL:
1844                     if (appData.debugMode)
1845                       fprintf(debugFP, "\n<WILL ");
1846                     switch (option = (unsigned char) buf[++i]) {
1847                       case TN_ECHO:
1848                         if (appData.debugMode)
1849                           fprintf(debugFP, "ECHO ");
1850                         /* Reply only if this is a change, according
1851                            to the protocol rules. */
1852                         if (remoteEchoOption) break;
1853                         if (appData.localLineEditing &&
1854                             atoi(appData.icsPort) == TN_PORT) {
1855                             TelnetRequest(TN_DONT, TN_ECHO);
1856                         } else {
1857                             EchoOff();
1858                             TelnetRequest(TN_DO, TN_ECHO);
1859                             remoteEchoOption = TRUE;
1860                         }
1861                         break;
1862                       default:
1863                         if (appData.debugMode)
1864                           fprintf(debugFP, "%d ", option);
1865                         /* Whatever this is, we don't want it. */
1866                         TelnetRequest(TN_DONT, option);
1867                         break;
1868                     }
1869                     break;
1870                   case TN_WONT:
1871                     if (appData.debugMode)
1872                       fprintf(debugFP, "\n<WONT ");
1873                     switch (option = (unsigned char) buf[++i]) {
1874                       case TN_ECHO:
1875                         if (appData.debugMode)
1876                           fprintf(debugFP, "ECHO ");
1877                         /* Reply only if this is a change, according
1878                            to the protocol rules. */
1879                         if (!remoteEchoOption) break;
1880                         EchoOn();
1881                         TelnetRequest(TN_DONT, TN_ECHO);
1882                         remoteEchoOption = FALSE;
1883                         break;
1884                       default:
1885                         if (appData.debugMode)
1886                           fprintf(debugFP, "%d ", (unsigned char) option);
1887                         /* Whatever this is, it must already be turned
1888                            off, because we never agree to turn on
1889                            anything non-default, so according to the
1890                            protocol rules, we don't reply. */
1891                         break;
1892                     }
1893                     break;
1894                   case TN_DO:
1895                     if (appData.debugMode)
1896                       fprintf(debugFP, "\n<DO ");
1897                     switch (option = (unsigned char) buf[++i]) {
1898                       default:
1899                         /* Whatever this is, we refuse to do it. */
1900                         if (appData.debugMode)
1901                           fprintf(debugFP, "%d ", option);
1902                         TelnetRequest(TN_WONT, option);
1903                         break;
1904                     }
1905                     break;
1906                   case TN_DONT:
1907                     if (appData.debugMode)
1908                       fprintf(debugFP, "\n<DONT ");
1909                     switch (option = (unsigned char) buf[++i]) {
1910                       default:
1911                         if (appData.debugMode)
1912                           fprintf(debugFP, "%d ", option);
1913                         /* Whatever this is, we are already not doing
1914                            it, because we never agree to do anything
1915                            non-default, so according to the protocol
1916                            rules, we don't reply. */
1917                         break;
1918                     }
1919                     break;
1920                   case TN_IAC:
1921                     if (appData.debugMode)
1922                       fprintf(debugFP, "\n<IAC ");
1923                     /* Doubled IAC; pass it through */
1924                     i--;
1925                     break;
1926                   default:
1927                     if (appData.debugMode)
1928                       fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
1929                     /* Drop all other telnet commands on the floor */
1930                     break;
1931                 }
1932                 if (oldi > next_out)
1933                   SendToPlayer(&buf[next_out], oldi - next_out);
1934                 if (++i > next_out)
1935                   next_out = i;
1936                 continue;
1937             }
1938                 
1939             /* OK, this at least will *usually* work */
1940             if (!loggedOn && looking_at(buf, &i, "ics%")) {
1941                 loggedOn = TRUE;
1942             }
1943             
1944             if (loggedOn && !intfSet) {
1945                 if (ics_type == ICS_ICC) {
1946                         sprintf(str,
1947                           "/set-quietly interface %s\n/set-quietly style 12\n",
1948                           programVersion);
1949
1950                 } else if (ics_type == ICS_CHESSNET) {
1951                   sprintf(str, "/style 12\n");
1952                 } else {
1953                   strcpy(str, "alias $ @\n$set interface ");
1954                   strcat(str, programVersion);
1955                   strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
1956 #ifdef WIN32
1957                   strcat(str, "$iset nohighlight 1\n");
1958 #endif
1959                   strcat(str, "$iset lock 1\n$style 12\n");
1960                 }
1961                 SendToICS(str);
1962                 intfSet = TRUE;
1963             }
1964
1965             if (started == STARTED_COMMENT) {
1966                 /* Accumulate characters in comment */
1967                 parse[parse_pos++] = buf[i];
1968                 if (buf[i] == '\n') {
1969                     parse[parse_pos] = NULLCHAR;
1970                     AppendComment(forwardMostMove, StripHighlight(parse));
1971                     started = STARTED_NONE;
1972                 } else {
1973                     /* Don't match patterns against characters in chatter */
1974                     i++;
1975                     continue;
1976                 }
1977             }
1978             if (started == STARTED_CHATTER) {
1979                 if (buf[i] != '\n') {
1980                     /* Don't match patterns against characters in chatter */
1981                     i++;
1982                     continue;
1983                 }
1984                 started = STARTED_NONE;
1985             }
1986
1987             /* Kludge to deal with rcmd protocol */
1988             if (firstTime && looking_at(buf, &i, "\001*")) {
1989                 DisplayFatalError(&buf[1], 0, 1);
1990                 continue;
1991             } else {
1992                 firstTime = FALSE;
1993             }
1994
1995             if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
1996                 ics_type = ICS_ICC;
1997                 ics_prefix = "/";
1998                 if (appData.debugMode)
1999                   fprintf(debugFP, "ics_type %d\n", ics_type);
2000                 continue;
2001             }
2002             if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
2003                 ics_type = ICS_FICS;
2004                 ics_prefix = "$";
2005                 if (appData.debugMode)
2006                   fprintf(debugFP, "ics_type %d\n", ics_type);
2007                 continue;
2008             }
2009             if (!loggedOn && looking_at(buf, &i, "chess.net")) {
2010                 ics_type = ICS_CHESSNET;
2011                 ics_prefix = "/";
2012                 if (appData.debugMode)
2013                   fprintf(debugFP, "ics_type %d\n", ics_type);
2014                 continue;
2015             }
2016
2017             if (!loggedOn &&
2018                 (looking_at(buf, &i, "\"*\" is *a registered name") ||
2019                  looking_at(buf, &i, "Logging you in as \"*\"") ||
2020                  looking_at(buf, &i, "will be \"*\""))) {
2021               strcpy(ics_handle, star_match[0]);
2022               continue;
2023             }
2024
2025             if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
2026               char buf[MSG_SIZ];
2027               sprintf(buf, "%s@%s", ics_handle, appData.icsHost);
2028               DisplayIcsInteractionTitle(buf);
2029               have_set_title = TRUE;
2030             }
2031
2032             /* skip finger notes */
2033             if (started == STARTED_NONE &&
2034                 ((buf[i] == ' ' && isdigit(buf[i+1])) ||
2035                  (buf[i] == '1' && buf[i+1] == '0')) &&
2036                 buf[i+2] == ':' && buf[i+3] == ' ') {
2037               started = STARTED_CHATTER;
2038               i += 3;
2039               continue;
2040             }
2041
2042             /* skip formula vars */
2043             if (started == STARTED_NONE &&
2044                 buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
2045               started = STARTED_CHATTER;
2046               i += 3;
2047               continue;
2048             }
2049             oldi = i;
2050
2051                 /* save position of char array pointer for zippy */
2052                 save = i;
2053                 /* Try found special things that never works with color */
2054                 /* I really don't know why - code is ok. */
2055                 q = p;
2056
2057                 if (appData.zippyTalk || appData.zippyPlay) {
2058                   if (looking_at(buf, &save, "* tells you: *") ||
2059                         looking_at(buf, &save, "* says: *")) {
2060                         player = StripHighlightAndTitle(star_match[0]);
2061                                         if (appData.zippyPassword[0] != NULLCHAR &&
2062                                             strncmp(star_match[1], appData.zippyPassword,
2063                                             strlen(appData.zippyPassword)) == 0) {
2064                                                         q = star_match[1] + strlen(appData.zippyPassword);
2065                                                         while (*q == ' ') q++;
2066                                                         fprintf(debugFP, "zippy 1 %s \n", q);
2067                                                         SendToICS(q);
2068                                                         SendToICS("\n");
2069                                         } else if(appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
2070                                                       strncmp(star_match[1], appData.zippyPassword2,
2071                                       strlen(appData.zippyPassword2)) == 0) {
2072                                                                 fprintf(debugFP, "zippy2vor: %s \n", q);
2073                                                                 q = star_match[1] + strlen(appData.zippyPassword2);
2074                                                                 while (*q == ' ') q++;
2075                                                                 SendToProgram(q, &first);
2076                                                                 SendToProgram("\n", &first);
2077
2078                                         } else if (appData.zippyPassword3[0] != NULLCHAR && first.initDone &&
2079                                                          strncmp(star_match[1], appData.zippyPassword3, 
2080                                                          strlen(appData.zippyPassword3)) == 0 &&
2081                                                          appData.userVersion == FALSE) {
2082                                                                 q = star_match[1] + strlen(appData.zippyPassword3);
2083                                                                 ParseZippyP3(q, player);
2084
2085                                         } else if (strncmp(star_match[1], "showinfo", 8) == 0 &&
2086                                                 appData.userVersion == FALSE && appData.icsAnalyze == TRUE &&
2087                                                 appData.icsAnalyzeOutPut == 3 && appData.ICC_feature == TRUE) {
2088                                                                 q = star_match[1] + strlen("showinfo");
2089                                                                 showInfo(q, player);
2090                                         } else if (strncmp(star_match[1], "showgames", 9) == 0 &&
2091                                                 appData.userVersion == FALSE && appData.icsAnalyze == TRUE &&
2092                                                 appData.icsAnalyzeOutPut == 3 && appData.ICC_feature == TRUE) {
2093                                                                 q = star_match[1];
2094                                                                 showInfo(q, player);
2095
2096                                         } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
2097                                                          strncmp(star_match[1], appData.zippyWrongPassword,
2098                                                      strlen(appData.zippyWrongPassword)) == 0) {
2099                                                                 q = star_match[1] + strlen(appData.zippyWrongPassword);
2100                                                                  while (*q == ' ') q++;
2101                                                                  sprintf(reply, "wrong %s\n", player);
2102                                                                  SendToICS(reply);
2103                                         }
2104                         }
2105                         /* workaround for icc and freechess.org */ 
2106                         if (looking_at(buf, &save, "Your opponent offers you a draw") ||
2107                                 looking_at(buf, &save, "offers you a draw") ||
2108                                 looking_at(buf, &save, "* offers you a draw")) {
2109                                 if (first.sendDrawOffers && first.initDone) {
2110                                         SendToProgram("draw\n", &first);
2111                                         /* Handling zippy Draw */
2112                                 } else if (appData.zippyDraw && first.initDone) {
2113                                         //ZippyDraw(1, programStats.score, programStats.depth);
2114                                 }
2115                         }
2116                     if (appData.zippyPlay && first.initDone && loggedOn == TRUE) ZippyMatch(buf, &save, player);              
2117                 }
2118         
2119                 /* Make color for all and for zippy */
2120                 if (/* Don't color "message" or "messages" output */
2121                     (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
2122                     looking_at(buf, &i, "*. * at *:*: ") ||
2123                     looking_at(buf, &i, "--* (*:*): ") ||
2124                     /* Regular tells and says */
2125                     (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
2126                     looking_at(buf, &i, "* (your partner) tells you: ") ||
2127                     looking_at(buf, &i, "* says: ") ||
2128                     /* Message notifications (same color as tells) */
2129                     looking_at(buf, &i, "* has left a message ") ||
2130                     looking_at(buf, &i, "* just sent you a message:\n") ||
2131                     /* Whispers and kibitzes */
2132                     (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
2133                     looking_at(buf, &i, "* kibitzes: ") ||
2134                     /* Channel tells */
2135                     (tkind = 3, looking_at(buf, &i, "*(*: "))) {
2136
2137                   if (tkind == 1 && strchr(star_match[0], ':')) {
2138                       /* Avoid "tells you:" spoofs in channels */
2139                      tkind = 3;
2140                   }
2141                   if (star_match[0][0] == NULLCHAR ||
2142                       strchr(star_match[0], ' ') ||
2143                       (tkind == 3 && strchr(star_match[1], ' '))) {
2144                     /* Reject bogus matches */
2145                     i = oldi;
2146                   } else {
2147                     if (appData.colorize) {
2148                       if (oldi > next_out) {
2149                         SendToPlayer(&buf[next_out], oldi - next_out);
2150                         next_out = oldi;
2151                       }
2152                       switch (tkind) {
2153                       case 1:
2154                         Colorize(ColorTell, FALSE);
2155                         curColor = ColorTell;
2156                         break;
2157                       case 2:
2158                         Colorize(ColorKibitz, FALSE);
2159                         curColor = ColorKibitz;
2160                         break;
2161                       case 3:
2162                         p = strrchr(star_match[1], '(');
2163                         if (p == NULL) {
2164                           p = star_match[1];
2165                         } else {
2166                           p++;
2167                         }
2168                         if (atoi(p) == 1) {
2169                           Colorize(ColorChannel1, FALSE);
2170                           curColor = ColorChannel1;
2171                         } else {
2172                           Colorize(ColorChannel, FALSE);
2173                           curColor = ColorChannel;
2174                         }
2175                         break;
2176                       case 5:
2177                         curColor = ColorNormal;
2178                         break;
2179                       }
2180                     }
2181                     if (started == STARTED_NONE && appData.autoComment &&
2182                         (gameMode == IcsObserving ||
2183                          gameMode == IcsPlayingWhite ||
2184                          gameMode == IcsPlayingBlack)) {
2185                       parse_pos = i - oldi;
2186                       memcpy(parse, &buf[oldi], parse_pos);
2187                       parse[parse_pos] = NULLCHAR;
2188                       started = STARTED_COMMENT;
2189                       savingComment = TRUE;
2190                     } else {
2191                       started = STARTED_CHATTER;
2192                       savingComment = FALSE;
2193                     }
2194                     loggedOn = TRUE;
2195                     continue;
2196                   }
2197                 }
2198
2199                 if (looking_at(buf, &i, "* s-shouts: ") ||
2200                     looking_at(buf, &i, "* c-shouts: ")) {
2201                     if (appData.colorize) {
2202                         if (oldi > next_out) {
2203                             SendToPlayer(&buf[next_out], oldi - next_out);
2204                             next_out = oldi;
2205                         }
2206                         Colorize(ColorSShout, FALSE);
2207                         curColor = ColorSShout;
2208                     }
2209                     loggedOn = TRUE;
2210                     started = STARTED_CHATTER;
2211                     continue;
2212                 }
2213
2214                 if (looking_at(buf, &i, "--->")) {
2215                     loggedOn = TRUE;
2216                     continue;
2217                 }
2218
2219                 if (looking_at(buf, &i, "* shouts: ") ||
2220                     looking_at(buf, &i, "--> ")) {
2221                     if (appData.colorize) {
2222                         if (oldi > next_out) {
2223                             SendToPlayer(&buf[next_out], oldi - next_out);
2224                             next_out = oldi;
2225                         }
2226                         Colorize(ColorShout, FALSE);
2227                         curColor = ColorShout;
2228                     }
2229                     loggedOn = TRUE;
2230                     started = STARTED_CHATTER;
2231                     continue;
2232                 }
2233
2234                 if (looking_at( buf, &i, "Challenge:")) {
2235                     if (appData.colorize) {
2236                         if (oldi > next_out) {
2237                             SendToPlayer(&buf[next_out], oldi - next_out);
2238                             next_out = oldi;
2239                         }
2240                         Colorize(ColorChallenge, FALSE);
2241                         curColor = ColorChallenge;
2242                     }
2243                     loggedOn = TRUE;
2244                     continue;
2245                 }
2246
2247                 if (looking_at(buf, &i, "* offers you") ||
2248                     looking_at(buf, &i, "* offers to be") ||
2249                     looking_at(buf, &i, "* would like to") ||
2250                     looking_at(buf, &i, "* requests to") ||
2251                     looking_at(buf, &i, "Your opponent offers") ||
2252                     looking_at(buf, &i, "Your opponent requests")) {
2253
2254                     if (appData.colorize) {
2255                         if (oldi > next_out) {
2256                             SendToPlayer(&buf[next_out], oldi - next_out);
2257                             next_out = oldi;
2258                         }
2259                         Colorize(ColorRequest, FALSE);
2260                         curColor = ColorRequest;
2261                     }
2262                     continue;
2263                 }
2264
2265                 if (looking_at(buf, &i, "* (*) seeking")) {
2266                     if (appData.colorize) {
2267                         if (oldi > next_out) {
2268                             SendToPlayer(&buf[next_out], oldi - next_out);
2269                             next_out = oldi;
2270                         }
2271                         Colorize(ColorSeek, FALSE);
2272                         curColor = ColorSeek;
2273                     }
2274                     continue;
2275                 }
2276            
2277
2278             if (looking_at(buf, &i, "\\   ")) {
2279                 if (prevColor != ColorNormal) {
2280                     if (oldi > next_out) {
2281                         SendToPlayer(&buf[next_out], oldi - next_out);
2282                         next_out = oldi;
2283                     }
2284                     Colorize(prevColor, TRUE);
2285                     curColor = prevColor;
2286                 }
2287                 if (savingComment) {
2288                     parse_pos = i - oldi;
2289                     memcpy(parse, &buf[oldi], parse_pos);
2290                     parse[parse_pos] = NULLCHAR;
2291                     started = STARTED_COMMENT;
2292                 } else {
2293                     started = STARTED_CHATTER;
2294                 }
2295                 continue;
2296             }
2297
2298             if (looking_at(buf, &i, "Black Strength :") ||
2299                 looking_at(buf, &i, "<<< style 10 board >>>") ||
2300                 looking_at(buf, &i, "<10>") ||
2301                 looking_at(buf, &i, "#@#")) {
2302                 /* Wrong board style */
2303                 loggedOn = TRUE;
2304                 SendToICS(ics_prefix);
2305                 SendToICS("set style 12\n");
2306                 SendToICS(ics_prefix);
2307                 SendToICS("refresh\n");
2308                 continue;
2309             }
2310             
2311             if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
2312                 ICSInitScript();
2313                 have_sent_ICS_logon = 1;
2314                 continue;
2315             }
2316               
2317             if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ && 
2318                 (looking_at(buf, &i, "\n<12> ") ||
2319                  looking_at(buf, &i, "<12> "))) {
2320                 loggedOn = TRUE;
2321                 if (oldi > next_out) {
2322                     SendToPlayer(&buf[next_out], oldi - next_out);
2323                 }
2324                 next_out = i;
2325                 started = STARTED_BOARD;
2326                 parse_pos = 0;
2327                 continue;
2328             }
2329
2330             if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
2331                 looking_at(buf, &i, "<b1> ")) {
2332                 if (oldi > next_out) {
2333                     SendToPlayer(&buf[next_out], oldi - next_out);
2334                 }
2335                 next_out = i;
2336                 started = STARTED_HOLDINGS;
2337                 parse_pos = 0;
2338                 continue;
2339             }
2340
2341                                 /* Send buf now to zippy */
2342             if (appData.zippyTalk || appData.zippyPlay) {
2343 #if ZIPPY
2344                 if (ZippyControl(buf, &save) || ZippyConverse(buf, &save)) {
2345                     loggedOn = TRUE;
2346                     continue;
2347                 }
2348 #endif 
2349                 }
2350
2351                 /* ICS: init icsQueue */
2352                 if (appData.zippyPlay && first.initDone && gameMode == IcsObserving) {
2353                         if (ics_gamenum > max_gamenum || ics_gamenum == -1) {
2354                                 if (appData.debugMode) fprintf(debugFP, "To high gamenumber or gamenumber -1 !\n");
2355                                 return;
2356                         }
2357                         if (icsQueue[ics_gamenum].killPv == 0) {
2358                                 icsQueue[ics_gamenum].move = currentMove;
2359                                 icsQueue[ics_gamenum].killPv = appData.icsKillPVs;
2360                                 icsQueue[ics_gamenum].counter = 0;
2361                                 strcpy(icsQueue[ics_gamenum].white, gameInfo.white);
2362                                 strcpy(icsQueue[ics_gamenum].black, gameInfo.black);
2363                         }
2364                 }
2365                                 
2366             if (looking_at(buf, &i, "* *vs. * *--- *")) {
2367                 loggedOn = TRUE;
2368                 /* Header for a move list -- first line */
2369                 switch (ics_getting_history) {
2370                   case H_FALSE:
2371                     switch (gameMode) {
2372                       case IcsIdle:
2373                       case BeginningOfGame:
2374                         /* User typed "moves" or "oldmoves" while we
2375                            were idle.  Pretend we asked for these
2376                            moves and soak them up so user can step
2377                            through them and/or save them.
2378                            */
2379                         Reset(FALSE, TRUE);
2380                         gameMode = IcsObserving;
2381                         ModeHighlight();
2382                         ics_gamenum = -1;
2383                         ics_getting_history = H_GOT_UNREQ_HEADER;
2384                         break;
2385                       case EditGame: /*?*/
2386                       case EditPosition: /*?*/
2387                         /* Should above feature work in these modes too? */
2388                         /* For now it doesn't */
2389                         ics_getting_history = H_GOT_UNWANTED_HEADER;
2390                         break;
2391                       default:
2392                         ics_getting_history = H_GOT_UNWANTED_HEADER;
2393                         break;
2394                     }
2395                     break;
2396                   case H_REQUESTED:
2397                     /* Is this the right one? */
2398                     if (gameInfo.white && gameInfo.black &&
2399                         strcmp(gameInfo.white, star_match[0]) == 0 &&
2400                         strcmp(gameInfo.black, star_match[2]) == 0) {
2401                         /* All is well */
2402                         ics_getting_history = H_GOT_REQ_HEADER;
2403                     }
2404                     break;
2405                   case H_GOT_REQ_HEADER:
2406                   case H_GOT_UNREQ_HEADER:
2407                   case H_GOT_UNWANTED_HEADER:
2408                   case H_GETTING_MOVES:
2409                     /* Should not happen */
2410                     DisplayError("Error gathering move list: two headers", 0);
2411                     ics_getting_history = H_FALSE;
2412                     break;
2413                 }
2414
2415                 /* Save player ratings into gameInfo if needed */
2416                 if ((ics_getting_history == H_GOT_REQ_HEADER ||
2417                      ics_getting_history == H_GOT_UNREQ_HEADER) &&
2418                     (gameInfo.whiteRating == -1 ||
2419                      gameInfo.blackRating == -1)) {
2420
2421                     gameInfo.whiteRating = string_to_rating(star_match[1]);
2422                     gameInfo.blackRating = string_to_rating(star_match[3]);
2423                     if (appData.debugMode)
2424                       fprintf(debugFP, "Ratings from header: W %d, B %d\n", 
2425                               gameInfo.whiteRating, gameInfo.blackRating);
2426                 }
2427                 continue;
2428             }
2429
2430             if (looking_at(buf, &i,
2431               "* * match, initial time: * minute*, increment: * second")) {
2432                 /* Header for a move list -- second line */
2433                 /* Initial board will follow if this is a wild game */
2434
2435                 if (gameInfo.event != NULL) free(gameInfo.event);
2436                 sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
2437                 gameInfo.event = StrSave(str);
2438                 gameInfo.variant = StringToVariant(gameInfo.event);
2439                 continue;
2440             }
2441
2442             if (looking_at(buf, &i, "Move  ")) {
2443                 /* Beginning of a move list */
2444                 switch (ics_getting_history) {
2445                   case H_FALSE:
2446                     /* Normally should not happen */
2447                     /* Maybe user hit reset while we were parsing */
2448                     break;
2449                   case H_REQUESTED:
2450                     /* Happens if we are ignoring a move list that is not
2451                      * the one we just requested.  Common if the user
2452                      * tries to observe two games without turning off
2453                      * getMoveList */
2454                     break;
2455                   case H_GETTING_MOVES:
2456                     /* Should not happen */
2457                     DisplayError("Error gathering move list: nested", 0);
2458                     ics_getting_history = H_FALSE;
2459                     break;
2460                   case H_GOT_REQ_HEADER:
2461                     ics_getting_history = H_GETTING_MOVES;
2462                     started = STARTED_MOVES;
2463                     parse_pos = 0;
2464                     if (oldi > next_out) {
2465                         SendToPlayer(&buf[next_out], oldi - next_out);
2466                     }
2467                     break;
2468                   case H_GOT_UNREQ_HEADER:
2469                     ics_getting_history = H_GETTING_MOVES;
2470                     started = STARTED_MOVES_NOHIDE;
2471                     parse_pos = 0;
2472                     break;
2473                   case H_GOT_UNWANTED_HEADER:
2474                     ics_getting_history = H_FALSE;
2475                     break;
2476                 }
2477                 continue;
2478             }                           
2479             
2480             if (looking_at(buf, &i, "% ") ||
2481                 ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
2482                  && looking_at(buf, &i, "}*"))) {
2483                 savingComment = FALSE;
2484                 switch (started) {
2485                   case STARTED_MOVES:
2486                   case STARTED_MOVES_NOHIDE:
2487                     memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
2488                     parse[parse_pos + i - oldi] = NULLCHAR;
2489                     ParseGameHistory(parse);
2490 #if ZIPPY
2491                     if (appData.zippyPlay && first.initDone) {
2492                                 if (appData.icsAnalyze && gameMode == IcsObserving) {
2493                                         IcsAnalyze(TRUE);
2494                                 }
2495                                 /* icsAnalyze send the moves to engine if we start new */
2496                                 if (!appData.icsAnalyze) FeedMovesToProgram(&first, forwardMostMove);
2497                                 /* Bug 4.2.6: Engine want play, skip that */
2498                                 if (gameMode == IcsExamining) {
2499                                         /* set idle mode */
2500                                         SendToProgram("force\n", &first);
2501                                 }
2502                         if (gameMode == IcsPlayingWhite) {
2503                                 if (appData.icsAnalyze) {
2504                                         IcsAnalyze(FALSE);
2505                                         appData.icsAnalyze = FALSE;
2506                                         
2507                                 }
2508                             if (WhiteOnMove(forwardMostMove)) {
2509                                 if (first.sendTime) {
2510                                   if (first.useColors) {
2511                                     SendToProgram("black\n", &first); 
2512                                   }
2513                                   SendTimeRemaining(&first, TRUE);
2514                                 }
2515                                 if (first.useColors) {
2516                                   SendToProgram("white\ngo\n", &first);
2517                                 } else {
2518                                   SendToProgram("go\n", &first);
2519                                 }
2520                                 first.maybeThinking = TRUE;
2521                             } else {
2522                                 if (first.usePlayother) {
2523                                   if (first.sendTime) {
2524                                     SendTimeRemaining(&first, TRUE);
2525                                   }
2526                                   SendToProgram("playother\n", &first);
2527                                   firstMove = FALSE;
2528                                 } else {
2529                                   firstMove = TRUE;
2530                                 }
2531                             }
2532                         } else if (gameMode == IcsPlayingBlack) {
2533                                 if (appData.icsAnalyze) {
2534                                         IcsAnalyze(FALSE);
2535                                         appData.icsAnalyze = FALSE;
2536                                         SendToICS("refresh\n");
2537                                 }
2538                                 /* engineRoom stay forever */
2539                             if (!WhiteOnMove(forwardMostMove)) {
2540                                 if (first.sendTime) {
2541                                   if (first.useColors) {
2542                                     SendToProgram("white\n", &first);
2543                                   }
2544                                   SendTimeRemaining(&first, FALSE);
2545                                 }
2546                                 if (first.useColors) {
2547                                   SendToProgram("black\ngo\n", &first);
2548                                 } else {
2549                                   SendToProgram("go\n", &first);
2550                                 }
2551                                 first.maybeThinking = TRUE;
2552                             } else {
2553                                 if (first.usePlayother) {
2554                                   if (first.sendTime) {
2555                                     SendTimeRemaining(&first, FALSE);
2556                                   }
2557                                   SendToProgram("playother\n", &first);
2558                                   firstMove = FALSE;
2559                                 } else {
2560                                   firstMove = TRUE;
2561                                 }
2562                             }
2563                         } 
2564                         
2565 #endif ZIPPY
2566                             
2567                  }
2568                     if (gameMode == IcsObserving && ics_gamenum == -1) {
2569                         /* Moves came from oldmoves or moves command
2570                            while we weren't doing anything else.
2571                            */
2572                         currentMove = forwardMostMove;
2573                         ClearHighlights();/*!!could figure this out*/
2574                         flipView = appData.flipView;
2575                         DrawPosition(FALSE, boards[currentMove]);
2576                         DisplayBothClocks();
2577                         sprintf(str, "%s vs. %s",
2578                                 gameInfo.white, gameInfo.black);
2579                         DisplayTitle(str);
2580                         gameMode = IcsIdle;
2581                     } else {
2582                         /* Moves were history of an active game */
2583                         if (gameInfo.resultDetails != NULL) {
2584                             free(gameInfo.resultDetails);
2585                             gameInfo.resultDetails = NULL;
2586                         }
2587                     }
2588                     HistorySet(parseList, backwardMostMove,
2589                                forwardMostMove, currentMove-1);
2590                     DisplayMove(currentMove - 1);
2591                     if (started == STARTED_MOVES) next_out = i;
2592                     started = STARTED_NONE;
2593                     ics_getting_history = H_FALSE;
2594                     break;
2595
2596                   case STARTED_OBSERVE:
2597                     started = STARTED_NONE;
2598                     SendToICS(ics_prefix);
2599                     SendToICS("refresh\n");
2600                     break;
2601
2602                   default:
2603                     break;
2604                 }
2605                 continue;
2606             }
2607             
2608             if ((started == STARTED_MOVES || started == STARTED_BOARD ||
2609                  started == STARTED_HOLDINGS ||
2610                  started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
2611                 /* Accumulate characters in move list or board */
2612                 parse[parse_pos++] = buf[i];
2613             }
2614             
2615             /* Start of game messages.  Mostly we detect start of game
2616                when the first board image arrives.  On some versions
2617                of the ICS, though, we need to do a "refresh" after starting
2618                to observe in order to get the current board right away. */
2619             if (looking_at(buf, &i, "Adding game * to observation list")) {
2620                 started = STARTED_OBSERVE;
2621                 continue;
2622             }
2623
2624             /* Handle auto-observe */
2625             if (appData.autoObserve &&
2626                 (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
2627                 looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
2628                 char *player;
2629                 /* Choose the player that was highlighted, if any. */
2630                 if (star_match[0][0] == '\033' ||
2631                     star_match[1][0] != '\033') {
2632                     player = star_match[0];
2633                 } else {
2634                     player = star_match[2];
2635                 }
2636                 sprintf(str, "%sobserve %s\n",
2637                         ics_prefix, StripHighlightAndTitle(player));
2638                 SendToICS(str);
2639
2640                 /* Save ratings from notify string */
2641                 strcpy(player1Name, star_match[0]);
2642                 player1Rating = string_to_rating(star_match[1]);
2643                 strcpy(player2Name, star_match[2]);
2644                 player2Rating = string_to_rating(star_match[3]);
2645
2646                 if (appData.debugMode)
2647                   fprintf(debugFP, 
2648                           "Ratings from 'Game notification:' %s %d, %s %d\n",
2649                           player1Name, player1Rating,
2650                           player2Name, player2Rating);
2651
2652                 continue;
2653             }
2654
2655             /* Deal with automatic examine mode after a game,
2656                and with IcsObserving -> IcsExamining transition */
2657             if (looking_at(buf, &i, "Entering examine mode for game *") ||
2658                 looking_at(buf, &i, "has made you an examiner of game *")) {
2659
2660                 int gamenum = atoi(star_match[0]);
2661                 if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
2662                     gamenum == ics_gamenum) {
2663                     /* We were already playing or observing this game;
2664                        no need to refetch history */
2665                     gameMode = IcsExamining;
2666                     if (pausing) {
2667                         pauseExamForwardMostMove = forwardMostMove;
2668                     } else if (currentMove < forwardMostMove) {
2669                         ForwardInner(forwardMostMove);
2670                     }
2671                 } else {
2672                     /* I don't think this case really can happen */
2673                     SendToICS(ics_prefix);
2674                     SendToICS("refresh\n");
2675                 }
2676                 continue;
2677             }    
2678             
2679             /* Error messages */
2680             if (ics_user_moved) {
2681                 if (looking_at(buf, &i, "Illegal move") ||
2682                     looking_at(buf, &i, "Not a legal move") ||
2683                     looking_at(buf, &i, "Your king is in check") ||
2684                     looking_at(buf, &i, "It isn't your turn") ||
2685                     looking_at(buf, &i, "It is not your move")) {
2686                     /* Illegal move */
2687                     ics_user_moved = 0;
2688                     if (forwardMostMove > backwardMostMove) {
2689                         currentMove = --forwardMostMove;
2690                         DisplayMove(currentMove - 1); /* before DMError */
2691                         DisplayMoveError("Illegal move (rejected by ICS)");
2692                         DrawPosition(FALSE, boards[currentMove]);
2693                         SwitchClocks();
2694                         DisplayBothClocks();
2695                     }
2696                     continue;
2697                 }
2698             }
2699
2700             if (looking_at(buf, &i, "still have time") ||
2701                 looking_at(buf, &i, "not out of time") ||
2702                 looking_at(buf, &i, "either player is out of time") ||
2703                 looking_at(buf, &i, "has timeseal; checking")) {
2704                 /* We must have called his flag a little too soon */
2705                 whiteFlag = blackFlag = FALSE;
2706                 continue;
2707             }
2708
2709             if (looking_at(buf, &i, "added * seconds to") ||
2710                 looking_at(buf, &i, "seconds were added to")) {
2711                 /* Update the clocks */
2712                 SendToICS(ics_prefix);
2713                 SendToICS("refresh\n");
2714                 continue;
2715             }
2716
2717             if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
2718                 ics_clock_paused = TRUE;
2719                 StopClocks();
2720                 continue;
2721             }
2722
2723             if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
2724                 ics_clock_paused = FALSE;
2725                 StartClocks();
2726                 continue;
2727             }
2728
2729             /* Grab player ratings from the Creating: message.
2730                Note we have to check for the special case when
2731                the ICS inserts things like [white] or [black]. */
2732             if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
2733                 looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
2734                 /* star_matches:
2735                    0    player 1 name (not necessarily white)
2736                    1    player 1 rating
2737                    2    empty, white, or black (IGNORED)
2738                    3    player 2 name (not necessarily black)
2739                    4    player 2 rating
2740                    
2741                    The names/ratings are sorted out when the game
2742                    actually starts (below).
2743                 */
2744                 strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
2745                 player1Rating = string_to_rating(star_match[1]);
2746                 strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
2747                 player2Rating = string_to_rating(star_match[4]);
2748
2749                 if (appData.debugMode)
2750                   fprintf(debugFP, 
2751                           "Ratings from 'Creating:' %s %d, %s %d\n",
2752                           player1Name, player1Rating,
2753                           player2Name, player2Rating);
2754
2755                 continue;
2756             }
2757             
2758             /* Improved generic start/end-of-game messages */
2759             if (looking_at(buf, &i, "{Game * (* vs. *) *}*")) {
2760                 /* star_match[0] is the game number */
2761                 /*           [1] is the white player's name */
2762                 /*           [2] is the black player's name */
2763                 /* For end-of-game: */
2764                 /*           [3] is the reason for the game end */
2765                 /*           [4] is a PGN end game-token, preceded by " " */
2766                 /* For start-of-game: */
2767                 /*           [3] begins with "Creating" or "Continuing" */
2768                 /*           [4] is " *" or empty (don't care). */
2769                 int gamenum = atoi(star_match[0]);
2770                 char *why = star_match[3];
2771                 char *endtoken = star_match[4];
2772                 ChessMove endtype = (ChessMove) 0;
2773
2774                 /* Game start messages */
2775                 if (strncmp(why, "Creating ", 9) == 0 ||
2776                     strncmp(why, "Continuing ", 11) == 0) {
2777                     gs_gamenum = gamenum;
2778                     strcpy(gs_kind, strchr(why, ' ') + 1);
2779 #if ZIPPY
2780                     if (appData.zippyPlay) {
2781                         ZippyGameStart(star_match[1], star_match[2]);
2782                     }
2783 #endif /*ZIPPY*/
2784                     continue;
2785                 }
2786
2787                 /* Game end messages */
2788                 if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
2789                     ics_gamenum != gamenum) {
2790                     continue;
2791                 }
2792                 while (endtoken[0] == ' ') endtoken++;
2793                 switch (endtoken[0]) {
2794                   case '*':
2795                   default:
2796                     endtype = GameUnfinished;
2797                     break;
2798                   case '0':
2799                     endtype = BlackWins;
2800                     break;
2801                   case '1':
2802                     if (endtoken[1] == '/')
2803                       endtype = GameIsDrawn;
2804                     else
2805                       endtype = WhiteWins;
2806                     break;
2807                 }
2808                 GameEnds(endtype, why, GE_ICS);
2809 #if ZIPPY
2810                 if (appData.zippyPlay && first.initDone) {
2811                     ZippyGameEnd(endtype, why);
2812                     if (first.pr == NULL) {
2813                       /* Start the next process early so that we'll
2814                          be ready for the next challenge */
2815                       StartChessProgram(&first);
2816                     }
2817                     /* Send "new" early, in case this command takes
2818                        a long time to finish, so that we'll be ready
2819                        for the next challenge. */
2820                     Reset(TRUE, TRUE);
2821                 }
2822 #endif /*ZIPPY*/
2823                 continue;
2824             }
2825
2826             if (looking_at(buf, &i, "Removing game * from observation") ||
2827                 looking_at(buf, &i, "no longer observing game *") ||
2828                 looking_at(buf, &i, "Game * (*) has no examiners")) {
2829                 if (gameMode == IcsObserving &&
2830                     atoi(star_match[0]) == ics_gamenum)
2831                   {
2832                         if (appData.icsAnalyze) IcsAnalyze(FALSE);
2833                           ResetIcsQueue(ics_gamenum);
2834                       StopClocks();
2835                       gameMode = IcsIdle;
2836                       ics_gamenum = -1;
2837                       ics_user_moved = FALSE;
2838                   }
2839                 continue;
2840             }
2841
2842             if (looking_at(buf, &i, "no longer examining game *")) {
2843                 if (gameMode == IcsExamining &&
2844                     atoi(star_match[0]) == ics_gamenum)
2845                   {             
2846                           if (appData.icsAnalyze) IcsAnalyze(FALSE);
2847                           ResetIcsQueue(ics_gamenum);
2848                       gameMode = IcsIdle;
2849                       ics_gamenum = -1;
2850                       ics_user_moved = FALSE;
2851                   }
2852                 continue;
2853             }
2854
2855             /* Advance leftover_start past any newlines we find,
2856                so only partial lines can get reparsed */
2857             if (looking_at(buf, &i, "\n")) {
2858                 prevColor = curColor;
2859                 if (curColor != ColorNormal) {
2860                     if (oldi > next_out) {
2861                         SendToPlayer(&buf[next_out], oldi - next_out);
2862                         next_out = oldi;
2863                     }
2864                     Colorize(ColorNormal, FALSE);
2865                     curColor = ColorNormal;
2866                 }
2867                 if (started == STARTED_BOARD) {
2868                     started = STARTED_NONE;
2869                     parse[parse_pos] = NULLCHAR;
2870                     ParseBoard12(parse);
2871                     ics_user_moved = 0;
2872
2873                     /* Send premove here */
2874                     if (appData.premove) {
2875                       char str[MSG_SIZ];
2876                       if (currentMove == 0 &&
2877                           gameMode == IcsPlayingWhite &&
2878                           appData.premoveWhite) {
2879                         sprintf(str, "%s%s\n", ics_prefix,
2880                                 appData.premoveWhiteText);
2881                         if (appData.debugMode)
2882                           fprintf(debugFP, "Sending premove:\n");
2883                         SendToICS(str);
2884                       } else if (currentMove == 1 &&
2885                                  gameMode == IcsPlayingBlack &&
2886                                  appData.premoveBlack) {
2887                         sprintf(str, "%s%s\n", ics_prefix,
2888                                 appData.premoveBlackText);
2889                         if (appData.debugMode)
2890                           fprintf(debugFP, "Sending premove:\n");
2891                         SendToICS(str);
2892                       } else if (gotPremove) {
2893                         gotPremove = 0;
2894                         ClearPremoveHighlights();
2895                         if (appData.debugMode)
2896                           fprintf(debugFP, "Sending premove:\n");
2897                           UserMoveEvent(premoveFromX, premoveFromY, 
2898                                         premoveToX, premoveToY, 
2899                                         premovePromoChar);
2900                       }
2901                     }
2902
2903                     /* Usually suppress following prompt */
2904                     if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
2905                         if (looking_at(buf, &i, "*% ")) {
2906                             savingComment = FALSE;
2907                         }
2908                     }
2909                     next_out = i;
2910                 } else if (started == STARTED_HOLDINGS) {
2911                     int gamenum;
2912                     char new_piece[MSG_SIZ];
2913                     started = STARTED_NONE;
2914                     parse[parse_pos] = NULLCHAR;
2915                     if (appData.debugMode)
2916                       fprintf(debugFP, "Parsing holdings: %s\n", parse);
2917                     if (sscanf(parse, " game %d", &gamenum) == 1 &&
2918                         gamenum == ics_gamenum) {
2919                         if (gameInfo.variant == VariantNormal) {
2920                           gameInfo.variant = VariantCrazyhouse; /*temp guess*/
2921                           /* Get a move list just to see the header, which
2922                              will tell us whether this is really bug or zh */
2923                           if (ics_getting_history == H_FALSE) {
2924                             ics_getting_history = H_REQUESTED;
2925                             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
2926                             SendToICS(str);
2927                           }
2928                         }
2929                         new_piece[0] = NULLCHAR;
2930                         sscanf(parse, "game %d white [%s black [%s <- %s",
2931                                &gamenum, white_holding, black_holding,
2932                                new_piece);
2933                         white_holding[strlen(white_holding)-1] = NULLCHAR;
2934                         black_holding[strlen(black_holding)-1] = NULLCHAR;
2935 #if ZIPPY
2936                         if (appData.zippyPlay && first.initDone) {
2937                             ZippyHoldings(white_holding, black_holding,
2938                                           new_piece);
2939                         }
2940 #endif /*ZIPPY*/
2941                         if (tinyLayout || smallLayout) {
2942                             char wh[16], bh[16];
2943                             PackHolding(wh, white_holding);
2944                             PackHolding(bh, black_holding);
2945                             sprintf(str, "[%s-%s] %s-%s", wh, bh,
2946                                     gameInfo.white, gameInfo.black);
2947                         } else {
2948                             sprintf(str, "%s [%s] vs. %s [%s]",
2949                                     gameInfo.white, white_holding,
2950                                     gameInfo.black, black_holding);
2951                         }
2952                         DrawPosition(FALSE, NULL);
2953                         DisplayTitle(str);
2954                     }
2955                     /* Suppress following prompt */
2956                     if (looking_at(buf, &i, "*% ")) {
2957                         savingComment = FALSE;
2958                     }
2959                     next_out = i;
2960                 }
2961                 continue;
2962             }
2963
2964             i++;                /* skip unparsed character and loop back */
2965         }
2966         
2967         if (started != STARTED_MOVES && started != STARTED_BOARD &&
2968             started != STARTED_HOLDINGS && i > next_out) {
2969             SendToPlayer(&buf[next_out], i - next_out);
2970             next_out = i;
2971         }
2972         
2973         leftover_len = buf_len - leftover_start;
2974         /* if buffer ends with something we couldn't parse,
2975            reparse it after appending the next read */
2976         
2977     } else if (count == 0) {
2978         RemoveInputSource(isr);
2979         DisplayFatalError("Connection closed by ICS", 0, 0);
2980     } else {
2981         DisplayFatalError("Error reading from ICS", error, 1);
2982     }
2983 }
2984
2985
2986 /* Board style 12 looks like this:
2987    
2988    <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
2989    
2990  * The "<12> " is stripped before it gets to this routine.  The two
2991  * trailing 0's (flip state and clock ticking) are later addition, and
2992  * some chess servers may not have them, or may have only the first.
2993  * Additional trailing fields may be added in the future.  
2994  */
2995
2996 #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"
2997
2998 #define RELATION_OBSERVING_PLAYED    0
2999 #define RELATION_OBSERVING_STATIC   -2   /* examined, oldmoves, or smoves */
3000 #define RELATION_PLAYING_MYMOVE      1
3001 #define RELATION_PLAYING_NOTMYMOVE  -1
3002 #define RELATION_EXAMINING           2
3003 #define RELATION_ISOLATED_BOARD     -3
3004 #define RELATION_STARTING_POSITION  -4   /* FICS only */
3005
3006 void
3007 ParseBoard12(string)
3008      char *string;
3009
3010     GameMode newGameMode;
3011     int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0;
3012     int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;
3013     int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
3014         int takeback, i;
3015     char to_play, board_chars[72];
3016     char move_str[500], str[500], elapsed_time[500];
3017     char black[32], white[32];
3018     Board board;
3019     int prevMove = currentMove;
3020     int ticking = 2;
3021     ChessMove moveType;
3022     int fromX, fromY, toX, toY;
3023     char promoChar;
3024
3025     fromX = fromY = toX = toY = -1;
3026     
3027     newGame = FALSE;
3028
3029     if (appData.debugMode)
3030       fprintf(debugFP, "Parsing board: %s\n", string);
3031
3032     move_str[0] = NULLCHAR;
3033     elapsed_time[0] = NULLCHAR;
3034     n = sscanf(string, PATTERN, board_chars, &to_play, &double_push,
3035                &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,
3036                &gamenum, white, black, &relation, &basetime, &increment,
3037                &white_stren, &black_stren, &white_time, &black_time,
3038                &moveNum, str, elapsed_time, move_str, &ics_flip,
3039                &ticking);
3040
3041     if (n < 22) {
3042         sprintf(str, "Failed to parse board string:\n\"%s\"", string);
3043         DisplayError(str, 0);
3044         return;
3045     }
3046
3047     /* Convert the move number to internal form */
3048     moveNum = (moveNum - 1) * 2;
3049     if (to_play == 'B') moveNum++;
3050     if (moveNum >= MAX_MOVES) {
3051       DisplayFatalError("Game too long; increase MAX_MOVES and recompile",
3052                         0, 1);
3053       return;
3054     }
3055   
3056     switch (relation) {
3057       case RELATION_OBSERVING_PLAYED:
3058       case RELATION_OBSERVING_STATIC:
3059         if (gamenum == -1) {
3060             /* Old ICC buglet */
3061             relation = RELATION_OBSERVING_STATIC;
3062         }
3063         newGameMode = IcsObserving;
3064         break;
3065       case RELATION_PLAYING_MYMOVE:
3066       case RELATION_PLAYING_NOTMYMOVE:
3067         newGameMode =
3068           ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
3069             IcsPlayingWhite : IcsPlayingBlack;
3070         break;
3071       case RELATION_EXAMINING:
3072         newGameMode = IcsExamining;
3073         break;
3074       case RELATION_ISOLATED_BOARD:
3075       default:
3076         /* Just display this board.  If user was doing something else,
3077            we will forget about it until the next board comes. */ 
3078         newGameMode = IcsIdle;
3079         break;
3080       case RELATION_STARTING_POSITION:
3081         newGameMode = gameMode;
3082         /* if we switch to a new board start IcsAnalyze */
3083         if(appData.icsAnalyze) IcsAnalyze(TRUE);
3084         break;
3085     }
3086     
3087     /* Modify behavior for initial board display on move listing
3088        of wild games.
3089        */
3090     switch (ics_getting_history) {
3091       case H_FALSE:
3092       case H_REQUESTED:
3093         break;
3094       case H_GOT_REQ_HEADER:
3095       case H_GOT_UNREQ_HEADER:
3096         /* This is the initial position of the current game */
3097         gamenum = ics_gamenum;
3098         moveNum = 0;            /* old ICS bug workaround */
3099         if (to_play == 'B') {
3100           startedFromSetupPosition = TRUE;
3101           blackPlaysFirst = TRUE;
3102           moveNum = 1;
3103           if (forwardMostMove == 0) forwardMostMove = 1;
3104           if (backwardMostMove == 0) backwardMostMove = 1;
3105           if (currentMove == 0) currentMove = 1;
3106         }
3107         newGameMode = gameMode;
3108         relation = RELATION_STARTING_POSITION; /* ICC needs this */
3109         break;
3110       case H_GOT_UNWANTED_HEADER:
3111         /* This is an initial board that we don't want */
3112         return;
3113       case H_GETTING_MOVES:
3114         /* Should not happen */
3115         DisplayError("Error gathering move list: extra board", 0);
3116         ics_getting_history = H_FALSE;
3117         return;
3118     }
3119     
3120     /* Take action if this is the first board of a new game, or of a
3121        different game than is currently being displayed.  */
3122     if (gamenum != ics_gamenum || newGameMode != gameMode ||
3123         relation == RELATION_ISOLATED_BOARD) {
3124         
3125         /* Forget the old game and get the history (if any) of the new one */
3126         if (gameMode != BeginningOfGame) {
3127           Reset(FALSE, TRUE);
3128         }
3129         newGame = TRUE;
3130         if (appData.autoRaiseBoard) BoardToTop();
3131         prevMove = -3;
3132         if (gamenum == -1) {
3133             newGameMode = IcsIdle;
3134         } else if (moveNum > 0 && newGameMode != IcsIdle &&
3135                    appData.getMoveList) {
3136             /* Need to get game history */
3137             ics_getting_history = H_REQUESTED;
3138             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3139             SendToICS(str);
3140         }
3141         
3142         
3143         /* Initially flip the board to have black on the bottom if playing
3144            black or if the ICS flip flag is set, but let the user change
3145            it with the Flip View button. */
3146         flipView = appData.autoFlipView ? 
3147           (newGameMode == IcsPlayingBlack) || ics_flip :
3148           appData.flipView;
3149         
3150         /* Done with values from previous mode; copy in new ones */
3151         gameMode = newGameMode;
3152         ModeHighlight();
3153         ics_gamenum = gamenum;
3154         if (gamenum == gs_gamenum) {
3155             int klen = strlen(gs_kind);
3156             if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
3157             sprintf(str, "ICS %s", gs_kind);
3158             gameInfo.event = StrSave(str);
3159         } else {
3160             gameInfo.event = StrSave("ICS game");
3161         }
3162         gameInfo.site = StrSave(appData.icsHost);
3163         gameInfo.date = PGNDate();
3164         gameInfo.round = StrSave("-");
3165         gameInfo.white = StrSave(white);
3166         gameInfo.black = StrSave(black);
3167         timeControl = basetime * 60 * 1000;
3168         timeIncrement = increment * 1000;
3169         movesPerSession = 0;
3170         gameInfo.timeControl = TimeControlTagValue();
3171         gameInfo.variant = StringToVariant(gameInfo.event);
3172         
3173         /* Do we have the ratings? */
3174         if (strcmp(player1Name, white) == 0 &&
3175             strcmp(player2Name, black) == 0) {
3176             if (appData.debugMode)
3177               fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
3178                       player1Rating, player2Rating);
3179             gameInfo.whiteRating = player1Rating;
3180             gameInfo.blackRating = player2Rating;
3181         } else if (strcmp(player2Name, white) == 0 &&
3182                    strcmp(player1Name, black) == 0) {
3183             if (appData.debugMode)
3184               fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
3185                       player2Rating, player1Rating);
3186             gameInfo.whiteRating = player2Rating;
3187             gameInfo.blackRating = player1Rating;
3188         }
3189         player1Name[0] = player2Name[0] = NULLCHAR;
3190
3191         /* Silence shouts if requested */
3192         if (appData.quietPlay &&
3193             (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)) {
3194             SendToICS(ics_prefix);
3195             SendToICS("set shout 0\n");
3196         }
3197     }
3198     
3199     /* Deal with midgame name changes */
3200     if (!newGame) {
3201         if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {
3202             if (gameInfo.white) free(gameInfo.white);
3203             gameInfo.white = StrSave(white);
3204         }
3205         if (!gameInfo.black || strcmp(gameInfo.black, black) != 0) {
3206             if (gameInfo.black) free(gameInfo.black);
3207             gameInfo.black = StrSave(black);
3208         }
3209     }
3210     
3211     /* Throw away game result if anything actually changes in examine mode */
3212     if (gameMode == IcsExamining && !newGame) {
3213         gameInfo.result = GameUnfinished;
3214         if (gameInfo.resultDetails != NULL) {
3215             free(gameInfo.resultDetails);
3216             gameInfo.resultDetails = NULL;
3217         }
3218     }
3219     
3220     /* In pausing && IcsExamining mode, we ignore boards coming
3221        in if they are in a different variation than we are. */
3222     if (pauseExamInvalid) return;
3223     if (pausing && gameMode == IcsExamining) {
3224         if (moveNum <= pauseExamForwardMostMove) {
3225             pauseExamInvalid = TRUE;
3226             forwardMostMove = pauseExamForwardMostMove;
3227             return;
3228         }
3229     }
3230     
3231     /* Parse the board */
3232     for (k = 0; k < 8; k++)
3233       for (j = 0; j < 8; j++)
3234         board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]);
3235     CopyBoard(boards[moveNum], board);
3236     if (moveNum == 0) {
3237         startedFromSetupPosition =
3238           !CompareBoards(board, initialPosition);
3239     }
3240     
3241     if (ics_getting_history == H_GOT_REQ_HEADER ||
3242         ics_getting_history == H_GOT_UNREQ_HEADER) {
3243         /* This was an initial position from a move list, not
3244            the current position */
3245         return;
3246     }
3247     
3248     /* Update currentMove and known move number limits */
3249     newMove = newGame || moveNum > forwardMostMove;
3250  
3251
3252         /* If we found takebacks during IcsAnalyze try send to engine */
3253         if (!newGame && appData.icsAnalyze && first.analyzing  &&
3254                 moveNum < forwardMostMove) {
3255                 if (appData.debugMode) fprintf(debugFP, "take back move\n");
3256                 takeback = forwardMostMove - moveNum;
3257                 if (!appData.icsWBprotoAgr) {
3258                         /* safty first */
3259                         SendToProgram("exit\n", &first);
3260                         SendToProgram("force\n", &first);
3261                         for (i = 0; i < takeback; i++) {
3262                                 SendToProgram("undo\n", &first); 
3263                                 /* Some engine need analyze and stat again */
3264                         }
3265                         SendToProgram("analyze\n", &first);
3266                 } else {
3267                         /* hard */
3268                         IcsAnalyze(FALSE);
3269                         IcsAnalyze(TRUE);
3270                 }       
3271         }
3272     if (newGame) {
3273         forwardMostMove = backwardMostMove = currentMove = moveNum;
3274         if (gameMode == IcsExamining && moveNum == 0) {
3275           /* Workaround for ICS limitation: we are not told the wild
3276              type when starting to examine a game.  But if we ask for
3277              the move list, the move list header will tell us */
3278             ics_getting_history = H_REQUESTED;
3279             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3280             SendToICS(str);
3281         }
3282     } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
3283                || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {
3284         forwardMostMove = moveNum;
3285         if (!pausing || currentMove > forwardMostMove)
3286           currentMove = forwardMostMove;
3287     } else {
3288         /* New part of history that is not contiguous with old part */ 
3289         if (pausing && gameMode == IcsExamining) {
3290             pauseExamInvalid = TRUE;
3291             forwardMostMove = pauseExamForwardMostMove;
3292             return;
3293         }
3294         forwardMostMove = backwardMostMove = currentMove = moveNum;
3295         if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {
3296             ics_getting_history = H_REQUESTED;
3297             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3298             SendToICS(str);
3299         }
3300     }
3301
3302     {
3303         int i = 0;
3304     /* Update the clocks */
3305     if (strchr(elapsed_time, '.')) {
3306       /* Time is in ms */
3307       timeRemaining[0][moveNum] = whiteTimeRemaining = white_time;
3308       timeRemaining[1][moveNum] = blackTimeRemaining = black_time;
3309     } else {
3310       /* Time is in seconds */
3311          i = 1;
3312       timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;
3313       timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;
3314     }
3315         }
3316
3317         /* Time workaround for ICC - send only sec :( */
3318         if (ics_type == ICS_ICC) {
3319                 if (timeRemaining[0][moveNum] >= 1500) timeRemaining[0][moveNum] = timeRemaining[0][moveNum] - 1500;
3320                 if (timeRemaining[1][moveNum] >= 1500) timeRemaining[1][moveNum] = timeRemaining[1][moveNum] - 1500;
3321
3322                 if (timeRemaining[0][moveNum] == 1000) timeRemaining[0][moveNum] = timeRemaining[0][moveNum] - 950;
3323                 if (timeRemaining[0][moveNum] == 1000) timeRemaining[1][moveNum] = timeRemaining[1][moveNum] - 950;
3324
3325         }
3326      
3327 #if ZIPPY
3328     if (appData.zippyPlay && newGame &&
3329         gameMode != IcsObserving && gameMode != IcsIdle &&
3330         gameMode != IcsExamining)
3331       ZippyFirstBoard(moveNum, basetime, increment);
3332 #endif
3333     
3334     /* Put the move on the move list, first converting
3335        to canonical algebraic form. */
3336     if (moveNum > 0) {
3337         if (moveNum <= backwardMostMove) {
3338             /* We don't know what the board looked like before
3339                this move.  Punt. */
3340             strcpy(parseList[moveNum - 1], move_str);
3341             strcat(parseList[moveNum - 1], " ");
3342             strcat(parseList[moveNum - 1], elapsed_time);
3343             moveList[moveNum - 1][0] = NULLCHAR;
3344         } else if (ParseOneMove(move_str, moveNum - 1, &moveType,
3345                                 &fromX, &fromY, &toX, &toY, &promoChar)) {
3346             (void) CoordsToAlgebraic(boards[moveNum - 1],
3347                                      PosFlags(moveNum - 1), EP_UNKNOWN,
3348                                      fromY, fromX, toY, toX, promoChar,
3349                                      parseList[moveNum-1]);
3350             switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN)){
3351               case MT_NONE:
3352               case MT_STALEMATE:
3353               default:
3354                 break;
3355               case MT_CHECK:
3356                 strcat(parseList[moveNum - 1], "+");
3357                 break;
3358               case MT_CHECKMATE:
3359                 strcat(parseList[moveNum - 1], "#");
3360                 break;
3361             }
3362             strcat(parseList[moveNum - 1], " ");
3363             strcat(parseList[moveNum - 1], elapsed_time);
3364             /* currentMoveString is set as a side-effect of ParseOneMove */
3365             strcpy(moveList[moveNum - 1], currentMoveString);
3366             strcat(moveList[moveNum - 1], "\n");
3367         } else if (strcmp(move_str, "none") == 0) {
3368             /* Again, we don't know what the board looked like;
3369                this is really the start of the game. */
3370             parseList[moveNum - 1][0] = NULLCHAR;
3371             moveList[moveNum - 1][0] = NULLCHAR;
3372             backwardMostMove = moveNum;
3373             startedFromSetupPosition = TRUE;
3374             fromX = fromY = toX = toY = -1;
3375         } else {
3376             /* Move from ICS was illegal!?  Punt. */
3377 #if 0
3378             if (appData.testLegality && appData.debugMode) {
3379                 sprintf(str, "Illegal move \"%s\" from ICS", move_str);
3380                 DisplayError(str, 0);
3381             }
3382 #endif
3383             strcpy(parseList[moveNum - 1], move_str);
3384             strcat(parseList[moveNum - 1], " ");
3385             strcat(parseList[moveNum - 1], elapsed_time);
3386             moveList[moveNum - 1][0] = NULLCHAR;
3387             fromX = fromY = toX = toY = -1;
3388         }
3389
3390 #if ZIPPY
3391         /* Send move to chess program (BEFORE animating it). */
3392         if (appData.zippyPlay && !newGame && newMove && 
3393            (!appData.getMoveList || backwardMostMove == 0) && first.initDone) {
3394
3395             if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||
3396                 (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {
3397                 if (moveList[moveNum - 1][0] == NULLCHAR) {
3398                     sprintf(str, "Couldn't parse move \"%s\" from ICS",
3399                             move_str);
3400                     DisplayError(str, 0);
3401                 } else {
3402                     if (first.sendTime) {
3403                         SendTimeRemaining(&first, gameMode == IcsPlayingWhite);
3404                     }
3405                     SendMoveToProgram(moveNum - 1, &first);
3406                     if (firstMove) {
3407                         firstMove = FALSE;
3408                         if (first.useColors) {
3409                           SendToProgram(gameMode == IcsPlayingWhite ?
3410                                         "white\ngo\n" :
3411                                         "black\ngo\n", &first);
3412                         } else {
3413                           SendToProgram("go\n", &first);
3414                         }
3415                         first.maybeThinking = TRUE;
3416                     }
3417                 }
3418             } else if (gameMode == IcsObserving || gameMode == IcsExamining) {
3419               if (moveList[moveNum - 1][0] == NULLCHAR) {
3420                 sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str);
3421                 DisplayError(str, 0);
3422               } else {
3423                           SendMoveToProgram(moveNum - 1, &first);        
3424               }
3425             }
3426         }
3427 #endif
3428     }
3429
3430     if (moveNum > 0 && !gotPremove) {
3431         /* If move comes from a remote source, animate it.  If it
3432            isn't remote, it will have already been animated. */
3433         if (!pausing && !ics_user_moved && prevMove == moveNum - 1) {
3434             AnimateMove(boards[moveNum - 1], fromX, fromY, toX, toY);
3435         }
3436         if (!pausing && appData.highlightLastMove) {
3437             SetHighlights(fromX, fromY, toX, toY);
3438         }
3439     }
3440     
3441     /* Start the clocks */
3442     whiteFlag = blackFlag = FALSE;
3443     appData.clockMode = !(basetime == 0 && increment == 0);
3444     if (ticking == 0) {
3445       ics_clock_paused = TRUE;
3446       StopClocks();
3447     } else if (ticking == 1) {
3448       ics_clock_paused = FALSE;
3449     }
3450     if (gameMode == IcsIdle ||
3451         relation == RELATION_OBSERVING_STATIC ||
3452         relation == RELATION_EXAMINING ||
3453         ics_clock_paused)
3454       DisplayBothClocks();
3455     else
3456       StartClocks();
3457     
3458     /* Display opponents and material strengths */
3459     if (gameInfo.variant != VariantBughouse &&
3460         gameInfo.variant != VariantCrazyhouse) {
3461         if (tinyLayout || smallLayout) {
3462             sprintf(str, "%s(%d) %s(%d) {%d %d}", 
3463                     gameInfo.white, white_stren, gameInfo.black, black_stren,
3464                     basetime, increment);
3465         } else {
3466             sprintf(str, "%s (%d) vs. %s (%d) {%d %d}", 
3467                     gameInfo.white, white_stren, gameInfo.black, black_stren,
3468                     basetime, increment);
3469         }
3470         DisplayTitle(str);
3471     }
3472
3473    
3474     /* Display the board */
3475     if (!pausing) {
3476       
3477       if (appData.premove)
3478           if (!gotPremove || 
3479              ((gameMode == IcsPlayingWhite) && (WhiteOnMove(currentMove))) ||
3480              ((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))
3481               ClearPremoveHighlights();
3482
3483       DrawPosition(FALSE, boards[currentMove]);
3484       DisplayMove(moveNum - 1);
3485       if (appData.ringBellAfterMoves && !ics_user_moved)
3486         RingBell();
3487     }
3488
3489     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
3490 }
3491
3492 void
3493 GetMoveListEvent()
3494 {
3495     char buf[MSG_SIZ];
3496     if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {
3497         ics_getting_history = H_REQUESTED;
3498         sprintf(buf, "%smoves %d\n", ics_prefix, ics_gamenum);
3499         SendToICS(buf);
3500     }
3501 }
3502
3503 void
3504 AnalysisPeriodicEvent(force)
3505      int force;
3506 {
3507         /* WB engine room */
3508         if (appData.AnalysisWindow && programStats.depth > 2) {
3509                 /* GUI disable send stat ? */
3510                 if (!appData.engineStatLine) SendToProgram(".\n", &first);
3511                 /* don't support Stats on game ?*/
3512                 if (supportStat == 0) {
3513                                 /* call Display every sec for time and nodes */
3514                                 DisplayAnalysis(1,0);
3515                 } else {
3516                             /* GUI makes time ???? */
3517                                 /* at the moment: yes! */
3518                                 DisplayAnalysis(1,0);
3519                 }
3520         } else if (appData.icsAnalyze && programStats.depth > 2) {
3521                         SendToProgram(".\n", &first);
3522                         DisplayAnalysis(1,0);
3523         } else if (((programStats.ok_to_send == 0 || programStats.line_is_book)
3524          && !force) || !appData.periodicUpdates)
3525       return;
3526         
3527     /* Send . command to Crafty to collect stats */
3528         if (!appData.AnalysisWindow && (gameMode == AnalyzeMode ||
3529                 gameMode == AnalyzeFile)) SendToProgram(".\n", &first);
3530
3531     /* Don't send another until we get a response (this makes
3532        us stop sending to old Crafty's which don't understand
3533        the "." command (sending illegal cmds resets node count & time,
3534        which looks bad)) */
3535     programStats.ok_to_send = 0;
3536 }
3537
3538 void
3539 SendMoveToProgram(moveNum, cps)
3540      int moveNum;
3541      ChessProgramState *cps;
3542 {
3543     char buf[MSG_SIZ];
3544     if (cps->useUsermove) {
3545       SendToProgram("usermove ", cps);
3546     }
3547     if (cps->useSAN) {
3548       char *space;
3549       if ((space = strchr(parseList[moveNum], ' ')) != NULL) {
3550         int len = space - parseList[moveNum];
3551         memcpy(buf, parseList[moveNum], len);
3552         buf[len++] = '\n';
3553         buf[len] = NULLCHAR;
3554       } else {
3555         sprintf(buf, "%s\n", parseList[moveNum]);
3556       }
3557       SendToProgram(buf, cps);
3558     } else {
3559       SendToProgram(moveList[moveNum], cps);
3560     }
3561 }
3562
3563 void
3564 SendMoveToICS(moveType, fromX, fromY, toX, toY)
3565      ChessMove moveType;
3566      int fromX, fromY, toX, toY;
3567 {
3568     char user_move[MSG_SIZ];
3569
3570     switch (moveType) {
3571       default:
3572         sprintf(user_move, "say Internal error; bad moveType %d (%d,%d-%d,%d)",
3573                 (int)moveType, fromX, fromY, toX, toY);
3574         DisplayError(user_move + strlen("say "), 0);
3575         break;
3576       case WhiteKingSideCastle:
3577       case BlackKingSideCastle:
3578       case WhiteQueenSideCastleWild:
3579       case BlackQueenSideCastleWild:
3580         sprintf(user_move, "o-o\n");
3581         break;
3582       case WhiteQueenSideCastle:
3583       case BlackQueenSideCastle:
3584       case WhiteKingSideCastleWild:
3585       case BlackKingSideCastleWild:
3586         sprintf(user_move, "o-o-o\n");
3587         break;
3588       case WhitePromotionQueen:
3589       case BlackPromotionQueen:
3590       case WhitePromotionRook:
3591       case BlackPromotionRook:
3592       case WhitePromotionBishop:
3593       case BlackPromotionBishop:
3594       case WhitePromotionKnight:
3595       case BlackPromotionKnight:
3596       case WhitePromotionKing:
3597       case BlackPromotionKing:
3598         sprintf(user_move, "%c%c%c%c=%c\n",
3599                 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY,
3600                 PieceToChar(PromoPiece(moveType)));
3601         break;
3602       case WhiteDrop:
3603       case BlackDrop:
3604         sprintf(user_move, "%c@%c%c\n",
3605                 ToUpper(PieceToChar((ChessSquare) fromX)),
3606                 'a' + toX, '1' + toY);
3607         break;
3608       case NormalMove:
3609       case WhiteCapturesEnPassant:
3610       case BlackCapturesEnPassant:
3611       case IllegalMove:  /* could be a variant we don't quite understand */
3612         sprintf(user_move, "%c%c%c%c\n",
3613                 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY);
3614         break;
3615     }
3616     SendToICS(user_move);
3617 }
3618
3619 void
3620 CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
3621      int rf, ff, rt, ft;
3622      char promoChar;
3623      char move[7];
3624 {
3625     if (rf == DROP_RANK) {
3626         sprintf(move, "%c@%c%c\n",
3627                 ToUpper(PieceToChar((ChessSquare) ff)), 'a' + ft, '1' + rt);
3628     } else {
3629         if (promoChar == 'x' || promoChar == NULLCHAR) {
3630             sprintf(move, "%c%c%c%c\n",
3631                     'a' + ff, '1' + rf, 'a' + ft, '1' + rt);
3632         } else {
3633             sprintf(move, "%c%c%c%c%c\n",
3634                     'a' + ff, '1' + rf, 'a' + ft, '1' + rt, promoChar);
3635         }
3636     }
3637 }
3638
3639 void
3640 ProcessICSInitScript(f)
3641      FILE *f;
3642 {
3643     char buf[MSG_SIZ];
3644
3645     while (fgets(buf, MSG_SIZ, f)) {
3646         SendToICSDelayed(buf,(long)appData.msLoginDelay);
3647     }
3648
3649     fclose(f);
3650 }
3651
3652
3653 /* Parser for moves from gnuchess, ICS, or user typein box */
3654 Boolean
3655 ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
3656      char *move;
3657      int moveNum;
3658      ChessMove *moveType;
3659      int *fromX, *fromY, *toX, *toY;
3660      char *promoChar;
3661 {       
3662     *moveType = yylexstr(moveNum, move);
3663     switch (*moveType) {
3664       case WhitePromotionQueen:
3665       case BlackPromotionQueen:
3666       case WhitePromotionRook:
3667       case BlackPromotionRook:
3668       case WhitePromotionBishop:
3669       case BlackPromotionBishop:
3670       case WhitePromotionKnight:
3671       case BlackPromotionKnight:
3672       case WhitePromotionKing:
3673       case BlackPromotionKing:
3674       case NormalMove:
3675       case WhiteCapturesEnPassant:
3676       case BlackCapturesEnPassant:
3677       case WhiteKingSideCastle:
3678       case WhiteQueenSideCastle:
3679       case BlackKingSideCastle:
3680       case BlackQueenSideCastle:
3681       case WhiteKingSideCastleWild:
3682       case WhiteQueenSideCastleWild:
3683       case BlackKingSideCastleWild:
3684       case BlackQueenSideCastleWild:
3685       case IllegalMove:         /* bug or odd chess variant */
3686         *fromX = currentMoveString[0] - 'a';
3687         *fromY = currentMoveString[1] - '1';
3688         *toX = currentMoveString[2] - 'a';
3689         *toY = currentMoveString[3] - '1';
3690         *promoChar = currentMoveString[4];
3691         if (*fromX < 0 || *fromX > 7 || *fromY < 0 || *fromY > 7 ||
3692             *toX < 0 || *toX > 7 || *toY < 0 || *toY > 7) {
3693             *fromX = *fromY = *toX = *toY = 0;
3694             return FALSE;
3695         }
3696         if (appData.testLegality) {
3697           return (*moveType != IllegalMove);
3698         } else {
3699           return !(fromX == fromY && toX == toY);
3700         }
3701
3702       case WhiteDrop:
3703       case BlackDrop:
3704         *fromX = *moveType == WhiteDrop ?
3705           (int) CharToPiece(ToUpper(currentMoveString[0])) :
3706         (int) CharToPiece(ToLower(currentMoveString[0]));
3707         *fromY = DROP_RANK;
3708         *toX = currentMoveString[2] - 'a';
3709         *toY = currentMoveString[3] - '1';
3710         *promoChar = NULLCHAR;
3711         return TRUE;
3712
3713       case AmbiguousMove:
3714       case ImpossibleMove:
3715       case (ChessMove) 0:       /* end of file */
3716       case ElapsedTime:
3717       case Comment:
3718       case PGNTag:
3719       case NAG:
3720       case WhiteWins:
3721       case BlackWins:
3722       case GameIsDrawn:
3723       default:
3724         /* bug? */
3725         *fromX = *fromY = *toX = *toY = 0;
3726         *promoChar = NULLCHAR;
3727         return FALSE;
3728     }
3729 }
3730
3731
3732 void
3733 InitPosition(redraw)
3734      int redraw;
3735 {
3736     currentMove = forwardMostMove = backwardMostMove = 0;
3737     switch (gameInfo.variant) {
3738     default:
3739       CopyBoard(boards[0], initialPosition);
3740       break;
3741     case VariantTwoKings:
3742       CopyBoard(boards[0], twoKingsPosition);
3743       startedFromSetupPosition = TRUE;
3744       break;
3745     case VariantWildCastle:
3746       CopyBoard(boards[0], initialPosition);
3747       /* !!?shuffle with kings guaranteed to be on d or e file */
3748       break;
3749     case VariantNoCastle:
3750       CopyBoard(boards[0], initialPosition);
3751       /* !!?unconstrained back-rank shuffle */
3752       break;
3753     case VariantFischeRandom:
3754       CopyBoard(boards[0], initialPosition);
3755       /* !!shuffle according to FR rules */
3756       break;
3757     }
3758     if (redraw)
3759       DrawPosition(FALSE, boards[currentMove]);
3760 }
3761
3762 void
3763 SendBoard(cps, moveNum)
3764      ChessProgramState *cps;
3765      int moveNum;
3766 {
3767     char message[MSG_SIZ];
3768     
3769     if (cps->useSetboard) {
3770       char* fen = PositionToFEN(moveNum);
3771       sprintf(message, "setboard %s\n", fen);
3772       SendToProgram(message, cps);
3773       free(fen);
3774
3775     } else {
3776       ChessSquare *bp;
3777       int i, j;
3778       /* Kludge to set black to move, avoiding the troublesome and now
3779        * deprecated "black" command.
3780        */
3781       if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);
3782
3783       SendToProgram("edit\n", cps);
3784       SendToProgram("#\n", cps);
3785       for (i = BOARD_SIZE - 1; i >= 0; i--) {
3786         bp = &boards[moveNum][i][0];
3787         for (j = 0; j < BOARD_SIZE; j++, bp++) {
3788           if ((int) *bp < (int) BlackPawn) {
3789             sprintf(message, "%c%c%c\n", PieceToChar(*bp), 
3790                     'a' + j, '1' + i);
3791             SendToProgram(message, cps);
3792           }
3793         }
3794       }
3795     
3796       SendToProgram("c\n", cps);
3797       for (i = BOARD_SIZE - 1; i >= 0; i--) {
3798         bp = &boards[moveNum][i][0];
3799         for (j = 0; j < BOARD_SIZE; j++, bp++) {
3800           if (((int) *bp != (int) EmptySquare)
3801               && ((int) *bp >= (int) BlackPawn)) {
3802             sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
3803                     'a' + j, '1' + i);
3804             SendToProgram(message, cps);
3805           }
3806         }
3807       }
3808     
3809       SendToProgram(".\n", cps);
3810     }
3811 }
3812
3813 int
3814 IsPromotion(fromX, fromY, toX, toY)
3815      int fromX, fromY, toX, toY;
3816 {
3817     return gameMode != EditPosition &&
3818       fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 &&
3819         ((boards[currentMove][fromY][fromX] == WhitePawn && toY == 7) ||
3820          (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0));
3821 }
3822
3823
3824 int
3825 PieceForSquare (x, y)
3826      int x;
3827      int y;
3828 {
3829   if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE)
3830      return -1;
3831   else
3832      return boards[currentMove][y][x];
3833 }
3834
3835 int
3836 OKToStartUserMove(x, y)
3837      int x, y;
3838 {
3839     ChessSquare from_piece;
3840     int white_piece;
3841
3842     if (matchMode) return FALSE;
3843     if (gameMode == EditPosition) return TRUE;
3844
3845     if (x >= 0 && y >= 0)
3846       from_piece = boards[currentMove][y][x];
3847     else
3848       from_piece = EmptySquare;
3849
3850     if (from_piece == EmptySquare) return FALSE;
3851
3852     white_piece = (int)from_piece >= (int)WhitePawn &&
3853       (int)from_piece <= (int)WhiteKing;
3854
3855     switch (gameMode) {
3856       case PlayFromGameFile:
3857       case AnalyzeFile:
3858       case TwoMachinesPlay:
3859       case EndOfGame:
3860         return FALSE;
3861
3862       case IcsObserving:
3863       case IcsIdle:
3864         return FALSE;
3865
3866       case MachinePlaysWhite:
3867       case IcsPlayingBlack:
3868         if (appData.zippyPlay) return FALSE;
3869         if (white_piece) {
3870             DisplayMoveError("You are playing Black");
3871             return FALSE;
3872         }
3873         break;
3874
3875       case MachinePlaysBlack:
3876       case IcsPlayingWhite:
3877         if (appData.zippyPlay) return FALSE;
3878         if (!white_piece) {
3879             DisplayMoveError("You are playing White");
3880             return FALSE;
3881         }
3882         break;
3883
3884       case EditGame:
3885         if (!white_piece && WhiteOnMove(currentMove)) {
3886             DisplayMoveError("It is White's turn");
3887             return FALSE;
3888         }           
3889         if (white_piece && !WhiteOnMove(currentMove)) {
3890             DisplayMoveError("It is Black's turn");
3891             return FALSE;
3892         }           
3893         if (cmailMsgLoaded && (currentMove < cmailOldMove)) {
3894             /* Editing correspondence game history */
3895             /* Could disallow this or prompt for confirmation */
3896             cmailOldMove = -1;
3897         }
3898         if (currentMove < forwardMostMove) {
3899             /* Discarding moves */
3900             /* Could prompt for confirmation here,
3901                but I don't think that's such a good idea */
3902             forwardMostMove = currentMove;
3903         }
3904         break;
3905
3906       case BeginningOfGame:
3907         if (appData.icsActive) return FALSE;
3908         if (!appData.noChessProgram) {
3909             if (!white_piece) {
3910                 DisplayMoveError("You are playing White");
3911                 return FALSE;
3912             }
3913         }
3914         break;
3915         
3916       case Training:
3917         if (!white_piece && WhiteOnMove(currentMove)) {
3918             DisplayMoveError("It is White's turn");
3919             return FALSE;
3920         }           
3921         if (white_piece && !WhiteOnMove(currentMove)) {
3922             DisplayMoveError("It is Black's turn");
3923             return FALSE;
3924         }           
3925         break;
3926
3927       default:
3928       case IcsExamining:
3929         break;
3930     }
3931     if (currentMove != forwardMostMove && gameMode != AnalyzeMode
3932         && gameMode != AnalyzeFile && gameMode != Training) {
3933         DisplayMoveError("Displayed position is not current");
3934         return FALSE;
3935     }
3936     return TRUE;
3937 }
3938
3939 FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL;
3940 int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;
3941 int lastLoadGameUseList = FALSE;
3942 char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
3943 ChessMove lastLoadGameStart = (ChessMove) 0;
3944
3945
3946 void
3947 UserMoveEvent(fromX, fromY, toX, toY, promoChar)
3948      int fromX, fromY, toX, toY;
3949      int promoChar;
3950 {
3951     ChessMove moveType;
3952
3953     if (fromX < 0 || fromY < 0) return;
3954     if ((fromX == toX) && (fromY == toY)) {
3955         return;
3956     }
3957         
3958     /* Check if the user is playing in turn.  This is complicated because we
3959        let the user "pick up" a piece before it is his turn.  So the piece he
3960        tried to pick up may have been captured by the time he puts it down!
3961        Therefore we use the color the user is supposed to be playing in this
3962        test, not the color of the piece that is currently on the starting
3963        square---except in EditGame mode, where the user is playing both
3964        sides; fortunately there the capture race can't happen.  (It can
3965        now happen in IcsExamining mode, but that's just too bad.  The user
3966        will get a somewhat confusing message in that case.)
3967        */
3968
3969     switch (gameMode) {
3970       case PlayFromGameFile:
3971       case AnalyzeFile:
3972       case TwoMachinesPlay:
3973       case EndOfGame:
3974       case IcsObserving:
3975       case IcsIdle:
3976         /* We switched into a game mode where moves are not accepted,
3977            perhaps while the mouse button was down. */
3978         return;
3979
3980       case MachinePlaysWhite:
3981         /* User is moving for Black */
3982         if (WhiteOnMove(currentMove)) {
3983             DisplayMoveError("It is White's turn");
3984             return;
3985         }
3986         break;
3987
3988       case MachinePlaysBlack:
3989         /* User is moving for White */
3990         if (!WhiteOnMove(currentMove)) {
3991             DisplayMoveError("It is Black's turn");
3992             return;
3993         }
3994         break;
3995
3996       case EditGame:
3997       case IcsExamining:
3998       case BeginningOfGame:
3999       case AnalyzeMode:
4000       case Training:
4001         if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&
4002             (int) boards[currentMove][fromY][fromX] <= (int) BlackKing) {
4003             /* User is moving for Black */
4004             if (WhiteOnMove(currentMove)) {
4005                 DisplayMoveError("It is White's turn");
4006                 return;
4007             }
4008         } else {
4009             /* User is moving for White */
4010             if (!WhiteOnMove(currentMove)) {
4011                 DisplayMoveError("It is Black's turn");
4012                 return;
4013             }
4014         }
4015         break;
4016
4017       case IcsPlayingBlack:
4018         /* User is moving for Black */
4019         if (WhiteOnMove(currentMove)) {
4020             if (!appData.premove) {
4021                 DisplayMoveError("It is White's turn");
4022             } else if (toX >= 0 && toY >= 0) {
4023                 premoveToX = toX;
4024                 premoveToY = toY;
4025                 premoveFromX = fromX;
4026                 premoveFromY = fromY;
4027                 premovePromoChar = promoChar;
4028                 gotPremove = 1;
4029                 if (appData.debugMode) 
4030                     fprintf(debugFP, "Got premove: fromX %d,"
4031                             "fromY %d, toX %d, toY %d\n",
4032                             fromX, fromY, toX, toY);
4033             }
4034             return;
4035         }
4036         break;
4037
4038       case IcsPlayingWhite:
4039         /* User is moving for White */
4040         if (!WhiteOnMove(currentMove)) {
4041             if (!appData.premove) {
4042                 DisplayMoveError("It is Black's turn");
4043             } else if (toX >= 0 && toY >= 0) {
4044                 premoveToX = toX;
4045                 premoveToY = toY;
4046                 premoveFromX = fromX;
4047                 premoveFromY = fromY;
4048                 premovePromoChar = promoChar;
4049                 gotPremove = 1;
4050                 if (appData.debugMode) 
4051                     fprintf(debugFP, "Got premove: fromX %d,"
4052                             "fromY %d, toX %d, toY %d\n",
4053                             fromX, fromY, toX, toY);
4054             }
4055             return;
4056         }
4057         break;
4058
4059       default:
4060         break;
4061
4062       case EditPosition:
4063         if (toX == -2 || toY == -2) {
4064             boards[0][fromY][fromX] = EmptySquare;
4065             DrawPosition(FALSE, boards[currentMove]);
4066         } else if (toX >= 0 && toY >= 0) {
4067             boards[0][toY][toX] = boards[0][fromY][fromX];
4068             boards[0][fromY][fromX] = EmptySquare;
4069             DrawPosition(FALSE, boards[currentMove]);
4070         }
4071         return;
4072     }
4073
4074     if (toX < 0 || toY < 0) return;
4075     userOfferedDraw = FALSE;
4076         
4077     if (appData.testLegality) {
4078         moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
4079                                 EP_UNKNOWN, fromY, fromX, toY, toX, promoChar);
4080         if (moveType == IllegalMove || moveType == ImpossibleMove) {
4081             DisplayMoveError("Illegal move");
4082             return;
4083         }
4084     } else {
4085         moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);
4086     }
4087
4088     if (gameMode == Training) {
4089       /* compare the move played on the board to the next move in the
4090        * game. If they match, display the move and the opponent's response. 
4091        * If they don't match, display an error message.
4092        */
4093       int saveAnimate;
4094       Board testBoard;
4095       CopyBoard(testBoard, boards[currentMove]);
4096       ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);
4097
4098       if (CompareBoards(testBoard, boards[currentMove+1])) {
4099         ForwardInner(currentMove+1);
4100
4101         /* Autoplay the opponent's response.
4102          * if appData.animate was TRUE when Training mode was entered,
4103          * the response will be animated.
4104          */
4105         saveAnimate = appData.animate;
4106         appData.animate = animateTraining;
4107         ForwardInner(currentMove+1);
4108         appData.animate = saveAnimate;
4109
4110         /* check for the end of the game */
4111         if (currentMove >= forwardMostMove) {
4112           gameMode = PlayFromGameFile;
4113           ModeHighlight();
4114           SetTrainingModeOff();
4115           DisplayInformation("End of game");
4116         }
4117       } else {
4118         DisplayError("Incorrect move", 0);
4119       }
4120       return;
4121     }
4122
4123     FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
4124 }
4125
4126 /* Common tail of UserMoveEvent and DropMenuEvent */
4127 void
4128 FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
4129      ChessMove moveType;
4130      int fromX, fromY, toX, toY;
4131      /*char*/int promoChar;
4132 {
4133   /* Ok, now we know that the move is good, so we can kill
4134      the previous line in Analysis Mode */
4135   if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
4136     forwardMostMove = currentMove;
4137   }
4138
4139   /* If we need the chess program but it's dead, restart it */
4140   ResurrectChessProgram();
4141
4142   /* A user move restarts a paused game*/
4143   if (pausing)
4144     PauseEvent();
4145
4146   thinkOutput[0] = NULLCHAR;
4147
4148   MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/
4149
4150   if (gameMode == BeginningOfGame) {
4151     if (appData.noChessProgram) {
4152       gameMode = EditGame;
4153       SetGameInfo();
4154     } else {
4155       char buf[MSG_SIZ];
4156       gameMode = MachinePlaysBlack;
4157       SetGameInfo();
4158       sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
4159       DisplayTitle(buf);
4160       if (first.sendName) {
4161         sprintf(buf, "name %s\n", gameInfo.white);
4162         SendToProgram(buf, &first);
4163       }
4164     }
4165     ModeHighlight();
4166   }
4167
4168   /* Relay move to ICS or chess engine */
4169   if (appData.icsActive) {
4170     if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
4171         gameMode == IcsExamining) {
4172       SendMoveToICS(moveType, fromX, fromY, toX, toY);
4173       ics_user_moved = 1;
4174     }
4175   } else {
4176     if (first.sendTime && (gameMode == BeginningOfGame ||
4177                            gameMode == MachinePlaysWhite ||
4178                            gameMode == MachinePlaysBlack)) {
4179       SendTimeRemaining(&first, gameMode != MachinePlaysBlack);
4180     }
4181         
4182     SendMoveToProgram(forwardMostMove-1, &first); 
4183     if (gameMode != EditGame && gameMode != PlayFromGameFile) {
4184       first.maybeThinking = TRUE;
4185     }
4186     if (currentMove == cmailOldMove + 1) {
4187       cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
4188     }
4189   }
4190
4191   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
4192
4193   switch (gameMode) {
4194   case EditGame:
4195     switch (MateTest(boards[currentMove], PosFlags(currentMove),
4196                      EP_UNKNOWN)) {
4197     case MT_NONE:
4198     case MT_CHECK:
4199       break;
4200     case MT_CHECKMATE:
4201       if (WhiteOnMove(currentMove)) {
4202         GameEnds(BlackWins, "Black mates", GE_PLAYER);
4203       } else {
4204         GameEnds(WhiteWins, "White mates", GE_PLAYER);
4205       }
4206       break;
4207     case MT_STALEMATE:
4208       GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
4209       break;
4210     }
4211     break;
4212     
4213   case MachinePlaysBlack:
4214   case MachinePlaysWhite:
4215     /* disable certain menu options while machine is thinking */
4216     SetMachineThinkingEnables();
4217     break;
4218
4219   default:
4220     break;
4221   }
4222 }
4223
4224 void
4225 HandleMachineMove(message, cps)
4226      char *message;
4227      ChessProgramState *cps;
4228 {
4229     char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];
4230     char realname[MSG_SIZ];
4231     int fromX, fromY, toX, toY;
4232     ChessMove moveType;
4233     char promoChar;
4234     char *p;
4235     int machineWhite;
4236         
4237     /*
4238      * Kludge to ignore BEL characters
4239      */
4240     while (*message == '\007') message++;
4241
4242     /*
4243      * Look for book output
4244      */
4245     if (cps == &first && bookRequested) {
4246         if (message[0] == '\t' || message[0] == ' ') {
4247             /* Part of the book output is here; append it */
4248             strcat(bookOutput, message);
4249             strcat(bookOutput, "  \n");
4250             return;
4251         } else if (bookOutput[0] != NULLCHAR) {
4252             /* All of book output has arrived; display it */
4253             char *p = bookOutput;
4254             while (*p != NULLCHAR) {
4255                 if (*p == '\t') *p = ' ';
4256                 p++;
4257             }
4258             DisplayInformation(bookOutput);
4259             bookRequested = FALSE;
4260             /* Fall through to parse the current output */
4261         }
4262     }
4263
4264     /*
4265      * Look for machine move.
4266      */
4267     if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 &&
4268          strcmp(buf2, "...") == 0) ||
4269         (sscanf(message, "%s %s", buf1, machineMove) == 2 &&
4270          strcmp(buf1, "move") == 0)) {
4271                 /* Save last score befor move for zippy draw handling */
4272                 if (appData.icsActive && appData.zippyDraw) {
4273                         //      ZippyDraw(0, programStats.score, programStats.depth);
4274                 }
4275         /* This method is only useful on engines that support ping */
4276         if (cps->lastPing != cps->lastPong) {
4277           if (gameMode == BeginningOfGame) {
4278             /* Extra move from before last new; ignore */
4279             if (appData.debugMode) {
4280                 fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
4281             }
4282           } else {
4283             if (appData.debugMode) {
4284                 fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
4285                         cps->which, gameMode);
4286             }
4287             SendToProgram("undo\n", cps);
4288           }
4289           return;
4290         }
4291
4292         switch (gameMode) {
4293           case BeginningOfGame:
4294             /* Extra move from before last reset; ignore */
4295             if (appData.debugMode) {
4296                 fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
4297             }
4298             return;
4299
4300           case EndOfGame:
4301           case IcsIdle:
4302           default:
4303             /* Extra move after we tried to stop.  The mode test is
4304                not a reliable way of detecting this problem, but it's
4305                the best we can do on engines that don't support ping.
4306             */
4307             if (appData.debugMode) {
4308                 fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
4309                         cps->which, gameMode);
4310             }
4311             SendToProgram("undo\n", cps);
4312             return;
4313
4314           case MachinePlaysWhite:
4315           case IcsPlayingWhite:
4316             machineWhite = TRUE;
4317             break;
4318
4319           case MachinePlaysBlack:
4320           case IcsPlayingBlack:
4321             machineWhite = FALSE;
4322             break;
4323
4324           case TwoMachinesPlay:
4325             machineWhite = (cps->twoMachinesColor[0] == 'w');
4326             break;
4327         }
4328         if (WhiteOnMove(forwardMostMove) != machineWhite) {
4329             if (appData.debugMode) {
4330                 fprintf(debugFP,
4331                         "Ignoring move out of turn by %s, gameMode %d"
4332                         ", forwardMost %d\n",
4333                         cps->which, gameMode, forwardMostMove);
4334             }
4335             return;
4336         }
4337
4338         if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
4339                               &fromX, &fromY, &toX, &toY, &promoChar)) {
4340             /* Machine move could not be parsed; ignore it. */
4341             sprintf(buf1, "Illegal move \"%s\" from %s machine",
4342                     machineMove, cps->which);
4343             /*!!if (appData.debugMode)*/ DisplayError(buf1, 0);
4344             return;
4345         }
4346
4347         hintRequested = FALSE;
4348         lastHint[0] = NULLCHAR;
4349         bookRequested = FALSE;
4350         /* Program may be pondering now */
4351         cps->maybeThinking = TRUE;
4352         if (cps->sendTime == 2) cps->sendTime = 1;
4353         if (cps->offeredDraw) cps->offeredDraw--;
4354
4355 #if ZIPPY
4356         if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&
4357             first.initDone) {
4358           SendMoveToICS(moveType, fromX, fromY, toX, toY);
4359           ics_user_moved = 1;
4360         }
4361 #endif
4362         /* currentMoveString is set as a side-effect of ParseOneMove */
4363         strcpy(machineMove, currentMoveString);
4364         strcat(machineMove, "\n");
4365         strcpy(moveList[forwardMostMove], machineMove);
4366     
4367         MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
4368     
4369         if (gameMode == TwoMachinesPlay) {
4370             if (cps->other->sendTime) {
4371                 SendTimeRemaining(cps->other,
4372                                   cps->other->twoMachinesColor[0] == 'w');
4373             }
4374             SendMoveToProgram(forwardMostMove-1, cps->other);
4375             if (firstMove) {
4376                 firstMove = FALSE;
4377                 if (cps->other->useColors) {
4378                   SendToProgram(cps->other->twoMachinesColor, cps->other);
4379                 }
4380                 SendToProgram("go\n", cps->other);
4381             }
4382             cps->other->maybeThinking = TRUE;
4383         }
4384
4385         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
4386         if (!pausing && appData.ringBellAfterMoves) {
4387             RingBell();
4388         }
4389
4390         /* 
4391          * Reenable menu items that were disabled while
4392          * machine was thinking
4393          */
4394         if (gameMode != TwoMachinesPlay)
4395             SetUserThinkingEnables();
4396         return;
4397     }
4398
4399         
4400     /* Set special modes for chess engines.  Later something general
4401      *  could be added here; for now there is just one kludge feature,
4402      *  needed because Crafty 15.10 and earlier don't ignore SIGINT
4403      *  when "xboard" is given as an interactive command.
4404      */
4405     if (strncmp(message, "kibitz Hello from Crafty", 24) == 0) {
4406         cps->useSigint = FALSE;
4407         cps->useSigterm = FALSE;
4408     }
4409
4410     /*
4411      * Look for communication commands
4412      */
4413     if (!strncmp(message, "telluser ", 9)) {
4414         DisplayInformation(message + 9);
4415         return;
4416     }
4417     if (!strncmp(message, "tellusererror ", 14)) {
4418         DisplayError(message + 14, 0);
4419         return;
4420     }
4421     if (!strncmp(message, "tellopponent ", 13)) {
4422       if (appData.icsActive) {
4423         if (loggedOn) {
4424           sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);
4425           SendToICS(buf1);
4426         }
4427       } else {
4428         DisplayInformation(message + 13);
4429       }
4430       return;
4431     }
4432     if (!strncmp(message, "tellothers ", 11)) {
4433       if (appData.icsActive) {
4434         if (loggedOn) {
4435           sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);
4436           SendToICS(buf1);
4437         }
4438       }
4439       return;
4440     }
4441     if (!strncmp(message, "tellall ", 8)) {
4442            if (appData.icsActive) { 
4443                    /* daniel*/
4444              if (loggedOn && !appData.icsAnalyze) {
4445                sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);
4446                SendToICS(buf1);
4447                  }
4448        } else { 
4449                   DisplayInformation(message + 8);
4450            }
4451       return;
4452     }
4453     if (strncmp(message, "warning", 7) == 0) {
4454         /* Undocumented feature, use tellusererror in new code */
4455         DisplayError(message, 0);
4456         return;
4457     }
4458     if (sscanf(message, "askuser %s %[^\n]", buf1, buf2) == 2) {
4459         strcpy(realname, cps->tidy);
4460         strcat(realname, " query");
4461         AskQuestion(realname, buf2, buf1, cps->pr);
4462         return;
4463     }
4464     /* Commands from the engine directly to ICS.  We don't allow these to be 
4465      *  sent until we are logged on. Crafty kibitzes have been known to 
4466      *  interfere with the login process.
4467      */
4468     if (loggedOn) {
4469         if (!strncmp(message, "tellics ", 8)) {
4470             SendToICS(message + 8);
4471             SendToICS("\n");
4472             return;
4473         }
4474         if (!strncmp(message, "tellicsnoalias ", 15)) {
4475             SendToICS(ics_prefix);
4476             SendToICS(message + 15);
4477             SendToICS("\n");
4478             return;
4479         }
4480         /* The following are for backward compatibility only */
4481         if (!strncmp(message,"whisper",7) || !strncmp(message,"kibitz",6) ||
4482             !strncmp(message,"draw",4) || !strncmp(message,"tell",3)) {
4483             SendToICS(ics_prefix);
4484             SendToICS(message);
4485             SendToICS("\n");
4486             return;
4487         }
4488     }
4489     if (strncmp(message, "feature ", 8) == 0) {
4490       ParseFeatures(message+8, cps);
4491     }
4492     if (sscanf(message, "pong %d", &cps->lastPong) == 1) {
4493       return;
4494     }
4495     /*
4496      * If the move is illegal, cancel it and redraw the board.
4497      * Also deal with other error cases.  Matching is rather loose
4498      * here to accommodate engines written before the spec.
4499      */
4500     if (strncmp(message + 1, "llegal move", 11) == 0 ||
4501         strncmp(message, "Error", 5) == 0) {
4502         if (StrStr(message, "name") || 
4503             StrStr(message, "rating") || StrStr(message, "?") ||
4504             StrStr(message, "result") || StrStr(message, "board") ||
4505             StrStr(message, "bk") || StrStr(message, "computer") ||
4506             StrStr(message, "variant") || StrStr(message, "hint") ||
4507             StrStr(message, "random") || StrStr(message, "depth") ||
4508             StrStr(message, "accepted")) {
4509             return;
4510         }
4511         if (StrStr(message, "protover")) {
4512           /* Program is responding to input, so it's apparently done
4513              initializing, and this error message indicates it is
4514              protocol version 1.  So we don't need to wait any longer
4515              for it to initialize and send feature commands. */
4516           FeatureDone(cps, 1);
4517           cps->protocolVersion = 1;
4518           return;
4519         }
4520         cps->maybeThinking = FALSE;
4521
4522         if (StrStr(message, "draw")) {
4523             /* Program doesn't have "draw" command */
4524             cps->sendDrawOffers = 0;
4525             return;
4526         }
4527         if (cps->sendTime != 1 &&
4528             (StrStr(message, "time") || StrStr(message, "otim"))) {
4529           /* Program apparently doesn't have "time" or "otim" command */
4530           cps->sendTime = 0;
4531           return;
4532         }
4533         if (StrStr(message, "analyze")) {
4534             cps->analysisSupport = FALSE;
4535             cps->analyzing = FALSE;
4536             Reset(FALSE, TRUE);
4537             sprintf(buf2, "%s does not support analysis", cps->tidy);
4538             DisplayError(buf2, 0);
4539             return;
4540         }
4541         if (StrStr(message, "st")) {
4542           cps->stKludge = TRUE;
4543           SendTimeControl(cps, movesPerSession, timeControl,
4544                           timeIncrement, appData.searchDepth,
4545                           searchTime);
4546           return;
4547         }
4548         if (StrStr(message, "sd")) {
4549           cps->sdKludge = TRUE;
4550           SendTimeControl(cps, movesPerSession, timeControl,
4551                           timeIncrement, appData.searchDepth,
4552                           searchTime);
4553           return;
4554         }
4555         if (!StrStr(message, "llegal")) return;
4556         if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
4557             gameMode == IcsIdle) return;
4558         if (forwardMostMove <= backwardMostMove) return;
4559         if (cps == &first && programStats.ok_to_send == 0) {
4560             /* Bogus message from Crafty responding to "."  This filtering
4561                can miss some of the bad messages, but fortunately the bug 
4562                is fixed in current Crafty versions, so it doesn't matter. */
4563             return;
4564         }
4565         if (pausing) PauseEvent();
4566         if (gameMode == PlayFromGameFile) {
4567             /* Stop reading this game file */
4568             gameMode = EditGame;
4569             ModeHighlight();
4570         }
4571
4572         
4573         currentMove = --forwardMostMove;
4574         DisplayMove(currentMove-1); /* before DisplayMoveError */
4575         SwitchClocks();
4576         DisplayBothClocks();
4577         sprintf(buf1, "Illegal move \"%s\" (rejected by %s chess program)",
4578                 parseList[currentMove], cps->which);
4579         DisplayMoveError(buf1);
4580         DrawPosition(FALSE, boards[currentMove]);
4581         return;
4582     }
4583     if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {
4584         /* Program has a broken "time" command that
4585            outputs a string not ending in newline.
4586            Don't use it. */
4587         cps->sendTime = 0;
4588     }
4589     
4590     /*
4591      * If chess program startup fails, exit with an error message.
4592      * Attempts to recover here are futile.
4593      */
4594     if ((StrStr(message, "unknown host") != NULL)
4595         || (StrStr(message, "No remote directory") != NULL)
4596         || (StrStr(message, "not found") != NULL)
4597         || (StrStr(message, "No such file") != NULL)
4598         || (StrStr(message, "can't alloc") != NULL)
4599         || (StrStr(message, "Permission denied") != NULL)) {
4600
4601         cps->maybeThinking = FALSE;
4602         sprintf(buf1, "Failed to start %s chess program %s on %s: %s\n",
4603                 cps->which, cps->program, cps->host, message);
4604         RemoveInputSource(cps->isr);
4605         DisplayFatalError(buf1, 0, 1);
4606         return;
4607     }
4608     
4609     /* 
4610      * Look for hint output
4611      */
4612     if (sscanf(message, "Hint: %s", buf1) == 1) {
4613                 switch (gameMode) {
4614                         case IcsPlayingWhite:
4615                         case IcsPlayingBlack:
4616                         case MachinePlaysWhite:
4617                         case MachinePlaysBlack:
4618                                 strcpy(programStats.ponderMove, buf1);
4619                         break;
4620                         default:
4621                                 programStats.ponderMove[0] = NULLCHAR;
4622                         break;
4623                 }               
4624         if (cps == &first && hintRequested) {
4625             hintRequested = FALSE;
4626             if (ParseOneMove(buf1, forwardMostMove, &moveType,
4627                                  &fromX, &fromY, &toX, &toY, &promoChar)) {
4628                 (void) CoordsToAlgebraic(boards[forwardMostMove],
4629                                     PosFlags(forwardMostMove), EP_UNKNOWN,
4630                                     fromY, fromX, toY, toX, promoChar, buf1);
4631                 sprintf(buf2, "Hint: %s", buf1);
4632                 DisplayInformation(buf2);
4633             } else {
4634                 /* Hint move could not be parsed!? */
4635                 sprintf(buf2,
4636                         "Illegal hint move \"%s\"\nfrom %s chess program",
4637                         buf1, cps->which);
4638                 DisplayError(buf2, 0);
4639             }
4640         } else {
4641                 /* Copy ponder move */
4642             strcpy(lastHint, buf1);
4643         }
4644         return;
4645         }
4646
4647     /*
4648      * Ignore other messages if game is not in progress
4649      */
4650     if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
4651         gameMode == IcsIdle || cps->lastPing != cps->lastPong) return;
4652
4653     /*
4654      * look for win, lose, draw, or draw offer
4655      */
4656     if (strncmp(message, "1-0", 3) == 0) {
4657         char *p, *q, *r = "";
4658         p = strchr(message, '{');
4659         if (p) {
4660             q = strchr(p, '}');
4661             if (q) {
4662                 *q = NULLCHAR;
4663                 r = p + 1;
4664             }
4665         }
4666         GameEnds(WhiteWins, r, GE_ENGINE);
4667         return;
4668     } else if (strncmp(message, "0-1", 3) == 0) {
4669         char *p, *q, *r = "";
4670         p = strchr(message, '{');
4671         if (p) {
4672             q = strchr(p, '}');
4673             if (q) {
4674                 *q = NULLCHAR;
4675                 r = p + 1;
4676             }
4677         }
4678         /* Kludge for Arasan 4.1 bug */
4679         if (strcmp(r, "Black resigns") == 0) {
4680             GameEnds(WhiteWins, r, GE_ENGINE);
4681             return;
4682         }
4683         GameEnds(BlackWins, r, GE_ENGINE);
4684         return;
4685     } else if (strncmp(message, "1/2", 3) == 0) {
4686         char *p, *q, *r = "";
4687         p = strchr(message, '{');
4688         if (p) {
4689             q = strchr(p, '}');
4690             if (q) {
4691                 *q = NULLCHAR;
4692                 r = p + 1;
4693             }
4694         }
4695         GameEnds(GameIsDrawn, r, GE_ENGINE);
4696         return;
4697
4698     } else if (strncmp(message, "White resign", 12) == 0) {
4699         GameEnds(BlackWins, "White resigns", GE_ENGINE);
4700         return;
4701     } else if (strncmp(message, "Black resign", 12) == 0) {
4702         GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
4703         return;
4704     } else if (strncmp(message, "White", 5) == 0 &&
4705                message[5] != '(' &&
4706                StrStr(message, "Black") == NULL) {
4707         GameEnds(WhiteWins, "White mates", GE_ENGINE);
4708         return;
4709     } else if (strncmp(message, "Black", 5) == 0 &&
4710                message[5] != '(') {
4711         GameEnds(BlackWins, "Black mates", GE_ENGINE);
4712         return;
4713     } else if (strcmp(message, "resign") == 0 ||
4714                strcmp(message, "computer resigns") == 0) {
4715         switch (gameMode) {
4716           case MachinePlaysBlack:
4717           case IcsPlayingBlack:
4718             GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
4719             break;
4720           case MachinePlaysWhite:
4721           case IcsPlayingWhite:
4722             GameEnds(BlackWins, "White resigns", GE_ENGINE);
4723             break;
4724           case TwoMachinesPlay:
4725             if (cps->twoMachinesColor[0] == 'w')
4726               GameEnds(BlackWins, "White resigns", GE_ENGINE);
4727             else
4728               GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
4729             break;
4730           default:
4731             /* can't happen */
4732             break;
4733         }
4734         return;
4735     } else if (strncmp(message, "opponent mates", 14) == 0) {
4736         switch (gameMode) {
4737           case MachinePlaysBlack:
4738           case IcsPlayingBlack:
4739             GameEnds(WhiteWins, "White mates", GE_ENGINE);
4740             break;
4741           case MachinePlaysWhite:
4742           case IcsPlayingWhite:
4743             GameEnds(BlackWins, "Black mates", GE_ENGINE);
4744             break;
4745           case TwoMachinesPlay:
4746             if (cps->twoMachinesColor[0] == 'w')
4747               GameEnds(BlackWins, "Black mates", GE_ENGINE);
4748             else
4749               GameEnds(WhiteWins, "White mates", GE_ENGINE);
4750             break;
4751           default:
4752             /* can't happen */
4753             break;
4754         }
4755         return;
4756     } else if (strncmp(message, "computer mates", 14) == 0) {
4757         switch (gameMode) {
4758           case MachinePlaysBlack:
4759           case IcsPlayingBlack:
4760             GameEnds(BlackWins, "Black mates", GE_ENGINE);
4761             break;
4762           case MachinePlaysWhite:
4763           case IcsPlayingWhite:
4764             GameEnds(WhiteWins, "White mates", GE_ENGINE);
4765             break;
4766           case TwoMachinesPlay:
4767             if (cps->twoMachinesColor[0] == 'w')
4768               GameEnds(WhiteWins, "White mates", GE_ENGINE);
4769             else
4770               GameEnds(BlackWins, "Black mates", GE_ENGINE);
4771             break;
4772           default:
4773             /* can't happen */
4774             break;
4775         }
4776         return;
4777     } else if (strncmp(message, "checkmate", 9) == 0) {
4778         if (WhiteOnMove(forwardMostMove)) {
4779             GameEnds(BlackWins, "Black mates", GE_ENGINE);
4780         } else {
4781             GameEnds(WhiteWins, "White mates", GE_ENGINE);
4782         }
4783         return;
4784     } else if (strstr(message, "Draw") != NULL ||
4785                strstr(message, "game is a draw") != NULL) {
4786         GameEnds(GameIsDrawn, "Draw", GE_ENGINE);
4787         return;
4788     } else if (strstr(message, "offer") != NULL &&
4789                strstr(message, "draw") != NULL) {
4790 #if ZIPPY
4791         if (appData.zippyPlay && first.initDone) {
4792             /* Relay offer to ICS */
4793             SendToICS(ics_prefix);
4794             SendToICS("draw\n");
4795         }
4796 #endif
4797         cps->offeredDraw = 2; /* valid until this engine moves twice */
4798         if (gameMode == TwoMachinesPlay) {
4799             if (cps->other->offeredDraw) {
4800                 GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
4801             } else {
4802                 if (cps->other->sendDrawOffers) {
4803                     SendToProgram("draw\n", cps->other);
4804                 }
4805             }
4806         } else if (gameMode == MachinePlaysWhite ||
4807                    gameMode == MachinePlaysBlack) {
4808           if (userOfferedDraw) {
4809             DisplayInformation("Machine accepts your draw offer");
4810             GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
4811           } else {
4812             DisplayInformation("Machine offers a draw\nSelect Action / Draw to agree");
4813           }
4814         }
4815     }
4816
4817     
4818     /*
4819      * Look for thinking output
4820      */
4821         /* Using icsAnalyze for future function */
4822     if (appData.showThinking || appData.icsAnalyze) {
4823         int plylev, mvleft, mvtot, curscore, time;
4824         char mvname[MOVE_LEN];
4825         unsigned long nodes;
4826         char plyext;
4827         int ignore = FALSE;
4828         prefixHint = FALSE;
4829         mvname[0] = NULLCHAR;
4830
4831         /* reset thinkoutput */
4832         thinkOutput[0] = NULLCHAR;
4833
4834         switch (gameMode) {
4835           case MachinePlaysBlack:
4836           case IcsPlayingBlack:
4837             if (WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
4838             break;
4839           case MachinePlaysWhite:
4840           case IcsPlayingWhite:
4841             if (!WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
4842             break;
4843           case AnalyzeMode:
4844           case AnalyzeFile:
4845                   break;
4846           case IcsObserving:
4847                   ignore = FALSE;
4848                   break;
4849           case TwoMachinesPlay:
4850             if ((cps->twoMachinesColor[0] == 'w') !=
4851                 WhiteOnMove(forwardMostMove)) {
4852                 ignore = TRUE;
4853             }
4854             break;
4855           default:
4856             ignore = TRUE;
4857             break;
4858         }
4859         
4860         if (!ignore) {
4861                 /* reset ponder move display */
4862             buf1[0] = NULLCHAR;
4863             if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",
4864                        &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
4865
4866                 if (plyext != ' ' && plyext != '\t') {
4867                     time *= 100;
4868                 }
4869                 programStats.depth = plylev;
4870                 programStats.nodes = nodes;
4871                 programStats.time = time;
4872                 programStats.score = curscore;
4873                 programStats.got_only_move = 0;
4874
4875                 /* Buffer overflow protection */
4876                 if (buf1[0] != NULLCHAR) {
4877                         if (strlen(buf1) >= MSG_SIZ) {
4878                                 if (appData.debugMode) fprintf(debugFP, "PV is to long. I use the first %d bytes. \n", MSG_SIZ);
4879                                 strncpy(programStats.movelist, buf1, MSG_SIZ);
4880                         } else {
4881                                 strcpy(programStats.movelist, buf1);
4882                         }
4883                 } else {
4884                         sprintf(programStats.movelist, " no PV \n");
4885                         if (appData.debugMode) fprintf(debugFP, "I found no PV \n");
4886                 }
4887
4888                 if (programStats.seen_stat) {
4889                     programStats.ok_to_send = 1;
4890                 }
4891
4892                 if (strchr(programStats.movelist, '(') != NULL) {
4893                     programStats.line_is_book = 1;
4894                     programStats.nr_moves = 0;
4895                     programStats.moves_left = 0;
4896                 } else {
4897                     programStats.line_is_book = 0;
4898                 }
4899                 
4900                 sprintf(thinkOutput, "%d %c%+.2f %s%s%s",
4901                         plylev, 
4902                         (gameMode == TwoMachinesPlay ?
4903                          ToUpper(cps->twoMachinesColor[0]) : ' '),
4904                         ((double) programStats.score) / 100.0,
4905                         prefixHint ? lastHint : "",
4906                         prefixHint ? " " : "", buf1);
4907                 /* Using icsAnalyze for future function */
4908                 if (currentMove == forwardMostMove || gameMode==AnalyzeMode || 
4909                         appData.icsAnalyzeWindow || appData.AnalysisWindow || 
4910                         gameMode == AnalyzeFile) {
4911                     if (appData.icsAnalyzeWindow || 
4912                                 appData.AnalysisWindow) DisplayAnalysis(0,1);
4913                                 DisplayMove(currentMove - 1);
4914                 } else {
4915                         if (appData.showThinking) DisplayMove(currentMove - 1);
4916                 }
4917                 return;
4918
4919             } else if ((p=StrStr(message, "(only move)")) != NULL) {
4920                 /* crafty (9.25+) says "(only move) <move>"
4921                  * if there is only 1 legal move
4922                  */
4923                 sscanf(p, "(only move) %s", buf1);
4924                 sprintf(thinkOutput, "%s (only move)", buf1);
4925                 sprintf(programStats.movelist, "%s (only move)", buf1);
4926                 programStats.depth = 2; /* don't use 0 or 1 it's book depth */
4927                 programStats.nr_moves = 1;
4928                 programStats.moves_left = 1;
4929                 programStats.nodes = 1;
4930                 programStats.time = 1;
4931                 programStats.got_only_move = 1;
4932
4933                 /* Not really, but we also use this member to
4934                    mean "line isn't going to change" (Crafty
4935                    isn't searching, so stats won't change) */
4936                 programStats.line_is_book = 1;
4937         if (currentMove == forwardMostMove || gameMode==AnalyzeMode || 
4938                         appData.icsAnalyzeWindow || appData.AnalysisWindow || 
4939                         gameMode == AnalyzeFile) {
4940                     if (appData.icsAnalyzeWindow || 
4941                                 appData.AnalysisWindow) DisplayAnalysis(0,1);
4942                                 DisplayMove(currentMove - 1);
4943                 } else {
4944                         if (appData.showThinking) DisplayMove(currentMove - 1);
4945                 }
4946                 return;
4947
4948             } else if (sscanf(message,"stat01: %d %lu %d %d %d %s",
4949                               &time, &nodes, &plylev, &mvleft,
4950                               &mvtot, mvname) >= 5) {
4951                 /* The stat01: line is from Crafty (9.29+) in response
4952                    to the "." command */
4953                 programStats.seen_stat = 1;
4954                 /* for display engine room */
4955                 supportStat = 1;
4956                 cps->maybeThinking = TRUE;
4957
4958                 if (programStats.got_only_move || !appData.periodicUpdates) return;
4959
4960                 programStats.depth = plylev;
4961                 programStats.time = time;
4962                 programStats.nodes = nodes;
4963                 programStats.moves_left = mvleft;
4964                 programStats.nr_moves = mvtot;
4965                 strcpy(programStats.move_name, mvname);
4966                 programStats.ok_to_send = 1;
4967                 if (appData.icsAnalyzeWindow ||
4968                         appData.AnalysisWindow) DisplayAnalysis(0,0);
4969                 return;
4970
4971             } else if (strncmp(message,"++",2) == 0) {
4972                 /* Crafty 9.29+ outputs this */
4973                 programStats.got_fail = 2;
4974                 return;
4975
4976             } else if (strncmp(message,"--",2) == 0) {
4977                 /* Crafty 9.29+ outputs this */
4978                 programStats.got_fail = 1;
4979                 return;
4980
4981             } else if (thinkOutput[0] != NULLCHAR &&
4982                        strncmp(message, "    ", 4) == 0) {
4983                 p = message;
4984                 while (*p && *p == ' ') p++;
4985                 strcat(thinkOutput, " ");
4986                 strcat(thinkOutput, p);
4987                 strcat(programStats.movelist, " ");
4988                 strcat(programStats.movelist, p);
4989                         
4990                 if (currentMove == forwardMostMove || gameMode==AnalyzeMode || 
4991                         appData.icsAnalyzeWindow || appData.AnalysisWindow || 
4992                         gameMode == AnalyzeFile) {
4993                     if (appData.icsAnalyzeWindow || 
4994                                 appData.AnalysisWindow) DisplayAnalysis(0,1);
4995                                 DisplayMove(currentMove - 1);
4996                 } else {
4997                         if (appData.showThinking) DisplayMove(currentMove - 1);
4998                 }
4999                 return;
5000
5001             }
5002         }       
5003     }
5004 }
5005
5006
5007 /* Parse a game score from the character string "game", and
5008    record it as the history of the current game.  The game
5009    score is NOT assumed to start from the standard position. 
5010    The display is not updated in any way.
5011    */
5012 void
5013 ParseGameHistory(game)
5014      char *game;
5015 {
5016     ChessMove moveType;
5017     int fromX, fromY, toX, toY, boardIndex;
5018     char promoChar;
5019     char *p, *q;
5020     char buf[MSG_SIZ];
5021
5022     if (appData.debugMode)
5023       fprintf(debugFP, "Parsing game history: %s\n", game);
5024
5025     if (gameInfo.event == NULL) gameInfo.event = StrSave("ICS game");
5026     gameInfo.site = StrSave(appData.icsHost);
5027     gameInfo.date = PGNDate();
5028     gameInfo.round = StrSave("-");
5029
5030     /* Parse out names of players */
5031     while (*game == ' ') game++;
5032     p = buf;
5033     while (*game != ' ') *p++ = *game++;
5034     *p = NULLCHAR;
5035     gameInfo.white = StrSave(buf);
5036     while (*game == ' ') game++;
5037     p = buf;
5038     while (*game != ' ' && *game != '\n') *p++ = *game++;
5039     *p = NULLCHAR;
5040     gameInfo.black = StrSave(buf);
5041
5042     /* Parse moves */
5043     boardIndex = blackPlaysFirst ? 1 : 0;
5044     yynewstr(game);
5045     for (;;) {
5046         yyboardindex = boardIndex;
5047         moveType = (ChessMove) yylex();
5048         switch (moveType) {
5049           case WhitePromotionQueen:
5050           case BlackPromotionQueen:
5051           case WhitePromotionRook:
5052           case BlackPromotionRook:
5053           case WhitePromotionBishop:
5054           case BlackPromotionBishop:
5055           case WhitePromotionKnight:
5056           case BlackPromotionKnight:
5057           case WhitePromotionKing:
5058           case BlackPromotionKing:
5059           case NormalMove:
5060           case WhiteCapturesEnPassant:
5061           case BlackCapturesEnPassant:
5062           case WhiteKingSideCastle:
5063           case WhiteQueenSideCastle:
5064           case BlackKingSideCastle:
5065           case BlackQueenSideCastle:
5066           case WhiteKingSideCastleWild:
5067           case WhiteQueenSideCastleWild:
5068           case BlackKingSideCastleWild:
5069           case BlackQueenSideCastleWild:
5070           case IllegalMove:             /* maybe suicide chess, etc. */
5071             fromX = currentMoveString[0] - 'a';
5072             fromY = currentMoveString[1] - '1';
5073             toX = currentMoveString[2] - 'a';
5074             toY = currentMoveString[3] - '1';
5075             promoChar = currentMoveString[4];
5076             break;
5077           case WhiteDrop:
5078           case BlackDrop:
5079             fromX = moveType == WhiteDrop ?
5080               (int) CharToPiece(ToUpper(currentMoveString[0])) :
5081             (int) CharToPiece(ToLower(currentMoveString[0]));
5082             fromY = DROP_RANK;
5083             toX = currentMoveString[2] - 'a';
5084             toY = currentMoveString[3] - '1';
5085             promoChar = NULLCHAR;
5086             break;
5087           case AmbiguousMove:
5088             /* bug? */
5089             sprintf(buf, "Ambiguous move in ICS output: \"%s\"", yy_text);
5090             DisplayError(buf, 0);
5091             return;
5092           case ImpossibleMove:
5093             /* bug? */
5094             sprintf(buf, "Illegal move in ICS output: \"%s\"", yy_text);
5095             DisplayError(buf, 0);
5096             return;
5097           case (ChessMove) 0:   /* end of file */
5098             if (boardIndex < backwardMostMove) {
5099                 /* Oops, gap.  How did that happen? */
5100                 DisplayError("Gap in move list", 0);
5101                 return;
5102             }
5103             backwardMostMove =  blackPlaysFirst ? 1 : 0;
5104             if (boardIndex > forwardMostMove) {
5105                 forwardMostMove = boardIndex;
5106             }
5107             return;
5108           case ElapsedTime:
5109             if (boardIndex > 0) {
5110                 strcat(parseList[boardIndex-1], " ");
5111                 strcat(parseList[boardIndex-1], yy_text);
5112             }
5113             continue;
5114           case Comment:
5115           case PGNTag:
5116           case NAG:
5117           default:
5118             /* ignore */
5119             continue;
5120           case WhiteWins:
5121           case BlackWins:
5122           case GameIsDrawn:
5123           case GameUnfinished:
5124             if (gameMode == IcsExamining) {
5125                 if (boardIndex < backwardMostMove) {
5126                     /* Oops, gap.  How did that happen? */
5127                     return;
5128                 }
5129                 backwardMostMove = blackPlaysFirst ? 1 : 0;
5130                 return;
5131             }
5132             gameInfo.result = moveType;
5133             p = strchr(yy_text, '{');
5134             if (p == NULL) p = strchr(yy_text, '(');
5135             if (p == NULL) {
5136                 p = yy_text;
5137                 if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
5138             } else {
5139                 q = strchr(p, *p == '{' ? '}' : ')');
5140                 if (q != NULL) *q = NULLCHAR;
5141                 p++;
5142             }
5143             gameInfo.resultDetails = StrSave(p);
5144             continue;
5145         }
5146         if (boardIndex >= forwardMostMove &&
5147             !(gameMode == IcsObserving && ics_gamenum == -1)) {
5148             backwardMostMove = blackPlaysFirst ? 1 : 0;
5149             return;
5150         }
5151         (void) CoordsToAlgebraic(boards[boardIndex], PosFlags(boardIndex),
5152                                  EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,
5153                                  parseList[boardIndex]);
5154         CopyBoard(boards[boardIndex + 1], boards[boardIndex]);
5155         /* currentMoveString is set as a side-effect of yylex */
5156         strcpy(moveList[boardIndex], currentMoveString);
5157         strcat(moveList[boardIndex], "\n");
5158         boardIndex++;
5159         ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
5160         switch (MateTest(boards[boardIndex],
5161                          PosFlags(boardIndex), EP_UNKNOWN)) {
5162           case MT_NONE:
5163           case MT_STALEMATE:
5164           default:
5165             break;
5166           case MT_CHECK:
5167             strcat(parseList[boardIndex - 1], "+");
5168             break;
5169           case MT_CHECKMATE:
5170             strcat(parseList[boardIndex - 1], "#");
5171             break;
5172         }
5173     }
5174 }
5175
5176
5177 /* Apply a move to the given board  */
5178 void
5179 ApplyMove(fromX, fromY, toX, toY, promoChar, board)
5180      int fromX, fromY, toX, toY;
5181      int promoChar;
5182      Board board;
5183 {
5184     ChessSquare captured = board[toY][toX];
5185     if (fromY == DROP_RANK) {
5186         /* must be first */
5187         board[toY][toX] = (ChessSquare) fromX;
5188     } else if (fromX == toX && fromY == toY) {
5189         return;
5190     } else if (fromY == 0 && fromX == 4
5191         && board[fromY][fromX] == WhiteKing
5192         && toY == 0 && toX == 6) {
5193         board[fromY][fromX] = EmptySquare;
5194         board[toY][toX] = WhiteKing;
5195         board[fromY][7] = EmptySquare;
5196         board[toY][5] = WhiteRook;
5197     } else if (fromY == 0 && fromX == 4
5198                && board[fromY][fromX] == WhiteKing
5199                && toY == 0 && toX == 2) {
5200         board[fromY][fromX] = EmptySquare;
5201         board[toY][toX] = WhiteKing;
5202         board[fromY][0] = EmptySquare;
5203         board[toY][3] = WhiteRook;
5204     } else if (fromY == 0 && fromX == 3
5205                && board[fromY][fromX] == WhiteKing
5206                && toY == 0 && toX == 5) {
5207         board[fromY][fromX] = EmptySquare;
5208         board[toY][toX] = WhiteKing;
5209         board[fromY][7] = EmptySquare;
5210         board[toY][4] = WhiteRook;
5211     } else if (fromY == 0 && fromX == 3
5212                && board[fromY][fromX] == WhiteKing
5213                && toY == 0 && toX == 1) {
5214         board[fromY][fromX] = EmptySquare;
5215         board[toY][toX] = WhiteKing;
5216         board[fromY][0] = EmptySquare;
5217         board[toY][2] = WhiteRook;
5218     } else if (board[fromY][fromX] == WhitePawn
5219                && toY == 7) {
5220         /* white pawn promotion */
5221         board[7][toX] = CharToPiece(ToUpper(promoChar));
5222         if (board[7][toX] == EmptySquare) {
5223             board[7][toX] = WhiteQueen;
5224         }
5225         board[fromY][fromX] = EmptySquare;
5226     } else if ((fromY == 4)
5227                && (toX != fromX)
5228                && (board[fromY][fromX] == WhitePawn)
5229                && (board[toY][toX] == EmptySquare)) {
5230         board[fromY][fromX] = EmptySquare;
5231         board[toY][toX] = WhitePawn;
5232         captured = board[toY - 1][toX];
5233         board[toY - 1][toX] = EmptySquare;
5234     } else if (fromY == 7 && fromX == 4
5235                && board[fromY][fromX] == BlackKing
5236                && toY == 7 && toX == 6) {
5237         board[fromY][fromX] = EmptySquare;
5238         board[toY][toX] = BlackKing;
5239         board[fromY][7] = EmptySquare;
5240         board[toY][5] = BlackRook;
5241     } else if (fromY == 7 && fromX == 4
5242                && board[fromY][fromX] == BlackKing
5243                && toY == 7 && toX == 2) {
5244         board[fromY][fromX] = EmptySquare;
5245         board[toY][toX] = BlackKing;
5246         board[fromY][0] = EmptySquare;
5247         board[toY][3] = BlackRook;
5248     } else if (fromY == 7 && fromX == 3
5249                && board[fromY][fromX] == BlackKing
5250                && toY == 7 && toX == 5) {
5251         board[fromY][fromX] = EmptySquare;
5252         board[toY][toX] = BlackKing;
5253         board[fromY][7] = EmptySquare;
5254         board[toY][4] = BlackRook;
5255     } else if (fromY == 7 && fromX == 3
5256                && board[fromY][fromX] == BlackKing
5257                && toY == 7 && toX == 1) {
5258         board[fromY][fromX] = EmptySquare;
5259         board[toY][toX] = BlackKing;
5260         board[fromY][0] = EmptySquare;
5261         board[toY][2] = BlackRook;
5262     } else if (board[fromY][fromX] == BlackPawn
5263                && toY == 0) {
5264         /* black pawn promotion */
5265         board[0][toX] = CharToPiece(ToLower(promoChar));
5266         if (board[0][toX] == EmptySquare) {
5267             board[0][toX] = BlackQueen;
5268         }
5269         board[fromY][fromX] = EmptySquare;
5270     } else if ((fromY == 3)
5271                && (toX != fromX)
5272                && (board[fromY][fromX] == BlackPawn)
5273                && (board[toY][toX] == EmptySquare)) {
5274         board[fromY][fromX] = EmptySquare;
5275         board[toY][toX] = BlackPawn;
5276         captured = board[toY + 1][toX];
5277         board[toY + 1][toX] = EmptySquare;
5278     } else {
5279         board[toY][toX] = board[fromY][fromX];
5280         board[fromY][fromX] = EmptySquare;
5281     }
5282     if (gameInfo.variant == VariantCrazyhouse) {
5283 #if 0
5284       /* !!A lot more code needs to be written to support holdings */
5285       if (fromY == DROP_RANK) {
5286         /* Delete from holdings */
5287         if (holdings[(int) fromX] > 0) holdings[(int) fromX]--;
5288       }
5289       if (captured != EmptySquare) {
5290         /* Add to holdings */
5291         if (captured < BlackPawn) {
5292           holdings[(int)captured - (int)BlackPawn + (int)WhitePawn]++;
5293         } else {
5294           holdings[(int)captured - (int)WhitePawn + (int)BlackPawn]++;
5295         }
5296       }
5297 #endif
5298     } else if (gameInfo.variant == VariantAtomic) {
5299       if (captured != EmptySquare) {
5300         int y, x;
5301         for (y = toY-1; y <= toY+1; y++) {
5302           for (x = toX-1; x <= toX+1; x++) {
5303             if (y >= 0 && y <= 7 && x >= 0 && x <= 7 &&
5304                 board[y][x] != WhitePawn && board[y][x] != BlackPawn) {
5305               board[y][x] = EmptySquare;
5306             }
5307           }
5308         }
5309         board[toY][toX] = EmptySquare;
5310       }
5311     }
5312 }
5313
5314 /* Updates forwardMostMove */
5315 void
5316 MakeMove(fromX, fromY, toX, toY, promoChar)
5317      int fromX, fromY, toX, toY;
5318      int promoChar;
5319 {
5320     forwardMostMove++;
5321     if (forwardMostMove >= MAX_MOVES) {
5322       DisplayFatalError("Game too long; increase MAX_MOVES and recompile",
5323                         0, 1);
5324       return;
5325     }
5326     SwitchClocks();
5327     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
5328     timeRemaining[1][forwardMostMove] = blackTimeRemaining;
5329     if (commentList[forwardMostMove] != NULL) {
5330         free(commentList[forwardMostMove]);
5331         commentList[forwardMostMove] = NULL;
5332     }
5333     CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);
5334     ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]);
5335     gameInfo.result = GameUnfinished;
5336     if (gameInfo.resultDetails != NULL) {
5337         free(gameInfo.resultDetails);
5338         gameInfo.resultDetails = NULL;
5339     }
5340     CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
5341                               moveList[forwardMostMove - 1]);
5342     (void) CoordsToAlgebraic(boards[forwardMostMove - 1],
5343                              PosFlags(forwardMostMove - 1), EP_UNKNOWN,
5344                              fromY, fromX, toY, toX, promoChar,
5345                              parseList[forwardMostMove - 1]);
5346     switch (MateTest(boards[forwardMostMove],
5347                      PosFlags(forwardMostMove), EP_UNKNOWN)){
5348       case MT_NONE:
5349       case MT_STALEMATE:
5350       default:
5351         break;
5352       case MT_CHECK:
5353         strcat(parseList[forwardMostMove - 1], "+");
5354         break;
5355       case MT_CHECKMATE:
5356         strcat(parseList[forwardMostMove - 1], "#");
5357         break;
5358     }
5359 }
5360
5361 /* Updates currentMove if not pausing */
5362 void
5363 ShowMove(fromX, fromY, toX, toY)
5364 {
5365     int instant = (gameMode == PlayFromGameFile) ?
5366         (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;
5367     if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
5368         if (!instant) {
5369             if (forwardMostMove == currentMove + 1) {
5370                 AnimateMove(boards[forwardMostMove - 1],
5371                             fromX, fromY, toX, toY);
5372             }
5373             if (appData.highlightLastMove) {
5374                 SetHighlights(fromX, fromY, toX, toY);
5375             }
5376         }
5377         currentMove = forwardMostMove;
5378     }
5379
5380     if (instant) return;
5381     DisplayMove(currentMove - 1);
5382     DrawPosition(FALSE, boards[currentMove]);
5383     DisplayBothClocks();
5384     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
5385 }
5386
5387
5388 void
5389 InitChessProgram(cps)
5390      ChessProgramState *cps;
5391 {
5392     char buf[MSG_SIZ];
5393     if (appData.noChessProgram) return;
5394     hintRequested = FALSE;
5395     bookRequested = FALSE;
5396     SendToProgram(cps->initString, cps);
5397     if (gameInfo.variant != VariantNormal &&
5398         gameInfo.variant != VariantLoadable) {
5399       char *v = VariantName(gameInfo.variant);
5400       if (StrStr(cps->variants, v) == NULL) {
5401         sprintf(buf, "Variant %s not supported by %s", v, cps->tidy);
5402         DisplayFatalError(buf, 0, 1);
5403         return;
5404       }
5405       sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
5406       SendToProgram(buf, cps);
5407     }
5408     if (cps->sendICS) {
5409       sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");
5410       SendToProgram(buf, cps);
5411     }
5412     cps->maybeThinking = FALSE;
5413     cps->offeredDraw = 0;
5414     if (!appData.icsActive) {
5415         SendTimeControl(cps, movesPerSession, timeControl,
5416                         timeIncrement, appData.searchDepth,
5417                         searchTime);
5418     }
5419     if (appData.showThinking) {
5420         SendToProgram("post\n", cps);
5421     }
5422     SendToProgram("hard\n", cps);
5423     if (!appData.ponderNextMove) {
5424         /* Warning: "easy" is a toggle in GNU Chess, so don't send
5425            it without being sure what state we are in first.  "hard"
5426            is not a toggle, so that one is OK.
5427          */
5428         SendToProgram("easy\n", cps);
5429     }
5430     if (cps->usePing) {
5431       sprintf(buf, "ping %d\n", ++cps->lastPing);
5432       SendToProgram(buf, cps);
5433     }
5434     cps->initDone = TRUE;
5435 }   
5436
5437
5438 void
5439 StartChessProgram(cps)
5440      ChessProgramState *cps;
5441 {
5442     char buf[MSG_SIZ];
5443     int err;
5444
5445     if (appData.noChessProgram) return;
5446     cps->initDone = FALSE;
5447
5448     if (strcmp(cps->host, "localhost") == 0) {
5449         err = StartChildProcess(cps->program, cps->dir, &cps->pr);
5450     } else if (*appData.remoteShell == NULLCHAR) {
5451         err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);
5452     } else {
5453         if (*appData.remoteUser == NULLCHAR) {
5454             sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,
5455                     cps->program);
5456         } else {
5457             sprintf(buf, "%s %s -l %s %s", appData.remoteShell,
5458                     cps->host, appData.remoteUser, cps->program);
5459         }
5460         err = StartChildProcess(buf, "", &cps->pr);
5461     }
5462     
5463     if (err != 0) {
5464         sprintf(buf, "Startup failure on '%s'", cps->program);
5465         DisplayFatalError(buf, err, 1);
5466         cps->pr = NoProc;
5467         cps->isr = NULL;
5468         return;
5469     }
5470     
5471     cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);
5472     if (cps->protocolVersion > 1) {
5473       sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);
5474       SendToProgram(buf, cps);
5475     } else {
5476       SendToProgram("xboard\n", cps);
5477     }
5478 }
5479
5480
5481 void
5482 TwoMachinesEventIfReady P((void))
5483 {
5484   if (first.lastPing != first.lastPong) {
5485     DisplayMessage("", "Waiting for first chess program");
5486     ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
5487     return;
5488   }
5489   if (second.lastPing != second.lastPong) {
5490     DisplayMessage("", "Waiting for second chess program");
5491     ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
5492     return;
5493   }
5494   ThawUI();
5495   TwoMachinesEvent();
5496 }
5497
5498 void
5499 NextMatchGame P((void))
5500 {
5501     Reset(FALSE, TRUE);
5502     if (*appData.loadGameFile != NULLCHAR) {
5503         LoadGameFromFile(appData.loadGameFile,
5504                          appData.loadGameIndex,
5505                          appData.loadGameFile, FALSE);
5506     } else if (*appData.loadPositionFile != NULLCHAR) {
5507         LoadPositionFromFile(appData.loadPositionFile,
5508                              appData.loadPositionIndex,
5509                              appData.loadPositionFile);
5510     }
5511     TwoMachinesEventIfReady();
5512 }
5513
5514 void
5515 GameEnds(result, resultDetails, whosays)
5516      ChessMove result;
5517      char *resultDetails;
5518      int whosays;
5519 {
5520     GameMode nextGameMode;
5521     int isIcsGame;
5522
5523     if (appData.debugMode) {
5524       fprintf(debugFP, "GameEnds(%d, %s, %d)\n",
5525               result, resultDetails ? resultDetails : "(null)", whosays);
5526     }
5527
5528         if (appData.icsAnalyze && gameMode == IcsObserving) ResetIcsQueue(ics_gamenum);
5529
5530     if (appData.icsActive && whosays == GE_ENGINE) {
5531         /* If we are playing on ICS, the server decides when the
5532            game is over, but the engine can offer to draw, claim 
5533            a draw, or resign. 
5534          */
5535 #if ZIPPY
5536         if (appData.zippyPlay && first.initDone) {
5537             if (result == GameIsDrawn) {
5538                 /* In case draw still needs to be claimed */
5539                 SendToICS(ics_prefix);
5540                 SendToICS("draw\n");
5541             } else if (StrCaseStr(resultDetails, "resign")) {
5542                 SendToICS(ics_prefix);
5543                 SendToICS("resign\n");
5544             }
5545      }
5546 #endif
5547         return;
5548     }
5549
5550     /* If we're loading the game from a file, stop */
5551     if (whosays == GE_FILE) {
5552       (void) StopLoadGameTimer();
5553       gameFileFP = NULL;
5554     }
5555
5556     /* Cancel draw offers */
5557    first.offeredDraw = second.offeredDraw = 0;
5558
5559     /* If this is an ICS game, only ICS can really say it's done;
5560        if not, anyone can. */
5561     isIcsGame = (gameMode == IcsPlayingWhite || 
5562                  gameMode == IcsPlayingBlack || 
5563                  gameMode == IcsObserving    || 
5564                  gameMode == IcsExamining);
5565
5566     if (!isIcsGame || whosays == GE_ICS) {
5567         /* OK -- not an ICS game, or ICS said it was done */
5568         StopClocks();
5569         if (!isIcsGame && !appData.noChessProgram) 
5570           SetUserThinkingEnables();
5571     
5572         if (resultDetails != NULL) {
5573             gameInfo.result = result;
5574             gameInfo.resultDetails = StrSave(resultDetails);
5575
5576             /* Tell program how game ended in case it is learning */
5577             if (gameMode == MachinePlaysWhite ||
5578                 gameMode == MachinePlaysBlack ||
5579                 gameMode == TwoMachinesPlay ||
5580                 gameMode == IcsPlayingWhite ||
5581                 gameMode == IcsPlayingBlack ||
5582                 gameMode == BeginningOfGame) {
5583                 char buf[MSG_SIZ];
5584                 sprintf(buf, "result %s {%s}\n", PGNResult(result),
5585                         resultDetails);
5586                 if (first.pr != NoProc) {
5587                     SendToProgram(buf, &first);
5588                 }
5589                 if (second.pr != NoProc &&
5590                     gameMode == TwoMachinesPlay) {
5591                     SendToProgram(buf, &second);
5592                 }
5593             }
5594
5595             /* display last move only if game was not loaded from file */
5596             if ((whosays != GE_FILE) && (currentMove == forwardMostMove))
5597                 DisplayMove(currentMove - 1);
5598     
5599             if (forwardMostMove != 0) {
5600                 if (gameMode != PlayFromGameFile && gameMode != EditGame) {
5601                     if (*appData.saveGameFile != NULLCHAR) {
5602                         SaveGameToFile(appData.saveGameFile, TRUE);
5603                     } else if (appData.autoSaveGames) {
5604                         AutoSaveGame();
5605                     }
5606                     if (*appData.savePositionFile != NULLCHAR) {
5607                         SavePositionToFile(appData.savePositionFile);
5608                     }
5609                 }
5610             }
5611         }
5612
5613         if (appData.icsActive) {
5614             if (appData.quietPlay &&
5615                 (gameMode == IcsPlayingWhite ||
5616                  gameMode == IcsPlayingBlack)) {
5617                 SendToICS(ics_prefix);
5618                 SendToICS("set shout 1\n");
5619             }
5620             nextGameMode = IcsIdle;
5621             ics_user_moved = FALSE;
5622             /* clean up premove.  It's ugly when the game has ended and the
5623              * premove highlights are still on the board.
5624              */
5625             if (gotPremove) {
5626               gotPremove = FALSE;
5627               ClearPremoveHighlights();
5628               DrawPosition(FALSE, boards[currentMove]);
5629             }
5630             if (whosays == GE_ICS) {
5631                 switch (result) {
5632                 case WhiteWins:
5633                     if (gameMode == IcsPlayingWhite)
5634                         PlayIcsWinSound();
5635                     else if(gameMode == IcsPlayingBlack)
5636                         PlayIcsLossSound();
5637                     break;
5638                 case BlackWins:
5639                     if (gameMode == IcsPlayingBlack)
5640                         PlayIcsWinSound();
5641                     else if(gameMode == IcsPlayingWhite)
5642                         PlayIcsLossSound();
5643                     break;
5644                 case GameIsDrawn:
5645                     PlayIcsDrawSound();
5646                     break;
5647                 default:
5648                     PlayIcsUnfinishedSound();
5649                 }
5650             }
5651         } else if (gameMode == EditGame ||
5652                    gameMode == PlayFromGameFile || 
5653                    gameMode == AnalyzeMode || 
5654                    gameMode == AnalyzeFile) {
5655             nextGameMode = gameMode;
5656         } else {
5657             nextGameMode = EndOfGame;
5658         }
5659         pausing = FALSE;
5660         ModeHighlight();
5661     } else {
5662         nextGameMode = gameMode;
5663     }
5664
5665     if (appData.noChessProgram) {
5666         gameMode = nextGameMode;
5667         ModeHighlight();
5668         return;
5669     }
5670
5671     if (first.reuse) {
5672         /* Put first chess program into idle state */
5673         if (first.pr != NoProc &&
5674             (gameMode == MachinePlaysWhite ||
5675              gameMode == MachinePlaysBlack ||
5676              gameMode == TwoMachinesPlay ||
5677              gameMode == IcsPlayingWhite ||
5678              gameMode == IcsPlayingBlack ||
5679              gameMode == BeginningOfGame)) {
5680             SendToProgram("force\n", &first);
5681             if (first.usePing) {
5682               char buf[MSG_SIZ];
5683               sprintf(buf, "ping %d\n", ++first.lastPing);
5684               SendToProgram(buf, &first);
5685             }
5686         }
5687     } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
5688         /* Kill off first chess program */
5689         if (first.isr != NULL)
5690           RemoveInputSource(first.isr);
5691         first.isr = NULL;
5692     
5693         if (first.pr != NoProc) {
5694             ExitAnalyzeMode();
5695             SendToProgram("quit\n", &first);
5696             DestroyChildProcess(first.pr, first.useSigterm);
5697         }
5698         first.pr = NoProc;
5699     }
5700     if (second.reuse) {
5701         /* Put second chess program into idle state */
5702         if (second.pr != NoProc &&
5703             gameMode == TwoMachinesPlay) {
5704             SendToProgram("force\n", &second);
5705             if (second.usePing) {
5706               char buf[MSG_SIZ];
5707               sprintf(buf, "ping %d\n", ++second.lastPing);
5708               SendToProgram(buf, &second);
5709             }
5710         }
5711     } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
5712         /* Kill off second chess program */
5713         if (second.isr != NULL)
5714           RemoveInputSource(second.isr);
5715         second.isr = NULL;
5716     
5717         if (second.pr != NoProc) {
5718             SendToProgram("quit\n", &second);
5719             DestroyChildProcess(second.pr, second.useSigterm);
5720         }
5721         second.pr = NoProc;
5722     }
5723
5724     if (matchMode && gameMode == TwoMachinesPlay) {
5725         switch (result) {
5726         case WhiteWins:
5727           if (first.twoMachinesColor[0] == 'w') {
5728             first.matchWins++;
5729           } else {
5730             second.matchWins++;
5731           }
5732           break;
5733         case BlackWins:
5734           if (first.twoMachinesColor[0] == 'b') {
5735             first.matchWins++;
5736           } else {
5737             second.matchWins++;
5738           }
5739           break;
5740         default:
5741           break;
5742         }
5743         if (matchGame < appData.matchGames) {
5744             char *tmp;
5745             tmp = first.twoMachinesColor;
5746             first.twoMachinesColor = second.twoMachinesColor;
5747             second.twoMachinesColor = tmp;
5748             gameMode = nextGameMode;
5749             matchGame++;
5750             ScheduleDelayedEvent(NextMatchGame, 10000);
5751             return;
5752         } else {
5753             char buf[MSG_SIZ];
5754             gameMode = nextGameMode;
5755             sprintf(buf, "Match %s vs. %s: final score %d-%d-%d",
5756                     first.tidy, second.tidy,
5757                     first.matchWins, second.matchWins,
5758                     appData.matchGames - (first.matchWins + second.matchWins));
5759             DisplayFatalError(buf, 0, 0);
5760         }
5761     }
5762     if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
5763         !(nextGameMode == AnalyzeMode || nextGameMode == AnalyzeFile))
5764       ExitAnalyzeMode();
5765     gameMode = nextGameMode;
5766     ModeHighlight();
5767 }
5768
5769 /* Assumes program was just initialized (initString sent).
5770    Leaves program in force mode. */
5771 void
5772 FeedMovesToProgram(cps, upto) 
5773      ChessProgramState *cps;
5774      int upto;
5775          
5776 {
5777     int i;
5778     if (appData.debugMode)
5779       fprintf(debugFP, "Feeding %smoves %d through %d to %s chess program\n",
5780               startedFromSetupPosition ? "position and " : "",
5781               backwardMostMove, upto, cps->which);
5782         /* daniel */
5783     if(!appData.icsAnalyze) SendToProgram("force\n", cps);
5784     if (startedFromSetupPosition) {
5785         SendBoard(cps, backwardMostMove);
5786     }
5787     for (i = backwardMostMove; i < upto; i++) {
5788         
5789         SendMoveToProgram(i, cps);
5790     }
5791 }
5792
5793
5794 void
5795 ResurrectChessProgram()
5796 {
5797      /* The chess program may have exited.
5798         If so, restart it and feed it all the moves made so far. */
5799
5800     if (appData.noChessProgram || first.pr != NoProc) return;
5801     
5802     StartChessProgram(&first);
5803     InitChessProgram(&first);
5804     FeedMovesToProgram(&first, currentMove);
5805
5806     if (!first.sendTime) {
5807         /* can't tell gnuchess what its clock should read,
5808            so we bow to its notion. */
5809         ResetClocks();
5810         timeRemaining[0][currentMove] = whiteTimeRemaining;
5811         timeRemaining[1][currentMove] = blackTimeRemaining;
5812     }
5813
5814     if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
5815         first.analysisSupport) {
5816       SendToProgram("analyze\n", &first);
5817       first.analyzing = TRUE;
5818     }
5819 }
5820
5821 /*
5822  * Button procedures
5823  */
5824 void
5825 Reset(redraw, init)
5826      int redraw, init;
5827 {
5828     int i;
5829
5830     if (appData.debugMode) {
5831         fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",
5832                 redraw, init, gameMode);
5833     }
5834
5835         /* reset send engine output to ics */
5836         appData.SendOutPutToICS = 1;
5837         
5838     pausing = pauseExamInvalid = FALSE;
5839     startedFromSetupPosition = blackPlaysFirst = FALSE;
5840     firstMove = TRUE;
5841     whiteFlag = blackFlag = FALSE;
5842     userOfferedDraw = FALSE;
5843     hintRequested = bookRequested = FALSE;
5844     first.maybeThinking = FALSE;
5845     second.maybeThinking = FALSE;
5846     thinkOutput[0] = NULLCHAR;
5847     lastHint[0] = NULLCHAR;
5848     ClearGameInfo(&gameInfo);
5849     gameInfo.variant = StringToVariant(appData.variant);
5850     ics_user_moved = ics_clock_paused = FALSE;
5851     ics_getting_history = H_FALSE;
5852     ics_gamenum = -1;
5853     white_holding[0] = black_holding[0] = NULLCHAR;
5854     ClearProgramStats();
5855     
5856     ResetFrontEnd();
5857     ClearHighlights();
5858     flipView = appData.flipView;
5859     ClearPremoveHighlights();
5860     gotPremove = FALSE;
5861     alarmSounded = FALSE;
5862
5863     GameEnds((ChessMove) 0, NULL, GE_PLAYER);
5864     ExitAnalyzeMode();
5865     gameMode = BeginningOfGame;
5866     ModeHighlight();
5867     InitPosition(redraw);
5868     for (i = 0; i < MAX_MOVES; i++) {
5869         if (commentList[i] != NULL) {
5870             free(commentList[i]);
5871             commentList[i] = NULL;
5872         }
5873     }
5874     ResetClocks();
5875     timeRemaining[0][0] = whiteTimeRemaining;
5876     timeRemaining[1][0] = blackTimeRemaining;
5877     if (first.pr == NULL) {
5878         StartChessProgram(&first);
5879     }
5880     if (init) InitChessProgram(&first);
5881     DisplayTitle("");
5882     DisplayMessage("", "");
5883     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
5884 }
5885
5886 void
5887 AutoPlayGameLoop()
5888 {
5889     for (;;) {
5890         if (!AutoPlayOneMove())
5891           return;
5892         if (matchMode || appData.timeDelay == 0)
5893           continue;
5894         if (appData.timeDelay < 0 || gameMode == AnalyzeFile)
5895           return;
5896         StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
5897         break;
5898     }
5899 }
5900
5901
5902 int
5903 AutoPlayOneMove()
5904 {
5905     int fromX, fromY, toX, toY;
5906
5907     if (appData.debugMode) {
5908       fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove);
5909     }
5910
5911     if (gameMode != PlayFromGameFile)
5912       return FALSE;
5913
5914     if (currentMove >= forwardMostMove) {
5915       gameMode = EditGame;
5916       ModeHighlight();
5917       return FALSE;
5918     }
5919     
5920     toX = moveList[currentMove][2] - 'a';
5921     toY = moveList[currentMove][3] - '1';
5922
5923     if (moveList[currentMove][1] == '@') {
5924         if (appData.highlightLastMove) {
5925             SetHighlights(-1, -1, toX, toY);
5926         }
5927     } else {
5928         fromX = moveList[currentMove][0] - 'a';
5929         fromY = moveList[currentMove][1] - '1';
5930         AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
5931
5932         if (appData.highlightLastMove) {
5933             SetHighlights(fromX, fromY, toX, toY);
5934         }
5935     }
5936     DisplayMove(currentMove);
5937     SendMoveToProgram(currentMove++, &first);
5938     DisplayBothClocks();
5939     DrawPosition(FALSE, boards[currentMove]);
5940     if (commentList[currentMove] != NULL) {
5941         DisplayComment(currentMove - 1, commentList[currentMove]);
5942     }
5943     return TRUE;
5944 }
5945
5946
5947 int
5948 LoadGameOneMove(readAhead)
5949      ChessMove readAhead;
5950 {
5951     int fromX = 0, fromY = 0, toX = 0, toY = 0, done;
5952     char promoChar = NULLCHAR;
5953     ChessMove moveType;
5954     char move[MSG_SIZ];
5955     char *p, *q;
5956     
5957     if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile && 
5958         gameMode != AnalyzeMode && gameMode != Training) {
5959         gameFileFP = NULL;
5960         return FALSE;
5961     }
5962     
5963     yyboardindex = forwardMostMove;
5964     if (readAhead != (ChessMove)0) {
5965       moveType = readAhead;
5966     } else {
5967       if (gameFileFP == NULL)
5968           return FALSE;
5969       moveType = (ChessMove) yylex();
5970     }
5971     
5972     done = FALSE;
5973     switch (moveType) {
5974       case Comment:
5975         if (appData.debugMode) 
5976           fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
5977         p = yy_text;
5978         if (*p == '{' || *p == '[' || *p == '(') {
5979             p[strlen(p) - 1] = NULLCHAR;
5980             p++;
5981         }
5982
5983         /* append the comment but don't display it */
5984         while (*p == '\n') p++;
5985         AppendComment(currentMove, p);
5986         return TRUE;
5987
5988       case WhiteCapturesEnPassant:
5989       case BlackCapturesEnPassant:
5990       case WhitePromotionQueen:
5991       case BlackPromotionQueen:
5992       case WhitePromotionRook:
5993       case BlackPromotionRook:
5994       case WhitePromotionBishop:
5995       case BlackPromotionBishop:
5996       case WhitePromotionKnight:
5997       case BlackPromotionKnight:
5998       case WhitePromotionKing:
5999       case BlackPromotionKing:
6000       case NormalMove:
6001       case WhiteKingSideCastle:
6002       case WhiteQueenSideCastle:
6003       case BlackKingSideCastle:
6004       case BlackQueenSideCastle:
6005       case WhiteKingSideCastleWild:
6006       case WhiteQueenSideCastleWild:
6007       case BlackKingSideCastleWild:
6008       case BlackQueenSideCastleWild:
6009         if (appData.debugMode)
6010           fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
6011         fromX = currentMoveString[0] - 'a';
6012         fromY = currentMoveString[1] - '1';
6013         toX = currentMoveString[2] - 'a';
6014         toY = currentMoveString[3] - '1';
6015         promoChar = currentMoveString[4];
6016         break;
6017
6018       case WhiteDrop:
6019       case BlackDrop:
6020         if (appData.debugMode)
6021           fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
6022         fromX = moveType == WhiteDrop ?
6023           (int) CharToPiece(ToUpper(currentMoveString[0])) :
6024         (int) CharToPiece(ToLower(currentMoveString[0]));
6025         fromY = DROP_RANK;
6026         toX = currentMoveString[2] - 'a';
6027         toY = currentMoveString[3] - '1';
6028         break;
6029
6030       case WhiteWins:
6031       case BlackWins:
6032       case GameIsDrawn:
6033       case GameUnfinished:
6034         if (appData.debugMode)
6035           fprintf(debugFP, "Parsed game end: %s\n", yy_text);
6036         p = strchr(yy_text, '{');
6037         if (p == NULL) p = strchr(yy_text, '(');
6038         if (p == NULL) {
6039             p = yy_text;
6040             if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
6041         } else {
6042             q = strchr(p, *p == '{' ? '}' : ')');
6043             if (q != NULL) *q = NULLCHAR;
6044             p++;
6045         }
6046         GameEnds(moveType, p, GE_FILE);
6047         done = TRUE;
6048         if (cmailMsgLoaded) {
6049             ClearHighlights();
6050             flipView = WhiteOnMove(currentMove);
6051             if (moveType == GameUnfinished) flipView = !flipView;
6052             if (appData.debugMode)
6053               fprintf(debugFP, "Setting flipView to %d\n", flipView) ;
6054         }
6055         break;
6056
6057       case (ChessMove) 0:       /* end of file */
6058         if (appData.debugMode)
6059           fprintf(debugFP, "Parser hit end of file\n");
6060         switch (MateTest(boards[currentMove], PosFlags(currentMove),
6061                          EP_UNKNOWN)) {
6062           case MT_NONE:
6063           case MT_CHECK:
6064             break;
6065           case MT_CHECKMATE:
6066             if (WhiteOnMove(currentMove)) {
6067                 GameEnds(BlackWins, "Black mates", GE_FILE);
6068             } else {
6069                 GameEnds(WhiteWins, "White mates", GE_FILE);
6070             }
6071             break;
6072           case MT_STALEMATE:
6073             GameEnds(GameIsDrawn, "Stalemate", GE_FILE);
6074             break;
6075         }
6076         done = TRUE;
6077         break;
6078
6079       case MoveNumberOne:
6080         if (lastLoadGameStart == GNUChessGame) {
6081             /* GNUChessGames have numbers, but they aren't move numbers */
6082             if (appData.debugMode)
6083               fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
6084                       yy_text, (int) moveType);
6085             return LoadGameOneMove((ChessMove)0); /* tail recursion */
6086         }
6087         /* else fall thru */
6088
6089       case XBoardGame:
6090       case GNUChessGame:
6091       case PGNTag:
6092         /* Reached start of next game in file */
6093         if (appData.debugMode)
6094           fprintf(debugFP, "Parsed start of next game: %s\n", yy_text);
6095         switch (MateTest(boards[currentMove], PosFlags(currentMove),
6096                          EP_UNKNOWN)) {
6097           case MT_NONE:
6098           case MT_CHECK:
6099             break;
6100           case MT_CHECKMATE:
6101             if (WhiteOnMove(currentMove)) {
6102                 GameEnds(BlackWins, "Black mates", GE_FILE);
6103             } else {
6104                 GameEnds(WhiteWins, "White mates", GE_FILE);
6105             }
6106             break;
6107           case MT_STALEMATE:
6108             GameEnds(GameIsDrawn, "Stalemate", GE_FILE);
6109             break;
6110         }
6111         done = TRUE;
6112         break;
6113
6114       case PositionDiagram:     /* should not happen; ignore */
6115       case ElapsedTime:         /* ignore */
6116       case NAG:                 /* ignore */
6117         if (appData.debugMode)
6118           fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
6119                   yy_text, (int) moveType);
6120         return LoadGameOneMove((ChessMove)0); /* tail recursion */
6121
6122       case IllegalMove:
6123         if (appData.testLegality) {
6124             if (appData.debugMode)
6125               fprintf(debugFP, "Parsed IllegalMove: %s\n", yy_text);
6126             sprintf(move, "Illegal move: %d.%s%s",
6127                     (forwardMostMove / 2) + 1,
6128                     WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
6129             DisplayError(move, 0);
6130             done = TRUE;
6131         } else {
6132             if (appData.debugMode)
6133               fprintf(debugFP, "Parsed %s into IllegalMove %s\n",
6134                       yy_text, currentMoveString);
6135             fromX = currentMoveString[0] - 'a';
6136             fromY = currentMoveString[1] - '1';
6137             toX = currentMoveString[2] - 'a';
6138             toY = currentMoveString[3] - '1';
6139             promoChar = currentMoveString[4];
6140         }
6141         break;
6142
6143       case AmbiguousMove:
6144         if (appData.debugMode)
6145           fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yy_text);
6146         sprintf(move, "Ambiguous move: %d.%s%s",
6147                 (forwardMostMove / 2) + 1,
6148                 WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
6149         DisplayError(move, 0);
6150         done = TRUE;
6151         break;
6152
6153       default:
6154       case ImpossibleMove:
6155         if (appData.debugMode)
6156           fprintf(debugFP, "Parsed ImpossibleMove: %s\n", yy_text);
6157         sprintf(move, "Illegal move: %d.%s%s",
6158                 (forwardMostMove / 2) + 1,
6159                 WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
6160         DisplayError(move, 0);
6161         done = TRUE;
6162         break;
6163     }
6164
6165     if (done) {
6166         if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {
6167             DrawPosition(FALSE, boards[currentMove]);
6168             DisplayBothClocks();
6169             if (!appData.matchMode && commentList[currentMove] != NULL)
6170               DisplayComment(currentMove - 1, commentList[currentMove]);
6171         }
6172         (void) StopLoadGameTimer();
6173         gameFileFP = NULL;
6174         cmailOldMove = forwardMostMove;
6175         return FALSE;
6176     } else {
6177         /* currentMoveString is set as a side-effect of yylex */
6178         strcat(currentMoveString, "\n");
6179         strcpy(moveList[forwardMostMove], currentMoveString);
6180         
6181         thinkOutput[0] = NULLCHAR;
6182         MakeMove(fromX, fromY, toX, toY, promoChar);
6183         currentMove = forwardMostMove;
6184         return TRUE;
6185     }
6186 }
6187
6188 /* Load the nth game from the given file */
6189 int
6190 LoadGameFromFile(filename, n, title, useList)
6191      char *filename;
6192      int n;
6193      char *title;
6194      /*Boolean*/ int useList;
6195 {
6196     FILE *f;
6197     char buf[MSG_SIZ];
6198
6199     if (strcmp(filename, "-") == 0) {
6200         f = stdin;
6201         title = "stdin";
6202     } else {
6203         f = fopen(filename, "rb");
6204         if (f == NULL) {
6205             sprintf(buf, "Can't open \"%s\"", filename);
6206             DisplayError(buf, errno);
6207             return FALSE;
6208         }
6209     }
6210     if (fseek(f, 0, 0) == -1) {
6211         /* f is not seekable; probably a pipe */
6212         useList = FALSE;
6213     }
6214     if (useList && n == 0) {
6215         int error = GameListBuild(f);
6216         if (error) {
6217             DisplayError("Cannot build game list", error);
6218         } else if (!ListEmpty(&gameList) &&
6219                    ((ListGame *) gameList.tailPred)->number > 1) {
6220             GameListPopUp(f, title);
6221             return TRUE;
6222         }
6223         GameListDestroy();
6224         n = 1;
6225     }
6226     if (n == 0) n = 1;
6227     return LoadGame(f, n, title, FALSE);
6228 }
6229
6230
6231 void
6232 MakeRegisteredMove()
6233 {
6234     int fromX, fromY, toX, toY;
6235     char promoChar;
6236     if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
6237         switch (cmailMoveType[lastLoadGameNumber - 1]) {
6238           case CMAIL_MOVE:
6239           case CMAIL_DRAW:
6240             if (appData.debugMode)
6241               fprintf(debugFP, "Restoring %s for game %d\n",
6242                       cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
6243     
6244             thinkOutput[0] = NULLCHAR;
6245             strcpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1]);
6246             fromX = cmailMove[lastLoadGameNumber - 1][0] - 'a';
6247             fromY = cmailMove[lastLoadGameNumber - 1][1] - '1';
6248             toX = cmailMove[lastLoadGameNumber - 1][2] - 'a';
6249             toY = cmailMove[lastLoadGameNumber - 1][3] - '1';
6250             promoChar = cmailMove[lastLoadGameNumber - 1][4];
6251             MakeMove(fromX, fromY, toX, toY, promoChar);
6252             ShowMove(fromX, fromY, toX, toY);
6253               
6254             switch (MateTest(boards[currentMove], PosFlags(currentMove),
6255                              EP_UNKNOWN)) {
6256               case MT_NONE:
6257               case MT_CHECK:
6258                 break;
6259                 
6260               case MT_CHECKMATE:
6261                 if (WhiteOnMove(currentMove)) {
6262                     GameEnds(BlackWins, "Black mates", GE_PLAYER);
6263                 } else {
6264                     GameEnds(WhiteWins, "White mates", GE_PLAYER);
6265                 }
6266                 break;
6267                 
6268               case MT_STALEMATE:
6269                 GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
6270                 break;
6271             }
6272
6273             break;
6274             
6275           case CMAIL_RESIGN:
6276             if (WhiteOnMove(currentMove)) {
6277                 GameEnds(BlackWins, "White resigns", GE_PLAYER);
6278             } else {
6279                 GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
6280             }
6281             break;
6282             
6283           case CMAIL_ACCEPT:
6284             GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
6285             break;
6286               
6287           default:
6288             break;
6289         }
6290     }
6291
6292     return;
6293 }
6294
6295 /* Wrapper around LoadGame for use when a Cmail message is loaded */
6296 int
6297 CmailLoadGame(f, gameNumber, title, useList)
6298      FILE *f;
6299      int gameNumber;
6300      char *title;
6301      int useList;
6302 {
6303     int retVal;
6304
6305     if (gameNumber > nCmailGames) {
6306         DisplayError("No more games in this message", 0);
6307         return FALSE;
6308     }
6309     if (f == lastLoadGameFP) {
6310         int offset = gameNumber - lastLoadGameNumber;
6311         if (offset == 0) {
6312             cmailMsg[0] = NULLCHAR;
6313             if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
6314                 cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
6315                 nCmailMovesRegistered--;
6316             }
6317             cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
6318             if (cmailResult[lastLoadGameNumber - 1] == CMAIL_NEW_RESULT) {
6319                 cmailResult[lastLoadGameNumber - 1] = CMAIL_NOT_RESULT;
6320             }
6321         } else {
6322             if (! RegisterMove()) return FALSE;
6323         }
6324     }
6325
6326     retVal = LoadGame(f, gameNumber, title, useList);
6327
6328     /* Make move registered during previous look at this game, if any */
6329     MakeRegisteredMove();
6330
6331     if (cmailCommentList[lastLoadGameNumber - 1] != NULL) {
6332         commentList[currentMove]
6333           = StrSave(cmailCommentList[lastLoadGameNumber - 1]);
6334         DisplayComment(currentMove - 1, commentList[currentMove]);
6335     }
6336
6337     return retVal;
6338 }
6339
6340 /* Support for LoadNextGame, LoadPreviousGame, ReloadSameGame */
6341 int
6342 ReloadGame(offset)
6343      int offset;
6344 {
6345     int gameNumber = lastLoadGameNumber + offset;
6346     if (lastLoadGameFP == NULL) {
6347         DisplayError("No game has been loaded yet", 0);
6348         return FALSE;
6349     }
6350     if (gameNumber <= 0) {
6351         DisplayError("Can't back up any further", 0);
6352         return FALSE;
6353     }
6354     if (cmailMsgLoaded) {
6355         return CmailLoadGame(lastLoadGameFP, gameNumber,
6356                              lastLoadGameTitle, lastLoadGameUseList);
6357     } else {
6358         return LoadGame(lastLoadGameFP, gameNumber,
6359                         lastLoadGameTitle, lastLoadGameUseList);
6360     }
6361 }
6362
6363
6364
6365 /* Load the nth game from open file f */
6366 int
6367 LoadGame(f, gameNumber, title, useList)
6368      FILE *f;
6369      int gameNumber;
6370      char *title;
6371      int useList;
6372 {
6373     ChessMove cm;
6374     char buf[MSG_SIZ];
6375     int gn = gameNumber;
6376     ListGame *lg = NULL;
6377     int numPGNTags = 0;
6378     int err;
6379     GameMode oldGameMode;
6380
6381     if (appData.debugMode) 
6382         fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);
6383
6384     if (gameMode == Training )
6385         SetTrainingModeOff();
6386
6387     oldGameMode = gameMode;
6388     if (gameMode != BeginningOfGame) {
6389       Reset(FALSE, TRUE);
6390     }
6391
6392     gameFileFP = f;
6393     if (lastLoadGameFP != NULL && lastLoadGameFP != f) {
6394         fclose(lastLoadGameFP);
6395     }
6396
6397     if (useList) {
6398         lg = (ListGame *) ListElem(&gameList, gameNumber-1);
6399         
6400         if (lg) {
6401             fseek(f, lg->offset, 0);
6402             GameListHighlight(gameNumber);
6403             gn = 1;
6404         }
6405         else {
6406             DisplayError("Game number out of range", 0);
6407             return FALSE;
6408         }
6409     } else {
6410         GameListDestroy();
6411         if (fseek(f, 0, 0) == -1) {
6412             if (f == lastLoadGameFP ?
6413                 gameNumber == lastLoadGameNumber + 1 :
6414                 gameNumber == 1) {
6415                 gn = 1;
6416             } else {
6417                 DisplayError("Can't seek on game file", 0);
6418                 return FALSE;
6419             }
6420         }
6421     }
6422     lastLoadGameFP = f;
6423     lastLoadGameNumber = gameNumber;
6424     strcpy(lastLoadGameTitle, title);
6425     lastLoadGameUseList = useList;
6426
6427     yynewfile(f);
6428
6429
6430     if (lg && lg->gameInfo.white && lg->gameInfo.black) {
6431         sprintf(buf, "%s vs. %s", lg->gameInfo.white,
6432                 lg->gameInfo.black);
6433             DisplayTitle(buf);
6434     } else if (*title != NULLCHAR) {
6435         if (gameNumber > 1) {
6436             sprintf(buf, "%s %d", title, gameNumber);
6437             DisplayTitle(buf);
6438         } else {
6439             DisplayTitle(title);
6440         }
6441     }
6442
6443     if (gameMode != AnalyzeFile && gameMode != AnalyzeMode) {
6444         gameMode = PlayFromGameFile;
6445         ModeHighlight();
6446     }
6447
6448     currentMove = forwardMostMove = backwardMostMove = 0;
6449     CopyBoard(boards[0], initialPosition);
6450     StopClocks();
6451
6452     /*
6453      * Skip the first gn-1 games in the file.
6454      * Also skip over anything that precedes an identifiable 
6455      * start of game marker, to avoid being confused by 
6456      * garbage at the start of the file.  Currently 
6457      * recognized start of game markers are the move number "1",
6458      * the pattern "gnuchess .* game", the pattern
6459      * "^[#;%] [^ ]* game file", and a PGN tag block.  
6460      * A game that starts with one of the latter two patterns
6461      * will also have a move number 1, possibly
6462      * following a position diagram.
6463      */
6464     cm = lastLoadGameStart = (ChessMove) 0;
6465     yyskipmoves = TRUE;
6466     while (gn > 0) {
6467         yyboardindex = forwardMostMove;
6468         cm = (ChessMove) yylex();
6469         yyskipmoves = FALSE;
6470         switch (cm) {
6471           case (ChessMove) 0:
6472             if (cmailMsgLoaded) {
6473                 nCmailGames = CMAIL_MAX_GAMES - gn;
6474             } else {
6475                 Reset(TRUE, TRUE);
6476                 DisplayError("Game not found in file", 0);
6477             }
6478             yyskipmoves = FALSE;
6479             return FALSE;
6480
6481           case GNUChessGame:
6482           case XBoardGame:
6483             gn--;
6484             lastLoadGameStart = cm;
6485             break;
6486             
6487           case MoveNumberOne:
6488             switch (lastLoadGameStart) {
6489               case GNUChessGame:
6490               case XBoardGame:
6491               case PGNTag:
6492                 break;
6493               case MoveNumberOne:
6494               case (ChessMove) 0:
6495                 gn--;           /* count this game */
6496                 lastLoadGameStart = cm;
6497                 break;
6498               default:
6499                 /* impossible */
6500                 break;
6501             }
6502             break;
6503
6504           case PGNTag:
6505             switch (lastLoadGameStart) {
6506               case GNUChessGame:
6507               case PGNTag:
6508               case MoveNumberOne:
6509               case (ChessMove) 0:
6510                 gn--;           /* count this game */
6511                 lastLoadGameStart = cm;
6512                 break;
6513               case XBoardGame:
6514                 lastLoadGameStart = cm; /* game counted already */
6515                 break;
6516               default:
6517                 /* impossible */
6518                 break;
6519             }
6520             if (gn > 0) {
6521                 do {
6522                     yyboardindex = forwardMostMove;
6523                     cm = (ChessMove) yylex();
6524                 } while (cm == PGNTag || cm == Comment);
6525             }
6526             break;
6527
6528           case WhiteWins:
6529           case BlackWins:
6530           case GameIsDrawn:
6531             if (cmailMsgLoaded && (CMAIL_MAX_GAMES == lastLoadGameNumber)) {
6532                 if (   cmailResult[CMAIL_MAX_GAMES - gn - 1]
6533                     != CMAIL_OLD_RESULT) {
6534                     nCmailResults ++ ;
6535                     cmailResult[  CMAIL_MAX_GAMES
6536                                 - gn - 1] = CMAIL_OLD_RESULT;
6537                 }
6538             }
6539             break;
6540
6541           default:
6542             break;
6543         }
6544     }
6545     yyskipmoves = FALSE;
6546     
6547     if (appData.debugMode)
6548       fprintf(debugFP, "Parsed game start '%s' (%d)\n", yy_text, (int) cm);
6549
6550     if (cm == XBoardGame) {
6551         /* Skip any header junk before position diagram and/or move 1 */
6552         for (;;) {
6553             yyboardindex = forwardMostMove;
6554             cm = (ChessMove) yylex();
6555
6556             if (cm == (ChessMove) 0 ||
6557                 cm == GNUChessGame || cm == XBoardGame) {
6558                 /* Empty game; pretend end-of-file and handle later */
6559                 cm = (ChessMove) 0;
6560                 break;
6561             }
6562
6563             if (cm == MoveNumberOne || cm == PositionDiagram ||
6564                 cm == PGNTag || cm == Comment)
6565               break;
6566         }
6567     } else if (cm == GNUChessGame) {
6568         if (gameInfo.event != NULL) {
6569             free(gameInfo.event);
6570         }
6571         gameInfo.event = StrSave(yy_text);
6572     }   
6573
6574     startedFromSetupPosition = FALSE;
6575     while (cm == PGNTag) {
6576         if (appData.debugMode) 
6577           fprintf(debugFP, "Parsed PGNTag: %s\n", yy_text);
6578         err = ParsePGNTag(yy_text, &gameInfo);
6579         if (!err) numPGNTags++;
6580
6581         if (gameInfo.fen != NULL) {
6582           Board initial_position;
6583           startedFromSetupPosition = TRUE;
6584           if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) {
6585             Reset(TRUE, TRUE);
6586             DisplayError("Bad FEN position in file", 0);
6587             return FALSE;
6588           }
6589           CopyBoard(boards[0], initial_position);
6590           if (blackPlaysFirst) {
6591             currentMove = forwardMostMove = backwardMostMove = 1;
6592             CopyBoard(boards[1], initial_position);
6593             strcpy(moveList[0], "");
6594             strcpy(parseList[0], "");
6595             timeRemaining[0][1] = whiteTimeRemaining;
6596             timeRemaining[1][1] = blackTimeRemaining;
6597             if (commentList[0] != NULL) {
6598               commentList[1] = commentList[0];
6599               commentList[0] = NULL;
6600             }
6601           } else {
6602             currentMove = forwardMostMove = backwardMostMove = 0;
6603           }
6604           yyboardindex = forwardMostMove;
6605           free(gameInfo.fen);
6606           gameInfo.fen = NULL;
6607         }
6608
6609         yyboardindex = forwardMostMove;
6610         cm = (ChessMove) yylex();
6611
6612         /* Handle comments interspersed among the tags */
6613         while (cm == Comment) {
6614             char *p;
6615             if (appData.debugMode) 
6616               fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
6617             p = yy_text;
6618             if (*p == '{' || *p == '[' || *p == '(') {
6619                 p[strlen(p) - 1] = NULLCHAR;
6620                 p++;
6621             }
6622             while (*p == '\n') p++;
6623             AppendComment(currentMove, p);
6624             yyboardindex = forwardMostMove;
6625             cm = (ChessMove) yylex();
6626         }
6627     }
6628
6629     /* don't rely on existence of Event tag since if game was
6630      * pasted from clipboard the Event tag may not exist
6631      */
6632     if (numPGNTags > 0){
6633         char *tags;
6634         if (gameInfo.variant == VariantNormal) {
6635           gameInfo.variant = StringToVariant(gameInfo.event);
6636         }
6637         if (!matchMode) {
6638           tags = PGNTags(&gameInfo);
6639           TagsPopUp(tags, CmailMsg());
6640           free(tags);
6641         }
6642     } else {
6643         /* Make something up, but don't display it now */
6644         SetGameInfo();
6645         TagsPopDown();
6646     }
6647
6648     if (cm == PositionDiagram) {
6649         int i, j;
6650         char *p;
6651         Board initial_position;
6652
6653         if (appData.debugMode)
6654           fprintf(debugFP, "Parsed PositionDiagram: %s\n", yy_text);
6655
6656         if (!startedFromSetupPosition) {
6657             p = yy_text;
6658             for (i = BOARD_SIZE - 1; i >= 0; i--)
6659               for (j = 0; j < BOARD_SIZE; p++)
6660                 switch (*p) {
6661                   case '[':
6662                   case '-':
6663                   case ' ':
6664                   case '\t':
6665                   case '\n':
6666                   case '\r':
6667                     break;
6668                   default:
6669                     initial_position[i][j++] = CharToPiece(*p);
6670                     break;
6671                 }
6672             while (*p == ' ' || *p == '\t' ||
6673                    *p == '\n' || *p == '\r') p++;
6674         
6675             if (strncmp(p, "black", strlen("black"))==0)
6676               blackPlaysFirst = TRUE;
6677             else
6678               blackPlaysFirst = FALSE;
6679             startedFromSetupPosition = TRUE;
6680         
6681             CopyBoard(boards[0], initial_position);
6682             if (blackPlaysFirst) {
6683                 currentMove = forwardMostMove = backwardMostMove = 1;
6684                 CopyBoard(boards[1], initial_position);
6685                 strcpy(moveList[0], "");
6686                 strcpy(parseList[0], "");
6687                 timeRemaining[0][1] = whiteTimeRemaining;
6688                 timeRemaining[1][1] = blackTimeRemaining;
6689                 if (commentList[0] != NULL) {
6690                     commentList[1] = commentList[0];
6691                     commentList[0] = NULL;
6692                 }
6693             } else {
6694                 currentMove = forwardMostMove = backwardMostMove = 0;
6695             }
6696         }
6697         yyboardindex = forwardMostMove;
6698         cm = (ChessMove) yylex();
6699     }
6700
6701     if (first.pr == NoProc) {
6702         StartChessProgram(&first);
6703     }
6704     InitChessProgram(&first);
6705     SendToProgram("force\n", &first);
6706     if (startedFromSetupPosition) {
6707         SendBoard(&first, forwardMostMove);
6708         DisplayBothClocks();
6709     }      
6710
6711     while (cm == Comment) {
6712         char *p;
6713         if (appData.debugMode) 
6714           fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
6715         p = yy_text;
6716         if (*p == '{' || *p == '[' || *p == '(') {
6717             p[strlen(p) - 1] = NULLCHAR;
6718             p++;
6719         }
6720         while (*p == '\n') p++;
6721         AppendComment(currentMove, p);
6722         yyboardindex = forwardMostMove;
6723         cm = (ChessMove) yylex();
6724     }
6725
6726     if (cm == (ChessMove) 0 || cm == WhiteWins || cm == BlackWins ||
6727         cm == GameIsDrawn || cm == GameUnfinished) {
6728         DisplayMessage("", "No moves in game");
6729         if (cmailMsgLoaded) {
6730             if (appData.debugMode)
6731               fprintf(debugFP, "Setting flipView to %d.\n", FALSE);
6732             ClearHighlights();
6733             flipView = FALSE;
6734         }
6735         DrawPosition(FALSE, boards[currentMove]);
6736         DisplayBothClocks();
6737         gameMode = EditGame;
6738         ModeHighlight();
6739         gameFileFP = NULL;
6740         cmailOldMove = 0;
6741         return TRUE;
6742     }
6743
6744     if (commentList[currentMove] != NULL) {
6745       if (!matchMode && (pausing || appData.timeDelay != 0)) {
6746         DisplayComment(currentMove - 1, commentList[currentMove]);
6747       }
6748     }
6749     if (!matchMode && appData.timeDelay != 0) 
6750       DrawPosition(FALSE, boards[currentMove]);
6751
6752     if (gameMode == AnalyzeFile || gameMode == AnalyzeMode) {
6753       programStats.ok_to_send = 1;
6754     }
6755
6756     /* if the first token after the PGN tags is a move
6757      * and not move number 1, retrieve it from the parser 
6758      */
6759     if (cm != MoveNumberOne)
6760         LoadGameOneMove(cm);
6761
6762     /* load the remaining moves from the file */
6763     while (LoadGameOneMove((ChessMove)0)) {
6764       timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
6765       timeRemaining[1][forwardMostMove] = blackTimeRemaining;
6766     }
6767
6768     /* rewind to the start of the game */
6769     currentMove = backwardMostMove;
6770
6771     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
6772
6773     if (oldGameMode == AnalyzeFile ||
6774         oldGameMode == AnalyzeMode) {
6775       AnalyzeFileEvent();
6776     }
6777
6778     if (matchMode || appData.timeDelay == 0) {
6779       ToEndEvent();
6780       gameMode = EditGame;
6781       ModeHighlight();
6782     } else if (appData.timeDelay > 0) {
6783       AutoPlayGameLoop();
6784     }
6785
6786     if (appData.debugMode) 
6787         fprintf(debugFP, "LoadGame(): on exit, gameMode %d\n", gameMode);
6788     return TRUE;
6789 }
6790
6791 /* Support for LoadNextPosition, LoadPreviousPosition, ReloadSamePosition */
6792 int
6793 ReloadPosition(offset)
6794      int offset;
6795 {
6796     int positionNumber = lastLoadPositionNumber + offset;
6797     if (lastLoadPositionFP == NULL) {
6798         DisplayError("No position has been loaded yet", 0);
6799         return FALSE;
6800     }
6801     if (positionNumber <= 0) {
6802         DisplayError("Can't back up any further", 0);
6803         return FALSE;
6804     }
6805     return LoadPosition(lastLoadPositionFP, positionNumber,
6806                         lastLoadPositionTitle);
6807 }
6808
6809 /* Load the nth position from the given file */
6810 int
6811 LoadPositionFromFile(filename, n, title)
6812      char *filename;
6813      int n;
6814      char *title;
6815 {
6816     FILE *f;
6817     char buf[MSG_SIZ];
6818
6819     if (strcmp(filename, "-") == 0) {
6820         return LoadPosition(stdin, n, "stdin");
6821     } else {
6822         f = fopen(filename, "rb");
6823         if (f == NULL) {
6824             sprintf(buf, "Can't open \"%s\"", filename);
6825             DisplayError(buf, errno);
6826             return FALSE;
6827         } else {
6828             return LoadPosition(f, n, title);
6829         }
6830     }
6831 }
6832
6833 /* Load the nth position from the given open file, and close it */
6834 int
6835 LoadPosition(f, positionNumber, title)
6836      FILE *f;
6837      int positionNumber;
6838      char *title;
6839 {
6840     char *p, line[MSG_SIZ];
6841     Board initial_position;
6842     int i, j, fenMode, pn;
6843     
6844     if (gameMode == Training )
6845         SetTrainingModeOff();
6846
6847     if (gameMode != BeginningOfGame) {
6848         Reset(FALSE, TRUE);
6849     }
6850     if (lastLoadPositionFP != NULL && lastLoadPositionFP != f) {
6851         fclose(lastLoadPositionFP);
6852     }
6853     if (positionNumber == 0) positionNumber = 1;
6854     lastLoadPositionFP = f;
6855     lastLoadPositionNumber = positionNumber;
6856     strcpy(lastLoadPositionTitle, title);
6857     if (first.pr == NoProc) {
6858       StartChessProgram(&first);
6859       InitChessProgram(&first);
6860     }    
6861     pn = positionNumber;
6862     if (positionNumber < 0) {
6863         /* Negative position number means to seek to that byte offset */
6864         if (fseek(f, -positionNumber, 0) == -1) {
6865             DisplayError("Can't seek on position file", 0);
6866             return FALSE;
6867         };
6868         pn = 1;
6869     } else {
6870         if (fseek(f, 0, 0) == -1) {
6871             if (f == lastLoadPositionFP ?
6872                 positionNumber == lastLoadPositionNumber + 1 :
6873                 positionNumber == 1) {
6874                 pn = 1;
6875             } else {
6876                 DisplayError("Can't seek on position file", 0);
6877                 return FALSE;
6878             }
6879         }
6880     }
6881     /* See if this file is FEN or old-style xboard */
6882     if (fgets(line, MSG_SIZ, f) == NULL) {
6883         DisplayError("Position not found in file", 0);
6884         return FALSE;
6885     }
6886     switch (line[0]) {
6887       case '#':  case 'x':
6888       default:
6889         fenMode = FALSE;
6890         break;
6891       case 'p':  case 'n':  case 'b':  case 'r':  case 'q':  case 'k':
6892       case 'P':  case 'N':  case 'B':  case 'R':  case 'Q':  case 'K':
6893       case '1':  case '2':  case '3':  case '4':  case '5':  case '6':
6894       case '7':  case '8':
6895         fenMode = TRUE;
6896         break;
6897     }
6898
6899     if (pn >= 2) {
6900         if (fenMode || line[0] == '#') pn--;
6901         while (pn > 0) {
6902             /* skip postions before number pn */
6903             if (fgets(line, MSG_SIZ, f) == NULL) {
6904                 DisplayError("Position not found in file", 0);
6905                 return FALSE;
6906             }
6907             if (fenMode || line[0] == '#') pn--;
6908         }
6909     }
6910
6911     if (fenMode) {
6912         if (!ParseFEN(initial_position, &blackPlaysFirst, line)) {
6913             DisplayError("Bad FEN position in file", 0);
6914             return FALSE;
6915         }
6916     } else {
6917         (void) fgets(line, MSG_SIZ, f);
6918         (void) fgets(line, MSG_SIZ, f);
6919     
6920         for (i = BOARD_SIZE - 1; i >= 0; i--) {
6921             (void) fgets(line, MSG_SIZ, f);
6922             for (p = line, j = 0; j < BOARD_SIZE; p++) {
6923                 if (*p == ' ')
6924                   continue;
6925                 initial_position[i][j++] = CharToPiece(*p);
6926             }
6927         }
6928     
6929         blackPlaysFirst = FALSE;
6930         if (!feof(f)) {
6931             (void) fgets(line, MSG_SIZ, f);
6932             if (strncmp(line, "black", strlen("black"))==0)
6933               blackPlaysFirst = TRUE;
6934         }
6935     }
6936     startedFromSetupPosition = TRUE;
6937     
6938     SendToProgram("force\n", &first);
6939     CopyBoard(boards[0], initial_position);
6940     if (blackPlaysFirst) {
6941         currentMove = forwardMostMove = backwardMostMove = 1;
6942         strcpy(moveList[0], "");
6943         strcpy(parseList[0], "");
6944         CopyBoard(boards[1], initial_position);
6945         DisplayMessage("", "Black to play");
6946     } else {
6947         currentMove = forwardMostMove = backwardMostMove = 0;
6948         DisplayMessage("", "White to play");
6949     }
6950     SendBoard(&first, forwardMostMove);
6951
6952     if (positionNumber > 1) {
6953         sprintf(line, "%s %d", title, positionNumber);
6954         DisplayTitle(line);
6955     } else {
6956         DisplayTitle(title);
6957     }
6958     gameMode = EditGame;
6959     ModeHighlight();
6960     ResetClocks();
6961     timeRemaining[0][1] = whiteTimeRemaining;
6962     timeRemaining[1][1] = blackTimeRemaining;
6963     DrawPosition(FALSE, boards[currentMove]);
6964    
6965     return TRUE;
6966 }
6967
6968
6969 void
6970 CopyPlayerNameIntoFileName(dest, src)
6971      char **dest, *src;
6972 {
6973     while (*src != NULLCHAR && *src != ',') {
6974         if (*src == ' ') {
6975             *(*dest)++ = '_';
6976             src++;
6977         } else {
6978             *(*dest)++ = *src++;
6979         }
6980     }
6981 }
6982
6983 char *DefaultFileName(ext)
6984      char *ext;
6985 {
6986     static char def[MSG_SIZ];
6987     char *p;
6988
6989     if (gameInfo.white != NULL && gameInfo.white[0] != '-') {
6990         p = def;
6991         CopyPlayerNameIntoFileName(&p, gameInfo.white);
6992         *p++ = '-';
6993         CopyPlayerNameIntoFileName(&p, gameInfo.black);
6994         *p++ = '.';
6995         strcpy(p, ext);
6996     } else {
6997         def[0] = NULLCHAR;
6998     }
6999     return def;
7000 }
7001
7002 /* Save the current game to the given file */
7003 int
7004 SaveGameToFile(filename, append)
7005      char *filename;
7006      int append;
7007 {
7008     FILE *f;
7009     char buf[MSG_SIZ];
7010
7011     if (strcmp(filename, "-") == 0) {
7012         return SaveGame(stdout, 0, NULL);
7013     } else {
7014         f = fopen(filename, append ? "a" : "w");
7015         if (f == NULL) {
7016             sprintf(buf, "Can't open \"%s\"", filename);
7017             DisplayError(buf, errno);
7018             return FALSE;
7019         } else {
7020             return SaveGame(f, 0, NULL);
7021         }
7022     }
7023 }
7024
7025 char *
7026 SavePart(str)
7027      char *str;
7028 {
7029     static char buf[MSG_SIZ];
7030     char *p;
7031     
7032     p = strchr(str, ' ');
7033     if (p == NULL) return str;
7034     strncpy(buf, str, p - str);
7035     buf[p - str] = NULLCHAR;
7036     return buf;
7037 }
7038
7039 #define PGN_MAX_LINE 75
7040
7041 /* Save game in PGN style and close the file */
7042 int
7043 SaveGamePGN(f)
7044      FILE *f;
7045 {
7046     int i, offset, linelen, newblock;
7047     time_t tm;
7048     char *movetext;
7049     char numtext[32];
7050     int movelen, numlen, blank;
7051     
7052     tm = time((time_t *) NULL);
7053     
7054     PrintPGNTags(f, &gameInfo);
7055     
7056     if (backwardMostMove > 0 || startedFromSetupPosition) {
7057         char *fen = PositionToFEN(backwardMostMove);
7058         fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);
7059         fprintf(f, "\n{--------------\n");
7060         PrintPosition(f, backwardMostMove);
7061         fprintf(f, "--------------}\n");
7062         free(fen);
7063     } else {
7064         fprintf(f, "\n");
7065     }
7066
7067     i = backwardMostMove;
7068     offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
7069     linelen = 0;
7070     newblock = TRUE;
7071
7072     while (i < forwardMostMove) {
7073         /* Print comments preceding this move */
7074         if (commentList[i] != NULL) {
7075             if (linelen > 0) fprintf(f, "\n");
7076             fprintf(f, "{\n%s}\n", commentList[i]);
7077             linelen = 0;
7078             newblock = TRUE;
7079         }
7080
7081         /* Format move number */
7082         if ((i % 2) == 0) {
7083             sprintf(numtext, "%d.", (i - offset)/2 + 1);
7084         } else {
7085             if (newblock) {
7086                 sprintf(numtext, "%d...", (i - offset)/2 + 1);
7087             } else {
7088                 numtext[0] = NULLCHAR;
7089             }
7090         }
7091         numlen = strlen(numtext);
7092         newblock = FALSE;
7093
7094         /* Print move number */
7095         blank = linelen > 0 && numlen > 0;
7096         if (linelen + (blank ? 1 : 0) + numlen > PGN_MAX_LINE) {
7097             fprintf(f, "\n");
7098             linelen = 0;
7099             blank = 0;
7100         }
7101         if (blank) {
7102             fprintf(f, " ");
7103             linelen++;
7104         }
7105         fprintf(f, numtext);
7106         linelen += numlen;
7107
7108         /* Get move */
7109         movetext = SavePart(parseList[i]);
7110         movelen = strlen(movetext);
7111
7112         /* Print move */
7113         blank = linelen > 0 && movelen > 0;
7114         if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {
7115             fprintf(f, "\n");
7116             linelen = 0;
7117             blank = 0;
7118         }
7119         if (blank) {
7120             fprintf(f, " ");
7121             linelen++;
7122         }
7123         fprintf(f, movetext);
7124         linelen += movelen;
7125
7126         i++;
7127     }
7128     
7129     /* Start a new line */
7130     if (linelen > 0) fprintf(f, "\n");
7131
7132     /* Print comments after last move */
7133     if (commentList[i] != NULL) {
7134         fprintf(f, "{\n%s}\n", commentList[i]);
7135     }
7136
7137     /* Print result */
7138     if (gameInfo.resultDetails != NULL &&
7139         gameInfo.resultDetails[0] != NULLCHAR) {
7140         fprintf(f, "{%s} %s\n\n", gameInfo.resultDetails,
7141                 PGNResult(gameInfo.result));
7142     } else {
7143         fprintf(f, "%s\n\n", PGNResult(gameInfo.result));
7144     }
7145
7146     fclose(f);
7147     return TRUE;
7148 }
7149
7150 /* Save game in old style and close the file */
7151 int
7152 SaveGameOldStyle(f)
7153      FILE *f;
7154 {
7155     int i, offset;
7156     time_t tm;
7157     
7158     tm = time((time_t *) NULL);
7159     
7160     fprintf(f, "# %s game file -- %s", programName, ctime(&tm));
7161     PrintOpponents(f);
7162     
7163     if (backwardMostMove > 0 || startedFromSetupPosition) {
7164         fprintf(f, "\n[--------------\n");
7165         PrintPosition(f, backwardMostMove);
7166         fprintf(f, "--------------]\n");
7167     } else {
7168         fprintf(f, "\n");
7169     }
7170
7171     i = backwardMostMove;
7172     offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
7173
7174     while (i < forwardMostMove) {
7175         if (commentList[i] != NULL) {
7176             fprintf(f, "[%s]\n", commentList[i]);
7177         }
7178
7179         if ((i % 2) == 1) {
7180             fprintf(f, "%d. ...  %s\n", (i - offset)/2 + 1, parseList[i]);
7181             i++;
7182         } else {
7183             fprintf(f, "%d. %s  ", (i - offset)/2 + 1, parseList[i]);
7184             i++;
7185             if (commentList[i] != NULL) {
7186                 fprintf(f, "\n");
7187                 continue;
7188             }
7189             if (i >= forwardMostMove) {
7190                 fprintf(f, "\n");
7191                 break;
7192             }
7193             fprintf(f, "%s\n", parseList[i]);
7194             i++;
7195         }
7196     }
7197     
7198     if (commentList[i] != NULL) {
7199         fprintf(f, "[%s]\n", commentList[i]);
7200     }
7201
7202     /* This isn't really the old style, but it's close enough */
7203     if (gameInfo.resultDetails != NULL &&
7204         gameInfo.resultDetails[0] != NULLCHAR) {
7205         fprintf(f, "%s (%s)\n\n", PGNResult(gameInfo.result),
7206                 gameInfo.resultDetails);
7207     } else {
7208         fprintf(f, "%s\n\n", PGNResult(gameInfo.result));
7209     }
7210
7211     fclose(f);
7212     return TRUE;
7213 }
7214
7215 /* Save the current game to open file f and close the file */
7216 int
7217 SaveGame(f, dummy, dummy2)
7218      FILE *f;
7219      int dummy;
7220      char *dummy2;
7221 {
7222     if (gameMode == EditPosition) EditPositionDone();
7223     if (appData.oldSaveStyle)
7224       return SaveGameOldStyle(f);
7225     else
7226       return SaveGamePGN(f);
7227 }
7228
7229 /* Save the current position to the given file */
7230 int
7231 SavePositionToFile(filename)
7232      char *filename;
7233 {
7234     FILE *f;
7235     char buf[MSG_SIZ];
7236
7237     if (strcmp(filename, "-") == 0) {
7238         return SavePosition(stdout, 0, NULL);
7239     } else {
7240         f = fopen(filename, "a");
7241         if (f == NULL) {
7242             sprintf(buf, "Can't open \"%s\"", filename);
7243             DisplayError(buf, errno);
7244             return FALSE;
7245         } else {
7246             SavePosition(f, 0, NULL);
7247             return TRUE;
7248         }
7249     }
7250 }
7251
7252 /* Save the current position to the given open file and close the file */
7253 int
7254 SavePosition(f, dummy, dummy2)
7255      FILE *f;
7256      int dummy;
7257      char *dummy2;
7258 {
7259     time_t tm;
7260     char *fen;
7261     
7262     if (appData.oldSaveStyle) {
7263         tm = time((time_t *) NULL);
7264     
7265         fprintf(f, "# %s position file -- %s", programName, ctime(&tm));
7266         PrintOpponents(f);
7267         fprintf(f, "[--------------\n");
7268         PrintPosition(f, currentMove);
7269         fprintf(f, "--------------]\n");
7270     } else {
7271         fen = PositionToFEN(currentMove);
7272         fprintf(f, "%s\n", fen);
7273         free(fen);
7274     }
7275     fclose(f);
7276     return TRUE;
7277 }
7278
7279 void
7280 ReloadCmailMsgEvent(unregister)
7281      int unregister;
7282 {
7283     static char *inFilename = NULL;
7284     static char *outFilename;
7285     int i;
7286     struct stat inbuf, outbuf;
7287     int status;
7288     
7289     /* Any registered moves are unregistered if unregister is set, */
7290     /* i.e. invoked by the signal handler */
7291     if (unregister) {
7292         for (i = 0; i < CMAIL_MAX_GAMES; i ++) {
7293             cmailMoveRegistered[i] = FALSE;
7294             if (cmailCommentList[i] != NULL) {
7295                 free(cmailCommentList[i]);
7296                 cmailCommentList[i] = NULL;
7297             }
7298         }
7299         nCmailMovesRegistered = 0;
7300     }
7301
7302     for (i = 0; i < CMAIL_MAX_GAMES; i ++) {
7303         cmailResult[i] = CMAIL_NOT_RESULT;
7304     }
7305     nCmailResults = 0;
7306
7307     if (inFilename == NULL) {
7308         /* Because the filenames are static they only get malloced once  */
7309         /* and they never get freed                                      */
7310         inFilename = (char *) malloc(strlen(appData.cmailGameName) + 9);
7311         sprintf(inFilename, "%s.game.in", appData.cmailGameName);
7312
7313         outFilename = (char *) malloc(strlen(appData.cmailGameName) + 5);
7314         sprintf(outFilename, "%s.out", appData.cmailGameName);
7315     }
7316     
7317     status = stat(outFilename, &outbuf);
7318     if (status < 0) {
7319         cmailMailedMove = FALSE;
7320     } else {
7321         status = stat(inFilename, &inbuf);
7322         cmailMailedMove = (inbuf.st_mtime < outbuf.st_mtime);
7323     }
7324     
7325     /* LoadGameFromFile(CMAIL_MAX_GAMES) with cmailMsgLoaded == TRUE
7326        counts the games, notes how each one terminated, etc.
7327        
7328        It would be nice to remove this kludge and instead gather all
7329        the information while building the game list.  (And to keep it
7330        in the game list nodes instead of having a bunch of fixed-size
7331        parallel arrays.)  Note this will require getting each game's
7332        termination from the PGN tags, as the game list builder does
7333        not process the game moves.  --mann
7334        */
7335     cmailMsgLoaded = TRUE;
7336     LoadGameFromFile(inFilename, CMAIL_MAX_GAMES, "", FALSE);
7337     
7338     /* Load first game in the file or popup game menu */
7339     LoadGameFromFile(inFilename, 0, appData.cmailGameName, TRUE);
7340
7341     return;
7342 }
7343
7344 int
7345 RegisterMove()
7346 {
7347     FILE *f;
7348     char string[MSG_SIZ];
7349
7350     if (   cmailMailedMove
7351         || (cmailResult[lastLoadGameNumber - 1] == CMAIL_OLD_RESULT)) {
7352         return TRUE;            /* Allow free viewing  */
7353     }
7354
7355     /* Unregister move to ensure that we don't leave RegisterMove        */
7356     /* with the move registered when the conditions for registering no   */
7357     /* longer hold                                                       */
7358     if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
7359         cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
7360         nCmailMovesRegistered --;
7361
7362         if (cmailCommentList[lastLoadGameNumber - 1] != NULL) 
7363           {
7364               free(cmailCommentList[lastLoadGameNumber - 1]);
7365               cmailCommentList[lastLoadGameNumber - 1] = NULL;
7366           }
7367     }
7368
7369     if (cmailOldMove == -1) {
7370         DisplayError("You have edited the game history.\nUse Reload Same Game and make your move again.", 0);
7371         return FALSE;
7372     }
7373
7374     if (currentMove > cmailOldMove + 1) {
7375         DisplayError("You have entered too many moves.\nBack up to the correct position and try again.", 0);
7376         return FALSE;
7377     }
7378
7379     if (currentMove < cmailOldMove) {
7380         DisplayError("Displayed position is not current.\nStep forward to the correct position and try again.", 0);
7381         return FALSE;
7382     }
7383
7384     if (forwardMostMove > currentMove) {
7385         /* Silently truncate extra moves */
7386         TruncateGame();
7387     }
7388
7389     if (   (currentMove == cmailOldMove + 1)
7390         || (   (currentMove == cmailOldMove)
7391             && (   (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_ACCEPT)
7392                 || (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_RESIGN)))) {
7393         if (gameInfo.result != GameUnfinished) {
7394             cmailResult[lastLoadGameNumber - 1] = CMAIL_NEW_RESULT;
7395         }
7396
7397         if (commentList[currentMove] != NULL) {
7398             cmailCommentList[lastLoadGameNumber - 1]
7399               = StrSave(commentList[currentMove]);
7400         }
7401         strcpy(cmailMove[lastLoadGameNumber - 1], moveList[currentMove - 1]);
7402
7403         if (appData.debugMode)
7404           fprintf(debugFP, "Saving %s for game %d\n",
7405                   cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
7406
7407         sprintf(string,
7408                 "%s.game.out.%d", appData.cmailGameName, lastLoadGameNumber);
7409         
7410         f = fopen(string, "w");
7411         if (appData.oldSaveStyle) {
7412             SaveGameOldStyle(f); /* also closes the file */
7413             
7414             sprintf(string, "%s.pos.out", appData.cmailGameName);
7415             f = fopen(string, "w");
7416             SavePosition(f, 0, NULL); /* also closes the file */
7417         } else {
7418             fprintf(f, "{--------------\n");
7419             PrintPosition(f, currentMove);
7420             fprintf(f, "--------------}\n\n");
7421             
7422             SaveGame(f, 0, NULL); /* also closes the file*/
7423         }
7424         
7425         cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE;
7426         nCmailMovesRegistered ++;
7427     } else if (nCmailGames == 1) {
7428         DisplayError("You have not made a move yet", 0);
7429         return FALSE;
7430     }
7431
7432     return TRUE;
7433 }
7434
7435 void
7436 MailMoveEvent()
7437 {
7438     static char *partCommandString = "cmail -xv%s -remail -game %s 2>&1";
7439     FILE *commandOutput;
7440     char buffer[MSG_SIZ], msg[MSG_SIZ], string[MSG_SIZ];
7441     int nBytes = 0;             /*  Suppress warnings on uninitialized variables    */
7442     int nBuffers;
7443     int i;
7444     int archived;
7445     char *arcDir;
7446
7447     if (! cmailMsgLoaded) {
7448         DisplayError("The cmail message is not loaded.\nUse Reload CMail Message and make your move again.", 0);
7449         return;
7450     }
7451
7452     if (nCmailGames == nCmailResults) {
7453         DisplayError("No unfinished games", 0);
7454         return;
7455     }
7456
7457 #if CMAIL_PROHIBIT_REMAIL
7458     if (cmailMailedMove) {
7459         sprintf(msg, "You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line.", appData.cmailGameName);
7460         DisplayError(msg, 0);
7461         return;
7462     }
7463 #endif
7464
7465     if (! (cmailMailedMove || RegisterMove())) return;
7466     
7467     if (   cmailMailedMove
7468         || (nCmailMovesRegistered + nCmailResults == nCmailGames)) {
7469         sprintf(string, partCommandString,
7470                 appData.debugMode ? " -v" : "", appData.cmailGameName);
7471         commandOutput = popen(string, "r");
7472
7473         if (commandOutput == NULL) {
7474             DisplayError("Failed to invoke cmail", 0);
7475         } else {
7476             for (nBuffers = 0; (! feof(commandOutput)); nBuffers ++) {
7477                 nBytes = fread(buffer, 1, MSG_SIZ - 1, commandOutput);
7478             }
7479             if (nBuffers > 1) {
7480                 (void) memcpy(msg, buffer + nBytes, MSG_SIZ - nBytes - 1);
7481                 (void) memcpy(msg + MSG_SIZ - nBytes - 1, buffer, nBytes);
7482                 nBytes = MSG_SIZ - 1;
7483             } else {
7484                 (void) memcpy(msg, buffer, nBytes);
7485             }
7486             *(msg + nBytes) = '\0'; /* \0 for end-of-string*/
7487
7488             if(StrStr(msg, "Mailed cmail message to ") != NULL) {
7489                 cmailMailedMove = TRUE; /* Prevent >1 moves    */
7490
7491                 archived = TRUE;
7492                 for (i = 0; i < nCmailGames; i ++) {
7493                     if (cmailResult[i] == CMAIL_NOT_RESULT) {
7494                         archived = FALSE;
7495                     }
7496                 }
7497                 if (   archived
7498                     && (   (arcDir = (char *) getenv("CMAIL_ARCDIR"))
7499                         != NULL)) {
7500                     sprintf(buffer, "%s/%s.%s.archive",
7501                             arcDir,
7502                             appData.cmailGameName,
7503                             gameInfo.date);
7504                     LoadGameFromFile(buffer, 1, buffer, FALSE);
7505                     cmailMsgLoaded = FALSE;
7506                 }
7507             }
7508
7509             DisplayInformation(msg);
7510             pclose(commandOutput);
7511         }
7512     } else {
7513         if ((*cmailMsg) != '\0') {
7514             DisplayInformation(cmailMsg);
7515         }
7516     }
7517
7518     return;
7519 }
7520
7521 char *
7522 CmailMsg()
7523 {
7524     int  prependComma = 0;
7525     char number[5];
7526     char string[MSG_SIZ];       /* Space for game-list */
7527     int  i;
7528     
7529     if (!cmailMsgLoaded) return "";
7530
7531     if (cmailMailedMove) {
7532         sprintf(cmailMsg, "Waiting for reply from opponent\n");
7533     } else {
7534         /* Create a list of games left */
7535         sprintf(string, "[");
7536         for (i = 0; i < nCmailGames; i ++) {
7537             if (! (   cmailMoveRegistered[i]
7538                    || (cmailResult[i] == CMAIL_OLD_RESULT))) {
7539                 if (prependComma) {
7540                     sprintf(number, ",%d", i + 1);
7541                 } else {
7542                     sprintf(number, "%d", i + 1);
7543                     prependComma = 1;
7544                 }
7545                 
7546                 strcat(string, number);
7547             }
7548         }
7549         strcat(string, "]");
7550
7551         if (nCmailMovesRegistered + nCmailResults == 0) {
7552             switch (nCmailGames) {
7553               case 1:
7554                 sprintf(cmailMsg,
7555                         "Still need to make move for game\n");
7556                 break;
7557                 
7558               case 2:
7559                 sprintf(cmailMsg,
7560                         "Still need to make moves for both games\n");
7561                 break;
7562                 
7563               default:
7564                 sprintf(cmailMsg,
7565                         "Still need to make moves for all %d games\n",
7566                         nCmailGames);
7567                 break;
7568             }
7569         } else {
7570             switch (nCmailGames - nCmailMovesRegistered - nCmailResults) {
7571               case 1:
7572                 sprintf(cmailMsg,
7573                         "Still need to make a move for game %s\n",
7574                         string);
7575                 break;
7576                 
7577               case 0:
7578                 if (nCmailResults == nCmailGames) {
7579                     sprintf(cmailMsg, "No unfinished games\n");
7580                 } else {
7581                     sprintf(cmailMsg, "Ready to send mail\n");
7582                 }
7583                 break;
7584                 
7585               default:
7586                 sprintf(cmailMsg,
7587                         "Still need to make moves for games %s\n",
7588                         string);
7589             }
7590         }
7591     }
7592
7593     return cmailMsg;
7594 }
7595
7596 void
7597 ResetGameEvent()
7598 {
7599     if (gameMode == Training)
7600       SetTrainingModeOff();
7601
7602     Reset(TRUE, TRUE);
7603     cmailMsgLoaded = FALSE;
7604     if (appData.icsActive) {
7605       SendToICS(ics_prefix);
7606       SendToICS("refresh\n");
7607     }
7608 }
7609
7610 static int exiting = 0;
7611
7612 void
7613 ExitEvent(status)
7614      int status;
7615 {
7616     exiting++;
7617     if (exiting > 2) {
7618       /* Give up on clean exit */
7619       exit(status);
7620     }
7621     if (exiting > 1) {
7622       /* Keep trying for clean exit */
7623       return;
7624     }
7625
7626     if (appData.icsActive && appData.colorize) Colorize(ColorNone, FALSE);
7627
7628     if (telnetISR != NULL) {
7629       RemoveInputSource(telnetISR);
7630     }
7631     if (icsPR != NoProc) {
7632       DestroyChildProcess(icsPR, TRUE);
7633     }
7634     /* Save game if resource set and not already saved by GameEnds() */
7635     if (gameInfo.resultDetails == NULL && forwardMostMove > 0) {
7636       if (*appData.saveGameFile != NULLCHAR) {
7637         SaveGameToFile(appData.saveGameFile, TRUE);
7638       } else if (appData.autoSaveGames) {
7639         AutoSaveGame();
7640       }
7641       if (*appData.savePositionFile != NULLCHAR) {
7642         SavePositionToFile(appData.savePositionFile);
7643       }
7644     }
7645     GameEnds((ChessMove) 0, NULL, GE_PLAYER);
7646
7647     /* Kill off chess programs */
7648     if (first.pr != NoProc) {
7649         ExitAnalyzeMode();
7650         SendToProgram("quit\n", &first);
7651         DestroyChildProcess(first.pr, first.useSigterm);
7652     }
7653     if (second.pr != NoProc) {
7654         SendToProgram("quit\n", &second);
7655         DestroyChildProcess(second.pr, second.useSigterm);
7656     }
7657     if (first.isr != NULL) {
7658         RemoveInputSource(first.isr);
7659     }
7660     if (second.isr != NULL) {
7661         RemoveInputSource(second.isr);
7662     }
7663
7664     ShutDownFrontEnd();
7665     exit(status);
7666 }
7667
7668 void
7669 PauseEvent()
7670 {
7671     if (appData.debugMode)
7672         fprintf(debugFP, "PauseEvent(): pausing %d\n", pausing);
7673     if (pausing) {
7674         pausing = FALSE;
7675         ModeHighlight();
7676         if (gameMode == MachinePlaysWhite ||
7677             gameMode == MachinePlaysBlack) {
7678             StartClocks();
7679         } else {
7680             DisplayBothClocks();
7681         }
7682         if (gameMode == PlayFromGameFile) {
7683             if (appData.timeDelay >= 0) 
7684                 AutoPlayGameLoop();
7685         } else if (gameMode == IcsExamining && pauseExamInvalid) {
7686             Reset(FALSE, TRUE);
7687             SendToICS(ics_prefix);
7688             SendToICS("refresh\n");
7689         } else if (currentMove < forwardMostMove) {
7690             ForwardInner(forwardMostMove);
7691         }
7692         pauseExamInvalid = FALSE;
7693     } else {
7694         switch (gameMode) {
7695           default:
7696             return;
7697           case IcsExamining:
7698             pauseExamForwardMostMove = forwardMostMove;
7699             pauseExamInvalid = FALSE;
7700             /* fall through */
7701           case IcsObserving:
7702           case IcsPlayingWhite:
7703           case IcsPlayingBlack:
7704             pausing = TRUE;
7705             ModeHighlight();
7706             return;
7707           case PlayFromGameFile:
7708             (void) StopLoadGameTimer();
7709             pausing = TRUE;
7710             ModeHighlight();
7711             break;
7712           case BeginningOfGame:
7713             if (appData.icsActive) return;
7714             /* else fall through */
7715           case MachinePlaysWhite:
7716           case MachinePlaysBlack:
7717           case TwoMachinesPlay:
7718             if (forwardMostMove == 0)
7719               return;           /* don't pause if no one has moved */
7720             if ((gameMode == MachinePlaysWhite &&
7721                  !WhiteOnMove(forwardMostMove)) ||
7722                 (gameMode == MachinePlaysBlack &&
7723                  WhiteOnMove(forwardMostMove))) {
7724                 StopClocks();
7725             }
7726             pausing = TRUE;
7727             ModeHighlight();
7728             break;
7729         }
7730     }
7731 }
7732
7733 void
7734 EditCommentEvent()
7735 {
7736     char title[MSG_SIZ];
7737
7738     if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) {
7739         strcpy(title, "Edit comment");
7740     } else {
7741         sprintf(title, "Edit comment on %d.%s%s", (currentMove - 1) / 2 + 1,
7742                 WhiteOnMove(currentMove - 1) ? " " : ".. ",
7743                 parseList[currentMove - 1]);
7744     }
7745
7746     EditCommentPopUp(currentMove, title, commentList[currentMove]);
7747 }
7748
7749
7750 void
7751 EditTagsEvent()
7752 {
7753     char *tags = PGNTags(&gameInfo);
7754     EditTagsPopUp(tags);
7755     free(tags);
7756 }
7757
7758 void
7759 AnalyzeModeEvent()
7760 {
7761     if (appData.noChessProgram || gameMode == AnalyzeMode)
7762       return;
7763
7764         /* Engine Room enable */
7765         /* We need a AnalysisDisplay(TRUE) for timer */
7766         appData.AnalysisWindow = TRUE;
7767
7768     if (gameMode != AnalyzeFile) {
7769         EditGameEvent();
7770         if (gameMode != EditGame) return;
7771         ResurrectChessProgram();
7772         SendToProgram("analyze\n", &first);
7773         first.analyzing = TRUE;
7774         /*first.maybeThinking = TRUE;*/
7775         first.maybeThinking = FALSE; /* avoid killing GNU Chess */
7776     }
7777     gameMode = AnalyzeMode;
7778     pausing = FALSE;
7779     ModeHighlight();
7780     SetGameInfo();
7781     StartAnalysisClock();
7782     GetTimeMark(&lastNodeCountTime);
7783     lastNodeCount = 0;
7784         PeriodicUpdatesEvent(TRUE);
7785 }
7786
7787 /* daniel */
7788 void IcsAnalyze(newState)
7789 int newState;
7790
7791 {
7792         if (appData.noChessProgram) {
7793                 DisplayFatalError("ICS-Analysis requires a chess engine", 0, 1);
7794                 return;
7795         }
7796
7797     if (!newState && first.analyzing) {
7798                 if (appData.debugMode) fprintf(debugFP, "ICS-Analyze: Shutting down engine\n");
7799                 ExitAnalyzeMode();
7800                 DisplayMessage("","Close ICS engine analyse...");
7801                 return;
7802         }
7803
7804         if (gameMode != IcsObserving) {
7805             MessageBox(0,"You are not observing a game",
7806                 "ICS Engine Analyse", MB_OK);
7807                 appData.icsAnalyze = FALSE;
7808                 /* Set state for GUI */
7809                 appData.icsEngineNone = TRUE;
7810             appData.icsEngineWhisper = FALSE;
7811             appData.icsEngineKibitz = FALSE;
7812             appData.icsEngineTell = FALSE;
7813                 appData.icsAnalyzeOutPut = 4;
7814                 return;
7815         }
7816
7817         if (first.analysisSupport == FALSE) {
7818                 MessageBox(0,"Sorry, this engine does not support analyze",
7819                          "ICS Engine Analyse", MB_OK);
7820                 return;
7821         }
7822
7823         if (newState) {
7824                 if (appData.debugMode) fprintf(debugFP, "Starting ICS analyze\n");
7825                 DisplayMessage("","Starting ICS engine analyse");
7826                         if (!appData.icsWBprotoAgr) {
7827                                 /* safty */
7828                                 /* Wo do that only if we start new or have a problem */
7829                                 SendToProgram("new\n", &first);
7830                                 SendToProgram("post\n", &first);
7831                                 SendToProgram("force\n", &first);
7832                                 FeedMovesToProgram(&first, currentMove);
7833                                 SendToProgram("analyze\n", &first);
7834                         } else {
7835                                 /* hard */
7836                                 SendToProgram("exit\n", &first);
7837                                 SendToProgram("new\n", &first);
7838                                 SendToProgram("post\n", &first);
7839                                 SendToProgram("hart\n", &first);
7840                                 SendToProgram("force\n", &first);
7841                                 FeedMovesToProgram(&first, currentMove);
7842                                 SendToProgram("analyze\n", &first);
7843                                 SendToProgram(".\n", &first);
7844                         } 
7845                         PeriodicUpdatesEvent(TRUE); /* appData.periodicUpdates */
7846                         first.analyzing = TRUE; /* Used for GUI and other ways */
7847                 }
7848 }
7849
7850 void
7851 IcsAnalyzeWindowUp()
7852 {
7853         /* Using allso for startup Engine Room 
7854          * So function name looks like mismatch 
7855          * Change it for a better program style - 
7856          * but this is not crirical
7857          */     
7858
7859         if (appData.AnalysisWindow) {
7860                 ClearProgramStats();
7861                 DisplayAnalysis(0,0);
7862                 PeriodicUpdatesEvent(TRUE);
7863         }
7864         /* StartAnalysisClock check gameMode */
7865         if (gameMode == AnalyzeMode || gameMode == AnalyzeFile ||
7866                 appData.icsAnalyze || appData.AnalysisWindow) {
7867                 StartAnalysisClock();
7868         }
7869         GetTimeMark(&lastNodeCountTime);        
7870         lastNodeCount = 0; /* importent */
7871 }
7872
7873 void
7874 AnalyzeFileEvent()
7875 {
7876     if (appData.noChessProgram || gameMode == AnalyzeFile)
7877       return;
7878
7879         /* Engine Room */
7880         /* We need a DisplayAnalysis(TRUE); */
7881         appData.AnalysisWindow = TRUE;
7882
7883     if (gameMode != AnalyzeMode) {
7884         EditGameEvent();
7885         if (gameMode != EditGame) return;
7886         ResurrectChessProgram();
7887         SendToProgram("analyze\n", &first);
7888         first.analyzing = TRUE;
7889         /*first.maybeThinking = TRUE;*/
7890         first.maybeThinking = FALSE; /* avoid killing GNU Chess */
7891     }
7892     gameMode = AnalyzeFile;
7893     pausing = FALSE;
7894     ModeHighlight();
7895     SetGameInfo();
7896
7897     StartAnalysisClock();
7898     GetTimeMark(&lastNodeCountTime);
7899     lastNodeCount = 0;
7900 }
7901
7902 void
7903 MachineWhiteEvent()
7904 {
7905     char buf[MSG_SIZ];
7906
7907     if (appData.noChessProgram || (gameMode == MachinePlaysWhite))
7908       return;
7909
7910
7911     if (gameMode == PlayFromGameFile || 
7912         gameMode == TwoMachinesPlay  || 
7913         gameMode == Training         || 
7914         gameMode == AnalyzeMode      || 
7915         gameMode == EndOfGame)
7916         EditGameEvent();
7917
7918     if (gameMode == EditPosition) 
7919         EditPositionDone();
7920
7921     if (!WhiteOnMove(currentMove)) {
7922         DisplayError("It is not White's turn", 0);
7923         return;
7924     }
7925   
7926     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
7927       ExitAnalyzeMode();
7928           
7929
7930     if (gameMode == EditGame || gameMode == AnalyzeMode || 
7931         gameMode == AnalyzeFile)
7932         TruncateGame();
7933
7934     ResurrectChessProgram();    /* in case it isn't running */
7935     gameMode = MachinePlaysWhite;
7936     pausing = FALSE;
7937     ModeHighlight();
7938     SetGameInfo();
7939     sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
7940     DisplayTitle(buf);
7941     if (first.sendName) {
7942       sprintf(buf, "name %s\n", gameInfo.black);
7943       SendToProgram(buf, &first);
7944     }
7945     if (first.sendTime) {
7946       if (first.useColors) {
7947         SendToProgram("black\n", &first); /*gnu kludge*/
7948       }
7949       SendTimeRemaining(&first, TRUE);
7950     }
7951     if (first.useColors) {
7952       SendToProgram("white\ngo\n", &first);
7953     } else {
7954       SendToProgram("go\n", &first);
7955     }
7956     SetMachineThinkingEnables();
7957     first.maybeThinking = TRUE;
7958     StartClocks();
7959
7960     if (appData.autoFlipView && !flipView) {
7961       flipView = !flipView;
7962       DrawPosition(FALSE, NULL);
7963     }
7964         if (appData.AnalysisWindow) appData.periodicUpdates = TRUE;
7965 }
7966
7967 void
7968 MachineBlackEvent()
7969 {
7970     char buf[MSG_SIZ];
7971
7972     if (appData.noChessProgram || (gameMode == MachinePlaysBlack))
7973         return;
7974
7975
7976     if (gameMode == PlayFromGameFile || 
7977         gameMode == TwoMachinesPlay  || 
7978         gameMode == Training         || 
7979         gameMode == AnalyzeMode      || 
7980         gameMode == EndOfGame)
7981         EditGameEvent();
7982
7983     if (gameMode == EditPosition) 
7984         EditPositionDone();
7985
7986     if (WhiteOnMove(currentMove)) {
7987         DisplayError("It is not Black's turn", 0);
7988         return;
7989     }
7990     
7991     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
7992       ExitAnalyzeMode();
7993
7994     if (gameMode == EditGame || gameMode == AnalyzeMode || 
7995         gameMode == AnalyzeFile)
7996         TruncateGame();
7997
7998     ResurrectChessProgram();    /* in case it isn't running */
7999     gameMode = MachinePlaysBlack;
8000     pausing = FALSE;
8001     ModeHighlight();
8002     SetGameInfo();
8003     sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
8004     DisplayTitle(buf);
8005     if (first.sendName) {
8006       sprintf(buf, "name %s\n", gameInfo.white);
8007       SendToProgram(buf, &first);
8008     }
8009     if (first.sendTime) {
8010       if (first.useColors) {
8011         SendToProgram("white\n", &first); /*gnu kludge*/
8012       }
8013       SendTimeRemaining(&first, FALSE);
8014     }
8015     if (first.useColors) {
8016       SendToProgram("black\ngo\n", &first);
8017     } else {
8018       SendToProgram("go\n", &first);
8019     }
8020     SetMachineThinkingEnables();
8021     first.maybeThinking = TRUE;
8022     StartClocks();
8023
8024     if (appData.autoFlipView && flipView) {
8025       flipView = !flipView;
8026       DrawPosition(FALSE, NULL);
8027     }
8028         if (appData.AnalysisWindow) appData.periodicUpdates = TRUE;
8029 }
8030
8031
8032 void
8033 DisplayTwoMachinesTitle()
8034 {
8035     char buf[MSG_SIZ];
8036     if (appData.matchGames > 0) {
8037         if (first.twoMachinesColor[0] == 'w') {
8038             sprintf(buf, "%s vs. %s (%d-%d-%d)",
8039                     gameInfo.white, gameInfo.black,
8040                     first.matchWins, second.matchWins,
8041                     matchGame - 1 - (first.matchWins + second.matchWins));
8042         } else {
8043             sprintf(buf, "%s vs. %s (%d-%d-%d)",
8044                     gameInfo.white, gameInfo.black,
8045                     second.matchWins, first.matchWins,
8046                     matchGame - 1 - (first.matchWins + second.matchWins));
8047         }
8048     } else {
8049         sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
8050     }
8051     DisplayTitle(buf);
8052 }
8053
8054 void
8055 TwoMachinesEvent P((void))
8056 {
8057     int i;
8058     char buf[MSG_SIZ];
8059     ChessProgramState *onmove;
8060     
8061     if (appData.noChessProgram) return;
8062
8063     switch (gameMode) {
8064       case TwoMachinesPlay:
8065         return;
8066       case MachinePlaysWhite:
8067       case MachinePlaysBlack:
8068         if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {
8069             DisplayError("Wait until your turn,\nor select Move Now", 0);
8070             return;
8071         }
8072         /* fall through */
8073       case BeginningOfGame:
8074       case PlayFromGameFile:
8075       case EndOfGame:
8076         EditGameEvent();
8077         if (gameMode != EditGame) return;
8078         break;
8079       case EditPosition:
8080         EditPositionDone();
8081         break;
8082       case AnalyzeMode:
8083       case AnalyzeFile:
8084         ExitAnalyzeMode();
8085         break;
8086       case EditGame:
8087       default:
8088         break;
8089     }
8090
8091     forwardMostMove = currentMove;
8092     ResurrectChessProgram();    /* in case first program isn't running */
8093
8094     if (second.pr == NULL) {
8095         StartChessProgram(&second);
8096         if (second.protocolVersion == 1) {
8097           TwoMachinesEventIfReady();
8098         } else {
8099           /* kludge: allow timeout for initial "feature" command */
8100           FreezeUI();
8101           DisplayMessage("", "Starting second chess program");
8102           ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);
8103         }
8104         return;
8105     }
8106     DisplayMessage("", "");
8107     InitChessProgram(&second);
8108     SendToProgram("force\n", &second);
8109     if (startedFromSetupPosition) {
8110         SendBoard(&second, backwardMostMove);
8111     }
8112     for (i = backwardMostMove; i < forwardMostMove; i++) {
8113         SendMoveToProgram(i, &second);
8114     }
8115
8116     gameMode = TwoMachinesPlay;
8117     pausing = FALSE;
8118     ModeHighlight();
8119     SetGameInfo();
8120     DisplayTwoMachinesTitle();
8121     firstMove = TRUE;
8122     if ((first.twoMachinesColor[0] == 'w') == WhiteOnMove(forwardMostMove)) {
8123         onmove = &first;
8124     } else {
8125         onmove = &second;
8126     }
8127
8128     SendToProgram(first.computerString, &first);
8129     if (first.sendName) {
8130       sprintf(buf, "name %s\n", second.tidy);
8131       SendToProgram(buf, &first);
8132     }
8133     SendToProgram(second.computerString, &second);
8134     if (second.sendName) {
8135       sprintf(buf, "name %s\n", first.tidy);
8136       SendToProgram(buf, &second);
8137     }
8138
8139     if (!first.sendTime || !second.sendTime) {
8140         ResetClocks();
8141         timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
8142         timeRemaining[1][forwardMostMove] = blackTimeRemaining;
8143     }
8144     if (onmove->sendTime) {
8145       if (onmove->useColors) {
8146         SendToProgram(onmove->other->twoMachinesColor, onmove); /*gnu kludge*/
8147       }
8148       SendTimeRemaining(onmove, WhiteOnMove(forwardMostMove));
8149     }
8150     if (onmove->useColors) {
8151       SendToProgram(onmove->twoMachinesColor, onmove);
8152     }
8153     SendToProgram("go\n", onmove);
8154     onmove->maybeThinking = TRUE;
8155     SetMachineThinkingEnables();
8156
8157     StartClocks();
8158 }
8159
8160 void
8161 TrainingEvent()
8162 {
8163     if (gameMode == Training) {
8164       SetTrainingModeOff();
8165       gameMode = PlayFromGameFile;
8166       DisplayMessage("", "Training mode off");
8167     } else {
8168       gameMode = Training;
8169       animateTraining = appData.animate;
8170
8171       /* make sure we are not already at the end of the game */
8172       if (currentMove < forwardMostMove) {
8173         SetTrainingModeOn();
8174         DisplayMessage("", "Training mode on");
8175       } else {
8176         gameMode = PlayFromGameFile;
8177         DisplayError("Already at end of game", 0);
8178       }
8179     }
8180     ModeHighlight();
8181 }
8182
8183 void
8184 IcsClientEvent()
8185 {
8186     if (!appData.icsActive) return;
8187     switch (gameMode) {
8188       case IcsPlayingWhite:
8189       case IcsPlayingBlack:
8190       case IcsObserving:
8191       case IcsIdle:
8192       case BeginningOfGame:
8193       case IcsExamining:
8194         return;
8195
8196       case EditGame:
8197         break;
8198
8199       case EditPosition:
8200         EditPositionDone();
8201         break;
8202
8203       case AnalyzeMode:
8204       case AnalyzeFile:
8205         ExitAnalyzeMode();
8206         break;
8207         
8208       default:
8209         EditGameEvent();
8210         break;
8211     }
8212
8213     gameMode = IcsIdle;
8214     ModeHighlight();
8215     return;
8216 }
8217
8218
8219 void
8220 EditGameEvent()
8221 {
8222     int i;
8223         
8224     switch (gameMode) {
8225       case Training:
8226         SetTrainingModeOff();
8227         break;
8228       case MachinePlaysWhite:
8229       case MachinePlaysBlack:
8230       case BeginningOfGame:
8231         SendToProgram("force\n", &first);
8232         SetUserThinkingEnables();
8233         break;
8234       case PlayFromGameFile:
8235         (void) StopLoadGameTimer();
8236         if (gameFileFP != NULL) {
8237             gameFileFP = NULL;
8238         }
8239         break;
8240       case EditPosition:
8241         EditPositionDone();
8242         break;
8243       case AnalyzeMode:
8244       case AnalyzeFile:
8245         ExitAnalyzeMode();
8246         SendToProgram("force\n", &first);
8247         break;
8248       case TwoMachinesPlay:
8249         GameEnds((ChessMove) 0, NULL, GE_PLAYER);
8250         ResurrectChessProgram();
8251         SetUserThinkingEnables();
8252         break;
8253       case EndOfGame:
8254         ResurrectChessProgram();
8255         break;
8256       case IcsPlayingBlack:
8257       case IcsPlayingWhite:
8258         DisplayError("Warning: You are still playing a game", 0);
8259         break;
8260       case IcsObserving:
8261         break;
8262       case IcsExamining:
8263         DisplayError("Warning: You are still examining a game", 0);
8264         break;
8265       case IcsIdle:
8266         break;
8267       case EditGame:
8268       default:
8269         return;
8270     }
8271     
8272     pausing = FALSE;
8273         /* daniel */
8274     if (gameMode != IcsObserving) StopClocks();
8275
8276     first.offeredDraw = second.offeredDraw = 0;
8277
8278     if (gameMode == PlayFromGameFile) {
8279         whiteTimeRemaining = timeRemaining[0][currentMove];
8280         blackTimeRemaining = timeRemaining[1][currentMove];
8281         DisplayTitle("");
8282     }
8283
8284     if (gameMode == MachinePlaysWhite ||
8285         gameMode == MachinePlaysBlack ||
8286         gameMode == TwoMachinesPlay ||
8287         gameMode == EndOfGame) {
8288         i = forwardMostMove;
8289         while (i > currentMove) {
8290             SendToProgram("undo\n", &first);
8291             i--;
8292         }
8293         whiteTimeRemaining = timeRemaining[0][currentMove];
8294         blackTimeRemaining = timeRemaining[1][currentMove];
8295         DisplayBothClocks();
8296         if (whiteFlag || blackFlag) {
8297             whiteFlag = blackFlag = 0;
8298         }
8299         DisplayTitle("");
8300     }           
8301     
8302     gameMode = EditGame;
8303     ModeHighlight();
8304     SetGameInfo();
8305 }
8306
8307
8308 void
8309 EditPositionEvent()
8310 {
8311     if (gameMode == EditPosition) {
8312         EditGameEvent();
8313         return;
8314     }
8315     
8316     EditGameEvent();
8317     if (gameMode != EditGame) return;
8318     
8319     gameMode = EditPosition;
8320     ModeHighlight();
8321     SetGameInfo();
8322     if (currentMove > 0)
8323       CopyBoard(boards[0], boards[currentMove]);
8324     
8325     blackPlaysFirst = !WhiteOnMove(currentMove);
8326     ResetClocks();
8327     currentMove = forwardMostMove = backwardMostMove = 0;
8328     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
8329     DisplayMove(-1);
8330 }
8331
8332 void
8333 ExitAnalyzeMode()
8334 {
8335     if (first.analysisSupport && first.analyzing) {
8336       SendToProgram("exit\n", &first);
8337       first.analyzing = FALSE;
8338     }
8339     AnalysisPopDown();
8340     thinkOutput[0] = NULLCHAR;
8341 }
8342
8343 void
8344 EditPositionDone()
8345 {
8346     startedFromSetupPosition = TRUE;
8347     InitChessProgram(&first);
8348     SendToProgram("force\n", &first);
8349     if (blackPlaysFirst) {
8350         strcpy(moveList[0], "");
8351         strcpy(parseList[0], "");
8352         currentMove = forwardMostMove = backwardMostMove = 1;
8353         CopyBoard(boards[1], boards[0]);
8354     } else {
8355         currentMove = forwardMostMove = backwardMostMove = 0;
8356     }
8357     SendBoard(&first, forwardMostMove);
8358     DisplayTitle("");
8359     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
8360     timeRemaining[1][forwardMostMove] = blackTimeRemaining;
8361     gameMode = EditGame;
8362     ModeHighlight();
8363     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
8364 }
8365
8366 /* Pause for `ms' milliseconds */
8367 /* !! Ugh, this is a kludge. Fix it sometime. --tpm */
8368 void
8369 TimeDelay(ms)
8370      long ms;
8371 {
8372     TimeMark m1, m2;
8373
8374     GetTimeMark(&m1);
8375     do {
8376         GetTimeMark(&m2);
8377     } while (SubtractTimeMarks(&m2, &m1) < ms);
8378 }
8379
8380 /* !! Ugh, this is a kludge. Fix it sometime. --tpm */
8381 void
8382 SendMultiLineToICS(buf)
8383      char *buf;
8384 {
8385     char temp[MSG_SIZ+1], *p;
8386     int len;
8387
8388     len = strlen(buf);
8389     if (len > MSG_SIZ)
8390       len = MSG_SIZ;
8391   
8392     strncpy(temp, buf, len);
8393     temp[len] = 0;
8394
8395     p = temp;
8396     while (*p) {
8397         if (*p == '\n' || *p == '\r')
8398           *p = ' ';
8399         ++p;
8400     }
8401
8402     strcat(temp, "\n");
8403     SendToICS(temp);
8404     SendToPlayer(temp, strlen(temp));
8405 }
8406
8407 void
8408 SetWhiteToPlayEvent()
8409 {
8410     if (gameMode == EditPosition) {
8411         blackPlaysFirst = FALSE;
8412         DisplayBothClocks();    /* works because currentMove is 0 */
8413     } else if (gameMode == IcsExamining) {
8414         SendToICS(ics_prefix);
8415         SendToICS("tomove white\n");
8416     }
8417 }
8418
8419 void
8420 SetBlackToPlayEvent()
8421 {
8422     if (gameMode == EditPosition) {
8423         blackPlaysFirst = TRUE;
8424         currentMove = 1;        /* kludge */
8425         DisplayBothClocks();
8426         currentMove = 0;
8427     } else if (gameMode == IcsExamining) {
8428         SendToICS(ics_prefix);
8429         SendToICS("tomove black\n");
8430     }
8431 }
8432
8433 void
8434 EditPositionMenuEvent(selection, x, y)
8435      ChessSquare selection;
8436      int x, y;
8437 {
8438     char buf[MSG_SIZ];
8439
8440     if (gameMode != EditPosition && gameMode != IcsExamining) return;
8441
8442     switch (selection) {
8443       case ClearBoard:
8444         if (gameMode == IcsExamining && ics_type == ICS_FICS) {
8445             SendToICS(ics_prefix);
8446             SendToICS("bsetup clear\n");
8447         } else if (gameMode == IcsExamining && ics_type == ICS_ICC) {
8448             SendToICS(ics_prefix);
8449             SendToICS("clearboard\n");
8450         } else {
8451             for (x = 0; x < BOARD_SIZE; x++) {
8452                 for (y = 0; y < BOARD_SIZE; y++) {
8453                     if (gameMode == IcsExamining) {
8454                         if (boards[currentMove][y][x] != EmptySquare) {
8455                             sprintf(buf, "%sx@%c%c\n", ics_prefix,
8456                                     'a' + x, '1' + y);
8457                             SendToICS(buf);
8458                         }
8459                     } else {
8460                         boards[0][y][x] = EmptySquare;
8461                     }
8462                 }
8463             }
8464         }
8465         if (gameMode == EditPosition) {
8466             DrawPosition(FALSE, boards[0]);
8467         }
8468         break;
8469
8470       case WhitePlay:
8471         SetWhiteToPlayEvent();
8472         break;
8473
8474       case BlackPlay:
8475         SetBlackToPlayEvent();
8476         break;
8477
8478       case EmptySquare:
8479         if (gameMode == IcsExamining) {
8480             sprintf(buf, "%sx@%c%c\n", ics_prefix, 'a' + x, '1' + y);
8481             SendToICS(buf);
8482         } else {
8483             boards[0][y][x] = EmptySquare;
8484             DrawPosition(FALSE, boards[0]);
8485         }
8486         break;
8487
8488       default:
8489         if (gameMode == IcsExamining) {
8490             sprintf(buf, "%s%c@%c%c\n", ics_prefix,
8491                     PieceToChar(selection), 'a' + x, '1' + y);
8492             SendToICS(buf);
8493         } else {
8494             boards[0][y][x] = selection;
8495             DrawPosition(FALSE, boards[0]);
8496         }
8497         break;
8498     }
8499 }
8500
8501
8502 void
8503 DropMenuEvent(selection, x, y)
8504      ChessSquare selection;
8505      int x, y;
8506 {
8507     ChessMove moveType;
8508
8509     switch (gameMode) {
8510       case IcsPlayingWhite:
8511       case MachinePlaysBlack:
8512         if (!WhiteOnMove(currentMove)) {
8513             DisplayMoveError("It is Black's turn");
8514             return;
8515         }
8516         moveType = WhiteDrop;
8517         break;
8518       case IcsPlayingBlack:
8519       case MachinePlaysWhite:
8520         if (WhiteOnMove(currentMove)) {
8521             DisplayMoveError("It is White's turn");
8522             return;
8523         }
8524         moveType = BlackDrop;
8525         break;
8526       case EditGame:
8527         moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;
8528         break;
8529       default:
8530         return;
8531     }
8532
8533     if (moveType == BlackDrop && selection < BlackPawn) {
8534       selection = (ChessSquare) ((int) selection
8535                                  + (int) BlackPawn - (int) WhitePawn);
8536     }
8537     if (boards[currentMove][y][x] != EmptySquare) {
8538         DisplayMoveError("That square is occupied");
8539         return;
8540     }
8541
8542     FinishMove(moveType, (int) selection, DROP_RANK, x, y, NULLCHAR);
8543 }
8544
8545 void
8546 AcceptEvent()
8547 {
8548     /* Accept a pending offer of any kind from opponent */
8549     
8550     if (appData.icsActive) {
8551         SendToICS(ics_prefix);
8552         SendToICS("accept\n");
8553     } else if (cmailMsgLoaded) {
8554         if (currentMove == cmailOldMove &&
8555             commentList[cmailOldMove] != NULL &&
8556             StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
8557                    "Black offers a draw" : "White offers a draw")) {
8558             TruncateGame();
8559             GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
8560             cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;
8561         } else {
8562             DisplayError("There is no pending offer on this move", 0);
8563             cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
8564         }
8565     } else {
8566         /* Not used for offers from chess program */
8567     }
8568 }
8569
8570 void
8571 DeclineEvent()
8572 {
8573     /* Decline a pending offer of any kind from opponent */
8574     
8575     if (appData.icsActive) {
8576         SendToICS(ics_prefix);
8577         SendToICS("decline\n");
8578     } else if (cmailMsgLoaded) {
8579         if (currentMove == cmailOldMove &&
8580             commentList[cmailOldMove] != NULL &&
8581             StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
8582                    "Black offers a draw" : "White offers a draw")) {
8583 #ifdef NOTDEF
8584             AppendComment(cmailOldMove, "Draw declined");
8585             DisplayComment(cmailOldMove - 1, "Draw declined");
8586 #endif /*NOTDEF*/
8587         } else {
8588             DisplayError("There is no pending offer on this move", 0);
8589         }
8590     } else {
8591         /* Not used for offers from chess program */
8592     }
8593 }
8594
8595 void
8596 RematchEvent()
8597 {
8598     /* Issue ICS rematch command */
8599     if (appData.icsActive) {
8600         SendToICS(ics_prefix);
8601         SendToICS("rematch\n");
8602     }
8603 }
8604
8605 void
8606 CallFlagEvent()
8607 {
8608     /* Call your opponent's flag (claim a win on time) */
8609     if (appData.icsActive) {
8610         SendToICS(ics_prefix);
8611         SendToICS("flag\n");
8612     } else {
8613         switch (gameMode) {
8614           default:
8615             return;
8616           case MachinePlaysWhite:
8617             if (whiteFlag) {
8618                 if (blackFlag)
8619                   GameEnds(GameIsDrawn, "Both players ran out of time",
8620                            GE_PLAYER);
8621                 else
8622                   GameEnds(BlackWins, "Black wins on time", GE_PLAYER);
8623             } else {
8624                 DisplayError("Your opponent is not out of time", 0);
8625             }
8626             break;
8627           case MachinePlaysBlack:
8628             if (blackFlag) {
8629                 if (whiteFlag)
8630                   GameEnds(GameIsDrawn, "Both players ran out of time",
8631                            GE_PLAYER);
8632                 else
8633                   GameEnds(WhiteWins, "White wins on time", GE_PLAYER);
8634             } else {
8635                 DisplayError("Your opponent is not out of time", 0);
8636             }
8637             break;
8638         }
8639     }
8640 }
8641
8642 void
8643 DrawEvent()
8644 {
8645     /* Offer draw or accept pending draw offer from opponent */
8646     
8647     if (appData.icsActive) {
8648         /* Note: tournament rules require draw offers to be
8649            made after you make your move but before you punch
8650            your clock.  Currently ICS doesn't let you do that;
8651            instead, you immediately punch your clock after making
8652            a move, but you can offer a draw at any time. */
8653         
8654         SendToICS(ics_prefix);
8655         SendToICS("draw\n");
8656     } else if (cmailMsgLoaded) {
8657         if (currentMove == cmailOldMove &&
8658             commentList[cmailOldMove] != NULL &&
8659             StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
8660                    "Black offers a draw" : "White offers a draw")) {
8661             GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
8662             cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;
8663         } else if (currentMove == cmailOldMove + 1) {
8664             char *offer = WhiteOnMove(cmailOldMove) ?
8665               "White offers a draw" : "Black offers a draw";
8666             AppendComment(currentMove, offer);
8667             DisplayComment(currentMove - 1, offer);
8668             cmailMoveType[lastLoadGameNumber - 1] = CMAIL_DRAW;
8669         } else {
8670             DisplayError("You must make your move before offering a draw", 0);
8671             cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
8672         }
8673     } else if (first.offeredDraw) {
8674         GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
8675     } else {
8676         if (first.sendDrawOffers) {
8677             SendToProgram("draw\n", &first);
8678             userOfferedDraw = TRUE;
8679         }
8680     }
8681 }
8682
8683 void
8684 AdjournEvent()
8685 {
8686     /* Offer Adjourn or accept pending Adjourn offer from opponent */
8687     
8688     if (appData.icsActive) {
8689         SendToICS(ics_prefix);
8690         SendToICS("adjourn\n");
8691     } else {
8692         /* Currently GNU Chess doesn't offer or accept Adjourns */
8693     }
8694 }
8695
8696
8697 void
8698 AbortEvent()
8699 {
8700     /* Offer Abort or accept pending Abort offer from opponent */
8701     
8702     if (appData.icsActive) {
8703         SendToICS(ics_prefix);
8704         SendToICS("abort\n");
8705     } else {
8706         GameEnds(GameUnfinished, "Game aborted", GE_PLAYER);
8707     }
8708 }
8709
8710 void
8711 ResignEvent()
8712 {
8713     /* Resign.  You can do this even if it's not your turn. */
8714     
8715     if (appData.icsActive) {
8716         SendToICS(ics_prefix);
8717         SendToICS("resign\n");
8718     } else {
8719         switch (gameMode) {
8720           case MachinePlaysWhite:
8721             GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
8722             break;
8723           case MachinePlaysBlack:
8724             GameEnds(BlackWins, "White resigns", GE_PLAYER);
8725             break;
8726           case EditGame:
8727             if (cmailMsgLoaded) {
8728                 TruncateGame();
8729                 if (WhiteOnMove(cmailOldMove)) {
8730                     GameEnds(BlackWins, "White resigns", GE_PLAYER);
8731                 } else {
8732                     GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
8733                 }
8734                 cmailMoveType[lastLoadGameNumber - 1] = CMAIL_RESIGN;
8735             }
8736             break;
8737           default:
8738             break;
8739         }
8740     }
8741 }
8742
8743
8744 void
8745 StopObservingEvent()
8746 {
8747     /* Stop observing current games */
8748     SendToICS(ics_prefix);
8749     SendToICS("unobserve\n");
8750 }
8751
8752 void
8753 StopExaminingEvent()
8754 {
8755     /* Stop observing current game */
8756     SendToICS(ics_prefix);
8757     SendToICS("unexamine\n");
8758 }
8759
8760 void
8761 ForwardInner(target)
8762      int target;
8763 {
8764     int limit;
8765
8766     if (appData.debugMode)
8767         fprintf(debugFP, "ForwardInner(%d), current %d, forward %d\n",
8768                 target, currentMove, forwardMostMove);
8769
8770     if (gameMode == EditPosition)
8771       return;
8772
8773     if (gameMode == PlayFromGameFile && !pausing)
8774       PauseEvent();
8775     
8776     if (gameMode == IcsExamining && pausing)
8777       limit = pauseExamForwardMostMove;
8778     else
8779       limit = forwardMostMove;
8780     
8781     if (target > limit) target = limit;
8782
8783     if (target > 0 && moveList[target - 1][0]) {
8784         int fromX, fromY, toX, toY;
8785         toX = moveList[target - 1][2] - 'a';
8786         toY = moveList[target - 1][3] - '1';
8787         if (moveList[target - 1][1] == '@') {
8788             if (appData.highlightLastMove) {
8789                 SetHighlights(-1, -1, toX, toY);
8790             }
8791         } else {
8792             fromX = moveList[target - 1][0] - 'a';
8793             fromY = moveList[target - 1][1] - '1';
8794             if (target == currentMove + 1) {
8795                 AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
8796             }
8797             if (appData.highlightLastMove) {
8798                 SetHighlights(fromX, fromY, toX, toY);
8799             }
8800         }
8801     }
8802     if (gameMode == EditGame || gameMode == AnalyzeMode || 
8803         gameMode == Training || gameMode == PlayFromGameFile || 
8804         gameMode == AnalyzeFile) {
8805         while (currentMove < target) {
8806             SendMoveToProgram(currentMove++, &first);
8807         }
8808     } else {
8809         currentMove = target;
8810     }
8811     
8812     if (gameMode == EditGame || gameMode == EndOfGame) {
8813         whiteTimeRemaining = timeRemaining[0][currentMove];
8814         blackTimeRemaining = timeRemaining[1][currentMove];
8815     }
8816     DisplayBothClocks();
8817     DisplayMove(currentMove - 1);
8818     DrawPosition(FALSE, boards[currentMove]);
8819     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
8820     if (commentList[currentMove] && !matchMode && gameMode != Training) {
8821         DisplayComment(currentMove - 1, commentList[currentMove]);
8822     }
8823 }
8824
8825
8826 void
8827 ForwardEvent()
8828 {
8829     if (gameMode == IcsExamining && !pausing) {
8830         SendToICS(ics_prefix);
8831         SendToICS("forward\n");
8832     } else {
8833         ForwardInner(currentMove + 1);
8834     }
8835 }
8836
8837 void
8838 ToEndEvent()
8839 {
8840     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
8841         /* to optimze, we temporarily turn off analysis mode while we feed
8842          * the remaining moves to the engine. Otherwise we get analysis output
8843          * after each move.
8844          */ 
8845         if (first.analysisSupport) {
8846           SendToProgram("exit\nforce\n", &first);
8847           first.analyzing = FALSE;
8848         }
8849     }
8850         
8851     if (gameMode == IcsExamining && !pausing) {
8852         SendToICS(ics_prefix);
8853         SendToICS("forward 999999\n");
8854     } else {
8855         ForwardInner(forwardMostMove);
8856     }
8857
8858     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
8859         /* we have fed all the moves, so reactivate analysis mode */
8860         SendToProgram("analyze\n", &first);
8861         first.analyzing = TRUE;
8862         /*first.maybeThinking = TRUE;*/
8863         first.maybeThinking = FALSE; /* avoid killing GNU Chess */
8864     }
8865 }
8866
8867 void
8868 BackwardInner(target)
8869      int target;
8870 {
8871     if (appData.debugMode)
8872         fprintf(debugFP, "BackwardInner(%d), current %d, forward %d\n",
8873                 target, currentMove, forwardMostMove);
8874
8875     if (gameMode == EditPosition) return;
8876     if (currentMove <= backwardMostMove) {
8877         ClearHighlights();
8878         DrawPosition(FALSE, boards[currentMove]);
8879         return;
8880     }
8881     if (gameMode == PlayFromGameFile && !pausing)
8882       PauseEvent();
8883     
8884     if (moveList[target][0]) {
8885         int fromX, fromY, toX, toY;
8886         toX = moveList[target][2] - 'a';
8887         toY = moveList[target][3] - '1';
8888         if (moveList[target][1] == '@') {
8889             if (appData.highlightLastMove) {
8890                 SetHighlights(-1, -1, toX, toY);
8891             }
8892         } else {
8893             fromX = moveList[target][0] - 'a';
8894             fromY = moveList[target][1] - '1';
8895             if (target == currentMove - 1) {
8896                 AnimateMove(boards[currentMove], toX, toY, fromX, fromY);
8897             }
8898             if (appData.highlightLastMove) {
8899                 SetHighlights(fromX, fromY, toX, toY);
8900             }
8901         }
8902     }
8903     if (gameMode == EditGame || gameMode==AnalyzeMode ||
8904         gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
8905         while (currentMove > target) {
8906             SendToProgram("undo\n", &first);
8907             currentMove--;
8908         }
8909     } else {
8910         currentMove = target;
8911     }
8912     
8913     if (gameMode == EditGame || gameMode == EndOfGame) {
8914         whiteTimeRemaining = timeRemaining[0][currentMove];
8915         blackTimeRemaining = timeRemaining[1][currentMove];
8916     }
8917     DisplayBothClocks();
8918     DisplayMove(currentMove - 1);
8919     DrawPosition(FALSE, boards[currentMove]);
8920     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
8921     if (commentList[currentMove] != NULL) {
8922         DisplayComment(currentMove - 1, commentList[currentMove]);
8923     }
8924 }
8925
8926 void
8927 BackwardEvent()
8928 {
8929     if (gameMode == IcsExamining && !pausing) {
8930         SendToICS(ics_prefix);
8931         SendToICS("backward\n");
8932     } else {
8933         BackwardInner(currentMove - 1);
8934     }
8935 }
8936
8937 void
8938 ToStartEvent()
8939 {
8940     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
8941         /* to optimze, we temporarily turn off analysis mode while we undo
8942          * all the moves. Otherwise we get analysis output after each undo.
8943          */ 
8944         if (first.analysisSupport) {
8945           SendToProgram("exit\nforce\n", &first);
8946           first.analyzing = FALSE;
8947         }
8948     }
8949
8950     if (gameMode == IcsExamining && !pausing) {
8951         SendToICS(ics_prefix);
8952         SendToICS("backward 999999\n");
8953     } else {
8954         BackwardInner(backwardMostMove);
8955     }
8956
8957     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
8958         /* we have fed all the moves, so reactivate analysis mode */
8959         SendToProgram("analyze\n", &first);
8960         first.analyzing = TRUE;
8961         /*first.maybeThinking = TRUE;*/
8962         first.maybeThinking = FALSE; /* avoid killing GNU Chess */
8963     }
8964 }
8965
8966 void
8967 ToNrEvent(int to)
8968 {
8969   if (gameMode == PlayFromGameFile && !pausing) PauseEvent();
8970   if (to >= forwardMostMove) to = forwardMostMove;
8971   if (to <= backwardMostMove) to = backwardMostMove;
8972   if (to < currentMove) {
8973     BackwardInner(to);
8974   } else {
8975     ForwardInner(to);
8976   }
8977 }
8978
8979 void
8980 RevertEvent()
8981 {
8982     if (gameMode != IcsExamining) {
8983         DisplayError("You are not examining a game", 0);
8984         return;
8985     }
8986     if (pausing) {
8987         DisplayError("You can't revert while pausing", 0);
8988         return;
8989     }
8990     SendToICS(ics_prefix);
8991     SendToICS("revert\n");
8992 }
8993
8994 void
8995 RetractMoveEvent()
8996 {
8997     switch (gameMode) {
8998       case MachinePlaysWhite:
8999       case MachinePlaysBlack:
9000         if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {
9001             DisplayError("Wait until your turn,\nor select Move Now", 0);
9002             return;
9003         }
9004         if (forwardMostMove < 2) return;
9005         currentMove = forwardMostMove = forwardMostMove - 2;
9006         whiteTimeRemaining = timeRemaining[0][currentMove];
9007         blackTimeRemaining = timeRemaining[1][currentMove];
9008         DisplayBothClocks();
9009         DisplayMove(currentMove - 1);
9010         ClearHighlights();/*!! could figure this out*/
9011         DrawPosition(FALSE, boards[currentMove]);
9012         SendToProgram("remove\n", &first);
9013         /*first.maybeThinking = TRUE;*/ /* GNU Chess does not ponder here */
9014         break;
9015
9016       case BeginningOfGame:
9017       default:
9018         break;
9019
9020       case IcsPlayingWhite:
9021       case IcsPlayingBlack:
9022         if (WhiteOnMove(forwardMostMove) == (gameMode == IcsPlayingWhite)) {
9023             SendToICS(ics_prefix);
9024             SendToICS("takeback 2\n");
9025         } else {
9026             SendToICS(ics_prefix);
9027             SendToICS("takeback 1\n");
9028         }
9029         break;
9030     }
9031 }
9032
9033 void
9034 MoveNowEvent()
9035 {
9036     ChessProgramState *cps;
9037
9038     switch (gameMode) {
9039       case MachinePlaysWhite:
9040         if (!WhiteOnMove(forwardMostMove)) {
9041             DisplayError("It is your turn", 0);
9042             return;
9043         }
9044         cps = &first;
9045         break;
9046       case MachinePlaysBlack:
9047         if (WhiteOnMove(forwardMostMove)) {
9048             DisplayError("It is your turn", 0);
9049             return;
9050         }
9051         cps = &first;
9052         break;
9053       case TwoMachinesPlay:
9054         if (WhiteOnMove(forwardMostMove) ==
9055             (first.twoMachinesColor[0] == 'w')) {
9056             cps = &first;
9057         } else {
9058             cps = &second;
9059         }
9060         break;
9061       case BeginningOfGame:
9062       default:
9063         return;
9064     }
9065     SendToProgram("?\n", cps);
9066 }
9067
9068 void
9069 TruncateGameEvent()
9070 {
9071     EditGameEvent();
9072     if (gameMode != EditGame) return;
9073     TruncateGame();
9074 }
9075
9076 void
9077 TruncateGame()
9078 {
9079     if (forwardMostMove > currentMove) {
9080         if (gameInfo.resultDetails != NULL) {
9081             free(gameInfo.resultDetails);
9082             gameInfo.resultDetails = NULL;
9083             gameInfo.result = GameUnfinished;
9084         }
9085         forwardMostMove = currentMove;
9086         HistorySet(parseList, backwardMostMove, forwardMostMove,
9087                    currentMove-1);
9088     }
9089 }
9090
9091 void
9092 HintEvent()
9093 {
9094     if (appData.noChessProgram) return;
9095     switch (gameMode) {
9096       case MachinePlaysWhite:
9097         if (WhiteOnMove(forwardMostMove)) {
9098             DisplayError("Wait until your turn", 0);
9099             return;
9100         }
9101         break;
9102       case BeginningOfGame:
9103       case MachinePlaysBlack:
9104         if (!WhiteOnMove(forwardMostMove)) {
9105             DisplayError("Wait until your turn", 0);
9106             return;
9107         }
9108         break;
9109       default:
9110         DisplayError("No hint available", 0);
9111         return;
9112     }
9113     SendToProgram("hint\n", &first);
9114     hintRequested = TRUE;
9115 }
9116
9117 void
9118 BookEvent()
9119 {
9120     if (appData.noChessProgram) return;
9121     switch (gameMode) {
9122       case MachinePlaysWhite:
9123         if (WhiteOnMove(forwardMostMove)) {
9124             DisplayError("Wait until your turn", 0);
9125             return;
9126         }
9127         break;
9128       case BeginningOfGame:
9129       case MachinePlaysBlack:
9130         if (!WhiteOnMove(forwardMostMove)) {
9131             DisplayError("Wait until your turn", 0);
9132             return;
9133         }
9134         break;
9135       case EditPosition:
9136         EditPositionDone();
9137         break;
9138       case TwoMachinesPlay:
9139         return;
9140       default:
9141         break;
9142     }
9143     SendToProgram("bk\n", &first);
9144     bookOutput[0] = NULLCHAR;
9145     bookRequested = TRUE;
9146 }
9147
9148 void
9149 AboutGameEvent()
9150 {
9151     char *tags = PGNTags(&gameInfo);
9152     TagsPopUp(tags, CmailMsg());
9153     free(tags);
9154 }
9155
9156 /* end button procedures */
9157
9158 void
9159 PrintPosition(fp, move)
9160      FILE *fp;
9161      int move;
9162 {
9163     int i, j;
9164     
9165     for (i = BOARD_SIZE - 1; i >= 0; i--) {
9166         for (j = 0; j < BOARD_SIZE; j++) {
9167             char c = PieceToChar(boards[move][i][j]);
9168             fputc(c == 'x' ? '.' : c, fp);
9169             fputc(j == BOARD_SIZE - 1 ? '\n' : ' ', fp);
9170         }
9171     }
9172     if ((gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0))
9173       fprintf(fp, "white to play\n");
9174     else
9175       fprintf(fp, "black to play\n");
9176 }
9177
9178 void
9179 PrintOpponents(fp)
9180      FILE *fp;
9181 {
9182     if (gameInfo.white != NULL) {
9183         fprintf(fp, "\t%s vs. %s\n", gameInfo.white, gameInfo.black);
9184     } else {
9185         fprintf(fp, "\n");
9186     }
9187 }
9188
9189 /* Find last component of program's own name, using some heuristics */
9190 void
9191 TidyProgramName(prog, host, buf)
9192      char *prog, *host, buf[MSG_SIZ];
9193 {
9194     char *p, *q;
9195     int local = (strcmp(host, "localhost") == 0);
9196     while (!local && (p = strchr(prog, ';')) != NULL) {
9197         p++;
9198         while (*p == ' ') p++;
9199         prog = p;
9200     }
9201     if (*prog == '"' || *prog == '\'') {
9202         q = strchr(prog + 1, *prog);
9203     } else {
9204         q = strchr(prog, ' ');
9205     }
9206     if (q == NULL) q = prog + strlen(prog);
9207     p = q;
9208     while (p >= prog && *p != '/' && *p != '\\') p--;
9209     p++;
9210     if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4;
9211     memcpy(buf, p, q - p);
9212     buf[q - p] = NULLCHAR;
9213     if (!local) {
9214         strcat(buf, "@");
9215         strcat(buf, host);
9216     }
9217 }
9218
9219 char *
9220 TimeControlTagValue()
9221 {
9222     char buf[MSG_SIZ];
9223     if (!appData.clockMode) {
9224         strcpy(buf, "-");
9225     } else if (movesPerSession > 0) {
9226         sprintf(buf, "%d/%ld", movesPerSession, timeControl/1000);
9227     } else if (timeIncrement == 0) {
9228         sprintf(buf, "%ld", timeControl/1000);
9229     } else {
9230         sprintf(buf, "%ld+%ld", timeControl/1000, timeIncrement/1000);
9231     }
9232     return StrSave(buf);
9233 }
9234
9235 void
9236 SetGameInfo()
9237 {
9238     /* This routine is used only for certain modes */
9239     VariantClass v = gameInfo.variant;
9240     ClearGameInfo(&gameInfo);
9241     gameInfo.variant = v;
9242
9243     switch (gameMode) {
9244       case MachinePlaysWhite:
9245         gameInfo.event = StrSave("Computer chess game");
9246         gameInfo.site = StrSave(HostName());
9247         gameInfo.date = PGNDate();
9248         gameInfo.round = StrSave("-");
9249         gameInfo.white = StrSave(first.tidy);
9250         gameInfo.black = StrSave(UserName());
9251         gameInfo.timeControl = TimeControlTagValue();
9252         break;
9253
9254       case MachinePlaysBlack:
9255         gameInfo.event = StrSave("Computer chess game");
9256         gameInfo.site = StrSave(HostName());
9257         gameInfo.date = PGNDate();
9258         gameInfo.round = StrSave("-");
9259         gameInfo.white = StrSave(UserName());
9260         gameInfo.black = StrSave(first.tidy);
9261         gameInfo.timeControl = TimeControlTagValue();
9262         break;
9263
9264       case TwoMachinesPlay:
9265         gameInfo.event = StrSave("Computer chess game");
9266         gameInfo.site = StrSave(HostName());
9267         gameInfo.date = PGNDate();
9268         if (matchGame > 0) {
9269             char buf[MSG_SIZ];
9270             sprintf(buf, "%d", matchGame);
9271             gameInfo.round = StrSave(buf);
9272         } else {
9273             gameInfo.round = StrSave("-");
9274         }
9275         if (first.twoMachinesColor[0] == 'w') {
9276             gameInfo.white = StrSave(first.tidy);
9277             gameInfo.black = StrSave(second.tidy);
9278         } else {
9279             gameInfo.white = StrSave(second.tidy);
9280             gameInfo.black = StrSave(first.tidy);
9281         }
9282         gameInfo.timeControl = TimeControlTagValue();
9283         break;
9284
9285       case EditGame:
9286         gameInfo.event = StrSave("Edited game");
9287         gameInfo.site = StrSave(HostName());
9288         gameInfo.date = PGNDate();
9289         gameInfo.round = StrSave("-");
9290         gameInfo.white = StrSave("-");
9291         gameInfo.black = StrSave("-");
9292         break;
9293
9294       case EditPosition:
9295         gameInfo.event = StrSave("Edited position");
9296         gameInfo.site = StrSave(HostName());
9297         gameInfo.date = PGNDate();
9298         gameInfo.round = StrSave("-");
9299         gameInfo.white = StrSave("-");
9300         gameInfo.black = StrSave("-");
9301         break;
9302
9303       case IcsPlayingWhite:
9304       case IcsPlayingBlack:
9305       case IcsObserving:
9306       case IcsExamining:
9307         break;
9308
9309       case PlayFromGameFile:
9310         gameInfo.event = StrSave("Game from non-PGN file");
9311         gameInfo.site = StrSave(HostName());
9312         gameInfo.date = PGNDate();
9313         gameInfo.round = StrSave("-");
9314         gameInfo.white = StrSave("?");
9315         gameInfo.black = StrSave("?");
9316         break;
9317
9318       default:
9319         break;
9320     }
9321 }
9322
9323 void
9324 ReplaceComment(index, text)
9325      int index;
9326      char *text;
9327 {
9328     int len;
9329
9330     while (*text == '\n') text++;
9331     len = strlen(text);
9332     while (len > 0 && text[len - 1] == '\n') len--;
9333
9334     if (commentList[index] != NULL)
9335       free(commentList[index]);
9336
9337     if (len == 0) {
9338         commentList[index] = NULL;
9339         return;
9340     }
9341     commentList[index] = (char *) malloc(len + 2);
9342     strncpy(commentList[index], text, len);
9343     commentList[index][len] = '\n';
9344     commentList[index][len + 1] = NULLCHAR;
9345 }
9346
9347 void
9348 AppendComment(index, text)
9349      int index;
9350      char *text;
9351 {
9352     int oldlen, len;
9353     char *old;
9354
9355     while (*text == '\n') text++;
9356     len = strlen(text);
9357     while (len > 0 && text[len - 1] == '\n') len--;
9358
9359     if (len == 0) return;
9360
9361     if (commentList[index] != NULL) {
9362         old = commentList[index];
9363         oldlen = strlen(old);
9364         commentList[index] = (char *) malloc(oldlen + len + 2);
9365         strcpy(commentList[index], old);
9366         free(old);
9367         strncpy(&commentList[index][oldlen], text, len);
9368         commentList[index][oldlen + len] = '\n';
9369         commentList[index][oldlen + len + 1] = NULLCHAR;
9370     } else {
9371         commentList[index] = (char *) malloc(len + 2);
9372         strncpy(commentList[index], text, len);
9373         commentList[index][len] = '\n';
9374         commentList[index][len + 1] = NULLCHAR;
9375     }
9376 }
9377
9378 void
9379 SendToProgram(message, cps)
9380      char *message;
9381      ChessProgramState *cps;
9382 {
9383     int count, outCount, error;
9384     char buf[MSG_SIZ];
9385
9386     if (cps->pr == NULL) return;
9387     Attention(cps);
9388     
9389     if (appData.debugMode) {
9390         TimeMark now;
9391         GetTimeMark(&now);
9392         fprintf(debugFP, "%ld >%-6s: %s", 
9393                 SubtractTimeMarks(&now, &programStartTime),
9394                 cps->which, message);
9395     }
9396     
9397     count = strlen(message);
9398     outCount = OutputToProcess(cps->pr, message, count, &error);
9399     if (outCount < count && !exiting) {
9400         sprintf(buf, "Error writing to %s chess program", cps->which);
9401         DisplayFatalError(buf, error, 1);
9402     }
9403 }
9404
9405 void
9406 ReceiveFromProgram(isr, closure, message, count, error)
9407      InputSourceRef isr;
9408      VOIDSTAR closure;
9409      char *message;
9410      int count;
9411      int error;
9412 {
9413     char *end_str;
9414     char buf[MSG_SIZ];
9415     ChessProgramState *cps = (ChessProgramState *)closure;
9416
9417     if (isr != cps->isr) return; /* Killed intentionally */
9418     if (count <= 0) {
9419         if (count == 0) {
9420             sprintf(buf,
9421                     "Error: %s chess program (%s) exited unexpectedly",
9422                     cps->which, cps->program);
9423             RemoveInputSource(cps->isr);
9424             DisplayFatalError(buf, 0, 1);
9425         } else {
9426             sprintf(buf,
9427                     "Error reading from %s chess program (%s)",
9428                     cps->which, cps->program);
9429             RemoveInputSource(cps->isr);
9430             DisplayFatalError(buf, error, 1);
9431         }
9432         GameEnds((ChessMove) 0, NULL, GE_PLAYER);
9433         return;
9434     }
9435     
9436     if ((end_str = strchr(message, '\r')) != NULL)
9437       *end_str = NULLCHAR;
9438     if ((end_str = strchr(message, '\n')) != NULL)
9439       *end_str = NULLCHAR;
9440     
9441     if (appData.debugMode) {
9442         TimeMark now;
9443         GetTimeMark(&now);
9444         fprintf(debugFP, "%ld <%-6s: %s\n", 
9445                 SubtractTimeMarks(&now, &programStartTime),
9446                 cps->which, message);
9447     }
9448
9449         if (appData.icsAnalyze) {
9450                 /* wb2 - can reset a setting - we are in possible ics mode - drop */
9451                 if (strstr(message, "whisper") != NULL ||
9452                     strstr(message, "kibitz") != NULL || 
9453                         strstr(message, "set 1") != NULL) {
9454                         if (appData.debugMode) fprintf(debugFP, "ICS Analyze: Drop engine output\n");
9455                 } else {
9456                         HandleMachineMove(message, cps);
9457                 }
9458         } else {
9459                 HandleMachineMove(message, cps);
9460         }
9461 }
9462
9463
9464 void
9465 SendTimeControl(cps, mps, tc, inc, sd, st)
9466      ChessProgramState *cps;
9467      int mps, inc, sd, st;
9468      long tc;
9469 {
9470     char buf[MSG_SIZ];
9471     int seconds = (tc / 1000) % 60;
9472
9473     if (st > 0) {
9474       /* Set exact time per move, normally using st command */
9475       if (cps->stKludge) {
9476         /* GNU Chess 4 has no st command; uses level in a nonstandard way */
9477         seconds = st % 60;
9478         if (seconds == 0) {
9479           sprintf(buf, "level 1 %d\n", st/60);
9480         } else {
9481           sprintf(buf, "level 1 %d:%02d\n", st/60, seconds);
9482         }
9483       } else {
9484         sprintf(buf, "st %d\n", st);
9485       }
9486     } else {
9487       /* Set conventional or incremental time control, using level command */
9488       if (seconds == 0) {
9489         /* Note old gnuchess bug -- minutes:seconds used to not work.
9490            Fixed in later versions, but still avoid :seconds
9491            when seconds is 0. */
9492         sprintf(buf, "level %d %ld %d\n", mps, tc/60000, inc/1000);
9493       } else {
9494         sprintf(buf, "level %d %ld:%02d %d\n", mps, tc/60000,
9495                 seconds, inc/1000);
9496       }
9497     }
9498     SendToProgram(buf, cps);
9499
9500     /* Orthoganally (except for GNU Chess 4), limit time to st seconds */
9501     /* Orthogonally, limit search to given depth */
9502     if (sd > 0) {
9503       if (cps->sdKludge) {
9504         sprintf(buf, "depth\n%d\n", sd);
9505       } else {
9506         sprintf(buf, "sd %d\n", sd);
9507       }
9508       SendToProgram(buf, cps);
9509     }
9510 }
9511
9512 void
9513 SendTimeRemaining(cps, machineWhite)
9514      ChessProgramState *cps;
9515      int /*boolean*/ machineWhite;
9516 {
9517     char message[MSG_SIZ];
9518     long time, otime;
9519
9520     /* Note: this routine must be called when the clocks are stopped
9521        or when they have *just* been set or switched; otherwise
9522        it will be off by the time since the current tick started.
9523     */
9524     if (machineWhite) {
9525         time = whiteTimeRemaining / 10;
9526         otime = blackTimeRemaining / 10;
9527     } else {
9528         time = blackTimeRemaining / 10;
9529         otime = whiteTimeRemaining / 10;
9530     }
9531     if (time <= 0) time = 1;
9532     if (otime <= 0) otime = 1;
9533     
9534     sprintf(message, "time %ld\notim %ld\n", time, otime);
9535     SendToProgram(message, cps);
9536 }
9537
9538 int
9539 BoolFeature(p, name, loc, cps)
9540      char **p;
9541      char *name;
9542      int *loc;
9543      ChessProgramState *cps;
9544 {
9545   char buf[MSG_SIZ];
9546   int len = strlen(name);
9547   int val;
9548   if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {
9549     (*p) += len + 1;
9550     sscanf(*p, "%d", &val);
9551     *loc = (val != 0);
9552     while (**p && **p != ' ') (*p)++;
9553     sprintf(buf, "accepted %s\n", name);
9554     SendToProgram(buf, cps);
9555     return TRUE;
9556   }
9557   return FALSE;
9558 }
9559
9560 int
9561 IntFeature(p, name, loc, cps)
9562      char **p;
9563      char *name;
9564      int *loc;
9565      ChessProgramState *cps;
9566 {
9567   char buf[MSG_SIZ];
9568   int len = strlen(name);
9569   if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {
9570     (*p) += len + 1;
9571     sscanf(*p, "%d", loc);
9572     while (**p && **p != ' ') (*p)++;
9573     sprintf(buf, "accepted %s\n", name);
9574     SendToProgram(buf, cps);
9575     return TRUE;
9576   }
9577   return FALSE;
9578 }
9579
9580 int
9581 StringFeature(p, name, loc, cps)
9582      char **p;
9583      char *name;
9584      char loc[];
9585      ChessProgramState *cps;
9586 {
9587   char buf[MSG_SIZ];
9588   int len = strlen(name);
9589   if (strncmp((*p), name, len) == 0
9590       && (*p)[len] == '=' && (*p)[len+1] == '\"') {
9591     (*p) += len + 2;
9592     sscanf(*p, "%[^\"]", loc);
9593     while (**p && **p != '\"') (*p)++;
9594     if (**p == '\"') (*p)++;
9595     sprintf(buf, "accepted %s\n", name);
9596     SendToProgram(buf, cps);
9597     return TRUE;
9598   }
9599   return FALSE;
9600 }
9601
9602 void
9603 FeatureDone(cps, val)
9604      ChessProgramState* cps;
9605      int val;
9606 {
9607   DelayedEventCallback cb = GetDelayedEvent();
9608   if ((cb == InitBackEnd3 && cps == &first) ||
9609       (cb == TwoMachinesEventIfReady && cps == &second)) {
9610     CancelDelayedEvent();
9611     ScheduleDelayedEvent(cb, val ? 1 : 3600000);
9612   }
9613   cps->initDone = val;
9614 }
9615
9616 /* Parse feature command from engine */
9617 void
9618 ParseFeatures(args, cps)
9619      char* args;
9620      ChessProgramState *cps;  
9621 {
9622   char *p = args;
9623   char *q;
9624   int val;
9625   char buf[MSG_SIZ];
9626
9627   for (;;) {
9628     while (*p == ' ') p++;
9629     if (*p == NULLCHAR) return;
9630
9631     if (BoolFeature(&p, "setboard", &cps->useSetboard, cps)) continue;
9632     if (BoolFeature(&p, "time", &cps->sendTime, cps)) continue;    
9633     if (BoolFeature(&p, "draw", &cps->sendDrawOffers, cps)) continue;    
9634     if (BoolFeature(&p, "sigint", &cps->useSigint, cps)) continue;    
9635     if (BoolFeature(&p, "sigterm", &cps->useSigterm, cps)) continue;    
9636     if (BoolFeature(&p, "reuse", &val, cps)) {
9637       /* Engine can disable reuse, but can't enable it if user said no */
9638       if (!val) cps->reuse = FALSE;
9639       continue;
9640     }
9641     if (BoolFeature(&p, "analyze", &cps->analysisSupport, cps)) continue;
9642     if (StringFeature(&p, "myname", &cps->tidy, cps)) {
9643       if (gameMode == TwoMachinesPlay) {
9644         DisplayTwoMachinesTitle();
9645       } else {
9646         DisplayTitle("");
9647       }
9648       continue;
9649     }
9650     if (StringFeature(&p, "variants", &cps->variants, cps)) continue;
9651     if (BoolFeature(&p, "san", &cps->useSAN, cps)) continue;
9652     if (BoolFeature(&p, "ping", &cps->usePing, cps)) continue;
9653     if (BoolFeature(&p, "playother", &cps->usePlayother, cps)) continue;
9654     if (BoolFeature(&p, "colors", &cps->useColors, cps)) continue;
9655     if (BoolFeature(&p, "usermove", &cps->useUsermove, cps)) continue;
9656     if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;
9657     if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;
9658     if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */
9659     if (IntFeature(&p, "done", &val, cps)) {
9660       FeatureDone(cps, val);
9661       continue;
9662     }
9663
9664     /* unknown feature: complain and skip */
9665     q = p;
9666     while (*q && *q != '=') q++;
9667     sprintf(buf, "rejected %.*s\n", q-p, p);
9668     SendToProgram(buf, cps);
9669     p = q;
9670     if (*p == '=') {
9671       p++;
9672       if (*p == '\"') {
9673         p++;
9674         while (*p && *p != '\"') p++;
9675         if (*p == '\"') p++;
9676       } else {
9677         while (*p && *p != ' ') p++;
9678       }
9679     }
9680   }
9681
9682 }
9683
9684 void
9685 PeriodicUpdatesEvent(newState)
9686      int newState;
9687 {
9688     if (newState == appData.periodicUpdates)
9689       return;
9690
9691     appData.periodicUpdates=newState;
9692
9693     /* Display type changes, so update it now */
9694     DisplayAnalysis(0,0);
9695
9696     /* Get the ball rolling again... */
9697     if (newState) {
9698                 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile ||
9699                         appData.icsAnalyze || appData.AnalysisWindow) {
9700                                 AnalysisPeriodicEvent(1);
9701                                 StartAnalysisClock();
9702                 }
9703     }
9704 }
9705
9706 void
9707 PonderNextMoveEvent(newState)
9708      int newState;
9709 {
9710     if (newState == appData.ponderNextMove) return;
9711     if (gameMode == EditPosition) EditPositionDone();
9712     if (newState) {
9713         SendToProgram("hard\n", &first);
9714         if (gameMode == TwoMachinesPlay) {
9715             SendToProgram("hard\n", &second);
9716         }
9717     } else {
9718         SendToProgram("easy\n", &first);
9719         thinkOutput[0] = NULLCHAR;
9720         if (gameMode == TwoMachinesPlay) {
9721             SendToProgram("easy\n", &second);
9722         }
9723     }
9724     appData.ponderNextMove = newState;
9725 }
9726
9727 void
9728 ShowThinkingEvent(newState)
9729      int newState;
9730 {
9731     if (newState == appData.showThinking) return;
9732     if (gameMode == EditPosition) EditPositionDone();
9733     if (newState) {
9734         SendToProgram("post\n", &first);
9735         if (gameMode == TwoMachinesPlay) {
9736             SendToProgram("post\n", &second);
9737         }
9738     } else {
9739                 if (appData.AnalysisWindow) {
9740                         AnalysisPopDown(); 
9741                         appData.AnalysisWindow = FALSE;
9742         }
9743         SendToProgram("nopost\n", &first);
9744         thinkOutput[0] = NULLCHAR;
9745         if (gameMode == TwoMachinesPlay) {
9746             SendToProgram("nopost\n", &second);
9747         }
9748     }
9749     appData.showThinking = newState;    
9750 }
9751
9752 void
9753 AskQuestionEvent(title, question, replyPrefix, which)
9754      char *title; char *question; char *replyPrefix; char *which;
9755 {
9756   ProcRef pr = (which[0] == '1') ? first.pr : second.pr;
9757   if (pr == NoProc) return;
9758   AskQuestion(title, question, replyPrefix, pr);
9759 }
9760
9761 void
9762 DisplayMove(moveNumber)
9763      int moveNumber;
9764 {
9765     char message[MSG_SIZ];
9766     char res[MSG_SIZ];
9767     char cpThinkOutput[MSG_SIZ];
9768         static char last[1024];
9769
9770         int i = 0;
9771
9772     if (moveNumber == forwardMostMove - 1 || 
9773         gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
9774
9775         strcpy(cpThinkOutput, thinkOutput);
9776         if (strchr(cpThinkOutput, '\n'))
9777           *strchr(cpThinkOutput, '\n') = NULLCHAR;
9778     } else {
9779         *cpThinkOutput = NULLCHAR;
9780     }
9781
9782     if (moveNumber == forwardMostMove - 1 &&
9783         gameInfo.resultDetails != NULL) {
9784         if (gameInfo.resultDetails[0] == NULLCHAR) {
9785             sprintf(res, " %s", PGNResult(gameInfo.result));
9786         } else {
9787             sprintf(res, " {%s} %s",
9788                     gameInfo.resultDetails, PGNResult(gameInfo.result));
9789         }
9790         
9791     } else {
9792         res[0] = NULLCHAR;
9793     }
9794     
9795     if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
9796                 /* Output to ICC if IcsAnalyze */
9797                 if(appData.icsAnalyze) IcsAnalyzeOutPut(&first, TRUE);
9798                 DisplayMessage(res, cpThinkOutput);
9799     } else {
9800                 sprintf(message, "%d.%s%s%s", moveNumber / 2 + 1,
9801                 WhiteOnMove(moveNumber) ? " " : ".. ",
9802                 parseList[moveNumber], res);
9803                 if(appData.icsAnalyze) IcsAnalyzeOutPut(&first, FALSE);
9804                 DisplayMessage(message, cpThinkOutput);
9805     }
9806
9807         /* Send Engine output to ICS */
9808         if (appData.icsActive && appData.ButtonSendOutPutToICS 
9809                 && appData.AnalysisWindow) {
9810
9811                 /* only send on move */
9812                 switch (gameMode) {
9813                 case IcsPlayingWhite:
9814                         if(WhiteOnMove(currentMove)) i = 1;
9815                         break;
9816                 case IcsPlayingBlack:
9817                         if(!WhiteOnMove(currentMove)) i = 1;
9818                         break;
9819                 }
9820
9821                 if (i == 0 || programStats.depth <= 1 || programStats.depth >= 30 || 
9822                         programStats.GUI_time <= 3) return;
9823
9824                 if (strcmp(programStats.movelist, last) == 0) return;
9825                 strcpy(last, programStats.movelist);
9826         
9827                 switch (appData.SendOutPutToICS) {
9828                 
9829                         case 1:
9830                                 sprintf(res, "whisper time: %d sec %+.2f/%d %s \n", programStats.GUI_time, 
9831                                         (((float)programStats.score)/100.0), programStats.depth, programStats.movelist);
9832                                 SendToICS(res);
9833                         break;
9834                         case 2:
9835                                 sprintf(res, "kibitz time: %d sec %+.2f/%d %s \n", programStats.GUI_time, 
9836                                         (((float)programStats.score)/100.0), programStats.depth, programStats.movelist);
9837                                 SendToICS(res);
9838                         break;  
9839                         
9840                         default:
9841                         break;
9842                 }
9843         }
9844 }
9845
9846
9847 void
9848 IcsAnalyzeOutPut(cps, endThink)
9849 ChessProgramState *cps;
9850 int endThink;
9851 {
9852         char cpIcsThinkOutput[MSG_SIZ], icsOutput[32], str[8192];
9853         static char last[MSG_SIZ];
9854         long currentTime;
9855         int diff, s, h, m, wait, info;
9856         static int lastGame, lastMove, firstRun, say, lastdepth, sec;
9857         double nps;
9858
9859         if (appData.icsAnalyzeOutPut == 4 || !appData.icsAnalyze ||
9860                 gameMode != IcsObserving) return;
9861
9862         if (appData.icsSmartQueue == 0) {
9863                         wait = 60;
9864         } else {
9865                         wait = 5;
9866         }
9867
9868         currentTime = programStats.time;
9869
9870         /* calculate some infos */
9871         s = currentTime / 100;
9872         sec = s; /* save complete sec */
9873         h = (s / (60*60));
9874         s = s - h*60*60;
9875         m = (s/60);
9876         s = s - m*60;
9877         if (programStats.time == 0) programStats.time = 1;
9878         nps = ((((double)programStats.nodes) /
9879                (((double)programStats.time)/100.0)) /1000);
9880
9881         if (ics_gamenum >  max_gamenum || ics_gamenum == -1) {
9882                 DisplayFatalError("ICS-Analysis: Fatal array error - Please make a bugreport.", 0, 1);
9883                 return;
9884         }
9885
9886         /* init ICS gamequeue - safty */
9887         /* normaly we do that with read_from_ics() */
9888         if (icsQueue[ics_gamenum].killPv == 0) {
9889                 icsQueue[ics_gamenum].move = currentMove;
9890                 icsQueue[ics_gamenum].killPv = appData.icsKillPVs;
9891                 icsQueue[ics_gamenum].counter = 0;
9892                 icsQueue[ics_gamenum].lastpv[0] = NULLCHAR;
9893         }
9894
9895         /* for chess.fm on ICC we must copy player names each move */
9896                 strcpy(icsQueue[ics_gamenum].white, gameInfo.white);
9897                 strcpy(icsQueue[ics_gamenum].black, gameInfo.black);
9898
9899         if (appData.smartQueue) {
9900                 if (lastGame == 0) lastGame = ics_gamenum;
9901                 if (lastMove == 0) lastMove = currentMove;
9902                 if (ics_gamenum != lastGame || lastMove != currentMove) {
9903                         firstRun = 1;
9904                         icsQueue[ics_gamenum].counter = 0;
9905                 }
9906         }
9907
9908         /* checking other games  - only for live broadcasting */
9909         /* init on a new move */
9910         if (appData.icsAnalyzeOutPut != 4) {
9911                 if (icsQueue[ics_gamenum].CurrentMove != currentMove) {
9912                         icsQueue[ics_gamenum].CurrentMove = currentMove;
9913                         icsQueue[ics_gamenum].flag = 0; /* we have not send */
9914                         icsQueue[ics_gamenum].counter = 0; 
9915                 }
9916         }
9917
9918         strcpy(cpIcsThinkOutput, thinkOutput);
9919         if (strchr(cpIcsThinkOutput, '\n')) {
9920                 *strchr(cpIcsThinkOutput, '\n') = NULLCHAR;
9921         }
9922         if (cpIcsThinkOutput[0] == NULLCHAR) return;
9923
9924         /* Special ICC fetures - works only with param /icc */
9925         if (ics_type == ICS_ICC && appData.ICC_feature == TRUE &&
9926                 appData.userVersion == FALSE) {
9927                         if (appData.icsAnalyzeOutPut == 3) {
9928                                 /* If we start a new broadcast analyse we inform user */
9929                                 if (icsQueue[ics_gamenum].lastpv[0] == NULLCHAR) {
9930                                         sprintf(str, "whisperto %d Hello from Winboard-DM \n", ics_gamenum);
9931                                         SendToICS(str);
9932                                 }
9933
9934                                 /* If long tourney games >= 120  0 */
9935                                 if (appData.icsSmartQueue == 0) {
9936                                         switch (currentMove) {
9937                                         case 20: case 40: case 60: case 80: case 100: case 135: case 160:
9938                                                 if (icsQueue[ics_gamenum].event == 0) {
9939                                                         sprintf(str, "whisperto %d Hello, i'm an analysis engine.\n", ics_gamenum);
9940                                                         SendToICS(str);
9941                                                         sprintf(str, "whisperto %d If you want my last analysis of this game, just type \"tell %s showinfo %d\"\n",
9942                                                                 ics_gamenum, ics_handle, ics_gamenum);
9943                                                         SendToICS(str);
9944                                                         if (strcmp(appData.icsTells, "211") == 0) {
9945                                                                 sprintf(str, "whisperto %d and/or type \"+ch 211\" for a ICC channel analysis\n", ics_gamenum);
9946                                                                 SendToICS(str);
9947                                                         }
9948                                                         icsQueue[ics_gamenum].event = 1;
9949                                                         
9950                                                         /* channel 165 on ICC */
9951                                                         if (currentMove == 20 && chessfm == 0) {
9952                                                                 info = 1; 
9953                                                                 chessfm = 1;
9954                                                         } else if (currentMove == 40 && (chessfm == 1 || chessfm == 0)) {
9955                                                                 info = 1;
9956                                                                 chessfm = 2;
9957                                                         } else if (currentMove == 60 && (chessfm == 2 || chessfm == 0)) {
9958                                                                 info = 1;
9959                                                                 chessfm = 3;
9960                                                         } else if (currentMove == 80 && (chessfm == 3 || chessfm == 0)) {
9961                                                                 info = 1;
9962                                                                 chessfm = 4;
9963                                                         } else if (currentMove == 100 && (chessfm == 4 || chessfm == 0)) {
9964                                                                 info = 1;
9965                                                                 chessfm = 5;
9966                                                         } else {
9967                                                                 info = 0;
9968                                                         }
9969                                                         
9970                                                         if (info == 1) {
9971                                                                 sprintf(str, "tell 165 Hi, i'm analyzing live broadcast games. \"tell %s showgames \" to see which games are currently analyzed.\n", ics_handle);
9972                                                                 SendToICS(str);
9973                                                                 sprintf(str, "tell 165 \"finger %s\" and \"finger live\" for more details \n", ics_handle);
9974                                                                 SendToICS(str);
9975                                                         }
9976                                                 }
9977                                         break;
9978                                         default:
9979                                         icsQueue[ics_gamenum].event = 0;                
9980                                         }
9981                                 } else {
9982                                         switch (currentMove) {
9983                                         case 20: case 60: case 100:
9984                                                 if (icsQueue[ics_gamenum].event == 0) {
9985                                                         sprintf(str, "whisperto %d Hello, i'm an analysis engine.\n", ics_gamenum);
9986                                                         SendToICS(str);
9987                                                         sprintf(str, "whisperto %d If you want my last analysis of this game, just type \"tell %s showinfo %d\"\n",
9988                                                                 ics_gamenum, ics_handle, ics_gamenum);
9989                                                         SendToICS(str);
9990                                                         if (strcmp(appData.icsTells, "211") == 0) {
9991                                                                 sprintf(str, "whisperto %d and/or type \"+ch 211\" for a ICC channel analysis\n", ics_gamenum);
9992                                                                 SendToICS(str);
9993                                                         }
9994                                                         icsQueue[ics_gamenum].event = 1;
9995                                                 }
9996                                         break;
9997                                         default:
9998                                         icsQueue[ics_gamenum].event = 0;
9999                                         }
10000
10001                                 }
10002                         /* save last game pv for user request */
10003                         sprintf(icsQueue[ics_gamenum].lastpv, "%s time: %02d:%02d min nps: %dK \n", cpIcsThinkOutput, m, s, (int)nps);
10004                 }
10005         }
10006
10007         switch(appData.icsAnalyzeOutPut) {
10008                 case 1:
10009                         switch (ics_type) {
10010                                 case ICS_ICC: strcpy(icsOutput, "whisperto");
10011                                 break;
10012                                 case ICS_FICS: strcpy(icsOutput, "xwhisper");
10013                                 break;
10014                                 case ICS_GENERIC:
10015                                 case ICS_CHESSNET:
10016                                 default:
10017                                         DisplayFatalError("ICS-Analysis: Sorry, this Serverprotocoll is not supported",0,2);
10018                                 break;
10019                         }
10020                         break;
10021                 case 2:
10022                         switch (ics_type) {
10023                                 case ICS_ICC: strcpy(icsOutput, "kibitzto");
10024                                 break;
10025                                 case ICS_FICS: strcpy(icsOutput, "xkibitz");
10026                                 break;
10027                                 case ICS_GENERIC:
10028                                 case ICS_CHESSNET:
10029                                 default:
10030                                         DisplayFatalError("ICS-Analysis: Sorry, this Serverprotocoll is not supported",0,2);
10031                                 break;
10032                         }
10033                         break;
10034                 case 3:
10035                         strcpy(icsOutput, "tell");
10036                         break;
10037                 default:
10038                         /* could't happen */
10039                         return;
10040         }
10041
10042         /* Drop double output - example Crafty */
10043         if (strcmp(cpIcsThinkOutput, last) == 0) {
10044         /* Check again if we can switch game - that will be faster as we wait for next ply */
10045                 if (icsQueue[ics_gamenum].flag == 1) {
10046                         if (SwitchGames() == 0) return;
10047                 }
10048                 /* special crafty feature - let interation pass */
10049                 if (strncmp(cps->tidy, "Crafty", 6) != 0) return;
10050         }
10051
10052         /* if skip high/fail low active we only send every complete interation from engine */
10053         if (lastdepth == 0 || lastdepth > programStats.depth) lastdepth = programStats.depth;
10054
10055
10056         /* Drop all about 25 plys - could be egtb output */
10057         if (programStats.depth > 25) {
10058                 if (appData.icsAnalyzeOutPut == 3) {
10059                         /* We don't stay longer on this game - just try to switching other games */
10060                         icsQueue[ics_gamenum].flag = 1; /* done */
10061                         icsQueue[ics_gamenum].MessageMove = currentMove;
10062                         if (SwitchGames() == 0) return;
10063                 }
10064                 return;
10065         }
10066
10067         /* Drop score above 10.0 - many engines send to many lines if mate */
10068         if ((programStats.score / 100) > 10 || (programStats.score / 100) < -10) {
10069                         if (appData.icsAnalyzeOutPut == 3) {
10070                                 /* We don't stay longer on this game - just switching to other games */
10071                                 icsQueue[ics_gamenum].flag = 1; /* done */
10072                                 icsQueue[ics_gamenum].MessageMove = currentMove;
10073                                 if (SwitchGames() == 0) return;
10074                         }
10075                         return;
10076         }
10077         /* tell ICC fail high move if possible */
10078         if (ics_type == ICS_ICC && appData.ICC_feature == TRUE && appData.userVersion == FALSE &&
10079                 appData.icsAnalyzeOutPut == 3 && programStats.depth >= icsQueue[ics_gamenum].killPv) {
10080                  //if (strchr(cpIcsThinkOutput, '!!') != NULL && strncmp(cps->tidy, "Yace" , 4) != 0) {
10081                                 //sprintf(str, "whisperto %d Analysis:  Something happened - Side on move maybe looks better as before. \n", ics_gamenum);
10082                                 //SendToICS(str);
10083                  //} else if (strchr(cpIcsThinkOutput, '(++)') != NULL && strncmp(cps->tidy, "Yace" , 4) == 0) {
10084                                 //sprintf(str, "whisperto %d Analysis:  Something happened - Side on move maybe looks better as before. \n", ics_gamenum);
10085                                 //SendToICS(str);
10086                  //}
10087         }
10088
10089         /* Drop fail high/low moves from engines */
10090         if (appData.windowMove) {
10091                 if (strchr(cpIcsThinkOutput, '?') != NULL || strchr(cpIcsThinkOutput, '!') != NULL ||
10092                         strchr(cpIcsThinkOutput, 't') != NULL || strchr(cpIcsThinkOutput, 'u') != NULL) {
10093                                 if (appData.debugMode) fprintf(debugFP, "ICS-Analyze: Drop move with string \n");
10094                                 return;
10095                 }
10096         }
10097
10098         /* smart Queue fast down handling */
10099         if (sec > wait && programStats.depth <= (icsQueue[ics_gamenum].killPv - 1) &&
10100                 appData.smartQueue) {
10101                         if (icsQueue[ics_gamenum].killPv > 8) {
10102                                 if (!appData.icsEngineKillPV) appData.icsEngineKillPV = TRUE;
10103                                 if (lastMove == currentMove) icsQueue[ics_gamenum].killPv--;
10104                                 if (appData.debugMode) fprintf(debugFP, "icsQueue[%d].killPv-- \n",
10105                                         icsQueue[ics_gamenum].killPv);
10106                         }
10107         }
10108         /* smart Queue fast up handling */
10109         if (sec < 30 && programStats.depth >= icsQueue[ics_gamenum].killPv &&
10110                 appData.smartQueue) {
10111                         if (!appData.icsEngineKillPV) appData.icsEngineKillPV = TRUE;
10112                         if (lastMove == currentMove ) {
10113                                 /* Crafty send 2x string so this is a importent reason */
10114                                 if (strncmp(cps->tidy, "Crafty", 6) == 0 &&
10115                                         strcmp(cpIcsThinkOutput, last) != 0) {
10116                                                 icsQueue[ics_gamenum].killPv++;
10117                                 } else if (strncmp(cps->tidy, "Crafty", 6) != 0) {
10118                                                 /* other programs */
10119                                                 icsQueue[ics_gamenum].killPv++;
10120                                 }
10121                         }
10122                         if (appData.debugMode) fprintf(debugFP, "icsQueue[%d].killPv++ \n",
10123                                 icsQueue[ics_gamenum].killPv);
10124         }
10125         /* smart Queue handling */
10126         if (appData.smartQueue && programStats.depth >= icsQueue[ics_gamenum].killPv) {
10127                 diff = ((int)currentTime / 100) - ((int)icsQueue[ics_gamenum].time / 100);
10128                 if ((diff < 7 && diff != 0) && icsQueue[ics_gamenum].counter > 3 &&
10129                      icsQueue[ics_gamenum].move <= currentMove) {
10130                                 if (!appData.icsEngineKillPV) appData.icsEngineKillPV = TRUE;
10131                                 appData.icsEngineKillPV = TRUE;
10132                                 icsQueue[ics_gamenum].killPv++;
10133                                 icsQueue[ics_gamenum].move = currentMove + 1;
10134                                 icsQueue[ics_gamenum].counter = 0;
10135                                 if (appData.debugMode) fprintf(debugFP, "icsQueue[%d].killPv++ \n",
10136                                         icsQueue[ics_gamenum].killPv);
10137                 /* normal down */
10138                 } else if (diff > wait && icsQueue[ics_gamenum].counter < 2 &&
10139                    icsQueue[ics_gamenum].move <= currentMove && firstRun == 1 &&
10140                    lastMove != currentMove) {
10141                         if(icsQueue[ics_gamenum].killPv > 8) {
10142                                 if (!appData.icsEngineKillPV) appData.icsEngineKillPV = TRUE;
10143                                 if (lastMove == currentMove) icsQueue[ics_gamenum].killPv--;
10144                                 firstRun = 0;
10145                                 if (appData.debugMode) fprintf(debugFP, "icsQueue[%d].killPv-- \n",
10146                                         icsQueue[ics_gamenum].killPv);
10147                         }
10148                                 icsQueue[ics_gamenum].move = currentMove + 1;
10149                                 icsQueue[ics_gamenum].counter = 0;
10150                 } else {
10151                                 icsQueue[ics_gamenum].counter++;
10152                                 lastGame = ics_gamenum;
10153                                 lastMove = currentMove;
10154                 }
10155         }
10156         icsQueue[ics_gamenum].time = currentTime;
10157
10158         /* Check other games part 2 */
10159         if (appData.icsAnalyzeOutPut != 4) {
10160                 /* if no other games avaible we coming back */
10161                 if (icsQueue[ics_gamenum].flag == 1) {
10162                         if (SwitchGames() == 0) return;
10163                 }
10164         }
10165
10166         /* kill PV here */
10167         if (programStats.line_is_book != 1 || programStats.depth != 0) {
10168                 if (appData.icsEngineKillPV || appData.smartQueue) {
10169                         if(programStats.depth < icsQueue[ics_gamenum].killPv ||
10170                            programStats.depth  < appData.icsKillPVs && programStats.depth != 0) return;
10171                 }
10172         }
10173
10174         /* send information */
10175         if (programStats.line_is_book == 1 && programStats.depth < 2 ||
10176                 programStats.depth == 0) {
10177                 if (appData.icsAnalyzeOutPut != 3) {
10178                                 /* We must whisperto <gamenumber> if we follow someone */
10179                                 /* on FCIS we need xwhisper */
10180                                 sprintf(str, "%s %d %s: Book depth=%s \n", icsOutput, ics_gamenum,
10181                                 cps->tidy,  cpIcsThinkOutput);
10182                         } else {
10183                                 if (currentMove > -1) {
10184                                         sprintf(str, "%s %s %s: (%s/%s) Book %d.%s depth=%s (game \"observe %d\")\n", icsOutput,
10185                                         appData.icsTells, cps->tidy, gameInfo.white, gameInfo.black,
10186                                         currentMove / 2 + 1, WhiteOnMove(currentMove) ? " " : "... ", 
10187                                         cpIcsThinkOutput, ics_gamenum);
10188
10189                                         icsQueue[ics_gamenum].MessageMove = currentMove;
10190                                 }
10191                                 icsQueue[ics_gamenum].flag = 1; /* we send */
10192                         }
10193                         SendToICS(str);
10194         } else {
10195                 /* interation check */
10196                 /* special crafty interation */
10197                 if (appData.windowMove && strncmp(cps->tidy, "Crafty" , 6) == 0 && 
10198                         strcmp(cpIcsThinkOutput, last) != 0) {
10199                                 strcpy(last,cpIcsThinkOutput);
10200                                 return;
10201                 }
10202                 /* normal interation check */
10203             if (appData.windowMove && programStats.depth <= lastdepth) return;
10204
10205                 if (appData.icsAnalyzeOutPut != 3) {
10206                         if (endThink == TRUE) {
10207                                 sprintf(str, "%s %d %s: first move (Depth=%s time: %02d:%02d min nps: %dK \n", icsOutput,
10208                                 ics_gamenum, cps->tidy,  cpIcsThinkOutput, m, s, (int)nps);
10209                         } else {
10210                                 sprintf(str, "%s %d %s: Depth=%s time: %02d:%02d min nps: %dK \n", icsOutput, ics_gamenum,
10211                                 cps->tidy,  cpIcsThinkOutput, m, s, (int)nps);
10212                         }
10213                         icsQueue[ics_gamenum].flag = 1; /* we send */
10214                         SendToICS(str);
10215                 } else {
10216                         if (endThink == TRUE) {
10217                                 sprintf(str, "%s %s %s: (%s/%s) first move (Depth=%s time: %02d:%02d min nps: %dK (game \"observe %d\")\n", icsOutput,
10218                                 appData.icsTells, cps->tidy, gameInfo.white, gameInfo.black,
10219                                 cpIcsThinkOutput, m, s, (int)nps, ics_gamenum);
10220                         } else {
10221                                 sprintf(str, "%s %s %s: (%s/%s) Depth=%s time: %02d:%02d min nps: %dK (game \"observe %d\")\n", icsOutput,
10222                                 appData.icsTells, cps->tidy, gameInfo.white, gameInfo.black,
10223                                 cpIcsThinkOutput, m, s, (int)nps, ics_gamenum);
10224                         }
10225                         icsQueue[ics_gamenum].flag = 1; /* we send */
10226                         icsQueue[ics_gamenum].MessageMove = currentMove;
10227                         SendToICS(str);
10228                 }
10229         }
10230         strcpy(last,cpIcsThinkOutput);
10231         lastdepth = programStats.depth;
10232
10233         /* At least we search for a open game once more */
10234         if (icsQueue[ics_gamenum].flag == 1) {
10235                 if (SwitchGames() == 0 && appData.debugMode) {
10236                         fprintf(debugFP, "Switch was at end of produce \n");
10237                 }
10238         }
10239 }
10240
10241
10242 /* Reset icsGameQueue if game ends or user abort */
10243 void
10244 ResetIcsQueue(gamenumber)
10245 {
10246         icsQueue[gamenumber].black[0] = NULLCHAR;
10247         icsQueue[gamenumber].white[0] = NULLCHAR;
10248         icsQueue[gamenumber].counter = 0;
10249         icsQueue[gamenumber].currentGame = 0;
10250         icsQueue[gamenumber].CurrentMove = 0;
10251         icsQueue[gamenumber].event = 0;
10252         icsQueue[gamenumber].killPv = 0;
10253         icsQueue[gamenumber].lastpv[0] = NULLCHAR;
10254         icsQueue[gamenumber].MessageMove = 0;
10255         icsQueue[gamenumber].move = 0;
10256         icsQueue[gamenumber].time = 0;
10257 }
10258
10259 /* GUI -> engine */
10260 void
10261 GuiCommand(command, param)
10262 int command;
10263 int param; /* reserved */
10264 {
10265         switch (command) {
10266                 case 1:
10267                         switch (gameMode) {
10268                                 case MachinePlaysWhite:
10269                                 case MachinePlaysBlack:
10270                                                 SendToProgram("?\n", &first);
10271                                 break;
10272                         }
10273                 break;
10274                 case 2:
10275                         switch (gameMode) {
10276                                 case AnalyzeMode:
10277                                 case AnalyzeFile:
10278                                         if (first.analyzing == TRUE) {
10279                                                 /* don't send stat */
10280                                                 appData.engineStatLine = TRUE;
10281                                                 SendToProgram("exit\n", &first);
10282                                                 SendToProgram("force\n", &first);
10283                                                 first.analyzing = FALSE;
10284                                                 PeriodicUpdatesEvent(FALSE);
10285                                                 DisplayAnalysis(6,0);
10286                                         } else {
10287                                                 /* safty with force */
10288                                                 /* send stat */
10289                                                 appData.engineStatLine = FALSE;
10290                                                 SendToProgram("analyze\n", &first);
10291                                                 first.analyzing = TRUE;
10292                                                 PeriodicUpdatesEvent(TRUE);
10293                                         }
10294                                 break;
10295                                 /* a lot of engine makes probs if we switch from
10296                                  * normal Mode to analyzeMode :( */
10297                                 case MachinePlaysWhite:
10298                                 case MachinePlaysBlack:
10299                                 case EditGame:
10300                                         if (first.maybeThinking == TRUE) {
10301                                                 /* don't send stat */
10302                                                 appData.engineStatLine = TRUE;
10303                                                 EditGameEvent();
10304                                                 first.maybeThinking = FALSE;
10305                                         } else {
10306                                                 /* get latest state */
10307                                                 if (supportStat == 1) appData.engineStatLine = FALSE;
10308                                                 if (WhiteOnMove(currentMove)) {
10309                                                                 MachineWhiteEvent();
10310                                                 } else {
10311                                                                 MachineBlackEvent();
10312                                                 }
10313                                         }
10314                                 break;
10315                                 case BeginningOfGame:
10316                                         MachineWhiteEvent();
10317                                 break;
10318                                 default:
10319                                 break;
10320                         }
10321         }
10322 }
10323
10324 static int
10325 only_one_move(str)
10326      char *str;
10327 {
10328     while (*str && isspace(*str)) ++str;
10329     while (*str && !isspace(*str)) ++str;
10330     if (!*str) return 1;
10331     while (*str && isspace(*str)) ++str;
10332     if (!*str) return 1;
10333     return 0;
10334 }
10335
10336 void
10337 DisplayAnalysis(newState, pv)
10338 int newState;   /* int newState:
10339                                  * 1 = timer++
10340                                  * 0 = do nothing
10341                                  * --- WB bootup (start) ---
10342                                  * 5 = WB startup if EngineRoom true: popup 
10343                                  * --- GUI Action ---
10344                                  * 6 = Start button 
10345                                  *     hold/reset time 
10346                                  * 7 = Stop button */
10347                         
10348
10349 int pv; /* only pv comes 1 = TRUE */
10350 {
10351         /* newState True = call from timer - see dirty hack */
10352     char buf[2048];
10353     char buf2[32];
10354         char buf3[32];
10355         char buf4[32];
10356         char buf5[32];
10357         static char buf6[64]; /* static need if pv only */
10358         static char lastline[1024];
10359         static int lastdepth, last_icsgamenum;
10360         int currentdepth;
10361         char currentline[1024];
10362         char buf7[128];
10363     double nps;
10364     int h, m, s;
10365         static char *xtra[] = { "", " (--)", " (++)" };
10366         static int oldGameMode; /* on the fly switch gameMode ? */
10367         static int StatLine_autodetect; /* Autodetect only first move out of book */
10368                                                                         /* 0 = should detect
10369                                                                          * 1 = done
10370                                                                          */
10371         int diff;
10372         int state; /* Using for ponder, opponent time action and
10373                             * using for color engine name
10374                                 * --- move statment (adv color)---
10375                             * 0 = we have the move and stanard color  
10376                                 * 1 = opponent have the move and opponent color
10377                                 * --- only color statement---
10378                             * 2 =  AnalyzeMode 
10379                                 * 3 = IcsObserving
10380                                 * --- special things ----
10381                             * 4 = Two engines - we don't support that
10382                                 */
10383         static long timer; /* hack without a stat line */
10384         static int move;         /* detect new move */
10385
10386         strcpy(currentline, programStats.movelist);
10387         currentdepth = programStats.depth;
10388
10389         if (appData.noChessProgram) return;
10390         /* if goes up to the end of this function */
10391         if (appData.icsAnalyzeWindow || appData.AnalysisWindow
10392                 || gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
10393         /* stop analysis */
10394         if (newState == 6) timer = 0;
10395
10396         /* use for no stat support */
10397         /* If user switch on the fly gameMode we must know that */
10398         if (move != currentMove || oldGameMode != gameMode) {
10399                 timer = 0;
10400                 if (oldGameMode != gameMode) {
10401                         /* clear pv screen */
10402                         sprintf(buf, "changing mode...");
10403                         sprintf(buf2, "wait");
10404                         sprintf(buf3, "0kN/s");
10405                         sprintf(buf4, "Nodes=0");
10406                         sprintf(buf5, "0.00");
10407                         sprintf(buf6, "wait...");
10408                         sprintf(buf7, "------------ ");
10409                         AnalysisPopUp(buf, buf2, buf3, buf4, buf5, buf6, buf7, 0);
10410                         oldGameMode = gameMode;
10411                 }
10412                 if (move > currentMove) {
10413                         supportStat = 0; /* new Game ? */
10414                         StatLine_autodetect = 0;
10415                 }
10416                 move = currentMove;
10417                 lastdepth = 0;
10418         } 
10419         
10420         /* ICS Analyze more then one game - check if new game */
10421         if (appData.icsActive && ics_gamenum != last_icsgamenum) {
10422                 timer = 0;
10423                 last_icsgamenum = ics_gamenum;
10424         }
10425
10426         if (gameMode == EditGame) timer = 0; /* no clock if edit */ 
10427
10428         /* hope a lot of programmer will add a stat line - easy to make */
10429         /* at the moment a dirty(!!) hack with time_id */
10430         /* calculate some infos */
10431         if (programStats.time == 0) programStats.time = 1;
10432         if (newState && gameMode != EditGame) timer++;
10433                 s = timer;
10434                 h = (s / (60*60));
10435                 s = s - h*60*60;
10436                 m = (s/60);
10437                 s = s - m*60;
10438
10439         /* save time */
10440         programStats.GUI_time = timer;
10441
10442         /* Who is on move or which mode ?*/
10443         /* Need for colorize EngineName */
10444         /* Check gameMode for Stat support */
10445         switch (gameMode) {
10446                 case MachinePlaysWhite:
10447                 case IcsPlayingWhite:
10448                         if (WhiteOnMove(currentMove)) state = 0;
10449                         if (!WhiteOnMove(currentMove)) state = 1;
10450                         break;
10451                 case MachinePlaysBlack:
10452                 case IcsPlayingBlack:
10453                         if (WhiteOnMove(currentMove)) state = 1;
10454                         if (!WhiteOnMove(currentMove)) state = 0;
10455                         break;
10456                 case AnalyzeMode:
10457                 case AnalyzeFile:
10458                         appData.engineStatLine = FALSE;
10459                         StatLine_autodetect = 0; /* switch back to play is a prob */
10460                         state = 2;
10461                         break;
10462                 case IcsObserving:
10463                         state = 3;
10464                         break;
10465                 /* not really a support for engine war - wait for war room */
10466                 case TwoMachinesPlay:
10467                         state = 4;
10468                         break;
10469                 default:
10470                         appData.engineStatLine = FALSE;
10471                         state = 0; /* green */
10472         }
10473         nps = ((((double)programStats.nodes) /
10474                (((double)programStats.time)/100.0)) /1000);
10475
10476         if (programStats.nodes > 0 && pv == 1) {        
10477                 if (strcmp(lastline, currentline) != 0) {
10478                         if (prefixHint == TRUE && programStats.ponderMove[0] != NULLCHAR) {
10479                                 if (programStats.score == 0 || programStats.score > 0) {
10480                                         /* format arial set */
10481                                         sprintf(buf, " d->%02d  %+.2f  (%s)  %s", programStats.depth,
10482                                         (((float)programStats.score)/100.0), 
10483                                         programStats.ponderMove, currentline);
10484                                 } else if (programStats.score < 0) {
10485                                         sprintf(buf, " d->%02d   %+.2f  (%s)  %s", programStats.depth,
10486                                         (((float)programStats.score)/100.0), 
10487                                         programStats.ponderMove, currentline);
10488                                 }
10489                                 strcpy(lastline, currentline);
10490                         } else {
10491                                 /* format arial set */
10492                                 if (programStats.score == 0 || programStats.score > 0) {
10493                                         sprintf(buf, " d->%02d  %+.2f  %s", programStats.depth,
10494                                         (((float)programStats.score)/100.0), currentline);
10495                                 } else if (programStats.score < 0) {
10496                                         sprintf(buf, " d->%02d   %+.2f  %s", programStats.depth,
10497                                         (((float)programStats.score)/100.0), currentline);
10498                                 }
10499                                 strcpy(lastline, currentline);
10500                         }
10501                         lastdepth = currentdepth;
10502                 } else {
10503                         if (lastdepth < currentdepth) {
10504                           if (prefixHint == TRUE && programStats.ponderMove[0] != NULLCHAR) {
10505                                 if (programStats.score == 0 || programStats.score > 0) {
10506                                         /* format arial set */
10507                                         sprintf(buf, " d->%02d  %+.2f  (%s)  %s", programStats.depth,
10508                                         (((float)programStats.score)/100.0), 
10509                                         programStats.ponderMove, currentline);
10510                                 } else if (programStats.score < 0) {
10511                                         sprintf(buf, " d->%02d   %+.2f  (%s)  %s", programStats.depth,
10512                                         (((float)programStats.score)/100.0), 
10513                                         programStats.ponderMove, currentline);
10514                                 }
10515                         } else {
10516                                 /* format arial set */
10517                                 if (programStats.score == 0 || programStats.score > 0) {
10518                                         sprintf(buf, " d->%02d  %+.2f  %s", programStats.depth,
10519                                         (((float)programStats.score)/100.0), currentline);
10520                                 } else if (programStats.score < 0) {
10521                                         sprintf(buf, " d->%02d   %+.2f  %s", programStats.depth,
10522                                         (((float)programStats.score)/100.0), currentline);
10523                                 }
10524                         }
10525                         lastdepth = currentdepth;
10526                         } else {
10527                                 buf[0] = NULLCHAR;
10528                         }
10529                 }
10530         } else {
10531                 buf[0] = NULLCHAR;
10532         }
10533
10534         /* remove Bookline - never works correct */
10535         /* dirty hack with wb2 proto :((*/
10536         if (programStats.depth == 0 || programStats.depth == 1) {
10537                         sprintf(buf2, "Book");
10538                         sprintf(buf3, "0kN/s");
10539                         sprintf(buf4, "Nodes=0");
10540                         sprintf(buf5, "0.00");
10541                         sprintf(buf6, "wait...");
10542         } else {
10543                 sprintf(buf2, "Depth=%d", programStats.depth);
10544                         sprintf(buf3, "%dkN/s", (int)nps);
10545                         sprintf(buf4, "Nodes=%lu", programStats.nodes);
10546                         sprintf(buf5, "%+.2f", (((float)programStats.score)/100.0));
10547         }
10548         sprintf(buf7, "%02d:%02d:%02d ", h, m, s);
10549         /* only pv */
10550         if (pv == 1 && strcmp(lastline, currentline) != 0) {
10551                         AnalysisPopUp(buf, buf2, buf3, buf4, buf5, buf6, buf7, state);
10552                         return;
10553         } else if (pv == 1 && strcmp(lastline, currentline) == 0 && 
10554           lastdepth < currentdepth) {
10555                         AnalysisPopUp(buf, buf2, buf3, buf4, buf5, buf6, buf7, state);
10556                         return;
10557         }
10558                 
10559         /* remove Bookline - never works correct */
10560         if (programStats.depth == 0 || programStats.depth == 1) {
10561                         sprintf(buf6, "wait...");
10562         } else {
10563                 if (supportStat == 0 && StatLine_autodetect == 0 &&
10564                         timer > 20) {
10565                         /* we should disable sending "." now and tell GUI */
10566                         /* Do that here only after first move out of book */
10567                                 appData.engineStatLine = TRUE; /* enable checkbox */
10568                                 StatLine_autodetect = 1; /* done */
10569                 }
10570                 if (programStats.depth > 1) {
10571                         if (supportStat == 0) {
10572                                 sprintf(buf6, "no support");
10573                         } else {        
10574                                 diff = (programStats.nr_moves-programStats.moves_left);
10575                                 if (diff < 0) {
10576                                         sprintf(buf6, "Error!");
10577                                 } else {
10578                                         if (programStats.moves_left > 0) {
10579                                                 if (programStats.move_name[0] != NULLCHAR) {
10580                                                         sprintf(buf6, "%d/%d %s", diff,
10581                                                         programStats.nr_moves, programStats.move_name);
10582                                                 } else {
10583                                                         /* without move. e.g. crafty */
10584                                                         sprintf(buf6, "%d/%d", diff,
10585                                                         programStats.nr_moves, only_one_move(programStats.movelist)?
10586                                                         xtra[programStats.got_fail] : "");
10587                                                 }
10588                                         }
10589                                 }
10590                         }
10591                 }
10592         }
10593         AnalysisPopUp(buf, buf2, buf3, buf4, buf5, buf6, buf7, state);
10594
10595         }
10596 }
10597
10598 void
10599 DisplayComment(moveNumber, text)
10600      int moveNumber;
10601      char *text;
10602 {
10603     char title[MSG_SIZ];
10604
10605     if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
10606         strcpy(title, "Comment");
10607     } else {
10608         sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,
10609                 WhiteOnMove(moveNumber) ? " " : ".. ",
10610                 parseList[moveNumber]);
10611     }
10612
10613     CommentPopUp(title, text);
10614 }
10615
10616 /* This routine sends a ^C interrupt to gnuchess, to awaken it if it
10617  * might be busy thinking or pondering.  It can be omitted if your
10618  * gnuchess is configured to stop thinking immediately on any user
10619  * input.  However, that gnuchess feature depends on the FIONREAD
10620  * ioctl, which does not work properly on some flavors of Unix.
10621  */
10622 void
10623 Attention(cps)
10624      ChessProgramState *cps;
10625 {
10626 #if ATTENTION
10627     if (!cps->useSigint) return;
10628     if (appData.noChessProgram || (cps->pr == NoProc)) return;
10629     switch (gameMode) {
10630       case MachinePlaysWhite:
10631       case MachinePlaysBlack:
10632       case TwoMachinesPlay:
10633       case IcsPlayingWhite:
10634       case IcsPlayingBlack:
10635       case AnalyzeMode:
10636       case AnalyzeFile:
10637         /* Skip if we know it isn't thinking */
10638         if (!cps->maybeThinking) return;
10639         if (appData.debugMode)
10640           fprintf(debugFP, "Interrupting %s\n", cps->which);
10641         InterruptChildProcess(cps->pr);
10642         cps->maybeThinking = FALSE;
10643         break;
10644       default:
10645         break;
10646     }
10647 #endif /*ATTENTION*/
10648 }
10649
10650 int
10651 CheckFlags()
10652 {
10653     if (whiteTimeRemaining <= 0) {
10654         if (!whiteFlag) {
10655             whiteFlag = TRUE;
10656             if (appData.icsActive) {
10657                 if (appData.autoCallFlag &&
10658                     gameMode == IcsPlayingBlack && !blackFlag) {
10659                   SendToICS(ics_prefix);
10660                   SendToICS("flag\n");
10661                 }
10662             } else {
10663                 if (blackFlag) {
10664                     DisplayTitle("Both flags fell");
10665                 } else {
10666                     DisplayTitle("White's flag fell");
10667                     if (appData.autoCallFlag) {
10668                         GameEnds(BlackWins, "Black wins on time", GE_XBOARD);
10669                         return TRUE;
10670                     }
10671                 }
10672             }
10673         }
10674     }
10675     if (blackTimeRemaining <= 0) {
10676         if (!blackFlag) {
10677             blackFlag = TRUE;
10678             if (appData.icsActive) {
10679                 if (appData.autoCallFlag &&
10680                     gameMode == IcsPlayingWhite && !whiteFlag) {
10681                   SendToICS(ics_prefix);
10682                   SendToICS("flag\n");
10683                 }
10684             } else {
10685                 if (whiteFlag) {
10686                     DisplayTitle("Both flags fell");
10687                 } else {
10688                     DisplayTitle("Black's flag fell");
10689                     if (appData.autoCallFlag) {
10690                         GameEnds(WhiteWins, "White wins on time", GE_XBOARD);
10691                         return TRUE;
10692                     }
10693                 }
10694             }
10695         }
10696     }
10697     return FALSE;
10698 }
10699
10700 void
10701 CheckTimeControl()
10702 {
10703     if (!appData.clockMode || appData.icsActive ||
10704         gameMode == PlayFromGameFile || forwardMostMove == 0) return;
10705
10706     if (timeIncrement >= 0) {
10707         if (WhiteOnMove(forwardMostMove)) {
10708             blackTimeRemaining += timeIncrement;
10709         } else {
10710             whiteTimeRemaining += timeIncrement;
10711         }
10712     }
10713     /*
10714      * add time to clocks when time control is achieved
10715      */
10716     if (movesPerSession) {
10717       switch ((forwardMostMove + 1) % (movesPerSession * 2)) {
10718       case 0:
10719         /* White made time control */
10720         whiteTimeRemaining += timeControl;
10721         break;
10722       case 1:
10723         /* Black made time control */
10724         blackTimeRemaining += timeControl;
10725         break;
10726       default:
10727         break;
10728       }
10729     }
10730 }
10731
10732 void
10733 DisplayBothClocks()
10734 {
10735     int wom = gameMode == EditPosition ?
10736       !blackPlaysFirst : WhiteOnMove(currentMove);
10737     DisplayWhiteClock(whiteTimeRemaining, wom);
10738     DisplayBlackClock(blackTimeRemaining, !wom);
10739 }
10740
10741
10742 /* Timekeeping seems to be a portability nightmare.  I think everyone
10743    has ftime(), but I'm really not sure, so I'm including some ifdefs
10744    to use other calls if you don't.  Clocks will be less accurate if
10745    you have neither ftime nor gettimeofday.
10746 */
10747
10748 /* Get the current time as a TimeMark */
10749 void
10750 GetTimeMark(tm)
10751      TimeMark *tm;
10752 {
10753 #if HAVE_GETTIMEOFDAY
10754
10755     struct timeval timeVal;
10756     struct timezone timeZone;
10757
10758     gettimeofday(&timeVal, &timeZone);
10759     tm->sec = (long) timeVal.tv_sec; 
10760     tm->ms = (int) (timeVal.tv_usec / 1000L);
10761
10762 #else /*!HAVE_GETTIMEOFDAY*/
10763 #if HAVE_FTIME
10764
10765 #include <sys/timeb.h>
10766     struct timeb timeB;
10767
10768     ftime(&timeB);
10769     tm->sec = (long) timeB.time;
10770     tm->ms = (int) timeB.millitm;
10771
10772 #else /*!HAVE_FTIME && !HAVE_GETTIMEOFDAY*/
10773     tm->sec = (long) time(NULL);
10774     tm->ms = 0;
10775 #endif
10776 #endif
10777 }
10778
10779 /* Return the difference in milliseconds between two
10780    time marks.  We assume the difference will fit in a long!
10781 */
10782 long
10783 SubtractTimeMarks(tm2, tm1)
10784      TimeMark *tm2, *tm1;
10785 {
10786     return 1000L*(tm2->sec - tm1->sec) +
10787            (long) (tm2->ms - tm1->ms);
10788 }
10789
10790
10791 /*
10792  * Code to manage the game clocks.
10793  *
10794  * In tournament play, black starts the clock and then white makes a move.
10795  * We give the human user a slight advantage if he is playing white---the
10796  * clocks don't run until he makes his first move, so it takes zero time.
10797  * Also, we don't account for network lag, so we could get out of sync
10798  * with GNU Chess's clock -- but then, referees are always right.  
10799  */
10800
10801 static TimeMark tickStartTM;
10802 static long intendedTickLength;
10803
10804 long
10805 NextTickLength(timeRemaining)
10806      long timeRemaining;
10807 {
10808     long nominalTickLength, nextTickLength;
10809
10810     if (timeRemaining > 0L && timeRemaining <= 10000L)
10811       nominalTickLength = 100L;
10812     else
10813       nominalTickLength = 1000L;
10814     nextTickLength = timeRemaining % nominalTickLength;
10815     if (nextTickLength <= 0) nextTickLength += nominalTickLength;
10816
10817     return nextTickLength;
10818 }
10819
10820 /* Stop clocks and reset to a fresh time control */
10821 void
10822 ResetClocks() 
10823 {
10824     (void) StopClockTimer();
10825     if (appData.icsActive) {
10826         whiteTimeRemaining = blackTimeRemaining = 0;
10827     } else {
10828         whiteTimeRemaining = blackTimeRemaining = timeControl;
10829     }
10830     if (whiteFlag || blackFlag) {
10831         DisplayTitle("");
10832         whiteFlag = blackFlag = FALSE;
10833     }
10834     DisplayBothClocks();
10835 }
10836
10837 #define FUDGE 25 /* 25ms = 1/40 sec; should be plenty even for 50 Hz clocks */
10838
10839 /* Decrement running clock by amount of time that has passed */
10840 void
10841 DecrementClocks()
10842 {
10843     long timeRemaining;
10844     long lastTickLength, fudge;
10845     TimeMark now;
10846
10847     if (!appData.clockMode) return;
10848     if (gameMode==AnalyzeMode || gameMode == AnalyzeFile) return; 
10849         
10850     GetTimeMark(&now);
10851
10852     lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
10853
10854     /* Fudge if we woke up a little too soon */
10855     fudge = intendedTickLength - lastTickLength;
10856     if (fudge < 0 || fudge > FUDGE) fudge = 0;
10857
10858     if (WhiteOnMove(forwardMostMove)) {
10859         timeRemaining = whiteTimeRemaining -= lastTickLength;
10860         DisplayWhiteClock(whiteTimeRemaining - fudge,
10861                           WhiteOnMove(currentMove));
10862     } else {
10863         timeRemaining = blackTimeRemaining -= lastTickLength;
10864         DisplayBlackClock(blackTimeRemaining - fudge,
10865                           !WhiteOnMove(currentMove));
10866     }
10867
10868     if (CheckFlags()) return;
10869         
10870     tickStartTM = now;
10871     intendedTickLength = NextTickLength(timeRemaining - fudge) + fudge;
10872     StartClockTimer(intendedTickLength);
10873
10874     /* if the time remaining has fallen below the alarm threshold, sound the
10875      * alarm. if the alarm has sounded and (due to a takeback or time control
10876      * with increment) the time remaining has increased to a level above the
10877      * threshold, reset the alarm so it can sound again. 
10878      */
10879     
10880     if (appData.icsActive && appData.icsAlarm) {
10881
10882         /* make sure we are dealing with the user's clock */
10883         if (!( ((gameMode == IcsPlayingWhite) && WhiteOnMove(currentMove)) ||
10884                ((gameMode == IcsPlayingBlack) && !WhiteOnMove(currentMove))
10885            )) return;
10886
10887         if (alarmSounded && (timeRemaining > appData.icsAlarmTime)) {
10888             alarmSounded = FALSE;
10889         } else if (!alarmSounded && (timeRemaining <= appData.icsAlarmTime)) { 
10890             PlayAlarmSound();
10891             alarmSounded = TRUE;
10892         }
10893     }
10894 }
10895
10896
10897 /* A player has just moved, so stop the previously running
10898    clock and (if in clock mode) start the other one.
10899    We redisplay both clocks in case we're in ICS mode, because
10900    ICS gives us an update to both clocks after every move.
10901    Note that this routine is called *after* forwardMostMove
10902    is updated, so the last fractional tick must be subtracted
10903    from the color that is *not* on move now.
10904 */
10905 void
10906 SwitchClocks()
10907 {
10908     long lastTickLength;
10909     TimeMark now;
10910     int flagged = FALSE;
10911
10912     GetTimeMark(&now);
10913
10914     if (StopClockTimer() && appData.clockMode) {
10915         lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
10916         if (WhiteOnMove(forwardMostMove)) {
10917             blackTimeRemaining -= lastTickLength;
10918         } else {
10919             whiteTimeRemaining -= lastTickLength;
10920         }
10921         flagged = CheckFlags();
10922     }
10923     CheckTimeControl();
10924
10925     if (flagged || !appData.clockMode) return;
10926
10927     switch (gameMode) {
10928       case MachinePlaysBlack:
10929       case MachinePlaysWhite:
10930       case BeginningOfGame:
10931         if (pausing) return;
10932         break;
10933
10934       case EditGame:
10935       case PlayFromGameFile:
10936       case IcsExamining:
10937         return;
10938
10939       default:
10940         break;
10941     }
10942
10943     tickStartTM = now;
10944     intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
10945       whiteTimeRemaining : blackTimeRemaining);
10946     StartClockTimer(intendedTickLength);
10947 }
10948         
10949
10950 /* Stop both clocks */
10951 void
10952 StopClocks()
10953 {       
10954     long lastTickLength;
10955     TimeMark now;
10956
10957     if (!StopClockTimer()) return;
10958     if (!appData.clockMode) return;
10959
10960     GetTimeMark(&now);
10961
10962     lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
10963     if (WhiteOnMove(forwardMostMove)) {
10964         whiteTimeRemaining -= lastTickLength;
10965         DisplayWhiteClock(whiteTimeRemaining, WhiteOnMove(currentMove));
10966     } else {
10967         blackTimeRemaining -= lastTickLength;
10968         DisplayBlackClock(blackTimeRemaining, !WhiteOnMove(currentMove));
10969     }
10970     CheckFlags();
10971 }
10972         
10973 /* Start clock of player on move.  Time may have been reset, so
10974    if clock is already running, stop and restart it. */
10975 void
10976 StartClocks()
10977 {
10978     (void) StopClockTimer(); /* in case it was running already */
10979     DisplayBothClocks();
10980     if (CheckFlags()) return;
10981
10982     if (!appData.clockMode) return;
10983     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) return;
10984
10985     GetTimeMark(&tickStartTM);
10986     intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
10987       whiteTimeRemaining : blackTimeRemaining);
10988     StartClockTimer(intendedTickLength);
10989 }
10990
10991 char *
10992 TimeString(ms)
10993      long ms;
10994 {
10995     long second, minute, hour, day;
10996     char *sign = "";
10997     static char buf[32];
10998     
10999     if (ms > 0 && ms <= 9900) {
11000       /* convert milliseconds to tenths, rounding up */
11001       double tenths = floor( ((double)(ms + 99L)) / 100.00 );
11002
11003       sprintf(buf, " %03.1f ", tenths/10.0);
11004       return buf;
11005     }
11006
11007     /* convert milliseconds to seconds, rounding up */
11008     /* use floating point to avoid strangeness of integer division
11009        with negative dividends on many machines */
11010     second = (long) floor(((double) (ms + 999L)) / 1000.0);
11011
11012     if (second < 0) {
11013         sign = "-";
11014         second = -second;
11015     }
11016     
11017     day = second / (60 * 60 * 24);
11018     second = second % (60 * 60 * 24);
11019     hour = second / (60 * 60);
11020     second = second % (60 * 60);
11021     minute = second / 60;
11022     second = second % 60;
11023     
11024     if (day > 0)
11025       sprintf(buf, " %s%ld:%02ld:%02ld:%02ld ",
11026               sign, day, hour, minute, second);
11027     else if (hour > 0)
11028       sprintf(buf, " %s%ld:%02ld:%02ld ", sign, hour, minute, second);
11029     else
11030       sprintf(buf, " %s%2ld:%02ld ", sign, minute, second);
11031     
11032     return buf;
11033 }
11034
11035
11036 /*
11037  * This is necessary because some C libraries aren't ANSI C compliant yet.
11038  */
11039 char *
11040 StrStr(string, match)
11041      char *string, *match;
11042 {
11043     int i, length;
11044     
11045     length = strlen(match);
11046     
11047     for (i = strlen(string) - length; i >= 0; i--, string++)
11048       if (!strncmp(match, string, length))
11049         return string;
11050     
11051     return NULL;
11052 }
11053
11054 char *
11055 StrCaseStr(string, match)
11056      char *string, *match;
11057 {
11058     int i, j, length;
11059     
11060     length = strlen(match);
11061     
11062     for (i = strlen(string) - length; i >= 0; i--, string++) {
11063         for (j = 0; j < length; j++) {
11064             if (ToLower(match[j]) != ToLower(string[j]))
11065               break;
11066         }
11067         if (j == length) return string;
11068     }
11069
11070     return NULL;
11071 }
11072
11073 #ifndef _amigados
11074 int
11075 StrCaseCmp(s1, s2)
11076      char *s1, *s2;
11077 {
11078     char c1, c2;
11079     
11080     for (;;) {
11081         c1 = ToLower(*s1++);
11082         c2 = ToLower(*s2++);
11083         if (c1 > c2) return 1;
11084         if (c1 < c2) return -1;
11085         if (c1 == NULLCHAR) return 0;
11086     }
11087 }
11088
11089
11090 int
11091 ToLower(c)
11092      int c;
11093 {
11094     return isupper(c) ? tolower(c) : c;
11095 }
11096
11097
11098 int
11099 ToUpper(c)
11100      int c;
11101 {
11102     return islower(c) ? toupper(c) : c;
11103 }
11104 #endif /* !_amigados    */
11105
11106 char *
11107 StrSave(s)
11108      char *s;
11109 {
11110     char *ret;
11111
11112     if ((ret = (char *) malloc(strlen(s) + 1))) {
11113         strcpy(ret, s);
11114     }
11115     return ret;
11116 }
11117
11118 char *
11119 StrSavePtr(s, savePtr)
11120      char *s, **savePtr;
11121 {
11122     if (*savePtr) {
11123         free(*savePtr);
11124     }
11125     if ((*savePtr = (char *) malloc(strlen(s) + 1))) {
11126         strcpy(*savePtr, s);
11127     }
11128     return(*savePtr);
11129 }
11130
11131 char *
11132 PGNDate()
11133 {
11134     time_t clock;
11135     struct tm *tm;
11136     char buf[MSG_SIZ];
11137
11138     clock = time((time_t *)NULL);
11139     tm = localtime(&clock);
11140     sprintf(buf, "%04d.%02d.%02d",
11141             tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
11142     return StrSave(buf);
11143 }
11144
11145
11146 char *
11147 PositionToFEN(move)
11148      int move;
11149 {
11150     int i, j, fromX, fromY, toX, toY;
11151     int whiteToPlay;
11152     char buf[128];
11153     char *p, *q;
11154     int emptycount;
11155
11156     whiteToPlay = (gameMode == EditPosition) ?
11157       !blackPlaysFirst : (move % 2 == 0);
11158     p = buf;
11159
11160     /* Piece placement data */
11161     for (i = BOARD_SIZE - 1; i >= 0; i--) {
11162         emptycount = 0;
11163         for (j = 0; j < BOARD_SIZE; j++) {
11164             if (boards[move][i][j] == EmptySquare) {
11165                 emptycount++;
11166             } else {
11167                 if (emptycount > 0) {
11168                     *p++ = '0' + emptycount;
11169                     emptycount = 0;
11170                 }
11171                 *p++ = PieceToChar(boards[move][i][j]);
11172             }
11173         }
11174         if (emptycount > 0) {
11175             *p++ = '0' + emptycount;
11176             emptycount = 0;
11177         }
11178         *p++ = '/';
11179     }
11180     *(p - 1) = ' ';
11181
11182     /* Active color */
11183     *p++ = whiteToPlay ? 'w' : 'b';
11184     *p++ = ' ';
11185
11186     /* !!We don't keep track of castling availability, so fake it */
11187     q = p;
11188     if (boards[move][0][4] == WhiteKing) {
11189         if (boards[move][0][7] == WhiteRook) *p++ = 'K';
11190         if (boards[move][0][0] == WhiteRook) *p++ = 'Q';
11191     }
11192     if (boards[move][7][4] == BlackKing) {
11193         if (boards[move][7][7] == BlackRook) *p++ = 'k';
11194         if (boards[move][7][0] == BlackRook) *p++ = 'q';
11195     }       
11196     if (q == p) *p++ = '-';
11197     *p++ = ' ';
11198
11199     /* En passant target square */
11200     if (move > backwardMostMove) {
11201         fromX = moveList[move - 1][0] - 'a';
11202         fromY = moveList[move - 1][1] - '1';
11203         toX = moveList[move - 1][2] - 'a';
11204         toY = moveList[move - 1][3] - '1';
11205         if (fromY == (whiteToPlay ? 6 : 1) &&
11206             toY == (whiteToPlay ? 4 : 3) &&
11207             boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) &&
11208             fromX == toX) {
11209             /* 2-square pawn move just happened */
11210             *p++ = toX + 'a';
11211             *p++ = whiteToPlay ? '6' : '3';
11212         } else {
11213             *p++ = '-';
11214         }
11215     } else {
11216         *p++ = '-';
11217     }
11218
11219     /* !!We don't keep track of halfmove clock for 50-move rule */
11220     strcpy(p, " 0 ");
11221     p += 3;
11222
11223     /* Fullmove number */
11224     sprintf(p, "%d", (move / 2) + 1);
11225     
11226     return StrSave(buf);
11227 }
11228
11229 Boolean
11230 ParseFEN(board, blackPlaysFirst, fen)
11231      Board board;
11232      int *blackPlaysFirst;
11233      char *fen;
11234 {
11235     int i, j;
11236     char *p;
11237     int emptycount;
11238
11239     p = fen;
11240
11241     /* Piece placement data */
11242     for (i = BOARD_SIZE - 1; i >= 0; i--) {
11243         j = 0;
11244         for (;;) {
11245             if (*p == '/' || *p == ' ') {
11246                 if (*p == '/') p++;
11247                 emptycount = BOARD_SIZE - j;
11248                 while (emptycount--) board[i][j++] = EmptySquare;
11249                 break;
11250             } else if (isdigit(*p)) {
11251                 emptycount = *p++ - '0';
11252                 if (j + emptycount > BOARD_SIZE) return FALSE;
11253                 while (emptycount--) board[i][j++] = EmptySquare;
11254             } else if (isalpha(*p)) {
11255                 if (j >= BOARD_SIZE) return FALSE;
11256                 board[i][j++] = CharToPiece(*p++);
11257             } else {
11258                 return FALSE;
11259             }
11260         }
11261     }
11262     while (*p == '/' || *p == ' ') p++;
11263
11264     /* Active color */
11265     switch (*p) {
11266       case 'w':
11267         *blackPlaysFirst = FALSE;
11268         break;
11269       case 'b': 
11270         *blackPlaysFirst = TRUE;
11271         break;
11272       default:
11273         return FALSE;
11274     }
11275     /* !!We ignore the rest of the FEN notation */
11276     return TRUE;
11277 }
11278       
11279 void
11280 EditPositionPasteFEN(char *fen)
11281 {
11282   if (fen != NULL) {
11283     Board initial_position;
11284
11285     if (!ParseFEN(initial_position, &blackPlaysFirst, fen)) {
11286       DisplayError("Bad FEN position in clipboard", 0);
11287       return ;
11288     } else {
11289       int savedBlackPlaysFirst = blackPlaysFirst;
11290       EditPositionEvent();
11291       blackPlaysFirst = savedBlackPlaysFirst;
11292       CopyBoard(boards[0], initial_position);
11293       EditPositionDone();
11294       DisplayBothClocks();
11295       DrawPosition(FALSE, boards[currentMove]);
11296     }
11297   }
11298 }