Add -variant seirawan to menu
[xboard.git] / backend.c
index de835ae..ba658ea 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -311,23 +311,19 @@ char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */
 
 char*
 safeStrCpy( char *dst, const char *src, size_t count )
-{
-  /* see for example: https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/854-BSI.html
-   *
-   * usage:   safeStrCpy( stringA, stringB, sizeof(stringA)/sizeof(stringA[0]);
-   */
-
+{ // [HGM] made safe
+  int i;
   assert( dst != NULL );
   assert( src != NULL );
   assert( count > 0 );
 
-  strncpy( dst, src, count );
-  if(  dst[ count-1 ] != '\0' )
+  for(i=0; i<count; i++) if((dst[i] = src[i]) == NULLCHAR) break;
+  if(  i == count-1 && dst[i] != NULLCHAR)
     {
+      dst[ count-1 ] = '\0'; // make sure incomplete copy still null-terminated
       if(appData.debugMode)
       printf("safeStrCpy: copying %s into %s didn't work, not enough space %d\n",src,dst,count);
     }
-  dst[ count-1 ] = '\0';
 
   return dst;
 }
@@ -961,6 +957,7 @@ InitBackEnd1()
       case VariantJanus:      /* should work */
       case VariantSuper:      /* experimental */
       case VariantGreat:      /* experimental, requires legality testing to be off */
+      case VariantSChess:     /* S-Chess, should work */
        break;
       }
     }
@@ -5595,7 +5592,8 @@ SendBoard(cps, moveNum)
       /* Kludge to set black to move, avoiding the troublesome and now
        * deprecated "black" command.
        */
-      if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);
+      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);
 
       SendToProgram("edit\n", cps);
       SendToProgram("#\n", cps);
@@ -6442,8 +6440,9 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
                MarkTargetSquares(0);
                DragPieceBegin(xPix, yPix);
            }
-           return;
           }
+          if(x == fromX && y == fromY) return; // if OnlyMove altered (x,y) we go on
+          second = FALSE; 
        }
        // ignore clicks on holdings
        if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
@@ -6521,7 +6520,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
     }
 
     // off-board moves should not be highlighted
-    if(x < 0 || x < 0) ClearHighlights();
+    if(x < 0 || y < 0) ClearHighlights();
 
     if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) {
        SetHighlights(fromX, fromY, toX, toY);
@@ -7456,6 +7455,15 @@ 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
+      int dummy, s=6; char buf[MSG_SIZ];
+      if(appData.icsActive || forwardMostMove != 0 || cps != &first || startedFromSetupPosition) return;
+      if(sscanf(message, "setup (%s", buf) == 1) s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf);
+      ParseFEN(boards[0], &dummy, message+s);
+      DrawPosition(TRUE, boards[0]);
+      startedFromSetupPosition = TRUE;
+      return;
+    }
     /* [HGM] Allow engine to set up a position. Don't ask me why one would
      * want this, I was asked to put it in, and obliged.
      */
@@ -7482,13 +7490,15 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
      * Look for communication commands
      */
     if (!strncmp(message, "telluser ", 9)) {
-       EscapeExpand(message+9, message+9); // [HGM] esc: allow escape sequences in popup box
+       if(message[9] == '\\' && message[10] == '\\')
+           EscapeExpand(message+9, message+11); // [HGM] esc: allow escape sequences in popup box
        DisplayNote(message + 9);
        return;
     }
     if (!strncmp(message, "tellusererror ", 14)) {
        cps->userError = 1;
-       EscapeExpand(message+14, message+14); // [HGM] esc: allow escape sequences in popup box
+       if(message[14] == '\\' && message[15] == '\\')
+           EscapeExpand(message+14, message+16); // [HGM] esc: allow escape sequences in popup box
        DisplayError(message + 14, 0);
        return;
     }
@@ -8212,7 +8222,7 @@ ParseGameHistory(game)
     yynewstr(game);
     for (;;) {
        yyboardindex = boardIndex;
-       moveType = (ChessMove) yylex();
+       moveType = (ChessMove) Myylex();
        switch (moveType) {
          case IllegalMove:             /* maybe suicide chess, etc. */
   if (appData.debugMode) {
@@ -9661,7 +9671,7 @@ LoadGameOneMove(readAhead)
     } else {
       if (gameFileFP == NULL)
          return FALSE;
-      moveType = (ChessMove) yylex();
+      moveType = (ChessMove) Myylex();
     }
 
     done = FALSE;
@@ -10156,7 +10166,7 @@ LoadGame(f, gameNumber, title, useList)
     cm = lastLoadGameStart = EndOfFile;
     while (gn > 0) {
        yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
+       cm = (ChessMove) Myylex();
        switch (cm) {
          case EndOfFile:
            if (cmailMsgLoaded) {
@@ -10209,7 +10219,7 @@ LoadGame(f, gameNumber, title, useList)
            if (gn > 0) {
                do {
                    yyboardindex = forwardMostMove;
-                   cm = (ChessMove) yylex();
+                   cm = (ChessMove) Myylex();
                } while (cm == PGNTag || cm == Comment);
            }
            break;
@@ -10248,7 +10258,7 @@ LoadGame(f, gameNumber, title, useList)
        /* Skip any header junk before position diagram and/or move 1 */
        for (;;) {
            yyboardindex = forwardMostMove;
-           cm = (ChessMove) yylex();
+           cm = (ChessMove) Myylex();
 
            if (cm == EndOfFile ||
                cm == GNUChessGame || cm == XBoardGame) {
@@ -10321,7 +10331,7 @@ LoadGame(f, gameNumber, title, useList)
        }
 
        yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
+       cm = (ChessMove) Myylex();
 
        /* Handle comments interspersed among the tags */
        while (cm == Comment) {
@@ -10331,7 +10341,7 @@ LoadGame(f, gameNumber, title, useList)
            p = yy_text;
            AppendComment(currentMove, p, FALSE);
            yyboardindex = forwardMostMove;
-           cm = (ChessMove) yylex();
+           cm = (ChessMove) Myylex();
        }
     }
 
@@ -10408,7 +10418,7 @@ LoadGame(f, gameNumber, title, useList)
            }
        }
        yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
+       cm = (ChessMove) Myylex();
     }
 
     if (first.pr == NoProc) {
@@ -10434,7 +10444,7 @@ LoadGame(f, gameNumber, title, useList)
        p = yy_text;
        AppendComment(currentMove, p, FALSE);
        yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
+       cm = (ChessMove) Myylex();
     }
 
     if ((cm == EndOfFile && lastLoadGameStart != EndOfFile ) ||
@@ -11843,6 +11853,37 @@ DisplayTwoMachinesTitle()
 }
 
 void
+SettingsMenuIfReady()
+{
+  if (second.lastPing != second.lastPong) {
+    DisplayMessage("", _("Waiting for second chess program"));
+    ScheduleDelayedEvent(SettingsMenuIfReady, 10); // [HGM] fast: lowered from 1000
+    return;
+  }
+  ThawUI();
+  DisplayMessage("", "");
+  SettingsPopUp(&second);
+}
+
+int
+WaitForSecond(DelayedEventCallback retry)
+{
+    if (second.pr == NULL) {
+       StartChessProgram(&second);
+       if (second.protocolVersion == 1) {
+         retry();
+       } else {
+         /* kludge: allow timeout for initial "feature" command */
+         FreezeUI();
+         DisplayMessage("", _("Starting second chess program"));
+         ScheduleDelayedEvent(retry, FEATURE_TIMEOUT);
+       }
+       return 1;
+    }
+    return 0;
+}
+
+void
 TwoMachinesEvent P((void))
 {
     int i;
@@ -11884,21 +11925,14 @@ TwoMachinesEvent P((void))
     TruncateGame(); // [HGM] vari: MachineWhite and MachineBlack do this...
     ResurrectChessProgram();   /* in case first program isn't running */
 
-    if (second.pr == NULL) {
-       StartChessProgram(&second);
-       if (second.protocolVersion == 1) {
-         TwoMachinesEventIfReady();
-       } else {
-         /* kludge: allow timeout for initial "feature" command */
-         FreezeUI();
-         DisplayMessage("", _("Starting second chess program"));
-         ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);
-       }
-       return;
-    }
+    if(WaitForSecond(TwoMachinesEventIfReady)) return;
     DisplayMessage("", "");
     InitChessProgram(&second, FALSE);
     SendToProgram("force\n", &second);
+    if(first.lastPing != first.lastPong) { // [HGM] wait till we are sure first engine has set up position
+      ScheduleDelayedEvent(TwoMachinesEvent, 10);
+      return;
+    }
     if (startedFromSetupPosition) {
        SendBoard(&second, backwardMostMove);
     if (appData.debugMode) {
@@ -13528,6 +13562,8 @@ ReceiveFromProgram(isr, closure, message, count, error)
                   sscanf(message, "error %c", &c)!=1 && sscanf(message, "illegal %c", &c)!=1 &&
                   sscanf(message, "tell%c", &c)!=1   && sscanf(message, "0-1 %c", &c)!=1 &&
                   sscanf(message, "1-0 %c", &c)!=1   && sscanf(message, "1/2-1/2 %c", &c)!=1 &&
+                  sscanf(message, "setboard %c", &c)!=1   && sscanf(message, "setup %c", &c)!=1 &&
+                  sscanf(message, "hint: %c", &c)!=1 && 
                   sscanf(message, "pong %c", &c)!=1   && start != '#') {
                    quote = appData.engineComments == 2 ? "# " : "### NON-COMPLIANT! ### ";
                    print = (appData.engineComments >= 2);
@@ -13830,6 +13866,7 @@ FeatureDone(cps, val)
 {
   DelayedEventCallback cb = GetDelayedEvent();
   if ((cb == InitBackEnd3 && cps == &first) ||
+      (cb == SettingsMenuIfReady && cps == &second) ||
       (cb == TwoMachinesEventIfReady && cps == &second)) {
     CancelDelayedEvent();
     ScheduleDelayedEvent(cb, val ? 1 : 3600000);