Lock game an position file during writing
[xboard.git] / backend.c
index 7b5d2fc..9ecc707 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -57,6 +57,9 @@
 
 #define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );
 
+int flock(int f, int code);
+#define LOCK_EX 2
+
 #else
 
 #define DoSleep( n ) if( (n) >= 0) sleep(n)
@@ -267,6 +270,7 @@ char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */
 extern int chatCount;
 int chattingPartner;
 char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */
+char lastMsg[MSG_SIZ];
 ChessSquare pieceSweep = EmptySquare;
 ChessSquare promoSweep = EmptySquare, defaultPromoChoice;
 int promoDefaultAltered;
@@ -325,7 +329,7 @@ safeStrCpy( char *dst, const char *src, size_t count )
     {
       dst[ count-1 ] = '\0'; // make sure incomplete copy still null-terminated
       if(appData.debugMode)
-      fprintf(debugFP, "safeStrCpy: copying %s into %s didn't work, not enough space %d\n",src,dst,count);
+      fprintf(debugFP, "safeStrCpy: copying %s into %s didn't work, not enough space %d\n",src,dst, (int)count);
     }
 
   return dst;
@@ -841,6 +845,7 @@ ReplaceEngine(ChessProgramState *cps, int n)
     appData.noChessProgram = False;
     appData.clockMode = True;
     InitEngine(cps, n);
+    if(n) return; // only startup first engine immediately; second can wait
     savCps = cps; // parameter to LoadEngine passed as globals, to allow scheduled calling :-(
     LoadEngine();
 }
@@ -1320,6 +1325,12 @@ InitBackEnd3 P((void))
     DisplayMessage("", "");
     if (StrCaseCmp(appData.initialMode, "") == 0) {
       initialMode = BeginningOfGame;
+      if(!appData.icsActive && appData.noChessProgram) { // [HGM] could be fall-back
+        gameMode = MachinePlaysBlack; // "Machine Black" might have been implicitly highlighted
+        ModeHighlight(); // make sure XBoard knows it is highlighted, so it will un-highlight it
+        gameMode = BeginningOfGame; // in case BeginningOfGame now means "Edit Position"
+        ModeHighlight();
+      }
     } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
       initialMode = TwoMachinesPlay;
     } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
@@ -2977,9 +2988,9 @@ read_from_ics(isr, closure, data, count, error)
                }
            } // [HGM] chat: end of patch
 
+          backup = i;
            if (appData.zippyTalk || appData.zippyPlay) {
                 /* [DM] Backup address for color zippy lines */
-                backup = i;
 #if ZIPPY
                if (loggedOn == TRUE)
                        if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||
@@ -3144,6 +3155,8 @@ read_from_ics(isr, closure, data, count, error)
                    continue;
            }
 
+          if(i < backup) { i = backup; continue; } // [HGM] for if ZippyControl matches, but the colorie code doesn't
+
            if (looking_at(buf, &i, "\\   ")) {
                if (prevColor != ColorNormal) {
                    if (oldi > next_out) {
@@ -7927,7 +7940,10 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
        snprintf(buf1, sizeof(buf1), _("Failed to start %s chess program %s on %s: %s\n"),
                _(cps->which), cps->program, cps->host, message);
        RemoveInputSource(cps->isr);
-       DisplayFatalError(buf1, 0, 1);
+       if(appData.icsActive) DisplayFatalError(buf1, 0, 1); else {
+           if(cps == &first) appData.noChessProgram = TRUE;
+           DisplayError(buf1, 0);
+       }
        return;
     }
 
@@ -10976,6 +10992,7 @@ SaveGameToFile(filename, append)
 {
     FILE *f;
     char buf[MSG_SIZ];
+    int result;
 
     if (strcmp(filename, "-") == 0) {
        return SaveGame(stdout, 0, NULL);
@@ -10986,7 +11003,14 @@ SaveGameToFile(filename, append)
            DisplayError(buf, errno);
            return FALSE;
        } else {
-           return SaveGame(f, 0, NULL);
+           safeStrCpy(buf, lastMsg, MSG_SIZ);
+           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...
+           result = SaveGame(f, 0, NULL);
+           DisplayMessage(buf, "");
+           return result;
        }
     }
 }
@@ -11348,7 +11372,13 @@ SavePositionToFile(filename)
            DisplayError(buf, errno);
            return FALSE;
        } else {
+           safeStrCpy(buf, lastMsg, MSG_SIZ);
+           DisplayMessage(_("Waiting for access to save file"), "");
+           flock(fileno(f), LOCK_EX); // [HGM] lock
+           DisplayMessage(_("Saving position"), "");
+           lseek(fileno(f), 0, SEEK_END);     // better safe than sorry...
            SavePosition(f, 0, NULL);
+           DisplayMessage(buf, "");
            return TRUE;
        }
     }
@@ -13786,6 +13816,7 @@ SendToProgram(message, cps)
     outCount = OutputToProcess(cps->pr, message, count, &error);
     if (outCount < count && !exiting
                          && !endingGame) { /* [HGM] crash: to not hang GameEnds() writing to deceased engines */
+      if(!cps->initDone) return; // [HGM] should not generate fatal error during engine load
       snprintf(buf, MSG_SIZ, _("Error writing to %s chess program"), _(cps->which));
         if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */
             if((signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) {