home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1996 May / PCOnline_05_1996.bin / linux / source / xap / gchess / xboard-3.0 / xboard-3 / xboard-3.0.pl9 / backend.c next >
C/C++ Source or Header  |  1993-09-09  |  111KB  |  4,254 lines

  1. /*
  2.  * backend.c -- Common back end for X and Windows NT versions of XBoard
  3.  * $Id: backend.c,v 1.22 1993/09/10 01:24:43 mann Exp $
  4.  *
  5.  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
  6.  * Enhancements Copyright 1992-93 Free Software Foundation, Inc.
  7.  *
  8.  * XBoard borrows its colors, icon and piece bitmaps from XChess
  9.  * which was written and is copyrighted by Wayne Christopher.
  10.  *
  11.  * The following terms apply to Digital Equipment Corporation's copyright
  12.  * interest in XBoard:
  13.  * ------------------------------------------------------------------------
  14.  * All Rights Reserved
  15.  *
  16.  * Permission to use, copy, modify, and distribute this software and its
  17.  * documentation for any purpose and without fee is hereby granted,
  18.  * provided that the above copyright notice appear in all copies and that
  19.  * both that copyright notice and this permission notice appear in
  20.  * supporting documentation, and that the name of Digital not be
  21.  * used in advertising or publicity pertaining to distribution of the
  22.  * software without specific, written prior permission.
  23.  *
  24.  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  25.  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  26.  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  27.  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  28.  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  29.  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  30.  * SOFTWARE.
  31.  * ------------------------------------------------------------------------
  32.  *
  33.  * The following terms apply to the enhanced version of XBoard distributed
  34.  * by the Free Software Foundation:
  35.  * ------------------------------------------------------------------------
  36.  * This program is free software; you can redistribute it and/or modify
  37.  * it under the terms of the GNU General Public License as published by
  38.  * the Free Software Foundation; either version 2 of the License, or
  39.  * (at your option) any later version.
  40.  *
  41.  * This program is distributed in the hope that it will be useful,
  42.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  43.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  44.  * GNU General Public License for more details.
  45.  *
  46.  * You should have received a copy of the GNU General Public License
  47.  * along with this program; if not, write to the Free Software
  48.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  49.  * ------------------------------------------------------------------------
  50.  *
  51.  * See the file ChangeLog for a revision history.
  52.  */
  53.  
  54. #include <stdio.h>
  55. #include <ctype.h>
  56. #include <signal.h>
  57. #include <errno.h>
  58. #include <sys/types.h>
  59. #include <time.h>
  60. #if (defined(__STDC__) || defined(WIN32)) && !defined(ESIX)
  61. #include <stdlib.h>
  62. #endif
  63. #if defined(SYSTEM_FIVE) || defined(SYSV)
  64. #include <sys/types.h>
  65. #include <sys/stat.h>
  66. #ifdef AIXV3
  67. #include <fcntl.h>
  68. #else
  69. #include <sys/fcntl.h>
  70. #endif /*AIXV3*/
  71. #ifdef SVR4
  72. #include <stropts.h>
  73. #ifdef sun
  74. #include <sys/systeminfo.h>
  75. #endif
  76. #endif
  77. #endif
  78. #if defined(__STDC__) || defined(SYSTEM_FIVE) || defined(SYSV) || defined(sun) || defined(WIN32)
  79. #include <string.h>
  80. #else
  81. #include <strings.h>
  82. #endif
  83. #include <math.h>
  84.  
  85. #include "common.h"
  86. #include "frontend.h"
  87. #include "backend.h"
  88. #include "parser.h"
  89.  
  90. int establish P((void));
  91. void read_from_player P((InputSourceRef isr, char *buf, int count, int error));
  92. void read_from_ics P((InputSourceRef isr, char *buf, int count, int error));
  93. void SendToICS P((char *s));
  94. ChessSquare CharToPiece P((int c));
  95. char PieceToChar P((ChessSquare p));
  96. ChessSquare PromoPiece P((ChessMove moveType));
  97. ChessMove CoordsToAlgebraic P((int fromX, int fromY, int toX, int toY,
  98.                    int promoChar, int currentBoardIndex,
  99.                    char out[MOVE_LEN]));
  100. void InitPosition P((int redraw));
  101. void CopyBoard P((Board to, Board from));
  102. Boolean CompareBoards P((Board board1, Board board2));
  103. void SendCurrentBoard P((ProcRef pr));
  104. void SendBoard P((ProcRef pr, Board board));
  105. void FinishUserMove P((ChessMove moveType, int toX, int toY));
  106. void HandleMachineMove P((char *message, InputSourceRef isr));
  107. void LoadGameLoop P((void));
  108. int LoadGameOneMove P((void));
  109. int LoadGameFromFile P((char *filename, int n));
  110. int LoadPositionFromFile P((char *filename, int n));
  111. int SaveGameToFile P((char *filename));
  112. int SavePositionToFile P((char *filename));
  113. void ApplyMove P((ChessMove *moveType, int fromX, int fromY,
  114.           int toX, int toY, Board board));
  115. void MakeMove P((ChessMove *moveType, int fromX, int fromY,
  116.          int toX, int toY));
  117. void GameEnds P((char *message));
  118. void ShutdownChessPrograms P((char *message));
  119. void Reset P((int redraw));
  120. void EditPositionDone P((void));
  121. void PrintOpponents P((FILE *fp));
  122. void PrintPosition P((FILE *fp, int move));
  123. void InitChessProgram P((char *hostName, char *programName,
  124.              ProcRef *pr, InputSourceRef *isr, int *sendTime));
  125. void SendToProgram P((char *message, ProcRef pr));
  126. void ReceiveFromProgram P((InputSourceRef isr, char *buf, int count, int error));
  127. void SendSearchDepth P((ProcRef pr));
  128. void SendTimeRemaining P((ProcRef pr));
  129. void Attention P((ProcRef pr));
  130. char *StrStr P((char *string, char *match));
  131. int ToLower P((int c));
  132. int ToUpper P((int c));
  133.  
  134. Boolean ParseMachineMove P((char *machineMove, int moveNum,
  135.                 ChessMove *moveType, int *fromX, int *fromY,
  136.                 int *toX, int *toY, char *promoChar));
  137. void ParseGameHistory P((char *game));
  138. void ParseBoard8 P((char *string));
  139. void StartClocks P((void));
  140. void DisplayBothClocks P((void));
  141. void SwitchClocks P((void));
  142. void StopClocks P((void));
  143. void ResetClocks P((void));
  144.  
  145. /* States for ics_getting_history */
  146. #define H_FALSE 0
  147. #define H_REQUESTED 1
  148. #define H_GOT_REQ_HEADER 2
  149. #define H_GOT_UNREQ_HEADER 3
  150. #define H_GETTING_MOVES 4
  151.  
  152. FILE *gameFileFP,
  153.   *fromUserFP = stdin, *toUserFP = stdout, *debugFP = stderr;
  154.  
  155. int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0,
  156.   firstMove = TRUE, flipView = FALSE,
  157.   blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE,
  158.   searchTime = 0, pausing = FALSE,
  159.   whiteFlag = FALSE, blackFlag = FALSE, maybeThinking = FALSE, 
  160.   ics_user_moved = 0, ics_gamenum = -1,
  161.   ics_getting_history = H_FALSE, matchMode = FALSE;
  162. ProcRef firstProgramPR = NoProc, secondProgramPR = NoProc, icsPR = NoProc,
  163.   lastMsgPR = NoProc;
  164. InputSourceRef firstProgramISR = NULL, secondProgramISR = NULL,
  165.   telnetISR = NULL, fromUserISR = NULL;
  166. int firstSendTime = 2, secondSendTime = 2;  /* 0=don't, 1=do, 2=test first*/
  167. GameMode gameMode = BeginningOfGame, lastGameMode = BeginningOfGame;
  168. char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2],
  169.   ptyname[24], *chessDir, *programName, ics_black[32], ics_white[32],
  170.   endMessage[MOVE_LEN * 4];
  171. char *commentList[MAX_MOVES];
  172.  
  173. long whiteTimeRemaining, blackTimeRemaining, timeControl;
  174. long timeRemaining[2][MAX_MOVES];
  175. extern char currentMoveString[];
  176. #ifdef FLEX
  177. extern char *yytext;
  178. #else
  179. extern char yytext[];
  180. #endif
  181. extern int yyboardindex;
  182.      
  183. Board boards[MAX_MOVES], initialPosition = {
  184.     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
  185.     WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
  186.     { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
  187.     WhitePawn, WhitePawn, WhitePawn, WhitePawn },
  188.     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
  189.     EmptySquare, EmptySquare, EmptySquare, EmptySquare },
  190.     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
  191.     EmptySquare, EmptySquare, EmptySquare, EmptySquare },
  192.     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
  193.     EmptySquare, EmptySquare, EmptySquare, EmptySquare },
  194.     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
  195.     EmptySquare, EmptySquare, EmptySquare, EmptySquare },
  196.     { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
  197.     BlackPawn, BlackPawn, BlackPawn, BlackPawn },
  198.     { BlackRook, BlackKnight, BlackBishop, BlackQueen,
  199.     BlackKing, BlackBishop, BlackKnight, BlackRook }
  200. };
  201.  
  202. AppData appData;
  203.  
  204. char pieceToChar[] = {
  205.     'P', 'R', 'N', 'B', 'Q', 'K',
  206.     'p', 'r', 'n', 'b', 'q', 'k', '.'
  207.   };
  208.  
  209. void InitBackEnd1()
  210. {
  211.     char buf[MSG_SIZ];
  212.     int matched, min, sec;
  213.  
  214.     /*
  215.      * Internet chess server status
  216.      */
  217.     if (appData.icsActive) {
  218.     appData.matchMode = FALSE;
  219.     appData.noChessProgram = TRUE;
  220.     }
  221.  
  222.     /*
  223.      * Parse timeControl resource
  224.      */
  225.     if (!ParseTimeControl(appData.timeControl)) {
  226.     sprintf(buf, "Bad timeControl option %s",
  227.         appData.timeControl);
  228.     DisplayFatalError(buf, 0);
  229.     ExitEvent(2);
  230.     }
  231.     if (appData.icsActive) timeControl = 0;
  232.     
  233.     /*
  234.      * Parse searchTime resource
  235.      */
  236.     if (*appData.searchTime != NULLCHAR) {
  237.     matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
  238.     if (matched == 1) {
  239.         searchTime = min * 60;
  240.     } else if (matched == 2) {
  241.         searchTime = min * 60 + sec;
  242.     } else {
  243.         sprintf(buf, "Bad searchTime option %s",
  244.             appData.searchTime);
  245.         DisplayFatalError(buf, 0);
  246.         ExitEvent(2);
  247.     }
  248.     }
  249.     
  250.     if ((*appData.searchTime != NULLCHAR) || (appData.searchDepth > 0)
  251.     || appData.noChessProgram)
  252.       appData.clockMode = FALSE;
  253.     if (appData.icsActive) appData.clockMode = TRUE;
  254. }
  255.  
  256. int ParseTimeControl(tc)
  257.      char *tc;
  258. {
  259.     int matched, min, sec;
  260.  
  261.     matched = sscanf(tc, "%d:%d", &min, &sec);
  262.     if (matched == 1) {
  263.     timeControl = min * 60 * 1000;
  264.     } else if (matched == 2) {
  265.     timeControl = (min * 60 + sec) * 1000;
  266.     } else {
  267.     return FALSE;
  268.     }
  269.     return TRUE;
  270. }
  271.  
  272. void InitBackEnd2()
  273. {
  274.     char buf[MSG_SIZ];
  275.     int err;
  276.  
  277.     if (appData.icsActive) {
  278.     err = establish();
  279.     if (err != 0) {
  280.         sprintf(buf,
  281.             "Could not connect to host %s, port %d",  
  282.             appData.icsHost, appData.icsPort);
  283.         DisplayFatalError(buf, err);
  284.         ExitEvent(1);
  285.     }
  286.     SetICSMode();
  287.     telnetISR = AddInputSource(icsPR, FALSE, read_from_ics);
  288.     fromUserISR = AddInputSource(NoProc, FALSE, read_from_player);
  289.     } else {
  290.     if (appData.noChessProgram)
  291.       SetNCPMode();
  292.     else
  293.       SetGNUMode();
  294.     }
  295.  
  296.     /*
  297.      * If there is to be a machine match, set it up.
  298.      */
  299.     if (appData.matchMode) {
  300.     if (appData.noChessProgram) {
  301.         DisplayFatalError("Can't have a match with no chess programs", 0);
  302.         ExitEvent(2);
  303.     }
  304.     Reset(TRUE);
  305.     matchMode = TRUE;
  306.     if (*appData.loadGameFile != NULLCHAR) {
  307.         if (!LoadGameFromFile(appData.loadGameFile,
  308.                   appData.loadGameIndex)) {
  309.         DisplayFatalError("Bad game file", 0);
  310.         ExitEvent(1);
  311.         }
  312.     } else if (*appData.loadPositionFile != NULLCHAR) {
  313.         if (!LoadPositionFromFile(appData.loadPositionFile,
  314.                       appData.loadPositionIndex)) {
  315.         DisplayFatalError("Bad position file", 0);
  316.         ExitEvent(1);
  317.         }
  318.     }
  319.     TwoMachinesEvent();
  320.     } else {
  321.     Reset(TRUE);
  322.     if (*appData.loadGameFile != NULLCHAR) {
  323.         (void) LoadGameFromFile(appData.loadGameFile,
  324.                     appData.loadGameIndex);
  325.     } else if (*appData.loadPositionFile != NULLCHAR) {
  326.         (void) LoadPositionFromFile(appData.loadPositionFile,
  327.                     appData.loadPositionIndex);
  328.     }
  329.     }
  330. }
  331.  
  332. /*
  333.  * Establish will establish a contact to a remote host.port.
  334.  * Sets icsPR to a ProcRef for a process (or pseudo-process)
  335.  *  used to talk to the host.
  336.  * Returns 0 if okay, error code if not.
  337.  */
  338. int establish()
  339. {
  340.     char buf[MSG_SIZ];
  341.  
  342.     if (*appData.icsCommPort != NULLCHAR) {
  343.     /* Talk to the host through a serial comm port */
  344.     Raw();
  345.     return OpenCommPort(appData.icsCommPort, &icsPR);
  346.  
  347.     } else if (*appData.gateway != NULLCHAR) {
  348.     /* Use rsh to run telnet program on a gateway host */
  349.     sprintf(buf, "%s %s %s %s %d", appData.remoteShell,
  350.         appData.gateway, appData.telnetProgram,
  351.         appData.icsHost, appData.icsPort);
  352.     return StartChildProcess(buf, &icsPR);
  353.  
  354.     } else if (appData.useTelnet) {
  355.     Raw();
  356.     return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
  357.  
  358.     } else {
  359.     /* TCP socket interface differs somewhat between
  360.        Unix and NT; handle details in the front end.
  361.     */
  362.     return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
  363.     }
  364. }
  365.  
  366. void read_from_player(isr, message, count, error)
  367.      InputSourceRef isr;
  368.      char *message;
  369.      int count;
  370.      int error;
  371. {
  372.     int outError, outCount;
  373.  
  374.     if (count > 0) {
  375.     outCount = OutputToProcess(icsPR, message, count, &outError);
  376.     if (outCount < count) {
  377.         DisplayFatalError("Error writing to ICS", outError);
  378.         ExitEvent(1);
  379.     }
  380.     } else if (count < 0) {
  381.     DisplayFatalError("Error reading from keyboard", error);
  382.     ExitEvent(1);
  383.     } else {
  384.     DisplayFatalError("Got end of file from keyboard", 0);
  385.     ExitEvent(0);
  386.     }
  387. }
  388.  
  389.  
  390. void SendToICS(s)
  391.      char *s;
  392. {
  393.     int count, outCount, outError;
  394.  
  395.     if (appData.debugMode)
  396.       fprintf(debugFP, "Sending to ICS: %s", s);
  397.  
  398.     count = strlen(s);
  399.     outCount = OutputToProcess(icsPR, s, count, &outError);
  400.     if (outCount < count) {
  401.     DisplayFatalError("Error writing to ICS", outError);
  402.     ExitEvent(1);
  403.     }
  404. }
  405.  
  406.  
  407. static int leftover_start = 0, leftover_len = 0;
  408. char star_match[STAR_MATCH_N][MSG_SIZ];
  409.  
  410. /* Test whether pattern is present at &buf[*index]; if so, return TRUE,
  411.    advance *index beyond it, and set leftover_start to the new value of
  412.    *index; else return FALSE.  If pattern contains the character '*', it
  413.    matches any sequence of characters not containing '\r', '\n', or the
  414.    character following the '*' (if any), and the matched sequence(s) are
  415.    copied into star_match.
  416. */
  417. int looking_at(buf, index, pattern)
  418.      char *buf;
  419.      int *index;
  420.      char *pattern;
  421. {
  422.     char *bufp = &buf[*index], *patternp = pattern;
  423.     int star_count = 0;
  424.     char *matchp = star_match[0];
  425.     
  426.     for (;;) {
  427.     if (*patternp == NULLCHAR) {
  428.         *index = leftover_start = bufp - buf;
  429.         *matchp = NULLCHAR;
  430.         return TRUE;
  431.     }
  432.     if (*bufp == NULLCHAR) return FALSE;
  433.     if (*patternp == '*') {
  434.         if (*bufp == *(patternp + 1)) {
  435.         *matchp = NULLCHAR;
  436.         matchp = star_match[++star_count];
  437.         patternp += 2;
  438.         bufp++;
  439.         continue;
  440.         } else if (*bufp == '\n' || *bufp == '\r') {
  441.         patternp++;
  442.         if (*patternp == NULLCHAR)
  443.           continue;
  444.         else
  445.           return FALSE;
  446.         } else {
  447.         *matchp++ = *bufp++;
  448.         continue;
  449.         }
  450.     }
  451.     if (*patternp != *bufp) return FALSE;
  452.     patternp++;
  453.     bufp++;
  454.     }
  455. }
  456.  
  457.  
  458. void read_from_ics(isr, data, count, error)
  459.      InputSourceRef isr;
  460.      char *data;
  461.      int count;
  462.      int error;
  463. {
  464. #define BUF_SIZE 8192
  465. #define STARTED_NONE 0
  466. #define STARTED_BOARD 1
  467. #define STARTED_MOVES 2
  468.     
  469.     static int started = STARTED_NONE;
  470.     static char parse[20000];
  471.     static int  parse_pos;
  472.     static char buf[BUF_SIZE + 1];
  473.     
  474.     char str[500];
  475.     int i, oldi;
  476.     int buf_len;
  477.     int next_out;
  478.     
  479.     if (count > 0) {
  480.     /* If last read ended with a partial line that we couldn't parse,
  481.        prepend it to the new read and try again. */
  482.     if (leftover_len > 0) {
  483.         for (i=0; i<leftover_len; i++)
  484.           buf[i] = buf[leftover_start + i];
  485.     }
  486.  
  487.     memcpy(&buf[leftover_len], data, count);
  488.     buf_len = count + leftover_len;
  489.     buf[buf_len] = NULLCHAR;
  490.     next_out = leftover_len;
  491.     leftover_start = 0;
  492.     
  493.     i = 0;
  494.     while (i < buf_len) {
  495.         
  496. #ifdef ZIPPY
  497.         if (ZippyConverse(buf, &i)) continue;
  498. #else
  499.         /* Skip over what people say */
  500.         if (looking_at(buf, &i, "shouts: *") ||
  501.         looking_at(buf, &i, "tells you: *") ||
  502.         looking_at(buf, &i, "says: *") ||
  503.         looking_at(buf, &i, "whispers: *") ||
  504.         looking_at(buf, &i, "kibitzes: *")) {
  505.         continue;
  506.         }
  507. #endif /*ZIPPY*/
  508.  
  509. #define WILL_ECHO "\377\373\1"
  510. #define WONT_ECHO "\377\374\1"
  511. #define DO_ECHO   "\377\375\1"
  512. #define DONT_ECHO "\377\376\1"
  513.  
  514.         /* Kludge to deal with part of TELNET protocol */
  515.         if (looking_at(buf, &i, WILL_ECHO)) {
  516.         SendToICS(DO_ECHO);
  517.         EchoOff();
  518.         }
  519.         if (looking_at(buf, &i, WONT_ECHO)) {
  520.         SendToICS(DONT_ECHO);
  521.         EchoOn();
  522.         }
  523.  
  524.         if (looking_at(buf, &i,
  525.                "a   b   c   d   e   f   g   h") || 
  526.         looking_at(buf, &i,
  527.                "h   g   f   e   d   c   b   a")) {
  528.         /* End of board style 1 */
  529.         SendToICS("set style 8\n");
  530.                 SendToICS("refresh\n");
  531.         continue;
  532.         }
  533.         
  534.         oldi = i;
  535.         if (looking_at(buf, &i, "#@#")) {
  536.         started = STARTED_BOARD;
  537.         parse_pos = 0;
  538.         if (oldi > next_out)
  539.           fwrite(&buf[next_out], oldi - next_out, 1, toUserFP);
  540.         continue;
  541.         }
  542.         
  543.         if (started == STARTED_BOARD && looking_at(buf, &i, "@#@")) {
  544.         /* Board read is done */
  545.         started = STARTED_NONE;
  546.         next_out = i;
  547.         parse[parse_pos] = NULLCHAR;
  548.         
  549.         /* Parse and display the board */
  550.         ParseBoard8(parse);
  551.         
  552.         ics_user_moved = 0;
  553.         continue;
  554.         }
  555.         
  556.         if (looking_at(buf, &i, "* *vs. * *--- *")) {
  557.         /* Header for a move list -- first line */
  558.         /* We might want to save some of these fields but
  559.            for now we don't */
  560.         switch (ics_getting_history) {
  561.           case H_FALSE:
  562.             switch (gameMode) {
  563.               case IcsIdle:
  564.               case BeginningOfGame:
  565.             /* User typed "moves" or "oldmoves" while we
  566.                were idle.  Pretend we asked for these
  567.                moves and soak them up so user can step
  568.                through them and/or save them.
  569.             */
  570.             Reset(FALSE);
  571.             gameMode = IcsObserving;
  572.             ModeHighlight();
  573.             ics_gamenum = -1;
  574.             ics_getting_history = H_GOT_REQ_HEADER;
  575.             break;
  576.               case ForceMoves: /*?*/
  577.               case EditPosition: /*?*/
  578.             /* Should above feature work in these modes too? */
  579.             /* For now it doesn't */
  580.             ics_getting_history = H_GOT_UNREQ_HEADER;
  581.             break;
  582.               default:
  583.             ics_getting_history = H_GOT_UNREQ_HEADER;
  584.             break;
  585.             }
  586.             break;
  587.           case H_REQUESTED:
  588.             /* All is well */
  589.             ics_getting_history = H_GOT_REQ_HEADER;
  590.             break;
  591.           case H_GOT_REQ_HEADER:
  592.           case H_GOT_UNREQ_HEADER:
  593.           case H_GETTING_MOVES:
  594.             /* Should not happen */
  595.             DisplayError("Error gathering move list", 0);
  596.             ics_getting_history = H_GOT_UNREQ_HEADER;
  597.             break;
  598.         }
  599.         continue;
  600.         }
  601.  
  602.         if (looking_at(buf, &i,
  603.               "* * match, initial time: * minutes, increment: * seconds.")) {
  604.         /* Header for a move list -- second line */
  605.         /* Initial board will follow if this is a wild game */
  606.         /* Again, we might want to save some of these fields later */
  607.         /* For now we do nothing with them. */
  608.         continue;
  609.         }
  610.  
  611.         oldi = i;
  612.         if (looking_at(buf, &i, "Move  ")) {
  613.         /* Beginning of a move list */
  614.         switch (ics_getting_history) {
  615.           case H_FALSE:
  616.             /* Normally should not happen */
  617.             /* Maybe user hit reset while we were parsing */
  618.             break;
  619.           case H_REQUESTED:
  620.           case H_GETTING_MOVES:
  621.             /* Should not happen */
  622.             DisplayError("Error gathering move list", 0);
  623.             break;
  624.           case H_GOT_REQ_HEADER:
  625.             ics_getting_history = H_GETTING_MOVES;
  626.             started = STARTED_MOVES;
  627.             parse_pos = 0;
  628.             if (oldi > next_out)
  629.               fwrite(&buf[next_out], oldi - next_out, 1, toUserFP);
  630.             break;
  631.           case H_GOT_UNREQ_HEADER:
  632.             ics_getting_history = H_FALSE;
  633.             break;
  634.         }
  635.         continue;
  636.         }                
  637.         
  638.         if(looking_at(buf, &i, "% ")) {
  639.         switch (started) {
  640.           case STARTED_NONE:
  641. #ifdef ZIPPY
  642.             ZippyPeriodic();
  643. #endif /*ZIPPY*/
  644.             continue;
  645.           case STARTED_BOARD:
  646.             /* Something went wrong; found a prompt while
  647.                accumulating a board */
  648.             started = STARTED_NONE;
  649.             DisplayError("Error gathering board", 0);
  650.             continue;
  651.           case STARTED_MOVES:
  652.             started = STARTED_NONE;
  653.             parse[parse_pos] = NULLCHAR;
  654.             ParseGameHistory(parse);
  655.              if (gameMode == IcsObserving && ics_gamenum == -1) {
  656.             /* Moves came from oldmoves or moves command
  657.                while we weren't doing anything else.
  658.             */
  659.             currentMove = forwardMostMove;
  660.             flipView = appData.flipView;
  661.             DrawPosition(FALSE, boards[currentMove]);
  662.             DisplayBothClocks();
  663.             sprintf(str, "%s vs. %s", ics_white, ics_black);
  664.             DisplayTitle(str);
  665.             gameMode = IcsIdle;
  666.             }
  667.             DisplayMove(currentMove - 1);
  668.             SendToICS("\n");  /*kludge: force a prompt*/
  669.             next_out = i;
  670.             ics_getting_history = H_FALSE;
  671.             continue;
  672.         }
  673.         }
  674.         
  675.         if (started != STARTED_NONE && i >= leftover_len) {
  676.         /* Accumulate characters in board
  677.            or move list*/
  678.         if (buf[i] != '\r' && buf[i] != NULLCHAR)
  679.           parse[parse_pos++] = buf[i];
  680.         }
  681.         
  682.         /* Start of game messages.  Mostly we detect start of game
  683.            when the first board image arrives, but we need to prime
  684.            the pump for games we're just observing. */
  685.         if (looking_at(buf, &i, "Adding game * to observation list")) {
  686.         sprintf(str, "refresh %d\n", atoi(star_match[0]));
  687.         SendToICS(str);
  688.         continue;
  689.         }
  690.         
  691.         /* Error messages */
  692.         if (ics_user_moved) {
  693.         if (looking_at(buf, &i, "No such command") ||
  694.             looking_at(buf, &i, "Illegal move") ||
  695.             looking_at(buf, &i, "Not a legal move") ||
  696.             looking_at(buf, &i, "Your king is in check") ||
  697.             looking_at(buf, &i, "It isn't your turn")) {
  698.             /**** Illegal move ****/
  699.             ics_user_moved = 0;
  700.             if (forwardMostMove > backwardMostMove) {
  701.             currentMove = --forwardMostMove;
  702.             DisplayError("Illegal move", 0);
  703.             DrawPosition(FALSE, boards[currentMove]);
  704.             SwitchClocks();
  705.             }
  706.             continue;
  707.         }
  708.         }
  709.  
  710.         if (looking_at(buf, &i, "You and your opponent still have time")) {
  711.         /* We must have called his flag a little too soon */
  712.         whiteFlag = blackFlag = FALSE;
  713.         continue;
  714.         }
  715.  
  716.         /* Start/end-of-game messages */
  717.         if (looking_at(buf, &i, "{Game * (* vs. *)* * *}")) {
  718.         /* New style generic game start/end messages */
  719.         /* star_match[0] is the game number */
  720.         /*           [1] is the white player's name */
  721.         /*           [2] is the black player's name */
  722.         /*           [3] is either ":" or empty (don't care) */
  723.         /*           [4] is usually the loser's name or a noise word */
  724.         /*           [5] contains the reason for the game end */
  725.         int gamenum = atoi(star_match[0]);
  726.         char *white = star_match[1];
  727.         char *loser = star_match[4];
  728.         char *why = star_match[5];
  729.         
  730. #ifdef ZIPPY
  731.         /* Game start messages */
  732.         if (strcmp(loser, "Creating") == 0 ||
  733.             strcmp(loser, "Continuing") == 0) {
  734.             ZippyGameStart(white, star_match[2]);
  735.             continue;
  736.         }
  737. #endif /*ZIPPY*/
  738.  
  739.         /* Game end messages */
  740.         if (ics_gamenum != gamenum) continue;
  741.  
  742.         if (StrStr(why, "checkmate")) {
  743.             if (strcmp(loser, white) == 0)
  744.               GameEnds("Black mates");
  745.             else
  746.               GameEnds("White mates");
  747.         } else if (StrStr(why, "resign")) {
  748.             if (strcmp(loser, white) == 0)
  749.               GameEnds("White resigns");
  750.             else
  751.               GameEnds("Black resigns");
  752.         } else if (StrStr(why, "forfeits on time")) {
  753.             if (strcmp(loser, white) == 0)
  754.               GameEnds("Black wins on time");
  755.             else
  756.               GameEnds("White wins on time");
  757.         } else if (StrStr(why, "stalemate")) {
  758.             GameEnds("Stalemate");
  759.         } else if (StrStr(why, "drawn by mutual agreement")) {
  760.             GameEnds("Draw agreed");
  761.         } else if (StrStr(why, "repetition")) {
  762.             GameEnds("Draw by repetition");
  763.         } else if (StrStr(why, "50")) {
  764.             GameEnds("Draw (50 move rule)");
  765.         } else if (StrStr(why, "neither player has mating")) {
  766.             GameEnds("Draw (insufficient material)");
  767.         } else if (StrStr(why, "no material")) {
  768.             GameEnds("Draw (insufficient material to win on time)");
  769.         } else if (StrStr(why, "time")) {
  770.             GameEnds("Draw (both players ran out of time)");
  771.         } else if (StrStr(why, "disconnected and forfeits")) {
  772.             /* in this case the word "abuser" preceded the loser */
  773.             loser = why;
  774.             why = strchr(loser, ' ');
  775.             *why++ = NULLCHAR;
  776.             if (strcmp(loser, white) == 0)
  777.               GameEnds("Black wins (forfeit)");
  778.             else
  779.               GameEnds("White wins (forfeit)");
  780.         } else if (StrStr(why, "assert")) {
  781.             /* "loser" is actually the winner in this case */
  782.             if (strcmp(loser, white) == 0)
  783.               GameEnds("White asserts a win");
  784.             else
  785.               GameEnds("Black asserts a win");
  786.         } else if (StrStr(why, "aborted")) {
  787.             GameEnds("Game aborted");
  788.         } else if (StrStr(why, "removed")) {
  789.             GameEnds("Game aborted");
  790.         } else if (StrStr(why, "adjourn")) {
  791.             GameEnds("Game adjourned");
  792.         }   
  793.         continue;
  794.         }
  795.  
  796.         if (looking_at(buf, &i, "Removing game * from observation list")) {
  797.         if (gameMode == IcsObserving &&
  798.             atoi(star_match[0]) == ics_gamenum)
  799.           {
  800.               StopClocks();
  801.               gameMode = IcsIdle;
  802.               ics_gamenum = -1;
  803.               ics_user_moved = FALSE;
  804.           }
  805.         continue;
  806.         }
  807.  
  808.         /* Advance leftover_start past any newlines we find,
  809.            so only partial lines can get reparsed */
  810.         if (looking_at(buf, &i, "\n")) continue;
  811.         if (looking_at(buf, &i, "\r")) {
  812. #ifdef WIN32
  813.         /* Kludge; change \r\000 to \r\r */
  814.         if (i < buf_len && buf[i] == NULLCHAR) buf[i++] = '\r';
  815. #endif
  816.         continue;
  817.         }
  818.  
  819.         i++;    /* skip unparsed character and loop back */
  820.     }
  821.     
  822.     if (started == STARTED_NONE && i > next_out)
  823.       fwrite(&buf[next_out], i - next_out, 1, toUserFP);
  824.     
  825.     leftover_len = buf_len - leftover_start;
  826.     /* if buffer ends with something we couldn't parse,
  827.        reparse it after appending the next read */
  828.     
  829.     } else if (count == 0) {
  830.     DisplayFatalError("Connection closed by ICS", 0);
  831.     ExitEvent(0);
  832.     } else {
  833.     DisplayFatalError("Error reading from ICS", error);
  834.     ExitEvent(1);
  835.     }
  836. }
  837.  
  838. /*
  839.   ICS board style 8 looks like this:
  840.   
  841.   #@#000observer        :aaa             :RNBQKBNRPPPPPPPP                                pppppppprnbqkbnr001W39390360003600@#@
  842.   
  843.   Information offsets, descriptions and lengths:
  844.   +3   Game # (3)
  845.   +6   White's name (16 + ':' = 17)
  846.   +23  Black's name (16 + ':' = 17)
  847.   +40  Board  (64)
  848.   +104 Move # (3)
  849.   +107 Whose move (1)
  850.   +108 White Strength (2)
  851.   +110 Black Strength (2)
  852.   +112 White Time (5)
  853.   +117 Black Time (5)
  854.   +122 Move string (variable
  855.   A "*" instead of a ":" after the name implies that the person using xboard
  856.     is playing the game.
  857.   The move string is either empty or consists of a move followed by
  858.     elapsed time in parentheses.
  859.   The pattern defined below doesn't include the #@# and @#@ brackets,
  860.     and it assumes the board string is null-terminated.  ParseBoard8's
  861.     caller takes care of this.
  862.   */
  863.  
  864. #define PATTERN "%3d%16s %1c%16s %1c%64c%3d%1c%2d%2d%5d%5d%s %s"
  865.  
  866. void ParseBoard8(string)
  867.      char *string;
  868.     GameMode newGameMode;
  869.     int gamenum;
  870.     int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;
  871.     char playing_white, playing_black, to_play, board_chars[64];
  872.     char move_str[500], str[500], elapsed_time[500];
  873.     char black[32], white[32];
  874.     Board board;
  875.     
  876.     if (appData.debugMode)
  877.       fprintf(debugFP, "Parsing board: %s\n", string);
  878.  
  879.     move_str[0] = NULLCHAR;
  880.     elapsed_time[0] = NULLCHAR;
  881.     n = sscanf(string, PATTERN, &gamenum, white, &playing_white,
  882.            black, &playing_black, board_chars, &moveNum, &to_play,
  883.            &white_stren, &black_stren, &white_time, &black_time,
  884.            move_str, elapsed_time);
  885.     if (n < 12) {
  886.     sprintf(str, "Failed to parse board string: \"%s\"", string);
  887.     DisplayError(str, 0);
  888.     return;
  889.     }
  890.  
  891.     if (playing_white == '*')
  892.       newGameMode = IcsPlayingWhite;
  893.     else if (playing_black == '*')
  894.       newGameMode = IcsPlayingBlack;
  895.     else
  896.       newGameMode = IcsObserving;
  897.     
  898.     /* Convert the move number to internal form */
  899.     moveNum = (moveNum - 1) * 2;
  900.     if (to_play == 'B') moveNum++;
  901.  
  902.     /* Deal with initial board display on move listing
  903.        of wild games.
  904.     */
  905.     switch (ics_getting_history) {
  906.       case H_FALSE:
  907.       case H_REQUESTED:
  908.     break;
  909.       case H_GOT_REQ_HEADER:
  910.     /* This is an initial board that we want */
  911.     gamenum = ics_gamenum;
  912.     moveNum = 0; /* !! ICS bug workaround */
  913.     break;
  914.       case H_GOT_UNREQ_HEADER:
  915.     /* This is an initial board that we don't want */
  916.     return;
  917.       case H_GETTING_MOVES:
  918.     /* Should not happen */
  919.     DisplayError("Error gathering move list", 0);
  920.     return;
  921.     }
  922.  
  923.     /* Take action if this is the first board of a new game */
  924.     if (gamenum != ics_gamenum) {
  925.     /* Check if trying to do two things at once */
  926.     if (gameMode == IcsObserving) {
  927.         /* Error: xboard can't handle two games at once */
  928.         /* Stop observing the old game */
  929.         sprintf(str, "observe %d\n", ics_gamenum);
  930.         SendToICS(str);
  931.         sprintf(str, "Aren't you observing game %d?  Attempting to stop observing it.",
  932.             ics_gamenum);
  933.         DisplayError(str, 0);
  934.         /* continue as in normal case */
  935.     } else if (gameMode == IcsPlayingBlack || gameMode == IcsPlayingWhite) {
  936.         /* Error: xboard can't handle two games at once */
  937.         if (newGameMode == IcsObserving) {
  938.         /* Stop observing the new game */
  939.         SendToICS("observe\n");
  940.         sprintf(str, "Aren't you playing a game?  Attempting to stop observing game %d.",
  941.             gamenum);
  942.         DisplayError(str, 0);
  943.         /* ignore this board */
  944.         return;
  945.         } else /* newGameMode == IcsPlaying(White|Black) */ {
  946.         /* Playing two games???  ICS supposedly can't do this. */
  947.         sprintf(str, "BUG: playing two games at once (%d and %d)",
  948.             ics_gamenum, gamenum);
  949.         DisplayError(str, 0);
  950.         /* continue as in normal case, hoping old game is gone */
  951.         }
  952.     }
  953.     /* Normal case, or error recovered */
  954.     Reset(FALSE);
  955.     if (moveNum > 0) {
  956.         /* Need to get game history */
  957.         ics_getting_history = H_REQUESTED;
  958.         sprintf(str, "moves %d\n", gamenum);
  959.         SendToICS(str);
  960.     }
  961.     }
  962.  
  963.     /* Initially flip the board to have black on the bottom iff playing
  964.        black, but let the user change it with the Flip View button. */
  965.     if (gameMode == IcsIdle || gameMode == BeginningOfGame)
  966.       flipView = (newGameMode == IcsPlayingBlack);
  967.  
  968.     /* Update known move number limits */
  969.     if (gameMode == IcsIdle || gameMode == BeginningOfGame) {
  970.     forwardMostMove = backwardMostMove = currentMove = moveNum;
  971.     } else if (moveNum > forwardMostMove) {
  972.     forwardMostMove = moveNum;
  973.     if (!pausing) 
  974.       currentMove = moveNum;
  975.     }
  976.  
  977.     /* Done with values from previous mode; copy in new ones */
  978.     gameMode = newGameMode;
  979.     ModeHighlight();
  980.     ics_gamenum = gamenum;
  981.     strcpy(ics_white, white);
  982.     strcpy(ics_black, black);
  983.  
  984.     /* Parse the board */
  985.     for (k = 0; k < 8; k++)
  986.       for (j = 0; j < 8; j++)
  987.     board[k][j] = CharToPiece(board_chars[k*8 + j]);
  988.     CopyBoard(boards[moveNum], board);
  989.     if (moveNum == 0) {
  990.     startedFromSetupPosition =
  991.       !CompareBoards(board, initialPosition);
  992.     }
  993.     
  994.     /* Put the move on the move list, first converting
  995.        to canonical algebraic form. */
  996.     if (moveNum > 0) {
  997.     ChessMove moveType;
  998.     int fromX, fromY, toX, toY;
  999.     char promoChar;
  1000.  
  1001.     if (moveNum - 1 < backwardMostMove) {
  1002.         /* We don't know what the board looked like before
  1003.            this move.  Punt. */
  1004.         strcpy(parseList[moveNum - 1], move_str);
  1005.     } else {
  1006.         if (ParseMachineMove(move_str, moveNum - 1, &moveType,
  1007.                  &fromX, &fromY, &toX, &toY, &promoChar)) {
  1008.         /* Work around ICS bug: pawn promotion is not indicated,
  1009.            even if underpromoted.  Unfortunately there is no
  1010.            workaround for the same bug when it bites us in
  1011.            ParseGameHistory().
  1012.            */
  1013.         if (move_str[0] == 'P' && (toY == 0 || toY == 7))
  1014.           promoChar = ToLower(PieceToChar(board[toY][toX]));
  1015.  
  1016.         (void) CoordsToAlgebraic(fromX, fromY, toX, toY, promoChar,
  1017.                      moveNum - 1, parseList[moveNum - 1]);
  1018.         strcat(parseList[moveNum - 1], " ");
  1019.         strcat(parseList[moveNum - 1], elapsed_time);
  1020.         } else {
  1021.         /* Move from ICS was illegal!?  Punt. */
  1022.         strcpy(parseList[moveNum - 1], move_str);
  1023.         }
  1024.     }
  1025.     }
  1026.     
  1027.     if (ics_getting_history == H_GOT_REQ_HEADER) {
  1028.     /* This was an initial position from a move list, not
  1029.        the current position, so don't display it */
  1030.     return;
  1031.     }
  1032.  
  1033.     /* Update and display the clocks */
  1034.     timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;
  1035.     timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;
  1036.     StartClocks();
  1037.  
  1038.     /* Display opponents and material strengths */
  1039.     sprintf(str, "%s (%d) vs. %s (%d)",
  1040.         ics_white, white_stren, ics_black, black_stren);
  1041.     DisplayTitle(str);
  1042.     
  1043.     /* Display the board */
  1044.     if (!pausing) {
  1045.     CommentPopDown();
  1046.     DrawPosition(FALSE, boards[currentMove]);
  1047.     DisplayMove(moveNum - 1);
  1048.     if (appData.ringBellAfterMoves && !ics_user_moved)
  1049.       RingBell();
  1050.     }
  1051. }
  1052.  
  1053. void ProcessICSInitScript(f)
  1054.      FILE *f;
  1055. {
  1056.     char buf[MSG_SIZ];
  1057.  
  1058.     while (fgets(buf, MSG_SIZ, f)) {
  1059.     SendToICS(buf);
  1060.     }
  1061.  
  1062.     fclose(f);
  1063. }
  1064.  
  1065. char PieceToChar(p)
  1066.      ChessSquare p;
  1067. {
  1068.     return pieceToChar[(int) p];
  1069. }
  1070.  
  1071. ChessSquare CharToPiece(c)
  1072.      int c;
  1073. {
  1074.     switch (c) {
  1075.       default:
  1076.       case '.':    return EmptySquare;
  1077.       case 'P':    return WhitePawn;
  1078.       case 'R':    return WhiteRook;
  1079.       case 'N':    return WhiteKnight;
  1080.       case 'B':    return WhiteBishop;
  1081.       case 'Q':    return WhiteQueen;
  1082.       case 'K':    return WhiteKing;
  1083.       case 'p':    return BlackPawn;
  1084.       case 'r':    return BlackRook;
  1085.       case 'n':    return BlackKnight;
  1086.       case 'b':    return BlackBishop;
  1087.       case 'q':    return BlackQueen;
  1088.       case 'k':    return BlackKing;
  1089.     }
  1090. }
  1091.  
  1092. ChessSquare PromoPiece(moveType)
  1093.      ChessMove moveType;
  1094. {
  1095.     switch (moveType) {
  1096.       default:
  1097.     return EmptySquare;
  1098.       case WhitePromotionQueen:
  1099.     return WhiteQueen;
  1100.       case BlackPromotionQueen:
  1101.     return BlackQueen;
  1102.       case WhitePromotionRook:
  1103.     return WhiteRook;
  1104.       case BlackPromotionRook:
  1105.     return BlackRook;
  1106.       case WhitePromotionBishop:
  1107.     return WhiteBishop;
  1108.       case BlackPromotionBishop:
  1109.     return BlackBishop;
  1110.       case WhitePromotionKnight:
  1111.     return WhiteKnight;
  1112.       case BlackPromotionKnight:
  1113.     return BlackKnight;
  1114.     }
  1115. }
  1116.  
  1117.  
  1118. /* Convert coordinates to normal algebraic notation.
  1119.    promoChar must be NULLCHAR or '.' if not a promotion.
  1120.    */
  1121. ChessMove CoordsToAlgebraic(fromX, fromY, toX, toY, promoChar,
  1122.                 currentBoardIndex, out)
  1123.      int fromX, fromY, toX, toY;
  1124.      int promoChar;
  1125.      int currentBoardIndex;
  1126.      char out[MOVE_LEN];
  1127. {
  1128.     ChessSquare piece;
  1129.     ChessMove ret;
  1130.     char *outp = out;
  1131.     int i;
  1132.     
  1133.     if (promoChar == '.') promoChar = NULLCHAR;
  1134.     piece = boards[currentBoardIndex][fromY][fromX];
  1135.     switch (piece) {
  1136.       case WhitePawn:
  1137.       case BlackPawn:
  1138.     /* Pawn move */
  1139.     *outp++ = fromX + 'a';
  1140.     if (fromX == toX) {
  1141.         /* Non-capture; use style "e5" or "e8Q" */
  1142.         *outp++ = toY + '1';
  1143.         *outp++ = ToUpper(promoChar);
  1144.         *outp = NULLCHAR;
  1145.     } else {
  1146.         /* Capture; use style "exd5" or "exd8Q" */
  1147.         *outp++ = 'x';
  1148.         *outp++ = toX + 'a';
  1149.         *outp++ = toY + '1';
  1150.         *outp++ = ToUpper(promoChar);
  1151.         *outp = NULLCHAR;
  1152.     }
  1153.     
  1154.     /* Test if okay by parsing; this notation should parse 
  1155.        unambiguously if the original move was legal.  More
  1156.        code would be needed if we wanted the style "ed" for
  1157.        captures, since that can be ambiguous.
  1158.        */
  1159.     ret = yylexstr(currentBoardIndex, out);
  1160.     break;
  1161.     
  1162.       case WhiteKing:
  1163.       case BlackKing:
  1164.     /* Test for castling or ICS wild castling */
  1165.     /* Use style "0-0" (zero-zero) */
  1166.     if (fromY == toY &&
  1167.         fromY == ((piece == WhiteKing) ? 0 : 7) &&
  1168.         ((fromX == 4 && (toX == 2 || toX == 6)) ||
  1169.          (fromX == 3 && (toX == 1 || toX == 5)))) {
  1170.         switch (toX) {
  1171.           case 1:
  1172.           case 6:
  1173.         strcpy(out, "0-0");
  1174.         break;
  1175.           case 2:
  1176.           case 5:
  1177.         strcpy(out, "0-0-0");
  1178.         break;
  1179.         }
  1180.         ret = yylexstr(currentBoardIndex, out);
  1181.         break;
  1182.     }
  1183.     /* else fall through */
  1184.     
  1185.       default:
  1186.     /* Piece move */
  1187.     /* First try style "Nf3" or "Nxf7" */
  1188.     *outp++ = ToUpper(PieceToChar(piece));
  1189.     
  1190.     /* Capture? */
  1191.     if(boards[currentBoardIndex][toY][toX] != EmptySquare)
  1192.       *outp++ = 'x';
  1193.     
  1194.     *outp++ = toX + 'a';
  1195.     *outp++ = toY + '1';
  1196.     *outp = NULLCHAR;
  1197.     
  1198.     /* Test if ambiguous */
  1199.     ret = yylexstr(currentBoardIndex, out);
  1200.     if (ret != AmbiguousMove) break;
  1201.     
  1202.     /* Try style "Ngf3" or "Nexf7" */
  1203.     for (i=4; i>=1; i--) out[i+1] = out[i];
  1204.     out[1] = fromX + 'a';
  1205.     
  1206.     /* Test if ambiguous */
  1207.     ret = yylexstr(currentBoardIndex, out);
  1208.     if (ret != AmbiguousMove) break;
  1209.     
  1210.     /* Try style "N1f3" */
  1211.     out[1] = fromY + '1';
  1212.     
  1213.     /* Test if ambiguous */
  1214.     ret = yylexstr(currentBoardIndex, out);
  1215.     if (ret != AmbiguousMove) break;
  1216.     
  1217.     /* Try style "Ng1f3" or "Ne5xf7" */
  1218.     /* Can be needed iff there are 3 or more pieces of the
  1219.        type being moved on the board, due to promotion */
  1220.     for (i=5; i>=2; i--) out[i+1] = out[i];
  1221.     out[1] = fromX + 'a';
  1222.     out[2] = fromY + '1';
  1223.     
  1224.     /* Test if okay */
  1225.     ret = yylexstr(currentBoardIndex, out);
  1226.     break; 
  1227.     
  1228.       case EmptySquare:
  1229.     /* Illegal move; use coordinate notation */
  1230.     *outp++ = fromX + 'a';
  1231.     *outp++ = fromY + '1';
  1232.     *outp++ = toX + 'a';
  1233.     *outp++ = toY + '1';
  1234.     *outp++ = ToUpper(promoChar);
  1235.     *outp = NULLCHAR;
  1236.     return BadMove;
  1237.     }
  1238.     
  1239.     switch (ret) {
  1240.       case NormalMove:
  1241.       case WhitePromotionKnight:
  1242.       case WhitePromotionBishop:
  1243.       case WhitePromotionRook:
  1244.       case WhitePromotionQueen:
  1245.       case BlackPromotionKnight:
  1246.       case BlackPromotionBishop:
  1247.       case BlackPromotionRook:
  1248.       case BlackPromotionQueen:
  1249.       case WhiteCapturesEnPassant:
  1250.       case BlackCapturesEnPassant:
  1251.       case WhiteKingSideCastle:
  1252.       case WhiteQueenSideCastle:
  1253.       case BlackKingSideCastle:
  1254.       case BlackQueenSideCastle:
  1255.       case WhiteKingSideCastleWild:
  1256.       case WhiteQueenSideCastleWild:
  1257.       case BlackKingSideCastleWild:
  1258.       case BlackQueenSideCastleWild:
  1259.     if (currentMoveString[0] != fromX + 'a' ||
  1260.         currentMoveString[1] != fromY + '1' ||
  1261.         currentMoveString[2] != toX + 'a' ||
  1262.         currentMoveString[3] != toY + '1' ||
  1263.         (promoChar != NULLCHAR &&
  1264.          currentMoveString[4] != ToLower(promoChar))) {
  1265.         /* Illegal move; use coordinate notation */
  1266.         outp = out;
  1267.         *outp++ = fromX + 'a';
  1268.         *outp++ = fromY + '1';
  1269.         *outp++ = toX + 'a';
  1270.         *outp++ = toY + '1';
  1271.         *outp++ = ToUpper(promoChar);
  1272.         *outp = NULLCHAR;
  1273.         return BadMove;
  1274.     }
  1275.     else
  1276.       return ret;
  1277.     
  1278.       default:
  1279.     /* Illegal move; use coordinate notation */
  1280.     outp = out;
  1281.     *outp++ = fromX + 'a';
  1282.     *outp++ = fromY + '1';
  1283.     *outp++ = toX + 'a';
  1284.     *outp++ = toY + '1';
  1285.     *outp++ = ToUpper(promoChar);
  1286.     *outp = NULLCHAR;
  1287.     return BadMove;
  1288.     }
  1289. }
  1290.  
  1291.  
  1292. void InitPosition(redraw)
  1293.      int redraw;
  1294. {
  1295.     currentMove = forwardMostMove = backwardMostMove = 0;
  1296.     CopyBoard(boards[0], initialPosition);
  1297.     if (redraw)
  1298.       DrawPosition(FALSE, boards[currentMove]);
  1299. }
  1300.  
  1301. void CopyBoard(to, from)
  1302.      Board to, from;
  1303. {
  1304.     int i, j;
  1305.     
  1306.     for (i = 0; i < BOARD_SIZE; i++)
  1307.       for (j = 0; j < BOARD_SIZE; j++)
  1308.     to[i][j] = from[i][j];
  1309. }
  1310.  
  1311. Boolean CompareBoards(board1, board2)
  1312.      Board board1, board2;
  1313. {
  1314.     int i, j;
  1315.     
  1316.     for (i = 0; i < BOARD_SIZE; i++)
  1317.       for (j = 0; j < BOARD_SIZE; j++) {
  1318.       if (board1[i][j] != board2[i][j])
  1319.         return FALSE;
  1320.     }
  1321.     return TRUE;
  1322. }
  1323.  
  1324. void SendCurrentBoard(pr)
  1325.      ProcRef pr;
  1326. {
  1327.     SendBoard(pr, boards[currentMove]);
  1328. }
  1329.  
  1330. void SendBoard(pr, board)
  1331.      ProcRef pr;
  1332.      Board board;
  1333. {
  1334.     char message[MSG_SIZ];
  1335.     ChessSquare *bp;
  1336.     int i, j;
  1337.     
  1338.     SendToProgram("edit\n", pr);
  1339.     SendToProgram("#\n", pr);
  1340.     for (i = BOARD_SIZE - 1; i >= 0; i--) {
  1341.     bp = &board[i][0];
  1342.     for (j = 0; j < BOARD_SIZE; j++, bp++) {
  1343.         if ((int) *bp < (int) BlackPawn) {
  1344.         sprintf(message, "%c%c%c\n", PieceToChar(*bp), 
  1345.             'a' + j, '1' + i);
  1346.         SendToProgram(message, pr);
  1347.         }
  1348.     }
  1349.     }
  1350.     
  1351.     SendToProgram("c\n", pr);
  1352.     for (i = BOARD_SIZE - 1; i >= 0; i--) {
  1353.     bp = &board[i][0];
  1354.     for (j = 0; j < BOARD_SIZE; j++, bp++) {
  1355.         if (((int) *bp != (int) EmptySquare)
  1356.         && ((int) *bp >= (int) BlackPawn)) {
  1357.         sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
  1358.             'a' + j, '1' + i);
  1359.         SendToProgram(message, pr);
  1360.         }
  1361.     }
  1362.     }
  1363.     
  1364.     SendToProgram(".\n", pr);
  1365. }
  1366.  
  1367.  
  1368. int IsPromotion(fromX, fromY, toX, toY)
  1369.      int fromX, fromY, toX, toY;
  1370. {
  1371.     return gameMode != EditPosition &&
  1372.       fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0 &&
  1373.     ((boards[currentMove][fromY][fromX] == WhitePawn && toY == 7) ||
  1374.      (boards[currentMove][fromY][fromX] == BlackPawn && toY == 0));
  1375. }
  1376.  
  1377.  
  1378. int OKToStartUserMove(x, y)
  1379.      int x, y;
  1380. {
  1381.     ChessSquare from_piece;
  1382.     int white_piece;
  1383.  
  1384.     if (matchMode) return FALSE;
  1385.  
  1386.     if (x >= 0 && y >= 0)
  1387.       from_piece = boards[currentMove][y][x];
  1388.     else
  1389.       from_piece = EmptySquare;
  1390.  
  1391.     if (gameMode == EditPosition) return TRUE;
  1392.     if (from_piece == EmptySquare) return FALSE;
  1393.  
  1394.     white_piece = (int)from_piece >= (int)WhitePawn &&
  1395.                   (int)from_piece <= (int)WhiteKing;
  1396.  
  1397.     switch (gameMode) {
  1398.       case EndOfGame:
  1399.       case PlayFromGameFile:
  1400.       case TwoMachinesPlay:
  1401.     return FALSE;
  1402.  
  1403.       case IcsObserving:
  1404.       case IcsIdle:
  1405.     return FALSE;
  1406.  
  1407.       case MachinePlaysWhite:
  1408.       case IcsPlayingBlack:
  1409.     if (white_piece) {
  1410.         DisplayError("You are playing Black", 0);
  1411.         return FALSE;
  1412.     }
  1413.     break;
  1414.  
  1415.       case IcsPlayingWhite:
  1416.       case MachinePlaysBlack:
  1417.     if (!white_piece) {
  1418.         DisplayError("You are playing White", 0);
  1419.         return FALSE;
  1420.     }
  1421.     break;
  1422.  
  1423.       case ForceMoves:
  1424.     /*!!Prompt for confirmation if discarding moves?*/
  1425.     forwardMostMove = currentMove;
  1426.     break;
  1427.  
  1428.       case BeginningOfGame:
  1429.     if (appData.icsActive) return FALSE;
  1430.     if (!appData.noChessProgram) {
  1431.         if (!white_piece) {
  1432.         DisplayError("You are playing White", 0);
  1433.         return FALSE;
  1434.         }
  1435.     }
  1436.     break;
  1437.  
  1438.       default:
  1439.     break;
  1440.     }
  1441.     if (currentMove != forwardMostMove) {
  1442.     DisplayError("Displayed position is not current", 0);
  1443.     return FALSE;
  1444.     }
  1445.     return TRUE;
  1446. }
  1447.  
  1448. void UserMoveEvent(fromX, fromY, toX, toY, promoChar)
  1449.      int fromX, fromY, toX, toY;
  1450.      int promoChar;
  1451. {
  1452.     ChessMove moveType;
  1453.     char user_move[MSG_SIZ];
  1454.     
  1455.     if (fromX < 0 || fromY < 0) return;
  1456.     if ((fromX == toX) && (fromY == toY)) {
  1457.     return;
  1458.     }
  1459.     
  1460.     if (toX < 0 || toY < 0) {
  1461.     if (gameMode == EditPosition && (toX == -2 || toY == -2)) {
  1462.         boards[0][fromY][fromX] = EmptySquare;
  1463.         DrawPosition(FALSE, boards[currentMove]);
  1464.     }
  1465.     return;
  1466.     }
  1467.     
  1468.     if (gameMode == EditPosition) {
  1469.     boards[0][toY][toX] = boards[0][fromY][fromX];
  1470.     boards[0][fromY][fromX] = EmptySquare;
  1471.     DrawPosition(FALSE, boards[currentMove]);
  1472.     return;
  1473.     }
  1474.     
  1475.     /* Check if the user is playing in turn.  This is complicated because we
  1476.        let the user "pick up" a piece before it is his turn.  So the piece he
  1477.        tried to pick up may have been captured by the time he puts it down!
  1478.        Therefore we use the color the user is supposed to be playing in this
  1479.        test, not the color of the piece that is currently on the starting
  1480.        square---except in ForceMoves mode, where the user is playing both
  1481.        sides; fortunately there the capture race can't happen. 
  1482.     */
  1483.     if (gameMode == MachinePlaysWhite || gameMode == IcsPlayingBlack ||
  1484.     ((gameMode == ForceMoves || gameMode == BeginningOfGame) && 
  1485.      (int) boards[currentMove][fromY][fromX] >= (int) BlackPawn &&
  1486.      (int) boards[currentMove][fromY][fromX] <= (int) BlackKing)) {
  1487.     /* User is moving for Black */
  1488.     if (WhiteOnMove(currentMove)) {
  1489.         DisplayError("It is White's turn", 0);
  1490.         return;
  1491.     }
  1492.     } else {
  1493.     /* User is moving for White */
  1494.     if (!WhiteOnMove(currentMove)) {
  1495.         DisplayError("It is Black's turn", 0);
  1496.         return;
  1497.     }
  1498.     }
  1499.  
  1500.     moveType = LegalityTest(WhiteOnMove(currentMove),
  1501.                  boards[currentMove], fromY, fromX,
  1502.                  toY, toX, promoChar);
  1503.  
  1504.     if (moveType == BadMove) {
  1505.     DisplayError("Illegal move", 0);
  1506.     return;
  1507.     }
  1508.  
  1509.     /* A user move restarts a paused game*/
  1510.     if (pausing)
  1511.       PauseEvent();
  1512.     
  1513.     MakeMove(&moveType, fromX, fromY, toX, toY);
  1514.  
  1515.     if (appData.icsActive) {
  1516.     if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) {
  1517.         sprintf(user_move, "%c%c%c%c\n",
  1518.             'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY);
  1519.         switch (moveType) {
  1520.           default:
  1521.         DisplayError("Internal error; bad moveType", 0);
  1522.         break;
  1523.           case WhiteKingSideCastle:
  1524.           case BlackKingSideCastle:
  1525.           case WhiteQueenSideCastleWild:
  1526.           case BlackQueenSideCastleWild:
  1527.         sprintf(user_move, "o-o\n");
  1528.         break;
  1529.           case WhiteQueenSideCastle:
  1530.           case BlackQueenSideCastle:
  1531.           case WhiteKingSideCastleWild:
  1532.           case BlackKingSideCastleWild:
  1533.         sprintf(user_move, "o-o-o\n");
  1534.         break;
  1535.           case WhitePromotionQueen:
  1536.           case BlackPromotionQueen:
  1537.         SendToICS("promote queen\n");
  1538.         break;
  1539.           case WhitePromotionRook:
  1540.           case BlackPromotionRook:
  1541.         SendToICS("promote rook\n");
  1542.         break;
  1543.           case WhitePromotionBishop:
  1544.           case BlackPromotionBishop:
  1545.         SendToICS("promote bishop\n");
  1546.         break;
  1547.           case WhitePromotionKnight:
  1548.           case BlackPromotionKnight:
  1549.         SendToICS("promote knight\n");
  1550.         break;
  1551.           case NormalMove:
  1552.           case WhiteCapturesEnPassant:
  1553.           case BlackCapturesEnPassant:
  1554.         break;
  1555.         }
  1556.         SendToICS(user_move);
  1557.         ics_user_moved = 1;
  1558.     }
  1559.     } else {
  1560.     promoChar = ToLower(PieceToChar(PromoPiece(moveType)));
  1561.     if (promoChar == '.')
  1562.       sprintf(user_move, "%c%c%c%c\n",
  1563.           'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY);
  1564.     else
  1565.       sprintf(user_move, "%c%c%c%c%c\n",
  1566.           'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY,
  1567.           promoChar);
  1568.     
  1569.     Attention(firstProgramPR);
  1570.     if (firstSendTime)
  1571.       SendTimeRemaining(firstProgramPR);
  1572.     SendToProgram(user_move, firstProgramPR);
  1573.     }
  1574.     
  1575.     strcpy(moveList[currentMove - 1], user_move);
  1576.     
  1577.     switch (gameMode) {
  1578.       case PlayFromGameFile:
  1579.     ForceMovesEvent();
  1580.     break;
  1581.       case BeginningOfGame:
  1582.     if (appData.noChessProgram)
  1583.       lastGameMode = gameMode = ForceMoves;
  1584.     else
  1585.       lastGameMode = gameMode = MachinePlaysBlack;
  1586.     ModeHighlight();
  1587.     break;
  1588.       case ForceMoves:
  1589.       case MachinePlaysBlack:
  1590.       case MachinePlaysWhite:
  1591.       default:
  1592.     break;
  1593.     }
  1594. }
  1595.  
  1596. /* Parser for moves from gnuchess or ICS */
  1597. Boolean ParseMachineMove(machineMove, moveNum,
  1598.              moveType, fromX, fromY, toX, toY, promoChar)
  1599.      char *machineMove;
  1600.      int moveNum;
  1601.      ChessMove *moveType;
  1602.      int *fromX, *fromY, *toX, *toY;
  1603.      char *promoChar;
  1604. {       
  1605.     char buf[MSG_SIZ];
  1606.  
  1607.     *moveType = yylexstr(moveNum, machineMove);
  1608.     switch (*moveType) {
  1609.       case WhitePromotionQueen:
  1610.       case BlackPromotionQueen:
  1611.       case WhitePromotionRook:
  1612.       case BlackPromotionRook:
  1613.       case WhitePromotionBishop:
  1614.       case BlackPromotionBishop:
  1615.       case WhitePromotionKnight:
  1616.       case BlackPromotionKnight:
  1617.       case NormalMove:
  1618.       case WhiteCapturesEnPassant:
  1619.       case BlackCapturesEnPassant:
  1620.       case WhiteKingSideCastle:
  1621.       case WhiteQueenSideCastle:
  1622.       case BlackKingSideCastle:
  1623.       case BlackQueenSideCastle:
  1624.       case WhiteKingSideCastleWild:
  1625.       case WhiteQueenSideCastleWild:
  1626.       case BlackKingSideCastleWild:
  1627.       case BlackQueenSideCastleWild:
  1628.     *fromX = currentMoveString[0] - 'a';
  1629.     *fromY = currentMoveString[1] - '1';
  1630.     *toX = currentMoveString[2] - 'a';
  1631.     *toY = currentMoveString[3] - '1';
  1632.     *promoChar = currentMoveString[4];
  1633.     return TRUE;
  1634.  
  1635.       case BadMove:
  1636.       case AmbiguousMove:
  1637.       case (ChessMove) 0:    /* end of file */
  1638.       case ElapsedTime:
  1639.       case Comment:
  1640.       case WhiteWins:
  1641.       case BlackWins:
  1642.       case GameIsDrawn:
  1643.       default:
  1644.     /* bug? */
  1645.     sprintf(buf, "Bad move in chess program output: %s",
  1646.         machineMove);
  1647.     DisplayError(buf, 0);
  1648.     *fromX = *fromY = *toX = *toY = 0;
  1649.     return FALSE;
  1650.     }
  1651. }
  1652.  
  1653. void HandleMachineMove(message, isr)
  1654.      char *message;
  1655.      InputSourceRef isr;
  1656. {
  1657.     char machineMove[MSG_SIZ], buf1[MSG_SIZ], buf2[MSG_SIZ];
  1658.     int fromX, fromY, toX, toY;
  1659.     ChessMove moveType;
  1660.     char promoChar;
  1661.     
  1662.     maybeThinking = FALSE;
  1663.     
  1664.     if (strncmp(message, "warning:", 8) == 0) {
  1665.     DisplayError(message, 0);
  1666.     return;
  1667.     }
  1668.     
  1669.     /*
  1670.      * If chess program startup fails, exit with an error message.
  1671.      * Attempts to recover here are futile.
  1672.      */
  1673.     if ((StrStr(message, "unknown host") != NULL)
  1674.     || (StrStr(message, "No remote directory") != NULL)
  1675.     || (StrStr(message, "not found") != NULL)
  1676.     || (StrStr(message, "No such file") != NULL)
  1677.     || (StrStr(message, "Permission denied") != NULL)) {
  1678.     sprintf(buf1,
  1679.         "Failed to start chess program %s on %s: %s\n",
  1680.         isr == firstProgramISR ? appData.firstChessProgram
  1681.         : appData.secondChessProgram,
  1682.         isr == firstProgramISR ? appData.firstHost
  1683.         : appData.secondHost,
  1684.         message);
  1685.     DisplayFatalError(buf1, 0);
  1686.     ExitEvent(1);
  1687.     }
  1688.     
  1689.     if (strncmp(message, "time", 4) == 0) {
  1690.     if (StrStr(message, "CHESS")) {
  1691.         /* Program has a broken "time" command that
  1692.            outputs a string not ending in newline.
  1693.            Don't use it. */
  1694.         if (isr == firstProgramISR) firstSendTime = 0;
  1695.         if (isr == secondProgramISR) secondSendTime = 0;
  1696.     } else {
  1697.         if (isr == firstProgramISR) firstSendTime = 1;
  1698.         if (isr == secondProgramISR) secondSendTime = 1;
  1699.     }
  1700.     }
  1701.     
  1702.     /*
  1703.      * If the move is illegal, cancel it and redraw the board.
  1704.      */
  1705.     if (strncmp(message, "Illegal move", 12) == 0) {
  1706.     
  1707.     if (isr == firstProgramISR && firstSendTime == 2) {
  1708.         /* First program doesn't have the "time" command */
  1709.         firstSendTime = 0;
  1710.         return;
  1711.     } else if (isr == secondProgramISR && secondSendTime == 2) {
  1712.         /* Second program doesn't have the "time" command */
  1713.         secondSendTime = 0;
  1714.         return;
  1715.     }
  1716.     if (forwardMostMove <= backwardMostMove) return;
  1717.     if (pausing) PauseEvent();
  1718.     if (gameMode == PlayFromGameFile) {
  1719.         /* Stop reading this game file */
  1720.         gameMode = ForceMoves;
  1721.         ModeHighlight();
  1722.     }
  1723.     currentMove = --forwardMostMove;
  1724.     SwitchClocks();
  1725.     sprintf(buf1, "Illegal move: %s", parseList[currentMove]);
  1726.     DisplayError(buf1, 0);
  1727.     
  1728.     DrawPosition(FALSE, boards[currentMove]);
  1729.     return;
  1730.     }
  1731.     
  1732.     if (strncmp(message, "Hint:", 5) == 0) {
  1733.     sscanf(message, "Hint: %s", machineMove);
  1734.     if (ParseMachineMove(machineMove, forwardMostMove, &moveType,
  1735.                  &fromX, &fromY, &toX, &toY, &promoChar)) {
  1736.         moveType = CoordsToAlgebraic(fromX, fromY, toX, toY, promoChar,
  1737.                      forwardMostMove, buf1);
  1738.         sprintf(buf2, "Hint: %s", buf1);
  1739.         DisplayMessage(buf2);
  1740.     } else {
  1741.         /* Hint move was illegal!? */
  1742.         DisplayMessage(message);
  1743.     }
  1744.     return;
  1745.     }
  1746.     
  1747.     /*
  1748.      * win, lose or draw
  1749.      */
  1750.     if (strncmp(message, "White", 5) == 0) {
  1751.     ShutdownChessPrograms("White mates");
  1752.     return;
  1753.     } else if (strncmp(message, "Black", 5) == 0) {
  1754.     ShutdownChessPrograms("Black mates");
  1755.     return;
  1756.     } else if (strncmp(message, "opponent mates!", 15) == 0) {
  1757.     switch (gameMode) {
  1758.       case MachinePlaysBlack:
  1759.         ShutdownChessPrograms("White mates");
  1760.         break;
  1761.       case MachinePlaysWhite:
  1762.         ShutdownChessPrograms("Black mates");
  1763.         break;
  1764.       case TwoMachinesPlay:
  1765.         ShutdownChessPrograms(isr == firstProgramISR ?
  1766.                   "White mates" : "Black mates");
  1767.         break;
  1768.       default:
  1769.         /* can't happen */
  1770.         break;
  1771.     }
  1772.     return;
  1773.     } else if (strncmp(message, "computer mates!", 15) == 0) {
  1774.     switch (gameMode) {
  1775.       case MachinePlaysBlack:
  1776.         ShutdownChessPrograms("Black mates");
  1777.         break;
  1778.       case MachinePlaysWhite:
  1779.         ShutdownChessPrograms("White mates");
  1780.         break;
  1781.       case TwoMachinesPlay:
  1782.         ShutdownChessPrograms(isr == firstProgramISR ?
  1783.                   "Black mates" : "White mates");
  1784.         break;
  1785.       default:
  1786.         /* can't happen */
  1787.         break;
  1788.     }
  1789.     return;
  1790.     } else if (strncmp(message, "Draw", 4) == 0) {
  1791.     ShutdownChessPrograms("Draw");
  1792.     return;
  1793.     }
  1794.     
  1795.     /*
  1796.      * normal machine reply move
  1797.      */
  1798.     maybeThinking = TRUE;
  1799.     if (StrStr(message, "...") != NULL) {
  1800.     sscanf(message, "%s %s %s", buf1, buf2, machineMove);
  1801.     if (machineMove[0] == NULLCHAR)
  1802.       return;
  1803.     } else
  1804.       return;        /* ignore noise */
  1805.     
  1806.     strcat(machineMove, "\n");
  1807.     strcpy(moveList[forwardMostMove], machineMove);
  1808.     
  1809.     if (!ParseMachineMove(machineMove, forwardMostMove, &moveType,
  1810.               &fromX, &fromY, &toX, &toY, &promoChar)) {
  1811.     /* Machine move was illegal!?  What to do? */
  1812.     DisplayError("Ignoring illegal move from machine", 0);
  1813.     return;
  1814.     }
  1815.     
  1816.     if (!pausing)
  1817.       currentMove = forwardMostMove;  /*display latest move*/
  1818.     
  1819.     MakeMove(&moveType, fromX, fromY, toX, toY);
  1820.     
  1821.     if (!pausing && appData.ringBellAfterMoves)
  1822.       RingBell();
  1823.     
  1824.     if (gameMode == TwoMachinesPlay) {
  1825.     if (WhiteOnMove(forwardMostMove)) {
  1826.         Attention(secondProgramPR);
  1827.         if (secondSendTime) 
  1828.           SendTimeRemaining(secondProgramPR);
  1829.         SendToProgram(machineMove, secondProgramPR);
  1830.         if (firstMove) {
  1831.         firstMove = FALSE;
  1832.         SendToProgram(appData.whiteString,
  1833.                   secondProgramPR);
  1834.         }
  1835.     } else {
  1836.         Attention(firstProgramPR);
  1837.         if (firstSendTime)
  1838.           SendTimeRemaining(firstProgramPR);
  1839.         SendToProgram(machineMove, firstProgramPR);
  1840.         if (firstMove) {
  1841.         firstMove = FALSE;
  1842.         SendToProgram(appData.blackString,
  1843.                   firstProgramPR);
  1844.         }
  1845.     }
  1846.     }
  1847. }
  1848.  
  1849.  
  1850. /* Parse a game score from the character string "game", and
  1851.    record it as the history of the current game.  The game
  1852.    score is NOT assumed to start from the standard position. 
  1853.    The display is not updated in any way.
  1854.    */
  1855. void ParseGameHistory(game)
  1856.      char *game;
  1857. {
  1858.     ChessMove moveType;
  1859.     int fromX, fromY, toX, toY, boardIndex;
  1860.     char promoChar;
  1861.     char *p;
  1862.     char buf[MSG_SIZ];
  1863.  
  1864.     if (appData.debugMode)
  1865.       fprintf(debugFP, "Parsing game history: %s\n", game);
  1866.  
  1867.     /* Parse out names of players */
  1868.     while (*game == ' ') game++;
  1869.     p = ics_white;
  1870.     while (*game != ' ') *p++ = *game++;
  1871.     *p = NULLCHAR;
  1872.     while (*game == ' ') game++;
  1873.     p = ics_black;
  1874.     while (*game != ' ' && *game != '\n') *p++ = *game++;
  1875.     *p = NULLCHAR;
  1876.  
  1877.     /* Parse moves */
  1878.     boardIndex = 0;
  1879.     yynewstr(game);
  1880.     for (;;) {
  1881.     yyboardindex = boardIndex;
  1882.     moveType = (ChessMove) yylex();
  1883.     switch (moveType) {
  1884.       case WhitePromotionQueen:
  1885.       case BlackPromotionQueen:
  1886.       case WhitePromotionRook:
  1887.       case BlackPromotionRook:
  1888.       case WhitePromotionBishop:
  1889.       case BlackPromotionBishop:
  1890.       case WhitePromotionKnight:
  1891.       case BlackPromotionKnight:
  1892.       case NormalMove:
  1893.       case WhiteCapturesEnPassant:
  1894.       case BlackCapturesEnPassant:
  1895.       case WhiteKingSideCastle:
  1896.       case WhiteQueenSideCastle:
  1897.       case BlackKingSideCastle:
  1898.       case BlackQueenSideCastle:
  1899.       case WhiteKingSideCastleWild:
  1900.       case WhiteQueenSideCastleWild:
  1901.       case BlackKingSideCastleWild:
  1902.       case BlackQueenSideCastleWild:
  1903.         fromX = currentMoveString[0] - 'a';
  1904.         fromY = currentMoveString[1] - '1';
  1905.         toX = currentMoveString[2] - 'a';
  1906.         toY = currentMoveString[3] - '1';
  1907.         promoChar = currentMoveString[4];
  1908.         break;
  1909.       case BadMove:
  1910.       case AmbiguousMove:
  1911.         /* bug? */
  1912.         sprintf(buf, "Bad move in ICS output: %s", yytext);
  1913.         DisplayError(buf, 0);
  1914.         /* fall thru */
  1915.       case (ChessMove) 0:    /* end of file */
  1916.         if (boardIndex < backwardMostMove) {
  1917.         /* Oops, gap.  How did that happen? */
  1918.         return;
  1919.         }
  1920.         backwardMostMove = 0;
  1921.         if (boardIndex > forwardMostMove) {
  1922.         forwardMostMove = boardIndex;
  1923.         }
  1924.         return;
  1925.       case ElapsedTime:
  1926.         if (boardIndex > 0) {
  1927.         strcat(parseList[boardIndex-1], " ");
  1928.         strcat(parseList[boardIndex-1], yytext);
  1929.         }
  1930.         continue;
  1931.       case Comment:
  1932.       default:
  1933.         /* ignore */
  1934.         continue;
  1935.       case WhiteWins:
  1936.       case BlackWins:
  1937.       case GameIsDrawn:
  1938.         strncpy(endMessage, yytext, MOVE_LEN * 4);
  1939.         endMessage[MOVE_LEN * 4 - 1] = NULLCHAR;
  1940.         continue;
  1941.     }
  1942.     (void) CoordsToAlgebraic(fromX, fromY, toX, toY, promoChar,
  1943.                  boardIndex, parseList[boardIndex]);
  1944.     CopyBoard(boards[boardIndex + 1], boards[boardIndex]);
  1945.     boardIndex++;
  1946.     ApplyMove(&moveType, fromX, fromY, toX, toY,
  1947.           boards[boardIndex]);
  1948.     }
  1949. }
  1950.  
  1951. void LoadGameLoop()
  1952. {
  1953.     for (;;) {
  1954.     if (!LoadGameOneMove())
  1955.       return;
  1956.     if (matchMode || appData.timeDelay == 0)
  1957.       continue;
  1958.     if (appData.timeDelay < 0)
  1959.       return;
  1960.         StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
  1961.     break;
  1962.     }
  1963. }
  1964.  
  1965. int LoadGameOneMove()
  1966. {
  1967.     int fromX, fromY, toX, toY, done;
  1968.     ChessMove moveType;
  1969.     char move[MSG_SIZ];
  1970.     
  1971.     if (gameFileFP == NULL)
  1972.       return FALSE;
  1973.     
  1974.     if (gameMode != PlayFromGameFile) {
  1975.     gameFileFP = NULL;
  1976.     return FALSE;
  1977.     }
  1978.     
  1979.     yyboardindex = forwardMostMove;
  1980.     moveType = (ChessMove) yylex();
  1981.     
  1982.     done = FALSE;
  1983.     switch (moveType) {
  1984.       case Comment:
  1985.     if (appData.debugMode) 
  1986.       fprintf(debugFP, "Parsed Comment: %s\n", yytext);
  1987.     if (!matchMode && (pausing || appData.timeDelay != 0)) {
  1988.         CommentPopUp(yytext);
  1989.     }
  1990.     AppendComment(currentMove, yytext);
  1991.     return TRUE;
  1992.  
  1993.       case WhiteCapturesEnPassant:
  1994.       case BlackCapturesEnPassant:
  1995.       case WhitePromotionQueen:
  1996.       case BlackPromotionQueen:
  1997.       case WhitePromotionRook:
  1998.       case BlackPromotionRook:
  1999.       case WhitePromotionBishop:
  2000.       case BlackPromotionBishop:
  2001.       case WhitePromotionKnight:
  2002.       case BlackPromotionKnight:
  2003.       case NormalMove:
  2004.       case WhiteKingSideCastle:
  2005.       case WhiteQueenSideCastle:
  2006.       case BlackKingSideCastle:
  2007.       case BlackQueenSideCastle:
  2008.       case WhiteKingSideCastleWild:
  2009.       case WhiteQueenSideCastleWild:
  2010.       case BlackKingSideCastleWild:
  2011.       case BlackQueenSideCastleWild:
  2012.     if (appData.debugMode)
  2013.       fprintf(debugFP, "Parsed %s into %s\n", yytext, currentMoveString);
  2014.     fromX = currentMoveString[0] - 'a';
  2015.     fromY = currentMoveString[1] - '1';
  2016.     toX = currentMoveString[2] - 'a';
  2017.     toY = currentMoveString[3] - '1';
  2018.     break;
  2019.  
  2020.       case (ChessMove) 0:  /* end of file */
  2021.     if (appData.debugMode)
  2022.       fprintf(debugFP, "Parser hit end of file\n");
  2023.     DisplayMessage("End of game file");
  2024.     done = TRUE;
  2025.     break;
  2026.  
  2027.       case WhiteWins:
  2028.       case BlackWins:
  2029.       case GameIsDrawn:
  2030.     if (appData.debugMode)
  2031.       fprintf(debugFP, "Parsed game end: %s\n", yytext);
  2032.     if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {
  2033.         DrawPosition(FALSE, boards[currentMove]);
  2034.         if (!appData.matchMode && commentList[currentMove] != NULL)
  2035.           CommentPopUp(commentList[currentMove]);
  2036.     }
  2037.     GameEnds(yytext);
  2038.     return FALSE;
  2039.  
  2040.       case XBoardGame:
  2041.       case GNUChessGame:
  2042.     /* Reached start of next game in file */
  2043.     if (appData.debugMode)
  2044.       fprintf(debugFP, "Parsed start of next game: %s\n", yytext);
  2045.     DisplayMessage("End of game");
  2046.     done = TRUE;
  2047.     break;
  2048.  
  2049.       case PositionDiagram:
  2050.     /* should not happen; ignore */
  2051.       case MoveNumberOne:
  2052.     /* ignore; we see too many bogus move numbers,
  2053.        especially in GNUChess game files */
  2054.       case ElapsedTime:
  2055.     /* ignore */
  2056.     if (appData.debugMode)
  2057.       fprintf(debugFP, "Parser ignoring: '%s' (%d)\n",
  2058.           yytext, (int) moveType);
  2059.     return LoadGameOneMove(); /* tail recursion */
  2060.  
  2061.       default:
  2062.       case BadMove:
  2063.     if (appData.debugMode)
  2064.       fprintf(debugFP, "Parsed BadMove: %s\n", yytext);
  2065.     sprintf(move, "Bad move: %d. %s%s",
  2066.         (forwardMostMove / 2) + 1,
  2067.         WhiteOnMove(forwardMostMove) ? "" : "... ", yytext);
  2068.     DisplayError(move, 0);
  2069.     done = TRUE;
  2070.     break;
  2071.  
  2072.       case AmbiguousMove:
  2073.     if (appData.debugMode)
  2074.       fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yytext);
  2075.     sprintf(move, "Ambiguous move: %d. %s%s",
  2076.         (forwardMostMove / 2) + 1,
  2077.         WhiteOnMove(forwardMostMove) ? "" : "... ", yytext);
  2078.     DisplayError(move, 0);
  2079.     done = TRUE;
  2080.     break;
  2081.     }
  2082.     
  2083.     if (done) {
  2084.     if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {
  2085.         DrawPosition(FALSE, boards[currentMove]);
  2086.         if (!appData.matchMode && commentList[currentMove] != NULL)
  2087.           CommentPopUp(commentList[currentMove]);
  2088.     }
  2089.     lastGameMode = gameMode;
  2090.     gameMode = ForceMoves;
  2091.     ModeHighlight();
  2092.         (void) StopLoadGameTimer();
  2093.     gameFileFP = NULL;
  2094.     return FALSE;
  2095.     } else {
  2096.     strcat(currentMoveString, "\n");
  2097.     strcpy(moveList[forwardMostMove], currentMoveString);
  2098.     SendToProgram(currentMoveString, firstProgramPR);
  2099.     
  2100.     MakeMove(&moveType, fromX, fromY, toX, toY);
  2101.     
  2102.     return TRUE;
  2103.     }
  2104. }
  2105.  
  2106. /* Apply a move to the given board.  Oddity: moveType is ignored on
  2107.    input unless the move is seen to be a pawn promotion, in which case
  2108.    moveType tells us what to promote to.
  2109. */
  2110. void ApplyMove(moveType, fromX, fromY, toX, toY, board)
  2111.      ChessMove *moveType;
  2112.      int fromX, fromY, toX, toY;
  2113.      Board board;
  2114. {
  2115.     if (fromY == 0 && fromX == 4
  2116.     && board[fromY][fromX] == WhiteKing
  2117.     && toY == 0 && toX == 6) {
  2118.     *moveType = WhiteKingSideCastle;
  2119.      board[fromY][fromX] = EmptySquare;
  2120.      board[toY][toX] = WhiteKing;
  2121.      board[fromY][7] = EmptySquare;
  2122.      board[toY][5] = WhiteRook;
  2123.     } else if (fromY == 0 && fromX == 4
  2124.            && board[fromY][fromX] == WhiteKing
  2125.            && toY == 0 && toX == 2) {
  2126.     *moveType = WhiteQueenSideCastle;
  2127.      board[fromY][fromX] = EmptySquare;
  2128.      board[toY][toX] = WhiteKing;
  2129.      board[fromY][0] = EmptySquare;
  2130.      board[toY][3] = WhiteRook;
  2131.     } else if (fromY == 0 && fromX == 3
  2132.      && board[fromY][fromX] == WhiteKing
  2133.      && toY == 0 && toX == 5) {
  2134.      *moveType = WhiteKingSideCastleWild;
  2135.      board[fromY][fromX] = EmptySquare;
  2136.      board[toY][toX] = WhiteKing;
  2137.      board[fromY][7] = EmptySquare;
  2138.      board[toY][4] = WhiteRook;
  2139.     } else if (fromY == 0 && fromX == 3
  2140.             && board[fromY][fromX] == WhiteKing
  2141.             && toY == 0 && toX == 1) {
  2142.      *moveType = WhiteQueenSideCastleWild;
  2143.      board[fromY][fromX] = EmptySquare;
  2144.      board[toY][toX] = WhiteKing;
  2145.      board[fromY][0] = EmptySquare;
  2146.      board[toY][2] = WhiteRook;
  2147.     } else if (fromY == 6
  2148.            && board[fromY][fromX] == WhitePawn
  2149.            && toY == 7) {
  2150.     /* white pawn promotion */
  2151.     board[7][toX] = PromoPiece(*moveType);
  2152.     if (board[7][toX] == EmptySquare) {
  2153.         board[7][toX] = WhiteQueen;
  2154.         *moveType = WhitePromotionQueen;
  2155.     }
  2156.     board[6][fromX] = EmptySquare;
  2157.     } else if ((fromY == 4)
  2158.            && (toX != fromX)
  2159.            && (board[fromY][fromX] == WhitePawn)
  2160.            && (board[toY][toX] == EmptySquare)) {
  2161.     *moveType = WhiteCapturesEnPassant;
  2162.     board[fromY][fromX] = EmptySquare;
  2163.     board[toY][toX] = WhitePawn;
  2164.     board[toY - 1][toX] = EmptySquare;
  2165.    } else if (fromY == 7 && fromX == 4
  2166.            && board[fromY][fromX] == BlackKing
  2167.            && toY == 7 && toX == 6) {
  2168.     *moveType = BlackKingSideCastle;
  2169.      board[fromY][fromX] = EmptySquare;
  2170.      board[toY][toX] = BlackKing;
  2171.      board[fromY][7] = EmptySquare;
  2172.      board[toY][5] = BlackRook;
  2173.    } else if (fromY == 7 && fromX == 4
  2174.            && board[fromY][fromX] == BlackKing
  2175.            && toY == 7 && toX == 2) {
  2176.     *moveType = BlackQueenSideCastle;
  2177.      board[fromY][fromX] = EmptySquare;
  2178.      board[toY][toX] = BlackKing;
  2179.      board[fromY][0] = EmptySquare;
  2180.      board[toY][3] = BlackRook;
  2181.     } else if (fromY == 7 && fromX == 3
  2182.             && board[fromY][fromX] == BlackKing
  2183.             && toY == 7 && toX == 5) {
  2184.      *moveType = BlackKingSideCastleWild;
  2185.      board[fromY][fromX] = EmptySquare;
  2186.      board[toY][toX] = BlackKing;
  2187.      board[fromY][7] = EmptySquare;
  2188.      board[toY][4] = BlackRook;
  2189.     } else if (fromY == 7 && fromX == 3
  2190.             && board[fromY][fromX] == BlackKing
  2191.             && toY == 7 && toX == 1) {
  2192.      *moveType = BlackQueenSideCastleWild;
  2193.      board[fromY][fromX] = EmptySquare;
  2194.      board[toY][toX] = BlackKing;
  2195.      board[fromY][0] = EmptySquare;
  2196.      board[toY][2] = BlackRook;
  2197.     } else if (fromY == 1
  2198.            && board[fromY][fromX] == BlackPawn
  2199.            && toY == 0) {
  2200.     /* black pawn promotion */
  2201.     board[0][toX] = PromoPiece(*moveType);
  2202.     if (board[0][toX] == EmptySquare) {
  2203.         board[0][toX] = BlackQueen;
  2204.         *moveType = BlackPromotionQueen;
  2205.     }
  2206.     board[1][fromX] = EmptySquare;
  2207.     } else if ((fromY == 3)
  2208.            && (toX != fromX)
  2209.            && (board[fromY][fromX] == BlackPawn)
  2210.            && (board[toY][toX] == EmptySquare)) {
  2211.     *moveType = BlackCapturesEnPassant;
  2212.     board[fromY][fromX] = EmptySquare;
  2213.     board[toY][toX] = BlackPawn;
  2214.     board[toY + 1][toX] = EmptySquare;
  2215.     } else {
  2216.     *moveType = NormalMove;
  2217.     board[toY][toX] = board[fromY][fromX];
  2218.     board[fromY][fromX] = EmptySquare;
  2219.     }
  2220. }
  2221.  
  2222. /*
  2223.  * MakeMove() displays moves.  If they are illegal, GNU chess will detect
  2224.  * this and send an Illegal move message.  XBoard will then retract the move.
  2225.  *
  2226.  * Oddity: moveType is ignored on input unless the move is seen to be a
  2227.  * pawn promotion, in which case moveType tells us what to promote to.
  2228.  */
  2229. void MakeMove(moveType, fromX, fromY, toX, toY)
  2230.      ChessMove *moveType;
  2231.      int fromX, fromY, toX, toY;
  2232. {
  2233.     CommentPopDown();
  2234.     forwardMostMove++;
  2235.     if (commentList[forwardMostMove] != NULL) {
  2236.     free(commentList[forwardMostMove]);
  2237.     commentList[forwardMostMove] = NULL;
  2238.     }
  2239.     CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);
  2240.     ApplyMove(moveType, fromX, fromY, toX, toY,
  2241.           boards[forwardMostMove]);
  2242.     endMessage[0] = NULLCHAR;
  2243.     (void) CoordsToAlgebraic(fromX, fromY, toX, toY,
  2244.                  ToLower(PieceToChar(PromoPiece(*moveType))),
  2245.                  forwardMostMove - 1,
  2246.                  parseList[forwardMostMove - 1]);
  2247.     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
  2248.     timeRemaining[1][forwardMostMove] = blackTimeRemaining;
  2249.  
  2250.     if (pausing && gameMode != PlayFromGameFile) return;
  2251.     
  2252.     currentMove = forwardMostMove;
  2253.  
  2254.     if ((gameMode == PlayFromGameFile) &&
  2255.     (matchMode || (appData.timeDelay == 0 && !pausing))) return;
  2256.  
  2257.     SwitchClocks();
  2258.     DisplayMove(currentMove - 1);
  2259.     DrawPosition(FALSE, boards[currentMove]);
  2260. }
  2261.  
  2262. void InitChessProgram(hostName, programName, pr, isr, sendTime)
  2263.      char *hostName, *programName;
  2264.      ProcRef *pr;
  2265.      InputSourceRef *isr;
  2266.      int *sendTime;
  2267. {
  2268.     char cmdLine[MSG_SIZ];
  2269.     char buf[MSG_SIZ];
  2270.     int err;
  2271.     
  2272.     if (appData.noChessProgram) return;
  2273.     
  2274.     if (strcmp(hostName, "localhost") == 0) {
  2275.     if (*appData.searchTime != NULLCHAR) {
  2276.         sprintf(cmdLine, "%s %d", programName, searchTime);
  2277.     } else if (appData.searchDepth > 0) {
  2278.         sprintf(cmdLine, "%s 1 9999", programName);
  2279.     } else {
  2280.         sprintf(cmdLine, "%s %d %s", programName,
  2281.             appData.movesPerSession, appData.timeControl);
  2282.     }
  2283.     } else {
  2284.     if (*appData.searchTime != NULLCHAR) {
  2285.         sprintf(cmdLine, "%s %s %s %d",
  2286.             appData.remoteShell, hostName, programName, searchTime);
  2287.     } else if (appData.searchDepth > 0) {
  2288.         sprintf(cmdLine, "%s %s %s 1 9999",
  2289.             appData.remoteShell, hostName, programName);
  2290.     } else {
  2291.         sprintf(cmdLine, "%s %s %s %d %s",
  2292.             appData.remoteShell, hostName, programName,
  2293.             appData.movesPerSession, appData.timeControl);
  2294.     }
  2295.     }
  2296.     
  2297.     err = StartChildProcess(cmdLine, pr);
  2298.     if (err != 0) {
  2299.     sprintf(buf, "Startup failure on '%s'", programName);
  2300.     DisplayFatalError(buf, err);
  2301.     *pr = NoProc;
  2302.     return; /* don't actually exit */
  2303.     }
  2304.     
  2305. #ifdef NOTDEF
  2306. /*!!*/
  2307.     /* Read "Chess" */
  2308.     if (fgets(buf, MSG_SIZ, *from) == NULL) {
  2309.     sprintf(buf, "No response from '%s'", programName);
  2310.     DisplayFatalError(buf, errno);
  2311.     DestroyChildProcess(*pr);
  2312.     *pr = NULL;
  2313.     }
  2314. #endif
  2315.  
  2316.     *isr = AddInputSource(*pr, TRUE, ReceiveFromProgram);
  2317.  
  2318.     SendToProgram(appData.initString, *pr);
  2319.     SendSearchDepth(*pr);
  2320.     
  2321.     if (*sendTime == 2) {
  2322.     /* Does program have "time" command? */
  2323.     char buf[MSG_SIZ];
  2324.     
  2325.     sprintf(buf, "time %d\nhelp\n", blackTimeRemaining/10);
  2326.     /* "help" is a kludge to work around a gnuchess bug;
  2327.        some versions do not send a newline at the end of
  2328.        their response to the time command */
  2329.     SendToProgram(buf, *pr);
  2330.     }
  2331. }
  2332.  
  2333.  
  2334. void GameEnds(why)
  2335.      char *why;
  2336. {
  2337.     GameMode gm;
  2338.     
  2339.     gm = gameMode;
  2340.  
  2341.     if (appData.icsActive) {
  2342.     gameMode = IcsIdle;
  2343.     ics_gamenum = -1;
  2344.     ics_user_moved = FALSE;
  2345.     }
  2346.     (void) StopLoadGameTimer();
  2347.     if (gameFileFP != NULL) {
  2348.     gameFileFP = NULL;
  2349.     }
  2350.     StopClocks();
  2351.     
  2352.     if (why == NULL) return;
  2353.     
  2354.     strncpy(endMessage, why, MOVE_LEN * 4);
  2355.     endMessage[MOVE_LEN * 4 - 1] = NULLCHAR;
  2356.     if (currentMove == forwardMostMove)
  2357.       DisplayMove(currentMove - 1);
  2358.     
  2359.     if (forwardMostMove == 0) return;
  2360.     if (gm != PlayFromGameFile) {
  2361.     if (*appData.saveGameFile != NULLCHAR) {
  2362.         SaveGameToFile(appData.saveGameFile);
  2363.     } else if (appData.autoSaveGames) {
  2364.         AutoSaveGame();
  2365.     }
  2366.     if (*appData.savePositionFile != NULLCHAR) {
  2367.         SavePositionToFile(appData.savePositionFile);
  2368.     }
  2369.     }
  2370. }
  2371.  
  2372. void ShutdownChessPrograms(why)
  2373.      char *why;
  2374. {
  2375.     char *quit = "quit\n";
  2376.     int dummy;
  2377.  
  2378.     GameEnds(why);
  2379.     
  2380.     if (appData.icsActive) return;
  2381.  
  2382.     lastGameMode = gameMode;
  2383.     gameMode = EndOfGame;
  2384.     ModeHighlight();
  2385.     
  2386.     if (firstProgramISR != NULL)
  2387.       RemoveInputSource(firstProgramISR);
  2388.     firstProgramISR = NULL;
  2389.     
  2390.     if (firstProgramPR != NoProc) {
  2391.     OutputToProcess(firstProgramPR, quit, strlen(quit), &dummy);
  2392.     DestroyChildProcess(firstProgramPR);
  2393.     }
  2394.     firstProgramPR = NoProc;
  2395.     
  2396.     if (secondProgramISR != NULL)
  2397.       RemoveInputSource(secondProgramISR);
  2398.     secondProgramISR = NULL;
  2399.     
  2400.     if (secondProgramPR != NoProc) {
  2401.     OutputToProcess(secondProgramPR, quit, strlen(quit), &dummy);
  2402.     DestroyChildProcess(secondProgramPR);
  2403.     }
  2404.     secondProgramPR = NoProc;
  2405.     
  2406.     if (matchMode) {
  2407.     exit(0);
  2408.     }
  2409. }
  2410.  
  2411.  
  2412. /*
  2413.  * Button procedures
  2414.  */
  2415. void ExitEvent(status)
  2416.      int status;
  2417. {
  2418.     /* Save game if resource set and not already saved */
  2419.     if (*endMessage == NULLCHAR && forwardMostMove > 0) {
  2420.     if (*appData.saveGameFile != NULLCHAR) {
  2421.         SaveGameToFile(appData.saveGameFile);
  2422.     } else if (appData.autoSaveGames) {
  2423.         AutoSaveGame();
  2424.     }
  2425.     if (*appData.savePositionFile != NULLCHAR) {
  2426.         SavePositionToFile(appData.savePositionFile);
  2427.     }
  2428.     }
  2429.     ShutdownChessPrograms(NULL);
  2430.     if (icsPR != NoProc) {
  2431.     DestroyChildProcess(icsPR);
  2432.     }
  2433.     exit(status);
  2434. }
  2435.  
  2436. void CallFlagEvent()
  2437. {
  2438.     /* Call your opponent's flag (claim a win on time) */
  2439.     if (appData.icsActive) {
  2440.     SendToICS("flag\n");
  2441.     } else {
  2442.     switch (gameMode) {
  2443.       default:
  2444.         return;
  2445.       case MachinePlaysWhite:
  2446.         if (whiteFlag) {
  2447.         if (blackFlag)
  2448.           ShutdownChessPrograms("Draw (both players ran out of time)");
  2449.         else
  2450.           ShutdownChessPrograms("Black wins on time");
  2451.         }
  2452.         break;
  2453.       case MachinePlaysBlack:
  2454.         if (blackFlag) {
  2455.         if (whiteFlag)
  2456.           ShutdownChessPrograms("Draw (both players ran out of time)");
  2457.         else
  2458.           ShutdownChessPrograms("White wins on time");
  2459.         }
  2460.         break;
  2461.     }
  2462.     }
  2463. }
  2464.  
  2465. void DrawEvent()
  2466. {
  2467.     /* Offer draw or accept pending draw offer from opponent */
  2468.     
  2469.     if (appData.icsActive) {
  2470.     /* Note: tournament rules require draw offers to be
  2471.        made after you make your move but before you punch
  2472.        your clock.  Currently ICS doesn't let you do that;
  2473.        instead, you always punch your clock after making a
  2474.        move, but you can offer a draw at any time. */
  2475.     
  2476.     SendToICS("draw\n");
  2477.     } else {
  2478.     /* Currently GNU Chess doesn't offer or accept draws
  2479.        at all, so there is no Draw button in GNU Chess
  2480.        mode.  */
  2481.     
  2482.     DisplayError("Function not implemented", 0);
  2483.     }
  2484. }
  2485.  
  2486.  
  2487. void DeclineDrawEvent()
  2488. {
  2489.     /* Decline a pending draw offer from opponent */
  2490.     
  2491.     if (appData.icsActive) {
  2492.     /* Note: ICS also lets you withdraw your own draw
  2493.        offer with this command.  I'm not sure how long
  2494.        your draw offer remains pending if you don't
  2495.        withdraw it. */
  2496.     
  2497.     SendToICS("decline draw\n");
  2498.     } else {
  2499.     /* Currently GNU Chess doesn't offer or accept draws
  2500.        at all, so there is no Decline Draw button in
  2501.        GNU Chess mode.  */
  2502.     
  2503.     DisplayError("Function not implemented", 0);
  2504.     }
  2505. }
  2506.  
  2507. void AdjournEvent()
  2508. {
  2509.     /* Offer Adjourn or accept pending Adjourn offer from opponent */
  2510.     
  2511.     if (appData.icsActive) {
  2512.     SendToICS("adjourn\n");
  2513.     } else {
  2514.     /* Currently GNU Chess doesn't offer or accept Adjourns
  2515.        at all, so there is no Adjourn button in GNU Chess
  2516.        mode.  */
  2517.     
  2518.     DisplayError("Function not implemented", 0);
  2519.     }
  2520. }
  2521.  
  2522.  
  2523. void DeclineAdjournEvent()
  2524. {
  2525.     /* Decline a pending Adjourn offer from opponent */
  2526.     
  2527.     if (appData.icsActive) {
  2528.     /* Note: ICS also lets you withdraw your own Adjourn
  2529.        offer with this command.  I'm not sure how long
  2530.        your Adjourn offer remains pending if you don't
  2531.        withdraw it. */
  2532.     
  2533.     SendToICS("decline adjourn\n");
  2534.     } else {
  2535.     /* Currently GNU Chess doesn't offer or accept Adjourns
  2536.        at all, so there is no Decline Adjourn button in
  2537.        GNU Chess mode.  */
  2538.     
  2539.     DisplayError("Function not implemented", 0);
  2540.     }
  2541. }
  2542.  
  2543. void AbortEvent()
  2544. {
  2545.     /* Offer Abort or accept pending Abort offer from opponent */
  2546.     
  2547.     if (appData.icsActive) {
  2548.     SendToICS("abort\n");
  2549.     } else {
  2550.     DisplayError("Function not implemented", 0);
  2551.     }
  2552. }
  2553.  
  2554.  
  2555. void DeclineAbortEvent()
  2556. {
  2557.     /* Decline a pending Abort offer from opponent */
  2558.     
  2559.     if (appData.icsActive) {
  2560.     /* Note: ICS also lets you withdraw your own Abort
  2561.        offer with this command.  I'm not sure how long
  2562.        your Abort offer remains pending if you don't
  2563.        withdraw it. */
  2564.     
  2565.     SendToICS("decline abort\n");
  2566.     } else {
  2567.     /* Currently GNU Chess doesn't offer or accept Aborts
  2568.        at all, so there is no Decline Abort button in
  2569.        GNU Chess mode.  */
  2570.     
  2571.     DisplayError("Function not implemented", 0);
  2572.     }
  2573. }
  2574.  
  2575.  
  2576. void ResignEvent()
  2577. {
  2578.     /* Resign.  You can do this even if it's not your turn. */
  2579.     
  2580.     if (appData.icsActive) {
  2581.     SendToICS("resign\n");
  2582.     } else {
  2583.     switch (gameMode) {
  2584.       case MachinePlaysWhite:
  2585.         ShutdownChessPrograms("Black resigns");
  2586.         break;
  2587.       case MachinePlaysBlack:
  2588.         ShutdownChessPrograms("White resigns");
  2589.         break;
  2590.       default:
  2591.         break;
  2592.     }
  2593.     }
  2594. }
  2595.  
  2596.  
  2597. int LoadPositionFromFile(filename, n)
  2598.      char *filename;
  2599.      int n;
  2600. {
  2601.     FILE *f;
  2602.     char buf[MSG_SIZ];
  2603.     int ret;
  2604.  
  2605.     f = fopen(filename, "r");
  2606.     if (f == NULL) {
  2607.     sprintf(buf, "Can't open %s", filename);
  2608.     DisplayError(buf, errno);
  2609.     return FALSE;
  2610.     } else {
  2611.     ret = LoadPosition(f, n);
  2612.     return ret;
  2613.     }
  2614. }
  2615.  
  2616. int LoadPosition(fp, positionNumber)
  2617.      FILE *fp;
  2618.      int positionNumber;
  2619. {
  2620.     char *p, line[MSG_SIZ];
  2621.     Board initial_position;
  2622.     int i, j;
  2623.     
  2624.     if (gameMode != BeginningOfGame) {
  2625.     Reset(TRUE);
  2626.     }
  2627.  
  2628.     lastGameMode = gameMode = ForceMoves;
  2629.     ModeHighlight();
  2630.     startedFromSetupPosition = TRUE;
  2631.     
  2632.     if (firstProgramPR == NoProc)
  2633.       InitChessProgram(appData.firstHost, appData.firstChessProgram,
  2634.                &firstProgramPR, &firstProgramISR, &firstSendTime);
  2635.     
  2636.     if (positionNumber < 2) {
  2637.     /* Backward compatibility---don't look for '#' */
  2638.     (void) fgets(line, MSG_SIZ, fp);
  2639.     } else {
  2640.     while (positionNumber > 0) {
  2641.         /* skip postions before number positionNumber */
  2642.         if (fgets(line, MSG_SIZ, fp) == NULL) {
  2643.         Reset(TRUE);
  2644.         DisplayError("Position not found in file", 0);
  2645.         return FALSE;
  2646.         }
  2647.         if (line[0] == '#') positionNumber--;
  2648.     }
  2649.     }
  2650.  
  2651.     (void) fgets(line, MSG_SIZ, fp);
  2652.     (void) fgets(line, MSG_SIZ, fp);
  2653.     
  2654.     for (i = BOARD_SIZE - 1; i >= 0; i--) {
  2655.     (void) fgets(line, MSG_SIZ, fp);
  2656.     for (p = line, j = 0; j < BOARD_SIZE; p++) {
  2657.         if (*p == ' ')
  2658.           continue;
  2659.         initial_position[i][j++] = CharToPiece(*p);
  2660.     }
  2661.     }
  2662.     
  2663.     blackPlaysFirst = FALSE;
  2664.     if (!feof(fp)) {
  2665.     (void) fgets(line, MSG_SIZ, fp);
  2666.     if (strncmp(line, "black", strlen("black"))==0)
  2667.       blackPlaysFirst = TRUE;
  2668.     }
  2669.     fclose(fp);
  2670.     
  2671.     if (blackPlaysFirst) {
  2672.     CopyBoard(boards[0], initial_position);
  2673.     strcpy(moveList[0], "");
  2674.     strcpy(parseList[0], "");
  2675.     currentMove = forwardMostMove = backwardMostMove = 1;
  2676.     CopyBoard(boards[1], initial_position);
  2677.     SendToProgram("force\na3\n", firstProgramPR);
  2678.     SendCurrentBoard(firstProgramPR);
  2679.     DisplayMessage("Black to play");
  2680.     } else {
  2681.     currentMove = forwardMostMove = backwardMostMove = 0;
  2682.     CopyBoard(boards[0], initial_position);
  2683.     SendCurrentBoard(firstProgramPR);
  2684.     SendToProgram("force\n", firstProgramPR);
  2685.     DisplayMessage("White to play");
  2686.     }
  2687.     
  2688.     ResetClocks();
  2689.     timeRemaining[0][1] = whiteTimeRemaining;
  2690.     timeRemaining[1][1] = blackTimeRemaining;
  2691.  
  2692.     DrawPosition(FALSE, boards[currentMove]);
  2693.     
  2694.     return TRUE;
  2695. }
  2696.  
  2697.  
  2698. int LoadGameFromFile(filename, n)
  2699.      char *filename;
  2700.      int n;
  2701. {
  2702.     FILE *f;
  2703.     char buf[MSG_SIZ];
  2704.     int ret;
  2705.  
  2706.     f = fopen(filename, "r");
  2707.     if (f == NULL) {
  2708.     sprintf(buf, "Can't open %s", filename);
  2709.     DisplayError(buf, errno);
  2710.     return FALSE;
  2711.     } else {
  2712.     ret = LoadGame(f, n);
  2713.     return ret;
  2714.     }
  2715. }
  2716.  
  2717.  
  2718. FILE *lastLoadGameFP = NULL;
  2719. int lastLoadGameNumber = 0;
  2720.  
  2721. int ReloadGame(offset)
  2722.      int offset;
  2723. {
  2724.     if (lastLoadGameFP == NULL) {
  2725.     DisplayError("No game has been loaded", 0);
  2726.     return FALSE;
  2727.     }
  2728.     rewind(lastLoadGameFP);
  2729.     return LoadGame(lastLoadGameFP, lastLoadGameNumber + offset);
  2730. }
  2731.  
  2732. int LoadGame(f, gameNumber)
  2733.      FILE *f;
  2734.      int gameNumber;
  2735. {
  2736.     ChessMove cm, lastStart;
  2737.  
  2738.     if (gameMode != BeginningOfGame) {
  2739.     Reset(FALSE);
  2740.     }
  2741.  
  2742.     yynewfile(f);
  2743.     gameFileFP = f;
  2744.     if (lastLoadGameFP != NULL && lastLoadGameFP != f) {
  2745.     fclose(lastLoadGameFP);
  2746.     }
  2747.     lastLoadGameFP = f;
  2748.     lastLoadGameNumber = gameNumber;
  2749.  
  2750.     lastGameMode = gameMode = PlayFromGameFile;
  2751.     ModeHighlight();
  2752.     InitPosition(FALSE);
  2753.     StopClocks();
  2754.     if (firstProgramPR == NoProc) {
  2755.     InitChessProgram(appData.firstHost, appData.firstChessProgram,
  2756.              &firstProgramPR, &firstProgramISR, &firstSendTime);
  2757.     } else {
  2758.     SendToProgram(appData.initString, firstProgramPR);
  2759.     SendSearchDepth(firstProgramPR);
  2760.     }
  2761.     SendToProgram("force\n", firstProgramPR);
  2762.     
  2763.     /*
  2764.      * Skip the first gameNumber-1 games in the file.
  2765.      * Also skip over anything that precedes an identifiable 
  2766.      * start of game marker, to avoid being confused by 
  2767.      * garbage at the start of the file.  Currently 
  2768.      * recognized start of game markers are the move number "1",
  2769.      * the pattern "gnuchess .* game", and the pattern
  2770.      * "^# [^ ]* game file".  A game that starts with the
  2771.      * latter pattern will also have a move number 1, possibly
  2772.      * following a position diagram.
  2773.      */
  2774.     lastStart = (ChessMove) 0;
  2775.     while (gameNumber > 0) {
  2776.     yyboardindex = forwardMostMove;
  2777.     cm = (ChessMove) yylex();
  2778.     switch (cm) {
  2779.       case (ChessMove) 0:
  2780.         Reset(TRUE);
  2781.         DisplayError("Game not found in file", 0);
  2782.         return FALSE;
  2783.  
  2784.       case GNUChessGame:
  2785.       case XBoardGame:
  2786.         gameNumber--;
  2787.         lastStart = cm;
  2788.         break;
  2789.         
  2790.       case MoveNumberOne:
  2791.         if (lastStart == MoveNumberOne || lastStart == (ChessMove) 0) {
  2792.         gameNumber--;
  2793.         lastStart = cm;
  2794.         } else if (lastStart == XBoardGame) {
  2795.         lastStart = cm;
  2796.         }
  2797.         break;
  2798.  
  2799.       default:
  2800.         break;
  2801.     }
  2802.     }
  2803.     
  2804.     if (appData.debugMode)
  2805.       fprintf(debugFP, "Parsed game start '%s' (%d)\n", yytext, (int) cm);
  2806.  
  2807.     if (lastStart == XBoardGame) {
  2808.     /* Skip header junk before position diagram and/or move 1 */
  2809.     for (;;) {
  2810.         int i, j;
  2811.         char *p;
  2812.         Board initial_position;
  2813.  
  2814.         yyboardindex = forwardMostMove;
  2815.         cm = (ChessMove) yylex();
  2816.  
  2817.         if (appData.debugMode)
  2818.           fprintf(debugFP, "Parsed '%s' (%d)\n", yytext, (int) cm);
  2819.  
  2820.         switch (cm) {
  2821.           default:
  2822.         continue;
  2823.  
  2824.           case (ChessMove) 0:
  2825.           case GNUChessGame:
  2826.           case XBoardGame:
  2827.         Reset(TRUE);
  2828.         DisplayMessage("No moves in game");
  2829.         return FALSE;
  2830.  
  2831.           case MoveNumberOne:
  2832.         break;
  2833.  
  2834.           case PositionDiagram:
  2835.         p = yytext;
  2836.         for (i = BOARD_SIZE - 1; i >= 0; i--)
  2837.           for (j = 0; j < BOARD_SIZE; p++)
  2838.             switch (*p) {
  2839.               case '[':
  2840.               case '-':
  2841.               case ' ':
  2842.               case '\t':
  2843.               case '\n':
  2844.               case '\r':
  2845.             break;
  2846.               default:
  2847.             initial_position[i][j++] = CharToPiece(*p);
  2848.             break;
  2849.             }
  2850.         while (*p == ' ' || *p == '\t' ||
  2851.                *p == '\n' || *p == '\r') p++;
  2852.     
  2853.         if (strncmp(p, "black", strlen("black"))==0)
  2854.           blackPlaysFirst = TRUE;
  2855.         else
  2856.           blackPlaysFirst = FALSE;
  2857.         startedFromSetupPosition = TRUE;
  2858.     
  2859.         if (blackPlaysFirst) {
  2860.             currentMove = forwardMostMove = backwardMostMove = 1;
  2861.             CopyBoard(boards[0], initial_position);
  2862.             CopyBoard(boards[1], initial_position);
  2863.             strcpy(moveList[0], "");
  2864.             strcpy(parseList[0], "");
  2865.             timeRemaining[0][1] = whiteTimeRemaining;
  2866.             timeRemaining[1][1] = blackTimeRemaining;
  2867.             SendToProgram("a3\n", firstProgramPR);
  2868.             SendCurrentBoard(firstProgramPR);
  2869.         } else {
  2870.             currentMove = forwardMostMove = backwardMostMove = 0;
  2871.             CopyBoard(boards[0], initial_position);
  2872.             SendCurrentBoard(firstProgramPR);
  2873.         }
  2874.         break;
  2875.  
  2876.           case Comment:
  2877.         if (appData.debugMode) 
  2878.           fprintf(debugFP, "Parsed Comment: %s\n", yytext);
  2879.         if (!matchMode && (pausing || appData.timeDelay != 0)) {
  2880.             CommentPopUp(yytext);
  2881.         }
  2882.         AppendComment(currentMove, yytext);
  2883.         if (!matchMode && appData.timeDelay != 0) {
  2884.             DrawPosition(FALSE, boards[currentMove]);
  2885.             if (appData.timeDelay > 0)
  2886.               StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
  2887.             return TRUE;
  2888.         }
  2889.         break;
  2890.         }
  2891.         break;
  2892.     }
  2893.     }
  2894.  
  2895.     if (!matchMode && appData.timeDelay != 0)
  2896.       DrawPosition(FALSE, boards[currentMove]);
  2897.  
  2898.     LoadGameLoop();
  2899.     
  2900.     return TRUE;
  2901. }
  2902.  
  2903. void ResurrectChessProgram()
  2904.      /* Get out of EndOfGame mode.  This may require us to restart the
  2905.     chess program and feed it all the moves made so far. */
  2906. {
  2907.     int i;
  2908.     
  2909.     /* assert(gameMode == EndOfGame) */
  2910.     
  2911.     gameMode = lastGameMode = ForceMoves;
  2912.     ModeHighlight();
  2913.     
  2914.     if (firstProgramPR != NoProc) return;
  2915.     
  2916.     InitChessProgram(appData.firstHost, appData.firstChessProgram,
  2917.              &firstProgramPR, &firstProgramISR, &firstSendTime);
  2918.     SendToProgram("force\n", firstProgramPR);
  2919.     
  2920.     if (startedFromSetupPosition) {
  2921.     if (backwardMostMove % 2 == 1)
  2922.       SendToProgram("a3\n", firstProgramPR);
  2923.     SendBoard(firstProgramPR, boards[backwardMostMove]);
  2924.     }
  2925.     
  2926.     for (i = backwardMostMove; i < currentMove; i++) {
  2927.     SendToProgram(moveList[i], firstProgramPR);
  2928.     }
  2929.     
  2930.     if (!firstSendTime) {
  2931.     /* can't tell gnuchess what its clock should read,
  2932.        so we bow to its notion. */
  2933.     ResetClocks();
  2934.     timeRemaining[0][currentMove] = whiteTimeRemaining;
  2935.     timeRemaining[1][currentMove] = blackTimeRemaining;
  2936.     }
  2937. }
  2938.  
  2939. void MachineBlackEvent()
  2940. {
  2941.     if (pausing) PauseEvent();
  2942.     if (gameMode == PlayFromGameFile) ForceMovesEvent();
  2943.     if (gameMode == EditPosition) EditPositionDone();
  2944.     
  2945.     if ((gameMode == EndOfGame) ||
  2946.     (appData.noChessProgram) || (gameMode == MachinePlaysBlack))
  2947.       return;
  2948.     
  2949.     if (WhiteOnMove(gameMode == ForceMoves ? currentMove : forwardMostMove)) {
  2950.     DisplayError("It is not Black's turn", 0);
  2951.     return;
  2952.     }
  2953.     if (gameMode == TwoMachinesPlay) ForceMovesEvent();
  2954.     
  2955.     if (gameMode == ForceMoves) forwardMostMove = currentMove;
  2956.     
  2957.     lastGameMode = gameMode = MachinePlaysBlack;
  2958.     ModeHighlight();
  2959.     SendToProgram(appData.blackString, firstProgramPR);
  2960.     StartClocks();
  2961. }
  2962.  
  2963. void ForwardInner(target)
  2964.      int target;
  2965. {
  2966.     if (gameMode == EditPosition)
  2967.       return;
  2968.     
  2969.     if (gameMode == PlayFromGameFile && !pausing)
  2970.       PauseEvent();
  2971.     
  2972.     if (currentMove >= forwardMostMove) {
  2973.     if (gameFileFP != NULL)
  2974.       (void) LoadGameOneMove();
  2975.     return;
  2976.     }
  2977.     
  2978.     if (gameMode == ForceMoves) {
  2979.     while (currentMove < target) {
  2980.         SendToProgram(moveList[currentMove++], firstProgramPR);
  2981.     }
  2982.     } else {
  2983.     currentMove = target;
  2984.     }
  2985.     
  2986.     if (gameMode == ForceMoves) {
  2987.     whiteTimeRemaining = timeRemaining[0][currentMove];
  2988.     blackTimeRemaining = timeRemaining[1][currentMove];
  2989.     }
  2990.     DisplayBothClocks();
  2991.     DisplayMove(currentMove - 1);
  2992.     DrawPosition(FALSE, boards[currentMove]);
  2993.     if (commentList[currentMove] == NULL) {
  2994.     CommentPopDown();
  2995.     } else {
  2996.     CommentPopUp(commentList[currentMove]);
  2997.     }
  2998. }
  2999.  
  3000.  
  3001. void ForwardEvent()
  3002. {
  3003.     ForwardInner(currentMove + 1);
  3004. }
  3005.  
  3006. void ToEndEvent()
  3007. {
  3008.     ForwardInner(forwardMostMove);
  3009. }
  3010.  
  3011. void Reset(redraw)
  3012.      int redraw;
  3013. {
  3014.     int i;
  3015.  
  3016.     pausing = FALSE;
  3017.     flipView = appData.flipView;
  3018.     startedFromSetupPosition = blackPlaysFirst = FALSE;
  3019.     firstMove = TRUE;
  3020.     whiteFlag = blackFlag = FALSE;
  3021.     maybeThinking = FALSE;
  3022.     endMessage[0] = NULLCHAR;
  3023.     ics_white[0] = ics_black[0] = NULLCHAR;
  3024.     ics_user_moved = FALSE;
  3025.     ics_getting_history = H_FALSE;
  3026.     ics_gamenum = -1;
  3027.     
  3028.     ResetFrontEnd();
  3029.  
  3030.     ShutdownChessPrograms(NULL);
  3031.     lastGameMode = gameMode = BeginningOfGame;
  3032.     ModeHighlight();
  3033.     InitPosition(redraw);
  3034.     for (i = 0; i < MAX_MOVES; i++) {
  3035.     if (commentList[i] != NULL) {
  3036.         free(commentList[i]);
  3037.         commentList[i] = NULL;
  3038.     }
  3039.     }
  3040.     ResetClocks();
  3041.     timeRemaining[0][0] = whiteTimeRemaining;
  3042.     timeRemaining[1][0] = blackTimeRemaining;
  3043.     InitChessProgram(appData.firstHost, appData.firstChessProgram,
  3044.              &firstProgramPR, &firstProgramISR, &firstSendTime);
  3045.     DisplayTitle("");
  3046.     DisplayMessage("");
  3047. }
  3048.  
  3049.  
  3050. void EditPositionEvent()
  3051. {
  3052.     if (gameMode == EditPosition) {
  3053.     ForceMovesEvent();
  3054.     return;
  3055.     }
  3056.     
  3057.     ForceMovesEvent();
  3058.     if (gameMode != ForceMoves) return;
  3059.     
  3060.     lastGameMode = gameMode = EditPosition;
  3061.     ModeHighlight();
  3062.     if (currentMove > 0)
  3063.       CopyBoard(boards[0], boards[currentMove]);
  3064.     
  3065.     blackPlaysFirst = !WhiteOnMove(currentMove);
  3066.     ResetClocks();
  3067.     currentMove = forwardMostMove = backwardMostMove = 0;
  3068. }
  3069.  
  3070. void EditPositionDone()
  3071. {
  3072.     startedFromSetupPosition = TRUE;
  3073.     SendToProgram(appData.initString, firstProgramPR);
  3074.     SendSearchDepth(firstProgramPR);
  3075.     if (blackPlaysFirst) {
  3076.     strcpy(moveList[0], "");
  3077.     strcpy(parseList[0], "");
  3078.     currentMove = forwardMostMove = backwardMostMove = 1;
  3079.     CopyBoard(boards[1], boards[0]);
  3080.     SendToProgram("force\na3\n", firstProgramPR);
  3081.     SendCurrentBoard(firstProgramPR);
  3082.     DisplayTitle("");
  3083.     DisplayMessage("Black to play");
  3084.     } else {
  3085.     currentMove = forwardMostMove = backwardMostMove = 0;
  3086.     SendCurrentBoard(firstProgramPR);
  3087.     SendToProgram("force\n", firstProgramPR);
  3088.     DisplayTitle("");
  3089.     DisplayMessage("White to play");
  3090.     }
  3091.     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
  3092.     timeRemaining[1][forwardMostMove] = blackTimeRemaining;
  3093.     lastGameMode = gameMode = ForceMoves;
  3094. }
  3095.  
  3096. void SetWhiteToPlayEvent()
  3097. {
  3098.     if (gameMode != EditPosition) return;
  3099.     blackPlaysFirst = FALSE;
  3100.     DisplayBothClocks(); /* works because currentMove is 0 */
  3101. }
  3102.  
  3103. void SetBlackToPlayEvent()
  3104. {
  3105.     if (gameMode != EditPosition) return;
  3106.     blackPlaysFirst = TRUE;
  3107.     currentMove = 1;    /* kludge */
  3108.     DisplayBothClocks();
  3109.     currentMove = 0;
  3110. }
  3111.  
  3112. void EditPositionMenuEvent(selection, x, y)
  3113.      ChessSquare selection;
  3114.      int x, y;
  3115. {
  3116.     if (gameMode != EditPosition) return;
  3117.     switch (selection) {
  3118.       case ClearBoard:
  3119.     for (x = 0; x < BOARD_SIZE; x++)
  3120.       for (y = 0; y < BOARD_SIZE; y++)
  3121.         boards[0][y][x] = EmptySquare;
  3122.     DrawPosition(FALSE, boards[0]);
  3123.     break;
  3124.  
  3125.       case WhitePlay:
  3126.     SetWhiteToPlayEvent();
  3127.     break;
  3128.  
  3129.       case BlackPlay:
  3130.     SetBlackToPlayEvent();
  3131.     break;
  3132.  
  3133.       default:
  3134.     boards[0][y][x] = selection;
  3135.     DrawPosition(FALSE, boards[0]);
  3136.     break;
  3137.     }
  3138. }
  3139.  
  3140.  
  3141. void MachineWhiteEvent()
  3142. {
  3143.     if (pausing) PauseEvent();
  3144.     if (gameMode == PlayFromGameFile) ForceMovesEvent();
  3145.     if (gameMode == EditPosition) EditPositionDone();
  3146.     
  3147.     if ((gameMode == EndOfGame) ||
  3148.     (appData.noChessProgram) || (gameMode == MachinePlaysWhite))
  3149.       return;
  3150.     
  3151.     if (!WhiteOnMove(gameMode == ForceMoves ? currentMove : forwardMostMove)) {
  3152.     DisplayError("It is not White's turn", 0);
  3153.     return;
  3154.     }
  3155.     
  3156.     if (gameMode == TwoMachinesPlay) ForceMovesEvent();
  3157.  
  3158.     if (gameMode == ForceMoves) forwardMostMove = currentMove;
  3159.     
  3160.     lastGameMode = gameMode = MachinePlaysWhite;
  3161.     ModeHighlight();
  3162.     SendToProgram(appData.whiteString, firstProgramPR);
  3163.     StartClocks();
  3164. }
  3165.  
  3166. void BackwardInner(target)
  3167.      int target;
  3168. {
  3169.     if ((currentMove <= backwardMostMove) || (gameMode == EditPosition))
  3170.       return;
  3171.     
  3172.     if (gameMode == EndOfGame)
  3173.       ResurrectChessProgram();
  3174.     
  3175.     if (gameMode == PlayFromGameFile && !pausing)
  3176.       PauseEvent();
  3177.     
  3178.     if (gameMode == ForceMoves) {
  3179.     Attention(firstProgramPR);
  3180.     while (currentMove > target) {
  3181.         SendToProgram("undo\n", firstProgramPR);
  3182.         currentMove--;
  3183.     }
  3184.     } else {
  3185.     currentMove = target;
  3186.     }
  3187.     
  3188.     if (gameMode == ForceMoves) {
  3189.     whiteTimeRemaining = timeRemaining[0][currentMove];
  3190.     blackTimeRemaining = timeRemaining[1][currentMove];
  3191.     }
  3192.     DisplayBothClocks();
  3193.     DisplayMove(currentMove - 1);
  3194.     DrawPosition(FALSE, boards[currentMove]);
  3195.     if (commentList[currentMove] == NULL) {
  3196.     CommentPopDown();
  3197.     } else {
  3198.     CommentPopUp(commentList[currentMove]);
  3199.     }
  3200. }
  3201.  
  3202. void BackwardEvent()
  3203. {
  3204.     BackwardInner(currentMove - 1);
  3205. }
  3206.  
  3207. void ToStartEvent()
  3208. {
  3209.     BackwardInner(backwardMostMove);
  3210. }
  3211.  
  3212.  
  3213. void FlipViewEvent()
  3214. {
  3215.     flipView = !flipView;
  3216.     DrawPosition(FALSE, boards[currentMove]);
  3217. }
  3218.  
  3219. char *DefaultFileName(ext)
  3220.      char *ext;
  3221. {
  3222.     static char def[MSG_SIZ];
  3223.  
  3224.     if (appData.icsActive && ics_white[0] != NULLCHAR) {
  3225.     sprintf(def, "%s-%s.%s", ics_white, ics_black, ext);
  3226.     } else {
  3227.     def[0] = NULLCHAR;
  3228.     }
  3229.  
  3230.     return def;
  3231. }
  3232.  
  3233. int SaveGameToFile(filename)
  3234.      char *filename;
  3235. {
  3236.     FILE *f;
  3237.     char buf[MSG_SIZ];
  3238.  
  3239.     f = fopen(filename, "a");
  3240.     if (f == NULL) {
  3241.     sprintf(buf, "Can't open %s", filename);
  3242.     DisplayError(buf, errno);
  3243.     return FALSE;
  3244.     } else {
  3245.     SaveGame(f, 0);
  3246.     return TRUE;
  3247.     }
  3248. }
  3249.  
  3250. int SaveGame(f, dummy)
  3251.      FILE *f;
  3252.      int dummy;
  3253. {
  3254.     int i;
  3255.     time_t tm;
  3256.     
  3257.     tm = time((time_t *) NULL);
  3258.     
  3259.     fprintf(f, "# %s game file -- %s", programName, ctime(&tm));
  3260.     PrintOpponents(f);
  3261.     fprintf(f, "\talgebraic\n");
  3262.     
  3263.     if (backwardMostMove > 0 || startedFromSetupPosition) {
  3264.     fprintf(f, "[--------------\n");
  3265.     PrintPosition(f, backwardMostMove);
  3266.     fprintf(f, "--------------]\n");
  3267.     }
  3268.  
  3269.     i = backwardMostMove;
  3270.  
  3271.     while (i < forwardMostMove) {
  3272.     if (commentList[i] != NULL)
  3273.       fprintf(f, "%s\n", commentList[i]);
  3274.  
  3275.     if ((i % 2) == 1) {
  3276.         fprintf(f, "%d. ...  %s\n", i/2 + 1, parseList[i]);
  3277.         i++;
  3278.     } else {
  3279.         fprintf(f, "%d. %s  ", i/2 + 1, parseList[i]);
  3280.         i++;
  3281.         if (commentList[i] != NULL) {
  3282.         fprintf(f, "\n");
  3283.         continue;
  3284.         }
  3285.         if (i >= forwardMostMove) {
  3286.         fprintf(f, "\n");
  3287.         break;
  3288.         }
  3289.         fprintf(f, "%s\n", parseList[i]);
  3290.         i++;
  3291.     }
  3292.     }
  3293.     
  3294.     if (commentList[i] != NULL)
  3295.       fprintf(f, "%s\n", commentList[i]);
  3296.     
  3297.     if (endMessage[0] != NULLCHAR)
  3298.       fprintf(f, "%s\n", endMessage);
  3299.  
  3300.     fclose(f);
  3301.     return TRUE;
  3302. }
  3303.  
  3304. #ifdef notdef  
  3305. /* currently unused */
  3306. void SwitchEvent()
  3307. {
  3308.     if (appData.noChessProgram) return;
  3309.     if (pausing) PauseEvent();
  3310.     switch (gameMode) {
  3311.       default:
  3312.     return;
  3313.       case MachinePlaysWhite:
  3314.     if (WhiteOnMove(forwardMostMove)) {
  3315.         DisplayError("Wait until your turn", 0);
  3316.         return;
  3317.     }
  3318.     lastGameMode = gameMode = MachinePlaysBlack;
  3319.     ModeHighlight();
  3320.     break;
  3321.       case BeginningOfGame:
  3322.       case MachinePlaysBlack:
  3323.     if (!WhiteOnMove(forwardMostMove)) {
  3324.         DisplayError("Wait until your turn", 0);
  3325.         return;
  3326.     }
  3327.     if (forwardMostMove == 0) {
  3328.         MachineWhiteEvent();
  3329.         return;
  3330.     }
  3331.     lastGameMode = gameMode = MachinePlaysWhite;
  3332.     ModeHighlight();
  3333.     break;
  3334.     }
  3335.     
  3336.     Attention(firstProgramPR);
  3337.     SendToProgram("switch\n", firstProgramPR);
  3338. }
  3339. #endif /*notdef*/
  3340.  
  3341. void ForceMovesEvent()
  3342. {
  3343.     int i;
  3344.     char str[MSG_SIZ];
  3345.     
  3346.     if (pausing) PauseEvent();
  3347.     switch (gameMode) {
  3348.       case MachinePlaysWhite:
  3349.     if (WhiteOnMove(forwardMostMove)) {
  3350.         DisplayError("Wait until your turn", 0);
  3351.         return;
  3352.     }
  3353.     Attention(firstProgramPR);
  3354.     SendToProgram("force\n", firstProgramPR);
  3355.     break;
  3356.       case MachinePlaysBlack:
  3357.     if (!WhiteOnMove(forwardMostMove)) {
  3358.         DisplayError("Wait until your turn", 0);
  3359.         return;
  3360.     }
  3361.     Attention(firstProgramPR);
  3362.     SendToProgram("force\n", firstProgramPR);
  3363.     break;
  3364.       case BeginningOfGame:
  3365.     SendToProgram("force\n", firstProgramPR);
  3366.     break;
  3367.       case PlayFromGameFile:
  3368.         (void) StopLoadGameTimer();
  3369.     if (gameFileFP != NULL) {
  3370.         gameFileFP = NULL;
  3371.     }
  3372.     break;
  3373.       case EndOfGame:
  3374.     ResurrectChessProgram();
  3375.     return;
  3376.       case EditPosition:
  3377.     EditPositionDone();
  3378.     break;
  3379.       case TwoMachinesPlay:
  3380.     ShutdownChessPrograms(NULL);
  3381.     ResurrectChessProgram();
  3382.     return;
  3383.       case IcsPlayingBlack:
  3384.       case IcsPlayingWhite:
  3385.     DisplayError("Aren't you playing a game?  If not, use Reset and try again.", 0);
  3386.     return;
  3387.       case IcsObserving:
  3388.     SendToICS("observe\n");
  3389.     sprintf(str, "Aren't you observing game %d?  Attempting to stop observing it.",
  3390.             ics_gamenum);
  3391.     DisplayError(str, 0);
  3392.     break;
  3393.       case IcsIdle:
  3394.     break;
  3395.       default:
  3396.     return;
  3397.     }
  3398.     
  3399.     if (gameMode == MachinePlaysWhite ||
  3400.     gameMode == MachinePlaysBlack ||
  3401.     gameMode == TwoMachinesPlay ||
  3402.     gameMode == PlayFromGameFile) {
  3403.     i = forwardMostMove;
  3404.     while (i > currentMove) {
  3405.         SendToProgram("undo\n", firstProgramPR);
  3406.         i--;
  3407.     }
  3408.     whiteTimeRemaining = timeRemaining[0][currentMove];
  3409.     blackTimeRemaining = timeRemaining[1][currentMove];
  3410.     if (whiteFlag || blackFlag) {
  3411.         whiteFlag = blackFlag = 0;
  3412.     }
  3413.     DisplayTitle("");
  3414.     }        
  3415.     
  3416.     lastGameMode = gameMode = ForceMoves;
  3417.     ModeHighlight();
  3418.     
  3419.     StopClocks();
  3420. }
  3421.  
  3422.  
  3423. void IcsClientEvent()
  3424. {
  3425.     if (!appData.icsActive) return;
  3426.     switch (gameMode) {
  3427.       case IcsPlayingWhite:
  3428.       case IcsPlayingBlack:
  3429.       case IcsObserving:
  3430.       case IcsIdle:
  3431.       case BeginningOfGame:
  3432.     return;
  3433.  
  3434.       case ForceMoves:
  3435.     break;
  3436.  
  3437.       case EditPosition:
  3438.     EditPositionDone();
  3439.     break;
  3440.  
  3441.       default:
  3442.     ForceMovesEvent();
  3443.     break;
  3444.     }
  3445.  
  3446.     gameMode = IcsIdle;
  3447.     ModeHighlight();
  3448.     return;
  3449. }
  3450.  
  3451.  
  3452. void HintEvent()
  3453. {
  3454.     if (appData.noChessProgram) return;
  3455.     switch (gameMode) {
  3456.       case MachinePlaysWhite:
  3457.     if (WhiteOnMove(forwardMostMove)) {
  3458.         DisplayError("Wait until your turn", 0);
  3459.         return;
  3460.     }
  3461.     break;
  3462.       case BeginningOfGame:
  3463.       case MachinePlaysBlack:
  3464.     if (!WhiteOnMove(forwardMostMove)) {
  3465.         DisplayError("Wait until your turn", 0);
  3466.         return;
  3467.     }
  3468.     break;
  3469.       default:
  3470.     DisplayError("No hint available", 0);
  3471.     return;
  3472.     }
  3473.     Attention(firstProgramPR);
  3474.     SendToProgram("hint\n", firstProgramPR);
  3475. }
  3476.  
  3477. void PrintPosition(fp, move)
  3478.      FILE *fp;
  3479.      int move;
  3480. {
  3481.     int i, j;
  3482.     
  3483.     for (i = BOARD_SIZE - 1; i >= 0; i--) {
  3484.     for (j = 0; j < BOARD_SIZE; j++) {
  3485.         fprintf(fp, "%c", PieceToChar(boards[move][i][j]));
  3486.         fputc(j == BOARD_SIZE - 1 ? '\n' : ' ', fp);
  3487.     }
  3488.     }
  3489.     if ((gameMode == EditPosition) ? !blackPlaysFirst : (move % 2 == 0))
  3490.       fprintf(fp, "white to play\n");
  3491.     else
  3492.       fprintf(fp, "black to play\n");
  3493. }
  3494.  
  3495. void PrintOpponents(fp)
  3496.      FILE *fp;
  3497. {
  3498.     switch (lastGameMode) {
  3499.       case MachinePlaysWhite:
  3500.     fprintf(fp, "\t%s@%s vs. %s@%s\n", appData.firstChessProgram,
  3501.         appData.firstHost, UserName(), HostName());
  3502.     break;
  3503.       case MachinePlaysBlack:
  3504.     fprintf(fp, "\t%s@%s vs. %s@%s\n", UserName(),
  3505.         HostName(), appData.firstChessProgram,
  3506.         appData.firstHost);
  3507.     break;
  3508.       case TwoMachinesPlay:
  3509.     fprintf(fp, "\t%s@%s vs. %s@%s\n", appData.secondChessProgram,
  3510.         appData.secondHost, appData.firstChessProgram,
  3511.         appData.firstHost);
  3512.     break;
  3513.       default:
  3514.     if (appData.icsActive && ics_white[0] != NULLCHAR) {
  3515.         fprintf(fp, "\t%s vs. %s\n",
  3516.             ics_white, ics_black);
  3517.     } else {
  3518.         fprintf(fp, "\n");
  3519.     }
  3520.     break;
  3521.     }
  3522. }
  3523.  
  3524.  
  3525. int SavePositionToFile(filename)
  3526.      char *filename;
  3527. {
  3528.     FILE *f;
  3529.     char buf[MSG_SIZ];
  3530.  
  3531.     f = fopen(filename, "a");
  3532.     if (f == NULL) {
  3533.     sprintf(buf, "Can't open %s", filename);
  3534.     DisplayError(buf, errno);
  3535.     return FALSE;
  3536.     } else {
  3537.     SavePosition(f, 0);
  3538.     return TRUE;
  3539.     }
  3540. }
  3541.  
  3542. int SavePosition(f, dummy)
  3543.      FILE *f;
  3544.      int dummy;
  3545. {
  3546.     time_t tm;
  3547.     
  3548.     tm = time((time_t *) NULL);
  3549.     
  3550.     fprintf(f, "# %s position file -- %s", programName, ctime(&tm));
  3551.     PrintOpponents(f);
  3552.     fprintf(f, "[--------------\n");
  3553.     PrintPosition(f, currentMove);
  3554.     fprintf(f, "--------------]\n");
  3555.     fclose(f);
  3556.     return TRUE;
  3557. }
  3558.  
  3559. void TwoMachinesEvent()
  3560. {
  3561.     int i;
  3562.     
  3563.     if (pausing) PauseEvent();
  3564.     if (gameMode == PlayFromGameFile) ForceMovesEvent();
  3565.     if (appData.noChessProgram) return;
  3566.     
  3567.     switch (gameMode) {
  3568.       case EndOfGame:
  3569.       case TwoMachinesPlay:
  3570.     return;
  3571.       case MachinePlaysWhite:
  3572.       case MachinePlaysBlack:
  3573.       case BeginningOfGame:
  3574.     ForceMovesEvent();
  3575.     if (gameMode != ForceMoves) return;
  3576.     break;
  3577.       case EditPosition:
  3578.     EditPositionDone();
  3579.     break;
  3580.       case ForceMoves:
  3581.       default:
  3582.     break;
  3583.     }
  3584.     
  3585.     forwardMostMove = currentMove;
  3586.  
  3587.     InitChessProgram(appData.secondHost, appData.secondChessProgram,
  3588.              &secondProgramPR, &secondProgramISR, &secondSendTime);
  3589.     if (startedFromSetupPosition) {
  3590.     if (blackPlaysFirst) {
  3591.         SendToProgram("force\na3\n", secondProgramPR);
  3592.         SendBoard(secondProgramPR, boards[backwardMostMove]);
  3593.     } else {
  3594.         SendBoard(secondProgramPR, boards[backwardMostMove]);
  3595.         SendToProgram("force\n", secondProgramPR);
  3596.     }
  3597.     } else {
  3598.     SendToProgram("force\n", secondProgramPR);
  3599.     }
  3600.     for (i = backwardMostMove; i < forwardMostMove; i++) {
  3601.     SendToProgram(moveList[i], secondProgramPR);
  3602.     }
  3603.     lastGameMode = gameMode = TwoMachinesPlay;
  3604.     ModeHighlight();
  3605.     firstMove = TRUE;
  3606.     if (WhiteOnMove(forwardMostMove))
  3607.       SendToProgram(appData.whiteString, secondProgramPR);
  3608.     else
  3609.       SendToProgram(appData.blackString, firstProgramPR);
  3610.     
  3611.     if (!firstSendTime || !secondSendTime) {
  3612.     ResetClocks();
  3613.     timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
  3614.     timeRemaining[1][forwardMostMove] = blackTimeRemaining;
  3615.     }
  3616.     StartClocks();
  3617. }
  3618.  
  3619. void PauseEvent()
  3620. {
  3621.     if (pausing) {
  3622.     pausing = FALSE;
  3623.     ModeHighlight();
  3624.     if (gameMode == MachinePlaysWhite ||
  3625.         gameMode == MachinePlaysBlack) {
  3626.         StartClocks();
  3627.     } else {
  3628.         DisplayBothClocks();
  3629.     }
  3630.     if (gameMode == PlayFromGameFile &&
  3631.         !LoadGameTimerRunning() &&
  3632.         appData.timeDelay >= 0) {
  3633.         StartLoadGameTimer((long)(1000.0 * appData.timeDelay));
  3634.     }
  3635.     } else {
  3636.     switch (gameMode) {
  3637.       case EndOfGame:
  3638.       case EditPosition:
  3639.       default:
  3640.         return;
  3641.       case IcsObserving:
  3642.       case IcsPlayingWhite:
  3643.       case IcsPlayingBlack:
  3644.         pausing = TRUE;
  3645.         ModeHighlight();
  3646.         return;
  3647.       case PlayFromGameFile:
  3648.         (void) StopLoadGameTimer();
  3649.         pausing = TRUE;
  3650.         ModeHighlight();
  3651.         break;
  3652.       case BeginningOfGame:
  3653.         if (appData.icsActive) return;
  3654.         /* else fall through */
  3655.       case MachinePlaysWhite:
  3656.       case MachinePlaysBlack:
  3657.       case TwoMachinesPlay:
  3658.         if (forwardMostMove == 0)
  3659.           return;        /* don't pause if no one has moved */
  3660.         if ((gameMode == MachinePlaysWhite &&
  3661.          !WhiteOnMove(forwardMostMove)) ||
  3662.         (gameMode == MachinePlaysBlack &&
  3663.          WhiteOnMove(forwardMostMove))) {
  3664.         StopClocks();
  3665.         }
  3666.         pausing = TRUE;
  3667.         ModeHighlight();
  3668.         break;
  3669.     }
  3670.     }
  3671. }
  3672.  
  3673. void EditCommentEvent()
  3674. {
  3675.     EditCommentPopUp(currentMove, commentList[currentMove]);
  3676. }
  3677.  
  3678. void ReplaceComment(index, text)
  3679.      int index;
  3680.      char *text;
  3681. {
  3682.     int len;
  3683.  
  3684.     len = strlen(text);
  3685.  
  3686.     if (commentList[index] != NULL)
  3687.       free(commentList[index]);
  3688.  
  3689.     if (len == 0) {
  3690.     commentList[index] = NULL;
  3691.     return;
  3692.     }
  3693.  
  3694.     commentList[index] = (char *) malloc(len + 3);
  3695.     if (text[0] != '[' ||
  3696.     (text[len-1] != ']' && text[len-2] != ']')) {
  3697.     strcpy(commentList[index], "[");
  3698.     strcat(commentList[index], text);
  3699.     strcat(commentList[index], "]");
  3700.     } else {
  3701.     strcpy(commentList[index], text);
  3702.     }
  3703. }
  3704.  
  3705. void AppendComment(index, text)
  3706.      int index;
  3707.      char *text;
  3708. {
  3709.     int oldlen, len;
  3710.     char *old;
  3711.  
  3712.     len = strlen(text);
  3713.  
  3714.     if (len == 0) return;
  3715.  
  3716.     if (commentList[index] != NULL) {
  3717.     old = commentList[index];
  3718.     oldlen = strlen(old);
  3719.     commentList[index] = (char *) malloc(oldlen + len + 4);
  3720.     strcpy(commentList[index], old);
  3721.     free(old);
  3722.     if (commentList[index][oldlen - 1] != '\n')
  3723.       strcat(commentList[index], "\n");
  3724.     } else {
  3725.     commentList[index] = (char *) malloc(len + 3);
  3726.     commentList[index][0] = NULLCHAR;
  3727.     }
  3728.  
  3729.     if (text[0] != '[' ||
  3730.     (text[len-1] != ']' && text[len-2] != ']')) {
  3731.     strcat(commentList[index], "[");
  3732.     strcat(commentList[index], text);
  3733.     strcat(commentList[index], "]");
  3734.     } else {
  3735.     strcat(commentList[index], text);
  3736.     }
  3737. }
  3738.  
  3739. void SendToProgram(message, pr)
  3740.      char *message;
  3741.      ProcRef pr;
  3742. {
  3743.     int count, outCount, error;
  3744.     char *which;
  3745.     char buf[MSG_SIZ];
  3746.  
  3747.     if (pr == NULL) return;
  3748.     lastMsgPR = pr;
  3749.     which = (pr == firstProgramPR) ? "first" : "second";
  3750.     
  3751.     if (appData.debugMode)
  3752.       fprintf(debugFP, "Sending to %s: %s", which, message);
  3753.     
  3754.     count = strlen(message);
  3755.     outCount = OutputToProcess(pr, message, count, &error);
  3756.     if (outCount < count) {
  3757.     sprintf(buf, "Error writing to %s chess program", which);
  3758.     DisplayFatalError(buf, error);
  3759.     ExitEvent(1);
  3760.     }
  3761. }
  3762.  
  3763. void ReceiveFromProgram(isr, message, count, error)
  3764.      InputSourceRef isr;
  3765.      char *message;
  3766.      int count;
  3767.      int error;
  3768. {
  3769.     char *end_str, *name, *which;
  3770.  
  3771.     if (count <= 0) {
  3772.     if (isr == firstProgramISR) {
  3773.         which = "first";
  3774.         name = appData.firstChessProgram;
  3775.     } else if (isr == secondProgramISR) {
  3776.         which = "second";
  3777.         name = appData.secondChessProgram;
  3778.     } else {
  3779.         return;
  3780.     }
  3781.     if (count == 0) {
  3782.         sprintf(message, "Error: %s chess program (%s) exited unexpectedly",
  3783.             which, name);
  3784.         DisplayFatalError(message, 0);
  3785.     } else {
  3786.         sprintf(message,
  3787.             "Error reading from %s chess program (%s)",
  3788.             which, name);
  3789.         DisplayFatalError(message, error);
  3790.     }
  3791.     ShutdownChessPrograms(message);
  3792.     return;  /* don't actually exit */
  3793.     }
  3794.     
  3795.     if ((end_str = strchr(message, '\r')) != NULL)
  3796.       *end_str = NULLCHAR;
  3797.     if ((end_str = strchr(message, '\n')) != NULL)
  3798.       *end_str = NULLCHAR;
  3799.     
  3800.     if (appData.debugMode)
  3801.       fprintf(debugFP, "Received from %s: %s\n",
  3802.           isr == firstProgramISR ? "first" : "second", message);
  3803.     HandleMachineMove(message, isr);
  3804. }
  3805.  
  3806.  
  3807. void SendSearchDepth(pr)
  3808.      ProcRef pr;
  3809. {
  3810.     char message[MSG_SIZ];
  3811.     
  3812.     if (appData.searchDepth <= 0) return;
  3813.     
  3814.     sprintf(message, "depth\n%d\nhelp\n", appData.searchDepth);
  3815.     /* note kludge: "help" command forces gnuchessx to print
  3816.        out something that ends with a newline. */
  3817.     SendToProgram(message, pr);
  3818. }
  3819.  
  3820. void SendTimeRemaining(pr)
  3821.      ProcRef pr;
  3822. {
  3823.     char message[MSG_SIZ];
  3824.     long time;
  3825.  
  3826.     if (WhiteOnMove(forwardMostMove))
  3827.       time = whiteTimeRemaining;
  3828.     else
  3829.       time = blackTimeRemaining;
  3830.     
  3831.     if (time <= 0) time = 1000;
  3832.     
  3833.     sprintf(message, "time %d\n", time/10);
  3834.     SendToProgram(message, pr);
  3835. }
  3836.  
  3837.  
  3838. void DisplayMove(moveNumber)
  3839.      int moveNumber;
  3840. {
  3841.     char message[MSG_SIZ];
  3842.     
  3843.     if (moveNumber < 0) {
  3844.     if (moveNumber == forwardMostMove - 1)
  3845.       DisplayMessage(endMessage);
  3846.     else
  3847.       DisplayMessage("");
  3848.     } else {
  3849.     sprintf(message, "%d. %s%s  %s", moveNumber / 2 + 1,
  3850.         WhiteOnMove(moveNumber) ? "" : "... ",
  3851.         parseList[moveNumber],
  3852.         moveNumber == forwardMostMove - 1 ? endMessage : "");
  3853.     DisplayMessage(message);
  3854.     }
  3855. }
  3856.  
  3857. /*
  3858.  * This routine sends a ^C interrupt to gnuchess to awaken it
  3859.  * if it might be busy thinking on our time.  This normally isn't needed,
  3860.  * but is useful on systems where the FIONREAD ioctl doesn't work (such 
  3861.  * as ESIX), since on those systems the gnuchess feature that lets you 
  3862.  * interrupt its thinking just by typing a command does not work.
  3863.  *
  3864.  * In the future, similar code could be used to stop gnuchess and make
  3865.  * it move immediately when it is thinking about its own move; this could
  3866.  * be useful if we want to make Backward or ForceMoves work while gnuchess
  3867.  * is thinking. --t.mann
  3868.  */
  3869. void Attention(pr)
  3870.      ProcRef pr;
  3871. {
  3872. #if defined(ATTENTION) || defined(ESIX)
  3873.     if (appData.noChessProgram || (pr == NoProc)) return;
  3874.     switch (gameMode) {
  3875.       case MachinePlaysWhite:
  3876.       case MachinePlaysBlack:
  3877.       case TwoMachinesPlay:
  3878.     if (forwardMostMove > backwardMostMove + 1 && maybeThinking) {
  3879.         if (appData.debugMode)
  3880.           fprintf(debugFP, "Interrupting %s\n",
  3881.               pr == firstProgramPR ? "first" : "second");
  3882.         InterruptChildProcess(pr);
  3883.     }
  3884.     break;
  3885.     }
  3886. #endif /*ATTENTION*/
  3887. }
  3888.  
  3889. void CheckFlags()
  3890. {
  3891.     if (whiteTimeRemaining <= 0) {
  3892.     if (!whiteFlag) {
  3893.         whiteFlag = TRUE;
  3894.         if (appData.icsActive) {
  3895.         if (appData.autoCallFlag &&
  3896.             gameMode == IcsPlayingBlack && !blackFlag)
  3897.           SendToICS("flag\n");
  3898.         } else {
  3899.         if (blackFlag)
  3900.           DisplayTitle("Both flags have fallen");
  3901.         else
  3902.           DisplayTitle("White's flag has fallen");
  3903.         }
  3904.     }
  3905.     }
  3906.     if (blackTimeRemaining <= 0) {
  3907.     if (!blackFlag) {
  3908.         blackFlag = TRUE;
  3909.         if (appData.icsActive) {
  3910.         if (appData.autoCallFlag &&
  3911.             gameMode == IcsPlayingWhite && !whiteFlag)
  3912.           SendToICS("flag\n");
  3913.         } else {
  3914.         if (whiteFlag)
  3915.           DisplayTitle("Both flags have fallen");
  3916.         else
  3917.           DisplayTitle("Black's flag has fallen");
  3918.         }
  3919.     }
  3920.     }
  3921. }
  3922.  
  3923. void CheckTimeControl()
  3924. {
  3925.     if (!appData.clockMode || appData.icsActive ||
  3926.     gameMode == PlayFromGameFile || forwardMostMove == 0) return;
  3927.     /*
  3928.      * add time to clocks when time control is achieved
  3929.      */
  3930.     if ((forwardMostMove % (appData.movesPerSession * 2)) == 0) {
  3931.     whiteTimeRemaining += timeControl;
  3932.     blackTimeRemaining += timeControl;
  3933.     }
  3934. }
  3935.  
  3936. void DisplayBothClocks()
  3937. {
  3938.     DisplayWhiteClock(whiteTimeRemaining, WhiteOnMove(currentMove));
  3939.     DisplayBlackClock(blackTimeRemaining, !WhiteOnMove(currentMove));
  3940. }
  3941.  
  3942.  
  3943. /* Timekeeping seems to be a portability nightmare.  I think everyone
  3944.    has ftime(), but I'm really not sure, so I'm including some ifdefs
  3945.    to use other calls if you don't.  Clocks will be less accurate if
  3946.    you have neither ftime nor gettimeofday.
  3947. */
  3948.  
  3949. /* A point in time */
  3950. typedef struct {
  3951.     long sec;  /* Assuming this is >= 32 bits */
  3952.     int ms;    /* Assuming this is >= 16 bits */
  3953. } TimeMark;
  3954.  
  3955. /* Get the current time as a TimeMark */
  3956. void GetTimeMark(tm)
  3957.      TimeMark *tm;
  3958. {
  3959. #ifdef HAS_GETTIMEOFDAY
  3960.  
  3961. #if !defined(ESIX)
  3962. #include <sys/time.h>
  3963. #endif
  3964.     struct timeval timeVal;
  3965.     struct timezone timeZone;
  3966.  
  3967.     gettimeofday(&timeVal, &timeZone);
  3968.     tm->sec = (long) timeVal.tv_sec; 
  3969.     tm->ms = (int) (timeVal.tv_usec / 1000L);
  3970.  
  3971. #else /*!HAS_GETTIMEOFDAY*/
  3972. #ifdef HAS_FTIME
  3973.  
  3974. #include <sys/timeb.h>
  3975.     struct timeb timeB;
  3976.  
  3977.     ftime(&timeB);
  3978.     tm->sec = (long) timeB.time;
  3979.     tm->ms = (int) timeB.millitm;
  3980.  
  3981. #else /*!HAS_FTIME && !HAS_GETTIMEOFDAY*/
  3982.  
  3983.     tm->sec = (long) time(NULL);
  3984.     tm->ms = 0;
  3985.  
  3986. #endif
  3987. #endif
  3988. }
  3989.  
  3990. /* Return the difference in milliseconds between two
  3991.    time marks.  We assume the difference will fit in a long!
  3992. */
  3993. long SubtractTimeMarks(tm2, tm1)
  3994.      TimeMark *tm2, *tm1;
  3995. {
  3996.     return 1000L*(tm2->sec - tm1->sec) +
  3997.            (long) (tm2->ms - tm1->ms);
  3998. }
  3999.  
  4000.  
  4001. /*
  4002.  * Code to manage the game clocks.
  4003.  *
  4004.  * In tournament play, black starts the clock and then white makes a move.
  4005.  * We give the human user a slight advantage if he is playing white---the
  4006.  * clocks don't run until he makes his first move, so it takes zero time.
  4007.  * Also, we doesn't account for network lag, so we could get
  4008.  * out of sync with GNU Chess's clock -- but then, referees are always right.
  4009.  */
  4010.  
  4011. static TimeMark tickStartTM;
  4012. static long intendedTickLength;
  4013.  
  4014. long NextTickLength(timeRemaining)
  4015.      long timeRemaining;
  4016. {
  4017.     long nominalTickLength, nextTickLength;
  4018.  
  4019.     if (timeRemaining > 0L && timeRemaining <= 1000L)
  4020.       nominalTickLength = 100L;
  4021.     else
  4022.       nominalTickLength = 1000L;
  4023.     nextTickLength = timeRemaining % nominalTickLength;
  4024.     if (nextTickLength <= 0) nextTickLength += nominalTickLength;
  4025.  
  4026.     return nextTickLength;
  4027. }
  4028.  
  4029. /* Stop clocks and reset to a fresh time control */
  4030. void ResetClocks() 
  4031. {
  4032.     (void) StopClockTimer();
  4033.     whiteTimeRemaining = timeControl;
  4034.     blackTimeRemaining = timeControl;
  4035.     if (whiteFlag || blackFlag) {
  4036.     DisplayTitle("");
  4037.     whiteFlag = blackFlag = FALSE;
  4038.     }
  4039.     DisplayBothClocks();
  4040. }
  4041.     
  4042. #define FUDGE 25 /* 25ms = 1/40 sec; should be plenty even for 50 Hz clocks */
  4043.  
  4044. /* Decrement running clock by amount of time that has passed */
  4045. void DecrementClocks()
  4046. {
  4047.     long timeRemaining;
  4048.     long lastTickLength, fudge;
  4049.     TimeMark now;
  4050.  
  4051.     if (!appData.clockMode) return;
  4052.     
  4053.     GetTimeMark(&now);
  4054.  
  4055.     lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
  4056.  
  4057.     /* Fudge if we woke up a little too soon */
  4058.     fudge = intendedTickLength - lastTickLength;
  4059.     if (fudge < 0 || fudge > FUDGE) fudge = 0;
  4060.  
  4061. /*!!*/if (appData.debugMode && fudge) fprintf(debugFP, "fudging %d   ", fudge);
  4062.  
  4063.     if (WhiteOnMove(forwardMostMove)) {
  4064.     timeRemaining = whiteTimeRemaining -= lastTickLength;
  4065.     DisplayWhiteClock(whiteTimeRemaining - fudge,
  4066.               WhiteOnMove(currentMove));
  4067.     } else {
  4068.     timeRemaining = blackTimeRemaining -= lastTickLength;
  4069.     DisplayBlackClock(blackTimeRemaining - fudge,
  4070.               !WhiteOnMove(currentMove));
  4071.     }
  4072.     
  4073.     CheckFlags();
  4074.     
  4075.     tickStartTM = now;
  4076.     intendedTickLength = NextTickLength(timeRemaining - fudge) + fudge;
  4077.     StartClockTimer(intendedTickLength);
  4078. }
  4079.  
  4080.     
  4081. /* A player has just moved, so stop the previously running
  4082.    clock and (if in clock mode) start the other one.
  4083.    We redisplay both clocks in case we're in ICS mode, because
  4084.    ICS gives us an update to both clocks after every move.
  4085. */
  4086. void SwitchClocks()
  4087. {
  4088.     long lastTickLength;
  4089.     TimeMark now;
  4090.  
  4091.     GetTimeMark(&now);
  4092.  
  4093.     if (StopClockTimer() && appData.clockMode) {
  4094.     lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
  4095.     if (WhiteOnMove(forwardMostMove)) {
  4096.         whiteTimeRemaining -= lastTickLength;
  4097.     } else {
  4098.         blackTimeRemaining -= lastTickLength;
  4099.     }
  4100.     CheckFlags();
  4101.     }
  4102.     CheckTimeControl();
  4103.     DisplayBothClocks();
  4104.  
  4105.     if (!appData.clockMode) return;
  4106.  
  4107.     switch (gameMode) {
  4108.       case MachinePlaysBlack:
  4109.       case MachinePlaysWhite:
  4110.       case BeginningOfGame:
  4111.     if (pausing) return;
  4112.     break;
  4113.  
  4114.       case ForceMoves:
  4115.       case PlayFromGameFile:
  4116.     return;
  4117.  
  4118.       default:
  4119.     break;
  4120.     }
  4121.  
  4122.     tickStartTM = now;
  4123.     intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
  4124.       whiteTimeRemaining : blackTimeRemaining);
  4125.     StartClockTimer(intendedTickLength);
  4126. }
  4127.     
  4128.  
  4129. /* Stop both clocks */
  4130. void StopClocks()
  4131. {    
  4132.     long lastTickLength;
  4133.     TimeMark now;
  4134.  
  4135.     if (!StopClockTimer()) return;
  4136.     if (!appData.clockMode) return;
  4137.  
  4138.     GetTimeMark(&now);
  4139.  
  4140.     lastTickLength = SubtractTimeMarks(&now, &tickStartTM);
  4141.     if (WhiteOnMove(forwardMostMove)) {
  4142.     whiteTimeRemaining -= lastTickLength;
  4143.     DisplayWhiteClock(whiteTimeRemaining, WhiteOnMove(currentMove));
  4144.     } else {
  4145.     blackTimeRemaining -= lastTickLength;
  4146.     DisplayBlackClock(blackTimeRemaining, !WhiteOnMove(currentMove));
  4147.     }
  4148.     CheckFlags();
  4149. }
  4150.     
  4151. /* Start clock of player on move.  Time may have been reset, so
  4152.    if clock is already running, stop and restart it. */
  4153. void StartClocks()
  4154. {
  4155.     (void) StopClockTimer(); /* in case it was running already */
  4156.     DisplayBothClocks();
  4157.  
  4158.     if (!appData.clockMode) return;
  4159.  
  4160.     GetTimeMark(&tickStartTM);
  4161.     intendedTickLength = NextTickLength(WhiteOnMove(forwardMostMove) ?
  4162.       whiteTimeRemaining : blackTimeRemaining);
  4163.     StartClockTimer(intendedTickLength);
  4164. }
  4165.  
  4166. char *TimeString(ms)
  4167.      long ms;
  4168. {
  4169.     long second, minute, hour, day;
  4170.     char *sign = "";
  4171.     static char buf[32];
  4172.     
  4173.     if (ms > 0 && ms <= 900) {
  4174.     /* convert milliseconds to tenths, rounding up */
  4175.     sprintf(buf, " 0.%1d ", (ms+99L)/100L);
  4176.     return buf;
  4177.     }
  4178.  
  4179.     /* convert milliseconds to seconds, rounding up */
  4180.     /* use floating point to avoid strangeness of integer division
  4181.        with negative dividends on many machines */
  4182.     second = (long) floor(((double) (ms + 999L)) / 1000.0);
  4183.  
  4184.     if (second < 0) {
  4185.     sign = "-";
  4186.     second = -second;
  4187.     }
  4188.     
  4189.     day = second / (60 * 60 * 24);
  4190.     second = second % (60 * 60 * 24);
  4191.     hour = second / (60 * 60);
  4192.     second = second % (60 * 60);
  4193.     minute = second / 60;
  4194.     second = second % 60;
  4195.     
  4196.     if (day > 0)
  4197.       sprintf(buf, " %s%d:%02d:%02d:%02d ", sign, day, hour, minute, second);
  4198.     else if (hour > 0)
  4199.       sprintf(buf, " %s%d:%02d:%02d ", sign, hour, minute, second);
  4200.     else
  4201.       sprintf(buf, " %s%2d:%02d ", sign, minute, second);
  4202.     
  4203.     return buf;
  4204. }
  4205.  
  4206.  
  4207. /*
  4208.  * This is necessary because some C libraries aren't ANSI C compliant yet.
  4209.  */
  4210. char *StrStr(string, match)
  4211.      char *string, *match;
  4212. {
  4213.     int i, length;
  4214.     
  4215.     length = strlen(match);
  4216.     
  4217.     for (i = strlen(string) - length; i >= 0; i--, string++)
  4218.       if (!strncmp(match, string, (size_t) length))
  4219.     return string;
  4220.     
  4221.     return NULL;
  4222. }
  4223.  
  4224. int StrCaseCmp(s1, s2)
  4225.      char *s1, *s2;
  4226. {
  4227.     char c1, c2;
  4228.     
  4229.     for (;;) {
  4230.     c1 = ToLower(*s1++);
  4231.     c2 = ToLower(*s2++);
  4232.     if (c1 > c2) return 1;
  4233.     if (c1 < c2) return -1;
  4234.     if (c1 == NULLCHAR) return 0;
  4235.     }
  4236. }
  4237.  
  4238.  
  4239. int ToLower(c)
  4240.      int c;
  4241. {
  4242.     return isupper(c) ? tolower(c) : c;
  4243. }
  4244.  
  4245.  
  4246. int ToUpper(c)
  4247.      int c;
  4248. {
  4249.     return islower(c) ? toupper(c) : c;
  4250. }
  4251.  
  4252.  
  4253.