Give the dual-board option a separate board window
[xboard.git] / backend.c
index 64b5f3d..5715e5d 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -5,7 +5,7 @@
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
  *
  * Enhancements Copyright 2005 Alessandro Scotti
  *
@@ -147,12 +147,6 @@ extern int gettimeofday(struct timeval *, struct timezone *);
 #endif
 
 
-/* A point in time */
-typedef struct {
-    long sec;  /* Assuming this is >= 32 bits */
-    int ms;    /* Assuming this is >= 16 bits */
-} TimeMark;
-
 int establish P((void));
 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
                         char *buf, int count, int error));
@@ -168,8 +162,6 @@ 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));
 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
@@ -204,7 +196,6 @@ 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));
@@ -213,8 +204,6 @@ 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));
@@ -233,6 +222,7 @@ int NextTourneyGame P((int nr, int *swap));
 int Pairing P((int nr, int nPlayers, int *w, int *b, int *sync));
 FILE *WriteTourneyFile P((char *results, FILE *f));
 void DisplayTwoMachinesTitle P(());
+static void ExcludeClick P((int index));
 
 #ifdef WIN32
        extern void ConsoleCreate();
@@ -323,7 +313,7 @@ int promoDefaultAltered;
 #define TN_PORT 23
 
 char*
-safeStrCpy( char *dst, const char *src, size_t count )
+safeStrCpy (char *dst, const char *src, size_t count)
 { // [HGM] made safe
   int i;
   assert( dst != NULL );
@@ -351,7 +341,7 @@ safeStrCpy( char *dst, const char *src, size_t count )
  * We used this for all compiler
  */
 double
-u64ToDouble(u64 value)
+u64ToDouble (u64 value)
 {
   double r;
   u64 tmp = value & u64Const(0x7fffffffffffffff);
@@ -369,7 +359,7 @@ u64ToDouble(u64 value)
    by this function.
  */
 int
-PosFlags(index)
+PosFlags (index)
 {
   int flags = F_ALL_CASTLE_OK;
   if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
@@ -403,7 +393,8 @@ PosFlags(index)
   return flags;
 }
 
-FILE *gameFileFP, *debugFP;
+FILE *gameFileFP, *debugFP, *serverFP;
+char *currentDebugFile; // [HGM] debug split: to remember name
 
 /*
     [AS] Note: sometimes, the sscanf() function is used to parse the input
@@ -465,6 +456,7 @@ int have_sent_ICS_logon = 0;
 int movesPerSession;
 int suddenDeath, whiteStartMove, blackStartMove; /* [HGM] for implementation of 'any per time' sessions, as in first part of byoyomi TC */
 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement, lastWhite, lastBlack;
+Boolean adjustedClock;
 long timeControl_2; /* [AS] Allow separate time controls */
 char *fullTimeControlString = NULL, *nextSession, *whiteTC, *blackTC; /* [HGM] secondary TC: merge of MPS, TC and inc */
 long timeRemaining[2][MAX_MOVES];
@@ -618,10 +610,10 @@ ChessSquare GothicArray[2][BOARD_FILES] = {
 
 #ifdef FALCON
 ChessSquare FalconArray[2][BOARD_FILES] = {
-    { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen,
-        WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },
-    { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen,
-        BlackKing, BlackLance, BlackBishop, BlackKnight, BlackRook }
+    { WhiteRook, WhiteKnight, WhiteBishop, WhiteFalcon, WhiteQueen,
+        WhiteKing, WhiteFalcon, WhiteBishop, WhiteKnight, WhiteRook },
+    { BlackRook, BlackKnight, BlackBishop, BlackFalcon, BlackQueen,
+        BlackKing, BlackFalcon, BlackBishop, BlackKnight, BlackRook }
 };
 #else // !FALCON
 #define FalconArray CapablancaArray
@@ -653,8 +645,7 @@ Board initialPosition;
 
    "++++", etc. Also strips ()'s */
 int
-string_to_rating(str)
-  char *str;
+string_to_rating (char *str)
 {
   while(*str && !isdigit(*str)) ++str;
   if (!*str)
@@ -664,7 +655,7 @@ string_to_rating(str)
 }
 
 void
-ClearProgramStats()
+ClearProgramStats ()
 {
     /* Init programStats */
     programStats.movelist[0] = 0;
@@ -680,7 +671,7 @@ ClearProgramStats()
 }
 
 void
-CommonEngineInit()
+CommonEngineInit ()
 {   // [HGM] moved some code here from InitBackend1 that has to be done after both engines have contributed their settings
     if (appData.firstPlaysBlack) {
        first.twoMachinesColor = "black\n";
@@ -714,7 +705,7 @@ CommonEngineInit()
 }
 
 void
-UnloadEngine(ChessProgramState *cps)
+UnloadEngine (ChessProgramState *cps)
 {
        /* Kill off first chess program */
        if (cps->isr != NULL)
@@ -733,7 +724,7 @@ UnloadEngine(ChessProgramState *cps)
 }
 
 void
-ClearOptions(ChessProgramState *cps)
+ClearOptions (ChessProgramState *cps)
 {
     int i;
     cps->nrOptions = cps->comboCnt = 0;
@@ -744,12 +735,16 @@ ClearOptions(ChessProgramState *cps)
 }
 
 char *engineNames[] = {
-"first",
-"second"
+  /* TRANSLATORS: "first" is the first of possible two chess engines. It is inserted into strings
+     such as "%s engine" / "%s chess program" / "%s machine" - all meaning the same thing */
+N_("first"),
+  /* TRANSLATORS: "second" is the second of possible two chess engines. It is inserted into strings
+     such as "%s engine" / "%s chess program" / "%s machine" - all meaning the same thing */
+N_("second")
 };
 
 void
-InitEngine(ChessProgramState *cps, int n)
+InitEngine (ChessProgramState *cps, int n)
 {   // [HGM] all engine initialiation put in a function that does one engine
 
     ClearOptions(cps);
@@ -825,7 +820,7 @@ InitEngine(ChessProgramState *cps, int n)
 
        len = snprintf(buf, MSG_SIZ, _("protocol version %d not supported"),
                       appData.protocolVersion[n]);
-       if( (len > MSG_SIZ) && appData.debugMode )
+       if( (len >= MSG_SIZ) && appData.debugMode )
          fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
 
        DisplayFatalError(buf, 0, 2);
@@ -836,12 +831,13 @@ InitEngine(ChessProgramState *cps, int n)
       }
 
     InitEngineUCI( installDir, cps );  // [HGM] moved here from winboard.c, to make available in xboard
+    ParseFeatures(appData.featureDefaults, cps);
 }
 
 ChessProgramState *savCps;
 
 void
-LoadEngine()
+LoadEngine ()
 {
     int i;
     if(WaitForEngine(savCps, LoadEngine)) return;
@@ -861,7 +857,7 @@ LoadEngine()
 }
 
 void
-ReplaceEngine(ChessProgramState *cps, int n)
+ReplaceEngine (ChessProgramState *cps, int n)
 {
     EditGameEvent();
     UnloadEngine(cps);
@@ -879,37 +875,64 @@ extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
 
 static char resetOptions[] = 
        "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
-       "-firstOptions \"\" -firstNPS -1 -fn \"\"";
+       "-firstInitString \"" INIT_STRING "\" -firstComputerString \"" COMPUTER_STRING "\" "
+       "-firstFeatures \"\" -firstLogo \"\" -firstAccumulateTC 1 "
+       "-firstOptions \"\" -firstNPS -1 -fn \"\" -firstScoreAbs false";
+
+void
+FloatToFront(char **list, char *engineLine)
+{
+    char buf[MSG_SIZ], tidy[MSG_SIZ], *p = buf, *q, *r = buf;
+    int i=0;
+    if(appData.recentEngines <= 0) return;
+    TidyProgramName(engineLine, "localhost", tidy+1);
+    tidy[0] = buf[0] = '\n'; strcat(tidy, "\n");
+    strncpy(buf+1, *list, MSG_SIZ-50);
+    if(p = strstr(buf, tidy)) { // tidy name appears in list
+       q = strchr(++p, '\n'); if(q == NULL) return; // malformed, don't touch
+       while(*p++ = *++q); // squeeze out
+    }
+    strcat(tidy, buf+1); // put list behind tidy name
+    p = tidy + 1; while(q = strchr(p, '\n')) i++, r = p, p = q + 1; // count entries in new list
+    if(i > appData.recentEngines) *r = NULLCHAR; // if maximum rached, strip off last
+    ASSIGN(*list, tidy+1);
+}
+
+char *insert, *wbOptions; // point in ChessProgramNames were we should insert new engine
 
 void
-Load(ChessProgramState *cps, int i)
+Load (ChessProgramState *cps, int i)
 {
     char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ];
     if(engineLine && engineLine[0]) { // an engine was selected from the combo box
        snprintf(buf, MSG_SIZ, "-fcp %s", engineLine);
        SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second*
-       ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL;
+       ParseArgsFromString(resetOptions); appData.pvSAN[0] = FALSE;
+       FREE(appData.fenOverride[0]); appData.fenOverride[0] = NULL;
+       appData.firstProtocolVersion = PROTOVER;
        ParseArgsFromString(buf);
        SwapEngines(i);
        ReplaceEngine(cps, i);
+       FloatToFront(&appData.recentEngineList, engineLine);
        return;
     }
     p = engineName;
     while(q = strchr(p, SLASH)) p = q+1;
     if(*p== NULLCHAR) { DisplayError(_("You did not specify the engine executable"), 0); return; }
-    if(engineDir[0] != NULLCHAR)
-       appData.directory[i] = engineDir;
-    else if(p != engineName) { // derive directory from engine path, when not given
+    if(engineDir[0] != NULLCHAR) {
+       ASSIGN(appData.directory[i], engineDir);
+    } else if(p != engineName) { // derive directory from engine path, when not given
        p[-1] = 0;
-       appData.directory[i] = strdup(engineName);
+       ASSIGN(appData.directory[i], engineName);
        p[-1] = SLASH;
-    } else appData.directory[i] = ".";
+       if(SLASH == '/' && p - engineName > 1) *(p -= 2) = '.'; // for XBoard use ./exeName as command after split!
+    } else { ASSIGN(appData.directory[i], "."); }
     if(params[0]) {
        if(strchr(p, ' ') && !strchr(p, '"')) snprintf(buf2, MSG_SIZ, "\"%s\"", p), p = buf2; // quote if it contains spaces
        snprintf(command, MSG_SIZ, "%s %s", p, params);
        p = command;
     }
-    appData.chessProgram[i] = strdup(p);
+    ASSIGN(appData.chessProgram[i], p);
     appData.isUCI[i] = isUCI;
     appData.protocolVersion[i] = v1 ? 1 : PROTOVER;
     appData.hasOwnBookUCI[i] = hasBook;
@@ -931,15 +954,18 @@ Load(ChessProgramState *cps, int i)
                        isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "",
                        storeVariant ? " -variant " : "",
                        storeVariant ? VariantName(gameInfo.variant) : "");
+       if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf)-1, MSG_SIZ-strlen(buf), " %s\n", wbOptions);
        firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1);
-       snprintf(firstChessProgramNames, len, "%s%s", q, buf);
+       if(insert != q) insert[-1] = NULLCHAR;
+       snprintf(firstChessProgramNames, len, "%s\n%s%s", q, buf, insert);
        if(q)   free(q);
+       FloatToFront(&appData.recentEngineList, buf);
     }
     ReplaceEngine(cps, i);
 }
 
 void
-InitTimeControls()
+InitTimeControls ()
 {
     int matched, min, sec;
     /*
@@ -970,7 +996,7 @@ InitTimeControls()
 }
 
 void
-InitBackEnd1()
+InitBackEnd1 ()
 {
 
     ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options
@@ -1070,7 +1096,7 @@ InitBackEnd1()
       case VariantKriegspiel:   /* need to hide pieces and move details */
        /* case VariantFischeRandom: (Fabien: moved below) */
        len = snprintf(buf,MSG_SIZ, _("Variant %s supported only in ICS mode"), appData.variant);
-       if( (len > MSG_SIZ) && appData.debugMode )
+       if( (len >= MSG_SIZ) && appData.debugMode )
          fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
 
        DisplayFatalError(buf, 0, 2);
@@ -1088,7 +1114,7 @@ InitBackEnd1()
       case Variant36:
       default:
        len = snprintf(buf, MSG_SIZ, _("Unknown variant name %s"), appData.variant);
-       if( (len > MSG_SIZ) && appData.debugMode )
+       if( (len >= MSG_SIZ) && appData.debugMode )
          fprintf(debugFP, "InitBackEnd1: buffer truncated.\n");
 
        DisplayFatalError(buf, 0, 2);
@@ -1134,7 +1160,8 @@ InitBackEnd1()
 
 }
 
-int NextIntegerFromString( char ** str, long * value )
+int
+NextIntegerFromString (char ** str, long * value)
 {
     int result = -1;
     char * s = *str;
@@ -1159,7 +1186,8 @@ int NextIntegerFromString( char ** str, long * value )
     return result;
 }
 
-int NextTimeControlFromString( char ** str, long * value )
+int
+NextTimeControlFromString (char ** str, long * value)
 {
     long temp;
     int result = NextIntegerFromString( str, &temp );
@@ -1176,7 +1204,8 @@ int NextTimeControlFromString( char ** str, long * value )
     return result;
 }
 
-int NextSessionFromString( char ** str, int *moves, long * tc, long *inc, int *incType)
+int
+NextSessionFromString (char ** str, int *moves, long * tc, long *inc, int *incType)
 {   /* [HGM] routine added to read '+moves/time' for secondary time control. */
     int result = -1, type = 0; long temp, temp2;
 
@@ -1217,18 +1246,17 @@ int NextSessionFromString( char ** str, int *moves, long * tc, long *inc, int *i
     return result;
 }
 
-int GetTimeQuota(int movenr, int lastUsed, char *tcString)
+int
+GetTimeQuota (int movenr, int lastUsed, char *tcString)
 {   /* [HGM] get time to add from the multi-session time-control string */
     int incType, moves=1; /* kludge to force reading of first session */
     long time, increment;
     char *s = tcString;
 
     if(!*s) return 0; // empty TC string means we ran out of the last sudden-death version
-    if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", tcString);
     do {
         if(moves) NextSessionFromString(&s, &moves, &time, &increment, &incType);
         nextSession = s; suddenDeath = moves == 0 && increment == 0;
-        if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment);
         if(movenr == -1) return time;    /* last move before new session     */
         if(incType == '*') increment = 0; else // for sandclock, time is added while not thinking
         if(incType == '!' && lastUsed < increment) increment = lastUsed;
@@ -1240,10 +1268,7 @@ int GetTimeQuota(int movenr, int lastUsed, char *tcString)
 }
 
 int
-ParseTimeControl(tc, ti, mps)
-     char *tc;
-     float ti;
-     int mps;
+ParseTimeControl (char *tc, float ti, int mps)
 {
   long tc1;
   long tc2;
@@ -1306,11 +1331,12 @@ ParseTimeControl(tc, ti, mps)
 }
 
 void
-InitBackEnd2()
+InitBackEnd2 ()
 {
     if (appData.debugMode) {
        fprintf(debugFP, "%s\n", programVersion);
     }
+    ASSIGN(currentDebugFile, appData.nameOfDebugFile); // [HGM] debug split: remember initial name in use
 
     set_cont_sequence(appData.wrapContSeq);
     if (appData.matchGames > 0) {
@@ -1336,7 +1362,7 @@ InitBackEnd2()
 }
 
 int
-CalculateIndex(int index, int gameNr)
+CalculateIndex (int index, int gameNr)
 {   // [HGM] autoinc: absolute way to determine load index from game number (taking auto-inc and rewind into account)
     int res;
     if(index > 0) return index; // fixed nmber
@@ -1347,7 +1373,7 @@ CalculateIndex(int index, int gameNr)
 }
 
 int
-LoadGameOrPosition(int gameNr)
+LoadGameOrPosition (int gameNr)
 {   // [HGM] taken out of MatchEvent and NextMatchGame (to combine it)
     if (*appData.loadGameFile != NULLCHAR) {
        if (!LoadGameFromFile(appData.loadGameFile,
@@ -1368,7 +1394,7 @@ LoadGameOrPosition(int gameNr)
 }
 
 void
-ReserveGame(int gameNr, char resChar)
+ReserveGame (int gameNr, char resChar)
 {
     FILE *tf = fopen(appData.tourneyFile, "r+");
     char *p, *q, c, buf[MSG_SIZ];
@@ -1404,13 +1430,17 @@ ReserveGame(int gameNr, char resChar)
     free(p); appData.results = q;
     if(nextGame <= appData.matchGames && resChar != ' ' && !abortMatch &&
        (gameNr < 0 || nextGame / appData.defaultMatchGames != gameNr / appData.defaultMatchGames)) {
+      int round = appData.defaultMatchGames * appData.tourneyType;
+      if(gameNr < 0 || appData.tourneyType < 1 ||  // gauntlet engine can always stay loaded as first engine
+        appData.tourneyType > 1 && nextGame/round != gameNr/round) // in multi-gauntlet change only after round
        UnloadEngine(&first);  // next game belongs to other pairing;
        UnloadEngine(&second); // already unload the engines, so TwoMachinesEvent will load new ones.
     }
+    if(appData.debugMode) fprintf(debugFP, "Reserved, next=%d, nr=%d\n", nextGame, gameNr);
 }
 
 void
-MatchEvent(int mode)
+MatchEvent (int mode)
 {      // [HGM] moved out of InitBackend3, to make it callable when match starts through menu
        int dummy;
        if(matchMode) { // already in match mode: switch it off
@@ -1463,6 +1493,8 @@ MatchEvent(int mode)
        NextMatchGame();
 }
 
+char *comboLine = NULL; // [HGM] recent: WinBoard's first-engine combobox line
+
 void
 InitBackEnd3 P((void))
 {
@@ -1476,6 +1508,7 @@ InitBackEnd3 P((void))
        free(programVersion);
        programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy));
        sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy);
+       FloatToFront(&appData.recentEngineList, comboLine ? comboLine : appData.firstChessProgram);
     }
 
     if (appData.icsActive) {
@@ -1493,7 +1526,7 @@ InitBackEnd3 P((void))
              len = snprintf(buf, MSG_SIZ, _("Could not connect to host %s, port %s"),
                        appData.icsHost, appData.icsPort);
 
-           if( (len > MSG_SIZ) && appData.debugMode )
+           if( (len >= MSG_SIZ) && appData.debugMode )
              fprintf(debugFP, "InitBackEnd3: buffer truncated.\n");
 
            DisplayFatalError(buf, err, 1);
@@ -1547,7 +1580,7 @@ InitBackEnd3 P((void))
       initialMode = Training;
     } else {
       len = snprintf(buf, MSG_SIZ, _("Unknown initialMode %s"), appData.initialMode);
-      if( (len > MSG_SIZ) && appData.debugMode )
+      if( (len >= MSG_SIZ) && appData.debugMode )
        fprintf(debugFP, "InitBackEnd3: buffer truncated.\n");
 
       DisplayFatalError(buf, 0, 2);
@@ -1663,8 +1696,10 @@ InitBackEnd3 P((void))
 }
 
 void
-HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
+HistorySet (char movelist[][2*MOVE_LEN], int first, int last, int current)
 {
+    DisplayBook(current+1);
+
     MoveHistorySet( movelist, first, last, current, pvInfoList );
 
     EvalGraphSet( first, last, current, pvInfoList );
@@ -1679,7 +1714,7 @@ HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )
  * Returns 0 if okay, error code if not.
  */
 int
-establish()
+establish ()
 {
     char buf[MSG_SIZ];
 
@@ -1720,7 +1755,8 @@ establish()
     }
 }
 
-void EscapeExpand(char *p, char *q)
+void
+EscapeExpand (char *p, char *q)
 {      // [HGM] initstring: routine to shape up string arguments
        while(*p++ = *q++) if(p[-1] == '\\')
            switch(*q++) {
@@ -1734,10 +1770,7 @@ void EscapeExpand(char *p, char *q)
 }
 
 void
-show_bytes(fp, buf, count)
-     FILE *fp;
-     char *buf;
-     int count;
+show_bytes (FILE *fp, char *buf, int count)
 {
     while (count--) {
        if (*buf < 040 || *(unsigned char *) buf > 0177) {
@@ -1752,11 +1785,7 @@ show_bytes(fp, buf, count)
 
 /* Returns an errno value */
 int
-OutputMaybeTelnet(pr, message, count, outError)
-     ProcRef pr;
-     char *message;
-     int count;
-     int *outError;
+OutputMaybeTelnet (ProcRef pr, char *message, int count, int *outError)
 {
     char buf[8192], *p, *q, *buflim;
     int left, newcount, outcount;
@@ -1810,12 +1839,7 @@ OutputMaybeTelnet(pr, message, count, outError)
 }
 
 void
-read_from_player(isr, closure, message, count, error)
-     InputSourceRef isr;
-     VOIDSTAR closure;
-     char *message;
-     int count;
-     int error;
+read_from_player (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
 {
     int outError, outCount;
     static int gotEof = 0;
@@ -1837,7 +1861,7 @@ read_from_player(isr, closure, message, count, error)
 }
 
 void
-KeepAlive()
+KeepAlive ()
 {   // [HGM] alive: periodically send dummy (date) command to ICS to prevent time-out
     if(!connectionAlive) DisplayFatalError("No response from ICS", 0, 1);
     connectionAlive = FALSE; // only sticks if no response to 'date' command.
@@ -1846,7 +1870,8 @@ KeepAlive()
 }
 
 /* added routine for printf style output to ics */
-void ics_printf(char *format, ...)
+void
+ics_printf (char *format, ...)
 {
     char buffer[MSG_SIZ];
     va_list args;
@@ -1859,12 +1884,11 @@ void ics_printf(char *format, ...)
 }
 
 void
-SendToICS(s)
-     char *s;
+SendToICS (char *s)
 {
     int count, outCount, outError;
 
-    if (icsPR == NULL) return;
+    if (icsPR == NoProc) return;
 
     count = strlen(s);
     outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
@@ -1877,13 +1901,11 @@ SendToICS(s)
    without a delay causes problems when using timestamp on ICC
    (at least on my machine). */
 void
-SendToICSDelayed(s,msdelay)
-     char *s;
-     long msdelay;
+SendToICSDelayed (char *s, long msdelay)
 {
     int count, outCount, outError;
 
-    if (icsPR == NULL) return;
+    if (icsPR == NoProc) return;
 
     count = strlen(s);
     if (appData.debugMode) {
@@ -1903,8 +1925,7 @@ SendToICSDelayed(s,msdelay)
    Also deletes any suffix starting with '('
    */
 char *
-StripHighlightAndTitle(s)
-     char *s;
+StripHighlightAndTitle (char *s)
 {
     static char retbuf[MSG_SIZ];
     char *p = retbuf;
@@ -1928,8 +1949,7 @@ StripHighlightAndTitle(s)
 
 /* Remove all highlighting escape sequences in s */
 char *
-StripHighlight(s)
-     char *s;
+StripHighlight (char *s)
 {
     static char retbuf[MSG_SIZ];
     char *p = retbuf;
@@ -1949,8 +1969,7 @@ StripHighlight(s)
 
 char *variantNames[] = VARIANT_NAMES;
 char *
-VariantName(v)
-     VariantClass v;
+VariantName (VariantClass v)
 {
     return variantNames[v];
 }
@@ -1959,8 +1978,7 @@ VariantName(v)
 /* Identify a variant from the strings the chess servers use or the
    PGN Variant tag names we use. */
 VariantClass
-StringToVariant(e)
-     char *e;
+StringToVariant (char *e)
 {
     char *p;
     int wnum = -1;
@@ -2144,7 +2162,7 @@ StringToVariant(e)
          break;
        default:
          len = snprintf(buf, MSG_SIZ, _("Unknown wild type %d"), wnum);
-         if( (len > MSG_SIZ) && appData.debugMode )
+         if( (len >= MSG_SIZ) && appData.debugMode )
            fprintf(debugFP, "StringToVariant: buffer truncated.\n");
 
          DisplayError(buf, 0);
@@ -2171,10 +2189,7 @@ char star_match[STAR_MATCH_N][MSG_SIZ];
    copied into star_match.
    */
 int
-looking_at(buf, index, pattern)
-     char *buf;
-     int *index;
-     char *pattern;
+looking_at ( char *buf, int *index, char *pattern)
 {
     char *bufp = &buf[*index], *patternp = pattern;
     int star_count = 0;
@@ -2212,9 +2227,7 @@ looking_at(buf, index, pattern)
 }
 
 void
-SendToPlayer(data, length)
-     char *data;
-     int length;
+SendToPlayer (char *data, int length)
 {
     int error, outCount;
     outCount = OutputToProcess(NoProc, data, length, &error);
@@ -2224,9 +2237,7 @@ SendToPlayer(data, length)
 }
 
 void
-PackHolding(packed, holding)
-     char packed[];
-     char *holding;
+PackHolding (char packed[], char *holding)
 {
     char *p = holding;
     char *q = packed;
@@ -2261,8 +2272,7 @@ PackHolding(packed, holding)
 
 /* Telnet protocol requests from the front end */
 void
-TelnetRequest(ddww, option)
-     unsigned char ddww, option;
+TelnetRequest (unsigned char ddww, unsigned char option)
 {
     unsigned char msg[3];
     int outCount, outError;
@@ -2310,21 +2320,21 @@ TelnetRequest(ddww, option)
 }
 
 void
-DoEcho()
+DoEcho ()
 {
     if (!appData.icsActive) return;
     TelnetRequest(TN_DO, TN_ECHO);
 }
 
 void
-DontEcho()
+DontEcho ()
 {
     if (!appData.icsActive) return;
     TelnetRequest(TN_DONT, TN_ECHO);
 }
 
 void
-CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
+CopyHoldings (Board board, char *holdings, ChessSquare lowestPiece)
 {
     /* put the holdings sent to us by the server on the board holdings area */
     int i, j, holdingsColumn, holdingsStartRow, direction, countsColumn;
@@ -2366,7 +2376,7 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
 
 
 void
-VariantSwitch(Board board, VariantClass newVariant)
+VariantSwitch (Board board, VariantClass newVariant)
 {
    int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;
    static Board oldBoard;
@@ -2477,7 +2487,7 @@ int hMargin = 10, vMargin = 20, h, w;
 extern int squareSize, lineGap;
 
 void
-PlotSeekAd(int i)
+PlotSeekAd (int i)
 {
        int x, y, color = 0, r = ratingList[i]; float tc = tcList[i];
        xList[i] = yList[i] = -100; // outside graph, so cannot be clicked
@@ -2499,7 +2509,7 @@ PlotSeekAd(int i)
 }
 
 void
-AddAd(char *handle, char *rating, int base, int inc,  char rated, char *type, int nr, Boolean plot)
+AddAd (char *handle, char *rating, int base, int inc,  char rated, char *type, int nr, Boolean plot)
 {
        char buf[MSG_SIZ], *ext = "";
        VariantClass v = StringToVariant(type);
@@ -2523,7 +2533,7 @@ AddAd(char *handle, char *rating, int base, int inc,  char rated, char *type, in
 }
 
 void
-EraseSeekDot(int i)
+EraseSeekDot (int i)
 {
     int x = xList[i], y = yList[i], d=squareSize/4, k;
     DrawSeekBackground(x-squareSize/8, y-squareSize/8, x+squareSize/8+1, y+squareSize/8+1);
@@ -2537,7 +2547,7 @@ EraseSeekDot(int i)
 }
 
 void
-RemoveSeekAd(int nr)
+RemoveSeekAd (int nr)
 {
        int i;
        for(i=0; i<nrOfSeekAds; i++) if(seekNrList[i] == nr) {
@@ -2557,7 +2567,7 @@ RemoveSeekAd(int nr)
 }
 
 Boolean
-MatchSoughtLine(char *line)
+MatchSoughtLine (char *line)
 {
     char handle[MSG_SIZ], rating[MSG_SIZ], type[MSG_SIZ];
     int nr, base, inc, u=0; char dummy;
@@ -2575,7 +2585,7 @@ MatchSoughtLine(char *line)
 }
 
 int
-DrawSeekGraph()
+DrawSeekGraph ()
 {
     int i;
     if(!seekGraphUp) return FALSE;
@@ -2609,9 +2619,18 @@ DrawSeekGraph()
     return TRUE;
 }
 
-int SeekGraphClick(ClickType click, int x, int y, int moving)
+int
+SeekGraphClick (ClickType click, int x, int y, int moving)
 {
     static int lastDown = 0, displayed = 0, lastSecond;
+    if(y < 0) return FALSE;
+    if(!(appData.seekGraph && appData.icsActive && loggedOn &&
+       (gameMode == BeginningOfGame || gameMode == IcsIdle))) {
+       if(!seekGraphUp) return FALSE;
+       seekGraphUp = FALSE; // seek graph is up when it shouldn't be: take it down
+       DrawPosition(TRUE, NULL);
+       return TRUE;
+    }
     if(!seekGraphUp) { // initiate cration of seek graph by requesting seek-ad list
        if(click == Release || moving) return FALSE;
        nrOfSeekAds = 0;
@@ -2661,12 +2680,7 @@ int SeekGraphClick(ClickType click, int x, int y, int moving)
 }
 
 void
-read_from_ics(isr, closure, data, count, error)
-     InputSourceRef isr;
-     VOIDSTAR closure;
-     char *data;
-     int count;
-     int error;
+read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int error)
 {
 #define BUF_SIZE (16*1024) /* overflowed at 8K with "inchannel 1" on FICS? */
 #define STARTED_NONE 0
@@ -2964,6 +2978,9 @@ read_from_ics(isr, closure, data, count, error)
                            OutputKibitz(suppressKibitz, parse);
                        } else {
                            char tmp[MSG_SIZ];
+                           if(gameMode == IcsObserving) // restore original ICS messages
+                             snprintf(tmp, MSG_SIZ, "%s kibitzes: %s", star_match[0], parse);
+                           else
                            snprintf(tmp, MSG_SIZ, _("your opponent kibitzes: %s"), parse);
                            SendToPlayer(tmp, strlen(tmp));
                        }
@@ -3097,7 +3114,8 @@ read_from_ics(isr, closure, data, count, error)
            if (appData.autoKibitz && started == STARTED_NONE &&
                 !appData.icsEngineAnalyze &&                     // [HGM] [DM] ICS analyze
                (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {
-               if((looking_at(buf, &i, "* kibitzes: ") || looking_at(buf, &i, "* whispers: ")) &&
+               if((looking_at(buf, &i, "\n* kibitzes: ") || looking_at(buf, &i, "\n* whispers: ") ||
+                   looking_at(buf, &i, "* kibitzes: ") || looking_at(buf, &i, "* whispers: ")) &&
                   (StrStr(star_match[0], gameInfo.white) == star_match[0] ||
                    StrStr(star_match[0], gameInfo.black) == star_match[0]   )) { // kibitz of self or opponent
                        suppressKibitz = TRUE;
@@ -3132,6 +3150,8 @@ read_from_ics(isr, closure, data, count, error)
                }
            } // [HGM] kibitz: end of patch
 
+           if(looking_at(buf, &i, "* rating adjustment: * --> *\n")) continue;
+
            // [HGM] chat: intercept tells by users for which we have an open chat window
            channel = -1;
            if(started == STARTED_NONE && (looking_at(buf, &i, "* tells you:") || looking_at(buf, &i, "* says:") ||
@@ -3557,7 +3577,7 @@ read_from_ics(isr, closure, data, count, error)
            if (looking_at(buf, &i, "% ") ||
                ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
                 && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book
-               if(ics_type == ICS_ICC && soughtPending) { // [HGM] seekgraph: on ICC sought-list has no termination line
+               if(soughtPending && nrOfSeekAds) { // [HGM] seekgraph: on ICC sought-list has no termination line
                    soughtPending = FALSE;
                    seekGraphUp = TRUE;
                    DrawSeekGraph();
@@ -3634,8 +3654,8 @@ read_from_ics(isr, closure, data, count, error)
                        flipView = appData.flipView;
                        DrawPosition(TRUE, boards[currentMove]);
                        DisplayBothClocks();
-                       snprintf(str, MSG_SIZ, "%s vs. %s",
-                               gameInfo.white, gameInfo.black);
+                       snprintf(str, MSG_SIZ, "%s %s %s",
+                               gameInfo.white, _("vs."),  gameInfo.black);
                        DisplayTitle(str);
                        gameMode = IcsIdle;
                    } else {
@@ -3861,6 +3881,7 @@ read_from_ics(isr, closure, data, count, error)
                    strncmp(why, "Continuing ", 11) == 0) {
                    gs_gamenum = gamenum;
                    safeStrCpy(gs_kind, strchr(why, ' ') + 1,sizeof(gs_kind)/sizeof(gs_kind[0]));
+                   if(ics_gamenum == -1) // [HGM] only if we are not already involved in a game (because gin=1 sends us such messages)
                    VariantSwitch(boards[currentMove], StringToVariant(gs_kind)); // [HGM] variantswitch: even before we get first board
 #if ZIPPY
                    if (appData.zippyPlay) {
@@ -3896,7 +3917,7 @@ read_from_ics(isr, closure, data, count, error)
 #if ZIPPY
                if (appData.zippyPlay && first.initDone) {
                    ZippyGameEnd(endtype, why);
-                   if (first.pr == NULL) {
+                   if (first.pr == NoProc) {
                      /* Start the next process early so that we'll
                         be ready for the next challenge */
                      StartChessProgram(&first);
@@ -4048,11 +4069,11 @@ read_from_ics(isr, closure, data, count, error)
                            char wh[16], bh[16];
                            PackHolding(wh, white_holding);
                            PackHolding(bh, black_holding);
-                           snprintf(str, MSG_SIZ,"[%s-%s] %s-%s", wh, bh,
+                           snprintf(str, MSG_SIZ, "[%s-%s] %s-%s", wh, bh,
                                    gameInfo.white, gameInfo.black);
                        } else {
-                         snprintf(str, MSG_SIZ, "%s [%s] vs. %s [%s]",
-                                   gameInfo.white, white_holding,
+                         snprintf(str, MSG_SIZ, "%s [%s] %s %s [%s]",
+                                   gameInfo.white, white_holding, _("vs."),
                                    gameInfo.black, black_holding);
                        }
                        if(!partnerUp) // [HGM] bughouse: when peeking at partner game we already know what he captured...
@@ -4128,8 +4149,7 @@ read_from_ics(isr, closure, data, count, error)
 #define RELATION_STARTING_POSITION  -4   /* FICS only */
 
 void
-ParseBoard12(string)
-     char *string;
+ParseBoard12 (char *string)
 {
     GameMode newGameMode;
     int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;
@@ -4205,6 +4225,7 @@ ParseBoard12(string)
        newGameMode =
          ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
            IcsPlayingWhite : IcsPlayingBlack;
+       soughtPending =FALSE; // [HGM] seekgraph: solve race condition
        break;
       case RELATION_EXAMINING:
        newGameMode = IcsExamining;
@@ -4244,7 +4265,10 @@ ParseBoard12(string)
       if(appData.dualBoard && !twoBoards) { twoBoards = 1; InitDrawingSizes(-2,0); }
       if(twoBoards) { partnerUp = 1; flipView = !flipView; } // [HGM] dual
       if(partnerUp) DrawPosition(FALSE, partnerBoard);
-      if(twoBoards) { partnerUp = 0; flipView = !flipView; } // [HGM] dual
+      if(twoBoards) {
+         DisplayWhiteClock(white_time, to_play == 'W');
+         DisplayBlackClock(black_time, to_play != 'W');
+                     partnerUp = 0; flipView = !flipView; } // [HGM] dual
       snprintf(partnerStatus, MSG_SIZ,"W: %d:%02d B: %d:%02d (%d-%d) %c", white_time/60000, (white_time%60000)/1000,
                 (black_time/60000), (black_time%60000)/1000, white_stren, black_stren, to_play);
       DisplayMessage(partnerStatus, "");
@@ -4436,6 +4460,13 @@ ParseBoard12(string)
            board[k][1] = board[k][BOARD_WIDTH-2] = (ChessSquare) 0;;
       }
     }
+    if(moveNum==0 && gameInfo.variant == VariantSChess) {
+      board[5][BOARD_RGHT+1] = WhiteAngel;
+      board[6][BOARD_RGHT+1] = WhiteMarshall;
+      board[1][0] = BlackMarshall;
+      board[2][0] = BlackAngel;
+      board[1][1] = board[2][1] = board[5][BOARD_RGHT] = board[6][BOARD_RGHT] = 1;
+    }
     CopyBoard(boards[moveNum], board);
     boards[moveNum][HOLDINGS_SET] = 0; // [HGM] indicate holdings not set
     if (moveNum == 0) {
@@ -4471,6 +4502,7 @@ ParseBoard12(string)
             if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;
         initialRights[4] = boards[moveNum][CASTLING][4] = (castle_bl == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
 
+       boards[moveNum][CASTLING][2] = boards[moveNum][CASTLING][5] = NoRights;
        if(gameInfo.variant == VariantKnightmate) { wKing = WhiteUnicorn; bKing = BlackUnicorn; }
         for(k=BOARD_LEFT; k<BOARD_RGHT; k++)
             if(board[0][k] == wKing) initialRights[2] = boards[moveNum][CASTLING][2] = k;
@@ -4496,7 +4528,10 @@ ParseBoard12(string)
         r = boards[moveNum][CASTLING][5] = initialRights[5];
     }
     /* [HGM] e.p. rights. Assume that ICS sends file number here? */
-    boards[moveNum][EP_STATUS] = double_push == -1 ? EP_NONE : double_push + BOARD_LEFT;
+    boards[moveNum][EP_STATUS] = EP_NONE;
+    if(str[0] == 'P') boards[moveNum][EP_STATUS] = EP_PAWN_MOVE;
+    if(strchr(move_str, 'x')) boards[moveNum][EP_STATUS] = EP_CAPTURE;
+    if(double_push !=  -1) boards[moveNum][EP_STATUS] = double_push + BOARD_LEFT;
 
 
     if (ics_getting_history == H_GOT_REQ_HEADER ||
@@ -4660,7 +4695,7 @@ ParseBoard12(string)
            strcat(moveList[moveNum - 1], "\n");
 
             if(gameInfo.holdingsWidth && !appData.disguise && gameInfo.variant != VariantSuper && gameInfo.variant != VariantGreat
-                                 && gameInfo.variant != VariantGrand) // inherit info that ICS does not give from previous board
+               && gameInfo.variant != VariantGrand&& gameInfo.variant != VariantSChess) // inherit info that ICS does not give from previous board
               for(k=0; k<ranks; k++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++) {
                 ChessSquare old, new = boards[moveNum][k][j];
                   if(fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
@@ -4775,12 +4810,12 @@ ParseBoard12(string)
                    basetime, increment, (int) gameInfo.variant);
        } else {
            if(gameInfo.variant == VariantNormal)
-             snprintf(str, MSG_SIZ, "%s (%d) vs. %s (%d) {%d %d}",
-                   gameInfo.white, white_stren, gameInfo.black, black_stren,
+             snprintf(str, MSG_SIZ, "%s (%d) %s %s (%d) {%d %d}",
+                   gameInfo.white, white_stren, _("vs."), gameInfo.black, black_stren,
                    basetime, increment);
            else
-             snprintf(str, MSG_SIZ, "%s (%d) vs. %s (%d) {%d %d %s}",
-                   gameInfo.white, white_stren, gameInfo.black, black_stren,
+             snprintf(str, MSG_SIZ, "%s (%d) %s %s (%d) {%d %d %s}",
+                   gameInfo.white, white_stren, _("vs."), gameInfo.black, black_stren,
                    basetime, increment, VariantName(gameInfo.variant));
        }
        DisplayTitle(str);
@@ -4828,7 +4863,7 @@ ParseBoard12(string)
 }
 
 void
-GetMoveListEvent()
+GetMoveListEvent ()
 {
     char buf[MSG_SIZ];
     if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {
@@ -4839,8 +4874,7 @@ GetMoveListEvent()
 }
 
 void
-AnalysisPeriodicEvent(force)
-     int force;
+AnalysisPeriodicEvent (int force)
 {
     if (((programStats.ok_to_send == 0 || programStats.line_is_book)
         && !force) || !appData.periodicUpdates)
@@ -4856,16 +4890,14 @@ AnalysisPeriodicEvent(force)
     programStats.ok_to_send = 0;
 }
 
-void ics_update_width(new_width)
-       int new_width;
+void
+ics_update_width (int new_width)
 {
        ics_printf("set width %d\n", new_width);
 }
 
 void
-SendMoveToProgram(moveNum, cps)
-     int moveNum;
-     ChessProgramState *cps;
+SendMoveToProgram (int moveNum, ChessProgramState *cps)
 {
     char buf[MSG_SIZ];
 
@@ -4943,12 +4975,15 @@ SendMoveToProgram(moveNum, cps)
 }
 
 void
-SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar)
-     ChessMove moveType;
-     int fromX, fromY, toX, toY;
-     char promoChar;
+SendMoveToICS (ChessMove moveType, int fromX, int fromY, int toX, int toY, char promoChar)
 {
     char user_move[MSG_SIZ];
+    char suffix[4];
+
+    if(gameInfo.variant == VariantSChess && promoChar) {
+       snprintf(suffix, 4, "=%c", toX == BOARD_WIDTH<<1 ? ToUpper(promoChar) : ToLower(promoChar));
+       if(moveType == NormalMove) moveType = WhitePromotion; // kludge to do gating
+    } else suffix[0] = NULLCHAR;
 
     switch (moveType) {
       default:
@@ -4964,7 +4999,7 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar)
       case WhiteHSideCastleFR:
       case BlackHSideCastleFR:
       /* POP Fabien */
-       snprintf(user_move, MSG_SIZ, "o-o\n");
+       snprintf(user_move, MSG_SIZ, "o-o%s\n", suffix);
        break;
       case WhiteQueenSideCastle:
       case BlackQueenSideCastle:
@@ -4974,7 +5009,7 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar)
       case WhiteASideCastleFR:
       case BlackASideCastleFR:
       /* POP Fabien */
-       snprintf(user_move, MSG_SIZ, "o-o-o\n");
+       snprintf(user_move, MSG_SIZ, "o-o-o%s\n",suffix);
        break;
       case WhiteNonPromotion:
       case BlackNonPromotion:
@@ -5017,13 +5052,13 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY, promoChar)
 }
 
 void
-UploadGameEvent()
+UploadGameEvent ()
 {   // [HGM] upload: send entire stored game to ICS as long-algebraic moves.
     int i, last = forwardMostMove; // make sure ICS reply cannot pre-empt us by clearing fmm
     static char *castlingStrings[4] = { "none", "kside", "qside", "both" };
     if(gameMode == IcsObserving || gameMode == IcsPlayingBlack || gameMode == IcsPlayingWhite) {
-       DisplayError("You cannot do this while you are playing or observing", 0);
-       return;
+      DisplayError(_("You cannot do this while you are playing or observing"), 0);
+      return;
     }
     if(gameMode != IcsExamining) { // is this ever not the case?
        char buf[MSG_SIZ], *p, *fen, command[MSG_SIZ], bsetup = 0;
@@ -5084,10 +5119,7 @@ UploadGameEvent()
 }
 
 void
-CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
-     int rf, ff, rt, ft;
-     char promoChar;
-     char move[7];
+CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[7])
 {
     if (rf == DROP_RANK) {
       if(ff == EmptySquare) sprintf(move, "@@@@\n"); else // [HGM] pass
@@ -5105,8 +5137,7 @@ CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
 }
 
 void
-ProcessICSInitScript(f)
-     FILE *f;
+ProcessICSInitScript (FILE *f)
 {
     char buf[MSG_SIZ];
 
@@ -5121,7 +5152,7 @@ ProcessICSInitScript(f)
 static int lastX, lastY, selectFlag, dragging;
 
 void
-Sweep(int step)
+Sweep (int step)
 {
     ChessSquare king = WhiteKing, pawn = WhitePawn, last = promoSweep;
     if(gameInfo.variant == VariantKnightmate) king = WhiteUnicorn;
@@ -5135,14 +5166,15 @@ Sweep(int step)
        else if((int)promoSweep == -1) promoSweep = WhiteKing;
        else if(promoSweep == BlackPawn && step < 0) promoSweep = WhitePawn;
        else if(promoSweep == WhiteKing && step > 0) promoSweep = BlackKing;
-       if(!step) step = 1;
+       if(!step) step = -1;
     } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn ||
            appData.testLegality && (promoSweep == king ||
            gameInfo.variant == VariantShogi && promoSweep != PROMOTED last && last != PROMOTED promoSweep && last != promoSweep));
     ChangeDragPiece(promoSweep);
 }
 
-int PromoScroll(int x, int y)
+int
+PromoScroll (int x, int y)
 {
   int step = 0;
 
@@ -5158,7 +5190,7 @@ int PromoScroll(int x, int y)
 }
 
 void
-NextPiece(int step)
+NextPiece (int step)
 {
     ChessSquare piece = boards[currentMove][toY][toX];
     do {
@@ -5173,7 +5205,7 @@ NextPiece(int step)
 }
 /* [HGM] Shogi move preprocessor: swap digits for letters, vice versa */
 void
-AlphaRank(char *move, int n)
+AlphaRank (char *move, int n)
 {
 //    char *p = move, c; int x, y;
 
@@ -5225,12 +5257,7 @@ char yy_textstr[8000];
 
 /* 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;
+ParseOneMove (char *move, int moveNum, ChessMove *moveType, int *fromX, int *fromY, int *toX, int *toY, char *promoChar)
 {
     *moveType = yylexstr(moveNum, move, yy_textstr, sizeof yy_textstr);
 
@@ -5313,7 +5340,7 @@ Boolean pushed = FALSE;
 char *lastParseAttempt;
 
 void
-ParsePV(char *pv, Boolean storeComments, Boolean atEnd)
+ParsePV (char *pv, Boolean storeComments, Boolean atEnd)
 { // Parse a string of PV moves, and append to current game, behind forwardMostMove
   int fromX, fromY, toX, toY; char promoChar;
   ChessMove moveType;
@@ -5330,9 +5357,6 @@ ParsePV(char *pv, Boolean storeComments, Boolean atEnd)
     if(nr == 0 && !storeComments && *pv == '(') pv++; // first (ponder) move can be in parentheses
     lastParseAttempt = pv;
     valid = ParseOneMove(pv, endPV, &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
-if(appData.debugMode){
-fprintf(debugFP,"parsePV: %d %c%c%c%c yy='%s'\nPV = '%s'\n", valid, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, yy_textstr, pv);
-}
     if(!valid && nr == 0 &&
        ParseOneMove(pv, endPV-1, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)){
         nr++; moveType = Comment; // First move has been played; kludge to make sure we continue
@@ -5379,7 +5403,7 @@ fprintf(debugFP,"parsePV: %d %c%c%c%c yy='%s'\nPV = '%s'\n", valid, fromX+AAA, f
 }
 
 int
-MultiPV(ChessProgramState *cps)
+MultiPV (ChessProgramState *cps)
 {      // check if engine supports MultiPV, and if so, return the number of the option that sets it
        int i;
        for(i=0; i<cps->nrOptions; i++)
@@ -5389,7 +5413,7 @@ MultiPV(ChessProgramState *cps)
 }
 
 Boolean
-LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end)
+LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end)
 {
        int startPV, multi, lineStart, origIndex = index;
        char *p, buf2[MSG_SIZ];
@@ -5412,6 +5436,9 @@ LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end)
                first.option[multi].value = n;
                *start = *end = 0;
                return FALSE;
+       } else if(strstr(buf+lineStart, "exclude:") == buf+lineStart) { // exclude moves clicked
+               ExcludeClick(origIndex - lineStart);
+               return FALSE;
        }
        ParsePV(buf+startPV, FALSE, gameMode != AnalyzeMode);
        *start = startPV; *end = index-1;
@@ -5419,10 +5446,10 @@ LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end)
 }
 
 char *
-PvToSAN(char *pv)
+PvToSAN (char *pv)
 {
        static char buf[10*MSG_SIZ];
-       int i, k=0, savedEnd=endPV;
+       int i, k=0, savedEnd=endPV, saveFMM = forwardMostMove;
        *buf = NULLCHAR;
        if(forwardMostMove < endPV) PushInner(forwardMostMove, endPV);
        ParsePV(pv, FALSE, 2); // this appends PV to game, suppressing any display of it
@@ -5432,13 +5459,13 @@ PvToSAN(char *pv)
            k += strlen(buf+k);
        }
        snprintf(buf+k, 10*MSG_SIZ-k, "%s", lastParseAttempt); // if we ran into stuff that could not be parsed, print it verbatim
-       if(forwardMostMove < savedEnd) PopInner(0);
+       if(forwardMostMove < savedEnd) { PopInner(0); forwardMostMove = saveFMM; } // PopInner would set fmm to endPV!
        endPV = savedEnd;
        return buf;
 }
 
 Boolean
-LoadPV(int x, int y)
+LoadPV (int x, int y)
 { // called on right mouse click to load PV
   int which = gameMode == TwoMachinesPlay && (WhiteOnMove(forwardMostMove) == (second.twoMachinesColor[0] == 'w'));
   lastX = x; lastY = y;
@@ -5447,10 +5474,11 @@ LoadPV(int x, int y)
 }
 
 void
-UnLoadPV()
+UnLoadPV ()
 {
   int oldFMM = forwardMostMove; // N.B.: this was currentMove before PV was loaded!
   if(endPV < 0) return;
+  if(appData.autoCopyPV) CopyFENToClipboard();
   endPV = -1;
   if(gameMode == AnalyzeMode && currentMove > forwardMostMove) {
        Boolean saveAnimate = appData.animate;
@@ -5473,7 +5501,7 @@ UnLoadPV()
 }
 
 void
-MovePV(int x, int y, int h)
+MovePV (int x, int y, int h)
 { // step through PV based on mouse coordinates (called on mouse move)
   int margin = h>>3, step = 0, threshold = (pieceSweep == EmptySquare ? 10 : 15);
 
@@ -5508,7 +5536,8 @@ int squaresLeft[4];
 int piecesLeft[(int)BlackPawn];
 int seed, nrOfShuffles;
 
-void GetPositionNumber()
+void
+GetPositionNumber ()
 {      // sets global variable seed
        int i;
 
@@ -5520,7 +5549,8 @@ void GetPositionNumber()
        }
 }
 
-int put(Board board, int pieceType, int rank, int n, int shade)
+int
+put (Board board, int pieceType, int rank, int n, int shade)
 // put the piece on the (n-1)-th empty squares of the given shade
 {
        int i;
@@ -5538,7 +5568,8 @@ int put(Board board, int pieceType, int rank, int n, int shade)
 }
 
 
-void AddOnePiece(Board board, int pieceType, int rank, int shade)
+void
+AddOnePiece (Board board, int pieceType, int rank, int shade)
 // calculate where the next piece goes, (any empty square), and put it there
 {
        int i;
@@ -5549,7 +5580,8 @@ void AddOnePiece(Board board, int pieceType, int rank, int shade)
         put(board, pieceType, rank, i, shade);
 }
 
-void AddTwoPieces(Board board, int pieceType, int rank)
+void
+AddTwoPieces (Board board, int pieceType, int rank)
 // calculate where the next 2 identical pieces go, (any empty square), and put it there
 {
        int i, n=squaresLeft[ANY], j=n-1, k;
@@ -5564,7 +5596,8 @@ void AddTwoPieces(Board board, int pieceType, int rank)
         put(board, pieceType, rank, i, ANY);
 }
 
-void SetUpShuffle(Board board, int number)
+void
+SetUpShuffle (Board board, int number)
 {
        int i, p, first=1;
 
@@ -5652,7 +5685,8 @@ void SetUpShuffle(Board board, int number)
        if(number >= 0) appData.defaultFrcPosition %= nrOfShuffles; // normalize
 }
 
-int SetCharTable( char *table, const char * map )
+int
+SetCharTable (char *table, const char * map)
 /* [HGM] moved here from winboard.c because of its general usefulness */
 /*       Basically a safe strcpy that uses the last character as King */
 {
@@ -5676,7 +5710,8 @@ int SetCharTable( char *table, const char * map )
     return result;
 }
 
-void Prelude(Board board)
+void
+Prelude (Board board)
 {      // [HGM] superchess: random selection of exo-pieces
        int i, j, k; ChessSquare p;
        static ChessSquare exoPieces[4] = { WhiteAngel, WhiteMarshall, WhiteSilver, WhiteLance };
@@ -5713,8 +5748,7 @@ void Prelude(Board board)
 }
 
 void
-InitPosition(redraw)
-     int redraw;
+InitPosition (int redraw)
 {
     ChessSquare (* pieces)[BOARD_FILES];
     int i, j, pawnRow, overrule,
@@ -6008,9 +6042,7 @@ InitPosition(redraw)
 }
 
 void
-SendBoard(cps, moveNum)
-     ChessProgramState *cps;
-     int moveNum;
+SendBoard (ChessProgramState *cps, int moveNum)
 {
     char message[MSG_SIZ];
 
@@ -6022,21 +6054,25 @@ SendBoard(cps, moveNum)
 
     } else {
       ChessSquare *bp;
-      int i, j;
+      int i, j, left=0, right=BOARD_WIDTH;
       /* Kludge to set black to move, avoiding the troublesome and now
        * deprecated "black" command.
        */
       if (!WhiteOnMove(moveNum)) // [HGM] but better a deprecated command than an illegal move...
         SendToProgram(boards[0][1][BOARD_LEFT] == WhitePawn ? "a2a3\n" : "black\n", cps);
 
+      if(!cps->extendedEdit) left = BOARD_LEFT, right = BOARD_RGHT; // only board proper
+
       SendToProgram("edit\n", cps);
       SendToProgram("#\n", cps);
       for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
-       bp = &boards[moveNum][i][BOARD_LEFT];
-        for (j = BOARD_LEFT; j < BOARD_RGHT; j++, bp++) {
+       bp = &boards[moveNum][i][left];
+        for (j = left; j < right; j++, bp++) {
+         if(j == BOARD_LEFT-1 || j == BOARD_RGHT) continue;
          if ((int) *bp < (int) BlackPawn) {
-           snprintf(message, MSG_SIZ, "%c%c%c\n", PieceToChar(*bp),
-                    AAA + j, ONE + i);
+           if(j == BOARD_RGHT+1)
+                snprintf(message, MSG_SIZ, "%c@%d\n", PieceToChar(*bp), bp[-1]);
+           else snprintf(message, MSG_SIZ, "%c%c%c\n", PieceToChar(*bp), AAA + j, ONE + i);
             if(message[0] == '+' || message[0] == '~') {
              snprintf(message, MSG_SIZ,"%c%c%c+\n",
                         PieceToChar((ChessSquare)(DEMOTED *bp)),
@@ -6053,11 +6089,14 @@ SendBoard(cps, moveNum)
 
       SendToProgram("c\n", cps);
       for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
-       bp = &boards[moveNum][i][BOARD_LEFT];
-        for (j = BOARD_LEFT; j < BOARD_RGHT; j++, bp++) {
+       bp = &boards[moveNum][i][left];
+        for (j = left; j < right; j++, bp++) {
+         if(j == BOARD_LEFT-1 || j == BOARD_RGHT) continue;
          if (((int) *bp != (int) EmptySquare)
              && ((int) *bp >= (int) BlackPawn)) {
-           snprintf(message,MSG_SIZ, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
+           if(j == BOARD_LEFT-2)
+                snprintf(message, MSG_SIZ, "%c@%d\n", ToUpper(PieceToChar(*bp)), bp[1]);
+           else snprintf(message,MSG_SIZ, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
                     AAA + j, ONE + i);
             if(message[0] == '+' || message[0] == '~') {
              snprintf(message, MSG_SIZ,"%c%c%c+\n",
@@ -6078,8 +6117,117 @@ SendBoard(cps, moveNum)
     setboardSpoiledMachineBlack = 0; /* [HGM] assume WB 4.2.7 already solves this after sending setboard */
 }
 
+char exclusionHeader[MSG_SIZ];
+int exCnt, excludePtr;
+typedef struct { int ff, fr, tf, tr, pc, mark; } Exclusion;
+static Exclusion excluTab[200];
+static char excludeMap[(BOARD_RANKS*BOARD_FILES*BOARD_RANKS*BOARD_FILES+7)/8]; // [HGM] exclude: bitmap for excluced moves
+
+static void
+WriteMap (int s)
+{
+    int j;
+    for(j=0; j<(BOARD_RANKS*BOARD_FILES*BOARD_RANKS*BOARD_FILES+7)/8; j++) excludeMap[j] = s;
+    exclusionHeader[19] = s ? '-' : '+'; // update tail state
+}
+
+static void
+ClearMap ()
+{
+    safeStrCpy(exclusionHeader, "exclude: none best +tail                                          \n", MSG_SIZ);
+    excludePtr = 24; exCnt = 0;
+    WriteMap(0);
+}
+
+static void
+UpdateExcludeHeader (int fromY, int fromX, int toY, int toX, char promoChar, char state)
+{   // search given move in table of header moves, to know where it is listed (and add if not there), and update state
+    char buf[2*MOVE_LEN], *p;
+    Exclusion *e = excluTab;
+    int i;
+    for(i=0; i<exCnt; i++)
+       if(e[i].ff == fromX && e[i].fr == fromY &&
+          e[i].tf == toX   && e[i].tr == toY && e[i].pc == promoChar) break;
+    if(i == exCnt) { // was not in exclude list; add it
+       CoordsToAlgebraic(boards[currentMove], PosFlags(currentMove), fromY, fromX, toY, toX, promoChar, buf);
+       if(strlen(exclusionHeader + excludePtr) < strlen(buf)) { // no space to write move
+           if(state != exclusionHeader[19]) exclusionHeader[19] = '*'; // tail is now in mixed state
+           return; // abort
+       }
+       e[i].ff = fromX; e[i].fr = fromY; e[i].tf = toX; e[i].tr = toY; e[i].pc = promoChar;
+       excludePtr++; e[i].mark = excludePtr++;
+       for(p=buf; *p; p++) exclusionHeader[excludePtr++] = *p; // copy move
+       exCnt++;
+    }
+    exclusionHeader[e[i].mark] = state;
+}
+
+static int
+ExcludeOneMove (int fromY, int fromX, int toY, int toX, signed char promoChar, char state)
+{   // include or exclude the given move, as specified by state ('+' or '-'), or toggle
+    char buf[MSG_SIZ];
+    int j, k;
+    ChessMove moveType;
+    if(promoChar == -1) { // kludge to indicate best move
+       if(!ParseOneMove(lastPV[0], currentMove, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) // get current best move from last PV
+           return 1; // if unparsable, abort
+    }
+    // update exclusion map (resolving toggle by consulting existing state)
+    k=(BOARD_FILES*fromY+fromX)*BOARD_RANKS*BOARD_FILES + (BOARD_FILES*toY+toX);
+    j = k%8; k >>= 3;
+    if(state == '*') state = (excludeMap[k] & 1<<j ? '+' : '-'); // toggle
+    if(state == '-' && !promoChar) // only non-promotions get marked as excluded, to allow exclusion of under-promotions
+        excludeMap[k] |=   1<<j;
+    else excludeMap[k] &= ~(1<<j);
+    // update header
+    UpdateExcludeHeader(fromY, fromX, toY, toX, promoChar, state);
+    // inform engine
+    snprintf(buf, MSG_SIZ, "%sclude ", state == '+' ? "in" : "ex");
+    CoordsToComputerAlgebraic(fromY, fromX, toY, toX, promoChar, buf+8);
+    SendToProgram(buf, &first);
+    return (state == '+');
+}
+
+static void
+ExcludeClick (int index)
+{
+    int i, j;
+    Exclusion *e = excluTab;
+    if(index < 25) { // none, best or tail clicked
+       if(index < 13) { // none: include all
+           WriteMap(0); // clear map
+           for(i=0; i<exCnt; i++) exclusionHeader[excluTab[i].mark] = '+'; // and moves
+           SendToProgram("include all\n", &first); // and inform engine
+       } else if(index > 18) { // tail
+           if(exclusionHeader[19] == '-') { // tail was excluded
+               SendToProgram("include all\n", &first);
+               WriteMap(0); // clear map completely
+               // now re-exclude selected moves
+               for(i=0; i<exCnt; i++) if(exclusionHeader[e[i].mark] == '-')
+                   ExcludeOneMove(e[i].fr, e[i].ff, e[i].tr, e[i].tf, e[i].pc, '-');
+           } else { // tail was included or in mixed state
+               SendToProgram("exclude all\n", &first);
+               WriteMap(0xFF); // fill map completely
+               // now re-include selected moves
+               j = 0; // count them
+               for(i=0; i<exCnt; i++) if(exclusionHeader[e[i].mark] == '+')
+                   ExcludeOneMove(e[i].fr, e[i].ff, e[i].tr, e[i].tf, e[i].pc, '+'), j++;
+               if(!j) ExcludeOneMove(0, 0, 0, 0, -1, '+'); // if no moves were selected, keep best
+           }
+       } else { // best
+           ExcludeOneMove(0, 0, 0, 0, -1, '-'); // exclude it
+       }
+    } else {
+       for(i=0; i<exCnt; i++) if(i == exCnt-1 || excluTab[i+1].mark > index) {
+           char *p=exclusionHeader + excluTab[i].mark; // do trust header more than map (promotions!)
+           ExcludeOneMove(e[i].fr, e[i].ff, e[i].tr, e[i].tf, e[i].pc, *p == '+' ? '-' : '+');
+           break;
+       }
+    }
+}
+
 ChessSquare
-DefaultPromoChoice(int white)
+DefaultPromoChoice (int white)
 {
     ChessSquare result;
     if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
@@ -6096,7 +6244,7 @@ DefaultPromoChoice(int white)
 static int autoQueen; // [HGM] oneclick
 
 int
-HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice, int sweepSelect)
+HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, int sweepSelect)
 {
     /* [HGM] rewritten IsPromotion to only flag promotions that offer a choice */
     /* [HGM] add Shogi promotions */
@@ -6198,8 +6346,7 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice, in
 }
 
 int
-InPalace(row, column)
-     int row, column;
+InPalace (int row, int column)
 {   /* [HGM] for Xiangqi */
     if( (row < 3 || row > BOARD_HEIGHT-4) &&
          column < (BOARD_WIDTH + 4)/2 &&
@@ -6208,9 +6355,7 @@ InPalace(row, column)
 }
 
 int
-PieceForSquare (x, y)
-     int x;
-     int y;
+PieceForSquare (int x, int y)
 {
   if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT)
      return -1;
@@ -6219,8 +6364,7 @@ PieceForSquare (x, y)
 }
 
 int
-OKToStartUserMove(x, y)
-     int x, y;
+OKToStartUserMove (int x, int y)
 {
     ChessSquare from_piece;
     int white_piece;
@@ -6320,7 +6464,8 @@ OKToStartUserMove(x, y)
 }
 
 Boolean
-OnlyMove(int *x, int *y, Boolean captures) {
+OnlyMove (int *x, int *y, Boolean captures) 
+{
     DisambiguateClosure cl;
     if (appData.zippyPlay || !appData.testLegality) return FALSE;
     switch(gameMode) {
@@ -6382,14 +6527,15 @@ int lastLoadGameNumber = 0, lastLoadPositionNumber = 0;
 int lastLoadGameUseList = FALSE;
 char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
 ChessMove lastLoadGameStart = EndOfFile;
+int doubleClick;
 
 void
-UserMoveEvent(fromX, fromY, toX, toY, promoChar)
-     int fromX, fromY, toX, toY;
-     int promoChar;
+UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar)
 {
     ChessMove moveType;
     ChessSquare pdown, pup;
+    int ff=fromX, rf=fromY, ft=toX, rt=toY;
+
 
     /* 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
@@ -6556,15 +6702,19 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
        }
     }
 
+    if(doubleClick) { // [HGM] exclude: move entered with double-click on from square is for exclusion, not playing
+        if(ExcludeOneMove(fromY, fromX, toY, toX, promoChar, '*')) // toggle
+            ClearPremoveHighlights(); // was included
+       else ClearHighlights(), SetPremoveHighlights(ff, rf, ft, rt); // exclusion indicated  by premove highlights
+       return;
+    }
+
     FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
 }
 
 /* Common tail of UserMoveEvent and DropMenuEvent */
 int
-FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
-     ChessMove moveType;
-     int fromX, fromY, toX, toY;
-     /*char*/int promoChar;
+FinishMove (ChessMove moveType, int fromX, int fromY, int toX, int toY, int promoChar)
 {
     char *bookHit = 0;
 
@@ -6630,6 +6780,8 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
     else forwardMostMove = currentMove;
   }
 
+  ClearMap();
+
   /* If we need the chess program but it's dead, restart it */
   ResurrectChessProgram();
 
@@ -6655,7 +6807,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
       gameMode = MachinePlaysBlack;
       StartClocks();
       SetGameInfo();
-      snprintf(buf, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
+      snprintf(buf, MSG_SIZ, "%s %s %s", gameInfo.white, _("vs."), gameInfo.black);
       DisplayTitle(buf);
       if (first.sendName) {
        snprintf(buf, MSG_SIZ,"name %s\n", gameInfo.white);
@@ -6749,12 +6901,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
 }
 
 void
-Mark(board, flags, kind, rf, ff, rt, ft, closure)
-     Board board;
-     int flags;
-     ChessMove kind;
-     int rf, ff, rt, ft;
-     VOIDSTAR closure;
+Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure)
 {
     typedef char Markers[BOARD_RANKS][BOARD_FILES];
     Markers *m = (Markers *) closure;
@@ -6766,7 +6913,7 @@ Mark(board, flags, kind, rf, ff, rt, ft, closure)
 }
 
 void
-MarkTargetSquares(int clear)
+MarkTargetSquares (int clear)
 {
   int x, y;
   if(!appData.markers || !appData.highlightDragging || appData.icsActive && gameInfo.variant < VariantShogi ||
@@ -6786,7 +6933,7 @@ MarkTargetSquares(int clear)
 }
 
 int
-Explode(Board board, int fromX, int fromY, int toX, int toY)
+Explode (Board board, int fromX, int fromY, int toX, int toY)
 {
     if(gameInfo.variant == VariantAtomic &&
        (board[toY][toX] != EmptySquare ||                     // capture?
@@ -6801,7 +6948,8 @@ Explode(Board board, int fromX, int fromY, int toX, int toY)
 
 ChessSquare gatingPiece = EmptySquare; // exported to front-end, for dragging
 
-int CanPromote(ChessSquare piece, int y)
+int
+CanPromote (ChessSquare piece, int y)
 {
        if(gameMode == EditPosition) return FALSE; // no promotions when editing position
        // some variants have fixed promotion piece, no promotion at all, or another selection mechanism
@@ -6815,19 +6963,19 @@ int CanPromote(ChessSquare piece, int y)
                piece == WhiteLance && y == BOARD_HEIGHT-2 );
 }
 
-void LeftClick(ClickType clickType, int xPix, int yPix)
+void
+LeftClick (ClickType clickType, int xPix, int yPix)
 {
     int x, y;
     Boolean saveAnimate;
     static int second = 0, promotionChoice = 0, clearFlag = 0;
     char promoChoice = NULLCHAR;
     ChessSquare piece;
+    static TimeMark lastClickTime, prevClickTime;
 
-    if(appData.seekGraph && appData.icsActive && loggedOn &&
-       (gameMode == BeginningOfGame || gameMode == IcsIdle)) {
-       SeekGraphClick(clickType, xPix, yPix, 0);
-       return;
-    }
+    if(SeekGraphClick(clickType, xPix, yPix, 0)) return;
+
+    prevClickTime = lastClickTime; GetTimeMark(&lastClickTime);
 
     if (clickType == Press) ErrorPopDown();
 
@@ -6876,7 +7024,13 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
               || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) )
        return;
 
-    if(clickType == Press && fromX == x && fromY == y && promoDefaultAltered)
+    if(gotPremove && x == premoveFromX && y == premoveFromY && clickType == Release) {
+       // could be static click on premove from-square: abort premove
+       gotPremove = 0;
+       ClearPremoveHighlights();
+    }
+
+    if(clickType == Press && fromX == x && fromY == y && promoDefaultAltered && SubtractTimeMarks(&lastClickTime, &prevClickTime) >= 200)
        fromX = fromY = -1; // second click on piece after altering default promo piece treated as first click
 
     if(!promoDefaultAltered) { // determine default promotion piece, based on the side the user is moving for
@@ -6897,6 +7051,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
        }
        return;
       }
+      doubleClick = FALSE;
       fromX = x; fromY = y; toX = toY = -1;
       if(!appData.oneClick || !OnlyMove(&x, &y, FALSE) ||
         // even if only move, we treat as normal when this would trigger a promotion popup, to allow sweep selection
@@ -6943,6 +7098,10 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
             !(fromP == BlackKing && toP == BlackRook && frc))) {
            /* Clicked again on same color piece -- changed his mind */
            second = (x == fromX && y == fromY);
+           if(second && gameMode == AnalyzeMode && SubtractTimeMarks(&lastClickTime, &prevClickTime) < 200) {
+               second = FALSE; // first double-click rather than scond click
+               doubleClick = first.excludeMoves; // used by UserMoveEvent to recognize exclude moves
+           }
            promoDefaultAltered = FALSE;
            MarkTargetSquares(1);
           if(!second || appData.oneClick && !OnlyMove(&x, &y, TRUE)) {
@@ -6956,7 +7115,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
                  (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) &&
                y == (toP < BlackPawn ? 0 : BOARD_HEIGHT-1))
                  gatingPiece = boards[currentMove][fromY][fromX];
-               else gatingPiece = EmptySquare;
+               else gatingPiece = doubleClick ? fromP : EmptySquare;
                fromX = x;
                fromY = y; dragging = 1;
                MarkTargetSquares(0);
@@ -7078,7 +7237,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
     // off-board moves should not be highlighted
     if(x < 0 || y < 0) ClearHighlights();
 
-    if(gatingPiece != EmptySquare) promoChoice = ToLower(PieceToChar(gatingPiece));
+    if(gatingPiece != EmptySquare && gameInfo.variant == VariantSChess) promoChoice = ToLower(PieceToChar(gatingPiece));
 
     if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, appData.sweepSelect)) {
        SetHighlights(fromX, fromY, toX, toY);
@@ -7113,7 +7272,8 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
     }
 }
 
-int RightClick(ClickType action, int x, int y, int *fromX, int *fromY)
+int
+RightClick (ClickType action, int x, int y, int *fromX, int *fromY)
 {   // front-end-free part taken out of PieceMenuPopup
     int whichMenu; int xSqr, ySqr;
 
@@ -7203,7 +7363,8 @@ int RightClick(ClickType action, int x, int y, int *fromX, int *fromY)
     return whichMenu;
 }
 
-void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats )
+void
+SendProgramStatsToFrontend (ChessProgramState * cps, ChessProgramStats * cpstats)
 {
 //    char * hint = lastHint;
     FrontEndProgramStats stats;
@@ -7230,7 +7391,7 @@ void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cp
 }
 
 void
-ClearEngineOutputPane(int which)
+ClearEngineOutputPane (int which)
 {
     static FrontEndProgramStats dummyStats;
     dummyStats.which = which;
@@ -7241,7 +7402,7 @@ ClearEngineOutputPane(int which)
 #define MAXPLAYERS 500
 
 char *
-TourneyStandings(int display)
+TourneyStandings (int display)
 {
     int i, w, b, color, wScore, bScore, dummy, nr=0, nPlayers=0;
     int score[MAXPLAYERS], ranking[MAXPLAYERS], points[MAXPLAYERS], games[MAXPLAYERS];
@@ -7285,7 +7446,7 @@ TourneyStandings(int display)
 }
 
 void
-Count(Board board, int pCnt[], int *nW, int *nB, int *wStale, int *bStale, int *bishopColor)
+Count (Board board, int pCnt[], int *nW, int *nB, int *wStale, int *bStale, int *bishopColor)
 {      // count all piece types
        int p, f, r;
        *nB = *nW = *wStale = *bStale = *bishopColor = 0;
@@ -7303,7 +7464,7 @@ Count(Board board, int pCnt[], int *nW, int *nB, int *wStale, int *bStale, int *
 }
 
 int
-SufficientDefence(int pCnt[], int side, int nMine, int nHis)
+SufficientDefence (int pCnt[], int side, int nMine, int nHis)
 {
        int myPawns = pCnt[WhitePawn+side]; // my total Pawn count;
        int majorDefense = pCnt[BlackRook-side] + pCnt[BlackCannon-side] + pCnt[BlackKnight-side];
@@ -7332,7 +7493,7 @@ SufficientDefence(int pCnt[], int side, int nMine, int nHis)
 }
 
 int
-MatingPotential(int pCnt[], int side, int nMine, int nHis, int stale, int bisColor)
+MatingPotential (int pCnt[], int side, int nMine, int nHis, int stale, int bisColor)
 {
        VariantClass v = gameInfo.variant;
 
@@ -7382,7 +7543,7 @@ MatingPotential(int pCnt[], int side, int nMine, int nHis, int stale, int bisCol
 }
 
 int
-CompareWithRights(Board b1, Board b2)
+CompareWithRights (Board b1, Board b2)
 {
     int rights = 0;
     if(!CompareBoards(b1, b2)) return FALSE;
@@ -7404,7 +7565,7 @@ CompareWithRights(Board b1, Board b2)
 }
 
 int
-Adjudicate(ChessProgramState *cps)
+Adjudicate (ChessProgramState *cps)
 {      // [HGM] some adjudications useful with buggy engines
        // [HGM] adjudicate: made into separate routine, which now can be called after every move
        //       In any case it determnes if the game is a claimable draw (filling in EP_STATUS).
@@ -7560,14 +7721,6 @@ Adjudicate(ChessProgramState *cps)
                      }
                 } else moveCount = 6;
            }
-       if (appData.debugMode) { int i;
-           fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n",
-                   forwardMostMove, backwardMostMove, boards[backwardMostMove][EP_STATUS],
-                   appData.drawRepeats);
-           for( i=forwardMostMove; i>=backwardMostMove; i-- )
-             fprintf(debugFP, "%d ep=%d\n", i, (signed char)boards[i][EP_STATUS]);
-
-       }
 
        // Repetition draws and 50-move rule can be applied independently of legality testing
 
@@ -7625,8 +7778,10 @@ Adjudicate(ChessProgramState *cps)
                                    hisPerpetual = PerpetualChase(k, forwardMostMove);
                                    ourPerpetual = PerpetualChase(k+1, forwardMostMove);
                                    if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit
+                                       static char resdet[MSG_SIZ];
                                        result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;
-                                       details = "Xboard adjudication: perpetual chasing";
+                                       details = resdet;
+                                       snprintf(resdet, MSG_SIZ, "Xboard adjudication: perpetual chasing of %c%c", ourPerpetual>>8, ourPerpetual&255);
                                    } else
                                    if(hisPerpetual && !ourPerpetual)   // he is chasing us, but did not repeat yet
                                        break; // Abort repetition-checking loop.
@@ -7711,7 +7866,8 @@ Adjudicate(ChessProgramState *cps)
        return 0;
 }
 
-char *SendMoveToBookUser(int moveNr, ChessProgramState *cps, int initial)
+char *
+SendMoveToBookUser (int moveNr, ChessProgramState *cps, int initial)
 {   // [HGM] book: this routine intercepts moves to simulate book replies
     char *bookHit = NULL;
 
@@ -7761,7 +7917,8 @@ char *SendMoveToBookUser(int moveNr, ChessProgramState *cps, int initial)
 
 char *savedMessage;
 ChessProgramState *savedState;
-void DeferredBookMove(void)
+void
+DeferredBookMove (void)
 {
        if(savedState->lastPing != savedState->lastPong)
                    ScheduleDelayedEvent(DeferredBookMove, 10);
@@ -7772,9 +7929,7 @@ void DeferredBookMove(void)
 static int savedWhitePlayer, savedBlackPlayer, pairingReceived;
 
 void
-HandleMachineMove(message, cps)
-     char *message;
-     ChessProgramState *cps;
+HandleMachineMove (char *message, ChessProgramState *cps)
 {
     char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];
     char realname[MSG_SIZ];
@@ -7901,11 +8056,6 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
            return;
        }
 
-    if (appData.debugMode) { int f = forwardMostMove;
-        fprintf(debugFP, "machine move %d, castling = %d %d %d %d %d %d\n", f,
-                boards[f][CASTLING][0],boards[f][CASTLING][1],boards[f][CASTLING][2],
-                boards[f][CASTLING][3],boards[f][CASTLING][4],boards[f][CASTLING][5]);
-    }
         if(cps->alphaRank) AlphaRank(machineMove, 4);
         if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
                               &fromX, &fromY, &toX, &toY, &promoChar)) {
@@ -7931,12 +8081,6 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
            ChessMove moveType;
            moveType = LegalityTest(boards[forwardMostMove], PosFlags(forwardMostMove),
                              fromY, fromX, toY, toX, promoChar);
-           if (appData.debugMode) {
-                int i;
-                for(i=0; i< nrCastlingRights; i++) fprintf(debugFP, "(%d,%d) ",
-                    boards[forwardMostMove][CASTLING][i], castlingRank[i]);
-                fprintf(debugFP, "castling rights\n");
-           }
             if(moveType == IllegalMove) {
              snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to illegal move: %s (%c%c%c%c)%c",
                         machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);
@@ -8115,10 +8259,12 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
       return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands!
     }
 
-    if (!appData.testLegality && !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position
+    if ((!appData.testLegality || gameInfo.variant == VariantFairy) && 
+                                       !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position
       int dummy, s=6; char buf[MSG_SIZ];
-      if(appData.icsActive || forwardMostMove != 0 || cps != &first || startedFromSetupPosition) return;
+      if(appData.icsActive || forwardMostMove != 0 || cps != &first) return;
       if(sscanf(message, "setup (%s", buf) == 1) s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf);
+      if(startedFromSetupPosition) return;
       ParseFEN(boards[0], &dummy, message+s);
       DrawPosition(TRUE, boards[0]);
       startedFromSetupPosition = TRUE;
@@ -8347,6 +8493,8 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
                parseList[currentMove], _(cps->which));
        DisplayMoveError(buf1);
        DrawPosition(FALSE, boards[currentMove]);
+
+       SetUserThinkingEnables();
        return;
     }
     if (strncmp(message, "time", 4) == 0 && StrStr(message, "Illegal")) {
@@ -8358,7 +8506,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
 
     /*
      * If chess program startup fails, exit with an error message.
-     * Attempts to recover here are futile.
+     * Attempts to recover here are futile. [HGM] Well, we try anyway
      */
     if ((StrStr(message, "unknown host") != NULL)
        || (StrStr(message, "No remote directory") != NULL)
@@ -8372,7 +8520,17 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
                _(cps->which), cps->program, cps->host, message);
        RemoveInputSource(cps->isr);
        if(appData.icsActive) DisplayFatalError(buf1, 0, 1); else {
-           if(cps == &first) appData.noChessProgram = TRUE;
+           cps->isr = NULL;
+           DestroyChildProcess(cps->pr, 9 ); // just to be sure
+           cps->pr = NoProc; 
+           if(cps == &first) {
+               appData.noChessProgram = TRUE;
+               gameMode = MachinePlaysBlack; ModeHighlight(); // kludge to unmark Machine Black menu
+               gameMode = BeginningOfGame; ModeHighlight();
+               SetNCPMode();
+           }
+           if(GetDelayedEvent()) CancelDelayedEvent(), ThawUI(); // [HGM] cancel remaining loading effort scheduled after feature timeout
+           DisplayMessage("", ""); // erase waiting message
            DisplayError(buf1, 0);
        }
        return;
@@ -8651,7 +8809,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
                        if(f = fopen(buf, "w")) { // export PV to applicable PV file
                                fprintf(f, "%5.2f/%-2d %s", curscore/100., plylev, pv);
                                fclose(f);
-                       } else DisplayError("failed writing PV", 0);
+                       } else DisplayError(_("failed writing PV"), 0);
                }
 
                tempStats.depth = plylev;
@@ -8868,8 +9026,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
    The display is not updated in any way.
    */
 void
-ParseGameHistory(game)
-     char *game;
+ParseGameHistory (char *game)
 {
     ChessMove moveType;
     int fromX, fromY, toX, toY, boardIndex;
@@ -9052,10 +9209,7 @@ ParseGameHistory(game)
 
 /* Apply a move to the given board  */
 void
-ApplyMove(fromX, fromY, toX, toY, promoChar, board)
-     int fromX, fromY, toX, toY;
-     int promoChar;
-     Board board;
+ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
 {
   ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;
   int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand ? 3 : 1;
@@ -9336,10 +9490,13 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
         board[fromY][fromX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); // S-Chess gating
     } else
     if(promoChar == '+') {
-        /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite orinary Pawn promotion) */
+        /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite ordinary Pawn promotion) */
         board[toY][toX] = (ChessSquare) (PROMOTED piece);
     } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar
-        board[toY][toX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
+        ChessSquare newPiece = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
+       if((newPiece <= WhiteMan || newPiece >= BlackPawn && newPiece <= BlackMan) // unpromoted piece specified
+          && pieceToChar[PROMOTED newPiece] == '~') newPiece = PROMOTED newPiece; // but promoted version available
+        board[toY][toX] = newPiece;
     }
     if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand)
                && promoChar != NULLCHAR && gameInfo.holdingsSize) {
@@ -9358,9 +9515,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
 
 /* Updates forwardMostMove */
 void
-MakeMove(fromX, fromY, toX, toY, promoChar)
-     int fromX, fromY, toX, toY;
-     int promoChar;
+MakeMove (int fromX, int fromY, int toX, int toY, int promoChar)
 {
 //    forwardMostMove++; // [HGM] bare: moved downstream
 
@@ -9420,9 +9575,8 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
         fflush(serverMoves);
     }
 
-    if (forwardMostMove+1 > framePtr) { // [HGM] vari: do not run into saved variations
-      DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),
-                       0, 1);
+    if (forwardMostMove+1 > framePtr) { // [HGM] vari: do not run into saved variations..
+       GameEnds(GameUnfinished, _("Game too long; increase MAX_MOVES and recompile"), GE_XBOARD);
       return;
     }
     UnLoadPV(); // [HGM] pv: if we are looking at a PV, abort this
@@ -9436,6 +9590,7 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
     SwitchClocks(forwardMostMove+1); // [HGM] race: incrementing move nr inside
     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
     timeRemaining[1][forwardMostMove] = blackTimeRemaining;
+    adjustedClock = FALSE;
     gameInfo.result = GameUnfinished;
     if (gameInfo.resultDetails != NULL) {
        free(gameInfo.resultDetails);
@@ -9457,15 +9612,12 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
        strcat(parseList[forwardMostMove - 1], "#");
        break;
     }
-    if (appData.debugMode) {
-        fprintf(debugFP, "move: %s, parse: %s (%c)\n", moveList[forwardMostMove-1], parseList[forwardMostMove-1], moveList[forwardMostMove-1][4]);
-    }
 
 }
 
 /* Updates currentMove if not pausing */
 void
-ShowMove(fromX, fromY, toX, toY)
+ShowMove (int fromX, int fromY, int toX, int toY)
 {
     int instant = (gameMode == PlayFromGameFile) ?
        (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;
@@ -9489,10 +9641,10 @@ ShowMove(fromX, fromY, toX, toY)
     DrawPosition(FALSE, boards[currentMove]);
     DisplayBothClocks();
     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
-    DisplayBook(currentMove);
 }
 
-void SendEgtPath(ChessProgramState *cps)
+void
+SendEgtPath (ChessProgramState *cps)
 {       /* [HGM] EGT: match formats given in feature with those given by user, and send info for each match */
        char buf[MSG_SIZ], name[MSG_SIZ], *p;
 
@@ -9526,15 +9678,15 @@ void SendEgtPath(ChessProgramState *cps)
 }
 
 void
-InitChessProgram(cps, setup)
-     ChessProgramState *cps;
-     int setup; /* [HGM] needed to setup FRC opening position */
+InitChessProgram (ChessProgramState *cps, int setup)
+/* setup needed to setup FRC opening position */
 {
     char buf[MSG_SIZ], b[MSG_SIZ]; int overruled;
     if (appData.noChessProgram) return;
     hintRequested = FALSE;
     bookRequested = FALSE;
 
+    ParseFeatures(appData.features[cps == &second], cps); // [HGM] allow user to overrule features
     /* [HGM] some new WB protocol commands to configure engine are sent now, if engine supports them */
     /*       moved to before sending initstring in 4.3.15, so Polyglot can delay UCI 'isready' to recepton of 'new' */
     if(cps->memSize) { /* [HGM] memory */
@@ -9648,8 +9800,7 @@ InitChessProgram(cps, setup)
 
 
 void
-StartChessProgram(cps)
-     ChessProgramState *cps;
+StartChessProgram (ChessProgramState *cps)
 {
     char buf[MSG_SIZ];
     int err;
@@ -9716,7 +9867,7 @@ TwoMachinesEventIfReady P((void))
 }
 
 char *
-MakeName(char *template)
+MakeName (char *template)
 {
     time_t clock;
     struct tm *tm;
@@ -9745,7 +9896,7 @@ MakeName(char *template)
 }
 
 int
-CountPlayers(char *p)
+CountPlayers (char *p)
 {
     int n = 0;
     while(p = strchr(p, '\n')) p++, n++; // count participants
@@ -9753,7 +9904,7 @@ CountPlayers(char *p)
 }
 
 FILE *
-WriteTourneyFile(char *results, FILE *f)
+WriteTourneyFile (char *results, FILE *f)
 {   // write tournament parameters on tourneyFile; on success return the stream pointer for closing
     if(f == NULL) f = fopen(appData.tourneyFile, "w");
     if(f == NULL) DisplayError(_("Could not write on tourney file"), 0); else {
@@ -9771,6 +9922,7 @@ WriteTourneyFile(char *results, FILE *f)
        fprintf(f, "-loadPositionFile \"%s\"\n", appData.loadPositionFile);
        fprintf(f, "-loadPositionIndex %d\n", appData.loadPositionIndex);
        fprintf(f, "-rewindIndex %d\n", appData.rewindIndex);
+       fprintf(f, "-discourageOwnBooks %s\n", appData.defNoBook ? "true" : "false");
        if(searchTime > 0)
                fprintf(f, "-searchTime \"%d:%02d\"\n", searchTime/60, searchTime%60);
        else {
@@ -9783,10 +9935,10 @@ WriteTourneyFile(char *results, FILE *f)
     return f;
 }
 
-#define MAXENGINES 1000
 char *command[MAXENGINES], *mnemonic[MAXENGINES];
 
-void Substitute(char *participants, int expunge)
+void
+Substitute (char *participants, int expunge)
 {
     int i, changed, changes=0, nPlayers=0;
     char *p, *q, *r, buf[MSG_SIZ];
@@ -9807,7 +9959,7 @@ void Substitute(char *participants, int expunge)
        q = r; while(*q) nPlayers += (*q++ == '\n');
        p = buf; while(*r && (*p = *r++) != '\n') p++;
        *p = NULLCHAR;
-       NamesToList(firstChessProgramNames, command, mnemonic);
+       NamesToList(firstChessProgramNames, command, mnemonic, "all");
        for(i=1; mnemonic[i]; i++) if(!strcmp(buf, mnemonic[i])) break;
        if(mnemonic[i]) { // The substitute is valid
            FILE *f;
@@ -9844,7 +9996,7 @@ void Substitute(char *participants, int expunge)
 }
 
 int
-CreateTourney(char *name)
+CreateTourney (char *name)
 {
        FILE *f;
        if(matchMode && strcmp(name, appData.tourneyFile)) {
@@ -9876,18 +10028,29 @@ CreateTourney(char *name)
        return 1;
 }
 
-void NamesToList(char *names, char **engineList, char **engineMnemonic)
+int
+NamesToList (char *names, char **engineList, char **engineMnemonic, char *group)
 {
     char buf[MSG_SIZ], *p, *q;
-    int i=1;
-    while(*names) {
-       p = names; q = buf;
+    int i=1, header, skip, all = !strcmp(group, "all"), depth = 0;
+    insert = names; // afterwards, this global will point just after last retrieved engine line or group end in the 'names'
+    skip = !all && group[0]; // if group requested, we start in skip mode
+    for(;*names && depth >= 0 && i < MAXENGINES-1; names = p) {
+       p = names; q = buf; header = 0;
        while(*p && *p != '\n') *q++ = *p++;
        *q = 0;
+       if(*p == '\n') p++;
+       if(buf[0] == '#') {
+           if(strstr(buf, "# end") == buf) { if(!--depth) insert = p; continue; } // leave group, and suppress printing label
+           depth++; // we must be entering a new group
+           if(all) continue; // suppress printing group headers when complete list requested
+           header = 1;
+           if(skip && !strcmp(group, buf)) { depth = 0; skip = FALSE; } // start when we reach requested group
+       }
+       if(depth != header && !all || skip) continue; // skip contents of group (but print first-level header)
        if(engineList[i]) free(engineList[i]);
        engineList[i] = strdup(buf);
-       if(*p == '\n') p++;
-       TidyProgramName(engineList[i], "localhost", buf);
+       if(buf[0] != '#') insert = p, TidyProgramName(engineList[i], "localhost", buf); // group headers not tidied
        if(engineMnemonic[i]) free(engineMnemonic[i]);
        if((q = strstr(engineList[i]+2, "variant")) && q[-2]== ' ' && (q[-1]=='/' || q[-1]=='-') && (q[7]==' ' || q[7]=='=')) {
            strcat(buf, " (");
@@ -9895,16 +10058,17 @@ void NamesToList(char *names, char **engineList, char **engineMnemonic)
            strcat(buf, ")");
        }
        engineMnemonic[i] = strdup(buf);
-       names = p; i++;
-      if(i > MAXENGINES - 2) break;
+       i++;
     }
     engineList[i] = engineMnemonic[i] = NULL;
+    return i;
 }
 
 // following implemented as macro to avoid type limitations
 #define SWAP(item, temp) temp = appData.item[0]; appData.item[0] = appData.item[n]; appData.item[n] = temp;
 
-void SwapEngines(int n)
+void
+SwapEngines (int n)
 {   // swap settings for first engine and other engine (so far only some selected options)
     int h;
     char *p;
@@ -9920,26 +10084,52 @@ void SwapEngines(int n)
     SWAP(logo, p)
     SWAP(pgnName, p)
     SWAP(pvSAN, h)
+    SWAP(engOptions, p)
+    SWAP(engInitString, p)
+    SWAP(computerString, p)
+    SWAP(features, p)
+    SWAP(fenOverride, p)
+    SWAP(NPS, h)
+    SWAP(accumulateTC, h)
+    SWAP(host, p)
 }
 
-void
-SetPlayer(int player)
+int
+SetPlayer (int player, char *p)
 {   // [HGM] find the engine line of the partcipant given by number, and parse its options.
     int i;
-    char buf[MSG_SIZ], *engineName, *p = appData.participants;
+    char buf[MSG_SIZ], *engineName;
     for(i=0; i<player; i++) p = strchr(p, '\n') + 1;
     engineName = strdup(p); if(p = strchr(engineName, '\n')) *p = NULLCHAR;
     for(i=1; command[i]; i++) if(!strcmp(mnemonic[i], engineName)) break;
     if(mnemonic[i]) {
        snprintf(buf, MSG_SIZ, "-fcp %s", command[i]);
-       ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL;
+       ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL; appData.pvSAN[0] = FALSE;
+       appData.firstHasOwnBookUCI = !appData.defNoBook; appData.protocolVersion[0] = PROTOVER;
        ParseArgsFromString(buf);
     }
     free(engineName);
+    return i;
+}
+
+char *recentEngines;
+
+void
+RecentEngineEvent (int nr)
+{
+    int n;
+//    SwapEngines(1); // bump first to second
+//    ReplaceEngine(&second, 1); // and load it there
+    NamesToList(firstChessProgramNames, command, mnemonic, "all"); // get mnemonics of installed engines
+    n = SetPlayer(nr, recentEngines); // select new (using original menu order!)
+    if(mnemonic[n]) { // if somehow the engine with the selected nickname is no longer found in the list, we skip
+       ReplaceEngine(&first, 0);
+       FloatToFront(&appData.recentEngineList, command[n]);
+    }
 }
 
 int
-Pairing(int nr, int nPlayers, int *whitePlayer, int *blackPlayer, int *syncInterval)
+Pairing (int nr, int nPlayers, int *whitePlayer, int *blackPlayer, int *syncInterval)
 {   // determine players from game number
     int curCycle, curRound, curPairing, gamesPerCycle, gamesPerRound, roundsPerCycle=1, pairingsPerRound=1;
 
@@ -9974,6 +10164,9 @@ Pairing(int nr, int nPlayers, int *whitePlayer, int *blackPlayer, int *syncInter
            *blackPlayer = curRound + (nPlayers-1)/2 - curPairing;
            if(*blackPlayer >= nPlayers-1+(nPlayers&1)) *blackPlayer -= nPlayers-1+(nPlayers&1);
        }
+    } else if(appData.tourneyType > 1) {
+       *blackPlayer = curPairing; // in multi-gauntlet, assign gauntlet engines to second, so first an be kept loaded during round
+       *whitePlayer = curRound + appData.tourneyType;
     } else if(appData.tourneyType > 0) {
        *whitePlayer = curPairing;
        *blackPlayer = curRound + appData.tourneyType;
@@ -9985,7 +10178,7 @@ Pairing(int nr, int nPlayers, int *whitePlayer, int *blackPlayer, int *syncInter
 }
 
 int
-NextTourneyGame(int nr, int *swapColors)
+NextTourneyGame (int nr, int *swapColors)
 {   // !!!major kludge!!! fiddle appData settings to get everything in order for next tourney game
     char *p, *q;
     int whitePlayer, blackPlayer, firstBusy=1000000000, syncInterval = 0, nPlayers;
@@ -10035,26 +10228,48 @@ NextTourneyGame(int nr, int *swapColors)
        matchGame = 1; roundNr = nr / syncInterval + 1;
     }
 
-    if(first.pr != NoProc) return 1; // engines already loaded
+    if(first.pr != NoProc && second.pr != NoProc || nr<0) return 1; // engines already loaded
 
     // redefine engines, engine dir, etc.
-    NamesToList(firstChessProgramNames, command, mnemonic); // get mnemonics of installed engines
-    SetPlayer(whitePlayer); // find white player amongst it, and parse its engine line
-    SwapEngines(1);
-    SetPlayer(blackPlayer); // find black player amongst it, and parse its engine line
-    SwapEngines(1);         // and make that valid for second engine by swapping
-    InitEngine(&first, 0);  // initialize ChessProgramStates based on new settings.
-    InitEngine(&second, 1);
+    NamesToList(firstChessProgramNames, command, mnemonic, "all"); // get mnemonics of installed engines
+    if(first.pr == NoProc) {
+      SetPlayer(whitePlayer, appData.participants); // find white player amongst it, and parse its engine line
+      InitEngine(&first, 0);  // initialize ChessProgramStates based on new settings.
+    }
+    if(second.pr == NoProc) {
+      SwapEngines(1);
+      SetPlayer(blackPlayer, appData.participants); // find black player amongst it, and parse its engine line
+      SwapEngines(1);         // and make that valid for second engine by swapping
+      InitEngine(&second, 1);
+    }
     CommonEngineInit();     // after this TwoMachinesEvent will create correct engine processes
     UpdateLogos(FALSE);     // leave display to ModeHiglight()
     return 1;
 }
 
 void
-NextMatchGame()
+NextMatchGame ()
 {   // performs game initialization that does not invoke engines, and then tries to start the game
-    int firstWhite, swapColors = 0;
+    int res, firstWhite, swapColors = 0;
     if(!NextTourneyGame(nextGame, &swapColors)) return; // this sets matchGame, -fcp / -scp and other options for next game, if needed
+    if(matchMode && appData.debugMode) { // [HGM] debug split: game is part of a match; we might have to create a debug file just for this game
+       char buf[MSG_SIZ];
+       snprintf(buf, MSG_SIZ, appData.nameOfDebugFile, nextGame+1); // expand name of debug file with %d in it
+       if(strcmp(buf, currentDebugFile)) { // name has changed
+           FILE *f = fopen(buf, "w");
+           if(f) { // if opening the new file failed, just keep using the old one
+               ASSIGN(currentDebugFile, buf);
+               fclose(debugFP);
+               debugFP = f;
+           }
+           if(appData.serverFileName) {
+               if(serverFP) fclose(serverFP);
+               serverFP = fopen(appData.serverFileName, "w");
+               if(serverFP && first.pr != NoProc) fprintf(serverFP, "StartChildProcess (dir=\".\") .\\%s\n", first.tidy);
+               if(serverFP && second.pr != NoProc) fprintf(serverFP, "StartChildProcess (dir=\".\") .\\%s\n", second.tidy);
+           }
+       }
+    }
     firstWhite = appData.firstPlaysBlack ^ (matchGame & 1 | appData.sameColorGames > 1); // non-incremental default
     firstWhite ^= swapColors; // reverses if NextTourneyGame says we are in an odd round
     first.twoMachinesColor =  firstWhite ? "white\n" : "black\n";   // perform actual color assignement
@@ -10062,12 +10277,14 @@ NextMatchGame()
     appData.noChessProgram = (first.pr == NoProc); // kludge to prevent Reset from starting up chess program
     if(appData.loadGameIndex == -2) srandom(appData.seedBase + 68163*(nextGame & ~1)); // deterministic seed to force same opening
     Reset(FALSE, first.pr != NoProc);
-    appData.noChessProgram = FALSE;
-    if(!LoadGameOrPosition(matchGame)) return; // setup game; abort when bad game/pos file
+    res = LoadGameOrPosition(matchGame); // setup game
+    appData.noChessProgram = FALSE; // LoadGameOrPosition might call Reset too!
+    if(!res) return; // abort when bad game/pos file
     TwoMachinesEvent();
 }
 
-void UserAdjudicationEvent( int result )
+void
+UserAdjudicationEvent (int result)
 {
     ChessMove gameResult = GameIsDrawn;
 
@@ -10085,7 +10302,8 @@ void UserAdjudicationEvent( int result )
 
 
 // [HGM] save: calculate checksum of game to make games easily identifiable
-int StringCheckSum(char *s)
+int
+StringCheckSum (char *s)
 {
        int i = 0;
        if(s==NULL) return 0;
@@ -10093,7 +10311,8 @@ int StringCheckSum(char *s)
        return i;
 }
 
-int GameCheckSum()
+int
+GameCheckSum ()
 {
        int i, sum=0;
        for(i=backwardMostMove; i<forwardMostMove; i++) {
@@ -10107,10 +10326,7 @@ int GameCheckSum()
 } // end of save patch
 
 void
-GameEnds(result, resultDetails, whosays)
-     ChessMove result;
-     char *resultDetails;
-     int whosays;
+GameEnds (ChessMove result, char *resultDetails, int whosays)
 {
     GameMode nextGameMode;
     int isIcsGame;
@@ -10464,6 +10680,7 @@ GameEnds(result, resultDetails, whosays)
                     first.matchWins, second.matchWins,
                     appData.matchGames - (first.matchWins + second.matchWins));
            if(!appData.tourneyFile[0]) matchGame++, DisplayTwoMachinesTitle(); // [HGM] update result in window title
+           if(ranking && strcmp(ranking, "busy") && appData.afterTourney && appData.afterTourney[0]) RunCommand(appData.afterTourney);
            popupRequested++; // [HGM] crash: postpone to after resetting endingGame
            if (appData.firstPlaysBlack) { // [HGM] match: back to original for next match
                first.twoMachinesColor = "black\n";
@@ -10501,9 +10718,7 @@ GameEnds(result, resultDetails, whosays)
 /* Assumes program was just initialized (initString sent).
    Leaves program in force mode. */
 void
-FeedMovesToProgram(cps, upto)
-     ChessProgramState *cps;
-     int upto;
+FeedMovesToProgram (ChessProgramState *cps, int upto)
 {
     int i;
 
@@ -10534,7 +10749,7 @@ FeedMovesToProgram(cps, upto)
 
 
 int
-ResurrectChessProgram()
+ResurrectChessProgram ()
 {
      /* The chess program may have exited.
        If so, restart it and feed it all the moves made so far. */
@@ -10573,8 +10788,7 @@ ResurrectChessProgram()
  * Button procedures
  */
 void
-Reset(redraw, init)
-     int redraw, init;
+Reset (int redraw, int init)
 {
     int i;
 
@@ -10631,6 +10845,7 @@ Reset(redraw, init)
     ModeHighlight();
     if(appData.icsActive) gameInfo.variant = VariantNormal;
     currentMove = forwardMostMove = backwardMostMove = 0;
+    MarkTargetSquares(1);
     InitPosition(redraw);
     for (i = 0; i < MAX_MOVES; i++) {
        if (commentList[i] != NULL) {
@@ -10642,7 +10857,7 @@ Reset(redraw, init)
     timeRemaining[0][0] = whiteTimeRemaining;
     timeRemaining[1][0] = blackTimeRemaining;
 
-    if (first.pr == NULL) {
+    if (first.pr == NoProc) {
        StartChessProgram(&first);
     }
     if (init) {
@@ -10652,10 +10867,11 @@ Reset(redraw, init)
     DisplayMessage("", "");
     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
     lastSavedGame = 0; // [HGM] save: make sure next game counts as unsaved
+    ClearMap();        // [HGM] exclude: invalidate map
 }
 
 void
-AutoPlayGameLoop()
+AutoPlayGameLoop ()
 {
     for (;;) {
        if (!AutoPlayOneMove())
@@ -10671,7 +10887,7 @@ AutoPlayGameLoop()
 
 
 int
-AutoPlayOneMove()
+AutoPlayOneMove ()
 {
     int fromX, fromY, toX, toY;
 
@@ -10730,8 +10946,7 @@ AutoPlayOneMove()
 
 
 int
-LoadGameOneMove(readAhead)
-     ChessMove readAhead;
+LoadGameOneMove (ChessMove readAhead)
 {
     int fromX = 0, fromY = 0, toX = 0, toY = 0, done;
     char promoChar = NULLCHAR;
@@ -10966,11 +11181,7 @@ LoadGameOneMove(readAhead)
 
 /* Load the nth game from the given file */
 int
-LoadGameFromFile(filename, n, title, useList)
-     char *filename;
-     int n;
-     char *title;
-     /*Boolean*/ int useList;
+LoadGameFromFile (char *filename, int n, char *title, int useList)
 {
     FILE *f;
     char buf[MSG_SIZ];
@@ -11008,7 +11219,7 @@ LoadGameFromFile(filename, n, title, useList)
 
 
 void
-MakeRegisteredMove()
+MakeRegisteredMove ()
 {
     int fromX, fromY, toX, toY;
     char promoChar;
@@ -11073,11 +11284,7 @@ MakeRegisteredMove()
 
 /* 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;
+CmailLoadGame (FILE *f, int gameNumber, char *title, int useList)
 {
     int retVal;
 
@@ -11118,8 +11325,7 @@ CmailLoadGame(f, gameNumber, title, useList)
 
 /* Support for LoadNextGame, LoadPreviousGame, ReloadSameGame */
 int
-ReloadGame(offset)
-     int offset;
+ReloadGame (int offset)
 {
     int gameNumber = lastLoadGameNumber + offset;
     if (lastLoadGameFP == NULL) {
@@ -11142,7 +11348,7 @@ ReloadGame(offset)
 int keys[EmptySquare+1];
 
 int
-PositionMatches(Board b1, Board b2)
+PositionMatches (Board b1, Board b2)
 {
     int r, f, sum=0;
     switch(appData.searchMode) {
@@ -11167,53 +11373,275 @@ PositionMatches(Board b1, Board b2)
     return TRUE;
 }
 
+#define Q_PROMO  4
+#define Q_EP     3
+#define Q_BCASTL 2
+#define Q_WCASTL 1
+
+int pieceList[256], quickBoard[256];
+ChessSquare pieceType[256] = { EmptySquare };
+Board soughtBoard, reverseBoard, flipBoard, rotateBoard;
+int counts[EmptySquare], minSought[EmptySquare], minReverse[EmptySquare], maxSought[EmptySquare], maxReverse[EmptySquare];
+int soughtTotal, turn;
+Boolean epOK, flipSearch;
+
+typedef struct {
+    unsigned char piece, to;
+} Move;
+
+#define DSIZE (250000)
+
+Move initialSpace[DSIZE+1000]; // gamble on that game will not be more than 500 moves
+Move *moveDatabase = initialSpace;
+unsigned int movePtr, dataSize = DSIZE;
+
+int
+MakePieceList (Board board, int *counts)
+{
+    int r, f, n=Q_PROMO, total=0;
+    for(r=0;r<EmptySquare;r++) counts[r] = 0; // piece-type counts
+    for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
+       int sq = f + (r<<4);
+        if(board[r][f] == EmptySquare) quickBoard[sq] = 0; else {
+           quickBoard[sq] = ++n;
+           pieceList[n] = sq;
+           pieceType[n] = board[r][f];
+           counts[board[r][f]]++;
+           if(board[r][f] == WhiteKing) pieceList[1] = n; else
+           if(board[r][f] == BlackKing) pieceList[2] = n; // remember which are Kings, for castling
+           total++;
+       }
+    }
+    epOK = gameInfo.variant != VariantXiangqi && gameInfo.variant != VariantBerolina;
+    return total;
+}
+
+void
+PackMove (int fromX, int fromY, int toX, int toY, ChessSquare promoPiece)
+{
+    int sq = fromX + (fromY<<4);
+    int piece = quickBoard[sq];
+    quickBoard[sq] = 0;
+    moveDatabase[movePtr].to = pieceList[piece] = sq = toX + (toY<<4);
+    if(piece == pieceList[1] && fromY == toY && (toX > fromX+1 || toX < fromX-1) && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1) {
+       int from = toX>fromX ? BOARD_RGHT-1 : BOARD_LEFT;
+       moveDatabase[movePtr++].piece = Q_WCASTL;
+       quickBoard[sq] = piece;
+       piece = quickBoard[from]; quickBoard[from] = 0;
+       moveDatabase[movePtr].to = pieceList[piece] = sq = toX>fromX ? sq-1 : sq+1;
+    } else
+    if(piece == pieceList[2] && fromY == toY && (toX > fromX+1 || toX < fromX-1) && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1) {
+       int from = (toX>fromX ? BOARD_RGHT-1 : BOARD_LEFT) + (BOARD_HEIGHT-1 <<4);
+       moveDatabase[movePtr++].piece = Q_BCASTL;
+       quickBoard[sq] = piece;
+       piece = quickBoard[from]; quickBoard[from] = 0;
+       moveDatabase[movePtr].to = pieceList[piece] = sq = toX>fromX ? sq-1 : sq+1;
+    } else
+    if(epOK && (pieceType[piece] == WhitePawn || pieceType[piece] == BlackPawn) && fromX != toX && quickBoard[sq] == 0) {
+       quickBoard[(fromY<<4)+toX] = 0;
+       moveDatabase[movePtr].piece = Q_EP;
+       moveDatabase[movePtr++].to = (fromY<<4)+toX;
+       moveDatabase[movePtr].to = sq;
+    } else
+    if(promoPiece != pieceType[piece]) {
+       moveDatabase[movePtr++].piece = Q_PROMO;
+       moveDatabase[movePtr].to = pieceType[piece] = (int) promoPiece;
+    }
+    moveDatabase[movePtr].piece = piece;
+    quickBoard[sq] = piece;
+    movePtr++;
+}
+
+int
+PackGame (Board board)
+{
+    Move *newSpace = NULL;
+    moveDatabase[movePtr].piece = 0; // terminate previous game
+    if(movePtr > dataSize) {
+       if(appData.debugMode) fprintf(debugFP, "move-cache overflow, enlarge to %d MB\n", dataSize/128);
+       dataSize *= 8; // increase size by factor 8 (512KB -> 4MB -> 32MB -> 256MB -> 2GB)
+       if(dataSize) newSpace = (Move*) calloc(dataSize + 1000, sizeof(Move));
+       if(newSpace) {
+           int i;
+           Move *p = moveDatabase, *q = newSpace;
+           for(i=0; i<movePtr; i++) *q++ = *p++;    // copy to newly allocated space
+           if(dataSize > 8*DSIZE) free(moveDatabase); // and free old space (if it was allocated)
+           moveDatabase = newSpace;
+       } else { // calloc failed, we must be out of memory. Too bad...
+           dataSize = 0; // prevent calloc events for all subsequent games
+           return 0;     // and signal this one isn't cached
+       }
+    }
+    movePtr++;
+    MakePieceList(board, counts);
+    return movePtr;
+}
+
+int
+QuickCompare (Board board, int *minCounts, int *maxCounts)
+{   // compare according to search mode
+    int r, f;
+    switch(appData.searchMode)
+    {
+      case 1: // exact position match
+       if(!(turn & board[EP_STATUS-1])) return FALSE; // wrong side to move
+       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
+           if(board[r][f] != pieceType[quickBoard[(r<<4)+f]]) return FALSE;
+       }
+       break;
+      case 2: // can have extra material on empty squares
+       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
+           if(board[r][f] == EmptySquare) continue;
+           if(board[r][f] != pieceType[quickBoard[(r<<4)+f]]) return FALSE;
+       }
+       break;
+      case 3: // material with exact Pawn structure
+       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
+           if(board[r][f] != WhitePawn && board[r][f] != BlackPawn) continue;
+           if(board[r][f] != pieceType[quickBoard[(r<<4)+f]]) return FALSE;
+       } // fall through to material comparison
+      case 4: // exact material
+       for(r=0; r<EmptySquare; r++) if(counts[r] != maxCounts[r]) return FALSE;
+       break;
+      case 6: // material range with given imbalance
+       for(r=0; r<BlackPawn; r++) if(counts[r] - minCounts[r] != counts[r+BlackPawn] - minCounts[r+BlackPawn]) return FALSE;
+       // fall through to range comparison
+      case 5: // material range
+       for(r=0; r<EmptySquare; r++) if(counts[r] < minCounts[r] || counts[r] > maxCounts[r]) return FALSE;
+    }
+    return TRUE;
+}
+
+int
+QuickScan (Board board, Move *move)
+{   // reconstruct game,and compare all positions in it
+    int cnt=0, stretch=0, total = MakePieceList(board, counts);
+    do {
+       int piece = move->piece;
+       int to = move->to, from = pieceList[piece];
+       if(piece <= Q_PROMO) { // special moves encoded by otherwise invalid piece numbers 1-4
+         if(!piece) return -1;
+         if(piece == Q_PROMO) { // promotion, encoded as (Q_PROMO, to) + (piece, promoType)
+           piece = (++move)->piece;
+           from = pieceList[piece];
+           counts[pieceType[piece]]--;
+           pieceType[piece] = (ChessSquare) move->to;
+           counts[move->to]++;
+         } else if(piece == Q_EP) { // e.p. capture, encoded as (Q_EP, ep-sqr) + (piece, to)
+           counts[pieceType[quickBoard[to]]]--;
+           quickBoard[to] = 0; total--;
+           move++;
+           continue;
+         } else if(piece <= Q_BCASTL) { // castling, encoded as (Q_XCASTL, king-to) + (rook, rook-to)
+           piece = pieceList[piece]; // first two elements of pieceList contain King numbers
+           from  = pieceList[piece]; // so this must be King
+           quickBoard[from] = 0;
+           quickBoard[to] = piece;
+           pieceList[piece] = to;
+           move++;
+           continue;
+         }
+       }
+       if(appData.searchMode > 2) counts[pieceType[quickBoard[to]]]--; // account capture
+       if((total -= (quickBoard[to] != 0)) < soughtTotal) return -1; // piece count dropped below what we search for
+       quickBoard[from] = 0;
+       quickBoard[to] = piece;
+       pieceList[piece] = to;
+       cnt++; turn ^= 3;
+       if(QuickCompare(soughtBoard, minSought, maxSought) ||
+          appData.ignoreColors && QuickCompare(reverseBoard, minReverse, maxReverse) ||
+          flipSearch && (QuickCompare(flipBoard, minSought, maxSought) ||
+                               appData.ignoreColors && QuickCompare(rotateBoard, minReverse, maxReverse))
+         ) {
+           static int lastCounts[EmptySquare+1];
+           int i;
+           if(stretch) for(i=0; i<EmptySquare; i++) if(lastCounts[i] != counts[i]) { stretch = 0; break; } // reset if material changes
+           if(stretch++ == 0) for(i=0; i<EmptySquare; i++) lastCounts[i] = counts[i]; // remember actual material
+       } else stretch = 0;
+       if(stretch && (appData.searchMode == 1 || stretch >= appData.stretch)) return cnt + 1 - stretch;
+       move++;
+    } while(1);
+}
+
+void
+InitSearch ()
+{
+    int r, f;
+    flipSearch = FALSE;
+    CopyBoard(soughtBoard, boards[currentMove]);
+    soughtTotal = MakePieceList(soughtBoard, maxSought);
+    soughtBoard[EP_STATUS-1] = (currentMove & 1) + 1;
+    if(currentMove == 0 && gameMode == EditPosition) soughtBoard[EP_STATUS-1] = blackPlaysFirst + 1; // (!)
+    CopyBoard(reverseBoard, boards[currentMove]);
+    for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
+       int piece = boards[currentMove][BOARD_HEIGHT-1-r][f];
+       if(piece < BlackPawn) piece += BlackPawn; else if(piece < EmptySquare) piece -= BlackPawn; // color-flip
+       reverseBoard[r][f] = piece;
+    }
+    reverseBoard[EP_STATUS-1] = soughtBoard[EP_STATUS-1] ^ 3; 
+    for(r=0; r<6; r++) reverseBoard[CASTLING][r] = boards[currentMove][CASTLING][(r+3)%6];
+    if(appData.findMirror && appData.searchMode <= 3 && (!nrCastlingRights
+                || (boards[currentMove][CASTLING][2] == NoRights || 
+                    boards[currentMove][CASTLING][0] == NoRights && boards[currentMove][CASTLING][1] == NoRights )
+                && (boards[currentMove][CASTLING][5] == NoRights || 
+                    boards[currentMove][CASTLING][3] == NoRights && boards[currentMove][CASTLING][4] == NoRights ) )
+      ) {
+       flipSearch = TRUE;
+       CopyBoard(flipBoard, soughtBoard);
+       CopyBoard(rotateBoard, reverseBoard);
+       for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) {
+           flipBoard[r][f]    = soughtBoard[r][BOARD_WIDTH-1-f];
+           rotateBoard[r][f] = reverseBoard[r][BOARD_WIDTH-1-f];
+       }
+    }
+    for(r=0; r<BlackPawn; r++) maxReverse[r] = maxSought[r+BlackPawn], maxReverse[r+BlackPawn] = maxSought[r];
+    if(appData.searchMode >= 5) {
+       for(r=BOARD_HEIGHT/2; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) soughtBoard[r][f] = EmptySquare;
+       MakePieceList(soughtBoard, minSought);
+       for(r=0; r<BlackPawn; r++) minReverse[r] = minSought[r+BlackPawn], minReverse[r+BlackPawn] = minSought[r];
+    }
+    if(gameInfo.variant == VariantCrazyhouse || gameInfo.variant == VariantShogi || gameInfo.variant == VariantBughouse)
+       soughtTotal = 0; // in drop games nr of pieces does not fall monotonously
+}
+
 GameInfo dummyInfo;
 
-int GameContainsPosition(FILE *f, ListGame *lg)
+int
+GameContainsPosition (FILE *f, ListGame *lg)
 {
     int next, btm=0, plyNr=0, scratch=forwardMostMove+2&~1;
     int fromX, fromY, toX, toY;
     char promoChar;
     static int initDone=FALSE;
 
+    // weed out games based on numerical tag comparison
+    if(lg->gameInfo.variant != gameInfo.variant) return -1; // wrong variant
+    if(appData.eloThreshold1 && (lg->gameInfo.whiteRating < appData.eloThreshold1 && lg->gameInfo.blackRating < appData.eloThreshold1)) return -1;
+    if(appData.eloThreshold2 && (lg->gameInfo.whiteRating < appData.eloThreshold2 || lg->gameInfo.blackRating < appData.eloThreshold2)) return -1;
+    if(appData.dateThreshold && (!lg->gameInfo.date || atoi(lg->gameInfo.date) < appData.dateThreshold)) return -1;
     if(!initDone) {
        for(next = WhitePawn; next<EmptySquare; next++) keys[next] = random()>>8 ^ random()<<6 ^random()<<20;
        initDone = TRUE;
     }
-    dummyInfo.variant = VariantNormal;
-    FREE(dummyInfo.fen); dummyInfo.fen = NULL;
-    dummyInfo.whiteRating = 0;
-    dummyInfo.blackRating = 0;
-    FREE(dummyInfo.date); dummyInfo.date = NULL;
+    if(lg->gameInfo.fen) ParseFEN(boards[scratch], &btm, lg->gameInfo.fen);
+    else CopyBoard(boards[scratch], initialPosition); // default start position
+    if(lg->moves) {
+       turn = btm + 1;
+       if((next = QuickScan( boards[scratch], &moveDatabase[lg->moves] )) < 0) return -1; // quick scan rules out it is there
+       if(appData.searchMode >= 4) return next; // for material searches, trust QuickScan.
+    }
+    if(btm) plyNr++;
+    if(PositionMatches(boards[scratch], boards[currentMove])) return plyNr;
     fseek(f, lg->offset, 0);
     yynewfile(f);
-    CopyBoard(boards[scratch], initialPosition); // default start position
     while(1) {
-       yyboardindex = scratch + (plyNr&1);
-      quickFlag = 1;
+       yyboardindex = scratch;
+       quickFlag = plyNr+1;
        next = Myylex();
-      quickFlag = 0;
+       quickFlag = 0;
        switch(next) {
            case PGNTag:
                if(plyNr) return -1; // after we have seen moves, any tags will be start of next game
-#if 0
-               ParsePGNTag(yy_text, &dummyInfo); // this has a bad memory leak...
-               if(dummyInfo.fen) ParseFEN(boards[scratch], &btm, dummyInfo.fen), free(dummyInfo.fen), dummyInfo.fen = NULL;
-#else
-               // do it ourselves avoiding malloc
-               { char *p = yy_text+1, *q;
-                 while(!isdigit(*p) && !isalpha(*p)) p++;
-                 q  = p; while(*p != ' ' && *p != '\t' && *p != '\n') p++;
-                 *p = NULLCHAR;
-                 if(!StrCaseCmp(q, "Date") && (p = strchr(p+1, '"'))) { if(atoi(p+1) < appData.dateThreshold) return -1; } else
-                 if(!StrCaseCmp(q, "Variant")  &&  (p = strchr(p+1, '"'))) dummyInfo.variant = StringToVariant(p+1); else
-                 if(!StrCaseCmp(q, "WhiteElo")  && (p = strchr(p+1, '"'))) dummyInfo.whiteRating = atoi(p+1); else
-                 if(!StrCaseCmp(q, "BlackElo")  && (p = strchr(p+1, '"'))) dummyInfo.blackRating = atoi(p+1); else
-                 if(!StrCaseCmp(q, "WhiteUSCF") && (p = strchr(p+1, '"'))) dummyInfo.whiteRating = atoi(p+1); else
-                 if(!StrCaseCmp(q, "BlackUSCF") && (p = strchr(p+1, '"'))) dummyInfo.blackRating = atoi(p+1); else
-                 if(!StrCaseCmp(q, "FEN")  && (p = strchr(p+1, '"'))) ParseFEN(boards[scratch], &btm, p+1);
-               }
-#endif
            default:
                continue;
 
@@ -11265,31 +11693,24 @@ int GameContainsPosition(FILE *f, ListGame *lg)
                fromY = DROP_RANK;
                toX = currentMoveString[2] - AAA;
                toY = currentMoveString[3] - ONE;
+               promoChar = 0;
                break;
        }
        // Move encountered; peform it. We need to shuttle between two boards, as even/odd index determines side to move
-       if(plyNr == 0) { // but first figure out variant and initial position
-           if(dummyInfo.variant != gameInfo.variant) return -1; // wrong variant
-           if(appData.eloThreshold1 && (dummyInfo.whiteRating < appData.eloThreshold1 && dummyInfo.blackRating < appData.eloThreshold1)) return -1;
-           if(appData.eloThreshold2 && (dummyInfo.whiteRating < appData.eloThreshold2 || dummyInfo.blackRating < appData.eloThreshold2)) return -1;
-           if(appData.dateThreshold && (!dummyInfo.date || atoi(dummyInfo.date) < appData.dateThreshold)) return -1;
-           if(btm) CopyBoard(boards[scratch+1], boards[scratch]), plyNr++;
-           if(PositionMatches(boards[scratch + plyNr], boards[currentMove])) return plyNr;
-       }
-       CopyBoard(boards[scratch + (plyNr+1&1)], boards[scratch + (plyNr&1)]);
        plyNr++;
-       ApplyMove(fromX, fromY, toX, toY, promoChar, boards[scratch + (plyNr&1)]);
-       if(PositionMatches(boards[scratch + (plyNr&1)], boards[currentMove])) return plyNr;
+       ApplyMove(fromX, fromY, toX, toY, promoChar, boards[scratch]);
+       if(PositionMatches(boards[scratch], boards[currentMove])) return plyNr;
+       if(appData.ignoreColors && PositionMatches(boards[scratch], reverseBoard)) return plyNr;
+       if(appData.findMirror) {
+           if(PositionMatches(boards[scratch], flipBoard)) return plyNr;
+           if(appData.ignoreColors && PositionMatches(boards[scratch], rotateBoard)) return plyNr;
+       }
     }
 }
 
 /* Load the nth game from open file f */
 int
-LoadGame(f, gameNumber, title, useList)
-     FILE *f;
-     int gameNumber;
-     char *title;
-     int useList;
+LoadGame (FILE *f, int gameNumber, char *title, int useList)
 {
     ChessMove cm;
     char buf[MSG_SIZ];
@@ -11350,7 +11771,7 @@ LoadGame(f, gameNumber, title, useList)
     yynewfile(f);
 
     if (lg && lg->gameInfo.white && lg->gameInfo.black) {
-      snprintf(buf, sizeof(buf), "%s vs. %s", lg->gameInfo.white,
+      snprintf(buf, sizeof(buf), "%s %s %s", lg->gameInfo.white, _("vs."),
                lg->gameInfo.black);
            DisplayTitle(buf);
     } else if (*title != NULLCHAR) {
@@ -11740,8 +12161,7 @@ LoadGame(f, gameNumber, title, useList)
 
 /* Support for LoadNextPosition, LoadPreviousPosition, ReloadSamePosition */
 int
-ReloadPosition(offset)
-     int offset;
+ReloadPosition (int offset)
 {
     int positionNumber = lastLoadPositionNumber + offset;
     if (lastLoadPositionFP == NULL) {
@@ -11758,10 +12178,7 @@ ReloadPosition(offset)
 
 /* Load the nth position from the given file */
 int
-LoadPositionFromFile(filename, n, title)
-     char *filename;
-     int n;
-     char *title;
+LoadPositionFromFile (char *filename, int n, char *title)
 {
     FILE *f;
     char buf[MSG_SIZ];
@@ -11782,10 +12199,7 @@ LoadPositionFromFile(filename, 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;
+LoadPosition (FILE *f, int positionNumber, char *title)
 {
     char *p, line[MSG_SIZ];
     Board initial_position;
@@ -11804,7 +12218,7 @@ LoadPosition(f, positionNumber, title)
     lastLoadPositionFP = f;
     lastLoadPositionNumber = positionNumber;
     safeStrCpy(lastLoadPositionTitle, title, sizeof(lastLoadPositionTitle)/sizeof(lastLoadPositionTitle[0]));
-    if (first.pr == NoProc) {
+    if (first.pr == NoProc && !appData.noChessProgram) {
       StartChessProgram(&first);
       InitChessProgram(&first, FALSE);
     }
@@ -11876,7 +12290,6 @@ LoadPosition(f, positionNumber, title)
     }
     startedFromSetupPosition = TRUE;
 
-    SendToProgram("force\n", &first);
     CopyBoard(boards[0], initial_position);
     if (blackPlaysFirst) {
        currentMove = forwardMostMove = backwardMostMove = 1;
@@ -11889,7 +12302,10 @@ LoadPosition(f, positionNumber, title)
        DisplayMessage("", _("White to play"));
     }
     initialRulePlies = FENrulePlies; /* [HGM] copy FEN attributes as well */
-    SendBoard(&first, forwardMostMove);
+    if(first.pr != NoProc) { // [HGM] in tourney-mode a position can be loaded before the chess engine is installed
+       SendToProgram("force\n", &first);
+       SendBoard(&first, forwardMostMove);
+    }
     if (appData.debugMode) {
 int i, j;
   for(i=0;i<2;i++){for(j=0;j<6;j++)fprintf(debugFP, " %d", boards[i][CASTLING][j]);fprintf(debugFP,"\n");}
@@ -11915,8 +12331,7 @@ int i, j;
 
 
 void
-CopyPlayerNameIntoFileName(dest, src)
-     char **dest, *src;
+CopyPlayerNameIntoFileName (char **dest, char *src)
 {
     while (*src != NULLCHAR && *src != ',') {
        if (*src == ' ') {
@@ -11928,8 +12343,8 @@ CopyPlayerNameIntoFileName(dest, src)
     }
 }
 
-char *DefaultFileName(ext)
-     char *ext;
+char *
+DefaultFileName (char *ext)
 {
     static char def[MSG_SIZ];
     char *p;
@@ -11949,18 +12364,22 @@ char *DefaultFileName(ext)
 
 /* Save the current game to the given file */
 int
-SaveGameToFile(filename, append)
-     char *filename;
-     int append;
+SaveGameToFile (char *filename, int append)
 {
     FILE *f;
     char buf[MSG_SIZ];
-    int result;
+    int result, i, t,tot=0;
 
     if (strcmp(filename, "-") == 0) {
        return SaveGame(stdout, 0, NULL);
     } else {
-       f = fopen(filename, append ? "a" : "w");
+       for(i=0; i<10; i++) { // upto 10 tries
+            f = fopen(filename, append ? "a" : "w");
+            if(f && i) fprintf(f, "[Delay \"%d retries, %d msec\"]\n",i,tot);
+            if(f || errno != 13) break;
+            DoSleep(t = 5 + random()%11); // wait 5-15 msec
+            tot += t;
+       }
        if (f == NULL) {
            snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);
            DisplayError(buf, errno);
@@ -11970,7 +12389,7 @@ SaveGameToFile(filename, append)
            DisplayMessage(_("Waiting for access to save file"), "");
            flock(fileno(f), LOCK_EX); // [HGM] lock: lock file while we are writing
            DisplayMessage(_("Saving game"), "");
-           if(lseek(fileno(f), 0, SEEK_END) == -1) DisplayError("Bad Seek", errno);     // better safe than sorry...
+           if(lseek(fileno(f), 0, SEEK_END) == -1) DisplayError(_("Bad Seek"), errno);     // better safe than sorry...
            result = SaveGame(f, 0, NULL);
            DisplayMessage(buf, "");
            return result;
@@ -11979,8 +12398,7 @@ SaveGameToFile(filename, append)
 }
 
 char *
-SavePart(str)
-     char *str;
+SavePart (char *str)
 {
     static char buf[MSG_SIZ];
     char *p;
@@ -11997,8 +12415,8 @@ SavePart(str)
 #define PGN_SIDE_WHITE  0
 #define PGN_SIDE_BLACK  1
 
-/* [AS] */
-static int FindFirstMoveOutOfBook( int side )
+static int
+FindFirstMoveOutOfBook (int side)
 {
     int result = -1;
 
@@ -12041,8 +12459,8 @@ static int FindFirstMoveOutOfBook( int side )
     return result;
 }
 
-/* [AS] */
-void GetOutOfBookInfo( char * buf )
+void
+GetOutOfBookInfo (char * buf)
 {
     int oob[2];
     int i;
@@ -12073,8 +12491,7 @@ void GetOutOfBookInfo( char * buf )
 
 /* Save game in PGN style and close the file */
 int
-SaveGamePGN(f)
-     FILE *f;
+SaveGamePGN (FILE *f)
 {
     int i, offset, linelen, newblock;
     time_t tm;
@@ -12089,6 +12506,8 @@ SaveGamePGN(f)
 
     PrintPGNTags(f, &gameInfo);
 
+    if(appData.numberTag && matchMode) fprintf(f, "[Number \"%d\"]\n", nextGame+1); // [HGM] number tag
+
     if (backwardMostMove > 0 || startedFromSetupPosition) {
         char *fen = PositionToFEN(backwardMostMove, NULL);
         fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);
@@ -12240,8 +12659,7 @@ SaveGamePGN(f)
 
 /* Save game in old style and close the file */
 int
-SaveGameOldStyle(f)
-     FILE *f;
+SaveGameOldStyle (FILE *f)
 {
     int i, offset;
     time_t tm;
@@ -12305,10 +12723,7 @@ SaveGameOldStyle(f)
 
 /* Save the current game to open file f and close the file */
 int
-SaveGame(f, dummy, dummy2)
-     FILE *f;
-     int dummy;
-     char *dummy2;
+SaveGame (FILE *f, int dummy, char *dummy2)
 {
     if (gameMode == EditPosition) EditPositionDone(TRUE);
     lastSavedGame = GameCheckSum(); // [HGM] save: remember ID of last saved game to prevent double saving
@@ -12320,8 +12735,7 @@ SaveGame(f, dummy, dummy2)
 
 /* Save the current position to the given file */
 int
-SavePositionToFile(filename)
-     char *filename;
+SavePositionToFile (char *filename)
 {
     FILE *f;
     char buf[MSG_SIZ];
@@ -12349,10 +12763,7 @@ SavePositionToFile(filename)
 
 /* Save the current position to the given open file and close the file */
 int
-SavePosition(f, dummy, dummy2)
-     FILE *f;
-     int dummy;
-     char *dummy2;
+SavePosition (FILE *f, int dummy, char *dummy2)
 {
     time_t tm;
     char *fen;
@@ -12376,8 +12787,7 @@ SavePosition(f, dummy, dummy2)
 }
 
 void
-ReloadCmailMsgEvent(unregister)
-     int unregister;
+ReloadCmailMsgEvent (int unregister)
 {
 #if !WIN32
     static char *inFilename = NULL;
@@ -12443,7 +12853,7 @@ ReloadCmailMsgEvent(unregister)
 }
 
 int
-RegisterMove()
+RegisterMove ()
 {
     FILE *f;
     char string[MSG_SIZ];
@@ -12533,7 +12943,7 @@ RegisterMove()
 }
 
 void
-MailMoveEvent()
+MailMoveEvent ()
 {
 #if !WIN32
     static char *partCommandString = "cmail -xv%s -remail -game %s 2>&1";
@@ -12621,7 +13031,7 @@ MailMoveEvent()
 }
 
 char *
-CmailMsg()
+CmailMsg ()
 {
 #if WIN32
     return NULL;
@@ -12694,7 +13104,7 @@ CmailMsg()
 }
 
 void
-ResetGameEvent()
+ResetGameEvent ()
 {
     if (gameMode == Training)
       SetTrainingModeOff();
@@ -12708,8 +13118,7 @@ ResetGameEvent()
 }
 
 void
-ExitEvent(status)
-     int status;
+ExitEvent (int status)
 {
     exiting++;
     if (exiting > 2) {
@@ -12771,7 +13180,7 @@ ExitEvent(status)
 }
 
 void
-PauseEvent()
+PauseEvent ()
 {
     if (appData.debugMode)
        fprintf(debugFP, "PauseEvent(): pausing %d\n", pausing);
@@ -12836,7 +13245,7 @@ PauseEvent()
 }
 
 void
-EditCommentEvent()
+EditCommentEvent ()
 {
     char title[MSG_SIZ];
 
@@ -12853,7 +13262,7 @@ EditCommentEvent()
 
 
 void
-EditTagsEvent()
+EditTagsEvent ()
 {
     char *tags = PGNTags(&gameInfo);
     bookUp = FALSE;
@@ -12862,7 +13271,7 @@ EditTagsEvent()
 }
 
 void
-AnalyzeModeEvent()
+AnalyzeModeEvent ()
 {
     if (appData.noChessProgram || gameMode == AnalyzeMode)
       return;
@@ -12890,7 +13299,7 @@ AnalyzeModeEvent()
 }
 
 void
-AnalyzeFileEvent()
+AnalyzeFileEvent ()
 {
     if (appData.noChessProgram || gameMode == AnalyzeFile)
       return;
@@ -12917,7 +13326,7 @@ AnalyzeFileEvent()
 }
 
 void
-MachineWhiteEvent()
+MachineWhiteEvent ()
 {
     char buf[MSG_SIZ];
     char *bookHit = NULL;
@@ -12957,7 +13366,7 @@ MachineWhiteEvent()
     pausing = FALSE;
     ModeHighlight();
     SetGameInfo();
-    snprintf(buf, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
+    snprintf(buf, MSG_SIZ, "%s %s %s", gameInfo.white, _("vs."), gameInfo.black);
     DisplayTitle(buf);
     if (first.sendName) {
       snprintf(buf, MSG_SIZ, "name %s\n", gameInfo.black);
@@ -12998,7 +13407,7 @@ MachineWhiteEvent()
 }
 
 void
-MachineBlackEvent()
+MachineBlackEvent ()
 {
   char buf[MSG_SIZ];
   char *bookHit = NULL;
@@ -13034,7 +13443,7 @@ MachineBlackEvent()
     pausing = FALSE;
     ModeHighlight();
     SetGameInfo();
-    snprintf(buf, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
+    snprintf(buf, MSG_SIZ, "%s %s %s", gameInfo.white, _("vs."), gameInfo.black);
     DisplayTitle(buf);
     if (first.sendName) {
       snprintf(buf, MSG_SIZ, "name %s\n", gameInfo.white);
@@ -13074,35 +13483,35 @@ MachineBlackEvent()
 
 
 void
-DisplayTwoMachinesTitle()
+DisplayTwoMachinesTitle ()
 {
     char buf[MSG_SIZ];
     if (appData.matchGames > 0) {
         if(appData.tourneyFile[0]) {
-         snprintf(buf, MSG_SIZ, "%s vs. %s (%d/%d%s)",
-                  gameInfo.white, gameInfo.black,
+         snprintf(buf, MSG_SIZ, "%s %s %s (%d/%d%s)",
+                  gameInfo.white, _("vs."), gameInfo.black,
                   nextGame+1, appData.matchGames+1,
                   appData.tourneyType>0 ? "gt" : appData.tourneyType<0 ? "sw" : "rr");
         } else 
         if (first.twoMachinesColor[0] == 'w') {
-         snprintf(buf, MSG_SIZ, "%s vs. %s (%d-%d-%d)",
-                  gameInfo.white, gameInfo.black,
+         snprintf(buf, MSG_SIZ, "%s %s %s (%d-%d-%d)",
+                  gameInfo.white, _("vs."),  gameInfo.black,
                   first.matchWins, second.matchWins,
                   matchGame - 1 - (first.matchWins + second.matchWins));
        } else {
-         snprintf(buf, MSG_SIZ, "%s vs. %s (%d-%d-%d)",
-                  gameInfo.white, gameInfo.black,
+         snprintf(buf, MSG_SIZ, "%s %s %s (%d-%d-%d)",
+                  gameInfo.white, _("vs."), gameInfo.black,
                   second.matchWins, first.matchWins,
                   matchGame - 1 - (first.matchWins + second.matchWins));
        }
     } else {
-      snprintf(buf, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);
+      snprintf(buf, MSG_SIZ, "%s %s %s", gameInfo.white, _("vs."), gameInfo.black);
     }
     DisplayTitle(buf);
 }
 
 void
-SettingsMenuIfReady()
+SettingsMenuIfReady ()
 {
   if (second.lastPing != second.lastPong) {
     DisplayMessage("", _("Waiting for second chess program"));
@@ -13115,17 +13524,17 @@ SettingsMenuIfReady()
 }
 
 int
-WaitForEngine(ChessProgramState *cps, DelayedEventCallback retry)
+WaitForEngine (ChessProgramState *cps, DelayedEventCallback retry)
 {
     char buf[MSG_SIZ];
-    if (cps->pr == NULL) {
+    if (cps->pr == NoProc) {
        StartChessProgram(cps);
        if (cps->protocolVersion == 1) {
          retry();
        } else {
          /* kludge: allow timeout for initial "feature" command */
          FreezeUI();
-         snprintf(buf, MSG_SIZ, _("Starting %s chess program"), cps->which);
+         snprintf(buf, MSG_SIZ, _("Starting %s chess program"), _(cps->which));
          DisplayMessage("", buf);
          ScheduleDelayedEvent(retry, FEATURE_TIMEOUT);
        }
@@ -13147,6 +13556,11 @@ TwoMachinesEvent P((void))
 
     if (appData.noChessProgram) return;
 
+    if(second.protocolVersion >= 2 && !strstr(second.variants, VariantName(gameInfo.variant))) {
+       DisplayError("second engine does not play this", 0);
+       return;
+    }
+
     switch (gameMode) {
       case TwoMachinesPlay:
        return;
@@ -13200,6 +13614,7 @@ TwoMachinesEvent P((void))
        ScheduleDelayedEvent(TwoMachinesEventIfReady, appData.matchPause - wait);
        return;
     }
+    // we are now committed to starting the game
     stalling = 0;
     DisplayMessage("", "");
     if (startedFromSetupPosition) {
@@ -13272,7 +13687,7 @@ TwoMachinesEvent P((void))
 }
 
 void
-TrainingEvent()
+TrainingEvent ()
 {
     if (gameMode == Training) {
       SetTrainingModeOff();
@@ -13295,7 +13710,7 @@ TrainingEvent()
 }
 
 void
-IcsClientEvent()
+IcsClientEvent ()
 {
     if (!appData.icsActive) return;
     switch (gameMode) {
@@ -13329,9 +13744,8 @@ IcsClientEvent()
     return;
 }
 
-
 void
-EditGameEvent()
+EditGameEvent ()
 {
     int i;
 
@@ -13403,9 +13817,11 @@ EditGameEvent()
            SendToProgram("undo\n", &first);
            i--;
        }
+       if(!adjustedClock) {
        whiteTimeRemaining = timeRemaining[0][currentMove];
        blackTimeRemaining = timeRemaining[1][currentMove];
        DisplayBothClocks();
+       }
        if (whiteFlag || blackFlag) {
            whiteFlag = blackFlag = 0;
        }
@@ -13419,7 +13835,7 @@ EditGameEvent()
 
 
 void
-EditPositionEvent()
+EditPositionEvent ()
 {
     if (gameMode == EditPosition) {
        EditGameEvent();
@@ -13440,10 +13856,11 @@ EditPositionEvent()
     currentMove = forwardMostMove = backwardMostMove = 0;
     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
     DisplayMove(-1);
+    if(!appData.pieceMenu) DisplayMessage(_("Click clock to clear board"), "");
 }
 
 void
-ExitAnalyzeMode()
+ExitAnalyzeMode ()
 {
     /* [DM] icsEngineAnalyze - possible call from other functions */
     if (appData.icsEngineAnalyze) {
@@ -13459,7 +13876,7 @@ ExitAnalyzeMode()
 }
 
 void
-EditPositionDone(Boolean fakeRights)
+EditPositionDone (Boolean fakeRights)
 {
     int king = gameInfo.variant == VariantKnightmate ? WhiteUnicorn : WhiteKing;
 
@@ -13502,8 +13919,7 @@ EditPositionDone(Boolean fakeRights)
 /* Pause for `ms' milliseconds */
 /* !! Ugh, this is a kludge. Fix it sometime. --tpm */
 void
-TimeDelay(ms)
-     long ms;
+TimeDelay (long ms)
 {
     TimeMark m1, m2;
 
@@ -13515,8 +13931,7 @@ TimeDelay(ms)
 
 /* !! Ugh, this is a kludge. Fix it sometime. --tpm */
 void
-SendMultiLineToICS(buf)
-     char *buf;
+SendMultiLineToICS (char *buf)
 {
     char temp[MSG_SIZ+1], *p;
     int len;
@@ -13541,7 +13956,7 @@ SendMultiLineToICS(buf)
 }
 
 void
-SetWhiteToPlayEvent()
+SetWhiteToPlayEvent ()
 {
     if (gameMode == EditPosition) {
        blackPlaysFirst = FALSE;
@@ -13553,7 +13968,7 @@ SetWhiteToPlayEvent()
 }
 
 void
-SetBlackToPlayEvent()
+SetBlackToPlayEvent ()
 {
     if (gameMode == EditPosition) {
        blackPlaysFirst = TRUE;
@@ -13567,9 +13982,7 @@ SetBlackToPlayEvent()
 }
 
 void
-EditPositionMenuEvent(selection, x, y)
-     ChessSquare selection;
-     int x, y;
+EditPositionMenuEvent (ChessSquare selection, int x, int y)
 {
     char buf[MSG_SIZ];
     ChessSquare piece = boards[0][y][x];
@@ -13690,6 +14103,8 @@ EditPositionMenuEvent(selection, x, y)
             } else
            boards[0][y][x] = selection;
            DrawPosition(TRUE, boards[0]);
+           ClearHighlights();
+           fromX = fromY = -1;
        }
        break;
     }
@@ -13697,9 +14112,7 @@ EditPositionMenuEvent(selection, x, y)
 
 
 void
-DropMenuEvent(selection, x, y)
-     ChessSquare selection;
-     int x, y;
+DropMenuEvent (ChessSquare selection, int x, int y)
 {
     ChessMove moveType;
 
@@ -13740,7 +14153,7 @@ DropMenuEvent(selection, x, y)
 }
 
 void
-AcceptEvent()
+AcceptEvent ()
 {
     /* Accept a pending offer of any kind from opponent */
 
@@ -13765,7 +14178,7 @@ AcceptEvent()
 }
 
 void
-DeclineEvent()
+DeclineEvent ()
 {
     /* Decline a pending offer of any kind from opponent */
 
@@ -13790,7 +14203,7 @@ DeclineEvent()
 }
 
 void
-RematchEvent()
+RematchEvent ()
 {
     /* Issue ICS rematch command */
     if (appData.icsActive) {
@@ -13800,7 +14213,7 @@ RematchEvent()
 }
 
 void
-CallFlagEvent()
+CallFlagEvent ()
 {
     /* Call your opponent's flag (claim a win on time) */
     if (appData.icsActive) {
@@ -13837,7 +14250,7 @@ CallFlagEvent()
 }
 
 void
-ClockClick(int which)
+ClockClick (int which)
 {      // [HGM] code moved to back-end from winboard.c
        if(which) { // black clock
          if (gameMode == EditPosition || gameMode == IcsExamining) {
@@ -13867,7 +14280,7 @@ ClockClick(int which)
 }
 
 void
-DrawEvent()
+DrawEvent ()
 {
     /* Offer draw or accept pending draw offer from opponent */
 
@@ -13909,7 +14322,7 @@ DrawEvent()
 }
 
 void
-AdjournEvent()
+AdjournEvent ()
 {
     /* Offer Adjourn or accept pending Adjourn offer from opponent */
 
@@ -13923,7 +14336,7 @@ AdjournEvent()
 
 
 void
-AbortEvent()
+AbortEvent ()
 {
     /* Offer Abort or accept pending Abort offer from opponent */
 
@@ -13936,7 +14349,7 @@ AbortEvent()
 }
 
 void
-ResignEvent()
+ResignEvent ()
 {
     /* Resign.  You can do this even if it's not your turn. */
 
@@ -13970,7 +14383,7 @@ ResignEvent()
 
 
 void
-StopObservingEvent()
+StopObservingEvent ()
 {
     /* Stop observing current games */
     SendToICS(ics_prefix);
@@ -13978,7 +14391,7 @@ StopObservingEvent()
 }
 
 void
-StopExaminingEvent()
+StopExaminingEvent ()
 {
     /* Stop observing current game */
     SendToICS(ics_prefix);
@@ -13986,10 +14399,9 @@ StopExaminingEvent()
 }
 
 void
-ForwardInner(target)
-     int target;
+ForwardInner (int target)
 {
-    int limit;
+    int limit; int oldSeekGraphUp = seekGraphUp;
 
     if (appData.debugMode)
        fprintf(debugFP, "ForwardInner(%d), current %d, forward %d\n",
@@ -13998,6 +14410,9 @@ ForwardInner(target)
     if (gameMode == EditPosition)
       return;
 
+    seekGraphUp = FALSE;
+    MarkTargetSquares(1);
+
     if (gameMode == PlayFromGameFile && !pausing)
       PauseEvent();
 
@@ -14043,17 +14458,17 @@ ForwardInner(target)
     }
     DisplayBothClocks();
     DisplayMove(currentMove - 1);
-    DrawPosition(FALSE, boards[currentMove]);
+    DrawPosition(oldSeekGraphUp, boards[currentMove]);
     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
     if ( !matchMode && gameMode != Training) { // [HGM] PV info: routine tests if empty
        DisplayComment(currentMove - 1, commentList[currentMove]);
     }
-    DisplayBook(currentMove);
+    ClearMap(); // [HGM] exclude: invalidate map
 }
 
 
 void
-ForwardEvent()
+ForwardEvent ()
 {
     if (gameMode == IcsExamining && !pausing) {
         SendToICS(ics_prefix);
@@ -14064,7 +14479,7 @@ ForwardEvent()
 }
 
 void
-ToEndEvent()
+ToEndEvent ()
 {
     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
        /* to optimze, we temporarily turn off analysis mode while we feed
@@ -14094,8 +14509,7 @@ ToEndEvent()
 }
 
 void
-BackwardInner(target)
-     int target;
+BackwardInner (int target)
 {
     int full_redraw = TRUE; /* [AS] Was FALSE, had to change it! */
 
@@ -14104,6 +14518,8 @@ BackwardInner(target)
                target, currentMove, forwardMostMove);
 
     if (gameMode == EditPosition) return;
+    seekGraphUp = FALSE;
+    MarkTargetSquares(1);
     if (currentMove <= backwardMostMove) {
        ClearHighlights();
        DrawPosition(full_redraw, boards[currentMove]);
@@ -14161,11 +14577,11 @@ BackwardInner(target)
     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
     // [HGM] PV info: routine tests if comment empty
     DisplayComment(currentMove - 1, commentList[currentMove]);
-    DisplayBook(currentMove);
+    ClearMap(); // [HGM] exclude: invalidate map
 }
 
 void
-BackwardEvent()
+BackwardEvent ()
 {
     if (gameMode == IcsExamining && !pausing) {
         SendToICS(ics_prefix);
@@ -14176,7 +14592,7 @@ BackwardEvent()
 }
 
 void
-ToStartEvent()
+ToStartEvent ()
 {
     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
        /* to optimize, we temporarily turn off analysis mode while we undo
@@ -14205,7 +14621,7 @@ ToStartEvent()
 }
 
 void
-ToNrEvent(int to)
+ToNrEvent (int to)
 {
   if (gameMode == PlayFromGameFile && !pausing) PauseEvent();
   if (to >= forwardMostMove) to = forwardMostMove;
@@ -14218,7 +14634,7 @@ ToNrEvent(int to)
 }
 
 void
-RevertEvent(Boolean annotate)
+RevertEvent (Boolean annotate)
 {
     if(PopTail(annotate)) { // [HGM] vari: restore old game tail
        return;
@@ -14236,7 +14652,7 @@ RevertEvent(Boolean annotate)
 }
 
 void
-RetractMoveEvent()
+RetractMoveEvent ()
 {
     switch (gameMode) {
       case MachinePlaysWhite:
@@ -14275,7 +14691,7 @@ RetractMoveEvent()
 }
 
 void
-MoveNowEvent()
+MoveNowEvent ()
 {
     ChessProgramState *cps;
 
@@ -14310,7 +14726,7 @@ MoveNowEvent()
 }
 
 void
-TruncateGameEvent()
+TruncateGameEvent ()
 {
     EditGameEvent();
     if (gameMode != EditGame) return;
@@ -14318,7 +14734,7 @@ TruncateGameEvent()
 }
 
 void
-TruncateGame()
+TruncateGame ()
 {
     CleanupTail(); // [HGM] vari: only keep current variation if we explicitly truncate
     if (forwardMostMove > currentMove) {
@@ -14334,7 +14750,7 @@ TruncateGame()
 }
 
 void
-HintEvent()
+HintEvent ()
 {
     if (appData.noChessProgram) return;
     switch (gameMode) {
@@ -14360,7 +14776,7 @@ HintEvent()
 }
 
 void
-BookEvent()
+BookEvent ()
 {
     if (appData.noChessProgram) return;
     switch (gameMode) {
@@ -14391,7 +14807,7 @@ BookEvent()
 }
 
 void
-AboutGameEvent()
+AboutGameEvent ()
 {
     char *tags = PGNTags(&gameInfo);
     TagsPopUp(tags, CmailMsg());
@@ -14401,9 +14817,7 @@ AboutGameEvent()
 /* end button procedures */
 
 void
-PrintPosition(fp, move)
-     FILE *fp;
-     int move;
+PrintPosition (FILE *fp, int move)
 {
     int i, j;
 
@@ -14421,8 +14835,7 @@ PrintPosition(fp, move)
 }
 
 void
-PrintOpponents(fp)
-     FILE *fp;
+PrintOpponents (FILE *fp)
 {
     if (gameInfo.white != NULL) {
        fprintf(fp, "\t%s vs. %s\n", gameInfo.white, gameInfo.black);
@@ -14433,10 +14846,9 @@ PrintOpponents(fp)
 
 /* Find last component of program's own name, using some heuristics */
 void
-TidyProgramName(prog, host, buf)
-     char *prog, *host, buf[MSG_SIZ];
+TidyProgramName (char *prog, char *host, char buf[MSG_SIZ])
 {
-    char *p, *q;
+    char *p, *q, c;
     int local = (strcmp(host, "localhost") == 0);
     while (!local && (p = strchr(prog, ';')) != NULL) {
        p++;
@@ -14453,7 +14865,8 @@ TidyProgramName(prog, host, buf)
     while (p >= prog && *p != '/' && *p != '\\') p--;
     p++;
     if(p == prog && *p == '"') p++;
-    if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4;
+    c = *q; *q = 0;
+    if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) *q = c, q -= 4; else *q = c;
     memcpy(buf, p, q - p);
     buf[q - p] = NULLCHAR;
     if (!local) {
@@ -14463,7 +14876,7 @@ TidyProgramName(prog, host, buf)
 }
 
 char *
-TimeControlTagValue()
+TimeControlTagValue ()
 {
     char buf[MSG_SIZ];
     if (!appData.clockMode) {
@@ -14479,7 +14892,7 @@ TimeControlTagValue()
 }
 
 void
-SetGameInfo()
+SetGameInfo ()
 {
     /* This routine is used only for certain modes */
     VariantClass v = gameInfo.variant;
@@ -14577,9 +14990,7 @@ SetGameInfo()
 }
 
 void
-ReplaceComment(index, text)
-     int index;
-     char *text;
+ReplaceComment (int index, char *text)
 {
     int len;
     char *p;
@@ -14620,8 +15031,7 @@ ReplaceComment(index, text)
 }
 
 void
-CrushCRs(text)
-     char *text;
+CrushCRs (char *text)
 {
   char *p = text;
   char *q = text;
@@ -14635,10 +15045,8 @@ CrushCRs(text)
 }
 
 void
-AppendComment(index, text, addBraces)
-     int index;
-     char *text;
-     Boolean addBraces; // [HGM] braces: tells if we should add {}
+AppendComment (int index, char *text, Boolean addBraces)
+/* addBraces  tells if we should add {} */
 {
     int oldlen, len;
     char *old;
@@ -14650,6 +15058,7 @@ if(appData.debugMode) fprintf(debugFP, "Append: in='%s' %d\n", text, addBraces);
     while (*text == '\n') text++;
     len = strlen(text);
     while (len > 0 && text[len - 1] == '\n') len--;
+    text[len] = NULLCHAR;
 
     if (len == 0) return;
 
@@ -14684,7 +15093,8 @@ if(appData.debugMode) fprintf(debugFP, "Append: in='%s' %d\n", text, addBraces);
     }
 }
 
-static char * FindStr( char * text, char * sub_text )
+static char *
+FindStr (char * text, char * sub_text)
 {
     char * result = strstr( text, sub_text );
 
@@ -14697,7 +15107,8 @@ static char * FindStr( char * text, char * sub_text )
 
 /* [AS] Try to extract PV info from PGN comment */
 /* [HGM] PV time: and then remove it, to prevent it appearing twice */
-char *GetInfoFromComment( int index, char * text )
+char *
+GetInfoFromComment (int index, char * text)
 {
     char * sep = text, *p;
 
@@ -14792,14 +15203,12 @@ char *GetInfoFromComment( int index, char * text )
 }
 
 void
-SendToProgram(message, cps)
-     char *message;
-     ChessProgramState *cps;
+SendToProgram (char *message, ChessProgramState *cps)
 {
     int count, outCount, error;
     char buf[MSG_SIZ];
 
-    if (cps->pr == NULL) return;
+    if (cps->pr == NoProc) return;
     Attention(cps);
 
     if (appData.debugMode) {
@@ -14808,6 +15217,10 @@ SendToProgram(message, cps)
        fprintf(debugFP, "%ld >%-6s: %s",
                SubtractTimeMarks(&now, &programStartTime),
                cps->which, message);
+       if(serverFP)
+           fprintf(serverFP, "%ld >%-6s: %s",
+               SubtractTimeMarks(&now, &programStartTime),
+               cps->which, message), fflush(serverFP);
     }
 
     count = strlen(message);
@@ -14834,12 +15247,7 @@ SendToProgram(message, cps)
 }
 
 void
-ReceiveFromProgram(isr, closure, message, count, error)
-     InputSourceRef isr;
-     VOIDSTAR closure;
-     char *message;
-     int count;
-     int error;
+ReceiveFromProgram (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
 {
     char *end_str;
     char buf[MSG_SIZ];
@@ -14914,6 +15322,11 @@ ReceiveFromProgram(isr, closure, message, count, error)
                        SubtractTimeMarks(&now, &programStartTime), cps->which,
                        quote,
                        message);
+               if(serverFP)
+                   fprintf(serverFP, "%ld <%-6s: %s%s\n",
+                       SubtractTimeMarks(&now, &programStartTime), cps->which,
+                       quote,
+                       message), fflush(serverFP);
        }
     }
 
@@ -14929,10 +15342,7 @@ ReceiveFromProgram(isr, closure, message, count, error)
 
 
 void
-SendTimeControl(cps, mps, tc, inc, sd, st)
-     ChessProgramState *cps;
-     int mps, inc, sd, st;
-     long tc;
+SendTimeControl (ChessProgramState *cps, int mps, long tc, int inc, int sd, int st)
 {
     char buf[MSG_SIZ];
     int seconds;
@@ -14996,7 +15406,8 @@ SendTimeControl(cps, mps, tc, inc, sd, st)
     }
 }
 
-ChessProgramState *WhitePlayer()
+ChessProgramState *
+WhitePlayer ()
 /* [HGM] return pointer to 'first' or 'second', depending on who plays white */
 {
     if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b' ||
@@ -15006,9 +15417,7 @@ ChessProgramState *WhitePlayer()
 }
 
 void
-SendTimeRemaining(cps, machineWhite)
-     ChessProgramState *cps;
-     int /*boolean*/ machineWhite;
+SendTimeRemaining (ChessProgramState *cps, int machineWhite)
 {
     char message[MSG_SIZ];
     long time, otime;
@@ -15026,9 +15435,6 @@ SendTimeRemaining(cps, machineWhite)
     }
     /* [HGM] translate opponent's time by time-odds factor */
     otime = (otime * cps->other->timeOdds) / cps->timeOdds;
-    if (appData.debugMode) {
-        fprintf(debugFP, "time odds: %f %f \n", cps->timeOdds, cps->other->timeOdds);
-    }
 
     if (time <= 0) time = 1;
     if (otime <= 0) otime = 1;
@@ -15041,11 +15447,7 @@ SendTimeRemaining(cps, machineWhite)
 }
 
 int
-BoolFeature(p, name, loc, cps)
-     char **p;
-     char *name;
-     int *loc;
-     ChessProgramState *cps;
+BoolFeature (char **p, char *name, int *loc, ChessProgramState *cps)
 {
   char buf[MSG_SIZ];
   int len = strlen(name);
@@ -15065,11 +15467,7 @@ BoolFeature(p, name, loc, cps)
 }
 
 int
-IntFeature(p, name, loc, cps)
-     char **p;
-     char *name;
-     int *loc;
-     ChessProgramState *cps;
+IntFeature (char **p, char *name, int *loc, ChessProgramState *cps)
 {
   char buf[MSG_SIZ];
   int len = strlen(name);
@@ -15085,11 +15483,7 @@ IntFeature(p, name, loc, cps)
 }
 
 int
-StringFeature(p, name, loc, cps)
-     char **p;
-     char *name;
-     char loc[];
-     ChessProgramState *cps;
+StringFeature (char **p, char *name, char loc[], ChessProgramState *cps)
 {
   char buf[MSG_SIZ];
   int len = strlen(name);
@@ -15107,7 +15501,7 @@ StringFeature(p, name, loc, cps)
 }
 
 int
-ParseOption(Option *opt, ChessProgramState *cps)
+ParseOption (Option *opt, ChessProgramState *cps)
 // [HGM] options: process the string that defines an engine option, and determine
 // name, type, default value, and allowed value range
 {
@@ -15148,8 +15542,8 @@ ParseOption(Option *opt, ChessProgramState *cps)
            if(sscanf(p, " -check %d", &def) < 1) return FALSE;
            opt->value = (def != 0);
            opt->type = CheckBox;
-       } else if(p = strstr(opt->name, " -combo ")) {
-           opt->textValue = (char*) (&cps->comboList[cps->comboCnt]); // cheat with pointer type
+       } else if(p = strstr(opt->name, " -combo ")) {
+           opt->textValue = (char*) (opt->choice = &cps->comboList[cps->comboCnt]); // cheat with pointer type
            cps->comboList[cps->comboCnt++] = q = p+8; // holds possible choices
            if(*q == '*') cps->comboList[cps->comboCnt-1]++;
            opt->value = n = 0;
@@ -15198,9 +15592,7 @@ ParseOption(Option *opt, ChessProgramState *cps)
 }
 
 void
-FeatureDone(cps, val)
-     ChessProgramState* cps;
-     int val;
+FeatureDone (ChessProgramState *cps, int val)
 {
   DelayedEventCallback cb = GetDelayedEvent();
   if ((cb == InitBackEnd3 && cps == &first) ||
@@ -15215,9 +15607,7 @@ FeatureDone(cps, val)
 
 /* Parse feature command from engine */
 void
-ParseFeatures(args, cps)
-     char* args;
-     ChessProgramState *cps;
+ParseFeatures (char *args, ChessProgramState *cps)
 {
   char *p = args;
   char *q;
@@ -15229,6 +15619,7 @@ ParseFeatures(args, cps)
     if (*p == NULLCHAR) return;
 
     if (BoolFeature(&p, "setboard", &cps->useSetboard, cps)) continue;
+    if (BoolFeature(&p, "xedit", &cps->extendedEdit, 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;
@@ -15239,7 +15630,7 @@ ParseFeatures(args, cps)
       continue;
     }
     if (BoolFeature(&p, "analyze", &cps->analysisSupport, cps)) continue;
-    if (StringFeature(&p, "myname", &cps->tidy, cps)) {
+    if (StringFeature(&p, "myname", cps->tidy, cps)) {
       if (gameMode == TwoMachinesPlay) {
        DisplayTwoMachinesTitle();
       } else {
@@ -15247,12 +15638,13 @@ ParseFeatures(args, cps)
       }
       continue;
     }
-    if (StringFeature(&p, "variants", &cps->variants, cps)) 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, "exclude", &cps->excludeMoves, 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 */
@@ -15271,8 +15663,11 @@ ParseFeatures(args, cps)
     if (IntFeature(&p, "level", &cps->maxNrOfSessions, cps)) continue;
     if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue;
     if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;
-    if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;
-    if (StringFeature(&p, "option", &(cps->option[cps->nrOptions].name), cps)) {
+    if (StringFeature(&p, "egt", cps->egtFormats, cps)) continue;
+    if (StringFeature(&p, "option", buf, cps)) {
+       FREE(cps->option[cps->nrOptions].name);
+       cps->option[cps->nrOptions].name = malloc(MSG_SIZ);
+       safeStrCpy(cps->option[cps->nrOptions].name, buf, MSG_SIZ);
        if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature
          snprintf(buf, MSG_SIZ, "rejected option %s\n", cps->option[--cps->nrOptions].name);
            SendToProgram(buf, cps);
@@ -15308,8 +15703,7 @@ ParseFeatures(args, cps)
 }
 
 void
-PeriodicUpdatesEvent(newState)
-     int newState;
+PeriodicUpdatesEvent (int newState)
 {
     if (newState == appData.periodicUpdates)
       return;
@@ -15327,8 +15721,7 @@ PeriodicUpdatesEvent(newState)
 }
 
 void
-PonderNextMoveEvent(newState)
-     int newState;
+PonderNextMoveEvent (int newState)
 {
     if (newState == appData.ponderNextMove) return;
     if (gameMode == EditPosition) EditPositionDone(TRUE);
@@ -15348,9 +15741,7 @@ PonderNextMoveEvent(newState)
 }
 
 void
-NewSettingEvent(option, feature, command, value)
-     char *command;
-     int option, value, *feature;
+NewSettingEvent (int option, int *feature, char *command, int value)
 {
     char buf[MSG_SIZ];
 
@@ -15363,7 +15754,7 @@ NewSettingEvent(option, feature, command, value)
 }
 
 void
-ShowThinkingEvent()
+ShowThinkingEvent ()
 // [HGM] thinking: this routine is now also called from "Options -> Engine..." popup
 {
     static int oldState = 2; // kludge alert! Neither true nor fals, so first time oldState is always updated
@@ -15390,8 +15781,7 @@ ShowThinkingEvent()
 }
 
 void
-AskQuestionEvent(title, question, replyPrefix, which)
-     char *title; char *question; char *replyPrefix; char *which;
+AskQuestionEvent (char *title, char *question, char *replyPrefix, char *which)
 {
   ProcRef pr = (which[0] == '1') ? first.pr : second.pr;
   if (pr == NoProc) return;
@@ -15399,7 +15789,7 @@ AskQuestionEvent(title, question, replyPrefix, which)
 }
 
 void
-TypeInEvent(char firstChar)
+TypeInEvent (char firstChar)
 {
     if ((gameMode == BeginningOfGame && !appData.icsActive) || 
         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
@@ -15413,7 +15803,7 @@ TypeInEvent(char firstChar)
 }
 
 void
-TypeInDoneEvent(char *move)
+TypeInDoneEvent (char *move)
 {
        Board board;
        int n, fromX, fromY, toX, toY;
@@ -15430,6 +15820,14 @@ TypeInDoneEvent(char *move)
          ToNrEvent(2*n-1);
          return;
        }
+       // undocumented kludge: allow command-line option to be typed in!
+       // (potentially fatal, and does not implement the effect of the option.)
+       // should only be used for options that are values on which future decisions will be made,
+       // and definitely not on options that would be used during initialization.
+       if(strstr(move, "!!! -") == move) {
+           ParseArgsFromString(move+4);
+           return;
+        }
 
       if (gameMode != EditGame && currentMove != forwardMostMove && 
        gameMode != Training) {
@@ -15448,8 +15846,7 @@ TypeInDoneEvent(char *move)
 }
 
 void
-DisplayMove(moveNumber)
-     int moveNumber;
+DisplayMove (int moveNumber)
 {
     char message[MSG_SIZ];
     char res[MSG_SIZ];
@@ -15506,9 +15903,7 @@ DisplayMove(moveNumber)
 }
 
 void
-DisplayComment(moveNumber, text)
-     int moveNumber;
-     char *text;
+DisplayComment (int moveNumber, char *text)
 {
     char title[MSG_SIZ];
 
@@ -15530,8 +15925,7 @@ DisplayComment(moveNumber, text)
  * ioctl, which does not work properly on some flavors of Unix.
  */
 void
-Attention(cps)
-     ChessProgramState *cps;
+Attention (ChessProgramState *cps)
 {
 #if ATTENTION
     if (!cps->useSigint) return;
@@ -15558,7 +15952,7 @@ Attention(cps)
 }
 
 int
-CheckFlags()
+CheckFlags ()
 {
     if (whiteTimeRemaining <= 0) {
        if (!whiteFlag) {
@@ -15608,7 +16002,7 @@ CheckFlags()
 }
 
 void
-CheckTimeControl()
+CheckTimeControl ()
 {
     if (!appData.clockMode || appData.icsActive || searchTime || // [HGM] st: no inc in st mode
        gameMode == PlayFromGameFile || forwardMostMove == 0) return;
@@ -15633,7 +16027,7 @@ CheckTimeControl()
 }
 
 void
-DisplayBothClocks()
+DisplayBothClocks ()
 {
     int wom = gameMode == EditPosition ?
       !blackPlaysFirst : WhiteOnMove(currentMove);
@@ -15655,8 +16049,7 @@ DisplayBothClocks()
 
 /* Get the current time as a TimeMark */
 void
-GetTimeMark(tm)
-     TimeMark *tm;
+GetTimeMark (TimeMark *tm)
 {
 #if HAVE_GETTIMEOFDAY
 
@@ -15688,8 +16081,7 @@ GetTimeMark(tm)
    time marks.  We assume the difference will fit in a long!
 */
 long
-SubtractTimeMarks(tm2, tm1)
-     TimeMark *tm2, *tm1;
+SubtractTimeMarks (TimeMark *tm2, TimeMark *tm1)
 {
     return 1000L*(tm2->sec - tm1->sec) +
            (long) (tm2->ms - tm1->ms);
@@ -15710,8 +16102,7 @@ static TimeMark tickStartTM;
 static long intendedTickLength;
 
 long
-NextTickLength(timeRemaining)
-     long timeRemaining;
+NextTickLength (long timeRemaining)
 {
     long nominalTickLength, nextTickLength;
 
@@ -15727,16 +16118,18 @@ NextTickLength(timeRemaining)
 
 /* Adjust clock one minute up or down */
 void
-AdjustClock(Boolean which, int dir)
+AdjustClock (Boolean which, int dir)
 {
+    if(appData.autoCallFlag) { DisplayError(_("Clock adjustment not allowed in auto-flag mode"), 0); return; }
     if(which) blackTimeRemaining += 60000*dir;
     else      whiteTimeRemaining += 60000*dir;
     DisplayBothClocks();
+    adjustedClock = TRUE;
 }
 
 /* Stop clocks and reset to a fresh time control */
 void
-ResetClocks()
+ResetClocks ()
 {
     (void) StopClockTimer();
     if (appData.icsActive) {
@@ -15755,13 +16148,14 @@ ResetClocks()
     }
     lastWhite = lastBlack = whiteStartMove = blackStartMove = 0;
     DisplayBothClocks();
+    adjustedClock = FALSE;
 }
 
 #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()
+DecrementClocks ()
 {
     long timeRemaining;
     long lastTickLength, fudge;
@@ -15841,7 +16235,7 @@ DecrementClocks()
    from the color that is *not* on move now.
 */
 void
-SwitchClocks(int newMoveNr)
+SwitchClocks (int newMoveNr)
 {
     long lastTickLength;
     TimeMark now;
@@ -15904,7 +16298,7 @@ SwitchClocks(int newMoveNr)
 
 /* Stop both clocks */
 void
-StopClocks()
+StopClocks ()
 {
     long lastTickLength;
     TimeMark now;
@@ -15930,7 +16324,7 @@ StopClocks()
 /* Start clock of player on move.  Time may have been reset, so
    if clock is already running, stop and restart it. */
 void
-StartClocks()
+StartClocks ()
 {
     (void) StopClockTimer(); /* in case it was running already */
     DisplayBothClocks();
@@ -15961,8 +16355,7 @@ StartClocks()
 }
 
 char *
-TimeString(ms)
-     long ms;
+TimeString (long ms)
 {
     long second, minute, hour, day;
     char *sign = "";
@@ -16009,8 +16402,7 @@ TimeString(ms)
  * This is necessary because some C libraries aren't ANSI C compliant yet.
  */
 char *
-StrStr(string, match)
-     char *string, *match;
+StrStr (char *string, char *match)
 {
     int i, length;
 
@@ -16024,8 +16416,7 @@ StrStr(string, match)
 }
 
 char *
-StrCaseStr(string, match)
-     char *string, *match;
+StrCaseStr (char *string, char *match)
 {
     int i, j, length;
 
@@ -16044,8 +16435,7 @@ StrCaseStr(string, match)
 
 #ifndef _amigados
 int
-StrCaseCmp(s1, s2)
-     char *s1, *s2;
+StrCaseCmp (char *s1, char *s2)
 {
     char c1, c2;
 
@@ -16060,24 +16450,21 @@ StrCaseCmp(s1, s2)
 
 
 int
-ToLower(c)
-     int c;
+ToLower (int c)
 {
     return isupper(c) ? tolower(c) : c;
 }
 
 
 int
-ToUpper(c)
-     int c;
+ToUpper (int c)
 {
     return islower(c) ? toupper(c) : c;
 }
 #endif /* !_amigados   */
 
 char *
-StrSave(s)
-     char *s;
+StrSave (char *s)
 {
   char *ret;
 
@@ -16089,8 +16476,7 @@ StrSave(s)
 }
 
 char *
-StrSavePtr(s, savePtr)
-     char *s, **savePtr;
+StrSavePtr (char *s, char **savePtr)
 {
     if (*savePtr) {
        free(*savePtr);
@@ -16102,7 +16488,7 @@ StrSavePtr(s, savePtr)
 }
 
 char *
-PGNDate()
+PGNDate ()
 {
     time_t clock;
     struct tm *tm;
@@ -16117,9 +16503,7 @@ PGNDate()
 
 
 char *
-PositionToFEN(move, overrideCastling)
-     int move;
-     char *overrideCastling;
+PositionToFEN (int move, char *overrideCastling)
 {
     int i, j, fromX, fromY, toX, toY;
     int whiteToPlay;
@@ -16287,10 +16671,7 @@ PositionToFEN(move, overrideCastling)
 }
 
 Boolean
-ParseFEN(board, blackPlaysFirst, fen)
-    Board board;
-     int *blackPlaysFirst;
-     char *fen;
+ParseFEN (Board board, int *blackPlaysFirst, char *fen)
 {
     int i, j;
     char *p, c;
@@ -16530,7 +16911,7 @@ ParseFEN(board, blackPlaysFirst, fen)
 }
 
 void
-EditPositionPasteFEN(char *fen)
+EditPositionPasteFEN (char *fen)
 {
   if (fen != NULL) {
     Board initial_position;
@@ -16553,7 +16934,8 @@ EditPositionPasteFEN(char *fen)
 
 static char cseq[12] = "\\   ";
 
-Boolean set_cont_sequence(char *new_seq)
+Boolean
+set_cont_sequence (char *new_seq)
 {
     int len;
     Boolean ret;
@@ -16578,7 +16960,8 @@ Boolean set_cont_sequence(char *new_seq)
     for the dest buffer.  lp argument indicats line position upon entry, and it's
     passed back upon exit.
 */
-int wrap(char *dest, char *src, int count, int width, int *lp)
+int
+wrap (char *dest, char *src, int count, int width, int *lp)
 {
     int len, i, ansi, cseq_len, line, old_line, old_i, old_len, clen;
 
@@ -16662,7 +17045,7 @@ int wrap(char *dest, char *src, int count, int width, int *lp)
 Boolean modeRestore = FALSE;
 
 void
-PushInner(int firstMove, int lastMove)
+PushInner (int firstMove, int lastMove)
 {
        int i, j, nrMoves = lastMove - firstMove;
 
@@ -16693,7 +17076,7 @@ PushInner(int firstMove, int lastMove)
 }
 
 void
-PushTail(int firstMove, int lastMove)
+PushTail (int firstMove, int lastMove)
 {
        if(appData.icsActive) { // only in local mode
                forwardMostMove = currentMove; // mimic old ICS behavior
@@ -16707,7 +17090,7 @@ PushTail(int firstMove, int lastMove)
 }
 
 void
-PopInner(Boolean annotate)
+PopInner (Boolean annotate)
 {
        int i, j, nrMoves;
        char buf[8000], moveBuf[20];
@@ -16754,7 +17137,7 @@ PopInner(Boolean annotate)
 }
 
 Boolean
-PopTail(Boolean annotate)
+PopTail (Boolean annotate)
 {
        if(appData.icsActive) return FALSE; // only in local mode
        if(!storedGames) return FALSE; // sanity
@@ -16769,7 +17152,7 @@ PopTail(Boolean annotate)
 }
 
 void
-CleanupTail()
+CleanupTail ()
 {      // remove all shelved variations
        int i;
        for(i=0; i<storedGames; i++) {
@@ -16786,7 +17169,7 @@ CleanupTail()
 }
 
 void
-LoadVariation(int index, char *text)
+LoadVariation (int index, char *text)
 {       // [HGM] vari: shelve previous line and load new variation, parsed from text around text[index]
        char *p = text, *start = NULL, *end = NULL, wait = NULLCHAR;
        int level = 0, move;