Merge commit 'v4.3.16'
[xboard.git] / backend.c
index 84937ae..b8cfa92 100644 (file)
--- a/backend.c
+++ b/backend.c
-/*
- * backend.c -- Common back end for X and Windows NT versions of
- * XBoard $Id$
- *
- * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
- * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
- *
- * The following terms apply to Digital Equipment Corporation's copyright
- * interest in XBoard:
- * ------------------------------------------------------------------------
- * All Rights Reserved
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted,
- * provided that the above copyright notice appear in all copies and that
- * both that copyright notice and this permission notice appear in
- * supporting documentation, and that the name of Digital not be
- * used in advertising or publicity pertaining to distribution of the
- * software without specific, written prior permission.
- *
- * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
- * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
- * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
- * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
- * ------------------------------------------------------------------------
- *
- * The following terms apply to the enhanced version of XBoard distributed
- * by the Free Software Foundation:
- * ------------------------------------------------------------------------
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * ------------------------------------------------------------------------
- *
- * See the file ChangeLog for a revision history.  */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <ctype.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <math.h>
-
-#if STDC_HEADERS
-# include <stdlib.h>
-# include <string.h>
-#else /* not STDC_HEADERS */
-# if HAVE_STRING_H
-#  include <string.h>
-# else /* not HAVE_STRING_H */
-#  include <strings.h>
-# endif /* not HAVE_STRING_H */
-#endif /* not STDC_HEADERS */
-
-#if HAVE_SYS_FCNTL_H
-# include <sys/fcntl.h>
-#else /* not HAVE_SYS_FCNTL_H */
-# if HAVE_FCNTL_H
-#  include <fcntl.h>
-# endif /* HAVE_FCNTL_H */
-#endif /* not HAVE_SYS_FCNTL_H */
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#if defined(_amigados) && !defined(__GNUC__)
-struct timezone {
-    int tz_minuteswest;
-    int tz_dsttime;
-};
-extern int gettimeofday(struct timeval *, struct timezone *);
-#endif
-
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#include "common.h"
-#include "frontend.h"
-#include "backend.h"
-#include "parser.h"
-#include "moves.h"
-#if ZIPPY
-# include "zippy.h"
-#endif
-#include "backendz.h"
-#include "gettext.h"
-
-#ifdef ENABLE_NLS
-# define  _(s) gettext (s)
-# define N_(s) gettext_noop (s)
-#else
-# define  _(s) (s)
-# define N_(s)  s
-#endif
-
-
-/* A point in time */
-typedef struct {
-    long sec;  /* Assuming this is >= 32 bits */
-    int ms;    /* Assuming this is >= 16 bits */
-} TimeMark;
-
-/* Search stats from chessprogram */
-typedef struct {
-  char movelist[2*MSG_SIZ]; /* Last PV we were sent */
-  int depth;              /* Current search depth */
-  int nr_moves;           /* Total nr of root moves */
-  int moves_left;         /* Moves remaining to be searched */
-  char move_name[MOVE_LEN];  /* Current move being searched, if provided */
-  u64 nodes;                     /* # of nodes searched */
-  int time;               /* Search time (centiseconds) */
-  int score;              /* Score (centipawns) */
-  int got_only_move;      /* If last msg was "(only move)" */
-  int got_fail;           /* 0 - nothing, 1 - got "--", 2 - got "++" */
-  int ok_to_send;         /* handshaking between send & recv */
-  int line_is_book;       /* 1 if movelist is book moves */
-  int seen_stat;          /* 1 if we've seen the stat01: line */
-} ChessProgramStats;
-
-int establish P((void));
-void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
-                        char *buf, int count, int error));
-void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
-                     char *buf, int count, int error));
-void SendToICS P((char *s));
-void SendToICSDelayed P((char *s, long msdelay));
-void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
-                     int toX, int toY));
-void InitPosition P((int redraw));
-void HandleMachineMove P((char *message, ChessProgramState *cps));
-int AutoPlayOneMove P((void));
-int LoadGameOneMove P((ChessMove readAhead));
-int LoadGameFromFile P((char *filename, int n, char *title, int useList));
-int LoadPositionFromFile P((char *filename, int n, char *title));
-int SavePositionToFile P((char *filename));
-void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
-                 Board board));
-void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
-void ShowMove P((int fromX, int fromY, int toX, int toY));
-void FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
-                  /*char*/int promoChar));
-void BackwardInner P((int target));
-void ForwardInner P((int target));
-void GameEnds P((ChessMove result, char *resultDetails, int whosays));
-void EditPositionDone P((void));
-void PrintOpponents P((FILE *fp));
-void PrintPosition P((FILE *fp, int move));
-void StartChessProgram P((ChessProgramState *cps));
-void SendToProgram P((char *message, ChessProgramState *cps));
-void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
-void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
-                          char *buf, int count, int error));
-void SendTimeControl P((ChessProgramState *cps,
-                       int mps, long tc, int inc, int sd, int st));
-char *TimeControlTagValue P((void));
-void Attention P((ChessProgramState *cps));
-void FeedMovesToProgram P((ChessProgramState *cps, int upto));
-void ResurrectChessProgram P((void));
-void DisplayComment P((int moveNumber, char *text));
-void DisplayMove P((int moveNumber));
-void DisplayAnalysis P((void));
-
-void ParseGameHistory P((char *game));
-void ParseBoard12 P((char *string));
-void StartClocks P((void));
-void SwitchClocks P((void));
-void StopClocks P((void));
-void ResetClocks P((void));
-char *PGNDate P((void));
-void SetGameInfo P((void));
-Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
-int RegisterMove P((void));
-void MakeRegisteredMove P((void));
-void TruncateGame P((void));
-int looking_at P((char *, int *, char *));
-void CopyPlayerNameIntoFileName P((char **, char *));
-char *SavePart P((char *));
-int SaveGameOldStyle P((FILE *));
-int SaveGamePGN P((FILE *));
-void GetTimeMark P((TimeMark *));
-long SubtractTimeMarks P((TimeMark *, TimeMark *));
-int CheckFlags P((void));
-long NextTickLength P((long));
-void CheckTimeControl P((void));
-void show_bytes P((FILE *, char *, int));
-int string_to_rating P((char *str));
-void ParseFeatures P((char* args, ChessProgramState *cps));
-void InitBackEnd3 P((void));
-void FeatureDone P((ChessProgramState* cps, int val));
-void InitChessProgram P((ChessProgramState *cps));
-double u64ToDouble P((u64 value));
-
-#ifdef WIN32
-       extern void ConsoleCreate();
-#endif
-
-extern int tinyLayout, smallLayout;
-static ChessProgramStats programStats;
-
-/* States for ics_getting_history */
-#define H_FALSE 0
-#define H_REQUESTED 1
-#define H_GOT_REQ_HEADER 2
-#define H_GOT_UNREQ_HEADER 3
-#define H_GETTING_MOVES 4
-#define H_GOT_UNWANTED_HEADER 5
-
-/* whosays values for GameEnds */
-#define GE_ICS 0
-#define GE_ENGINE 1
-#define GE_PLAYER 2
-#define GE_FILE 3
-#define GE_XBOARD 4
-
-/* Maximum number of games in a cmail message */
-#define CMAIL_MAX_GAMES 20
-
-/* Different types of move when calling RegisterMove */
-#define CMAIL_MOVE   0
-#define CMAIL_RESIGN 1
-#define CMAIL_DRAW   2
-#define CMAIL_ACCEPT 3
-
-/* Different types of result to remember for each game */
-#define CMAIL_NOT_RESULT 0
-#define CMAIL_OLD_RESULT 1
-#define CMAIL_NEW_RESULT 2
-
-/* Telnet protocol constants */
-#define TN_WILL 0373
-#define TN_WONT 0374
-#define TN_DO   0375
-#define TN_DONT 0376
-#define TN_IAC  0377
-#define TN_ECHO 0001
-#define TN_SGA  0003
-#define TN_PORT 23
-
-/* Some compiler can't cast u64 to double
- * This function do the job for us:
-
- * We use the highest bit for cast, this only
- * works if the highest bit is not
- * in use (This should not happen)
- *
- * We used this for all compiler
- */
-double
-u64ToDouble(u64 value)
-{
-  double r;
-  u64 tmp = value & u64Const(0x7fffffffffffffff);
-  r = (double)(s64)tmp;
-  if (value & u64Const(0x8000000000000000))
-       r +=  9.2233720368547758080e18; /* 2^63 */
- return r;
-}
-
-/* Fake up flags for now, as we aren't keeping track of castling
-   availability yet */
-int
-PosFlags(index)
-{
-  int flags = F_ALL_CASTLE_OK;
-  if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
-  switch (gameInfo.variant) {
-  case VariantSuicide:
-  case VariantGiveaway:
-    flags |= F_IGNORE_CHECK;
-    flags &= ~F_ALL_CASTLE_OK;
-    break;
-  case VariantAtomic:
-    flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
-    break;
-  case VariantKriegspiel:
-    flags |= F_KRIEGSPIEL_CAPTURE;
-    break;
-  case VariantNoCastle:
-    flags &= ~F_ALL_CASTLE_OK;
-    break;
-  default:
-    break;
-  }
-  return flags;
-}
-
-FILE *gameFileFP, *debugFP;
-
-char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
-char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
-char thinkOutput1[MSG_SIZ*10];
-
-ChessProgramState first, second;
-
-/* premove variables */
-int premoveToX = 0;
-int premoveToY = 0;
-int premoveFromX = 0;
-int premoveFromY = 0;
-int premovePromoChar = 0;
-int gotPremove = 0;
-Boolean alarmSounded;
-/* end premove variables */
-
-char *ics_prefix = "$";
-int ics_type = ICS_GENERIC;
-
-int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
-int pauseExamForwardMostMove = 0;
-int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
-int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
-int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
-int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
-int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
-int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
-int whiteFlag = FALSE, blackFlag = FALSE;
-int userOfferedDraw = FALSE;
-int ics_user_moved = 0, ics_gamenum = -1, ics_getting_history = H_FALSE;
-int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
-int cmailMoveType[CMAIL_MAX_GAMES];
-long ics_clock_paused = 0;
-ProcRef icsPR = NoProc, cmailPR = NoProc;
-InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
-GameMode gameMode = BeginningOfGame;
-char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
-char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
-char white_holding[64], black_holding[64];
-TimeMark lastNodeCountTime;
-long lastNodeCount=0;
-int have_sent_ICS_logon = 0;
-int movesPerSession;
-long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
-long timeRemaining[2][MAX_MOVES];
-int matchGame = 0;
-TimeMark programStartTime;
-char ics_handle[MSG_SIZ];
-int have_set_title = 0;
-
-/* animateTraining preserves the state of appData.animate
- * when Training mode is activated. This allows the
- * response to be animated when appData.animate == TRUE and
- * appData.animateDragging == TRUE.
- */
-Boolean animateTraining;
-
-GameInfo gameInfo;
-
-AppData appData;
-
-Board boards[MAX_MOVES];
-Board initialPosition = {
-    { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
-       WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
-    { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
-       WhitePawn, WhitePawn, WhitePawn, WhitePawn },
-    { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
-       EmptySquare, EmptySquare, EmptySquare, EmptySquare },
-    { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
-       EmptySquare, EmptySquare, EmptySquare, EmptySquare },
-    { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
-       EmptySquare, EmptySquare, EmptySquare, EmptySquare },
-    { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
-       EmptySquare, EmptySquare, EmptySquare, EmptySquare },
-    { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
-       BlackPawn, BlackPawn, BlackPawn, BlackPawn },
-    { BlackRook, BlackKnight, BlackBishop, BlackQueen,
-       BlackKing, BlackBishop, BlackKnight, BlackRook }
-};
-Board twoKingsPosition = {
-    { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
-       WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
-    { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
-       WhitePawn, WhitePawn, WhitePawn, WhitePawn },
-    { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
-       EmptySquare, EmptySquare, EmptySquare, EmptySquare },
-    { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
-       EmptySquare, EmptySquare, EmptySquare, EmptySquare },
-    { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
-       EmptySquare, EmptySquare, EmptySquare, EmptySquare },
-    { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
-       EmptySquare, EmptySquare, EmptySquare, EmptySquare },
-    { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
-       BlackPawn, BlackPawn, BlackPawn, BlackPawn },
-    { BlackRook, BlackKnight, BlackBishop, BlackQueen,
-       BlackKing, BlackKing, BlackKnight, BlackRook }
-};
-
-
-/* Convert str to a rating. Checks for special cases of "----",
-   "++++", etc. Also strips ()'s */
-int
-string_to_rating(str)
-  char *str;
-{
-  while(*str && !isdigit(*str)) ++str;
-  if (!*str)
-    return 0;  /* One of the special "no rating" cases */
-  else
-    return atoi(str);
-}
-
-void
-ClearProgramStats()
-{
-    /* Init programStats */
-    programStats.movelist[0] = 0;
-    programStats.depth = 0;
-    programStats.nr_moves = 0;
-    programStats.moves_left = 0;
-    programStats.nodes = 0;
-    programStats.time = 100;
-    programStats.score = 0;
-    programStats.got_only_move = 0;
-    programStats.got_fail = 0;
-    programStats.line_is_book = 0;
-}
-
-void
-InitBackEnd1()
-{
-    int matched, min, sec;
-
-    GetTimeMark(&programStartTime);
-
-    ClearProgramStats();
-    programStats.ok_to_send = 1;
-    programStats.seen_stat = 0;
-
-    /*
-     * Initialize game list
-     */
-    ListNew(&gameList);
-
-
-    /*
-     * Internet chess server status
-     */
-    if (appData.icsActive) {
-       appData.matchMode = FALSE;
-       appData.matchGames = 0;
-#if ZIPPY
-       appData.noChessProgram = !appData.zippyPlay;
-#else
-       appData.zippyPlay = FALSE;
-       appData.zippyTalk = FALSE;
-       appData.noChessProgram = TRUE;
-#endif
-       if (*appData.icsHelper != NULLCHAR) {
-           appData.useTelnet = TRUE;
-           appData.telnetProgram = appData.icsHelper;
-       }
-    } else {
-       appData.zippyTalk = appData.zippyPlay = FALSE;
-    }
-
-    /*
-     * Parse timeControl resource
-     */
-    if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
-                         appData.movesPerSession)) {
-       char buf[MSG_SIZ];
-       sprintf(buf, _("bad timeControl option %s"), appData.timeControl);
-       DisplayFatalError(buf, 0, 2);
-    }
-
-    /*
-     * Parse searchTime resource
-     */
-    if (*appData.searchTime != NULLCHAR) {
-       matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
-       if (matched == 1) {
-           searchTime = min * 60;
-       } else if (matched == 2) {
-           searchTime = min * 60 + sec;
-       } else {
-           char buf[MSG_SIZ];
-           sprintf(buf, _("bad searchTime option %s"), appData.searchTime);
-           DisplayFatalError(buf, 0, 2);
-       }
-    }
-
-    first.which = "first";
-    second.which = "second";
-    first.maybeThinking = second.maybeThinking = FALSE;
-    first.pr = second.pr = NoProc;
-    first.isr = second.isr = NULL;
-    first.sendTime = second.sendTime = 2;
-    first.sendDrawOffers = 1;
-    if (appData.firstPlaysBlack) {
-       first.twoMachinesColor = "black\n";
-       second.twoMachinesColor = "white\n";
-    } else {
-       first.twoMachinesColor = "white\n";
-       second.twoMachinesColor = "black\n";
-    }
-    first.program = appData.firstChessProgram;
-    second.program = appData.secondChessProgram;
-    first.host = appData.firstHost;
-    second.host = appData.secondHost;
-    first.dir = appData.firstDirectory;
-    second.dir = appData.secondDirectory;
-    first.other = &second;
-    second.other = &first;
-    first.initString = appData.initString;
-    second.initString = appData.secondInitString;
-    first.computerString = appData.firstComputerString;
-    second.computerString = appData.secondComputerString;
-    first.useSigint = second.useSigint = TRUE;
-    first.useSigterm = second.useSigterm = TRUE;
-    first.reuse = appData.reuseFirst;
-    second.reuse = appData.reuseSecond;
-    first.useSetboard = second.useSetboard = FALSE;
-    first.useSAN = second.useSAN = FALSE;
-    first.usePing = second.usePing = FALSE;
-    first.lastPing = second.lastPing = 0;
-    first.lastPong = second.lastPong = 0;
-    first.usePlayother = second.usePlayother = FALSE;
-    first.useColors = second.useColors = TRUE;
-    first.useUsermove = second.useUsermove = FALSE;
-    first.sendICS = second.sendICS = FALSE;
-    first.sendName = second.sendName = appData.icsActive;
-    first.sdKludge = second.sdKludge = FALSE;
-    first.stKludge = second.stKludge = FALSE;
-    TidyProgramName(first.program, first.host, first.tidy);
-    TidyProgramName(second.program, second.host, second.tidy);
-    first.matchWins = second.matchWins = 0;
-    strcpy(first.variants, appData.variant);
-    strcpy(second.variants, appData.variant);
-    first.analysisSupport = second.analysisSupport = 2; /* detect */
-    first.analyzing = second.analyzing = FALSE;
-    first.initDone = second.initDone = FALSE;
-
-    if (appData.firstProtocolVersion > PROTOVER ||
-       appData.firstProtocolVersion < 1) {
-      char buf[MSG_SIZ];
-      sprintf(buf, _("protocol version %d not supported"),
-             appData.firstProtocolVersion);
-      DisplayFatalError(buf, 0, 2);
-    } else {
-      first.protocolVersion = appData.firstProtocolVersion;
-    }
-
-    if (appData.secondProtocolVersion > PROTOVER ||
-       appData.secondProtocolVersion < 1) {
-      char buf[MSG_SIZ];
-      sprintf(buf, _("protocol version %d not supported"),
-             appData.secondProtocolVersion);
-      DisplayFatalError(buf, 0, 2);
-    } else {
-      second.protocolVersion = appData.secondProtocolVersion;
-    }
-
-    if (appData.icsActive) {
-        appData.clockMode = TRUE;  /* changes dynamically in ICS mode */
-    } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
-       appData.clockMode = FALSE;
-       first.sendTime = second.sendTime = 0;
-    }
-
-#if ZIPPY
-    /* Override some settings from environment variables, for backward
-       compatibility.  Unfortunately it's not feasible to have the env
-       vars just set defaults, at least in xboard.  Ugh.
-    */
-    if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
-      ZippyInit();
-    }
-#endif
-
-    if (appData.noChessProgram) {
-       programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)
-                                       + strlen(PATCHLEVEL));
-       sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);
-    } else {
-       char *p, *q;
-       q = first.program;
-       while (*q != ' ' && *q != NULLCHAR) q++;
-       p = q;
-       while (p > first.program && *(p-1) != '/') p--;
-       programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
-                                       + strlen(PATCHLEVEL) + (q - p));
-       sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);
-       strncat(programVersion, p, q - p);
-    }
-
-    if (!appData.icsActive) {
-      char buf[MSG_SIZ];
-      /* Check for variants that are supported only in ICS mode,
-         or not at all.  Some that are accepted here nevertheless
-         have bugs; see comments below.
-      */
-      VariantClass variant = StringToVariant(appData.variant);
-      switch (variant) {
-      case VariantBughouse:     /* need four players and two boards */
-      case VariantKriegspiel:   /* need to hide pieces and move details */
-      case VariantFischeRandom: /* castling doesn't work, shuffle not done */
-       sprintf(buf, _("Variant %s supported only in ICS mode"), appData.variant);
-       DisplayFatalError(buf, 0, 2);
-       return;
-
-      case VariantUnknown:
-      case VariantLoadable:
-      case Variant29:
-      case Variant30:
-      case Variant31:
-      case Variant32:
-      case Variant33:
-      case Variant34:
-      case Variant35:
-      case Variant36:
-      default:
-       sprintf(buf, _("Unknown variant name %s"), appData.variant);
-       DisplayFatalError(buf, 0, 2);
-       return;
-
-      case VariantNormal:     /* definitely works! */
-      case VariantWildCastle: /* pieces not automatically shuffled */
-      case VariantNoCastle:   /* pieces not automatically shuffled */
-      case VariantCrazyhouse: /* holdings not shown,
-                                offboard interposition not understood */
-      case VariantLosers:     /* should work except for win condition,
-                                and doesn't know captures are mandatory */
-      case VariantSuicide:    /* should work except for win condition,
-                                and doesn't know captures are mandatory */
-      case VariantGiveaway:   /* should work except for win condition,
-                                and doesn't know captures are mandatory */
-      case VariantTwoKings:   /* should work */
-      case VariantAtomic:     /* should work except for win condition */
-      case Variant3Check:     /* should work except for win condition */
-      case VariantShatranj:   /* might work if TestLegality is off */
-       break;
-      }
-    }
-}
-
-int
-ParseTimeControl(tc, ti, mps)
-     char *tc;
-     int ti;
-     int mps;
-{
-    int matched, min, sec;
-
-    matched = sscanf(tc, "%d:%d", &min, &sec);
-    if (matched == 1) {
-       timeControl = min * 60 * 1000;
-    } else if (matched == 2) {
-       timeControl = (min * 60 + sec) * 1000;
-    } else {
-       return FALSE;
-    }
-
-    if (ti >= 0) {
-       timeIncrement = ti * 1000;  /* convert to ms */
-       movesPerSession = 0;
-    } else {
-       timeIncrement = 0;
-       movesPerSession = mps;
-    }
-    return TRUE;
-}
-
-void
-InitBackEnd2()
-{
-    if (appData.debugMode) {
-       fprintf(debugFP, "%s\n", programVersion);
-    }
-
-    if (appData.matchGames > 0) {
-       appData.matchMode = TRUE;
-    } else if (appData.matchMode) {
-       appData.matchGames = 1;
-    }
-    Reset(TRUE, FALSE);
-    if (appData.noChessProgram || first.protocolVersion == 1) {
-      InitBackEnd3();
-    } else {
-      /* kludge: allow timeout for initial "feature" commands */
-      FreezeUI();
-      DisplayMessage("", "Starting chess program");
-      ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
-    }
-}
-
-void
-InitBackEnd3 P((void))
-{
-    GameMode initialMode;
-    char buf[MSG_SIZ];
-    int err;
-
-    InitChessProgram(&first);
-
-    #ifdef WIN32
-               /* Make a console window if needed */
-               if (appData.icsActive) ConsoleCreate();
-       #endif
-
-    if (appData.icsActive) {
-       err = establish();
-       if (err != 0) {
-           if (*appData.icsCommPort != NULLCHAR) {
-               sprintf(buf, _("Could not open comm port %s"),
-                       appData.icsCommPort);
-           } else {
-               sprintf(buf, _("Could not connect to host %s, port %s"),
-                       appData.icsHost, appData.icsPort);
-           }
-           DisplayFatalError(buf, err, 1);
-           return;
-       }
-       SetICSMode();
-       telnetISR =
-         AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
-       fromUserISR =
-         AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
-    } else if (appData.noChessProgram) {
-       SetNCPMode();
-    } else {
-       SetGNUMode();
-    }
-
-    if (*appData.cmailGameName != NULLCHAR) {
-       SetCmailMode();
-       OpenLoopback(&cmailPR);
-       cmailISR =
-         AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
-    }
-
-    ThawUI();
-    DisplayMessage("", "");
-    if (StrCaseCmp(appData.initialMode, "") == 0) {
-      initialMode = BeginningOfGame;
-    } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
-      initialMode = TwoMachinesPlay;
-    } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
-      initialMode = AnalyzeFile;
-    } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
-      initialMode = AnalyzeMode;
-    } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
-      initialMode = MachinePlaysWhite;
-    } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
-      initialMode = MachinePlaysBlack;
-    } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
-      initialMode = EditGame;
-    } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
-      initialMode = EditPosition;
-    } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
-      initialMode = Training;
-    } else {
-      sprintf(buf, _("Unknown initialMode %s"), appData.initialMode);
-      DisplayFatalError(buf, 0, 2);
-      return;
-    }
-
-    if (appData.matchMode) {
-       /* Set up machine vs. machine match */
-       if (appData.noChessProgram) {
-           DisplayFatalError(_("Can't have a match with no chess programs"),
-                             0, 2);
-           return;
-       }
-       matchMode = TRUE;
-       matchGame = 1;
-       if (*appData.loadGameFile != NULLCHAR) {
-           if (!LoadGameFromFile(appData.loadGameFile,
-                                 appData.loadGameIndex,
-                                 appData.loadGameFile, FALSE)) {
-               DisplayFatalError(_("Bad game file"), 0, 1);
-               return;
-           }
-       } else if (*appData.loadPositionFile != NULLCHAR) {
-           if (!LoadPositionFromFile(appData.loadPositionFile,
-                                     appData.loadPositionIndex,
-                                     appData.loadPositionFile)) {
-               DisplayFatalError(_("Bad position file"), 0, 1);
-               return;
-           }
-       }
-       TwoMachinesEvent();
-    } else if (*appData.cmailGameName != NULLCHAR) {
-       /* Set up cmail mode */
-       ReloadCmailMsgEvent(TRUE);
-    } else {
-       /* Set up other modes */
-       if (initialMode == AnalyzeFile) {
-         if (*appData.loadGameFile == NULLCHAR) {
-           DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1);
-           return;
-         }
-       }
-       if (*appData.loadGameFile != NULLCHAR) {
-           (void) LoadGameFromFile(appData.loadGameFile,
-                                   appData.loadGameIndex,
-                                   appData.loadGameFile, TRUE);
-       } else if (*appData.loadPositionFile != NULLCHAR) {
-           (void) LoadPositionFromFile(appData.loadPositionFile,
-                                       appData.loadPositionIndex,
-                                       appData.loadPositionFile);
-       }
-       if (initialMode == AnalyzeMode) {
-         if (appData.noChessProgram) {
-           DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2);
-           return;
-         }
-         if (appData.icsActive) {
-           DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2);
-           return;
-         }
-         AnalyzeModeEvent();
-       } else if (initialMode == AnalyzeFile) {
-         ShowThinkingEvent(TRUE);
-         AnalyzeFileEvent();
-         AnalysisPeriodicEvent(1);
-       } else if (initialMode == MachinePlaysWhite) {
-         if (appData.noChessProgram) {
-           DisplayFatalError(_("MachineWhite mode requires a chess engine"),
-                             0, 2);
-           return;
-         }
-         if (appData.icsActive) {
-           DisplayFatalError(_("MachineWhite mode does not work with ICS mode"),
-                             0, 2);
-           return;
-         }
-         MachineWhiteEvent();
-       } else if (initialMode == MachinePlaysBlack) {
-         if (appData.noChessProgram) {
-           DisplayFatalError(_("MachineBlack mode requires a chess engine"),
-                             0, 2);
-           return;
-         }
-         if (appData.icsActive) {
-           DisplayFatalError(_("MachineBlack mode does not work with ICS mode"),
-                             0, 2);
-           return;
-         }
-         MachineBlackEvent();
-       } else if (initialMode == TwoMachinesPlay) {
-         if (appData.noChessProgram) {
-           DisplayFatalError(_("TwoMachines mode requires a chess engine"),
-                             0, 2);
-           return;
-         }
-         if (appData.icsActive) {
-           DisplayFatalError(_("TwoMachines mode does not work with ICS mode"),
-                             0, 2);
-           return;
-         }
-         TwoMachinesEvent();
-       } else if (initialMode == EditGame) {
-         EditGameEvent();
-       } else if (initialMode == EditPosition) {
-         EditPositionEvent();
-       } else if (initialMode == Training) {
-         if (*appData.loadGameFile == NULLCHAR) {
-           DisplayFatalError(_("Training mode requires a game file"), 0, 2);
-           return;
-         }
-         TrainingEvent();
-       }
-    }
-}
-
-/*
- * Establish will establish a contact to a remote host.port.
- * Sets icsPR to a ProcRef for a process (or pseudo-process)
- *  used to talk to the host.
- * Returns 0 if okay, error code if not.
- */
-int
-establish()
-{
-    char buf[MSG_SIZ];
-
-    if (*appData.icsCommPort != NULLCHAR) {
-       /* Talk to the host through a serial comm port */
-       return OpenCommPort(appData.icsCommPort, &icsPR);
-
-    } else if (*appData.gateway != NULLCHAR) {
-       if (*appData.remoteShell == NULLCHAR) {
-           /* Use the rcmd protocol to run telnet program on a gateway host */
-           sprintf(buf, "%s %s %s",
-                   appData.telnetProgram, appData.icsHost, appData.icsPort);
-           return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);
-
-       } else {
-           /* Use the rsh program to run telnet program on a gateway host */
-           if (*appData.remoteUser == NULLCHAR) {
-               sprintf(buf, "%s %s %s %s %s", appData.remoteShell,
-                       appData.gateway, appData.telnetProgram,
-                       appData.icsHost, appData.icsPort);
-           } else {
-               sprintf(buf, "%s %s -l %s %s %s %s",
-                       appData.remoteShell, appData.gateway,
-                       appData.remoteUser, appData.telnetProgram,
-                       appData.icsHost, appData.icsPort);
-           }
-           return StartChildProcess(buf, "", &icsPR);
-
-       }
-    } else if (appData.useTelnet) {
-       return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
-
-    } else {
-       /* TCP socket interface differs somewhat between
-          Unix and NT; handle details in the front end.
-          */
-       return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
-    }
-}
-
-void
-show_bytes(fp, buf, count)
-     FILE *fp;
-     char *buf;
-     int count;
-{
-    while (count--) {
-       if (*buf < 040 || *(unsigned char *) buf > 0177) {
-           fprintf(fp, "\\%03o", *buf & 0xff);
-       } else {
-           putc(*buf, fp);
-       }
-       buf++;
-    }
-    fflush(fp);
-}
-
-/* Returns an errno value */
-int
-OutputMaybeTelnet(pr, message, count, outError)
-     ProcRef pr;
-     char *message;
-     int count;
-     int *outError;
-{
-    char buf[8192], *p, *q, *buflim;
-    int left, newcount, outcount;
-
-    if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
-       *appData.gateway != NULLCHAR) {
-       if (appData.debugMode) {
-           fprintf(debugFP, ">ICS: ");
-           show_bytes(debugFP, message, count);
-           fprintf(debugFP, "\n");
-       }
-       return OutputToProcess(pr, message, count, outError);
-    }
-
-    buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
-    p = message;
-    q = buf;
-    left = count;
-    newcount = 0;
-    while (left) {
-       if (q >= buflim) {
-           if (appData.debugMode) {
-               fprintf(debugFP, ">ICS: ");
-               show_bytes(debugFP, buf, newcount);
-               fprintf(debugFP, "\n");
-           }
-           outcount = OutputToProcess(pr, buf, newcount, outError);
-           if (outcount < newcount) return -1; /* to be sure */
-           q = buf;
-           newcount = 0;
-       }
-       if (*p == '\n') {
-           *q++ = '\r';
-           newcount++;
-       } else if (((unsigned char) *p) == TN_IAC) {
-           *q++ = (char) TN_IAC;
-           newcount ++;
-       }
-       *q++ = *p++;
-       newcount++;
-       left--;
-    }
-    if (appData.debugMode) {
-       fprintf(debugFP, ">ICS: ");
-       show_bytes(debugFP, buf, newcount);
-       fprintf(debugFP, "\n");
-    }
-    outcount = OutputToProcess(pr, buf, newcount, outError);
-    if (outcount < newcount) return -1; /* to be sure */
-    return count;
-}
-
-void
-read_from_player(isr, closure, message, count, error)
-     InputSourceRef isr;
-     VOIDSTAR closure;
-     char *message;
-     int count;
-     int error;
-{
-    int outError, outCount;
-    static int gotEof = 0;
-
-    /* Pass data read from player on to ICS */
-    if (count > 0) {
-       gotEof = 0;
-       outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
-       if (outCount < count) {
-           DisplayFatalError(_("Error writing to ICS"), outError, 1);
-       }
-    } else if (count < 0) {
-       RemoveInputSource(isr);
-       DisplayFatalError(_("Error reading from keyboard"), error, 1);
-    } else if (gotEof++ > 0) {
-       RemoveInputSource(isr);
-       DisplayFatalError(_("Got end of file from keyboard"), 0, 0);
-    }
-}
-
-void
-SendToICS(s)
-     char *s;
-{
-    int count, outCount, outError;
-
-    if (icsPR == NULL) return;
-
-    count = strlen(s);
-    outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
-    if (outCount < count) {
-       DisplayFatalError(_("Error writing to ICS"), outError, 1);
-    }
-}
-
-/* This is used for sending logon scripts to the ICS. Sending
-   without a delay causes problems when using timestamp on ICC
-   (at least on my machine). */
-void
-SendToICSDelayed(s,msdelay)
-     char *s;
-     long msdelay;
-{
-    int count, outCount, outError;
-
-    if (icsPR == NULL) return;
-
-    count = strlen(s);
-    if (appData.debugMode) {
-       fprintf(debugFP, ">ICS: ");
-       show_bytes(debugFP, s, count);
-       fprintf(debugFP, "\n");
-    }
-    outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
-                                     msdelay);
-    if (outCount < count) {
-       DisplayFatalError(_("Error writing to ICS"), outError, 1);
-    }
-}
-
-
-/* Remove all highlighting escape sequences in s
-   Also deletes any suffix starting with '('
-   */
-char *
-StripHighlightAndTitle(s)
-     char *s;
-{
-    static char retbuf[MSG_SIZ];
-    char *p = retbuf;
-
-    while (*s != NULLCHAR) {
-       while (*s == '\033') {
-           while (*s != NULLCHAR && !isalpha(*s)) s++;
-           if (*s != NULLCHAR) s++;
-       }
-       while (*s != NULLCHAR && *s != '\033') {
-           if (*s == '(' || *s == '[') {
-               *p = NULLCHAR;
-               return retbuf;
-           }
-           *p++ = *s++;
-       }
-    }
-    *p = NULLCHAR;
-    return retbuf;
-}
-
-/* Remove all highlighting escape sequences in s */
-char *
-StripHighlight(s)
-     char *s;
-{
-    static char retbuf[MSG_SIZ];
-    char *p = retbuf;
-
-    while (*s != NULLCHAR) {
-       while (*s == '\033') {
-           while (*s != NULLCHAR && !isalpha(*s)) s++;
-           if (*s != NULLCHAR) s++;
-       }
-       while (*s != NULLCHAR && *s != '\033') {
-           *p++ = *s++;
-       }
-    }
-    *p = NULLCHAR;
-    return retbuf;
-}
-
-char *variantNames[] = VARIANT_NAMES;
-char *
-VariantName(v)
-     VariantClass v;
-{
-    return variantNames[v];
-}
-
-
-/* Identify a variant from the strings the chess servers use or the
-   PGN Variant tag names we use. */
-VariantClass
-StringToVariant(e)
-     char *e;
-{
-    char *p;
-    int wnum = -1;
-    VariantClass v = VariantNormal;
-    int i, found = FALSE;
-    char buf[MSG_SIZ];
-
-    if (!e) return v;
-
-    for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
-      if (StrCaseStr(e, variantNames[i])) {
-       v = (VariantClass) i;
-       found = TRUE;
-       break;
-      }
-    }
-
-    if (!found) {
-      if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
-         || StrCaseStr(e, "wild/fr")) {
-        v = VariantFischeRandom;
-      } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
-                (i = 1, p = StrCaseStr(e, "w"))) {
-       p += i;
-       while (*p && (isspace(*p) || *p == '(' || *p == '/')) p++;
-       if (isdigit(*p)) {
-         wnum = atoi(p);
-       } else {
-         wnum = -1;
-       }
-       switch (wnum) {
-       case 0: /* FICS only, actually */
-       case 1:
-         /* Castling legal even if K starts on d-file */
-         v = VariantWildCastle;
-         break;
-       case 2:
-       case 3:
-       case 4:
-         /* Castling illegal even if K & R happen to start in
-            normal positions. */
-         v = VariantNoCastle;
-         break;
-       case 5:
-       case 7:
-       case 8:
-       case 10:
-       case 11:
-       case 12:
-       case 13:
-       case 14:
-       case 15:
-       case 18:
-       case 19:
-         /* Castling legal iff K & R start in normal positions */
-         v = VariantNormal;
-         break;
-       case 6:
-       case 20:
-       case 21:
-         /* Special wilds for position setup; unclear what to do here */
-         v = VariantLoadable;
-         break;
-       case 9:
-         /* Bizarre ICC game */
-         v = VariantTwoKings;
-         break;
-       case 16:
-         v = VariantKriegspiel;
-         break;
-       case 17:
-         v = VariantLosers;
-         break;
-       case 22:
-         v = VariantFischeRandom;
-         break;
-       case 23:
-         v = VariantCrazyhouse;
-         break;
-       case 24:
-         v = VariantBughouse;
-         break;
-       case 25:
-         v = Variant3Check;
-         break;
-       case 26:
-         /* Not quite the same as FICS suicide! */
-         v = VariantGiveaway;
-         break;
-       case 27:
-         v = VariantAtomic;
-         break;
-       case 28:
-         v = VariantShatranj;
-         break;
-
-       /* Temporary names for future ICC types.  The name *will* change in
-          the next xboard/WinBoard release after ICC defines it. */
-       case 29:
-         v = Variant29;
-         break;
-       case 30:
-         v = Variant30;
-         break;
-       case 31:
-         v = Variant31;
-         break;
-       case 32:
-         v = Variant32;
-         break;
-       case 33:
-         v = Variant33;
-         break;
-       case 34:
-         v = Variant34;
-         break;
-       case 35:
-         v = Variant35;
-         break;
-       case 36:
-         v = Variant36;
-         break;
-
-       case -1:
-         /* Found "wild" or "w" in the string but no number;
-            must assume it's normal chess. */
-         v = VariantNormal;
-         break;
-       default:
-         sprintf(buf, "Unknown wild type %d", wnum);
-         DisplayError(buf, 0);
-         v = VariantUnknown;
-         break;
-       }
-      }
-    }
-    if (appData.debugMode) {
-      fprintf(debugFP, _("recognized '%s' (%d) as variant %s\n"),
-             e, wnum, VariantName(v));
-    }
-    return v;
-}
-
-static int leftover_start = 0, leftover_len = 0;
-char star_match[STAR_MATCH_N][MSG_SIZ];
-
-/* Test whether pattern is present at &buf[*index]; if so, return TRUE,
-   advance *index beyond it, and set leftover_start to the new value of
-   *index; else return FALSE.  If pattern contains the character '*', it
-   matches any sequence of characters not containing '\r', '\n', or the
-   character following the '*' (if any), and the matched sequence(s) are
-   copied into star_match.
-   */
-int
-looking_at(buf, index, pattern)
-     char *buf;
-     int *index;
-     char *pattern;
-{
-    char *bufp = &buf[*index], *patternp = pattern;
-    int star_count = 0;
-    char *matchp = star_match[0];
-
-    for (;;) {
-       if (*patternp == NULLCHAR) {
-           *index = leftover_start = bufp - buf;
-           *matchp = NULLCHAR;
-           return TRUE;
-       }
-       if (*bufp == NULLCHAR) return FALSE;
-       if (*patternp == '*') {
-           if (*bufp == *(patternp + 1)) {
-               *matchp = NULLCHAR;
-               matchp = star_match[++star_count];
-               patternp += 2;
-               bufp++;
-               continue;
-           } else if (*bufp == '\n' || *bufp == '\r') {
-               patternp++;
-               if (*patternp == NULLCHAR)
-                 continue;
-               else
-                 return FALSE;
-           } else {
-               *matchp++ = *bufp++;
-               continue;
-           }
-       }
-       if (*patternp != *bufp) return FALSE;
-       patternp++;
-       bufp++;
-    }
-}
-
-void
-SendToPlayer(data, length)
-     char *data;
-     int length;
-{
-    int error, outCount;
-    outCount = OutputToProcess(NoProc, data, length, &error);
-    if (outCount < length) {
-       DisplayFatalError(_("Error writing to display"), error, 1);
-    }
-}
-
-void
-PackHolding(packed, holding)
-     char packed[];
-     char *holding;
-{
-    char *p = holding;
-    char *q = packed;
-    int runlength = 0;
-    int curr = 9999;
-    do {
-       if (*p == curr) {
-           runlength++;
-       } else {
-           switch (runlength) {
-             case 0:
-               break;
-             case 1:
-               *q++ = curr;
-               break;
-             case 2:
-               *q++ = curr;
-               *q++ = curr;
-               break;
-             default:
-               sprintf(q, "%d", runlength);
-               while (*q) q++;
-               *q++ = curr;
-               break;
-           }
-           runlength = 1;
-           curr = *p;
-       }
-    } while (*p++);
-    *q = NULLCHAR;
-}
-
-/* Telnet protocol requests from the front end */
-void
-TelnetRequest(ddww, option)
-     unsigned char ddww, option;
-{
-    unsigned char msg[3];
-    int outCount, outError;
-
-    if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
-
-    if (appData.debugMode) {
-       char buf1[8], buf2[8], *ddwwStr, *optionStr;
-       switch (ddww) {
-         case TN_DO:
-           ddwwStr = "DO";
-           break;
-         case TN_DONT:
-           ddwwStr = "DONT";
-           break;
-         case TN_WILL:
-           ddwwStr = "WILL";
-           break;
-         case TN_WONT:
-           ddwwStr = "WONT";
-           break;
-         default:
-           ddwwStr = buf1;
-           sprintf(buf1, "%d", ddww);
-           break;
-       }
-       switch (option) {
-         case TN_ECHO:
-           optionStr = "ECHO";
-           break;
-         default:
-           optionStr = buf2;
-           sprintf(buf2, "%d", option);
-           break;
-       }
-       fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
-    }
-    msg[0] = TN_IAC;
-    msg[1] = ddww;
-    msg[2] = option;
-    outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
-    if (outCount < 3) {
-       DisplayFatalError(_("Error writing to ICS"), outError, 1);
-    }
-}
-
-void
-DoEcho()
-{
-    if (!appData.icsActive) return;
-    TelnetRequest(TN_DO, TN_ECHO);
-}
-
-void
-DontEcho()
-{
-    if (!appData.icsActive) return;
-    TelnetRequest(TN_DONT, TN_ECHO);
-}
-
-static int loggedOn = FALSE;
-
-/*-- Game start info cache: --*/
-int gs_gamenum;
-char gs_kind[MSG_SIZ];
-static char player1Name[128] = "";
-static char player2Name[128] = "";
-static int player1Rating = -1;
-static int player2Rating = -1;
-/*----------------------------*/
-
-void
-read_from_ics(isr, closure, data, count, error)
-     InputSourceRef isr;
-     VOIDSTAR closure;
-     char *data;
-     int count;
-     int error;
-{
-#define BUF_SIZE 8192
-#define STARTED_NONE 0
-#define STARTED_MOVES 1
-#define STARTED_BOARD 2
-#define STARTED_OBSERVE 3
-#define STARTED_HOLDINGS 4
-#define STARTED_CHATTER 5
-#define STARTED_COMMENT 6
-#define STARTED_MOVES_NOHIDE 7
-
-    static int started = STARTED_NONE;
-    static char parse[20000];
-    static int parse_pos = 0;
-    static char buf[BUF_SIZE + 1];
-    static int firstTime = TRUE, intfSet = FALSE;
-    static ColorClass curColor = ColorNormal;
-    static ColorClass prevColor = ColorNormal;
-    static int savingComment = FALSE;
-    char str[500];
-    int i, oldi;
-    int buf_len;
-    int next_out;
-    int tkind;
-    int backup;
-    char *p;
-
-    if (appData.debugMode) {
-      if (!error) {
-       fprintf(debugFP, "<ICS: ");
-       show_bytes(debugFP, data, count);
-       fprintf(debugFP, "\n");
-      }
-    }
-
-    if (count > 0) {
-       /* If last read ended with a partial line that we couldn't parse,
-          prepend it to the new read and try again. */
-       if (leftover_len > 0) {
-           for (i=0; i<leftover_len; i++)
-             buf[i] = buf[leftover_start + i];
-       }
-
-       /* Copy in new characters, removing nulls and \r's */
-       buf_len = leftover_len;
-       for (i = 0; i < count; i++) {
-           if (data[i] != NULLCHAR && data[i] != '\r')
-             buf[buf_len++] = data[i];
-       }
-
-       buf[buf_len] = NULLCHAR;
-       next_out = leftover_len;
-       leftover_start = 0;
-
-       i = 0;
-       while (i < buf_len) {
-           /* Deal with part of the TELNET option negotiation
-              protocol.  We refuse to do anything beyond the
-              defaults, except that we allow the WILL ECHO option,
-              which ICS uses to turn off password echoing when we are
-              directly connected to it.  We reject this option
-              if localLineEditing mode is on (always on in xboard)
-               and we are talking to port 23, which might be a real
-              telnet server that will try to keep WILL ECHO on permanently.
-             */
-           if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
-               static int remoteEchoOption = FALSE; /* telnet ECHO option */
-               unsigned char option;
-               oldi = i;
-               switch ((unsigned char) buf[++i]) {
-                 case TN_WILL:
-                   if (appData.debugMode)
-                     fprintf(debugFP, "\n<WILL ");
-                   switch (option = (unsigned char) buf[++i]) {
-                     case TN_ECHO:
-                       if (appData.debugMode)
-                         fprintf(debugFP, "ECHO ");
-                       /* Reply only if this is a change, according
-                          to the protocol rules. */
-                       if (remoteEchoOption) break;
-                       if (appData.localLineEditing &&
-                           atoi(appData.icsPort) == TN_PORT) {
-                           TelnetRequest(TN_DONT, TN_ECHO);
-                       } else {
-                           EchoOff();
-                           TelnetRequest(TN_DO, TN_ECHO);
-                           remoteEchoOption = TRUE;
-                       }
-                       break;
-                     default:
-                       if (appData.debugMode)
-                         fprintf(debugFP, "%d ", option);
-                       /* Whatever this is, we don't want it. */
-                       TelnetRequest(TN_DONT, option);
-                       break;
-                   }
-                   break;
-                 case TN_WONT:
-                   if (appData.debugMode)
-                     fprintf(debugFP, "\n<WONT ");
-                   switch (option = (unsigned char) buf[++i]) {
-                     case TN_ECHO:
-                       if (appData.debugMode)
-                         fprintf(debugFP, "ECHO ");
-                       /* Reply only if this is a change, according
-                          to the protocol rules. */
-                       if (!remoteEchoOption) break;
-                       EchoOn();
-                       TelnetRequest(TN_DONT, TN_ECHO);
-                       remoteEchoOption = FALSE;
-                       break;
-                     default:
-                       if (appData.debugMode)
-                         fprintf(debugFP, "%d ", (unsigned char) option);
-                       /* Whatever this is, it must already be turned
-                          off, because we never agree to turn on
-                          anything non-default, so according to the
-                          protocol rules, we don't reply. */
-                       break;
-                   }
-                   break;
-                 case TN_DO:
-                   if (appData.debugMode)
-                     fprintf(debugFP, "\n<DO ");
-                   switch (option = (unsigned char) buf[++i]) {
-                     default:
-                       /* Whatever this is, we refuse to do it. */
-                       if (appData.debugMode)
-                         fprintf(debugFP, "%d ", option);
-                       TelnetRequest(TN_WONT, option);
-                       break;
-                   }
-                   break;
-                 case TN_DONT:
-                   if (appData.debugMode)
-                     fprintf(debugFP, "\n<DONT ");
-                   switch (option = (unsigned char) buf[++i]) {
-                     default:
-                       if (appData.debugMode)
-                         fprintf(debugFP, "%d ", option);
-                       /* Whatever this is, we are already not doing
-                          it, because we never agree to do anything
-                          non-default, so according to the protocol
-                          rules, we don't reply. */
-                       break;
-                   }
-                   break;
-                 case TN_IAC:
-                   if (appData.debugMode)
-                     fprintf(debugFP, "\n<IAC ");
-                   /* Doubled IAC; pass it through */
-                   i--;
-                   break;
-                 default:
-                   if (appData.debugMode)
-                     fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
-                   /* Drop all other telnet commands on the floor */
-                   break;
-               }
-               if (oldi > next_out)
-                 SendToPlayer(&buf[next_out], oldi - next_out);
-               if (++i > next_out)
-                 next_out = i;
-               continue;
-           }
-
-           /* OK, this at least will *usually* work */
-           if (!loggedOn && looking_at(buf, &i, "ics%")) {
-               loggedOn = TRUE;
-           }
-
-           if (loggedOn && !intfSet) {
-               if (ics_type == ICS_ICC) {
-                 sprintf(str,
-                         "/set-quietly interface %s\n/set-quietly style 12\n",
-                         programVersion);
-
-               } else if (ics_type == ICS_CHESSNET) {
-                 sprintf(str, "/style 12\n");
-               } else {
-                 strcpy(str, "alias $ @\n$set interface ");
-                 strcat(str, programVersion);
-                 strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
-#ifdef WIN32
-                 strcat(str, "$iset nohighlight 1\n");
-#endif
-                 strcat(str, "$iset lock 1\n$style 12\n");
-               }
-               SendToICS(str);
-               intfSet = TRUE;
-           }
-
-           if (started == STARTED_COMMENT) {
-               /* Accumulate characters in comment */
-               parse[parse_pos++] = buf[i];
-               if (buf[i] == '\n') {
-                   parse[parse_pos] = NULLCHAR;
-                   AppendComment(forwardMostMove, StripHighlight(parse));
-                   started = STARTED_NONE;
-               } else {
-                   /* Don't match patterns against characters in chatter */
-                   i++;
-                   continue;
-               }
-           }
-           if (started == STARTED_CHATTER) {
-               if (buf[i] != '\n') {
-                   /* Don't match patterns against characters in chatter */
-                   i++;
-                   continue;
-               }
-               started = STARTED_NONE;
-           }
-
-            /* Kludge to deal with rcmd protocol */
-           if (firstTime && looking_at(buf, &i, "\001*")) {
-               DisplayFatalError(&buf[1], 0, 1);
-               continue;
-           } else {
-               firstTime = FALSE;
-           }
-
-           if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
-               ics_type = ICS_ICC;
-               ics_prefix = "/";
-               if (appData.debugMode)
-                 fprintf(debugFP, "ics_type %d\n", ics_type);
-               continue;
-           }
-           if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
-               ics_type = ICS_FICS;
-               ics_prefix = "$";
-               if (appData.debugMode)
-                 fprintf(debugFP, "ics_type %d\n", ics_type);
-               continue;
-           }
-           if (!loggedOn && looking_at(buf, &i, "chess.net")) {
-               ics_type = ICS_CHESSNET;
-               ics_prefix = "/";
-               if (appData.debugMode)
-                 fprintf(debugFP, "ics_type %d\n", ics_type);
-               continue;
-           }
-
-           if (!loggedOn &&
-               (looking_at(buf, &i, "\"*\" is *a registered name") ||
-                looking_at(buf, &i, "Logging you in as \"*\"") ||
-                looking_at(buf, &i, "will be \"*\""))) {
-             strcpy(ics_handle, star_match[0]);
-             continue;
-           }
-
-           if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
-             char buf[MSG_SIZ];
-             sprintf(buf, "%s@%s", ics_handle, appData.icsHost);
-             DisplayIcsInteractionTitle(buf);
-             have_set_title = TRUE;
-           }
-
-           /* skip finger notes */
-           if (started == STARTED_NONE &&
-               ((buf[i] == ' ' && isdigit(buf[i+1])) ||
-                (buf[i] == '1' && buf[i+1] == '0')) &&
-               buf[i+2] == ':' && buf[i+3] == ' ') {
-             started = STARTED_CHATTER;
-             i += 3;
-             continue;
-           }
-
-           /* skip formula vars */
-           if (started == STARTED_NONE &&
-               buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
-             started = STARTED_CHATTER;
-             i += 3;
-             continue;
-           }
-
-           oldi = i;
-           if (appData.zippyTalk || appData.zippyPlay) {
-                /* Backup address for color zippy lines */
-                backup = i;
-#if ZIPPY
-       #ifdef WIN32
-               if (loggedOn == TRUE)
-                       if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||
-                       (appData.zippyPlay && ZippyMatch(buf, &backup)));
-               #else
-               if (ZippyControl(buf, &i) ||
-                   ZippyConverse(buf, &i) ||
-                   (appData.zippyPlay && ZippyMatch(buf, &i))) {
-                   loggedOn = TRUE;
-                   if (!appData.colorize) continue;
-               }
-       #endif
-#endif
-           }
-               if (/* Don't color "message" or "messages" output */
-                   (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
-                   looking_at(buf, &i, "*. * at *:*: ") ||
-                   looking_at(buf, &i, "--* (*:*): ") ||
-                   /* Regular tells and says */
-                   (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
-                   looking_at(buf, &i, "* (your partner) tells you: ") ||
-                   looking_at(buf, &i, "* says: ") ||
-                   /* Message notifications (same color as tells) */
-                   looking_at(buf, &i, "* has left a message ") ||
-                   looking_at(buf, &i, "* just sent you a message:\n") ||
-                   /* Whispers and kibitzes */
-                   (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
-                   looking_at(buf, &i, "* kibitzes: ") ||
-                   /* Channel tells */
-                   (tkind = 3, looking_at(buf, &i, "*(*: "))) {
-
-                 if (tkind == 1 && strchr(star_match[0], ':')) {
-                     /* Avoid "tells you:" spoofs in channels */
-                    tkind = 3;
-                 }
-                 if (star_match[0][0] == NULLCHAR ||
-                     strchr(star_match[0], ' ') ||
-                     (tkind == 3 && strchr(star_match[1], ' '))) {
-                   /* Reject bogus matches */
-                   i = oldi;
-                 } else {
-                   if (appData.colorize) {
-                     if (oldi > next_out) {
-                       SendToPlayer(&buf[next_out], oldi - next_out);
-                       next_out = oldi;
-                     }
-                     switch (tkind) {
-                     case 1:
-                       Colorize(ColorTell, FALSE);
-                       curColor = ColorTell;
-                       break;
-                     case 2:
-                       Colorize(ColorKibitz, FALSE);
-                       curColor = ColorKibitz;
-                       break;
-                     case 3:
-                       p = strrchr(star_match[1], '(');
-                       if (p == NULL) {
-                         p = star_match[1];
-                       } else {
-                         p++;
-                       }
-                       if (atoi(p) == 1) {
-                         Colorize(ColorChannel1, FALSE);
-                         curColor = ColorChannel1;
-                       } else {
-                         Colorize(ColorChannel, FALSE);
-                         curColor = ColorChannel;
-                       }
-                       break;
-                     case 5:
-                       curColor = ColorNormal;
-                       break;
-                     }
-                   }
-                   if (started == STARTED_NONE && appData.autoComment &&
-                       (gameMode == IcsObserving ||
-                        gameMode == IcsPlayingWhite ||
-                        gameMode == IcsPlayingBlack)) {
-                     parse_pos = i - oldi;
-                     memcpy(parse, &buf[oldi], parse_pos);
-                     parse[parse_pos] = NULLCHAR;
-                     started = STARTED_COMMENT;
-                     savingComment = TRUE;
-                   } else {
-                     started = STARTED_CHATTER;
-                     savingComment = FALSE;
-                   }
-                   loggedOn = TRUE;
-                   continue;
-                 }
-               }
-
-               if (looking_at(buf, &i, "* s-shouts: ") ||
-                   looking_at(buf, &i, "* c-shouts: ")) {
-                   if (appData.colorize) {
-                       if (oldi > next_out) {
-                           SendToPlayer(&buf[next_out], oldi - next_out);
-                           next_out = oldi;
-                       }
-                       Colorize(ColorSShout, FALSE);
-                       curColor = ColorSShout;
-                   }
-                   loggedOn = TRUE;
-                   started = STARTED_CHATTER;
-                   continue;
-               }
-
-               if (looking_at(buf, &i, "--->")) {
-                   loggedOn = TRUE;
-                   continue;
-               }
-
-               if (looking_at(buf, &i, "* shouts: ") ||
-                   looking_at(buf, &i, "--> ")) {
-                   if (appData.colorize) {
-                       if (oldi > next_out) {
-                           SendToPlayer(&buf[next_out], oldi - next_out);
-                           next_out = oldi;
-                       }
-                       Colorize(ColorShout, FALSE);
-                       curColor = ColorShout;
-                   }
-                   loggedOn = TRUE;
-                   started = STARTED_CHATTER;
-                   continue;
-               }
-
-               if (looking_at( buf, &i, "Challenge:")) {
-                   if (appData.colorize) {
-                       if (oldi > next_out) {
-                           SendToPlayer(&buf[next_out], oldi - next_out);
-                           next_out = oldi;
-                       }
-                       Colorize(ColorChallenge, FALSE);
-                       curColor = ColorChallenge;
-                   }
-                   loggedOn = TRUE;
-                   continue;
-               }
-
-               if (looking_at(buf, &i, "* offers you") ||
-                   looking_at(buf, &i, "* offers to be") ||
-                   looking_at(buf, &i, "* would like to") ||
-                   looking_at(buf, &i, "* requests to") ||
-                   looking_at(buf, &i, "Your opponent offers") ||
-                   looking_at(buf, &i, "Your opponent requests")) {
-
-                   if (appData.colorize) {
-                       if (oldi > next_out) {
-                           SendToPlayer(&buf[next_out], oldi - next_out);
-                           next_out = oldi;
-                       }
-                       Colorize(ColorRequest, FALSE);
-                       curColor = ColorRequest;
-                   }
-                   continue;
-               }
-
-               if (looking_at(buf, &i, "* (*) seeking")) {
-                   if (appData.colorize) {
-                       if (oldi > next_out) {
-                           SendToPlayer(&buf[next_out], oldi - next_out);
-                           next_out = oldi;
-                       }
-                       Colorize(ColorSeek, FALSE);
-                       curColor = ColorSeek;
-                   }
-                   continue;
-           }
-
-           if (looking_at(buf, &i, "\\   ")) {
-               if (prevColor != ColorNormal) {
-                   if (oldi > next_out) {
-                       SendToPlayer(&buf[next_out], oldi - next_out);
-                       next_out = oldi;
-                   }
-                   Colorize(prevColor, TRUE);
-                   curColor = prevColor;
-               }
-               if (savingComment) {
-                   parse_pos = i - oldi;
-                   memcpy(parse, &buf[oldi], parse_pos);
-                   parse[parse_pos] = NULLCHAR;
-                   started = STARTED_COMMENT;
-               } else {
-                   started = STARTED_CHATTER;
-               }
-               continue;
-           }
-
-           if (looking_at(buf, &i, "Black Strength :") ||
-               looking_at(buf, &i, "<<< style 10 board >>>") ||
-               looking_at(buf, &i, "<10>") ||
-               looking_at(buf, &i, "#@#")) {
-               /* Wrong board style */
-               loggedOn = TRUE;
-               SendToICS(ics_prefix);
-               SendToICS("set style 12\n");
-               SendToICS(ics_prefix);
-               SendToICS("refresh\n");
-               continue;
-           }
-
-           if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
-               ICSInitScript();
-               have_sent_ICS_logon = 1;
-               continue;
-           }
-
-           if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
-               (looking_at(buf, &i, "\n<12> ") ||
-                looking_at(buf, &i, "<12> "))) {
-               loggedOn = TRUE;
-               if (oldi > next_out) {
-                   SendToPlayer(&buf[next_out], oldi - next_out);
-               }
-               next_out = i;
-               started = STARTED_BOARD;
-               parse_pos = 0;
-               continue;
-           }
-
-           if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
-               looking_at(buf, &i, "<b1> ")) {
-               if (oldi > next_out) {
-                   SendToPlayer(&buf[next_out], oldi - next_out);
-               }
-               next_out = i;
-               started = STARTED_HOLDINGS;
-               parse_pos = 0;
-               continue;
-           }
-
-           if (looking_at(buf, &i, "* *vs. * *--- *")) {
-               loggedOn = TRUE;
-               /* Header for a move list -- first line */
-
-               switch (ics_getting_history) {
-                 case H_FALSE:
-                   switch (gameMode) {
-                     case IcsIdle:
-                     case BeginningOfGame:
-                       /* User typed "moves" or "oldmoves" while we
-                          were idle.  Pretend we asked for these
-                          moves and soak them up so user can step
-                          through them and/or save them.
-                          */
-                       Reset(FALSE, TRUE);
-                       gameMode = IcsObserving;
-                       ModeHighlight();
-                       ics_gamenum = -1;
-                       ics_getting_history = H_GOT_UNREQ_HEADER;
-                       break;
-                     case EditGame: /*?*/
-                     case EditPosition: /*?*/
-                       /* Should above feature work in these modes too? */
-                       /* For now it doesn't */
-                       ics_getting_history = H_GOT_UNWANTED_HEADER;
-                       break;
-                     default:
-                       ics_getting_history = H_GOT_UNWANTED_HEADER;
-                       break;
-                   }
-                   break;
-                 case H_REQUESTED:
-                   /* Is this the right one? */
-                   if (gameInfo.white && gameInfo.black &&
-                       strcmp(gameInfo.white, star_match[0]) == 0 &&
-                       strcmp(gameInfo.black, star_match[2]) == 0) {
-                       /* All is well */
-                       ics_getting_history = H_GOT_REQ_HEADER;
-                   }
-                   break;
-                 case H_GOT_REQ_HEADER:
-                 case H_GOT_UNREQ_HEADER:
-                 case H_GOT_UNWANTED_HEADER:
-                 case H_GETTING_MOVES:
-                   /* Should not happen */
-                   DisplayError(_("Error gathering move list: two headers"), 0);
-                   ics_getting_history = H_FALSE;
-                   break;
-               }
-
-               /* Save player ratings into gameInfo if needed */
-               if ((ics_getting_history == H_GOT_REQ_HEADER ||
-                    ics_getting_history == H_GOT_UNREQ_HEADER) &&
-                   (gameInfo.whiteRating == -1 ||
-                    gameInfo.blackRating == -1)) {
-
-                   gameInfo.whiteRating = string_to_rating(star_match[1]);
-                   gameInfo.blackRating = string_to_rating(star_match[3]);
-                   if (appData.debugMode)
-                     fprintf(debugFP, _("Ratings from header: W %d, B %d\n"),
-                             gameInfo.whiteRating, gameInfo.blackRating);
-               }
-               continue;
-           }
-
-           if (looking_at(buf, &i,
-             "* * match, initial time: * minute*, increment: * second")) {
-               /* Header for a move list -- second line */
-               /* Initial board will follow if this is a wild game */
-
-               if (gameInfo.event != NULL) free(gameInfo.event);
-               sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
-               gameInfo.event = StrSave(str);
-               gameInfo.variant = StringToVariant(gameInfo.event);
-               continue;
-           }
-
-           if (looking_at(buf, &i, "Move  ")) {
-               /* Beginning of a move list */
-               switch (ics_getting_history) {
-                 case H_FALSE:
-                   /* Normally should not happen */
-                   /* Maybe user hit reset while we were parsing */
-                   break;
-                 case H_REQUESTED:
-                   /* Happens if we are ignoring a move list that is not
-                    * the one we just requested.  Common if the user
-                    * tries to observe two games without turning off
-                    * getMoveList */
-                   break;
-                 case H_GETTING_MOVES:
-                   /* Should not happen */
-                   DisplayError(_("Error gathering move list: nested"), 0);
-                   ics_getting_history = H_FALSE;
-                   break;
-                 case H_GOT_REQ_HEADER:
-                   ics_getting_history = H_GETTING_MOVES;
-                   started = STARTED_MOVES;
-                   parse_pos = 0;
-                   if (oldi > next_out) {
-                       SendToPlayer(&buf[next_out], oldi - next_out);
-                   }
-                   break;
-                 case H_GOT_UNREQ_HEADER:
-                   ics_getting_history = H_GETTING_MOVES;
-                   started = STARTED_MOVES_NOHIDE;
-                   parse_pos = 0;
-                   break;
-                 case H_GOT_UNWANTED_HEADER:
-                   ics_getting_history = H_FALSE;
-                   break;
-               }
-               continue;
-           }
-
-           if (looking_at(buf, &i, "% ") ||
-               ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
-                && looking_at(buf, &i, "}*"))) {
-               savingComment = FALSE;
-               switch (started) {
-                 case STARTED_MOVES:
-                 case STARTED_MOVES_NOHIDE:
-                   memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
-                   parse[parse_pos + i - oldi] = NULLCHAR;
-                   ParseGameHistory(parse);
-#if ZIPPY
-                   if (appData.zippyPlay && first.initDone) {
-                       FeedMovesToProgram(&first, forwardMostMove);
-                       if (gameMode == IcsPlayingWhite) {
-                           if (WhiteOnMove(forwardMostMove)) {
-                               if (first.sendTime) {
-                                 if (first.useColors) {
-                                   SendToProgram("black\n", &first);
-                                 }
-                                 SendTimeRemaining(&first, TRUE);
-                               }
-                               if (first.useColors) {
-                                 SendToProgram("white\ngo\n", &first);
-                               } else {
-                                 SendToProgram("go\n", &first);
-                               }
-                               first.maybeThinking = TRUE;
-                           } else {
-                               if (first.usePlayother) {
-                                 if (first.sendTime) {
-                                   SendTimeRemaining(&first, TRUE);
-                                 }
-                                 SendToProgram("playother\n", &first);
-                                 firstMove = FALSE;
-                               } else {
-                                 firstMove = TRUE;
-                               }
-                           }
-                       } else if (gameMode == IcsPlayingBlack) {
-                           if (!WhiteOnMove(forwardMostMove)) {
-                               if (first.sendTime) {
-                                 if (first.useColors) {
-                                   SendToProgram("white\n", &first);
-                                 }
-                                 SendTimeRemaining(&first, FALSE);
-                               }
-                               if (first.useColors) {
-                                 SendToProgram("black\ngo\n", &first);
-                               } else {
-                                 SendToProgram("go\n", &first);
-                               }
-                               first.maybeThinking = TRUE;
-                           } else {
-                               if (first.usePlayother) {
-                                 if (first.sendTime) {
-                                   SendTimeRemaining(&first, FALSE);
-                                 }
-                                 SendToProgram("playother\n", &first);
-                                 firstMove = FALSE;
-                               } else {
-                                 firstMove = TRUE;
-                               }
-                           }
-                       }
-                   }
-#endif
-                   if (gameMode == IcsObserving && ics_gamenum == -1) {
-                       /* Moves came from oldmoves or moves command
-                          while we weren't doing anything else.
-                          */
-                       currentMove = forwardMostMove;
-                       ClearHighlights();/*!!could figure this out*/
-                       flipView = appData.flipView;
-                       DrawPosition(FALSE, boards[currentMove]);
-                       DisplayBothClocks();
-                       sprintf(str, "%s vs. %s",
-                               gameInfo.white, gameInfo.black);
-                       DisplayTitle(str);
-                       gameMode = IcsIdle;
-                   } else {
-                       /* Moves were history of an active game */
-                       if (gameInfo.resultDetails != NULL) {
-                           free(gameInfo.resultDetails);
-                           gameInfo.resultDetails = NULL;
-                       }
-                   }
-                   HistorySet(parseList, backwardMostMove,
-                              forwardMostMove, currentMove-1);
-                   DisplayMove(currentMove - 1);
-                   if (started == STARTED_MOVES) next_out = i;
-                   started = STARTED_NONE;
-                   ics_getting_history = H_FALSE;
-                   break;
-
-                 case STARTED_OBSERVE:
-                   started = STARTED_NONE;
-                   SendToICS(ics_prefix);
-                   SendToICS("refresh\n");
-                   break;
-
-                 default:
-                   break;
-               }
-               continue;
-           }
-
-           if ((started == STARTED_MOVES || started == STARTED_BOARD ||
-                started == STARTED_HOLDINGS ||
-                started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
-               /* Accumulate characters in move list or board */
-               parse[parse_pos++] = buf[i];
-           }
-
-           /* Start of game messages.  Mostly we detect start of game
-              when the first board image arrives.  On some versions
-              of the ICS, though, we need to do a "refresh" after starting
-              to observe in order to get the current board right away. */
-           if (looking_at(buf, &i, "Adding game * to observation list")) {
-               started = STARTED_OBSERVE;
-               continue;
-           }
-
-           /* Handle auto-observe */
-           if (appData.autoObserve &&
-               (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
-               looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
-               char *player;
-               /* Choose the player that was highlighted, if any. */
-               if (star_match[0][0] == '\033' ||
-                   star_match[1][0] != '\033') {
-                   player = star_match[0];
-               } else {
-                   player = star_match[2];
-               }
-               sprintf(str, "%sobserve %s\n",
-                       ics_prefix, StripHighlightAndTitle(player));
-               SendToICS(str);
-
-               /* Save ratings from notify string */
-               strcpy(player1Name, star_match[0]);
-               player1Rating = string_to_rating(star_match[1]);
-               strcpy(player2Name, star_match[2]);
-               player2Rating = string_to_rating(star_match[3]);
-
-               if (appData.debugMode)
-                 fprintf(debugFP,
-                         "Ratings from 'Game notification:' %s %d, %s %d\n",
-                         player1Name, player1Rating,
-                         player2Name, player2Rating);
-
-               continue;
-           }
-
-           /* Deal with automatic examine mode after a game,
-              and with IcsObserving -> IcsExamining transition */
-           if (looking_at(buf, &i, "Entering examine mode for game *") ||
-               looking_at(buf, &i, "has made you an examiner of game *")) {
-
-               int gamenum = atoi(star_match[0]);
-               if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
-                   gamenum == ics_gamenum) {
-                   /* We were already playing or observing this game;
-                      no need to refetch history */
-                   gameMode = IcsExamining;
-                   if (pausing) {
-                       pauseExamForwardMostMove = forwardMostMove;
-                   } else if (currentMove < forwardMostMove) {
-                       ForwardInner(forwardMostMove);
-                   }
-               } else {
-                   /* I don't think this case really can happen */
-                   SendToICS(ics_prefix);
-                   SendToICS("refresh\n");
-               }
-               continue;
-           }
-
-           /* Error messages */
-           if (ics_user_moved) {
-               if (looking_at(buf, &i, "Illegal move") ||
-                   looking_at(buf, &i, "Not a legal move") ||
-                   looking_at(buf, &i, "Your king is in check") ||
-                   looking_at(buf, &i, "It isn't your turn") ||
-                   looking_at(buf, &i, "It is not your move")) {
-                   /* Illegal move */
-                   ics_user_moved = 0;
-                   if (forwardMostMove > backwardMostMove) {
-                       currentMove = --forwardMostMove;
-                       DisplayMove(currentMove - 1); /* before DMError */
-                       DisplayMoveError("Illegal move (rejected by ICS)");
-                       DrawPosition(FALSE, boards[currentMove]);
-                       SwitchClocks();
-                       DisplayBothClocks();
-                   }
-                   continue;
-               }
-           }
-
-           if (looking_at(buf, &i, "still have time") ||
-               looking_at(buf, &i, "not out of time") ||
-               looking_at(buf, &i, "either player is out of time") ||
-               looking_at(buf, &i, "has timeseal; checking")) {
-               /* We must have called his flag a little too soon */
-               whiteFlag = blackFlag = FALSE;
-               continue;
-           }
-
-           if (looking_at(buf, &i, "added * seconds to") ||
-               looking_at(buf, &i, "seconds were added to")) {
-               /* Update the clocks */
-               SendToICS(ics_prefix);
-               SendToICS("refresh\n");
-               continue;
-           }
-
-           if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
-               ics_clock_paused = TRUE;
-               StopClocks();
-               continue;
-           }
-
-           if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
-               ics_clock_paused = FALSE;
-               StartClocks();
-               continue;
-           }
-
-           /* Grab player ratings from the Creating: message.
-              Note we have to check for the special case when
-              the ICS inserts things like [white] or [black]. */
-           if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
-               looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
-               /* star_matches:
-                  0    player 1 name (not necessarily white)
-                  1    player 1 rating
-                  2    empty, white, or black (IGNORED)
-                  3    player 2 name (not necessarily black)
-                  4    player 2 rating
-
-                  The names/ratings are sorted out when the game
-                  actually starts (below).
-               */
-               strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
-               player1Rating = string_to_rating(star_match[1]);
-               strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
-               player2Rating = string_to_rating(star_match[4]);
-
-               if (appData.debugMode)
-                 fprintf(debugFP,
-                         "Ratings from 'Creating:' %s %d, %s %d\n",
-                         player1Name, player1Rating,
-                         player2Name, player2Rating);
-
-               continue;
-           }
-
-           /* Improved generic start/end-of-game messages */
-           if ((tkind=0, looking_at(buf, &i, "{Game * (* vs. *) *}*")) ||
-               (tkind=1, looking_at(buf, &i, "{Game * (*(*) vs. *(*)) *}*"))){
-               /* If tkind == 0: */
-               /* star_match[0] is the game number */
-               /*           [1] is the white player's name */
-               /*           [2] is the black player's name */
-               /* For end-of-game: */
-               /*           [3] is the reason for the game end */
-               /*           [4] is a PGN end game-token, preceded by " " */
-               /* For start-of-game: */
-               /*           [3] begins with "Creating" or "Continuing" */
-               /*           [4] is " *" or empty (don't care). */
-               int gamenum = atoi(star_match[0]);
-               char *whitename, *blackname, *why, *endtoken;
-               ChessMove endtype = (ChessMove) 0;
-
-               if (tkind == 0) {
-                 whitename = star_match[1];
-                 blackname = star_match[2];
-                 why = star_match[3];
-                 endtoken = star_match[4];
-               } else {
-                 whitename = star_match[1];
-                 blackname = star_match[3];
-                 why = star_match[5];
-                 endtoken = star_match[6];
-               }
-
-                /* Game start messages */
-               if (strncmp(why, "Creating ", 9) == 0 ||
-                   strncmp(why, "Continuing ", 11) == 0) {
-                   gs_gamenum = gamenum;
-                   strcpy(gs_kind, strchr(why, ' ') + 1);
-#if ZIPPY
-                   if (appData.zippyPlay) {
-                       ZippyGameStart(whitename, blackname);
-                   }
-#endif /*ZIPPY*/
-                   continue;
-               }
-
-               /* Game end messages */
-               if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
-                   ics_gamenum != gamenum) {
-                   continue;
-               }
-               while (endtoken[0] == ' ') endtoken++;
-               switch (endtoken[0]) {
-                 case '*':
-                 default:
-                   endtype = GameUnfinished;
-                   break;
-                 case '0':
-                   endtype = BlackWins;
-                   break;
-                 case '1':
-                   if (endtoken[1] == '/')
-                     endtype = GameIsDrawn;
-                   else
-                     endtype = WhiteWins;
-                   break;
-               }
-               GameEnds(endtype, why, GE_ICS);
-#if ZIPPY
-               if (appData.zippyPlay && first.initDone) {
-                   ZippyGameEnd(endtype, why);
-                   if (first.pr == NULL) {
-                     /* Start the next process early so that we'll
-                        be ready for the next challenge */
-                     StartChessProgram(&first);
-                   }
-                   /* Send "new" early, in case this command takes
-                      a long time to finish, so that we'll be ready
-                      for the next challenge. */
-                   Reset(TRUE, TRUE);
-               }
-#endif /*ZIPPY*/
-               continue;
-           }
-
-           if (looking_at(buf, &i, "Removing game * from observation") ||
-               looking_at(buf, &i, "no longer observing game *") ||
-               looking_at(buf, &i, "Game * (*) has no examiners")) {
-               if (gameMode == IcsObserving &&
-                   atoi(star_match[0]) == ics_gamenum)
-                 {
-                         /* icsEngineAnalyze */
-                         if (appData.icsEngineAnalyze) {
-                            ExitAnalyzeMode();
-                                ModeHighlight();
-                         }
-                     StopClocks();
-                     gameMode = IcsIdle;
-                     ics_gamenum = -1;
-                     ics_user_moved = FALSE;
-                 }
-               continue;
-           }
-
-           if (looking_at(buf, &i, "no longer examining game *")) {
-               if (gameMode == IcsExamining &&
-                   atoi(star_match[0]) == ics_gamenum)
-                 {
-                     gameMode = IcsIdle;
-                     ics_gamenum = -1;
-                     ics_user_moved = FALSE;
-                 }
-               continue;
-           }
-
-           /* Advance leftover_start past any newlines we find,
-              so only partial lines can get reparsed */
-           if (looking_at(buf, &i, "\n")) {
-               prevColor = curColor;
-               if (curColor != ColorNormal) {
-                   if (oldi > next_out) {
-                       SendToPlayer(&buf[next_out], oldi - next_out);
-                       next_out = oldi;
-                   }
-                   Colorize(ColorNormal, FALSE);
-                   curColor = ColorNormal;
-               }
-               if (started == STARTED_BOARD) {
-                   started = STARTED_NONE;
-                   parse[parse_pos] = NULLCHAR;
-                   ParseBoard12(parse);
-                   ics_user_moved = 0;
-
-                   /* Send premove here */
-                   if (appData.premove) {
-                     char str[MSG_SIZ];
-                     if (currentMove == 0 &&
-                         gameMode == IcsPlayingWhite &&
-                         appData.premoveWhite) {
-                       sprintf(str, "%s%s\n", ics_prefix,
-                               appData.premoveWhiteText);
-                       if (appData.debugMode)
-                         fprintf(debugFP, "Sending premove:\n");
-                       SendToICS(str);
-                     } else if (currentMove == 1 &&
-                                gameMode == IcsPlayingBlack &&
-                                appData.premoveBlack) {
-                       sprintf(str, "%s%s\n", ics_prefix,
-                               appData.premoveBlackText);
-                       if (appData.debugMode)
-                         fprintf(debugFP, "Sending premove:\n");
-                       SendToICS(str);
-                     } else if (gotPremove) {
-                       gotPremove = 0;
-                       ClearPremoveHighlights();
-                       if (appData.debugMode)
-                         fprintf(debugFP, "Sending premove:\n");
-                         UserMoveEvent(premoveFromX, premoveFromY,
-                                       premoveToX, premoveToY,
-                                       premovePromoChar);
-                     }
-                   }
-
-                   /* Usually suppress following prompt */
-                   if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
-                       if (looking_at(buf, &i, "*% ")) {
-                           savingComment = FALSE;
-                       }
-                   }
-                   next_out = i;
-               } else if (started == STARTED_HOLDINGS) {
-                   int gamenum;
-                   char new_piece[MSG_SIZ];
-                   started = STARTED_NONE;
-                   parse[parse_pos] = NULLCHAR;
-                   if (appData.debugMode)
-                     fprintf(debugFP, "Parsing holdings: %s\n", parse);
-                   if (sscanf(parse, " game %d", &gamenum) == 1 &&
-                       gamenum == ics_gamenum) {
-                       if (gameInfo.variant == VariantNormal) {
-                         gameInfo.variant = VariantCrazyhouse; /*temp guess*/
-                         /* Get a move list just to see the header, which
-                            will tell us whether this is really bug or zh */
-                         if (ics_getting_history == H_FALSE) {
-                           ics_getting_history = H_REQUESTED;
-                           sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
-                           SendToICS(str);
-                         }
-                       }
-                       new_piece[0] = NULLCHAR;
-                       sscanf(parse, "game %d white [%s black [%s <- %s",
-                              &gamenum, white_holding, black_holding,
-                              new_piece);
-                        white_holding[strlen(white_holding)-1] = NULLCHAR;
-                        black_holding[strlen(black_holding)-1] = NULLCHAR;
-#if ZIPPY
-                       if (appData.zippyPlay && first.initDone) {
-                           ZippyHoldings(white_holding, black_holding,
-                                         new_piece);
-                       }
-#endif /*ZIPPY*/
-                       if (tinyLayout || smallLayout) {
-                           char wh[16], bh[16];
-                           PackHolding(wh, white_holding);
-                           PackHolding(bh, black_holding);
-                           sprintf(str, "[%s-%s] %s-%s", wh, bh,
-                                   gameInfo.white, gameInfo.black);
-                       } else {
-                           sprintf(str, "%s [%s] vs. %s [%s]",
-                                   gameInfo.white, white_holding,
-                                   gameInfo.black, black_holding);
-                       }
-                       DrawPosition(FALSE, NULL);
-                       DisplayTitle(str);
-                   }
-                   /* Suppress following prompt */
-                   if (looking_at(buf, &i, "*% ")) {
-                       savingComment = FALSE;
-                   }
-                   next_out = i;
-               }
-               continue;
-           }
-
-           i++;                /* skip unparsed character and loop back */
-       }
-
-       if (started != STARTED_MOVES && started != STARTED_BOARD &&
-           started != STARTED_HOLDINGS && i > next_out) {
-           SendToPlayer(&buf[next_out], i - next_out);
-           next_out = i;
-       }
-
-       leftover_len = buf_len - leftover_start;
-       /* if buffer ends with something we couldn't parse,
-          reparse it after appending the next read */
-
-    } else if (count == 0) {
-       RemoveInputSource(isr);
-        DisplayFatalError(_("Connection closed by ICS"), 0, 0);
-    } else {
-       DisplayFatalError(_("Error reading from ICS"), error, 1);
-    }
-}
-
-
-/* Board style 12 looks like this:
-
-   <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
-
- * The "<12> " is stripped before it gets to this routine.  The two
- * trailing 0's (flip state and clock ticking) are later addition, and
- * some chess servers may not have them, or may have only the first.
- * Additional trailing fields may be added in the future.
- */
-
-#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"
-
-#define RELATION_OBSERVING_PLAYED    0
-#define RELATION_OBSERVING_STATIC   -2   /* examined, oldmoves, or smoves */
-#define RELATION_PLAYING_MYMOVE      1
-#define RELATION_PLAYING_NOTMYMOVE  -1
-#define RELATION_EXAMINING           2
-#define RELATION_ISOLATED_BOARD     -3
-#define RELATION_STARTING_POSITION  -4   /* FICS only */
-
-void
-ParseBoard12(string)
-     char *string;
-{
-    GameMode newGameMode;
-    int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;
-    int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;
-    int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
-    char to_play, board_chars[72];
-    char move_str[500], str[500], elapsed_time[500];
-    char black[32], white[32];
-    Board board;
-    int prevMove = currentMove;
-    int ticking = 2;
-    ChessMove moveType;
-    int fromX, fromY, toX, toY;
-    char promoChar;
-
-    fromX = fromY = toX = toY = -1;
-
-    newGame = FALSE;
-
-    if (appData.debugMode)
-      fprintf(debugFP, _("Parsing board: %s\n"), string);
-
-    move_str[0] = NULLCHAR;
-    elapsed_time[0] = NULLCHAR;
-    n = sscanf(string, PATTERN, board_chars, &to_play, &double_push,
-              &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,
-              &gamenum, white, black, &relation, &basetime, &increment,
-              &white_stren, &black_stren, &white_time, &black_time,
-              &moveNum, str, elapsed_time, move_str, &ics_flip,
-              &ticking);
-
-    if (n < 22) {
-       sprintf(str, _("Failed to parse board string:\n\"%s\""), string);
-       DisplayError(str, 0);
-       return;
-    }
-
-    /* Convert the move number to internal form */
-    moveNum = (moveNum - 1) * 2;
-    if (to_play == 'B') moveNum++;
-    if (moveNum >= MAX_MOVES) {
-      DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),
-                       0, 1);
-      return;
-    }
-
-    switch (relation) {
-      case RELATION_OBSERVING_PLAYED:
-      case RELATION_OBSERVING_STATIC:
-       if (gamenum == -1) {
-           /* Old ICC buglet */
-           relation = RELATION_OBSERVING_STATIC;
-       }
-       newGameMode = IcsObserving;
-       break;
-      case RELATION_PLAYING_MYMOVE:
-      case RELATION_PLAYING_NOTMYMOVE:
-       newGameMode =
-         ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
-           IcsPlayingWhite : IcsPlayingBlack;
-       break;
-      case RELATION_EXAMINING:
-       newGameMode = IcsExamining;
-       break;
-      case RELATION_ISOLATED_BOARD:
-      default:
-       /* Just display this board.  If user was doing something else,
-          we will forget about it until the next board comes. */
-       newGameMode = IcsIdle;
-       break;
-      case RELATION_STARTING_POSITION:
-       newGameMode = gameMode;
-       break;
-    }
-
-    /* Modify behavior for initial board display on move listing
-       of wild games.
-       */
-    switch (ics_getting_history) {
-      case H_FALSE:
-      case H_REQUESTED:
-       break;
-      case H_GOT_REQ_HEADER:
-      case H_GOT_UNREQ_HEADER:
-       /* This is the initial position of the current game */
-       gamenum = ics_gamenum;
-       moveNum = 0;            /* old ICS bug workaround */
-       if (to_play == 'B') {
-         startedFromSetupPosition = TRUE;
-         blackPlaysFirst = TRUE;
-         moveNum = 1;
-         if (forwardMostMove == 0) forwardMostMove = 1;
-         if (backwardMostMove == 0) backwardMostMove = 1;
-         if (currentMove == 0) currentMove = 1;
-       }
-       newGameMode = gameMode;
-       relation = RELATION_STARTING_POSITION; /* ICC needs this */
-       break;
-      case H_GOT_UNWANTED_HEADER:
-       /* This is an initial board that we don't want */
-       return;
-      case H_GETTING_MOVES:
-       /* Should not happen */
-       DisplayError(_("Error gathering move list: extra board"), 0);
-       ics_getting_history = H_FALSE;
-       return;
-    }
-
-    /* Take action if this is the first board of a new game, or of a
-       different game than is currently being displayed.  */
-    if (gamenum != ics_gamenum || newGameMode != gameMode ||
-       relation == RELATION_ISOLATED_BOARD) {
-
-       /* Forget the old game and get the history (if any) of the new one */
-       if (gameMode != BeginningOfGame) {
-       Reset(FALSE, TRUE);
-       }
-       newGame = TRUE;
-       if (appData.autoRaiseBoard) BoardToTop();
-       prevMove = -3;
-       if (gamenum == -1) {
-           newGameMode = IcsIdle;
-       } else if (moveNum > 0 && newGameMode != IcsIdle &&
-                  appData.getMoveList) {
-           /* Need to get game history */
-           ics_getting_history = H_REQUESTED;
-           sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
-           SendToICS(str);
-       }
-
-       /* Initially flip the board to have black on the bottom if playing
-          black or if the ICS flip flag is set, but let the user change
-          it with the Flip View button. */
-       flipView = appData.autoFlipView ?
-         (newGameMode == IcsPlayingBlack) || ics_flip :
-         appData.flipView;
-
-       /* Done with values from previous mode; copy in new ones */
-       gameMode = newGameMode;
-       ModeHighlight();
-       ics_gamenum = gamenum;
-       if (gamenum == gs_gamenum) {
-           int klen = strlen(gs_kind);
-           if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
-           sprintf(str, "ICS %s", gs_kind);
-           gameInfo.event = StrSave(str);
-       } else {
-           gameInfo.event = StrSave("ICS game");
-       }
-       gameInfo.site = StrSave(appData.icsHost);
-       gameInfo.date = PGNDate();
-       gameInfo.round = StrSave("-");
-       gameInfo.white = StrSave(white);
-       gameInfo.black = StrSave(black);
-       timeControl = basetime * 60 * 1000;
-       timeIncrement = increment * 1000;
-       movesPerSession = 0;
-       gameInfo.timeControl = TimeControlTagValue();
-       gameInfo.variant = StringToVariant(gameInfo.event);
-
-       /* Do we have the ratings? */
-       if (strcmp(player1Name, white) == 0 &&
-           strcmp(player2Name, black) == 0) {
-           if (appData.debugMode)
-             fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
-                     player1Rating, player2Rating);
-           gameInfo.whiteRating = player1Rating;
-           gameInfo.blackRating = player2Rating;
-       } else if (strcmp(player2Name, white) == 0 &&
-                  strcmp(player1Name, black) == 0) {
-           if (appData.debugMode)
-             fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
-                     player2Rating, player1Rating);
-           gameInfo.whiteRating = player2Rating;
-           gameInfo.blackRating = player1Rating;
-       }
-       player1Name[0] = player2Name[0] = NULLCHAR;
-
-       /* Silence shouts if requested */
-       if (appData.quietPlay &&
-           (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)) {
-           SendToICS(ics_prefix);
-           SendToICS("set shout 0\n");
-       }
-    }
-
-    /* Deal with midgame name changes */
-    if (!newGame) {
-       if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {
-           if (gameInfo.white) free(gameInfo.white);
-           gameInfo.white = StrSave(white);
-       }
-       if (!gameInfo.black || strcmp(gameInfo.black, black) != 0) {
-           if (gameInfo.black) free(gameInfo.black);
-           gameInfo.black = StrSave(black);
-       }
-    }
-
-    /* Throw away game result if anything actually changes in examine mode */
-    if (gameMode == IcsExamining && !newGame) {
-       gameInfo.result = GameUnfinished;
-       if (gameInfo.resultDetails != NULL) {
-           free(gameInfo.resultDetails);
-           gameInfo.resultDetails = NULL;
-       }
-    }
-
-    /* In pausing && IcsExamining mode, we ignore boards coming
-       in if they are in a different variation than we are. */
-    if (pauseExamInvalid) return;
-    if (pausing && gameMode == IcsExamining) {
-       if (moveNum <= pauseExamForwardMostMove) {
-           pauseExamInvalid = TRUE;
-           forwardMostMove = pauseExamForwardMostMove;
-           return;
-       }
-    }
-
-    /* Parse the board */
-    for (k = 0; k < 8; k++)
-      for (j = 0; j < 8; j++)
-       board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]);
-    CopyBoard(boards[moveNum], board);
-    if (moveNum == 0) {
-       startedFromSetupPosition =
-         !CompareBoards(board, initialPosition);
-    }
-
-    if (ics_getting_history == H_GOT_REQ_HEADER ||
-       ics_getting_history == H_GOT_UNREQ_HEADER) {
-       /* This was an initial position from a move list, not
-          the current position */
-       return;
-    }
-
-    /* Update currentMove and known move number limits */
-    newMove = newGame || moveNum > forwardMostMove;
-
-       /* If we found takebacks during icsEngineAnalyze
-          try send to engine */
-       if (!newGame && appData.icsEngineAnalyze && moveNum < forwardMostMove) {
-               takeback = forwardMostMove - moveNum;
-               for (i = 0; i < takeback; i++) {
-                   if (appData.debugMode) fprintf(debugFP, "take back move\n");
-                   SendToProgram("undo\n", &first);
-                }
-       }
-    if (newGame) {
-       forwardMostMove = backwardMostMove = currentMove = moveNum;
-       if (gameMode == IcsExamining && moveNum == 0) {
-         /* Workaround for ICS limitation: we are not told the wild
-            type when starting to examine a game.  But if we ask for
-            the move list, the move list header will tell us */
-           ics_getting_history = H_REQUESTED;
-           sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
-           SendToICS(str);
-       }
-    } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
-              || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {
-       forwardMostMove = moveNum;
-       if (!pausing || currentMove > forwardMostMove)
-         currentMove = forwardMostMove;
-    } else {
-       /* New part of history that is not contiguous with old part */
-       if (pausing && gameMode == IcsExamining) {
-           pauseExamInvalid = TRUE;
-           forwardMostMove = pauseExamForwardMostMove;
-           return;
-       }
-       forwardMostMove = backwardMostMove = currentMove = moveNum;
-       if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {
-           ics_getting_history = H_REQUESTED;
-           sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
-           SendToICS(str);
-       }
-    }
-
-    /* Update the clocks */
-    if (strchr(elapsed_time, '.')) {
-      /* Time is in ms */
-      timeRemaining[0][moveNum] = whiteTimeRemaining = white_time;
-      timeRemaining[1][moveNum] = blackTimeRemaining = black_time;
-    } else {
-      /* Time is in seconds */
-      timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;
-      timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;
-    }
-
-
-#if ZIPPY
-    if (appData.zippyPlay && newGame &&
-       gameMode != IcsObserving && gameMode != IcsIdle &&
-       gameMode != IcsExamining)
-      ZippyFirstBoard(moveNum, basetime, increment);
-#endif
-
-    /* Put the move on the move list, first converting
-       to canonical algebraic form. */
-    if (moveNum > 0) {
-       if (moveNum <= backwardMostMove) {
-           /* We don't know what the board looked like before
-              this move.  Punt. */
-           strcpy(parseList[moveNum - 1], move_str);
-           strcat(parseList[moveNum - 1], " ");
-           strcat(parseList[moveNum - 1], elapsed_time);
-           moveList[moveNum - 1][0] = NULLCHAR;
-       } else if (ParseOneMove(move_str, moveNum - 1, &moveType,
-                               &fromX, &fromY, &toX, &toY, &promoChar)) {
-           (void) CoordsToAlgebraic(boards[moveNum - 1],
-                                    PosFlags(moveNum - 1), EP_UNKNOWN,
-                                    fromY, fromX, toY, toX, promoChar,
-                                    parseList[moveNum-1]);
-           switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN)){
-             case MT_NONE:
-             case MT_STALEMATE:
-             default:
-               break;
-             case MT_CHECK:
-               strcat(parseList[moveNum - 1], "+");
-               break;
-             case MT_CHECKMATE:
-               strcat(parseList[moveNum - 1], "#");
-               break;
-           }
-           strcat(parseList[moveNum - 1], " ");
-           strcat(parseList[moveNum - 1], elapsed_time);
-           /* currentMoveString is set as a side-effect of ParseOneMove */
-           strcpy(moveList[moveNum - 1], currentMoveString);
-           strcat(moveList[moveNum - 1], "\n");
-       } else if (strcmp(move_str, "none") == 0) {
-           /* Again, we don't know what the board looked like;
-              this is really the start of the game. */
-           parseList[moveNum - 1][0] = NULLCHAR;
-           moveList[moveNum - 1][0] = NULLCHAR;
-           backwardMostMove = moveNum;
-           startedFromSetupPosition = TRUE;
-           fromX = fromY = toX = toY = -1;
-       } else {
-           /* Move from ICS was illegal!?  Punt. */
-#if 0
-           if (appData.testLegality && appData.debugMode) {
-               sprintf(str, "Illegal move \"%s\" from ICS", move_str);
-               DisplayError(str, 0);
-           }
-#endif
-           strcpy(parseList[moveNum - 1], move_str);
-           strcat(parseList[moveNum - 1], " ");
-           strcat(parseList[moveNum - 1], elapsed_time);
-           moveList[moveNum - 1][0] = NULLCHAR;
-           fromX = fromY = toX = toY = -1;
-       }
-
-#if ZIPPY
-       /* Send move to chess program (BEFORE animating it). */
-       if (appData.zippyPlay && !newGame && newMove &&
-          (!appData.getMoveList || backwardMostMove == 0) && first.initDone) {
-
-           if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||
-               (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {
-               if (moveList[moveNum - 1][0] == NULLCHAR) {
-                   sprintf(str, _("Couldn't parse move \"%s\" from ICS"),
-                           move_str);
-                   DisplayError(str, 0);
-               } else {
-                   if (first.sendTime) {
-                       SendTimeRemaining(&first, gameMode == IcsPlayingWhite);
-                   }
-                   SendMoveToProgram(moveNum - 1, &first);
-                   if (firstMove) {
-                       firstMove = FALSE;
-                       if (first.useColors) {
-                         SendToProgram(gameMode == IcsPlayingWhite ?
-                                       "white\ngo\n" :
-                                       "black\ngo\n", &first);
-                       } else {
-                         SendToProgram("go\n", &first);
-                       }
-                       first.maybeThinking = TRUE;
-                   }
-               }
-           } else if (gameMode == IcsObserving || gameMode == IcsExamining) {
-             if (moveList[moveNum - 1][0] == NULLCHAR) {
-               sprintf(str, _("Couldn't parse move \"%s\" from ICS"), move_str);
-               DisplayError(str, 0);
-             } else {
-               SendMoveToProgram(moveNum - 1, &first);
-             }
-           }
-       }
-#endif
-    }
-
-    if (moveNum > 0 && !gotPremove) {
-       /* If move comes from a remote source, animate it.  If it
-          isn't remote, it will have already been animated. */
-       if (!pausing && !ics_user_moved && prevMove == moveNum - 1) {
-           AnimateMove(boards[moveNum - 1], fromX, fromY, toX, toY);
-       }
-       if (!pausing && appData.highlightLastMove) {
-           SetHighlights(fromX, fromY, toX, toY);
-       }
-    }
-
-    /* Start the clocks */
-    whiteFlag = blackFlag = FALSE;
-    appData.clockMode = !(basetime == 0 && increment == 0);
-    if (ticking == 0) {
-      ics_clock_paused = TRUE;
-      StopClocks();
-    } else if (ticking == 1) {
-      ics_clock_paused = FALSE;
-    }
-    if (gameMode == IcsIdle ||
-       relation == RELATION_OBSERVING_STATIC ||
-       relation == RELATION_EXAMINING ||
-       ics_clock_paused)
-      DisplayBothClocks();
-    else
-      StartClocks();
-
-    /* Display opponents and material strengths */
-    if (gameInfo.variant != VariantBughouse &&
-       gameInfo.variant != VariantCrazyhouse) {
-       if (tinyLayout || smallLayout) {
-           sprintf(str, "%s(%d) %s(%d) {%d %d}",
-                   gameInfo.white, white_stren, gameInfo.black, black_stren,
-                   basetime, increment);
-       } else {
-           sprintf(str, "%s (%d) vs. %s (%d) {%d %d}",
-                   gameInfo.white, white_stren, gameInfo.black, black_stren,
-                   basetime, increment);
-       }
-       DisplayTitle(str);
-    }
-
-
-    /* Display the board */
-    if (!pausing) {
-
-      if (appData.premove)
-         if (!gotPremove ||
-            ((gameMode == IcsPlayingWhite) && (WhiteOnMove(currentMove))) ||
-            ((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))
-             ClearPremoveHighlights();
-
-      DrawPosition(FALSE, boards[currentMove]);
-      DisplayMove(moveNum - 1);
-      if (appData.ringBellAfterMoves && !ics_user_moved)
-       RingBell();
-    }
-
-    HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
-}
-
-void
-GetMoveListEvent()
-{
-    char buf[MSG_SIZ];
-    if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {
-       ics_getting_history = H_REQUESTED;
-       sprintf(buf, "%smoves %d\n", ics_prefix, ics_gamenum);
-       SendToICS(buf);
-    }
-}
-
-void
-AnalysisPeriodicEvent(force)
-     int force;
-{
-    if (((programStats.ok_to_send == 0 || programStats.line_is_book)
-        && !force) || !appData.periodicUpdates)
-      return;
-
-    /* Send . command to Crafty to collect stats */
-    SendToProgram(".\n", &first);
-
-    /* Don't send another until we get a response (this makes
-       us stop sending to old Crafty's which don't understand
-       the "." command (sending illegal cmds resets node count & time,
-       which looks bad)) */
-    programStats.ok_to_send = 0;
-}
-
-void
-SendMoveToProgram(moveNum, cps)
-     int moveNum;
-     ChessProgramState *cps;
-{
-    char buf[MSG_SIZ];
-    if (cps->useUsermove) {
-      SendToProgram("usermove ", cps);
-    }
-    if (cps->useSAN) {
-      char *space;
-      if ((space = strchr(parseList[moveNum], ' ')) != NULL) {
-       int len = space - parseList[moveNum];
-       memcpy(buf, parseList[moveNum], len);
-       buf[len++] = '\n';
-       buf[len] = NULLCHAR;
-      } else {
-       sprintf(buf, "%s\n", parseList[moveNum]);
-      }
-      SendToProgram(buf, cps);
-    } else {
-      SendToProgram(moveList[moveNum], cps);
-    }
-}
-
-void
-SendMoveToICS(moveType, fromX, fromY, toX, toY)
-     ChessMove moveType;
-     int fromX, fromY, toX, toY;
-{
-    char user_move[MSG_SIZ];
-
-    switch (moveType) {
-      default:
-       sprintf(user_move, "say Internal error; bad moveType %d (%d,%d-%d,%d)",
-               (int)moveType, fromX, fromY, toX, toY);
-       DisplayError(user_move + strlen("say "), 0);
-       break;
-      case WhiteKingSideCastle:
-      case BlackKingSideCastle:
-      case WhiteQueenSideCastleWild:
-      case BlackQueenSideCastleWild:
-       sprintf(user_move, "o-o\n");
-       break;
-      case WhiteQueenSideCastle:
-      case BlackQueenSideCastle:
-      case WhiteKingSideCastleWild:
-      case BlackKingSideCastleWild:
-       sprintf(user_move, "o-o-o\n");
-       break;
-      case WhitePromotionQueen:
-      case BlackPromotionQueen:
-      case WhitePromotionRook:
-      case BlackPromotionRook:
-      case WhitePromotionBishop:
-      case BlackPromotionBishop:
-      case WhitePromotionKnight:
-      case BlackPromotionKnight:
-      case WhitePromotionKing:
-      case BlackPromotionKing:
-       sprintf(user_move, "%c%c%c%c=%c\n",
-               'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY,
-               PieceToChar(PromoPiece(moveType)));
-       break;
-      case WhiteDrop:
-      case BlackDrop:
-       sprintf(user_move, "%c@%c%c\n",
-               ToUpper(PieceToChar((ChessSquare) fromX)),
-               'a' + toX, '1' + toY);
-       break;
-      case NormalMove:
-      case WhiteCapturesEnPassant:
-      case BlackCapturesEnPassant:
-      case IllegalMove:  /* could be a variant we don't quite understand */
-       sprintf(user_move, "%c%c%c%c\n",
-               'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY);
-       break;
-    }
-    SendToICS(user_move);
-}
-
-void
-CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
-     int rf, ff, rt, ft;
-     char promoChar;
-     char move[7];
-{
-    if (rf == DROP_RANK) {
-       sprintf(move, "%c@%c%c\n",
-               ToUpper(PieceToChar((ChessSquare) ff)), 'a' + ft, '1' + rt);
-    } else {
-       if (promoChar == 'x' || promoChar == NULLCHAR) {
-           sprintf(move, "%c%c%c%c\n",
-                   'a' + ff, '1' + rf, 'a' + ft, '1' + rt);
-       } else {
-           sprintf(move, "%c%c%c%c%c\n",
-                   'a' + ff, '1' + rf, 'a' + ft, '1' + rt, promoChar);
-       }
-    }
-}
-
-void
-ProcessICSInitScript(f)
-     FILE *f;
-{
-    char buf[MSG_SIZ];
-
-    while (fgets(buf, MSG_SIZ, f)) {
-       SendToICSDelayed(buf,(long)appData.msLoginDelay);
-    }
-
-    fclose(f);
-}
-
-
-/* Parser for moves from gnuchess, ICS, or user typein box */
-Boolean
-ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
-     char *move;
-     int moveNum;
-     ChessMove *moveType;
-     int *fromX, *fromY, *toX, *toY;
-     char *promoChar;
-{
-    *moveType = yylexstr(moveNum, move);
-    switch (*moveType) {
-      case WhitePromotionQueen:
-      case BlackPromotionQueen:
-      case WhitePromotionRook:
-      case BlackPromotionRook:
-      case WhitePromotionBishop:
-      case BlackPromotionBishop:
-      case WhitePromotionKnight:
-      case BlackPromotionKnight:
-      case WhitePromotionKing:
-      case BlackPromotionKing:
-      case NormalMove:
-      case WhiteCapturesEnPassant:
-      case BlackCapturesEnPassant:
-      case WhiteKingSideCastle:
-      case WhiteQueenSideCastle:
-      case BlackKingSideCastle:
-      case BlackQueenSideCastle:
-      case WhiteKingSideCastleWild:
-      case WhiteQueenSideCastleWild:
-      case BlackKingSideCastleWild:
-      case BlackQueenSideCastleWild:
-      case IllegalMove:                /* bug or odd chess variant */
-       *fromX = currentMoveString[0] - 'a';
-       *fromY = currentMoveString[1] - '1';
-       *toX = currentMoveString[2] - 'a';
-       *toY = currentMoveString[3] - '1';
-       *promoChar = currentMoveString[4];
-       if (*fromX < 0 || *fromX > 7 || *fromY < 0 || *fromY > 7 ||
-           *toX < 0 || *toX > 7 || *toY < 0 || *toY > 7) {
-           *fromX = *fromY = *toX = *toY = 0;
-           return FALSE;
-       }
-       if (appData.testLegality) {
-         return (*moveType != IllegalMove);
-       } else {
-         return !(fromX == fromY && toX == toY);
-       }
-
-      case WhiteDrop:
-      case BlackDrop:
-       *fromX = *moveType == WhiteDrop ?
-         (int) CharToPiece(ToUpper(currentMoveString[0])) :
-       (int) CharToPiece(ToLower(currentMoveString[0]));
-       *fromY = DROP_RANK;
-       *toX = currentMoveString[2] - 'a';
-       *toY = currentMoveString[3] - '1';
-       *promoChar = NULLCHAR;
-       return TRUE;
-
-      case AmbiguousMove:
-      case ImpossibleMove:
-      case (ChessMove) 0:      /* end of file */
-      case ElapsedTime:
-      case Comment:
-      case PGNTag:
-      case NAG:
-      case WhiteWins:
-      case BlackWins:
-      case GameIsDrawn:
-      default:
-       /* bug? */
-       *fromX = *fromY = *toX = *toY = 0;
-       *promoChar = NULLCHAR;
-       return FALSE;
-    }
-}
-
-
-void
-InitPosition(redraw)
-     int redraw;
-{
-    currentMove = forwardMostMove = backwardMostMove = 0;
-    switch (gameInfo.variant) {
-    default:
-      CopyBoard(boards[0], initialPosition);
-      break;
-    case VariantTwoKings:
-      CopyBoard(boards[0], twoKingsPosition);
-      startedFromSetupPosition = TRUE;
-      break;
-    case VariantWildCastle:
-      CopyBoard(boards[0], initialPosition);
-      /* !!?shuffle with kings guaranteed to be on d or e file */
-      break;
-    case VariantNoCastle:
-      CopyBoard(boards[0], initialPosition);
-      /* !!?unconstrained back-rank shuffle */
-      break;
-    case VariantFischeRandom:
-      CopyBoard(boards[0], initialPosition);
-      /* !!shuffle according to FR rules */
-      break;
-    }
-    if (redraw)
-      DrawPosition(FALSE, boards[currentMove]);
-}
-
-void
-SendBoard(cps, moveNum)
-     ChessProgramState *cps;
-     int moveNum;
-{
-    char message[MSG_SIZ];
-
-    if (cps->useSetboard) {
-      char* fen = PositionToFEN(moveNum);
-      sprintf(message, "setboard %s\n", fen);
-      SendToProgram(message, cps);
-      free(fen);
-
-    } else {
-      ChessSquare *bp;
-      int i, j;
-      /* Kludge to set black to move, avoiding the troublesome and now
-       * deprecated "black" command.
-       */
-      if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);
-
-      SendToProgram("edit\n", cps);
-      SendToProgram("#\n", cps);
-      for (i = BOARD_SIZE - 1; i >= 0; i--) {
-       bp = &boards[moveNum][i][0];
-       for (j = 0; j < BOARD_SIZE; j++, bp++) {
-         if ((int) *bp < (int) BlackPawn) {
-           sprintf(message, "%c%c%c\n", PieceToChar(*bp),
-                   'a' + j, '1' + i);
-           SendToProgram(message, cps);
-         }
-       }
-      }
-
-      SendToProgram("c\n", cps);
-      for (i = BOARD_SIZE - 1; i >= 0; i--) {
-       bp = &boards[moveNum][i][0];
-       for (j = 0; j < BOARD_SIZE; j++, bp++) {
-         if (((int) *bp != (int) EmptySquare)
-             && ((int) *bp >= (int) BlackPawn)) {
-           sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
-                   'a' + j, '1' + i);
-           SendToProgram(message, cps);
-         }
-       }
-      }
-
-      SendToProgram(".\n", cps);
-    }
-}
-
-int
-IsPromotion(fromX, fromY, toX, toY)
-     int fromX, fromY, toX, toY;
-{
-    return gameMode != EditPosition &&
-      fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 &&
-       ((boards[currentMove][fromY][fromX] == WhitePawn && toY == 7) ||
-        (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0));
-}
-
-
-int
-PieceForSquare (x, y)
-     int x;
-     int y;
-{
-  if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE)
-     return -1;
-  else
-     return boards[currentMove][y][x];
-}
-
-int
-OKToStartUserMove(x, y)
-     int x, y;
-{
-    ChessSquare from_piece;
-    int white_piece;
-
-    if (matchMode) return FALSE;
-    if (gameMode == EditPosition) return TRUE;
-
-    if (x >= 0 && y >= 0)
-      from_piece = boards[currentMove][y][x];
-    else
-      from_piece = EmptySquare;
-
-    if (from_piece == EmptySquare) return FALSE;
-
-    white_piece = (int)from_piece >= (int)WhitePawn &&
-      (int)from_piece <= (int)WhiteKing;
-
-    switch (gameMode) {
-      case PlayFromGameFile:
-      case AnalyzeFile:
-      case TwoMachinesPlay:
-      case EndOfGame:
-       return FALSE;
-
-      case IcsObserving:
-      case IcsIdle:
-       return FALSE;
-
-      case MachinePlaysWhite:
-      case IcsPlayingBlack:
-       if (appData.zippyPlay) return FALSE;
-       if (white_piece) {
-           DisplayMoveError(_("You are playing Black"));
-           return FALSE;
-       }
-       break;
-
-      case MachinePlaysBlack:
-      case IcsPlayingWhite:
-       if (appData.zippyPlay) return FALSE;
-       if (!white_piece) {
-           DisplayMoveError(_("You are playing White"));
-           return FALSE;
-       }
-       break;
-
-      case EditGame:
-       if (!white_piece && WhiteOnMove(currentMove)) {
-           DisplayMoveError(_("It is White's turn"));
-           return FALSE;
-       }
-       if (white_piece && !WhiteOnMove(currentMove)) {
-           DisplayMoveError(_("It is Black's turn"));
-           return FALSE;
-       }
-       if (cmailMsgLoaded && (currentMove < cmailOldMove)) {
-           /* Editing correspondence game history */
-           /* Could disallow this or prompt for confirmation */
-           cmailOldMove = -1;
-       }
-       if (currentMove < forwardMostMove) {
-           /* Discarding moves */
-           /* Could prompt for confirmation here,
-              but I don't think that's such a good idea */
-           forwardMostMove = currentMove;
-       }
-       break;
-
-      case BeginningOfGame:
-       if (appData.icsActive) return FALSE;
-       if (!appData.noChessProgram) {
-           if (!white_piece) {
-               DisplayMoveError(_("You are playing White"));
-               return FALSE;
-           }
-       }
-       break;
-
-      case Training:
-       if (!white_piece && WhiteOnMove(currentMove)) {
-           DisplayMoveError(_("It is White's turn"));
-           return FALSE;
-       }
-       if (white_piece && !WhiteOnMove(currentMove)) {
-           DisplayMoveError(_("It is Black's turn"));
-           return FALSE;
-       }
-       break;
-
-      default:
-      case IcsExamining:
-       break;
-    }
-    if (currentMove != forwardMostMove && gameMode != AnalyzeMode
-       && gameMode != AnalyzeFile && gameMode != Training) {
-       DisplayMoveError(_("Displayed position is not current"));
-       return FALSE;
-    }
-    return TRUE;
-}
-
-FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL;
-int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;
-int lastLoadGameUseList = FALSE;
-char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
-ChessMove lastLoadGameStart = (ChessMove) 0;
-
-
-void
-UserMoveEvent(fromX, fromY, toX, toY, promoChar)
-     int fromX, fromY, toX, toY;
-     int promoChar;
-{
-    ChessMove moveType;
-
-    if (fromX < 0 || fromY < 0) return;
-    if ((fromX == toX) && (fromY == toY)) {
-       return;
-    }
-
-    /* Check if the user is playing in turn.  This is complicated because we
-       let the user "pick up" a piece before it is his turn.  So the piece he
-       tried to pick up may have been captured by the time he puts it down!
-       Therefore we use the color the user is supposed to be playing in this
-       test, not the color of the piece that is currently on the starting
-       square---except in EditGame mode, where the user is playing both
-       sides; fortunately there the capture race can't happen.  (It can
-       now happen in IcsExamining mode, but that's just too bad.  The user
-       will get a somewhat confusing message in that case.)
-       */
-
-    switch (gameMode) {
-      case PlayFromGameFile:
-      case AnalyzeFile:
-      case TwoMachinesPlay:
-      case EndOfGame:
-      case IcsObserving:
-      case IcsIdle:
-       /* We switched into a game mode where moves are not accepted,
-           perhaps while the mouse button was down. */
-       return;
-
-      case MachinePlaysWhite:
-       /* User is moving for Black */
-       if (WhiteOnMove(currentMove)) {
-           DisplayMoveError(_("It is White's turn"));
-           return;
-       }
-       break;
-
-      case MachinePlaysBlack:
-       /* User is moving for White */
-       if (!WhiteOnMove(currentMove)) {
-           DisplayMoveError(_("It is Black's turn"));
-           return;
-       }
-       break;
-
-      case EditGame:
-      case IcsExamining:
-      case BeginningOfGame:
-      case AnalyzeMode:
-      case Training:
-       if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&
-           (int) boards[currentMove][fromY][fromX] <= (int) BlackKing) {
-           /* User is moving for Black */
-           if (WhiteOnMove(currentMove)) {
-               DisplayMoveError(_("It is White's turn"));
-               return;
-           }
-       } else {
-           /* User is moving for White */
-           if (!WhiteOnMove(currentMove)) {
-               DisplayMoveError(_("It is Black's turn"));
-               return;
-           }
-       }
-       break;
-
-      case IcsPlayingBlack:
-       /* User is moving for Black */
-       if (WhiteOnMove(currentMove)) {
-           if (!appData.premove) {
-               DisplayMoveError(_("It is White's turn"));
-           } else if (toX >= 0 && toY >= 0) {
-               premoveToX = toX;
-               premoveToY = toY;
-               premoveFromX = fromX;
-               premoveFromY = fromY;
-               premovePromoChar = promoChar;
-               gotPremove = 1;
-               if (appData.debugMode)
-                   fprintf(debugFP, "Got premove: fromX %d,"
-                           "fromY %d, toX %d, toY %d\n",
-                           fromX, fromY, toX, toY);
-           }
-           return;
-       }
-       break;
-
-      case IcsPlayingWhite:
-       /* User is moving for White */
-       if (!WhiteOnMove(currentMove)) {
-           if (!appData.premove) {
-               DisplayMoveError(_("It is Black's turn"));
-           } else if (toX >= 0 && toY >= 0) {
-               premoveToX = toX;
-               premoveToY = toY;
-               premoveFromX = fromX;
-               premoveFromY = fromY;
-               premovePromoChar = promoChar;
-               gotPremove = 1;
-               if (appData.debugMode)
-                   fprintf(debugFP, "Got premove: fromX %d,"
-                           "fromY %d, toX %d, toY %d\n",
-                           fromX, fromY, toX, toY);
-           }
-           return;
-       }
-       break;
-
-      default:
-       break;
-
-      case EditPosition:
-       if (toX == -2 || toY == -2) {
-           boards[0][fromY][fromX] = EmptySquare;
-           DrawPosition(FALSE, boards[currentMove]);
-       } else if (toX >= 0 && toY >= 0) {
-           boards[0][toY][toX] = boards[0][fromY][fromX];
-           boards[0][fromY][fromX] = EmptySquare;
-           DrawPosition(FALSE, boards[currentMove]);
-       }
-       return;
-    }
-
-    if (toX < 0 || toY < 0) return;
-    userOfferedDraw = FALSE;
-
-    if (appData.testLegality) {
-       moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
-                               EP_UNKNOWN, fromY, fromX, toY, toX, promoChar);
-       if (moveType == IllegalMove || moveType == ImpossibleMove) {
-           DisplayMoveError(_("Illegal move"));
-           return;
-       }
-    } else {
-       moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);
-    }
-
-    if (gameMode == Training) {
-      /* compare the move played on the board to the next move in the
-       * game. If they match, display the move and the opponent's response.
-       * If they don't match, display an error message.
-       */
-      int saveAnimate;
-      Board testBoard;
-      CopyBoard(testBoard, boards[currentMove]);
-      ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);
-
-      if (CompareBoards(testBoard, boards[currentMove+1])) {
-       ForwardInner(currentMove+1);
-
-       /* Autoplay the opponent's response.
-        * if appData.animate was TRUE when Training mode was entered,
-        * the response will be animated.
-        */
-       saveAnimate = appData.animate;
-       appData.animate = animateTraining;
-       ForwardInner(currentMove+1);
-       appData.animate = saveAnimate;
-
-       /* check for the end of the game */
-       if (currentMove >= forwardMostMove) {
-         gameMode = PlayFromGameFile;
-         ModeHighlight();
-         SetTrainingModeOff();
-         DisplayInformation(_("End of game"));
-       }
-      } else {
-       DisplayError(_("Incorrect move"), 0);
-      }
-      return;
-    }
-
-    FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
-}
-
-/* Common tail of UserMoveEvent and DropMenuEvent */
-void
-FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
-     ChessMove moveType;
-     int fromX, fromY, toX, toY;
-     /*char*/int promoChar;
-{
-  /* Ok, now we know that the move is good, so we can kill
-     the previous line in Analysis Mode */
-  if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
-    forwardMostMove = currentMove;
-  }
-
-  /* If we need the chess program but it's dead, restart it */
-  ResurrectChessProgram();
-
-  /* A user move restarts a paused game*/
-  if (pausing)
-    PauseEvent();
-
-  thinkOutput[0] = NULLCHAR;
-
-  MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/
-
-  if (gameMode == BeginningOfGame) {
-    if (appData.noChessProgram) {
-      gameMode = EditGame;
-      SetGameInfo();
-    } else {
-      char buf[MSG_SIZ];
-      gameMode = MachinePlaysBlack;
-      SetGameInfo();
-      sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
-      DisplayTitle(buf);
-      if (first.sendName) {
-       sprintf(buf, "name %s\n", gameInfo.white);
-       SendToProgram(buf, &first);
-      }
-    }
-    ModeHighlight();
-  }
-
-  /* Relay move to ICS or chess engine */
-  if (appData.icsActive) {
-    if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
-       gameMode == IcsExamining) {
-      SendMoveToICS(moveType, fromX, fromY, toX, toY);
-      ics_user_moved = 1;
-    }
-  } else {
-    if (first.sendTime && (gameMode == BeginningOfGame ||
-                          gameMode == MachinePlaysWhite ||
-                          gameMode == MachinePlaysBlack)) {
-      SendTimeRemaining(&first, gameMode != MachinePlaysBlack);
-    }
-    SendMoveToProgram(forwardMostMove-1, &first);
-    if (gameMode != EditGame && gameMode != PlayFromGameFile) {
-      first.maybeThinking = TRUE;
-    }
-    if (currentMove == cmailOldMove + 1) {
-      cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
-    }
-  }
-
-  ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
-
-  switch (gameMode) {
-  case EditGame:
-    switch (MateTest(boards[currentMove], PosFlags(currentMove),
-                    EP_UNKNOWN)) {
-    case MT_NONE:
-    case MT_CHECK:
-      break;
-    case MT_CHECKMATE:
-      if (WhiteOnMove(currentMove)) {
-       GameEnds(BlackWins, "Black mates", GE_PLAYER);
-      } else {
-       GameEnds(WhiteWins, "White mates", GE_PLAYER);
-      }
-      break;
-    case MT_STALEMATE:
-      GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
-      break;
-    }
-    break;
-
-  case MachinePlaysBlack:
-  case MachinePlaysWhite:
-    /* disable certain menu options while machine is thinking */
-    SetMachineThinkingEnables();
-    break;
-
-  default:
-    break;
-  }
-}
-
-void
-HandleMachineMove(message, cps)
-     char *message;
-     ChessProgramState *cps;
-{
-    char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];
-    char realname[MSG_SIZ];
-    int fromX, fromY, toX, toY;
-    ChessMove moveType;
-    char promoChar;
-    char *p;
-    int machineWhite;
-
-    /*
-     * Kludge to ignore BEL characters
-     */
-    while (*message == '\007') message++;
-
-    /*
-     * Look for book output
-     */
-    if (cps == &first && bookRequested) {
-       if (message[0] == '\t' || message[0] == ' ') {
-           /* Part of the book output is here; append it */
-           strcat(bookOutput, message);
-           strcat(bookOutput, "  \n");
-           return;
-       } else if (bookOutput[0] != NULLCHAR) {
-           /* All of book output has arrived; display it */
-           char *p = bookOutput;
-           while (*p != NULLCHAR) {
-               if (*p == '\t') *p = ' ';
-               p++;
-           }
-           DisplayInformation(bookOutput);
-           bookRequested = FALSE;
-           /* Fall through to parse the current output */
-       }
-    }
-
-    /*
-     * Look for machine move.
-     */
-    if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 &&
-        strcmp(buf2, "...") == 0) ||
-       (sscanf(message, "%s %s", buf1, machineMove) == 2 &&
-        strcmp(buf1, "move") == 0)) {
-
-        /* This method is only useful on engines that support ping */
-        if (cps->lastPing != cps->lastPong) {
-         if (gameMode == BeginningOfGame) {
-           /* Extra move from before last new; ignore */
-           if (appData.debugMode) {
-               fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
-           }
-         } else {
-           if (appData.debugMode) {
-               fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
-                       cps->which, gameMode);
-           }
-           SendToProgram("undo\n", cps);
-         }
-         return;
-       }
-
-       switch (gameMode) {
-         case BeginningOfGame:
-           /* Extra move from before last reset; ignore */
-           if (appData.debugMode) {
-               fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
-           }
-           return;
-
-         case EndOfGame:
-         case IcsIdle:
-         default:
-           /* Extra move after we tried to stop.  The mode test is
-              not a reliable way of detecting this problem, but it's
-              the best we can do on engines that don't support ping.
-           */
-           if (appData.debugMode) {
-               fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",
-                       cps->which, gameMode);
-           }
-           SendToProgram("undo\n", cps);
-           return;
-
-         case MachinePlaysWhite:
-         case IcsPlayingWhite:
-           machineWhite = TRUE;
-           break;
-
-         case MachinePlaysBlack:
-         case IcsPlayingBlack:
-           machineWhite = FALSE;
-           break;
-
-         case TwoMachinesPlay:
-           machineWhite = (cps->twoMachinesColor[0] == 'w');
-           break;
-       }
-       if (WhiteOnMove(forwardMostMove) != machineWhite) {
-           if (appData.debugMode) {
-               fprintf(debugFP,
-                       "Ignoring move out of turn by %s, gameMode %d"
-                       ", forwardMost %d\n",
-                       cps->which, gameMode, forwardMostMove);
-           }
-           return;
-       }
-
-       if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
-                             &fromX, &fromY, &toX, &toY, &promoChar)) {
-           /* Machine move could not be parsed; ignore it. */
-           sprintf(buf1, _("Illegal move \"%s\" from %s machine"),
-                   machineMove, cps->which);
-           DisplayError(buf1, 0);
-           if (gameMode == TwoMachinesPlay) {
-             GameEnds(machineWhite ? BlackWins : WhiteWins,
-                      "Forfeit due to illegal move", GE_XBOARD);
-           }
-           return;
-       }
-
-       hintRequested = FALSE;
-       lastHint[0] = NULLCHAR;
-       bookRequested = FALSE;
-       /* Program may be pondering now */
-       cps->maybeThinking = TRUE;
-       if (cps->sendTime == 2) cps->sendTime = 1;
-       if (cps->offeredDraw) cps->offeredDraw--;
-
-#if ZIPPY
-       if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&
-           first.initDone) {
-         SendMoveToICS(moveType, fromX, fromY, toX, toY);
-         ics_user_moved = 1;
-       }
-#endif
-       /* currentMoveString is set as a side-effect of ParseOneMove */
-       strcpy(machineMove, currentMoveString);
-       strcat(machineMove, "\n");
-       strcpy(moveList[forwardMostMove], machineMove);
-
-       MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
-
-       if (gameMode == TwoMachinesPlay) {
-           if (cps->other->sendTime) {
-               SendTimeRemaining(cps->other,
-                                 cps->other->twoMachinesColor[0] == 'w');
-           }
-           SendMoveToProgram(forwardMostMove-1, cps->other);
-           if (firstMove) {
-               firstMove = FALSE;
-               if (cps->other->useColors) {
-                 SendToProgram(cps->other->twoMachinesColor, cps->other);
-               }
-               SendToProgram("go\n", cps->other);
-           }
-           cps->other->maybeThinking = TRUE;
-       }
-
-       ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
-       if (!pausing && appData.ringBellAfterMoves) {
-           RingBell();
-       }
-       /*
-        * Reenable menu items that were disabled while
-        * machine was thinking
-        */
-       if (gameMode != TwoMachinesPlay)
-           SetUserThinkingEnables();
-       return;
-    }
-
-    /* Set special modes for chess engines.  Later something general
-     *  could be added here; for now there is just one kludge feature,
-     *  needed because Crafty 15.10 and earlier don't ignore SIGINT
-     *  when "xboard" is given as an interactive command.
-     */
-    if (strncmp(message, "kibitz Hello from Crafty", 24) == 0) {
-       cps->useSigint = FALSE;
-       cps->useSigterm = FALSE;
-    }
-
-    /*
-     * Look for communication commands
-     */
-    if (!strncmp(message, "telluser ", 9)) {
-       DisplayNote(message + 9);
-       return;
-    }
-    if (!strncmp(message, "tellusererror ", 14)) {
-       DisplayError(message + 14, 0);
-       return;
-    }
-    if (!strncmp(message, "tellopponent ", 13)) {
-      if (appData.icsActive) {
-       if (loggedOn) {
-         sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);
-         SendToICS(buf1);
-       }
-      } else {
-       DisplayNote(message + 13);
-      }
-      return;
-    }
-    if (!strncmp(message, "tellothers ", 11)) {
-      if (appData.icsActive) {
-       if (loggedOn) {
-         sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);
-         SendToICS(buf1);
-       }
-      }
-      return;
-    }
-    if (!strncmp(message, "tellall ", 8)) {
-      if (appData.icsActive) {
-       if (loggedOn) {
-         sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);
-         SendToICS(buf1);
-       }
-      } else {
-       DisplayNote(message + 8);
-      }
-      return;
-    }
-    if (strncmp(message, "warning", 7) == 0) {
-       /* Undocumented feature, use tellusererror in new code */
-       DisplayError(message, 0);
-       return;
-    }
-    if (sscanf(message, "askuser %s %[^\n]", buf1, buf2) == 2) {
-       strcpy(realname, cps->tidy);
-       strcat(realname, " query");
-       AskQuestion(realname, buf2, buf1, cps->pr);
-       return;
-    }
-    /* Commands from the engine directly to ICS.  We don't allow these to be
-     *  sent until we are logged on. Crafty kibitzes have been known to
-     *  interfere with the login process.
-     */
-    if (loggedOn) {
-       if (!strncmp(message, "tellics ", 8)) {
-           SendToICS(message + 8);
-           SendToICS("\n");
-           return;
-       }
-       if (!strncmp(message, "tellicsnoalias ", 15)) {
-           SendToICS(ics_prefix);
-           SendToICS(message + 15);
-           SendToICS("\n");
-           return;
-       }
-       /* The following are for backward compatibility only */
-       if (!strncmp(message,"whisper",7) || !strncmp(message,"kibitz",6) ||
-           !strncmp(message,"draw",4) || !strncmp(message,"tell",3)) {
-           SendToICS(ics_prefix);
-           SendToICS(message);
-           SendToICS("\n");
-           return;
-       }
-    }
-    if (strncmp(message, "feature ", 8) == 0) {
-      ParseFeatures(message+8, cps);
-    }
-    if (sscanf(message, "pong %d", &cps->lastPong) == 1) {
-      return;
-    }
-    /*
-     * If the move is illegal, cancel it and redraw the board.
-     * Also deal with other error cases.  Matching is rather loose
-     * here to accommodate engines written before the spec.
-     */
-    if (strncmp(message + 1, "llegal move", 11) == 0 ||
-       strncmp(message, "Error", 5) == 0) {
-       if (StrStr(message, "name") ||
-           StrStr(message, "rating") || StrStr(message, "?") ||
-           StrStr(message, "result") || StrStr(message, "board") ||
-           StrStr(message, "bk") || StrStr(message, "computer") ||
-           StrStr(message, "variant") || StrStr(message, "hint") ||
-           StrStr(message, "random") || StrStr(message, "depth") ||
-           StrStr(message, "accepted")) {
-           return;
-       }
-       if (StrStr(message, "protover")) {
-         /* Program is responding to input, so it's apparently done
-             initializing, and this error message indicates it is
-             protocol version 1.  So we don't need to wait any longer
-             for it to initialize and send feature commands. */
-         FeatureDone(cps, 1);
-         cps->protocolVersion = 1;
-         return;
-       }
-       cps->maybeThinking = FALSE;
-
-       if (StrStr(message, "draw")) {
-           /* Program doesn't have "draw" command */
-           cps->sendDrawOffers = 0;
-           return;
-       }
-       if (cps->sendTime != 1 &&
-           (StrStr(message, "time") || StrStr(message, "otim"))) {
-         /* Program apparently doesn't have "time" or "otim" command */
-         cps->sendTime = 0;
-         return;
-       }
-       if (StrStr(message, "analyze")) {
-           cps->analysisSupport = FALSE;
-           cps->analyzing = FALSE;
-           Reset(FALSE, TRUE);
-           sprintf(buf2, "%s does not support analysis", cps->tidy);
-           DisplayError(buf2, 0);
-           return;
-       }
-       if (StrStr(message, "(no matching move)st")) {
-         /* Special kludge for GNU Chess 4 only */
-         cps->stKludge = TRUE;
-         SendTimeControl(cps, movesPerSession, timeControl,
-                         timeIncrement, appData.searchDepth,
-                         searchTime);
-         return;
-       }
-       if (StrStr(message, "(no matching move)sd")) {
-         /* Special kludge for GNU Chess 4 only */
-         cps->sdKludge = TRUE;
-         SendTimeControl(cps, movesPerSession, timeControl,
-                         timeIncrement, appData.searchDepth,
-                         searchTime);
-         return;
-       }
-       if (!StrStr(message, "llegal")) return;
-       if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
-           gameMode == IcsIdle) return;
-       if (forwardMostMove <= backwardMostMove) return;
-#if 0
-       /* Following removed: it caused a bug where a real illegal move
-          message in analyze mored would be ignored. */
-       if (cps == &first && programStats.ok_to_send == 0) {
-           /* Bogus message from Crafty responding to "."  This filtering
-              can miss some of the bad messages, but fortunately the bug
-              is fixed in current Crafty versions, so it doesn't matter. */
-           return;
-       }
-#endif
-       if (pausing) PauseEvent();
-       if (gameMode == PlayFromGameFile) {
-           /* Stop reading this game file */
-           gameMode = EditGame;
-           ModeHighlight();
-       }
-       currentMove = --forwardMostMove;
-       DisplayMove(currentMove-1); /* before DisplayMoveError */
-       SwitchClocks();
-       DisplayBothClocks();
-       sprintf(buf1, _("Illegal move \"%s\" (rejected by %s chess program)"),
-               parseList[currentMove], cps->which);
-       DisplayMoveError(buf1);
-       DrawPosition(FALSE, boards[currentMove]);
-       return;
-    }
-    if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {
-       /* Program has a broken "time" command that
-          outputs a string not ending in newline.
-          Don't use it. */
-       cps->sendTime = 0;
-    }
-
-    /*
-     * If chess program startup fails, exit with an error message.
-     * Attempts to recover here are futile.
-     */
-    if ((StrStr(message, "unknown host") != NULL)
-       || (StrStr(message, "No remote directory") != NULL)
-       || (StrStr(message, "not found") != NULL)
-       || (StrStr(message, "No such file") != NULL)
-       || (StrStr(message, "can't alloc") != NULL)
-       || (StrStr(message, "Permission denied") != NULL)) {
-
-       cps->maybeThinking = FALSE;
-       sprintf(buf1, _("Failed to start %s chess program %s on %s: %s\n"),
-               cps->which, cps->program, cps->host, message);
-       RemoveInputSource(cps->isr);
-       DisplayFatalError(buf1, 0, 1);
-       return;
-    }
-
-    /*
-     * Look for hint output
-     */
-    if (sscanf(message, "Hint: %s", buf1) == 1) {
-       if (cps == &first && hintRequested) {
-           hintRequested = FALSE;
-           if (ParseOneMove(buf1, forwardMostMove, &moveType,
-                                &fromX, &fromY, &toX, &toY, &promoChar)) {
-               (void) CoordsToAlgebraic(boards[forwardMostMove],
-                                   PosFlags(forwardMostMove), EP_UNKNOWN,
-                                   fromY, fromX, toY, toX, promoChar, buf1);
-               sprintf(buf2, "Hint: %s", buf1);
-               DisplayInformation(buf2);
-           } else {
-               /* Hint move could not be parsed!? */
-               sprintf(buf2,
-                       _("Illegal hint move \"%s\"\nfrom %s chess program"),
-                       buf1, cps->which);
-               DisplayError(buf2, 0);
-           }
-       } else {
-           strcpy(lastHint, buf1);
-       }
-       return;
-    }
-
-    /*
-     * Ignore other messages if game is not in progress
-     */
-    if (gameMode == BeginningOfGame || gameMode == EndOfGame ||
-       gameMode == IcsIdle || cps->lastPing != cps->lastPong) return;
-
-    /*
-     * look for win, lose, draw, or draw offer
-     */
-    if (strncmp(message, "1-0", 3) == 0) {
-       char *p, *q, *r = "";
-        p = strchr(message, '{');
-       if (p) {
-           q = strchr(p, '}');
-           if (q) {
-               *q = NULLCHAR;
-               r = p + 1;
-           }
-       }
-       GameEnds(WhiteWins, r, GE_ENGINE);
-       return;
-    } else if (strncmp(message, "0-1", 3) == 0) {
-       char *p, *q, *r = "";
-        p = strchr(message, '{');
-       if (p) {
-           q = strchr(p, '}');
-           if (q) {
-               *q = NULLCHAR;
-               r = p + 1;
-           }
-       }
-       /* Kludge for Arasan 4.1 bug */
-       if (strcmp(r, "Black resigns") == 0) {
-           GameEnds(WhiteWins, r, GE_ENGINE);
-           return;
-       }
-       GameEnds(BlackWins, r, GE_ENGINE);
-       return;
-    } else if (strncmp(message, "1/2", 3) == 0) {
-       char *p, *q, *r = "";
-        p = strchr(message, '{');
-       if (p) {
-           q = strchr(p, '}');
-           if (q) {
-               *q = NULLCHAR;
-               r = p + 1;
-           }
-       }
-       GameEnds(GameIsDrawn, r, GE_ENGINE);
-       return;
-
-    } else if (strncmp(message, "White resign", 12) == 0) {
-       GameEnds(BlackWins, "White resigns", GE_ENGINE);
-       return;
-    } else if (strncmp(message, "Black resign", 12) == 0) {
-       GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
-       return;
-    } else if (strncmp(message, "White", 5) == 0 &&
-              message[5] != '(' &&
-              StrStr(message, "Black") == NULL) {
-       GameEnds(WhiteWins, "White mates", GE_ENGINE);
-       return;
-    } else if (strncmp(message, "Black", 5) == 0 &&
-              message[5] != '(') {
-       GameEnds(BlackWins, "Black mates", GE_ENGINE);
-       return;
-    } else if (strcmp(message, "resign") == 0 ||
-              strcmp(message, "computer resigns") == 0) {
-       switch (gameMode) {
-         case MachinePlaysBlack:
-         case IcsPlayingBlack:
-           GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
-           break;
-         case MachinePlaysWhite:
-         case IcsPlayingWhite:
-           GameEnds(BlackWins, "White resigns", GE_ENGINE);
-           break;
-         case TwoMachinesPlay:
-           if (cps->twoMachinesColor[0] == 'w')
-             GameEnds(BlackWins, "White resigns", GE_ENGINE);
-           else
-             GameEnds(WhiteWins, "Black resigns", GE_ENGINE);
-           break;
-         default:
-           /* can't happen */
-           break;
-       }
-       return;
-    } else if (strncmp(message, "opponent mates", 14) == 0) {
-       switch (gameMode) {
-         case MachinePlaysBlack:
-         case IcsPlayingBlack:
-           GameEnds(WhiteWins, "White mates", GE_ENGINE);
-           break;
-         case MachinePlaysWhite:
-         case IcsPlayingWhite:
-           GameEnds(BlackWins, "Black mates", GE_ENGINE);
-           break;
-         case TwoMachinesPlay:
-           if (cps->twoMachinesColor[0] == 'w')
-             GameEnds(BlackWins, "Black mates", GE_ENGINE);
-           else
-             GameEnds(WhiteWins, "White mates", GE_ENGINE);
-           break;
-         default:
-           /* can't happen */
-           break;
-       }
-       return;
-    } else if (strncmp(message, "computer mates", 14) == 0) {
-       switch (gameMode) {
-         case MachinePlaysBlack:
-         case IcsPlayingBlack:
-           GameEnds(BlackWins, "Black mates", GE_ENGINE);
-           break;
-         case MachinePlaysWhite:
-         case IcsPlayingWhite:
-           GameEnds(WhiteWins, "White mates", GE_ENGINE);
-           break;
-         case TwoMachinesPlay:
-           if (cps->twoMachinesColor[0] == 'w')
-             GameEnds(WhiteWins, "White mates", GE_ENGINE);
-           else
-             GameEnds(BlackWins, "Black mates", GE_ENGINE);
-           break;
-         default:
-           /* can't happen */
-           break;
-       }
-       return;
-    } else if (strncmp(message, "checkmate", 9) == 0) {
-       if (WhiteOnMove(forwardMostMove)) {
-           GameEnds(BlackWins, "Black mates", GE_ENGINE);
-       } else {
-           GameEnds(WhiteWins, "White mates", GE_ENGINE);
-       }
-       return;
-    } else if (strstr(message, "Draw") != NULL ||
-              strstr(message, "game is a draw") != NULL) {
-       GameEnds(GameIsDrawn, "Draw", GE_ENGINE);
-       return;
-    } else if (strstr(message, "offer") != NULL &&
-              strstr(message, "draw") != NULL) {
-#if ZIPPY
-       if (appData.zippyPlay && first.initDone) {
-           /* Relay offer to ICS */
-           SendToICS(ics_prefix);
-           SendToICS("draw\n");
-       }
-#endif
-       cps->offeredDraw = 2; /* valid until this engine moves twice */
-       if (gameMode == TwoMachinesPlay) {
-           if (cps->other->offeredDraw) {
-               GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
-           } else {
-               if (cps->other->sendDrawOffers) {
-                   SendToProgram("draw\n", cps->other);
-               }
-           }
-       } else if (gameMode == MachinePlaysWhite ||
-                  gameMode == MachinePlaysBlack) {
-         if (userOfferedDraw) {
-           DisplayInformation(_("Machine accepts your draw offer"));
-           GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
-         } else {
-            DisplayInformation(_("Machine offers a draw\nSelect Action / Draw to agree"));
-         }
-       }
-    }
-
-
-    /*
-     * Look for thinking output
-     */
-    if (appData.showThinking) {
-       int plylev, mvleft, mvtot, curscore, time;
-       char mvname[MOVE_LEN];
-       u64 nodes;
-       char plyext;
-       int ignore = FALSE;
-       int prefixHint = FALSE;
-       mvname[0] = NULLCHAR;
-
-       switch (gameMode) {
-         case MachinePlaysBlack:
-         case IcsPlayingBlack:
-           if (WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
-           break;
-         case MachinePlaysWhite:
-         case IcsPlayingWhite:
-           if (!WhiteOnMove(forwardMostMove)) prefixHint = TRUE;
-           break;
-         case AnalyzeMode:
-         case AnalyzeFile:
-               break;
-         /* icsEngineAnalyze */
-         case IcsObserving:
-               if (!appData.icsEngineAnalyze) ignore = TRUE;
-           break;
-         case TwoMachinesPlay:
-           if ((cps->twoMachinesColor[0] == 'w') !=
-               WhiteOnMove(forwardMostMove)) {
-               ignore = TRUE;
-           }
-           break;
-         default:
-           ignore = TRUE;
-           break;
-       }
-
-       if (!ignore) {
-           buf1[0] = NULLCHAR;
-           if (sscanf(message, "%d%c %d %d" u64Display "%[^\n]\n",
-                      &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
-
-               if (plyext != ' ' && plyext != '\t') {
-                   time *= 100;
-               }
-               programStats.depth = plylev;
-               programStats.nodes = nodes;
-               programStats.time = time;
-               programStats.score = curscore;
-               programStats.got_only_move = 0;
-
-               /* Buffer overflow protection */
-               if (buf1[0] != NULLCHAR) {
-                   if (strlen(buf1) >= sizeof(programStats.movelist)
-                       && appData.debugMode) {
-                       fprintf(debugFP,
-                               "PV is too long; using the first %d bytes.\n",
-                               sizeof(programStats.movelist) - 1);
-                   }
-                   strncpy(programStats.movelist, buf1,
-                           sizeof(programStats.movelist));
-                   programStats.movelist[sizeof(programStats.movelist) - 1]
-                     = NULLCHAR;
-               } else {
-                   sprintf(programStats.movelist, " no PV\n");
-               }
-
-               if (programStats.seen_stat) {
-                   programStats.ok_to_send = 1;
-               }
-
-               if (strchr(programStats.movelist, '(') != NULL) {
-                   programStats.line_is_book = 1;
-                   programStats.nr_moves = 0;
-                   programStats.moves_left = 0;
-               } else {
-                   programStats.line_is_book = 0;
-               }
-
-               sprintf(thinkOutput, "[%d]%c%+.2f %s%s%s",
-                       plylev,
-                       (gameMode == TwoMachinesPlay ?
-                        ToUpper(cps->twoMachinesColor[0]) : ' '),
-                       ((double) curscore) / 100.0,
-                       prefixHint ? lastHint : "",
-                       prefixHint ? " " : "", programStats.movelist);
-
-               if (currentMove == forwardMostMove || gameMode == AnalyzeMode
-                       || gameMode == AnalyzeFile || appData.icsEngineAnalyze) {
-                   DisplayMove(currentMove - 1);
-                   DisplayAnalysis();
-               }
-               return;
-
-           } else if ((p=StrStr(message, "(only move)")) != NULL) {
-               /* crafty (9.25+) says "(only move) <move>"
-                * if there is only 1 legal move
-                 */
-               sscanf(p, "(only move) %s", buf1);
-               sprintf(thinkOutput, "%s (only move)", buf1);
-               sprintf(programStats.movelist, "%s (only move)", buf1);
-               programStats.depth = 1;
-               programStats.nr_moves = 1;
-               programStats.moves_left = 1;
-               programStats.nodes = 1;
-               programStats.time = 1;
-               programStats.got_only_move = 1;
-
-               /* Not really, but we also use this member to
-                  mean "line isn't going to change" (Crafty
-                  isn't searching, so stats won't change) */
-               programStats.line_is_book = 1;
-
-               if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
-                   gameMode == AnalyzeFile || appData.icsEngineAnalyze) {
-                   DisplayMove(currentMove - 1);
-                   DisplayAnalysis();
-               }
-               return;
-           } else if (sscanf(message,"stat01: %d" u64Display "%d %d %d %s",
-                             &time, &nodes, &plylev, &mvleft,
-                             &mvtot, mvname) >= 5) {
-               /* The stat01: line is from Crafty (9.29+) in response
-                  to the "." command */
-               programStats.seen_stat = 1;
-               cps->maybeThinking = TRUE;
-
-               if (programStats.got_only_move || !appData.periodicUpdates)
-                 return;
-
-               programStats.depth = plylev;
-               programStats.time = time;
-               programStats.nodes = nodes;
-               programStats.moves_left = mvleft;
-               programStats.nr_moves = mvtot;
-               strcpy(programStats.move_name, mvname);
-               programStats.ok_to_send = 1;
-               DisplayAnalysis();
-               return;
-
-           } else if (strncmp(message,"++",2) == 0) {
-               /* Crafty 9.29+ outputs this */
-               programStats.got_fail = 2;
-               return;
-
-           } else if (strncmp(message,"--",2) == 0) {
-               /* Crafty 9.29+ outputs this */
-               programStats.got_fail = 1;
-               return;
-
-           } else if (thinkOutput[0] != NULLCHAR &&
-                      strncmp(message, "    ", 4) == 0) {
-               p = message;
-               while (*p && *p == ' ') p++;
-               strcat(thinkOutput, " ");
-               strcat(thinkOutput, p);
-               strcat(programStats.movelist, " ");
-               strcat(programStats.movelist, p);
-               if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
-                   gameMode == AnalyzeFile || appData.icsEngineAnalyze) {
-                   DisplayMove(currentMove - 1);
-                   DisplayAnalysis();
-               }
-               return;
-           }
-       }
-    }
-}
-
-
-/* Parse a game score from the character string "game", and
-   record it as the history of the current game.  The game
-   score is NOT assumed to start from the standard position.
-   The display is not updated in any way.
-   */
-void
-ParseGameHistory(game)
-     char *game;
-{
-    ChessMove moveType;
-    int fromX, fromY, toX, toY, boardIndex;
-    char promoChar;
-    char *p, *q;
-    char buf[MSG_SIZ];
-
-    if (appData.debugMode)
-      fprintf(debugFP, "Parsing game history: %s\n", game);
-
-    if (gameInfo.event == NULL) gameInfo.event = StrSave("ICS game");
-    gameInfo.site = StrSave(appData.icsHost);
-    gameInfo.date = PGNDate();
-    gameInfo.round = StrSave("-");
-
-    /* Parse out names of players */
-    while (*game == ' ') game++;
-    p = buf;
-    while (*game != ' ') *p++ = *game++;
-    *p = NULLCHAR;
-    gameInfo.white = StrSave(buf);
-    while (*game == ' ') game++;
-    p = buf;
-    while (*game != ' ' && *game != '\n') *p++ = *game++;
-    *p = NULLCHAR;
-    gameInfo.black = StrSave(buf);
-
-    /* Parse moves */
-    boardIndex = blackPlaysFirst ? 1 : 0;
-    yynewstr(game);
-    for (;;) {
-       yyboardindex = boardIndex;
-       moveType = (ChessMove) yylex();
-       switch (moveType) {
-         case WhitePromotionQueen:
-         case BlackPromotionQueen:
-         case WhitePromotionRook:
-         case BlackPromotionRook:
-         case WhitePromotionBishop:
-         case BlackPromotionBishop:
-         case WhitePromotionKnight:
-         case BlackPromotionKnight:
-         case WhitePromotionKing:
-         case BlackPromotionKing:
-         case NormalMove:
-         case WhiteCapturesEnPassant:
-         case BlackCapturesEnPassant:
-         case WhiteKingSideCastle:
-         case WhiteQueenSideCastle:
-         case BlackKingSideCastle:
-         case BlackQueenSideCastle:
-         case WhiteKingSideCastleWild:
-         case WhiteQueenSideCastleWild:
-         case BlackKingSideCastleWild:
-         case BlackQueenSideCastleWild:
-         case IllegalMove:             /* maybe suicide chess, etc. */
-           fromX = currentMoveString[0] - 'a';
-           fromY = currentMoveString[1] - '1';
-           toX = currentMoveString[2] - 'a';
-           toY = currentMoveString[3] - '1';
-           promoChar = currentMoveString[4];
-           break;
-         case WhiteDrop:
-         case BlackDrop:
-           fromX = moveType == WhiteDrop ?
-             (int) CharToPiece(ToUpper(currentMoveString[0])) :
-           (int) CharToPiece(ToLower(currentMoveString[0]));
-           fromY = DROP_RANK;
-           toX = currentMoveString[2] - 'a';
-           toY = currentMoveString[3] - '1';
-           promoChar = NULLCHAR;
-           break;
-         case AmbiguousMove:
-           /* bug? */
-           sprintf(buf, _("Ambiguous move in ICS output: \"%s\""), yy_text);
-           DisplayError(buf, 0);
-           return;
-         case ImpossibleMove:
-           /* bug? */
-           sprintf(buf, _("Illegal move in ICS output: \"%s\""), yy_text);
-           DisplayError(buf, 0);
-           return;
-         case (ChessMove) 0:   /* end of file */
-           if (boardIndex < backwardMostMove) {
-               /* Oops, gap.  How did that happen? */
-               DisplayError(_("Gap in move list"), 0);
-               return;
-           }
-           backwardMostMove =  blackPlaysFirst ? 1 : 0;
-           if (boardIndex > forwardMostMove) {
-               forwardMostMove = boardIndex;
-           }
-           return;
-         case ElapsedTime:
-           if (boardIndex > (blackPlaysFirst ? 1 : 0)) {
-               strcat(parseList[boardIndex-1], " ");
-               strcat(parseList[boardIndex-1], yy_text);
-           }
-           continue;
-         case Comment:
-         case PGNTag:
-         case NAG:
-         default:
-           /* ignore */
-           continue;
-         case WhiteWins:
-         case BlackWins:
-         case GameIsDrawn:
-         case GameUnfinished:
-           if (gameMode == IcsExamining) {
-               if (boardIndex < backwardMostMove) {
-                   /* Oops, gap.  How did that happen? */
-                   return;
-               }
-               backwardMostMove = blackPlaysFirst ? 1 : 0;
-               return;
-           }
-           gameInfo.result = moveType;
-           p = strchr(yy_text, '{');
-           if (p == NULL) p = strchr(yy_text, '(');
-           if (p == NULL) {
-               p = yy_text;
-               if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
-           } else {
-               q = strchr(p, *p == '{' ? '}' : ')');
-               if (q != NULL) *q = NULLCHAR;
-               p++;
-           }
-           gameInfo.resultDetails = StrSave(p);
-           continue;
-       }
-       if (boardIndex >= forwardMostMove &&
-           !(gameMode == IcsObserving && ics_gamenum == -1)) {
-           backwardMostMove = blackPlaysFirst ? 1 : 0;
-           return;
-       }
-       (void) CoordsToAlgebraic(boards[boardIndex], PosFlags(boardIndex),
-                                EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,
-                                parseList[boardIndex]);
-       CopyBoard(boards[boardIndex + 1], boards[boardIndex]);
-       /* currentMoveString is set as a side-effect of yylex */
-       strcpy(moveList[boardIndex], currentMoveString);
-       strcat(moveList[boardIndex], "\n");
-       boardIndex++;
-       ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);
-       switch (MateTest(boards[boardIndex],
-                        PosFlags(boardIndex), EP_UNKNOWN)) {
-         case MT_NONE:
-         case MT_STALEMATE:
-         default:
-           break;
-         case MT_CHECK:
-           strcat(parseList[boardIndex - 1], "+");
-           break;
-         case MT_CHECKMATE:
-           strcat(parseList[boardIndex - 1], "#");
-           break;
-       }
-    }
-}
-
-
-/* Apply a move to the given board  */
-void
-ApplyMove(fromX, fromY, toX, toY, promoChar, board)
-     int fromX, fromY, toX, toY;
-     int promoChar;
-     Board board;
-{
-    ChessSquare captured = board[toY][toX];
-    if (fromY == DROP_RANK) {
-       /* must be first */
-       board[toY][toX] = (ChessSquare) fromX;
-    } else if (fromX == toX && fromY == toY) {
-       return;
-    } else if (fromY == 0 && fromX == 4
-       && board[fromY][fromX] == WhiteKing
-       && toY == 0 && toX == 6) {
-       board[fromY][fromX] = EmptySquare;
-       board[toY][toX] = WhiteKing;
-       board[fromY][7] = EmptySquare;
-       board[toY][5] = WhiteRook;
-    } else if (fromY == 0 && fromX == 4
-              && board[fromY][fromX] == WhiteKing
-              && toY == 0 && toX == 2) {
-       board[fromY][fromX] = EmptySquare;
-       board[toY][toX] = WhiteKing;
-       board[fromY][0] = EmptySquare;
-       board[toY][3] = WhiteRook;
-    } else if (fromY == 0 && fromX == 3
-              && board[fromY][fromX] == WhiteKing
-              && toY == 0 && toX == 5) {
-       board[fromY][fromX] = EmptySquare;
-       board[toY][toX] = WhiteKing;
-       board[fromY][7] = EmptySquare;
-       board[toY][4] = WhiteRook;
-    } else if (fromY == 0 && fromX == 3
-              && board[fromY][fromX] == WhiteKing
-              && toY == 0 && toX == 1) {
-       board[fromY][fromX] = EmptySquare;
-       board[toY][toX] = WhiteKing;
-       board[fromY][0] = EmptySquare;
-       board[toY][2] = WhiteRook;
-    } else if (board[fromY][fromX] == WhitePawn
-              && toY == 7) {
-       /* white pawn promotion */
-       board[7][toX] = CharToPiece(ToUpper(promoChar));
-       if (board[7][toX] == EmptySquare) {
-           board[7][toX] = WhiteQueen;
-       }
-       board[fromY][fromX] = EmptySquare;
-    } else if ((fromY == 4)
-              && (toX != fromX)
-              && (board[fromY][fromX] == WhitePawn)
-              && (board[toY][toX] == EmptySquare)) {
-       board[fromY][fromX] = EmptySquare;
-       board[toY][toX] = WhitePawn;
-       captured = board[toY - 1][toX];
-       board[toY - 1][toX] = EmptySquare;
-    } else if (fromY == 7 && fromX == 4
-              && board[fromY][fromX] == BlackKing
-              && toY == 7 && toX == 6) {
-       board[fromY][fromX] = EmptySquare;
-       board[toY][toX] = BlackKing;
-       board[fromY][7] = EmptySquare;
-       board[toY][5] = BlackRook;
-    } else if (fromY == 7 && fromX == 4
-              && board[fromY][fromX] == BlackKing
-              && toY == 7 && toX == 2) {
-       board[fromY][fromX] = EmptySquare;
-       board[toY][toX] = BlackKing;
-       board[fromY][0] = EmptySquare;
-       board[toY][3] = BlackRook;
-    } else if (fromY == 7 && fromX == 3
-              && board[fromY][fromX] == BlackKing
-              && toY == 7 && toX == 5) {
-       board[fromY][fromX] = EmptySquare;
-       board[toY][toX] = BlackKing;
-       board[fromY][7] = EmptySquare;
-       board[toY][4] = BlackRook;
-    } else if (fromY == 7 && fromX == 3
-              && board[fromY][fromX] == BlackKing
-              && toY == 7 && toX == 1) {
-       board[fromY][fromX] = EmptySquare;
-       board[toY][toX] = BlackKing;
-       board[fromY][0] = EmptySquare;
-       board[toY][2] = BlackRook;
-    } else if (board[fromY][fromX] == BlackPawn
-              && toY == 0) {
-       /* black pawn promotion */
-       board[0][toX] = CharToPiece(ToLower(promoChar));
-       if (board[0][toX] == EmptySquare) {
-           board[0][toX] = BlackQueen;
-       }
-       board[fromY][fromX] = EmptySquare;
-    } else if ((fromY == 3)
-              && (toX != fromX)
-              && (board[fromY][fromX] == BlackPawn)
-              && (board[toY][toX] == EmptySquare)) {
-       board[fromY][fromX] = EmptySquare;
-       board[toY][toX] = BlackPawn;
-       captured = board[toY + 1][toX];
-       board[toY + 1][toX] = EmptySquare;
-    } else {
-       board[toY][toX] = board[fromY][fromX];
-       board[fromY][fromX] = EmptySquare;
-    }
-    if (gameInfo.variant == VariantCrazyhouse) {
-#if 0
-      /* !!A lot more code needs to be written to support holdings */
-      if (fromY == DROP_RANK) {
-       /* Delete from holdings */
-       if (holdings[(int) fromX] > 0) holdings[(int) fromX]--;
-      }
-      if (captured != EmptySquare) {
-       /* Add to holdings */
-       if (captured < BlackPawn) {
-         holdings[(int)captured - (int)BlackPawn + (int)WhitePawn]++;
-       } else {
-         holdings[(int)captured - (int)WhitePawn + (int)BlackPawn]++;
-       }
-      }
-#endif
-    } else if (gameInfo.variant == VariantAtomic) {
-      if (captured != EmptySquare) {
-       int y, x;
-       for (y = toY-1; y <= toY+1; y++) {
-         for (x = toX-1; x <= toX+1; x++) {
-           if (y >= 0 && y <= 7 && x >= 0 && x <= 7 &&
-               board[y][x] != WhitePawn && board[y][x] != BlackPawn) {
-             board[y][x] = EmptySquare;
-           }
-         }
-       }
-       board[toY][toX] = EmptySquare;
-      }
-    }
-}
-
-/* Updates forwardMostMove */
-void
-MakeMove(fromX, fromY, toX, toY, promoChar)
-     int fromX, fromY, toX, toY;
-     int promoChar;
-{
-    forwardMostMove++;
-    if (forwardMostMove >= MAX_MOVES) {
-      DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),
-                       0, 1);
-      return;
-    }
-    SwitchClocks();
-    timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
-    timeRemaining[1][forwardMostMove] = blackTimeRemaining;
-    if (commentList[forwardMostMove] != NULL) {
-       free(commentList[forwardMostMove]);
-       commentList[forwardMostMove] = NULL;
-    }
-    CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);
-    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]);
-    gameInfo.result = GameUnfinished;
-    if (gameInfo.resultDetails != NULL) {
-       free(gameInfo.resultDetails);
-       gameInfo.resultDetails = NULL;
-    }
-    CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,
-                             moveList[forwardMostMove - 1]);
-    (void) CoordsToAlgebraic(boards[forwardMostMove - 1],
-                            PosFlags(forwardMostMove - 1), EP_UNKNOWN,
-                            fromY, fromX, toY, toX, promoChar,
-                            parseList[forwardMostMove - 1]);
-    switch (MateTest(boards[forwardMostMove],
-                    PosFlags(forwardMostMove), EP_UNKNOWN)){
-      case MT_NONE:
-      case MT_STALEMATE:
-      default:
-       break;
-      case MT_CHECK:
-       strcat(parseList[forwardMostMove - 1], "+");
-       break;
-      case MT_CHECKMATE:
-       strcat(parseList[forwardMostMove - 1], "#");
-       break;
-    }
-}
-
-/* Updates currentMove if not pausing */
-void
-ShowMove(fromX, fromY, toX, toY)
-{
-    int instant = (gameMode == PlayFromGameFile) ?
-       (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;
-    if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
-       if (!instant) {
-           if (forwardMostMove == currentMove + 1) {
-               AnimateMove(boards[forwardMostMove - 1],
-                           fromX, fromY, toX, toY);
-           }
-           if (appData.highlightLastMove) {
-               SetHighlights(fromX, fromY, toX, toY);
-           }
-       }
-       currentMove = forwardMostMove;
-    }
-
-    if (instant) return;
-    DisplayMove(currentMove - 1);
-    DrawPosition(FALSE, boards[currentMove]);
-    DisplayBothClocks();
-    HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
-}
-
-
-void
-InitChessProgram(cps)
-     ChessProgramState *cps;
-{
-    char buf[MSG_SIZ];
-    if (appData.noChessProgram) return;
-    hintRequested = FALSE;
-    bookRequested = FALSE;
-    SendToProgram(cps->initString, cps);
-    if (gameInfo.variant != VariantNormal &&
-       gameInfo.variant != VariantLoadable) {
-      char *v = VariantName(gameInfo.variant);
-      if (StrStr(cps->variants, v) == NULL) {
-       sprintf(buf, _("Variant %s not supported by %s"), v, cps->tidy);
-       DisplayFatalError(buf, 0, 1);
-       return;
-      }
-      sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));
-      SendToProgram(buf, cps);
-    }
-    if (cps->sendICS) {
-      sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");
-      SendToProgram(buf, cps);
-    }
-    cps->maybeThinking = FALSE;
-    cps->offeredDraw = 0;
-    if (!appData.icsActive) {
-       SendTimeControl(cps, movesPerSession, timeControl,
-                       timeIncrement, appData.searchDepth,
-                       searchTime);
-    }
-    if (appData.showThinking) {
-       SendToProgram("post\n", cps);
-    }
-    SendToProgram("hard\n", cps);
-    if (!appData.ponderNextMove) {
-       /* Warning: "easy" is a toggle in GNU Chess, so don't send
-          it without being sure what state we are in first.  "hard"
-          is not a toggle, so that one is OK.
-        */
-       SendToProgram("easy\n", cps);
-    }
-    if (cps->usePing) {
-      sprintf(buf, "ping %d\n", ++cps->lastPing);
-      SendToProgram(buf, cps);
-    }
-    cps->initDone = TRUE;
-}
-
-
-void
-StartChessProgram(cps)
-     ChessProgramState *cps;
-{
-    char buf[MSG_SIZ];
-    int err;
-
-    if (appData.noChessProgram) return;
-    cps->initDone = FALSE;
-
-    if (strcmp(cps->host, "localhost") == 0) {
-       err = StartChildProcess(cps->program, cps->dir, &cps->pr);
-    } else if (*appData.remoteShell == NULLCHAR) {
-       err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);
-    } else {
-       if (*appData.remoteUser == NULLCHAR) {
-           sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,
-                   cps->program);
-       } else {
-           sprintf(buf, "%s %s -l %s %s", appData.remoteShell,
-                   cps->host, appData.remoteUser, cps->program);
-       }
-       err = StartChildProcess(buf, "", &cps->pr);
-    }
-
-    if (err != 0) {
-       sprintf(buf, "Startup failure on '%s'", cps->program);
-       DisplayFatalError(buf, err, 1);
-       cps->pr = NoProc;
-       cps->isr = NULL;
-       return;
-    }
-
-    cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);
-    if (cps->protocolVersion > 1) {
-      sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);
-      SendToProgram(buf, cps);
-    } else {
-      SendToProgram("xboard\n", cps);
-    }
-}
-
-
-void
-TwoMachinesEventIfReady P((void))
-{
-  if (first.lastPing != first.lastPong) {
-    DisplayMessage("", "Waiting for first chess program");
-    ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
-    return;
-  }
-  if (second.lastPing != second.lastPong) {
-    DisplayMessage("", "Waiting for second chess program");
-    ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);
-    return;
-  }
-  ThawUI();
-  TwoMachinesEvent();
-}
-
-void
-NextMatchGame P((void))
-{
-    Reset(FALSE, TRUE);
-    if (*appData.loadGameFile != NULLCHAR) {
-       LoadGameFromFile(appData.loadGameFile,
-                        appData.loadGameIndex,
-                        appData.loadGameFile, FALSE);
-    } else if (*appData.loadPositionFile != NULLCHAR) {
-       LoadPositionFromFile(appData.loadPositionFile,
-                            appData.loadPositionIndex,
-                            appData.loadPositionFile);
-    }
-    TwoMachinesEventIfReady();
-}
-
-void
-GameEnds(result, resultDetails, whosays)
-     ChessMove result;
-     char *resultDetails;
-     int whosays;
-{
-    GameMode nextGameMode;
-    int isIcsGame;
-
-    if (appData.debugMode) {
-      fprintf(debugFP, "GameEnds(%d, %s, %d)\n",
-             result, resultDetails ? resultDetails : "(null)", whosays);
-    }
-
-    if (appData.icsActive && whosays == GE_ENGINE) {
-       /* If we are playing on ICS, the server decides when the
-          game is over, but the engine can offer to draw, claim
-          a draw, or resign.
-        */
-#if ZIPPY
-       if (appData.zippyPlay && first.initDone) {
-           if (result == GameIsDrawn) {
-               /* In case draw still needs to be claimed */
-               SendToICS(ics_prefix);
-               SendToICS("draw\n");
-           } else if (StrCaseStr(resultDetails, "resign")) {
-               SendToICS(ics_prefix);
-               SendToICS("resign\n");
-           }
-        }
-#endif
-       return;
-    }
-
-    /* If we're loading the game from a file, stop */
-    if (whosays == GE_FILE) {
-      (void) StopLoadGameTimer();
-      gameFileFP = NULL;
-    }
-
-    /* Cancel draw offers */
-   first.offeredDraw = second.offeredDraw = 0;
-
-    /* If this is an ICS game, only ICS can really say it's done;
-       if not, anyone can. */
-    isIcsGame = (gameMode == IcsPlayingWhite ||
-                gameMode == IcsPlayingBlack ||
-                gameMode == IcsObserving    ||
-                gameMode == IcsExamining);
-
-    if (!isIcsGame || whosays == GE_ICS) {
-       /* OK -- not an ICS game, or ICS said it was done */
-       StopClocks();
-       if (!isIcsGame && !appData.noChessProgram)
-         SetUserThinkingEnables();
-
-       if (resultDetails != NULL) {
-           gameInfo.result = result;
-           gameInfo.resultDetails = StrSave(resultDetails);
-
-           /* Tell program how game ended in case it is learning */
-           if (gameMode == MachinePlaysWhite ||
-               gameMode == MachinePlaysBlack ||
-               gameMode == TwoMachinesPlay ||
-               gameMode == IcsPlayingWhite ||
-               gameMode == IcsPlayingBlack ||
-               gameMode == BeginningOfGame) {
-               char buf[MSG_SIZ];
-               sprintf(buf, "result %s {%s}\n", PGNResult(result),
-                       resultDetails);
-               if (first.pr != NoProc) {
-                   SendToProgram(buf, &first);
-               }
-               if (second.pr != NoProc &&
-                   gameMode == TwoMachinesPlay) {
-                   SendToProgram(buf, &second);
-               }
-           }
-
-           /* display last move only if game was not loaded from file */
-           if ((whosays != GE_FILE) && (currentMove == forwardMostMove))
-               DisplayMove(currentMove - 1);
-
-           if (forwardMostMove != 0) {
-               if (gameMode != PlayFromGameFile && gameMode != EditGame) {
-                   if (*appData.saveGameFile != NULLCHAR) {
-                       SaveGameToFile(appData.saveGameFile, TRUE);
-                   } else if (appData.autoSaveGames) {
-                       AutoSaveGame();
-                   }
-                   if (*appData.savePositionFile != NULLCHAR) {
-                       SavePositionToFile(appData.savePositionFile);
-                   }
-               }
-           }
-       }
-
-       if (appData.icsActive) {
-           if (appData.quietPlay &&
-               (gameMode == IcsPlayingWhite ||
-                gameMode == IcsPlayingBlack)) {
-               SendToICS(ics_prefix);
-               SendToICS("set shout 1\n");
-           }
-           nextGameMode = IcsIdle;
-           ics_user_moved = FALSE;
-           /* clean up premove.  It's ugly when the game has ended and the
-            * premove highlights are still on the board.
-            */
-           if (gotPremove) {
-             gotPremove = FALSE;
-             ClearPremoveHighlights();
-             DrawPosition(FALSE, boards[currentMove]);
-           }
-           if (whosays == GE_ICS) {
-               switch (result) {
-               case WhiteWins:
-                   if (gameMode == IcsPlayingWhite)
-                       PlayIcsWinSound();
-                   else if(gameMode == IcsPlayingBlack)
-                       PlayIcsLossSound();
-                   break;
-               case BlackWins:
-                   if (gameMode == IcsPlayingBlack)
-                       PlayIcsWinSound();
-                   else if(gameMode == IcsPlayingWhite)
-                       PlayIcsLossSound();
-                   break;
-               case GameIsDrawn:
-                   PlayIcsDrawSound();
-                   break;
-               default:
-                   PlayIcsUnfinishedSound();
-               }
-           }
-       } else if (gameMode == EditGame ||
-                  gameMode == PlayFromGameFile ||
-                  gameMode == AnalyzeMode ||
-                  gameMode == AnalyzeFile) {
-           nextGameMode = gameMode;
-       } else {
-           nextGameMode = EndOfGame;
-       }
-       pausing = FALSE;
-       ModeHighlight();
-    } else {
-       nextGameMode = gameMode;
-    }
-
-    if (appData.noChessProgram) {
-       gameMode = nextGameMode;
-       ModeHighlight();
-       return;
-    }
-
-    if (first.reuse) {
-       /* Put first chess program into idle state */
-       if (first.pr != NoProc &&
-           (gameMode == MachinePlaysWhite ||
-            gameMode == MachinePlaysBlack ||
-            gameMode == TwoMachinesPlay ||
-            gameMode == IcsPlayingWhite ||
-            gameMode == IcsPlayingBlack ||
-            gameMode == BeginningOfGame)) {
-           SendToProgram("force\n", &first);
-           if (first.usePing) {
-             char buf[MSG_SIZ];
-             sprintf(buf, "ping %d\n", ++first.lastPing);
-             SendToProgram(buf, &first);
-           }
-       }
-    } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
-       /* Kill off first chess program */
-       if (first.isr != NULL)
-         RemoveInputSource(first.isr);
-       first.isr = NULL;
-
-       if (first.pr != NoProc) {
-           ExitAnalyzeMode();
-           SendToProgram("quit\n", &first);
-           DestroyChildProcess(first.pr, first.useSigterm);
-       }
-       first.pr = NoProc;
-    }
-    if (second.reuse) {
-       /* Put second chess program into idle state */
-       if (second.pr != NoProc &&
-           gameMode == TwoMachinesPlay) {
-           SendToProgram("force\n", &second);
-           if (second.usePing) {
-             char buf[MSG_SIZ];
-             sprintf(buf, "ping %d\n", ++second.lastPing);
-             SendToProgram(buf, &second);
-           }
-       }
-    } else if (result != GameUnfinished || nextGameMode == IcsIdle) {
-       /* Kill off second chess program */
-       if (second.isr != NULL)
-         RemoveInputSource(second.isr);
-       second.isr = NULL;
-
-       if (second.pr != NoProc) {
-           SendToProgram("quit\n", &second);
-           DestroyChildProcess(second.pr, second.useSigterm);
-       }
-       second.pr = NoProc;
-    }
-
-    if (matchMode && gameMode == TwoMachinesPlay) {
-        switch (result) {
-       case WhiteWins:
-         if (first.twoMachinesColor[0] == 'w') {
-           first.matchWins++;
-         } else {
-           second.matchWins++;
-         }
-         break;
-       case BlackWins:
-         if (first.twoMachinesColor[0] == 'b') {
-           first.matchWins++;
-         } else {
-           second.matchWins++;
-         }
-         break;
-       default:
-         break;
-       }
-       if (matchGame < appData.matchGames) {
-           char *tmp;
-           tmp = first.twoMachinesColor;
-           first.twoMachinesColor = second.twoMachinesColor;
-           second.twoMachinesColor = tmp;
-           gameMode = nextGameMode;
-           matchGame++;
-           ScheduleDelayedEvent(NextMatchGame, 10000);
-           return;
-       } else {
-           char buf[MSG_SIZ];
-           gameMode = nextGameMode;
-           sprintf(buf, _("Match %s vs. %s: final score %d-%d-%d"),
-                   first.tidy, second.tidy,
-                   first.matchWins, second.matchWins,
-                   appData.matchGames - (first.matchWins + second.matchWins));
-           DisplayFatalError(buf, 0, 0);
-       }
-    }
-    if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&
-       !(nextGameMode == AnalyzeMode || nextGameMode == AnalyzeFile))
-      ExitAnalyzeMode();
-    gameMode = nextGameMode;
-    ModeHighlight();
-}
-
-/* Assumes program was just initialized (initString sent).
-   Leaves program in force mode. */
-void
-FeedMovesToProgram(cps, upto)
-     ChessProgramState *cps;
-     int upto;
-{
-    int i;
-
-    if (appData.debugMode)
-      fprintf(debugFP, "Feeding %smoves %d through %d to %s chess program\n",
-             startedFromSetupPosition ? "position and " : "",
-             backwardMostMove, upto, cps->which);
-    SendToProgram("force\n", cps);
-    if (startedFromSetupPosition) {
-       SendBoard(cps, backwardMostMove);
-    }
-    for (i = backwardMostMove; i < upto; i++) {
-       SendMoveToProgram(i, cps);
-    }
-}
-
-
-void
-ResurrectChessProgram()
-{
-     /* The chess program may have exited.
-       If so, restart it and feed it all the moves made so far. */
-
-    if (appData.noChessProgram || first.pr != NoProc) return;
-
-    StartChessProgram(&first);
-    InitChessProgram(&first);
-    FeedMovesToProgram(&first, currentMove);
-
-    if (!first.sendTime) {
-       /* can't tell gnuchess what its clock should read,
-          so we bow to its notion. */
-       ResetClocks();
-       timeRemaining[0][currentMove] = whiteTimeRemaining;
-       timeRemaining[1][currentMove] = blackTimeRemaining;
-    }
-
-    if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile ||
-                appData.icsEngineAnalyze) && first.analysisSupport) {
-      SendToProgram("analyze\n", &first);
-      first.analyzing = TRUE;
-    }
-}
-
-/*
- * Button procedures
- */
-void
-Reset(redraw, init)
-     int redraw, init;
-{
-    int i;
-
-    if (appData.debugMode) {
-       fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",
-               redraw, init, gameMode);
-    }
-
-    pausing = pauseExamInvalid = FALSE;
-    startedFromSetupPosition = blackPlaysFirst = FALSE;
-    firstMove = TRUE;
-    whiteFlag = blackFlag = FALSE;
-    userOfferedDraw = FALSE;
-    hintRequested = bookRequested = FALSE;
-    first.maybeThinking = FALSE;
-    second.maybeThinking = FALSE;
-    thinkOutput[0] = NULLCHAR;
-    lastHint[0] = NULLCHAR;
-    ClearGameInfo(&gameInfo);
-    gameInfo.variant = StringToVariant(appData.variant);
-    ics_user_moved = ics_clock_paused = FALSE;
-    ics_getting_history = H_FALSE;
-    ics_gamenum = -1;
-    white_holding[0] = black_holding[0] = NULLCHAR;
-    ClearProgramStats();
-
-    ResetFrontEnd();
-    ClearHighlights();
-    flipView = appData.flipView;
-    ClearPremoveHighlights();
-    gotPremove = FALSE;
-    alarmSounded = FALSE;
-
-    GameEnds((ChessMove) 0, NULL, GE_PLAYER);
-    ExitAnalyzeMode();
-    gameMode = BeginningOfGame;
-    ModeHighlight();
-    InitPosition(redraw);
-    for (i = 0; i < MAX_MOVES; i++) {
-       if (commentList[i] != NULL) {
-           free(commentList[i]);
-           commentList[i] = NULL;
-       }
-    }
-    ResetClocks();
-    timeRemaining[0][0] = whiteTimeRemaining;
-    timeRemaining[1][0] = blackTimeRemaining;
-    if (first.pr == NULL) {
-       StartChessProgram(&first);
-    }
-    if (init) InitChessProgram(&first);
-    DisplayTitle("");
-    DisplayMessage("", "");
-    HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
-}
-
-void
-AutoPlayGameLoop()
-{
-    for (;;) {
-       if (!AutoPlayOneMove())
-         return;
-       if (matchMode || appData.timeDelay == 0)
-         continue;
-       if (appData.timeDelay < 0 || gameMode == AnalyzeFile)
-         return;
-       StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
-       break;
-    }
-}
-
-
-int
-AutoPlayOneMove()
-{
-    int fromX, fromY, toX, toY;
-
-    if (appData.debugMode) {
-      fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove);
-    }
-
-    if (gameMode != PlayFromGameFile)
-      return FALSE;
-
-    if (currentMove >= forwardMostMove) {
-      gameMode = EditGame;
-      ModeHighlight();
-      return FALSE;
-    }
-
-    toX = moveList[currentMove][2] - 'a';
-    toY = moveList[currentMove][3] - '1';
-
-    if (moveList[currentMove][1] == '@') {
-       if (appData.highlightLastMove) {
-           SetHighlights(-1, -1, toX, toY);
-       }
-    } else {
-       fromX = moveList[currentMove][0] - 'a';
-       fromY = moveList[currentMove][1] - '1';
-       AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
-
-       if (appData.highlightLastMove) {
-           SetHighlights(fromX, fromY, toX, toY);
-       }
-    }
-    DisplayMove(currentMove);
-    SendMoveToProgram(currentMove++, &first);
-    DisplayBothClocks();
-    DrawPosition(FALSE, boards[currentMove]);
-    if (commentList[currentMove] != NULL) {
-       DisplayComment(currentMove - 1, commentList[currentMove]);
-    }
-    return TRUE;
-}
-
-
-int
-LoadGameOneMove(readAhead)
-     ChessMove readAhead;
-{
-    int fromX = 0, fromY = 0, toX = 0, toY = 0, done;
-    char promoChar = NULLCHAR;
-    ChessMove moveType;
-    char move[MSG_SIZ];
-    char *p, *q;
-
-    if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile &&
-       gameMode != AnalyzeMode && gameMode != Training) {
-       gameFileFP = NULL;
-       return FALSE;
-    }
-
-    yyboardindex = forwardMostMove;
-    if (readAhead != (ChessMove)0) {
-      moveType = readAhead;
-    } else {
-      if (gameFileFP == NULL)
-         return FALSE;
-      moveType = (ChessMove) yylex();
-    }
-
-    done = FALSE;
-    switch (moveType) {
-      case Comment:
-       if (appData.debugMode)
-         fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
-       p = yy_text;
-       if (*p == '{' || *p == '[' || *p == '(') {
-           p[strlen(p) - 1] = NULLCHAR;
-           p++;
-       }
-
-       /* append the comment but don't display it */
-       while (*p == '\n') p++;
-       AppendComment(currentMove, p);
-       return TRUE;
-
-      case WhiteCapturesEnPassant:
-      case BlackCapturesEnPassant:
-      case WhitePromotionQueen:
-      case BlackPromotionQueen:
-      case WhitePromotionRook:
-      case BlackPromotionRook:
-      case WhitePromotionBishop:
-      case BlackPromotionBishop:
-      case WhitePromotionKnight:
-      case BlackPromotionKnight:
-      case WhitePromotionKing:
-      case BlackPromotionKing:
-      case NormalMove:
-      case WhiteKingSideCastle:
-      case WhiteQueenSideCastle:
-      case BlackKingSideCastle:
-      case BlackQueenSideCastle:
-      case WhiteKingSideCastleWild:
-      case WhiteQueenSideCastleWild:
-      case BlackKingSideCastleWild:
-      case BlackQueenSideCastleWild:
-       if (appData.debugMode)
-         fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
-       fromX = currentMoveString[0] - 'a';
-       fromY = currentMoveString[1] - '1';
-       toX = currentMoveString[2] - 'a';
-       toY = currentMoveString[3] - '1';
-       promoChar = currentMoveString[4];
-       break;
-
-      case WhiteDrop:
-      case BlackDrop:
-       if (appData.debugMode)
-         fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
-       fromX = moveType == WhiteDrop ?
-         (int) CharToPiece(ToUpper(currentMoveString[0])) :
-       (int) CharToPiece(ToLower(currentMoveString[0]));
-       fromY = DROP_RANK;
-       toX = currentMoveString[2] - 'a';
-       toY = currentMoveString[3] - '1';
-       break;
-
-      case WhiteWins:
-      case BlackWins:
-      case GameIsDrawn:
-      case GameUnfinished:
-       if (appData.debugMode)
-         fprintf(debugFP, "Parsed game end: %s\n", yy_text);
-       p = strchr(yy_text, '{');
-       if (p == NULL) p = strchr(yy_text, '(');
-       if (p == NULL) {
-           p = yy_text;
-           if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";
-       } else {
-           q = strchr(p, *p == '{' ? '}' : ')');
-           if (q != NULL) *q = NULLCHAR;
-           p++;
-       }
-       GameEnds(moveType, p, GE_FILE);
-       done = TRUE;
-       if (cmailMsgLoaded) {
-           ClearHighlights();
-           flipView = WhiteOnMove(currentMove);
-           if (moveType == GameUnfinished) flipView = !flipView;
-           if (appData.debugMode)
-             fprintf(debugFP, "Setting flipView to %d\n", flipView) ;
-       }
-       break;
-
-      case (ChessMove) 0:      /* end of file */
-       if (appData.debugMode)
-         fprintf(debugFP, "Parser hit end of file\n");
-       switch (MateTest(boards[currentMove], PosFlags(currentMove),
-                        EP_UNKNOWN)) {
-         case MT_NONE:
-         case MT_CHECK:
-           break;
-         case MT_CHECKMATE:
-           if (WhiteOnMove(currentMove)) {
-               GameEnds(BlackWins, "Black mates", GE_FILE);
-           } else {
-               GameEnds(WhiteWins, "White mates", GE_FILE);
-           }
-           break;
-         case MT_STALEMATE:
-           GameEnds(GameIsDrawn, "Stalemate", GE_FILE);
-           break;
-       }
-       done = TRUE;
-       break;
-
-      case MoveNumberOne:
-       if (lastLoadGameStart == GNUChessGame) {
-           /* GNUChessGames have numbers, but they aren't move numbers */
-           if (appData.debugMode)
-             fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
-                     yy_text, (int) moveType);
-           return LoadGameOneMove((ChessMove)0); /* tail recursion */
-       }
-       /* else fall thru */
-
-      case XBoardGame:
-      case GNUChessGame:
-      case PGNTag:
-       /* Reached start of next game in file */
-       if (appData.debugMode)
-         fprintf(debugFP, "Parsed start of next game: %s\n", yy_text);
-       switch (MateTest(boards[currentMove], PosFlags(currentMove),
-                        EP_UNKNOWN)) {
-         case MT_NONE:
-         case MT_CHECK:
-           break;
-         case MT_CHECKMATE:
-           if (WhiteOnMove(currentMove)) {
-               GameEnds(BlackWins, "Black mates", GE_FILE);
-           } else {
-               GameEnds(WhiteWins, "White mates", GE_FILE);
-           }
-           break;
-         case MT_STALEMATE:
-           GameEnds(GameIsDrawn, "Stalemate", GE_FILE);
-           break;
-       }
-       done = TRUE;
-       break;
-
-      case PositionDiagram:    /* should not happen; ignore */
-      case ElapsedTime:                /* ignore */
-      case NAG:                 /* ignore */
-       if (appData.debugMode)
-         fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
-                 yy_text, (int) moveType);
-       return LoadGameOneMove((ChessMove)0); /* tail recursion */
-
-      case IllegalMove:
-       if (appData.testLegality) {
-           if (appData.debugMode)
-             fprintf(debugFP, "Parsed IllegalMove: %s\n", yy_text);
-           sprintf(move, _("Illegal move: %d.%s%s"),
-                   (forwardMostMove / 2) + 1,
-                   WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
-           DisplayError(move, 0);
-           done = TRUE;
-       } else {
-           if (appData.debugMode)
-             fprintf(debugFP, "Parsed %s into IllegalMove %s\n",
-                     yy_text, currentMoveString);
-           fromX = currentMoveString[0] - 'a';
-           fromY = currentMoveString[1] - '1';
-           toX = currentMoveString[2] - 'a';
-           toY = currentMoveString[3] - '1';
-           promoChar = currentMoveString[4];
-       }
-       break;
-
-      case AmbiguousMove:
-       if (appData.debugMode)
-         fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yy_text);
-       sprintf(move, _("Ambiguous move: %d.%s%s"),
-               (forwardMostMove / 2) + 1,
-               WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
-       DisplayError(move, 0);
-       done = TRUE;
-       break;
-
-      default:
-      case ImpossibleMove:
-       if (appData.debugMode)
-         fprintf(debugFP, "Parsed ImpossibleMove: %s\n", yy_text);
-       sprintf(move, _("Illegal move: %d.%s%s"),
-               (forwardMostMove / 2) + 1,
-               WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);
-       DisplayError(move, 0);
-       done = TRUE;
-       break;
-    }
-
-    if (done) {
-       if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {
-           DrawPosition(FALSE, boards[currentMove]);
-           DisplayBothClocks();
-           if (!appData.matchMode && commentList[currentMove] != NULL)
-             DisplayComment(currentMove - 1, commentList[currentMove]);
-       }
-       (void) StopLoadGameTimer();
-       gameFileFP = NULL;
-       cmailOldMove = forwardMostMove;
-       return FALSE;
-    } else {
-       /* currentMoveString is set as a side-effect of yylex */
-       strcat(currentMoveString, "\n");
-       strcpy(moveList[forwardMostMove], currentMoveString);
-
-       thinkOutput[0] = NULLCHAR;
-       MakeMove(fromX, fromY, toX, toY, promoChar);
-       currentMove = forwardMostMove;
-       return TRUE;
-    }
-}
-
-/* Load the nth game from the given file */
-int
-LoadGameFromFile(filename, n, title, useList)
-     char *filename;
-     int n;
-     char *title;
-     /*Boolean*/ int useList;
-{
-    FILE *f;
-    char buf[MSG_SIZ];
-
-    if (strcmp(filename, "-") == 0) {
-       f = stdin;
-       title = "stdin";
-    } else {
-       f = fopen(filename, "rb");
-       if (f == NULL) {
-           sprintf(buf, _("Can't open \"%s\""), filename);
-           DisplayError(buf, errno);
-           return FALSE;
-       }
-    }
-    if (fseek(f, 0, 0) == -1) {
-       /* f is not seekable; probably a pipe */
-       useList = FALSE;
-    }
-    if (useList && n == 0) {
-       int error = GameListBuild(f);
-       if (error) {
-           DisplayError(_("Cannot build game list"), error);
-       } else if (!ListEmpty(&gameList) &&
-                  ((ListGame *) gameList.tailPred)->number > 1) {
-           GameListPopUp(f, title);
-           return TRUE;
-       }
-       GameListDestroy();
-       n = 1;
-    }
-    if (n == 0) n = 1;
-    return LoadGame(f, n, title, FALSE);
-}
-
-
-void
-MakeRegisteredMove()
-{
-    int fromX, fromY, toX, toY;
-    char promoChar;
-    if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
-       switch (cmailMoveType[lastLoadGameNumber - 1]) {
-         case CMAIL_MOVE:
-         case CMAIL_DRAW:
-           if (appData.debugMode)
-             fprintf(debugFP, "Restoring %s for game %d\n",
-                     cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
-
-           thinkOutput[0] = NULLCHAR;
-           strcpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1]);
-           fromX = cmailMove[lastLoadGameNumber - 1][0] - 'a';
-           fromY = cmailMove[lastLoadGameNumber - 1][1] - '1';
-           toX = cmailMove[lastLoadGameNumber - 1][2] - 'a';
-           toY = cmailMove[lastLoadGameNumber - 1][3] - '1';
-           promoChar = cmailMove[lastLoadGameNumber - 1][4];
-           MakeMove(fromX, fromY, toX, toY, promoChar);
-           ShowMove(fromX, fromY, toX, toY);
-
-           switch (MateTest(boards[currentMove], PosFlags(currentMove),
-                            EP_UNKNOWN)) {
-             case MT_NONE:
-             case MT_CHECK:
-               break;
-
-             case MT_CHECKMATE:
-               if (WhiteOnMove(currentMove)) {
-                   GameEnds(BlackWins, "Black mates", GE_PLAYER);
-               } else {
-                   GameEnds(WhiteWins, "White mates", GE_PLAYER);
-               }
-               break;
-
-             case MT_STALEMATE:
-               GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);
-               break;
-           }
-
-           break;
-
-         case CMAIL_RESIGN:
-           if (WhiteOnMove(currentMove)) {
-               GameEnds(BlackWins, "White resigns", GE_PLAYER);
-           } else {
-               GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
-           }
-           break;
-
-         case CMAIL_ACCEPT:
-           GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
-           break;
-
-         default:
-           break;
-       }
-    }
-
-    return;
-}
-
-/* Wrapper around LoadGame for use when a Cmail message is loaded */
-int
-CmailLoadGame(f, gameNumber, title, useList)
-     FILE *f;
-     int gameNumber;
-     char *title;
-     int useList;
-{
-    int retVal;
-
-    if (gameNumber > nCmailGames) {
-       DisplayError(_("No more games in this message"), 0);
-       return FALSE;
-    }
-    if (f == lastLoadGameFP) {
-       int offset = gameNumber - lastLoadGameNumber;
-       if (offset == 0) {
-           cmailMsg[0] = NULLCHAR;
-           if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
-               cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
-               nCmailMovesRegistered--;
-           }
-           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
-           if (cmailResult[lastLoadGameNumber - 1] == CMAIL_NEW_RESULT) {
-               cmailResult[lastLoadGameNumber - 1] = CMAIL_NOT_RESULT;
-           }
-       } else {
-           if (! RegisterMove()) return FALSE;
-       }
-    }
-
-    retVal = LoadGame(f, gameNumber, title, useList);
-
-    /* Make move registered during previous look at this game, if any */
-    MakeRegisteredMove();
-
-    if (cmailCommentList[lastLoadGameNumber - 1] != NULL) {
-       commentList[currentMove]
-         = StrSave(cmailCommentList[lastLoadGameNumber - 1]);
-       DisplayComment(currentMove - 1, commentList[currentMove]);
-    }
-
-    return retVal;
-}
-
-/* Support for LoadNextGame, LoadPreviousGame, ReloadSameGame */
-int
-ReloadGame(offset)
-     int offset;
-{
-    int gameNumber = lastLoadGameNumber + offset;
-    if (lastLoadGameFP == NULL) {
-       DisplayError(_("No game has been loaded yet"), 0);
-       return FALSE;
-    }
-    if (gameNumber <= 0) {
-       DisplayError(_("Can't back up any further"), 0);
-       return FALSE;
-    }
-    if (cmailMsgLoaded) {
-       return CmailLoadGame(lastLoadGameFP, gameNumber,
-                            lastLoadGameTitle, lastLoadGameUseList);
-    } else {
-       return LoadGame(lastLoadGameFP, gameNumber,
-                       lastLoadGameTitle, lastLoadGameUseList);
-    }
-}
-
-
-
-/* Load the nth game from open file f */
-int
-LoadGame(f, gameNumber, title, useList)
-     FILE *f;
-     int gameNumber;
-     char *title;
-     int useList;
-{
-    ChessMove cm;
-    char buf[MSG_SIZ];
-    int gn = gameNumber;
-    ListGame *lg = NULL;
-    int numPGNTags = 0;
-    int err;
-    GameMode oldGameMode;
-
-    if (appData.debugMode)
-       fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);
-
-    if (gameMode == Training )
-       SetTrainingModeOff();
-
-    oldGameMode = gameMode;
-    if (gameMode != BeginningOfGame) {
-      Reset(FALSE, TRUE);
-    }
-
-    gameFileFP = f;
-    if (lastLoadGameFP != NULL && lastLoadGameFP != f) {
-       fclose(lastLoadGameFP);
-    }
-
-    if (useList) {
-       lg = (ListGame *) ListElem(&gameList, gameNumber-1);
-
-       if (lg) {
-           fseek(f, lg->offset, 0);
-           GameListHighlight(gameNumber);
-           gn = 1;
-       }
-       else {
-           DisplayError(_("Game number out of range"), 0);
-           return FALSE;
-       }
-    } else {
-       GameListDestroy();
-       if (fseek(f, 0, 0) == -1) {
-           if (f == lastLoadGameFP ?
-               gameNumber == lastLoadGameNumber + 1 :
-               gameNumber == 1) {
-               gn = 1;
-           } else {
-               DisplayError(_("Can't seek on game file"), 0);
-               return FALSE;
-           }
-       }
-    }
-    lastLoadGameFP = f;
-    lastLoadGameNumber = gameNumber;
-    strcpy(lastLoadGameTitle, title);
-    lastLoadGameUseList = useList;
-
-    yynewfile(f);
-
-
-    if (lg && lg->gameInfo.white && lg->gameInfo.black) {
-       sprintf(buf, "%s vs. %s", lg->gameInfo.white,
-               lg->gameInfo.black);
-           DisplayTitle(buf);
-    } else if (*title != NULLCHAR) {
-       if (gameNumber > 1) {
-           sprintf(buf, "%s %d", title, gameNumber);
-           DisplayTitle(buf);
-       } else {
-           DisplayTitle(title);
-       }
-    }
-
-    if (gameMode != AnalyzeFile && gameMode != AnalyzeMode) {
-       gameMode = PlayFromGameFile;
-       ModeHighlight();
-    }
-
-    currentMove = forwardMostMove = backwardMostMove = 0;
-    CopyBoard(boards[0], initialPosition);
-    StopClocks();
-
-    /*
-     * Skip the first gn-1 games in the file.
-     * Also skip over anything that precedes an identifiable
-     * start of game marker, to avoid being confused by
-     * garbage at the start of the file.  Currently
-     * recognized start of game markers are the move number "1",
-     * the pattern "gnuchess .* game", the pattern
-     * "^[#;%] [^ ]* game file", and a PGN tag block.
-     * A game that starts with one of the latter two patterns
-     * will also have a move number 1, possibly
-     * following a position diagram.
-     * 5-4-02: Let's try being more lenient and allowing a game to
-     * start with an unnumbered move.  Does that break anything?
-     */
-    cm = lastLoadGameStart = (ChessMove) 0;
-    while (gn > 0) {
-       yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
-       switch (cm) {
-         case (ChessMove) 0:
-           if (cmailMsgLoaded) {
-               nCmailGames = CMAIL_MAX_GAMES - gn;
-           } else {
-               Reset(TRUE, TRUE);
-               DisplayError(_("Game not found in file"), 0);
-           }
-           return FALSE;
-
-         case GNUChessGame:
-         case XBoardGame:
-           gn--;
-           lastLoadGameStart = cm;
-           break;
-
-         case MoveNumberOne:
-           switch (lastLoadGameStart) {
-             case GNUChessGame:
-             case XBoardGame:
-             case PGNTag:
-               break;
-             case MoveNumberOne:
-             case (ChessMove) 0:
-               gn--;           /* count this game */
-               lastLoadGameStart = cm;
-               break;
-             default:
-               /* impossible */
-               break;
-           }
-           break;
-
-         case PGNTag:
-           switch (lastLoadGameStart) {
-             case GNUChessGame:
-             case PGNTag:
-             case MoveNumberOne:
-             case (ChessMove) 0:
-               gn--;           /* count this game */
-               lastLoadGameStart = cm;
-               break;
-             case XBoardGame:
-               lastLoadGameStart = cm; /* game counted already */
-               break;
-             default:
-               /* impossible */
-               break;
-           }
-           if (gn > 0) {
-               do {
-                   yyboardindex = forwardMostMove;
-                   cm = (ChessMove) yylex();
-               } while (cm == PGNTag || cm == Comment);
-           }
-           break;
-
-         case WhiteWins:
-         case BlackWins:
-         case GameIsDrawn:
-           if (cmailMsgLoaded && (CMAIL_MAX_GAMES == lastLoadGameNumber)) {
-               if (   cmailResult[CMAIL_MAX_GAMES - gn - 1]
-                   != CMAIL_OLD_RESULT) {
-                   nCmailResults ++ ;
-                   cmailResult[  CMAIL_MAX_GAMES
-                               - gn - 1] = CMAIL_OLD_RESULT;
-               }
-           }
-           break;
-
-         case NormalMove:
-           /* Only a NormalMove can be at the start of a game
-            * without a position diagram. */
-           if (lastLoadGameStart == (ChessMove) 0) {
-             gn--;
-             lastLoadGameStart = MoveNumberOne;
-           }
-           break;
-
-         default:
-           break;
-       }
-    }
-
-    if (appData.debugMode)
-      fprintf(debugFP, "Parsed game start '%s' (%d)\n", yy_text, (int) cm);
-
-    if (cm == XBoardGame) {
-       /* Skip any header junk before position diagram and/or move 1 */
-       for (;;) {
-           yyboardindex = forwardMostMove;
-           cm = (ChessMove) yylex();
-
-           if (cm == (ChessMove) 0 ||
-               cm == GNUChessGame || cm == XBoardGame) {
-               /* Empty game; pretend end-of-file and handle later */
-               cm = (ChessMove) 0;
-               break;
-           }
-
-           if (cm == MoveNumberOne || cm == PositionDiagram ||
-               cm == PGNTag || cm == Comment)
-             break;
-       }
-    } else if (cm == GNUChessGame) {
-       if (gameInfo.event != NULL) {
-           free(gameInfo.event);
-       }
-       gameInfo.event = StrSave(yy_text);
-    }
-
-    startedFromSetupPosition = FALSE;
-    while (cm == PGNTag) {
-       if (appData.debugMode)
-         fprintf(debugFP, "Parsed PGNTag: %s\n", yy_text);
-       err = ParsePGNTag(yy_text, &gameInfo);
-       if (!err) numPGNTags++;
-
-       if (gameInfo.fen != NULL) {
-         Board initial_position;
-         startedFromSetupPosition = TRUE;
-         if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) {
-           Reset(TRUE, TRUE);
-           DisplayError(_("Bad FEN position in file"), 0);
-           return FALSE;
-         }
-         CopyBoard(boards[0], initial_position);
-         if (blackPlaysFirst) {
-           currentMove = forwardMostMove = backwardMostMove = 1;
-           CopyBoard(boards[1], initial_position);
-           strcpy(moveList[0], "");
-           strcpy(parseList[0], "");
-           timeRemaining[0][1] = whiteTimeRemaining;
-           timeRemaining[1][1] = blackTimeRemaining;
-           if (commentList[0] != NULL) {
-             commentList[1] = commentList[0];
-             commentList[0] = NULL;
-           }
-         } else {
-           currentMove = forwardMostMove = backwardMostMove = 0;
-         }
-         yyboardindex = forwardMostMove;
-         free(gameInfo.fen);
-         gameInfo.fen = NULL;
-       }
-
-       yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
-
-       /* Handle comments interspersed among the tags */
-       while (cm == Comment) {
-           char *p;
-           if (appData.debugMode)
-             fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
-           p = yy_text;
-           if (*p == '{' || *p == '[' || *p == '(') {
-               p[strlen(p) - 1] = NULLCHAR;
-               p++;
-           }
-           while (*p == '\n') p++;
-           AppendComment(currentMove, p);
-           yyboardindex = forwardMostMove;
-           cm = (ChessMove) yylex();
-       }
-    }
-
-    /* don't rely on existence of Event tag since if game was
-     * pasted from clipboard the Event tag may not exist
-     */
-    if (numPGNTags > 0){
-        char *tags;
-       if (gameInfo.variant == VariantNormal) {
-         gameInfo.variant = StringToVariant(gameInfo.event);
-       }
-       if (!matchMode) {
-         tags = PGNTags(&gameInfo);
-         TagsPopUp(tags, CmailMsg());
-         free(tags);
-       }
-    } else {
-       /* Make something up, but don't display it now */
-       SetGameInfo();
-       TagsPopDown();
-    }
-
-    if (cm == PositionDiagram) {
-       int i, j;
-       char *p;
-       Board initial_position;
-
-       if (appData.debugMode)
-         fprintf(debugFP, "Parsed PositionDiagram: %s\n", yy_text);
-
-       if (!startedFromSetupPosition) {
-           p = yy_text;
-           for (i = BOARD_SIZE - 1; i >= 0; i--)
-             for (j = 0; j < BOARD_SIZE; p++)
-               switch (*p) {
-                 case '[':
-                 case '-':
-                 case ' ':
-                 case '\t':
-                 case '\n':
-                 case '\r':
-                   break;
-                 default:
-                   initial_position[i][j++] = CharToPiece(*p);
-                   break;
-               }
-           while (*p == ' ' || *p == '\t' ||
-                  *p == '\n' || *p == '\r') p++;
-
-           if (strncmp(p, "black", strlen("black"))==0)
-             blackPlaysFirst = TRUE;
-           else
-             blackPlaysFirst = FALSE;
-           startedFromSetupPosition = TRUE;
-
-           CopyBoard(boards[0], initial_position);
-           if (blackPlaysFirst) {
-               currentMove = forwardMostMove = backwardMostMove = 1;
-               CopyBoard(boards[1], initial_position);
-               strcpy(moveList[0], "");
-               strcpy(parseList[0], "");
-               timeRemaining[0][1] = whiteTimeRemaining;
-               timeRemaining[1][1] = blackTimeRemaining;
-               if (commentList[0] != NULL) {
-                   commentList[1] = commentList[0];
-                   commentList[0] = NULL;
-               }
-           } else {
-               currentMove = forwardMostMove = backwardMostMove = 0;
-           }
-       }
-       yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
-    }
-
-    if (first.pr == NoProc) {
-       StartChessProgram(&first);
-    }
-    InitChessProgram(&first);
-    SendToProgram("force\n", &first);
-    if (startedFromSetupPosition) {
-       SendBoard(&first, forwardMostMove);
-       DisplayBothClocks();
-    }
-
-    while (cm == Comment) {
-       char *p;
-       if (appData.debugMode)
-         fprintf(debugFP, "Parsed Comment: %s\n", yy_text);
-       p = yy_text;
-       if (*p == '{' || *p == '[' || *p == '(') {
-           p[strlen(p) - 1] = NULLCHAR;
-           p++;
-       }
-       while (*p == '\n') p++;
-       AppendComment(currentMove, p);
-       yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
-    }
-
-    if ((cm == (ChessMove) 0 && lastLoadGameStart != (ChessMove) 0) ||
-       cm == WhiteWins || cm == BlackWins ||
-       cm == GameIsDrawn || cm == GameUnfinished) {
-       DisplayMessage("", _("No moves in game"));
-       if (cmailMsgLoaded) {
-           if (appData.debugMode)
-             fprintf(debugFP, "Setting flipView to %d.\n", FALSE);
-           ClearHighlights();
-           flipView = FALSE;
-       }
-       DrawPosition(FALSE, boards[currentMove]);
-       DisplayBothClocks();
-       gameMode = EditGame;
-       ModeHighlight();
-       gameFileFP = NULL;
-       cmailOldMove = 0;
-       return TRUE;
-    }
-
-    if (commentList[currentMove] != NULL) {
-      if (!matchMode && (pausing || appData.timeDelay != 0)) {
-       DisplayComment(currentMove - 1, commentList[currentMove]);
-      }
-    }
-    if (!matchMode && appData.timeDelay != 0)
-      DrawPosition(FALSE, boards[currentMove]);
-
-    if (gameMode == AnalyzeFile || gameMode == AnalyzeMode) {
-      programStats.ok_to_send = 1;
-    }
-
-    /* if the first token after the PGN tags is a move
-     * and not move number 1, retrieve it from the parser
-     */
-    if (cm != MoveNumberOne)
-       LoadGameOneMove(cm);
-
-    /* load the remaining moves from the file */
-    while (LoadGameOneMove((ChessMove)0)) {
-      timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
-      timeRemaining[1][forwardMostMove] = blackTimeRemaining;
-    }
-
-    /* rewind to the start of the game */
-    currentMove = backwardMostMove;
-
-    HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
-
-    if (oldGameMode == AnalyzeFile ||
-       oldGameMode == AnalyzeMode) {
-      AnalyzeFileEvent();
-    }
-
-    if (matchMode || appData.timeDelay == 0) {
-      ToEndEvent();
-      gameMode = EditGame;
-      ModeHighlight();
-    } else if (appData.timeDelay > 0) {
-      AutoPlayGameLoop();
-    }
-
-    if (appData.debugMode)
-       fprintf(debugFP, "LoadGame(): on exit, gameMode %d\n", gameMode);
-    return TRUE;
-}
-
-/* Support for LoadNextPosition, LoadPreviousPosition, ReloadSamePosition */
-int
-ReloadPosition(offset)
-     int offset;
-{
-    int positionNumber = lastLoadPositionNumber + offset;
-    if (lastLoadPositionFP == NULL) {
-       DisplayError(_("No position has been loaded yet"), 0);
-       return FALSE;
-    }
-    if (positionNumber <= 0) {
-       DisplayError(_("Can't back up any further"), 0);
-       return FALSE;
-    }
-    return LoadPosition(lastLoadPositionFP, positionNumber,
-                       lastLoadPositionTitle);
-}
-
-/* Load the nth position from the given file */
-int
-LoadPositionFromFile(filename, n, title)
-     char *filename;
-     int n;
-     char *title;
-{
-    FILE *f;
-    char buf[MSG_SIZ];
-
-    if (strcmp(filename, "-") == 0) {
-       return LoadPosition(stdin, n, "stdin");
-    } else {
-       f = fopen(filename, "rb");
-       if (f == NULL) {
-           sprintf(buf, _("Can't open \"%s\""), filename);
-           DisplayError(buf, errno);
-           return FALSE;
-       } else {
-           return LoadPosition(f, n, title);
-       }
-    }
-}
-
-/* Load the nth position from the given open file, and close it */
-int
-LoadPosition(f, positionNumber, title)
-     FILE *f;
-     int positionNumber;
-     char *title;
-{
-    char *p, line[MSG_SIZ];
-    Board initial_position;
-    int i, j, fenMode, pn;
-
-    if (gameMode == Training )
-       SetTrainingModeOff();
-
-    if (gameMode != BeginningOfGame) {
-       Reset(FALSE, TRUE);
-    }
-    if (lastLoadPositionFP != NULL && lastLoadPositionFP != f) {
-       fclose(lastLoadPositionFP);
-    }
-    if (positionNumber == 0) positionNumber = 1;
-    lastLoadPositionFP = f;
-    lastLoadPositionNumber = positionNumber;
-    strcpy(lastLoadPositionTitle, title);
-    if (first.pr == NoProc) {
-      StartChessProgram(&first);
-      InitChessProgram(&first);
-    }
-    pn = positionNumber;
-    if (positionNumber < 0) {
-       /* Negative position number means to seek to that byte offset */
-       if (fseek(f, -positionNumber, 0) == -1) {
-           DisplayError(_("Can't seek on position file"), 0);
-           return FALSE;
-       };
-       pn = 1;
-    } else {
-       if (fseek(f, 0, 0) == -1) {
-           if (f == lastLoadPositionFP ?
-               positionNumber == lastLoadPositionNumber + 1 :
-               positionNumber == 1) {
-               pn = 1;
-           } else {
-               DisplayError(_("Can't seek on position file"), 0);
-               return FALSE;
-           }
-       }
-    }
-    /* See if this file is FEN or old-style xboard */
-    if (fgets(line, MSG_SIZ, f) == NULL) {
-       DisplayError(_("Position not found in file"), 0);
-       return FALSE;
-    }
-    switch (line[0]) {
-      case '#':  case 'x':
-      default:
-       fenMode = FALSE;
-       break;
-      case 'p':  case 'n':  case 'b':  case 'r':  case 'q':  case 'k':
-      case 'P':  case 'N':  case 'B':  case 'R':  case 'Q':  case 'K':
-      case '1':  case '2':  case '3':  case '4':  case '5':  case '6':
-      case '7':  case '8':
-       fenMode = TRUE;
-       break;
-    }
-
-    if (pn >= 2) {
-       if (fenMode || line[0] == '#') pn--;
-       while (pn > 0) {
-           /* skip postions before number pn */
-           if (fgets(line, MSG_SIZ, f) == NULL) {
-               Reset(TRUE, TRUE);
-               DisplayError(_("Position not found in file"), 0);
-               return FALSE;
-           }
-           if (fenMode || line[0] == '#') pn--;
-       }
-    }
-
-    if (fenMode) {
-       if (!ParseFEN(initial_position, &blackPlaysFirst, line)) {
-           DisplayError(_("Bad FEN position in file"), 0);
-           return FALSE;
-       }
-    } else {
-       (void) fgets(line, MSG_SIZ, f);
-       (void) fgets(line, MSG_SIZ, f);
-
-       for (i = BOARD_SIZE - 1; i >= 0; i--) {
-           (void) fgets(line, MSG_SIZ, f);
-           for (p = line, j = 0; j < BOARD_SIZE; p++) {
-               if (*p == ' ')
-                 continue;
-               initial_position[i][j++] = CharToPiece(*p);
-           }
-       }
-
-       blackPlaysFirst = FALSE;
-       if (!feof(f)) {
-           (void) fgets(line, MSG_SIZ, f);
-           if (strncmp(line, "black", strlen("black"))==0)
-             blackPlaysFirst = TRUE;
-       }
-    }
-    startedFromSetupPosition = TRUE;
-
-    SendToProgram("force\n", &first);
-    CopyBoard(boards[0], initial_position);
-    if (blackPlaysFirst) {
-       currentMove = forwardMostMove = backwardMostMove = 1;
-       strcpy(moveList[0], "");
-       strcpy(parseList[0], "");
-       CopyBoard(boards[1], initial_position);
-       DisplayMessage("", _("Black to play"));
-    } else {
-       currentMove = forwardMostMove = backwardMostMove = 0;
-       DisplayMessage("", _("White to play"));
-    }
-    SendBoard(&first, forwardMostMove);
-
-    if (positionNumber > 1) {
-       sprintf(line, "%s %d", title, positionNumber);
-       DisplayTitle(line);
-    } else {
-       DisplayTitle(title);
-    }
-    gameMode = EditGame;
-    ModeHighlight();
-    ResetClocks();
-    timeRemaining[0][1] = whiteTimeRemaining;
-    timeRemaining[1][1] = blackTimeRemaining;
-    DrawPosition(FALSE, boards[currentMove]);
-
-    return TRUE;
-}
-
-
-void
-CopyPlayerNameIntoFileName(dest, src)
-     char **dest, *src;
-{
-    while (*src != NULLCHAR && *src != ',') {
-       if (*src == ' ') {
-           *(*dest)++ = '_';
-           src++;
-       } else {
-           *(*dest)++ = *src++;
-       }
-    }
-}
-
-char *DefaultFileName(ext)
-     char *ext;
-{
-    static char def[MSG_SIZ];
-    char *p;
-
-    if (gameInfo.white != NULL && gameInfo.white[0] != '-') {
-       p = def;
-       CopyPlayerNameIntoFileName(&p, gameInfo.white);
-       *p++ = '-';
-       CopyPlayerNameIntoFileName(&p, gameInfo.black);
-       *p++ = '.';
-       strcpy(p, ext);
-    } else {
-       def[0] = NULLCHAR;
-    }
-    return def;
-}
-
-/* Save the current game to the given file */
-int
-SaveGameToFile(filename, append)
-     char *filename;
-     int append;
-{
-    FILE *f;
-    char buf[MSG_SIZ];
-
-    if (strcmp(filename, "-") == 0) {
-       return SaveGame(stdout, 0, NULL);
-    } else {
-       f = fopen(filename, append ? "a" : "w");
-       if (f == NULL) {
-           sprintf(buf, _("Can't open \"%s\""), filename);
-           DisplayError(buf, errno);
-           return FALSE;
-       } else {
-           return SaveGame(f, 0, NULL);
-       }
-    }
-}
-
-char *
-SavePart(str)
-     char *str;
-{
-    static char buf[MSG_SIZ];
-    char *p;
-
-    p = strchr(str, ' ');
-    if (p == NULL) return str;
-    strncpy(buf, str, p - str);
-    buf[p - str] = NULLCHAR;
-    return buf;
-}
-
-#define PGN_MAX_LINE 75
-
-/* Save game in PGN style and close the file */
-int
-SaveGamePGN(f)
-     FILE *f;
-{
-    int i, offset, linelen, newblock;
-    time_t tm;
-    char *movetext;
-    char numtext[32];
-    int movelen, numlen, blank;
-
-    tm = time((time_t *) NULL);
-
-    PrintPGNTags(f, &gameInfo);
-
-    if (backwardMostMove > 0 || startedFromSetupPosition) {
-        char *fen = PositionToFEN(backwardMostMove);
-        fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);
-       fprintf(f, "\n{--------------\n");
-       PrintPosition(f, backwardMostMove);
-       fprintf(f, "--------------}\n");
-        free(fen);
-    } else {
-       fprintf(f, "\n");
-    }
-
-    i = backwardMostMove;
-    offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
-    linelen = 0;
-    newblock = TRUE;
-
-    while (i < forwardMostMove) {
-       /* Print comments preceding this move */
-       if (commentList[i] != NULL) {
-           if (linelen > 0) fprintf(f, "\n");
-           fprintf(f, "{\n%s}\n", commentList[i]);
-           linelen = 0;
-           newblock = TRUE;
-       }
-
-       /* Format move number */
-       if ((i % 2) == 0) {
-           sprintf(numtext, "%d.", (i - offset)/2 + 1);
-       } else {
-           if (newblock) {
-               sprintf(numtext, "%d...", (i - offset)/2 + 1);
-           } else {
-               numtext[0] = NULLCHAR;
-           }
-       }
-       numlen = strlen(numtext);
-       newblock = FALSE;
-
-       /* Print move number */
-       blank = linelen > 0 && numlen > 0;
-       if (linelen + (blank ? 1 : 0) + numlen > PGN_MAX_LINE) {
-           fprintf(f, "\n");
-           linelen = 0;
-           blank = 0;
-       }
-       if (blank) {
-           fprintf(f, " ");
-           linelen++;
-       }
-       fprintf(f, numtext);
-       linelen += numlen;
-
-       /* Get move */
-       movetext = SavePart(parseList[i]);
-       movelen = strlen(movetext);
-
-       /* Print move */
-       blank = linelen > 0 && movelen > 0;
-       if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {
-           fprintf(f, "\n");
-           linelen = 0;
-           blank = 0;
-       }
-       if (blank) {
-           fprintf(f, " ");
-           linelen++;
-       }
-       fprintf(f, movetext);
-       linelen += movelen;
-
-       i++;
-    }
-
-    /* Start a new line */
-    if (linelen > 0) fprintf(f, "\n");
-
-    /* Print comments after last move */
-    if (commentList[i] != NULL) {
-       fprintf(f, "{\n%s}\n", commentList[i]);
-    }
-
-    /* Print result */
-    if (gameInfo.resultDetails != NULL &&
-       gameInfo.resultDetails[0] != NULLCHAR) {
-       fprintf(f, "{%s} %s\n\n", gameInfo.resultDetails,
-               PGNResult(gameInfo.result));
-    } else {
-       fprintf(f, "%s\n\n", PGNResult(gameInfo.result));
-    }
-
-    fclose(f);
-    return TRUE;
-}
-
-/* Save game in old style and close the file */
-int
-SaveGameOldStyle(f)
-     FILE *f;
-{
-    int i, offset;
-    time_t tm;
-
-    tm = time((time_t *) NULL);
-
-    fprintf(f, "# %s game file -- %s", programName, ctime(&tm));
-    PrintOpponents(f);
-
-    if (backwardMostMove > 0 || startedFromSetupPosition) {
-       fprintf(f, "\n[--------------\n");
-       PrintPosition(f, backwardMostMove);
-       fprintf(f, "--------------]\n");
-    } else {
-       fprintf(f, "\n");
-    }
-
-    i = backwardMostMove;
-    offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
-
-    while (i < forwardMostMove) {
-       if (commentList[i] != NULL) {
-           fprintf(f, "[%s]\n", commentList[i]);
-       }
-
-       if ((i % 2) == 1) {
-           fprintf(f, "%d. ...  %s\n", (i - offset)/2 + 1, parseList[i]);
-           i++;
-       } else {
-           fprintf(f, "%d. %s  ", (i - offset)/2 + 1, parseList[i]);
-           i++;
-           if (commentList[i] != NULL) {
-               fprintf(f, "\n");
-               continue;
-           }
-           if (i >= forwardMostMove) {
-               fprintf(f, "\n");
-               break;
-           }
-           fprintf(f, "%s\n", parseList[i]);
-           i++;
-       }
-    }
-
-    if (commentList[i] != NULL) {
-       fprintf(f, "[%s]\n", commentList[i]);
-    }
-
-    /* This isn't really the old style, but it's close enough */
-    if (gameInfo.resultDetails != NULL &&
-       gameInfo.resultDetails[0] != NULLCHAR) {
-       fprintf(f, "%s (%s)\n\n", PGNResult(gameInfo.result),
-               gameInfo.resultDetails);
-    } else {
-       fprintf(f, "%s\n\n", PGNResult(gameInfo.result));
-    }
-
-    fclose(f);
-    return TRUE;
-}
-
-/* Save the current game to open file f and close the file */
-int
-SaveGame(f, dummy, dummy2)
-     FILE *f;
-     int dummy;
-     char *dummy2;
-{
-    if (gameMode == EditPosition) EditPositionDone();
-    if (appData.oldSaveStyle)
-      return SaveGameOldStyle(f);
-    else
-      return SaveGamePGN(f);
-}
-
-/* Save the current position to the given file */
-int
-SavePositionToFile(filename)
-     char *filename;
-{
-    FILE *f;
-    char buf[MSG_SIZ];
-
-    if (strcmp(filename, "-") == 0) {
-       return SavePosition(stdout, 0, NULL);
-    } else {
-       f = fopen(filename, "a");
-       if (f == NULL) {
-           sprintf(buf, _("Can't open \"%s\""), filename);
-           DisplayError(buf, errno);
-           return FALSE;
-       } else {
-           SavePosition(f, 0, NULL);
-           return TRUE;
-       }
-    }
-}
-
-/* Save the current position to the given open file and close the file */
-int
-SavePosition(f, dummy, dummy2)
-     FILE *f;
-     int dummy;
-     char *dummy2;
-{
-    time_t tm;
-    char *fen;
-
-    if (appData.oldSaveStyle) {
-       tm = time((time_t *) NULL);
-
-       fprintf(f, "# %s position file -- %s", programName, ctime(&tm));
-       PrintOpponents(f);
-       fprintf(f, "[--------------\n");
-       PrintPosition(f, currentMove);
-       fprintf(f, "--------------]\n");
-    } else {
-       fen = PositionToFEN(currentMove);
-       fprintf(f, "%s\n", fen);
-       free(fen);
-    }
-    fclose(f);
-    return TRUE;
-}
-
-void
-ReloadCmailMsgEvent(unregister)
-     int unregister;
-{
-#if !WIN32
-    static char *inFilename = NULL;
-    static char *outFilename;
-    int i;
-    struct stat inbuf, outbuf;
-    int status;
-
-    /* Any registered moves are unregistered if unregister is set, */
-    /* i.e. invoked by the signal handler */
-    if (unregister) {
-       for (i = 0; i < CMAIL_MAX_GAMES; i ++) {
-           cmailMoveRegistered[i] = FALSE;
-           if (cmailCommentList[i] != NULL) {
-               free(cmailCommentList[i]);
-               cmailCommentList[i] = NULL;
-           }
-       }
-       nCmailMovesRegistered = 0;
-    }
-
-    for (i = 0; i < CMAIL_MAX_GAMES; i ++) {
-       cmailResult[i] = CMAIL_NOT_RESULT;
-    }
-    nCmailResults = 0;
-
-    if (inFilename == NULL) {
-       /* Because the filenames are static they only get malloced once  */
-       /* and they never get freed                                      */
-       inFilename = (char *) malloc(strlen(appData.cmailGameName) + 9);
-       sprintf(inFilename, "%s.game.in", appData.cmailGameName);
-
-       outFilename = (char *) malloc(strlen(appData.cmailGameName) + 5);
-       sprintf(outFilename, "%s.out", appData.cmailGameName);
-    }
-
-    status = stat(outFilename, &outbuf);
-    if (status < 0) {
-       cmailMailedMove = FALSE;
-    } else {
-       status = stat(inFilename, &inbuf);
-       cmailMailedMove = (inbuf.st_mtime < outbuf.st_mtime);
-    }
-
-    /* LoadGameFromFile(CMAIL_MAX_GAMES) with cmailMsgLoaded == TRUE
-       counts the games, notes how each one terminated, etc.
-
-       It would be nice to remove this kludge and instead gather all
-       the information while building the game list.  (And to keep it
-       in the game list nodes instead of having a bunch of fixed-size
-       parallel arrays.)  Note this will require getting each game's
-       termination from the PGN tags, as the game list builder does
-       not process the game moves.  --mann
-       */
-    cmailMsgLoaded = TRUE;
-    LoadGameFromFile(inFilename, CMAIL_MAX_GAMES, "", FALSE);
-
-    /* Load first game in the file or popup game menu */
-    LoadGameFromFile(inFilename, 0, appData.cmailGameName, TRUE);
-
-#endif /* !WIN32 */
-    return;
-}
-
-int
-RegisterMove()
-{
-    FILE *f;
-    char string[MSG_SIZ];
-
-    if (   cmailMailedMove
-       || (cmailResult[lastLoadGameNumber - 1] == CMAIL_OLD_RESULT)) {
-       return TRUE;            /* Allow free viewing  */
-    }
-
-    /* Unregister move to ensure that we don't leave RegisterMove        */
-    /* with the move registered when the conditions for registering no   */
-    /* longer hold                                                       */
-    if (cmailMoveRegistered[lastLoadGameNumber - 1]) {
-       cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;
-       nCmailMovesRegistered --;
-
-       if (cmailCommentList[lastLoadGameNumber - 1] != NULL)
-         {
-             free(cmailCommentList[lastLoadGameNumber - 1]);
-             cmailCommentList[lastLoadGameNumber - 1] = NULL;
-         }
-    }
-
-    if (cmailOldMove == -1) {
-       DisplayError(_("You have edited the game history.\nUse Reload Same Game and make your move again."), 0);
-       return FALSE;
-    }
-
-    if (currentMove > cmailOldMove + 1) {
-       DisplayError(_("You have entered too many moves.\nBack up to the correct position and try again."), 0);
-       return FALSE;
-    }
-
-    if (currentMove < cmailOldMove) {
-       DisplayError(_("Displayed position is not current.\nStep forward to the correct position and try again."), 0);
-       return FALSE;
-    }
-
-    if (forwardMostMove > currentMove) {
-       /* Silently truncate extra moves */
-       TruncateGame();
-    }
-
-    if (   (currentMove == cmailOldMove + 1)
-       || (   (currentMove == cmailOldMove)
-           && (   (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_ACCEPT)
-               || (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_RESIGN)))) {
-       if (gameInfo.result != GameUnfinished) {
-           cmailResult[lastLoadGameNumber - 1] = CMAIL_NEW_RESULT;
-       }
-
-       if (commentList[currentMove] != NULL) {
-           cmailCommentList[lastLoadGameNumber - 1]
-             = StrSave(commentList[currentMove]);
-       }
-       strcpy(cmailMove[lastLoadGameNumber - 1], moveList[currentMove - 1]);
-
-       if (appData.debugMode)
-         fprintf(debugFP, "Saving %s for game %d\n",
-                 cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);
-
-       sprintf(string,
-               "%s.game.out.%d", appData.cmailGameName, lastLoadGameNumber);
-
-       f = fopen(string, "w");
-       if (appData.oldSaveStyle) {
-           SaveGameOldStyle(f); /* also closes the file */
-
-           sprintf(string, "%s.pos.out", appData.cmailGameName);
-           f = fopen(string, "w");
-           SavePosition(f, 0, NULL); /* also closes the file */
-       } else {
-           fprintf(f, "{--------------\n");
-           PrintPosition(f, currentMove);
-           fprintf(f, "--------------}\n\n");
-
-           SaveGame(f, 0, NULL); /* also closes the file*/
-       }
-
-       cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE;
-       nCmailMovesRegistered ++;
-    } else if (nCmailGames == 1) {
-       DisplayError(_("You have not made a move yet"), 0);
-       return FALSE;
-    }
-
-    return TRUE;
-}
-
-void
-MailMoveEvent()
-{
-#if !WIN32
-    static char *partCommandString = "cmail -xv%s -remail -game %s 2>&1";
-    FILE *commandOutput;
-    char buffer[MSG_SIZ], msg[MSG_SIZ], string[MSG_SIZ];
-    int nBytes = 0;            /*  Suppress warnings on uninitialized variables    */
-    int nBuffers;
-    int i;
-    int archived;
-    char *arcDir;
-
-    if (! cmailMsgLoaded) {
-       DisplayError(_("The cmail message is not loaded.\nUse Reload CMail Message and make your move again."), 0);
-       return;
-    }
-
-    if (nCmailGames == nCmailResults) {
-       DisplayError(_("No unfinished games"), 0);
-       return;
-    }
-
-#if CMAIL_PROHIBIT_REMAIL
-    if (cmailMailedMove) {
-       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);
-       DisplayError(msg, 0);
-       return;
-    }
-#endif
-
-    if (! (cmailMailedMove || RegisterMove())) return;
-
-    if (   cmailMailedMove
-       || (nCmailMovesRegistered + nCmailResults == nCmailGames)) {
-       sprintf(string, partCommandString,
-               appData.debugMode ? " -v" : "", appData.cmailGameName);
-       commandOutput = popen(string, "r");
-
-       if (commandOutput == NULL) {
-           DisplayError(_("Failed to invoke cmail"), 0);
-       } else {
-           for (nBuffers = 0; (! feof(commandOutput)); nBuffers ++) {
-               nBytes = fread(buffer, 1, MSG_SIZ - 1, commandOutput);
-           }
-           if (nBuffers > 1) {
-               (void) memcpy(msg, buffer + nBytes, MSG_SIZ - nBytes - 1);
-               (void) memcpy(msg + MSG_SIZ - nBytes - 1, buffer, nBytes);
-               nBytes = MSG_SIZ - 1;
-           } else {
-               (void) memcpy(msg, buffer, nBytes);
-           }
-           *(msg + nBytes) = '\0'; /* \0 for end-of-string*/
-
-           if(StrStr(msg, "Mailed cmail message to ") != NULL) {
-               cmailMailedMove = TRUE; /* Prevent >1 moves    */
-
-               archived = TRUE;
-               for (i = 0; i < nCmailGames; i ++) {
-                   if (cmailResult[i] == CMAIL_NOT_RESULT) {
-                       archived = FALSE;
-                   }
-               }
-               if (   archived
-                   && (   (arcDir = (char *) getenv("CMAIL_ARCDIR"))
-                       != NULL)) {
-                   sprintf(buffer, "%s/%s.%s.archive",
-                           arcDir,
-                           appData.cmailGameName,
-                           gameInfo.date);
-                   LoadGameFromFile(buffer, 1, buffer, FALSE);
-                   cmailMsgLoaded = FALSE;
-               }
-           }
-
-           DisplayInformation(msg);
-           pclose(commandOutput);
-       }
-    } else {
-       if ((*cmailMsg) != '\0') {
-           DisplayInformation(cmailMsg);
-       }
-    }
-
-    return;
-#endif /* !WIN32 */
-}
-
-char *
-CmailMsg()
-{
-#if WIN32
-    return NULL;
-#else
-    int  prependComma = 0;
-    char number[5];
-    char string[MSG_SIZ];      /* Space for game-list */
-    int  i;
-
-    if (!cmailMsgLoaded) return "";
-
-    if (cmailMailedMove) {
-       sprintf(cmailMsg, _("Waiting for reply from opponent\n"));
-    } else {
-       /* Create a list of games left */
-       sprintf(string, "[");
-       for (i = 0; i < nCmailGames; i ++) {
-           if (! (   cmailMoveRegistered[i]
-                  || (cmailResult[i] == CMAIL_OLD_RESULT))) {
-               if (prependComma) {
-                   sprintf(number, ",%d", i + 1);
-               } else {
-                   sprintf(number, "%d", i + 1);
-                   prependComma = 1;
-               }
-
-               strcat(string, number);
-           }
-       }
-       strcat(string, "]");
-
-       if (nCmailMovesRegistered + nCmailResults == 0) {
-           switch (nCmailGames) {
-             case 1:
-               sprintf(cmailMsg,
-                       _("Still need to make move for game\n"));
-               break;
-
-             case 2:
-               sprintf(cmailMsg,
-                       _("Still need to make moves for both games\n"));
-               break;
-
-             default:
-               sprintf(cmailMsg,
-                       _("Still need to make moves for all %d games\n"),
-                       nCmailGames);
-               break;
-           }
-       } else {
-           switch (nCmailGames - nCmailMovesRegistered - nCmailResults) {
-             case 1:
-               sprintf(cmailMsg,
-                       _("Still need to make a move for game %s\n"),
-                       string);
-               break;
-
-             case 0:
-               if (nCmailResults == nCmailGames) {
-                   sprintf(cmailMsg, _("No unfinished games\n"));
-               } else {
-                   sprintf(cmailMsg, _("Ready to send mail\n"));
-               }
-               break;
-
-             default:
-               sprintf(cmailMsg,
-                       _("Still need to make moves for games %s\n"),
-                       string);
-           }
-       }
-    }
-    return cmailMsg;
-#endif /* WIN32 */
-}
-
-void
-ResetGameEvent()
-{
-    if (gameMode == Training)
-      SetTrainingModeOff();
-
-    Reset(TRUE, TRUE);
-    cmailMsgLoaded = FALSE;
-    if (appData.icsActive) {
-      SendToICS(ics_prefix);
-      SendToICS("refresh\n");
-    }
-}
-
-static int exiting = 0;
-
-void
-ExitEvent(status)
-     int status;
-{
-    exiting++;
-    if (exiting > 2) {
-      /* Give up on clean exit */
-      exit(status);
-    }
-    if (exiting > 1) {
-      /* Keep trying for clean exit */
-      return;
-    }
-
-    if (appData.icsActive && appData.colorize) Colorize(ColorNone, FALSE);
-
-    if (telnetISR != NULL) {
-      RemoveInputSource(telnetISR);
-    }
-    if (icsPR != NoProc) {
-      DestroyChildProcess(icsPR, TRUE);
-    }
-    /* Save game if resource set and not already saved by GameEnds() */
-    if (gameInfo.resultDetails == NULL && forwardMostMove > 0) {
-      if (*appData.saveGameFile != NULLCHAR) {
-       SaveGameToFile(appData.saveGameFile, TRUE);
-      } else if (appData.autoSaveGames) {
-       AutoSaveGame();
-      }
-      if (*appData.savePositionFile != NULLCHAR) {
-       SavePositionToFile(appData.savePositionFile);
-      }
-    }
-    GameEnds((ChessMove) 0, NULL, GE_PLAYER);
-
-    /* Kill off chess programs */
-    if (first.pr != NoProc) {
-       ExitAnalyzeMode();
-       SendToProgram("quit\n", &first);
-       DestroyChildProcess(first.pr, first.useSigterm);
-    }
-    if (second.pr != NoProc) {
-       SendToProgram("quit\n", &second);
-       DestroyChildProcess(second.pr, second.useSigterm);
-    }
-    if (first.isr != NULL) {
-       RemoveInputSource(first.isr);
-    }
-    if (second.isr != NULL) {
-       RemoveInputSource(second.isr);
-    }
-
-    ShutDownFrontEnd();
-    exit(status);
-}
-
-void
-PauseEvent()
-{
-    if (appData.debugMode)
-       fprintf(debugFP, "PauseEvent(): pausing %d\n", pausing);
-    if (pausing) {
-       pausing = FALSE;
-       ModeHighlight();
-       if (gameMode == MachinePlaysWhite ||
-           gameMode == MachinePlaysBlack) {
-           StartClocks();
-       } else {
-           DisplayBothClocks();
-       }
-       if (gameMode == PlayFromGameFile) {
-           if (appData.timeDelay >= 0)
-               AutoPlayGameLoop();
-       } else if (gameMode == IcsExamining && pauseExamInvalid) {
-           Reset(FALSE, TRUE);
-           SendToICS(ics_prefix);
-           SendToICS("refresh\n");
-       } else if (currentMove < forwardMostMove) {
-           ForwardInner(forwardMostMove);
-       }
-       pauseExamInvalid = FALSE;
-    } else {
-       switch (gameMode) {
-         default:
-           return;
-         case IcsExamining:
-           pauseExamForwardMostMove = forwardMostMove;
-           pauseExamInvalid = FALSE;
-           /* fall through */
-         case IcsObserving:
-         case IcsPlayingWhite:
-         case IcsPlayingBlack:
-           pausing = TRUE;
-           ModeHighlight();
-           return;
-         case PlayFromGameFile:
-           (void) StopLoadGameTimer();
-           pausing = TRUE;
-           ModeHighlight();
-           break;
-         case BeginningOfGame:
-           if (appData.icsActive) return;
-           /* else fall through */
-         case MachinePlaysWhite:
-         case MachinePlaysBlack:
-         case TwoMachinesPlay:
-           if (forwardMostMove == 0)
-             return;           /* don't pause if no one has moved */
-           if ((gameMode == MachinePlaysWhite &&
-                !WhiteOnMove(forwardMostMove)) ||
-               (gameMode == MachinePlaysBlack &&
-                WhiteOnMove(forwardMostMove))) {
-               StopClocks();
-           }
-           pausing = TRUE;
-           ModeHighlight();
-           break;
-       }
-    }
-}
-
-void
-EditCommentEvent()
-{
-    char title[MSG_SIZ];
-
-    if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) {
-       strcpy(title, _("Edit comment"));
-    } else {
-       sprintf(title, _("Edit comment on %d.%s%s"), (currentMove - 1) / 2 + 1,
-               WhiteOnMove(currentMove - 1) ? " " : ".. ",
-               parseList[currentMove - 1]);
-    }
-
-    EditCommentPopUp(currentMove, title, commentList[currentMove]);
-}
-
-
-void
-EditTagsEvent()
-{
-    char *tags = PGNTags(&gameInfo);
-    EditTagsPopUp(tags);
-    free(tags);
-}
-
-void
-AnalyzeModeEvent()
-{
-    if (appData.noChessProgram || gameMode == AnalyzeMode)
-      return;
-
-    if (gameMode != AnalyzeFile) {
-               if (!appData.icsEngineAnalyze) {
-                       EditGameEvent();
-               if (gameMode != EditGame) return;
-               }
-       ResurrectChessProgram();
-       SendToProgram("analyze\n", &first);
-       first.analyzing = TRUE;
-       /*first.maybeThinking = TRUE;*/
-       first.maybeThinking = FALSE; /* avoid killing GNU Chess */
-       AnalysisPopUp(_("Analysis"),
-                     _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."));
-    }
-    if (!appData.icsEngineAnalyze) gameMode = AnalyzeMode;
-    pausing = FALSE;
-    ModeHighlight();
-    SetGameInfo();
-
-    StartAnalysisClock();
-    GetTimeMark(&lastNodeCountTime);
-    lastNodeCount = 0;
-}
-
-void
-AnalyzeFileEvent()
-{
-    if (appData.noChessProgram || gameMode == AnalyzeFile)
-      return;
-
-    if (gameMode != AnalyzeMode) {
-       EditGameEvent();
-       if (gameMode != EditGame) return;
-       ResurrectChessProgram();
-       SendToProgram("analyze\n", &first);
-       first.analyzing = TRUE;
-       /*first.maybeThinking = TRUE;*/
-       first.maybeThinking = FALSE; /* avoid killing GNU Chess */
-       AnalysisPopUp(_("Analysis"),
-                     _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."));
-    }
-    gameMode = AnalyzeFile;
-    pausing = FALSE;
-    ModeHighlight();
-    SetGameInfo();
-
-    StartAnalysisClock();
-    GetTimeMark(&lastNodeCountTime);
-    lastNodeCount = 0;
-}
-
-void
-MachineWhiteEvent()
-{
-    char buf[MSG_SIZ];
-
-    if (appData.noChessProgram || (gameMode == MachinePlaysWhite))
-      return;
-
-
-    if (gameMode == PlayFromGameFile ||
-       gameMode == TwoMachinesPlay  ||
-       gameMode == Training         ||
-       gameMode == AnalyzeMode      ||
-       gameMode == EndOfGame)
-       EditGameEvent();
-
-    if (gameMode == EditPosition)
-        EditPositionDone();
-
-    if (!WhiteOnMove(currentMove)) {
-       DisplayError(_("It is not White's turn"), 0);
-       return;
-    }
-
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
-      ExitAnalyzeMode();
-
-    if (gameMode == EditGame || gameMode == AnalyzeMode ||
-       gameMode == AnalyzeFile)
-       TruncateGame();
-
-    ResurrectChessProgram();   /* in case it isn't running */
-    gameMode = MachinePlaysWhite;
-    pausing = FALSE;
-    ModeHighlight();
-    SetGameInfo();
-    sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
-    DisplayTitle(buf);
-    if (first.sendName) {
-      sprintf(buf, "name %s\n", gameInfo.black);
-      SendToProgram(buf, &first);
-    }
-    if (first.sendTime) {
-      if (first.useColors) {
-       SendToProgram("black\n", &first); /*gnu kludge*/
-      }
-      SendTimeRemaining(&first, TRUE);
-    }
-    if (first.useColors) {
-      SendToProgram("white\ngo\n", &first);
-    } else {
-      SendToProgram("go\n", &first);
-    }
-    SetMachineThinkingEnables();
-    first.maybeThinking = TRUE;
-    StartClocks();
-
-    if (appData.autoFlipView && !flipView) {
-      flipView = !flipView;
-      DrawPosition(FALSE, NULL);
-    }
-}
-
-void
-MachineBlackEvent()
-{
-    char buf[MSG_SIZ];
-
-    if (appData.noChessProgram || (gameMode == MachinePlaysBlack))
-       return;
-
-
-    if (gameMode == PlayFromGameFile ||
-       gameMode == TwoMachinesPlay  ||
-       gameMode == Training         ||
-       gameMode == AnalyzeMode      ||
-       gameMode == EndOfGame)
-        EditGameEvent();
-
-    if (gameMode == EditPosition)
-        EditPositionDone();
-
-    if (WhiteOnMove(currentMove)) {
-       DisplayError(_("It is not Black's turn"), 0);
-       return;
-    }
-
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
-      ExitAnalyzeMode();
-
-    if (gameMode == EditGame || gameMode == AnalyzeMode ||
-       gameMode == AnalyzeFile)
-       TruncateGame();
-
-    ResurrectChessProgram();   /* in case it isn't running */
-    gameMode = MachinePlaysBlack;
-    pausing = FALSE;
-    ModeHighlight();
-    SetGameInfo();
-    sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
-    DisplayTitle(buf);
-    if (first.sendName) {
-      sprintf(buf, "name %s\n", gameInfo.white);
-      SendToProgram(buf, &first);
-    }
-    if (first.sendTime) {
-      if (first.useColors) {
-       SendToProgram("white\n", &first); /*gnu kludge*/
-      }
-      SendTimeRemaining(&first, FALSE);
-    }
-    if (first.useColors) {
-      SendToProgram("black\ngo\n", &first);
-    } else {
-      SendToProgram("go\n", &first);
-    }
-    SetMachineThinkingEnables();
-    first.maybeThinking = TRUE;
-    StartClocks();
-
-    if (appData.autoFlipView && flipView) {
-      flipView = !flipView;
-      DrawPosition(FALSE, NULL);
-    }
-}
-
-
-void
-DisplayTwoMachinesTitle()
-{
-    char buf[MSG_SIZ];
-    if (appData.matchGames > 0) {
-        if (first.twoMachinesColor[0] == 'w') {
-           sprintf(buf, "%s vs. %s (%d-%d-%d)",
-                   gameInfo.white, gameInfo.black,
-                   first.matchWins, second.matchWins,
-                   matchGame - 1 - (first.matchWins + second.matchWins));
-       } else {
-           sprintf(buf, "%s vs. %s (%d-%d-%d)",
-                   gameInfo.white, gameInfo.black,
-                   second.matchWins, first.matchWins,
-                   matchGame - 1 - (first.matchWins + second.matchWins));
-       }
-    } else {
-       sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
-    }
-    DisplayTitle(buf);
-}
-
-void
-TwoMachinesEvent P((void))
-{
-    int i;
-    char buf[MSG_SIZ];
-    ChessProgramState *onmove;
-
-    if (appData.noChessProgram) return;
-
-    switch (gameMode) {
-      case TwoMachinesPlay:
-       return;
-      case MachinePlaysWhite:
-      case MachinePlaysBlack:
-       if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {
-           DisplayError(_("Wait until your turn,\nor select Move Now"), 0);
-           return;
-       }
-       /* fall through */
-      case BeginningOfGame:
-      case PlayFromGameFile:
-      case EndOfGame:
-       EditGameEvent();
-       if (gameMode != EditGame) return;
-       break;
-      case EditPosition:
-       EditPositionDone();
-       break;
-      case AnalyzeMode:
-      case AnalyzeFile:
-       ExitAnalyzeMode();
-       break;
-      case EditGame:
-      default:
-       break;
-    }
-
-    forwardMostMove = currentMove;
-    ResurrectChessProgram();   /* in case first program isn't running */
-
-    if (second.pr == NULL) {
-       StartChessProgram(&second);
-       if (second.protocolVersion == 1) {
-         TwoMachinesEventIfReady();
-       } else {
-         /* kludge: allow timeout for initial "feature" command */
-         FreezeUI();
-         DisplayMessage("", _("Starting second chess program"));
-         ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);
-       }
-       return;
-    }
-    DisplayMessage("", "");
-    InitChessProgram(&second);
-    SendToProgram("force\n", &second);
-    if (startedFromSetupPosition) {
-       SendBoard(&second, backwardMostMove);
-    }
-    for (i = backwardMostMove; i < forwardMostMove; i++) {
-       SendMoveToProgram(i, &second);
-    }
-
-    gameMode = TwoMachinesPlay;
-    pausing = FALSE;
-    ModeHighlight();
-    SetGameInfo();
-    DisplayTwoMachinesTitle();
-    firstMove = TRUE;
-    if ((first.twoMachinesColor[0] == 'w') == WhiteOnMove(forwardMostMove)) {
-       onmove = &first;
-    } else {
-       onmove = &second;
-    }
-
-    SendToProgram(first.computerString, &first);
-    if (first.sendName) {
-      sprintf(buf, "name %s\n", second.tidy);
-      SendToProgram(buf, &first);
-    }
-    SendToProgram(second.computerString, &second);
-    if (second.sendName) {
-      sprintf(buf, "name %s\n", first.tidy);
-      SendToProgram(buf, &second);
-    }
-
-    if (!first.sendTime || !second.sendTime) {
-       ResetClocks();
-       timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
-       timeRemaining[1][forwardMostMove] = blackTimeRemaining;
-    }
-    if (onmove->sendTime) {
-      if (onmove->useColors) {
-       SendToProgram(onmove->other->twoMachinesColor, onmove); /*gnu kludge*/
-      }
-      SendTimeRemaining(onmove, WhiteOnMove(forwardMostMove));
-    }
-    if (onmove->useColors) {
-      SendToProgram(onmove->twoMachinesColor, onmove);
-    }
-    SendToProgram("go\n", onmove);
-    onmove->maybeThinking = TRUE;
-    SetMachineThinkingEnables();
-
-    StartClocks();
-}
-
-void
-TrainingEvent()
-{
-    if (gameMode == Training) {
-      SetTrainingModeOff();
-      gameMode = PlayFromGameFile;
-      DisplayMessage("", _("Training mode off"));
-    } else {
-      gameMode = Training;
-      animateTraining = appData.animate;
-
-      /* make sure we are not already at the end of the game */
-      if (currentMove < forwardMostMove) {
-       SetTrainingModeOn();
-       DisplayMessage("", _("Training mode on"));
-      } else {
-       gameMode = PlayFromGameFile;
-       DisplayError(_("Already at end of game"), 0);
-      }
-    }
-    ModeHighlight();
-}
-
-void
-IcsClientEvent()
-{
-    if (!appData.icsActive) return;
-    switch (gameMode) {
-      case IcsPlayingWhite:
-      case IcsPlayingBlack:
-      case IcsObserving:
-      case IcsIdle:
-      case BeginningOfGame:
-      case IcsExamining:
-       return;
-
-      case EditGame:
-       break;
-
-      case EditPosition:
-       EditPositionDone();
-       break;
-
-      case AnalyzeMode:
-      case AnalyzeFile:
-       ExitAnalyzeMode();
-       break;
-
-      default:
-       EditGameEvent();
-       break;
-    }
-
-    gameMode = IcsIdle;
-    ModeHighlight();
-    return;
-}
-
-
-void
-EditGameEvent()
-{
-    int i;
-
-    switch (gameMode) {
-      case Training:
-       SetTrainingModeOff();
-       break;
-      case MachinePlaysWhite:
-      case MachinePlaysBlack:
-      case BeginningOfGame:
-       SendToProgram("force\n", &first);
-       SetUserThinkingEnables();
-       break;
-      case PlayFromGameFile:
-       (void) StopLoadGameTimer();
-       if (gameFileFP != NULL) {
-           gameFileFP = NULL;
-       }
-       break;
-      case EditPosition:
-       EditPositionDone();
-       break;
-      case AnalyzeMode:
-      case AnalyzeFile:
-       ExitAnalyzeMode();
-       SendToProgram("force\n", &first);
-       break;
-      case TwoMachinesPlay:
-       GameEnds((ChessMove) 0, NULL, GE_PLAYER);
-       ResurrectChessProgram();
-       SetUserThinkingEnables();
-       break;
-      case EndOfGame:
-       ResurrectChessProgram();
-       break;
-      case IcsPlayingBlack:
-      case IcsPlayingWhite:
-       DisplayError(_("Warning: You are still playing a game"), 0);
-       break;
-      case IcsObserving:
-       DisplayError(_("Warning: You are still observing a game"), 0);
-       break;
-      case IcsExamining:
-       DisplayError(_("Warning: You are still examining a game"), 0);
-       break;
-      case IcsIdle:
-       break;
-      case EditGame:
-      default:
-       return;
-    }
-
-    pausing = FALSE;
-    StopClocks();
-    first.offeredDraw = second.offeredDraw = 0;
-
-    if (gameMode == PlayFromGameFile) {
-       whiteTimeRemaining = timeRemaining[0][currentMove];
-       blackTimeRemaining = timeRemaining[1][currentMove];
-       DisplayTitle("");
-    }
-
-    if (gameMode == MachinePlaysWhite ||
-       gameMode == MachinePlaysBlack ||
-       gameMode == TwoMachinesPlay ||
-       gameMode == EndOfGame) {
-       i = forwardMostMove;
-       while (i > currentMove) {
-           SendToProgram("undo\n", &first);
-           i--;
-       }
-       whiteTimeRemaining = timeRemaining[0][currentMove];
-       blackTimeRemaining = timeRemaining[1][currentMove];
-       DisplayBothClocks();
-       if (whiteFlag || blackFlag) {
-           whiteFlag = blackFlag = 0;
-       }
-       DisplayTitle("");
-    }
-
-    gameMode = EditGame;
-    ModeHighlight();
-    SetGameInfo();
-}
-
-
-void
-EditPositionEvent()
-{
-    if (gameMode == EditPosition) {
-       EditGameEvent();
-       return;
-    }
-
-    EditGameEvent();
-    if (gameMode != EditGame) return;
-
-    gameMode = EditPosition;
-    ModeHighlight();
-    SetGameInfo();
-    if (currentMove > 0)
-      CopyBoard(boards[0], boards[currentMove]);
-
-    blackPlaysFirst = !WhiteOnMove(currentMove);
-    ResetClocks();
-    currentMove = forwardMostMove = backwardMostMove = 0;
-    HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
-    DisplayMove(-1);
-}
-
-void
-ExitAnalyzeMode()
-{
-       /* icsEngineAnalyze - possible call from other functions */
-       if (appData.icsEngineAnalyze) {
-           appData.icsEngineAnalyze = FALSE;
-               DisplayMessage("","Close ICS engine analyze...");
-       }
-    if (first.analysisSupport && first.analyzing) {
-      SendToProgram("exit\n", &first);
-      first.analyzing = FALSE;
-    }
-    AnalysisPopDown();
-    thinkOutput[0] = NULLCHAR;
-}
-
-void
-EditPositionDone()
-{
-    startedFromSetupPosition = TRUE;
-    InitChessProgram(&first);
-    SendToProgram("force\n", &first);
-    if (blackPlaysFirst) {
-       strcpy(moveList[0], "");
-       strcpy(parseList[0], "");
-       currentMove = forwardMostMove = backwardMostMove = 1;
-       CopyBoard(boards[1], boards[0]);
-    } else {
-       currentMove = forwardMostMove = backwardMostMove = 0;
-    }
-    SendBoard(&first, forwardMostMove);
-    DisplayTitle("");
-    timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
-    timeRemaining[1][forwardMostMove] = blackTimeRemaining;
-    gameMode = EditGame;
-    ModeHighlight();
-    HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
-}
-
-/* Pause for `ms' milliseconds */
-/* !! Ugh, this is a kludge. Fix it sometime. --tpm */
-void
-TimeDelay(ms)
-     long ms;
-{
-    TimeMark m1, m2;
-
-    GetTimeMark(&m1);
-    do {
-       GetTimeMark(&m2);
-    } while (SubtractTimeMarks(&m2, &m1) < ms);
-}
-
-/* !! Ugh, this is a kludge. Fix it sometime. --tpm */
-void
-SendMultiLineToICS(buf)
-     char *buf;
-{
-    char temp[MSG_SIZ+1], *p;
-    int len;
-
-    len = strlen(buf);
-    if (len > MSG_SIZ)
-      len = MSG_SIZ;
-
-    strncpy(temp, buf, len);
-    temp[len] = 0;
-
-    p = temp;
-    while (*p) {
-       if (*p == '\n' || *p == '\r')
-         *p = ' ';
-       ++p;
-    }
-
-    strcat(temp, "\n");
-    SendToICS(temp);
-    SendToPlayer(temp, strlen(temp));
-}
-
-void
-SetWhiteToPlayEvent()
-{
-    if (gameMode == EditPosition) {
-       blackPlaysFirst = FALSE;
-       DisplayBothClocks();    /* works because currentMove is 0 */
-    } else if (gameMode == IcsExamining) {
-        SendToICS(ics_prefix);
-       SendToICS("tomove white\n");
-    }
-}
-
-void
-SetBlackToPlayEvent()
-{
-    if (gameMode == EditPosition) {
-       blackPlaysFirst = TRUE;
-       currentMove = 1;        /* kludge */
-       DisplayBothClocks();
-       currentMove = 0;
-    } else if (gameMode == IcsExamining) {
-        SendToICS(ics_prefix);
-       SendToICS("tomove black\n");
-    }
-}
-
-void
-EditPositionMenuEvent(selection, x, y)
-     ChessSquare selection;
-     int x, y;
-{
-    char buf[MSG_SIZ];
-
-    if (gameMode != EditPosition && gameMode != IcsExamining) return;
-
-    switch (selection) {
-      case ClearBoard:
-       if (gameMode == IcsExamining && ics_type == ICS_FICS) {
-           SendToICS(ics_prefix);
-           SendToICS("bsetup clear\n");
-       } else if (gameMode == IcsExamining && ics_type == ICS_ICC) {
-           SendToICS(ics_prefix);
-           SendToICS("clearboard\n");
-       } else {
-           for (x = 0; x < BOARD_SIZE; x++) {
-               for (y = 0; y < BOARD_SIZE; y++) {
-                   if (gameMode == IcsExamining) {
-                       if (boards[currentMove][y][x] != EmptySquare) {
-                           sprintf(buf, "%sx@%c%c\n", ics_prefix,
-                                   'a' + x, '1' + y);
-                           SendToICS(buf);
-                       }
-                   } else {
-                       boards[0][y][x] = EmptySquare;
-                   }
-               }
-           }
-       }
-       if (gameMode == EditPosition) {
-           DrawPosition(FALSE, boards[0]);
-       }
-       break;
-
-      case WhitePlay:
-       SetWhiteToPlayEvent();
-       break;
-
-      case BlackPlay:
-       SetBlackToPlayEvent();
-       break;
-
-      case EmptySquare:
-       if (gameMode == IcsExamining) {
-           sprintf(buf, "%sx@%c%c\n", ics_prefix, 'a' + x, '1' + y);
-           SendToICS(buf);
-       } else {
-           boards[0][y][x] = EmptySquare;
-           DrawPosition(FALSE, boards[0]);
-       }
-       break;
-
-      default:
-       if (gameMode == IcsExamining) {
-           sprintf(buf, "%s%c@%c%c\n", ics_prefix,
-                   PieceToChar(selection), 'a' + x, '1' + y);
-           SendToICS(buf);
-       } else {
-           boards[0][y][x] = selection;
-           DrawPosition(FALSE, boards[0]);
-       }
-       break;
-    }
-}
-
-
-void
-DropMenuEvent(selection, x, y)
-     ChessSquare selection;
-     int x, y;
-{
-    ChessMove moveType;
-
-    switch (gameMode) {
-      case IcsPlayingWhite:
-      case MachinePlaysBlack:
-       if (!WhiteOnMove(currentMove)) {
-           DisplayMoveError(_("It is Black's turn"));
-           return;
-       }
-       moveType = WhiteDrop;
-       break;
-      case IcsPlayingBlack:
-      case MachinePlaysWhite:
-       if (WhiteOnMove(currentMove)) {
-           DisplayMoveError(_("It is White's turn"));
-           return;
-       }
-       moveType = BlackDrop;
-       break;
-      case EditGame:
-       moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;
-       break;
-      default:
-       return;
-    }
-
-    if (moveType == BlackDrop && selection < BlackPawn) {
-      selection = (ChessSquare) ((int) selection
-                                + (int) BlackPawn - (int) WhitePawn);
-    }
-    if (boards[currentMove][y][x] != EmptySquare) {
-       DisplayMoveError(_("That square is occupied"));
-       return;
-    }
-
-    FinishMove(moveType, (int) selection, DROP_RANK, x, y, NULLCHAR);
-}
-
-void
-AcceptEvent()
-{
-    /* Accept a pending offer of any kind from opponent */
-
-    if (appData.icsActive) {
-        SendToICS(ics_prefix);
-       SendToICS("accept\n");
-    } else if (cmailMsgLoaded) {
-       if (currentMove == cmailOldMove &&
-           commentList[cmailOldMove] != NULL &&
-           StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
-                  "Black offers a draw" : "White offers a draw")) {
-           TruncateGame();
-           GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
-           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;
-       } else {
-           DisplayError(_("There is no pending offer on this move"), 0);
-           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
-       }
-    } else {
-       /* Not used for offers from chess program */
-    }
-}
-
-void
-DeclineEvent()
-{
-    /* Decline a pending offer of any kind from opponent */
-
-    if (appData.icsActive) {
-        SendToICS(ics_prefix);
-       SendToICS("decline\n");
-    } else if (cmailMsgLoaded) {
-       if (currentMove == cmailOldMove &&
-           commentList[cmailOldMove] != NULL &&
-           StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
-                  "Black offers a draw" : "White offers a draw")) {
-#ifdef NOTDEF
-           AppendComment(cmailOldMove, "Draw declined");
-           DisplayComment(cmailOldMove - 1, "Draw declined");
-#endif /*NOTDEF*/
-       } else {
-           DisplayError(_("There is no pending offer on this move"), 0);
-       }
-    } else {
-       /* Not used for offers from chess program */
-    }
-}
-
-void
-RematchEvent()
-{
-    /* Issue ICS rematch command */
-    if (appData.icsActive) {
-        SendToICS(ics_prefix);
-       SendToICS("rematch\n");
-    }
-}
-
-void
-CallFlagEvent()
-{
-    /* Call your opponent's flag (claim a win on time) */
-    if (appData.icsActive) {
-        SendToICS(ics_prefix);
-       SendToICS("flag\n");
-    } else {
-       switch (gameMode) {
-         default:
-           return;
-         case MachinePlaysWhite:
-           if (whiteFlag) {
-               if (blackFlag)
-                 GameEnds(GameIsDrawn, "Both players ran out of time",
-                          GE_PLAYER);
-               else
-                 GameEnds(BlackWins, "Black wins on time", GE_PLAYER);
-           } else {
-               DisplayError(_("Your opponent is not out of time"), 0);
-           }
-           break;
-         case MachinePlaysBlack:
-           if (blackFlag) {
-               if (whiteFlag)
-                 GameEnds(GameIsDrawn, "Both players ran out of time",
-                          GE_PLAYER);
-               else
-                 GameEnds(WhiteWins, "White wins on time", GE_PLAYER);
-           } else {
-               DisplayError(_("Your opponent is not out of time"), 0);
-           }
-           break;
-       }
-    }
-}
-
-void
-DrawEvent()
-{
-    /* Offer draw or accept pending draw offer from opponent */
-
-    if (appData.icsActive) {
-       /* Note: tournament rules require draw offers to be
-          made after you make your move but before you punch
-          your clock.  Currently ICS doesn't let you do that;
-          instead, you immediately punch your clock after making
-          a move, but you can offer a draw at any time. */
-
-        SendToICS(ics_prefix);
-       SendToICS("draw\n");
-    } else if (cmailMsgLoaded) {
-       if (currentMove == cmailOldMove &&
-           commentList[cmailOldMove] != NULL &&
-           StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?
-                  "Black offers a draw" : "White offers a draw")) {
-           GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);
-           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;
-       } else if (currentMove == cmailOldMove + 1) {
-           char *offer = WhiteOnMove(cmailOldMove) ?
-             "White offers a draw" : "Black offers a draw";
-           AppendComment(currentMove, offer);
-           DisplayComment(currentMove - 1, offer);
-           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_DRAW;
-       } else {
-           DisplayError(_("You must make your move before offering a draw"), 0);
-           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
-       }
-    } else if (first.offeredDraw) {
-       GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);
-    } else {
-        if (first.sendDrawOffers) {
-           SendToProgram("draw\n", &first);
-            userOfferedDraw = TRUE;
-       }
-    }
-}
-
-void
-AdjournEvent()
-{
-    /* Offer Adjourn or accept pending Adjourn offer from opponent */
-
-    if (appData.icsActive) {
-        SendToICS(ics_prefix);
-       SendToICS("adjourn\n");
-    } else {
-       /* Currently GNU Chess doesn't offer or accept Adjourns */
-    }
-}
-
-
-void
-AbortEvent()
-{
-    /* Offer Abort or accept pending Abort offer from opponent */
-
-    if (appData.icsActive) {
-        SendToICS(ics_prefix);
-       SendToICS("abort\n");
-    } else {
-       GameEnds(GameUnfinished, "Game aborted", GE_PLAYER);
-    }
-}
-
-void
-ResignEvent()
-{
-    /* Resign.  You can do this even if it's not your turn. */
-
-    if (appData.icsActive) {
-        SendToICS(ics_prefix);
-       SendToICS("resign\n");
-    } else {
-       switch (gameMode) {
-         case MachinePlaysWhite:
-           GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
-           break;
-         case MachinePlaysBlack:
-           GameEnds(BlackWins, "White resigns", GE_PLAYER);
-           break;
-         case EditGame:
-           if (cmailMsgLoaded) {
-               TruncateGame();
-               if (WhiteOnMove(cmailOldMove)) {
-                   GameEnds(BlackWins, "White resigns", GE_PLAYER);
-               } else {
-                   GameEnds(WhiteWins, "Black resigns", GE_PLAYER);
-               }
-               cmailMoveType[lastLoadGameNumber - 1] = CMAIL_RESIGN;
-           }
-           break;
-         default:
-           break;
-       }
-    }
-}
-
-
-void
-StopObservingEvent()
-{
-    /* Stop observing current games */
-    SendToICS(ics_prefix);
-    SendToICS("unobserve\n");
-}
-
-void
-StopExaminingEvent()
-{
-    /* Stop observing current game */
-    SendToICS(ics_prefix);
-    SendToICS("unexamine\n");
-}
-
-void
-ForwardInner(target)
-     int target;
-{
-    int limit;
-
-    if (appData.debugMode)
-       fprintf(debugFP, "ForwardInner(%d), current %d, forward %d\n",
-               target, currentMove, forwardMostMove);
-
-    if (gameMode == EditPosition)
-      return;
-
-    if (gameMode == PlayFromGameFile && !pausing)
-      PauseEvent();
-
-    if (gameMode == IcsExamining && pausing)
-      limit = pauseExamForwardMostMove;
-    else
-      limit = forwardMostMove;
-
-    if (target > limit) target = limit;
-
-    if (target > 0 && moveList[target - 1][0]) {
-       int fromX, fromY, toX, toY;
-       toX = moveList[target - 1][2] - 'a';
-       toY = moveList[target - 1][3] - '1';
-       if (moveList[target - 1][1] == '@') {
-           if (appData.highlightLastMove) {
-               SetHighlights(-1, -1, toX, toY);
-           }
-       } else {
-           fromX = moveList[target - 1][0] - 'a';
-           fromY = moveList[target - 1][1] - '1';
-           if (target == currentMove + 1) {
-               AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
-           }
-           if (appData.highlightLastMove) {
-               SetHighlights(fromX, fromY, toX, toY);
-           }
-       }
-    }
-    if (gameMode == EditGame || gameMode == AnalyzeMode ||
-       gameMode == Training || gameMode == PlayFromGameFile ||
-       gameMode == AnalyzeFile) {
-       while (currentMove < target) {
-           SendMoveToProgram(currentMove++, &first);
-       }
-    } else {
-       currentMove = target;
-    }
-
-    if (gameMode == EditGame || gameMode == EndOfGame) {
-       whiteTimeRemaining = timeRemaining[0][currentMove];
-       blackTimeRemaining = timeRemaining[1][currentMove];
-    }
-    DisplayBothClocks();
-    DisplayMove(currentMove - 1);
-    DrawPosition(FALSE, boards[currentMove]);
-    HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
-    if (commentList[currentMove] && !matchMode && gameMode != Training) {
-       DisplayComment(currentMove - 1, commentList[currentMove]);
-    }
-}
-
-
-void
-ForwardEvent()
-{
-    if (gameMode == IcsExamining && !pausing) {
-        SendToICS(ics_prefix);
-       SendToICS("forward\n");
-    } else {
-       ForwardInner(currentMove + 1);
-    }
-}
-
-void
-ToEndEvent()
-{
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
-       /* to optimze, we temporarily turn off analysis mode while we feed
-        * the remaining moves to the engine. Otherwise we get analysis output
-        * after each move.
-        */
-        if (first.analysisSupport) {
-         SendToProgram("exit\nforce\n", &first);
-         first.analyzing = FALSE;
-       }
-    }
-
-    if (gameMode == IcsExamining && !pausing) {
-        SendToICS(ics_prefix);
-       SendToICS("forward 999999\n");
-    } else {
-       ForwardInner(forwardMostMove);
-    }
-
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
-       /* we have fed all the moves, so reactivate analysis mode */
-       SendToProgram("analyze\n", &first);
-       first.analyzing = TRUE;
-       /*first.maybeThinking = TRUE;*/
-       first.maybeThinking = FALSE; /* avoid killing GNU Chess */
-    }
-}
-
-void
-BackwardInner(target)
-     int target;
-{
-    if (appData.debugMode)
-       fprintf(debugFP, "BackwardInner(%d), current %d, forward %d\n",
-               target, currentMove, forwardMostMove);
-
-    if (gameMode == EditPosition) return;
-    if (currentMove <= backwardMostMove) {
-       ClearHighlights();
-       DrawPosition(FALSE, boards[currentMove]);
-       return;
-    }
-    if (gameMode == PlayFromGameFile && !pausing)
-      PauseEvent();
-
-    if (moveList[target][0]) {
-       int fromX, fromY, toX, toY;
-       toX = moveList[target][2] - 'a';
-       toY = moveList[target][3] - '1';
-       if (moveList[target][1] == '@') {
-           if (appData.highlightLastMove) {
-               SetHighlights(-1, -1, toX, toY);
-           }
-       } else {
-           fromX = moveList[target][0] - 'a';
-           fromY = moveList[target][1] - '1';
-           if (target == currentMove - 1) {
-               AnimateMove(boards[currentMove], toX, toY, fromX, fromY);
-           }
-           if (appData.highlightLastMove) {
-               SetHighlights(fromX, fromY, toX, toY);
-           }
-       }
-    }
-    if (gameMode == EditGame || gameMode==AnalyzeMode ||
-       gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
-       while (currentMove > target) {
-           SendToProgram("undo\n", &first);
-           currentMove--;
-       }
-    } else {
-       currentMove = target;
-    }
-
-    if (gameMode == EditGame || gameMode == EndOfGame) {
-       whiteTimeRemaining = timeRemaining[0][currentMove];
-       blackTimeRemaining = timeRemaining[1][currentMove];
-    }
-    DisplayBothClocks();
-    DisplayMove(currentMove - 1);
-    DrawPosition(FALSE, boards[currentMove]);
-    HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
-    if (commentList[currentMove] != NULL) {
-       DisplayComment(currentMove - 1, commentList[currentMove]);
-    }
-}
-
-void
-BackwardEvent()
-{
-    if (gameMode == IcsExamining && !pausing) {
-        SendToICS(ics_prefix);
-       SendToICS("backward\n");
-    } else {
-       BackwardInner(currentMove - 1);
-    }
-}
-
-void
-ToStartEvent()
-{
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
-       /* to optimze, we temporarily turn off analysis mode while we undo
-        * all the moves. Otherwise we get analysis output after each undo.
-        */
-        if (first.analysisSupport) {
-         SendToProgram("exit\nforce\n", &first);
-         first.analyzing = FALSE;
-       }
-    }
-
-    if (gameMode == IcsExamining && !pausing) {
-        SendToICS(ics_prefix);
-       SendToICS("backward 999999\n");
-    } else {
-       BackwardInner(backwardMostMove);
-    }
-
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
-       /* we have fed all the moves, so reactivate analysis mode */
-       SendToProgram("analyze\n", &first);
-       first.analyzing = TRUE;
-       /*first.maybeThinking = TRUE;*/
-       first.maybeThinking = FALSE; /* avoid killing GNU Chess */
-    }
-}
-
-void
-ToNrEvent(int to)
-{
-  if (gameMode == PlayFromGameFile && !pausing) PauseEvent();
-  if (to >= forwardMostMove) to = forwardMostMove;
-  if (to <= backwardMostMove) to = backwardMostMove;
-  if (to < currentMove) {
-    BackwardInner(to);
-  } else {
-    ForwardInner(to);
-  }
-}
-
-void
-RevertEvent()
-{
-    if (gameMode != IcsExamining) {
-       DisplayError(_("You are not examining a game"), 0);
-       return;
-    }
-    if (pausing) {
-       DisplayError(_("You can't revert while pausing"), 0);
-       return;
-    }
-    SendToICS(ics_prefix);
-    SendToICS("revert\n");
-}
-
-void
-RetractMoveEvent()
-{
-    switch (gameMode) {
-      case MachinePlaysWhite:
-      case MachinePlaysBlack:
-       if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {
-           DisplayError(_("Wait until your turn,\nor select Move Now"), 0);
-           return;
-       }
-       if (forwardMostMove < 2) return;
-       currentMove = forwardMostMove = forwardMostMove - 2;
-       whiteTimeRemaining = timeRemaining[0][currentMove];
-       blackTimeRemaining = timeRemaining[1][currentMove];
-       DisplayBothClocks();
-       DisplayMove(currentMove - 1);
-       ClearHighlights();/*!! could figure this out*/
-       DrawPosition(FALSE, boards[currentMove]);
-       SendToProgram("remove\n", &first);
-       /*first.maybeThinking = TRUE;*/ /* GNU Chess does not ponder here */
-       break;
-
-      case BeginningOfGame:
-      default:
-       break;
-
-      case IcsPlayingWhite:
-      case IcsPlayingBlack:
-       if (WhiteOnMove(forwardMostMove) == (gameMode == IcsPlayingWhite)) {
-           SendToICS(ics_prefix);
-           SendToICS("takeback 2\n");
-       } else {
-           SendToICS(ics_prefix);
-           SendToICS("takeback 1\n");
-       }
-       break;
-    }
-}
-
-void
-MoveNowEvent()
-{
-    ChessProgramState *cps;
-
-    switch (gameMode) {
-      case MachinePlaysWhite:
-       if (!WhiteOnMove(forwardMostMove)) {
-           DisplayError(_("It is your turn"), 0);
-           return;
-       }
-       cps = &first;
-       break;
-      case MachinePlaysBlack:
-       if (WhiteOnMove(forwardMostMove)) {
-           DisplayError(_("It is your turn"), 0);
-           return;
-       }
-       cps = &first;
-       break;
-      case TwoMachinesPlay:
-       if (WhiteOnMove(forwardMostMove) ==
-           (first.twoMachinesColor[0] == 'w')) {
-           cps = &first;
-       } else {
-           cps = &second;
-       }
-       break;
-      case BeginningOfGame:
-      default:
-       return;
-    }
-    SendToProgram("?\n", cps);
-}
-
-void
-TruncateGameEvent()
-{
-    EditGameEvent();
-    if (gameMode != EditGame) return;
-    TruncateGame();
-}
-
-void
-TruncateGame()
-{
-    if (forwardMostMove > currentMove) {
-       if (gameInfo.resultDetails != NULL) {
-           free(gameInfo.resultDetails);
-           gameInfo.resultDetails = NULL;
-           gameInfo.result = GameUnfinished;
-       }
-       forwardMostMove = currentMove;
-       HistorySet(parseList, backwardMostMove, forwardMostMove,
-                  currentMove-1);
-    }
-}
-
-void
-HintEvent()
-{
-    if (appData.noChessProgram) return;
-    switch (gameMode) {
-      case MachinePlaysWhite:
-       if (WhiteOnMove(forwardMostMove)) {
-           DisplayError(_("Wait until your turn"), 0);
-           return;
-       }
-       break;
-      case BeginningOfGame:
-      case MachinePlaysBlack:
-       if (!WhiteOnMove(forwardMostMove)) {
-           DisplayError(_("Wait until your turn"), 0);
-           return;
-       }
-       break;
-      default:
-       DisplayError(_("No hint available"), 0);
-       return;
-    }
-    SendToProgram("hint\n", &first);
-    hintRequested = TRUE;
-}
-
-void
-BookEvent()
-{
-    if (appData.noChessProgram) return;
-    switch (gameMode) {
-      case MachinePlaysWhite:
-       if (WhiteOnMove(forwardMostMove)) {
-           DisplayError(_("Wait until your turn"), 0);
-           return;
-       }
-       break;
-      case BeginningOfGame:
-      case MachinePlaysBlack:
-       if (!WhiteOnMove(forwardMostMove)) {
-           DisplayError(_("Wait until your turn"), 0);
-           return;
-       }
-       break;
-      case EditPosition:
-       EditPositionDone();
-       break;
-      case TwoMachinesPlay:
-       return;
-      default:
-       break;
-    }
-    SendToProgram("bk\n", &first);
-    bookOutput[0] = NULLCHAR;
-    bookRequested = TRUE;
-}
-
-void
-AboutGameEvent()
-{
-    char *tags = PGNTags(&gameInfo);
-    TagsPopUp(tags, CmailMsg());
-    free(tags);
-}
-
-/* end button procedures */
-
-void
-PrintPosition(fp, move)
-     FILE *fp;
-     int move;
-{
-    int i, j;
-
-    for (i = BOARD_SIZE - 1; i >= 0; i--) {
-       for (j = 0; j < BOARD_SIZE; j++) {
-           char c = PieceToChar(boards[move][i][j]);
-           fputc(c == 'x' ? '.' : c, fp);
-           fputc(j == BOARD_SIZE - 1 ? '\n' : ' ', fp);
-       }
-    }
-    if ((gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0))
-      fprintf(fp, "white to play\n");
-    else
-      fprintf(fp, "black to play\n");
-}
-
-void
-PrintOpponents(fp)
-     FILE *fp;
-{
-    if (gameInfo.white != NULL) {
-       fprintf(fp, "\t%s vs. %s\n", gameInfo.white, gameInfo.black);
-    } else {
-       fprintf(fp, "\n");
-    }
-}
-
-/* Find last component of program's own name, using some heuristics */
-void
-TidyProgramName(prog, host, buf)
-     char *prog, *host, buf[MSG_SIZ];
-{
-    char *p, *q;
-    int local = (strcmp(host, "localhost") == 0);
-    while (!local && (p = strchr(prog, ';')) != NULL) {
-       p++;
-       while (*p == ' ') p++;
-       prog = p;
-    }
-    if (*prog == '"' || *prog == '\'') {
-       q = strchr(prog + 1, *prog);
-    } else {
-       q = strchr(prog, ' ');
-    }
-    if (q == NULL) q = prog + strlen(prog);
-    p = q;
-    while (p >= prog && *p != '/' && *p != '\\') p--;
-    p++;
-    if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4;
-    memcpy(buf, p, q - p);
-    buf[q - p] = NULLCHAR;
-    if (!local) {
-       strcat(buf, "@");
-       strcat(buf, host);
-    }
-}
-
-char *
-TimeControlTagValue()
-{
-    char buf[MSG_SIZ];
-    if (!appData.clockMode) {
-       strcpy(buf, "-");
-    } else if (movesPerSession > 0) {
-       sprintf(buf, "%d/%ld", movesPerSession, timeControl/1000);
-    } else if (timeIncrement == 0) {
-       sprintf(buf, "%ld", timeControl/1000);
-    } else {
-       sprintf(buf, "%ld+%ld", timeControl/1000, timeIncrement/1000);
-    }
-    return StrSave(buf);
-}
-
-void
-SetGameInfo()
-{
-    /* This routine is used only for certain modes */
-    VariantClass v = gameInfo.variant;
-    ClearGameInfo(&gameInfo);
-    gameInfo.variant = v;
-
-    switch (gameMode) {
-      case MachinePlaysWhite:
-       gameInfo.event = StrSave("Computer chess game");
-       gameInfo.site = StrSave(HostName());
-       gameInfo.date = PGNDate();
-       gameInfo.round = StrSave("-");
-       gameInfo.white = StrSave(first.tidy);
-       gameInfo.black = StrSave(UserName());
-       gameInfo.timeControl = TimeControlTagValue();
-       break;
-
-      case MachinePlaysBlack:
-       gameInfo.event = StrSave("Computer chess game");
-       gameInfo.site = StrSave(HostName());
-       gameInfo.date = PGNDate();
-       gameInfo.round = StrSave("-");
-       gameInfo.white = StrSave(UserName());
-       gameInfo.black = StrSave(first.tidy);
-       gameInfo.timeControl = TimeControlTagValue();
-       break;
-
-      case TwoMachinesPlay:
-       gameInfo.event = StrSave("Computer chess game");
-       gameInfo.site = StrSave(HostName());
-       gameInfo.date = PGNDate();
-       if (matchGame > 0) {
-           char buf[MSG_SIZ];
-           sprintf(buf, "%d", matchGame);
-           gameInfo.round = StrSave(buf);
-       } else {
-           gameInfo.round = StrSave("-");
-       }
-       if (first.twoMachinesColor[0] == 'w') {
-           gameInfo.white = StrSave(first.tidy);
-           gameInfo.black = StrSave(second.tidy);
-       } else {
-           gameInfo.white = StrSave(second.tidy);
-           gameInfo.black = StrSave(first.tidy);
-       }
-       gameInfo.timeControl = TimeControlTagValue();
-       break;
-
-      case EditGame:
-       gameInfo.event = StrSave("Edited game");
-       gameInfo.site = StrSave(HostName());
-       gameInfo.date = PGNDate();
-       gameInfo.round = StrSave("-");
-       gameInfo.white = StrSave("-");
-       gameInfo.black = StrSave("-");
-       break;
-
-      case EditPosition:
-       gameInfo.event = StrSave("Edited position");
-       gameInfo.site = StrSave(HostName());
-       gameInfo.date = PGNDate();
-       gameInfo.round = StrSave("-");
-       gameInfo.white = StrSave("-");
-       gameInfo.black = StrSave("-");
-       break;
-
-      case IcsPlayingWhite:
-      case IcsPlayingBlack:
-      case IcsObserving:
-      case IcsExamining:
-       break;
-
-      case PlayFromGameFile:
-       gameInfo.event = StrSave("Game from non-PGN file");
-       gameInfo.site = StrSave(HostName());
-       gameInfo.date = PGNDate();
-       gameInfo.round = StrSave("-");
-       gameInfo.white = StrSave("?");
-       gameInfo.black = StrSave("?");
-       break;
-
-      default:
-       break;
-    }
-}
-
-void
-ReplaceComment(index, text)
-     int index;
-     char *text;
-{
-    int len;
-
-    while (*text == '\n') text++;
-    len = strlen(text);
-    while (len > 0 && text[len - 1] == '\n') len--;
-
-    if (commentList[index] != NULL)
-      free(commentList[index]);
-
-    if (len == 0) {
-       commentList[index] = NULL;
-       return;
-    }
-    commentList[index] = (char *) malloc(len + 2);
-    strncpy(commentList[index], text, len);
-    commentList[index][len] = '\n';
-    commentList[index][len + 1] = NULLCHAR;
-}
-
-void
-CrushCRs(text)
-     char *text;
-{
-  char *p = text;
-  char *q = text;
-  char ch;
-
-  do {
-    ch = *p++;
-    if (ch == '\r') continue;
-    *q++ = ch;
-  } while (ch != '\0');
-}
-
-void
-AppendComment(index, text)
-     int index;
-     char *text;
-{
-    int oldlen, len;
-    char *old;
-
-    CrushCRs(text);
-    while (*text == '\n') text++;
-    len = strlen(text);
-    while (len > 0 && text[len - 1] == '\n') len--;
-
-    if (len == 0) return;
-
-    if (commentList[index] != NULL) {
-       old = commentList[index];
-       oldlen = strlen(old);
-       commentList[index] = (char *) malloc(oldlen + len + 2);
-       strcpy(commentList[index], old);
-       free(old);
-       strncpy(&commentList[index][oldlen], text, len);
-       commentList[index][oldlen + len] = '\n';
-       commentList[index][oldlen + len + 1] = NULLCHAR;
-    } else {
-       commentList[index] = (char *) malloc(len + 2);
-       strncpy(commentList[index], text, len);
-       commentList[index][len] = '\n';
-       commentList[index][len + 1] = NULLCHAR;
-    }
-}
-
-void
-SendToProgram(message, cps)
-     char *message;
-     ChessProgramState *cps;
-{
-    int count, outCount, error;
-    char buf[MSG_SIZ];
-
-    if (cps->pr == NULL) return;
-    Attention(cps);
-
-    if (appData.debugMode) {
-       TimeMark now;
-       GetTimeMark(&now);
-       fprintf(debugFP, "%ld >%-6s: %s",
-               SubtractTimeMarks(&now, &programStartTime),
-               cps->which, message);
-    }
-
-    count = strlen(message);
-    outCount = OutputToProcess(cps->pr, message, count, &error);
-    if (outCount < count && !exiting) {
-       sprintf(buf, _("Error writing to %s chess program"), cps->which);
-       DisplayFatalError(buf, error, 1);
-    }
-}
-
-void
-ReceiveFromProgram(isr, closure, message, count, error)
-     InputSourceRef isr;
-     VOIDSTAR closure;
-     char *message;
-     int count;
-     int error;
-{
-    char *end_str;
-    char buf[MSG_SIZ];
-    ChessProgramState *cps = (ChessProgramState *)closure;
-
-    if (isr != cps->isr) return; /* Killed intentionally */
-    if (count <= 0) {
-       if (count == 0) {
-           sprintf(buf,
-                   _("Error: %s chess program (%s) exited unexpectedly"),
-                   cps->which, cps->program);
-           RemoveInputSource(cps->isr);
-           DisplayFatalError(buf, 0, 1);
-       } else {
-           sprintf(buf,
-                   _("Error reading from %s chess program (%s)"),
-                   cps->which, cps->program);
-           RemoveInputSource(cps->isr);
-           DisplayFatalError(buf, error, 1);
-       }
-       GameEnds((ChessMove) 0, NULL, GE_PLAYER);
-       return;
-    }
-
-    if ((end_str = strchr(message, '\r')) != NULL)
-      *end_str = NULLCHAR;
-    if ((end_str = strchr(message, '\n')) != NULL)
-      *end_str = NULLCHAR;
-
-    if (appData.debugMode) {
-       TimeMark now;
-       GetTimeMark(&now);
-       fprintf(debugFP, "%ld <%-6s: %s\n",
-               SubtractTimeMarks(&now, &programStartTime),
-               cps->which, message);
-    }
-       /* if icsEngineAnalyze is active we block all
-          whisper and kibitz output, because nobody want
-          see this
-        */
-       if (appData.icsEngineAnalyze) {
-               if (strstr(message, "whisper") != NULL ||
-                   strstr(message, "kibitz") != NULL ||
-                       strstr(message, "tellics") != NULL) return;
-               HandleMachineMove(message, cps);
-       } else {
-               HandleMachineMove(message, cps);
-       }
-}
-
-
-void
-SendTimeControl(cps, mps, tc, inc, sd, st)
-     ChessProgramState *cps;
-     int mps, inc, sd, st;
-     long tc;
-{
-    char buf[MSG_SIZ];
-    int seconds = (tc / 1000) % 60;
-
-    if (st > 0) {
-      /* Set exact time per move, normally using st command */
-      if (cps->stKludge) {
-       /* GNU Chess 4 has no st command; uses level in a nonstandard way */
-       seconds = st % 60;
-       if (seconds == 0) {
-         sprintf(buf, "level 1 %d\n", st/60);
-       } else {
-         sprintf(buf, "level 1 %d:%02d\n", st/60, seconds);
-       }
-      } else {
-       sprintf(buf, "st %d\n", st);
-      }
-    } else {
-      /* Set conventional or incremental time control, using level command */
-      if (seconds == 0) {
-       /* Note old gnuchess bug -- minutes:seconds used to not work.
-          Fixed in later versions, but still avoid :seconds
-          when seconds is 0. */
-       sprintf(buf, "level %d %ld %d\n", mps, tc/60000, inc/1000);
-      } else {
-       sprintf(buf, "level %d %ld:%02d %d\n", mps, tc/60000,
-               seconds, inc/1000);
-      }
-    }
-    SendToProgram(buf, cps);
-
-    /* Orthoganally (except for GNU Chess 4), limit time to st seconds */
-    /* Orthogonally, limit search to given depth */
-    if (sd > 0) {
-      if (cps->sdKludge) {
-       sprintf(buf, "depth\n%d\n", sd);
-      } else {
-       sprintf(buf, "sd %d\n", sd);
-      }
-      SendToProgram(buf, cps);
-    }
-}
-
-void
-SendTimeRemaining(cps, machineWhite)
-     ChessProgramState *cps;
-     int /*boolean*/ machineWhite;
-{
-    char message[MSG_SIZ];
-    long time, otime;
-
-    /* Note: this routine must be called when the clocks are stopped
-       or when they have *just* been set or switched; otherwise
-       it will be off by the time since the current tick started.
-    */
-    if (machineWhite) {
-       time = whiteTimeRemaining / 10;
-       otime = blackTimeRemaining / 10;
-    } else {
-       time = blackTimeRemaining / 10;
-       otime = whiteTimeRemaining / 10;
-    }
-    if (time <= 0) time = 1;
-    if (otime <= 0) otime = 1;
-
-    sprintf(message, "time %ld\notim %ld\n", time, otime);
-    SendToProgram(message, cps);
-}
-
-int
-BoolFeature(p, name, loc, cps)
-     char **p;
-     char *name;
-     int *loc;
-     ChessProgramState *cps;
-{
-  char buf[MSG_SIZ];
-  int len = strlen(name);
-  int val;
-  if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {
-    (*p) += len + 1;
-    sscanf(*p, "%d", &val);
-    *loc = (val != 0);
-    while (**p && **p != ' ') (*p)++;
-    sprintf(buf, "accepted %s\n", name);
-    SendToProgram(buf, cps);
-    return TRUE;
-  }
-  return FALSE;
-}
-
-int
-IntFeature(p, name, loc, cps)
-     char **p;
-     char *name;
-     int *loc;
-     ChessProgramState *cps;
-{
-  char buf[MSG_SIZ];
-  int len = strlen(name);
-  if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {
-    (*p) += len + 1;
-    sscanf(*p, "%d", loc);
-    while (**p && **p != ' ') (*p)++;
-    sprintf(buf, "accepted %s\n", name);
-    SendToProgram(buf, cps);
-    return TRUE;
-  }
-  return FALSE;
-}
-
-int
-StringFeature(p, name, loc, cps)
-     char **p;
-     char *name;
-     char loc[];
-     ChessProgramState *cps;
-{
-  char buf[MSG_SIZ];
-  int len = strlen(name);
-  if (strncmp((*p), name, len) == 0
-      && (*p)[len] == '=' && (*p)[len+1] == '\"') {
-    (*p) += len + 2;
-    sscanf(*p, "%[^\"]", loc);
-    while (**p && **p != '\"') (*p)++;
-    if (**p == '\"') (*p)++;
-    sprintf(buf, "accepted %s\n", name);
-    SendToProgram(buf, cps);
-    return TRUE;
-  }
-  return FALSE;
-}
-
-void
-FeatureDone(cps, val)
-     ChessProgramState* cps;
-     int val;
-{
-  DelayedEventCallback cb = GetDelayedEvent();
-  if ((cb == InitBackEnd3 && cps == &first) ||
-      (cb == TwoMachinesEventIfReady && cps == &second)) {
-    CancelDelayedEvent();
-    ScheduleDelayedEvent(cb, val ? 1 : 3600000);
-  }
-  cps->initDone = val;
-}
-
-/* Parse feature command from engine */
-void
-ParseFeatures(args, cps)
-     char* args;
-     ChessProgramState *cps;
-{
-  char *p = args;
-  char *q;
-  int val;
-  char buf[MSG_SIZ];
-
-  for (;;) {
-    while (*p == ' ') p++;
-    if (*p == NULLCHAR) return;
-
-    if (BoolFeature(&p, "setboard", &cps->useSetboard, cps)) continue;
-    if (BoolFeature(&p, "time", &cps->sendTime, cps)) continue;
-    if (BoolFeature(&p, "draw", &cps->sendDrawOffers, cps)) continue;
-    if (BoolFeature(&p, "sigint", &cps->useSigint, cps)) continue;
-    if (BoolFeature(&p, "sigterm", &cps->useSigterm, cps)) continue;
-    if (BoolFeature(&p, "reuse", &val, cps)) {
-      /* Engine can disable reuse, but can't enable it if user said no */
-      if (!val) cps->reuse = FALSE;
-      continue;
-    }
-    if (BoolFeature(&p, "analyze", &cps->analysisSupport, cps)) continue;
-    if (StringFeature(&p, "myname", &cps->tidy, cps)) {
-      if (gameMode == TwoMachinesPlay) {
-       DisplayTwoMachinesTitle();
-      } else {
-       DisplayTitle("");
-      }
-      continue;
-    }
-    if (StringFeature(&p, "variants", &cps->variants, cps)) continue;
-    if (BoolFeature(&p, "san", &cps->useSAN, cps)) continue;
-    if (BoolFeature(&p, "ping", &cps->usePing, cps)) continue;
-    if (BoolFeature(&p, "playother", &cps->usePlayother, cps)) continue;
-    if (BoolFeature(&p, "colors", &cps->useColors, cps)) continue;
-    if (BoolFeature(&p, "usermove", &cps->useUsermove, cps)) continue;
-    if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;
-    if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;
-    if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */
-    if (IntFeature(&p, "done", &val, cps)) {
-      FeatureDone(cps, val);
-      continue;
-    }
-
-    /* unknown feature: complain and skip */
-    q = p;
-    while (*q && *q != '=') q++;
-    sprintf(buf, "rejected %.*s\n", q-p, p);
-    SendToProgram(buf, cps);
-    p = q;
-    if (*p == '=') {
-      p++;
-      if (*p == '\"') {
-       p++;
-       while (*p && *p != '\"') p++;
-       if (*p == '\"') p++;
-      } else {
-       while (*p && *p != ' ') p++;
-      }
-    }
-  }
-
-}
-
-void
-PeriodicUpdatesEvent(newState)
-     int newState;
-{
-    if (newState == appData.periodicUpdates)
-      return;
-
-    appData.periodicUpdates=newState;
-
-    /* Display type changes, so update it now */
-    DisplayAnalysis();
-
-    /* Get the ball rolling again... */
-    if (newState) {
-       AnalysisPeriodicEvent(1);
-       StartAnalysisClock();
-    }
-}
-
-void
-PonderNextMoveEvent(newState)
-     int newState;
-{
-    if (newState == appData.ponderNextMove) return;
-    if (gameMode == EditPosition) EditPositionDone();
-    if (newState) {
-       SendToProgram("hard\n", &first);
-       if (gameMode == TwoMachinesPlay) {
-           SendToProgram("hard\n", &second);
-       }
-    } else {
-       SendToProgram("easy\n", &first);
-       thinkOutput[0] = NULLCHAR;
-       if (gameMode == TwoMachinesPlay) {
-           SendToProgram("easy\n", &second);
-       }
-    }
-    appData.ponderNextMove = newState;
-}
-
-void
-ShowThinkingEvent(newState)
-     int newState;
-{
-    if (newState == appData.showThinking) return;
-    if (gameMode == EditPosition) EditPositionDone();
-    if (newState) {
-       SendToProgram("post\n", &first);
-       if (gameMode == TwoMachinesPlay) {
-           SendToProgram("post\n", &second);
-       }
-    } else {
-       SendToProgram("nopost\n", &first);
-       thinkOutput[0] = NULLCHAR;
-       if (gameMode == TwoMachinesPlay) {
-           SendToProgram("nopost\n", &second);
-       }
-    }
-    appData.showThinking = newState;
-}
-
-void
-AskQuestionEvent(title, question, replyPrefix, which)
-     char *title; char *question; char *replyPrefix; char *which;
-{
-  ProcRef pr = (which[0] == '1') ? first.pr : second.pr;
-  if (pr == NoProc) return;
-  AskQuestion(title, question, replyPrefix, pr);
-}
-
-void
-DisplayMove(moveNumber)
-     int moveNumber;
-{
-    char message[MSG_SIZ];
-    char res[MSG_SIZ];
-    char cpThinkOutput[MSG_SIZ];
-
-    if (moveNumber == forwardMostMove - 1 ||
-       gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
-
-       strcpy(cpThinkOutput, thinkOutput);
-       if (strchr(cpThinkOutput, '\n'))
-         *strchr(cpThinkOutput, '\n') = NULLCHAR;
-    } else {
-       *cpThinkOutput = NULLCHAR;
-    }
-
-    if (moveNumber == forwardMostMove - 1 &&
-       gameInfo.resultDetails != NULL) {
-       if (gameInfo.resultDetails[0] == NULLCHAR) {
-           sprintf(res, " %s", PGNResult(gameInfo.result));
-       } else {
-           sprintf(res, " {%s} %s",
-                   gameInfo.resultDetails, PGNResult(gameInfo.result));
-       }
-    } else {
-       res[0] = NULLCHAR;
-    }
-
-    if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
-       DisplayMessage(res, cpThinkOutput);
-    } else {
-       sprintf(message, "%d.%s%s%s", moveNumber / 2 + 1,
-               WhiteOnMove(moveNumber) ? " " : ".. ",
-               parseList[moveNumber], res);
-       DisplayMessage(message, cpThinkOutput);
-    }
-}
-
-void
-DisplayAnalysisText(text)
-     char *text;
-{
-    char buf[MSG_SIZ];
-
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
-               || appData.icsEngineAnalyze) {
-       sprintf(buf, "Analysis (%s)", first.tidy);
-       AnalysisPopUp(buf, text);
-    }
-}
-
-static int
-only_one_move(str)
-     char *str;
-{
-    while (*str && isspace(*str)) ++str;
-    while (*str && !isspace(*str)) ++str;
-    if (!*str) return 1;
-    while (*str && isspace(*str)) ++str;
-    if (!*str) return 1;
-    return 0;
-}
-
-void
-DisplayAnalysis()
-{
-    char buf[MSG_SIZ];
-    double nps;
-    static char *xtra[] = { "", " (--)", " (++)" };
-    int h, m, s, cs;
-
-    if (programStats.time == 0) {
-       programStats.time = 1;
-    }
-
-    if (programStats.got_only_move) {
-       strcpy(buf, programStats.movelist);
-    } else {
-       nps = (u64ToDouble(programStats.nodes) /
-             ((double)programStats.time /100.0));
-
-       cs = programStats.time % 100;
-       s = programStats.time / 100;
-       h = (s / (60*60));
-       s = s - h*60*60;
-       m = (s/60);
-       s = s - m*60;
-
-       if (programStats.moves_left > 0 && appData.periodicUpdates) {
-         if (programStats.move_name[0] != NULLCHAR) {
-           sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: "u64Display" NPS: %d\nTime: %02d:%02d:%02d.%02d",
-                   programStats.depth,
-                   programStats.nr_moves-programStats.moves_left,
-                   programStats.nr_moves, programStats.move_name,
-                   ((float)programStats.score)/100.0, programStats.movelist,
-                   only_one_move(programStats.movelist)?
-                   xtra[programStats.got_fail] : "",
-                   (u64)programStats.nodes, (int)nps, h, m, s, cs);
-         } else {
-           sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: "u64Display" NPS: %d\nTime: %02d:%02d:%02d.%02d",
-                   programStats.depth,
-                   programStats.nr_moves-programStats.moves_left,
-                   programStats.nr_moves, ((float)programStats.score)/100.0,
-                   programStats.movelist,
-                   only_one_move(programStats.movelist)?
-                   xtra[programStats.got_fail] : "",
-                   (u64)programStats.nodes, (int)nps, h, m, s, cs);
-         }
-       } else {
-           sprintf(buf, "depth=%d %+.2f %s%s\nNodes: "u64Display" NPS: %d\nTime: %02d:%02d:%02d.%02d",
-                   programStats.depth,
-                   ((float)programStats.score)/100.0,
-                   programStats.movelist,
-                   only_one_move(programStats.movelist)?
-                   xtra[programStats.got_fail] : "",
-                   (u64)programStats.nodes, (int)nps, h, m, s, cs);
-       }
-    }
-    DisplayAnalysisText(buf);
-}
-
-void
-DisplayComment(moveNumber, text)
-     int moveNumber;
-     char *text;
-{
-    char title[MSG_SIZ];
-
-    if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
-       strcpy(title, "Comment");
-    } else {
-       sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,
-               WhiteOnMove(moveNumber) ? " " : ".. ",
-               parseList[moveNumber]);
-    }
-
-    CommentPopUp(title, text);
-}
-
-/* This routine sends a ^C interrupt to gnuchess, to awaken it if it
- * might be busy thinking or pondering.  It can be omitted if your
- * gnuchess is configured to stop thinking immediately on any user
- * input.  However, that gnuchess feature depends on the FIONREAD
- * ioctl, which does not work properly on some flavors of Unix.
- */
-void
-Attention(cps)
-     ChessProgramState *cps;
-{
-#if ATTENTION
-    if (!cps->useSigint) return;
-    if (appData.noChessProgram || (cps->pr == NoProc)) return;
-    switch (gameMode) {
-      case MachinePlaysWhite:
-      case MachinePlaysBlack:
-      case TwoMachinesPlay:
-      case IcsPlayingWhite:
-      case IcsPlayingBlack:
-      case AnalyzeMode:
-      case AnalyzeFile:
-       /* Skip if we know it isn't thinking */
-       if (!cps->maybeThinking) return;
-       if (appData.debugMode)
-         fprintf(debugFP, "Interrupting %s\n", cps->which);
-       InterruptChildProcess(cps->pr);
-       cps->maybeThinking = FALSE;
-       break;
-      default:
-       break;
-    }
-#endif /*ATTENTION*/
-}
-
-int
-CheckFlags()
-{
-    if (whiteTimeRemaining <= 0) {
-       if (!whiteFlag) {
-           whiteFlag = TRUE;
-           if (appData.icsActive) {
-               if (appData.autoCallFlag &&
-                   gameMode == IcsPlayingBlack && !blackFlag) {
-                 SendToICS(ics_prefix);
-                 SendToICS("flag\n");
-               }
-           } else {
-               if (blackFlag) {
-                   DisplayTitle(_("Both flags fell"));
-               } else {
-                   DisplayTitle(_("White's flag fell"));
-                   if (appData.autoCallFlag) {
-                       GameEnds(BlackWins, "Black wins on time", GE_XBOARD);
-                       return TRUE;
-                   }
-               }
-           }
-       }
-    }
-    if (blackTimeRemaining <= 0) {
-       if (!blackFlag) {
-           blackFlag = TRUE;
-           if (appData.icsActive) {
-               if (appData.autoCallFlag &&
-                   gameMode == IcsPlayingWhite && !whiteFlag) {
-                 SendToICS(ics_prefix);
-                 SendToICS("flag\n");
-               }
-           } else {
-               if (whiteFlag) {
-                   DisplayTitle(_("Both flags fell"));
-               } else {
-                   DisplayTitle(_("Black's flag fell"));
-                   if (appData.autoCallFlag) {
-                       GameEnds(WhiteWins, "White wins on time", GE_XBOARD);
-                       return TRUE;
-                   }
-               }
-           }
-       }
-    }
-    return FALSE;
-}
-
-void
-CheckTimeControl()
-{
-    if (!appData.clockMode || appData.icsActive ||
-       gameMode == PlayFromGameFile || forwardMostMove == 0) return;
-
-    if (timeIncrement >= 0) {
-       if (WhiteOnMove(forwardMostMove)) {
-           blackTimeRemaining += timeIncrement;
-       } else {
-           whiteTimeRemaining += timeIncrement;
-       }
-    }
-    /*
-     * add time to clocks when time control is achieved
-     */
-    if (movesPerSession) {
-      switch ((forwardMostMove + 1) % (movesPerSession * 2)) {
-      case 0:
-       /* White made time control */
-       whiteTimeRemaining += timeControl;
-       break;
-      case 1:
-       /* Black made time control */
-       blackTimeRemaining += timeControl;
-       break;
-      default:
-       break;
-      }
-    }
-}
-
-void
-DisplayBothClocks()
-{
-    int wom = gameMode == EditPosition ?
-      !blackPlaysFirst : WhiteOnMove(currentMove);
-    DisplayWhiteClock(whiteTimeRemaining, wom);
-    DisplayBlackClock(blackTimeRemaining, !wom);
-}
-
-
-/* Timekeeping seems to be a portability nightmare.  I think everyone
-   has ftime(), but I'm really not sure, so I'm including some ifdefs
-   to use other calls if you don't.  Clocks will be less accurate if
-   you have neither ftime nor gettimeofday.
-*/
-
-/* Get the current time as a TimeMark */
-void
-GetTimeMark(tm)
-     TimeMark *tm;
-{
-#if HAVE_GETTIMEOFDAY
-
-    struct timeval timeVal;
-    struct timezone timeZone;
-
-    gettimeofday(&timeVal, &timeZone);
-    tm->sec = (long) timeVal.tv_sec;
-    tm->ms = (int) (timeVal.tv_usec / 1000L);
-
-#else /*!HAVE_GETTIMEOFDAY*/
-#if HAVE_FTIME
-
-#include <sys/timeb.h>
-    struct timeb timeB;
-
-    ftime(&timeB);
-    tm->sec = (long) timeB.time;
-    tm->ms = (int) timeB.millitm;
-
-#else /*!HAVE_FTIME && !HAVE_GETTIMEOFDAY*/
-    tm->sec = (long) time(NULL);
-    tm->ms = 0;
-#endif
-#endif
-}
-
-/* Return the difference in milliseconds between two
-   time marks.  We assume the difference will fit in a long!
-*/
-long
-SubtractTimeMarks(tm2, tm1)
-     TimeMark *tm2, *tm1;
-{
-    return 1000L*(tm2->sec - tm1->sec) +
-           (long) (tm2->ms - tm1->ms);
-}
-
-
-/*
- * Code to manage the game clocks.
- *
- * In tournament play, black starts the clock and then white makes a move.
- * We give the human user a slight advantage if he is playing white---the
- * clocks don't run until he makes his first move, so it takes zero time.
- * Also, we don't account for network lag, so we could get out of sync
- * with GNU Chess's clock -- but then, referees are always right.
- */
-
-static TimeMark tickStartTM;
-static long intendedTickLength;
-
-long
-NextTickLength(timeRemaining)
-     long timeRemaining;
-{
-    long nominalTickLength, nextTickLength;
-
-    if (timeRemaining > 0L && timeRemaining <= 10000L)
-      nominalTickLength = 100L;
-    else
-      nominalTickLength = 1000L;
-    nextTickLength = timeRemaining % nominalTickLength;
-    if (nextTickLength <= 0) nextTickLength += nominalTickLength;
-
-    return nextTickLength;
-}
-
-/* Stop clocks and reset to a fresh time control */
-void
-ResetClocks()
-{
-    (void) StopClockTimer();
-    if (appData.icsActive) {
-       whiteTimeRemaining = blackTimeRemaining = 0;
-    } else {
-       whiteTimeRemaining = blackTimeRemaining = timeControl;
-    }
-    if (whiteFlag || blackFlag) {
-       DisplayTitle("");
-       whiteFlag = blackFlag = FALSE;
-    }
-    DisplayBothClocks();
-}
-
-#define FUDGE 25 /* 25ms = 1/40 sec; should be plenty even for 50 Hz clocks */
-
-/* Decrement running clock by amount of time that has passed */
-void
-DecrementClocks()
-{
-    long timeRemaining;
-    long lastTickLength, fudge;
-    TimeMark now;
-
-    if (!appData.clockMode) return;
-    if (gameMode==AnalyzeMode || gameMode == AnalyzeFile) return;
-
-    GetTimeMark(&now);
-
-    lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
-
-    /* Fudge if we woke up a little too soon */
-    fudge = intendedTickLength - lastTickLength;
-    if (fudge < 0 || fudge > FUDGE) fudge = 0;
-
-    if (WhiteOnMove(forwardMostMove)) {
-       timeRemaining = whiteTimeRemaining -= lastTickLength;
-       DisplayWhiteClock(whiteTimeRemaining - fudge,
-                         WhiteOnMove(currentMove));
-    } else {
-       timeRemaining = blackTimeRemaining -= lastTickLength;
-       DisplayBlackClock(blackTimeRemaining - fudge,
-                         !WhiteOnMove(currentMove));
-    }
-
-    if (CheckFlags()) return;
-
-    tickStartTM = now;
-    intendedTickLength = NextTickLength(timeRemaining - fudge) + fudge;
-    StartClockTimer(intendedTickLength);
-
-    /* if the time remaining has fallen below the alarm threshold, sound the
-     * alarm. if the alarm has sounded and (due to a takeback or time control
-     * with increment) the time remaining has increased to a level above the
-     * threshold, reset the alarm so it can sound again.
-     */
-
-    if (appData.icsActive && appData.icsAlarm) {
-
-       /* make sure we are dealing with the user's clock */
-       if (!( ((gameMode == IcsPlayingWhite) && WhiteOnMove(currentMove)) ||
-              ((gameMode == IcsPlayingBlack) && !WhiteOnMove(currentMove))
-          )) return;
-
-       if (alarmSounded && (timeRemaining > appData.icsAlarmTime)) {
-           alarmSounded = FALSE;
-       } else if (!alarmSounded && (timeRemaining <= appData.icsAlarmTime)) {
-           PlayAlarmSound();
-           alarmSounded = TRUE;
-       }
-    }
-}
-
-
-/* A player has just moved, so stop the previously running
-   clock and (if in clock mode) start the other one.
-   We redisplay both clocks in case we're in ICS mode, because
-   ICS gives us an update to both clocks after every move.
-   Note that this routine is called *after* forwardMostMove
-   is updated, so the last fractional tick must be subtracted
-   from the color that is *not* on move now.
-*/
-void
-SwitchClocks()
-{
-    long lastTickLength;
-    TimeMark now;
-    int flagged = FALSE;
-
-    GetTimeMark(&now);
-
-    if (StopClockTimer() && appData.clockMode) {
-       lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
-       if (WhiteOnMove(forwardMostMove)) {
-           blackTimeRemaining -= lastTickLength;
-       } else {
-           whiteTimeRemaining -= lastTickLength;
-       }
-       flagged = CheckFlags();
-    }
-    CheckTimeControl();
-
-    if (flagged || !appData.clockMode) return;
-
-    switch (gameMode) {
-      case MachinePlaysBlack:
-      case MachinePlaysWhite:
-      case BeginningOfGame:
-       if (pausing) return;
-       break;
-
-      case EditGame:
-      case PlayFromGameFile:
-      case IcsExamining:
-       return;
-
-      default:
-       break;
-    }
-
-    tickStartTM = now;
-    intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
-      whiteTimeRemaining : blackTimeRemaining);
-    StartClockTimer(intendedTickLength);
-}
-
-
-/* Stop both clocks */
-void
-StopClocks()
-{
-    long lastTickLength;
-    TimeMark now;
-
-    if (!StopClockTimer()) return;
-    if (!appData.clockMode) return;
-
-    GetTimeMark(&now);
-
-    lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
-    if (WhiteOnMove(forwardMostMove)) {
-       whiteTimeRemaining -= lastTickLength;
-       DisplayWhiteClock(whiteTimeRemaining, WhiteOnMove(currentMove));
-    } else {
-       blackTimeRemaining -= lastTickLength;
-       DisplayBlackClock(blackTimeRemaining, !WhiteOnMove(currentMove));
-    }
-    CheckFlags();
-}
-
-/* Start clock of player on move.  Time may have been reset, so
-   if clock is already running, stop and restart it. */
-void
-StartClocks()
-{
-    (void) StopClockTimer(); /* in case it was running already */
-    DisplayBothClocks();
-    if (CheckFlags()) return;
-
-    if (!appData.clockMode) return;
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) return;
-
-    GetTimeMark(&tickStartTM);
-    intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
-      whiteTimeRemaining : blackTimeRemaining);
-    StartClockTimer(intendedTickLength);
-}
-
-char *
-TimeString(ms)
-     long ms;
-{
-    long second, minute, hour, day;
-    char *sign = "";
-    static char buf[32];
-
-    if (ms > 0 && ms <= 9900) {
-      /* convert milliseconds to tenths, rounding up */
-      double tenths = floor( ((double)(ms + 99L)) / 100.00 );
-
-      sprintf(buf, " %03.1f ", tenths/10.0);
-      return buf;
-    }
-
-    /* convert milliseconds to seconds, rounding up */
-    /* use floating point to avoid strangeness of integer division
-       with negative dividends on many machines */
-    second = (long) floor(((double) (ms + 999L)) / 1000.0);
-
-    if (second < 0) {
-       sign = "-";
-       second = -second;
-    }
-
-    day = second / (60 * 60 * 24);
-    second = second % (60 * 60 * 24);
-    hour = second / (60 * 60);
-    second = second % (60 * 60);
-    minute = second / 60;
-    second = second % 60;
-
-    if (day > 0)
-      sprintf(buf, " %s%ld:%02ld:%02ld:%02ld ",
-             sign, day, hour, minute, second);
-    else if (hour > 0)
-      sprintf(buf, " %s%ld:%02ld:%02ld ", sign, hour, minute, second);
-    else
-      sprintf(buf, " %s%2ld:%02ld ", sign, minute, second);
-
-    return buf;
-}
-
-
-/*
- * This is necessary because some C libraries aren't ANSI C compliant yet.
- */
-char *
-StrStr(string, match)
-     char *string, *match;
-{
-    int i, length;
-
-    length = strlen(match);
-
-    for (i = strlen(string) - length; i >= 0; i--, string++)
-      if (!strncmp(match, string, length))
-       return string;
-
-    return NULL;
-}
-
-char *
-StrCaseStr(string, match)
-     char *string, *match;
-{
-    int i, j, length;
-
-    length = strlen(match);
-
-    for (i = strlen(string) - length; i >= 0; i--, string++) {
-       for (j = 0; j < length; j++) {
-           if (ToLower(match[j]) != ToLower(string[j]))
-             break;
-       }
-       if (j == length) return string;
-    }
-
-    return NULL;
-}
-
-#ifndef _amigados
-int
-StrCaseCmp(s1, s2)
-     char *s1, *s2;
-{
-    char c1, c2;
-
-    for (;;) {
-       c1 = ToLower(*s1++);
-       c2 = ToLower(*s2++);
-       if (c1 > c2) return 1;
-       if (c1 < c2) return -1;
-       if (c1 == NULLCHAR) return 0;
-    }
-}
-
-
-int
-ToLower(c)
-     int c;
-{
-    return isupper(c) ? tolower(c) : c;
-}
-
-
-int
-ToUpper(c)
-     int c;
-{
-    return islower(c) ? toupper(c) : c;
-}
-#endif /* !_amigados   */
-
-char *
-StrSave(s)
-     char *s;
-{
-    char *ret;
-
-    if ((ret = (char *) malloc(strlen(s) + 1))) {
-       strcpy(ret, s);
-    }
-    return ret;
-}
-
-char *
-StrSavePtr(s, savePtr)
-     char *s, **savePtr;
-{
-    if (*savePtr) {
-       free(*savePtr);
-    }
-    if ((*savePtr = (char *) malloc(strlen(s) + 1))) {
-       strcpy(*savePtr, s);
-    }
-    return(*savePtr);
-}
-
-char *
-PGNDate()
-{
-    time_t clock;
-    struct tm *tm;
-    char buf[MSG_SIZ];
-
-    clock = time((time_t *)NULL);
-    tm = localtime(&clock);
-    sprintf(buf, "%04d.%02d.%02d",
-           tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
-    return StrSave(buf);
-}
-
-
-char *
-PositionToFEN(move)
-     int move;
-{
-    int i, j, fromX, fromY, toX, toY;
-    int whiteToPlay;
-    char buf[128];
-    char *p, *q;
-    int emptycount;
-
-    whiteToPlay = (gameMode == EditPosition) ?
-      !blackPlaysFirst : (move % 2 == 0);
-    p = buf;
-
-    /* Piece placement data */
-    for (i = BOARD_SIZE - 1; i >= 0; i--) {
-       emptycount = 0;
-       for (j = 0; j < BOARD_SIZE; j++) {
-           if (boards[move][i][j] == EmptySquare) {
-               emptycount++;
-           } else {
-               if (emptycount > 0) {
-                   *p++ = '0' + emptycount;
-                   emptycount = 0;
-               }
-               *p++ = PieceToChar(boards[move][i][j]);
-           }
-       }
-       if (emptycount > 0) {
-           *p++ = '0' + emptycount;
-           emptycount = 0;
-       }
-       *p++ = '/';
-    }
-    *(p - 1) = ' ';
-
-    /* Active color */
-    *p++ = whiteToPlay ? 'w' : 'b';
-    *p++ = ' ';
-
-    /* !!We don't keep track of castling availability, so fake it */
-    q = p;
-    if (boards[move][0][4] == WhiteKing) {
-       if (boards[move][0][7] == WhiteRook) *p++ = 'K';
-       if (boards[move][0][0] == WhiteRook) *p++ = 'Q';
-    }
-    if (boards[move][7][4] == BlackKing) {
-       if (boards[move][7][7] == BlackRook) *p++ = 'k';
-       if (boards[move][7][0] == BlackRook) *p++ = 'q';
-    }
-    if (q == p) *p++ = '-';
-    *p++ = ' ';
-
-    /* En passant target square */
-    if (move > backwardMostMove) {
-       fromX = moveList[move - 1][0] - 'a';
-       fromY = moveList[move - 1][1] - '1';
-       toX = moveList[move - 1][2] - 'a';
-       toY = moveList[move - 1][3] - '1';
-       if (fromY == (whiteToPlay ? 6 : 1) &&
-           toY == (whiteToPlay ? 4 : 3) &&
-           boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) &&
-           fromX == toX) {
-           /* 2-square pawn move just happened */
-           *p++ = toX + 'a';
-           *p++ = whiteToPlay ? '6' : '3';
-       } else {
-           *p++ = '-';
-       }
-    } else {
-       *p++ = '-';
-    }
-
-    /* !!We don't keep track of halfmove clock for 50-move rule */
-    strcpy(p, " 0 ");
-    p += 3;
-
-    /* Fullmove number */
-    sprintf(p, "%d", (move / 2) + 1);
-
-    return StrSave(buf);
-}
-
-Boolean
-ParseFEN(board, blackPlaysFirst, fen)
-     Board board;
-     int *blackPlaysFirst;
-     char *fen;
-{
-    int i, j;
-    char *p;
-    int emptycount;
-
-    p = fen;
-
-    /* Piece placement data */
-    for (i = BOARD_SIZE - 1; i >= 0; i--) {
-       j = 0;
-       for (;;) {
-           if (*p == '/' || *p == ' ') {
-               if (*p == '/') p++;
-               emptycount = BOARD_SIZE - j;
-               while (emptycount--) board[i][j++] = EmptySquare;
-               break;
-           } else if (isdigit(*p)) {
-               emptycount = *p++ - '0';
-               if (j + emptycount > BOARD_SIZE) return FALSE;
-               while (emptycount--) board[i][j++] = EmptySquare;
-           } else if (isalpha(*p)) {
-               if (j >= BOARD_SIZE) return FALSE;
-               board[i][j++] = CharToPiece(*p++);
-           } else {
-               return FALSE;
-           }
-       }
-    }
-    while (*p == '/' || *p == ' ') p++;
-
-    /* Active color */
-    switch (*p) {
-      case 'w':
-       *blackPlaysFirst = FALSE;
-       break;
-      case 'b':
-       *blackPlaysFirst = TRUE;
-       break;
-      default:
-       return FALSE;
-    }
-    /* !!We ignore the rest of the FEN notation */
-    return TRUE;
-}
-
-void
-EditPositionPasteFEN(char *fen)
-{
-  if (fen != NULL) {
-    Board initial_position;
-
-    if (!ParseFEN(initial_position, &blackPlaysFirst, fen)) {
-      DisplayError(_("Bad FEN position in clipboard"), 0);
-      return ;
-    } else {
-      int savedBlackPlaysFirst = blackPlaysFirst;
-      EditPositionEvent();
-      blackPlaysFirst = savedBlackPlaysFirst;
-      CopyBoard(boards[0], initial_position);
-      EditPositionDone();
-      DisplayBothClocks();
-      DrawPosition(FALSE, boards[currentMove]);
-    }
-  }
-}
+/*\r
+ * backend.c -- Common back end for X and Windows NT versions of\r
+ * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $\r
+ *\r
+ * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
+ * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
+ *\r
+ * The following terms apply to Digital Equipment Corporation's copyright\r
+ * interest in XBoard:\r
+ * ------------------------------------------------------------------------\r
+ * All Rights Reserved\r
+ *\r
+ * Permission to use, copy, modify, and distribute this software and its\r
+ * documentation for any purpose and without fee is hereby granted,\r
+ * provided that the above copyright notice appear in all copies and that\r
+ * both that copyright notice and this permission notice appear in\r
+ * supporting documentation, and that the name of Digital not be\r
+ * used in advertising or publicity pertaining to distribution of the\r
+ * software without specific, written prior permission.\r
+ *\r
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
+ * SOFTWARE.\r
+ * ------------------------------------------------------------------------\r
+ *\r
+ * The following terms apply to the enhanced version of XBoard distributed\r
+ * by the Free Software Foundation:\r
+ * ------------------------------------------------------------------------\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+ * ------------------------------------------------------------------------\r
+ *\r
+ * See the file ChangeLog for a revision history.  */\r
+\r
+/* [AS] Also useful here for debugging */\r
+#ifdef WIN32\r
+#include <windows.h>\r
+\r
+#define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );\r
+\r
+#else\r
+\r
+#define DoSleep( n ) if( (n) >= 0) sleep(n)\r
+\r
+#endif\r
+\r
+#include "config.h"\r
+\r
+#include <assert.h>\r
+#include <stdio.h>\r
+#include <ctype.h>\r
+#include <errno.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <math.h>\r
+\r
+#if STDC_HEADERS\r
+# include <stdlib.h>\r
+# include <string.h>\r
+#else /* not STDC_HEADERS */\r
+# if HAVE_STRING_H\r
+#  include <string.h>\r
+# else /* not HAVE_STRING_H */\r
+#  include <strings.h>\r
+# endif /* not HAVE_STRING_H */\r
+#endif /* not STDC_HEADERS */\r
+\r
+#if HAVE_SYS_FCNTL_H\r
+# include <sys/fcntl.h>\r
+#else /* not HAVE_SYS_FCNTL_H */\r
+# if HAVE_FCNTL_H\r
+#  include <fcntl.h>\r
+# endif /* HAVE_FCNTL_H */\r
+#endif /* not HAVE_SYS_FCNTL_H */\r
+\r
+#if TIME_WITH_SYS_TIME\r
+# include <sys/time.h>\r
+# include <time.h>\r
+#else\r
+# if HAVE_SYS_TIME_H\r
+#  include <sys/time.h>\r
+# else\r
+#  include <time.h>\r
+# endif\r
+#endif\r
+\r
+#if defined(_amigados) && !defined(__GNUC__)\r
+struct timezone {\r
+    int tz_minuteswest;\r
+    int tz_dsttime;\r
+};\r
+extern int gettimeofday(struct timeval *, struct timezone *);\r
+#endif\r
+\r
+#if HAVE_UNISTD_H\r
+# include <unistd.h>\r
+#endif\r
+\r
+#include "common.h"\r
+#include "frontend.h"\r
+#include "backend.h"\r
+#include "parser.h"\r
+#include "moves.h"\r
+#if ZIPPY\r
+# include "zippy.h"\r
+#endif\r
+#include "backendz.h"\r
+#include "gettext.h" \r
\r
+#ifdef ENABLE_NLS \r
+# define _(s) gettext (s) \r
+# define N_(s) gettext_noop (s) \r
+#else \r
+# define _(s) (s) \r
+# define N_(s) s \r
+#endif \r
+\r
+\r
+/* A point in time */\r
+typedef struct {\r
+    long sec;  /* Assuming this is >= 32 bits */\r
+    int ms;    /* Assuming this is >= 16 bits */\r
+} TimeMark;\r
+\r
+int establish P((void));\r
+void read_from_player P((InputSourceRef isr, VOIDSTAR closure,\r
+                        char *buf, int count, int error));\r
+void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,\r
+                     char *buf, int count, int error));\r
+void SendToICS P((char *s));\r
+void SendToICSDelayed P((char *s, long msdelay));\r
+void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,\r
+                     int toX, int toY));\r
+void InitPosition P((int redraw));\r
+void HandleMachineMove P((char *message, ChessProgramState *cps));\r
+int AutoPlayOneMove P((void));\r
+int LoadGameOneMove P((ChessMove readAhead));\r
+int LoadGameFromFile P((char *filename, int n, char *title, int useList));\r
+int LoadPositionFromFile P((char *filename, int n, char *title));\r
+int SavePositionToFile P((char *filename));\r
+void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,\r
+                 Board board));\r
+void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));\r
+void ShowMove P((int fromX, int fromY, int toX, int toY));\r
+int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
+                  /*char*/int promoChar));\r
+void BackwardInner P((int target));\r
+void ForwardInner P((int target));\r
+void GameEnds P((ChessMove result, char *resultDetails, int whosays));\r
+void EditPositionDone P((void));\r
+void PrintOpponents P((FILE *fp));\r
+void PrintPosition P((FILE *fp, int move));\r
+void StartChessProgram P((ChessProgramState *cps));\r
+void SendToProgram P((char *message, ChessProgramState *cps));\r
+void SendMoveToProgram P((int moveNum, ChessProgramState *cps));\r
+void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,\r
+                          char *buf, int count, int error));\r
+void SendTimeControl P((ChessProgramState *cps,\r
+                       int mps, long tc, int inc, int sd, int st));\r
+char *TimeControlTagValue P((void));\r
+void Attention P((ChessProgramState *cps));\r
+void FeedMovesToProgram P((ChessProgramState *cps, int upto));\r
+void ResurrectChessProgram P((void));\r
+void DisplayComment P((int moveNumber, char *text));\r
+void DisplayMove P((int moveNumber));\r
+void DisplayAnalysis P((void));\r
+\r
+void ParseGameHistory P((char *game));\r
+void ParseBoard12 P((char *string));\r
+void StartClocks P((void));\r
+void SwitchClocks P((void));\r
+void StopClocks P((void));\r
+void ResetClocks P((void));\r
+char *PGNDate P((void));\r
+void SetGameInfo P((void));\r
+Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
+int RegisterMove P((void));\r
+void MakeRegisteredMove P((void));\r
+void TruncateGame P((void));\r
+int looking_at P((char *, int *, char *));\r
+void CopyPlayerNameIntoFileName P((char **, char *));\r
+char *SavePart P((char *));\r
+int SaveGameOldStyle P((FILE *));\r
+int SaveGamePGN P((FILE *));\r
+void GetTimeMark P((TimeMark *));\r
+long SubtractTimeMarks P((TimeMark *, TimeMark *));\r
+int CheckFlags P((void));\r
+long NextTickLength P((long));\r
+void CheckTimeControl P((void));\r
+void show_bytes P((FILE *, char *, int));\r
+int string_to_rating P((char *str));\r
+void ParseFeatures P((char* args, ChessProgramState *cps));\r
+void InitBackEnd3 P((void));\r
+void FeatureDone P((ChessProgramState* cps, int val));\r
+void InitChessProgram P((ChessProgramState *cps, int setup));\r
+\r
+#ifdef WIN32\r
+       extern void ConsoleCreate();\r
+#endif\r
+\r
+ChessProgramState *WhitePlayer();\r
+void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c\r
+int VerifyDisplayMode P(());\r
+\r
+char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment\r
+void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c\r
+char *ProbeBook P((int moveNr, char *book)); // [HGM] book: returns a book move\r
+char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book\r
+extern char installDir[MSG_SIZ];\r
+\r
+extern int tinyLayout, smallLayout;\r
+ChessProgramStats programStats;\r
+static int exiting = 0; /* [HGM] moved to top */\r
+static int setboardSpoiledMachineBlack = 0, errorExitFlag = 0;\r
+extern int startedFromPositionFile;\r
+int startedFromPositionFile = FALSE; Board filePosition;       /* [HGM] loadPos */\r
+char endingGame = 0;    /* [HGM] crash: flag to prevent recursion of GameEnds() */\r
+int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS     */\r
+VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */\r
+int lastIndex = 0;      /* [HGM] autoinc: last game/position used in match mode */\r
+int opponentKibitzes;\r
+\r
+/* States for ics_getting_history */\r
+#define H_FALSE 0\r
+#define H_REQUESTED 1\r
+#define H_GOT_REQ_HEADER 2\r
+#define H_GOT_UNREQ_HEADER 3\r
+#define H_GETTING_MOVES 4\r
+#define H_GOT_UNWANTED_HEADER 5\r
+\r
+/* whosays values for GameEnds */\r
+#define GE_ICS 0\r
+#define GE_ENGINE 1\r
+#define GE_PLAYER 2\r
+#define GE_FILE 3\r
+#define GE_XBOARD 4\r
+#define GE_ENGINE1 5\r
+#define GE_ENGINE2 6\r
+\r
+/* Maximum number of games in a cmail message */\r
+#define CMAIL_MAX_GAMES 20\r
+\r
+/* Different types of move when calling RegisterMove */\r
+#define CMAIL_MOVE   0\r
+#define CMAIL_RESIGN 1\r
+#define CMAIL_DRAW   2\r
+#define CMAIL_ACCEPT 3\r
+\r
+/* Different types of result to remember for each game */\r
+#define CMAIL_NOT_RESULT 0\r
+#define CMAIL_OLD_RESULT 1\r
+#define CMAIL_NEW_RESULT 2\r
+\r
+/* Telnet protocol constants */\r
+#define TN_WILL 0373\r
+#define TN_WONT 0374\r
+#define TN_DO   0375\r
+#define TN_DONT 0376\r
+#define TN_IAC  0377\r
+#define TN_ECHO 0001\r
+#define TN_SGA  0003\r
+#define TN_PORT 23\r
+\r
+/* [AS] */\r
+static char * safeStrCpy( char * dst, const char * src, size_t count )\r
+{\r
+    assert( dst != NULL );\r
+    assert( src != NULL );\r
+    assert( count > 0 );\r
+\r
+    strncpy( dst, src, count );\r
+    dst[ count-1 ] = '\0';\r
+    return dst;\r
+}\r
+\r
+static char * safeStrCat( char * dst, const char * src, size_t count )\r
+{\r
+    size_t  dst_len;\r
+\r
+    assert( dst != NULL );\r
+    assert( src != NULL );\r
+    assert( count > 0 );\r
+\r
+    dst_len = strlen(dst);\r
+\r
+    assert( count > dst_len ); /* Buffer size must be greater than current length */\r
+\r
+    safeStrCpy( dst + dst_len, src, count - dst_len );\r
+\r
+    return dst;\r
+}\r
+\r
+/* Some compiler can't cast u64 to double\r
+ * This function do the job for us:\r
+\r
+ * We use the highest bit for cast, this only\r
+ * works if the highest bit is not\r
+ * in use (This should not happen)\r
+ *\r
+ * We used this for all compiler\r
+ */\r
+double\r
+u64ToDouble(u64 value)\r
+{\r
+  double r;\r
+  u64 tmp = value & u64Const(0x7fffffffffffffff);\r
+  r = (double)(s64)tmp;\r
+  if (value & u64Const(0x8000000000000000))\r
+       r +=  9.2233720368547758080e18; /* 2^63 */\r
+ return r;\r
+}\r
+\r
+/* Fake up flags for now, as we aren't keeping track of castling\r
+   availability yet. [HGM] Change of logic: the flag now only\r
+   indicates the type of castlings allowed by the rule of the game.\r
+   The actual rights themselves are maintained in the array\r
+   castlingRights, as part of the game history, and are not probed\r
+   by this function.\r
+ */\r
+int\r
+PosFlags(index)\r
+{\r
+  int flags = F_ALL_CASTLE_OK;\r
+  if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;\r
+  switch (gameInfo.variant) {\r
+  case VariantSuicide:\r
+    flags &= ~F_ALL_CASTLE_OK;\r
+  case VariantGiveaway:                // [HGM] moved this case label one down: seems Giveaway does have castling on ICC!\r
+    flags |= F_IGNORE_CHECK;\r
+    break;\r
+  case VariantAtomic:\r
+    flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;\r
+    break;\r
+  case VariantKriegspiel:\r
+    flags |= F_KRIEGSPIEL_CAPTURE;\r
+    break;\r
+  case VariantCapaRandom: \r
+  case VariantFischeRandom:\r
+    flags |= F_FRC_TYPE_CASTLING; /* [HGM] enable this through flag */\r
+  case VariantNoCastle:\r
+  case VariantShatranj:\r
+  case VariantCourier:\r
+    flags &= ~F_ALL_CASTLE_OK;\r
+    break;\r
+  default:\r
+    break;\r
+  }\r
+  return flags;\r
+}\r
+\r
+FILE *gameFileFP, *debugFP;\r
+\r
+/* \r
+    [AS] Note: sometimes, the sscanf() function is used to parse the input\r
+    into a fixed-size buffer. Because of this, we must be prepared to\r
+    receive strings as long as the size of the input buffer, which is currently\r
+    set to 4K for Windows and 8K for the rest.\r
+    So, we must either allocate sufficiently large buffers here, or\r
+    reduce the size of the input buffer in the input reading part.\r
+*/\r
+\r
+char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];\r
+char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];\r
+char thinkOutput1[MSG_SIZ*10];\r
+\r
+ChessProgramState first, second;\r
+\r
+/* premove variables */\r
+int premoveToX = 0;\r
+int premoveToY = 0;\r
+int premoveFromX = 0;\r
+int premoveFromY = 0;\r
+int premovePromoChar = 0;\r
+int gotPremove = 0;\r
+Boolean alarmSounded;\r
+/* end premove variables */\r
+\r
+char *ics_prefix = "$";\r
+int ics_type = ICS_GENERIC;\r
+\r
+int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;\r
+int pauseExamForwardMostMove = 0;\r
+int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;\r
+int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];\r
+int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;\r
+int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;\r
+int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;\r
+int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;\r
+int whiteFlag = FALSE, blackFlag = FALSE;\r
+int userOfferedDraw = FALSE;\r
+int ics_user_moved = 0, ics_gamenum = -1, ics_getting_history = H_FALSE;\r
+int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;\r
+int cmailMoveType[CMAIL_MAX_GAMES];\r
+long ics_clock_paused = 0;\r
+ProcRef icsPR = NoProc, cmailPR = NoProc;\r
+InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;\r
+GameMode gameMode = BeginningOfGame;\r
+char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];\r
+char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];\r
+ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */\r
+int hiddenThinkOutputState = 0; /* [AS] */\r
+int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */\r
+int adjudicateLossPlies = 6;\r
+char white_holding[64], black_holding[64];\r
+TimeMark lastNodeCountTime;\r
+long lastNodeCount=0;\r
+int have_sent_ICS_logon = 0;\r
+int movesPerSession;\r
+long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;\r
+long timeControl_2; /* [AS] Allow separate time controls */\r
+char *fullTimeControlString = NULL; /* [HGM] secondary TC: merge of MPS, TC and inc */\r
+long timeRemaining[2][MAX_MOVES];\r
+int matchGame = 0;\r
+TimeMark programStartTime;\r
+char ics_handle[MSG_SIZ];\r
+int have_set_title = 0;\r
+\r
+/* animateTraining preserves the state of appData.animate\r
+ * when Training mode is activated. This allows the\r
+ * response to be animated when appData.animate == TRUE and\r
+ * appData.animateDragging == TRUE.\r
+ */\r
+Boolean animateTraining;\r
+\r
+GameInfo gameInfo;\r
+\r
+AppData appData;\r
+\r
+Board boards[MAX_MOVES];\r
+/* [HGM] Following 7 needed for accurate legality tests: */\r
+char  epStatus[MAX_MOVES];\r
+char  castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1\r
+char  castlingRank[BOARD_SIZE]; // and corresponding ranks\r
+char  initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE], fileRights[BOARD_SIZE];\r
+int   nrCastlingRights; // For TwoKings, or to implement castling-unknown status\r
+int   initialRulePlies, FENrulePlies;\r
+char  FENepStatus;\r
+FILE  *serverMoves = NULL; // next two for broadcasting (/serverMoves option)\r
+int loadFlag = 0; \r
+int shuffleOpenings;\r
+\r
+ChessSquare  FIDEArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,\r
+       WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackBishop, BlackQueen,\r
+       BlackKing, BlackBishop, BlackKnight, BlackRook }\r
+};\r
+\r
+ChessSquare twoKingsArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,\r
+       WhiteKing, WhiteKing, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackBishop, BlackQueen,\r
+        BlackKing, BlackKing, BlackKnight, BlackRook }\r
+};\r
+\r
+ChessSquare  KnightmateArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteMan, WhiteBishop, WhiteQueen,\r
+        WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook },\r
+    { BlackRook, BlackMan, BlackBishop, BlackQueen,\r
+        BlackUnicorn, BlackBishop, BlackMan, BlackRook }\r
+};\r
+\r
+ChessSquare fairyArray[2][BOARD_SIZE] = { /* [HGM] Queen side differs from King side */\r
+    { WhiteCannon, WhiteNightrider, WhiteAlfil, WhiteQueen,\r
+        WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },\r
+    { BlackCannon, BlackNightrider, BlackAlfil, BlackQueen,\r
+       BlackKing, BlackBishop, BlackKnight, BlackRook }\r
+};\r
+\r
+ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */\r
+    { WhiteRook, WhiteKnight, WhiteAlfil, WhiteKing,\r
+        WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackAlfil, BlackKing,\r
+        BlackFerz, BlackAlfil, BlackKnight, BlackRook }\r
+};\r
+\r
+\r
+#if (BOARD_SIZE>=10)\r
+ChessSquare ShogiArray[2][BOARD_SIZE] = {\r
+    { WhiteQueen, WhiteKnight, WhiteFerz, WhiteWazir,\r
+        WhiteKing, WhiteWazir, WhiteFerz, WhiteKnight, WhiteQueen },\r
+    { BlackQueen, BlackKnight, BlackFerz, BlackWazir,\r
+        BlackKing, BlackWazir, BlackFerz, BlackKnight, BlackQueen }\r
+};\r
+\r
+ChessSquare XiangqiArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz,\r
+        WhiteWazir, WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackAlfil, BlackFerz,\r
+        BlackWazir, BlackFerz, BlackAlfil, BlackKnight, BlackRook }\r
+};\r
+\r
+ChessSquare CapablancaArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen, \r
+        WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen, \r
+        BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }\r
+};\r
+\r
+ChessSquare GreatArray[2][BOARD_SIZE] = {\r
+    { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing, \r
+        WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },\r
+    { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing, \r
+        BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },\r
+};\r
+\r
+ChessSquare JanusArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing, \r
+        WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },\r
+    { BlackRook, BlackAngel, BlackKnight, BlackBishop, BlackKing, \r
+        BlackQueen, BlackBishop, BlackKnight, BlackAngel, BlackRook }\r
+};\r
+\r
+#ifdef GOTHIC\r
+ChessSquare GothicArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall, \r
+        WhiteKing, WhiteAngel, WhiteBishop, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall, \r
+        BlackKing, BlackAngel, BlackBishop, BlackKnight, BlackRook }\r
+};\r
+#else // !GOTHIC\r
+#define GothicArray CapablancaArray\r
+#endif // !GOTHIC\r
+\r
+#ifdef FALCON\r
+ChessSquare FalconArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen, \r
+        WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen, \r
+        BlackKing, BlackLance, BlackBishop, BlackKnight, BlackRook }\r
+};\r
+#else // !FALCON\r
+#define FalconArray CapablancaArray\r
+#endif // !FALCON\r
+\r
+#else // !(BOARD_SIZE>=10)\r
+#define XiangqiPosition FIDEArray\r
+#define CapablancaArray FIDEArray\r
+#define GothicArray FIDEArray\r
+#define GreatArray FIDEArray\r
+#endif // !(BOARD_SIZE>=10)\r
+\r
+#if (BOARD_SIZE>=12)\r
+ChessSquare CourierArray[2][BOARD_SIZE] = {\r
+    { WhiteRook, WhiteKnight, WhiteAlfil, WhiteBishop, WhiteMan, WhiteKing,\r
+        WhiteFerz, WhiteWazir, WhiteBishop, WhiteAlfil, WhiteKnight, WhiteRook },\r
+    { BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing,\r
+        BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook }\r
+};\r
+#else // !(BOARD_SIZE>=12)\r
+#define CourierArray CapablancaArray\r
+#endif // !(BOARD_SIZE>=12)\r
+\r
+\r
+Board initialPosition;\r
+\r
+\r
+/* Convert str to a rating. Checks for special cases of "----",\r
+\r
+   "++++", etc. Also strips ()'s */\r
+int\r
+string_to_rating(str)\r
+  char *str;\r
+{\r
+  while(*str && !isdigit(*str)) ++str;\r
+  if (!*str)\r
+    return 0;  /* One of the special "no rating" cases */\r
+  else\r
+    return atoi(str);\r
+}\r
+\r
+void\r
+ClearProgramStats()\r
+{\r
+    /* Init programStats */\r
+    programStats.movelist[0] = 0;\r
+    programStats.depth = 0;\r
+    programStats.nr_moves = 0;\r
+    programStats.moves_left = 0;\r
+    programStats.nodes = 0;\r
+    programStats.time = -1;        // [HGM] PGNtime: make invalid to recognize engine output\r
+    programStats.score = 0;\r
+    programStats.got_only_move = 0;\r
+    programStats.got_fail = 0;\r
+    programStats.line_is_book = 0;\r
+}\r
+\r
+void\r
+InitBackEnd1()\r
+{\r
+    int matched, min, sec;\r
+\r
+    ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options\r
+\r
+    GetTimeMark(&programStartTime);\r
+\r
+    ClearProgramStats();\r
+    programStats.ok_to_send = 1;\r
+    programStats.seen_stat = 0;\r
+\r
+    /*\r
+     * Initialize game list\r
+     */\r
+    ListNew(&gameList);\r
+\r
+\r
+    /*\r
+     * Internet chess server status\r
+     */\r
+    if (appData.icsActive) {\r
+       appData.matchMode = FALSE;\r
+       appData.matchGames = 0;\r
+#if ZIPPY      \r
+       appData.noChessProgram = !appData.zippyPlay;\r
+#else\r
+       appData.zippyPlay = FALSE;\r
+       appData.zippyTalk = FALSE;\r
+       appData.noChessProgram = TRUE;\r
+#endif\r
+       if (*appData.icsHelper != NULLCHAR) {\r
+           appData.useTelnet = TRUE;\r
+           appData.telnetProgram = appData.icsHelper;\r
+       }\r
+    } else {\r
+       appData.zippyTalk = appData.zippyPlay = FALSE;\r
+    }\r
+\r
+    /* [AS] Initialize pv info list [HGM] and game state */\r
+    {\r
+        int i, j;\r
+\r
+        for( i=0; i<MAX_MOVES; i++ ) {\r
+            pvInfoList[i].depth = -1;\r
+            epStatus[i]=EP_NONE;\r
+            for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;\r
+        }\r
+    }\r
+\r
+    /*\r
+     * Parse timeControl resource\r
+     */\r
+    if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,\r
+                         appData.movesPerSession)) {\r
+       char buf[MSG_SIZ];\r
+       sprintf(buf, _("bad timeControl option %s"), appData.timeControl);\r
+       DisplayFatalError(buf, 0, 2);\r
+    }\r
+\r
+    /*\r
+     * Parse searchTime resource\r
+     */\r
+    if (*appData.searchTime != NULLCHAR) {\r
+       matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);\r
+       if (matched == 1) {\r
+           searchTime = min * 60;\r
+       } else if (matched == 2) {\r
+           searchTime = min * 60 + sec;\r
+       } else {\r
+           char buf[MSG_SIZ];\r
+           sprintf(buf, _("bad searchTime option %s"), appData.searchTime);\r
+           DisplayFatalError(buf, 0, 2);\r
+       }\r
+    }\r
+\r
+    /* [AS] Adjudication threshold */\r
+    adjudicateLossThreshold = appData.adjudicateLossThreshold;\r
+    \r
+    first.which = "first";\r
+    second.which = "second";\r
+    first.maybeThinking = second.maybeThinking = FALSE;\r
+    first.pr = second.pr = NoProc;\r
+    first.isr = second.isr = NULL;\r
+    first.sendTime = second.sendTime = 2;\r
+    first.sendDrawOffers = 1;\r
+    if (appData.firstPlaysBlack) {\r
+       first.twoMachinesColor = "black\n";\r
+       second.twoMachinesColor = "white\n";\r
+    } else {\r
+       first.twoMachinesColor = "white\n";\r
+       second.twoMachinesColor = "black\n";\r
+    }\r
+    first.program = appData.firstChessProgram;\r
+    second.program = appData.secondChessProgram;\r
+    first.host = appData.firstHost;\r
+    second.host = appData.secondHost;\r
+    first.dir = appData.firstDirectory;\r
+    second.dir = appData.secondDirectory;\r
+    first.other = &second;\r
+    second.other = &first;\r
+    first.initString = appData.initString;\r
+    second.initString = appData.secondInitString;\r
+    first.computerString = appData.firstComputerString;\r
+    second.computerString = appData.secondComputerString;\r
+    first.useSigint = second.useSigint = TRUE;\r
+    first.useSigterm = second.useSigterm = TRUE;\r
+    first.reuse = appData.reuseFirst;\r
+    second.reuse = appData.reuseSecond;\r
+    first.nps = appData.firstNPS;   // [HGM] nps: copy nodes per second\r
+    second.nps = appData.secondNPS;\r
+    first.useSetboard = second.useSetboard = FALSE;\r
+    first.useSAN = second.useSAN = FALSE;\r
+    first.usePing = second.usePing = FALSE;\r
+    first.lastPing = second.lastPing = 0;\r
+    first.lastPong = second.lastPong = 0;\r
+    first.usePlayother = second.usePlayother = FALSE;\r
+    first.useColors = second.useColors = TRUE;\r
+    first.useUsermove = second.useUsermove = FALSE;\r
+    first.sendICS = second.sendICS = FALSE;\r
+    first.sendName = second.sendName = appData.icsActive;\r
+    first.sdKludge = second.sdKludge = FALSE;\r
+    first.stKludge = second.stKludge = FALSE;\r
+    TidyProgramName(first.program, first.host, first.tidy);\r
+    TidyProgramName(second.program, second.host, second.tidy);\r
+    first.matchWins = second.matchWins = 0;\r
+    strcpy(first.variants, appData.variant);\r
+    strcpy(second.variants, appData.variant);\r
+    first.analysisSupport = second.analysisSupport = 2; /* detect */\r
+    first.analyzing = second.analyzing = FALSE;\r
+    first.initDone = second.initDone = FALSE;\r
+\r
+    /* New features added by Tord: */\r
+    first.useFEN960 = FALSE; second.useFEN960 = FALSE;\r
+    first.useOOCastle = TRUE; second.useOOCastle = TRUE;\r
+    /* End of new features added by Tord. */\r
+\r
+    /* [HGM] time odds: set factor for each machine */\r
+    first.timeOdds  = appData.firstTimeOdds;\r
+    second.timeOdds = appData.secondTimeOdds;\r
+    { int norm = 1;\r
+        if(appData.timeOddsMode) {\r
+            norm = first.timeOdds;\r
+            if(norm > second.timeOdds) norm = second.timeOdds;\r
+        }\r
+        first.timeOdds /= norm;\r
+        second.timeOdds /= norm;\r
+    }\r
+\r
+    /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/\r
+    first.accumulateTC = appData.firstAccumulateTC;\r
+    second.accumulateTC = appData.secondAccumulateTC;\r
+    first.maxNrOfSessions = second.maxNrOfSessions = 1;\r
+\r
+    /* [HGM] debug */\r
+    first.debug = second.debug = FALSE;\r
+    first.supportsNPS = second.supportsNPS = UNKNOWN;\r
+\r
+    first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */\r
+    second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */\r
+    first.isUCI = appData.firstIsUCI; /* [AS] */\r
+    second.isUCI = appData.secondIsUCI; /* [AS] */\r
+    first.hasOwnBookUCI = appData.firstHasOwnBookUCI; /* [AS] */\r
+    second.hasOwnBookUCI = appData.secondHasOwnBookUCI; /* [AS] */\r
+\r
+    if (appData.firstProtocolVersion > PROTOVER ||\r
+       appData.firstProtocolVersion < 1) {\r
+      char buf[MSG_SIZ];\r
+      sprintf(buf, _("protocol version %d not supported"),\r
+             appData.firstProtocolVersion);\r
+      DisplayFatalError(buf, 0, 2);\r
+    } else {\r
+      first.protocolVersion = appData.firstProtocolVersion;\r
+    }\r
+\r
+    if (appData.secondProtocolVersion > PROTOVER ||\r
+       appData.secondProtocolVersion < 1) {\r
+      char buf[MSG_SIZ];\r
+      sprintf(buf, _("protocol version %d not supported"),\r
+             appData.secondProtocolVersion);\r
+      DisplayFatalError(buf, 0, 2);\r
+    } else {\r
+      second.protocolVersion = appData.secondProtocolVersion;\r
+    }\r
+\r
+    if (appData.icsActive) {\r
+        appData.clockMode = TRUE;  /* changes dynamically in ICS mode */\r
+    } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {\r
+       appData.clockMode = FALSE;\r
+       first.sendTime = second.sendTime = 0;\r
+    }\r
+    \r
+#if ZIPPY\r
+    /* Override some settings from environment variables, for backward\r
+       compatibility.  Unfortunately it's not feasible to have the env\r
+       vars just set defaults, at least in xboard.  Ugh.\r
+    */\r
+    if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {\r
+      ZippyInit();\r
+    }\r
+#endif\r
+    \r
+    if (appData.noChessProgram) {\r
+       programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)\r
+                                       + strlen(PATCHLEVEL));\r
+       sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);\r
+    } else {\r
+#if 0\r
+       char *p, *q;\r
+       q = first.program;\r
+       while (*q != ' ' && *q != NULLCHAR) q++;\r
+       p = q;\r
+       while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] bckslash added */\r
+       programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)\r
+                                       + strlen(PATCHLEVEL) + (q - p));\r
+       sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);\r
+       strncat(programVersion, p, q - p);\r
+#else\r
+       /* [HGM] tidy: use tidy name, in stead of full pathname (which was probably a bug due to / vs \ ) */\r
+       programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)\r
+                                       + strlen(PATCHLEVEL) + strlen(first.tidy));\r
+       sprintf(programVersion, "%s %s.%s + %s", PRODUCT, VERSION, PATCHLEVEL, first.tidy);\r
+#endif\r
+    }\r
+\r
+    if (!appData.icsActive) {\r
+      char buf[MSG_SIZ];\r
+      /* Check for variants that are supported only in ICS mode,\r
+         or not at all.  Some that are accepted here nevertheless\r
+         have bugs; see comments below.\r
+      */\r
+      VariantClass variant = StringToVariant(appData.variant);\r
+      switch (variant) {\r
+      case VariantBughouse:     /* need four players and two boards */\r
+      case VariantKriegspiel:   /* need to hide pieces and move details */\r
+      /* case VariantFischeRandom: (Fabien: moved below) */\r
+       sprintf(buf, _("Variant %s supported only in ICS mode"), appData.variant);\r
+       DisplayFatalError(buf, 0, 2);\r
+       return;\r
+\r
+      case VariantUnknown:\r
+      case VariantLoadable:\r
+      case Variant29:\r
+      case Variant30:\r
+      case Variant31:\r
+      case Variant32:\r
+      case Variant33:\r
+      case Variant34:\r
+      case Variant35:\r
+      case Variant36:\r
+      default:\r
+       sprintf(buf, _("Unknown variant name %s"), appData.variant);\r
+       DisplayFatalError(buf, 0, 2);\r
+       return;\r
+\r
+      case VariantXiangqi:    /* [HGM] repetition rules not implemented */\r
+      case VariantFairy:      /* [HGM] TestLegality definitely off! */\r
+      case VariantGothic:     /* [HGM] should work */\r
+      case VariantCapablanca: /* [HGM] should work */\r
+      case VariantCourier:    /* [HGM] initial forced moves not implemented */\r
+      case VariantShogi:      /* [HGM] drops not tested for legality */\r
+      case VariantKnightmate: /* [HGM] should work */\r
+      case VariantCylinder:   /* [HGM] untested */\r
+      case VariantFalcon:     /* [HGM] untested */\r
+      case VariantCrazyhouse: /* holdings not shown, ([HGM] fixed that!)\r
+                                offboard interposition not understood */\r
+      case VariantNormal:     /* definitely works! */\r
+      case VariantWildCastle: /* pieces not automatically shuffled */\r
+      case VariantNoCastle:   /* pieces not automatically shuffled */\r
+      case VariantFischeRandom: /* [HGM] works and shuffles pieces */\r
+      case VariantLosers:     /* should work except for win condition,\r
+                                and doesn't know captures are mandatory */\r
+      case VariantSuicide:    /* should work except for win condition,\r
+                                and doesn't know captures are mandatory */\r
+      case VariantGiveaway:   /* should work except for win condition,\r
+                                and doesn't know captures are mandatory */\r
+      case VariantTwoKings:   /* should work */\r
+      case VariantAtomic:     /* should work except for win condition */\r
+      case Variant3Check:     /* should work except for win condition */\r
+      case VariantShatranj:   /* should work except for all win conditions */\r
+      case VariantBerolina:   /* might work if TestLegality is off */\r
+      case VariantCapaRandom: /* should work */\r
+      case VariantJanus:      /* should work */\r
+      case VariantSuper:      /* experimental */\r
+      case VariantGreat:      /* experimental, requires legality testing to be off */\r
+       break;\r
+      }\r
+    }\r
+\r
+    InitEngineUCI( installDir, &first );  // [HGM] moved here from winboard.c, to make available in xboard\r
+    InitEngineUCI( installDir, &second );\r
+}\r
+\r
+int NextIntegerFromString( char ** str, long * value )\r
+{\r
+    int result = -1;\r
+    char * s = *str;\r
+\r
+    while( *s == ' ' || *s == '\t' ) {\r
+        s++;\r
+    }\r
+\r
+    *value = 0;\r
+\r
+    if( *s >= '0' && *s <= '9' ) {\r
+        while( *s >= '0' && *s <= '9' ) {\r
+            *value = *value * 10 + (*s - '0');\r
+            s++;\r
+        }\r
+\r
+        result = 0;\r
+    }\r
+\r
+    *str = s;\r
+\r
+    return result;\r
+}\r
+\r
+int NextTimeControlFromString( char ** str, long * value )\r
+{\r
+    long temp;\r
+    int result = NextIntegerFromString( str, &temp );\r
+\r
+    if( result == 0 ) {\r
+        *value = temp * 60; /* Minutes */\r
+        if( **str == ':' ) {\r
+            (*str)++;\r
+            result = NextIntegerFromString( str, &temp );\r
+            *value += temp; /* Seconds */\r
+        }\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+int NextSessionFromString( char ** str, int *moves, long * tc, long *inc)\r
+{   /* [HGM] routine added to read '+moves/time' for secondary time control */\r
+    int result = -1; long temp, temp2;\r
+\r
+    if(**str != '+') return -1; // old params remain in force!\r
+    (*str)++;\r
+    if( NextTimeControlFromString( str, &temp ) ) return -1;\r
+\r
+    if(**str != '/') {\r
+        /* time only: incremental or sudden-death time control */\r
+        if(**str == '+') { /* increment follows; read it */\r
+            (*str)++;\r
+            if(result = NextIntegerFromString( str, &temp2)) return -1;\r
+            *inc = temp2 * 1000;\r
+        } else *inc = 0;\r
+        *moves = 0; *tc = temp * 1000; \r
+        return 0;\r
+    } else if(temp % 60 != 0) return -1;     /* moves was given as min:sec */\r
+\r
+    (*str)++; /* classical time control */\r
+    result = NextTimeControlFromString( str, &temp2);\r
+    if(result == 0) {\r
+        *moves = temp/60;\r
+        *tc    = temp2 * 1000;\r
+        *inc   = 0;\r
+    }\r
+    return result;\r
+}\r
+\r
+int GetTimeQuota(int movenr)\r
+{   /* [HGM] get time to add from the multi-session time-control string */\r
+    int moves=1; /* kludge to force reading of first session */\r
+    long time, increment;\r
+    char *s = fullTimeControlString;\r
+\r
+    if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", fullTimeControlString);\r
+    do {\r
+        if(moves) NextSessionFromString(&s, &moves, &time, &increment);\r
+        if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment);\r
+        if(movenr == -1) return time;    /* last move before new session     */\r
+        if(!moves) return increment;     /* current session is incremental   */\r
+        if(movenr >= 0) movenr -= moves; /* we already finished this session */\r
+    } while(movenr >= -1);               /* try again for next session       */\r
+\r
+    return 0; // no new time quota on this move\r
+}\r
+\r
+int\r
+ParseTimeControl(tc, ti, mps)\r
+     char *tc;\r
+     int ti;\r
+     int mps;\r
+{\r
+#if 0\r
+    int matched, min, sec;\r
+\r
+    matched = sscanf(tc, "%d:%d", &min, &sec);\r
+    if (matched == 1) {\r
+       timeControl = min * 60 * 1000;\r
+    } else if (matched == 2) {\r
+       timeControl = (min * 60 + sec) * 1000;\r
+    } else {\r
+       return FALSE;\r
+    }\r
+#else\r
+    long tc1;\r
+    long tc2;\r
+    char buf[MSG_SIZ];\r
+\r
+    if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;\r
+    if(ti > 0) {\r
+        if(mps)\r
+             sprintf(buf, "+%d/%s+%d", mps, tc, ti);\r
+        else sprintf(buf, "+%s+%d", tc, ti);\r
+    } else {\r
+        if(mps)\r
+             sprintf(buf, "+%d/%s", mps, tc);\r
+        else sprintf(buf, "+%s", tc);\r
+    }\r
+    fullTimeControlString = StrSave(buf);\r
+\r
+    if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {\r
+        return FALSE;\r
+    }\r
+\r
+    if( *tc == '/' ) {\r
+        /* Parse second time control */\r
+        tc++;\r
+\r
+        if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {\r
+            return FALSE;\r
+        }\r
+\r
+        if( tc2 == 0 ) {\r
+            return FALSE;\r
+        }\r
+\r
+        timeControl_2 = tc2 * 1000;\r
+    }\r
+    else {\r
+        timeControl_2 = 0;\r
+    }\r
+\r
+    if( tc1 == 0 ) {\r
+        return FALSE;\r
+    }\r
+\r
+    timeControl = tc1 * 1000;\r
+#endif\r
+\r
+    if (ti >= 0) {\r
+       timeIncrement = ti * 1000;  /* convert to ms */\r
+       movesPerSession = 0;\r
+    } else {\r
+       timeIncrement = 0;\r
+       movesPerSession = mps;\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+void\r
+InitBackEnd2()\r
+{\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, "%s\n", programVersion);\r
+    }\r
+\r
+    if (appData.matchGames > 0) {\r
+       appData.matchMode = TRUE;\r
+    } else if (appData.matchMode) {\r
+       appData.matchGames = 1;\r
+    }\r
+    if(appData.matchMode && appData.sameColorGames > 0) /* [HGM] alternate: overrule matchGames */\r
+       appData.matchGames = appData.sameColorGames;\r
+    if(appData.rewindIndex > 1) { /* [HGM] autoinc: rewind implies auto-increment and overrules given index */\r
+       if(appData.loadPositionIndex >= 0) appData.loadPositionIndex = -1;\r
+       if(appData.loadGameIndex >= 0) appData.loadGameIndex = -1;\r
+    }\r
+    Reset(TRUE, FALSE);\r
+    if (appData.noChessProgram || first.protocolVersion == 1) {\r
+      InitBackEnd3();\r
+    } else {\r
+      /* kludge: allow timeout for initial "feature" commands */\r
+      FreezeUI();\r
+      DisplayMessage("", _("Starting chess program"));\r
+      ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);\r
+    }\r
+}\r
+\r
+void\r
+InitBackEnd3 P((void))\r
+{\r
+    GameMode initialMode;\r
+    char buf[MSG_SIZ];\r
+    int err;\r
+\r
+    InitChessProgram(&first, startedFromSetupPosition);\r
+\r
+\r
+    if (appData.icsActive) {\r
+#ifdef WIN32\r
+        /* [DM] Make a console window if needed [HGM] merged ifs */\r
+        ConsoleCreate(); \r
+#endif\r
+       err = establish();\r
+       if (err != 0) {\r
+           if (*appData.icsCommPort != NULLCHAR) {\r
+               sprintf(buf, _("Could not open comm port %s"),  \r
+                       appData.icsCommPort);\r
+           } else {\r
+               sprintf(buf, _("Could not connect to host %s, port %s"),  \r
+                       appData.icsHost, appData.icsPort);\r
+           }\r
+           DisplayFatalError(buf, err, 1);\r
+           return;\r
+       }\r
+       SetICSMode();\r
+       telnetISR =\r
+         AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);\r
+       fromUserISR =\r
+         AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);\r
+    } else if (appData.noChessProgram) {\r
+       SetNCPMode();\r
+    } else {\r
+       SetGNUMode();\r
+    }\r
+\r
+    if (*appData.cmailGameName != NULLCHAR) {\r
+       SetCmailMode();\r
+       OpenLoopback(&cmailPR);\r
+       cmailISR =\r
+         AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);\r
+    }\r
+    \r
+    ThawUI();\r
+    DisplayMessage("", "");\r
+    if (StrCaseCmp(appData.initialMode, "") == 0) {\r
+      initialMode = BeginningOfGame;\r
+    } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {\r
+      initialMode = TwoMachinesPlay;\r
+    } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {\r
+      initialMode = AnalyzeFile; \r
+    } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {\r
+      initialMode = AnalyzeMode;\r
+    } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {\r
+      initialMode = MachinePlaysWhite;\r
+    } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {\r
+      initialMode = MachinePlaysBlack;\r
+    } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {\r
+      initialMode = EditGame;\r
+    } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {\r
+      initialMode = EditPosition;\r
+    } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {\r
+      initialMode = Training;\r
+    } else {\r
+      sprintf(buf, _("Unknown initialMode %s"), appData.initialMode);\r
+      DisplayFatalError(buf, 0, 2);\r
+      return;\r
+    }\r
+\r
+    if (appData.matchMode) {\r
+       /* Set up machine vs. machine match */\r
+       if (appData.noChessProgram) {\r
+           DisplayFatalError(_("Can't have a match with no chess programs"),\r
+                             0, 2);\r
+           return;\r
+       }\r
+       matchMode = TRUE;\r
+       matchGame = 1;\r
+       if (*appData.loadGameFile != NULLCHAR) {\r
+           int index = appData.loadGameIndex; // [HGM] autoinc\r
+           if(index<0) lastIndex = index = 1;\r
+           if (!LoadGameFromFile(appData.loadGameFile,\r
+                                 index,\r
+                                 appData.loadGameFile, FALSE)) {\r
+               DisplayFatalError(_("Bad game file"), 0, 1);\r
+               return;\r
+           }\r
+       } else if (*appData.loadPositionFile != NULLCHAR) {\r
+           int index = appData.loadPositionIndex; // [HGM] autoinc\r
+           if(index<0) lastIndex = index = 1;\r
+           if (!LoadPositionFromFile(appData.loadPositionFile,\r
+                                     index,\r
+                                     appData.loadPositionFile)) {\r
+               DisplayFatalError(_("Bad position file"), 0, 1);\r
+               return;\r
+           }\r
+       }\r
+       TwoMachinesEvent();\r
+    } else if (*appData.cmailGameName != NULLCHAR) {\r
+       /* Set up cmail mode */\r
+       ReloadCmailMsgEvent(TRUE);\r
+    } else {\r
+       /* Set up other modes */\r
+       if (initialMode == AnalyzeFile) {\r
+         if (*appData.loadGameFile == NULLCHAR) {\r
+           DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1);\r
+           return;\r
+         }\r
+       }\r
+       if (*appData.loadGameFile != NULLCHAR) {\r
+           (void) LoadGameFromFile(appData.loadGameFile,\r
+                                   appData.loadGameIndex,\r
+                                   appData.loadGameFile, TRUE);\r
+       } else if (*appData.loadPositionFile != NULLCHAR) {\r
+           (void) LoadPositionFromFile(appData.loadPositionFile,\r
+                                       appData.loadPositionIndex,\r
+                                       appData.loadPositionFile);\r
+            /* [HGM] try to make self-starting even after FEN load */\r
+            /* to allow automatic setup of fairy variants with wtm */\r
+            if(initialMode == BeginningOfGame && !blackPlaysFirst) {\r
+                gameMode = BeginningOfGame;\r
+                setboardSpoiledMachineBlack = 1;\r
+            }\r
+            /* [HGM] loadPos: make that every new game uses the setup */\r
+            /* from file as long as we do not switch variant          */\r
+            if(!blackPlaysFirst) { int i;\r
+                startedFromPositionFile = TRUE;\r
+                CopyBoard(filePosition, boards[0]);\r
+                for(i=0; i<BOARD_SIZE; i++) fileRights[i] = castlingRights[0][i];\r
+            }\r
+       }\r
+       if (initialMode == AnalyzeMode) {\r
+         if (appData.noChessProgram) {\r
+           DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2);\r
+           return;\r
+         }\r
+         if (appData.icsActive) {\r
+           DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2);\r
+           return;\r
+         }\r
+         AnalyzeModeEvent();\r
+       } else if (initialMode == AnalyzeFile) {\r
+         appData.showThinking = TRUE; // [HGM] thinking: moved out of ShowThinkingEvent\r
+         ShowThinkingEvent();\r
+         AnalyzeFileEvent();\r
+         AnalysisPeriodicEvent(1);\r
+       } else if (initialMode == MachinePlaysWhite) {\r
+         if (appData.noChessProgram) {\r
+           DisplayFatalError(_("MachineWhite mode requires a chess engine"),\r
+                             0, 2);\r
+           return;\r
+         }\r
+         if (appData.icsActive) {\r
+           DisplayFatalError(_("MachineWhite mode does not work with ICS mode"),\r
+                             0, 2);\r
+           return;\r
+         }\r
+         MachineWhiteEvent();\r
+       } else if (initialMode == MachinePlaysBlack) {\r
+         if (appData.noChessProgram) {\r
+           DisplayFatalError(_("MachineBlack mode requires a chess engine"),\r
+                             0, 2);\r
+           return;\r
+         }\r
+         if (appData.icsActive) {\r
+           DisplayFatalError(_("MachineBlack mode does not work with ICS mode"),\r
+                             0, 2);\r
+           return;\r
+         }\r
+         MachineBlackEvent();\r
+       } else if (initialMode == TwoMachinesPlay) {\r
+         if (appData.noChessProgram) {\r
+           DisplayFatalError(_("TwoMachines mode requires a chess engine"),\r
+                             0, 2);\r
+           return;\r
+         }\r
+         if (appData.icsActive) {\r
+           DisplayFatalError(_("TwoMachines mode does not work with ICS mode"),\r
+                             0, 2);\r
+           return;\r
+         }\r
+         TwoMachinesEvent();\r
+       } else if (initialMode == EditGame) {\r
+         EditGameEvent();\r
+       } else if (initialMode == EditPosition) {\r
+         EditPositionEvent();\r
+       } else if (initialMode == Training) {\r
+         if (*appData.loadGameFile == NULLCHAR) {\r
+           DisplayFatalError(_("Training mode requires a game file"), 0, 2);\r
+           return;\r
+         }\r
+         TrainingEvent();\r
+       }\r
+    }\r
+}\r
+\r
+/*\r
+ * Establish will establish a contact to a remote host.port.\r
+ * Sets icsPR to a ProcRef for a process (or pseudo-process)\r
+ *  used to talk to the host.\r
+ * Returns 0 if okay, error code if not.\r
+ */\r
+int\r
+establish()\r
+{\r
+    char buf[MSG_SIZ];\r
+\r
+    if (*appData.icsCommPort != NULLCHAR) {\r
+       /* Talk to the host through a serial comm port */\r
+       return OpenCommPort(appData.icsCommPort, &icsPR);\r
+\r
+    } else if (*appData.gateway != NULLCHAR) {\r
+       if (*appData.remoteShell == NULLCHAR) {\r
+           /* Use the rcmd protocol to run telnet program on a gateway host */\r
+           sprintf(buf, "%s %s %s",\r
+                   appData.telnetProgram, appData.icsHost, appData.icsPort);\r
+           return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);\r
+\r
+       } else {\r
+           /* Use the rsh program to run telnet program on a gateway host */\r
+           if (*appData.remoteUser == NULLCHAR) {\r
+               sprintf(buf, "%s %s %s %s %s", appData.remoteShell,\r
+                       appData.gateway, appData.telnetProgram,\r
+                       appData.icsHost, appData.icsPort);\r
+           } else {\r
+               sprintf(buf, "%s %s -l %s %s %s %s",\r
+                       appData.remoteShell, appData.gateway, \r
+                       appData.remoteUser, appData.telnetProgram,\r
+                       appData.icsHost, appData.icsPort);\r
+           }\r
+           return StartChildProcess(buf, "", &icsPR);\r
+\r
+       }\r
+    } else if (appData.useTelnet) {\r
+       return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);\r
+\r
+    } else {\r
+       /* TCP socket interface differs somewhat between\r
+          Unix and NT; handle details in the front end.\r
+          */\r
+       return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);\r
+    }\r
+}\r
+\r
+void\r
+show_bytes(fp, buf, count)\r
+     FILE *fp;\r
+     char *buf;\r
+     int count;\r
+{\r
+    while (count--) {\r
+       if (*buf < 040 || *(unsigned char *) buf > 0177) {\r
+           fprintf(fp, "\\%03o", *buf & 0xff);\r
+       } else {\r
+           putc(*buf, fp);\r
+       }\r
+       buf++;\r
+    }\r
+    fflush(fp);\r
+}\r
+\r
+/* Returns an errno value */\r
+int\r
+OutputMaybeTelnet(pr, message, count, outError)\r
+     ProcRef pr;\r
+     char *message;\r
+     int count;\r
+     int *outError;\r
+{\r
+    char buf[8192], *p, *q, *buflim;\r
+    int left, newcount, outcount;\r
+\r
+    if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||\r
+       *appData.gateway != NULLCHAR) {\r
+       if (appData.debugMode) {\r
+           fprintf(debugFP, ">ICS: ");\r
+           show_bytes(debugFP, message, count);\r
+           fprintf(debugFP, "\n");\r
+       }\r
+       return OutputToProcess(pr, message, count, outError);\r
+    }\r
+\r
+    buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */\r
+    p = message;\r
+    q = buf;\r
+    left = count;\r
+    newcount = 0;\r
+    while (left) {\r
+       if (q >= buflim) {\r
+           if (appData.debugMode) {\r
+               fprintf(debugFP, ">ICS: ");\r
+               show_bytes(debugFP, buf, newcount);\r
+               fprintf(debugFP, "\n");\r
+           }\r
+           outcount = OutputToProcess(pr, buf, newcount, outError);\r
+           if (outcount < newcount) return -1; /* to be sure */\r
+           q = buf;\r
+           newcount = 0;\r
+       }\r
+       if (*p == '\n') {\r
+           *q++ = '\r';\r
+           newcount++;\r
+       } else if (((unsigned char) *p) == TN_IAC) {\r
+           *q++ = (char) TN_IAC;\r
+           newcount ++;\r
+       }\r
+       *q++ = *p++;\r
+       newcount++;\r
+       left--;\r
+    }\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, ">ICS: ");\r
+       show_bytes(debugFP, buf, newcount);\r
+       fprintf(debugFP, "\n");\r
+    }\r
+    outcount = OutputToProcess(pr, buf, newcount, outError);\r
+    if (outcount < newcount) return -1; /* to be sure */\r
+    return count;\r
+}\r
+\r
+void\r
+read_from_player(isr, closure, message, count, error)\r
+     InputSourceRef isr;\r
+     VOIDSTAR closure;\r
+     char *message;\r
+     int count;\r
+     int error;\r
+{\r
+    int outError, outCount;\r
+    static int gotEof = 0;\r
+\r
+    /* Pass data read from player on to ICS */\r
+    if (count > 0) {\r
+       gotEof = 0;\r
+       outCount = OutputMaybeTelnet(icsPR, message, count, &outError);\r
+       if (outCount < count) {\r
+            DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
+       }\r
+    } else if (count < 0) {\r
+       RemoveInputSource(isr);\r
+       DisplayFatalError(_("Error reading from keyboard"), error, 1);\r
+    } else if (gotEof++ > 0) {\r
+       RemoveInputSource(isr);\r
+       DisplayFatalError(_("Got end of file from keyboard"), 0, 0);\r
+    }\r
+}\r
+\r
+void\r
+SendToICS(s)\r
+     char *s;\r
+{\r
+    int count, outCount, outError;\r
+\r
+    if (icsPR == NULL) return;\r
+\r
+    count = strlen(s);\r
+    outCount = OutputMaybeTelnet(icsPR, s, count, &outError);\r
+    if (outCount < count) {\r
+       DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
+    }\r
+}\r
+\r
+/* This is used for sending logon scripts to the ICS. Sending\r
+   without a delay causes problems when using timestamp on ICC\r
+   (at least on my machine). */\r
+void\r
+SendToICSDelayed(s,msdelay)\r
+     char *s;\r
+     long msdelay;\r
+{\r
+    int count, outCount, outError;\r
+\r
+    if (icsPR == NULL) return;\r
+\r
+    count = strlen(s);\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, ">ICS: ");\r
+       show_bytes(debugFP, s, count);\r
+       fprintf(debugFP, "\n");\r
+    }\r
+    outCount = OutputToProcessDelayed(icsPR, s, count, &outError,\r
+                                     msdelay);\r
+    if (outCount < count) {\r
+       DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
+    }\r
+}\r
+\r
+\r
+/* Remove all highlighting escape sequences in s\r
+   Also deletes any suffix starting with '(' \r
+   */\r
+char *\r
+StripHighlightAndTitle(s)\r
+     char *s;\r
+{\r
+    static char retbuf[MSG_SIZ];\r
+    char *p = retbuf;\r
+\r
+    while (*s != NULLCHAR) {\r
+       while (*s == '\033') {\r
+           while (*s != NULLCHAR && !isalpha(*s)) s++;\r
+           if (*s != NULLCHAR) s++;\r
+       }\r
+       while (*s != NULLCHAR && *s != '\033') {\r
+           if (*s == '(' || *s == '[') {\r
+               *p = NULLCHAR;\r
+               return retbuf;\r
+           }\r
+           *p++ = *s++;\r
+       }\r
+    }\r
+    *p = NULLCHAR;\r
+    return retbuf;\r
+}\r
+\r
+/* Remove all highlighting escape sequences in s */\r
+char *\r
+StripHighlight(s)\r
+     char *s;\r
+{\r
+    static char retbuf[MSG_SIZ];\r
+    char *p = retbuf;\r
+\r
+    while (*s != NULLCHAR) {\r
+       while (*s == '\033') {\r
+           while (*s != NULLCHAR && !isalpha(*s)) s++;\r
+           if (*s != NULLCHAR) s++;\r
+       }\r
+       while (*s != NULLCHAR && *s != '\033') {\r
+           *p++ = *s++;\r
+       }\r
+    }\r
+    *p = NULLCHAR;\r
+    return retbuf;\r
+}\r
+\r
+char *variantNames[] = VARIANT_NAMES;\r
+char *\r
+VariantName(v)\r
+     VariantClass v;\r
+{\r
+    return variantNames[v];\r
+}\r
+\r
+\r
+/* Identify a variant from the strings the chess servers use or the\r
+   PGN Variant tag names we use. */\r
+VariantClass\r
+StringToVariant(e)\r
+     char *e;\r
+{\r
+    char *p;\r
+    int wnum = -1;\r
+    VariantClass v = VariantNormal;\r
+    int i, found = FALSE;\r
+    char buf[MSG_SIZ];\r
+\r
+    if (!e) return v;\r
+\r
+    /* [HGM] skip over optional board-size prefixes */\r
+    if( sscanf(e, "%dx%d_", &i, &i) == 2 ||\r
+        sscanf(e, "%dx%d+%d_", &i, &i, &i) == 3 ) {\r
+        while( *e++ != '_');\r
+    }\r
+\r
+    for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {\r
+      if (StrCaseStr(e, variantNames[i])) {\r
+       v = (VariantClass) i;\r
+       found = TRUE;\r
+       break;\r
+      }\r
+    }\r
+\r
+    if (!found) {\r
+      if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))\r
+         || StrCaseStr(e, "wild/fr") \r
+         || StrCaseStr(e, "frc") || StrCaseStr(e, "960")) {\r
+        v = VariantFischeRandom;\r
+      } else if ((i = 4, p = StrCaseStr(e, "wild")) ||\r
+                (i = 1, p = StrCaseStr(e, "w"))) {\r
+       p += i;\r
+       while (*p && (isspace(*p) || *p == '(' || *p == '/')) p++;\r
+       if (isdigit(*p)) {\r
+         wnum = atoi(p);\r
+       } else {\r
+         wnum = -1;\r
+       }\r
+       switch (wnum) {\r
+       case 0: /* FICS only, actually */\r
+       case 1:\r
+         /* Castling legal even if K starts on d-file */\r
+         v = VariantWildCastle;\r
+         break;\r
+       case 2:\r
+       case 3:\r
+       case 4:\r
+         /* Castling illegal even if K & R happen to start in\r
+            normal positions. */\r
+         v = VariantNoCastle;\r
+         break;\r
+       case 5:\r
+       case 7:\r
+       case 8:\r
+       case 10:\r
+       case 11:\r
+       case 12:\r
+       case 13:\r
+       case 14:\r
+       case 15:\r
+       case 18:\r
+       case 19:\r
+         /* Castling legal iff K & R start in normal positions */\r
+         v = VariantNormal;\r
+         break;\r
+       case 6:\r
+       case 20:\r
+       case 21:\r
+         /* Special wilds for position setup; unclear what to do here */\r
+         v = VariantLoadable;\r
+         break;\r
+       case 9:\r
+         /* Bizarre ICC game */\r
+         v = VariantTwoKings;\r
+         break;\r
+       case 16:\r
+         v = VariantKriegspiel;\r
+         break;\r
+       case 17:\r
+         v = VariantLosers;\r
+         break;\r
+       case 22:\r
+         v = VariantFischeRandom;\r
+         break;\r
+       case 23:\r
+         v = VariantCrazyhouse;\r
+         break;\r
+       case 24:\r
+         v = VariantBughouse;\r
+         break;\r
+       case 25:\r
+         v = Variant3Check;\r
+         break;\r
+       case 26:\r
+         /* Not quite the same as FICS suicide! */\r
+         v = VariantGiveaway;\r
+         break;\r
+       case 27:\r
+         v = VariantAtomic;\r
+         break;\r
+       case 28:\r
+         v = VariantShatranj;\r
+         break;\r
+\r
+       /* Temporary names for future ICC types.  The name *will* change in \r
+          the next xboard/WinBoard release after ICC defines it. */\r
+       case 29:\r
+         v = Variant29;\r
+         break;\r
+       case 30:\r
+         v = Variant30;\r
+         break;\r
+       case 31:\r
+         v = Variant31;\r
+         break;\r
+       case 32:\r
+         v = Variant32;\r
+         break;\r
+       case 33:\r
+         v = Variant33;\r
+         break;\r
+       case 34:\r
+         v = Variant34;\r
+         break;\r
+       case 35:\r
+         v = Variant35;\r
+         break;\r
+       case 36:\r
+         v = Variant36;\r
+         break;\r
+        case 37:\r
+          v = VariantShogi;\r
+         break;\r
+        case 38:\r
+          v = VariantXiangqi;\r
+         break;\r
+        case 39:\r
+          v = VariantCourier;\r
+         break;\r
+        case 40:\r
+          v = VariantGothic;\r
+         break;\r
+        case 41:\r
+          v = VariantCapablanca;\r
+         break;\r
+        case 42:\r
+          v = VariantKnightmate;\r
+         break;\r
+        case 43:\r
+          v = VariantFairy;\r
+          break;\r
+        case 44:\r
+          v = VariantCylinder;\r
+         break;\r
+        case 45:\r
+          v = VariantFalcon;\r
+         break;\r
+        case 46:\r
+          v = VariantCapaRandom;\r
+         break;\r
+        case 47:\r
+          v = VariantBerolina;\r
+         break;\r
+        case 48:\r
+          v = VariantJanus;\r
+         break;\r
+        case 49:\r
+          v = VariantSuper;\r
+         break;\r
+        case 50:\r
+          v = VariantGreat;\r
+         break;\r
+       case -1:\r
+         /* Found "wild" or "w" in the string but no number;\r
+            must assume it's normal chess. */\r
+         v = VariantNormal;\r
+         break;\r
+       default:\r
+         sprintf(buf, _("Unknown wild type %d"), wnum);\r
+         DisplayError(buf, 0);\r
+         v = VariantUnknown;\r
+         break;\r
+       }\r
+      }\r
+    }\r
+    if (appData.debugMode) {\r
+      fprintf(debugFP, _("recognized '%s' (%d) as variant %s\n"),\r
+             e, wnum, VariantName(v));\r
+    }\r
+    return v;\r
+}\r
+\r
+static int leftover_start = 0, leftover_len = 0;\r
+char star_match[STAR_MATCH_N][MSG_SIZ];\r
+\r
+/* Test whether pattern is present at &buf[*index]; if so, return TRUE,\r
+   advance *index beyond it, and set leftover_start to the new value of\r
+   *index; else return FALSE.  If pattern contains the character '*', it\r
+   matches any sequence of characters not containing '\r', '\n', or the\r
+   character following the '*' (if any), and the matched sequence(s) are\r
+   copied into star_match.\r
+   */\r
+int\r
+looking_at(buf, index, pattern)\r
+     char *buf;\r
+     int *index;\r
+     char *pattern;\r
+{\r
+    char *bufp = &buf[*index], *patternp = pattern;\r
+    int star_count = 0;\r
+    char *matchp = star_match[0];\r
+    \r
+    for (;;) {\r
+       if (*patternp == NULLCHAR) {\r
+           *index = leftover_start = bufp - buf;\r
+           *matchp = NULLCHAR;\r
+           return TRUE;\r
+       }\r
+       if (*bufp == NULLCHAR) return FALSE;\r
+       if (*patternp == '*') {\r
+           if (*bufp == *(patternp + 1)) {\r
+               *matchp = NULLCHAR;\r
+               matchp = star_match[++star_count];\r
+               patternp += 2;\r
+               bufp++;\r
+               continue;\r
+           } else if (*bufp == '\n' || *bufp == '\r') {\r
+               patternp++;\r
+               if (*patternp == NULLCHAR)\r
+                 continue;\r
+               else\r
+                 return FALSE;\r
+           } else {\r
+               *matchp++ = *bufp++;\r
+               continue;\r
+           }\r
+       }\r
+       if (*patternp != *bufp) return FALSE;\r
+       patternp++;\r
+       bufp++;\r
+    }\r
+}\r
+\r
+void\r
+SendToPlayer(data, length)\r
+     char *data;\r
+     int length;\r
+{\r
+    int error, outCount;\r
+    outCount = OutputToProcess(NoProc, data, length, &error);\r
+    if (outCount < length) {\r
+       DisplayFatalError(_("Error writing to display"), error, 1);\r
+    }\r
+}\r
+\r
+void\r
+PackHolding(packed, holding)\r
+     char packed[];\r
+     char *holding;\r
+{\r
+    char *p = holding;\r
+    char *q = packed;\r
+    int runlength = 0;\r
+    int curr = 9999;\r
+    do {\r
+       if (*p == curr) {\r
+           runlength++;\r
+       } else {\r
+           switch (runlength) {\r
+             case 0:\r
+               break;\r
+             case 1:\r
+               *q++ = curr;\r
+               break;\r
+             case 2:\r
+               *q++ = curr;\r
+               *q++ = curr;\r
+               break;\r
+             default:\r
+               sprintf(q, "%d", runlength);\r
+               while (*q) q++;\r
+               *q++ = curr;\r
+               break;\r
+           }\r
+           runlength = 1;\r
+           curr = *p;\r
+       }\r
+    } while (*p++);\r
+    *q = NULLCHAR;\r
+}\r
+\r
+/* Telnet protocol requests from the front end */\r
+void\r
+TelnetRequest(ddww, option)\r
+     unsigned char ddww, option;\r
+{\r
+    unsigned char msg[3];\r
+    int outCount, outError;\r
+\r
+    if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;\r
+\r
+    if (appData.debugMode) {\r
+       char buf1[8], buf2[8], *ddwwStr, *optionStr;\r
+       switch (ddww) {\r
+         case TN_DO:\r
+           ddwwStr = "DO";\r
+           break;\r
+         case TN_DONT:\r
+           ddwwStr = "DONT";\r
+           break;\r
+         case TN_WILL:\r
+           ddwwStr = "WILL";\r
+           break;\r
+         case TN_WONT:\r
+           ddwwStr = "WONT";\r
+           break;\r
+         default:\r
+           ddwwStr = buf1;\r
+           sprintf(buf1, "%d", ddww);\r
+           break;\r
+       }\r
+       switch (option) {\r
+         case TN_ECHO:\r
+           optionStr = "ECHO";\r
+           break;\r
+         default:\r
+           optionStr = buf2;\r
+           sprintf(buf2, "%d", option);\r
+           break;\r
+       }\r
+       fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);\r
+    }\r
+    msg[0] = TN_IAC;\r
+    msg[1] = ddww;\r
+    msg[2] = option;\r
+    outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);\r
+    if (outCount < 3) {\r
+       DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
+    }\r
+}\r
+\r
+void\r
+DoEcho()\r
+{\r
+    if (!appData.icsActive) return;\r
+    TelnetRequest(TN_DO, TN_ECHO);\r
+}\r
+\r
+void\r
+DontEcho()\r
+{\r
+    if (!appData.icsActive) return;\r
+    TelnetRequest(TN_DONT, TN_ECHO);\r
+}\r
+\r
+void\r
+CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)\r
+{\r
+    /* put the holdings sent to us by the server on the board holdings area */\r
+    int i, j, holdingsColumn, holdingsStartRow, direction, countsColumn;\r
+    char p;\r
+    ChessSquare piece;\r
+\r
+    if(gameInfo.holdingsWidth < 2)  return;\r
+\r
+    if( (int)lowestPiece >= BlackPawn ) {\r
+        holdingsColumn = 0;\r
+        countsColumn = 1;\r
+        holdingsStartRow = BOARD_HEIGHT-1;\r
+        direction = -1;\r
+    } else {\r
+        holdingsColumn = BOARD_WIDTH-1;\r
+        countsColumn = BOARD_WIDTH-2;\r
+        holdingsStartRow = 0;\r
+        direction = 1;\r
+    }\r
+\r
+    for(i=0; i<BOARD_HEIGHT; i++) { /* clear holdings */\r
+        board[i][holdingsColumn] = EmptySquare;\r
+        board[i][countsColumn]   = (ChessSquare) 0;\r
+    }\r
+    while( (p=*holdings++) != NULLCHAR ) {\r
+        piece = CharToPiece( ToUpper(p) );\r
+        if(piece == EmptySquare) continue;\r
+        /*j = (int) piece - (int) WhitePawn;*/\r
+        j = PieceToNumber(piece);\r
+        if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */\r
+        if(j < 0) continue;               /* should not happen */\r
+        piece = (ChessSquare) ( (int)piece + (int)lowestPiece );\r
+        board[holdingsStartRow+j*direction][holdingsColumn] = piece;\r
+        board[holdingsStartRow+j*direction][countsColumn]++;\r
+    }\r
+\r
+}\r
+\r
+\r
+void\r
+VariantSwitch(Board board, VariantClass newVariant)\r
+{\r
+   int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;\r
+   int oldCurrentMove = currentMove, oldForwardMostMove = forwardMostMove, oldBackwardMostMove = backwardMostMove;\r
+   Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;\r
+\r
+   startedFromPositionFile = FALSE;\r
+   if(gameInfo.variant == newVariant) return;\r
+\r
+   /* [HGM] This routine is called each time an assignment is made to\r
+    * gameInfo.variant during a game, to make sure the board sizes\r
+    * are set to match the new variant. If that means adding or deleting\r
+    * holdings, we shift the playing board accordingly\r
+    * This kludge is needed because in ICS observe mode, we get boards\r
+    * of an ongoing game without knowing the variant, and learn about the\r
+    * latter only later. This can be because of the move list we requested,\r
+    * in which case the game history is refilled from the beginning anyway,\r
+    * but also when receiving holdings of a crazyhouse game. In the latter\r
+    * case we want to add those holdings to the already received position.\r
+    */\r
+\r
+\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Switch board from %s to %s\n",\r
+               VariantName(gameInfo.variant), VariantName(newVariant));\r
+    setbuf(debugFP, NULL);\r
+  }\r
+    shuffleOpenings = 0;       /* [HGM] shuffle */\r
+    gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */\r
+    switch(newVariant) {\r
+            case VariantShogi:\r
+              newWidth = 9;  newHeight = 9;\r
+              gameInfo.holdingsSize = 7;\r
+            case VariantBughouse:\r
+            case VariantCrazyhouse:\r
+              newHoldingsWidth = 2; break;\r
+            default:\r
+              newHoldingsWidth = gameInfo.holdingsSize = 0;\r
+    }\r
+\r
+    if(newWidth  != gameInfo.boardWidth  ||\r
+       newHeight != gameInfo.boardHeight ||\r
+       newHoldingsWidth != gameInfo.holdingsWidth ) {\r
+\r
+        /* shift position to new playing area, if needed */\r
+        if(newHoldingsWidth > gameInfo.holdingsWidth) {\r
+           for(i=0; i<BOARD_HEIGHT; i++) \r
+               for(j=BOARD_RGHT-1; j>=BOARD_LEFT; j--)\r
+                   board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =\r
+                                                     board[i][j];\r
+           for(i=0; i<newHeight; i++) {\r
+               board[i][0] = board[i][newWidth+2*newHoldingsWidth-1] = EmptySquare;\r
+               board[i][1] = board[i][newWidth+2*newHoldingsWidth-2] = (ChessSquare) 0;\r
+           }\r
+        } else if(newHoldingsWidth < gameInfo.holdingsWidth) {\r
+           for(i=0; i<BOARD_HEIGHT; i++)\r
+               for(j=BOARD_LEFT; j<BOARD_RGHT; j++)\r
+                   board[i][j+newHoldingsWidth-gameInfo.holdingsWidth] =\r
+                                                 board[i][j];\r
+        }\r
+\r
+        gameInfo.boardWidth  = newWidth;\r
+        gameInfo.boardHeight = newHeight;\r
+        gameInfo.holdingsWidth = newHoldingsWidth;\r
+        gameInfo.variant = newVariant;\r
+        InitDrawingSizes(-2, 0);\r
+\r
+        /* [HGM] The following should definitely be solved in a better way */\r
+#if 0\r
+        CopyBoard(board, tempBoard); /* save position in case it is board[0] */\r
+        for(i=0; i<BOARD_SIZE; i++) saveCastling[i] = castlingRights[0][i];\r
+        saveEP = epStatus[0];\r
+#endif\r
+        InitPosition(FALSE);          /* this sets up board[0], but also other stuff        */\r
+#if 0\r
+        epStatus[0] = saveEP;\r
+        for(i=0; i<BOARD_SIZE; i++) castlingRights[0][i] = saveCastling[i];\r
+        CopyBoard(tempBoard, board); /* restore position received from ICS   */\r
+#endif\r
+    } else { gameInfo.variant = newVariant; InitPosition(FALSE); }\r
+\r
+    forwardMostMove = oldForwardMostMove;\r
+    backwardMostMove = oldBackwardMostMove;\r
+    currentMove = oldCurrentMove; /* InitPos reset these, but we need still to redraw the position */\r
+}\r
+\r
+static int loggedOn = FALSE;\r
+\r
+/*-- Game start info cache: --*/\r
+int gs_gamenum;\r
+char gs_kind[MSG_SIZ];\r
+static char player1Name[128] = "";\r
+static char player2Name[128] = "";\r
+static int player1Rating = -1;\r
+static int player2Rating = -1;\r
+/*----------------------------*/\r
+\r
+ColorClass curColor = ColorNormal;\r
+int suppressKibitz = 0;\r
+\r
+void\r
+read_from_ics(isr, closure, data, count, error)\r
+     InputSourceRef isr;\r
+     VOIDSTAR closure;\r
+     char *data;\r
+     int count;\r
+     int error;\r
+{\r
+#define BUF_SIZE 8192\r
+#define STARTED_NONE 0\r
+#define STARTED_MOVES 1\r
+#define STARTED_BOARD 2\r
+#define STARTED_OBSERVE 3\r
+#define STARTED_HOLDINGS 4\r
+#define STARTED_CHATTER 5\r
+#define STARTED_COMMENT 6\r
+#define STARTED_MOVES_NOHIDE 7\r
+    \r
+    static int started = STARTED_NONE;\r
+    static char parse[20000];\r
+    static int parse_pos = 0;\r
+    static char buf[BUF_SIZE + 1];\r
+    static int firstTime = TRUE, intfSet = FALSE;\r
+    static ColorClass prevColor = ColorNormal;\r
+    static int savingComment = FALSE;\r
+    char str[500];\r
+    int i, oldi;\r
+    int buf_len;\r
+    int next_out;\r
+    int tkind;\r
+    int backup;    /* [DM] For zippy color lines */\r
+    char *p;\r
+\r
+    if (appData.debugMode) {\r
+      if (!error) {\r
+       fprintf(debugFP, "<ICS: ");\r
+       show_bytes(debugFP, data, count);\r
+       fprintf(debugFP, "\n");\r
+      }\r
+    }\r
+\r
+    if (appData.debugMode) { int f = forwardMostMove;\r
+        fprintf(debugFP, "ics input %d, castling = %d %d %d %d %d %d\n", f,\r
+                castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);\r
+    }\r
+    if (count > 0) {\r
+       /* If last read ended with a partial line that we couldn't parse,\r
+          prepend it to the new read and try again. */\r
+       if (leftover_len > 0) {\r
+           for (i=0; i<leftover_len; i++)\r
+             buf[i] = buf[leftover_start + i];\r
+       }\r
+\r
+       /* Copy in new characters, removing nulls and \r's */\r
+       buf_len = leftover_len;\r
+       for (i = 0; i < count; i++) {\r
+           if (data[i] != NULLCHAR && data[i] != '\r')\r
+             buf[buf_len++] = data[i];\r
+           if(buf_len >= 5 && buf[buf_len-5]=='\n' && buf[buf_len-4]=='\\' && \r
+                               buf[buf_len-3]==' '  && buf[buf_len-2]==' '  && buf[buf_len-1]==' ') \r
+               buf_len -= 5; // [HGM] ICS: join continuation line of Lasker 2.2.3 server with previous\r
+       }\r
+\r
+       buf[buf_len] = NULLCHAR;\r
+       next_out = leftover_len;\r
+       leftover_start = 0;\r
+       \r
+       i = 0;\r
+       while (i < buf_len) {\r
+           /* Deal with part of the TELNET option negotiation\r
+              protocol.  We refuse to do anything beyond the\r
+              defaults, except that we allow the WILL ECHO option,\r
+              which ICS uses to turn off password echoing when we are\r
+              directly connected to it.  We reject this option\r
+              if localLineEditing mode is on (always on in xboard)\r
+               and we are talking to port 23, which might be a real\r
+              telnet server that will try to keep WILL ECHO on permanently.\r
+             */\r
+           if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {\r
+               static int remoteEchoOption = FALSE; /* telnet ECHO option */\r
+               unsigned char option;\r
+               oldi = i;\r
+               switch ((unsigned char) buf[++i]) {\r
+                 case TN_WILL:\r
+                   if (appData.debugMode)\r
+                     fprintf(debugFP, "\n<WILL ");\r
+                   switch (option = (unsigned char) buf[++i]) {\r
+                     case TN_ECHO:\r
+                       if (appData.debugMode)\r
+                         fprintf(debugFP, "ECHO ");\r
+                       /* Reply only if this is a change, according\r
+                          to the protocol rules. */\r
+                       if (remoteEchoOption) break;\r
+                       if (appData.localLineEditing &&\r
+                           atoi(appData.icsPort) == TN_PORT) {\r
+                           TelnetRequest(TN_DONT, TN_ECHO);\r
+                       } else {\r
+                           EchoOff();\r
+                           TelnetRequest(TN_DO, TN_ECHO);\r
+                           remoteEchoOption = TRUE;\r
+                       }\r
+                       break;\r
+                     default:\r
+                       if (appData.debugMode)\r
+                         fprintf(debugFP, "%d ", option);\r
+                       /* Whatever this is, we don't want it. */\r
+                       TelnetRequest(TN_DONT, option);\r
+                       break;\r
+                   }\r
+                   break;\r
+                 case TN_WONT:\r
+                   if (appData.debugMode)\r
+                     fprintf(debugFP, "\n<WONT ");\r
+                   switch (option = (unsigned char) buf[++i]) {\r
+                     case TN_ECHO:\r
+                       if (appData.debugMode)\r
+                         fprintf(debugFP, "ECHO ");\r
+                       /* Reply only if this is a change, according\r
+                          to the protocol rules. */\r
+                       if (!remoteEchoOption) break;\r
+                       EchoOn();\r
+                       TelnetRequest(TN_DONT, TN_ECHO);\r
+                       remoteEchoOption = FALSE;\r
+                       break;\r
+                     default:\r
+                       if (appData.debugMode)\r
+                         fprintf(debugFP, "%d ", (unsigned char) option);\r
+                       /* Whatever this is, it must already be turned\r
+                          off, because we never agree to turn on\r
+                          anything non-default, so according to the\r
+                          protocol rules, we don't reply. */\r
+                       break;\r
+                   }\r
+                   break;\r
+                 case TN_DO:\r
+                   if (appData.debugMode)\r
+                     fprintf(debugFP, "\n<DO ");\r
+                   switch (option = (unsigned char) buf[++i]) {\r
+                     default:\r
+                       /* Whatever this is, we refuse to do it. */\r
+                       if (appData.debugMode)\r
+                         fprintf(debugFP, "%d ", option);\r
+                       TelnetRequest(TN_WONT, option);\r
+                       break;\r
+                   }\r
+                   break;\r
+                 case TN_DONT:\r
+                   if (appData.debugMode)\r
+                     fprintf(debugFP, "\n<DONT ");\r
+                   switch (option = (unsigned char) buf[++i]) {\r
+                     default:\r
+                       if (appData.debugMode)\r
+                         fprintf(debugFP, "%d ", option);\r
+                       /* Whatever this is, we are already not doing\r
+                          it, because we never agree to do anything\r
+                          non-default, so according to the protocol\r
+                          rules, we don't reply. */\r
+                       break;\r
+                   }\r
+                   break;\r
+                 case TN_IAC:\r
+                   if (appData.debugMode)\r
+                     fprintf(debugFP, "\n<IAC ");\r
+                   /* Doubled IAC; pass it through */\r
+                   i--;\r
+                   break;\r
+                 default:\r
+                   if (appData.debugMode)\r
+                     fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);\r
+                   /* Drop all other telnet commands on the floor */\r
+                   break;\r
+               }\r
+               if (oldi > next_out)\r
+                 SendToPlayer(&buf[next_out], oldi - next_out);\r
+               if (++i > next_out)\r
+                 next_out = i;\r
+               continue;\r
+           }\r
+               \r
+           /* OK, this at least will *usually* work */\r
+           if (!loggedOn && looking_at(buf, &i, "ics%")) {\r
+               loggedOn = TRUE;\r
+           }\r
+           \r
+           if (loggedOn && !intfSet) {\r
+               if (ics_type == ICS_ICC) {\r
+                 sprintf(str,\r
+                         "/set-quietly interface %s\n/set-quietly style 12\n",\r
+                         programVersion);\r
+\r
+               } else if (ics_type == ICS_CHESSNET) {\r
+                 sprintf(str, "/style 12\n");\r
+               } else {\r
+                 strcpy(str, "alias $ @\n$set interface ");\r
+                 strcat(str, programVersion);\r
+                 strcat(str, "\n$iset startpos 1\n$iset ms 1\n");\r
+#ifdef WIN32\r
+                 strcat(str, "$iset nohighlight 1\n");\r
+#endif\r
+                 strcat(str, "$iset lock 1\n$style 12\n");\r
+               }\r
+               SendToICS(str);\r
+               intfSet = TRUE;\r
+           }\r
+\r
+           if (started == STARTED_COMMENT) {\r
+               /* Accumulate characters in comment */\r
+               parse[parse_pos++] = buf[i];\r
+               if (buf[i] == '\n') {\r
+                   parse[parse_pos] = NULLCHAR;\r
+                   if(!suppressKibitz) // [HGM] kibitz\r
+                       AppendComment(forwardMostMove, StripHighlight(parse));\r
+                   else { // [HGM kibitz: divert memorized engine kibitz to engine-output window\r
+                       int nrDigit = 0, nrAlph = 0, i;\r
+                       if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input\r
+                       { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }\r
+                       parse[parse_pos] = NULLCHAR;\r
+                       // try to be smart: if it does not look like search info, it should go to\r
+                       // ICS interaction window after all, not to engine-output window.\r
+                       for(i=0; i<parse_pos; i++) { // count letters and digits\r
+                           nrDigit += (parse[i] >= '0' && parse[i] <= '9');\r
+                           nrAlph  += (parse[i] >= 'a' && parse[i] <= 'z');\r
+                           nrAlph  += (parse[i] >= 'A' && parse[i] <= 'Z');\r
+                       }\r
+                       if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info\r
+                           OutputKibitz(suppressKibitz, parse);\r
+                       } else {\r
+                           char tmp[MSG_SIZ];\r
+                           sprintf(tmp, _("your opponent kibitzes: %s"), parse);\r
+                           SendToPlayer(tmp, strlen(tmp));\r
+                       }\r
+                   }\r
+                   started = STARTED_NONE;\r
+               } else {\r
+                   /* Don't match patterns against characters in chatter */\r
+                   i++;\r
+                   continue;\r
+               }\r
+           }\r
+           if (started == STARTED_CHATTER) {\r
+               if (buf[i] != '\n') {\r
+                   /* Don't match patterns against characters in chatter */\r
+                   i++;\r
+                   continue;\r
+               }\r
+               started = STARTED_NONE;\r
+           }\r
+\r
+            /* Kludge to deal with rcmd protocol */\r
+           if (firstTime && looking_at(buf, &i, "\001*")) {\r
+               DisplayFatalError(&buf[1], 0, 1);\r
+               continue;\r
+           } else {\r
+               firstTime = FALSE;\r
+           }\r
+\r
+           if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {\r
+               ics_type = ICS_ICC;\r
+               ics_prefix = "/";\r
+               if (appData.debugMode)\r
+                 fprintf(debugFP, "ics_type %d\n", ics_type);\r
+               continue;\r
+           }\r
+           if (!loggedOn && looking_at(buf, &i, "freechess.org")) {\r
+               ics_type = ICS_FICS;\r
+               ics_prefix = "$";\r
+               if (appData.debugMode)\r
+                 fprintf(debugFP, "ics_type %d\n", ics_type);\r
+               continue;\r
+           }\r
+           if (!loggedOn && looking_at(buf, &i, "chess.net")) {\r
+               ics_type = ICS_CHESSNET;\r
+               ics_prefix = "/";\r
+               if (appData.debugMode)\r
+                 fprintf(debugFP, "ics_type %d\n", ics_type);\r
+               continue;\r
+           }\r
+\r
+           if (!loggedOn &&\r
+               (looking_at(buf, &i, "\"*\" is *a registered name") ||\r
+                looking_at(buf, &i, "Logging you in as \"*\"") ||\r
+                looking_at(buf, &i, "will be \"*\""))) {\r
+             strcpy(ics_handle, star_match[0]);\r
+             continue;\r
+           }\r
+\r
+           if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {\r
+             char buf[MSG_SIZ];\r
+             sprintf(buf, "%s@%s", ics_handle, appData.icsHost);\r
+             DisplayIcsInteractionTitle(buf);\r
+             have_set_title = TRUE;\r
+           }\r
+\r
+           /* skip finger notes */\r
+           if (started == STARTED_NONE &&\r
+               ((buf[i] == ' ' && isdigit(buf[i+1])) ||\r
+                (buf[i] == '1' && buf[i+1] == '0')) &&\r
+               buf[i+2] == ':' && buf[i+3] == ' ') {\r
+             started = STARTED_CHATTER;\r
+             i += 3;\r
+             continue;\r
+           }\r
+\r
+           /* skip formula vars */\r
+           if (started == STARTED_NONE &&\r
+               buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {\r
+             started = STARTED_CHATTER;\r
+             i += 3;\r
+             continue;\r
+           }\r
+\r
+           oldi = i;\r
+           // [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window\r
+           if (appData.autoKibitz && started == STARTED_NONE && \r
+                !appData.icsEngineAnalyze &&                     // [HGM] [DM] ICS analyze\r
+               (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {\r
+               if(looking_at(buf, &i, "* kibitzes: ") &&\r
+                  (StrStr(star_match[0], gameInfo.white) == star_match[0] || \r
+                   StrStr(star_match[0], gameInfo.black) == star_match[0]   )) { // kibitz of self or opponent\r
+                       suppressKibitz = TRUE;\r
+                       if((StrStr(star_match[0], gameInfo.white) == star_match[0])\r
+                               && (gameMode == IcsPlayingWhite) ||\r
+                          (StrStr(star_match[0], gameInfo.black) == star_match[0])\r
+                               && (gameMode == IcsPlayingBlack)   ) // opponent kibitz\r
+                           started = STARTED_CHATTER; // own kibitz we simply discard\r
+                       else {\r
+                           started = STARTED_COMMENT; // make sure it will be collected in parse[]\r
+                           parse_pos = 0; parse[0] = NULLCHAR;\r
+                           savingComment = TRUE;\r
+                           suppressKibitz = gameMode != IcsObserving ? 2 :\r
+                               (StrStr(star_match[0], gameInfo.white) == NULL) + 1;\r
+                       } \r
+                       continue;\r
+               } else\r
+               if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz\r
+                   started = STARTED_CHATTER;\r
+                   suppressKibitz = TRUE;\r
+               }\r
+           } // [HGM] kibitz: end of patch\r
+\r
+           if (appData.zippyTalk || appData.zippyPlay) {\r
+                /* [DM] Backup address for color zippy lines */\r
+                backup = i;\r
+#if ZIPPY\r
+       #ifdef WIN32\r
+               if (loggedOn == TRUE)\r
+                       if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||\r
+                          (appData.zippyPlay && ZippyMatch(buf, &backup)));\r
+       #else\r
+                if (ZippyControl(buf, &i) ||\r
+                    ZippyConverse(buf, &i) ||\r
+                    (appData.zippyPlay && ZippyMatch(buf, &i))) {\r
+                     loggedOn = TRUE;\r
+                      if (!appData.colorize) continue;\r
+               }\r
+       #endif\r
+#endif\r
+           } // [DM] 'else { ' deleted\r
+               if (/* Don't color "message" or "messages" output */\r
+                   (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||\r
+                   looking_at(buf, &i, "*. * at *:*: ") ||\r
+                   looking_at(buf, &i, "--* (*:*): ") ||\r
+                   /* Regular tells and says */\r
+                   (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||\r
+                   looking_at(buf, &i, "* (your partner) tells you: ") ||\r
+                   looking_at(buf, &i, "* says: ") ||\r
+                   /* Message notifications (same color as tells) */\r
+                   looking_at(buf, &i, "* has left a message ") ||\r
+                   looking_at(buf, &i, "* just sent you a message:\n") ||\r
+                   /* Whispers and kibitzes */\r
+                   (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||\r
+                   looking_at(buf, &i, "* kibitzes: ") ||\r
+                   /* Channel tells */\r
+                   (tkind = 3, looking_at(buf, &i, "*(*: "))) {\r
+\r
+                 if (tkind == 1 && strchr(star_match[0], ':')) {\r
+                     /* Avoid "tells you:" spoofs in channels */\r
+                    tkind = 3;\r
+                 }\r
+                 if (star_match[0][0] == NULLCHAR ||\r
+                     strchr(star_match[0], ' ') ||\r
+                     (tkind == 3 && strchr(star_match[1], ' '))) {\r
+                   /* Reject bogus matches */\r
+                   i = oldi;\r
+                 } else {\r
+                   if (appData.colorize) {\r
+                     if (oldi > next_out) {\r
+                       SendToPlayer(&buf[next_out], oldi - next_out);\r
+                       next_out = oldi;\r
+                     }\r
+                     switch (tkind) {\r
+                     case 1:\r
+                       Colorize(ColorTell, FALSE);\r
+                       curColor = ColorTell;\r
+                       break;\r
+                     case 2:\r
+                       Colorize(ColorKibitz, FALSE);\r
+                       curColor = ColorKibitz;\r
+                       break;\r
+                     case 3:\r
+                       p = strrchr(star_match[1], '(');\r
+                       if (p == NULL) {\r
+                         p = star_match[1];\r
+                       } else {\r
+                         p++;\r
+                       }\r
+                       if (atoi(p) == 1) {\r
+                         Colorize(ColorChannel1, FALSE);\r
+                         curColor = ColorChannel1;\r
+                       } else {\r
+                         Colorize(ColorChannel, FALSE);\r
+                         curColor = ColorChannel;\r
+                       }\r
+                       break;\r
+                     case 5:\r
+                       curColor = ColorNormal;\r
+                       break;\r
+                     }\r
+                   }\r
+                   if (started == STARTED_NONE && appData.autoComment &&\r
+                       (gameMode == IcsObserving ||\r
+                        gameMode == IcsPlayingWhite ||\r
+                        gameMode == IcsPlayingBlack)) {\r
+                     parse_pos = i - oldi;\r
+                     memcpy(parse, &buf[oldi], parse_pos);\r
+                     parse[parse_pos] = NULLCHAR;\r
+                     started = STARTED_COMMENT;\r
+                     savingComment = TRUE;\r
+                   } else {\r
+                     started = STARTED_CHATTER;\r
+                     savingComment = FALSE;\r
+                   }\r
+                   loggedOn = TRUE;\r
+                   continue;\r
+                 }\r
+               }\r
+\r
+               if (looking_at(buf, &i, "* s-shouts: ") ||\r
+                   looking_at(buf, &i, "* c-shouts: ")) {\r
+                   if (appData.colorize) {\r
+                       if (oldi > next_out) {\r
+                           SendToPlayer(&buf[next_out], oldi - next_out);\r
+                           next_out = oldi;\r
+                       }\r
+                       Colorize(ColorSShout, FALSE);\r
+                       curColor = ColorSShout;\r
+                   }\r
+                   loggedOn = TRUE;\r
+                   started = STARTED_CHATTER;\r
+                   continue;\r
+               }\r
+\r
+               if (looking_at(buf, &i, "--->")) {\r
+                   loggedOn = TRUE;\r
+                   continue;\r
+               }\r
+\r
+               if (looking_at(buf, &i, "* shouts: ") ||\r
+                   looking_at(buf, &i, "--> ")) {\r
+                   if (appData.colorize) {\r
+                       if (oldi > next_out) {\r
+                           SendToPlayer(&buf[next_out], oldi - next_out);\r
+                           next_out = oldi;\r
+                       }\r
+                       Colorize(ColorShout, FALSE);\r
+                       curColor = ColorShout;\r
+                   }\r
+                   loggedOn = TRUE;\r
+                   started = STARTED_CHATTER;\r
+                   continue;\r
+               }\r
+\r
+               if (looking_at( buf, &i, "Challenge:")) {\r
+                   if (appData.colorize) {\r
+                       if (oldi > next_out) {\r
+                           SendToPlayer(&buf[next_out], oldi - next_out);\r
+                           next_out = oldi;\r
+                       }\r
+                       Colorize(ColorChallenge, FALSE);\r
+                       curColor = ColorChallenge;\r
+                   }\r
+                   loggedOn = TRUE;\r
+                   continue;\r
+               }\r
+\r
+               if (looking_at(buf, &i, "* offers you") ||\r
+                   looking_at(buf, &i, "* offers to be") ||\r
+                   looking_at(buf, &i, "* would like to") ||\r
+                   looking_at(buf, &i, "* requests to") ||\r
+                   looking_at(buf, &i, "Your opponent offers") ||\r
+                   looking_at(buf, &i, "Your opponent requests")) {\r
+\r
+                   if (appData.colorize) {\r
+                       if (oldi > next_out) {\r
+                           SendToPlayer(&buf[next_out], oldi - next_out);\r
+                           next_out = oldi;\r
+                       }\r
+                       Colorize(ColorRequest, FALSE);\r
+                       curColor = ColorRequest;\r
+                   }\r
+                   continue;\r
+               }\r
+\r
+               if (looking_at(buf, &i, "* (*) seeking")) {\r
+                   if (appData.colorize) {\r
+                       if (oldi > next_out) {\r
+                           SendToPlayer(&buf[next_out], oldi - next_out);\r
+                           next_out = oldi;\r
+                       }\r
+                       Colorize(ColorSeek, FALSE);\r
+                       curColor = ColorSeek;\r
+                   }\r
+                   continue;\r
+           }\r
+\r
+           if (looking_at(buf, &i, "\\   ")) {\r
+               if (prevColor != ColorNormal) {\r
+                   if (oldi > next_out) {\r
+                       SendToPlayer(&buf[next_out], oldi - next_out);\r
+                       next_out = oldi;\r
+                   }\r
+                   Colorize(prevColor, TRUE);\r
+                   curColor = prevColor;\r
+               }\r
+               if (savingComment) {\r
+                   parse_pos = i - oldi;\r
+                   memcpy(parse, &buf[oldi], parse_pos);\r
+                   parse[parse_pos] = NULLCHAR;\r
+                   started = STARTED_COMMENT;\r
+               } else {\r
+                   started = STARTED_CHATTER;\r
+               }\r
+               continue;\r
+           }\r
+\r
+           if (looking_at(buf, &i, "Black Strength :") ||\r
+               looking_at(buf, &i, "<<< style 10 board >>>") ||\r
+               looking_at(buf, &i, "<10>") ||\r
+               looking_at(buf, &i, "#@#")) {\r
+               /* Wrong board style */\r
+               loggedOn = TRUE;\r
+               SendToICS(ics_prefix);\r
+               SendToICS("set style 12\n");\r
+               SendToICS(ics_prefix);\r
+               SendToICS("refresh\n");\r
+               continue;\r
+           }\r
+           \r
+           if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {\r
+               ICSInitScript();\r
+               have_sent_ICS_logon = 1;\r
+               continue;\r
+           }\r
+             \r
+           if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ && \r
+               (looking_at(buf, &i, "\n<12> ") ||\r
+                looking_at(buf, &i, "<12> "))) {\r
+               loggedOn = TRUE;\r
+               if (oldi > next_out) {\r
+                   SendToPlayer(&buf[next_out], oldi - next_out);\r
+               }\r
+               next_out = i;\r
+               started = STARTED_BOARD;\r
+               parse_pos = 0;\r
+               continue;\r
+           }\r
+\r
+           if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||\r
+               looking_at(buf, &i, "<b1> ")) {\r
+               if (oldi > next_out) {\r
+                   SendToPlayer(&buf[next_out], oldi - next_out);\r
+               }\r
+               next_out = i;\r
+               started = STARTED_HOLDINGS;\r
+               parse_pos = 0;\r
+               continue;\r
+           }\r
+\r
+           if (looking_at(buf, &i, "* *vs. * *--- *")) {\r
+               loggedOn = TRUE;\r
+               /* Header for a move list -- first line */\r
+\r
+               switch (ics_getting_history) {\r
+                 case H_FALSE:\r
+                   switch (gameMode) {\r
+                     case IcsIdle:\r
+                     case BeginningOfGame:\r
+                       /* User typed "moves" or "oldmoves" while we\r
+                          were idle.  Pretend we asked for these\r
+                          moves and soak them up so user can step\r
+                          through them and/or save them.\r
+                          */\r
+                       Reset(FALSE, TRUE);\r
+                       gameMode = IcsObserving;\r
+                       ModeHighlight();\r
+                       ics_gamenum = -1;\r
+                       ics_getting_history = H_GOT_UNREQ_HEADER;\r
+                       break;\r
+                     case EditGame: /*?*/\r
+                     case EditPosition: /*?*/\r
+                       /* Should above feature work in these modes too? */\r
+                       /* For now it doesn't */\r
+                       ics_getting_history = H_GOT_UNWANTED_HEADER;\r
+                       break;\r
+                     default:\r
+                       ics_getting_history = H_GOT_UNWANTED_HEADER;\r
+                       break;\r
+                   }\r
+                   break;\r
+                 case H_REQUESTED:\r
+                   /* Is this the right one? */\r
+                   if (gameInfo.white && gameInfo.black &&\r
+                       strcmp(gameInfo.white, star_match[0]) == 0 &&\r
+                       strcmp(gameInfo.black, star_match[2]) == 0) {\r
+                       /* All is well */\r
+                       ics_getting_history = H_GOT_REQ_HEADER;\r
+                   }\r
+                   break;\r
+                 case H_GOT_REQ_HEADER:\r
+                 case H_GOT_UNREQ_HEADER:\r
+                 case H_GOT_UNWANTED_HEADER:\r
+                 case H_GETTING_MOVES:\r
+                   /* Should not happen */\r
+                   DisplayError(_("Error gathering move list: two headers"), 0);\r
+                   ics_getting_history = H_FALSE;\r
+                   break;\r
+               }\r
+\r
+               /* Save player ratings into gameInfo if needed */\r
+               if ((ics_getting_history == H_GOT_REQ_HEADER ||\r
+                    ics_getting_history == H_GOT_UNREQ_HEADER) &&\r
+                   (gameInfo.whiteRating == -1 ||\r
+                    gameInfo.blackRating == -1)) {\r
+\r
+                   gameInfo.whiteRating = string_to_rating(star_match[1]);\r
+                   gameInfo.blackRating = string_to_rating(star_match[3]);\r
+                   if (appData.debugMode)\r
+                     fprintf(debugFP, _("Ratings from header: W %d, B %d\n"), \r
+                             gameInfo.whiteRating, gameInfo.blackRating);\r
+               }\r
+               continue;\r
+           }\r
+\r
+           if (looking_at(buf, &i,\r
+             "* * match, initial time: * minute*, increment: * second")) {\r
+               /* Header for a move list -- second line */\r
+               /* Initial board will follow if this is a wild game */\r
+               if (gameInfo.event != NULL) free(gameInfo.event);\r
+               sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);\r
+               gameInfo.event = StrSave(str);\r
+                /* [HGM] we switched variant. Translate boards if needed. */\r
+                VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event));\r
+               continue;\r
+           }\r
+\r
+           if (looking_at(buf, &i, "Move  ")) {\r
+               /* Beginning of a move list */\r
+               switch (ics_getting_history) {\r
+                 case H_FALSE:\r
+                   /* Normally should not happen */\r
+                   /* Maybe user hit reset while we were parsing */\r
+                   break;\r
+                 case H_REQUESTED:\r
+                   /* Happens if we are ignoring a move list that is not\r
+                    * the one we just requested.  Common if the user\r
+                    * tries to observe two games without turning off\r
+                    * getMoveList */\r
+                   break;\r
+                 case H_GETTING_MOVES:\r
+                   /* Should not happen */\r
+                   DisplayError(_("Error gathering move list: nested"), 0);\r
+                   ics_getting_history = H_FALSE;\r
+                   break;\r
+                 case H_GOT_REQ_HEADER:\r
+                   ics_getting_history = H_GETTING_MOVES;\r
+                   started = STARTED_MOVES;\r
+                   parse_pos = 0;\r
+                   if (oldi > next_out) {\r
+                       SendToPlayer(&buf[next_out], oldi - next_out);\r
+                   }\r
+                   break;\r
+                 case H_GOT_UNREQ_HEADER:\r
+                   ics_getting_history = H_GETTING_MOVES;\r
+                   started = STARTED_MOVES_NOHIDE;\r
+                   parse_pos = 0;\r
+                   break;\r
+                 case H_GOT_UNWANTED_HEADER:\r
+                   ics_getting_history = H_FALSE;\r
+                   break;\r
+               }\r
+               continue;\r
+           }                           \r
+           \r
+           if (looking_at(buf, &i, "% ") ||\r
+               ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)\r
+                && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book\r
+               savingComment = FALSE;\r
+               switch (started) {\r
+                 case STARTED_MOVES:\r
+                 case STARTED_MOVES_NOHIDE:\r
+                   memcpy(&parse[parse_pos], &buf[oldi], i - oldi);\r
+                   parse[parse_pos + i - oldi] = NULLCHAR;\r
+                   ParseGameHistory(parse);\r
+#if ZIPPY\r
+                   if (appData.zippyPlay && first.initDone) {\r
+                       FeedMovesToProgram(&first, forwardMostMove);\r
+                       if (gameMode == IcsPlayingWhite) {\r
+                           if (WhiteOnMove(forwardMostMove)) {\r
+                               if (first.sendTime) {\r
+                                 if (first.useColors) {\r
+                                   SendToProgram("black\n", &first); \r
+                                 }\r
+                                 SendTimeRemaining(&first, TRUE);\r
+                               }\r
+#if 0\r
+                               if (first.useColors) {\r
+                                 SendToProgram("white\ngo\n", &first);\r
+                               } else {\r
+                                 SendToProgram("go\n", &first);\r
+                               }\r
+#else\r
+                               if (first.useColors) {\r
+                                 SendToProgram("white\n", &first); // [HGM] book: made sending of "go\n" book dependent\r
+                               }\r
+                               bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: probe book for initial pos\r
+#endif\r
+                               first.maybeThinking = TRUE;\r
+                           } else {\r
+                               if (first.usePlayother) {\r
+                                 if (first.sendTime) {\r
+                                   SendTimeRemaining(&first, TRUE);\r
+                                 }\r
+                                 SendToProgram("playother\n", &first);\r
+                                 firstMove = FALSE;\r
+                               } else {\r
+                                 firstMove = TRUE;\r
+                               }\r
+                           }\r
+                       } else if (gameMode == IcsPlayingBlack) {\r
+                           if (!WhiteOnMove(forwardMostMove)) {\r
+                               if (first.sendTime) {\r
+                                 if (first.useColors) {\r
+                                   SendToProgram("white\n", &first);\r
+                                 }\r
+                                 SendTimeRemaining(&first, FALSE);\r
+                               }\r
+#if 0\r
+                               if (first.useColors) {\r
+                                 SendToProgram("black\ngo\n", &first);\r
+                               } else {\r
+                                 SendToProgram("go\n", &first);\r
+                               }\r
+#else\r
+                               if (first.useColors) {\r
+                                 SendToProgram("black\n", &first);\r
+                               }\r
+                               bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE);\r
+#endif\r
+                               first.maybeThinking = TRUE;\r
+                           } else {\r
+                               if (first.usePlayother) {\r
+                                 if (first.sendTime) {\r
+                                   SendTimeRemaining(&first, FALSE);\r
+                                 }\r
+                                 SendToProgram("playother\n", &first);\r
+                                 firstMove = FALSE;\r
+                               } else {\r
+                                 firstMove = TRUE;\r
+                               }\r
+                           }\r
+                       }                       \r
+                   }\r
+#endif\r
+                   if (gameMode == IcsObserving && ics_gamenum == -1) {\r
+                       /* Moves came from oldmoves or moves command\r
+                          while we weren't doing anything else.\r
+                          */\r
+                       currentMove = forwardMostMove;\r
+                       ClearHighlights();/*!!could figure this out*/\r
+                       flipView = appData.flipView;\r
+                       DrawPosition(FALSE, boards[currentMove]);\r
+                       DisplayBothClocks();\r
+                       sprintf(str, "%s vs. %s",\r
+                               gameInfo.white, gameInfo.black);\r
+                       DisplayTitle(str);\r
+                       gameMode = IcsIdle;\r
+                   } else {\r
+                       /* Moves were history of an active game */\r
+                       if (gameInfo.resultDetails != NULL) {\r
+                           free(gameInfo.resultDetails);\r
+                           gameInfo.resultDetails = NULL;\r
+                       }\r
+                   }\r
+                   HistorySet(parseList, backwardMostMove,\r
+                              forwardMostMove, currentMove-1);\r
+                   DisplayMove(currentMove - 1);\r
+                   if (started == STARTED_MOVES) next_out = i;\r
+                   started = STARTED_NONE;\r
+                   ics_getting_history = H_FALSE;\r
+                   break;\r
+\r
+                 case STARTED_OBSERVE:\r
+                   started = STARTED_NONE;\r
+                   SendToICS(ics_prefix);\r
+                   SendToICS("refresh\n");\r
+                   break;\r
+\r
+                 default:\r
+                   break;\r
+               }\r
+               if(bookHit) { // [HGM] book: simulate book reply\r
+                   static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+                   programStats.depth = programStats.nodes = programStats.time = \r
+                   programStats.score = programStats.got_only_move = 0;\r
+                   sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
+\r
+                   strcpy(bookMove, "move ");\r
+                   strcat(bookMove, bookHit);\r
+                   HandleMachineMove(bookMove, &first);\r
+               }\r
+               continue;\r
+           }\r
+           \r
+           if ((started == STARTED_MOVES || started == STARTED_BOARD ||\r
+                started == STARTED_HOLDINGS ||\r
+                started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {\r
+               /* Accumulate characters in move list or board */\r
+               parse[parse_pos++] = buf[i];\r
+           }\r
+           \r
+           /* Start of game messages.  Mostly we detect start of game\r
+              when the first board image arrives.  On some versions\r
+              of the ICS, though, we need to do a "refresh" after starting\r
+              to observe in order to get the current board right away. */\r
+           if (looking_at(buf, &i, "Adding game * to observation list")) {\r
+               started = STARTED_OBSERVE;\r
+               continue;\r
+           }\r
+\r
+           /* Handle auto-observe */\r
+           if (appData.autoObserve &&\r
+               (gameMode == IcsIdle || gameMode == BeginningOfGame) &&\r
+               looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {\r
+               char *player;\r
+               /* Choose the player that was highlighted, if any. */\r
+               if (star_match[0][0] == '\033' ||\r
+                   star_match[1][0] != '\033') {\r
+                   player = star_match[0];\r
+               } else {\r
+                   player = star_match[2];\r
+               }\r
+               sprintf(str, "%sobserve %s\n",\r
+                       ics_prefix, StripHighlightAndTitle(player));\r
+               SendToICS(str);\r
+\r
+               /* Save ratings from notify string */\r
+               strcpy(player1Name, star_match[0]);\r
+               player1Rating = string_to_rating(star_match[1]);\r
+               strcpy(player2Name, star_match[2]);\r
+               player2Rating = string_to_rating(star_match[3]);\r
+\r
+               if (appData.debugMode)\r
+                 fprintf(debugFP, \r
+                         "Ratings from 'Game notification:' %s %d, %s %d\n",\r
+                         player1Name, player1Rating,\r
+                         player2Name, player2Rating);\r
+\r
+               continue;\r
+           }\r
+\r
+           /* Deal with automatic examine mode after a game,\r
+              and with IcsObserving -> IcsExamining transition */\r
+           if (looking_at(buf, &i, "Entering examine mode for game *") ||\r
+               looking_at(buf, &i, "has made you an examiner of game *")) {\r
+\r
+               int gamenum = atoi(star_match[0]);\r
+               if ((gameMode == IcsIdle || gameMode == IcsObserving) &&\r
+                   gamenum == ics_gamenum) {\r
+                   /* We were already playing or observing this game;\r
+                      no need to refetch history */\r
+                   gameMode = IcsExamining;\r
+                   if (pausing) {\r
+                       pauseExamForwardMostMove = forwardMostMove;\r
+                   } else if (currentMove < forwardMostMove) {\r
+                       ForwardInner(forwardMostMove);\r
+                   }\r
+               } else {\r
+                   /* I don't think this case really can happen */\r
+                   SendToICS(ics_prefix);\r
+                   SendToICS("refresh\n");\r
+               }\r
+               continue;\r
+           }    \r
+           \r
+           /* Error messages */\r
+           if (ics_user_moved) {\r
+               if (looking_at(buf, &i, "Illegal move") ||\r
+                   looking_at(buf, &i, "Not a legal move") ||\r
+                   looking_at(buf, &i, "Your king is in check") ||\r
+                   looking_at(buf, &i, "It isn't your turn") ||\r
+                   looking_at(buf, &i, "It is not your move")) {\r
+                   /* Illegal move */\r
+                   ics_user_moved = 0;\r
+                   if (forwardMostMove > backwardMostMove) {\r
+                       currentMove = --forwardMostMove;\r
+                       DisplayMove(currentMove - 1); /* before DMError */\r
+                       DisplayMoveError(_("Illegal move (rejected by ICS)"));\r
+                       DrawPosition(FALSE, boards[currentMove]);\r
+                       SwitchClocks();\r
+                       DisplayBothClocks();\r
+                   }\r
+                   continue;\r
+               }\r
+           }\r
+\r
+           if (looking_at(buf, &i, "still have time") ||\r
+               looking_at(buf, &i, "not out of time") ||\r
+               looking_at(buf, &i, "either player is out of time") ||\r
+               looking_at(buf, &i, "has timeseal; checking")) {\r
+               /* We must have called his flag a little too soon */\r
+               whiteFlag = blackFlag = FALSE;\r
+               continue;\r
+           }\r
+\r
+           if (looking_at(buf, &i, "added * seconds to") ||\r
+               looking_at(buf, &i, "seconds were added to")) {\r
+               /* Update the clocks */\r
+               SendToICS(ics_prefix);\r
+               SendToICS("refresh\n");\r
+               continue;\r
+           }\r
+\r
+           if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {\r
+               ics_clock_paused = TRUE;\r
+               StopClocks();\r
+               continue;\r
+           }\r
+\r
+           if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {\r
+               ics_clock_paused = FALSE;\r
+               StartClocks();\r
+               continue;\r
+           }\r
+\r
+           /* Grab player ratings from the Creating: message.\r
+              Note we have to check for the special case when\r
+              the ICS inserts things like [white] or [black]. */\r
+           if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||\r
+               looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {\r
+               /* star_matches:\r
+                  0    player 1 name (not necessarily white)\r
+                  1    player 1 rating\r
+                  2    empty, white, or black (IGNORED)\r
+                  3    player 2 name (not necessarily black)\r
+                  4    player 2 rating\r
+                  \r
+                  The names/ratings are sorted out when the game\r
+                  actually starts (below).\r
+               */\r
+               strcpy(player1Name, StripHighlightAndTitle(star_match[0]));\r
+               player1Rating = string_to_rating(star_match[1]);\r
+               strcpy(player2Name, StripHighlightAndTitle(star_match[3]));\r
+               player2Rating = string_to_rating(star_match[4]);\r
+\r
+               if (appData.debugMode)\r
+                 fprintf(debugFP, \r
+                         "Ratings from 'Creating:' %s %d, %s %d\n",\r
+                         player1Name, player1Rating,\r
+                         player2Name, player2Rating);\r
+\r
+               continue;\r
+           }\r
+           \r
+           /* Improved generic start/end-of-game messages */\r
+           if ((tkind=0, looking_at(buf, &i, "{Game * (* vs. *) *}*")) ||\r
+               (tkind=1, looking_at(buf, &i, "{Game * (*(*) vs. *(*)) *}*"))){\r
+               /* If tkind == 0: */\r
+               /* star_match[0] is the game number */\r
+               /*           [1] is the white player's name */\r
+               /*           [2] is the black player's name */\r
+               /* For end-of-game: */\r
+               /*           [3] is the reason for the game end */\r
+               /*           [4] is a PGN end game-token, preceded by " " */\r
+               /* For start-of-game: */\r
+               /*           [3] begins with "Creating" or "Continuing" */\r
+               /*           [4] is " *" or empty (don't care). */\r
+               int gamenum = atoi(star_match[0]);\r
+               char *whitename, *blackname, *why, *endtoken;\r
+               ChessMove endtype = (ChessMove) 0;\r
+\r
+               if (tkind == 0) {\r
+                 whitename = star_match[1];\r
+                 blackname = star_match[2];\r
+                 why = star_match[3];\r
+                 endtoken = star_match[4];\r
+               } else {\r
+                 whitename = star_match[1];\r
+                 blackname = star_match[3];\r
+                 why = star_match[5];\r
+                 endtoken = star_match[6];\r
+               }\r
+\r
+                /* Game start messages */\r
+               if (strncmp(why, "Creating ", 9) == 0 ||\r
+                   strncmp(why, "Continuing ", 11) == 0) {\r
+                   gs_gamenum = gamenum;\r
+                   strcpy(gs_kind, strchr(why, ' ') + 1);\r
+#if ZIPPY\r
+                   if (appData.zippyPlay) {\r
+                       ZippyGameStart(whitename, blackname);\r
+                   }\r
+#endif /*ZIPPY*/\r
+                   continue;\r
+               }\r
+\r
+               /* Game end messages */\r
+               if (gameMode == IcsIdle || gameMode == BeginningOfGame ||\r
+                   ics_gamenum != gamenum) {\r
+                   continue;\r
+               }\r
+               while (endtoken[0] == ' ') endtoken++;\r
+               switch (endtoken[0]) {\r
+                 case '*':\r
+                 default:\r
+                   endtype = GameUnfinished;\r
+                   break;\r
+                 case '0':\r
+                   endtype = BlackWins;\r
+                   break;\r
+                 case '1':\r
+                   if (endtoken[1] == '/')\r
+                     endtype = GameIsDrawn;\r
+                   else\r
+                     endtype = WhiteWins;\r
+                   break;\r
+               }\r
+               GameEnds(endtype, why, GE_ICS);\r
+#if ZIPPY\r
+               if (appData.zippyPlay && first.initDone) {\r
+                   ZippyGameEnd(endtype, why);\r
+                   if (first.pr == NULL) {\r
+                     /* Start the next process early so that we'll\r
+                        be ready for the next challenge */\r
+                     StartChessProgram(&first);\r
+                   }\r
+                   /* Send "new" early, in case this command takes\r
+                      a long time to finish, so that we'll be ready\r
+                      for the next challenge. */\r
+                   gameInfo.variant = VariantNormal; // [HGM] variantswitch: suppress sending of 'variant'\r
+                   Reset(TRUE, TRUE);\r
+               }\r
+#endif /*ZIPPY*/\r
+               continue;\r
+           }\r
+\r
+           if (looking_at(buf, &i, "Removing game * from observation") ||\r
+               looking_at(buf, &i, "no longer observing game *") ||\r
+               looking_at(buf, &i, "Game * (*) has no examiners")) {\r
+               if (gameMode == IcsObserving &&\r
+                   atoi(star_match[0]) == ics_gamenum)\r
+                 {\r
+                      /* icsEngineAnalyze */\r
+                      if (appData.icsEngineAnalyze) {\r
+                            ExitAnalyzeMode();\r
+                            ModeHighlight();\r
+                      }\r
+                     StopClocks();\r
+                     gameMode = IcsIdle;\r
+                     ics_gamenum = -1;\r
+                     ics_user_moved = FALSE;\r
+                 }\r
+               continue;\r
+           }\r
+\r
+           if (looking_at(buf, &i, "no longer examining game *")) {\r
+               if (gameMode == IcsExamining &&\r
+                   atoi(star_match[0]) == ics_gamenum)\r
+                 {\r
+                     gameMode = IcsIdle;\r
+                     ics_gamenum = -1;\r
+                     ics_user_moved = FALSE;\r
+                 }\r
+               continue;\r
+           }\r
+\r
+           /* Advance leftover_start past any newlines we find,\r
+              so only partial lines can get reparsed */\r
+           if (looking_at(buf, &i, "\n")) {\r
+               prevColor = curColor;\r
+               if (curColor != ColorNormal) {\r
+                   if (oldi > next_out) {\r
+                       SendToPlayer(&buf[next_out], oldi - next_out);\r
+                       next_out = oldi;\r
+                   }\r
+                   Colorize(ColorNormal, FALSE);\r
+                   curColor = ColorNormal;\r
+               }\r
+               if (started == STARTED_BOARD) {\r
+                   started = STARTED_NONE;\r
+                   parse[parse_pos] = NULLCHAR;\r
+                   ParseBoard12(parse);\r
+                   ics_user_moved = 0;\r
+\r
+                   /* Send premove here */\r
+                   if (appData.premove) {\r
+                     char str[MSG_SIZ];\r
+                     if (currentMove == 0 &&\r
+                         gameMode == IcsPlayingWhite &&\r
+                         appData.premoveWhite) {\r
+                       sprintf(str, "%s%s\n", ics_prefix,\r
+                               appData.premoveWhiteText);\r
+                       if (appData.debugMode)\r
+                         fprintf(debugFP, "Sending premove:\n");\r
+                       SendToICS(str);\r
+                     } else if (currentMove == 1 &&\r
+                                gameMode == IcsPlayingBlack &&\r
+                                appData.premoveBlack) {\r
+                       sprintf(str, "%s%s\n", ics_prefix,\r
+                               appData.premoveBlackText);\r
+                       if (appData.debugMode)\r
+                         fprintf(debugFP, "Sending premove:\n");\r
+                       SendToICS(str);\r
+                     } else if (gotPremove) {\r
+                       gotPremove = 0;\r
+                       ClearPremoveHighlights();\r
+                       if (appData.debugMode)\r
+                         fprintf(debugFP, "Sending premove:\n");\r
+                          UserMoveEvent(premoveFromX, premoveFromY, \r
+                                       premoveToX, premoveToY, \r
+                                        premovePromoChar);\r
+                     }\r
+                   }\r
+\r
+                   /* Usually suppress following prompt */\r
+                   if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {\r
+                       if (looking_at(buf, &i, "*% ")) {\r
+                           savingComment = FALSE;\r
+                       }\r
+                   }\r
+                   next_out = i;\r
+               } else if (started == STARTED_HOLDINGS) {\r
+                   int gamenum;\r
+                   char new_piece[MSG_SIZ];\r
+                   started = STARTED_NONE;\r
+                   parse[parse_pos] = NULLCHAR;\r
+                   if (appData.debugMode)\r
+                      fprintf(debugFP, "Parsing holdings: %s, currentMove = %d\n",\r
+                                                        parse, currentMove);\r
+                   if (sscanf(parse, " game %d", &gamenum) == 1 &&\r
+                       gamenum == ics_gamenum) {\r
+                       if (gameInfo.variant == VariantNormal) {\r
+                          /* [HGM] We seem to switch variant during a game!\r
+                           * Presumably no holdings were displayed, so we have\r
+                           * to move the position two files to the right to\r
+                           * create room for them!\r
+                           */\r
+                          VariantSwitch(boards[currentMove], VariantCrazyhouse); /* temp guess */\r
+                         /* Get a move list just to see the header, which\r
+                            will tell us whether this is really bug or zh */\r
+                         if (ics_getting_history == H_FALSE) {\r
+                           ics_getting_history = H_REQUESTED;\r
+                           sprintf(str, "%smoves %d\n", ics_prefix, gamenum);\r
+                           SendToICS(str);\r
+                         }\r
+                       }\r
+                       new_piece[0] = NULLCHAR;\r
+                       sscanf(parse, "game %d white [%s black [%s <- %s",\r
+                              &gamenum, white_holding, black_holding,\r
+                              new_piece);\r
+                        white_holding[strlen(white_holding)-1] = NULLCHAR;\r
+                        black_holding[strlen(black_holding)-1] = NULLCHAR;\r
+                        /* [HGM] copy holdings to board holdings area */\r
+                        CopyHoldings(boards[currentMove], white_holding, WhitePawn);\r
+                        CopyHoldings(boards[currentMove], black_holding, BlackPawn);\r
+#if ZIPPY\r
+                       if (appData.zippyPlay && first.initDone) {\r
+                           ZippyHoldings(white_holding, black_holding,\r
+                                         new_piece);\r
+                       }\r
+#endif /*ZIPPY*/\r
+                       if (tinyLayout || smallLayout) {\r
+                           char wh[16], bh[16];\r
+                           PackHolding(wh, white_holding);\r
+                           PackHolding(bh, black_holding);\r
+                           sprintf(str, "[%s-%s] %s-%s", wh, bh,\r
+                                   gameInfo.white, gameInfo.black);\r
+                       } else {\r
+                           sprintf(str, "%s [%s] vs. %s [%s]",\r
+                                   gameInfo.white, white_holding,\r
+                                   gameInfo.black, black_holding);\r
+                       }\r
+\r
+                        DrawPosition(FALSE, boards[currentMove]);\r
+                       DisplayTitle(str);\r
+                   }\r
+                   /* Suppress following prompt */\r
+                   if (looking_at(buf, &i, "*% ")) {\r
+                       savingComment = FALSE;\r
+                   }\r
+                   next_out = i;\r
+               }\r
+               continue;\r
+           }\r
+\r
+           i++;                /* skip unparsed character and loop back */\r
+       }\r
+       \r
+       if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window\r
+           started != STARTED_HOLDINGS && i > next_out) {\r
+           SendToPlayer(&buf[next_out], i - next_out);\r
+           next_out = i;\r
+       }\r
+       suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above\r
+       \r
+       leftover_len = buf_len - leftover_start;\r
+       /* if buffer ends with something we couldn't parse,\r
+          reparse it after appending the next read */\r
+       \r
+    } else if (count == 0) {\r
+       RemoveInputSource(isr);\r
+        DisplayFatalError(_("Connection closed by ICS"), 0, 0);\r
+    } else {\r
+       DisplayFatalError(_("Error reading from ICS"), error, 1);\r
+    }\r
+}\r
+\r
+\r
+/* Board style 12 looks like this:\r
+   \r
+   <12> r-b---k- pp----pp ---bP--- ---p---- q------- ------P- P--Q--BP -----R-K W -1 0 0 0 0 0 0 paf MaxII 0 2 12 21 25 234 174 24 Q/d7-a4 (0:06) Qxa4 0 0\r
+   \r
+ * The "<12> " is stripped before it gets to this routine.  The two\r
+ * trailing 0's (flip state and clock ticking) are later addition, and\r
+ * some chess servers may not have them, or may have only the first.\r
+ * Additional trailing fields may be added in the future.  \r
+ */\r
+\r
+#define PATTERN "%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"\r
+\r
+#define RELATION_OBSERVING_PLAYED    0\r
+#define RELATION_OBSERVING_STATIC   -2   /* examined, oldmoves, or smoves */\r
+#define RELATION_PLAYING_MYMOVE      1\r
+#define RELATION_PLAYING_NOTMYMOVE  -1\r
+#define RELATION_EXAMINING           2\r
+#define RELATION_ISOLATED_BOARD     -3\r
+#define RELATION_STARTING_POSITION  -4   /* FICS only */\r
+\r
+void\r
+ParseBoard12(string)\r
+     char *string;\r
+{ \r
+    GameMode newGameMode;\r
+    int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;\r
+    int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;\r
+    int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;\r
+    char to_play, board_chars[200];\r
+    char move_str[500], str[500], elapsed_time[500];\r
+    char black[32], white[32];\r
+    Board board;\r
+    int prevMove = currentMove;\r
+    int ticking = 2;\r
+    ChessMove moveType;\r
+    int fromX, fromY, toX, toY;\r
+    char promoChar;\r
+    int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */\r
+    char *bookHit = NULL; // [HGM] book\r
+\r
+    fromX = fromY = toX = toY = -1;\r
+    \r
+    newGame = FALSE;\r
+\r
+    if (appData.debugMode)\r
+      fprintf(debugFP, _("Parsing board: %s\n"), string);\r
+\r
+    move_str[0] = NULLCHAR;\r
+    elapsed_time[0] = NULLCHAR;\r
+    {   /* [HGM] figure out how many ranks and files the board has, for ICS extension used by Capablanca server */\r
+        int  i = 0, j;\r
+        while(i < 199 && (string[i] != ' ' || string[i+2] != ' ')) {\r
+           if(string[i] == ' ') { ranks++; files = 0; }\r
+            else files++;\r
+           i++;\r
+       }\r
+       for(j = 0; j <i; j++) board_chars[j] = string[j];\r
+        board_chars[i] = '\0';\r
+       string += i + 1;\r
+    }\r
+    n = sscanf(string, PATTERN, &to_play, &double_push,\r
+              &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,\r
+              &gamenum, white, black, &relation, &basetime, &increment,\r
+              &white_stren, &black_stren, &white_time, &black_time,\r
+              &moveNum, str, elapsed_time, move_str, &ics_flip,\r
+              &ticking);\r
+\r
+    if (n < 21) {\r
+       sprintf(str, _("Failed to parse board string:\n\"%s\""), string);\r
+       DisplayError(str, 0);\r
+       return;\r
+    }\r
+\r
+    /* Convert the move number to internal form */\r
+    moveNum = (moveNum - 1) * 2;\r
+    if (to_play == 'B') moveNum++;\r
+    if (moveNum >= MAX_MOVES) {\r
+      DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),\r
+                       0, 1);\r
+      return;\r
+    }\r
+    \r
+    switch (relation) {\r
+      case RELATION_OBSERVING_PLAYED:\r
+      case RELATION_OBSERVING_STATIC:\r
+       if (gamenum == -1) {\r
+           /* Old ICC buglet */\r
+           relation = RELATION_OBSERVING_STATIC;\r
+       }\r
+       newGameMode = IcsObserving;\r
+       break;\r
+      case RELATION_PLAYING_MYMOVE:\r
+      case RELATION_PLAYING_NOTMYMOVE:\r
+       newGameMode =\r
+         ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?\r
+           IcsPlayingWhite : IcsPlayingBlack;\r
+       break;\r
+      case RELATION_EXAMINING:\r
+       newGameMode = IcsExamining;\r
+       break;\r
+      case RELATION_ISOLATED_BOARD:\r
+      default:\r
+       /* Just display this board.  If user was doing something else,\r
+          we will forget about it until the next board comes. */ \r
+       newGameMode = IcsIdle;\r
+       break;\r
+      case RELATION_STARTING_POSITION:\r
+       newGameMode = gameMode;\r
+       break;\r
+    }\r
+    \r
+    /* Modify behavior for initial board display on move listing\r
+       of wild games.\r
+       */\r
+    switch (ics_getting_history) {\r
+      case H_FALSE:\r
+      case H_REQUESTED:\r
+       break;\r
+      case H_GOT_REQ_HEADER:\r
+      case H_GOT_UNREQ_HEADER:\r
+       /* This is the initial position of the current game */\r
+       gamenum = ics_gamenum;\r
+       moveNum = 0;            /* old ICS bug workaround */\r
+       if (to_play == 'B') {\r
+         startedFromSetupPosition = TRUE;\r
+         blackPlaysFirst = TRUE;\r
+         moveNum = 1;\r
+         if (forwardMostMove == 0) forwardMostMove = 1;\r
+         if (backwardMostMove == 0) backwardMostMove = 1;\r
+         if (currentMove == 0) currentMove = 1;\r
+       }\r
+       newGameMode = gameMode;\r
+       relation = RELATION_STARTING_POSITION; /* ICC needs this */\r
+       break;\r
+      case H_GOT_UNWANTED_HEADER:\r
+       /* This is an initial board that we don't want */\r
+       return;\r
+      case H_GETTING_MOVES:\r
+       /* Should not happen */\r
+       DisplayError(_("Error gathering move list: extra board"), 0);\r
+       ics_getting_history = H_FALSE;\r
+       return;\r
+    }\r
+    \r
+    /* Take action if this is the first board of a new game, or of a\r
+       different game than is currently being displayed.  */\r
+    if (gamenum != ics_gamenum || newGameMode != gameMode ||\r
+       relation == RELATION_ISOLATED_BOARD) {\r
+       \r
+       /* Forget the old game and get the history (if any) of the new one */\r
+       if (gameMode != BeginningOfGame) {\r
+         Reset(FALSE, TRUE);\r
+       }\r
+       newGame = TRUE;\r
+       if (appData.autoRaiseBoard) BoardToTop();\r
+       prevMove = -3;\r
+       if (gamenum == -1) {\r
+           newGameMode = IcsIdle;\r
+       } else if (moveNum > 0 && newGameMode != IcsIdle &&\r
+                  appData.getMoveList) {\r
+           /* Need to get game history */\r
+           ics_getting_history = H_REQUESTED;\r
+           sprintf(str, "%smoves %d\n", ics_prefix, gamenum);\r
+           SendToICS(str);\r
+       }\r
+       \r
+       /* Initially flip the board to have black on the bottom if playing\r
+          black or if the ICS flip flag is set, but let the user change\r
+          it with the Flip View button. */\r
+       flipView = appData.autoFlipView ? \r
+         (newGameMode == IcsPlayingBlack) || ics_flip :\r
+         appData.flipView;\r
+       \r
+       /* Done with values from previous mode; copy in new ones */\r
+       gameMode = newGameMode;\r
+       ModeHighlight();\r
+       ics_gamenum = gamenum;\r
+       if (gamenum == gs_gamenum) {\r
+           int klen = strlen(gs_kind);\r
+           if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;\r
+           sprintf(str, "ICS %s", gs_kind);\r
+           gameInfo.event = StrSave(str);\r
+       } else {\r
+           gameInfo.event = StrSave("ICS game");\r
+       }\r
+       gameInfo.site = StrSave(appData.icsHost);\r
+       gameInfo.date = PGNDate();\r
+       gameInfo.round = StrSave("-");\r
+       gameInfo.white = StrSave(white);\r
+       gameInfo.black = StrSave(black);\r
+       timeControl = basetime * 60 * 1000;\r
+        timeControl_2 = 0;\r
+       timeIncrement = increment * 1000;\r
+       movesPerSession = 0;\r
+       gameInfo.timeControl = TimeControlTagValue();\r
+        VariantSwitch(board, StringToVariant(gameInfo.event) );\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event);\r
+    fprintf(debugFP, "recognized as %s\n", VariantName(gameInfo.variant));\r
+    setbuf(debugFP, NULL);\r
+  }\r
+\r
+        gameInfo.outOfBook = NULL;\r
+       \r
+       /* Do we have the ratings? */\r
+       if (strcmp(player1Name, white) == 0 &&\r
+           strcmp(player2Name, black) == 0) {\r
+           if (appData.debugMode)\r
+             fprintf(debugFP, "Remembered ratings: W %d, B %d\n",\r
+                     player1Rating, player2Rating);\r
+           gameInfo.whiteRating = player1Rating;\r
+           gameInfo.blackRating = player2Rating;\r
+       } else if (strcmp(player2Name, white) == 0 &&\r
+                  strcmp(player1Name, black) == 0) {\r
+           if (appData.debugMode)\r
+             fprintf(debugFP, "Remembered ratings: W %d, B %d\n",\r
+                     player2Rating, player1Rating);\r
+           gameInfo.whiteRating = player2Rating;\r
+           gameInfo.blackRating = player1Rating;\r
+       }\r
+       player1Name[0] = player2Name[0] = NULLCHAR;\r
+\r
+       /* Silence shouts if requested */\r
+       if (appData.quietPlay &&\r
+           (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)) {\r
+           SendToICS(ics_prefix);\r
+           SendToICS("set shout 0\n");\r
+       }\r
+    }\r
+    \r
+    /* Deal with midgame name changes */\r
+    if (!newGame) {\r
+       if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {\r
+           if (gameInfo.white) free(gameInfo.white);\r
+           gameInfo.white = StrSave(white);\r
+       }\r
+       if (!gameInfo.black || strcmp(gameInfo.black, black) != 0) {\r
+           if (gameInfo.black) free(gameInfo.black);\r
+           gameInfo.black = StrSave(black);\r
+       }\r
+    }\r
+    \r
+    /* Throw away game result if anything actually changes in examine mode */\r
+    if (gameMode == IcsExamining && !newGame) {\r
+       gameInfo.result = GameUnfinished;\r
+       if (gameInfo.resultDetails != NULL) {\r
+           free(gameInfo.resultDetails);\r
+           gameInfo.resultDetails = NULL;\r
+       }\r
+    }\r
+    \r
+    /* In pausing && IcsExamining mode, we ignore boards coming\r
+       in if they are in a different variation than we are. */\r
+    if (pauseExamInvalid) return;\r
+    if (pausing && gameMode == IcsExamining) {\r
+       if (moveNum <= pauseExamForwardMostMove) {\r
+           pauseExamInvalid = TRUE;\r
+           forwardMostMove = pauseExamForwardMostMove;\r
+           return;\r
+       }\r
+    }\r
+    \r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "load %dx%d board\n", files, ranks);\r
+  }\r
+    /* Parse the board */\r
+    for (k = 0; k < ranks; k++) {\r
+      for (j = 0; j < files; j++)\r
+        board[k][j+gameInfo.holdingsWidth] = CharToPiece(board_chars[(ranks-1-k)*(files+1) + j]);\r
+      if(gameInfo.holdingsWidth > 1) {\r
+           board[k][0] = board[k][BOARD_WIDTH-1] = EmptySquare;\r
+           board[k][1] = board[k][BOARD_WIDTH-2] = (ChessSquare) 0;;\r
+      }\r
+    }\r
+    CopyBoard(boards[moveNum], board);\r
+    if (moveNum == 0) {\r
+       startedFromSetupPosition =\r
+         !CompareBoards(board, initialPosition);\r
+        if(startedFromSetupPosition)\r
+            initialRulePlies = irrev_count; /* [HGM] 50-move counter offset */\r
+    }\r
+\r
+    /* [HGM] Set castling rights. Take the outermost Rooks,\r
+       to make it also work for FRC opening positions. Note that board12\r
+       is really defective for later FRC positions, as it has no way to\r
+       indicate which Rook can castle if they are on the same side of King.\r
+       For the initial position we grant rights to the outermost Rooks,\r
+       and remember thos rights, and we then copy them on positions\r
+       later in an FRC game. This means WB might not recognize castlings with\r
+       Rooks that have moved back to their original position as illegal,\r
+       but in ICS mode that is not its job anyway.\r
+    */\r
+    if(moveNum == 0 || gameInfo.variant != VariantFischeRandom)\r
+    { int i, j; ChessSquare wKing = WhiteKing, bKing = BlackKing;\r
+\r
+        for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)\r
+            if(board[0][i] == WhiteRook) j = i;\r
+        initialRights[0] = castlingRights[moveNum][0] = (castle_ws == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+        for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)\r
+            if(board[0][i] == WhiteRook) j = i;\r
+        initialRights[1] = castlingRights[moveNum][1] = (castle_wl == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+        for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)\r
+            if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;\r
+        initialRights[3] = castlingRights[moveNum][3] = (castle_bs == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+        for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)\r
+            if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;\r
+        initialRights[4] = castlingRights[moveNum][4] = (castle_bl == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+\r
+       if(gameInfo.variant == VariantKnightmate) { wKing = WhiteUnicorn; bKing = BlackUnicorn; }\r
+        for(k=BOARD_LEFT; k<BOARD_RGHT; k++)\r
+            if(board[0][k] == wKing) initialRights[2] = castlingRights[moveNum][2] = k;\r
+        for(k=BOARD_LEFT; k<BOARD_RGHT; k++)\r
+            if(board[BOARD_HEIGHT-1][k] == bKing)\r
+                initialRights[5] = castlingRights[moveNum][5] = k;\r
+    } else { int r;\r
+        r = castlingRights[moveNum][0] = initialRights[0];\r
+        if(board[0][r] != WhiteRook) castlingRights[moveNum][0] = -1;\r
+        r = castlingRights[moveNum][1] = initialRights[1];\r
+        if(board[0][r] != WhiteRook) castlingRights[moveNum][1] = -1;\r
+        r = castlingRights[moveNum][3] = initialRights[3];\r
+        if(board[BOARD_HEIGHT-1][r] != BlackRook) castlingRights[moveNum][3] = -1;\r
+        r = castlingRights[moveNum][4] = initialRights[4];\r
+        if(board[BOARD_HEIGHT-1][r] != BlackRook) castlingRights[moveNum][4] = -1;\r
+        /* wildcastle kludge: always assume King has rights */\r
+        r = castlingRights[moveNum][2] = initialRights[2];\r
+        r = castlingRights[moveNum][5] = initialRights[5];\r
+    }\r
+    /* [HGM] e.p. rights. Assume that ICS sends file number here? */\r
+    epStatus[moveNum] = double_push == -1 ? EP_NONE : double_push + BOARD_LEFT;\r
+\r
+    \r
+    if (ics_getting_history == H_GOT_REQ_HEADER ||\r
+       ics_getting_history == H_GOT_UNREQ_HEADER) {\r
+       /* This was an initial position from a move list, not\r
+          the current position */\r
+       return;\r
+    }\r
+    \r
+    /* Update currentMove and known move number limits */\r
+    newMove = newGame || moveNum > forwardMostMove;\r
+\r
+    /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */\r
+    if (!newGame && appData.icsEngineAnalyze && moveNum < forwardMostMove) {\r
+        takeback = forwardMostMove - moveNum;\r
+        for (i = 0; i < takeback; i++) {\r
+             if (appData.debugMode) fprintf(debugFP, "take back move\n");\r
+             SendToProgram("undo\n", &first);\r
+        }\r
+    }\r
+\r
+    if (newGame) {\r
+       forwardMostMove = backwardMostMove = currentMove = moveNum;\r
+       if (gameMode == IcsExamining && moveNum == 0) {\r
+         /* Workaround for ICS limitation: we are not told the wild\r
+            type when starting to examine a game.  But if we ask for\r
+            the move list, the move list header will tell us */\r
+           ics_getting_history = H_REQUESTED;\r
+           sprintf(str, "%smoves %d\n", ics_prefix, gamenum);\r
+           SendToICS(str);\r
+       }\r
+    } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove\r
+              || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {\r
+       forwardMostMove = moveNum;\r
+       if (!pausing || currentMove > forwardMostMove)\r
+         currentMove = forwardMostMove;\r
+    } else {\r
+       /* New part of history that is not contiguous with old part */ \r
+       if (pausing && gameMode == IcsExamining) {\r
+           pauseExamInvalid = TRUE;\r
+           forwardMostMove = pauseExamForwardMostMove;\r
+           return;\r
+       }\r
+       forwardMostMove = backwardMostMove = currentMove = moveNum;\r
+       if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {\r
+           ics_getting_history = H_REQUESTED;\r
+           sprintf(str, "%smoves %d\n", ics_prefix, gamenum);\r
+           SendToICS(str);\r
+       }\r
+    }\r
+    \r
+    /* Update the clocks */\r
+    if (strchr(elapsed_time, '.')) {\r
+      /* Time is in ms */\r
+      timeRemaining[0][moveNum] = whiteTimeRemaining = white_time;\r
+      timeRemaining[1][moveNum] = blackTimeRemaining = black_time;\r
+    } else {\r
+      /* Time is in seconds */\r
+      timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;\r
+      timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;\r
+    }\r
+      \r
+\r
+#if ZIPPY\r
+    if (appData.zippyPlay && newGame &&\r
+       gameMode != IcsObserving && gameMode != IcsIdle &&\r
+       gameMode != IcsExamining)\r
+      ZippyFirstBoard(moveNum, basetime, increment);\r
+#endif\r
+    \r
+    /* Put the move on the move list, first converting\r
+       to canonical algebraic form. */\r
+    if (moveNum > 0) {\r
+  if (appData.debugMode) {\r
+    if (appData.debugMode) { int f = forwardMostMove;\r
+        fprintf(debugFP, "parseboard %d, castling = %d %d %d %d %d %d\n", f,\r
+                castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);\r
+    }\r
+    fprintf(debugFP, "accepted move %s from ICS, parse it.\n", move_str);\r
+    fprintf(debugFP, "moveNum = %d\n", moveNum);\r
+    fprintf(debugFP, "board = %d-%d x %d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT);\r
+    setbuf(debugFP, NULL);\r
+  }\r
+       if (moveNum <= backwardMostMove) {\r
+           /* We don't know what the board looked like before\r
+              this move.  Punt. */\r
+           strcpy(parseList[moveNum - 1], move_str);\r
+           strcat(parseList[moveNum - 1], " ");\r
+           strcat(parseList[moveNum - 1], elapsed_time);\r
+           moveList[moveNum - 1][0] = NULLCHAR;\r
+       } else if (strcmp(move_str, "none") == 0) {\r
+           // [HGM] long SAN: swapped order; test for 'none' before parsing move\r
+           /* Again, we don't know what the board looked like;\r
+              this is really the start of the game. */\r
+           parseList[moveNum - 1][0] = NULLCHAR;\r
+           moveList[moveNum - 1][0] = NULLCHAR;\r
+           backwardMostMove = moveNum;\r
+           startedFromSetupPosition = TRUE;\r
+           fromX = fromY = toX = toY = -1;\r
+       } else {\r
+         // [HGM] long SAN: if legality-testing is off, disambiguation might not work or give wrong move. \r
+         //                 So we parse the long-algebraic move string in stead of the SAN move\r
+         int valid; char buf[MSG_SIZ], *prom;\r
+\r
+         // str looks something like "Q/a1-a2"; kill the slash\r
+         if(str[1] == '/') \r
+               sprintf(buf, "%c%s", str[0], str+2);\r
+         else  strcpy(buf, str); // might be castling\r
+         if((prom = strstr(move_str, "=")) && !strstr(buf, "=")) \r
+               strcat(buf, prom); // long move lacks promo specification!\r
+         if(!appData.testLegality) {\r
+               if(appData.debugMode) \r
+                       fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf);\r
+               strcpy(move_str, buf);\r
+          }\r
+         valid = ParseOneMove(move_str, moveNum - 1, &moveType,\r
+                               &fromX, &fromY, &toX, &toY, &promoChar)\r
+              || ParseOneMove(buf, moveNum - 1, &moveType,\r
+                               &fromX, &fromY, &toX, &toY, &promoChar);\r
+         // end of long SAN patch\r
+         if (valid) {\r
+           (void) CoordsToAlgebraic(boards[moveNum - 1],\r
+                                    PosFlags(moveNum - 1), EP_UNKNOWN,\r
+                                    fromY, fromX, toY, toX, promoChar,\r
+                                    parseList[moveNum-1]);\r
+            switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN,\r
+                             castlingRights[moveNum]) ) {\r
+             case MT_NONE:\r
+             case MT_STALEMATE:\r
+             default:\r
+               break;\r
+             case MT_CHECK:\r
+                if(gameInfo.variant != VariantShogi)\r
+                    strcat(parseList[moveNum - 1], "+");\r
+               break;\r
+             case MT_CHECKMATE:\r
+               strcat(parseList[moveNum - 1], "#");\r
+               break;\r
+           }\r
+           strcat(parseList[moveNum - 1], " ");\r
+           strcat(parseList[moveNum - 1], elapsed_time);\r
+           /* currentMoveString is set as a side-effect of ParseOneMove */\r
+           strcpy(moveList[moveNum - 1], currentMoveString);\r
+           strcat(moveList[moveNum - 1], "\n");\r
+         } else {\r
+           /* Move from ICS was illegal!?  Punt. */\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Illegal move from ICS '%s'\n", move_str);\r
+    fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
+  }\r
+#if 0\r
+           if (appData.testLegality && appData.debugMode) {\r
+               sprintf(str, "Illegal move \"%s\" from ICS", move_str);\r
+               DisplayError(str, 0);\r
+           }\r
+#endif\r
+           strcpy(parseList[moveNum - 1], move_str);\r
+           strcat(parseList[moveNum - 1], " ");\r
+           strcat(parseList[moveNum - 1], elapsed_time);\r
+           moveList[moveNum - 1][0] = NULLCHAR;\r
+           fromX = fromY = toX = toY = -1;\r
+         }\r
+       }\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Move parsed to '%s'\n", parseList[moveNum - 1]);\r
+    setbuf(debugFP, NULL);\r
+  }\r
+\r
+#if ZIPPY\r
+       /* Send move to chess program (BEFORE animating it). */\r
+       if (appData.zippyPlay && !newGame && newMove && \r
+          (!appData.getMoveList || backwardMostMove == 0) && first.initDone) {\r
+\r
+           if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||\r
+               (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {\r
+               if (moveList[moveNum - 1][0] == NULLCHAR) {\r
+                   sprintf(str, _("Couldn't parse move \"%s\" from ICS"),\r
+                           move_str);\r
+                   DisplayError(str, 0);\r
+               } else {\r
+                   if (first.sendTime) {\r
+                       SendTimeRemaining(&first, gameMode == IcsPlayingWhite);\r
+                   }\r
+                   bookHit = SendMoveToBookUser(moveNum - 1, &first, FALSE); // [HGM] book\r
+                   if (firstMove && !bookHit) {\r
+                       firstMove = FALSE;\r
+                       if (first.useColors) {\r
+                         SendToProgram(gameMode == IcsPlayingWhite ?\r
+                                       "white\ngo\n" :\r
+                                       "black\ngo\n", &first);\r
+                       } else {\r
+                         SendToProgram("go\n", &first);\r
+                       }\r
+                       first.maybeThinking = TRUE;\r
+                   }\r
+               }\r
+           } else if (gameMode == IcsObserving || gameMode == IcsExamining) {\r
+             if (moveList[moveNum - 1][0] == NULLCHAR) {\r
+               sprintf(str, _("Couldn't parse move \"%s\" from ICS"), move_str);\r
+               DisplayError(str, 0);\r
+             } else {\r
+               if(gameInfo.variant == currentlyInitializedVariant) // [HGM] refrain sending moves engine can't understand!\r
+               SendMoveToProgram(moveNum - 1, &first);\r
+             }\r
+           }\r
+       }\r
+#endif\r
+    }\r
+\r
+    if (moveNum > 0 && !gotPremove) {\r
+       /* If move comes from a remote source, animate it.  If it\r
+          isn't remote, it will have already been animated. */\r
+       if (!pausing && !ics_user_moved && prevMove == moveNum - 1) {\r
+           AnimateMove(boards[moveNum - 1], fromX, fromY, toX, toY);\r
+       }\r
+       if (!pausing && appData.highlightLastMove) {\r
+           SetHighlights(fromX, fromY, toX, toY);\r
+       }\r
+    }\r
+    \r
+    /* Start the clocks */\r
+    whiteFlag = blackFlag = FALSE;\r
+    appData.clockMode = !(basetime == 0 && increment == 0);\r
+    if (ticking == 0) {\r
+      ics_clock_paused = TRUE;\r
+      StopClocks();\r
+    } else if (ticking == 1) {\r
+      ics_clock_paused = FALSE;\r
+    }\r
+    if (gameMode == IcsIdle ||\r
+       relation == RELATION_OBSERVING_STATIC ||\r
+       relation == RELATION_EXAMINING ||\r
+       ics_clock_paused)\r
+      DisplayBothClocks();\r
+    else\r
+      StartClocks();\r
+    \r
+    /* Display opponents and material strengths */\r
+    if (gameInfo.variant != VariantBughouse &&\r
+       gameInfo.variant != VariantCrazyhouse) {\r
+       if (tinyLayout || smallLayout) {\r
+           if(gameInfo.variant == VariantNormal)\r
+               sprintf(str, "%s(%d) %s(%d) {%d %d}", \r
+                   gameInfo.white, white_stren, gameInfo.black, black_stren,\r
+                   basetime, increment);\r
+           else\r
+               sprintf(str, "%s(%d) %s(%d) {%d %d w%d}", \r
+                   gameInfo.white, white_stren, gameInfo.black, black_stren,\r
+                   basetime, increment, (int) gameInfo.variant);\r
+       } else {\r
+           if(gameInfo.variant == VariantNormal)\r
+               sprintf(str, "%s (%d) vs. %s (%d) {%d %d}", \r
+                   gameInfo.white, white_stren, gameInfo.black, black_stren,\r
+                   basetime, increment);\r
+           else\r
+               sprintf(str, "%s (%d) vs. %s (%d) {%d %d %s}", \r
+                   gameInfo.white, white_stren, gameInfo.black, black_stren,\r
+                   basetime, increment, VariantName(gameInfo.variant));\r
+       }\r
+       DisplayTitle(str);\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Display title '%s, gameInfo.variant = %d'\n", str, gameInfo.variant);\r
+  }\r
+    }\r
+\r
+   \r
+    /* Display the board */\r
+    if (!pausing) {\r
+      \r
+      if (appData.premove)\r
+         if (!gotPremove || \r
+            ((gameMode == IcsPlayingWhite) && (WhiteOnMove(currentMove))) ||\r
+            ((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))\r
+             ClearPremoveHighlights();\r
+\r
+      DrawPosition(FALSE, boards[currentMove]);\r
+      DisplayMove(moveNum - 1);\r
+      if (appData.ringBellAfterMoves && !ics_user_moved)\r
+       RingBell();\r
+    }\r
+\r
+    HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+#if ZIPPY\r
+    if(bookHit) { // [HGM] book: simulate book reply\r
+       static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.score = programStats.got_only_move = 0;\r
+       sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+    }\r
+#endif\r
+}\r
+\r
+void\r
+GetMoveListEvent()\r
+{\r
+    char buf[MSG_SIZ];\r
+    if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {\r
+       ics_getting_history = H_REQUESTED;\r
+       sprintf(buf, "%smoves %d\n", ics_prefix, ics_gamenum);\r
+       SendToICS(buf);\r
+    }\r
+}\r
+\r
+void\r
+AnalysisPeriodicEvent(force)\r
+     int force;\r
+{\r
+    if (((programStats.ok_to_send == 0 || programStats.line_is_book)\r
+        && !force) || !appData.periodicUpdates)\r
+      return;\r
+\r
+    /* Send . command to Crafty to collect stats */\r
+    SendToProgram(".\n", &first);\r
+\r
+    /* Don't send another until we get a response (this makes\r
+       us stop sending to old Crafty's which don't understand\r
+       the "." command (sending illegal cmds resets node count & time,\r
+       which looks bad)) */\r
+    programStats.ok_to_send = 0;\r
+}\r
+\r
+void\r
+SendMoveToProgram(moveNum, cps)\r
+     int moveNum;\r
+     ChessProgramState *cps;\r
+{\r
+    char buf[MSG_SIZ];\r
+\r
+    if (cps->useUsermove) {\r
+      SendToProgram("usermove ", cps);\r
+    }\r
+    if (cps->useSAN) {\r
+      char *space;\r
+      if ((space = strchr(parseList[moveNum], ' ')) != NULL) {\r
+       int len = space - parseList[moveNum];\r
+       memcpy(buf, parseList[moveNum], len);\r
+       buf[len++] = '\n';\r
+       buf[len] = NULLCHAR;\r
+      } else {\r
+       sprintf(buf, "%s\n", parseList[moveNum]);\r
+      }\r
+      SendToProgram(buf, cps);\r
+    } else {\r
+      if(cps->alphaRank) { /* [HGM] shogi: temporarily convert to shogi coordinates before sending */\r
+       AlphaRank(moveList[moveNum], 4);\r
+       SendToProgram(moveList[moveNum], cps);\r
+       AlphaRank(moveList[moveNum], 4); // and back\r
+      } else\r
+      /* Added by Tord: Send castle moves in "O-O" in FRC games if required by\r
+       * the engine. It would be nice to have a better way to identify castle \r
+       * moves here. */\r
+      if((gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom)\r
+                                                                        && cps->useOOCastle) {\r
+        int fromX = moveList[moveNum][0] - AAA; \r
+        int fromY = moveList[moveNum][1] - ONE;\r
+        int toX = moveList[moveNum][2] - AAA; \r
+        int toY = moveList[moveNum][3] - ONE;\r
+        if((boards[moveNum][fromY][fromX] == WhiteKing \r
+            && boards[moveNum][toY][toX] == WhiteRook)\r
+           || (boards[moveNum][fromY][fromX] == BlackKing \r
+               && boards[moveNum][toY][toX] == BlackRook)) {\r
+         if(toX > fromX) SendToProgram("O-O\n", cps);\r
+         else SendToProgram("O-O-O\n", cps);\r
+       }\r
+       else SendToProgram(moveList[moveNum], cps);\r
+      }\r
+      else SendToProgram(moveList[moveNum], cps);\r
+      /* End of additions by Tord */\r
+    }\r
+\r
+    /* [HGM] setting up the opening has brought engine in force mode! */\r
+    /*       Send 'go' if we are in a mode where machine should play. */\r
+    if( (moveNum == 0 && setboardSpoiledMachineBlack && cps == &first) &&\r
+        (gameMode == TwoMachinesPlay   ||\r
+#ifdef ZIPPY\r
+         gameMode == IcsPlayingBlack     || gameMode == IcsPlayingWhite ||\r
+#endif\r
+         gameMode == MachinePlaysBlack || gameMode == MachinePlaysWhite) ) {\r
+        SendToProgram("go\n", cps);\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "(extra)\n");\r
+  }\r
+    }\r
+    setboardSpoiledMachineBlack = 0;\r
+}\r
+\r
+void\r
+SendMoveToICS(moveType, fromX, fromY, toX, toY)\r
+     ChessMove moveType;\r
+     int fromX, fromY, toX, toY;\r
+{\r
+    char user_move[MSG_SIZ];\r
+\r
+    switch (moveType) {\r
+      default:\r
+       sprintf(user_move, _("say Internal error; bad moveType %d (%d,%d-%d,%d)"),\r
+               (int)moveType, fromX, fromY, toX, toY);\r
+       DisplayError(user_move + strlen("say "), 0);\r
+       break;\r
+      case WhiteKingSideCastle:\r
+      case BlackKingSideCastle:\r
+      case WhiteQueenSideCastleWild:\r
+      case BlackQueenSideCastleWild:\r
+      /* PUSH Fabien */\r
+      case WhiteHSideCastleFR:\r
+      case BlackHSideCastleFR:\r
+      /* POP Fabien */\r
+       sprintf(user_move, "o-o\n");\r
+       break;\r
+      case WhiteQueenSideCastle:\r
+      case BlackQueenSideCastle:\r
+      case WhiteKingSideCastleWild:\r
+      case BlackKingSideCastleWild:\r
+      /* PUSH Fabien */\r
+      case WhiteASideCastleFR:\r
+      case BlackASideCastleFR:\r
+      /* POP Fabien */\r
+       sprintf(user_move, "o-o-o\n");\r
+       break;\r
+      case WhitePromotionQueen:\r
+      case BlackPromotionQueen:\r
+      case WhitePromotionRook:\r
+      case BlackPromotionRook:\r
+      case WhitePromotionBishop:\r
+      case BlackPromotionBishop:\r
+      case WhitePromotionKnight:\r
+      case BlackPromotionKnight:\r
+      case WhitePromotionKing:\r
+      case BlackPromotionKing:\r
+      case WhitePromotionChancellor:\r
+      case BlackPromotionChancellor:\r
+      case WhitePromotionArchbishop:\r
+      case BlackPromotionArchbishop:\r
+        if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier)\r
+            sprintf(user_move, "%c%c%c%c=%c\n",\r
+                AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
+               PieceToChar(WhiteFerz));\r
+        else if(gameInfo.variant == VariantGreat)\r
+            sprintf(user_move, "%c%c%c%c=%c\n",\r
+                AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
+               PieceToChar(WhiteMan));\r
+        else\r
+            sprintf(user_move, "%c%c%c%c=%c\n",\r
+                AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
+               PieceToChar(PromoPiece(moveType)));\r
+       break;\r
+      case WhiteDrop:\r
+      case BlackDrop:\r
+       sprintf(user_move, "%c@%c%c\n",\r
+               ToUpper(PieceToChar((ChessSquare) fromX)),\r
+                AAA + toX, ONE + toY);\r
+       break;\r
+      case NormalMove:\r
+      case WhiteCapturesEnPassant:\r
+      case BlackCapturesEnPassant:\r
+      case IllegalMove:  /* could be a variant we don't quite understand */\r
+       sprintf(user_move, "%c%c%c%c\n",\r
+                AAA + fromX, ONE + fromY, AAA + toX, ONE + toY);\r
+       break;\r
+    }\r
+    SendToICS(user_move);\r
+}\r
+\r
+void\r
+CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)\r
+     int rf, ff, rt, ft;\r
+     char promoChar;\r
+     char move[7];\r
+{\r
+    if (rf == DROP_RANK) {\r
+       sprintf(move, "%c@%c%c\n",\r
+                ToUpper(PieceToChar((ChessSquare) ff)), AAA + ft, ONE + rt);\r
+    } else {\r
+       if (promoChar == 'x' || promoChar == NULLCHAR) {\r
+           sprintf(move, "%c%c%c%c\n",\r
+                    AAA + ff, ONE + rf, AAA + ft, ONE + rt);\r
+       } else {\r
+           sprintf(move, "%c%c%c%c%c\n",\r
+                    AAA + ff, ONE + rf, AAA + ft, ONE + rt, promoChar);\r
+       }\r
+    }\r
+}\r
+\r
+void\r
+ProcessICSInitScript(f)\r
+     FILE *f;\r
+{\r
+    char buf[MSG_SIZ];\r
+\r
+    while (fgets(buf, MSG_SIZ, f)) {\r
+       SendToICSDelayed(buf,(long)appData.msLoginDelay);\r
+    }\r
+\r
+    fclose(f);\r
+}\r
+\r
+\r
+/* [HGM] Shogi move preprocessor: swap digits for letters, vice versa */\r
+void\r
+AlphaRank(char *move, int n)\r
+{\r
+    char *p = move, c; int x, y;\r
+\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "alphaRank(%s,%d)\n", move, n);\r
+    }\r
+\r
+    if(move[1]=='*' && \r
+       move[2]>='0' && move[2]<='9' &&\r
+       move[3]>='a' && move[3]<='x'    ) {\r
+        move[1] = '@';\r
+        move[2] = BOARD_RGHT  -1 - (move[2]-'1') + AAA;\r
+        move[3] = BOARD_HEIGHT-1 - (move[3]-'a') + ONE;\r
+    } else\r
+    if(move[0]>='0' && move[0]<='9' &&\r
+       move[1]>='a' && move[1]<='x' &&\r
+       move[2]>='0' && move[2]<='9' &&\r
+       move[3]>='a' && move[3]<='x'    ) {\r
+        /* input move, Shogi -> normal */\r
+        move[0] = BOARD_RGHT  -1 - (move[0]-'1') + AAA;\r
+        move[1] = BOARD_HEIGHT-1 - (move[1]-'a') + ONE;\r
+        move[2] = BOARD_RGHT  -1 - (move[2]-'1') + AAA;\r
+        move[3] = BOARD_HEIGHT-1 - (move[3]-'a') + ONE;\r
+    } else\r
+    if(move[1]=='@' &&\r
+       move[3]>='0' && move[3]<='9' &&\r
+       move[2]>='a' && move[2]<='x'    ) {\r
+        move[1] = '*';\r
+        move[2] = BOARD_RGHT - 1 - (move[2]-AAA) + '1';\r
+        move[3] = BOARD_HEIGHT-1 - (move[3]-ONE) + 'a';\r
+    } else\r
+    if(\r
+       move[0]>='a' && move[0]<='x' &&\r
+       move[3]>='0' && move[3]<='9' &&\r
+       move[2]>='a' && move[2]<='x'    ) {\r
+         /* output move, normal -> Shogi */\r
+        move[0] = BOARD_RGHT - 1 - (move[0]-AAA) + '1';\r
+        move[1] = BOARD_HEIGHT-1 - (move[1]-ONE) + 'a';\r
+        move[2] = BOARD_RGHT - 1 - (move[2]-AAA) + '1';\r
+        move[3] = BOARD_HEIGHT-1 - (move[3]-ONE) + 'a';\r
+        if(move[4] == PieceToChar(BlackQueen)) move[4] = '+';\r
+    }\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "   out = '%s'\n", move);\r
+    }\r
+}\r
+\r
+/* Parser for moves from gnuchess, ICS, or user typein box */\r
+Boolean\r
+ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)\r
+     char *move;\r
+     int moveNum;\r
+     ChessMove *moveType;\r
+     int *fromX, *fromY, *toX, *toY;\r
+     char *promoChar;\r
+{       \r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "move to parse: %s\n", move);\r
+    }\r
+    *moveType = yylexstr(moveNum, move);\r
+\r
+    switch (*moveType) {\r
+      case WhitePromotionChancellor:\r
+      case BlackPromotionChancellor:\r
+      case WhitePromotionArchbishop:\r
+      case BlackPromotionArchbishop:\r
+      case WhitePromotionQueen:\r
+      case BlackPromotionQueen:\r
+      case WhitePromotionRook:\r
+      case BlackPromotionRook:\r
+      case WhitePromotionBishop:\r
+      case BlackPromotionBishop:\r
+      case WhitePromotionKnight:\r
+      case BlackPromotionKnight:\r
+      case WhitePromotionKing:\r
+      case BlackPromotionKing:\r
+      case NormalMove:\r
+      case WhiteCapturesEnPassant:\r
+      case BlackCapturesEnPassant:\r
+      case WhiteKingSideCastle:\r
+      case WhiteQueenSideCastle:\r
+      case BlackKingSideCastle:\r
+      case BlackQueenSideCastle:\r
+      case WhiteKingSideCastleWild:\r
+      case WhiteQueenSideCastleWild:\r
+      case BlackKingSideCastleWild:\r
+      case BlackQueenSideCastleWild:\r
+      /* Code added by Tord: */\r
+      case WhiteHSideCastleFR:\r
+      case WhiteASideCastleFR:\r
+      case BlackHSideCastleFR:\r
+      case BlackASideCastleFR:\r
+      /* End of code added by Tord */\r
+      case IllegalMove:                /* bug or odd chess variant */\r
+        *fromX = currentMoveString[0] - AAA;\r
+        *fromY = currentMoveString[1] - ONE;\r
+        *toX = currentMoveString[2] - AAA;\r
+        *toY = currentMoveString[3] - ONE;\r
+       *promoChar = currentMoveString[4];\r
+        if (*fromX < BOARD_LEFT || *fromX >= BOARD_RGHT || *fromY < 0 || *fromY >= BOARD_HEIGHT ||\r
+            *toX < BOARD_LEFT || *toX >= BOARD_RGHT || *toY < 0 || *toY >= BOARD_HEIGHT) {\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "Off-board move (%d,%d)-(%d,%d)%c, type = %d\n", *fromX, *fromY, *toX, *toY, *promoChar, *moveType);\r
+    }\r
+           *fromX = *fromY = *toX = *toY = 0;\r
+           return FALSE;\r
+       }\r
+       if (appData.testLegality) {\r
+         return (*moveType != IllegalMove);\r
+       } else {\r
+         return !(fromX == fromY && toX == toY);\r
+       }\r
+\r
+      case WhiteDrop:\r
+      case BlackDrop:\r
+       *fromX = *moveType == WhiteDrop ?\r
+         (int) CharToPiece(ToUpper(currentMoveString[0])) :\r
+         (int) CharToPiece(ToLower(currentMoveString[0]));\r
+       *fromY = DROP_RANK;\r
+        *toX = currentMoveString[2] - AAA;\r
+        *toY = currentMoveString[3] - ONE;\r
+       *promoChar = NULLCHAR;\r
+       return TRUE;\r
+\r
+      case AmbiguousMove:\r
+      case ImpossibleMove:\r
+      case (ChessMove) 0:      /* end of file */\r
+      case ElapsedTime:\r
+      case Comment:\r
+      case PGNTag:\r
+      case NAG:\r
+      case WhiteWins:\r
+      case BlackWins:\r
+      case GameIsDrawn:\r
+      default:\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "Impossible move %s, type = %d\n", currentMoveString, *moveType);\r
+    }\r
+       /* bug? */\r
+       *fromX = *fromY = *toX = *toY = 0;\r
+       *promoChar = NULLCHAR;\r
+       return FALSE;\r
+    }\r
+}\r
+\r
+/* [AS] FRC game initialization */\r
+static int FindEmptySquare( Board board, int n )\r
+{\r
+    int i = 0;\r
+\r
+    while( 1 ) {\r
+        while( board[0][i] != EmptySquare ) i++;\r
+        if( n == 0 )\r
+            break;\r
+        n--;\r
+        i++;\r
+    }\r
+\r
+    return i;\r
+}\r
+\r
+#if 0\r
+static void ShuffleFRC( Board board )\r
+{\r
+    int i;\r
+\r
+    srand( time(0) );\r
+    \r
+    for( i=0; i<8; i++ ) {\r
+        board[0][i] = EmptySquare;\r
+    }\r
+\r
+    board[0][(rand() % 4)*2  ] = WhiteBishop; /* On dark square */\r
+    board[0][(rand() % 4)*2+1] = WhiteBishop; /* On lite square */\r
+    board[0][FindEmptySquare(board, rand() % 6)] = WhiteQueen;\r
+    board[0][FindEmptySquare(board, rand() % 5)] = WhiteKnight;\r
+    board[0][FindEmptySquare(board, rand() % 4)] = WhiteKnight;\r
+    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
+    initialRights[1]  = initialRights[4]  =\r
+    castlingRights[0][1] = castlingRights[0][4] = i;\r
+    board[0][ i=FindEmptySquare(board, 0) ] = WhiteKing;\r
+    initialRights[2]  = initialRights[5]  =\r
+    castlingRights[0][2] = castlingRights[0][5] = i;\r
+    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
+    initialRights[0]  = initialRights[3]  =\r
+    castlingRights[0][0] = castlingRights[0][3] = i;\r
+\r
+    for( i=BOARD_LEFT; i<BOARD_RGHT; i++ ) {\r
+        board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
+    }\r
+}\r
+\r
+static unsigned char FRC_KnightTable[10] = {\r
+    0x00, 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x22, 0x23, 0x33\r
+};\r
+\r
+static void SetupFRC( Board board, int pos_index )\r
+{\r
+    int i;\r
+    unsigned char knights;\r
+\r
+    /* Bring the position index into a safe range (just in case...) */\r
+    if( pos_index < 0 ) pos_index = 0;\r
+\r
+    pos_index %= 960;\r
+\r
+    /* Clear the board */\r
+    for( i=0; i<8; i++ ) {\r
+        board[0][i] = EmptySquare;\r
+    }\r
+\r
+    /* Place bishops and queen */\r
+    board[0][ (pos_index % 4)*2 + 1 ] = WhiteBishop; /* On lite square */\r
+    pos_index /= 4;\r
+    \r
+    board[0][ (pos_index % 4)*2     ] = WhiteBishop; /* On dark square */\r
+    pos_index /= 4;\r
+\r
+    board[0][ FindEmptySquare(board, pos_index % 6) ] = WhiteQueen;\r
+    pos_index /= 6;\r
+\r
+    /* Place knigths */\r
+    knights = FRC_KnightTable[ pos_index ];\r
+\r
+    board[0][ FindEmptySquare(board, knights / 16) ] = WhiteKnight;\r
+    board[0][ FindEmptySquare(board, knights % 16) ] = WhiteKnight;\r
+\r
+    /* Place rooks and king */\r
+    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
+    initialRights[1]  = initialRights[4]  =\r
+    castlingRights[0][1] = castlingRights[0][4] = i;\r
+    board[0][ i=FindEmptySquare(board, 0) ] = WhiteKing;\r
+    initialRights[2]  = initialRights[5]  =\r
+    castlingRights[0][2] = castlingRights[0][5] = i;\r
+    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
+    initialRights[0]  = initialRights[3]  =\r
+    castlingRights[0][0] = castlingRights[0][3] = i;\r
+\r
+    /* Mirror piece placement for black */\r
+    for( i=BOARD_LEFT; i<BOARD_RGHT; i++ ) {\r
+        board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
+    }\r
+}\r
+#else\r
+// [HGM] shuffle: a more general way to suffle opening setups, applicable to arbitrry variants.\r
+// All positions will have equal probability, but the current method will not provide a unique\r
+// numbering scheme for arrays that contain 3 or more pieces of the same kind.\r
+#define DARK 1\r
+#define LITE 2\r
+#define ANY 3\r
+\r
+int squaresLeft[4];\r
+int piecesLeft[(int)BlackPawn];\r
+long long int seed, nrOfShuffles;\r
+\r
+void GetPositionNumber()\r
+{      // sets global variable seed\r
+       int i;\r
+\r
+       seed = appData.defaultFrcPosition;\r
+       if(seed < 0) { // randomize based on time for negative FRC position numbers\r
+               srandom(time(0)); \r
+               for(i=0; i<50; i++) seed += random();\r
+               seed = random() ^ random() >> 8 ^ random() << 8;\r
+               if(seed<0) seed = -seed;\r
+       }\r
+}\r
+\r
+int put(Board board, int pieceType, int rank, int n, int shade)\r
+// put the piece on the (n-1)-th empty squares of the given shade\r
+{\r
+       int i;\r
+\r
+       for(i=BOARD_LEFT; i<BOARD_RGHT; i++) {\r
+               if( ((i-BOARD_LEFT)&1)+1 & shade && board[rank][i] == EmptySquare && n-- == 0) {\r
+                       board[rank][i] = (ChessSquare) pieceType;\r
+                       squaresLeft[(i-BOARD_LEFT&1) + 1]--;\r
+                       squaresLeft[ANY]--;\r
+                       piecesLeft[pieceType]--; \r
+                       return i;\r
+               }\r
+       }\r
+        return -1;\r
+}\r
+\r
+\r
+void AddOnePiece(Board board, int pieceType, int rank, int shade)\r
+// calculate where the next piece goes, (any empty square), and put it there\r
+{\r
+       int i;\r
+\r
+        i = seed % squaresLeft[shade];\r
+       nrOfShuffles *= squaresLeft[shade];\r
+       seed /= squaresLeft[shade];\r
+        put(board, pieceType, rank, i, shade);\r
+}\r
+\r
+void AddTwoPieces(Board board, int pieceType, int rank)\r
+// calculate where the next 2 identical pieces go, (any empty square), and put it there\r
+{\r
+       int i, n=squaresLeft[ANY], j=n-1, k;\r
+\r
+       k = n*(n-1)/2; // nr of possibilities, not counting permutations\r
+        i = seed % k;  // pick one\r
+       nrOfShuffles *= k;\r
+       seed /= k;\r
+       while(i >= j) i -= j--;\r
+        j = n - 1 - j; i += j;\r
+        put(board, pieceType, rank, j, ANY);\r
+        put(board, pieceType, rank, i, ANY);\r
+}\r
+\r
+void SetUpShuffle(Board board, int number)\r
+{\r
+       int i, p, first=1;\r
+\r
+       GetPositionNumber(); nrOfShuffles = 1;\r
+\r
+       squaresLeft[DARK] = (BOARD_RGHT - BOARD_LEFT + 1)/2;\r
+       squaresLeft[ANY]  = BOARD_RGHT - BOARD_LEFT;\r
+       squaresLeft[LITE] = squaresLeft[ANY] - squaresLeft[DARK];\r
+\r
+       for(p = 0; p<=(int)WhiteKing; p++) piecesLeft[p] = 0;\r
+\r
+       for(i=BOARD_LEFT; i<BOARD_RGHT; i++) { // count pieces and clear board\r
+           p = (int) board[0][i];\r
+           if(p < (int) BlackPawn) piecesLeft[p] ++;\r
+           board[0][i] = EmptySquare;\r
+       }\r
+\r
+       if(PosFlags(0) & F_ALL_CASTLE_OK) {\r
+           // shuffles restricted to allow normal castling put KRR first\r
+           if(piecesLeft[(int)WhiteKing]) // King goes rightish of middle\r
+               put(board, WhiteKing, 0, (gameInfo.boardWidth+1)/2, ANY);\r
+           else if(piecesLeft[(int)WhiteUnicorn]) // in Knightmate Unicorn castles\r
+               put(board, WhiteUnicorn, 0, (gameInfo.boardWidth+1)/2, ANY);\r
+           if(piecesLeft[(int)WhiteRook]) // First supply a Rook for K-side castling\r
+               put(board, WhiteRook, 0, gameInfo.boardWidth-2, ANY);\r
+           if(piecesLeft[(int)WhiteRook]) // Then supply a Rook for Q-side castling\r
+               put(board, WhiteRook, 0, 0, ANY);\r
+           // in variants with super-numerary Kings and Rooks, we leave these for the shuffle\r
+       }\r
+\r
+       if((BOARD_RGHT-BOARD_LEFT & 1) == 0)\r
+           // only for even boards make effort to put pairs of colorbound pieces on opposite colors\r
+           for(p = (int) WhiteKing; p > (int) WhitePawn; p--) {\r
+               if(p != (int) WhiteBishop && p != (int) WhiteFerz && p != (int) WhiteAlfil) continue;\r
+               while(piecesLeft[p] >= 2) {\r
+                   AddOnePiece(board, p, 0, LITE);\r
+                   AddOnePiece(board, p, 0, DARK);\r
+               }\r
+               // Odd color-bound pieces are shuffled with the rest (to not run out of paired squares)\r
+           }\r
+\r
+       for(p = (int) WhiteKing - 2; p > (int) WhitePawn; p--) {\r
+           // Remaining pieces (non-colorbound, or odd color bound) can be put anywhere\r
+           // but we leave King and Rooks for last, to possibly obey FRC restriction\r
+           if(p == (int)WhiteRook) continue;\r
+           while(piecesLeft[p] >= 2) AddTwoPieces(board, p, 0); // add in pairs, for not counting permutations\r
+           if(piecesLeft[p]) AddOnePiece(board, p, 0, ANY);     // add the odd piece\r
+       }\r
+\r
+       // now everything is placed, except perhaps King (Unicorn) and Rooks\r
+\r
+       if(PosFlags(0) & F_FRC_TYPE_CASTLING) {\r
+           // Last King gets castling rights\r
+           while(piecesLeft[(int)WhiteUnicorn]) {\r
+               i = put(board, WhiteUnicorn, 0, piecesLeft[(int)WhiteRook]/2, ANY);\r
+               initialRights[2]  = initialRights[5]  = castlingRights[0][2] = castlingRights[0][5] = i;\r
+           }\r
+\r
+           while(piecesLeft[(int)WhiteKing]) {\r
+               i = put(board, WhiteKing, 0, piecesLeft[(int)WhiteRook]/2, ANY);\r
+               initialRights[2]  = initialRights[5]  = castlingRights[0][2] = castlingRights[0][5] = i;\r
+           }\r
+\r
+\r
+       } else {\r
+           while(piecesLeft[(int)WhiteKing])    AddOnePiece(board, WhiteKing, 0, ANY);\r
+           while(piecesLeft[(int)WhiteUnicorn]) AddOnePiece(board, WhiteUnicorn, 0, ANY);\r
+       }\r
+\r
+       // Only Rooks can be left; simply place them all\r
+       while(piecesLeft[(int)WhiteRook]) {\r
+               i = put(board, WhiteRook, 0, 0, ANY);\r
+               if(PosFlags(0) & F_FRC_TYPE_CASTLING) { // first and last Rook get FRC castling rights\r
+                       if(first) {\r
+                               first=0;\r
+                               initialRights[1]  = initialRights[4]  = castlingRights[0][1] = castlingRights[0][4] = i;\r
+                       }\r
+                       initialRights[0]  = initialRights[3]  = castlingRights[0][0] = castlingRights[0][3] = i;\r
+               }\r
+       }\r
+       for(i=BOARD_LEFT; i<BOARD_RGHT; i++) { // copy black from white\r
+           board[BOARD_HEIGHT-1][i] =  (int) board[0][i] < BlackPawn ? WHITE_TO_BLACK board[0][i] : EmptySquare;\r
+       }\r
+\r
+       if(number >= 0) appData.defaultFrcPosition %= nrOfShuffles; // normalize\r
+}\r
+\r
+#endif\r
+\r
+int SetCharTable( char *table, const char * map )\r
+/* [HGM] moved here from winboard.c because of its general usefulness */\r
+/*       Basically a safe strcpy that uses the last character as King */\r
+{\r
+    int result = FALSE; int NrPieces;\r
+\r
+    if( map != NULL && (NrPieces=strlen(map)) <= (int) EmptySquare \r
+                    && NrPieces >= 12 && !(NrPieces&1)) {\r
+        int i; /* [HGM] Accept even length from 12 to 34 */\r
+\r
+        for( i=0; i<(int) EmptySquare; i++ ) table[i] = '.';\r
+        for( i=0; i<NrPieces/2-1; i++ ) {\r
+            table[i] = map[i];\r
+            table[i + (int)BlackPawn - (int) WhitePawn] = map[i+NrPieces/2];\r
+        }\r
+        table[(int) WhiteKing]  = map[NrPieces/2-1];\r
+        table[(int) BlackKing]  = map[NrPieces-1];\r
+\r
+        result = TRUE;\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+void Prelude(Board board)\r
+{      // [HGM] superchess: random selection of exo-pieces\r
+       int i, j, k; ChessSquare p; \r
+       static ChessSquare exoPieces[4] = { WhiteAngel, WhiteMarshall, WhiteSilver, WhiteLance };\r
+\r
+       GetPositionNumber(); // use FRC position number\r
+\r
+       if(appData.pieceToCharTable != NULL) { // select pieces to participate from given char table\r
+           SetCharTable(pieceToChar, appData.pieceToCharTable);\r
+           for(i=(int)WhiteQueen+1, j=0; i<(int)WhiteKing && j<4; i++) \r
+               if(PieceToChar((ChessSquare)i) != '.') exoPieces[j++] = (ChessSquare) i;\r
+       }\r
+\r
+       j = seed%4;                 seed /= 4; \r
+       p = board[0][BOARD_LEFT+j];   board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p);\r
+       board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;\r
+       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;\r
+       j = seed%3 + (seed%3 >= j); seed /= 3; \r
+       p = board[0][BOARD_LEFT+j];   board[0][BOARD_LEFT+j] = EmptySquare; k = PieceToNumber(p);\r
+       board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;\r
+       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;\r
+       j = seed%3;                 seed /= 3; \r
+       p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p);\r
+       board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;\r
+       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;\r
+       j = seed%2 + (seed%2 >= j); seed /= 2; \r
+       p = board[0][BOARD_LEFT+j+5]; board[0][BOARD_LEFT+j+5] = EmptySquare; k = PieceToNumber(p);\r
+       board[k][BOARD_WIDTH-1] = p;  board[k][BOARD_WIDTH-2]++;\r
+       board[BOARD_HEIGHT-1-k][0] = WHITE_TO_BLACK p;  board[BOARD_HEIGHT-1-k][1]++;\r
+       j = seed%4; seed /= 4; put(board, exoPieces[3],    0, j, ANY);\r
+       j = seed%3; seed /= 3; put(board, exoPieces[2],   0, j, ANY);\r
+       j = seed%2; seed /= 2; put(board, exoPieces[1], 0, j, ANY);\r
+       put(board, exoPieces[0],    0, 0, ANY);\r
+       for(i=BOARD_LEFT; i<BOARD_RGHT; i++) board[BOARD_HEIGHT-1][i] = WHITE_TO_BLACK board[0][i];\r
+}\r
+\r
+void\r
+InitPosition(redraw)\r
+     int redraw;\r
+{\r
+    ChessSquare (* pieces)[BOARD_SIZE];\r
+    int i, j, pawnRow, overrule,\r
+    oldx = gameInfo.boardWidth,\r
+    oldy = gameInfo.boardHeight,\r
+    oldh = gameInfo.holdingsWidth,\r
+    oldv = gameInfo.variant;\r
+\r
+    currentMove = forwardMostMove = backwardMostMove = 0;\r
+    if(appData.icsActive) shuffleOpenings = FALSE; // [HGM] shuffle: in ICS mode, only shuffle on ICS request\r
+\r
+    /* [AS] Initialize pv info list [HGM] and game status */\r
+    {\r
+        for( i=0; i<MAX_MOVES; i++ ) {\r
+            pvInfoList[i].depth = 0;\r
+            epStatus[i]=EP_NONE;\r
+            for( j=0; j<BOARD_SIZE; j++ ) castlingRights[i][j] = -1;\r
+        }\r
+\r
+        initialRulePlies = 0; /* 50-move counter start */\r
+\r
+        castlingRank[0] = castlingRank[1] = castlingRank[2] = 0;\r
+        castlingRank[3] = castlingRank[4] = castlingRank[5] = BOARD_HEIGHT-1;\r
+    }\r
+\r
+    \r
+    /* [HGM] logic here is completely changed. In stead of full positions */\r
+    /* the initialized data only consist of the two backranks. The switch */\r
+    /* selects which one we will use, which is than copied to the Board   */\r
+    /* initialPosition, which for the rest is initialized by Pawns and    */\r
+    /* empty squares. This initial position is then copied to boards[0],  */\r
+    /* possibly after shuffling, so that it remains available.            */\r
+\r
+    gameInfo.holdingsWidth = 0; /* default board sizes */\r
+    gameInfo.boardWidth    = 8;\r
+    gameInfo.boardHeight   = 8;\r
+    gameInfo.holdingsSize  = 0;\r
+    nrCastlingRights = -1; /* [HGM] Kludge to indicate default should be used */\r
+    for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1; /* but no rights yet */\r
+    SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k"); \r
+\r
+    switch (gameInfo.variant) {\r
+    case VariantFischeRandom:\r
+      shuffleOpenings = TRUE;\r
+    default:\r
+      pieces = FIDEArray;\r
+      break;\r
+    case VariantShatranj:\r
+      pieces = ShatranjArray;\r
+      nrCastlingRights = 0;\r
+      SetCharTable(pieceToChar, "PN.R.QB...Kpn.r.qb...k"); \r
+      break;\r
+    case VariantTwoKings:\r
+      pieces = twoKingsArray;\r
+      nrCastlingRights = 8;                 /* add rights for second King */\r
+      castlingRights[0][6] = initialRights[2] = 5;\r
+      castlingRights[0][7] = initialRights[5] = 5;\r
+      castlingRank[6] = 0;\r
+      castlingRank[7] = BOARD_HEIGHT-1;\r
+      break;\r
+    case VariantCapaRandom:\r
+      shuffleOpenings = TRUE;\r
+    case VariantCapablanca:\r
+      pieces = CapablancaArray;\r
+      gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack"); \r
+      break;\r
+    case VariantGothic:\r
+      pieces = GothicArray;\r
+      gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack"); \r
+      break;\r
+    case VariantJanus:\r
+      pieces = JanusArray;\r
+      gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PNBRQ..JKpnbrq..jk"); \r
+      nrCastlingRights = 6;\r
+        castlingRights[0][0] = initialRights[0] = BOARD_RGHT-1;\r
+        castlingRights[0][1] = initialRights[1] = BOARD_LEFT;\r
+        castlingRights[0][2] = initialRights[2] = BOARD_WIDTH-1>>1;\r
+        castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1;\r
+        castlingRights[0][4] = initialRights[4] = BOARD_LEFT;\r
+        castlingRights[0][5] = initialRights[5] = BOARD_WIDTH-1>>1;\r
+      break;\r
+    case VariantFalcon:\r
+      pieces = FalconArray;\r
+      gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PNBRQ.............FKpnbrq.............fk"); \r
+      break;\r
+    case VariantXiangqi:\r
+      pieces = XiangqiArray;\r
+      gameInfo.boardWidth  = 9;\r
+      gameInfo.boardHeight = 10;\r
+      nrCastlingRights = 0;\r
+      SetCharTable(pieceToChar, "PH.R.AE..K.C.ph.r.ae..k.c."); \r
+      break;\r
+    case VariantShogi:\r
+      pieces = ShogiArray;\r
+      gameInfo.boardWidth  = 9;\r
+      gameInfo.boardHeight = 9;\r
+      gameInfo.holdingsSize = 7;\r
+      nrCastlingRights = 0;\r
+      SetCharTable(pieceToChar, "PNBRLS...G.++++++Kpnbrls...g.++++++k"); \r
+      break;\r
+    case VariantCourier:\r
+      pieces = CourierArray;\r
+      gameInfo.boardWidth  = 12;\r
+      nrCastlingRights = 0;\r
+      SetCharTable(pieceToChar, "PNBR.FE..WMKpnbr.fe..wmk"); \r
+      for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
+      break;\r
+    case VariantKnightmate:\r
+      pieces = KnightmateArray;\r
+      SetCharTable(pieceToChar, "P.BRQ.....M.........K.p.brq.....m.........k."); \r
+      break;\r
+    case VariantFairy:\r
+      pieces = fairyArray;\r
+      SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); \r
+      break;\r
+    case VariantGreat:\r
+      pieces = GreatArray;\r
+      gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PN....E...S..HWGMKpn....e...s..hwgmk");\r
+      gameInfo.holdingsSize = 8;\r
+      break;\r
+    case VariantSuper:\r
+      pieces = FIDEArray;\r
+      SetCharTable(pieceToChar, "PNBRQ..SE.......V.AKpnbrq..se.......v.ak");\r
+      gameInfo.holdingsSize = 8;\r
+      startedFromSetupPosition = TRUE;\r
+      break;\r
+    case VariantCrazyhouse:\r
+    case VariantBughouse:\r
+      pieces = FIDEArray;\r
+      SetCharTable(pieceToChar, "PNBRQ.......~~~~Kpnbrq.......~~~~k"); \r
+      gameInfo.holdingsSize = 5;\r
+      break;\r
+    case VariantWildCastle:\r
+      pieces = FIDEArray;\r
+      /* !!?shuffle with kings guaranteed to be on d or e file */\r
+      shuffleOpenings = 1;\r
+      break;\r
+    case VariantNoCastle:\r
+      pieces = FIDEArray;\r
+      nrCastlingRights = 0;\r
+      for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
+      /* !!?unconstrained back-rank shuffle */\r
+      shuffleOpenings = 1;\r
+      break;\r
+    }\r
+\r
+    overrule = 0;\r
+    if(appData.NrFiles >= 0) {\r
+        if(gameInfo.boardWidth != appData.NrFiles) overrule++;\r
+        gameInfo.boardWidth = appData.NrFiles;\r
+    }\r
+    if(appData.NrRanks >= 0) {\r
+        gameInfo.boardHeight = appData.NrRanks;\r
+    }\r
+    if(appData.holdingsSize >= 0) {\r
+        i = appData.holdingsSize;\r
+        if(i > gameInfo.boardHeight) i = gameInfo.boardHeight;\r
+        gameInfo.holdingsSize = i;\r
+    }\r
+    if(gameInfo.holdingsSize) gameInfo.holdingsWidth = 2;\r
+    if(BOARD_HEIGHT > BOARD_SIZE || BOARD_WIDTH > BOARD_SIZE)\r
+        DisplayFatalError(_("Recompile to support this BOARD_SIZE!"), 0, 2);\r
+\r
+    pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */\r
+    if(pawnRow < 1) pawnRow = 1;\r
+\r
+    /* User pieceToChar list overrules defaults */\r
+    if(appData.pieceToCharTable != NULL)\r
+        SetCharTable(pieceToChar, appData.pieceToCharTable);\r
+\r
+    for( j=0; j<BOARD_WIDTH; j++ ) { ChessSquare s = EmptySquare;\r
+\r
+        if(j==BOARD_LEFT-1 || j==BOARD_RGHT)\r
+            s = (ChessSquare) 0; /* account holding counts in guard band */\r
+        for( i=0; i<BOARD_HEIGHT; i++ )\r
+            initialPosition[i][j] = s;\r
+\r
+        if(j < BOARD_LEFT || j >= BOARD_RGHT || overrule) continue;\r
+        initialPosition[0][j] = pieces[0][j-gameInfo.holdingsWidth];\r
+        initialPosition[pawnRow][j] = WhitePawn;\r
+        initialPosition[BOARD_HEIGHT-pawnRow-1][j] = BlackPawn;\r
+        if(gameInfo.variant == VariantXiangqi) {\r
+            if(j&1) {\r
+                initialPosition[pawnRow][j] = \r
+                initialPosition[BOARD_HEIGHT-pawnRow-1][j] = EmptySquare;\r
+                if(j==BOARD_LEFT+1 || j>=BOARD_RGHT-2) {\r
+                   initialPosition[2][j] = WhiteCannon;\r
+                   initialPosition[BOARD_HEIGHT-3][j] = BlackCannon;\r
+                }\r
+            }\r
+        }\r
+        initialPosition[BOARD_HEIGHT-1][j] =  pieces[1][j-gameInfo.holdingsWidth];\r
+    }\r
+    if( (gameInfo.variant == VariantShogi) && !overrule ) {\r
+\r
+            j=BOARD_LEFT+1;\r
+            initialPosition[1][j] = WhiteBishop;\r
+            initialPosition[BOARD_HEIGHT-2][j] = BlackRook;\r
+            j=BOARD_RGHT-2;\r
+            initialPosition[1][j] = WhiteRook;\r
+            initialPosition[BOARD_HEIGHT-2][j] = BlackBishop;\r
+    }\r
+\r
+    if( nrCastlingRights == -1) {\r
+        /* [HGM] Build normal castling rights (must be done after board sizing!) */\r
+        /*       This sets default castling rights from none to normal corners   */\r
+        /* Variants with other castling rights must set them themselves above    */\r
+        nrCastlingRights = 6;\r
+       \r
+        castlingRights[0][0] = initialRights[0] = BOARD_RGHT-1;\r
+        castlingRights[0][1] = initialRights[1] = BOARD_LEFT;\r
+        castlingRights[0][2] = initialRights[2] = BOARD_WIDTH>>1;\r
+        castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1;\r
+        castlingRights[0][4] = initialRights[4] = BOARD_LEFT;\r
+        castlingRights[0][5] = initialRights[5] = BOARD_WIDTH>>1;\r
+     }\r
+\r
+     if(gameInfo.variant == VariantSuper) Prelude(initialPosition);\r
+     if(gameInfo.variant == VariantGreat) { // promotion commoners\r
+       initialPosition[PieceToNumber(WhiteMan)][BOARD_RGHT-1] = WhiteMan;\r
+       initialPosition[PieceToNumber(WhiteMan)][BOARD_RGHT-2] = 9;\r
+       initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;\r
+       initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;\r
+     }\r
+#if 0\r
+    if(gameInfo.variant == VariantFischeRandom) {\r
+      if( appData.defaultFrcPosition < 0 ) {\r
+        ShuffleFRC( initialPosition );\r
+      }\r
+      else {\r
+        SetupFRC( initialPosition, appData.defaultFrcPosition );\r
+      }\r
+      startedFromSetupPosition = TRUE;\r
+    } else \r
+#else\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "shuffleOpenings = %d\n", shuffleOpenings);\r
+  }\r
+    if(shuffleOpenings) {\r
+       SetUpShuffle(initialPosition, appData.defaultFrcPosition);\r
+       startedFromSetupPosition = TRUE;\r
+    }\r
+#endif\r
+    if(startedFromPositionFile) {\r
+      /* [HGM] loadPos: use PositionFile for every new game */\r
+      CopyBoard(initialPosition, filePosition);\r
+      for(i=0; i<nrCastlingRights; i++)\r
+          castlingRights[0][i] = initialRights[i] = fileRights[i];\r
+      startedFromSetupPosition = TRUE;\r
+    }\r
+\r
+    CopyBoard(boards[0], initialPosition);\r
+\r
+    if(oldx != gameInfo.boardWidth ||\r
+       oldy != gameInfo.boardHeight ||\r
+       oldh != gameInfo.holdingsWidth\r
+#ifdef GOTHIC\r
+       || oldv == VariantGothic ||        // For licensing popups\r
+       gameInfo.variant == VariantGothic\r
+#endif\r
+#ifdef FALCON\r
+       || oldv == VariantFalcon ||\r
+       gameInfo.variant == VariantFalcon\r
+#endif\r
+                                         )\r
+            InitDrawingSizes(-2 ,0);\r
+\r
+    if (redraw)\r
+      DrawPosition(TRUE, boards[currentMove]);\r
+}\r
+\r
+void\r
+SendBoard(cps, moveNum)\r
+     ChessProgramState *cps;\r
+     int moveNum;\r
+{\r
+    char message[MSG_SIZ];\r
+    \r
+    if (cps->useSetboard) {\r
+      char* fen = PositionToFEN(moveNum, cps->useFEN960);\r
+      sprintf(message, "setboard %s\n", fen);\r
+      SendToProgram(message, cps);\r
+      free(fen);\r
+\r
+    } else {\r
+      ChessSquare *bp;\r
+      int i, j;\r
+      /* Kludge to set black to move, avoiding the troublesome and now\r
+       * deprecated "black" command.\r
+       */\r
+      if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);\r
+\r
+      SendToProgram("edit\n", cps);\r
+      SendToProgram("#\n", cps);\r
+      for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+       bp = &boards[moveNum][i][BOARD_LEFT];\r
+        for (j = BOARD_LEFT; j < BOARD_RGHT; j++, bp++) {\r
+         if ((int) *bp < (int) BlackPawn) {\r
+           sprintf(message, "%c%c%c\n", PieceToChar(*bp), \r
+                    AAA + j, ONE + i);\r
+            if(message[0] == '+' || message[0] == '~') {\r
+                sprintf(message, "%c%c%c+\n",\r
+                        PieceToChar((ChessSquare)(DEMOTED *bp)),\r
+                        AAA + j, ONE + i);\r
+            }\r
+            if(cps->alphaRank) { /* [HGM] shogi: translate coords */\r
+                message[1] = BOARD_RGHT   - 1 - j + '1';\r
+                message[2] = BOARD_HEIGHT - 1 - i + 'a';\r
+            }\r
+           SendToProgram(message, cps);\r
+         }\r
+       }\r
+      }\r
+    \r
+      SendToProgram("c\n", cps);\r
+      for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+       bp = &boards[moveNum][i][BOARD_LEFT];\r
+        for (j = BOARD_LEFT; j < BOARD_RGHT; j++, bp++) {\r
+         if (((int) *bp != (int) EmptySquare)\r
+             && ((int) *bp >= (int) BlackPawn)) {\r
+           sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),\r
+                    AAA + j, ONE + i);\r
+            if(message[0] == '+' || message[0] == '~') {\r
+                sprintf(message, "%c%c%c+\n",\r
+                        PieceToChar((ChessSquare)(DEMOTED *bp)),\r
+                        AAA + j, ONE + i);\r
+            }\r
+            if(cps->alphaRank) { /* [HGM] shogi: translate coords */\r
+                message[1] = BOARD_RGHT   - 1 - j + '1';\r
+                message[2] = BOARD_HEIGHT - 1 - i + 'a';\r
+            }\r
+           SendToProgram(message, cps);\r
+         }\r
+       }\r
+      }\r
+    \r
+      SendToProgram(".\n", cps);\r
+    }\r
+    setboardSpoiledMachineBlack = 0; /* [HGM] assume WB 4.2.7 already solves this after sending setboard */\r
+}\r
+\r
+int\r
+IsPromotion(fromX, fromY, toX, toY)\r
+     int fromX, fromY, toX, toY;\r
+{\r
+    /* [HGM] add Shogi promotions */\r
+    int promotionZoneSize=1, highestPromotingPiece = (int)WhitePawn;\r
+    ChessSquare piece;\r
+\r
+    if(gameMode == EditPosition || gameInfo.variant == VariantXiangqi ||\r
+      !(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) return FALSE;\r
+   /* [HGM] Note to self: line above also weeds out drops */\r
+    piece = boards[currentMove][fromY][fromX];\r
+    if(gameInfo.variant == VariantShogi) {\r
+        promotionZoneSize = 3;\r
+        highestPromotingPiece = (int)WhiteKing;\r
+        /* [HGM] Should be Silver = Ferz, really, but legality testing is off,\r
+           and if in normal chess we then allow promotion to King, why not\r
+           allow promotion of other piece in Shogi?                         */\r
+    }\r
+    if((int)piece >= BlackPawn) {\r
+        if(toY >= promotionZoneSize && fromY >= promotionZoneSize)\r
+             return FALSE;\r
+        highestPromotingPiece = WHITE_TO_BLACK highestPromotingPiece;\r
+    } else {\r
+        if(  toY < BOARD_HEIGHT - promotionZoneSize &&\r
+           fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE;\r
+    }\r
+    return ( (int)piece <= highestPromotingPiece );\r
+}\r
+\r
+int\r
+InPalace(row, column)\r
+     int row, column;\r
+{   /* [HGM] for Xiangqi */\r
+    if( (row < 3 || row > BOARD_HEIGHT-4) &&\r
+         column < (BOARD_WIDTH + 4)/2 &&\r
+         column > (BOARD_WIDTH - 5)/2 ) return TRUE;\r
+    return FALSE;\r
+}\r
+\r
+int\r
+PieceForSquare (x, y)\r
+     int x;\r
+     int y;\r
+{\r
+  if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT)\r
+     return -1;\r
+  else\r
+     return boards[currentMove][y][x];\r
+}\r
+\r
+int\r
+OKToStartUserMove(x, y)\r
+     int x, y;\r
+{\r
+    ChessSquare from_piece;\r
+    int white_piece;\r
+\r
+    if (matchMode) return FALSE;\r
+    if (gameMode == EditPosition) return TRUE;\r
+\r
+    if (x >= 0 && y >= 0)\r
+      from_piece = boards[currentMove][y][x];\r
+    else\r
+      from_piece = EmptySquare;\r
+\r
+    if (from_piece == EmptySquare) return FALSE;\r
+\r
+    white_piece = (int)from_piece >= (int)WhitePawn &&\r
+      (int)from_piece < (int)BlackPawn; /* [HGM] can be > King! */\r
+\r
+    switch (gameMode) {\r
+      case PlayFromGameFile:\r
+      case AnalyzeFile:\r
+      case TwoMachinesPlay:\r
+      case EndOfGame:\r
+       return FALSE;\r
+\r
+      case IcsObserving:\r
+      case IcsIdle:\r
+       return FALSE;\r
+\r
+      case MachinePlaysWhite:\r
+      case IcsPlayingBlack:\r
+       if (appData.zippyPlay) return FALSE;\r
+       if (white_piece) {\r
+           DisplayMoveError(_("You are playing Black"));\r
+           return FALSE;\r
+       }\r
+       break;\r
+\r
+      case MachinePlaysBlack:\r
+      case IcsPlayingWhite:\r
+       if (appData.zippyPlay) return FALSE;\r
+       if (!white_piece) {\r
+           DisplayMoveError(_("You are playing White"));\r
+           return FALSE;\r
+       }\r
+       break;\r
+\r
+      case EditGame:\r
+       if (!white_piece && WhiteOnMove(currentMove)) {\r
+           DisplayMoveError(_("It is White's turn"));\r
+           return FALSE;\r
+       }           \r
+       if (white_piece && !WhiteOnMove(currentMove)) {\r
+           DisplayMoveError(_("It is Black's turn"));\r
+           return FALSE;\r
+       }           \r
+       if (cmailMsgLoaded && (currentMove < cmailOldMove)) {\r
+           /* Editing correspondence game history */\r
+           /* Could disallow this or prompt for confirmation */\r
+           cmailOldMove = -1;\r
+       }\r
+       if (currentMove < forwardMostMove) {\r
+           /* Discarding moves */\r
+           /* Could prompt for confirmation here,\r
+              but I don't think that's such a good idea */\r
+           forwardMostMove = currentMove;\r
+       }\r
+       break;\r
+\r
+      case BeginningOfGame:\r
+       if (appData.icsActive) return FALSE;\r
+       if (!appData.noChessProgram) {\r
+           if (!white_piece) {\r
+               DisplayMoveError(_("You are playing White"));\r
+               return FALSE;\r
+           }\r
+       }\r
+       break;\r
+       \r
+      case Training:\r
+       if (!white_piece && WhiteOnMove(currentMove)) {\r
+           DisplayMoveError(_("It is White's turn"));\r
+           return FALSE;\r
+       }           \r
+       if (white_piece && !WhiteOnMove(currentMove)) {\r
+           DisplayMoveError(_("It is Black's turn"));\r
+           return FALSE;\r
+       }           \r
+       break;\r
+\r
+      default:\r
+      case IcsExamining:\r
+       break;\r
+    }\r
+    if (currentMove != forwardMostMove && gameMode != AnalyzeMode\r
+       && gameMode != AnalyzeFile && gameMode != Training) {\r
+       DisplayMoveError(_("Displayed position is not current"));\r
+       return FALSE;\r
+    }\r
+    return TRUE;\r
+}\r
+\r
+FILE *lastLoadGameFP = NULL, *lastLoadPositionFP = NULL;\r
+int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;\r
+int lastLoadGameUseList = FALSE;\r
+char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];\r
+ChessMove lastLoadGameStart = (ChessMove) 0;\r
+\r
+\r
+ChessMove\r
+UserMoveTest(fromX, fromY, toX, toY, promoChar)\r
+     int fromX, fromY, toX, toY;\r
+     int promoChar;\r
+{\r
+    ChessMove moveType;\r
+    ChessSquare pdown, pup;\r
+\r
+    if (fromX < 0 || fromY < 0) return ImpossibleMove;\r
+    if ((fromX == toX) && (fromY == toY)) {\r
+        return ImpossibleMove;\r
+    }\r
+\r
+    /* [HGM] suppress all moves into holdings area and guard band */\r
+    if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 )\r
+            return ImpossibleMove;\r
+\r
+    /* [HGM] <sameColor> moved to here from winboard.c */\r
+    /* note: this code seems to exist for filtering out some obviously illegal premoves */\r
+    pdown = boards[currentMove][fromY][fromX];\r
+    pup = boards[currentMove][toY][toX];\r
+    if (    gameMode != EditPosition &&\r
+            (WhitePawn <= pdown && pdown < BlackPawn &&\r
+             WhitePawn <= pup && pup < BlackPawn  ||\r
+             BlackPawn <= pdown && pdown < EmptySquare &&\r
+             BlackPawn <= pup && pup < EmptySquare \r
+            ) && !((gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) &&\r
+                    (pup == WhiteRook && pdown == WhiteKing && fromY == 0 && toY == 0||\r
+                     pup == BlackRook && pdown == BlackKing && fromY == BOARD_HEIGHT-1 && toY == BOARD_HEIGHT-1  ) \r
+        )           )\r
+         return ImpossibleMove;\r
+\r
+    /* Check if the user is playing in turn.  This is complicated because we\r
+       let the user "pick up" a piece before it is his turn.  So the piece he\r
+       tried to pick up may have been captured by the time he puts it down!\r
+       Therefore we use the color the user is supposed to be playing in this\r
+       test, not the color of the piece that is currently on the starting\r
+       square---except in EditGame mode, where the user is playing both\r
+       sides; fortunately there the capture race can't happen.  (It can\r
+       now happen in IcsExamining mode, but that's just too bad.  The user\r
+       will get a somewhat confusing message in that case.)\r
+       */\r
+\r
+    switch (gameMode) {\r
+      case PlayFromGameFile:\r
+      case AnalyzeFile:\r
+      case TwoMachinesPlay:\r
+      case EndOfGame:\r
+      case IcsObserving:\r
+      case IcsIdle:\r
+       /* We switched into a game mode where moves are not accepted,\r
+           perhaps while the mouse button was down. */\r
+        return ImpossibleMove;\r
+\r
+      case MachinePlaysWhite:\r
+       /* User is moving for Black */\r
+       if (WhiteOnMove(currentMove)) {\r
+           DisplayMoveError(_("It is White's turn"));\r
+            return ImpossibleMove;\r
+       }\r
+       break;\r
+\r
+      case MachinePlaysBlack:\r
+       /* User is moving for White */\r
+       if (!WhiteOnMove(currentMove)) {\r
+           DisplayMoveError(_("It is Black's turn"));\r
+            return ImpossibleMove;\r
+       }\r
+       break;\r
+\r
+      case EditGame:\r
+      case IcsExamining:\r
+      case BeginningOfGame:\r
+      case AnalyzeMode:\r
+      case Training:\r
+       if ((int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&\r
+            (int) boards[currentMove][fromY][fromX] < (int) EmptySquare) {\r
+           /* User is moving for Black */\r
+           if (WhiteOnMove(currentMove)) {\r
+               DisplayMoveError(_("It is White's turn"));\r
+                return ImpossibleMove;\r
+           }\r
+       } else {\r
+           /* User is moving for White */\r
+           if (!WhiteOnMove(currentMove)) {\r
+               DisplayMoveError(_("It is Black's turn"));\r
+                return ImpossibleMove;\r
+           }\r
+       }\r
+       break;\r
+\r
+      case IcsPlayingBlack:\r
+       /* User is moving for Black */\r
+       if (WhiteOnMove(currentMove)) {\r
+           if (!appData.premove) {\r
+               DisplayMoveError(_("It is White's turn"));\r
+           } else if (toX >= 0 && toY >= 0) {\r
+               premoveToX = toX;\r
+               premoveToY = toY;\r
+               premoveFromX = fromX;\r
+               premoveFromY = fromY;\r
+               premovePromoChar = promoChar;\r
+               gotPremove = 1;\r
+               if (appData.debugMode) \r
+                   fprintf(debugFP, "Got premove: fromX %d,"\r
+                           "fromY %d, toX %d, toY %d\n",\r
+                           fromX, fromY, toX, toY);\r
+           }\r
+            return ImpossibleMove;\r
+       }\r
+       break;\r
+\r
+      case IcsPlayingWhite:\r
+       /* User is moving for White */\r
+       if (!WhiteOnMove(currentMove)) {\r
+           if (!appData.premove) {\r
+               DisplayMoveError(_("It is Black's turn"));\r
+           } else if (toX >= 0 && toY >= 0) {\r
+               premoveToX = toX;\r
+               premoveToY = toY;\r
+               premoveFromX = fromX;\r
+               premoveFromY = fromY;\r
+               premovePromoChar = promoChar;\r
+               gotPremove = 1;\r
+               if (appData.debugMode) \r
+                   fprintf(debugFP, "Got premove: fromX %d,"\r
+                           "fromY %d, toX %d, toY %d\n",\r
+                           fromX, fromY, toX, toY);\r
+           }\r
+            return ImpossibleMove;\r
+       }\r
+       break;\r
+\r
+      default:\r
+       break;\r
+\r
+      case EditPosition:\r
+       /* EditPosition, empty square, or different color piece;\r
+          click-click move is possible */\r
+       if (toX == -2 || toY == -2) {\r
+           boards[0][fromY][fromX] = EmptySquare;\r
+           return AmbiguousMove;\r
+       } else if (toX >= 0 && toY >= 0) {\r
+           boards[0][toY][toX] = boards[0][fromY][fromX];\r
+           boards[0][fromY][fromX] = EmptySquare;\r
+           return AmbiguousMove;\r
+       }\r
+        return ImpossibleMove;\r
+    }\r
+\r
+    /* [HGM] If move started in holdings, it means a drop */\r
+    if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) { \r
+         if( pup != EmptySquare ) return ImpossibleMove;\r
+         if(appData.testLegality) {\r
+             /* it would be more logical if LegalityTest() also figured out\r
+              * which drops are legal. For now we forbid pawns on back rank.\r
+              * Shogi is on its own here...\r
+              */\r
+             if( (pdown == WhitePawn || pdown == BlackPawn) &&\r
+                 (toY == 0 || toY == BOARD_HEIGHT -1 ) )\r
+                 return(ImpossibleMove); /* no pawn drops on 1st/8th */\r
+         }\r
+         return WhiteDrop; /* Not needed to specify white or black yet */\r
+    }\r
+\r
+    userOfferedDraw = FALSE;\r
+       \r
+    /* [HGM] always test for legality, to get promotion info */\r
+    moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),\r
+                          epStatus[currentMove], castlingRights[currentMove],\r
+                                         fromY, fromX, toY, toX, promoChar);\r
+\r
+    /* [HGM] but possibly ignore an IllegalMove result */\r
+    if (appData.testLegality) {\r
+       if (moveType == IllegalMove || moveType == ImpossibleMove) {\r
+           DisplayMoveError(_("Illegal move"));\r
+            return ImpossibleMove;\r
+       }\r
+    }\r
+if(appData.debugMode) fprintf(debugFP, "moveType 3 = %d, promochar = %x\n", moveType, promoChar);\r
+    return moveType;\r
+    /* [HGM] <popupFix> in stead of calling FinishMove directly, this\r
+       function is made into one that returns an OK move type if FinishMove\r
+       should be called. This to give the calling driver routine the\r
+       opportunity to finish the userMove input with a promotion popup,\r
+       without bothering the user with this for invalid or illegal moves */\r
+\r
+/*    FinishMove(moveType, fromX, fromY, toX, toY, promoChar); */\r
+}\r
+\r
+/* Common tail of UserMoveEvent and DropMenuEvent */\r
+int\r
+FinishMove(moveType, fromX, fromY, toX, toY, promoChar)\r
+     ChessMove moveType;\r
+     int fromX, fromY, toX, toY;\r
+     /*char*/int promoChar;\r
+{\r
+    char *bookHit = 0;\r
+if(appData.debugMode) fprintf(debugFP, "moveType 5 = %d, promochar = %x\n", moveType, promoChar);\r
+    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && promoChar != NULLCHAR) { \r
+       // [HGM] superchess: suppress promotions to non-available piece\r
+       int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
+       if(WhiteOnMove(currentMove)) {\r
+           if(!boards[currentMove][k][BOARD_WIDTH-2]) return 0;\r
+       } else {\r
+           if(!boards[currentMove][BOARD_HEIGHT-1-k][1]) return 0;\r
+       }\r
+    }\r
+\r
+    /* [HGM] <popupFix> kludge to avoid having to know the exact promotion\r
+       move type in caller when we know the move is a legal promotion */\r
+    if(moveType == NormalMove && promoChar)\r
+        moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);\r
+if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", moveType, promoChar);\r
+    /* [HGM] convert drag-and-drop piece drops to standard form */\r
+    if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) {\r
+         moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;\r
+         fromX = boards[currentMove][fromY][fromX];\r
+         fromY = DROP_RANK;\r
+    }\r
+\r
+    /* [HGM] <popupFix> The following if has been moved here from\r
+       UserMoveEvent(). Because it seemed to belon here (why not allow\r
+       piece drops in training games?), and because it can only be\r
+       performed after it is known to what we promote. */\r
+    if (gameMode == Training) {\r
+      /* compare the move played on the board to the next move in the\r
+       * game. If they match, display the move and the opponent's response. \r
+       * If they don't match, display an error message.\r
+       */\r
+      int saveAnimate;\r
+      Board testBoard;\r
+      CopyBoard(testBoard, boards[currentMove]);\r
+      ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);\r
+\r
+      if (CompareBoards(testBoard, boards[currentMove+1])) {\r
+       ForwardInner(currentMove+1);\r
+\r
+       /* Autoplay the opponent's response.\r
+        * if appData.animate was TRUE when Training mode was entered,\r
+        * the response will be animated.\r
+        */\r
+       saveAnimate = appData.animate;\r
+       appData.animate = animateTraining;\r
+       ForwardInner(currentMove+1);\r
+       appData.animate = saveAnimate;\r
+\r
+       /* check for the end of the game */\r
+       if (currentMove >= forwardMostMove) {\r
+         gameMode = PlayFromGameFile;\r
+         ModeHighlight();\r
+         SetTrainingModeOff();\r
+         DisplayInformation(_("End of game"));\r
+       }\r
+      } else {\r
+       DisplayError(_("Incorrect move"), 0);\r
+      }\r
+      return 1;\r
+    }\r
+\r
+  /* Ok, now we know that the move is good, so we can kill\r
+     the previous line in Analysis Mode */\r
+  if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {\r
+    forwardMostMove = currentMove;\r
+  }\r
+\r
+  /* If we need the chess program but it's dead, restart it */\r
+  ResurrectChessProgram();\r
+\r
+  /* A user move restarts a paused game*/\r
+  if (pausing)\r
+    PauseEvent();\r
+\r
+  thinkOutput[0] = NULLCHAR;\r
+\r
+  MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/\r
+\r
+    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) \r
+               && promoChar != NULLCHAR && gameInfo.holdingsSize) { \r
+       // [HGM] superchess: take promotion piece out of holdings\r
+       int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
+       if(WhiteOnMove(forwardMostMove-1)) {\r
+           if(!--boards[forwardMostMove][k][BOARD_WIDTH-2])\r
+               boards[forwardMostMove][k][BOARD_WIDTH-1] = EmptySquare;\r
+       } else {\r
+           if(!--boards[forwardMostMove][BOARD_HEIGHT-1-k][1])\r
+               boards[forwardMostMove][BOARD_HEIGHT-1-k][0] = EmptySquare;\r
+       }\r
+    }\r
+\r
+  if (gameMode == BeginningOfGame) {\r
+    if (appData.noChessProgram) {\r
+      gameMode = EditGame;\r
+      SetGameInfo();\r
+    } else {\r
+      char buf[MSG_SIZ];\r
+      gameMode = MachinePlaysBlack;\r
+      StartClocks();\r
+      SetGameInfo();\r
+      sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);\r
+      DisplayTitle(buf);\r
+      if (first.sendName) {\r
+       sprintf(buf, "name %s\n", gameInfo.white);\r
+       SendToProgram(buf, &first);\r
+      }\r
+      StartClocks();\r
+    }\r
+    ModeHighlight();\r
+  }\r
+if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", moveType, promoChar);\r
+  /* Relay move to ICS or chess engine */\r
+  if (appData.icsActive) {\r
+    if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
+       gameMode == IcsExamining) {\r
+      SendMoveToICS(moveType, fromX, fromY, toX, toY);\r
+      ics_user_moved = 1;\r
+    }\r
+  } else {\r
+    if (first.sendTime && (gameMode == BeginningOfGame ||\r
+                          gameMode == MachinePlaysWhite ||\r
+                          gameMode == MachinePlaysBlack)) {\r
+      SendTimeRemaining(&first, gameMode != MachinePlaysBlack);\r
+    }\r
+    if (gameMode != EditGame && gameMode != PlayFromGameFile) {\r
+        // [HGM] book: if program might be playing, let it use book\r
+       bookHit = SendMoveToBookUser(forwardMostMove-1, &first, FALSE);\r
+       first.maybeThinking = TRUE;\r
+    } else SendMoveToProgram(forwardMostMove-1, &first);\r
+    if (currentMove == cmailOldMove + 1) {\r
+      cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
+    }\r
+  }\r
+\r
+  ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+\r
+  switch (gameMode) {\r
+  case EditGame:\r
+    switch (MateTest(boards[currentMove], PosFlags(currentMove),\r
+                     EP_UNKNOWN, castlingRights[currentMove]) ) {\r
+    case MT_NONE:\r
+    case MT_CHECK:\r
+      break;\r
+    case MT_CHECKMATE:\r
+      if (WhiteOnMove(currentMove)) {\r
+       GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
+      } else {\r
+       GameEnds(WhiteWins, "White mates", GE_PLAYER);\r
+      }\r
+      break;\r
+    case MT_STALEMATE:\r
+      GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);\r
+      break;\r
+    }\r
+    break;\r
+    \r
+  case MachinePlaysBlack:\r
+  case MachinePlaysWhite:\r
+    /* disable certain menu options while machine is thinking */\r
+    SetMachineThinkingEnables();\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+  if(bookHit) { // [HGM] book: simulate book reply\r
+       static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.score = programStats.got_only_move = 0;\r
+       sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+  }\r
+  return 1;\r
+}\r
+\r
+void\r
+UserMoveEvent(fromX, fromY, toX, toY, promoChar)\r
+     int fromX, fromY, toX, toY;\r
+     int promoChar;\r
+{\r
+    /* [HGM] This routine was added to allow calling of its two logical\r
+       parts from other modules in the old way. Before, UserMoveEvent()\r
+       automatically called FinishMove() if the move was OK, and returned\r
+       otherwise. I separated the two, in order to make it possible to\r
+       slip a promotion popup in between. But that it always needs two\r
+       calls, to the first part, (now called UserMoveTest() ), and to\r
+       FinishMove if the first part succeeded. Calls that do not need\r
+       to do anything in between, can call this routine the old way. \r
+    */\r
+    ChessMove moveType = UserMoveTest(fromX, fromY, toX, toY, promoChar);\r
+if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", moveType, promoChar);\r
+    if(moveType != ImpossibleMove)\r
+        FinishMove(moveType, fromX, fromY, toX, toY, promoChar);\r
+}\r
+\r
+void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats )\r
+{\r
+    char * hint = lastHint;\r
+    FrontEndProgramStats stats;\r
+\r
+    stats.which = cps == &first ? 0 : 1;\r
+    stats.depth = cpstats->depth;\r
+    stats.nodes = cpstats->nodes;\r
+    stats.score = cpstats->score;\r
+    stats.time = cpstats->time;\r
+    stats.pv = cpstats->movelist;\r
+    stats.hint = lastHint;\r
+    stats.an_move_index = 0;\r
+    stats.an_move_count = 0;\r
+\r
+    if( gameMode == AnalyzeMode || gameMode == AnalyzeFile ) {\r
+        stats.hint = cpstats->move_name;\r
+        stats.an_move_index = cpstats->nr_moves - cpstats->moves_left;\r
+        stats.an_move_count = cpstats->nr_moves;\r
+    }\r
+\r
+    SetProgramStats( &stats );\r
+}\r
+\r
+char *SendMoveToBookUser(int moveNr, ChessProgramState *cps, int initial)\r
+{   // [HGM] book: this routine intercepts moves to simulate book replies\r
+    char *bookHit = NULL;\r
+\r
+    //first determine if the incoming move brings opponent into his book\r
+    if(appData.usePolyglotBook && (cps == &first ? !appData.firstHasOwnBookUCI : !appData.secondHasOwnBookUCI))\r
+       bookHit = ProbeBook(moveNr+1, appData.polyglotBook); // returns move\r
+    if(appData.debugMode) fprintf(debugFP, "book hit = %s\n", bookHit ? bookHit : "(NULL)");\r
+    if(bookHit != NULL && !cps->bookSuspend) {\r
+       // make sure opponent is not going to reply after receiving move to book position\r
+       SendToProgram("force\n", cps);\r
+       cps->bookSuspend = TRUE; // flag indicating it has to be restarted\r
+    }\r
+    if(!initial) SendMoveToProgram(moveNr, cps); // with hit on initial position there is no move\r
+    // now arrange restart after book miss\r
+    if(bookHit) {\r
+       // after a book hit we never send 'go', and the code after the call to this routine\r
+       // has '&& !bookHit' added to suppress potential sending there (based on 'firstMove').\r
+       char buf[MSG_SIZ];\r
+       if (cps->useUsermove) sprintf(buf, "usermove "); // sorry, no SAN yet :(\r
+       sprintf(buf, "%s\n", bookHit); // force book move into program supposed to play it\r
+       SendToProgram(buf, cps);\r
+       if(!initial) firstMove = FALSE; // normally we would clear the firstMove condition after return & sending 'go'\r
+    } else if(initial) { // 'go' was needed irrespective of firstMove, and it has to be done in this routine\r
+       SendToProgram("go\n", cps);\r
+       cps->bookSuspend = FALSE; // after a 'go' we are never suspended\r
+    } else { // 'go' might be sent based on 'firstMove' after this routine returns\r
+       if(cps->bookSuspend && !firstMove) // 'go' needed, and it will not be done after we return\r
+           SendToProgram("go\n", cps); \r
+       cps->bookSuspend = FALSE; // anyhow, we will not be suspended after a miss\r
+    }\r
+    return bookHit; // notify caller of hit, so it can take action to send move to opponent\r
+}\r
+\r
+char *savedMessage;\r
+ChessProgramState *savedState;\r
+void DeferredBookMove(void)\r
+{\r
+       if(savedState->lastPing != savedState->lastPong)\r
+                   ScheduleDelayedEvent(DeferredBookMove, 10);\r
+       else\r
+       HandleMachineMove(savedMessage, savedState);\r
+}\r
+\r
+void\r
+HandleMachineMove(message, cps)\r
+     char *message;\r
+     ChessProgramState *cps;\r
+{\r
+    char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];\r
+    char realname[MSG_SIZ];\r
+    int fromX, fromY, toX, toY;\r
+    ChessMove moveType;\r
+    char promoChar;\r
+    char *p;\r
+    int machineWhite;\r
+    char *bookHit;\r
+\r
+FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book hit\r
+    /*\r
+     * Kludge to ignore BEL characters\r
+     */\r
+    while (*message == '\007') message++;\r
+\r
+    /*\r
+     * [HGM] engine debug message: ignore lines starting with '#' character\r
+     */\r
+    if(cps->debug && *message == '#') return;\r
+\r
+    /*\r
+     * Look for book output\r
+     */\r
+    if (cps == &first && bookRequested) {\r
+       if (message[0] == '\t' || message[0] == ' ') {\r
+           /* Part of the book output is here; append it */\r
+           strcat(bookOutput, message);\r
+           strcat(bookOutput, "  \n");\r
+           return;\r
+       } else if (bookOutput[0] != NULLCHAR) {\r
+           /* All of book output has arrived; display it */\r
+           char *p = bookOutput;\r
+           while (*p != NULLCHAR) {\r
+               if (*p == '\t') *p = ' ';\r
+               p++;\r
+           }\r
+           DisplayInformation(bookOutput);\r
+           bookRequested = FALSE;\r
+           /* Fall through to parse the current output */\r
+       }\r
+    }\r
+\r
+    /*\r
+     * Look for machine move.\r
+     */\r
+    if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 && strcmp(buf2, "...") == 0) ||\r
+       (sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0)) \r
+    {\r
+        /* This method is only useful on engines that support ping */\r
+        if (cps->lastPing != cps->lastPong) {\r
+         if (gameMode == BeginningOfGame) {\r
+           /* Extra move from before last new; ignore */\r
+           if (appData.debugMode) {\r
+               fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);\r
+           }\r
+         } else {\r
+           if (appData.debugMode) {\r
+               fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",\r
+                       cps->which, gameMode);\r
+           }\r
+\r
+            SendToProgram("undo\n", cps);\r
+         }\r
+         return;\r
+       }\r
+\r
+       switch (gameMode) {\r
+         case BeginningOfGame:\r
+           /* Extra move from before last reset; ignore */\r
+           if (appData.debugMode) {\r
+               fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);\r
+           }\r
+           return;\r
+\r
+         case EndOfGame:\r
+         case IcsIdle:\r
+         default:\r
+           /* Extra move after we tried to stop.  The mode test is\r
+              not a reliable way of detecting this problem, but it's\r
+              the best we can do on engines that don't support ping.\r
+           */\r
+           if (appData.debugMode) {\r
+               fprintf(debugFP, "Undoing extra move from %s, gameMode %d\n",\r
+                       cps->which, gameMode);\r
+           }\r
+           SendToProgram("undo\n", cps);\r
+           return;\r
+\r
+         case MachinePlaysWhite:\r
+         case IcsPlayingWhite:\r
+           machineWhite = TRUE;\r
+           break;\r
+\r
+         case MachinePlaysBlack:\r
+         case IcsPlayingBlack:\r
+           machineWhite = FALSE;\r
+           break;\r
+\r
+         case TwoMachinesPlay:\r
+           machineWhite = (cps->twoMachinesColor[0] == 'w');\r
+           break;\r
+       }\r
+       if (WhiteOnMove(forwardMostMove) != machineWhite) {\r
+           if (appData.debugMode) {\r
+               fprintf(debugFP,\r
+                       "Ignoring move out of turn by %s, gameMode %d"\r
+                       ", forwardMost %d\n",\r
+                       cps->which, gameMode, forwardMostMove);\r
+           }\r
+           return;\r
+       }\r
+\r
+    if (appData.debugMode) { int f = forwardMostMove;\r
+        fprintf(debugFP, "machine move %d, castling = %d %d %d %d %d %d\n", f,\r
+                castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);\r
+    }\r
+        if(cps->alphaRank) AlphaRank(machineMove, 4);\r
+        if (!ParseOneMove(machineMove, forwardMostMove, &moveType,\r
+                              &fromX, &fromY, &toX, &toY, &promoChar)) {\r
+           /* Machine move could not be parsed; ignore it. */\r
+            sprintf(buf1, _("Illegal move \"%s\" from %s machine"),\r
+                   machineMove, cps->which);\r
+           DisplayError(buf1, 0);\r
+            sprintf(buf1, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d%c",\r
+                    machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);\r
+           if (gameMode == TwoMachinesPlay) {\r
+             GameEnds(machineWhite ? BlackWins : WhiteWins,\r
+                       buf1, GE_XBOARD);\r
+           }\r
+           return;\r
+       }\r
+\r
+        /* [HGM] Apparently legal, but so far only tested with EP_UNKOWN */\r
+        /* So we have to redo legality test with true e.p. status here,  */\r
+        /* to make sure an illegal e.p. capture does not slip through,   */\r
+        /* to cause a forfeit on a justified illegal-move complaint      */\r
+        /* of the opponent.                                              */\r
+        if( gameMode==TwoMachinesPlay && appData.testLegality\r
+            && fromY != DROP_RANK /* [HGM] temporary; should still add legality test for drops */\r
+                                                              ) {\r
+           ChessMove moveType;\r
+           moveType = LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),\r
+                        epStatus[forwardMostMove], castlingRights[forwardMostMove],\r
+                             fromY, fromX, toY, toX, promoChar);\r
+           if (appData.debugMode) {\r
+                int i;\r
+                for(i=0; i< nrCastlingRights; i++) fprintf(debugFP, "(%d,%d) ",\r
+                    castlingRights[forwardMostMove][i], castlingRank[i]);\r
+                fprintf(debugFP, "castling rights\n");\r
+           }\r
+            if(moveType == IllegalMove) {\r
+                sprintf(buf1, "Xboard: Forfeit due to illegal move: %s (%c%c%c%c)%c",\r
+                        machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);\r
+                GameEnds(machineWhite ? BlackWins : WhiteWins,\r
+                           buf1, GE_XBOARD);\r
+           } else if(gameInfo.variant != VariantFischeRandom && gameInfo.variant != VariantCapaRandom)\r
+           /* [HGM] Kludge to handle engines that send FRC-style castling\r
+              when they shouldn't (like TSCP-Gothic) */\r
+           switch(moveType) {\r
+             case WhiteASideCastleFR:\r
+             case BlackASideCastleFR:\r
+               toX+=2;\r
+               currentMoveString[2]++;\r
+               break;\r
+             case WhiteHSideCastleFR:\r
+             case BlackHSideCastleFR:\r
+               toX--;\r
+               currentMoveString[2]--;\r
+               break;\r
+           }\r
+        }\r
+       hintRequested = FALSE;\r
+       lastHint[0] = NULLCHAR;\r
+       bookRequested = FALSE;\r
+       /* Program may be pondering now */\r
+       cps->maybeThinking = TRUE;\r
+       if (cps->sendTime == 2) cps->sendTime = 1;\r
+       if (cps->offeredDraw) cps->offeredDraw--;\r
+\r
+#if ZIPPY\r
+       if ((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) &&\r
+           first.initDone) {\r
+         SendMoveToICS(moveType, fromX, fromY, toX, toY);\r
+         ics_user_moved = 1;\r
+         if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */\r
+               char buf[3*MSG_SIZ];\r
+\r
+               sprintf(buf, "kibitz %d/%+.2f (%.2f sec, %.0f nodes, %1.0f knps) PV = %s\n",\r
+                       programStats.depth,\r
+                       programStats.score / 100.,\r
+                       programStats.time / 100.,\r
+                       (double) programStats.nodes,\r
+                       programStats.nodes / (10*abs(programStats.time) + 1.),\r
+                       programStats.movelist);\r
+               SendToICS(buf);\r
+         }\r
+       }\r
+#endif\r
+       /* currentMoveString is set as a side-effect of ParseOneMove */\r
+       strcpy(machineMove, currentMoveString);\r
+       strcat(machineMove, "\n");\r
+       strcpy(moveList[forwardMostMove], machineMove);\r
+\r
+        /* [AS] Save move info and clear stats for next move */\r
+        pvInfoList[ forwardMostMove ].score = programStats.score;\r
+        pvInfoList[ forwardMostMove ].depth = programStats.depth;\r
+        pvInfoList[ forwardMostMove ].time =  programStats.time; // [HGM] PGNtime: take time from engine stats\r
+        ClearProgramStats();\r
+        thinkOutput[0] = NULLCHAR;\r
+        hiddenThinkOutputState = 0;\r
+\r
+       MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/\r
+\r
+        /* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */\r
+        if( gameMode == TwoMachinesPlay && adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) {\r
+            int count = 0;\r
+\r
+            while( count < adjudicateLossPlies ) {\r
+                int score = pvInfoList[ forwardMostMove - count - 1 ].score;\r
+\r
+                if( count & 1 ) {\r
+                    score = -score; /* Flip score for winning side */\r
+                }\r
+\r
+                if( score > adjudicateLossThreshold ) {\r
+                    break;\r
+                }\r
+\r
+                count++;\r
+            }\r
+\r
+            if( count >= adjudicateLossPlies ) {\r
+               ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+\r
+                GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
+                    "Xboard adjudication", \r
+                    GE_XBOARD );\r
+\r
+                return;\r
+            }\r
+        }\r
+\r
+       if( gameMode == TwoMachinesPlay ) {\r
+         // [HGM] some adjudications useful with buggy engines\r
+            int k, count = 0, epFile = epStatus[forwardMostMove]; static int bare = 1;\r
+         if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {\r
+\r
+            if(appData.testLegality)\r
+            // don't wait for engine to announce game end if we can judge ourselves\r
+            switch (MateTest(boards[forwardMostMove],\r
+                                 PosFlags(forwardMostMove), epFile,\r
+                                       castlingRights[forwardMostMove]) ) {\r
+             case MT_NONE:\r
+             case MT_CHECK:\r
+             default:\r
+               break;\r
+             case MT_STALEMATE:\r
+               epStatus[forwardMostMove] = EP_STALEMATE;\r
+                if(appData.checkMates) {\r
+                   SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                   GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate",\r
+                       GE_XBOARD );\r
+               }\r
+               break;\r
+             case MT_CHECKMATE:\r
+               epStatus[forwardMostMove] = EP_CHECKMATE;\r
+                if(appData.checkMates) {\r
+                   SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                   GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, \r
+                   "Xboard adjudication: Checkmate", \r
+                   GE_XBOARD );\r
+               }\r
+               break;\r
+           }\r
+\r
+           if( appData.testLegality )\r
+           {   /* [HGM] Some more adjudications for obstinate engines */\r
+               int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0,\r
+                    NrWQ=0, NrBQ=0, NrW=0, bishopsColor = 0,\r
+                    NrPieces=0, NrPawns=0, PawnAdvance=0, i, j, k;\r
+               static int moveCount = 6;\r
+\r
+                /* First absolutely insufficient mating material. Count what is on board. */\r
+               for(i=0; i<BOARD_HEIGHT; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++)\r
+               {   ChessSquare p = boards[forwardMostMove][i][j];\r
+                   int m=i;\r
+\r
+                   switch((int) p)\r
+                   {   /* count B,N,R and other of each side */\r
+                        case WhiteKnight:\r
+                             NrWN++; break;\r
+                        case WhiteBishop:\r
+                        case WhiteFerz:    // [HGM] shatranj: kludge to mke it work in shatranj\r
+                             bishopsColor |= 1 << ((i^j)&1);\r
+                             NrWB++; break;\r
+                        case BlackKnight:\r
+                             NrBN++; break;\r
+                        case BlackBishop:\r
+                        case BlackFerz:    // [HGM] shatranj: kludge to mke it work in shatranj\r
+                             bishopsColor |= 1 << ((i^j)&1);\r
+                             NrBB++; break;\r
+                        case WhiteRook:\r
+                             NrWR++; break;\r
+                        case BlackRook:\r
+                             NrBR++; break;\r
+                        case WhiteQueen:\r
+                             NrWQ++; break;\r
+                        case BlackQueen:\r
+                             NrBQ++; break;\r
+                        case EmptySquare: \r
+                             break;\r
+                        case BlackPawn:\r
+                             m = 7-i;\r
+                        case WhitePawn:\r
+                             PawnAdvance += m; NrPawns++;\r
+                    }\r
+                    NrPieces += (p != EmptySquare);\r
+                    NrW += ((int)p < (int)BlackPawn);\r
+                   if(gameInfo.variant == VariantXiangqi && \r
+                     (p == WhiteFerz || p == WhiteAlfil || p == BlackFerz || p == BlackAlfil)) {\r
+                       NrPieces--; // [HGM] XQ: do not count purely defensive pieces\r
+                        NrW -= ((int)p < (int)BlackPawn);\r
+                   }\r
+                }\r
+\r
+                if( NrPieces == 2 || gameInfo.variant != VariantXiangqi &&\r
+                       (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 ||\r
+                        NrPieces == NrBB+NrWB+2 && bishopsColor != 3)) // [HGM] all Bishops (Ferz!) same color\r
+                {    /* KBK, KNK, KK of KBKB with like Bishops */\r
+\r
+                     /* always flag draws, for judging claims */\r
+                     epStatus[forwardMostMove] = EP_INSUF_DRAW;\r
+\r
+                     if(appData.materialDraws) {\r
+                         /* but only adjudicate them if adjudication enabled */\r
+                        SendToProgram("force\n", cps->other); // suppress reply\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see last move */\r
+                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                         GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD );\r
+                         return;\r
+                     }\r
+                }\r
+\r
+               /* Shatranj baring rule */\r
+                if( gameInfo.variant == VariantShatranj && (NrW == 1 || NrPieces - NrW == 1) )\r
+                {    /* bare King */\r
+\r
+                     if(--bare < 0 && appData.checkMates) {\r
+                         /* but only adjudicate them if adjudication enabled */\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                         GameEnds( NrW > 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, \r
+                                                       "Xboard adjudication: Bare king", GE_XBOARD );\r
+                         return;\r
+                     }\r
+                } else bare = 1;\r
+\r
+                /* Then some trivial draws (only adjudicate, cannot be claimed) */\r
+                if(NrPieces == 4 && \r
+                   (   NrWR == 1 && NrBR == 1 /* KRKR */\r
+                   || NrWQ==1 && NrBQ==1     /* KQKQ */\r
+                   || NrWN==2 || NrBN==2     /* KNNK */\r
+                   || NrWN+NrWB == 1 && NrBN+NrBB == 1 /* KBKN, KBKB, KNKN */\r
+                  ) ) {\r
+                     if(--moveCount < 0 && appData.trivialDraws)\r
+                     {    /* if the first 3 moves do not show a tactical win, declare draw */\r
+                         SendToProgram("force\n", cps->other); // suppress reply\r
+                         SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                          ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                          GameEnds( GameIsDrawn, "Xboard adjudication: Trivial draw", GE_XBOARD );\r
+                          return;\r
+                     }\r
+                } else moveCount = 6;\r
+           }\r
+         }\r
+#if 1\r
+    if (appData.debugMode) { int i;\r
+      fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n",\r
+              forwardMostMove, backwardMostMove, epStatus[backwardMostMove],\r
+              appData.drawRepeats);\r
+      for( i=forwardMostMove; i>=backwardMostMove; i-- )\r
+           fprintf(debugFP, "%d ep=%d\n", i, epStatus[i]);\r
+\r
+    }\r
+#endif\r
+                /* Check for rep-draws */\r
+                count = 0;\r
+                for(k = forwardMostMove-2;\r
+                    k>=backwardMostMove && k>=forwardMostMove-100 &&\r
+                        epStatus[k] < EP_UNKNOWN &&\r
+                        epStatus[k+2] <= EP_NONE && epStatus[k+1] <= EP_NONE;\r
+                    k-=2)\r
+                {   int rights=0;\r
+#if 0\r
+    if (appData.debugMode) {\r
+      fprintf(debugFP, " loop\n");\r
+    }\r
+#endif\r
+                    if(CompareBoards(boards[k], boards[forwardMostMove])) {\r
+#if 0\r
+    if (appData.debugMode) {\r
+      fprintf(debugFP, "match\n");\r
+    }\r
+#endif\r
+                        /* compare castling rights */\r
+                        if( castlingRights[forwardMostMove][2] != castlingRights[k][2] &&\r
+                             (castlingRights[k][0] >= 0 || castlingRights[k][1] >= 0) )\r
+                                rights++; /* King lost rights, while rook still had them */\r
+                        if( castlingRights[forwardMostMove][2] >= 0 ) { /* king has rights */\r
+                            if( castlingRights[forwardMostMove][0] != castlingRights[k][0] ||\r
+                                castlingRights[forwardMostMove][1] != castlingRights[k][1] )\r
+                                   rights++; /* but at least one rook lost them */\r
+                        }\r
+                        if( castlingRights[forwardMostMove][5] != castlingRights[k][5] &&\r
+                             (castlingRights[k][3] >= 0 || castlingRights[k][4] >= 0) )\r
+                                rights++; \r
+                        if( castlingRights[forwardMostMove][5] >= 0 ) {\r
+                            if( castlingRights[forwardMostMove][3] != castlingRights[k][3] ||\r
+                                castlingRights[forwardMostMove][4] != castlingRights[k][4] )\r
+                                   rights++;\r
+                        }\r
+#if 0\r
+    if (appData.debugMode) {\r
+      for(i=0; i<nrCastlingRights; i++)\r
+      fprintf(debugFP, " (%d,%d)", castlingRights[forwardMostMove][i], castlingRights[k][i]);\r
+    }\r
+\r
+    if (appData.debugMode) {\r
+      fprintf(debugFP, " %d %d\n", rights, k);\r
+    }\r
+#endif\r
+                        if( rights == 0 && ++count > appData.drawRepeats-2\r
+                            && appData.drawRepeats > 1) {\r
+                             /* adjudicate after user-specified nr of repeats */\r
+                            SendToProgram("force\n", cps->other); // suppress reply\r
+                            SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                             ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                            if(gameInfo.variant == VariantXiangqi && appData.testLegality) { \r
+                               // [HGM] xiangqi: check for forbidden perpetuals\r
+                               int m, ourPerpetual = 1, hisPerpetual = 1;\r
+                               for(m=forwardMostMove; m>k; m-=2) {\r
+                                   if(MateTest(boards[m], PosFlags(m), \r
+                                                       EP_NONE, castlingRights[m]) != MT_CHECK)\r
+                                       ourPerpetual = 0; // the current mover did not always check\r
+                                   if(MateTest(boards[m-1], PosFlags(m-1), \r
+                                                       EP_NONE, castlingRights[m-1]) != MT_CHECK)\r
+                                       hisPerpetual = 0; // the opponent did not always check\r
+                               }\r
+                               if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit\r
+                                   GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
+                                          "Xboard adjudication: perpetual checking", GE_XBOARD );\r
+                                   return;\r
+                               }\r
+                               if(hisPerpetual && !ourPerpetual)   // he is checking us, but did not repeat yet\r
+                                   break; // (or we would have caught him before). Abort repetition-checking loop.\r
+                               // if neither of us is checking all the time, or both are, it is draw\r
+                               // (illegal-chase forfeits not implemented yet!)\r
+                            }\r
+                             GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD );\r
+                             return;\r
+                        }\r
+                        if( rights == 0 && count > 1 ) /* occurred 2 or more times before */\r
+                             epStatus[forwardMostMove] = EP_REP_DRAW;\r
+                    }\r
+                }\r
+\r
+                /* Now we test for 50-move draws. Determine ply count */\r
+                count = forwardMostMove;\r
+                /* look for last irreversble move */\r
+                while( epStatus[count] <= EP_NONE && count > backwardMostMove )\r
+                    count--;\r
+                /* if we hit starting position, add initial plies */\r
+                if( count == backwardMostMove )\r
+                    count -= initialRulePlies;\r
+                count = forwardMostMove - count; \r
+                if( count >= 100)\r
+                         epStatus[forwardMostMove] = EP_RULE_DRAW;\r
+                         /* this is used to judge if draw claims are legal */\r
+                if(appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) {\r
+                        SendToProgram("force\n", cps->other); // suppress reply\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                         GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD );\r
+                         return;\r
+                }\r
+\r
+                /* if draw offer is pending, treat it as a draw claim\r
+                 * when draw condition present, to allow engines a way to\r
+                 * claim draws before making their move to avoid a race\r
+                 * condition occurring after their move\r
+                 */\r
+                if( cps->other->offeredDraw || cps->offeredDraw ) {\r
+                         char *p = NULL;\r
+                         if(epStatus[forwardMostMove] == EP_RULE_DRAW)\r
+                             p = "Draw claim: 50-move rule";\r
+                         if(epStatus[forwardMostMove] == EP_REP_DRAW)\r
+                             p = "Draw claim: 3-fold repetition";\r
+                         if(epStatus[forwardMostMove] == EP_INSUF_DRAW)\r
+                             p = "Draw claim: insufficient mating material";\r
+                         if( p != NULL ) {\r
+                            SendToProgram("force\n", cps->other); // suppress reply\r
+                            SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                             GameEnds( GameIsDrawn, p, GE_XBOARD );\r
+                             ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                             return;\r
+                         }\r
+                }\r
+\r
+\r
+               if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {\r
+                   SendToProgram("force\n", cps->other); // suppress reply\r
+                   SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+\r
+                   GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );\r
+\r
+                   return;\r
+               }\r
+        }\r
+\r
+       bookHit = NULL;\r
+       if (gameMode == TwoMachinesPlay) {\r
+            /* [HGM] relaying draw offers moved to after reception of move */\r
+            /* and interpreting offer as claim if it brings draw condition */\r
+            if (cps->offeredDraw == 1 && cps->other->sendDrawOffers) {\r
+                SendToProgram("draw\n", cps->other);\r
+            }\r
+           if (cps->other->sendTime) {\r
+               SendTimeRemaining(cps->other,\r
+                                 cps->other->twoMachinesColor[0] == 'w');\r
+           }\r
+           bookHit = SendMoveToBookUser(forwardMostMove-1, cps->other, FALSE);\r
+           if (firstMove && !bookHit) {\r
+               firstMove = FALSE;\r
+               if (cps->other->useColors) {\r
+                 SendToProgram(cps->other->twoMachinesColor, cps->other);\r
+               }\r
+               SendToProgram("go\n", cps->other);\r
+           }\r
+           cps->other->maybeThinking = TRUE;\r
+       }\r
+\r
+       ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+       \r
+        if (!pausing && appData.ringBellAfterMoves) {\r
+           RingBell();\r
+       }\r
+\r
+       /* \r
+        * Reenable menu items that were disabled while\r
+        * machine was thinking\r
+        */\r
+       if (gameMode != TwoMachinesPlay)\r
+           SetUserThinkingEnables();\r
+\r
+       // [HGM] book: after book hit opponent has received move and is now in force mode\r
+       // force the book reply into it, and then fake that it outputted this move by jumping\r
+       // back to the beginning of HandleMachineMove, with cps toggled and message set to this move\r
+       if(bookHit) {\r
+               static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+               strcpy(bookMove, "move ");\r
+               strcat(bookMove, bookHit);\r
+               message = bookMove;\r
+               cps = cps->other;\r
+               programStats.depth = programStats.nodes = programStats.time = \r
+               programStats.score = programStats.got_only_move = 0;\r
+               sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
+\r
+               if(cps->lastPing != cps->lastPong) {\r
+                   savedMessage = message; // args for deferred call\r
+                   savedState = cps;\r
+                   ScheduleDelayedEvent(DeferredBookMove, 10);\r
+                   return;\r
+               }\r
+               goto FakeBookMove;\r
+       }\r
+\r
+       return;\r
+    }\r
+\r
+    /* Set special modes for chess engines.  Later something general\r
+     *  could be added here; for now there is just one kludge feature,\r
+     *  needed because Crafty 15.10 and earlier don't ignore SIGINT\r
+     *  when "xboard" is given as an interactive command.\r
+     */\r
+    if (strncmp(message, "kibitz Hello from Crafty", 24) == 0) {\r
+       cps->useSigint = FALSE;\r
+       cps->useSigterm = FALSE;\r
+    }\r
+\r
+    /* [HGM] Allow engine to set up a position. Don't ask me why one would\r
+     * want this, I was asked to put it in, and obliged.\r
+     */\r
+    if (!strncmp(message, "setboard ", 9)) {\r
+        Board initial_position; int i;\r
+\r
+        GameEnds(GameUnfinished, "Engine aborts game", GE_XBOARD);\r
+\r
+        if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9)) {\r
+            DisplayError(_("Bad FEN received from engine"), 0);\r
+            return ;\r
+        } else {\r
+           Reset(FALSE, FALSE);\r
+           CopyBoard(boards[0], initial_position);\r
+           initialRulePlies = FENrulePlies;\r
+           epStatus[0] = FENepStatus;\r
+           for( i=0; i<nrCastlingRights; i++ )\r
+                castlingRights[0][i] = FENcastlingRights[i];\r
+           if(blackPlaysFirst) gameMode = MachinePlaysWhite;\r
+           else gameMode = MachinePlaysBlack;                 \r
+           DrawPosition(FALSE, boards[currentMove]);\r
+        }\r
+       return;\r
+    }\r
+\r
+    /*\r
+     * Look for communication commands\r
+     */\r
+    if (!strncmp(message, "telluser ", 9)) {\r
+       DisplayNote(message + 9);\r
+       return;\r
+    }\r
+    if (!strncmp(message, "tellusererror ", 14)) {\r
+       DisplayError(message + 14, 0);\r
+       return;\r
+    }\r
+    if (!strncmp(message, "tellopponent ", 13)) {\r
+      if (appData.icsActive) {\r
+       if (loggedOn) {\r
+         sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);\r
+         SendToICS(buf1);\r
+       }\r
+      } else {\r
+       DisplayNote(message + 13);\r
+      }\r
+      return;\r
+    }\r
+    if (!strncmp(message, "tellothers ", 11)) {\r
+      if (appData.icsActive) {\r
+       if (loggedOn) {\r
+         sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);\r
+         SendToICS(buf1);\r
+       }\r
+      }\r
+      return;\r
+    }\r
+    if (!strncmp(message, "tellall ", 8)) {\r
+      if (appData.icsActive) {\r
+       if (loggedOn) {\r
+         sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);\r
+         SendToICS(buf1);\r
+       }\r
+      } else {\r
+       DisplayNote(message + 8);\r
+      }\r
+      return;\r
+    }\r
+    if (strncmp(message, "warning", 7) == 0) {\r
+       /* Undocumented feature, use tellusererror in new code */\r
+       DisplayError(message, 0);\r
+       return;\r
+    }\r
+    if (sscanf(message, "askuser %s %[^\n]", buf1, buf2) == 2) {\r
+       strcpy(realname, cps->tidy);\r
+       strcat(realname, " query");\r
+       AskQuestion(realname, buf2, buf1, cps->pr);\r
+       return;\r
+    }\r
+    /* Commands from the engine directly to ICS.  We don't allow these to be \r
+     *  sent until we are logged on. Crafty kibitzes have been known to \r
+     *  interfere with the login process.\r
+     */\r
+    if (loggedOn) {\r
+       if (!strncmp(message, "tellics ", 8)) {\r
+           SendToICS(message + 8);\r
+           SendToICS("\n");\r
+           return;\r
+       }\r
+       if (!strncmp(message, "tellicsnoalias ", 15)) {\r
+           SendToICS(ics_prefix);\r
+           SendToICS(message + 15);\r
+           SendToICS("\n");\r
+           return;\r
+       }\r
+       /* The following are for backward compatibility only */\r
+       if (!strncmp(message,"whisper",7) || !strncmp(message,"kibitz",6) ||\r
+           !strncmp(message,"draw",4) || !strncmp(message,"tell",3)) {\r
+           SendToICS(ics_prefix);\r
+           SendToICS(message);\r
+           SendToICS("\n");\r
+           return;\r
+       }\r
+    }\r
+    if (strncmp(message, "feature ", 8) == 0) {\r
+      ParseFeatures(message+8, cps);\r
+    }\r
+    if (sscanf(message, "pong %d", &cps->lastPong) == 1) {\r
+       return;\r
+    }\r
+    /*\r
+     * If the move is illegal, cancel it and redraw the board.\r
+     * Also deal with other error cases.  Matching is rather loose\r
+     * here to accommodate engines written before the spec.\r
+     */\r
+    if (strncmp(message + 1, "llegal move", 11) == 0 ||\r
+       strncmp(message, "Error", 5) == 0) {\r
+       if (StrStr(message, "name") || \r
+           StrStr(message, "rating") || StrStr(message, "?") ||\r
+           StrStr(message, "result") || StrStr(message, "board") ||\r
+           StrStr(message, "bk") || StrStr(message, "computer") ||\r
+           StrStr(message, "variant") || StrStr(message, "hint") ||\r
+           StrStr(message, "random") || StrStr(message, "depth") ||\r
+           StrStr(message, "accepted")) {\r
+           return;\r
+       }\r
+       if (StrStr(message, "protover")) {\r
+         /* Program is responding to input, so it's apparently done\r
+             initializing, and this error message indicates it is\r
+             protocol version 1.  So we don't need to wait any longer\r
+             for it to initialize and send feature commands. */\r
+         FeatureDone(cps, 1);\r
+         cps->protocolVersion = 1;\r
+         return;\r
+       }\r
+       cps->maybeThinking = FALSE;\r
+\r
+       if (StrStr(message, "draw")) {\r
+           /* Program doesn't have "draw" command */\r
+           cps->sendDrawOffers = 0;\r
+           return;\r
+       }\r
+       if (cps->sendTime != 1 &&\r
+           (StrStr(message, "time") || StrStr(message, "otim"))) {\r
+         /* Program apparently doesn't have "time" or "otim" command */\r
+         cps->sendTime = 0;\r
+         return;\r
+       }\r
+       if (StrStr(message, "analyze")) {\r
+           cps->analysisSupport = FALSE;\r
+           cps->analyzing = FALSE;\r
+           Reset(FALSE, TRUE);\r
+           sprintf(buf2, _("%s does not support analysis"), cps->tidy);\r
+           DisplayError(buf2, 0);\r
+           return;\r
+       }\r
+       if (StrStr(message, "(no matching move)st")) {\r
+         /* Special kludge for GNU Chess 4 only */\r
+         cps->stKludge = TRUE;\r
+         SendTimeControl(cps, movesPerSession, timeControl,\r
+                         timeIncrement, appData.searchDepth,\r
+                         searchTime);\r
+         return;\r
+       }\r
+       if (StrStr(message, "(no matching move)sd")) {\r
+         /* Special kludge for GNU Chess 4 only */\r
+         cps->sdKludge = TRUE;\r
+         SendTimeControl(cps, movesPerSession, timeControl,\r
+                         timeIncrement, appData.searchDepth,\r
+                         searchTime);\r
+         return;\r
+       }\r
+        if (!StrStr(message, "llegal")) {\r
+            return;\r
+        }\r
+       if (gameMode == BeginningOfGame || gameMode == EndOfGame ||\r
+           gameMode == IcsIdle) return;\r
+       if (forwardMostMove <= backwardMostMove) return;\r
+#if 0\r
+       /* Following removed: it caused a bug where a real illegal move\r
+          message in analyze mored would be ignored. */\r
+       if (cps == &first && programStats.ok_to_send == 0) {\r
+           /* Bogus message from Crafty responding to "."  This filtering\r
+              can miss some of the bad messages, but fortunately the bug \r
+              is fixed in current Crafty versions, so it doesn't matter. */\r
+           return;\r
+       }\r
+#endif\r
+       if (pausing) PauseEvent();\r
+       if (gameMode == PlayFromGameFile) {\r
+           /* Stop reading this game file */\r
+           gameMode = EditGame;\r
+           ModeHighlight();\r
+       }\r
+       currentMove = --forwardMostMove;\r
+       DisplayMove(currentMove-1); /* before DisplayMoveError */\r
+       SwitchClocks();\r
+       DisplayBothClocks();\r
+       sprintf(buf1, _("Illegal move \"%s\" (rejected by %s chess program)"),\r
+               parseList[currentMove], cps->which);\r
+       DisplayMoveError(buf1);\r
+       DrawPosition(FALSE, boards[currentMove]);\r
+\r
+        /* [HGM] illegal-move claim should forfeit game when Xboard */\r
+        /* only passes fully legal moves                            */\r
+        if( appData.testLegality && gameMode == TwoMachinesPlay ) {\r
+            GameEnds( cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,\r
+                                "False illegal-move claim", GE_XBOARD );\r
+        }\r
+       return;\r
+    }\r
+    if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {\r
+       /* Program has a broken "time" command that\r
+          outputs a string not ending in newline.\r
+          Don't use it. */\r
+       cps->sendTime = 0;\r
+    }\r
+    \r
+    /*\r
+     * If chess program startup fails, exit with an error message.\r
+     * Attempts to recover here are futile.\r
+     */\r
+    if ((StrStr(message, "unknown host") != NULL)\r
+       || (StrStr(message, "No remote directory") != NULL)\r
+       || (StrStr(message, "not found") != NULL)\r
+       || (StrStr(message, "No such file") != NULL)\r
+       || (StrStr(message, "can't alloc") != NULL)\r
+       || (StrStr(message, "Permission denied") != NULL)) {\r
+\r
+       cps->maybeThinking = FALSE;\r
+       sprintf(buf1, _("Failed to start %s chess program %s on %s: %s\n"),\r
+               cps->which, cps->program, cps->host, message);\r
+       RemoveInputSource(cps->isr);\r
+       DisplayFatalError(buf1, 0, 1);\r
+       return;\r
+    }\r
+    \r
+    /* \r
+     * Look for hint output\r
+     */\r
+    if (sscanf(message, "Hint: %s", buf1) == 1) {\r
+       if (cps == &first && hintRequested) {\r
+           hintRequested = FALSE;\r
+           if (ParseOneMove(buf1, forwardMostMove, &moveType,\r
+                                &fromX, &fromY, &toX, &toY, &promoChar)) {\r
+               (void) CoordsToAlgebraic(boards[forwardMostMove],\r
+                                   PosFlags(forwardMostMove), EP_UNKNOWN,\r
+                                   fromY, fromX, toY, toX, promoChar, buf1);\r
+               sprintf(buf2, _("Hint: %s"), buf1);\r
+               DisplayInformation(buf2);\r
+           } else {\r
+               /* Hint move could not be parsed!? */\r
+               sprintf(buf2,\r
+                       _("Illegal hint move \"%s\"\nfrom %s chess program"),\r
+                       buf1, cps->which);\r
+               DisplayError(buf2, 0);\r
+           }\r
+       } else {\r
+           strcpy(lastHint, buf1);\r
+       }\r
+       return;\r
+    }\r
+\r
+    /*\r
+     * Ignore other messages if game is not in progress\r
+     */\r
+    if (gameMode == BeginningOfGame || gameMode == EndOfGame ||\r
+       gameMode == IcsIdle || cps->lastPing != cps->lastPong) return;\r
+\r
+    /*\r
+     * look for win, lose, draw, or draw offer\r
+     */\r
+    if (strncmp(message, "1-0", 3) == 0) {\r
+       char *p, *q, *r = "";\r
+        p = strchr(message, '{');\r
+       if (p) {\r
+           q = strchr(p, '}');\r
+           if (q) {\r
+               *q = NULLCHAR;\r
+               r = p + 1;\r
+           }\r
+       }\r
+        GameEnds(WhiteWins, r, GE_ENGINE1 + (cps != &first)); /* [HGM] pass claimer indication for claim test */\r
+       return;\r
+    } else if (strncmp(message, "0-1", 3) == 0) {\r
+       char *p, *q, *r = "";\r
+        p = strchr(message, '{');\r
+       if (p) {\r
+           q = strchr(p, '}');\r
+           if (q) {\r
+               *q = NULLCHAR;\r
+               r = p + 1;\r
+           }\r
+       }\r
+       /* Kludge for Arasan 4.1 bug */\r
+       if (strcmp(r, "Black resigns") == 0) {\r
+            GameEnds(WhiteWins, r, GE_ENGINE1 + (cps != &first));\r
+           return;\r
+       }\r
+        GameEnds(BlackWins, r, GE_ENGINE1 + (cps != &first));\r
+       return;\r
+    } else if (strncmp(message, "1/2", 3) == 0) {\r
+       char *p, *q, *r = "";\r
+        p = strchr(message, '{');\r
+       if (p) {\r
+           q = strchr(p, '}');\r
+           if (q) {\r
+               *q = NULLCHAR;\r
+               r = p + 1;\r
+           }\r
+       }\r
+            \r
+        GameEnds(GameIsDrawn, r, GE_ENGINE1 + (cps != &first));\r
+       return;\r
+\r
+    } else if (strncmp(message, "White resign", 12) == 0) {\r
+        GameEnds(BlackWins, "White resigns", GE_ENGINE1 + (cps != &first));\r
+       return;\r
+    } else if (strncmp(message, "Black resign", 12) == 0) {\r
+        GameEnds(WhiteWins, "Black resigns", GE_ENGINE1 + (cps != &first));\r
+       return;\r
+    } else if (strncmp(message, "White matches", 13) == 0 ||\r
+               strncmp(message, "Black matches", 13) == 0   ) {\r
+        /* [HGM] ignore GNUShogi noises */\r
+        return;\r
+    } else if (strncmp(message, "White", 5) == 0 &&\r
+              message[5] != '(' &&\r
+              StrStr(message, "Black") == NULL) {\r
+        GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first));\r
+       return;\r
+    } else if (strncmp(message, "Black", 5) == 0 &&\r
+              message[5] != '(') {\r
+        GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first));\r
+       return;\r
+    } else if (strcmp(message, "resign") == 0 ||\r
+              strcmp(message, "computer resigns") == 0) {\r
+       switch (gameMode) {\r
+         case MachinePlaysBlack:\r
+         case IcsPlayingBlack:\r
+            GameEnds(WhiteWins, "Black resigns", GE_ENGINE);\r
+           break;\r
+         case MachinePlaysWhite:\r
+         case IcsPlayingWhite:\r
+            GameEnds(BlackWins, "White resigns", GE_ENGINE);\r
+           break;\r
+         case TwoMachinesPlay:\r
+           if (cps->twoMachinesColor[0] == 'w')\r
+              GameEnds(BlackWins, "White resigns", GE_ENGINE1 + (cps != &first));\r
+           else\r
+              GameEnds(WhiteWins, "Black resigns", GE_ENGINE1 + (cps != &first));\r
+           break;\r
+         default:\r
+           /* can't happen */\r
+           break;\r
+       }\r
+       return;\r
+    } else if (strncmp(message, "opponent mates", 14) == 0) {\r
+       switch (gameMode) {\r
+         case MachinePlaysBlack:\r
+         case IcsPlayingBlack:\r
+            GameEnds(WhiteWins, "White mates", GE_ENGINE);\r
+           break;\r
+         case MachinePlaysWhite:\r
+         case IcsPlayingWhite:\r
+            GameEnds(BlackWins, "Black mates", GE_ENGINE);\r
+           break;\r
+         case TwoMachinesPlay:\r
+           if (cps->twoMachinesColor[0] == 'w')\r
+              GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first));\r
+           else\r
+              GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first));\r
+           break;\r
+         default:\r
+           /* can't happen */\r
+           break;\r
+       }\r
+       return;\r
+    } else if (strncmp(message, "computer mates", 14) == 0) {\r
+       switch (gameMode) {\r
+         case MachinePlaysBlack:\r
+         case IcsPlayingBlack:\r
+            GameEnds(BlackWins, "Black mates", GE_ENGINE1);\r
+           break;\r
+         case MachinePlaysWhite:\r
+         case IcsPlayingWhite:\r
+            GameEnds(WhiteWins, "White mates", GE_ENGINE);\r
+           break;\r
+         case TwoMachinesPlay:\r
+           if (cps->twoMachinesColor[0] == 'w')\r
+              GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first));\r
+           else\r
+              GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first));\r
+           break;\r
+         default:\r
+           /* can't happen */\r
+           break;\r
+       }\r
+       return;\r
+    } else if (strncmp(message, "checkmate", 9) == 0) {\r
+       if (WhiteOnMove(forwardMostMove)) {\r
+            GameEnds(BlackWins, "Black mates", GE_ENGINE1 + (cps != &first));\r
+       } else {\r
+            GameEnds(WhiteWins, "White mates", GE_ENGINE1 + (cps != &first));\r
+       }\r
+       return;\r
+    } else if (strstr(message, "Draw") != NULL ||\r
+              strstr(message, "game is a draw") != NULL) {\r
+        GameEnds(GameIsDrawn, "Draw", GE_ENGINE1 + (cps != &first));\r
+       return;\r
+    } else if (strstr(message, "offer") != NULL &&\r
+              strstr(message, "draw") != NULL) {\r
+#if ZIPPY\r
+       if (appData.zippyPlay && first.initDone) {\r
+           /* Relay offer to ICS */\r
+           SendToICS(ics_prefix);\r
+           SendToICS("draw\n");\r
+       }\r
+#endif\r
+       cps->offeredDraw = 2; /* valid until this engine moves twice */\r
+       if (gameMode == TwoMachinesPlay) {\r
+           if (cps->other->offeredDraw) {\r
+               GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);\r
+            /* [HGM] in two-machine mode we delay relaying draw offer      */\r
+            /* until after we also have move, to see if it is really claim */\r
+           }\r
+#if 0\r
+              else {\r
+               if (cps->other->sendDrawOffers) {\r
+                   SendToProgram("draw\n", cps->other);\r
+               }\r
+           }\r
+#endif\r
+       } else if (gameMode == MachinePlaysWhite ||\r
+                  gameMode == MachinePlaysBlack) {\r
+         if (userOfferedDraw) {\r
+           DisplayInformation(_("Machine accepts your draw offer"));\r
+           GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);\r
+         } else {\r
+            DisplayInformation(_("Machine offers a draw\nSelect Action / Draw to agree"));\r
+         }\r
+       }\r
+    }\r
+\r
+    \r
+    /*\r
+     * Look for thinking output\r
+     */\r
+    if ( appData.showThinking // [HGM] thinking: test all options that cause this output\r
+         || !appData.hideThinkingFromHuman || appData.adjudicateLossThreshold != 0 || EngineOutputIsUp()\r
+                               ) {\r
+       int plylev, mvleft, mvtot, curscore, time;\r
+       char mvname[MOVE_LEN];\r
+       u64 nodes; // [DM]\r
+       char plyext;\r
+       int ignore = FALSE;\r
+       int prefixHint = FALSE;\r
+       mvname[0] = NULLCHAR;\r
+\r
+       switch (gameMode) {\r
+         case MachinePlaysBlack:\r
+         case IcsPlayingBlack:\r
+           if (WhiteOnMove(forwardMostMove)) prefixHint = TRUE;\r
+           break;\r
+         case MachinePlaysWhite:\r
+         case IcsPlayingWhite:\r
+           if (!WhiteOnMove(forwardMostMove)) prefixHint = TRUE;\r
+           break;\r
+         case AnalyzeMode:\r
+         case AnalyzeFile:\r
+            break;\r
+          case IcsObserving: /* [DM] icsEngineAnalyze */\r
+            if (!appData.icsEngineAnalyze) ignore = TRUE;\r
+           break;\r
+         case TwoMachinesPlay:\r
+           if ((cps->twoMachinesColor[0] == 'w') != WhiteOnMove(forwardMostMove)) {\r
+               ignore = TRUE;\r
+           }\r
+           break;\r
+         default:\r
+           ignore = TRUE;\r
+           break;\r
+       }\r
+\r
+       if (!ignore) {\r
+           buf1[0] = NULLCHAR;\r
+           if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",\r
+                      &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {\r
+\r
+               if (plyext != ' ' && plyext != '\t') {\r
+                   time *= 100;\r
+               }\r
+\r
+                /* [AS] Negate score if machine is playing black and reporting absolute scores */\r
+                if( cps->scoreIsAbsolute && \r
+                    ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) )\r
+                {\r
+                    curscore = -curscore;\r
+                }\r
+\r
+\r
+               programStats.depth = plylev;\r
+               programStats.nodes = nodes;\r
+               programStats.time = time;\r
+               programStats.score = curscore;\r
+               programStats.got_only_move = 0;\r
+\r
+               if(cps->nps >= 0) { /* [HGM] nps: use engine nodes or time to decrement clock */\r
+                       int ticklen;\r
+\r
+                       if(cps->nps == 0) ticklen = 10*time;       // use engine reported time\r
+                       else ticklen = (1000. * nodes) / cps->nps; // convert node count to time\r
+                       if(WhiteOnMove(forwardMostMove)) \r
+                            whiteTimeRemaining = timeRemaining[0][forwardMostMove] - ticklen;\r
+                       else blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen;\r
+               }\r
+\r
+               /* Buffer overflow protection */\r
+               if (buf1[0] != NULLCHAR) {\r
+                   if (strlen(buf1) >= sizeof(programStats.movelist)\r
+                       && appData.debugMode) {\r
+                       fprintf(debugFP,\r
+                               "PV is too long; using the first %d bytes.\n",\r
+                               sizeof(programStats.movelist) - 1);\r
+                   }\r
+\r
+                    safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) );\r
+               } else {\r
+                   sprintf(programStats.movelist, " no PV\n");\r
+               }\r
+\r
+               if (programStats.seen_stat) {\r
+                   programStats.ok_to_send = 1;\r
+               }\r
+\r
+               if (strchr(programStats.movelist, '(') != NULL) {\r
+                   programStats.line_is_book = 1;\r
+                   programStats.nr_moves = 0;\r
+                   programStats.moves_left = 0;\r
+               } else {\r
+                   programStats.line_is_book = 0;\r
+               }\r
+\r
+                SendProgramStatsToFrontend( cps, &programStats );\r
+\r
+                /* \r
+                    [AS] Protect the thinkOutput buffer from overflow... this\r
+                    is only useful if buf1 hasn't overflowed first!\r
+                */\r
+               sprintf(thinkOutput, "[%d]%c%+.2f %s%s",\r
+                       plylev, \r
+                       (gameMode == TwoMachinesPlay ?\r
+                        ToUpper(cps->twoMachinesColor[0]) : ' '),\r
+                       ((double) curscore) / 100.0,\r
+                       prefixHint ? lastHint : "",\r
+                       prefixHint ? " " : "" );\r
+\r
+                if( buf1[0] != NULLCHAR ) {\r
+                    unsigned max_len = sizeof(thinkOutput) - strlen(thinkOutput) - 1;\r
+\r
+                    if( strlen(buf1) > max_len ) {\r
+                       if( appData.debugMode) {\r
+                           fprintf(debugFP,"PV is too long for thinkOutput, truncating.\n");\r
+                        }\r
+                        buf1[max_len+1] = '\0';\r
+                    }\r
+\r
+                    strcat( thinkOutput, buf1 );\r
+                }\r
+\r
+                if (currentMove == forwardMostMove || gameMode == AnalyzeMode\r
+                        || gameMode == AnalyzeFile || appData.icsEngineAnalyze) {\r
+                   DisplayMove(currentMove - 1);\r
+                   DisplayAnalysis();\r
+               }\r
+               return;\r
+\r
+           } else if ((p=StrStr(message, "(only move)")) != NULL) {\r
+               /* crafty (9.25+) says "(only move) <move>"\r
+                * if there is only 1 legal move\r
+                 */\r
+               sscanf(p, "(only move) %s", buf1);\r
+               sprintf(thinkOutput, "%s (only move)", buf1);\r
+               sprintf(programStats.movelist, "%s (only move)", buf1);\r
+               programStats.depth = 1;\r
+               programStats.nr_moves = 1;\r
+               programStats.moves_left = 1;\r
+               programStats.nodes = 1;\r
+               programStats.time = 1;\r
+               programStats.got_only_move = 1;\r
+\r
+               /* Not really, but we also use this member to\r
+                  mean "line isn't going to change" (Crafty\r
+                  isn't searching, so stats won't change) */\r
+               programStats.line_is_book = 1;\r
+\r
+                SendProgramStatsToFrontend( cps, &programStats );\r
+                \r
+               if (currentMove == forwardMostMove || gameMode==AnalyzeMode || \r
+                           gameMode == AnalyzeFile || appData.icsEngineAnalyze) {\r
+                   DisplayMove(currentMove - 1);\r
+                   DisplayAnalysis();\r
+               }\r
+               return;\r
+           } else if (sscanf(message,"stat01: %d " u64Display " %d %d %d %s",\r
+                             &time, &nodes, &plylev, &mvleft,\r
+                             &mvtot, mvname) >= 5) {\r
+               /* The stat01: line is from Crafty (9.29+) in response\r
+                  to the "." command */\r
+               programStats.seen_stat = 1;\r
+               cps->maybeThinking = TRUE;\r
+\r
+               if (programStats.got_only_move || !appData.periodicUpdates)\r
+                 return;\r
+\r
+               programStats.depth = plylev;\r
+               programStats.time = time;\r
+               programStats.nodes = nodes;\r
+               programStats.moves_left = mvleft;\r
+               programStats.nr_moves = mvtot;\r
+               strcpy(programStats.move_name, mvname);\r
+               programStats.ok_to_send = 1;\r
+                programStats.movelist[0] = '\0';\r
+\r
+                SendProgramStatsToFrontend( cps, &programStats );\r
+\r
+               DisplayAnalysis();\r
+               return;\r
+\r
+           } else if (strncmp(message,"++",2) == 0) {\r
+               /* Crafty 9.29+ outputs this */\r
+               programStats.got_fail = 2;\r
+               return;\r
+\r
+           } else if (strncmp(message,"--",2) == 0) {\r
+               /* Crafty 9.29+ outputs this */\r
+               programStats.got_fail = 1;\r
+               return;\r
+\r
+           } else if (thinkOutput[0] != NULLCHAR &&\r
+                      strncmp(message, "    ", 4) == 0) {\r
+                unsigned message_len;\r
+\r
+               p = message;\r
+               while (*p && *p == ' ') p++;\r
+\r
+                message_len = strlen( p );\r
+\r
+                /* [AS] Avoid buffer overflow */\r
+                if( sizeof(thinkOutput) - strlen(thinkOutput) - 1 > message_len ) {\r
+                   strcat(thinkOutput, " ");\r
+                   strcat(thinkOutput, p);\r
+                }\r
+\r
+                if( sizeof(programStats.movelist) - strlen(programStats.movelist) - 1 > message_len ) {\r
+                   strcat(programStats.movelist, " ");\r
+                   strcat(programStats.movelist, p);\r
+                }\r
+\r
+               if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||\r
+                           gameMode == AnalyzeFile || appData.icsEngineAnalyze) {\r
+                   DisplayMove(currentMove - 1);\r
+                   DisplayAnalysis();\r
+               }\r
+               return;\r
+           }\r
+       }\r
+        else {\r
+           buf1[0] = NULLCHAR;\r
+\r
+           if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",\r
+                      &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) \r
+            {\r
+                ChessProgramStats cpstats;\r
+\r
+               if (plyext != ' ' && plyext != '\t') {\r
+                   time *= 100;\r
+               }\r
+\r
+                /* [AS] Negate score if machine is playing black and reporting absolute scores */\r
+                if( cps->scoreIsAbsolute && ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) ) {\r
+                    curscore = -curscore;\r
+                }\r
+\r
+               cpstats.depth = plylev;\r
+               cpstats.nodes = nodes;\r
+               cpstats.time = time;\r
+               cpstats.score = curscore;\r
+               cpstats.got_only_move = 0;\r
+                cpstats.movelist[0] = '\0';\r
+\r
+               if (buf1[0] != NULLCHAR) {\r
+                    safeStrCpy( cpstats.movelist, buf1, sizeof(cpstats.movelist) );\r
+               }\r
+\r
+               cpstats.ok_to_send = 0;\r
+               cpstats.line_is_book = 0;\r
+               cpstats.nr_moves = 0;\r
+               cpstats.moves_left = 0;\r
+\r
+                SendProgramStatsToFrontend( cps, &cpstats );\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+\r
+/* Parse a game score from the character string "game", and\r
+   record it as the history of the current game.  The game\r
+   score is NOT assumed to start from the standard position. \r
+   The display is not updated in any way.\r
+   */\r
+void\r
+ParseGameHistory(game)\r
+     char *game;\r
+{\r
+    ChessMove moveType;\r
+    int fromX, fromY, toX, toY, boardIndex;\r
+    char promoChar;\r
+    char *p, *q;\r
+    char buf[MSG_SIZ];\r
+\r
+    if (appData.debugMode)\r
+      fprintf(debugFP, "Parsing game history: %s\n", game);\r
+\r
+    if (gameInfo.event == NULL) gameInfo.event = StrSave("ICS game");\r
+    gameInfo.site = StrSave(appData.icsHost);\r
+    gameInfo.date = PGNDate();\r
+    gameInfo.round = StrSave("-");\r
+\r
+    /* Parse out names of players */\r
+    while (*game == ' ') game++;\r
+    p = buf;\r
+    while (*game != ' ') *p++ = *game++;\r
+    *p = NULLCHAR;\r
+    gameInfo.white = StrSave(buf);\r
+    while (*game == ' ') game++;\r
+    p = buf;\r
+    while (*game != ' ' && *game != '\n') *p++ = *game++;\r
+    *p = NULLCHAR;\r
+    gameInfo.black = StrSave(buf);\r
+\r
+    /* Parse moves */\r
+    boardIndex = blackPlaysFirst ? 1 : 0;\r
+    yynewstr(game);\r
+    for (;;) {\r
+       yyboardindex = boardIndex;\r
+       moveType = (ChessMove) yylex();\r
+       switch (moveType) {\r
+         case IllegalMove:             /* maybe suicide chess, etc. */\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Illegal move from ICS: '%s'\n", yy_text);\r
+    fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
+    setbuf(debugFP, NULL);\r
+  }\r
+          case WhitePromotionChancellor:\r
+          case BlackPromotionChancellor:\r
+          case WhitePromotionArchbishop:\r
+          case BlackPromotionArchbishop:\r
+         case WhitePromotionQueen:\r
+         case BlackPromotionQueen:\r
+         case WhitePromotionRook:\r
+         case BlackPromotionRook:\r
+         case WhitePromotionBishop:\r
+         case BlackPromotionBishop:\r
+         case WhitePromotionKnight:\r
+         case BlackPromotionKnight:\r
+         case WhitePromotionKing:\r
+         case BlackPromotionKing:\r
+         case NormalMove:\r
+         case WhiteCapturesEnPassant:\r
+         case BlackCapturesEnPassant:\r
+         case WhiteKingSideCastle:\r
+         case WhiteQueenSideCastle:\r
+         case BlackKingSideCastle:\r
+         case BlackQueenSideCastle:\r
+         case WhiteKingSideCastleWild:\r
+         case WhiteQueenSideCastleWild:\r
+         case BlackKingSideCastleWild:\r
+         case BlackQueenSideCastleWild:\r
+          /* PUSH Fabien */\r
+          case WhiteHSideCastleFR:\r
+          case WhiteASideCastleFR:\r
+          case BlackHSideCastleFR:\r
+          case BlackASideCastleFR:\r
+          /* POP Fabien */\r
+            fromX = currentMoveString[0] - AAA;\r
+            fromY = currentMoveString[1] - ONE;\r
+            toX = currentMoveString[2] - AAA;\r
+            toY = currentMoveString[3] - ONE;\r
+           promoChar = currentMoveString[4];\r
+           break;\r
+         case WhiteDrop:\r
+         case BlackDrop:\r
+           fromX = moveType == WhiteDrop ?\r
+             (int) CharToPiece(ToUpper(currentMoveString[0])) :\r
+           (int) CharToPiece(ToLower(currentMoveString[0]));\r
+           fromY = DROP_RANK;\r
+            toX = currentMoveString[2] - AAA;\r
+            toY = currentMoveString[3] - ONE;\r
+           promoChar = NULLCHAR;\r
+           break;\r
+         case AmbiguousMove:\r
+           /* bug? */\r
+           sprintf(buf, _("Ambiguous move in ICS output: \"%s\""), yy_text);\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Ambiguous move from ICS: '%s'\n", yy_text);\r
+    fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
+    setbuf(debugFP, NULL);\r
+  }\r
+           DisplayError(buf, 0);\r
+           return;\r
+         case ImpossibleMove:\r
+           /* bug? */\r
+           sprintf(buf, _("Illegal move in ICS output: \"%s\""), yy_text);\r
+  if (appData.debugMode) {\r
+    fprintf(debugFP, "Impossible move from ICS: '%s'\n", yy_text);\r
+    fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
+    setbuf(debugFP, NULL);\r
+  }\r
+           DisplayError(buf, 0);\r
+           return;\r
+         case (ChessMove) 0:   /* end of file */\r
+           if (boardIndex < backwardMostMove) {\r
+               /* Oops, gap.  How did that happen? */\r
+               DisplayError(_("Gap in move list"), 0);\r
+               return;\r
+           }\r
+           backwardMostMove =  blackPlaysFirst ? 1 : 0;\r
+           if (boardIndex > forwardMostMove) {\r
+               forwardMostMove = boardIndex;\r
+           }\r
+           return;\r
+         case ElapsedTime:\r
+           if (boardIndex > (blackPlaysFirst ? 1 : 0)) {\r
+               strcat(parseList[boardIndex-1], " ");\r
+               strcat(parseList[boardIndex-1], yy_text);\r
+           }\r
+           continue;\r
+         case Comment:\r
+         case PGNTag:\r
+         case NAG:\r
+         default:\r
+           /* ignore */\r
+           continue;\r
+         case WhiteWins:\r
+         case BlackWins:\r
+         case GameIsDrawn:\r
+         case GameUnfinished:\r
+           if (gameMode == IcsExamining) {\r
+               if (boardIndex < backwardMostMove) {\r
+                   /* Oops, gap.  How did that happen? */\r
+                   return;\r
+               }\r
+               backwardMostMove = blackPlaysFirst ? 1 : 0;\r
+               return;\r
+           }\r
+           gameInfo.result = moveType;\r
+           p = strchr(yy_text, '{');\r
+           if (p == NULL) p = strchr(yy_text, '(');\r
+           if (p == NULL) {\r
+               p = yy_text;\r
+               if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";\r
+           } else {\r
+               q = strchr(p, *p == '{' ? '}' : ')');\r
+               if (q != NULL) *q = NULLCHAR;\r
+               p++;\r
+           }\r
+           gameInfo.resultDetails = StrSave(p);\r
+           continue;\r
+       }\r
+       if (boardIndex >= forwardMostMove &&\r
+           !(gameMode == IcsObserving && ics_gamenum == -1)) {\r
+           backwardMostMove = blackPlaysFirst ? 1 : 0;\r
+           return;\r
+       }\r
+       (void) CoordsToAlgebraic(boards[boardIndex], PosFlags(boardIndex),\r
+                                EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,\r
+                                parseList[boardIndex]);\r
+       CopyBoard(boards[boardIndex + 1], boards[boardIndex]);\r
+       /* currentMoveString is set as a side-effect of yylex */\r
+       strcpy(moveList[boardIndex], currentMoveString);\r
+       strcat(moveList[boardIndex], "\n");\r
+       boardIndex++;\r
+       ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);\r
+        switch (MateTest(boards[boardIndex], PosFlags(boardIndex),\r
+                                 EP_UNKNOWN, castlingRights[boardIndex]) ) {\r
+         case MT_NONE:\r
+         case MT_STALEMATE:\r
+         default:\r
+           break;\r
+         case MT_CHECK:\r
+            if(gameInfo.variant != VariantShogi)\r
+                strcat(parseList[boardIndex - 1], "+");\r
+           break;\r
+         case MT_CHECKMATE:\r
+           strcat(parseList[boardIndex - 1], "#");\r
+           break;\r
+       }\r
+    }\r
+}\r
+\r
+\r
+/* Apply a move to the given board  */\r
+void\r
+ApplyMove(fromX, fromY, toX, toY, promoChar, board)\r
+     int fromX, fromY, toX, toY;\r
+     int promoChar;\r
+     Board board;\r
+{\r
+  ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;\r
+\r
+    /* [HGM] compute & store e.p. status and castling rights for new position */\r
+    /* if we are updating a board for which those exist (i.e. in boards[])    */\r
+    if((p = ((int)board - (int)boards[0])/((int)boards[1]-(int)boards[0])) < MAX_MOVES && p > 0)\r
+    { int i, j;\r
+\r
+      if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;\r
+      oldEP = epStatus[p-1];\r
+      epStatus[p] = EP_NONE;\r
+\r
+      if( board[toY][toX] != EmptySquare ) \r
+           epStatus[p] = EP_CAPTURE;  \r
+\r
+      if( board[fromY][fromX] == WhitePawn ) {\r
+           if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
+              epStatus[p] = EP_PAWN_MOVE;\r
+           if( toY-fromY==2) {\r
+               if(toX>BOARD_LEFT   && board[toY][toX-1] == BlackPawn &&\r
+                       gameInfo.variant != VariantBerolina || toX < fromX)\r
+                     epStatus[p] = toX | berolina;\r
+               if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&\r
+                       gameInfo.variant != VariantBerolina || toX > fromX) \r
+                     epStatus[p] = toX;\r
+          }\r
+      } else \r
+      if( board[fromY][fromX] == BlackPawn ) {\r
+           if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
+              epStatus[p] = EP_PAWN_MOVE; \r
+           if( toY-fromY== -2) {\r
+               if(toX>BOARD_LEFT   && board[toY][toX-1] == WhitePawn &&\r
+                       gameInfo.variant != VariantBerolina || toX < fromX)\r
+                     epStatus[p] = toX | berolina;\r
+               if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&\r
+                       gameInfo.variant != VariantBerolina || toX > fromX) \r
+                     epStatus[p] = toX;\r
+          }\r
+       }\r
+\r
+       for(i=0; i<nrCastlingRights; i++) {\r
+           castlingRights[p][i] = castlingRights[p-1][i];\r
+           if(castlingRights[p][i] == fromX && castlingRank[i] == fromY ||\r
+              castlingRights[p][i] == toX   && castlingRank[i] == toY   \r
+             ) castlingRights[p][i] = -1; // revoke for moved or captured piece\r
+       }\r
+\r
+    }\r
+\r
+  /* [HGM] In Shatranj and Courier all promotions are to Ferz */\r
+  if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier)\r
+       && promoChar != 0) promoChar = PieceToChar(WhiteFerz);\r
+         \r
+  if (fromX == toX && fromY == toY) return;\r
+\r
+  if (fromY == DROP_RANK) {\r
+       /* must be first */\r
+        piece = board[toY][toX] = (ChessSquare) fromX;\r
+  } else {\r
+     piece = board[fromY][fromX]; /* [HGM] remember, for Shogi promotion */\r
+     king = piece < (int) BlackPawn ? WhiteKing : BlackKing; /* [HGM] Knightmate simplify testing for castling */\r
+     if(gameInfo.variant == VariantKnightmate)\r
+         king += (int) WhiteUnicorn - (int) WhiteKing;\r
+\r
+    /* Code added by Tord: */\r
+    /* FRC castling assumed when king captures friendly rook. */\r
+    if (board[fromY][fromX] == WhiteKing &&\r
+            board[toY][toX] == WhiteRook) {\r
+      board[fromY][fromX] = EmptySquare;\r
+      board[toY][toX] = EmptySquare;\r
+      if(toX > fromX) {\r
+        board[0][BOARD_RGHT-2] = WhiteKing; board[0][BOARD_RGHT-3] = WhiteRook;\r
+      } else {\r
+        board[0][BOARD_LEFT+2] = WhiteKing; board[0][BOARD_LEFT+3] = WhiteRook;\r
+      }\r
+    } else if (board[fromY][fromX] == BlackKing &&\r
+              board[toY][toX] == BlackRook) {\r
+      board[fromY][fromX] = EmptySquare;\r
+      board[toY][toX] = EmptySquare;\r
+      if(toX > fromX) {\r
+        board[BOARD_HEIGHT-1][BOARD_RGHT-2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_RGHT-3] = BlackRook;\r
+      } else {\r
+        board[BOARD_HEIGHT-1][BOARD_LEFT+2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_LEFT+3] = BlackRook;\r
+      }\r
+    /* End of code added by Tord */\r
+\r
+    } else if (board[fromY][fromX] == king\r
+        && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
+        && toY == fromY && toX > fromX+1) {\r
+       board[fromY][fromX] = EmptySquare;\r
+        board[toY][toX] = king;\r
+        board[toY][toX-1] = board[fromY][BOARD_RGHT-1];\r
+        board[fromY][BOARD_RGHT-1] = EmptySquare;\r
+    } else if (board[fromY][fromX] == king\r
+        && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
+               && toY == fromY && toX < fromX-1) {\r
+       board[fromY][fromX] = EmptySquare;\r
+        board[toY][toX] = king;\r
+        board[toY][toX+1] = board[fromY][BOARD_LEFT];\r
+        board[fromY][BOARD_LEFT] = EmptySquare;\r
+    } else if (board[fromY][fromX] == WhitePawn\r
+               && toY == BOARD_HEIGHT-1\r
+               && gameInfo.variant != VariantXiangqi\r
+               ) {\r
+       /* white pawn promotion */\r
+        board[toY][toX] = CharToPiece(ToUpper(promoChar));\r
+        if (board[toY][toX] == EmptySquare) {\r
+            board[toY][toX] = WhiteQueen;\r
+       }\r
+        if(gameInfo.variant==VariantBughouse ||\r
+           gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */\r
+            board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);\r
+       board[fromY][fromX] = EmptySquare;\r
+    } else if ((fromY == BOARD_HEIGHT-4)\r
+              && (toX != fromX)\r
+               && gameInfo.variant != VariantXiangqi\r
+               && gameInfo.variant != VariantBerolina\r
+              && (board[fromY][fromX] == WhitePawn)\r
+              && (board[toY][toX] == EmptySquare)) {\r
+       board[fromY][fromX] = EmptySquare;\r
+       board[toY][toX] = WhitePawn;\r
+       captured = board[toY - 1][toX];\r
+       board[toY - 1][toX] = EmptySquare;\r
+    } else if ((fromY == BOARD_HEIGHT-4)\r
+              && (toX == fromX)\r
+               && gameInfo.variant == VariantBerolina\r
+              && (board[fromY][fromX] == WhitePawn)\r
+              && (board[toY][toX] == EmptySquare)) {\r
+       board[fromY][fromX] = EmptySquare;\r
+       board[toY][toX] = WhitePawn;\r
+       if(oldEP & EP_BEROLIN_A) {\r
+               captured = board[fromY][fromX-1];\r
+               board[fromY][fromX-1] = EmptySquare;\r
+       }else{  captured = board[fromY][fromX+1];\r
+               board[fromY][fromX+1] = EmptySquare;\r
+       }\r
+    } else if (board[fromY][fromX] == king\r
+        && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
+               && toY == fromY && toX > fromX+1) {\r
+       board[fromY][fromX] = EmptySquare;\r
+        board[toY][toX] = king;\r
+        board[toY][toX-1] = board[fromY][BOARD_RGHT-1];\r
+        board[fromY][BOARD_RGHT-1] = EmptySquare;\r
+    } else if (board[fromY][fromX] == king\r
+        && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
+               && toY == fromY && toX < fromX-1) {\r
+       board[fromY][fromX] = EmptySquare;\r
+        board[toY][toX] = king;\r
+        board[toY][toX+1] = board[fromY][BOARD_LEFT];\r
+        board[fromY][BOARD_LEFT] = EmptySquare;\r
+    } else if (fromY == 7 && fromX == 3\r
+              && board[fromY][fromX] == BlackKing\r
+              && toY == 7 && toX == 5) {\r
+       board[fromY][fromX] = EmptySquare;\r
+       board[toY][toX] = BlackKing;\r
+       board[fromY][7] = EmptySquare;\r
+       board[toY][4] = BlackRook;\r
+    } else if (fromY == 7 && fromX == 3\r
+              && board[fromY][fromX] == BlackKing\r
+              && toY == 7 && toX == 1) {\r
+       board[fromY][fromX] = EmptySquare;\r
+       board[toY][toX] = BlackKing;\r
+       board[fromY][0] = EmptySquare;\r
+       board[toY][2] = BlackRook;\r
+    } else if (board[fromY][fromX] == BlackPawn\r
+              && toY == 0\r
+               && gameInfo.variant != VariantXiangqi\r
+               ) {\r
+       /* black pawn promotion */\r
+       board[0][toX] = CharToPiece(ToLower(promoChar));\r
+       if (board[0][toX] == EmptySquare) {\r
+           board[0][toX] = BlackQueen;\r
+       }\r
+        if(gameInfo.variant==VariantBughouse ||\r
+           gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */\r
+            board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);\r
+       board[fromY][fromX] = EmptySquare;\r
+    } else if ((fromY == 3)\r
+              && (toX != fromX)\r
+               && gameInfo.variant != VariantXiangqi\r
+               && gameInfo.variant != VariantBerolina\r
+              && (board[fromY][fromX] == BlackPawn)\r
+              && (board[toY][toX] == EmptySquare)) {\r
+       board[fromY][fromX] = EmptySquare;\r
+       board[toY][toX] = BlackPawn;\r
+       captured = board[toY + 1][toX];\r
+       board[toY + 1][toX] = EmptySquare;\r
+    } else if ((fromY == 3)\r
+              && (toX == fromX)\r
+               && gameInfo.variant == VariantBerolina\r
+              && (board[fromY][fromX] == BlackPawn)\r
+              && (board[toY][toX] == EmptySquare)) {\r
+       board[fromY][fromX] = EmptySquare;\r
+       board[toY][toX] = BlackPawn;\r
+       if(oldEP & EP_BEROLIN_A) {\r
+               captured = board[fromY][fromX-1];\r
+               board[fromY][fromX-1] = EmptySquare;\r
+       }else{  captured = board[fromY][fromX+1];\r
+               board[fromY][fromX+1] = EmptySquare;\r
+       }\r
+    } else {\r
+       board[toY][toX] = board[fromY][fromX];\r
+       board[fromY][fromX] = EmptySquare;\r
+    }\r
+\r
+    /* [HGM] now we promote for Shogi, if needed */\r
+    if(gameInfo.variant == VariantShogi && promoChar == 'q')\r
+        board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
+  }\r
+\r
+    if (gameInfo.holdingsWidth != 0) {\r
+\r
+      /* !!A lot more code needs to be written to support holdings  */\r
+      /* [HGM] OK, so I have written it. Holdings are stored in the */\r
+      /* penultimate board files, so they are automaticlly stored   */\r
+      /* in the game history.                                       */\r
+      if (fromY == DROP_RANK) {\r
+        /* Delete from holdings, by decreasing count */\r
+        /* and erasing image if necessary            */\r
+        p = (int) fromX;\r
+        if(p < (int) BlackPawn) { /* white drop */\r
+             p -= (int)WhitePawn;\r
+             if(p >= gameInfo.holdingsSize) p = 0;\r
+             if(--board[p][BOARD_WIDTH-2] == 0)\r
+                  board[p][BOARD_WIDTH-1] = EmptySquare;\r
+        } else {                  /* black drop */\r
+             p -= (int)BlackPawn;\r
+             if(p >= gameInfo.holdingsSize) p = 0;\r
+             if(--board[BOARD_HEIGHT-1-p][1] == 0)\r
+                  board[BOARD_HEIGHT-1-p][0] = EmptySquare;\r
+        }\r
+      }\r
+      if (captured != EmptySquare && gameInfo.holdingsSize > 0\r
+          && gameInfo.variant != VariantBughouse        ) {\r
+        /* [HGM] holdings: Add to holdings, if holdings exist */\r
+       if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { \r
+               // [HGM] superchess: suppress flipping color of captured pieces by reverse pre-flip\r
+               captured = (int) captured >= (int) BlackPawn ? BLACK_TO_WHITE captured : WHITE_TO_BLACK captured;\r
+       }\r
+        p = (int) captured;\r
+        if (p >= (int) BlackPawn) {\r
+          p -= (int)BlackPawn;\r
+          if(gameInfo.variant == VariantShogi && DEMOTED p >= 0) {\r
+                  /* in Shogi restore piece to its original  first */\r
+                  captured = (ChessSquare) (DEMOTED captured);\r
+                  p = DEMOTED p;\r
+          }\r
+          p = PieceToNumber((ChessSquare)p);\r
+          if(p >= gameInfo.holdingsSize) { p = 0; captured = BlackPawn; }\r
+          board[p][BOARD_WIDTH-2]++;\r
+          board[p][BOARD_WIDTH-1] = BLACK_TO_WHITE captured;\r
+       } else {\r
+          p -= (int)WhitePawn;\r
+          if(gameInfo.variant == VariantShogi && DEMOTED p >= 0) {\r
+                  captured = (ChessSquare) (DEMOTED captured);\r
+                  p = DEMOTED p;\r
+          }\r
+          p = PieceToNumber((ChessSquare)p);\r
+          if(p >= gameInfo.holdingsSize) { p = 0; captured = WhitePawn; }\r
+          board[BOARD_HEIGHT-1-p][1]++;\r
+          board[BOARD_HEIGHT-1-p][0] = WHITE_TO_BLACK captured;\r
+       }\r
+      }\r
+\r
+    } else if (gameInfo.variant == VariantAtomic) {\r
+      if (captured != EmptySquare) {\r
+       int y, x;\r
+       for (y = toY-1; y <= toY+1; y++) {\r
+         for (x = toX-1; x <= toX+1; x++) {\r
+            if (y >= 0 && y < BOARD_HEIGHT && x >= BOARD_LEFT && x < BOARD_RGHT &&\r
+               board[y][x] != WhitePawn && board[y][x] != BlackPawn) {\r
+             board[y][x] = EmptySquare;\r
+           }\r
+         }\r
+       }\r
+       board[toY][toX] = EmptySquare;\r
+      }\r
+    }\r
+    if(gameInfo.variant == VariantShogi && promoChar != NULLCHAR && promoChar != '=') {\r
+        /* [HGM] Shogi promotions */\r
+        board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
+    }\r
+\r
+}\r
+\r
+/* Updates forwardMostMove */\r
+void\r
+MakeMove(fromX, fromY, toX, toY, promoChar)\r
+     int fromX, fromY, toX, toY;\r
+     int promoChar;\r
+{\r
+    forwardMostMove++;\r
+\r
+    if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting */\r
+        int timeLeft; static int lastLoadFlag=0; int king, piece;\r
+        piece = boards[forwardMostMove-1][fromY][fromX];\r
+        king = piece < (int) BlackPawn ? WhiteKing : BlackKing;\r
+        if(gameInfo.variant == VariantKnightmate)\r
+            king += (int) WhiteUnicorn - (int) WhiteKing;\r
+        if(forwardMostMove == 1) {\r
+            if(blackPlaysFirst) \r
+                fprintf(serverMoves, "%s;", second.tidy);\r
+            fprintf(serverMoves, "%s;", first.tidy);\r
+            if(!blackPlaysFirst) \r
+                fprintf(serverMoves, "%s;", second.tidy);\r
+        } else fprintf(serverMoves, loadFlag|lastLoadFlag ? ":" : ";");\r
+        lastLoadFlag = loadFlag;\r
+        // print base move\r
+        fprintf(serverMoves, "%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+toY);\r
+        // print castling suffix\r
+        if( toY == fromY && piece == king ) {\r
+            if(toX-fromX > 1)\r
+                fprintf(serverMoves, ":%c%c:%c%c", AAA+BOARD_RGHT-1, ONE+fromY, AAA+toX-1,ONE+toY);\r
+            if(fromX-toX >1)\r
+                fprintf(serverMoves, ":%c%c:%c%c", AAA+BOARD_LEFT, ONE+fromY, AAA+toX+1,ONE+toY);\r
+        }\r
+        // e.p. suffix\r
+        if( (boards[forwardMostMove-1][fromY][fromX] == WhitePawn ||\r
+             boards[forwardMostMove-1][fromY][fromX] == BlackPawn   ) &&\r
+             boards[forwardMostMove-1][toY][toX] == EmptySquare\r
+             && fromX != toX )\r
+                fprintf(serverMoves, ":%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+fromY);\r
+        // promotion suffix\r
+        if(promoChar != NULLCHAR)\r
+                fprintf(serverMoves, ":%c:%c%c", promoChar, AAA+toX, ONE+toY);\r
+        if(!loadFlag) {\r
+            fprintf(serverMoves, "/%d/%d",\r
+               pvInfoList[forwardMostMove-1].depth, pvInfoList[forwardMostMove-1].score);\r
+            if(forwardMostMove & 1) timeLeft = whiteTimeRemaining/1000;\r
+            else                    timeLeft = blackTimeRemaining/1000;\r
+            fprintf(serverMoves, "/%d", timeLeft);\r
+        }\r
+        fflush(serverMoves);\r
+    }\r
+\r
+    if (forwardMostMove >= MAX_MOVES) {\r
+      DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),\r
+                       0, 1);\r
+      return;\r
+    }\r
+    SwitchClocks();\r
+    timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
+    timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
+    if (commentList[forwardMostMove] != NULL) {\r
+       free(commentList[forwardMostMove]);\r
+       commentList[forwardMostMove] = NULL;\r
+    }\r
+    CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);\r
+    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]);\r
+    gameInfo.result = GameUnfinished;\r
+    if (gameInfo.resultDetails != NULL) {\r
+       free(gameInfo.resultDetails);\r
+       gameInfo.resultDetails = NULL;\r
+    }\r
+    CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar,\r
+                             moveList[forwardMostMove - 1]);\r
+    (void) CoordsToAlgebraic(boards[forwardMostMove - 1],\r
+                            PosFlags(forwardMostMove - 1), EP_UNKNOWN,\r
+                            fromY, fromX, toY, toX, promoChar,\r
+                            parseList[forwardMostMove - 1]);\r
+    switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove),\r
+                       epStatus[forwardMostMove], /* [HGM] use true e.p. */\r
+                            castlingRights[forwardMostMove]) ) {\r
+      case MT_NONE:\r
+      case MT_STALEMATE:\r
+      default:\r
+       break;\r
+      case MT_CHECK:\r
+        if(gameInfo.variant != VariantShogi)\r
+            strcat(parseList[forwardMostMove - 1], "+");\r
+       break;\r
+      case MT_CHECKMATE:\r
+       strcat(parseList[forwardMostMove - 1], "#");\r
+       break;\r
+    }\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "move: %s, parse: %s (%c)\n", moveList[forwardMostMove-1], parseList[forwardMostMove-1], moveList[forwardMostMove-1][4]);\r
+    }\r
+\r
+}\r
+\r
+/* Updates currentMove if not pausing */\r
+void\r
+ShowMove(fromX, fromY, toX, toY)\r
+{\r
+    int instant = (gameMode == PlayFromGameFile) ?\r
+       (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;\r
+    if(appData.noGUI) return;\r
+    if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {\r
+       if (!instant) {\r
+           if (forwardMostMove == currentMove + 1) {\r
+               AnimateMove(boards[forwardMostMove - 1],\r
+                           fromX, fromY, toX, toY);\r
+           }\r
+           if (appData.highlightLastMove) {\r
+               SetHighlights(fromX, fromY, toX, toY);\r
+           }\r
+       }\r
+       currentMove = forwardMostMove;\r
+    }\r
+\r
+    if (instant) return;\r
+\r
+    DisplayMove(currentMove - 1);\r
+    DrawPosition(FALSE, boards[currentMove]);\r
+    DisplayBothClocks();\r
+    HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
+}\r
+\r
+void SendEgtPath(ChessProgramState *cps)\r
+{       /* [HGM] EGT: match formats given in feature with those given by user, and send info for each match */\r
+       char buf[MSG_SIZ], name[MSG_SIZ], *p;\r
+\r
+       if((p = cps->egtFormats) == NULL || appData.egtFormats == NULL) return;\r
+\r
+       while(*p) {\r
+           char c, *q = name+1, *r, *s;\r
+\r
+           name[0] = ','; // extract next format name from feature and copy with prefixed ','\r
+           while(*p && *p != ',') *q++ = *p++;\r
+           *q++ = ':'; *q = 0;\r
+           if( appData.defaultPathEGTB && appData.defaultPathEGTB[0] && \r
+               strcmp(name, ",nalimov:") == 0 ) {\r
+               // take nalimov path from the menu-changeable option first, if it is defined\r
+               sprintf(buf, "egtpath nalimov %s\n", appData.defaultPathEGTB);\r
+               SendToProgram(buf,cps);     // send egtbpath command for nalimov\r
+           } else\r
+           if( (s = StrStr(appData.egtFormats, name+1)) == appData.egtFormats ||\r
+               (s = StrStr(appData.egtFormats, name)) != NULL) {\r
+               // format name occurs amongst user-supplied formats, at beginning or immediately after comma\r
+               s = r = StrStr(s, ":") + 1; // beginning of path info\r
+               while(*r && *r != ',') r++; // path info is everything upto next ';' or end of string\r
+               c = *r; *r = 0;             // temporarily null-terminate path info\r
+                   *--q = 0;               // strip of trailig ':' from name\r
+                   sprintf(buf, "egtbpath %s %s\n", name+1, s);\r
+               *r = c;\r
+               SendToProgram(buf,cps);     // send egtbpath command for this format\r
+           }\r
+           if(*p == ',') p++; // read away comma to position for next format name\r
+       }\r
+}\r
+\r
+void\r
+InitChessProgram(cps, setup)\r
+     ChessProgramState *cps;\r
+     int setup; /* [HGM] needed to setup FRC opening position */\r
+{\r
+    char buf[MSG_SIZ], b[MSG_SIZ]; int overruled;\r
+    if (appData.noChessProgram) return;\r
+    hintRequested = FALSE;\r
+    bookRequested = FALSE;\r
+\r
+    /* [HGM] some new WB protocol commands to configure engine are sent now, if engine supports them */\r
+    /*       moved to before sending initstring in 4.3.15, so Polyglot can delay UCI 'isready' to recepton of 'new' */\r
+    if(cps->memSize) { /* [HGM] memory */\r
+       sprintf(buf, "memory %d\n", appData.defaultHashSize + appData.defaultCacheSizeEGTB);\r
+       SendToProgram(buf, cps);\r
+    }\r
+    SendEgtPath(cps); /* [HGM] EGT */\r
+    if(cps->maxCores) { /* [HGM] SMP: (protocol specified must be last settings command before new!) */\r
+       sprintf(buf, "cores %d\n", appData.smpCores);\r
+       SendToProgram(buf, cps);\r
+    }\r
+\r
+    SendToProgram(cps->initString, cps);\r
+    if (gameInfo.variant != VariantNormal &&\r
+       gameInfo.variant != VariantLoadable\r
+        /* [HGM] also send variant if board size non-standard */\r
+        || gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0\r
+                                            ) {\r
+      char *v = VariantName(gameInfo.variant);\r
+      if (cps->protocolVersion != 1 && StrStr(cps->variants, v) == NULL) {\r
+        /* [HGM] in protocol 1 we have to assume all variants valid */\r
+       sprintf(buf, _("Variant %s not supported by %s"), v, cps->tidy);\r
+       DisplayFatalError(buf, 0, 1);\r
+       return;\r
+      }\r
+\r
+      /* [HGM] make prefix for non-standard board size. Awkward testing... */\r
+      overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
+      if( gameInfo.variant == VariantXiangqi )\r
+           overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 0;\r
+      if( gameInfo.variant == VariantShogi )\r
+           overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 9 || gameInfo.holdingsSize != 7;\r
+      if( gameInfo.variant == VariantBughouse || gameInfo.variant == VariantCrazyhouse )\r
+           overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 5;\r
+      if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantCapaRandom || \r
+                               gameInfo.variant == VariantGothic  || gameInfo.variant == VariantFalcon )\r
+           overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
+      if( gameInfo.variant == VariantCourier )\r
+           overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
+      if( gameInfo.variant == VariantSuper )\r
+           overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;\r
+      if( gameInfo.variant == VariantGreat )\r
+           overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;\r
+\r
+      if(overruled) {\r
+           sprintf(b, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, \r
+                               gameInfo.holdingsSize, VariantName(gameInfo.variant)); // cook up sized variant name\r
+           /* [HGM] varsize: try first if this defiant size variant is specifically known */\r
+           if(StrStr(cps->variants, b) == NULL) { \r
+               // specific sized variant not known, check if general sizing allowed\r
+               if (cps->protocolVersion != 1) { // for protocol 1 we cannot check and hope for the best\r
+                   if(StrStr(cps->variants, "boardsize") == NULL) {\r
+                       sprintf(buf, "Board size %dx%d+%d not supported by %s",\r
+                            gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize, cps->tidy);\r
+                       DisplayFatalError(buf, 0, 1);\r
+                       return;\r
+                   }\r
+                   /* [HGM] here we really should compare with the maximum supported board size */\r
+               }\r
+           }\r
+      } else sprintf(b, "%s", VariantName(gameInfo.variant));\r
+      sprintf(buf, "variant %s\n", b);\r
+      SendToProgram(buf, cps);\r
+    }\r
+    currentlyInitializedVariant = gameInfo.variant;\r
+\r
+    /* [HGM] send opening position in FRC to first engine */\r
+    if(setup) {\r
+          SendToProgram("force\n", cps);\r
+          SendBoard(cps, 0);\r
+          /* engine is now in force mode! Set flag to wake it up after first move. */\r
+          setboardSpoiledMachineBlack = 1;\r
+    }\r
+\r
+    if (cps->sendICS) {\r
+      sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
+      SendToProgram(buf, cps);\r
+    }\r
+    cps->maybeThinking = FALSE;\r
+    cps->offeredDraw = 0;\r
+    if (!appData.icsActive) {\r
+       SendTimeControl(cps, movesPerSession, timeControl,\r
+                       timeIncrement, appData.searchDepth,\r
+                       searchTime);\r
+    }\r
+    if (appData.showThinking \r
+       // [HGM] thinking: four options require thinking output to be sent\r
+       || !appData.hideThinkingFromHuman || appData.adjudicateLossThreshold != 0 || EngineOutputIsUp()\r
+                               ) {\r
+       SendToProgram("post\n", cps);\r
+    }\r
+    SendToProgram("hard\n", cps);\r
+    if (!appData.ponderNextMove) {\r
+       /* Warning: "easy" is a toggle in GNU Chess, so don't send\r
+          it without being sure what state we are in first.  "hard"\r
+          is not a toggle, so that one is OK.\r
+        */\r
+       SendToProgram("easy\n", cps);\r
+    }\r
+    if (cps->usePing) {\r
+      sprintf(buf, "ping %d\n", ++cps->lastPing);\r
+      SendToProgram(buf, cps);\r
+    }\r
+    cps->initDone = TRUE;\r
+}   \r
+\r
+\r
+void\r
+StartChessProgram(cps)\r
+     ChessProgramState *cps;\r
+{\r
+    char buf[MSG_SIZ];\r
+    int err;\r
+\r
+    if (appData.noChessProgram) return;\r
+    cps->initDone = FALSE;\r
+\r
+    if (strcmp(cps->host, "localhost") == 0) {\r
+       err = StartChildProcess(cps->program, cps->dir, &cps->pr);\r
+    } else if (*appData.remoteShell == NULLCHAR) {\r
+       err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);\r
+    } else {\r
+       if (*appData.remoteUser == NULLCHAR) {\r
+           sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,\r
+                   cps->program);\r
+       } else {\r
+           sprintf(buf, "%s %s -l %s %s", appData.remoteShell,\r
+                   cps->host, appData.remoteUser, cps->program);\r
+       }\r
+       err = StartChildProcess(buf, "", &cps->pr);\r
+    }\r
+    \r
+    if (err != 0) {\r
+       sprintf(buf, _("Startup failure on '%s'"), cps->program);\r
+       DisplayFatalError(buf, err, 1);\r
+       cps->pr = NoProc;\r
+       cps->isr = NULL;\r
+       return;\r
+    }\r
+    \r
+    cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);\r
+    if (cps->protocolVersion > 1) {\r
+      sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);\r
+      cps->nrOptions = 0; // [HGM] options: clear all engine-specific options\r
+      cps->comboCnt = 0;  //                and values of combo boxes\r
+      SendToProgram(buf, cps);\r
+    } else {\r
+      SendToProgram("xboard\n", cps);\r
+    }\r
+}\r
+\r
+\r
+void\r
+TwoMachinesEventIfReady P((void))\r
+{\r
+  if (first.lastPing != first.lastPong) {\r
+    DisplayMessage("", _("Waiting for first chess program"));\r
+    ScheduleDelayedEvent(TwoMachinesEventIfReady, 10); // [HGM] fast: lowered from 1000\r
+    return;\r
+  }\r
+  if (second.lastPing != second.lastPong) {\r
+    DisplayMessage("", _("Waiting for second chess program"));\r
+    ScheduleDelayedEvent(TwoMachinesEventIfReady, 10); // [HGM] fast: lowered from 1000\r
+    return;\r
+  }\r
+  ThawUI();\r
+  TwoMachinesEvent();\r
+}\r
+\r
+void\r
+NextMatchGame P((void))\r
+{\r
+    int index; /* [HGM] autoinc: step lod index during match */\r
+    Reset(FALSE, TRUE);\r
+    if (*appData.loadGameFile != NULLCHAR) {\r
+       index = appData.loadGameIndex;\r
+       if(index < 0) { // [HGM] autoinc\r
+           lastIndex = index = (index == -2 && first.twoMachinesColor[0] == 'b') ? lastIndex : lastIndex+1;\r
+           if(appData.rewindIndex > 0 && index > appData.rewindIndex) lastIndex = index = 1;\r
+       } \r
+       LoadGameFromFile(appData.loadGameFile,\r
+                        index,\r
+                        appData.loadGameFile, FALSE);\r
+    } else if (*appData.loadPositionFile != NULLCHAR) {\r
+       index = appData.loadPositionIndex;\r
+       if(index < 0) { // [HGM] autoinc\r
+           lastIndex = index = (index == -2 && first.twoMachinesColor[0] == 'b') ? lastIndex : lastIndex+1;\r
+           if(appData.rewindIndex > 0 && index > appData.rewindIndex) lastIndex = index = 1;\r
+       } \r
+       LoadPositionFromFile(appData.loadPositionFile,\r
+                            index,\r
+                            appData.loadPositionFile);\r
+    }\r
+    TwoMachinesEventIfReady();\r
+}\r
+\r
+void UserAdjudicationEvent( int result )\r
+{\r
+    ChessMove gameResult = GameIsDrawn;\r
+\r
+    if( result > 0 ) {\r
+        gameResult = WhiteWins;\r
+    }\r
+    else if( result < 0 ) {\r
+        gameResult = BlackWins;\r
+    }\r
+\r
+    if( gameMode == TwoMachinesPlay ) {\r
+        GameEnds( gameResult, "User adjudication", GE_XBOARD );\r
+    }\r
+}\r
+\r
+\r
+void\r
+GameEnds(result, resultDetails, whosays)\r
+     ChessMove result;\r
+     char *resultDetails;\r
+     int whosays;\r
+{\r
+    GameMode nextGameMode;\r
+    int isIcsGame;\r
+    char buf[MSG_SIZ];\r
+\r
+    if(endingGame) return; /* [HGM] crash: forbid recursion */\r
+    endingGame = 1;\r
+\r
+    if (appData.debugMode) {\r
+      fprintf(debugFP, "GameEnds(%d, %s, %d)\n",\r
+             result, resultDetails ? resultDetails : "(null)", whosays);\r
+    }\r
+\r
+    if (appData.icsActive && (whosays == GE_ENGINE || whosays >= GE_ENGINE1)) {\r
+       /* If we are playing on ICS, the server decides when the\r
+          game is over, but the engine can offer to draw, claim \r
+          a draw, or resign. \r
+        */\r
+#if ZIPPY\r
+       if (appData.zippyPlay && first.initDone) {\r
+           if (result == GameIsDrawn) {\r
+               /* In case draw still needs to be claimed */\r
+               SendToICS(ics_prefix);\r
+               SendToICS("draw\n");\r
+           } else if (StrCaseStr(resultDetails, "resign")) {\r
+               SendToICS(ics_prefix);\r
+               SendToICS("resign\n");\r
+           }\r
+        }\r
+#endif\r
+       endingGame = 0; /* [HGM] crash */\r
+        return;\r
+    }\r
+\r
+    /* If we're loading the game from a file, stop */\r
+    if (whosays == GE_FILE) {\r
+      (void) StopLoadGameTimer();\r
+      gameFileFP = NULL;\r
+    }\r
+\r
+    /* Cancel draw offers */\r
+    first.offeredDraw = second.offeredDraw = 0;\r
+\r
+    /* If this is an ICS game, only ICS can really say it's done;\r
+       if not, anyone can. */\r
+    isIcsGame = (gameMode == IcsPlayingWhite || \r
+                gameMode == IcsPlayingBlack || \r
+                gameMode == IcsObserving    || \r
+                gameMode == IcsExamining);\r
+\r
+    if (!isIcsGame || whosays == GE_ICS) {\r
+       /* OK -- not an ICS game, or ICS said it was done */\r
+       StopClocks();\r
+       if (!isIcsGame && !appData.noChessProgram) \r
+         SetUserThinkingEnables();\r
+    \r
+        /* [HGM] if a machine claims the game end we verify this claim */\r
+        if(gameMode == TwoMachinesPlay && appData.testClaims) {\r
+           if(appData.testLegality && whosays >= GE_ENGINE1 ) {\r
+                char claimer;\r
+\r
+                claimer = whosays == GE_ENGINE1 ?      /* color of claimer */\r
+                                            first.twoMachinesColor[0] :\r
+                                            second.twoMachinesColor[0] ;\r
+                if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) &&\r
+                    (result == WhiteWins && claimer == 'w' ||\r
+                     result == BlackWins && claimer == 'b'   ) ) {\r
+               if (appData.debugMode) {\r
+                    fprintf(debugFP, "result=%d sp=%d move=%d\n",\r
+                       result, epStatus[forwardMostMove], forwardMostMove);\r
+               }\r
+                      /* [HGM] verify: engine mate claims accepted if they were flagged */\r
+                     if(epStatus[forwardMostMove] != EP_CHECKMATE &&\r
+                        result != (WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins)) {\r
+                             sprintf(buf, "False win claim: '%s'", resultDetails);\r
+                             result = claimer == 'w' ? BlackWins : WhiteWins;\r
+                             resultDetails = buf;\r
+                     }\r
+                } else\r
+                if( result == GameIsDrawn && epStatus[forwardMostMove] > EP_DRAWS\r
+                    && (forwardMostMove <= backwardMostMove ||\r
+                        epStatus[forwardMostMove-1] > EP_DRAWS ||\r
+                        (claimer=='b')==(forwardMostMove&1))\r
+                                                                                  ) {\r
+                      /* [HGM] verify: draws that were not flagged are false claims */\r
+                      sprintf(buf, "False draw claim: '%s'", resultDetails);\r
+                      result = claimer == 'w' ? BlackWins : WhiteWins;\r
+                      resultDetails = buf;\r
+                }\r
+                /* (Claiming a loss is accepted no questions asked!) */\r
+           }\r
+           /* [HGM] bare: don't allow bare King to win */\r
+           if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
+                        && result != GameIsDrawn)\r
+           {   int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);\r
+               for(j=BOARD_LEFT; j<BOARD_RGHT; j++) for(i=0; i<BOARD_HEIGHT; i++) {\r
+                       int p = (int)boards[forwardMostMove][i][j] - color;\r
+                       if(p >= 0 && p <= (int)WhiteKing) k++;\r
+               }\r
+               if (appData.debugMode) {\r
+                    fprintf(debugFP, "GE(%d, %s, %d) bare king k=%d color=%d\n",\r
+                       result, resultDetails ? resultDetails : "(null)", whosays, k, color);\r
+               }\r
+               if(k <= 1) {\r
+                       result = GameIsDrawn;\r
+                       sprintf(buf, "%s but bare king", resultDetails);\r
+                       resultDetails = buf;\r
+               }\r
+           }\r
+        }\r
+\r
+\r
+        if(serverMoves != NULL && !loadFlag) { char c = '=';\r
+            if(result==WhiteWins) c = '+';\r
+            if(result==BlackWins) c = '-';\r
+            if(resultDetails != NULL)\r
+                fprintf(serverMoves, ";%c;%s\n", c, resultDetails);\r
+        }\r
+       if (resultDetails != NULL) {\r
+           gameInfo.result = result;\r
+           gameInfo.resultDetails = StrSave(resultDetails);\r
+\r
+           /* display last move only if game was not loaded from file */\r
+           if ((whosays != GE_FILE) && (currentMove == forwardMostMove))\r
+               DisplayMove(currentMove - 1);\r
+    \r
+           if (forwardMostMove != 0) {\r
+               if (gameMode != PlayFromGameFile && gameMode != EditGame) {\r
+                   if (*appData.saveGameFile != NULLCHAR) {\r
+                       SaveGameToFile(appData.saveGameFile, TRUE);\r
+                   } else if (appData.autoSaveGames) {\r
+                       AutoSaveGame();\r
+                   }\r
+                   if (*appData.savePositionFile != NULLCHAR) {\r
+                       SavePositionToFile(appData.savePositionFile);\r
+                   }\r
+               }\r
+           }\r
+\r
+           /* Tell program how game ended in case it is learning */\r
+            /* [HGM] Moved this to after saving the PGN, just in case */\r
+            /* engine died and we got here through time loss. In that */\r
+            /* case we will get a fatal error writing the pipe, which */\r
+            /* would otherwise lose us the PGN.                       */\r
+            /* [HGM] crash: not needed anymore, but doesn't hurt;     */\r
+            /* output during GameEnds should never be fatal anymore   */\r
+           if (gameMode == MachinePlaysWhite ||\r
+               gameMode == MachinePlaysBlack ||\r
+               gameMode == TwoMachinesPlay ||\r
+               gameMode == IcsPlayingWhite ||\r
+               gameMode == IcsPlayingBlack ||\r
+               gameMode == BeginningOfGame) {\r
+               char buf[MSG_SIZ];\r
+               sprintf(buf, "result %s {%s}\n", PGNResult(result),\r
+                       resultDetails);\r
+               if (first.pr != NoProc) {\r
+                   SendToProgram(buf, &first);\r
+               }\r
+               if (second.pr != NoProc &&\r
+                   gameMode == TwoMachinesPlay) {\r
+                   SendToProgram(buf, &second);\r
+               }\r
+           }\r
+       }\r
+\r
+       if (appData.icsActive) {\r
+           if (appData.quietPlay &&\r
+               (gameMode == IcsPlayingWhite ||\r
+                gameMode == IcsPlayingBlack)) {\r
+               SendToICS(ics_prefix);\r
+               SendToICS("set shout 1\n");\r
+           }\r
+           nextGameMode = IcsIdle;\r
+           ics_user_moved = FALSE;\r
+           /* clean up premove.  It's ugly when the game has ended and the\r
+            * premove highlights are still on the board.\r
+            */\r
+           if (gotPremove) {\r
+             gotPremove = FALSE;\r
+             ClearPremoveHighlights();\r
+             DrawPosition(FALSE, boards[currentMove]);\r
+           }\r
+           if (whosays == GE_ICS) {\r
+               switch (result) {\r
+               case WhiteWins:\r
+                   if (gameMode == IcsPlayingWhite)\r
+                       PlayIcsWinSound();\r
+                   else if(gameMode == IcsPlayingBlack)\r
+                       PlayIcsLossSound();\r
+                   break;\r
+               case BlackWins:\r
+                   if (gameMode == IcsPlayingBlack)\r
+                       PlayIcsWinSound();\r
+                   else if(gameMode == IcsPlayingWhite)\r
+                       PlayIcsLossSound();\r
+                   break;\r
+               case GameIsDrawn:\r
+                   PlayIcsDrawSound();\r
+                   break;\r
+               default:\r
+                   PlayIcsUnfinishedSound();\r
+               }\r
+           }\r
+       } else if (gameMode == EditGame ||\r
+                  gameMode == PlayFromGameFile || \r
+                  gameMode == AnalyzeMode || \r
+                  gameMode == AnalyzeFile) {\r
+           nextGameMode = gameMode;\r
+       } else {\r
+           nextGameMode = EndOfGame;\r
+       }\r
+       pausing = FALSE;\r
+       ModeHighlight();\r
+    } else {\r
+       nextGameMode = gameMode;\r
+    }\r
+\r
+    if (appData.noChessProgram) {\r
+       gameMode = nextGameMode;\r
+       ModeHighlight();\r
+       endingGame = 0; /* [HGM] crash */\r
+        return;\r
+    }\r
+\r
+    if (first.reuse) {\r
+       /* Put first chess program into idle state */\r
+       if (first.pr != NoProc &&\r
+           (gameMode == MachinePlaysWhite ||\r
+            gameMode == MachinePlaysBlack ||\r
+            gameMode == TwoMachinesPlay ||\r
+            gameMode == IcsPlayingWhite ||\r
+            gameMode == IcsPlayingBlack ||\r
+            gameMode == BeginningOfGame)) {\r
+           SendToProgram("force\n", &first);\r
+           if (first.usePing) {\r
+             char buf[MSG_SIZ];\r
+             sprintf(buf, "ping %d\n", ++first.lastPing);\r
+             SendToProgram(buf, &first);\r
+           }\r
+       }\r
+    } else if (result != GameUnfinished || nextGameMode == IcsIdle) {\r
+       /* Kill off first chess program */\r
+       if (first.isr != NULL)\r
+         RemoveInputSource(first.isr);\r
+       first.isr = NULL;\r
+    \r
+       if (first.pr != NoProc) {\r
+           ExitAnalyzeMode();\r
+            DoSleep( appData.delayBeforeQuit );\r
+           SendToProgram("quit\n", &first);\r
+            DoSleep( appData.delayAfterQuit );\r
+           DestroyChildProcess(first.pr, first.useSigterm);\r
+       }\r
+       first.pr = NoProc;\r
+    }\r
+    if (second.reuse) {\r
+       /* Put second chess program into idle state */\r
+       if (second.pr != NoProc &&\r
+           gameMode == TwoMachinesPlay) {\r
+           SendToProgram("force\n", &second);\r
+           if (second.usePing) {\r
+             char buf[MSG_SIZ];\r
+             sprintf(buf, "ping %d\n", ++second.lastPing);\r
+             SendToProgram(buf, &second);\r
+           }\r
+       }\r
+    } else if (result != GameUnfinished || nextGameMode == IcsIdle) {\r
+       /* Kill off second chess program */\r
+       if (second.isr != NULL)\r
+         RemoveInputSource(second.isr);\r
+       second.isr = NULL;\r
+    \r
+       if (second.pr != NoProc) {\r
+            DoSleep( appData.delayBeforeQuit );\r
+           SendToProgram("quit\n", &second);\r
+            DoSleep( appData.delayAfterQuit );\r
+           DestroyChildProcess(second.pr, second.useSigterm);\r
+       }\r
+       second.pr = NoProc;\r
+    }\r
+\r
+    if (matchMode && gameMode == TwoMachinesPlay) {\r
+        switch (result) {\r
+       case WhiteWins:\r
+         if (first.twoMachinesColor[0] == 'w') {\r
+           first.matchWins++;\r
+         } else {\r
+           second.matchWins++;\r
+         }\r
+         break;\r
+       case BlackWins:\r
+         if (first.twoMachinesColor[0] == 'b') {\r
+           first.matchWins++;\r
+         } else {\r
+           second.matchWins++;\r
+         }\r
+         break;\r
+       default:\r
+         break;\r
+       }\r
+       if (matchGame < appData.matchGames) {\r
+           char *tmp;\r
+           if(appData.sameColorGames <= 1) { /* [HGM] alternate: suppress color swap */\r
+               tmp = first.twoMachinesColor;\r
+               first.twoMachinesColor = second.twoMachinesColor;\r
+               second.twoMachinesColor = tmp;\r
+           }\r
+           gameMode = nextGameMode;\r
+           matchGame++;\r
+            if(appData.matchPause>10000 || appData.matchPause<10)\r
+                appData.matchPause = 10000; /* [HGM] make pause adjustable */\r
+            ScheduleDelayedEvent(NextMatchGame, appData.matchPause);\r
+           endingGame = 0; /* [HGM] crash */\r
+           return;\r
+       } else {\r
+           char buf[MSG_SIZ];\r
+           gameMode = nextGameMode;\r
+           sprintf(buf, _("Match %s vs. %s: final score %d-%d-%d"),\r
+                   first.tidy, second.tidy,\r
+                   first.matchWins, second.matchWins,\r
+                   appData.matchGames - (first.matchWins + second.matchWins));\r
+           DisplayFatalError(buf, 0, 0);\r
+       }\r
+    }\r
+    if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&\r
+       !(nextGameMode == AnalyzeMode || nextGameMode == AnalyzeFile))\r
+      ExitAnalyzeMode();\r
+    gameMode = nextGameMode;\r
+    ModeHighlight();\r
+    endingGame = 0;  /* [HGM] crash */\r
+}\r
+\r
+/* Assumes program was just initialized (initString sent).\r
+   Leaves program in force mode. */\r
+void\r
+FeedMovesToProgram(cps, upto) \r
+     ChessProgramState *cps;\r
+     int upto;\r
+{\r
+    int i;\r
+    \r
+    if (appData.debugMode)\r
+      fprintf(debugFP, "Feeding %smoves %d through %d to %s chess program\n",\r
+             startedFromSetupPosition ? "position and " : "",\r
+             backwardMostMove, upto, cps->which);\r
+    if(currentlyInitializedVariant != gameInfo.variant) { char buf[MSG_SIZ];\r
+        // [HGM] variantswitch: make engine aware of new variant\r
+       if(cps->protocolVersion > 1 && StrStr(cps->variants, VariantName(gameInfo.variant)) == NULL)\r
+               return; // [HGM] refrain from feeding moves altogether if variant is unsupported!\r
+       sprintf(buf, "variant %s\n", VariantName(gameInfo.variant));\r
+       SendToProgram(buf, cps);\r
+        currentlyInitializedVariant = gameInfo.variant;\r
+    }\r
+    SendToProgram("force\n", cps);\r
+    if (startedFromSetupPosition) {\r
+       SendBoard(cps, backwardMostMove);\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "feedMoves\n");\r
+    }\r
+    }\r
+    for (i = backwardMostMove; i < upto; i++) {\r
+       SendMoveToProgram(i, cps);\r
+    }\r
+}\r
+\r
+\r
+void\r
+ResurrectChessProgram()\r
+{\r
+     /* The chess program may have exited.\r
+       If so, restart it and feed it all the moves made so far. */\r
+\r
+    if (appData.noChessProgram || first.pr != NoProc) return;\r
+    \r
+    StartChessProgram(&first);\r
+    InitChessProgram(&first, FALSE);\r
+    FeedMovesToProgram(&first, currentMove);\r
+\r
+    if (!first.sendTime) {\r
+       /* can't tell gnuchess what its clock should read,\r
+          so we bow to its notion. */\r
+       ResetClocks();\r
+       timeRemaining[0][currentMove] = whiteTimeRemaining;\r
+       timeRemaining[1][currentMove] = blackTimeRemaining;\r
+    }\r
+\r
+    if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile ||\r
+                appData.icsEngineAnalyze) && first.analysisSupport) {\r
+      SendToProgram("analyze\n", &first);\r
+      first.analyzing = TRUE;\r
+    }\r
+}\r
+\r
+/*\r
+ * Button procedures\r
+ */\r
+void\r
+Reset(redraw, init)\r
+     int redraw, init;\r
+{\r
+    int i;\r
+\r
+    if (appData.debugMode) {\r
+       fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",\r
+               redraw, init, gameMode);\r
+    }\r
+    pausing = pauseExamInvalid = FALSE;\r
+    startedFromSetupPosition = blackPlaysFirst = FALSE;\r
+    firstMove = TRUE;\r
+    whiteFlag = blackFlag = FALSE;\r
+    userOfferedDraw = FALSE;\r
+    hintRequested = bookRequested = FALSE;\r
+    first.maybeThinking = FALSE;\r
+    second.maybeThinking = FALSE;\r
+    first.bookSuspend = FALSE; // [HGM] book\r
+    second.bookSuspend = FALSE;\r
+    thinkOutput[0] = NULLCHAR;\r
+    lastHint[0] = NULLCHAR;\r
+    ClearGameInfo(&gameInfo);\r
+    gameInfo.variant = StringToVariant(appData.variant);\r
+    ics_user_moved = ics_clock_paused = FALSE;\r
+    ics_getting_history = H_FALSE;\r
+    ics_gamenum = -1;\r
+    white_holding[0] = black_holding[0] = NULLCHAR;\r
+    ClearProgramStats();\r
+    opponentKibitzes = FALSE; // [HGM] kibitz: do not reserve space in engine-output window in zippy mode\r
+    \r
+    ResetFrontEnd();\r
+    ClearHighlights();\r
+    flipView = appData.flipView;\r
+    ClearPremoveHighlights();\r
+    gotPremove = FALSE;\r
+    alarmSounded = FALSE;\r
+\r
+    GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
+    if(appData.serverMovesName != NULL) {\r
+        /* [HGM] prepare to make moves file for broadcasting */\r
+        clock_t t = clock();\r
+        if(serverMoves != NULL) fclose(serverMoves);\r
+        serverMoves = fopen(appData.serverMovesName, "r");\r
+        if(serverMoves != NULL) {\r
+            fclose(serverMoves);\r
+            /* delay 15 sec before overwriting, so all clients can see end */\r
+            while(clock()-t < appData.serverPause*CLOCKS_PER_SEC);\r
+        }\r
+        serverMoves = fopen(appData.serverMovesName, "w");\r
+    }\r
+\r
+    ExitAnalyzeMode();\r
+    gameMode = BeginningOfGame;\r
+    ModeHighlight();\r
+    if(appData.icsActive) gameInfo.variant = VariantNormal;\r
+    InitPosition(redraw);\r
+    for (i = 0; i < MAX_MOVES; i++) {\r
+       if (commentList[i] != NULL) {\r
+           free(commentList[i]);\r
+           commentList[i] = NULL;\r
+       }\r
+    }\r
+    ResetClocks();\r
+    timeRemaining[0][0] = whiteTimeRemaining;\r
+    timeRemaining[1][0] = blackTimeRemaining;\r
+    if (first.pr == NULL) {\r
+       StartChessProgram(&first);\r
+    }\r
+    if (init) {\r
+           InitChessProgram(&first, startedFromSetupPosition);\r
+    }\r
+    DisplayTitle("");\r
+    DisplayMessage("", "");\r
+    HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+}\r
+\r
+void\r
+AutoPlayGameLoop()\r
+{\r
+    for (;;) {\r
+       if (!AutoPlayOneMove())\r
+         return;\r
+       if (matchMode || appData.timeDelay == 0)\r
+         continue;\r
+       if (appData.timeDelay < 0 || gameMode == AnalyzeFile)\r
+         return;\r
+       StartLoadGameTimer((long)(1000.0 * appData.timeDelay));\r
+       break;\r
+    }\r
+}\r
+\r
+\r
+int\r
+AutoPlayOneMove()\r
+{\r
+    int fromX, fromY, toX, toY;\r
+\r
+    if (appData.debugMode) {\r
+      fprintf(debugFP, "AutoPlayOneMove(): current %d\n", currentMove);\r
+    }\r
+\r
+    if (gameMode != PlayFromGameFile)\r
+      return FALSE;\r
+\r
+    if (currentMove >= forwardMostMove) {\r
+      gameMode = EditGame;\r
+      ModeHighlight();\r
+\r
+      /* [AS] Clear current move marker at the end of a game */\r
+      /* HistorySet(parseList, backwardMostMove, forwardMostMove, -1); */\r
+\r
+      return FALSE;\r
+    }\r
+    \r
+    toX = moveList[currentMove][2] - AAA;\r
+    toY = moveList[currentMove][3] - ONE;\r
+\r
+    if (moveList[currentMove][1] == '@') {\r
+       if (appData.highlightLastMove) {\r
+           SetHighlights(-1, -1, toX, toY);\r
+       }\r
+    } else {\r
+        fromX = moveList[currentMove][0] - AAA;\r
+        fromY = moveList[currentMove][1] - ONE;\r
+\r
+        HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */\r
+\r
+       AnimateMove(boards[currentMove], fromX, fromY, toX, toY);\r
+\r
+       if (appData.highlightLastMove) {\r
+           SetHighlights(fromX, fromY, toX, toY);\r
+       }\r
+    }\r
+    DisplayMove(currentMove);\r
+    SendMoveToProgram(currentMove++, &first);\r
+    DisplayBothClocks();\r
+    DrawPosition(FALSE, boards[currentMove]);\r
+    // [HGM] PV info: always display, routine tests if empty\r
+    DisplayComment(currentMove - 1, commentList[currentMove]);\r
+    return TRUE;\r
+}\r
+\r
+\r
+int\r
+LoadGameOneMove(readAhead)\r
+     ChessMove readAhead;\r
+{\r
+    int fromX = 0, fromY = 0, toX = 0, toY = 0, done;\r
+    char promoChar = NULLCHAR;\r
+    ChessMove moveType;\r
+    char move[MSG_SIZ];\r
+    char *p, *q;\r
+    \r
+    if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile && \r
+       gameMode != AnalyzeMode && gameMode != Training) {\r
+       gameFileFP = NULL;\r
+       return FALSE;\r
+    }\r
+    \r
+    yyboardindex = forwardMostMove;\r
+    if (readAhead != (ChessMove)0) {\r
+      moveType = readAhead;\r
+    } else {\r
+      if (gameFileFP == NULL)\r
+         return FALSE;\r
+      moveType = (ChessMove) yylex();\r
+    }\r
+    \r
+    done = FALSE;\r
+    switch (moveType) {\r
+      case Comment:\r
+       if (appData.debugMode) \r
+         fprintf(debugFP, "Parsed Comment: %s\n", yy_text);\r
+       p = yy_text;\r
+       if (*p == '{' || *p == '[' || *p == '(') {\r
+           p[strlen(p) - 1] = NULLCHAR;\r
+           p++;\r
+       }\r
+\r
+       /* append the comment but don't display it */\r
+       while (*p == '\n') p++;\r
+       AppendComment(currentMove, p);\r
+       return TRUE;\r
+\r
+      case WhiteCapturesEnPassant:\r
+      case BlackCapturesEnPassant:\r
+      case WhitePromotionChancellor:\r
+      case BlackPromotionChancellor:\r
+      case WhitePromotionArchbishop:\r
+      case BlackPromotionArchbishop:\r
+      case WhitePromotionCentaur:\r
+      case BlackPromotionCentaur:\r
+      case WhitePromotionQueen:\r
+      case BlackPromotionQueen:\r
+      case WhitePromotionRook:\r
+      case BlackPromotionRook:\r
+      case WhitePromotionBishop:\r
+      case BlackPromotionBishop:\r
+      case WhitePromotionKnight:\r
+      case BlackPromotionKnight:\r
+      case WhitePromotionKing:\r
+      case BlackPromotionKing:\r
+      case NormalMove:\r
+      case WhiteKingSideCastle:\r
+      case WhiteQueenSideCastle:\r
+      case BlackKingSideCastle:\r
+      case BlackQueenSideCastle:\r
+      case WhiteKingSideCastleWild:\r
+      case WhiteQueenSideCastleWild:\r
+      case BlackKingSideCastleWild:\r
+      case BlackQueenSideCastleWild:\r
+      /* PUSH Fabien */\r
+      case WhiteHSideCastleFR:\r
+      case WhiteASideCastleFR:\r
+      case BlackHSideCastleFR:\r
+      case BlackASideCastleFR:\r
+      /* POP Fabien */\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);\r
+        fromX = currentMoveString[0] - AAA;\r
+        fromY = currentMoveString[1] - ONE;\r
+        toX = currentMoveString[2] - AAA;\r
+        toY = currentMoveString[3] - ONE;\r
+       promoChar = currentMoveString[4];\r
+       break;\r
+\r
+      case WhiteDrop:\r
+      case BlackDrop:\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);\r
+       fromX = moveType == WhiteDrop ?\r
+         (int) CharToPiece(ToUpper(currentMoveString[0])) :\r
+       (int) CharToPiece(ToLower(currentMoveString[0]));\r
+       fromY = DROP_RANK;\r
+        toX = currentMoveString[2] - AAA;\r
+        toY = currentMoveString[3] - ONE;\r
+       break;\r
+\r
+      case WhiteWins:\r
+      case BlackWins:\r
+      case GameIsDrawn:\r
+      case GameUnfinished:\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Parsed game end: %s\n", yy_text);\r
+       p = strchr(yy_text, '{');\r
+       if (p == NULL) p = strchr(yy_text, '(');\r
+       if (p == NULL) {\r
+           p = yy_text;\r
+           if (p[0] == '0' || p[0] == '1' || p[0] == '*') p = "";\r
+       } else {\r
+           q = strchr(p, *p == '{' ? '}' : ')');\r
+           if (q != NULL) *q = NULLCHAR;\r
+           p++;\r
+       }\r
+       GameEnds(moveType, p, GE_FILE);\r
+       done = TRUE;\r
+       if (cmailMsgLoaded) {\r
+           ClearHighlights();\r
+           flipView = WhiteOnMove(currentMove);\r
+           if (moveType == GameUnfinished) flipView = !flipView;\r
+           if (appData.debugMode)\r
+             fprintf(debugFP, "Setting flipView to %d\n", flipView) ;\r
+       }\r
+       break;\r
+\r
+      case (ChessMove) 0:      /* end of file */\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Parser hit end of file\n");\r
+       switch (MateTest(boards[currentMove], PosFlags(currentMove),\r
+                         EP_UNKNOWN, castlingRights[currentMove]) ) {\r
+         case MT_NONE:\r
+         case MT_CHECK:\r
+           break;\r
+         case MT_CHECKMATE:\r
+           if (WhiteOnMove(currentMove)) {\r
+               GameEnds(BlackWins, "Black mates", GE_FILE);\r
+           } else {\r
+               GameEnds(WhiteWins, "White mates", GE_FILE);\r
+           }\r
+           break;\r
+         case MT_STALEMATE:\r
+           GameEnds(GameIsDrawn, "Stalemate", GE_FILE);\r
+           break;\r
+       }\r
+       done = TRUE;\r
+       break;\r
+\r
+      case MoveNumberOne:\r
+       if (lastLoadGameStart == GNUChessGame) {\r
+           /* GNUChessGames have numbers, but they aren't move numbers */\r
+           if (appData.debugMode)\r
+             fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",\r
+                     yy_text, (int) moveType);\r
+           return LoadGameOneMove((ChessMove)0); /* tail recursion */\r
+       }\r
+       /* else fall thru */\r
+\r
+      case XBoardGame:\r
+      case GNUChessGame:\r
+      case PGNTag:\r
+       /* Reached start of next game in file */\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Parsed start of next game: %s\n", yy_text);\r
+       switch (MateTest(boards[currentMove], PosFlags(currentMove),\r
+                         EP_UNKNOWN, castlingRights[currentMove]) ) {\r
+         case MT_NONE:\r
+         case MT_CHECK:\r
+           break;\r
+         case MT_CHECKMATE:\r
+           if (WhiteOnMove(currentMove)) {\r
+               GameEnds(BlackWins, "Black mates", GE_FILE);\r
+           } else {\r
+               GameEnds(WhiteWins, "White mates", GE_FILE);\r
+           }\r
+           break;\r
+         case MT_STALEMATE:\r
+           GameEnds(GameIsDrawn, "Stalemate", GE_FILE);\r
+           break;\r
+       }\r
+       done = TRUE;\r
+       break;\r
+\r
+      case PositionDiagram:    /* should not happen; ignore */\r
+      case ElapsedTime:                /* ignore */\r
+      case NAG:                 /* ignore */\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",\r
+                 yy_text, (int) moveType);\r
+       return LoadGameOneMove((ChessMove)0); /* tail recursion */\r
+\r
+      case IllegalMove:\r
+       if (appData.testLegality) {\r
+           if (appData.debugMode)\r
+             fprintf(debugFP, "Parsed IllegalMove: %s\n", yy_text);\r
+           sprintf(move, _("Illegal move: %d.%s%s"),\r
+                   (forwardMostMove / 2) + 1,\r
+                   WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
+           DisplayError(move, 0);\r
+           done = TRUE;\r
+       } else {\r
+           if (appData.debugMode)\r
+             fprintf(debugFP, "Parsed %s into IllegalMove %s\n",\r
+                     yy_text, currentMoveString);\r
+            fromX = currentMoveString[0] - AAA;\r
+            fromY = currentMoveString[1] - ONE;\r
+            toX = currentMoveString[2] - AAA;\r
+            toY = currentMoveString[3] - ONE;\r
+           promoChar = currentMoveString[4];\r
+       }\r
+       break;\r
+\r
+      case AmbiguousMove:\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yy_text);\r
+       sprintf(move, _("Ambiguous move: %d.%s%s"),\r
+               (forwardMostMove / 2) + 1,\r
+               WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
+       DisplayError(move, 0);\r
+       done = TRUE;\r
+       break;\r
+\r
+      default:\r
+      case ImpossibleMove:\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Parsed ImpossibleMove (type = %d): %s\n", moveType, yy_text);\r
+       sprintf(move, _("Illegal move: %d.%s%s"),\r
+               (forwardMostMove / 2) + 1,\r
+               WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
+       DisplayError(move, 0);\r
+       done = TRUE;\r
+       break;\r
+    }\r
+\r
+    if (done) {\r
+       if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {\r
+           DrawPosition(FALSE, boards[currentMove]);\r
+           DisplayBothClocks();\r
+            if (!appData.matchMode) // [HGM] PV info: routine tests if empty\r
+             DisplayComment(currentMove - 1, commentList[currentMove]);\r
+       }\r
+       (void) StopLoadGameTimer();\r
+       gameFileFP = NULL;\r
+       cmailOldMove = forwardMostMove;\r
+       return FALSE;\r
+    } else {\r
+       /* currentMoveString is set as a side-effect of yylex */\r
+       strcat(currentMoveString, "\n");\r
+       strcpy(moveList[forwardMostMove], currentMoveString);\r
+       \r
+       thinkOutput[0] = NULLCHAR;\r
+       MakeMove(fromX, fromY, toX, toY, promoChar);\r
+       currentMove = forwardMostMove;\r
+       return TRUE;\r
+    }\r
+}\r
+\r
+/* Load the nth game from the given file */\r
+int\r
+LoadGameFromFile(filename, n, title, useList)\r
+     char *filename;\r
+     int n;\r
+     char *title;\r
+     /*Boolean*/ int useList;\r
+{\r
+    FILE *f;\r
+    char buf[MSG_SIZ];\r
+\r
+    if (strcmp(filename, "-") == 0) {\r
+       f = stdin;\r
+       title = "stdin";\r
+    } else {\r
+       f = fopen(filename, "rb");\r
+       if (f == NULL) {\r
+           sprintf(buf, _("Can't open \"%s\""), filename);\r
+           DisplayError(buf, errno);\r
+           return FALSE;\r
+       }\r
+    }\r
+    if (fseek(f, 0, 0) == -1) {\r
+       /* f is not seekable; probably a pipe */\r
+       useList = FALSE;\r
+    }\r
+    if (useList && n == 0) {\r
+       int error = GameListBuild(f);\r
+       if (error) {\r
+           DisplayError(_("Cannot build game list"), error);\r
+       } else if (!ListEmpty(&gameList) &&\r
+                  ((ListGame *) gameList.tailPred)->number > 1) {\r
+           GameListPopUp(f, title);\r
+           return TRUE;\r
+       }\r
+       GameListDestroy();\r
+       n = 1;\r
+    }\r
+    if (n == 0) n = 1;\r
+    return LoadGame(f, n, title, FALSE);\r
+}\r
+\r
+\r
+void\r
+MakeRegisteredMove()\r
+{\r
+    int fromX, fromY, toX, toY;\r
+    char promoChar;\r
+    if (cmailMoveRegistered[lastLoadGameNumber - 1]) {\r
+       switch (cmailMoveType[lastLoadGameNumber - 1]) {\r
+         case CMAIL_MOVE:\r
+         case CMAIL_DRAW:\r
+           if (appData.debugMode)\r
+             fprintf(debugFP, "Restoring %s for game %d\n",\r
+                     cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);\r
+    \r
+           thinkOutput[0] = NULLCHAR;\r
+           strcpy(moveList[currentMove], cmailMove[lastLoadGameNumber - 1]);\r
+            fromX = cmailMove[lastLoadGameNumber - 1][0] - AAA;\r
+            fromY = cmailMove[lastLoadGameNumber - 1][1] - ONE;\r
+            toX = cmailMove[lastLoadGameNumber - 1][2] - AAA;\r
+            toY = cmailMove[lastLoadGameNumber - 1][3] - ONE;\r
+           promoChar = cmailMove[lastLoadGameNumber - 1][4];\r
+           MakeMove(fromX, fromY, toX, toY, promoChar);\r
+           ShowMove(fromX, fromY, toX, toY);\r
+             \r
+           switch (MateTest(boards[currentMove], PosFlags(currentMove),\r
+                             EP_UNKNOWN, castlingRights[currentMove]) ) {\r
+             case MT_NONE:\r
+             case MT_CHECK:\r
+               break;\r
+               \r
+             case MT_CHECKMATE:\r
+               if (WhiteOnMove(currentMove)) {\r
+                   GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
+               } else {\r
+                   GameEnds(WhiteWins, "White mates", GE_PLAYER);\r
+               }\r
+               break;\r
+               \r
+             case MT_STALEMATE:\r
+               GameEnds(GameIsDrawn, "Stalemate", GE_PLAYER);\r
+               break;\r
+           }\r
+\r
+           break;\r
+           \r
+         case CMAIL_RESIGN:\r
+           if (WhiteOnMove(currentMove)) {\r
+               GameEnds(BlackWins, "White resigns", GE_PLAYER);\r
+           } else {\r
+               GameEnds(WhiteWins, "Black resigns", GE_PLAYER);\r
+           }\r
+           break;\r
+           \r
+         case CMAIL_ACCEPT:\r
+           GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);\r
+           break;\r
+             \r
+         default:\r
+           break;\r
+       }\r
+    }\r
+\r
+    return;\r
+}\r
+\r
+/* Wrapper around LoadGame for use when a Cmail message is loaded */\r
+int\r
+CmailLoadGame(f, gameNumber, title, useList)\r
+     FILE *f;\r
+     int gameNumber;\r
+     char *title;\r
+     int useList;\r
+{\r
+    int retVal;\r
+\r
+    if (gameNumber > nCmailGames) {\r
+       DisplayError(_("No more games in this message"), 0);\r
+       return FALSE;\r
+    }\r
+    if (f == lastLoadGameFP) {\r
+       int offset = gameNumber - lastLoadGameNumber;\r
+       if (offset == 0) {\r
+           cmailMsg[0] = NULLCHAR;\r
+           if (cmailMoveRegistered[lastLoadGameNumber - 1]) {\r
+               cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;\r
+               nCmailMovesRegistered--;\r
+           }\r
+           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
+           if (cmailResult[lastLoadGameNumber - 1] == CMAIL_NEW_RESULT) {\r
+               cmailResult[lastLoadGameNumber - 1] = CMAIL_NOT_RESULT;\r
+           }\r
+       } else {\r
+           if (! RegisterMove()) return FALSE;\r
+       }\r
+    }\r
+\r
+    retVal = LoadGame(f, gameNumber, title, useList);\r
+\r
+    /* Make move registered during previous look at this game, if any */\r
+    MakeRegisteredMove();\r
+\r
+    if (cmailCommentList[lastLoadGameNumber - 1] != NULL) {\r
+       commentList[currentMove]\r
+         = StrSave(cmailCommentList[lastLoadGameNumber - 1]);\r
+       DisplayComment(currentMove - 1, commentList[currentMove]);\r
+    }\r
+\r
+    return retVal;\r
+}\r
+\r
+/* Support for LoadNextGame, LoadPreviousGame, ReloadSameGame */\r
+int\r
+ReloadGame(offset)\r
+     int offset;\r
+{\r
+    int gameNumber = lastLoadGameNumber + offset;\r
+    if (lastLoadGameFP == NULL) {\r
+       DisplayError(_("No game has been loaded yet"), 0);\r
+       return FALSE;\r
+    }\r
+    if (gameNumber <= 0) {\r
+       DisplayError(_("Can't back up any further"), 0);\r
+       return FALSE;\r
+    }\r
+    if (cmailMsgLoaded) {\r
+       return CmailLoadGame(lastLoadGameFP, gameNumber,\r
+                            lastLoadGameTitle, lastLoadGameUseList);\r
+    } else {\r
+       return LoadGame(lastLoadGameFP, gameNumber,\r
+                       lastLoadGameTitle, lastLoadGameUseList);\r
+    }\r
+}\r
+\r
+\r
+\r
+/* Load the nth game from open file f */\r
+int\r
+LoadGame(f, gameNumber, title, useList)\r
+     FILE *f;\r
+     int gameNumber;\r
+     char *title;\r
+     int useList;\r
+{\r
+    ChessMove cm;\r
+    char buf[MSG_SIZ];\r
+    int gn = gameNumber;\r
+    ListGame *lg = NULL;\r
+    int numPGNTags = 0;\r
+    int err;\r
+    GameMode oldGameMode;\r
+    VariantClass oldVariant = gameInfo.variant; /* [HGM] PGNvariant */\r
+\r
+    if (appData.debugMode) \r
+       fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);\r
+\r
+    if (gameMode == Training )\r
+       SetTrainingModeOff();\r
+\r
+    oldGameMode = gameMode;\r
+    if (gameMode != BeginningOfGame) {\r
+      Reset(FALSE, TRUE);\r
+    }\r
+\r
+    gameFileFP = f;\r
+    if (lastLoadGameFP != NULL && lastLoadGameFP != f) {\r
+       fclose(lastLoadGameFP);\r
+    }\r
+\r
+    if (useList) {\r
+       lg = (ListGame *) ListElem(&gameList, gameNumber-1);\r
+       \r
+       if (lg) {\r
+           fseek(f, lg->offset, 0);\r
+           GameListHighlight(gameNumber);\r
+           gn = 1;\r
+       }\r
+       else {\r
+           DisplayError(_("Game number out of range"), 0);\r
+           return FALSE;\r
+       }\r
+    } else {\r
+       GameListDestroy();\r
+       if (fseek(f, 0, 0) == -1) {\r
+           if (f == lastLoadGameFP ?\r
+               gameNumber == lastLoadGameNumber + 1 :\r
+               gameNumber == 1) {\r
+               gn = 1;\r
+           } else {\r
+               DisplayError(_("Can't seek on game file"), 0);\r
+               return FALSE;\r
+           }\r
+       }\r
+    }\r
+    lastLoadGameFP = f;\r
+    lastLoadGameNumber = gameNumber;\r
+    strcpy(lastLoadGameTitle, title);\r
+    lastLoadGameUseList = useList;\r
+\r
+    yynewfile(f);\r
+\r
+    if (lg && lg->gameInfo.white && lg->gameInfo.black) {\r
+       sprintf(buf, "%s vs. %s", lg->gameInfo.white,\r
+               lg->gameInfo.black);\r
+           DisplayTitle(buf);\r
+    } else if (*title != NULLCHAR) {\r
+       if (gameNumber > 1) {\r
+           sprintf(buf, "%s %d", title, gameNumber);\r
+           DisplayTitle(buf);\r
+       } else {\r
+           DisplayTitle(title);\r
+       }\r
+    }\r
+\r
+    if (gameMode != AnalyzeFile && gameMode != AnalyzeMode) {\r
+       gameMode = PlayFromGameFile;\r
+       ModeHighlight();\r
+    }\r
+\r
+    currentMove = forwardMostMove = backwardMostMove = 0;\r
+    CopyBoard(boards[0], initialPosition);\r
+    StopClocks();\r
+\r
+    /*\r
+     * Skip the first gn-1 games in the file.\r
+     * Also skip over anything that precedes an identifiable \r
+     * start of game marker, to avoid being confused by \r
+     * garbage at the start of the file.  Currently \r
+     * recognized start of game markers are the move number "1",\r
+     * the pattern "gnuchess .* game", the pattern\r
+     * "^[#;%] [^ ]* game file", and a PGN tag block.  \r
+     * A game that starts with one of the latter two patterns\r
+     * will also have a move number 1, possibly\r
+     * following a position diagram.\r
+     * 5-4-02: Let's try being more lenient and allowing a game to\r
+     * start with an unnumbered move.  Does that break anything?\r
+     */\r
+    cm = lastLoadGameStart = (ChessMove) 0;\r
+    while (gn > 0) {\r
+       yyboardindex = forwardMostMove;\r
+       cm = (ChessMove) yylex();\r
+       switch (cm) {\r
+         case (ChessMove) 0:\r
+           if (cmailMsgLoaded) {\r
+               nCmailGames = CMAIL_MAX_GAMES - gn;\r
+           } else {\r
+               Reset(TRUE, TRUE);\r
+               DisplayError(_("Game not found in file"), 0);\r
+           }\r
+           return FALSE;\r
+\r
+         case GNUChessGame:\r
+         case XBoardGame:\r
+           gn--;\r
+           lastLoadGameStart = cm;\r
+           break;\r
+           \r
+         case MoveNumberOne:\r
+           switch (lastLoadGameStart) {\r
+             case GNUChessGame:\r
+             case XBoardGame:\r
+             case PGNTag:\r
+               break;\r
+             case MoveNumberOne:\r
+             case (ChessMove) 0:\r
+               gn--;           /* count this game */\r
+               lastLoadGameStart = cm;\r
+               break;\r
+             default:\r
+               /* impossible */\r
+               break;\r
+           }\r
+           break;\r
+\r
+         case PGNTag:\r
+           switch (lastLoadGameStart) {\r
+             case GNUChessGame:\r
+             case PGNTag:\r
+             case MoveNumberOne:\r
+             case (ChessMove) 0:\r
+               gn--;           /* count this game */\r
+               lastLoadGameStart = cm;\r
+               break;\r
+             case XBoardGame:\r
+               lastLoadGameStart = cm; /* game counted already */\r
+               break;\r
+             default:\r
+               /* impossible */\r
+               break;\r
+           }\r
+           if (gn > 0) {\r
+               do {\r
+                   yyboardindex = forwardMostMove;\r
+                   cm = (ChessMove) yylex();\r
+               } while (cm == PGNTag || cm == Comment);\r
+           }\r
+           break;\r
+\r
+         case WhiteWins:\r
+         case BlackWins:\r
+         case GameIsDrawn:\r
+           if (cmailMsgLoaded && (CMAIL_MAX_GAMES == lastLoadGameNumber)) {\r
+               if (   cmailResult[CMAIL_MAX_GAMES - gn - 1]\r
+                   != CMAIL_OLD_RESULT) {\r
+                   nCmailResults ++ ;\r
+                   cmailResult[  CMAIL_MAX_GAMES\r
+                               - gn - 1] = CMAIL_OLD_RESULT;\r
+               }\r
+           }\r
+           break;\r
+\r
+         case NormalMove:\r
+           /* Only a NormalMove can be at the start of a game\r
+            * without a position diagram. */\r
+           if (lastLoadGameStart == (ChessMove) 0) {\r
+             gn--;\r
+             lastLoadGameStart = MoveNumberOne;\r
+           }\r
+           break;\r
+\r
+         default:\r
+           break;\r
+       }\r
+    }\r
+    \r
+    if (appData.debugMode)\r
+      fprintf(debugFP, "Parsed game start '%s' (%d)\n", yy_text, (int) cm);\r
+\r
+    if (cm == XBoardGame) {\r
+       /* Skip any header junk before position diagram and/or move 1 */\r
+       for (;;) {\r
+           yyboardindex = forwardMostMove;\r
+           cm = (ChessMove) yylex();\r
+\r
+           if (cm == (ChessMove) 0 ||\r
+               cm == GNUChessGame || cm == XBoardGame) {\r
+               /* Empty game; pretend end-of-file and handle later */\r
+               cm = (ChessMove) 0;\r
+               break;\r
+           }\r
+\r
+           if (cm == MoveNumberOne || cm == PositionDiagram ||\r
+               cm == PGNTag || cm == Comment)\r
+             break;\r
+       }\r
+    } else if (cm == GNUChessGame) {\r
+       if (gameInfo.event != NULL) {\r
+           free(gameInfo.event);\r
+       }\r
+       gameInfo.event = StrSave(yy_text);\r
+    }  \r
+\r
+    startedFromSetupPosition = FALSE;\r
+    while (cm == PGNTag) {\r
+       if (appData.debugMode) \r
+         fprintf(debugFP, "Parsed PGNTag: %s\n", yy_text);\r
+       err = ParsePGNTag(yy_text, &gameInfo);\r
+       if (!err) numPGNTags++;\r
+\r
+        /* [HGM] PGNvariant: automatically switch to variant given in PGN tag */\r
+        if(gameInfo.variant != oldVariant) {\r
+            startedFromPositionFile = FALSE; /* [HGM] loadPos: variant switch likely makes position invalid */\r
+           InitPosition(TRUE);\r
+            oldVariant = gameInfo.variant;\r
+           if (appData.debugMode) \r
+             fprintf(debugFP, "New variant %d\n", (int) oldVariant);\r
+        }\r
+\r
+\r
+       if (gameInfo.fen != NULL) {\r
+         Board initial_position;\r
+         startedFromSetupPosition = TRUE;\r
+         if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) {\r
+           Reset(TRUE, TRUE);\r
+           DisplayError(_("Bad FEN position in file"), 0);\r
+           return FALSE;\r
+         }\r
+         CopyBoard(boards[0], initial_position);\r
+         if (blackPlaysFirst) {\r
+           currentMove = forwardMostMove = backwardMostMove = 1;\r
+           CopyBoard(boards[1], initial_position);\r
+           strcpy(moveList[0], "");\r
+           strcpy(parseList[0], "");\r
+           timeRemaining[0][1] = whiteTimeRemaining;\r
+           timeRemaining[1][1] = blackTimeRemaining;\r
+           if (commentList[0] != NULL) {\r
+             commentList[1] = commentList[0];\r
+             commentList[0] = NULL;\r
+           }\r
+         } else {\r
+           currentMove = forwardMostMove = backwardMostMove = 0;\r
+         }\r
+          /* [HGM] copy FEN attributes as well. Bugfix 4.3.14m and 4.3.15e: moved to after 'blackPlaysFirst' */\r
+          {   int i;\r
+              initialRulePlies = FENrulePlies;\r
+              epStatus[forwardMostMove] = FENepStatus;\r
+              for( i=0; i< nrCastlingRights; i++ )\r
+                  initialRights[i] = castlingRights[forwardMostMove][i] = FENcastlingRights[i];\r
+          }\r
+         yyboardindex = forwardMostMove;\r
+         free(gameInfo.fen);\r
+         gameInfo.fen = NULL;\r
+       }\r
+\r
+       yyboardindex = forwardMostMove;\r
+       cm = (ChessMove) yylex();\r
+\r
+       /* Handle comments interspersed among the tags */\r
+       while (cm == Comment) {\r
+           char *p;\r
+           if (appData.debugMode) \r
+             fprintf(debugFP, "Parsed Comment: %s\n", yy_text);\r
+           p = yy_text;\r
+           if (*p == '{' || *p == '[' || *p == '(') {\r
+               p[strlen(p) - 1] = NULLCHAR;\r
+               p++;\r
+           }\r
+           while (*p == '\n') p++;\r
+           AppendComment(currentMove, p);\r
+           yyboardindex = forwardMostMove;\r
+           cm = (ChessMove) yylex();\r
+       }\r
+    }\r
+\r
+    /* don't rely on existence of Event tag since if game was\r
+     * pasted from clipboard the Event tag may not exist\r
+     */\r
+    if (numPGNTags > 0){\r
+        char *tags;\r
+       if (gameInfo.variant == VariantNormal) {\r
+         gameInfo.variant = StringToVariant(gameInfo.event);\r
+       }\r
+       if (!matchMode) {\r
+          if( appData.autoDisplayTags ) {\r
+           tags = PGNTags(&gameInfo);\r
+           TagsPopUp(tags, CmailMsg());\r
+           free(tags);\r
+          }\r
+       }\r
+    } else {\r
+       /* Make something up, but don't display it now */\r
+       SetGameInfo();\r
+       TagsPopDown();\r
+    }\r
+\r
+    if (cm == PositionDiagram) {\r
+       int i, j;\r
+       char *p;\r
+       Board initial_position;\r
+\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Parsed PositionDiagram: %s\n", yy_text);\r
+\r
+       if (!startedFromSetupPosition) {\r
+           p = yy_text;\r
+            for (i = BOARD_HEIGHT - 1; i >= 0; i--)\r
+              for (j = BOARD_LEFT; j < BOARD_RGHT; p++)\r
+               switch (*p) {\r
+                 case '[':\r
+                 case '-':\r
+                 case ' ':\r
+                 case '\t':\r
+                 case '\n':\r
+                 case '\r':\r
+                   break;\r
+                 default:\r
+                   initial_position[i][j++] = CharToPiece(*p);\r
+                   break;\r
+               }\r
+           while (*p == ' ' || *p == '\t' ||\r
+                  *p == '\n' || *p == '\r') p++;\r
+       \r
+           if (strncmp(p, "black", strlen("black"))==0)\r
+             blackPlaysFirst = TRUE;\r
+           else\r
+             blackPlaysFirst = FALSE;\r
+           startedFromSetupPosition = TRUE;\r
+       \r
+           CopyBoard(boards[0], initial_position);\r
+           if (blackPlaysFirst) {\r
+               currentMove = forwardMostMove = backwardMostMove = 1;\r
+               CopyBoard(boards[1], initial_position);\r
+               strcpy(moveList[0], "");\r
+               strcpy(parseList[0], "");\r
+               timeRemaining[0][1] = whiteTimeRemaining;\r
+               timeRemaining[1][1] = blackTimeRemaining;\r
+               if (commentList[0] != NULL) {\r
+                   commentList[1] = commentList[0];\r
+                   commentList[0] = NULL;\r
+               }\r
+           } else {\r
+               currentMove = forwardMostMove = backwardMostMove = 0;\r
+           }\r
+       }\r
+       yyboardindex = forwardMostMove;\r
+       cm = (ChessMove) yylex();\r
+    }\r
+\r
+    if (first.pr == NoProc) {\r
+       StartChessProgram(&first);\r
+    }\r
+    InitChessProgram(&first, FALSE);\r
+    SendToProgram("force\n", &first);\r
+    if (startedFromSetupPosition) {\r
+       SendBoard(&first, forwardMostMove);\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "Load Game\n");\r
+    }\r
+       DisplayBothClocks();\r
+    }      \r
+\r
+    /* [HGM] server: flag to write setup moves in broadcast file as one */\r
+    loadFlag = appData.suppressLoadMoves;\r
+\r
+    while (cm == Comment) {\r
+       char *p;\r
+       if (appData.debugMode) \r
+         fprintf(debugFP, "Parsed Comment: %s\n", yy_text);\r
+       p = yy_text;\r
+       if (*p == '{' || *p == '[' || *p == '(') {\r
+           p[strlen(p) - 1] = NULLCHAR;\r
+           p++;\r
+       }\r
+       while (*p == '\n') p++;\r
+       AppendComment(currentMove, p);\r
+       yyboardindex = forwardMostMove;\r
+       cm = (ChessMove) yylex();\r
+    }\r
+\r
+    if ((cm == (ChessMove) 0 && lastLoadGameStart != (ChessMove) 0) ||\r
+       cm == WhiteWins || cm == BlackWins ||\r
+       cm == GameIsDrawn || cm == GameUnfinished) {\r
+       DisplayMessage("", _("No moves in game"));\r
+       if (cmailMsgLoaded) {\r
+           if (appData.debugMode)\r
+             fprintf(debugFP, "Setting flipView to %d.\n", FALSE);\r
+           ClearHighlights();\r
+           flipView = FALSE;\r
+       }\r
+       DrawPosition(FALSE, boards[currentMove]);\r
+       DisplayBothClocks();\r
+       gameMode = EditGame;\r
+       ModeHighlight();\r
+       gameFileFP = NULL;\r
+       cmailOldMove = 0;\r
+       return TRUE;\r
+    }\r
+\r
+    // [HGM] PV info: routine tests if comment empty\r
+    if (!matchMode && (pausing || appData.timeDelay != 0)) {\r
+       DisplayComment(currentMove - 1, commentList[currentMove]);\r
+    }\r
+    if (!matchMode && appData.timeDelay != 0) \r
+      DrawPosition(FALSE, boards[currentMove]);\r
+\r
+    if (gameMode == AnalyzeFile || gameMode == AnalyzeMode) {\r
+      programStats.ok_to_send = 1;\r
+    }\r
+\r
+    /* if the first token after the PGN tags is a move\r
+     * and not move number 1, retrieve it from the parser \r
+     */\r
+    if (cm != MoveNumberOne)\r
+       LoadGameOneMove(cm);\r
+\r
+    /* load the remaining moves from the file */\r
+    while (LoadGameOneMove((ChessMove)0)) {\r
+      timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
+      timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
+    }\r
+\r
+    /* rewind to the start of the game */\r
+    currentMove = backwardMostMove;\r
+\r
+    HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+\r
+    if (oldGameMode == AnalyzeFile ||\r
+       oldGameMode == AnalyzeMode) {\r
+      AnalyzeFileEvent();\r
+    }\r
+\r
+    if (matchMode || appData.timeDelay == 0) {\r
+      ToEndEvent();\r
+      gameMode = EditGame;\r
+      ModeHighlight();\r
+    } else if (appData.timeDelay > 0) {\r
+      AutoPlayGameLoop();\r
+    }\r
+\r
+    if (appData.debugMode) \r
+       fprintf(debugFP, "LoadGame(): on exit, gameMode %d\n", gameMode);\r
+\r
+    loadFlag = 0; /* [HGM] true game starts */\r
+    return TRUE;\r
+}\r
+\r
+/* Support for LoadNextPosition, LoadPreviousPosition, ReloadSamePosition */\r
+int\r
+ReloadPosition(offset)\r
+     int offset;\r
+{\r
+    int positionNumber = lastLoadPositionNumber + offset;\r
+    if (lastLoadPositionFP == NULL) {\r
+       DisplayError(_("No position has been loaded yet"), 0);\r
+       return FALSE;\r
+    }\r
+    if (positionNumber <= 0) {\r
+       DisplayError(_("Can't back up any further"), 0);\r
+       return FALSE;\r
+    }\r
+    return LoadPosition(lastLoadPositionFP, positionNumber,\r
+                       lastLoadPositionTitle);\r
+}\r
+\r
+/* Load the nth position from the given file */\r
+int\r
+LoadPositionFromFile(filename, n, title)\r
+     char *filename;\r
+     int n;\r
+     char *title;\r
+{\r
+    FILE *f;\r
+    char buf[MSG_SIZ];\r
+\r
+    if (strcmp(filename, "-") == 0) {\r
+       return LoadPosition(stdin, n, "stdin");\r
+    } else {\r
+       f = fopen(filename, "rb");\r
+       if (f == NULL) {\r
+           sprintf(buf, _("Can't open \"%s\""), filename);\r
+           DisplayError(buf, errno);\r
+           return FALSE;\r
+       } else {\r
+           return LoadPosition(f, n, title);\r
+       }\r
+    }\r
+}\r
+\r
+/* Load the nth position from the given open file, and close it */\r
+int\r
+LoadPosition(f, positionNumber, title)\r
+     FILE *f;\r
+     int positionNumber;\r
+     char *title;\r
+{\r
+    char *p, line[MSG_SIZ];\r
+    Board initial_position;\r
+    int i, j, fenMode, pn;\r
+    \r
+    if (gameMode == Training )\r
+       SetTrainingModeOff();\r
+\r
+    if (gameMode != BeginningOfGame) {\r
+       Reset(FALSE, TRUE);\r
+    }\r
+    if (lastLoadPositionFP != NULL && lastLoadPositionFP != f) {\r
+       fclose(lastLoadPositionFP);\r
+    }\r
+    if (positionNumber == 0) positionNumber = 1;\r
+    lastLoadPositionFP = f;\r
+    lastLoadPositionNumber = positionNumber;\r
+    strcpy(lastLoadPositionTitle, title);\r
+    if (first.pr == NoProc) {\r
+      StartChessProgram(&first);\r
+      InitChessProgram(&first, FALSE);\r
+    }    \r
+    pn = positionNumber;\r
+    if (positionNumber < 0) {\r
+       /* Negative position number means to seek to that byte offset */\r
+       if (fseek(f, -positionNumber, 0) == -1) {\r
+           DisplayError(_("Can't seek on position file"), 0);\r
+           return FALSE;\r
+       };\r
+       pn = 1;\r
+    } else {\r
+       if (fseek(f, 0, 0) == -1) {\r
+           if (f == lastLoadPositionFP ?\r
+               positionNumber == lastLoadPositionNumber + 1 :\r
+               positionNumber == 1) {\r
+               pn = 1;\r
+           } else {\r
+               DisplayError(_("Can't seek on position file"), 0);\r
+               return FALSE;\r
+           }\r
+       }\r
+    }\r
+    /* See if this file is FEN or old-style xboard */\r
+    if (fgets(line, MSG_SIZ, f) == NULL) {\r
+       DisplayError(_("Position not found in file"), 0);\r
+       return FALSE;\r
+    }\r
+#if 0\r
+    switch (line[0]) {\r
+      case '#':  case 'x':\r
+      default:\r
+       fenMode = FALSE;\r
+       break;\r
+      case 'p':  case 'n':  case 'b':  case 'r':  case 'q':  case 'k':\r
+      case 'P':  case 'N':  case 'B':  case 'R':  case 'Q':  case 'K':\r
+      case '1':  case '2':  case '3':  case '4':  case '5':  case '6':\r
+      case '7':  case '8':  case '9':\r
+      case 'H':  case 'A':  case 'M':  case 'h':  case 'a':  case 'm':\r
+      case 'E':  case 'F':  case 'G':  case 'e':  case 'f':  case 'g':\r
+      case 'C':  case 'W':             case 'c':  case 'w': \r
+       fenMode = TRUE;\r
+       break;\r
+    }\r
+#else\r
+    // [HGM] FEN can begin with digit, any piece letter valid in this variant, or a + for Shogi promoted pieces\r
+    fenMode = line[0] >= '0' && line[0] <= '9' || line[0] == '+' || CharToPiece(line[0]) != EmptySquare;\r
+#endif\r
+\r
+    if (pn >= 2) {\r
+       if (fenMode || line[0] == '#') pn--;\r
+       while (pn > 0) {\r
+           /* skip positions before number pn */\r
+           if (fgets(line, MSG_SIZ, f) == NULL) {\r
+               Reset(TRUE, TRUE);\r
+               DisplayError(_("Position not found in file"), 0);\r
+               return FALSE;\r
+           }\r
+           if (fenMode || line[0] == '#') pn--;\r
+       }\r
+    }\r
+\r
+    if (fenMode) {\r
+       if (!ParseFEN(initial_position, &blackPlaysFirst, line)) {\r
+           DisplayError(_("Bad FEN position in file"), 0);\r
+           return FALSE;\r
+       }\r
+    } else {\r
+       (void) fgets(line, MSG_SIZ, f);\r
+       (void) fgets(line, MSG_SIZ, f);\r
+    \r
+        for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+           (void) fgets(line, MSG_SIZ, f);\r
+            for (p = line, j = BOARD_LEFT; j < BOARD_RGHT; p++) {\r
+               if (*p == ' ')\r
+                 continue;\r
+               initial_position[i][j++] = CharToPiece(*p);\r
+           }\r
+       }\r
+    \r
+       blackPlaysFirst = FALSE;\r
+       if (!feof(f)) {\r
+           (void) fgets(line, MSG_SIZ, f);\r
+           if (strncmp(line, "black", strlen("black"))==0)\r
+             blackPlaysFirst = TRUE;\r
+       }\r
+    }\r
+    startedFromSetupPosition = TRUE;\r
+    \r
+    SendToProgram("force\n", &first);\r
+    CopyBoard(boards[0], initial_position);\r
+    if (blackPlaysFirst) {\r
+       currentMove = forwardMostMove = backwardMostMove = 1;\r
+       strcpy(moveList[0], "");\r
+       strcpy(parseList[0], "");\r
+       CopyBoard(boards[1], initial_position);\r
+       DisplayMessage("", _("Black to play"));\r
+    } else {\r
+       currentMove = forwardMostMove = backwardMostMove = 0;\r
+       DisplayMessage("", _("White to play"));\r
+    }\r
+          /* [HGM] copy FEN attributes as well */\r
+          {   int i;\r
+              initialRulePlies = FENrulePlies;\r
+              epStatus[forwardMostMove] = FENepStatus;\r
+              for( i=0; i< nrCastlingRights; i++ )\r
+                  castlingRights[forwardMostMove][i] = FENcastlingRights[i];\r
+          }\r
+    SendBoard(&first, forwardMostMove);\r
+    if (appData.debugMode) {\r
+int i, j;\r
+  for(i=0;i<2;i++){for(j=0;j<6;j++)fprintf(debugFP, " %d", castlingRights[i][j]);fprintf(debugFP,"\n");}\r
+  for(j=0;j<6;j++)fprintf(debugFP, " %d", initialRights[j]);fprintf(debugFP,"\n");\r
+        fprintf(debugFP, "Load Position\n");\r
+    }\r
+\r
+    if (positionNumber > 1) {\r
+       sprintf(line, "%s %d", title, positionNumber);\r
+       DisplayTitle(line);\r
+    } else {\r
+       DisplayTitle(title);\r
+    }\r
+    gameMode = EditGame;\r
+    ModeHighlight();\r
+    ResetClocks();\r
+    timeRemaining[0][1] = whiteTimeRemaining;\r
+    timeRemaining[1][1] = blackTimeRemaining;\r
+    DrawPosition(FALSE, boards[currentMove]);\r
+   \r
+    return TRUE;\r
+}\r
+\r
+\r
+void\r
+CopyPlayerNameIntoFileName(dest, src)\r
+     char **dest, *src;\r
+{\r
+    while (*src != NULLCHAR && *src != ',') {\r
+       if (*src == ' ') {\r
+           *(*dest)++ = '_';\r
+           src++;\r
+       } else {\r
+           *(*dest)++ = *src++;\r
+       }\r
+    }\r
+}\r
+\r
+char *DefaultFileName(ext)\r
+     char *ext;\r
+{\r
+    static char def[MSG_SIZ];\r
+    char *p;\r
+\r
+    if (gameInfo.white != NULL && gameInfo.white[0] != '-') {\r
+       p = def;\r
+       CopyPlayerNameIntoFileName(&p, gameInfo.white);\r
+       *p++ = '-';\r
+       CopyPlayerNameIntoFileName(&p, gameInfo.black);\r
+       *p++ = '.';\r
+       strcpy(p, ext);\r
+    } else {\r
+       def[0] = NULLCHAR;\r
+    }\r
+    return def;\r
+}\r
+\r
+/* Save the current game to the given file */\r
+int\r
+SaveGameToFile(filename, append)\r
+     char *filename;\r
+     int append;\r
+{\r
+    FILE *f;\r
+    char buf[MSG_SIZ];\r
+\r
+    if (strcmp(filename, "-") == 0) {\r
+       return SaveGame(stdout, 0, NULL);\r
+    } else {\r
+       f = fopen(filename, append ? "a" : "w");\r
+       if (f == NULL) {\r
+           sprintf(buf, _("Can't open \"%s\""), filename);\r
+           DisplayError(buf, errno);\r
+           return FALSE;\r
+       } else {\r
+           return SaveGame(f, 0, NULL);\r
+       }\r
+    }\r
+}\r
+\r
+char *\r
+SavePart(str)\r
+     char *str;\r
+{\r
+    static char buf[MSG_SIZ];\r
+    char *p;\r
+    \r
+    p = strchr(str, ' ');\r
+    if (p == NULL) return str;\r
+    strncpy(buf, str, p - str);\r
+    buf[p - str] = NULLCHAR;\r
+    return buf;\r
+}\r
+\r
+#define PGN_MAX_LINE 75\r
+\r
+#define PGN_SIDE_WHITE  0\r
+#define PGN_SIDE_BLACK  1\r
+\r
+/* [AS] */\r
+static int FindFirstMoveOutOfBook( int side )\r
+{\r
+    int result = -1;\r
+\r
+    if( backwardMostMove == 0 && ! startedFromSetupPosition) {\r
+        int index = backwardMostMove;\r
+        int has_book_hit = 0;\r
+\r
+        if( (index % 2) != side ) {\r
+            index++;\r
+        }\r
+\r
+        while( index < forwardMostMove ) {\r
+            /* Check to see if engine is in book */\r
+            int depth = pvInfoList[index].depth;\r
+            int score = pvInfoList[index].score;\r
+            int in_book = 0;\r
+\r
+            if( depth <= 2 ) {\r
+                in_book = 1;\r
+            }\r
+            else if( score == 0 && depth == 63 ) {\r
+                in_book = 1; /* Zappa */\r
+            }\r
+            else if( score == 2 && depth == 99 ) {\r
+                in_book = 1; /* Abrok */\r
+            }\r
+\r
+            has_book_hit += in_book;\r
+\r
+            if( ! in_book ) {\r
+                result = index;\r
+\r
+                break;\r
+            }\r
+\r
+            index += 2;\r
+        }\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+/* [AS] */\r
+void GetOutOfBookInfo( char * buf )\r
+{\r
+    int oob[2];\r
+    int i;\r
+    int offset = backwardMostMove & (~1L); /* output move numbers start at 1 */\r
+\r
+    oob[0] = FindFirstMoveOutOfBook( PGN_SIDE_WHITE );\r
+    oob[1] = FindFirstMoveOutOfBook( PGN_SIDE_BLACK );\r
+\r
+    *buf = '\0';\r
+\r
+    if( oob[0] >= 0 || oob[1] >= 0 ) {\r
+        for( i=0; i<2; i++ ) {\r
+            int idx = oob[i];\r
+\r
+            if( idx >= 0 ) {\r
+                if( i > 0 && oob[0] >= 0 ) {\r
+                    strcat( buf, "   " );\r
+                }\r
+\r
+                sprintf( buf+strlen(buf), "%d%s. ", (idx - offset)/2 + 1, idx & 1 ? ".." : "" );\r
+                sprintf( buf+strlen(buf), "%s%.2f", \r
+                    pvInfoList[idx].score >= 0 ? "+" : "",\r
+                    pvInfoList[idx].score / 100.0 );\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+/* Save game in PGN style and close the file */\r
+int\r
+SaveGamePGN(f)\r
+     FILE *f;\r
+{\r
+    int i, offset, linelen, newblock;\r
+    time_t tm;\r
+    char *movetext;\r
+    char numtext[32];\r
+    int movelen, numlen, blank;\r
+    char move_buffer[100]; /* [AS] Buffer for move+PV info */\r
+\r
+    offset = backwardMostMove & (~1L); /* output move numbers start at 1 */\r
+    \r
+    tm = time((time_t *) NULL);\r
+    \r
+    PrintPGNTags(f, &gameInfo);\r
+    \r
+    if (backwardMostMove > 0 || startedFromSetupPosition) {\r
+        char *fen = PositionToFEN(backwardMostMove, 1);\r
+        fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);\r
+       fprintf(f, "\n{--------------\n");\r
+       PrintPosition(f, backwardMostMove);\r
+       fprintf(f, "--------------}\n");\r
+        free(fen);\r
+    }\r
+    else {\r
+        /* [AS] Out of book annotation */\r
+        if( appData.saveOutOfBookInfo ) {\r
+            char buf[64];\r
+\r
+            GetOutOfBookInfo( buf );\r
+\r
+            if( buf[0] != '\0' ) {\r
+                fprintf( f, "[%s \"%s\"]\n", PGN_OUT_OF_BOOK, buf ); \r
+            }\r
+        }\r
+\r
+       fprintf(f, "\n");\r
+    }\r
+\r
+    i = backwardMostMove;\r
+    linelen = 0;\r
+    newblock = TRUE;\r
+\r
+    while (i < forwardMostMove) {\r
+       /* Print comments preceding this move */\r
+       if (commentList[i] != NULL) {\r
+           if (linelen > 0) fprintf(f, "\n");\r
+           fprintf(f, "{\n%s}\n", commentList[i]);\r
+           linelen = 0;\r
+           newblock = TRUE;\r
+       }\r
+\r
+       /* Format move number */\r
+       if ((i % 2) == 0) {\r
+           sprintf(numtext, "%d.", (i - offset)/2 + 1);\r
+       } else {\r
+           if (newblock) {\r
+               sprintf(numtext, "%d...", (i - offset)/2 + 1);\r
+           } else {\r
+               numtext[0] = NULLCHAR;\r
+           }\r
+       }\r
+       numlen = strlen(numtext);\r
+       newblock = FALSE;\r
+\r
+       /* Print move number */\r
+       blank = linelen > 0 && numlen > 0;\r
+       if (linelen + (blank ? 1 : 0) + numlen > PGN_MAX_LINE) {\r
+           fprintf(f, "\n");\r
+           linelen = 0;\r
+           blank = 0;\r
+       }\r
+       if (blank) {\r
+           fprintf(f, " ");\r
+           linelen++;\r
+       }\r
+       fprintf(f, numtext);\r
+       linelen += numlen;\r
+\r
+       /* Get move */\r
+       movelen = strlen(parseList[i]); /* [HGM] pgn: line-break point before move */\r
+\r
+       /* Print move */\r
+       blank = linelen > 0 && movelen > 0;\r
+       if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {\r
+           fprintf(f, "\n");\r
+           linelen = 0;\r
+           blank = 0;\r
+       }\r
+       if (blank) {\r
+           fprintf(f, " ");\r
+           linelen++;\r
+       }\r
+       fprintf(f, parseList[i]);\r
+       linelen += movelen;\r
+\r
+        /* [AS] Add PV info if present */\r
+        if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) {\r
+            /* [HGM] add time */\r
+            char buf[MSG_SIZ]; int seconds = 0;\r
+\r
+#if 0\r
+            if(i >= backwardMostMove) {\r
+               if(WhiteOnMove(i))\r
+                       seconds = timeRemaining[0][i] - timeRemaining[0][i+1]\r
+                                 + GetTimeQuota(i/2) / WhitePlayer()->timeOdds;\r
+               else\r
+                       seconds = timeRemaining[1][i] - timeRemaining[1][i+1]\r
+                                  + GetTimeQuota(i/2) / WhitePlayer()->other->timeOdds;\r
+            }\r
+            seconds = (seconds+50)/100; // deci-seconds, rounded to nearest\r
+#else\r
+            seconds = (pvInfoList[i].time + 5)/10; // [HGM] PVtime: use engine time\r
+#endif\r
+    if (appData.debugMode,0) {\r
+        fprintf(debugFP, "times = %d %d %d %d, seconds=%d\n",\r
+                timeRemaining[0][i+1], timeRemaining[0][i],\r
+                     timeRemaining[1][i+1], timeRemaining[1][i], seconds\r
+        );\r
+    }\r
+\r
+            if( seconds <= 0) buf[0] = 0; else\r
+            if( seconds < 30 ) sprintf(buf, " %3.1f%c", seconds/10., 0); else {\r
+               seconds = (seconds + 4)/10; // round to full seconds\r
+               if( seconds < 60 ) sprintf(buf, " %d%c", seconds, 0); else\r
+                                  sprintf(buf, " %d:%02d%c", seconds/60, seconds%60, 0);\r
+           }\r
+\r
+            sprintf( move_buffer, "{%s%.2f/%d%s}", \r
+                pvInfoList[i].score >= 0 ? "+" : "",\r
+                pvInfoList[i].score / 100.0,\r
+                pvInfoList[i].depth,\r
+               buf );\r
+\r
+           movelen = strlen(move_buffer); /* [HGM] pgn: line-break point after move */\r
+\r
+           /* Print score/depth */\r
+           blank = linelen > 0 && movelen > 0;\r
+           if (linelen + (blank ? 1 : 0) + movelen > PGN_MAX_LINE) {\r
+               fprintf(f, "\n");\r
+               linelen = 0;\r
+               blank = 0;\r
+           }\r
+           if (blank) {\r
+               fprintf(f, " ");\r
+               linelen++;\r
+           }\r
+           fprintf(f, move_buffer);\r
+           linelen += movelen;\r
+        }\r
+\r
+       i++;\r
+    }\r
+    \r
+    /* Start a new line */\r
+    if (linelen > 0) fprintf(f, "\n");\r
+\r
+    /* Print comments after last move */\r
+    if (commentList[i] != NULL) {\r
+       fprintf(f, "{\n%s}\n", commentList[i]);\r
+    }\r
+\r
+    /* Print result */\r
+    if (gameInfo.resultDetails != NULL &&\r
+       gameInfo.resultDetails[0] != NULLCHAR) {\r
+       fprintf(f, "{%s} %s\n\n", gameInfo.resultDetails,\r
+               PGNResult(gameInfo.result));\r
+    } else {\r
+       fprintf(f, "%s\n\n", PGNResult(gameInfo.result));\r
+    }\r
+\r
+    fclose(f);\r
+    return TRUE;\r
+}\r
+\r
+/* Save game in old style and close the file */\r
+int\r
+SaveGameOldStyle(f)\r
+     FILE *f;\r
+{\r
+    int i, offset;\r
+    time_t tm;\r
+    \r
+    tm = time((time_t *) NULL);\r
+    \r
+    fprintf(f, "# %s game file -- %s", programName, ctime(&tm));\r
+    PrintOpponents(f);\r
+    \r
+    if (backwardMostMove > 0 || startedFromSetupPosition) {\r
+       fprintf(f, "\n[--------------\n");\r
+       PrintPosition(f, backwardMostMove);\r
+       fprintf(f, "--------------]\n");\r
+    } else {\r
+       fprintf(f, "\n");\r
+    }\r
+\r
+    i = backwardMostMove;\r
+    offset = backwardMostMove & (~1L); /* output move numbers start at 1 */\r
+\r
+    while (i < forwardMostMove) {\r
+       if (commentList[i] != NULL) {\r
+           fprintf(f, "[%s]\n", commentList[i]);\r
+       }\r
+\r
+       if ((i % 2) == 1) {\r
+           fprintf(f, "%d. ...  %s\n", (i - offset)/2 + 1, parseList[i]);\r
+           i++;\r
+       } else {\r
+           fprintf(f, "%d. %s  ", (i - offset)/2 + 1, parseList[i]);\r
+           i++;\r
+           if (commentList[i] != NULL) {\r
+               fprintf(f, "\n");\r
+               continue;\r
+           }\r
+           if (i >= forwardMostMove) {\r
+               fprintf(f, "\n");\r
+               break;\r
+           }\r
+           fprintf(f, "%s\n", parseList[i]);\r
+           i++;\r
+       }\r
+    }\r
+    \r
+    if (commentList[i] != NULL) {\r
+       fprintf(f, "[%s]\n", commentList[i]);\r
+    }\r
+\r
+    /* This isn't really the old style, but it's close enough */\r
+    if (gameInfo.resultDetails != NULL &&\r
+       gameInfo.resultDetails[0] != NULLCHAR) {\r
+       fprintf(f, "%s (%s)\n\n", PGNResult(gameInfo.result),\r
+               gameInfo.resultDetails);\r
+    } else {\r
+       fprintf(f, "%s\n\n", PGNResult(gameInfo.result));\r
+    }\r
+\r
+    fclose(f);\r
+    return TRUE;\r
+}\r
+\r
+/* Save the current game to open file f and close the file */\r
+int\r
+SaveGame(f, dummy, dummy2)\r
+     FILE *f;\r
+     int dummy;\r
+     char *dummy2;\r
+{\r
+    if (gameMode == EditPosition) EditPositionDone();\r
+    if (appData.oldSaveStyle)\r
+      return SaveGameOldStyle(f);\r
+    else\r
+      return SaveGamePGN(f);\r
+}\r
+\r
+/* Save the current position to the given file */\r
+int\r
+SavePositionToFile(filename)\r
+     char *filename;\r
+{\r
+    FILE *f;\r
+    char buf[MSG_SIZ];\r
+\r
+    if (strcmp(filename, "-") == 0) {\r
+       return SavePosition(stdout, 0, NULL);\r
+    } else {\r
+       f = fopen(filename, "a");\r
+       if (f == NULL) {\r
+           sprintf(buf, _("Can't open \"%s\""), filename);\r
+           DisplayError(buf, errno);\r
+           return FALSE;\r
+       } else {\r
+           SavePosition(f, 0, NULL);\r
+           return TRUE;\r
+       }\r
+    }\r
+}\r
+\r
+/* Save the current position to the given open file and close the file */\r
+int\r
+SavePosition(f, dummy, dummy2)\r
+     FILE *f;\r
+     int dummy;\r
+     char *dummy2;\r
+{\r
+    time_t tm;\r
+    char *fen;\r
+    \r
+    if (appData.oldSaveStyle) {\r
+       tm = time((time_t *) NULL);\r
+    \r
+       fprintf(f, "# %s position file -- %s", programName, ctime(&tm));\r
+       PrintOpponents(f);\r
+       fprintf(f, "[--------------\n");\r
+       PrintPosition(f, currentMove);\r
+       fprintf(f, "--------------]\n");\r
+    } else {\r
+       fen = PositionToFEN(currentMove, 1);\r
+       fprintf(f, "%s\n", fen);\r
+       free(fen);\r
+    }\r
+    fclose(f);\r
+    return TRUE;\r
+}\r
+\r
+void\r
+ReloadCmailMsgEvent(unregister)\r
+     int unregister;\r
+{\r
+#if !WIN32\r
+    static char *inFilename = NULL;\r
+    static char *outFilename;\r
+    int i;\r
+    struct stat inbuf, outbuf;\r
+    int status;\r
+    \r
+    /* Any registered moves are unregistered if unregister is set, */\r
+    /* i.e. invoked by the signal handler */\r
+    if (unregister) {\r
+       for (i = 0; i < CMAIL_MAX_GAMES; i ++) {\r
+           cmailMoveRegistered[i] = FALSE;\r
+           if (cmailCommentList[i] != NULL) {\r
+               free(cmailCommentList[i]);\r
+               cmailCommentList[i] = NULL;\r
+           }\r
+       }\r
+       nCmailMovesRegistered = 0;\r
+    }\r
+\r
+    for (i = 0; i < CMAIL_MAX_GAMES; i ++) {\r
+       cmailResult[i] = CMAIL_NOT_RESULT;\r
+    }\r
+    nCmailResults = 0;\r
+\r
+    if (inFilename == NULL) {\r
+       /* Because the filenames are static they only get malloced once  */\r
+       /* and they never get freed                                      */\r
+       inFilename = (char *) malloc(strlen(appData.cmailGameName) + 9);\r
+       sprintf(inFilename, "%s.game.in", appData.cmailGameName);\r
+\r
+       outFilename = (char *) malloc(strlen(appData.cmailGameName) + 5);\r
+       sprintf(outFilename, "%s.out", appData.cmailGameName);\r
+    }\r
+    \r
+    status = stat(outFilename, &outbuf);\r
+    if (status < 0) {\r
+       cmailMailedMove = FALSE;\r
+    } else {\r
+       status = stat(inFilename, &inbuf);\r
+       cmailMailedMove = (inbuf.st_mtime < outbuf.st_mtime);\r
+    }\r
+    \r
+    /* LoadGameFromFile(CMAIL_MAX_GAMES) with cmailMsgLoaded == TRUE\r
+       counts the games, notes how each one terminated, etc.\r
+       \r
+       It would be nice to remove this kludge and instead gather all\r
+       the information while building the game list.  (And to keep it\r
+       in the game list nodes instead of having a bunch of fixed-size\r
+       parallel arrays.)  Note this will require getting each game's\r
+       termination from the PGN tags, as the game list builder does\r
+       not process the game moves.  --mann\r
+       */\r
+    cmailMsgLoaded = TRUE;\r
+    LoadGameFromFile(inFilename, CMAIL_MAX_GAMES, "", FALSE);\r
+    \r
+    /* Load first game in the file or popup game menu */\r
+    LoadGameFromFile(inFilename, 0, appData.cmailGameName, TRUE);\r
+\r
+#endif /* !WIN32 */\r
+    return;\r
+}\r
+\r
+int\r
+RegisterMove()\r
+{\r
+    FILE *f;\r
+    char string[MSG_SIZ];\r
+\r
+    if (   cmailMailedMove\r
+       || (cmailResult[lastLoadGameNumber - 1] == CMAIL_OLD_RESULT)) {\r
+       return TRUE;            /* Allow free viewing  */\r
+    }\r
+\r
+    /* Unregister move to ensure that we don't leave RegisterMove        */\r
+    /* with the move registered when the conditions for registering no   */\r
+    /* longer hold                                                       */\r
+    if (cmailMoveRegistered[lastLoadGameNumber - 1]) {\r
+       cmailMoveRegistered[lastLoadGameNumber - 1] = FALSE;\r
+       nCmailMovesRegistered --;\r
+\r
+       if (cmailCommentList[lastLoadGameNumber - 1] != NULL) \r
+         {\r
+             free(cmailCommentList[lastLoadGameNumber - 1]);\r
+             cmailCommentList[lastLoadGameNumber - 1] = NULL;\r
+         }\r
+    }\r
+\r
+    if (cmailOldMove == -1) {\r
+       DisplayError(_("You have edited the game history.\nUse Reload Same Game and make your move again."), 0);\r
+       return FALSE;\r
+    }\r
+\r
+    if (currentMove > cmailOldMove + 1) {\r
+       DisplayError(_("You have entered too many moves.\nBack up to the correct position and try again."), 0);\r
+       return FALSE;\r
+    }\r
+\r
+    if (currentMove < cmailOldMove) {\r
+       DisplayError(_("Displayed position is not current.\nStep forward to the correct position and try again."), 0);\r
+       return FALSE;\r
+    }\r
+\r
+    if (forwardMostMove > currentMove) {\r
+       /* Silently truncate extra moves */\r
+       TruncateGame();\r
+    }\r
+\r
+    if (   (currentMove == cmailOldMove + 1)\r
+       || (   (currentMove == cmailOldMove)\r
+           && (   (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_ACCEPT)\r
+               || (cmailMoveType[lastLoadGameNumber - 1] == CMAIL_RESIGN)))) {\r
+       if (gameInfo.result != GameUnfinished) {\r
+           cmailResult[lastLoadGameNumber - 1] = CMAIL_NEW_RESULT;\r
+       }\r
+\r
+       if (commentList[currentMove] != NULL) {\r
+           cmailCommentList[lastLoadGameNumber - 1]\r
+             = StrSave(commentList[currentMove]);\r
+       }\r
+       strcpy(cmailMove[lastLoadGameNumber - 1], moveList[currentMove - 1]);\r
+\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Saving %s for game %d\n",\r
+                 cmailMove[lastLoadGameNumber - 1], lastLoadGameNumber);\r
+\r
+       sprintf(string,\r
+               "%s.game.out.%d", appData.cmailGameName, lastLoadGameNumber);\r
+       \r
+       f = fopen(string, "w");\r
+       if (appData.oldSaveStyle) {\r
+           SaveGameOldStyle(f); /* also closes the file */\r
+           \r
+           sprintf(string, "%s.pos.out", appData.cmailGameName);\r
+           f = fopen(string, "w");\r
+           SavePosition(f, 0, NULL); /* also closes the file */\r
+       } else {\r
+           fprintf(f, "{--------------\n");\r
+           PrintPosition(f, currentMove);\r
+           fprintf(f, "--------------}\n\n");\r
+           \r
+           SaveGame(f, 0, NULL); /* also closes the file*/\r
+       }\r
+       \r
+       cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE;\r
+       nCmailMovesRegistered ++;\r
+    } else if (nCmailGames == 1) {\r
+       DisplayError(_("You have not made a move yet"), 0);\r
+       return FALSE;\r
+    }\r
+\r
+    return TRUE;\r
+}\r
+\r
+void\r
+MailMoveEvent()\r
+{\r
+#if !WIN32\r
+    static char *partCommandString = "cmail -xv%s -remail -game %s 2>&1";\r
+    FILE *commandOutput;\r
+    char buffer[MSG_SIZ], msg[MSG_SIZ], string[MSG_SIZ];\r
+    int nBytes = 0;            /*  Suppress warnings on uninitialized variables    */\r
+    int nBuffers;\r
+    int i;\r
+    int archived;\r
+    char *arcDir;\r
+\r
+    if (! cmailMsgLoaded) {\r
+       DisplayError(_("The cmail message is not loaded.\nUse Reload CMail Message and make your move again."), 0);\r
+       return;\r
+    }\r
+\r
+    if (nCmailGames == nCmailResults) {\r
+       DisplayError(_("No unfinished games"), 0);\r
+       return;\r
+    }\r
+\r
+#if CMAIL_PROHIBIT_REMAIL\r
+    if (cmailMailedMove) {\r
+       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);\r
+       DisplayError(msg, 0);\r
+       return;\r
+    }\r
+#endif\r
+\r
+    if (! (cmailMailedMove || RegisterMove())) return;\r
+    \r
+    if (   cmailMailedMove\r
+       || (nCmailMovesRegistered + nCmailResults == nCmailGames)) {\r
+       sprintf(string, partCommandString,\r
+               appData.debugMode ? " -v" : "", appData.cmailGameName);\r
+       commandOutput = popen(string, "r");\r
+\r
+       if (commandOutput == NULL) {\r
+           DisplayError(_("Failed to invoke cmail"), 0);\r
+       } else {\r
+           for (nBuffers = 0; (! feof(commandOutput)); nBuffers ++) {\r
+               nBytes = fread(buffer, 1, MSG_SIZ - 1, commandOutput);\r
+           }\r
+           if (nBuffers > 1) {\r
+               (void) memcpy(msg, buffer + nBytes, MSG_SIZ - nBytes - 1);\r
+               (void) memcpy(msg + MSG_SIZ - nBytes - 1, buffer, nBytes);\r
+               nBytes = MSG_SIZ - 1;\r
+           } else {\r
+               (void) memcpy(msg, buffer, nBytes);\r
+           }\r
+           *(msg + nBytes) = '\0'; /* \0 for end-of-string*/\r
+\r
+           if(StrStr(msg, "Mailed cmail message to ") != NULL) {\r
+               cmailMailedMove = TRUE; /* Prevent >1 moves    */\r
+\r
+               archived = TRUE;\r
+               for (i = 0; i < nCmailGames; i ++) {\r
+                   if (cmailResult[i] == CMAIL_NOT_RESULT) {\r
+                       archived = FALSE;\r
+                   }\r
+               }\r
+               if (   archived\r
+                   && (   (arcDir = (char *) getenv("CMAIL_ARCDIR"))\r
+                       != NULL)) {\r
+                   sprintf(buffer, "%s/%s.%s.archive",\r
+                           arcDir,\r
+                           appData.cmailGameName,\r
+                           gameInfo.date);\r
+                   LoadGameFromFile(buffer, 1, buffer, FALSE);\r
+                   cmailMsgLoaded = FALSE;\r
+               }\r
+           }\r
+\r
+           DisplayInformation(msg);\r
+           pclose(commandOutput);\r
+       }\r
+    } else {\r
+       if ((*cmailMsg) != '\0') {\r
+           DisplayInformation(cmailMsg);\r
+       }\r
+    }\r
+\r
+    return;\r
+#endif /* !WIN32 */\r
+}\r
+\r
+char *\r
+CmailMsg()\r
+{\r
+#if WIN32\r
+    return NULL;\r
+#else\r
+    int  prependComma = 0;\r
+    char number[5];\r
+    char string[MSG_SIZ];      /* Space for game-list */\r
+    int  i;\r
+    \r
+    if (!cmailMsgLoaded) return "";\r
+\r
+    if (cmailMailedMove) {\r
+       sprintf(cmailMsg, _("Waiting for reply from opponent\n"));\r
+    } else {\r
+       /* Create a list of games left */\r
+       sprintf(string, "[");\r
+       for (i = 0; i < nCmailGames; i ++) {\r
+           if (! (   cmailMoveRegistered[i]\r
+                  || (cmailResult[i] == CMAIL_OLD_RESULT))) {\r
+               if (prependComma) {\r
+                   sprintf(number, ",%d", i + 1);\r
+               } else {\r
+                   sprintf(number, "%d", i + 1);\r
+                   prependComma = 1;\r
+               }\r
+               \r
+               strcat(string, number);\r
+           }\r
+       }\r
+       strcat(string, "]");\r
+\r
+       if (nCmailMovesRegistered + nCmailResults == 0) {\r
+           switch (nCmailGames) {\r
+             case 1:\r
+               sprintf(cmailMsg,\r
+                       _("Still need to make move for game\n"));\r
+               break;\r
+               \r
+             case 2:\r
+               sprintf(cmailMsg,\r
+                       _("Still need to make moves for both games\n"));\r
+               break;\r
+               \r
+             default:\r
+               sprintf(cmailMsg,\r
+                       _("Still need to make moves for all %d games\n"),\r
+                       nCmailGames);\r
+               break;\r
+           }\r
+       } else {\r
+           switch (nCmailGames - nCmailMovesRegistered - nCmailResults) {\r
+             case 1:\r
+               sprintf(cmailMsg,\r
+                       _("Still need to make a move for game %s\n"),\r
+                       string);\r
+               break;\r
+               \r
+             case 0:\r
+               if (nCmailResults == nCmailGames) {\r
+                   sprintf(cmailMsg, _("No unfinished games\n"));\r
+               } else {\r
+                   sprintf(cmailMsg, _("Ready to send mail\n"));\r
+               }\r
+               break;\r
+               \r
+             default:\r
+               sprintf(cmailMsg,\r
+                       _("Still need to make moves for games %s\n"),\r
+                       string);\r
+           }\r
+       }\r
+    }\r
+    return cmailMsg;\r
+#endif /* WIN32 */\r
+}\r
+\r
+void\r
+ResetGameEvent()\r
+{\r
+    if (gameMode == Training)\r
+      SetTrainingModeOff();\r
+\r
+    Reset(TRUE, TRUE);\r
+    cmailMsgLoaded = FALSE;\r
+    if (appData.icsActive) {\r
+      SendToICS(ics_prefix);\r
+      SendToICS("refresh\n");\r
+    }\r
+}\r
+\r
+void\r
+ExitEvent(status)\r
+     int status;\r
+{\r
+    exiting++;\r
+    if (exiting > 2) {\r
+      /* Give up on clean exit */\r
+      exit(status);\r
+    }\r
+    if (exiting > 1) {\r
+      /* Keep trying for clean exit */\r
+      return;\r
+    }\r
+\r
+    if (appData.icsActive && appData.colorize) Colorize(ColorNone, FALSE);\r
+\r
+    if (telnetISR != NULL) {\r
+      RemoveInputSource(telnetISR);\r
+    }\r
+    if (icsPR != NoProc) {\r
+      DestroyChildProcess(icsPR, TRUE);\r
+    }\r
+#if 0\r
+    /* Save game if resource set and not already saved by GameEnds() */\r
+    if ((gameInfo.resultDetails == NULL || errorExitFlag )\r
+                             && forwardMostMove > 0) {\r
+      if (*appData.saveGameFile != NULLCHAR) {\r
+       SaveGameToFile(appData.saveGameFile, TRUE);\r
+      } else if (appData.autoSaveGames) {\r
+       AutoSaveGame();\r
+      }\r
+      if (*appData.savePositionFile != NULLCHAR) {\r
+       SavePositionToFile(appData.savePositionFile);\r
+      }\r
+    }\r
+    GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
+#else\r
+    /* [HGM] crash: leave writing PGN and position entirely to GameEnds() */\r
+    GameEnds(gameInfo.result, gameInfo.resultDetails==NULL ? "xboard exit" : gameInfo.resultDetails, GE_PLAYER);\r
+#endif\r
+    /* [HGM] crash: the above GameEnds() is a dud if another one was running */\r
+    /* make sure this other one finishes before killing it!                  */\r
+    if(endingGame) { int count = 0;\r
+        if(appData.debugMode) fprintf(debugFP, "ExitEvent() during GameEnds(), wait\n");\r
+        while(endingGame && count++ < 10) DoSleep(1);\r
+        if(appData.debugMode && endingGame) fprintf(debugFP, "GameEnds() seems stuck, proceed exiting\n");\r
+    }\r
+\r
+    /* Kill off chess programs */\r
+    if (first.pr != NoProc) {\r
+       ExitAnalyzeMode();\r
+        \r
+        DoSleep( appData.delayBeforeQuit );\r
+       SendToProgram("quit\n", &first);\r
+        DoSleep( appData.delayAfterQuit );\r
+       DestroyChildProcess(first.pr, 10 /* [AS] first.useSigterm */ );\r
+    }\r
+    if (second.pr != NoProc) {\r
+        DoSleep( appData.delayBeforeQuit );\r
+       SendToProgram("quit\n", &second);\r
+        DoSleep( appData.delayAfterQuit );\r
+       DestroyChildProcess(second.pr, 10 /* [AS] second.useSigterm */ );\r
+    }\r
+    if (first.isr != NULL) {\r
+       RemoveInputSource(first.isr);\r
+    }\r
+    if (second.isr != NULL) {\r
+       RemoveInputSource(second.isr);\r
+    }\r
+\r
+    ShutDownFrontEnd();\r
+    exit(status);\r
+}\r
+\r
+void\r
+PauseEvent()\r
+{\r
+    if (appData.debugMode)\r
+       fprintf(debugFP, "PauseEvent(): pausing %d\n", pausing);\r
+    if (pausing) {\r
+       pausing = FALSE;\r
+       ModeHighlight();\r
+       if (gameMode == MachinePlaysWhite ||\r
+           gameMode == MachinePlaysBlack) {\r
+           StartClocks();\r
+       } else {\r
+           DisplayBothClocks();\r
+       }\r
+       if (gameMode == PlayFromGameFile) {\r
+           if (appData.timeDelay >= 0) \r
+               AutoPlayGameLoop();\r
+       } else if (gameMode == IcsExamining && pauseExamInvalid) {\r
+           Reset(FALSE, TRUE);\r
+           SendToICS(ics_prefix);\r
+           SendToICS("refresh\n");\r
+       } else if (currentMove < forwardMostMove) {\r
+           ForwardInner(forwardMostMove);\r
+       }\r
+       pauseExamInvalid = FALSE;\r
+    } else {\r
+       switch (gameMode) {\r
+         default:\r
+           return;\r
+         case IcsExamining:\r
+           pauseExamForwardMostMove = forwardMostMove;\r
+           pauseExamInvalid = FALSE;\r
+           /* fall through */\r
+         case IcsObserving:\r
+         case IcsPlayingWhite:\r
+         case IcsPlayingBlack:\r
+           pausing = TRUE;\r
+           ModeHighlight();\r
+           return;\r
+         case PlayFromGameFile:\r
+           (void) StopLoadGameTimer();\r
+           pausing = TRUE;\r
+           ModeHighlight();\r
+           break;\r
+         case BeginningOfGame:\r
+           if (appData.icsActive) return;\r
+           /* else fall through */\r
+         case MachinePlaysWhite:\r
+         case MachinePlaysBlack:\r
+         case TwoMachinesPlay:\r
+           if (forwardMostMove == 0)\r
+             return;           /* don't pause if no one has moved */\r
+           if ((gameMode == MachinePlaysWhite &&\r
+                !WhiteOnMove(forwardMostMove)) ||\r
+               (gameMode == MachinePlaysBlack &&\r
+                WhiteOnMove(forwardMostMove))) {\r
+               StopClocks();\r
+           }\r
+           pausing = TRUE;\r
+           ModeHighlight();\r
+           break;\r
+       }\r
+    }\r
+}\r
+\r
+void\r
+EditCommentEvent()\r
+{\r
+    char title[MSG_SIZ];\r
+\r
+    if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) {\r
+       strcpy(title, _("Edit comment"));\r
+    } else {\r
+       sprintf(title, _("Edit comment on %d.%s%s"), (currentMove - 1) / 2 + 1,\r
+               WhiteOnMove(currentMove - 1) ? " " : ".. ",\r
+               parseList[currentMove - 1]);\r
+    }\r
+\r
+    EditCommentPopUp(currentMove, title, commentList[currentMove]);\r
+}\r
+\r
+\r
+void\r
+EditTagsEvent()\r
+{\r
+    char *tags = PGNTags(&gameInfo);\r
+    EditTagsPopUp(tags);\r
+    free(tags);\r
+}\r
+\r
+void\r
+AnalyzeModeEvent()\r
+{\r
+    if (appData.noChessProgram || gameMode == AnalyzeMode)\r
+      return;\r
+\r
+    if (gameMode != AnalyzeFile) {\r
+        if (!appData.icsEngineAnalyze) {\r
+               EditGameEvent();\r
+               if (gameMode != EditGame) return;\r
+        }\r
+       ResurrectChessProgram();\r
+       SendToProgram("analyze\n", &first);\r
+       first.analyzing = TRUE;\r
+       /*first.maybeThinking = TRUE;*/\r
+       first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
+       AnalysisPopUp(_("Analysis"),\r
+                     _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."));\r
+    }\r
+    if (!appData.icsEngineAnalyze) gameMode = AnalyzeMode;\r
+    pausing = FALSE;\r
+    ModeHighlight();\r
+    SetGameInfo();\r
+\r
+    StartAnalysisClock();\r
+    GetTimeMark(&lastNodeCountTime);\r
+    lastNodeCount = 0;\r
+}\r
+\r
+void\r
+AnalyzeFileEvent()\r
+{\r
+    if (appData.noChessProgram || gameMode == AnalyzeFile)\r
+      return;\r
+\r
+    if (gameMode != AnalyzeMode) {\r
+       EditGameEvent();\r
+       if (gameMode != EditGame) return;\r
+       ResurrectChessProgram();\r
+       SendToProgram("analyze\n", &first);\r
+       first.analyzing = TRUE;\r
+       /*first.maybeThinking = TRUE;*/\r
+       first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
+       AnalysisPopUp(_("Analysis"),\r
+                     _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."));\r
+    }\r
+    gameMode = AnalyzeFile;\r
+    pausing = FALSE;\r
+    ModeHighlight();\r
+    SetGameInfo();\r
+\r
+    StartAnalysisClock();\r
+    GetTimeMark(&lastNodeCountTime);\r
+    lastNodeCount = 0;\r
+}\r
+\r
+void\r
+MachineWhiteEvent()\r
+{\r
+    char buf[MSG_SIZ];\r
+    char *bookHit = NULL;\r
+\r
+    if (appData.noChessProgram || (gameMode == MachinePlaysWhite))\r
+      return;\r
+\r
+\r
+    if (gameMode == PlayFromGameFile || \r
+       gameMode == TwoMachinesPlay  || \r
+       gameMode == Training         || \r
+       gameMode == AnalyzeMode      || \r
+       gameMode == EndOfGame)\r
+       EditGameEvent();\r
+\r
+    if (gameMode == EditPosition) \r
+        EditPositionDone();\r
+\r
+    if (!WhiteOnMove(currentMove)) {\r
+       DisplayError(_("It is not White's turn"), 0);\r
+       return;\r
+    }\r
+  \r
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)\r
+      ExitAnalyzeMode();\r
+\r
+    if (gameMode == EditGame || gameMode == AnalyzeMode || \r
+       gameMode == AnalyzeFile)\r
+       TruncateGame();\r
+\r
+    ResurrectChessProgram();   /* in case it isn't running */\r
+    if(gameMode == BeginningOfGame) { /* [HGM] time odds: to get right odds in human mode */\r
+       gameMode = MachinePlaysWhite;\r
+       ResetClocks();\r
+    } else\r
+    gameMode = MachinePlaysWhite;\r
+    pausing = FALSE;\r
+    ModeHighlight();\r
+    SetGameInfo();\r
+    sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);\r
+    DisplayTitle(buf);\r
+    if (first.sendName) {\r
+      sprintf(buf, "name %s\n", gameInfo.black);\r
+      SendToProgram(buf, &first);\r
+    }\r
+    if (first.sendTime) {\r
+      if (first.useColors) {\r
+       SendToProgram("black\n", &first); /*gnu kludge*/\r
+      }\r
+      SendTimeRemaining(&first, TRUE);\r
+    }\r
+    if (first.useColors) {\r
+      SendToProgram("white\n", &first); // [HGM] book: send 'go' separately\r
+    }\r
+    bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move\r
+    SetMachineThinkingEnables();\r
+    first.maybeThinking = TRUE;\r
+    StartClocks();\r
+\r
+    if (appData.autoFlipView && !flipView) {\r
+      flipView = !flipView;\r
+      DrawPosition(FALSE, NULL);\r
+      DisplayBothClocks();       // [HGM] logo: clocks might have to be exchanged;\r
+    }\r
+\r
+    if(bookHit) { // [HGM] book: simulate book reply\r
+       static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.score = programStats.got_only_move = 0;\r
+       sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+    }\r
+}\r
+\r
+void\r
+MachineBlackEvent()\r
+{\r
+    char buf[MSG_SIZ];\r
+   char *bookHit = NULL;\r
+\r
+    if (appData.noChessProgram || (gameMode == MachinePlaysBlack))\r
+       return;\r
+\r
+\r
+    if (gameMode == PlayFromGameFile || \r
+       gameMode == TwoMachinesPlay  || \r
+       gameMode == Training         || \r
+       gameMode == AnalyzeMode      || \r
+       gameMode == EndOfGame)\r
+        EditGameEvent();\r
+\r
+    if (gameMode == EditPosition) \r
+        EditPositionDone();\r
+\r
+    if (WhiteOnMove(currentMove)) {\r
+       DisplayError(_("It is not Black's turn"), 0);\r
+       return;\r
+    }\r
+    \r
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)\r
+      ExitAnalyzeMode();\r
+\r
+    if (gameMode == EditGame || gameMode == AnalyzeMode || \r
+       gameMode == AnalyzeFile)\r
+       TruncateGame();\r
+\r
+    ResurrectChessProgram();   /* in case it isn't running */\r
+    gameMode = MachinePlaysBlack;\r
+    pausing = FALSE;\r
+    ModeHighlight();\r
+    SetGameInfo();\r
+    sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);\r
+    DisplayTitle(buf);\r
+    if (first.sendName) {\r
+      sprintf(buf, "name %s\n", gameInfo.white);\r
+      SendToProgram(buf, &first);\r
+    }\r
+    if (first.sendTime) {\r
+      if (first.useColors) {\r
+       SendToProgram("white\n", &first); /*gnu kludge*/\r
+      }\r
+      SendTimeRemaining(&first, FALSE);\r
+    }\r
+    if (first.useColors) {\r
+      SendToProgram("black\n", &first); // [HGM] book: 'go' sent separately\r
+    }\r
+    bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move\r
+    SetMachineThinkingEnables();\r
+    first.maybeThinking = TRUE;\r
+    StartClocks();\r
+\r
+    if (appData.autoFlipView && flipView) {\r
+      flipView = !flipView;\r
+      DrawPosition(FALSE, NULL);\r
+      DisplayBothClocks();       // [HGM] logo: clocks might have to be exchanged;\r
+    }\r
+    if(bookHit) { // [HGM] book: simulate book reply\r
+       static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.score = programStats.got_only_move = 0;\r
+       sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+    }\r
+}\r
+\r
+\r
+void\r
+DisplayTwoMachinesTitle()\r
+{\r
+    char buf[MSG_SIZ];\r
+    if (appData.matchGames > 0) {\r
+        if (first.twoMachinesColor[0] == 'w') {\r
+           sprintf(buf, "%s vs. %s (%d-%d-%d)",\r
+                   gameInfo.white, gameInfo.black,\r
+                   first.matchWins, second.matchWins,\r
+                   matchGame - 1 - (first.matchWins + second.matchWins));\r
+       } else {\r
+           sprintf(buf, "%s vs. %s (%d-%d-%d)",\r
+                   gameInfo.white, gameInfo.black,\r
+                   second.matchWins, first.matchWins,\r
+                   matchGame - 1 - (first.matchWins + second.matchWins));\r
+       }\r
+    } else {\r
+       sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);\r
+    }\r
+    DisplayTitle(buf);\r
+}\r
+\r
+void\r
+TwoMachinesEvent P((void))\r
+{\r
+    int i;\r
+    char buf[MSG_SIZ];\r
+    ChessProgramState *onmove;\r
+    char *bookHit = NULL;\r
+    \r
+    if (appData.noChessProgram) return;\r
+\r
+    switch (gameMode) {\r
+      case TwoMachinesPlay:\r
+       return;\r
+      case MachinePlaysWhite:\r
+      case MachinePlaysBlack:\r
+       if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {\r
+           DisplayError(_("Wait until your turn,\nor select Move Now"), 0);\r
+           return;\r
+       }\r
+       /* fall through */\r
+      case BeginningOfGame:\r
+      case PlayFromGameFile:\r
+      case EndOfGame:\r
+       EditGameEvent();\r
+       if (gameMode != EditGame) return;\r
+       break;\r
+      case EditPosition:\r
+       EditPositionDone();\r
+       break;\r
+      case AnalyzeMode:\r
+      case AnalyzeFile:\r
+       ExitAnalyzeMode();\r
+       break;\r
+      case EditGame:\r
+      default:\r
+       break;\r
+    }\r
+\r
+    forwardMostMove = currentMove;\r
+    ResurrectChessProgram();   /* in case first program isn't running */\r
+\r
+    if (second.pr == NULL) {\r
+       StartChessProgram(&second);\r
+       if (second.protocolVersion == 1) {\r
+         TwoMachinesEventIfReady();\r
+       } else {\r
+         /* kludge: allow timeout for initial "feature" command */\r
+         FreezeUI();\r
+         DisplayMessage("", _("Starting second chess program"));\r
+         ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);\r
+       }\r
+       return;\r
+    }\r
+    DisplayMessage("", "");\r
+    InitChessProgram(&second, FALSE);\r
+    SendToProgram("force\n", &second);\r
+    if (startedFromSetupPosition) {\r
+       SendBoard(&second, backwardMostMove);\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "Two Machines\n");\r
+    }\r
+    }\r
+    for (i = backwardMostMove; i < forwardMostMove; i++) {\r
+       SendMoveToProgram(i, &second);\r
+    }\r
+\r
+    gameMode = TwoMachinesPlay;\r
+    pausing = FALSE;\r
+    ModeHighlight();\r
+    SetGameInfo();\r
+    DisplayTwoMachinesTitle();\r
+    firstMove = TRUE;\r
+    if ((first.twoMachinesColor[0] == 'w') == WhiteOnMove(forwardMostMove)) {\r
+       onmove = &first;\r
+    } else {\r
+       onmove = &second;\r
+    }\r
+\r
+    SendToProgram(first.computerString, &first);\r
+    if (first.sendName) {\r
+      sprintf(buf, "name %s\n", second.tidy);\r
+      SendToProgram(buf, &first);\r
+    }\r
+    SendToProgram(second.computerString, &second);\r
+    if (second.sendName) {\r
+      sprintf(buf, "name %s\n", first.tidy);\r
+      SendToProgram(buf, &second);\r
+    }\r
+\r
+    ResetClocks();\r
+    if (!first.sendTime || !second.sendTime) {\r
+       timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
+       timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
+    }\r
+    if (onmove->sendTime) {\r
+      if (onmove->useColors) {\r
+       SendToProgram(onmove->other->twoMachinesColor, onmove); /*gnu kludge*/\r
+      }\r
+      SendTimeRemaining(onmove, WhiteOnMove(forwardMostMove));\r
+    }\r
+    if (onmove->useColors) {\r
+      SendToProgram(onmove->twoMachinesColor, onmove);\r
+    }\r
+    bookHit = SendMoveToBookUser(forwardMostMove-1, onmove, TRUE); // [HGM] book: send go or retrieve book move\r
+//    SendToProgram("go\n", onmove);\r
+    onmove->maybeThinking = TRUE;\r
+    SetMachineThinkingEnables();\r
+\r
+    StartClocks();\r
+\r
+    if(bookHit) { // [HGM] book: simulate book reply\r
+       static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.score = programStats.got_only_move = 0;\r
+       sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
+\r
+       strcpy(bookMove, "move ");\r
+       strcat(bookMove, bookHit);\r
+       HandleMachineMove(bookMove, &first);\r
+    }\r
+}\r
+\r
+void\r
+TrainingEvent()\r
+{\r
+    if (gameMode == Training) {\r
+      SetTrainingModeOff();\r
+      gameMode = PlayFromGameFile;\r
+      DisplayMessage("", _("Training mode off"));\r
+    } else {\r
+      gameMode = Training;\r
+      animateTraining = appData.animate;\r
+\r
+      /* make sure we are not already at the end of the game */\r
+      if (currentMove < forwardMostMove) {\r
+       SetTrainingModeOn();\r
+       DisplayMessage("", _("Training mode on"));\r
+      } else {\r
+       gameMode = PlayFromGameFile;\r
+       DisplayError(_("Already at end of game"), 0);\r
+      }\r
+    }\r
+    ModeHighlight();\r
+}\r
+\r
+void\r
+IcsClientEvent()\r
+{\r
+    if (!appData.icsActive) return;\r
+    switch (gameMode) {\r
+      case IcsPlayingWhite:\r
+      case IcsPlayingBlack:\r
+      case IcsObserving:\r
+      case IcsIdle:\r
+      case BeginningOfGame:\r
+      case IcsExamining:\r
+       return;\r
+\r
+      case EditGame:\r
+       break;\r
+\r
+      case EditPosition:\r
+       EditPositionDone();\r
+       break;\r
+\r
+      case AnalyzeMode:\r
+      case AnalyzeFile:\r
+       ExitAnalyzeMode();\r
+       break;\r
+       \r
+      default:\r
+       EditGameEvent();\r
+       break;\r
+    }\r
+\r
+    gameMode = IcsIdle;\r
+    ModeHighlight();\r
+    return;\r
+}\r
+\r
+\r
+void\r
+EditGameEvent()\r
+{\r
+    int i;\r
+\r
+    switch (gameMode) {\r
+      case Training:\r
+       SetTrainingModeOff();\r
+       break;\r
+      case MachinePlaysWhite:\r
+      case MachinePlaysBlack:\r
+      case BeginningOfGame:\r
+       SendToProgram("force\n", &first);\r
+       SetUserThinkingEnables();\r
+       break;\r
+      case PlayFromGameFile:\r
+       (void) StopLoadGameTimer();\r
+       if (gameFileFP != NULL) {\r
+           gameFileFP = NULL;\r
+       }\r
+       break;\r
+      case EditPosition:\r
+       EditPositionDone();\r
+       break;\r
+      case AnalyzeMode:\r
+      case AnalyzeFile:\r
+       ExitAnalyzeMode();\r
+       SendToProgram("force\n", &first);\r
+       break;\r
+      case TwoMachinesPlay:\r
+       GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
+       ResurrectChessProgram();\r
+       SetUserThinkingEnables();\r
+       break;\r
+      case EndOfGame:\r
+       ResurrectChessProgram();\r
+       break;\r
+      case IcsPlayingBlack:\r
+      case IcsPlayingWhite:\r
+       DisplayError(_("Warning: You are still playing a game"), 0);\r
+       break;\r
+      case IcsObserving:\r
+       DisplayError(_("Warning: You are still observing a game"), 0);\r
+       break;\r
+      case IcsExamining:\r
+       DisplayError(_("Warning: You are still examining a game"), 0);\r
+       break;\r
+      case IcsIdle:\r
+       break;\r
+      case EditGame:\r
+      default:\r
+       return;\r
+    }\r
+    \r
+    pausing = FALSE;\r
+    StopClocks();\r
+    first.offeredDraw = second.offeredDraw = 0;\r
+\r
+    if (gameMode == PlayFromGameFile) {\r
+       whiteTimeRemaining = timeRemaining[0][currentMove];\r
+       blackTimeRemaining = timeRemaining[1][currentMove];\r
+       DisplayTitle("");\r
+    }\r
+\r
+    if (gameMode == MachinePlaysWhite ||\r
+       gameMode == MachinePlaysBlack ||\r
+       gameMode == TwoMachinesPlay ||\r
+       gameMode == EndOfGame) {\r
+       i = forwardMostMove;\r
+       while (i > currentMove) {\r
+           SendToProgram("undo\n", &first);\r
+           i--;\r
+       }\r
+       whiteTimeRemaining = timeRemaining[0][currentMove];\r
+       blackTimeRemaining = timeRemaining[1][currentMove];\r
+       DisplayBothClocks();\r
+       if (whiteFlag || blackFlag) {\r
+           whiteFlag = blackFlag = 0;\r
+       }\r
+       DisplayTitle("");\r
+    }          \r
+    \r
+    gameMode = EditGame;\r
+    ModeHighlight();\r
+    SetGameInfo();\r
+}\r
+\r
+\r
+void\r
+EditPositionEvent()\r
+{\r
+    if (gameMode == EditPosition) {\r
+       EditGameEvent();\r
+       return;\r
+    }\r
+    \r
+    EditGameEvent();\r
+    if (gameMode != EditGame) return;\r
+    \r
+    gameMode = EditPosition;\r
+    ModeHighlight();\r
+    SetGameInfo();\r
+    if (currentMove > 0)\r
+      CopyBoard(boards[0], boards[currentMove]);\r
+    \r
+    blackPlaysFirst = !WhiteOnMove(currentMove);\r
+    ResetClocks();\r
+    currentMove = forwardMostMove = backwardMostMove = 0;\r
+    HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+    DisplayMove(-1);\r
+}\r
+\r
+void\r
+ExitAnalyzeMode()\r
+{\r
+    /* [DM] icsEngineAnalyze - possible call from other functions */\r
+    if (appData.icsEngineAnalyze) {\r
+        appData.icsEngineAnalyze = FALSE;\r
+\r
+        DisplayMessage("",_("Close ICS engine analyze..."));\r
+    }\r
+    if (first.analysisSupport && first.analyzing) {\r
+      SendToProgram("exit\n", &first);\r
+      first.analyzing = FALSE;\r
+    }\r
+    AnalysisPopDown();\r
+    thinkOutput[0] = NULLCHAR;\r
+}\r
+\r
+void\r
+EditPositionDone()\r
+{\r
+    startedFromSetupPosition = TRUE;\r
+    InitChessProgram(&first, FALSE);\r
+    SendToProgram("force\n", &first);\r
+    if (blackPlaysFirst) {\r
+       strcpy(moveList[0], "");\r
+       strcpy(parseList[0], "");\r
+       currentMove = forwardMostMove = backwardMostMove = 1;\r
+       CopyBoard(boards[1], boards[0]);\r
+       /* [HGM] copy rights as well, as this code is also used after pasting a FEN */\r
+       { int i;\r
+         epStatus[1] = epStatus[0];\r
+         for(i=0; i<nrCastlingRights; i++) castlingRights[1][i] = castlingRights[0][i];\r
+       }\r
+    } else {\r
+       currentMove = forwardMostMove = backwardMostMove = 0;\r
+    }\r
+    SendBoard(&first, forwardMostMove);\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "EditPosDone\n");\r
+    }\r
+    DisplayTitle("");\r
+    timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
+    timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
+    gameMode = EditGame;\r
+    ModeHighlight();\r
+    HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
+    ClearHighlights(); /* [AS] */\r
+}\r
+\r
+/* Pause for `ms' milliseconds */\r
+/* !! Ugh, this is a kludge. Fix it sometime. --tpm */\r
+void\r
+TimeDelay(ms)\r
+     long ms;\r
+{\r
+    TimeMark m1, m2;\r
+\r
+    GetTimeMark(&m1);\r
+    do {\r
+       GetTimeMark(&m2);\r
+    } while (SubtractTimeMarks(&m2, &m1) < ms);\r
+}\r
+\r
+/* !! Ugh, this is a kludge. Fix it sometime. --tpm */\r
+void\r
+SendMultiLineToICS(buf)\r
+     char *buf;\r
+{\r
+    char temp[MSG_SIZ+1], *p;\r
+    int len;\r
+\r
+    len = strlen(buf);\r
+    if (len > MSG_SIZ)\r
+      len = MSG_SIZ;\r
+  \r
+    strncpy(temp, buf, len);\r
+    temp[len] = 0;\r
+\r
+    p = temp;\r
+    while (*p) {\r
+       if (*p == '\n' || *p == '\r')\r
+         *p = ' ';\r
+       ++p;\r
+    }\r
+\r
+    strcat(temp, "\n");\r
+    SendToICS(temp);\r
+    SendToPlayer(temp, strlen(temp));\r
+}\r
+\r
+void\r
+SetWhiteToPlayEvent()\r
+{\r
+    if (gameMode == EditPosition) {\r
+       blackPlaysFirst = FALSE;\r
+       DisplayBothClocks();    /* works because currentMove is 0 */\r
+    } else if (gameMode == IcsExamining) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("tomove white\n");\r
+    }\r
+}\r
+\r
+void\r
+SetBlackToPlayEvent()\r
+{\r
+    if (gameMode == EditPosition) {\r
+       blackPlaysFirst = TRUE;\r
+       currentMove = 1;        /* kludge */\r
+       DisplayBothClocks();\r
+       currentMove = 0;\r
+    } else if (gameMode == IcsExamining) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("tomove black\n");\r
+    }\r
+}\r
+\r
+void\r
+EditPositionMenuEvent(selection, x, y)\r
+     ChessSquare selection;\r
+     int x, y;\r
+{\r
+    char buf[MSG_SIZ];\r
+    ChessSquare piece = boards[0][y][x];\r
+\r
+    if (gameMode != EditPosition && gameMode != IcsExamining) return;\r
+\r
+    switch (selection) {\r
+      case ClearBoard:\r
+       if (gameMode == IcsExamining && ics_type == ICS_FICS) {\r
+           SendToICS(ics_prefix);\r
+           SendToICS("bsetup clear\n");\r
+       } else if (gameMode == IcsExamining && ics_type == ICS_ICC) {\r
+           SendToICS(ics_prefix);\r
+           SendToICS("clearboard\n");\r
+       } else {\r
+            for (x = 0; x < BOARD_WIDTH; x++) { ChessSquare p = EmptySquare;\r
+               if(x == BOARD_LEFT-1 || x == BOARD_RGHT) p = (ChessSquare) 0; /* [HGM] holdings */\r
+                for (y = 0; y < BOARD_HEIGHT; y++) {\r
+                   if (gameMode == IcsExamining) {\r
+                       if (boards[currentMove][y][x] != EmptySquare) {\r
+                           sprintf(buf, "%sx@%c%c\n", ics_prefix,\r
+                                    AAA + x, ONE + y);\r
+                           SendToICS(buf);\r
+                       }\r
+                   } else {\r
+                       boards[0][y][x] = p;\r
+                   }\r
+               }\r
+           }\r
+       }\r
+       if (gameMode == EditPosition) {\r
+           DrawPosition(FALSE, boards[0]);\r
+       }\r
+       break;\r
+\r
+      case WhitePlay:\r
+       SetWhiteToPlayEvent();\r
+       break;\r
+\r
+      case BlackPlay:\r
+       SetBlackToPlayEvent();\r
+       break;\r
+\r
+      case EmptySquare:\r
+       if (gameMode == IcsExamining) {\r
+            sprintf(buf, "%sx@%c%c\n", ics_prefix, AAA + x, ONE + y);\r
+           SendToICS(buf);\r
+       } else {\r
+           boards[0][y][x] = EmptySquare;\r
+           DrawPosition(FALSE, boards[0]);\r
+       }\r
+       break;\r
+\r
+      case PromotePiece:\r
+        if(piece >= (int)WhitePawn && piece < (int)WhiteMan ||\r
+           piece >= (int)BlackPawn && piece < (int)BlackMan   ) {\r
+            selection = (ChessSquare) (PROMOTED piece);\r
+        } else if(piece == EmptySquare) selection = WhiteSilver;\r
+        else selection = (ChessSquare)((int)piece - 1);\r
+        goto defaultlabel;\r
+\r
+      case DemotePiece:\r
+        if(piece > (int)WhiteMan && piece <= (int)WhiteKing ||\r
+           piece > (int)BlackMan && piece <= (int)BlackKing   ) {\r
+            selection = (ChessSquare) (DEMOTED piece);\r
+        } else if(piece == EmptySquare) selection = BlackSilver;\r
+        else selection = (ChessSquare)((int)piece + 1);       \r
+        goto defaultlabel;\r
+\r
+      case WhiteQueen:\r
+      case BlackQueen:\r
+        if(gameInfo.variant == VariantShatranj ||\r
+           gameInfo.variant == VariantXiangqi  ||\r
+           gameInfo.variant == VariantCourier    )\r
+            selection = (ChessSquare)((int)selection - (int)WhiteQueen + (int)WhiteFerz);\r
+        goto defaultlabel;\r
+\r
+      case WhiteKing:\r
+      case BlackKing:\r
+        if(gameInfo.variant == VariantXiangqi)\r
+            selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteWazir);\r
+        if(gameInfo.variant == VariantKnightmate)\r
+            selection = (ChessSquare)((int)selection - (int)WhiteKing + (int)WhiteUnicorn);\r
+      default:\r
+        defaultlabel:\r
+       if (gameMode == IcsExamining) {\r
+           sprintf(buf, "%s%c@%c%c\n", ics_prefix,\r
+                    PieceToChar(selection), AAA + x, ONE + y);\r
+           SendToICS(buf);\r
+       } else {\r
+           boards[0][y][x] = selection;\r
+           DrawPosition(FALSE, boards[0]);\r
+       }\r
+       break;\r
+    }\r
+}\r
+\r
+\r
+void\r
+DropMenuEvent(selection, x, y)\r
+     ChessSquare selection;\r
+     int x, y;\r
+{\r
+    ChessMove moveType;\r
+\r
+    switch (gameMode) {\r
+      case IcsPlayingWhite:\r
+      case MachinePlaysBlack:\r
+       if (!WhiteOnMove(currentMove)) {\r
+           DisplayMoveError(_("It is Black's turn"));\r
+           return;\r
+       }\r
+       moveType = WhiteDrop;\r
+       break;\r
+      case IcsPlayingBlack:\r
+      case MachinePlaysWhite:\r
+       if (WhiteOnMove(currentMove)) {\r
+           DisplayMoveError(_("It is White's turn"));\r
+           return;\r
+       }\r
+       moveType = BlackDrop;\r
+       break;\r
+      case EditGame:\r
+       moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;\r
+       break;\r
+      default:\r
+       return;\r
+    }\r
+\r
+    if (moveType == BlackDrop && selection < BlackPawn) {\r
+      selection = (ChessSquare) ((int) selection\r
+                                + (int) BlackPawn - (int) WhitePawn);\r
+    }\r
+    if (boards[currentMove][y][x] != EmptySquare) {\r
+       DisplayMoveError(_("That square is occupied"));\r
+       return;\r
+    }\r
+\r
+    FinishMove(moveType, (int) selection, DROP_RANK, x, y, NULLCHAR);\r
+}\r
+\r
+void\r
+AcceptEvent()\r
+{\r
+    /* Accept a pending offer of any kind from opponent */\r
+    \r
+    if (appData.icsActive) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("accept\n");\r
+    } else if (cmailMsgLoaded) {\r
+       if (currentMove == cmailOldMove &&\r
+           commentList[cmailOldMove] != NULL &&\r
+           StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?\r
+                  "Black offers a draw" : "White offers a draw")) {\r
+           TruncateGame();\r
+           GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);\r
+           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;\r
+       } else {\r
+           DisplayError(_("There is no pending offer on this move"), 0);\r
+           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
+       }\r
+    } else {\r
+       /* Not used for offers from chess program */\r
+    }\r
+}\r
+\r
+void\r
+DeclineEvent()\r
+{\r
+    /* Decline a pending offer of any kind from opponent */\r
+    \r
+    if (appData.icsActive) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("decline\n");\r
+    } else if (cmailMsgLoaded) {\r
+       if (currentMove == cmailOldMove &&\r
+           commentList[cmailOldMove] != NULL &&\r
+           StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?\r
+                  "Black offers a draw" : "White offers a draw")) {\r
+#ifdef NOTDEF\r
+           AppendComment(cmailOldMove, "Draw declined");\r
+           DisplayComment(cmailOldMove - 1, "Draw declined");\r
+#endif /*NOTDEF*/\r
+       } else {\r
+           DisplayError(_("There is no pending offer on this move"), 0);\r
+       }\r
+    } else {\r
+       /* Not used for offers from chess program */\r
+    }\r
+}\r
+\r
+void\r
+RematchEvent()\r
+{\r
+    /* Issue ICS rematch command */\r
+    if (appData.icsActive) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("rematch\n");\r
+    }\r
+}\r
+\r
+void\r
+CallFlagEvent()\r
+{\r
+    /* Call your opponent's flag (claim a win on time) */\r
+    if (appData.icsActive) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("flag\n");\r
+    } else {\r
+       switch (gameMode) {\r
+         default:\r
+           return;\r
+         case MachinePlaysWhite:\r
+           if (whiteFlag) {\r
+               if (blackFlag)\r
+                 GameEnds(GameIsDrawn, "Both players ran out of time",\r
+                          GE_PLAYER);\r
+               else\r
+                 GameEnds(BlackWins, "Black wins on time", GE_PLAYER);\r
+           } else {\r
+               DisplayError(_("Your opponent is not out of time"), 0);\r
+           }\r
+           break;\r
+         case MachinePlaysBlack:\r
+           if (blackFlag) {\r
+               if (whiteFlag)\r
+                 GameEnds(GameIsDrawn, "Both players ran out of time",\r
+                          GE_PLAYER);\r
+               else\r
+                 GameEnds(WhiteWins, "White wins on time", GE_PLAYER);\r
+           } else {\r
+               DisplayError(_("Your opponent is not out of time"), 0);\r
+           }\r
+           break;\r
+       }\r
+    }\r
+}\r
+\r
+void\r
+DrawEvent()\r
+{\r
+    /* Offer draw or accept pending draw offer from opponent */\r
+    \r
+    if (appData.icsActive) {\r
+       /* Note: tournament rules require draw offers to be\r
+          made after you make your move but before you punch\r
+          your clock.  Currently ICS doesn't let you do that;\r
+          instead, you immediately punch your clock after making\r
+          a move, but you can offer a draw at any time. */\r
+       \r
+        SendToICS(ics_prefix);\r
+       SendToICS("draw\n");\r
+    } else if (cmailMsgLoaded) {\r
+       if (currentMove == cmailOldMove &&\r
+           commentList[cmailOldMove] != NULL &&\r
+           StrStr(commentList[cmailOldMove], WhiteOnMove(cmailOldMove) ?\r
+                  "Black offers a draw" : "White offers a draw")) {\r
+           GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);\r
+           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;\r
+       } else if (currentMove == cmailOldMove + 1) {\r
+           char *offer = WhiteOnMove(cmailOldMove) ?\r
+             "White offers a draw" : "Black offers a draw";\r
+           AppendComment(currentMove, offer);\r
+           DisplayComment(currentMove - 1, offer);\r
+           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_DRAW;\r
+       } else {\r
+           DisplayError(_("You must make your move before offering a draw"), 0);\r
+           cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
+       }\r
+    } else if (first.offeredDraw) {\r
+       GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);\r
+    } else {\r
+        if (first.sendDrawOffers) {\r
+           SendToProgram("draw\n", &first);\r
+            userOfferedDraw = TRUE;\r
+       }\r
+    }\r
+}\r
+\r
+void\r
+AdjournEvent()\r
+{\r
+    /* Offer Adjourn or accept pending Adjourn offer from opponent */\r
+    \r
+    if (appData.icsActive) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("adjourn\n");\r
+    } else {\r
+       /* Currently GNU Chess doesn't offer or accept Adjourns */\r
+    }\r
+}\r
+\r
+\r
+void\r
+AbortEvent()\r
+{\r
+    /* Offer Abort or accept pending Abort offer from opponent */\r
+    \r
+    if (appData.icsActive) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("abort\n");\r
+    } else {\r
+       GameEnds(GameUnfinished, "Game aborted", GE_PLAYER);\r
+    }\r
+}\r
+\r
+void\r
+ResignEvent()\r
+{\r
+    /* Resign.  You can do this even if it's not your turn. */\r
+    \r
+    if (appData.icsActive) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("resign\n");\r
+    } else {\r
+       switch (gameMode) {\r
+         case MachinePlaysWhite:\r
+           GameEnds(WhiteWins, "Black resigns", GE_PLAYER);\r
+           break;\r
+         case MachinePlaysBlack:\r
+           GameEnds(BlackWins, "White resigns", GE_PLAYER);\r
+           break;\r
+         case EditGame:\r
+           if (cmailMsgLoaded) {\r
+               TruncateGame();\r
+               if (WhiteOnMove(cmailOldMove)) {\r
+                   GameEnds(BlackWins, "White resigns", GE_PLAYER);\r
+               } else {\r
+                   GameEnds(WhiteWins, "Black resigns", GE_PLAYER);\r
+               }\r
+               cmailMoveType[lastLoadGameNumber - 1] = CMAIL_RESIGN;\r
+           }\r
+           break;\r
+         default:\r
+           break;\r
+       }\r
+    }\r
+}\r
+\r
+\r
+void\r
+StopObservingEvent()\r
+{\r
+    /* Stop observing current games */\r
+    SendToICS(ics_prefix);\r
+    SendToICS("unobserve\n");\r
+}\r
+\r
+void\r
+StopExaminingEvent()\r
+{\r
+    /* Stop observing current game */\r
+    SendToICS(ics_prefix);\r
+    SendToICS("unexamine\n");\r
+}\r
+\r
+void\r
+ForwardInner(target)\r
+     int target;\r
+{\r
+    int limit;\r
+\r
+    if (appData.debugMode)\r
+       fprintf(debugFP, "ForwardInner(%d), current %d, forward %d\n",\r
+               target, currentMove, forwardMostMove);\r
+\r
+    if (gameMode == EditPosition)\r
+      return;\r
+\r
+    if (gameMode == PlayFromGameFile && !pausing)\r
+      PauseEvent();\r
+    \r
+    if (gameMode == IcsExamining && pausing)\r
+      limit = pauseExamForwardMostMove;\r
+    else\r
+      limit = forwardMostMove;\r
+    \r
+    if (target > limit) target = limit;\r
+\r
+    if (target > 0 && moveList[target - 1][0]) {\r
+       int fromX, fromY, toX, toY;\r
+        toX = moveList[target - 1][2] - AAA;\r
+        toY = moveList[target - 1][3] - ONE;\r
+       if (moveList[target - 1][1] == '@') {\r
+           if (appData.highlightLastMove) {\r
+               SetHighlights(-1, -1, toX, toY);\r
+           }\r
+       } else {\r
+            fromX = moveList[target - 1][0] - AAA;\r
+            fromY = moveList[target - 1][1] - ONE;\r
+           if (target == currentMove + 1) {\r
+               AnimateMove(boards[currentMove], fromX, fromY, toX, toY);\r
+           }\r
+           if (appData.highlightLastMove) {\r
+               SetHighlights(fromX, fromY, toX, toY);\r
+           }\r
+       }\r
+    }\r
+    if (gameMode == EditGame || gameMode == AnalyzeMode || \r
+       gameMode == Training || gameMode == PlayFromGameFile || \r
+       gameMode == AnalyzeFile) {\r
+       while (currentMove < target) {\r
+           SendMoveToProgram(currentMove++, &first);\r
+       }\r
+    } else {\r
+       currentMove = target;\r
+    }\r
+    \r
+    if (gameMode == EditGame || gameMode == EndOfGame) {\r
+       whiteTimeRemaining = timeRemaining[0][currentMove];\r
+       blackTimeRemaining = timeRemaining[1][currentMove];\r
+    }\r
+    DisplayBothClocks();\r
+    DisplayMove(currentMove - 1);\r
+    DrawPosition(FALSE, boards[currentMove]);\r
+    HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
+    if ( !matchMode && gameMode != Training) { // [HGM] PV info: routine tests if empty\r
+       DisplayComment(currentMove - 1, commentList[currentMove]);\r
+    }\r
+}\r
+\r
+\r
+void\r
+ForwardEvent()\r
+{\r
+    if (gameMode == IcsExamining && !pausing) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("forward\n");\r
+    } else {\r
+       ForwardInner(currentMove + 1);\r
+    }\r
+}\r
+\r
+void\r
+ToEndEvent()\r
+{\r
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+       /* to optimze, we temporarily turn off analysis mode while we feed\r
+        * the remaining moves to the engine. Otherwise we get analysis output\r
+        * after each move.\r
+        */ \r
+        if (first.analysisSupport) {\r
+         SendToProgram("exit\nforce\n", &first);\r
+         first.analyzing = FALSE;\r
+       }\r
+    }\r
+       \r
+    if (gameMode == IcsExamining && !pausing) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("forward 999999\n");\r
+    } else {\r
+       ForwardInner(forwardMostMove);\r
+    }\r
+\r
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+       /* we have fed all the moves, so reactivate analysis mode */\r
+       SendToProgram("analyze\n", &first);\r
+       first.analyzing = TRUE;\r
+       /*first.maybeThinking = TRUE;*/\r
+       first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
+    }\r
+}\r
+\r
+void\r
+BackwardInner(target)\r
+     int target;\r
+{\r
+    int full_redraw = TRUE; /* [AS] Was FALSE, had to change it! */\r
+\r
+    if (appData.debugMode)\r
+       fprintf(debugFP, "BackwardInner(%d), current %d, forward %d\n",\r
+               target, currentMove, forwardMostMove);\r
+\r
+    if (gameMode == EditPosition) return;\r
+    if (currentMove <= backwardMostMove) {\r
+       ClearHighlights();\r
+       DrawPosition(full_redraw, boards[currentMove]);\r
+       return;\r
+    }\r
+    if (gameMode == PlayFromGameFile && !pausing)\r
+      PauseEvent();\r
+    \r
+    if (moveList[target][0]) {\r
+       int fromX, fromY, toX, toY;\r
+        toX = moveList[target][2] - AAA;\r
+        toY = moveList[target][3] - ONE;\r
+       if (moveList[target][1] == '@') {\r
+           if (appData.highlightLastMove) {\r
+               SetHighlights(-1, -1, toX, toY);\r
+           }\r
+       } else {\r
+            fromX = moveList[target][0] - AAA;\r
+            fromY = moveList[target][1] - ONE;\r
+           if (target == currentMove - 1) {\r
+               AnimateMove(boards[currentMove], toX, toY, fromX, fromY);\r
+           }\r
+           if (appData.highlightLastMove) {\r
+               SetHighlights(fromX, fromY, toX, toY);\r
+           }\r
+       }\r
+    }\r
+    if (gameMode == EditGame || gameMode==AnalyzeMode ||\r
+       gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {\r
+       while (currentMove > target) {\r
+           SendToProgram("undo\n", &first);\r
+           currentMove--;\r
+       }\r
+    } else {\r
+       currentMove = target;\r
+    }\r
+    \r
+    if (gameMode == EditGame || gameMode == EndOfGame) {\r
+       whiteTimeRemaining = timeRemaining[0][currentMove];\r
+       blackTimeRemaining = timeRemaining[1][currentMove];\r
+    }\r
+    DisplayBothClocks();\r
+    DisplayMove(currentMove - 1);\r
+    DrawPosition(full_redraw, boards[currentMove]);\r
+    HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
+    // [HGM] PV info: routine tests if comment empty\r
+    DisplayComment(currentMove - 1, commentList[currentMove]);\r
+}\r
+\r
+void\r
+BackwardEvent()\r
+{\r
+    if (gameMode == IcsExamining && !pausing) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("backward\n");\r
+    } else {\r
+       BackwardInner(currentMove - 1);\r
+    }\r
+}\r
+\r
+void\r
+ToStartEvent()\r
+{\r
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+       /* to optimze, we temporarily turn off analysis mode while we undo\r
+        * all the moves. Otherwise we get analysis output after each undo.\r
+        */ \r
+        if (first.analysisSupport) {\r
+         SendToProgram("exit\nforce\n", &first);\r
+         first.analyzing = FALSE;\r
+       }\r
+    }\r
+\r
+    if (gameMode == IcsExamining && !pausing) {\r
+        SendToICS(ics_prefix);\r
+       SendToICS("backward 999999\n");\r
+    } else {\r
+       BackwardInner(backwardMostMove);\r
+    }\r
+\r
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+       /* we have fed all the moves, so reactivate analysis mode */\r
+       SendToProgram("analyze\n", &first);\r
+       first.analyzing = TRUE;\r
+       /*first.maybeThinking = TRUE;*/\r
+       first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
+    }\r
+}\r
+\r
+void\r
+ToNrEvent(int to)\r
+{\r
+  if (gameMode == PlayFromGameFile && !pausing) PauseEvent();\r
+  if (to >= forwardMostMove) to = forwardMostMove;\r
+  if (to <= backwardMostMove) to = backwardMostMove;\r
+  if (to < currentMove) {\r
+    BackwardInner(to);\r
+  } else {\r
+    ForwardInner(to);\r
+  }\r
+}\r
+\r
+void\r
+RevertEvent()\r
+{\r
+    if (gameMode != IcsExamining) {\r
+       DisplayError(_("You are not examining a game"), 0);\r
+       return;\r
+    }\r
+    if (pausing) {\r
+       DisplayError(_("You can't revert while pausing"), 0);\r
+       return;\r
+    }\r
+    SendToICS(ics_prefix);\r
+    SendToICS("revert\n");\r
+}\r
+\r
+void\r
+RetractMoveEvent()\r
+{\r
+    switch (gameMode) {\r
+      case MachinePlaysWhite:\r
+      case MachinePlaysBlack:\r
+       if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {\r
+           DisplayError(_("Wait until your turn,\nor select Move Now"), 0);\r
+           return;\r
+       }\r
+       if (forwardMostMove < 2) return;\r
+       currentMove = forwardMostMove = forwardMostMove - 2;\r
+       whiteTimeRemaining = timeRemaining[0][currentMove];\r
+       blackTimeRemaining = timeRemaining[1][currentMove];\r
+       DisplayBothClocks();\r
+       DisplayMove(currentMove - 1);\r
+       ClearHighlights();/*!! could figure this out*/\r
+       DrawPosition(TRUE, boards[currentMove]); /* [AS] Changed to full redraw! */\r
+       SendToProgram("remove\n", &first);\r
+       /*first.maybeThinking = TRUE;*/ /* GNU Chess does not ponder here */\r
+       break;\r
+\r
+      case BeginningOfGame:\r
+      default:\r
+       break;\r
+\r
+      case IcsPlayingWhite:\r
+      case IcsPlayingBlack:\r
+       if (WhiteOnMove(forwardMostMove) == (gameMode == IcsPlayingWhite)) {\r
+           SendToICS(ics_prefix);\r
+           SendToICS("takeback 2\n");\r
+       } else {\r
+           SendToICS(ics_prefix);\r
+           SendToICS("takeback 1\n");\r
+       }\r
+       break;\r
+    }\r
+}\r
+\r
+void\r
+MoveNowEvent()\r
+{\r
+    ChessProgramState *cps;\r
+\r
+    switch (gameMode) {\r
+      case MachinePlaysWhite:\r
+       if (!WhiteOnMove(forwardMostMove)) {\r
+           DisplayError(_("It is your turn"), 0);\r
+           return;\r
+       }\r
+       cps = &first;\r
+       break;\r
+      case MachinePlaysBlack:\r
+       if (WhiteOnMove(forwardMostMove)) {\r
+           DisplayError(_("It is your turn"), 0);\r
+           return;\r
+       }\r
+       cps = &first;\r
+       break;\r
+      case TwoMachinesPlay:\r
+       if (WhiteOnMove(forwardMostMove) ==\r
+           (first.twoMachinesColor[0] == 'w')) {\r
+           cps = &first;\r
+       } else {\r
+           cps = &second;\r
+       }\r
+       break;\r
+      case BeginningOfGame:\r
+      default:\r
+       return;\r
+    }\r
+    SendToProgram("?\n", cps);\r
+}\r
+\r
+void\r
+TruncateGameEvent()\r
+{\r
+    EditGameEvent();\r
+    if (gameMode != EditGame) return;\r
+    TruncateGame();\r
+}\r
+\r
+void\r
+TruncateGame()\r
+{\r
+    if (forwardMostMove > currentMove) {\r
+       if (gameInfo.resultDetails != NULL) {\r
+           free(gameInfo.resultDetails);\r
+           gameInfo.resultDetails = NULL;\r
+           gameInfo.result = GameUnfinished;\r
+       }\r
+       forwardMostMove = currentMove;\r
+       HistorySet(parseList, backwardMostMove, forwardMostMove,\r
+                  currentMove-1);\r
+    }\r
+}\r
+\r
+void\r
+HintEvent()\r
+{\r
+    if (appData.noChessProgram) return;\r
+    switch (gameMode) {\r
+      case MachinePlaysWhite:\r
+       if (WhiteOnMove(forwardMostMove)) {\r
+           DisplayError(_("Wait until your turn"), 0);\r
+           return;\r
+       }\r
+       break;\r
+      case BeginningOfGame:\r
+      case MachinePlaysBlack:\r
+       if (!WhiteOnMove(forwardMostMove)) {\r
+           DisplayError(_("Wait until your turn"), 0);\r
+           return;\r
+       }\r
+       break;\r
+      default:\r
+       DisplayError(_("No hint available"), 0);\r
+       return;\r
+    }\r
+    SendToProgram("hint\n", &first);\r
+    hintRequested = TRUE;\r
+}\r
+\r
+void\r
+BookEvent()\r
+{\r
+    if (appData.noChessProgram) return;\r
+    switch (gameMode) {\r
+      case MachinePlaysWhite:\r
+       if (WhiteOnMove(forwardMostMove)) {\r
+           DisplayError(_("Wait until your turn"), 0);\r
+           return;\r
+       }\r
+       break;\r
+      case BeginningOfGame:\r
+      case MachinePlaysBlack:\r
+       if (!WhiteOnMove(forwardMostMove)) {\r
+           DisplayError(_("Wait until your turn"), 0);\r
+           return;\r
+       }\r
+       break;\r
+      case EditPosition:\r
+       EditPositionDone();\r
+       break;\r
+      case TwoMachinesPlay:\r
+       return;\r
+      default:\r
+       break;\r
+    }\r
+    SendToProgram("bk\n", &first);\r
+    bookOutput[0] = NULLCHAR;\r
+    bookRequested = TRUE;\r
+}\r
+\r
+void\r
+AboutGameEvent()\r
+{\r
+    char *tags = PGNTags(&gameInfo);\r
+    TagsPopUp(tags, CmailMsg());\r
+    free(tags);\r
+}\r
+\r
+/* end button procedures */\r
+\r
+void\r
+PrintPosition(fp, move)\r
+     FILE *fp;\r
+     int move;\r
+{\r
+    int i, j;\r
+    \r
+    for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+        for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {\r
+           char c = PieceToChar(boards[move][i][j]);\r
+           fputc(c == 'x' ? '.' : c, fp);\r
+            fputc(j == BOARD_RGHT - 1 ? '\n' : ' ', fp);\r
+       }\r
+    }\r
+    if ((gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0))\r
+      fprintf(fp, "white to play\n");\r
+    else\r
+      fprintf(fp, "black to play\n");\r
+}\r
+\r
+void\r
+PrintOpponents(fp)\r
+     FILE *fp;\r
+{\r
+    if (gameInfo.white != NULL) {\r
+       fprintf(fp, "\t%s vs. %s\n", gameInfo.white, gameInfo.black);\r
+    } else {\r
+       fprintf(fp, "\n");\r
+    }\r
+}\r
+\r
+/* Find last component of program's own name, using some heuristics */\r
+void\r
+TidyProgramName(prog, host, buf)\r
+     char *prog, *host, buf[MSG_SIZ];\r
+{\r
+    char *p, *q;\r
+    int local = (strcmp(host, "localhost") == 0);\r
+    while (!local && (p = strchr(prog, ';')) != NULL) {\r
+       p++;\r
+       while (*p == ' ') p++;\r
+       prog = p;\r
+    }\r
+    if (*prog == '"' || *prog == '\'') {\r
+       q = strchr(prog + 1, *prog);\r
+    } else {\r
+       q = strchr(prog, ' ');\r
+    }\r
+    if (q == NULL) q = prog + strlen(prog);\r
+    p = q;\r
+    while (p >= prog && *p != '/' && *p != '\\') p--;\r
+    p++;\r
+    if(p == prog && *p == '"') p++;\r
+    if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4;\r
+    memcpy(buf, p, q - p);\r
+    buf[q - p] = NULLCHAR;\r
+    if (!local) {\r
+       strcat(buf, "@");\r
+       strcat(buf, host);\r
+    }\r
+}\r
+\r
+char *\r
+TimeControlTagValue()\r
+{\r
+    char buf[MSG_SIZ];\r
+    if (!appData.clockMode) {\r
+       strcpy(buf, "-");\r
+    } else if (movesPerSession > 0) {\r
+       sprintf(buf, "%d/%ld", movesPerSession, timeControl/1000);\r
+    } else if (timeIncrement == 0) {\r
+       sprintf(buf, "%ld", timeControl/1000);\r
+    } else {\r
+       sprintf(buf, "%ld+%ld", timeControl/1000, timeIncrement/1000);\r
+    }\r
+    return StrSave(buf);\r
+}\r
+\r
+void\r
+SetGameInfo()\r
+{\r
+    /* This routine is used only for certain modes */\r
+    VariantClass v = gameInfo.variant;\r
+    ClearGameInfo(&gameInfo);\r
+    gameInfo.variant = v;\r
+\r
+    switch (gameMode) {\r
+      case MachinePlaysWhite:\r
+       gameInfo.event = StrSave( appData.pgnEventHeader );\r
+       gameInfo.site = StrSave(HostName());\r
+       gameInfo.date = PGNDate();\r
+       gameInfo.round = StrSave("-");\r
+       gameInfo.white = StrSave(first.tidy);\r
+       gameInfo.black = StrSave(UserName());\r
+       gameInfo.timeControl = TimeControlTagValue();\r
+       break;\r
+\r
+      case MachinePlaysBlack:\r
+       gameInfo.event = StrSave( appData.pgnEventHeader );\r
+       gameInfo.site = StrSave(HostName());\r
+       gameInfo.date = PGNDate();\r
+       gameInfo.round = StrSave("-");\r
+       gameInfo.white = StrSave(UserName());\r
+       gameInfo.black = StrSave(first.tidy);\r
+       gameInfo.timeControl = TimeControlTagValue();\r
+       break;\r
+\r
+      case TwoMachinesPlay:\r
+       gameInfo.event = StrSave( appData.pgnEventHeader );\r
+       gameInfo.site = StrSave(HostName());\r
+       gameInfo.date = PGNDate();\r
+       if (matchGame > 0) {\r
+           char buf[MSG_SIZ];\r
+           sprintf(buf, "%d", matchGame);\r
+           gameInfo.round = StrSave(buf);\r
+       } else {\r
+           gameInfo.round = StrSave("-");\r
+       }\r
+       if (first.twoMachinesColor[0] == 'w') {\r
+           gameInfo.white = StrSave(first.tidy);\r
+           gameInfo.black = StrSave(second.tidy);\r
+       } else {\r
+           gameInfo.white = StrSave(second.tidy);\r
+           gameInfo.black = StrSave(first.tidy);\r
+       }\r
+       gameInfo.timeControl = TimeControlTagValue();\r
+       break;\r
+\r
+      case EditGame:\r
+       gameInfo.event = StrSave("Edited game");\r
+       gameInfo.site = StrSave(HostName());\r
+       gameInfo.date = PGNDate();\r
+       gameInfo.round = StrSave("-");\r
+       gameInfo.white = StrSave("-");\r
+       gameInfo.black = StrSave("-");\r
+       break;\r
+\r
+      case EditPosition:\r
+       gameInfo.event = StrSave("Edited position");\r
+       gameInfo.site = StrSave(HostName());\r
+       gameInfo.date = PGNDate();\r
+       gameInfo.round = StrSave("-");\r
+       gameInfo.white = StrSave("-");\r
+       gameInfo.black = StrSave("-");\r
+       break;\r
+\r
+      case IcsPlayingWhite:\r
+      case IcsPlayingBlack:\r
+      case IcsObserving:\r
+      case IcsExamining:\r
+       break;\r
+\r
+      case PlayFromGameFile:\r
+       gameInfo.event = StrSave("Game from non-PGN file");\r
+       gameInfo.site = StrSave(HostName());\r
+       gameInfo.date = PGNDate();\r
+       gameInfo.round = StrSave("-");\r
+       gameInfo.white = StrSave("?");\r
+       gameInfo.black = StrSave("?");\r
+       break;\r
+\r
+      default:\r
+       break;\r
+    }\r
+}\r
+\r
+void\r
+ReplaceComment(index, text)\r
+     int index;\r
+     char *text;\r
+{\r
+    int len;\r
+\r
+    while (*text == '\n') text++;\r
+    len = strlen(text);\r
+    while (len > 0 && text[len - 1] == '\n') len--;\r
+\r
+    if (commentList[index] != NULL)\r
+      free(commentList[index]);\r
+\r
+    if (len == 0) {\r
+       commentList[index] = NULL;\r
+       return;\r
+    }\r
+    commentList[index] = (char *) malloc(len + 2);\r
+    strncpy(commentList[index], text, len);\r
+    commentList[index][len] = '\n';\r
+    commentList[index][len + 1] = NULLCHAR;\r
+}\r
+\r
+void\r
+CrushCRs(text)\r
+     char *text;\r
+{\r
+  char *p = text;\r
+  char *q = text;\r
+  char ch;\r
+\r
+  do {\r
+    ch = *p++;\r
+    if (ch == '\r') continue;\r
+    *q++ = ch;\r
+  } while (ch != '\0');\r
+}\r
+\r
+void\r
+AppendComment(index, text)\r
+     int index;\r
+     char *text;\r
+{\r
+    int oldlen, len;\r
+    char *old;\r
+\r
+    text = GetInfoFromComment( index, text ); /* [HGM] PV time: strip PV info from comment */\r
+\r
+    CrushCRs(text);\r
+    while (*text == '\n') text++;\r
+    len = strlen(text);\r
+    while (len > 0 && text[len - 1] == '\n') len--;\r
+\r
+    if (len == 0) return;\r
+\r
+    if (commentList[index] != NULL) {\r
+       old = commentList[index];\r
+       oldlen = strlen(old);\r
+       commentList[index] = (char *) malloc(oldlen + len + 2);\r
+       strcpy(commentList[index], old);\r
+       free(old);\r
+       strncpy(&commentList[index][oldlen], text, len);\r
+       commentList[index][oldlen + len] = '\n';\r
+       commentList[index][oldlen + len + 1] = NULLCHAR;\r
+    } else {\r
+       commentList[index] = (char *) malloc(len + 2);\r
+       strncpy(commentList[index], text, len);\r
+       commentList[index][len] = '\n';\r
+       commentList[index][len + 1] = NULLCHAR;\r
+    }\r
+}\r
+\r
+static char * FindStr( char * text, char * sub_text )\r
+{\r
+    char * result = strstr( text, sub_text );\r
+\r
+    if( result != NULL ) {\r
+        result += strlen( sub_text );\r
+    }\r
+\r
+    return result;\r
+}\r
+\r
+/* [AS] Try to extract PV info from PGN comment */\r
+/* [HGM] PV time: and then remove it, to prevent it appearing twice */\r
+char *GetInfoFromComment( int index, char * text )\r
+{\r
+    char * sep = text;\r
+\r
+    if( text != NULL && index > 0 ) {\r
+        int score = 0;\r
+        int depth = 0;\r
+        int time = -1, sec = 0, deci;\r
+        char * s_eval = FindStr( text, "[%eval " );\r
+        char * s_emt = FindStr( text, "[%emt " );\r
+\r
+        if( s_eval != NULL || s_emt != NULL ) {\r
+            /* New style */\r
+            char delim;\r
+\r
+            if( s_eval != NULL ) {\r
+                if( sscanf( s_eval, "%d,%d%c", &score, &depth, &delim ) != 3 ) {\r
+                    return text;\r
+                }\r
+\r
+                if( delim != ']' ) {\r
+                    return text;\r
+                }\r
+            }\r
+\r
+            if( s_emt != NULL ) {\r
+            }\r
+        }\r
+        else {\r
+            /* We expect something like: [+|-]nnn.nn/dd */\r
+            int score_lo = 0;\r
+\r
+            sep = strchr( text, '/' );\r
+            if( sep == NULL || sep < (text+4) ) {\r
+                return text;\r
+            }\r
+\r
+            time = -1; sec = -1; deci = -1;\r
+            if( sscanf( text, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 5 &&\r
+               sscanf( text, "%d.%d/%d %d.%d", &score, &score_lo, &depth, &time, &deci ) != 5 &&\r
+                sscanf( text, "%d.%d/%d %d", &score, &score_lo, &depth, &time ) != 4 &&\r
+                sscanf( text, "%d.%d/%d", &score, &score_lo, &depth ) != 3   ) {\r
+                return text;\r
+            }\r
+\r
+            if( score_lo < 0 || score_lo >= 100 ) {\r
+                return text;\r
+            }\r
+\r
+            if(sec >= 0) time = 600*time + 10*sec; else\r
+            if(deci >= 0) time = 10*time + deci; else time *= 10; // deci-sec\r
+\r
+            score = score >= 0 ? score*100 + score_lo : score*100 - score_lo;\r
+\r
+            /* [HGM] PV time: now locate end of PV info */\r
+            while( *++sep >= '0' && *sep <= '9'); // strip depth\r
+            if(time >= 0)\r
+            while( *++sep >= '0' && *sep <= '9'); // strip time\r
+            if(sec >= 0)\r
+            while( *++sep >= '0' && *sep <= '9'); // strip seconds\r
+            if(deci >= 0)\r
+            while( *++sep >= '0' && *sep <= '9'); // strip fractional seconds\r
+            while(*sep == ' ') sep++;\r
+        }\r
+\r
+        if( depth <= 0 ) {\r
+            return text;\r
+        }\r
+\r
+        if( time < 0 ) {\r
+            time = -1;\r
+        }\r
+\r
+        pvInfoList[index-1].depth = depth;\r
+        pvInfoList[index-1].score = score;\r
+        pvInfoList[index-1].time  = 10*time; // centi-sec\r
+    }\r
+    return sep;\r
+}\r
+\r
+void\r
+SendToProgram(message, cps)\r
+     char *message;\r
+     ChessProgramState *cps;\r
+{\r
+    int count, outCount, error;\r
+    char buf[MSG_SIZ];\r
+\r
+    if (cps->pr == NULL) return;\r
+    Attention(cps);\r
+    \r
+    if (appData.debugMode) {\r
+       TimeMark now;\r
+       GetTimeMark(&now);\r
+       fprintf(debugFP, "%ld >%-6s: %s", \r
+               SubtractTimeMarks(&now, &programStartTime),\r
+               cps->which, message);\r
+    }\r
+    \r
+    count = strlen(message);\r
+    outCount = OutputToProcess(cps->pr, message, count, &error);\r
+    if (outCount < count && !exiting \r
+                         && !endingGame) { /* [HGM] crash: to not hang GameEnds() writing to deceased engines */\r
+       sprintf(buf, _("Error writing to %s chess program"), cps->which);\r
+        if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */\r
+            if(epStatus[forwardMostMove] <= EP_DRAWS) {\r
+                gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */\r
+                sprintf(buf, "%s program exits in draw position (%s)", cps->which, cps->program);\r
+            } else {\r
+                gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;\r
+            }\r
+            gameInfo.resultDetails = buf;\r
+        }\r
+        DisplayFatalError(buf, error, 1);\r
+    }\r
+}\r
+\r
+void\r
+ReceiveFromProgram(isr, closure, message, count, error)\r
+     InputSourceRef isr;\r
+     VOIDSTAR closure;\r
+     char *message;\r
+     int count;\r
+     int error;\r
+{\r
+    char *end_str;\r
+    char buf[MSG_SIZ];\r
+    ChessProgramState *cps = (ChessProgramState *)closure;\r
+\r
+    if (isr != cps->isr) return; /* Killed intentionally */\r
+    if (count <= 0) {\r
+       if (count == 0) {\r
+           sprintf(buf,\r
+                   _("Error: %s chess program (%s) exited unexpectedly"),\r
+                   cps->which, cps->program);\r
+        if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */\r
+                if(epStatus[forwardMostMove] <= EP_DRAWS) {\r
+                    gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */\r
+                    sprintf(buf, _("%s program exits in draw position (%s)"), cps->which, cps->program);\r
+                } else {\r
+                    gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;\r
+                }\r
+                gameInfo.resultDetails = buf;\r
+            }\r
+           RemoveInputSource(cps->isr);\r
+           DisplayFatalError(buf, 0, 1);\r
+       } else {\r
+           sprintf(buf,\r
+                   _("Error reading from %s chess program (%s)"),\r
+                   cps->which, cps->program);\r
+           RemoveInputSource(cps->isr);\r
+\r
+            /* [AS] Program is misbehaving badly... kill it */\r
+            if( count == -2 ) {\r
+                DestroyChildProcess( cps->pr, 9 );\r
+                cps->pr = NoProc;\r
+            }\r
+\r
+            DisplayFatalError(buf, error, 1);\r
+       }\r
+       return;\r
+    }\r
+    \r
+    if ((end_str = strchr(message, '\r')) != NULL)\r
+      *end_str = NULLCHAR;\r
+    if ((end_str = strchr(message, '\n')) != NULL)\r
+      *end_str = NULLCHAR;\r
+    \r
+    if (appData.debugMode) {\r
+       TimeMark now; int print = 1;\r
+       char *quote = ""; char c; int i;\r
+\r
+       if(appData.engineComments != 1) { /* [HGM] debug: decide if protocol-violating output is written */\r
+               char start = message[0];\r
+               if(start >='A' && start <= 'Z') start += 'a' - 'A'; // be tolerant to capitalizing\r
+               if(sscanf(message, "%d%c%d%d%d", &i, &c, &i, &i, &i) != 5 && \r
+                  sscanf(message, "move %c", &c)!=1  && sscanf(message, "offer%c", &c)!=1 &&\r
+                  sscanf(message, "resign%c", &c)!=1 && sscanf(message, "feature %c", &c)!=1 &&\r
+                  sscanf(message, "error %c", &c)!=1 && sscanf(message, "illegal %c", &c)!=1 &&\r
+                  sscanf(message, "tell%c", &c)!=1   && sscanf(message, "0-1 %c", &c)!=1 &&\r
+                  sscanf(message, "1-0 %c", &c)!=1   && sscanf(message, "1/2-1/2 %c", &c)!=1 && start != '#')\r
+                       { quote = "# "; print = (appData.engineComments == 2); }\r
+               message[0] = start; // restore original message\r
+       }\r
+       if(print) {\r
+               GetTimeMark(&now);\r
+               fprintf(debugFP, "%ld <%-6s: %s%s\n", \r
+                       SubtractTimeMarks(&now, &programStartTime), cps->which, \r
+                       quote,\r
+                       message);\r
+       }\r
+    }\r
+\r
+    /* [DM] if icsEngineAnalyze is active we block all whisper and kibitz output, because nobody want to see this */\r
+    if (appData.icsEngineAnalyze) {\r
+        if (strstr(message, "whisper") != NULL ||\r
+             strstr(message, "kibitz") != NULL || \r
+            strstr(message, "tellics") != NULL) return;\r
+    }\r
+\r
+    HandleMachineMove(message, cps);\r
+}\r
+\r
+\r
+void\r
+SendTimeControl(cps, mps, tc, inc, sd, st)\r
+     ChessProgramState *cps;\r
+     int mps, inc, sd, st;\r
+     long tc;\r
+{\r
+    char buf[MSG_SIZ];\r
+    int seconds;\r
+\r
+    if( timeControl_2 > 0 ) {\r
+        if( (gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b') ) {\r
+            tc = timeControl_2;\r
+        }\r
+    }\r
+    tc  /= cps->timeOdds; /* [HGM] time odds: apply before telling engine */\r
+    inc /= cps->timeOdds;\r
+    st  /= cps->timeOdds;\r
+\r
+    seconds = (tc / 1000) % 60; /* [HGM] displaced to after applying odds */\r
+\r
+    if (st > 0) {\r
+      /* Set exact time per move, normally using st command */\r
+      if (cps->stKludge) {\r
+       /* GNU Chess 4 has no st command; uses level in a nonstandard way */\r
+       seconds = st % 60;\r
+       if (seconds == 0) {\r
+         sprintf(buf, "level 1 %d\n", st/60);\r
+       } else {\r
+         sprintf(buf, "level 1 %d:%02d\n", st/60, seconds);\r
+       }\r
+      } else {\r
+       sprintf(buf, "st %d\n", st);\r
+      }\r
+    } else {\r
+      /* Set conventional or incremental time control, using level command */\r
+      if (seconds == 0) {\r
+       /* Note old gnuchess bug -- minutes:seconds used to not work.\r
+          Fixed in later versions, but still avoid :seconds\r
+          when seconds is 0. */\r
+       sprintf(buf, "level %d %ld %d\n", mps, tc/60000, inc/1000);\r
+      } else {\r
+       sprintf(buf, "level %d %ld:%02d %d\n", mps, tc/60000,\r
+               seconds, inc/1000);\r
+      }\r
+    }\r
+    SendToProgram(buf, cps);\r
+\r
+    /* Orthoganally (except for GNU Chess 4), limit time to st seconds */\r
+    /* Orthogonally, limit search to given depth */\r
+    if (sd > 0) {\r
+      if (cps->sdKludge) {\r
+       sprintf(buf, "depth\n%d\n", sd);\r
+      } else {\r
+       sprintf(buf, "sd %d\n", sd);\r
+      }\r
+      SendToProgram(buf, cps);\r
+    }\r
+\r
+    if(cps->nps > 0) { /* [HGM] nps */\r
+       if(cps->supportsNPS == FALSE) cps->nps = -1; // don't use if engine explicitly says not supported!\r
+       else {\r
+               sprintf(buf, "nps %d\n", cps->nps);\r
+             SendToProgram(buf, cps);\r
+       }\r
+    }\r
+}\r
+\r
+ChessProgramState *WhitePlayer()\r
+/* [HGM] return pointer to 'first' or 'second', depending on who plays white */\r
+{\r
+    if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b' || \r
+       gameMode == BeginningOfGame || gameMode == MachinePlaysBlack)\r
+        return &second;\r
+    return &first;\r
+}\r
+\r
+void\r
+SendTimeRemaining(cps, machineWhite)\r
+     ChessProgramState *cps;\r
+     int /*boolean*/ machineWhite;\r
+{\r
+    char message[MSG_SIZ];\r
+    long time, otime;\r
+\r
+    /* Note: this routine must be called when the clocks are stopped\r
+       or when they have *just* been set or switched; otherwise\r
+       it will be off by the time since the current tick started.\r
+    */\r
+    if (machineWhite) {\r
+       time = whiteTimeRemaining / 10;\r
+       otime = blackTimeRemaining / 10;\r
+    } else {\r
+       time = blackTimeRemaining / 10;\r
+       otime = whiteTimeRemaining / 10;\r
+    }\r
+    /* [HGM] translate opponent's time by time-odds factor */\r
+    otime = (otime * cps->other->timeOdds) / cps->timeOdds;\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "time odds: %d %d \n", cps->timeOdds, cps->other->timeOdds);\r
+    }\r
+\r
+    if (time <= 0) time = 1;\r
+    if (otime <= 0) otime = 1;\r
+    \r
+    sprintf(message, "time %ld\n", time);\r
+    SendToProgram(message, cps);\r
+\r
+    sprintf(message, "otim %ld\n", otime);\r
+    SendToProgram(message, cps);\r
+}\r
+\r
+int\r
+BoolFeature(p, name, loc, cps)\r
+     char **p;\r
+     char *name;\r
+     int *loc;\r
+     ChessProgramState *cps;\r
+{\r
+  char buf[MSG_SIZ];\r
+  int len = strlen(name);\r
+  int val;\r
+  if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {\r
+    (*p) += len + 1;\r
+    sscanf(*p, "%d", &val);\r
+    *loc = (val != 0);\r
+    while (**p && **p != ' ') (*p)++;\r
+    sprintf(buf, "accepted %s\n", name);\r
+    SendToProgram(buf, cps);\r
+    return TRUE;\r
+  }\r
+  return FALSE;\r
+}\r
+\r
+int\r
+IntFeature(p, name, loc, cps)\r
+     char **p;\r
+     char *name;\r
+     int *loc;\r
+     ChessProgramState *cps;\r
+{\r
+  char buf[MSG_SIZ];\r
+  int len = strlen(name);\r
+  if (strncmp((*p), name, len) == 0 && (*p)[len] == '=') {\r
+    (*p) += len + 1;\r
+    sscanf(*p, "%d", loc);\r
+    while (**p && **p != ' ') (*p)++;\r
+    sprintf(buf, "accepted %s\n", name);\r
+    SendToProgram(buf, cps);\r
+    return TRUE;\r
+  }\r
+  return FALSE;\r
+}\r
+\r
+int\r
+StringFeature(p, name, loc, cps)\r
+     char **p;\r
+     char *name;\r
+     char loc[];\r
+     ChessProgramState *cps;\r
+{\r
+  char buf[MSG_SIZ];\r
+  int len = strlen(name);\r
+  if (strncmp((*p), name, len) == 0\r
+      && (*p)[len] == '=' && (*p)[len+1] == '\"') {\r
+    (*p) += len + 2;\r
+    sscanf(*p, "%[^\"]", loc);\r
+    while (**p && **p != '\"') (*p)++;\r
+    if (**p == '\"') (*p)++;\r
+    sprintf(buf, "accepted %s\n", name);\r
+    SendToProgram(buf, cps);\r
+    return TRUE;\r
+  }\r
+  return FALSE;\r
+}\r
+\r
+int \r
+ParseOption(Option *opt, ChessProgramState *cps)\r
+// [HGM] options: process the string that defines an engine option, and determine\r
+// name, type, default value, and allowed value range\r
+{\r
+       char *p, *q, buf[MSG_SIZ];\r
+       int n, min = (-1)<<31, max = 1<<31, def;\r
+\r
+       if(p = strstr(opt->name, " -spin ")) {\r
+           if((n = sscanf(p, " -spin %d %d %d", &def, &min, &max)) < 3 ) return FALSE;\r
+           if(max < min) max = min; // enforce consistency\r
+           if(def < min) def = min;\r
+           if(def > max) def = max;\r
+           opt->value = def;\r
+           opt->min = min;\r
+           opt->max = max;\r
+           opt->type = Spin;\r
+       } else if(p = strstr(opt->name, " -string ")) {\r
+           opt->textValue = p+9;\r
+           opt->type = TextBox;\r
+       } else if(p = strstr(opt->name, " -check ")) {\r
+           if(sscanf(p, " -check %d", &def) < 1) return FALSE;\r
+           opt->value = (def != 0);\r
+           opt->type = CheckBox;\r
+       } else if(p = strstr(opt->name, " -combo ")) {\r
+           opt->textValue = (char*) (&cps->comboList[cps->comboCnt]); // cheat with pointer type\r
+           cps->comboList[cps->comboCnt++] = q = p+8; // holds possible choices\r
+           opt->value = n = 0;\r
+           while(q = StrStr(q, " /// ")) {\r
+               n++; *q = 0;    // count choices, and null-terminate each of them\r
+               q += 5;\r
+               if(*q == '*') { // remember default, which is marked with * prefix\r
+                   q++;\r
+                   opt->value = n;\r
+               }\r
+               cps->comboList[cps->comboCnt++] = q;\r
+           }\r
+           cps->comboList[cps->comboCnt++] = NULL;\r
+           opt->max = n + 1;\r
+           opt->type = ComboBox;\r
+       } else if(p = strstr(opt->name, " -button")) {\r
+           opt->type = Button;\r
+       } else if(p = strstr(opt->name, " -save")) {\r
+           opt->type = SaveButton;\r
+       } else return FALSE;\r
+       *p = 0; // terminate option name\r
+       return TRUE;\r
+}\r
+\r
+void\r
+FeatureDone(cps, val)\r
+     ChessProgramState* cps;\r
+     int val;\r
+{\r
+  DelayedEventCallback cb = GetDelayedEvent();\r
+  if ((cb == InitBackEnd3 && cps == &first) ||\r
+      (cb == TwoMachinesEventIfReady && cps == &second)) {\r
+    CancelDelayedEvent();\r
+    ScheduleDelayedEvent(cb, val ? 1 : 3600000);\r
+  }\r
+  cps->initDone = val;\r
+}\r
+\r
+/* Parse feature command from engine */\r
+void\r
+ParseFeatures(args, cps)\r
+     char* args;\r
+     ChessProgramState *cps;  \r
+{\r
+  char *p = args;\r
+  char *q;\r
+  int val;\r
+  char buf[MSG_SIZ];\r
+\r
+  for (;;) {\r
+    while (*p == ' ') p++;\r
+    if (*p == NULLCHAR) return;\r
+\r
+    if (BoolFeature(&p, "setboard", &cps->useSetboard, cps)) continue;\r
+    if (BoolFeature(&p, "time", &cps->sendTime, cps)) continue;    \r
+    if (BoolFeature(&p, "draw", &cps->sendDrawOffers, cps)) continue;    \r
+    if (BoolFeature(&p, "sigint", &cps->useSigint, cps)) continue;    \r
+    if (BoolFeature(&p, "sigterm", &cps->useSigterm, cps)) continue;    \r
+    if (BoolFeature(&p, "reuse", &val, cps)) {\r
+      /* Engine can disable reuse, but can't enable it if user said no */\r
+      if (!val) cps->reuse = FALSE;\r
+      continue;\r
+    }\r
+    if (BoolFeature(&p, "analyze", &cps->analysisSupport, cps)) continue;\r
+    if (StringFeature(&p, "myname", &cps->tidy, cps)) {\r
+      if (gameMode == TwoMachinesPlay) {\r
+       DisplayTwoMachinesTitle();\r
+      } else {\r
+       DisplayTitle("");\r
+      }\r
+      continue;\r
+    }\r
+    if (StringFeature(&p, "variants", &cps->variants, cps)) continue;\r
+    if (BoolFeature(&p, "san", &cps->useSAN, cps)) continue;\r
+    if (BoolFeature(&p, "ping", &cps->usePing, cps)) continue;\r
+    if (BoolFeature(&p, "playother", &cps->usePlayother, cps)) continue;\r
+    if (BoolFeature(&p, "colors", &cps->useColors, cps)) continue;\r
+    if (BoolFeature(&p, "usermove", &cps->useUsermove, cps)) continue;\r
+    if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;\r
+    if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;\r
+    if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */\r
+    if (IntFeature(&p, "done", &val, cps)) {\r
+      FeatureDone(cps, val);\r
+      continue;\r
+    }\r
+    /* Added by Tord: */\r
+    if (BoolFeature(&p, "fen960", &cps->useFEN960, cps)) continue;\r
+    if (BoolFeature(&p, "oocastle", &cps->useOOCastle, cps)) continue;\r
+    /* End of additions by Tord */\r
+\r
+    /* [HGM] added features: */\r
+    if (BoolFeature(&p, "debug", &cps->debug, cps)) continue;\r
+    if (BoolFeature(&p, "nps", &cps->supportsNPS, cps)) continue;\r
+    if (IntFeature(&p, "level", &cps->maxNrOfSessions, cps)) continue;\r
+    if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue;\r
+    if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;\r
+    if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;\r
+    if (StringFeature(&p, "option", &(cps->option[cps->nrOptions].name), cps)) {\r
+       ParseOption(&(cps->option[cps->nrOptions++]), cps); // [HGM] options: add option feature\r
+       if(cps->nrOptions >= MAX_OPTIONS) {\r
+           cps->nrOptions--;\r
+           sprintf(buf, "%s engine has too many options\n", cps->which);\r
+           DisplayError(buf, 0);\r
+       }\r
+       continue;\r
+    }\r
+    if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;\r
+    /* End of additions by HGM */\r
+\r
+    /* unknown feature: complain and skip */\r
+    q = p;\r
+    while (*q && *q != '=') q++;\r
+    sprintf(buf, "rejected %.*s\n", q-p, p);\r
+    SendToProgram(buf, cps);\r
+    p = q;\r
+    if (*p == '=') {\r
+      p++;\r
+      if (*p == '\"') {\r
+       p++;\r
+       while (*p && *p != '\"') p++;\r
+       if (*p == '\"') p++;\r
+      } else {\r
+       while (*p && *p != ' ') p++;\r
+      }\r
+    }\r
+  }\r
+\r
+}\r
+\r
+void\r
+PeriodicUpdatesEvent(newState)\r
+     int newState;\r
+{\r
+    if (newState == appData.periodicUpdates)\r
+      return;\r
+\r
+    appData.periodicUpdates=newState;\r
+\r
+    /* Display type changes, so update it now */\r
+    DisplayAnalysis();\r
+\r
+    /* Get the ball rolling again... */\r
+    if (newState) {\r
+       AnalysisPeriodicEvent(1);\r
+       StartAnalysisClock();\r
+    }\r
+}\r
+\r
+void\r
+PonderNextMoveEvent(newState)\r
+     int newState;\r
+{\r
+    if (newState == appData.ponderNextMove) return;\r
+    if (gameMode == EditPosition) EditPositionDone();\r
+    if (newState) {\r
+       SendToProgram("hard\n", &first);\r
+       if (gameMode == TwoMachinesPlay) {\r
+           SendToProgram("hard\n", &second);\r
+       }\r
+    } else {\r
+       SendToProgram("easy\n", &first);\r
+       thinkOutput[0] = NULLCHAR;\r
+       if (gameMode == TwoMachinesPlay) {\r
+           SendToProgram("easy\n", &second);\r
+       }\r
+    }\r
+    appData.ponderNextMove = newState;\r
+}\r
+\r
+void\r
+NewSettingEvent(option, command, value)\r
+     char *command;\r
+     int option, value;\r
+{\r
+    char buf[MSG_SIZ];\r
+\r
+    if (gameMode == EditPosition) EditPositionDone();\r
+    sprintf(buf, "%s%s %d\n", (option ? "option ": ""), command, value);\r
+    SendToProgram(buf, &first);\r
+    if (gameMode == TwoMachinesPlay) {\r
+       SendToProgram(buf, &second);\r
+    }\r
+}\r
+\r
+void\r
+ShowThinkingEvent()\r
+// [HGM] thinking: this routine is now also called from "Options -> Engine..." popup\r
+{\r
+    static int oldState = 2; // kludge alert! Neither true nor fals, so first time oldState is always updated\r
+    int newState = appData.showThinking\r
+       // [HGM] thinking: other features now need thinking output as well\r
+       || !appData.hideThinkingFromHuman || appData.adjudicateLossThreshold != 0 || EngineOutputIsUp();\r
+    \r
+    if (oldState == newState) return;\r
+    oldState = newState;\r
+    if (gameMode == EditPosition) EditPositionDone();\r
+    if (oldState) {\r
+       SendToProgram("post\n", &first);\r
+       if (gameMode == TwoMachinesPlay) {\r
+           SendToProgram("post\n", &second);\r
+       }\r
+    } else {\r
+       SendToProgram("nopost\n", &first);\r
+       thinkOutput[0] = NULLCHAR;\r
+       if (gameMode == TwoMachinesPlay) {\r
+           SendToProgram("nopost\n", &second);\r
+       }\r
+    }\r
+//    appData.showThinking = newState; // [HGM] thinking: responsible option should already have be changed when calling this routine!\r
+}\r
+\r
+void\r
+AskQuestionEvent(title, question, replyPrefix, which)\r
+     char *title; char *question; char *replyPrefix; char *which;\r
+{\r
+  ProcRef pr = (which[0] == '1') ? first.pr : second.pr;\r
+  if (pr == NoProc) return;\r
+  AskQuestion(title, question, replyPrefix, pr);\r
+}\r
+\r
+void\r
+DisplayMove(moveNumber)\r
+     int moveNumber;\r
+{\r
+    char message[MSG_SIZ];\r
+    char res[MSG_SIZ];\r
+    char cpThinkOutput[MSG_SIZ];\r
+\r
+    if (moveNumber == forwardMostMove - 1 || \r
+       gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+\r
+       safeStrCpy(cpThinkOutput, thinkOutput, sizeof(cpThinkOutput));\r
+\r
+        if (strchr(cpThinkOutput, '\n')) {\r
+           *strchr(cpThinkOutput, '\n') = NULLCHAR;\r
+        }\r
+    } else {\r
+       *cpThinkOutput = NULLCHAR;\r
+    }\r
+\r
+    /* [AS] Hide thinking from human user */\r
+    if( appData.hideThinkingFromHuman && gameMode != TwoMachinesPlay ) {\r
+        *cpThinkOutput = NULLCHAR;\r
+        if( thinkOutput[0] != NULLCHAR ) {\r
+            int i;\r
+\r
+            for( i=0; i<=hiddenThinkOutputState; i++ ) {\r
+                cpThinkOutput[i] = '.';\r
+            }\r
+            cpThinkOutput[i] = NULLCHAR;\r
+            hiddenThinkOutputState = (hiddenThinkOutputState + 1) % 3;\r
+        }\r
+    }\r
+\r
+    if (moveNumber == forwardMostMove - 1 &&\r
+       gameInfo.resultDetails != NULL) {\r
+       if (gameInfo.resultDetails[0] == NULLCHAR) {\r
+           sprintf(res, " %s", PGNResult(gameInfo.result));\r
+       } else {\r
+           sprintf(res, " {%s} %s",\r
+                   gameInfo.resultDetails, PGNResult(gameInfo.result));\r
+       }\r
+    } else {\r
+       res[0] = NULLCHAR;\r
+    }\r
+    \r
+    if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {\r
+       DisplayMessage(res, cpThinkOutput);\r
+    } else {\r
+       sprintf(message, "%d.%s%s%s", moveNumber / 2 + 1,\r
+               WhiteOnMove(moveNumber) ? " " : ".. ",\r
+               parseList[moveNumber], res);\r
+       DisplayMessage(message, cpThinkOutput);\r
+    }\r
+}\r
+\r
+void\r
+DisplayAnalysisText(text)\r
+     char *text;\r
+{\r
+    char buf[MSG_SIZ];\r
+\r
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile \r
+               || appData.icsEngineAnalyze) {\r
+       sprintf(buf, "Analysis (%s)", first.tidy);\r
+       AnalysisPopUp(buf, text);\r
+    }\r
+}\r
+\r
+static int\r
+only_one_move(str)\r
+     char *str;\r
+{\r
+    while (*str && isspace(*str)) ++str;\r
+    while (*str && !isspace(*str)) ++str;\r
+    if (!*str) return 1;\r
+    while (*str && isspace(*str)) ++str;\r
+    if (!*str) return 1;\r
+    return 0;\r
+}\r
+\r
+void\r
+DisplayAnalysis()\r
+{\r
+    char buf[MSG_SIZ];\r
+    char lst[MSG_SIZ / 2];\r
+    double nps;\r
+    static char *xtra[] = { "", " (--)", " (++)" };\r
+    int h, m, s, cs;\r
+  \r
+    if (programStats.time == 0) {\r
+       programStats.time = 1;\r
+    }\r
+  \r
+    if (programStats.got_only_move) {\r
+       safeStrCpy(buf, programStats.movelist, sizeof(buf));\r
+    } else {\r
+        safeStrCpy( lst, programStats.movelist, sizeof(lst));\r
+\r
+        nps = (u64ToDouble(programStats.nodes) /\r
+             ((double)programStats.time /100.0));\r
+\r
+       cs = programStats.time % 100;\r
+       s = programStats.time / 100;\r
+       h = (s / (60*60));\r
+       s = s - h*60*60;\r
+       m = (s/60);\r
+       s = s - m*60;\r
+\r
+       if (programStats.moves_left > 0 && appData.periodicUpdates) {\r
+         if (programStats.move_name[0] != NULLCHAR) {\r
+           sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+                   programStats.depth,\r
+                   programStats.nr_moves-programStats.moves_left,\r
+                   programStats.nr_moves, programStats.move_name,\r
+                   ((float)programStats.score)/100.0, lst,\r
+                   only_one_move(lst)?\r
+                   xtra[programStats.got_fail] : "",\r
+                   (u64)programStats.nodes, (int)nps, h, m, s, cs);\r
+         } else {\r
+           sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+                   programStats.depth,\r
+                   programStats.nr_moves-programStats.moves_left,\r
+                   programStats.nr_moves, ((float)programStats.score)/100.0,\r
+                   lst,\r
+                   only_one_move(lst)?\r
+                   xtra[programStats.got_fail] : "",\r
+                   (u64)programStats.nodes, (int)nps, h, m, s, cs);\r
+         }\r
+       } else {\r
+           sprintf(buf, "depth=%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+                   programStats.depth,\r
+                   ((float)programStats.score)/100.0,\r
+                   lst,\r
+                   only_one_move(lst)?\r
+                   xtra[programStats.got_fail] : "",\r
+                   (u64)programStats.nodes, (int)nps, h, m, s, cs);\r
+       }\r
+    }\r
+    DisplayAnalysisText(buf);\r
+}\r
+\r
+void\r
+DisplayComment(moveNumber, text)\r
+     int moveNumber;\r
+     char *text;\r
+{\r
+    char title[MSG_SIZ];\r
+    char buf[8000]; // comment can be long!\r
+    int score, depth;\r
+\r
+    if( appData.autoDisplayComment ) {\r
+        if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {\r
+           strcpy(title, "Comment");\r
+        } else {\r
+           sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,\r
+                   WhiteOnMove(moveNumber) ? " " : ".. ",\r
+                   parseList[moveNumber]);\r
+        }\r
+    } else title[0] = 0;\r
+\r
+    // [HGM] PV info: display PV info together with (or as) comment\r
+    if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {\r
+        if(text == NULL) text = "";                                           \r
+        score = pvInfoList[moveNumber].score;\r
+        sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,\r
+                              depth, (pvInfoList[moveNumber].time+50)/100, text);\r
+        CommentPopUp(title, buf);\r
+    } else\r
+    if (text != NULL)\r
+        CommentPopUp(title, text);\r
+}\r
+\r
+/* This routine sends a ^C interrupt to gnuchess, to awaken it if it\r
+ * might be busy thinking or pondering.  It can be omitted if your\r
+ * gnuchess is configured to stop thinking immediately on any user\r
+ * input.  However, that gnuchess feature depends on the FIONREAD\r
+ * ioctl, which does not work properly on some flavors of Unix.\r
+ */\r
+void\r
+Attention(cps)\r
+     ChessProgramState *cps;\r
+{\r
+#if ATTENTION\r
+    if (!cps->useSigint) return;\r
+    if (appData.noChessProgram || (cps->pr == NoProc)) return;\r
+    switch (gameMode) {\r
+      case MachinePlaysWhite:\r
+      case MachinePlaysBlack:\r
+      case TwoMachinesPlay:\r
+      case IcsPlayingWhite:\r
+      case IcsPlayingBlack:\r
+      case AnalyzeMode:\r
+      case AnalyzeFile:\r
+       /* Skip if we know it isn't thinking */\r
+       if (!cps->maybeThinking) return;\r
+       if (appData.debugMode)\r
+         fprintf(debugFP, "Interrupting %s\n", cps->which);\r
+       InterruptChildProcess(cps->pr);\r
+       cps->maybeThinking = FALSE;\r
+       break;\r
+      default:\r
+       break;\r
+    }\r
+#endif /*ATTENTION*/\r
+}\r
+\r
+int\r
+CheckFlags()\r
+{\r
+    if (whiteTimeRemaining <= 0) {\r
+       if (!whiteFlag) {\r
+           whiteFlag = TRUE;\r
+           if (appData.icsActive) {\r
+               if (appData.autoCallFlag &&\r
+                   gameMode == IcsPlayingBlack && !blackFlag) {\r
+                 SendToICS(ics_prefix);\r
+                 SendToICS("flag\n");\r
+               }\r
+           } else {\r
+               if (blackFlag) {\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("Both flags fell"));\r
+               } else {\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("White's flag fell"));\r
+                   if (appData.autoCallFlag) {\r
+                       GameEnds(BlackWins, "Black wins on time", GE_XBOARD);\r
+                       return TRUE;\r
+                   }\r
+               }\r
+           }\r
+       }\r
+    }\r
+    if (blackTimeRemaining <= 0) {\r
+       if (!blackFlag) {\r
+           blackFlag = TRUE;\r
+           if (appData.icsActive) {\r
+               if (appData.autoCallFlag &&\r
+                   gameMode == IcsPlayingWhite && !whiteFlag) {\r
+                 SendToICS(ics_prefix);\r
+                 SendToICS("flag\n");\r
+               }\r
+           } else {\r
+               if (whiteFlag) {\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("Both flags fell"));\r
+               } else {\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("Black's flag fell"));\r
+                   if (appData.autoCallFlag) {\r
+                       GameEnds(WhiteWins, "White wins on time", GE_XBOARD);\r
+                       return TRUE;\r
+                   }\r
+               }\r
+           }\r
+       }\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+void\r
+CheckTimeControl()\r
+{\r
+    if (!appData.clockMode || appData.icsActive ||\r
+       gameMode == PlayFromGameFile || forwardMostMove == 0) return;\r
+\r
+    /*\r
+     * add time to clocks when time control is achieved ([HGM] now also used for increment)\r
+     */\r
+    if ( !WhiteOnMove(forwardMostMove) )\r
+       /* White made time control */\r
+        whiteTimeRemaining += GetTimeQuota((forwardMostMove-1)/2)\r
+        /* [HGM] time odds: correct new time quota for time odds! */\r
+                                            / WhitePlayer()->timeOdds;\r
+      else\r
+       /* Black made time control */\r
+        blackTimeRemaining += GetTimeQuota((forwardMostMove-1)/2)\r
+                                            / WhitePlayer()->other->timeOdds;\r
+}\r
+\r
+void\r
+DisplayBothClocks()\r
+{\r
+    int wom = gameMode == EditPosition ?\r
+      !blackPlaysFirst : WhiteOnMove(currentMove);\r
+    DisplayWhiteClock(whiteTimeRemaining, wom);\r
+    DisplayBlackClock(blackTimeRemaining, !wom);\r
+}\r
+\r
+\r
+/* Timekeeping seems to be a portability nightmare.  I think everyone\r
+   has ftime(), but I'm really not sure, so I'm including some ifdefs\r
+   to use other calls if you don't.  Clocks will be less accurate if\r
+   you have neither ftime nor gettimeofday.\r
+*/\r
+\r
+/* Get the current time as a TimeMark */\r
+void\r
+GetTimeMark(tm)\r
+     TimeMark *tm;\r
+{\r
+#if HAVE_GETTIMEOFDAY\r
+\r
+    struct timeval timeVal;\r
+    struct timezone timeZone;\r
+\r
+    gettimeofday(&timeVal, &timeZone);\r
+    tm->sec = (long) timeVal.tv_sec; \r
+    tm->ms = (int) (timeVal.tv_usec / 1000L);\r
+\r
+#else /*!HAVE_GETTIMEOFDAY*/\r
+#if HAVE_FTIME\r
+\r
+#include <sys/timeb.h>\r
+    struct timeb timeB;\r
+\r
+    ftime(&timeB);\r
+    tm->sec = (long) timeB.time;\r
+    tm->ms = (int) timeB.millitm;\r
+\r
+#else /*!HAVE_FTIME && !HAVE_GETTIMEOFDAY*/\r
+    tm->sec = (long) time(NULL);\r
+    tm->ms = 0;\r
+#endif\r
+#endif\r
+}\r
+\r
+/* Return the difference in milliseconds between two\r
+   time marks.  We assume the difference will fit in a long!\r
+*/\r
+long\r
+SubtractTimeMarks(tm2, tm1)\r
+     TimeMark *tm2, *tm1;\r
+{\r
+    return 1000L*(tm2->sec - tm1->sec) +\r
+           (long) (tm2->ms - tm1->ms);\r
+}\r
+\r
+\r
+/*\r
+ * Code to manage the game clocks.\r
+ *\r
+ * In tournament play, black starts the clock and then white makes a move.\r
+ * We give the human user a slight advantage if he is playing white---the\r
+ * clocks don't run until he makes his first move, so it takes zero time.\r
+ * Also, we don't account for network lag, so we could get out of sync\r
+ * with GNU Chess's clock -- but then, referees are always right.  \r
+ */\r
+\r
+static TimeMark tickStartTM;\r
+static long intendedTickLength;\r
+\r
+long\r
+NextTickLength(timeRemaining)\r
+     long timeRemaining;\r
+{\r
+    long nominalTickLength, nextTickLength;\r
+\r
+    if (timeRemaining > 0L && timeRemaining <= 10000L)\r
+      nominalTickLength = 100L;\r
+    else\r
+      nominalTickLength = 1000L;\r
+    nextTickLength = timeRemaining % nominalTickLength;\r
+    if (nextTickLength <= 0) nextTickLength += nominalTickLength;\r
+\r
+    return nextTickLength;\r
+}\r
+\r
+/* Adjust clock one minute up or down */\r
+void\r
+AdjustClock(Boolean which, int dir)\r
+{\r
+    if(which) blackTimeRemaining += 60000*dir;\r
+    else      whiteTimeRemaining += 60000*dir;\r
+    DisplayBothClocks();\r
+}\r
+\r
+/* Stop clocks and reset to a fresh time control */\r
+void\r
+ResetClocks() \r
+{\r
+    (void) StopClockTimer();\r
+    if (appData.icsActive) {\r
+       whiteTimeRemaining = blackTimeRemaining = 0;\r
+    } else { /* [HGM] correct new time quote for time odds */\r
+        whiteTimeRemaining = GetTimeQuota(-1) / WhitePlayer()->timeOdds;\r
+        blackTimeRemaining = GetTimeQuota(-1) / WhitePlayer()->other->timeOdds;\r
+    }\r
+    if (whiteFlag || blackFlag) {\r
+       DisplayTitle("");\r
+       whiteFlag = blackFlag = FALSE;\r
+    }\r
+    DisplayBothClocks();\r
+}\r
+\r
+#define FUDGE 25 /* 25ms = 1/40 sec; should be plenty even for 50 Hz clocks */\r
+\r
+/* Decrement running clock by amount of time that has passed */\r
+void\r
+DecrementClocks()\r
+{\r
+    long timeRemaining;\r
+    long lastTickLength, fudge;\r
+    TimeMark now;\r
+\r
+    if (!appData.clockMode) return;\r
+    if (gameMode==AnalyzeMode || gameMode == AnalyzeFile) return;\r
+       \r
+    GetTimeMark(&now);\r
+\r
+    lastTickLength = SubtractTimeMarks(&now, &tickStartTM);\r
+\r
+    /* Fudge if we woke up a little too soon */\r
+    fudge = intendedTickLength - lastTickLength;\r
+    if (fudge < 0 || fudge > FUDGE) fudge = 0;\r
+\r
+    if (WhiteOnMove(forwardMostMove)) {\r
+       if(whiteNPS >= 0) lastTickLength = 0;\r
+       timeRemaining = whiteTimeRemaining -= lastTickLength;\r
+       DisplayWhiteClock(whiteTimeRemaining - fudge,\r
+                         WhiteOnMove(currentMove));\r
+    } else {\r
+       if(blackNPS >= 0) lastTickLength = 0;\r
+       timeRemaining = blackTimeRemaining -= lastTickLength;\r
+       DisplayBlackClock(blackTimeRemaining - fudge,\r
+                         !WhiteOnMove(currentMove));\r
+    }\r
+\r
+    if (CheckFlags()) return;\r
+       \r
+    tickStartTM = now;\r
+    intendedTickLength = NextTickLength(timeRemaining - fudge) + fudge;\r
+    StartClockTimer(intendedTickLength);\r
+\r
+    /* if the time remaining has fallen below the alarm threshold, sound the\r
+     * alarm. if the alarm has sounded and (due to a takeback or time control\r
+     * with increment) the time remaining has increased to a level above the\r
+     * threshold, reset the alarm so it can sound again. \r
+     */\r
+    \r
+    if (appData.icsActive && appData.icsAlarm) {\r
+\r
+       /* make sure we are dealing with the user's clock */\r
+       if (!( ((gameMode == IcsPlayingWhite) && WhiteOnMove(currentMove)) ||\r
+              ((gameMode == IcsPlayingBlack) && !WhiteOnMove(currentMove))\r
+          )) return;\r
+\r
+       if (alarmSounded && (timeRemaining > appData.icsAlarmTime)) {\r
+           alarmSounded = FALSE;\r
+       } else if (!alarmSounded && (timeRemaining <= appData.icsAlarmTime)) { \r
+           PlayAlarmSound();\r
+           alarmSounded = TRUE;\r
+       }\r
+    }\r
+}\r
+\r
+\r
+/* A player has just moved, so stop the previously running\r
+   clock and (if in clock mode) start the other one.\r
+   We redisplay both clocks in case we're in ICS mode, because\r
+   ICS gives us an update to both clocks after every move.\r
+   Note that this routine is called *after* forwardMostMove\r
+   is updated, so the last fractional tick must be subtracted\r
+   from the color that is *not* on move now.\r
+*/\r
+void\r
+SwitchClocks()\r
+{\r
+    long lastTickLength;\r
+    TimeMark now;\r
+    int flagged = FALSE;\r
+\r
+    GetTimeMark(&now);\r
+\r
+    if (StopClockTimer() && appData.clockMode) {\r
+       lastTickLength = SubtractTimeMarks(&now, &tickStartTM);\r
+       if (WhiteOnMove(forwardMostMove)) {\r
+           if(blackNPS >= 0) lastTickLength = 0;\r
+           blackTimeRemaining -= lastTickLength;\r
+           /* [HGM] PGNtime: save time for PGN file if engine did not give it */\r
+//         if(pvInfoList[forwardMostMove-1].time == -1)\r
+                 pvInfoList[forwardMostMove-1].time =               // use GUI time\r
+                      (timeRemaining[1][forwardMostMove-1] - blackTimeRemaining)/10;\r
+       } else {\r
+          if(whiteNPS >= 0) lastTickLength = 0;\r
+          whiteTimeRemaining -= lastTickLength;\r
+           /* [HGM] PGNtime: save time for PGN file if engine did not give it */\r
+//         if(pvInfoList[forwardMostMove-1].time == -1)\r
+                 pvInfoList[forwardMostMove-1].time = \r
+                      (timeRemaining[0][forwardMostMove-1] - whiteTimeRemaining)/10;\r
+       }\r
+       flagged = CheckFlags();\r
+    }\r
+    CheckTimeControl();\r
+\r
+    if (flagged || !appData.clockMode) return;\r
+\r
+    switch (gameMode) {\r
+      case MachinePlaysBlack:\r
+      case MachinePlaysWhite:\r
+      case BeginningOfGame:\r
+       if (pausing) return;\r
+       break;\r
+\r
+      case EditGame:\r
+      case PlayFromGameFile:\r
+      case IcsExamining:\r
+       return;\r
+\r
+      default:\r
+       break;\r
+    }\r
+\r
+    tickStartTM = now;\r
+    intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?\r
+      whiteTimeRemaining : blackTimeRemaining);\r
+    StartClockTimer(intendedTickLength);\r
+}\r
+       \r
+\r
+/* Stop both clocks */\r
+void\r
+StopClocks()\r
+{      \r
+    long lastTickLength;\r
+    TimeMark now;\r
+\r
+    if (!StopClockTimer()) return;\r
+    if (!appData.clockMode) return;\r
+\r
+    GetTimeMark(&now);\r
+\r
+    lastTickLength = SubtractTimeMarks(&now, &tickStartTM);\r
+    if (WhiteOnMove(forwardMostMove)) {\r
+       if(whiteNPS >= 0) lastTickLength = 0;\r
+       whiteTimeRemaining -= lastTickLength;\r
+       DisplayWhiteClock(whiteTimeRemaining, WhiteOnMove(currentMove));\r
+    } else {\r
+       if(blackNPS >= 0) lastTickLength = 0;\r
+       blackTimeRemaining -= lastTickLength;\r
+       DisplayBlackClock(blackTimeRemaining, !WhiteOnMove(currentMove));\r
+    }\r
+    CheckFlags();\r
+}\r
+       \r
+/* Start clock of player on move.  Time may have been reset, so\r
+   if clock is already running, stop and restart it. */\r
+void\r
+StartClocks()\r
+{\r
+    (void) StopClockTimer(); /* in case it was running already */\r
+    DisplayBothClocks();\r
+    if (CheckFlags()) return;\r
+\r
+    if (!appData.clockMode) return;\r
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) return;\r
+\r
+    GetTimeMark(&tickStartTM);\r
+    intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?\r
+      whiteTimeRemaining : blackTimeRemaining);\r
+\r
+   /* [HGM] nps: figure out nps factors, by determining which engine plays white and/or black once and for all */\r
+    whiteNPS = blackNPS = -1; \r
+    if(gameMode == MachinePlaysWhite || gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'w'\r
+       || appData.zippyPlay && gameMode == IcsPlayingBlack) // first (perhaps only) engine has white\r
+       whiteNPS = first.nps;\r
+    if(gameMode == MachinePlaysBlack || gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b'\r
+       || appData.zippyPlay && gameMode == IcsPlayingWhite) // first (perhaps only) engine has black\r
+       blackNPS = first.nps;\r
+    if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b') // second only used in Two-Machines mode\r
+       whiteNPS = second.nps;\r
+    if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'w')\r
+       blackNPS = second.nps;\r
+    if(appData.debugMode) fprintf(debugFP, "nps: w=%d, b=%d\n", whiteNPS, blackNPS);\r
+\r
+    StartClockTimer(intendedTickLength);\r
+}\r
+\r
+char *\r
+TimeString(ms)\r
+     long ms;\r
+{\r
+    long second, minute, hour, day;\r
+    char *sign = "";\r
+    static char buf[32];\r
+    \r
+    if (ms > 0 && ms <= 9900) {\r
+      /* convert milliseconds to tenths, rounding up */\r
+      double tenths = floor( ((double)(ms + 99L)) / 100.00 );\r
+\r
+      sprintf(buf, " %03.1f ", tenths/10.0);\r
+      return buf;\r
+    }\r
+\r
+    /* convert milliseconds to seconds, rounding up */\r
+    /* use floating point to avoid strangeness of integer division\r
+       with negative dividends on many machines */\r
+    second = (long) floor(((double) (ms + 999L)) / 1000.0);\r
+\r
+    if (second < 0) {\r
+       sign = "-";\r
+       second = -second;\r
+    }\r
+    \r
+    day = second / (60 * 60 * 24);\r
+    second = second % (60 * 60 * 24);\r
+    hour = second / (60 * 60);\r
+    second = second % (60 * 60);\r
+    minute = second / 60;\r
+    second = second % 60;\r
+    \r
+    if (day > 0)\r
+      sprintf(buf, " %s%ld:%02ld:%02ld:%02ld ",\r
+             sign, day, hour, minute, second);\r
+    else if (hour > 0)\r
+      sprintf(buf, " %s%ld:%02ld:%02ld ", sign, hour, minute, second);\r
+    else\r
+      sprintf(buf, " %s%2ld:%02ld ", sign, minute, second);\r
+    \r
+    return buf;\r
+}\r
+\r
+\r
+/*\r
+ * This is necessary because some C libraries aren't ANSI C compliant yet.\r
+ */\r
+char *\r
+StrStr(string, match)\r
+     char *string, *match;\r
+{\r
+    int i, length;\r
+    \r
+    length = strlen(match);\r
+    \r
+    for (i = strlen(string) - length; i >= 0; i--, string++)\r
+      if (!strncmp(match, string, length))\r
+       return string;\r
+    \r
+    return NULL;\r
+}\r
+\r
+char *\r
+StrCaseStr(string, match)\r
+     char *string, *match;\r
+{\r
+    int i, j, length;\r
+    \r
+    length = strlen(match);\r
+    \r
+    for (i = strlen(string) - length; i >= 0; i--, string++) {\r
+       for (j = 0; j < length; j++) {\r
+           if (ToLower(match[j]) != ToLower(string[j]))\r
+             break;\r
+       }\r
+       if (j == length) return string;\r
+    }\r
+\r
+    return NULL;\r
+}\r
+\r
+#ifndef _amigados\r
+int\r
+StrCaseCmp(s1, s2)\r
+     char *s1, *s2;\r
+{\r
+    char c1, c2;\r
+    \r
+    for (;;) {\r
+       c1 = ToLower(*s1++);\r
+       c2 = ToLower(*s2++);\r
+       if (c1 > c2) return 1;\r
+       if (c1 < c2) return -1;\r
+       if (c1 == NULLCHAR) return 0;\r
+    }\r
+}\r
+\r
+\r
+int\r
+ToLower(c)\r
+     int c;\r
+{\r
+    return isupper(c) ? tolower(c) : c;\r
+}\r
+\r
+\r
+int\r
+ToUpper(c)\r
+     int c;\r
+{\r
+    return islower(c) ? toupper(c) : c;\r
+}\r
+#endif /* !_amigados   */\r
+\r
+char *\r
+StrSave(s)\r
+     char *s;\r
+{\r
+    char *ret;\r
+\r
+    if ((ret = (char *) malloc(strlen(s) + 1))) {\r
+       strcpy(ret, s);\r
+    }\r
+    return ret;\r
+}\r
+\r
+char *\r
+StrSavePtr(s, savePtr)\r
+     char *s, **savePtr;\r
+{\r
+    if (*savePtr) {\r
+       free(*savePtr);\r
+    }\r
+    if ((*savePtr = (char *) malloc(strlen(s) + 1))) {\r
+       strcpy(*savePtr, s);\r
+    }\r
+    return(*savePtr);\r
+}\r
+\r
+char *\r
+PGNDate()\r
+{\r
+    time_t clock;\r
+    struct tm *tm;\r
+    char buf[MSG_SIZ];\r
+\r
+    clock = time((time_t *)NULL);\r
+    tm = localtime(&clock);\r
+    sprintf(buf, "%04d.%02d.%02d",\r
+           tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);\r
+    return StrSave(buf);\r
+}\r
+\r
+\r
+char *\r
+PositionToFEN(move, useFEN960)\r
+     int move;\r
+     int useFEN960;\r
+{\r
+    int i, j, fromX, fromY, toX, toY;\r
+    int whiteToPlay;\r
+    char buf[128];\r
+    char *p, *q;\r
+    int emptycount;\r
+    ChessSquare piece;\r
+\r
+    whiteToPlay = (gameMode == EditPosition) ?\r
+      !blackPlaysFirst : (move % 2 == 0);\r
+    p = buf;\r
+\r
+    /* Piece placement data */\r
+    for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+       emptycount = 0;\r
+        for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {\r
+           if (boards[move][i][j] == EmptySquare) {\r
+               emptycount++;\r
+            } else { ChessSquare piece = boards[move][i][j];\r
+               if (emptycount > 0) {\r
+                    if(emptycount<10) /* [HGM] can be >= 10 */\r
+                        *p++ = '0' + emptycount;\r
+                    else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; }\r
+                   emptycount = 0;\r
+               }\r
+                if(PieceToChar(piece) == '+') {\r
+                    /* [HGM] write promoted pieces as '+<unpromoted>' (Shogi) */\r
+                    *p++ = '+';\r
+                    piece = (ChessSquare)(DEMOTED piece);\r
+                } \r
+                *p++ = PieceToChar(piece);\r
+                if(p[-1] == '~') {\r
+                    /* [HGM] flag promoted pieces as '<promoted>~' (Crazyhouse) */\r
+                    p[-1] = PieceToChar((ChessSquare)(DEMOTED piece));\r
+                    *p++ = '~';\r
+                }\r
+           }\r
+       }\r
+       if (emptycount > 0) {\r
+            if(emptycount<10) /* [HGM] can be >= 10 */\r
+                *p++ = '0' + emptycount;\r
+            else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; }\r
+           emptycount = 0;\r
+       }\r
+       *p++ = '/';\r
+    }\r
+    *(p - 1) = ' ';\r
+\r
+    /* [HGM] print Crazyhouse or Shogi holdings */\r
+    if( gameInfo.holdingsWidth ) {\r
+        *(p-1) = '['; /* if we wanted to support BFEN, this could be '/' */\r
+        q = p;\r
+        for(i=0; i<gameInfo.holdingsSize; i++) { /* white holdings */\r
+            piece = boards[move][i][BOARD_WIDTH-1];\r
+            if( piece != EmptySquare )\r
+              for(j=0; j<(int) boards[move][i][BOARD_WIDTH-2]; j++)\r
+                  *p++ = PieceToChar(piece);\r
+        }\r
+        for(i=0; i<gameInfo.holdingsSize; i++) { /* black holdings */\r
+            piece = boards[move][BOARD_HEIGHT-i-1][0];\r
+            if( piece != EmptySquare )\r
+              for(j=0; j<(int) boards[move][BOARD_HEIGHT-i-1][1]; j++)\r
+                  *p++ = PieceToChar(piece);\r
+        }\r
+\r
+        if( q == p ) *p++ = '-';\r
+        *p++ = ']';\r
+        *p++ = ' ';\r
+    }\r
+\r
+    /* Active color */\r
+    *p++ = whiteToPlay ? 'w' : 'b';\r
+    *p++ = ' ';\r
+\r
+  if(nrCastlingRights) {\r
+     q = p;\r
+     if(gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) {\r
+       /* [HGM] write directly from rights */\r
+           if(castlingRights[move][2] >= 0 &&\r
+              castlingRights[move][0] >= 0   )\r
+                *p++ = castlingRights[move][0] + AAA + 'A' - 'a';\r
+           if(castlingRights[move][2] >= 0 &&\r
+              castlingRights[move][1] >= 0   )\r
+                *p++ = castlingRights[move][1] + AAA + 'A' - 'a';\r
+           if(castlingRights[move][5] >= 0 &&\r
+              castlingRights[move][3] >= 0   )\r
+                *p++ = castlingRights[move][3] + AAA;\r
+           if(castlingRights[move][5] >= 0 &&\r
+              castlingRights[move][4] >= 0   )\r
+                *p++ = castlingRights[move][4] + AAA;\r
+     } else {\r
+\r
+        /* [HGM] write true castling rights */\r
+        if( nrCastlingRights == 6 ) {\r
+            if(castlingRights[move][0] == BOARD_RGHT-1 &&\r
+               castlingRights[move][2] >= 0  ) *p++ = 'K';\r
+            if(castlingRights[move][1] == BOARD_LEFT &&\r
+               castlingRights[move][2] >= 0  ) *p++ = 'Q';\r
+            if(castlingRights[move][3] == BOARD_RGHT-1 &&\r
+               castlingRights[move][5] >= 0  ) *p++ = 'k';\r
+            if(castlingRights[move][4] == BOARD_LEFT &&\r
+               castlingRights[move][5] >= 0  ) *p++ = 'q';\r
+        }\r
+     }\r
+     if (q == p) *p++ = '-'; /* No castling rights */\r
+     *p++ = ' ';\r
+  }\r
+\r
+  if(gameInfo.variant != VariantShogi    && gameInfo.variant != VariantXiangqi &&\r
+     gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) { \r
+    /* En passant target square */\r
+    if (move > backwardMostMove) {\r
+        fromX = moveList[move - 1][0] - AAA;\r
+        fromY = moveList[move - 1][1] - ONE;\r
+        toX = moveList[move - 1][2] - AAA;\r
+        toY = moveList[move - 1][3] - ONE;\r
+       if (fromY == (whiteToPlay ? BOARD_HEIGHT-2 : 1) &&\r
+           toY == (whiteToPlay ? BOARD_HEIGHT-4 : 3) &&\r
+           boards[move][toY][toX] == (whiteToPlay ? BlackPawn : WhitePawn) &&\r
+           fromX == toX) {\r
+           /* 2-square pawn move just happened */\r
+            *p++ = toX + AAA;\r
+           *p++ = whiteToPlay ? '6'+BOARD_HEIGHT-8 : '3';\r
+       } else {\r
+           *p++ = '-';\r
+       }\r
+    } else {\r
+       *p++ = '-';\r
+    }\r
+    *p++ = ' ';\r
+  }\r
+\r
+    /* [HGM] find reversible plies */\r
+    {   int i = 0, j=move;\r
+\r
+        if (appData.debugMode) { int k;\r
+            fprintf(debugFP, "write FEN 50-move: %d %d %d\n", initialRulePlies, forwardMostMove, backwardMostMove);\r
+            for(k=backwardMostMove; k<=forwardMostMove; k++)\r
+                fprintf(debugFP, "e%d. p=%d\n", k, epStatus[k]);\r
+\r
+        }\r
+\r
+        while(j > backwardMostMove && epStatus[j] <= EP_NONE) j--,i++;\r
+        if( j == backwardMostMove ) i += initialRulePlies;\r
+        sprintf(p, "%d ", i);\r
+        p += i>=100 ? 4 : i >= 10 ? 3 : 2;\r
+    }\r
+    /* Fullmove number */\r
+    sprintf(p, "%d", (move / 2) + 1);\r
+    \r
+    return StrSave(buf);\r
+}\r
+\r
+Boolean\r
+ParseFEN(board, blackPlaysFirst, fen)\r
+    Board board;\r
+     int *blackPlaysFirst;\r
+     char *fen;\r
+{\r
+    int i, j;\r
+    char *p;\r
+    int emptycount;\r
+    ChessSquare piece;\r
+\r
+    p = fen;\r
+\r
+    /* [HGM] by default clear Crazyhouse holdings, if present */\r
+    if(gameInfo.holdingsWidth) {\r
+       for(i=0; i<BOARD_HEIGHT; i++) {\r
+           board[i][0]             = EmptySquare; /* black holdings */\r
+           board[i][BOARD_WIDTH-1] = EmptySquare; /* white holdings */\r
+           board[i][1]             = (ChessSquare) 0; /* black counts */\r
+           board[i][BOARD_WIDTH-2] = (ChessSquare) 0; /* white counts */\r
+       }\r
+    }\r
+\r
+    /* Piece placement data */\r
+    for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
+       j = 0;\r
+       for (;;) {\r
+            if (*p == '/' || *p == ' ' || (*p == '[' && i == 0) ) {\r
+                if (*p == '/') p++;\r
+                emptycount = gameInfo.boardWidth - j;\r
+                while (emptycount--)\r
+                        board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
+               break;\r
+#if(BOARD_SIZE >= 10)\r
+            } else if(*p=='x' || *p=='X') { /* [HGM] X means 10 */\r
+                p++; emptycount=10;\r
+                if (j + emptycount > gameInfo.boardWidth) return FALSE;\r
+                while (emptycount--)\r
+                        board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
+#endif\r
+            } else if (isdigit(*p)) {\r
+               emptycount = *p++ - '0';\r
+                while(isdigit(*p)) emptycount = 10*emptycount + *p++ - '0'; /* [HGM] allow > 9 */\r
+                if (j + emptycount > gameInfo.boardWidth) return FALSE;\r
+                while (emptycount--)\r
+                        board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
+            } else if (*p == '+' || isalpha(*p)) {\r
+                if (j >= gameInfo.boardWidth) return FALSE;\r
+                if(*p=='+') {\r
+                    piece = CharToPiece(*++p);\r
+                    if(piece == EmptySquare) return FALSE; /* unknown piece */\r
+                    piece = (ChessSquare) (PROMOTED piece ); p++;\r
+                    if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */\r
+                } else piece = CharToPiece(*p++);\r
+\r
+                if(piece==EmptySquare) return FALSE; /* unknown piece */\r
+                if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */\r
+                    piece = (ChessSquare) (PROMOTED piece);\r
+                    if(PieceToChar(piece) != '~') return FALSE; /* cannot be a promoted piece */\r
+                    p++;\r
+                }\r
+                board[i][(j++)+gameInfo.holdingsWidth] = piece;\r
+           } else {\r
+               return FALSE;\r
+           }\r
+       }\r
+    }\r
+    while (*p == '/' || *p == ' ') p++;\r
+\r
+    /* [HGM] look for Crazyhouse holdings here */\r
+    while(*p==' ') p++;\r
+    if( gameInfo.holdingsWidth && p[-1] == '/' || *p == '[') {\r
+        if(*p == '[') p++;\r
+        if(*p == '-' ) *p++; /* empty holdings */ else {\r
+            if( !gameInfo.holdingsWidth ) return FALSE; /* no room to put holdings! */\r
+            /* if we would allow FEN reading to set board size, we would   */\r
+            /* have to add holdings and shift the board read so far here   */\r
+            while( (piece = CharToPiece(*p) ) != EmptySquare ) {\r
+                *p++;\r
+                if((int) piece >= (int) BlackPawn ) {\r
+                    i = (int)piece - (int)BlackPawn;\r
+                   i = PieceToNumber((ChessSquare)i);\r
+                    if( i >= gameInfo.holdingsSize ) return FALSE;\r
+                    board[BOARD_HEIGHT-1-i][0] = piece; /* black holdings */\r
+                    board[BOARD_HEIGHT-1-i][1]++;       /* black counts   */\r
+                } else {\r
+                    i = (int)piece - (int)WhitePawn;\r
+                   i = PieceToNumber((ChessSquare)i);\r
+                    if( i >= gameInfo.holdingsSize ) return FALSE;\r
+                    board[i][BOARD_WIDTH-1] = piece;    /* white holdings */\r
+                    board[i][BOARD_WIDTH-2]++;          /* black holdings */\r
+                }\r
+            }\r
+        }\r
+        if(*p == ']') *p++;\r
+    }\r
+\r
+    while(*p == ' ') p++;\r
+\r
+    /* Active color */\r
+    switch (*p++) {\r
+      case 'w':\r
+        *blackPlaysFirst = FALSE;\r
+       break;\r
+      case 'b': \r
+       *blackPlaysFirst = TRUE;\r
+       break;\r
+      default:\r
+       return FALSE;\r
+    }\r
+\r
+    /* [HGM] We NO LONGER ignore the rest of the FEN notation */\r
+    /* return the extra info in global variiables             */\r
+\r
+    /* set defaults in case FEN is incomplete */\r
+    FENepStatus = EP_UNKNOWN;\r
+    for(i=0; i<nrCastlingRights; i++ ) {\r
+        FENcastlingRights[i] =\r
+            gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom ? -1 : initialRights[i];\r
+    }   /* assume possible unless obviously impossible */\r
+    if(initialRights[0]>=0 && board[castlingRank[0]][initialRights[0]] != WhiteRook) FENcastlingRights[0] = -1;\r
+    if(initialRights[1]>=0 && board[castlingRank[1]][initialRights[1]] != WhiteRook) FENcastlingRights[1] = -1;\r
+    if(initialRights[2]>=0 && board[castlingRank[2]][initialRights[2]] != WhiteKing) FENcastlingRights[2] = -1;\r
+    if(initialRights[3]>=0 && board[castlingRank[3]][initialRights[3]] != BlackRook) FENcastlingRights[3] = -1;\r
+    if(initialRights[4]>=0 && board[castlingRank[4]][initialRights[4]] != BlackRook) FENcastlingRights[4] = -1;\r
+    if(initialRights[5]>=0 && board[castlingRank[5]][initialRights[5]] != BlackKing) FENcastlingRights[5] = -1;\r
+    FENrulePlies = 0;\r
+\r
+    while(*p==' ') p++;\r
+    if(nrCastlingRights) {\r
+      if(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {\r
+          /* castling indicator present, so default becomes no castlings */\r
+          for(i=0; i<nrCastlingRights; i++ ) {\r
+                 FENcastlingRights[i] = -1;\r
+          }\r
+      }\r
+      while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-' ||\r
+             (gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) &&\r
+             ( *p >= 'a' && *p < 'a' + gameInfo.boardWidth) ||\r
+             ( *p >= 'A' && *p < 'A' + gameInfo.boardWidth)   ) {\r
+        char c = *p++; int whiteKingFile=-1, blackKingFile=-1;\r
+\r
+        for(i=BOARD_LEFT; i<BOARD_RGHT; i++) {\r
+            if(board[BOARD_HEIGHT-1][i] == BlackKing) blackKingFile = i;\r
+            if(board[0             ][i] == WhiteKing) whiteKingFile = i;\r
+        }\r
+        switch(c) {\r
+          case'K':\r
+              for(i=BOARD_RGHT-1; board[0][i]!=WhiteRook && i>whiteKingFile; i--);\r
+              FENcastlingRights[0] = i != whiteKingFile ? i : -1;\r
+              FENcastlingRights[2] = whiteKingFile;\r
+              break;\r
+          case'Q':\r
+              for(i=BOARD_LEFT; board[0][i]!=WhiteRook && i<whiteKingFile; i++);\r
+              FENcastlingRights[1] = i != whiteKingFile ? i : -1;\r
+              FENcastlingRights[2] = whiteKingFile;\r
+              break;\r
+          case'k':\r
+              for(i=BOARD_RGHT-1; board[BOARD_HEIGHT-1][i]!=BlackRook && i>blackKingFile; i--);\r
+              FENcastlingRights[3] = i != blackKingFile ? i : -1;\r
+              FENcastlingRights[5] = blackKingFile;\r
+              break;\r
+          case'q':\r
+              for(i=BOARD_LEFT; board[BOARD_HEIGHT-1][i]!=BlackRook && i<blackKingFile; i++);\r
+              FENcastlingRights[4] = i != blackKingFile ? i : -1;\r
+              FENcastlingRights[5] = blackKingFile;\r
+          case '-':\r
+              break;\r
+          default: /* FRC castlings */\r
+              if(c >= 'a') { /* black rights */\r
+                  for(i=BOARD_LEFT; i<BOARD_RGHT; i++)\r
+                    if(board[BOARD_HEIGHT-1][i] == BlackKing) break;\r
+                  if(i == BOARD_RGHT) break;\r
+                  FENcastlingRights[5] = i;\r
+                  c -= AAA;\r
+                  if(board[BOARD_HEIGHT-1][c] <  BlackPawn ||\r
+                     board[BOARD_HEIGHT-1][c] >= BlackKing   ) break;\r
+                  if(c > i)\r
+                      FENcastlingRights[3] = c;\r
+                  else\r
+                      FENcastlingRights[4] = c;\r
+              } else { /* white rights */\r
+                  for(i=BOARD_LEFT; i<BOARD_RGHT; i++)\r
+                    if(board[0][i] == WhiteKing) break;\r
+                  if(i == BOARD_RGHT) break;\r
+                  FENcastlingRights[2] = i;\r
+                  c -= AAA - 'a' + 'A';\r
+                  if(board[0][c] >= WhiteKing) break;\r
+                  if(c > i)\r
+                      FENcastlingRights[0] = c;\r
+                  else\r
+                      FENcastlingRights[1] = c;\r
+              }\r
+        }\r
+      }\r
+    if (appData.debugMode) {\r
+        fprintf(debugFP, "FEN castling rights:");\r
+        for(i=0; i<nrCastlingRights; i++)\r
+        fprintf(debugFP, " %d", FENcastlingRights[i]);\r
+        fprintf(debugFP, "\n");\r
+    }\r
+\r
+      while(*p==' ') p++;\r
+    }\r
+\r
+    /* read e.p. field in games that know e.p. capture */\r
+    if(gameInfo.variant != VariantShogi    && gameInfo.variant != VariantXiangqi &&\r
+       gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) { \r
+      if(*p=='-') {\r
+        p++; FENepStatus = EP_NONE;\r
+      } else {\r
+         char c = *p++ - AAA;\r
+\r
+         if(c < BOARD_LEFT || c >= BOARD_RGHT) return TRUE;\r
+         if(*p >= '0' && *p <='9') *p++;\r
+         FENepStatus = c;\r
+      }\r
+    }\r
+\r
+\r
+    if(sscanf(p, "%d", &i) == 1) {\r
+        FENrulePlies = i; /* 50-move ply counter */\r
+        /* (The move number is still ignored)    */\r
+    }\r
+\r
+    return TRUE;\r
+}\r
+      \r
+void\r
+EditPositionPasteFEN(char *fen)\r
+{\r
+  if (fen != NULL) {\r
+    Board initial_position;\r
+\r
+    if (!ParseFEN(initial_position, &blackPlaysFirst, fen)) {\r
+      DisplayError(_("Bad FEN position in clipboard"), 0);\r
+      return ;\r
+    } else {\r
+      int savedBlackPlaysFirst = blackPlaysFirst;\r
+      EditPositionEvent();\r
+      blackPlaysFirst = savedBlackPlaysFirst;\r
+      CopyBoard(boards[0], initial_position);\r
+          /* [HGM] copy FEN attributes as well */\r
+          {   int i;\r
+              initialRulePlies = FENrulePlies;\r
+              epStatus[0] = FENepStatus;\r
+              for( i=0; i<nrCastlingRights; i++ )\r
+                  castlingRights[0][i] = FENcastlingRights[i];\r
+          }\r
+      EditPositionDone();\r
+      DisplayBothClocks();\r
+      DrawPosition(FALSE, boards[currentMove]);\r
+    }\r
+  }\r
+}\r